autojanet/skills/azure-pipeline-docker/SKILL.md
Zoë cc74ad0bd0
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
fix: use library/ Harbor project, add skills, fix pipeline secrets
- .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/
2026-05-30 15:43:14 -07:00

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