# Jenkinsfile Reference

## Full Template

```groovy
String GITHUB_REPOSITORY         = 'ows-<domain>'
String ECR_ACCOUNT_ID            = '<ecr-account-id>'            // e.g. 086679231553
List<String> AWS_REGIONS         = ['us-east-1']
String SLACK_NOTIFICATIONS_CHANNEL = '#insights-engineering'
String QA_ACCOUNT_ID             = '<qa-account-id>'
String QA_DEPLOYMENT_ROLE        = '<qa-deployment-role>'        // e.g. qa-jenkins-pipeline-deploy-role
String PROD_ACCOUNT_ID           = '<prod-account-id>'
String PROD_DEPLOYMENT_ROLE      = '<prod-deployment-role>'      // e.g. prod-jenkins-pipeline-deploy-role
List<String> VULNERABILITIES_TO_IGNORE = []                      // Empty until a CVE is investigated

pipeline {
    agent any

    options {
        ansiColor('xterm')
        disableConcurrentBuilds()
        timestamps()
    }

    parameters {
        booleanParam(name: 'DEPLOY_TO_PROD', defaultValue: true,
            description: 'Whether or not to deploy to prod.')
        string(name: 'SHARED_LIBRARIES_VERSION', defaultValue: 'master',
            description: 'The version of the Jenkins shared libraries to use.')
    }

    triggers {
        issueCommentTrigger('.*retest this please.*')
    }

    stages {
        stage('Load Shared Libraries') {
            steps {
                library "jenkins-global-libraries@${params.SHARED_LIBRARIES_VERSION}"
            }
        }

        stage('Compliance Checks') {
            steps {
                complianceChecks()
            }
        }

        stage('Build Test and Scan') {
            parallel {
                stage('Validate Software Catalog Definition') {
                    steps {
                        datadogSoftwareCatalogValidate()
                    }
                }

                stage('Unit Tests and Style Checks') {
                    environment {
                        COMPOSE_PROJECT_NAME = "${env.BUILD_TAG}"
                        ENVIRONMENT         = 'test'
                        // Dummy Snowflake values — real connections are mocked in tests
                        SNOWFLAKE_ACCOUNT   = 'test'
                        SNOWFLAKE_USER      = 'test'
                        SNOWFLAKE_DATABASE  = 'test'
                        SNOWFLAKE_WAREHOUSE = 'test'
                        SNOWFLAKE_ROLE      = 'test'
                    }
                    steps {
                        sh 'make unit_lint_job'
                    }
                    post {
                        always {
                            xunit([JUnit(pattern: 'pyunit.xml', skipNoTestFiles: true)])
                            recordCoverage(tools: [[parser: 'COBERTURA', pattern: 'coverage.xml']])
                        }
                    }
                }

                stage('Static Application Security Tests') {
                    steps {
                        sastTests()
                    }
                }

                stage('Sonar Scan and Analysis') {
                    when { branch 'master' }
                    steps {
                        sonarScanAndAnalysis()
                    }
                }

                stage('Create and Scan a Release') {
                    steps {
                        dockerToEcr(
                            awsRegions: AWS_REGIONS,
                            ecrAccountId: ECR_ACCOUNT_ID,
                            githubRepository: GITHUB_REPOSITORY,
                            imageTag: env.GIT_COMMIT,
                            dockerBuildTarget: 'deploy',
                            vulnerabilitiesToIgnore: VULNERABILITIES_TO_IGNORE,
                        )
                    }
                }
            }
        }

        stage('Deploy to QA') {
            when { branch 'master' }
            steps {
                fargateDeploy(
                    environment: 'qa',
                    awsAccountId: QA_ACCOUNT_ID,
                    awsDeploymentRoleName: QA_DEPLOYMENT_ROLE,
                    awsRegions: AWS_REGIONS,
                    ecrAccountId: ECR_ACCOUNT_ID,
                    githubRepository: GITHUB_REPOSITORY,
                    imageTag: env.GIT_COMMIT,
                )
            }
        }

        stage('Integration Tests') {
            when { branch 'master' }
            environment {
                ENVIRONMENT = 'qa'
            }
            steps {
                withCredentials([aws(credentialsId: 'jenkins-qa-aws-credentials')]) {
                    sh 'make integration_job'
                }
            }
            post {
                always {
                    xunit([JUnit(pattern: 'integration-pyunit.xml', skipNoTestFiles: true)])
                }
            }
        }

        stage('E2E Tests') {
            when { branch 'master' }
            steps {
                playwrightTests(tags: '@insights')
            }
        }

        stage('Deploy to Prod') {
            when {
                allOf {
                    branch 'master'
                    expression { params.DEPLOY_TO_PROD }
                }
            }
            steps {
                fargateDeploy(
                    environment: 'prod',
                    awsAccountId: PROD_ACCOUNT_ID,
                    awsDeploymentRoleName: PROD_DEPLOYMENT_ROLE,
                    awsRegions: AWS_REGIONS,
                    ecrAccountId: ECR_ACCOUNT_ID,
                    githubRepository: GITHUB_REPOSITORY,
                    imageTag: env.GIT_COMMIT,
                )
                datadogSoftwareCatalogPublish()
            }
        }
    }

    post {
        regression {
            slackNotify(channel: SLACK_NOTIFICATIONS_CHANNEL)
        }
        fixed {
            slackNotify(channel: SLACK_NOTIFICATIONS_CHANNEL)
        }
        cleanup {
            cleanWs()
        }
    }
}
```

## Key Rules

1. **`VULNERABILITIES_TO_IGNORE = []`** in new services. Only add a CVE ID after investigation, with a comment: `// CVE-2025-XXXX: mitigated by <reason>; remove by <date>`.
2. **`imageTag: env.GIT_COMMIT`** — never `env.BUILD_NUMBER`. Git commit SHA is the canonical image tag.
3. **`dockerBuildTarget: 'deploy'`** — only the deploy stage is built and pushed to ECR.
4. **`datadogSoftwareCatalogPublish()`** is called in Deploy to Prod, not QA. Called once per prod deploy.
5. **`datadogSoftwareCatalogValidate()`** runs in every build (inside the parallel Build Test and Scan stage).
6. **`disableConcurrentBuilds()`** is always present — prevents deploy race conditions.
7. **`failBuild: false`** is NOT set — CVE scans block the build (shift-left security). Only override with `VULNERABILITIES_TO_IGNORE` list after investigation.
8. Slack notifications use `regression` + `fixed` (not `always`) — only notify on status changes to reduce noise.
9. **ECR account and deploy account IDs are distinct** in the standard setup. Verify with the platform team before setting values.
10. E2E Tests stage: omit if the service has no Playwright tests. Never omit it by default — ask during setup.

## Makefile Targets (referenced by Jenkinsfile)

```makefile
.PHONY: ci_unit_lint unit_lint_job integration_job

# Used by Jenkins Unit Tests stage (Docker-based)
ci_unit_lint:
	docker-compose -f docker-compose.test.yml run --rm unit-lint

# Used by make ci_unit_lint → inside the pr_tests container
unit_lint_job: lint test_unit mypy

integration_job: test_integration_qa
```

## `docker-compose.test.yml` (for Jenkins unit stage)

```yaml
version: "3.8"

services:
  unit-lint:
    build:
      context: .
      target: pr_tests
    environment:
      ENVIRONMENT: test
      SNOWFLAKE_ACCOUNT: test
      SNOWFLAKE_USER: test
    command: make unit_lint_job
    # Do NOT mount host directory — it overwrites the container's installed venv
```
