- .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/
5.6 KiB
| name | description |
|---|---|
| azure-devops-pipeline | Generates Azure DevOps pipeline YAML using EKS-Pool with nonprod auto-deploy and prod manual approval gate. Always load this skill first, then load the type-specific skill before generating any YAML. |
What I do
Guide the generation of a complete azure-pipelines.yml file for a self-hosted EKS-Pool Azure DevOps agent pool. I define all shared standards. You MUST also load the appropriate type skill before generating YAML:
- Lambda deployments → load
azure-pipeline-lambda - Ansible playbooks → load
azure-pipeline-ansible - Docker builds → load
azure-pipeline-docker
IMPORTANT — do not generate YAML without loading a type skill
STOP. Before generating any pipeline YAML, you MUST load the type skill that matches the requested pipeline type:
azure-pipeline-lambdafor Lambdaazure-pipeline-ansiblefor Ansibleazure-pipeline-dockerfor Docker
Generate nothing until that skill is loaded.
Required inputs — ask the user for these before generating
- Service/repo name — used in display names and tags
- Pipeline type —
lambda|ansible|docker - Target tier —
nonprod|prod - Trigger branch — branch that triggers auto-deploy (default:
main) - Secret sources — which are in use:
ADO variable groups|AWS SSM/Secrets Manager|Vault/OpenBao(can be multiple) - ADO variable group name(s) — if ADO variable groups selected
Pipeline skeleton — always use this structure
trigger:
branches:
include:
- <trigger-branch>
pool: EKS-Pool
stages:
- stage: Lint
displayName: "Lint"
jobs:
- job: Lint
pool: EKS-Pool
timeoutInMinutes: 30
continueOnError: false
steps: [] # type skill fills this in
- stage: SecurityScan
displayName: "Security Scan"
dependsOn: Lint
condition: succeeded()
jobs:
- job: SecurityScan
pool: EKS-Pool
timeoutInMinutes: 30
continueOnError: false
steps: [] # type skill fills this in
- stage: Build
displayName: "Build"
dependsOn: SecurityScan
condition: succeeded()
jobs:
- job: Build
pool: EKS-Pool
timeoutInMinutes: 30
continueOnError: false
steps: [] # type skill fills this in
- stage: DeployNonprod
displayName: "Deploy — Nonprod"
dependsOn: Build
condition: succeeded()
jobs:
- deployment: DeployNonprod
displayName: "Deploy to Nonprod"
pool: EKS-Pool
timeoutInMinutes: 30
environment: nonprod
strategy:
runOnce:
deploy:
steps: [] # type skill fills this in
- stage: DeployProd
displayName: "Deploy — Prod"
dependsOn: DeployNonprod
condition: and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/<trigger-branch>'))
jobs:
- deployment: DeployProd
displayName: "Deploy to Prod"
pool: EKS-Pool
timeoutInMinutes: 30
environment: prod # manual approval gate configured in ADO environment settings
strategy:
runOnce:
deploy:
steps: [] # type skill fills this in + git tag step below
Prod tier pipelines
When target tier is prod, omit DeployNonprod entirely. The pipeline contains only Lint → SecurityScan → Build → DeployProd with the manual approval gate.
When target tier is nonprod, omit DeployProd entirely.
Git tagging on prod deploy
Add this as the final step inside DeployProd's steps (prod tier only):
- script: |
git config user.email "azdo-pipeline@$(System.TeamProject)"
git config user.name "Azure DevOps Pipeline"
git remote set-url origin "https://x-token:$(System.AccessToken)@$(echo $BUILD_REPOSITORY_URI | sed 's|https://||')"
git tag $(Build.BuildNumber) $(Build.SourceVersion)
git push origin $(Build.BuildNumber)
displayName: "Tag commit with build number"
env:
SYSTEM_ACCESSTOKEN: $(System.AccessToken)
BUILD_REPOSITORY_URI: $(Build.Repository.Uri)
Secret handling patterns
Emit the correct block(s) based on declared secret sources:
ADO variable groups
variables:
- group: <variable-group-name>
Reference values as $(VAR_NAME) throughout the pipeline.
AWS SSM Parameter Store
- script: |
VALUE=$(aws ssm get-parameter \
--name "/myapp/mykey" \
--with-decryption \
--query "Parameter.Value" \
--output text)
echo "##vso[task.setvariable variable=MY_VAR;issecret=true]$VALUE"
displayName: "Fetch secret from SSM"
AWS Secrets Manager
- script: |
VALUE=$(aws secretsmanager get-secret-value \
--secret-id "myapp/mykey" \
--query "SecretString" \
--output text)
echo "##vso[task.setvariable variable=MY_VAR;issecret=true]$VALUE"
displayName: "Fetch secret from Secrets Manager"
Vault / OpenBao
- script: |
VALUE=$(vault kv get -field=mykey secret/myapp/mykey)
echo "##vso[task.setvariable variable=MY_VAR;issecret=true]$VALUE"
displayName: "Fetch secret from Vault"
env:
VAULT_ADDR: $(VAULT_ADDR)
VAULT_TOKEN: $(VAULT_TOKEN)
Hard rules — always follow these
pool: EKS-Poolon every job — no exceptionstimeoutInMinutes: 30on every jobcontinueOnError: falseat job level on every job (not step level). Step-levelcontinueOnErrormay be omitted.- No secrets hardcoded in YAML — all via variable groups or runtime fetch
- Every stage and job has a
displayName:set pool: EKS-Poolmust appear at job level, not stage level, to ensure it applies correctly