diff --git a/Jenkinsfile b/Jenkinsfile index c904b25..71ca95c 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -2,24 +2,12 @@ pipeline { agent any environment { - // Non-secret config injected from Jenkins Credentials (Secret Text) - AWS_REGION = credentials('AWS_REGION') - AWS_ACCOUNT_ID = credentials('AWS_ACCOUNT_ID') - CODEART_DOMAIN = credentials('CODEART_DOMAIN') - CODEART_REPO = credentials('CODEART_REPO') - - // Build configuration - Corporate-friendly non-root approach - PYTHON_VERSION = '3.11-slim' - BUILD_IMAGE = "python:${PYTHON_VERSION}" - } - - options { - // Prevent concurrent builds - disableConcurrentBuilds() - // Keep build logs for audit - buildDiscarder(logRotator(numToKeepStr: '50')) - // Timeout protection - timeout(time: 30, unit: 'MINUTES') + BUILD_IMAGE = 'python:3.9-slim' + AWS_REGION = 'us-east-1' + AWS_ACCOUNT_ID = '123456789012' + CODEART_DOMAIN = 'jacquesingram' + CODEART_REPO = 'hello-codeartifact' + CODEART_TOKEN = credentials('codeartifact-token') } stages { @@ -29,22 +17,17 @@ pipeline { } } - stage('Authenticate & Configure') { + stage('Setup') { steps { - withAWS(credentials: 'jenkins-codeartifact', region: env.AWS_REGION) { - script { - // Fetch a short-lived CodeArtifact token - env.CODEART_TOKEN = sh( - script: """ - aws codeartifact get-authorization-token \\ - --domain ${env.CODEART_DOMAIN} \\ - --domain-owner ${env.AWS_ACCOUNT_ID} \\ - --query authorizationToken --output text - """, returnStdout: true - ).trim() - - // Store repository URL for Docker container - env.CODEART_URL = "https://aws:${env.CODEART_TOKEN}@${env.CODEART_DOMAIN}-${env.AWS_ACCOUNT_ID}.d.codeartifact.${env.AWS_REGION}.amazonaws.com/pypi/${env.CODEART_REPO}/simple/" + script { + docker.image(env.BUILD_IMAGE).inside { + sh ''' + pip install --upgrade pip + pip install build twine pytest + if [ -f requirements.txt ]; then + pip install -r requirements.txt + fi + ''' } } } @@ -53,88 +36,21 @@ pipeline { stage('Build') { steps { script { - // Use Docker for consistent, isolated build environment - docker.image(env.BUILD_IMAGE).inside('-e HOME=/tmp -e PIP_CACHE_DIR=/tmp/.pip') { + docker.image(env.BUILD_IMAGE).inside { sh ''' - # Corporate standard: CodeArtifact primary, PyPI fallback - export PIP_INDEX_URL="${CODEART_URL}" - export PIP_EXTRA_INDEX_URL="https://pypi.org/simple/" - - # Install build dependencies - pip install --upgrade setuptools wheel twine - - # Build the package - python3 setup.py sdist bdist_wheel + python -m build ''' } } } } - stage('Security Scan') { - parallel { - stage('Trivy Filesystem Scan') { - steps { - sh ''' - docker run --rm -v ${WORKSPACE}:/project \\ - aquasec/trivy:latest fs \\ - --severity HIGH,CRITICAL \\ - --exit-code 1 \\ - --format json \\ - --output trivy-fs-report.json \\ - /project - ''' - - // Archive security scan results - archiveArtifacts artifacts: 'trivy-fs-report.json', allowEmptyArchive: true - } - } - - stage('Package Vulnerability Scan') { - when { - // Only scan if build artifacts exist - expression { fileExists('dist/*.whl') } - } - steps { - script { - docker.image(env.BUILD_IMAGE).inside('-e HOME=/tmp -e PIP_CACHE_DIR=/tmp/.pip') { - sh ''' - # Corporate standard: CodeArtifact primary, PyPI fallback - export PIP_INDEX_URL="${CODEART_URL}" - export PIP_EXTRA_INDEX_URL="https://pypi.org/simple/" - - pip install safety - safety check --json --output safety-report.json || true - ''' - } - } - archiveArtifacts artifacts: 'safety-report.json', allowEmptyArchive: true - } - } - } - } - stage('Test') { steps { script { - docker.image(env.BUILD_IMAGE).inside('-e HOME=/tmp -e PIP_CACHE_DIR=/tmp/.pip') { + docker.image(env.BUILD_IMAGE).inside { sh ''' - # Corporate standard: CodeArtifact primary, PyPI fallback - export PIP_INDEX_URL="${CODEART_URL}" - export PIP_EXTRA_INDEX_URL="https://pypi.org/simple/" - - # Install test dependencies if they exist - if [ -f requirements-test.txt ]; then - pip install -r requirements-test.txt - fi - - # Install the built package for testing - pip install dist/*.whl - - # Run tests if they exist - if [ -f pytest.ini ] || [ -d tests ]; then - python -m pytest --junitxml=test-results.xml || true - fi + python -m pytest tests/ --junitxml=test-results.xml || true ''' } } @@ -174,32 +90,18 @@ EOF } } } - + } post { always { // Archive build artifacts - archiveArtifacts artifacts: 'dist/*', allowEmptyArchive: true - - // Clean up workspace to prevent credential leakage - cleanWs() + archiveArtifacts artifacts: 'dist/**', fingerprint: true, allowEmptyArchive: true } - success { - echo '✅ Build succeeded and package published to CodeArtifact.' - - // Notify success (Slack, email, etc.) - // slackSend channel: '#builds', color: 'good', message: "✅ ${env.JOB_NAME} - ${env.BUILD_NUMBER} succeeded" + echo 'Pipeline completed successfully!' } - failure { - echo '❌ Build failed — check the console output for errors.' - - // Notify failure - // slackSend channel: '#builds', color: 'danger', message: "❌ ${env.JOB_NAME} - ${env.BUILD_NUMBER} failed" + echo 'Pipeline failed!' } - - unstable { - echo '⚠️ Build unstable — tests may have failed.' - } - } \ No newline at end of file + } +} \ No newline at end of file