--- 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 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 }} 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 skopeo run: | sudo apt-get update sudo apt-get install -y skopeo - 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:-${IMAGE_NAME_VAR:-${IMAGE_NAME}}}" target_tags="${TARGET_TAGS_INPUT:-$SOURCE_TAG}" { 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: 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 }} 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.}" : "${COSIGN_PASSWORD:?Repository secret COSIGN_PASSWORD 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 }} COSIGN_PASSWORD: ${{ secrets.COSIGN_PASSWORD }} DIGEST: ${{ steps.promote.outputs.digest }} run: | set -euo pipefail cosign sign -y --key env://COSIGN_PRIVATE_KEY "${ACR_REGISTRY}/${ACR_NAMESPACE}/${IMAGE_NAME}@${DIGEST}"