From 75f30511833eb72dfe24fb4e2946aab65571173c Mon Sep 17 00:00:00 2001 From: tranch Date: Thu, 9 Jul 2026 18:21:37 +0800 Subject: Promote ACR images manually Remove push-triggered builds and split ACR publishing out of the build workflow. Add a manual promote-acr workflow that copies an existing GHCR tag to ACR, supports explicit target tags, validates the required ACR configuration, and signs the promoted ACR digest. Verification: parsed both workflow YAML files and checked embedded run blocks with bash -n. --- .github/workflows/promote-acr.yml | 147 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 147 insertions(+) create mode 100644 .github/workflows/promote-acr.yml (limited to '.github/workflows/promote-acr.yml') diff --git a/.github/workflows/promote-acr.yml b/.github/workflows/promote-acr.yml new file mode 100644 index 0000000..99a67f8 --- /dev/null +++ b/.github/workflows/promote-acr.yml @@ -0,0 +1,147 @@ +--- +name: Promote image to ACR + +on: + workflow_dispatch: + inputs: + source_tag: + description: "Existing GHCR tag to promote" + required: true + type: string + target_tags: + description: "Space-separated ACR tags to publish; defaults to source_tag" + required: false + type: string + image_name: + description: "Image name override; defaults to image-template.env" + required: false + type: string + +env: + IMAGE_REGISTRY: "ghcr.io/${{ github.repository_owner }}" + +concurrency: + group: ${{ github.workflow }}-${{ inputs.image_name || 'default' }}-${{ inputs.source_tag }} + cancel-in-progress: false + +jobs: + promote_acr: + name: Promote GHCR image to ACR + runs-on: ubuntu-24.04 + + permissions: + contents: read + packages: read + + steps: + - name: Checkout + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7 + + - name: Install just + uses: extractions/setup-just@53165ef7e734c5c07cb06b3c8e7b647c5aa16db3 #v4.0.0 + + - name: Install skopeo + run: | + sudo apt-get update + sudo apt-get install -y skopeo + + - name: Prepare image variables + env: + IMAGE_NAME_INPUT: ${{ inputs.image_name }} + SOURCE_TAG: ${{ inputs.source_tag }} + TARGET_TAGS_INPUT: ${{ inputs.target_tags }} + run: | + set -euo pipefail + + image_name="${IMAGE_NAME_INPUT:-$(just image_name)}" + target_tags="${TARGET_TAGS_INPUT:-$SOURCE_TAG}" + + echo "IMAGE_REGISTRY=${IMAGE_REGISTRY,,}" >> "$GITHUB_ENV" + echo "IMAGE_NAME=${image_name,,}" >> "$GITHUB_ENV" + echo "SOURCE_TAG=${SOURCE_TAG}" >> "$GITHUB_ENV" + echo "TARGET_TAGS=${target_tags}" >> "$GITHUB_ENV" + + - name: Validate ACR configuration + env: + ACR_REGISTRY: ${{ vars.ACR_REGISTRY }} + ACR_NAMESPACE: ${{ vars.ACR_NAMESPACE }} + ACR_USERNAME: ${{ secrets.ACR_USERNAME }} + ACR_PASSWORD: ${{ secrets.ACR_PASSWORD }} + COSIGN_PRIVATE_KEY: ${{ secrets.SIGNING_SECRET }} + run: | + set -euo pipefail + + : "${ACR_REGISTRY:?Repository variable ACR_REGISTRY is required.}" + : "${ACR_NAMESPACE:?Repository variable ACR_NAMESPACE is required.}" + : "${ACR_USERNAME:?Repository secret ACR_USERNAME is required.}" + : "${ACR_PASSWORD:?Repository secret ACR_PASSWORD is required.}" + : "${COSIGN_PRIVATE_KEY:?Repository secret SIGNING_SECRET is required.}" + + - name: Login to GitHub Container Registry + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + set -euo pipefail + echo "$GITHUB_TOKEN" | sudo -E podman login ghcr.io -u "${{ github.actor }}" --password-stdin + + - name: Login to ACR + env: + ACR_REGISTRY: ${{ vars.ACR_REGISTRY }} + ACR_USERNAME: ${{ secrets.ACR_USERNAME }} + ACR_PASSWORD: ${{ secrets.ACR_PASSWORD }} + run: | + set -euo pipefail + echo "$ACR_PASSWORD" | sudo -E podman login "$ACR_REGISTRY" -u "$ACR_USERNAME" --password-stdin + echo "$ACR_PASSWORD" | docker login "$ACR_REGISTRY" -u "$ACR_USERNAME" --password-stdin + + - name: Promote image + id: promote + env: + ACR_REGISTRY: ${{ vars.ACR_REGISTRY }} + ACR_NAMESPACE: ${{ vars.ACR_NAMESPACE }} + run: | + set -euo pipefail + + source_image="docker://${IMAGE_REGISTRY}/${IMAGE_NAME}:${SOURCE_TAG}" + first_target="" + + { + echo "# Promote to ACR result" + echo '```' + } >> "$GITHUB_STEP_SUMMARY" + + for tag in ${TARGET_TAGS}; do + dest_image="${ACR_REGISTRY}/${ACR_NAMESPACE}/${IMAGE_NAME}:${tag}" + sudo -E skopeo copy "$source_image" "docker://$dest_image" + echo "$dest_image" >> "$GITHUB_STEP_SUMMARY" + if [ -z "$first_target" ]; then + first_target="$dest_image" + fi + done + + echo '```' >> "$GITHUB_STEP_SUMMARY" + + digest=$(sudo -E skopeo inspect --format '{{.Digest}}' "docker://$first_target") + if [ -z "$digest" ]; then + echo "Failed to resolve digest for $first_target" >&2 + exit 1 + fi + + echo "digest=${digest}" >> "$GITHUB_OUTPUT" + + - name: Install Cosign + uses: sigstore/cosign-installer@6f9f17788090df1f26f669e9d70d6ae9567deba6 # v4.1.2 + with: + cosign-release: 'v2.6.3' + + - name: Sign ACR image + env: + ACR_REGISTRY: ${{ vars.ACR_REGISTRY }} + ACR_NAMESPACE: ${{ vars.ACR_NAMESPACE }} + COSIGN_EXPERIMENTAL: false + COSIGN_PRIVATE_KEY: ${{ secrets.SIGNING_SECRET }} + DIGEST: ${{ steps.promote.outputs.digest }} + run: | + set -euo pipefail + + cosign sign -y --key env://COSIGN_PRIVATE_KEY "${ACR_REGISTRY}/${ACR_NAMESPACE}/${IMAGE_NAME}@${DIGEST}" -- cgit v1.2.3 From 6913c30a0e63b96c8af6b1de084ed103a73eaf2b Mon Sep 17 00:00:00 2001 From: tranch Date: Wed, 15 Jul 2026 18:43:23 +0800 Subject: Fix ACR promotion environment vars Bind the manual promotion job to the selected GitHub Environment so environment-scoped ACR variables and secrets are available. Resolve the image name from the dispatch override first, then IMAGE_NAME, then image-template.env through just. Pass the Cosign password during signing. --- .github/workflows/promote-acr.yml | 36 ++++++++++++++++++++++++------------ 1 file changed, 24 insertions(+), 12 deletions(-) (limited to '.github/workflows/promote-acr.yml') diff --git a/.github/workflows/promote-acr.yml b/.github/workflows/promote-acr.yml index 99a67f8..890962f 100644 --- a/.github/workflows/promote-acr.yml +++ b/.github/workflows/promote-acr.yml @@ -4,6 +4,11 @@ name: Promote image to ACR on: workflow_dispatch: inputs: + environment: + description: "GitHub Environment containing ACR vars/secrets" + required: true + default: testing + type: environment source_tag: description: "Existing GHCR tag to promote" required: true @@ -13,7 +18,7 @@ on: required: false type: string image_name: - description: "Image name override; defaults to image-template.env" + description: "Image name override; defaults to IMAGE_NAME var, then image-template.env" required: false type: string @@ -21,13 +26,14 @@ env: IMAGE_REGISTRY: "ghcr.io/${{ github.repository_owner }}" concurrency: - group: ${{ github.workflow }}-${{ inputs.image_name || 'default' }}-${{ inputs.source_tag }} + group: ${{ github.workflow }}-${{ inputs.environment }}-${{ inputs.image_name || 'default' }}-${{ inputs.source_tag }} cancel-in-progress: false jobs: promote_acr: name: Promote GHCR image to ACR runs-on: ubuntu-24.04 + environment: ${{ inputs.environment }} permissions: contents: read @@ -48,18 +54,21 @@ jobs: - name: Prepare image variables env: IMAGE_NAME_INPUT: ${{ inputs.image_name }} + IMAGE_NAME_VAR: ${{ vars.IMAGE_NAME }} SOURCE_TAG: ${{ inputs.source_tag }} TARGET_TAGS_INPUT: ${{ inputs.target_tags }} run: | set -euo pipefail - image_name="${IMAGE_NAME_INPUT:-$(just image_name)}" + image_name="${IMAGE_NAME_INPUT:-${IMAGE_NAME_VAR:-$(just image_name)}}" target_tags="${TARGET_TAGS_INPUT:-$SOURCE_TAG}" - echo "IMAGE_REGISTRY=${IMAGE_REGISTRY,,}" >> "$GITHUB_ENV" - echo "IMAGE_NAME=${image_name,,}" >> "$GITHUB_ENV" - echo "SOURCE_TAG=${SOURCE_TAG}" >> "$GITHUB_ENV" - echo "TARGET_TAGS=${target_tags}" >> "$GITHUB_ENV" + { + echo "IMAGE_REGISTRY=${IMAGE_REGISTRY,,}" + echo "IMAGE_NAME=${image_name,,}" + echo "SOURCE_TAG=${SOURCE_TAG}" + echo "TARGET_TAGS=${target_tags}" + } >> "$GITHUB_ENV" - name: Validate ACR configuration env: @@ -68,14 +77,16 @@ jobs: ACR_USERNAME: ${{ secrets.ACR_USERNAME }} ACR_PASSWORD: ${{ secrets.ACR_PASSWORD }} COSIGN_PRIVATE_KEY: ${{ secrets.SIGNING_SECRET }} + COSIGN_PASSWORD: ${{ secrets.COSIGN_PASSWORD }} run: | set -euo pipefail - : "${ACR_REGISTRY:?Repository variable ACR_REGISTRY is required.}" - : "${ACR_NAMESPACE:?Repository variable ACR_NAMESPACE is required.}" - : "${ACR_USERNAME:?Repository secret ACR_USERNAME is required.}" - : "${ACR_PASSWORD:?Repository secret ACR_PASSWORD is required.}" - : "${COSIGN_PRIVATE_KEY:?Repository secret SIGNING_SECRET is required.}" + : "${ACR_REGISTRY:?Repository or environment variable ACR_REGISTRY is required.}" + : "${ACR_NAMESPACE:?Repository or environment variable ACR_NAMESPACE is required.}" + : "${ACR_USERNAME:?Repository or environment secret ACR_USERNAME is required.}" + : "${ACR_PASSWORD:?Repository or environment secret ACR_PASSWORD is required.}" + : "${COSIGN_PRIVATE_KEY:?Repository or environment secret SIGNING_SECRET is required.}" + : "${COSIGN_PASSWORD:?Repository or environment secret COSIGN_PASSWORD is required.}" - name: Login to GitHub Container Registry env: @@ -140,6 +151,7 @@ jobs: ACR_NAMESPACE: ${{ vars.ACR_NAMESPACE }} COSIGN_EXPERIMENTAL: false COSIGN_PRIVATE_KEY: ${{ secrets.SIGNING_SECRET }} + COSIGN_PASSWORD: ${{ secrets.COSIGN_PASSWORD }} DIGEST: ${{ steps.promote.outputs.digest }} run: | set -euo pipefail -- cgit v1.2.3 From e77483dcb6570a003001ea1c81a962c9f45fda74 Mon Sep 17 00:00:00 2001 From: tranch Date: Wed, 15 Jul 2026 18:50:32 +0800 Subject: Remove ACR environment input Keep ACR promotion compatible with repositories that do not configure GitHub Environments by using repository-level variables and secrets. --- .github/workflows/promote-acr.yml | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) (limited to '.github/workflows/promote-acr.yml') diff --git a/.github/workflows/promote-acr.yml b/.github/workflows/promote-acr.yml index 890962f..6b00e3b 100644 --- a/.github/workflows/promote-acr.yml +++ b/.github/workflows/promote-acr.yml @@ -4,11 +4,6 @@ name: Promote image to ACR on: workflow_dispatch: inputs: - environment: - description: "GitHub Environment containing ACR vars/secrets" - required: true - default: testing - type: environment source_tag: description: "Existing GHCR tag to promote" required: true @@ -26,14 +21,13 @@ env: IMAGE_REGISTRY: "ghcr.io/${{ github.repository_owner }}" concurrency: - group: ${{ github.workflow }}-${{ inputs.environment }}-${{ inputs.image_name || 'default' }}-${{ inputs.source_tag }} + group: ${{ github.workflow }}-${{ inputs.image_name || 'default' }}-${{ inputs.source_tag }} cancel-in-progress: false jobs: promote_acr: name: Promote GHCR image to ACR runs-on: ubuntu-24.04 - environment: ${{ inputs.environment }} permissions: contents: read @@ -81,12 +75,12 @@ jobs: run: | set -euo pipefail - : "${ACR_REGISTRY:?Repository or environment variable ACR_REGISTRY is required.}" - : "${ACR_NAMESPACE:?Repository or environment variable ACR_NAMESPACE is required.}" - : "${ACR_USERNAME:?Repository or environment secret ACR_USERNAME is required.}" - : "${ACR_PASSWORD:?Repository or environment secret ACR_PASSWORD is required.}" - : "${COSIGN_PRIVATE_KEY:?Repository or environment secret SIGNING_SECRET is required.}" - : "${COSIGN_PASSWORD:?Repository or environment secret COSIGN_PASSWORD is required.}" + : "${ACR_REGISTRY:?Repository variable ACR_REGISTRY is required.}" + : "${ACR_NAMESPACE:?Repository variable ACR_NAMESPACE is required.}" + : "${ACR_USERNAME:?Repository secret ACR_USERNAME is required.}" + : "${ACR_PASSWORD:?Repository secret ACR_PASSWORD is required.}" + : "${COSIGN_PRIVATE_KEY:?Repository secret SIGNING_SECRET is required.}" + : "${COSIGN_PASSWORD:?Repository secret COSIGN_PASSWORD is required.}" - name: Login to GitHub Container Registry env: -- cgit v1.2.3 From 10411b5e32b78f57b6ad995c434cfc4fc390d689 Mon Sep 17 00:00:00 2001 From: tranch Date: Wed, 26 Aug 2026 10:59:21 +0800 Subject: fix(config): make local dotenv optional Load a private .env when present, but keep CI configuration in GitHub runtime variables and retain the tracked template as a guide. --- .github/workflows/promote-acr.yml | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) (limited to '.github/workflows/promote-acr.yml') diff --git a/.github/workflows/promote-acr.yml b/.github/workflows/promote-acr.yml index 6b00e3b..ceb5c0f 100644 --- a/.github/workflows/promote-acr.yml +++ b/.github/workflows/promote-acr.yml @@ -13,12 +13,13 @@ on: required: false type: string image_name: - description: "Image name override; defaults to IMAGE_NAME var, then image-template.env" + description: "Image name override; defaults to the runtime IMAGE_NAME variable" required: false type: string env: IMAGE_REGISTRY: "ghcr.io/${{ github.repository_owner }}" + IMAGE_NAME: ${{ vars.IMAGE_NAME || github.event.repository.name }} concurrency: group: ${{ github.workflow }}-${{ inputs.image_name || 'default' }}-${{ inputs.source_tag }} @@ -37,9 +38,6 @@ jobs: - name: Checkout uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7 - - name: Install just - uses: extractions/setup-just@53165ef7e734c5c07cb06b3c8e7b647c5aa16db3 #v4.0.0 - - name: Install skopeo run: | sudo apt-get update @@ -54,7 +52,7 @@ jobs: run: | set -euo pipefail - image_name="${IMAGE_NAME_INPUT:-${IMAGE_NAME_VAR:-$(just image_name)}}" + image_name="${IMAGE_NAME_INPUT:-${IMAGE_NAME_VAR:-${IMAGE_NAME}}}" target_tags="${TARGET_TAGS_INPUT:-$SOURCE_TAG}" { -- cgit v1.2.3