metadata-action icon indicating copy to clipboard operation
metadata-action copied to clipboard

when using labels across multiple tags, the label is only applied to 1 of the tags (at random)

Open nickboldt opened this issue 5 months ago • 2 comments

Contributing guidelines

I've found a bug, and:

  • [x] The documentation does not mention anything about my problem
  • [x] There are no open or closed issues that are related to my problem

Description

Using this code:

        - name: Set container metadata (for releases, 183d expiry)
          id: meta-release
          if: ${{ env.REF_NAME_SHORT != 'next' }}
          uses: docker/metadata-action@v5
          with:
            images: ${{ env.REGISTRY }}/${{ env.REGISTRY_IMAGE }}
            tags: |
              type=raw,value=${{ env.REF_NAME }}
              type=raw,value=${{ env.REF_NAME }}-${{ env.SHORT_SHA }}
              type=raw,value=${{ env.REF_NAME_SHORT }}
              type=raw,value=${{ env.REF_NAME_SHORT }}-${{ env.SHORT_SHA }}
            labels: |
              quay.expires-after=183d

in this workflow:

https://github.com/redhat-developer/rhdh/blob/main/.github/workflows/next-build-image.yaml#L194-L206

when pushing a tag to the repo results in 4 correctly created tags, but ONLY ONE of them has the quay.expires-after=183d label applied; the other 3 have no tag expiration set.

You can see the created tags here https://quay.io/repository/rhdh-community/rhdh?tab=tags

Image

Related downstream issue (in case this is a configuration problem or I've done something wrong): https://issues.redhat.com/browse/RHIDP-8270

Expected behaviour

all tags in a given push should get the same labels applied, not just whichever one is found first in the hashmap/array

Actual behaviour

only 1 of 4 tags has the correct expiration date set

Repository URL

https://github.com/redhat-developer/rhdh/actions/workflows/next-build-image.yaml

Workflow run URL

https://github.com/redhat-developer/rhdh/actions/runs/16476194526/job/46583106978

YAML workflow

name: Build Next and Tag Image

on:
  # workflow_dispatch so that it can be triggered manually if needed
  workflow_dispatch:
  schedule:
    # run at 3:18 UTC every day
    - cron: "18 3 * * *"

  # in addition to building multi-arch :next images daily,
  # also build multi-arch images for any x.y or x.y.z tag pushed to the repo
  push:
    tags:
      - '[0-9]+.[0-9]+.[0-9]+'
      - '[0-9]+.[0-9]+'

concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true

env:
  REGISTRY: quay.io
  REGISTRY_IMAGE: rhdh-community/rhdh

jobs:
  build-image:
    name: Build Image
    strategy:
      fail-fast: false
      matrix:
        os:
          - ubuntu-24.04-arm
          - ubuntu-24.04
    runs-on: ${{ matrix.os }}
    timeout-minutes: 720 # Set to 12 hours instead of default 360 = 6hrs
    permissions:
      contents: read
      packages: write

    steps:
      - name: Checkout
        uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: Prepare
        run: |
          if [ "${{ matrix.os }}" == "ubuntu-24.04" ]; then
            platform="linux/amd64"
          elif [ "${{ matrix.os }}" == "ubuntu-24.04-arm" ]; then
            platform="linux/arm64"
          else
            echo "Unknown platform"
            exit 1
          fi

          ref_name=${{ github.ref_name }}
          if [ "$ref_name" == "main" ]; then
            ref_name="next"
          fi
          # from 1.6.1 => 1.6
          ref_name_short="${ref_name%.*}"
          if [[ $ref_name_short == "1" ]]; then
            ref_name_short="${ref_name}"
          fi
          echo "REF_NAME=$ref_name" >> $GITHUB_ENV
          echo "REF_NAME_SHORT=${ref_name_short}" >> $GITHUB_ENV
          echo "PLATFORM=$platform" >> $GITHUB_ENV
          echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV
          echo "PLATFORM_ARCH=${platform#*/}" >> $GITHUB_ENV

      - name: Get the last commit short SHA
        uses: ./.github/actions/get-sha

      - name: Build and Push with Buildx (for :next builds, 14d expiry)
        uses: ./.github/actions/docker-build
        id: build-next
        if: ${{ env.REF_NAME_SHORT == 'next' }}
        with:
          registry: ${{ env.REGISTRY }}
          username: ${{ secrets.QUAY_USERNAME }}
          password: ${{ secrets.QUAY_TOKEN }}
          imageName: ${{ env.REGISTRY_IMAGE }}
          imageTags: |
            type=raw,value=${{ env.REF_NAME }}-${{ env.PLATFORM_ARCH }}
            type=raw,value=${{ env.REF_NAME }}-${{ env.SHORT_SHA }}-${{ env.PLATFORM_ARCH }}
          imageLabels: quay.expires-after=14d
          push: true
          platform: ${{ env.PLATFORM }}

      - name: Build and Push with Buildx (for releases, 183d expiry)
        uses: ./.github/actions/docker-build
        id: build-release
        if: ${{ env.REF_NAME_SHORT != 'next' }}
        with:
          registry: ${{ env.REGISTRY }}
          username: ${{ secrets.QUAY_USERNAME }}
          password: ${{ secrets.QUAY_TOKEN }}
          imageName: ${{ env.REGISTRY_IMAGE }}
          imageTags: |
            type=raw,value=${{ env.REF_NAME }}-${{ env.PLATFORM_ARCH }}
            type=raw,value=${{ env.REF_NAME }}-${{ env.SHORT_SHA }}-${{ env.PLATFORM_ARCH }}
          imageLabels: quay.expires-after=183d
          push: true
          platform: ${{ env.PLATFORM }}

      - name: Export digest (for :next builds)
        id: export-digest-next
        if: ${{ env.REF_NAME_SHORT == 'next' }}
        run: |
          mkdir -p /tmp/digests
          digest="${{ steps.build-next.outputs.digest }}"
          touch "/tmp/digests/${digest#sha256:}"

      - name: Export digest (for releases)
        id: export-digest-release
        if: ${{ env.REF_NAME_SHORT != 'next' }}
        run: |
          mkdir -p /tmp/digests
          digest="${{ steps.build-release.outputs.digest }}"
          touch "/tmp/digests/${digest#sha256:}"

      - name: Upload digest
        uses: actions/upload-artifact@v4
        with:
          name: digests-${{ env.PLATFORM_PAIR }}
          path: /tmp/digests/*
          if-no-files-found: error
          retention-days: 1

  merge:
      runs-on: ubuntu-latest
      needs:
        - build-image
      steps:
        - name: Prepare
          run: |
            ref_name=${{ github.ref_name }}
            if [ "$ref_name" == "main" ]; then
              ref_name="next"
            fi
            # from 1.6.1 => 1.6
            ref_name_short="${ref_name%.*}"
            if [[ $ref_name_short == "1" ]]; then
              ref_name_short="${ref_name}"
            fi
            echo "REF_NAME=$ref_name" >> $GITHUB_ENV
            echo "REF_NAME_SHORT=${ref_name_short}" >> $GITHUB_ENV

        - name: Checkout
          uses: actions/checkout@v4
          with:
            fetch-depth: 0

        - name: Download digests
          uses: actions/download-artifact@v4
          with:
            path: /tmp/digests
            pattern: digests-*
            merge-multiple: true

        - name: Get the last commit short SHA
          uses: ./.github/actions/get-sha

        - name: Set up Docker Buildx
          uses: docker/setup-buildx-action@v3

        - name: Set container metadata (for :next builds, 14d expiry)
          id: meta-next
          if: ${{ env.REF_NAME_SHORT == 'next'}}
          uses: docker/metadata-action@v5
          with:
            images: ${{ env.REGISTRY }}/${{ env.REGISTRY_IMAGE }}
            tags: |
              type=raw,value=${{ env.REF_NAME }}
              type=raw,value=${{ env.REF_NAME }}-${{ env.SHORT_SHA }}
            labels: |
              quay.expires-after=14d

        - name: Set container metadata (for releases, 183d expiry)
          id: meta-release
          if: ${{ env.REF_NAME_SHORT != 'next' }}
          uses: docker/metadata-action@v5
          with:
            images: ${{ env.REGISTRY }}/${{ env.REGISTRY_IMAGE }}
            tags: |
              type=raw,value=${{ env.REF_NAME }}
              type=raw,value=${{ env.REF_NAME }}-${{ env.SHORT_SHA }}
              type=raw,value=${{ env.REF_NAME_SHORT }}
              type=raw,value=${{ env.REF_NAME_SHORT }}-${{ env.SHORT_SHA }}
            labels: |
              quay.expires-after=183d

        - name: Login to Docker Hub
          uses: docker/login-action@v3
          with:
            registry: ${{ env.REGISTRY }}
            username: ${{ secrets.QUAY_USERNAME }}
            password: ${{ secrets.QUAY_TOKEN }}

        - name: Create manifest list and push
          working-directory: /tmp/digests
          run: |
            docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
              $(printf '${{ env.REGISTRY }}/${{ env.REGISTRY_IMAGE }}@sha256:%s ' *)

        - name: Inspect image (for :next builds)
          id: inspect-next
          if: ${{ env.REF_NAME_SHORT == 'next'}}
          run: |
            docker buildx imagetools inspect ${{ env.REGISTRY }}/${{ env.REGISTRY_IMAGE }}:${{ steps.meta-next.outputs.version }}

        - name: Inspect image (for releases)
          id: inspect-release
          if: ${{ env.REF_NAME_SHORT != 'next'}}
          run: |
            docker buildx imagetools inspect ${{ env.REGISTRY }}/${{ env.REGISTRY_IMAGE }}:${{ steps.meta-release.outputs.version }}

Workflow logs

Problem occurs in the merge step but I've attached all three logs in case you want them

2_merge.txt 1_Build Image (ubuntu-24.04-arm).txt 0_Build Image (ubuntu-24.04).txt

BuildKit logs


Additional info

No response

nickboldt avatar Jul 23 '25 18:07 nickboldt

FWIW the reason I think this the label is only being applied to the first item in the map is that earlier today when I was playing with 14d expiration dates, I had a different tag get the expiration:

Image

I mean, it could be the last item in the map, but it seems clear that the order of items being pushed to quay varies, despite the order of them listed in my GH action workflow yaml.

nickboldt avatar Jul 23 '25 18:07 nickboldt

Doesn't seem related to this action but this flow https://docs.docker.com/build/ci/github-actions/multi-platform/#distribute-build-across-multiple-runners where metadata are merged when creating the manifest list in merge job. In this flow we could probably use the --annotation flag of buildx imagetools create. Although only annotations are supported not labels. Does quay support annotations?

Also similar to https://github.com/docker/build-push-action/discussions/1022

crazy-max avatar Aug 11 '25 08:08 crazy-max