From 2bb5c4ae00f72c5d729eb08f9daaafc55d734892 Mon Sep 17 00:00:00 2001 From: "Alex M. Lowe" Date: Tue, 12 May 2026 15:16:47 +0200 Subject: [PATCH 1/4] fix: include caller job name in coverage artifact names When test-python.yaml is called multiple times in the same workflow run (e.g. test-common, test-java-plugins, test-python-plugins), the coverage artifact names collide because they only include platform and Python version. This causes 409 Conflict errors on upload. Add ${{ github.job }} to all coverage artifact names so each caller gets unique artifact names (e.g. coverage-test-common-slow-ubuntu-24.04-3.12 vs coverage-test-java-plugins-slow-ubuntu-24.04-3.12). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .github/workflows/self-test-qa.yaml | 3 +++ .github/workflows/test-python.yaml | 6 +++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/.github/workflows/self-test-qa.yaml b/.github/workflows/self-test-qa.yaml index 7f3b6160..434165d2 100644 --- a/.github/workflows/self-test-qa.yaml +++ b/.github/workflows/self-test-qa.yaml @@ -32,6 +32,9 @@ jobs: with: # Test on many OS's to ensure that these workflows work everywhere. # Also ensure we can add a list of tags for self-hosted runners. + # Intentionally overlaps with test-python-custom to verify that + # coverage artifact names are unique across callers (github.job + # is included in the artifact name to prevent 409 Conflict errors). fast-test-platforms: '["ubuntu-latest"]' fast-test-python-versions: '["3.14"]' slow-test-platforms: '["ubuntu-latest"]' diff --git a/.github/workflows/test-python.yaml b/.github/workflows/test-python.yaml index 5bc02b99..c1c44387 100644 --- a/.github/workflows/test-python.yaml +++ b/.github/workflows/test-python.yaml @@ -89,7 +89,7 @@ jobs: - name: Upload test coverage uses: actions/upload-artifact@v7 with: - name: coverage-fast-${{ join(matrix.platform, '-') }} + name: coverage-${{ github.job }}-fast-${{ join(matrix.platform, '-') }} overwrite: true path: | ./coverage.xml @@ -117,7 +117,7 @@ jobs: - name: Upload test coverage uses: actions/upload-artifact@v7 with: - name: coverage-slow-${{ join(matrix.platform, '-') }}-${{ matrix.python-version }} + name: coverage-${{ github.job }}-slow-${{ join(matrix.platform, '-') }}-${{ matrix.python-version }} overwrite: true path: | ./coverage.xml @@ -147,7 +147,7 @@ jobs: - name: Upload test coverage uses: actions/upload-artifact@v7 with: - name: coverage-lowest-py${{ inputs.lowest-python-version }} + name: coverage-${{ github.job }}-lowest-py${{ inputs.lowest-python-version }} overwrite: true path: | ./coverage.xml From 37b3252c0ad3b710cf8f1da4bb19ca81ed81646e Mon Sep 17 00:00:00 2001 From: "Alex M. Lowe" Date: Tue, 12 May 2026 16:39:09 +0200 Subject: [PATCH 2/4] test: add job to verify coverage artifacts are unique Adds a verify-coverage-artifacts job that runs after all test jobs and uses the GitHub API to check that: - No duplicate coverage artifact names exist - Each caller job (test-python, test-python-custom, test-python-as-root) produced fast, slow, and lowest coverage artifacts Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .github/workflows/self-test-qa.yaml | 43 ++++++++++++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/.github/workflows/self-test-qa.yaml b/.github/workflows/self-test-qa.yaml index 434165d2..bcaac486 100644 --- a/.github/workflows/self-test-qa.yaml +++ b/.github/workflows/self-test-qa.yaml @@ -21,7 +21,7 @@ jobs: slow-test-platforms: '["ubuntu-latest"]' slow-test-python-versions: '["3.14"]' lowest-python-version: "3.8" - lowest-python-platform: '["jammy", "arm64"]' + lowest-python-platform: '["focal", "amd64"]' use-lxd: true pytest-markers: smoketest and not steamtest setup-vars: SETUP_EXTRA=1 @@ -42,3 +42,44 @@ jobs: lowest-python-version: "3.8" lowest-python-platform: "ubuntu-latest" test-command-prefix: sudo # Test that we can run with sudo. + verify-coverage-artifacts: + name: Verify coverage artifacts + if: ${{ !cancelled() }} + needs: [test-python, test-python-custom, test-python-as-root] + runs-on: ubuntu-latest + steps: + - name: Verify coverage artifacts are unique and present + env: + GH_TOKEN: ${{ github.token }} + run: | + artifacts=$(gh api "repos/${{ github.repository }}/actions/runs/${{ github.run_id }}/artifacts" \ + --paginate --jq '.artifacts[].name') + coverage=$(echo "$artifacts" | grep '^coverage-' | sort) + + echo "=== Coverage artifacts ===" + echo "$coverage" + echo "" + + # Verify no duplicate artifact names + dupes=$(echo "$coverage" | uniq -d) + if [ -n "$dupes" ]; then + echo "::error::Duplicate coverage artifact names:" + echo "$dupes" + exit 1 + fi + + exit_code=0 + + # Verify each caller job produced fast, slow, and lowest artifacts + for job in test-python test-python-custom test-python-as-root; do + for type in fast slow lowest; do + if ! echo "$coverage" | grep -q "^coverage-${job}-${type}-"; then + echo "::error::Missing coverage-${job}-${type}-* artifact" + exit_code=1 + fi + done + done + + total=$(echo "$coverage" | wc -l) + echo "Total: ${total} unique coverage artifacts" + exit $exit_code From f825533e19499560eceb8bbffb5bc536f5524f63 Mon Sep 17 00:00:00 2001 From: "Alex M. Lowe" Date: Tue, 12 May 2026 17:00:48 +0200 Subject: [PATCH 3/4] fix: use job.check_run_id API to resolve caller job name github.job returns the job ID within the reusable workflow (e.g. 'fast', 'slow'), not the caller's job key. Instead, use the recently-shipped job.check_run_id context property to look up the full compound job name via the REST API (e.g. 'test-common / Fast tests') and extract the caller's job key for unique artifact naming. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .github/workflows/test-python.yaml | 36 +++++++++++++++++++++++++++--- 1 file changed, 33 insertions(+), 3 deletions(-) diff --git a/.github/workflows/test-python.yaml b/.github/workflows/test-python.yaml index c1c44387..a1bb3906 100644 --- a/.github/workflows/test-python.yaml +++ b/.github/workflows/test-python.yaml @@ -86,10 +86,20 @@ jobs: echo "::endgroup::" done exit $exit_code + - name: Resolve caller job name + id: ctx + if: ${{ !cancelled() }} + shell: bash + env: + GH_TOKEN: ${{ github.token }} + run: | + name=$(gh api "repos/${GITHUB_REPOSITORY}/actions/jobs/${{ job.check_run_id }}" --jq '.name') + caller="${name%% / *}" + echo "caller-job=${caller}" >> "$GITHUB_OUTPUT" - name: Upload test coverage uses: actions/upload-artifact@v7 with: - name: coverage-${{ github.job }}-fast-${{ join(matrix.platform, '-') }} + name: coverage-${{ steps.ctx.outputs.caller-job }}-fast-${{ join(matrix.platform, '-') }} overwrite: true path: | ./coverage.xml @@ -114,10 +124,20 @@ jobs: MARKERS: ${{ inputs.pytest-markers }} run: | ${{ inputs.test-command-prefix }} make test-coverage PYTEST_ADDOPTS="--no-header -v -rN -m 'slow ${MARKERS:+and ($MARKERS)}'" + - name: Resolve caller job name + id: ctx + if: ${{ !cancelled() }} + shell: bash + env: + GH_TOKEN: ${{ github.token }} + run: | + name=$(gh api "repos/${GITHUB_REPOSITORY}/actions/jobs/${{ job.check_run_id }}" --jq '.name') + caller="${name%% / *}" + echo "caller-job=${caller}" >> "$GITHUB_OUTPUT" - name: Upload test coverage uses: actions/upload-artifact@v7 with: - name: coverage-${{ github.job }}-slow-${{ join(matrix.platform, '-') }}-${{ matrix.python-version }} + name: coverage-${{ steps.ctx.outputs.caller-job }}-slow-${{ join(matrix.platform, '-') }}-${{ matrix.python-version }} overwrite: true path: | ./coverage.xml @@ -144,10 +164,20 @@ jobs: MARKERS: ${{ inputs.pytest-markers }} run: | ${{ inputs.test-command-prefix }} make test-coverage PYTEST_ADDOPTS="-m '${MARKERS}'" + - name: Resolve caller job name + id: ctx + if: ${{ !cancelled() }} + shell: bash + env: + GH_TOKEN: ${{ github.token }} + run: | + name=$(gh api "repos/${GITHUB_REPOSITORY}/actions/jobs/${{ job.check_run_id }}" --jq '.name') + caller="${name%% / *}" + echo "caller-job=${caller}" >> "$GITHUB_OUTPUT" - name: Upload test coverage uses: actions/upload-artifact@v7 with: - name: coverage-${{ github.job }}-lowest-py${{ inputs.lowest-python-version }} + name: coverage-${{ steps.ctx.outputs.caller-job }}-lowest-py${{ inputs.lowest-python-version }} overwrite: true path: | ./coverage.xml From 74880aee8e13a06acb55d27a5fa8927df0d4ef98 Mon Sep 17 00:00:00 2001 From: Alex Lowe Date: Tue, 12 May 2026 17:12:56 +0200 Subject: [PATCH 4/4] Apply suggestions from code review Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> Signed-off-by: Alex Lowe --- .github/workflows/self-test-qa.yaml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/self-test-qa.yaml b/.github/workflows/self-test-qa.yaml index bcaac486..b0ccbf63 100644 --- a/.github/workflows/self-test-qa.yaml +++ b/.github/workflows/self-test-qa.yaml @@ -33,8 +33,9 @@ jobs: # Test on many OS's to ensure that these workflows work everywhere. # Also ensure we can add a list of tags for self-hosted runners. # Intentionally overlaps with test-python-custom to verify that - # coverage artifact names are unique across callers (github.job - # is included in the artifact name to prevent 409 Conflict errors). + # coverage artifact names are unique across callers (the reusable + # workflow includes the caller job identifier in the artifact name + # to prevent 409 Conflict errors). fast-test-platforms: '["ubuntu-latest"]' fast-test-python-versions: '["3.14"]' slow-test-platforms: '["ubuntu-latest"]' @@ -80,6 +81,6 @@ jobs: done done - total=$(echo "$coverage" | wc -l) + total=$(printf %s "$coverage" | grep -c .) echo "Total: ${total} unique coverage artifacts" exit $exit_code