---
name: cicd-pipeline
description: Use when creating, updating, reviewing, or fixing a Jenkinsfile for a PDEGO service. Triggers on phrases like "add a Jenkinsfile", "set up CI/CD", "create a pipeline", "my deploy stage broke", "update the pipeline", "make the Jenkinsfile consistent", "review my pipeline". SKIP if the repo uses GitHub Actions (.github/workflows/ exists) or is not a PDEGO service (ows-* or lambda-*).
---

# CI/CD Pipeline

## When to Use This Skill

**TRIGGER — create mode** on any of these (or close variants):
- "Add a Jenkinsfile to my service"
- "Set up CI/CD for my service"
- "Create a pipeline for my new service"

**TRIGGER — maintain mode** on any of these (or close variants):
- "My deploy stage is broken / failing"
- "Update the pipeline to follow PDEGO standards"
- "Review my Jenkinsfile for consistency"
- "Make the pipeline more consistent"
- "My build isn't scanning the Docker image on PRs"
- "Check my pipeline"
- "What's wrong with my Jenkinsfile"

**SKIP** if:
- The repo has a `.github/workflows/` directory (GitHub Actions, not Jenkins)
- The user is asking about a non-Jenkins CI system (CircleCI, GitLab CI, etc.)

## When to Load Each Reference

**Do not preload references.** Load each reference file exactly once, exactly when the condition below is met — no earlier. Loading references speculatively wastes tokens and context.

**Create mode** — follow this sequence:

| When in the workflow | Condition | Load |
|---|---|---|
| Round 1 answer received — service type is Lambda | Always | [lambda-specifics.md](./references/lambda-specifics.md) |
| Round 1 answer received — service type is Fargate | Always | nothing extra |
| About to write the pipeline variables block | Always | [pipeline-variables.md](./references/pipeline-variables.md) |
| About to write Load Shared Libraries or Compliance Checks | Always | [stage-load-shared-libraries.md](./references/stage-load-shared-libraries.md), [stage-compliance-checks.md](./references/stage-compliance-checks.md) |
| About to write the Build Test and Scan stage | Always | [stage-build-test-scan.md](./references/stage-build-test-scan.md) |
| About to write the Deploy stages | Always | [stage-deploy.md](./references/stage-deploy.md) |
| About to write the post block | Always | [stage-post-notifications.md](./references/stage-post-notifications.md) |

**Maintain mode** — load all references upfront before running the checklist, since you need full context to detect drift across all stages:

| Condition | Load |
|---|---|
| Service type is Lambda | [lambda-specifics.md](./references/lambda-specifics.md) |
| Always | [pipeline-variables.md](./references/pipeline-variables.md), [stage-load-shared-libraries.md](./references/stage-load-shared-libraries.md), [stage-compliance-checks.md](./references/stage-compliance-checks.md), [stage-build-test-scan.md](./references/stage-build-test-scan.md), [stage-deploy.md](./references/stage-deploy.md), [stage-post-notifications.md](./references/stage-post-notifications.md) |

## Overview

PDEGO pipelines are Jenkins declarative pipelines that build, test, scan, and deploy services to QA and production. All pipelines share a common stage structure and must follow the principles below.

## Principles

**Shift-left security.** Vulnerability scans (`dockerScan`, `sastTests`) run during PR review, not after merge. Findings block the PR, not a production deployment.

**No concurrent builds.** `disableConcurrentBuilds()` must always be set in the `options` block to prevent deployment race conditions.

**Maximise parallelism.** All steps within a stage that have no ordering dependency must run inside a `parallel {}` block.

## Pipeline Variables

Every Jenkinsfile must declare these variables at the top, outside the `pipeline {}` block. See [pipeline-variables.md](./references/pipeline-variables.md) for values, naming conventions, and notes on each.

```groovy
String GITHUB_REPOSITORY = '<service-name>'
String ECR_ACCOUNT_ID = '<ecr-account-id>'
List<String> AWS_REGIONS = ['us-east-1']
String SLACK_NOTIFICATIONS_CHANNEL = '<channel>'
String QA_ACCOUNT_ID = '<qa-account-id>'
String QA_DEPLOYMENT_ROLE = '<qa-role-name>'
String PROD_ACCOUNT_ID = '<prod-account-id>'
String PROD_DEPLOYMENT_ROLE = '<prod-role-name>'
List<String> VULNERABILITIES_TO_IGNORE = []
```

## Required Stages

Every PDEGO pipeline must include these stages in the following order:

| # | Stage | Branch | Reference |
|---|-------|---------|-----------|
| 1 | Load Shared Libraries | all | [stage-load-shared-libraries.md](./references/stage-load-shared-libraries.md) |
| 2 | Compliance Checks | all | [stage-compliance-checks.md](./references/stage-compliance-checks.md) |
| 3 | Build Test and Scan | all | [stage-build-test-scan.md](./references/stage-build-test-scan.md) |
| 4 | Deploy to QA | master only | [stage-deploy.md](./references/stage-deploy.md) |
| 5 | Integration / E2E Tests | master only | [stage-deploy.md](./references/stage-deploy.md) |
| 6 | Deploy to Prod | master only | [stage-deploy.md](./references/stage-deploy.md) |

**Post actions** (Slack regression/fixed notifications, workspace cleanup) are required on all pipelines — see [stage-post-notifications.md](./references/stage-post-notifications.md).

## Required Parameters

```groovy
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.')
}
```

## Required Triggers

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

## Questions to Ask Before Writing a New Pipeline

Use the `AskUserQuestion` tool for each round — this enforces a pause for the answer. `AskUserQuestion` supports a maximum of 4 questions per call and always provides an "Other" option for free-text input. Provide the most common values as options so the user can select them directly; they will use "Other" to type a custom value.

**Round 1 — Context + ECR** (3 questions):
- Service type: Fargate service / Lambda mono-repo
- GitHub repository name (provide `ows-my-service` and `lambda-my-service` as format examples)
- ECR account ID (provide `086679231553` as the default option)

**Round 2 — QA deployment** (2 questions):
- QA account ID (free-text, provide a format example)
- QA deployment role name (provide `qa-jenkins-pipeline-deploy-role` as the default option)

**Round 3 — Prod deployment** (2 questions):
- Prod account ID (free-text, provide a format example)
- Prod deployment role name (provide `prod-jenkins-pipeline-deploy-role` as the default option)

**Round 4 — Slack + Notification preference** (2 questions):
- Slack notifications channel (provide `#permissions-platform-alerts` as an option)
- Should the pipeline notify on every successful master build (`always`), or only on status changes (`regression`/`fixed`)? See [stage-post-notifications.md](./references/stage-post-notifications.md) for the tradeoff.

**Round 5 — E2E Tests** (Fargate only, 2 questions):
- Does the service have E2E (Playwright) tests? (Yes / No)
- If yes: what Playwright tag(s) should be used? (provide `@qa_smoke` as the default option; engineers with additional or service-specific tags should select "Other" and type them — multiple tags must be separated with ` or `, e.g. `@qa_smoke or @ows_my_service`)

Skip Round 5 entirely for Lambda mono-repos.

**Variable mapping** (answers → Jenkinsfile variables):

| Answer | Variable |
|---|---|
| ECR account ID | `ECR_ACCOUNT_ID` |
| QA account ID | `QA_ACCOUNT_ID` |
| QA deployment role | `QA_DEPLOYMENT_ROLE` |
| Prod account ID | `PROD_ACCOUNT_ID` |
| Prod deployment role | `PROD_DEPLOYMENT_ROLE` |
| Slack channel | `SLACK_NOTIFICATIONS_CHANNEL` |

## Maintain an Existing Pipeline

Use this workflow when the user has an existing Jenkinsfile and wants it reviewed, fixed, or brought into compliance.

**Step 1 — Determine service type.**
Use `AskUserQuestion` to ask whether the service is a Fargate service or a Lambda mono-repo. Load references accordingly (see maintain mode table above).

**Step 2 — Run the Verification Checklist.**
Work through every item in the Verification Checklist below against the existing file. For each failure, note:
- What is wrong (the specific item that failed)
- What the fix is (the exact change needed)

**Step 3 — Propose changes before editing.**
Present all failures as a numbered list. For each item, show the current value (or note it is absent) and the required value. Example:

```
1. `disableConcurrentBuilds()` missing from options block — must be added.
2. `Build Test and Scan` stage is not wrapped in `parallel {}` — sub-stages must run in parallel.
3. `imageTag` in Create and Scan a Release uses `env.BUILD_NUMBER` — must be `env.GIT_COMMIT`.
```

Ask the user to confirm before making any edits.

**Step 4 — Apply minimal edits.**
Fix only the items that failed the checklist. Do not reformat, rename, or restructure anything that is not a compliance failure. Preserve all custom stages, environment variables, make targets, and other team-specific additions.

**Step 5 — Re-run the Verification Checklist.**
After editing, verify every item passes. Report the result explicitly.

## Assembly Checklist (Creating a New Pipeline)

Write the Jenkinsfile in this exact order. Do not skip or reorder sections.

**1. Pipeline-level variables** (top of file, before `pipeline {}`)
- Fill all values from interview answers using the variable mapping table above
- Lambda: add `String LAMBDA_FUNCTION_NAMES = ''` parameter and include `@Field` import if needed

**2. `pipeline {` block**
- Fargate: `agent any`
- Lambda: `agent none` — required, individual stages declare their own agents

**3. `options {`**
- Always include: `ansiColor('xterm')`, `disableConcurrentBuilds()`, `timestamps()`

**4. `parameters {`**
- Always include: `DEPLOY_TO_PROD`, `SHARED_LIBRARIES_VERSION`
- Lambda only: add `LAMBDA_FUNCTION_NAMES`

**5. `triggers {`**
- Always include: `issueCommentTrigger('.*retest this please.*')`

**6. `stages {`** — in this order:

  1. **Load Shared Libraries** — always, all branches
  2. **Compliance Checks** — always, all branches. Lambda: wrap with `withModifiedFunctions`
  3. **Build Test and Scan** — always, all branches. Must use `parallel {}` containing:
     - Validate Software Catalog Definition
     - Unit Tests and Style Checks — must set `COMPOSE_PROJECT_NAME` env var
     - Static Application Security Tests
     - Sonar Scan and Analysis — `when { branch 'master' }` only
     - Create and Scan a Release — `imageTag: env.GIT_COMMIT`
     - Lambda: every sub-stage wraps steps with `withModifiedFunctions`
  4. **Deploy to QA** — `when { branch 'master' }`. Fargate: `fargateDeploy`. Lambda: `lambdaDeploy` inside `withModifiedFunctions`
  5. **Integration Tests** — `when { branch 'master' }`. Fargate only. Omit for Lambda.
     **E2E Tests** — `when { branch 'master' }`. Fargate only; omit if Round 5 answer was No. Use `playwrightTests tags: env.TAGS` with `TAGS` set to the tag from Round 5.
  6. **Deploy to Prod** — `when { allOf { branch 'master'; expression { params.DEPLOY_TO_PROD } } }`. Call `datadogSoftwareCatalogPublish()` after deploy

**7. `post {`**
- `always` or `regression`/`fixed` per notification preference answer
- Always include `cleanup { cleanWs() }`

**8. Helper functions** (Lambda only, after closing `}` of `pipeline {}`)
- `withModifiedFunctions(Map args = [:], Closure steps)`
- `getMonorepoUtils()`

## Verification Checklist

After writing the Jenkinsfile, read it back and verify every item below before reporting success. Do not skip this step. If any item fails, fix the file before declaring done.

**Structure**
- [ ] Pipeline-level variables declared before `pipeline {}`
- [ ] `disableConcurrentBuilds()` present in `options {}`
- [ ] `ansiColor('xterm')` and `timestamps()` present in `options {}`
- [ ] `DEPLOY_TO_PROD` and `SHARED_LIBRARIES_VERSION` present in `parameters {}`
- [ ] `issueCommentTrigger('.*retest this please.*')` present in `triggers {}`

**Stages — all six present in order**
- [ ] Stage 1: `Load Shared Libraries`
- [ ] Stage 2: `Compliance Checks`
- [ ] Stage 3: `Build Test and Scan` — wrapped in `parallel {}`
- [ ] Stage 4: `Deploy to QA` — guarded by `when { branch 'master' }`
- [ ] Stage 5: `Integration Tests` — Fargate only; guarded by `when { branch 'master' }` (omit for Lambda)
- [ ] Stage 5: `E2E Tests` — Fargate only; present only if Round 5 answer was Yes; uses `playwrightTests tags: env.TAGS`
- [ ] Stage 6: `Deploy to Prod` — guarded by `when { allOf { branch 'master'; expression { params.DEPLOY_TO_PROD } } }`

**Build Test and Scan sub-stages — all five present inside `parallel {}`**
- [ ] `Validate Software Catalog Definition`
- [ ] `Unit Tests and Style Checks` — `COMPOSE_PROJECT_NAME` env var set
- [ ] `Static Application Security Tests`
- [ ] `Sonar Scan and Analysis` — guarded by `when { branch 'master' }`
- [ ] `Create and Scan a Release` — `imageTag: env.GIT_COMMIT`

**Post block**
- [ ] `post {}` block present
- [ ] `cleanup { cleanWs() }` present

**Fargate-specific**
- [ ] `agent any` at pipeline level
- [ ] `fargateDeploy` used in Deploy to QA and Deploy to Prod
- [ ] `datadogSoftwareCatalogPublish()` called after prod deploy
- [ ] E2E Tests stage present if Round 5 answer was Yes; omitted if No
- [ ] If E2E Tests present: `playwrightTests tags: env.TAGS` with `TAGS` environment variable set to the tag from Round 5

**Lambda-specific**
- [ ] `agent none` at pipeline level
- [ ] `LAMBDA_FUNCTION_NAMES` parameter present
- [ ] All stages wrap steps with `withModifiedFunctions`
- [ ] `withModifiedFunctions` and `getMonorepoUtils` helper functions defined after `pipeline {}` closing brace
- [ ] `datadogSoftwareCatalogPublish(servicePath: "lambda/${functionName}")` called after prod deploy

## Service Type Variations

- **Fargate services** — use `fargateDeploy` in deploy stages. See [stage-build-test-scan.md](./references/stage-build-test-scan.md) and [stage-deploy.md](./references/stage-deploy.md).
- **Lambda mono-repos** — require per-function parallelisation via `withModifiedFunctions`. See [lambda-specifics.md](./references/lambda-specifics.md).
