autojanet/skills/azure-devops-pipeline/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

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-lambda for Lambda
  • azure-pipeline-ansible for Ansible
  • azure-pipeline-docker for Docker

Generate nothing until that skill is loaded.

Required inputs — ask the user for these before generating

  1. Service/repo name — used in display names and tags
  2. Pipeline typelambda | ansible | docker
  3. Target tiernonprod | prod
  4. Trigger branch — branch that triggers auto-deploy (default: main)
  5. Secret sources — which are in use: ADO variable groups | AWS SSM/Secrets Manager | Vault/OpenBao (can be multiple)
  6. 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 LintSecurityScanBuildDeployProd 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-Pool on every job — no exceptions
  • timeoutInMinutes: 30 on every job
  • continueOnError: false at job level on every job (not step level). Step-level continueOnError may be omitted.
  • No secrets hardcoded in YAML — all via variable groups or runtime fetch
  • Every stage and job has a displayName: set
  • pool: EKS-Pool must appear at job level, not stage level, to ensure it applies correctly