Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
- .woodpecker.yaml: image paths -> library/autojanet-{agent,dispatcher}
- .woodpecker.yaml: secret names RS_HARBOR_USER / RS_HARBOR_PASS (global)
- container/Dockerfile: restore COPY skills/, skills/ populated from opencode config
- skills/: 84 opencode skills bundled into image
- k8s/manifests: update image refs to library/
160 lines
5.6 KiB
Markdown
160 lines
5.6 KiB
Markdown
---
|
|
name: azure-pipeline-docker
|
|
description: Extends azure-devops-pipeline for Docker image builds and pushes. Handles buildx with layer caching, Trivy scanning, ECR and ACR login, and a git-SHA/tag tagging strategy. Always load azure-devops-pipeline first.
|
|
---
|
|
|
|
## What I add
|
|
|
|
Type-specific steps for Docker image pipelines. Merge these into the skeleton from `azure-devops-pipeline`.
|
|
|
|
## Additional required inputs — ask the user
|
|
|
|
1. **Registry type** — `ECR` | `ACR`
|
|
2. **Registry URL** — e.g. `123456789.dkr.ecr.us-east-1.amazonaws.com` or `myregistry.azurecr.io`
|
|
3. **Image repository name** — e.g. `myapp/api`
|
|
4. **Dockerfile path** — default `./Dockerfile`
|
|
5. **AWS region** — required if ECR
|
|
6. **AWS service connection name** — required if ECR
|
|
7. **ACR service connection name** — required if ACR
|
|
|
|
## Lint stage steps
|
|
|
|
```yaml
|
|
- script: |
|
|
docker run --rm -i hadolint/hadolint < <dockerfile-path>
|
|
displayName: "Lint — hadolint Dockerfile"
|
|
```
|
|
|
|
## Security scan stage steps
|
|
|
|
The security scan builds the image locally and runs Trivy against it **before** pushing. This ensures vulnerabilities are caught pre-push.
|
|
|
|
```yaml
|
|
- script: |
|
|
docker build \
|
|
-t scan-target:$(Build.SourceVersion) \
|
|
-f <dockerfile-path> \
|
|
.
|
|
docker run --rm \
|
|
-v /var/run/docker.sock:/var/run/docker.sock \
|
|
aquasec/trivy:latest image \
|
|
--exit-code 1 \
|
|
--severity HIGH,CRITICAL \
|
|
--format json \
|
|
--output trivy-results.json \
|
|
scan-target:$(Build.SourceVersion)
|
|
displayName: "Security scan — Trivy"
|
|
- task: PublishBuildArtifacts@1
|
|
inputs:
|
|
pathToPublish: trivy-results.json
|
|
artifactName: security-scan
|
|
condition: always()
|
|
displayName: "Publish Trivy results"
|
|
```
|
|
|
|
Note: `condition: always()` on the publish step ensures results are available even when Trivy exits 1. The `--exit-code 1` on the scan step itself still fails the pipeline on HIGH/CRITICAL findings.
|
|
|
|
## Build stage steps
|
|
|
|
### Step order — always emit in this order
|
|
|
|
1. Registry login
|
|
2. docker buildx build + push
|
|
|
|
### Registry login — ECR
|
|
|
|
```yaml
|
|
- script: |
|
|
aws ecr get-login-password --region <aws-region> | \
|
|
docker login --username AWS --password-stdin <registry-url>
|
|
displayName: "Login — ECR"
|
|
env:
|
|
AWS_DEFAULT_REGION: <aws-region>
|
|
# Wire the OIDC service connection at the job level, not inside the script step.
|
|
# In the job or deployment job that contains this step, set:
|
|
#
|
|
# job: Build
|
|
# pool: EKS-Pool
|
|
# container: {} # omit if not containerised
|
|
# services:
|
|
# ...
|
|
#
|
|
# For OIDC federation, the AWSCLI task approach is preferred.
|
|
# Alternatively, wrap with AWSShellScript@1:
|
|
#
|
|
# - task: AWSShellScript@1
|
|
# inputs:
|
|
# awsCredentials: <aws-service-connection-name>
|
|
# regionName: <aws-region>
|
|
# scriptType: inline
|
|
# inlineScript: |
|
|
# aws ecr get-login-password --region <aws-region> | \
|
|
# docker login --username AWS --password-stdin <registry-url>
|
|
# displayName: "Login — ECR (via service connection)"
|
|
```
|
|
|
|
AWS credentials come from the OIDC service connection configured on the job — do not add any `AWS_ACCESS_KEY_ID` or `AWS_SECRET_ACCESS_KEY` env vars.
|
|
|
|
### Registry login — ACR
|
|
|
|
```yaml
|
|
- task: Docker@2
|
|
inputs:
|
|
command: login
|
|
containerRegistry: <acr-service-connection-name>
|
|
displayName: "Login — ACR"
|
|
```
|
|
|
|
### Build and push — nonprod
|
|
|
|
```yaml
|
|
- script: |
|
|
docker buildx create --use --name pipeline-builder 2>/dev/null || \
|
|
docker buildx use pipeline-builder
|
|
docker buildx build \
|
|
--cache-from type=registry,ref=<registry-url>/<image-repo>:cache \
|
|
--cache-to type=registry,ref=<registry-url>/<image-repo>:cache,mode=max \
|
|
--tag <registry-url>/<image-repo>:$(Build.SourceVersion) \
|
|
--tag <registry-url>/<image-repo>:latest \
|
|
--file <dockerfile-path> \
|
|
--push \
|
|
.
|
|
displayName: "Build and push — nonprod"
|
|
```
|
|
|
|
### Build and push — prod
|
|
|
|
```yaml
|
|
- script: |
|
|
docker buildx create --use --name pipeline-builder 2>/dev/null || \
|
|
docker buildx use pipeline-builder
|
|
docker buildx build \
|
|
--cache-from type=registry,ref=<registry-url>/<image-repo>:cache \
|
|
--cache-to type=registry,ref=<registry-url>/<image-repo>:cache,mode=max \
|
|
--tag <registry-url>/<image-repo>:$(Build.SourceBranchName) \
|
|
--tag <registry-url>/<image-repo>:$(Build.SourceVersion) \
|
|
--file <dockerfile-path> \
|
|
--push \
|
|
.
|
|
displayName: "Build and push — prod"
|
|
```
|
|
|
|
## Tagging strategy
|
|
|
|
| Tier | Tags applied |
|
|
|---------|---------------------------------------------------|
|
|
| Nonprod | `<git-sha>`, `latest` |
|
|
| Prod | `<git-tag / branch-name>`, `<git-sha>` |
|
|
|
|
Never tag prod images as `latest`.
|
|
|
|
## Hard rules for Docker
|
|
|
|
- Always use `docker buildx` — never plain `docker build`
|
|
- Trivy scan must run before push — the scan in SecurityScan stage uses a locally built image, not a registry pull
|
|
- `--exit-code 1` on Trivy is non-negotiable — HIGH and CRITICAL findings must fail the pipeline
|
|
- Never tag prod images as `latest` — prod tags use `$(Build.SourceBranchName)` and `$(Build.SourceVersion)` only
|
|
- Build args containing secrets must come from ADO variables injected via `env:` — never hardcoded in YAML
|
|
- Registry layer cache lives in the registry itself (not ADO pipeline cache) for reproducibility across EKS-Pool agents
|
|
- ECR login uses OIDC credentials only — never hardcode `AWS_ACCESS_KEY_ID` or `AWS_SECRET_ACCESS_KEY`
|
|
- The `docker buildx create --use ... || docker buildx use ...` pattern is required to handle re-use across runs without error
|