This is an automated email from the ASF dual-hosted git repository.

github-merge-queue[bot] pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/texera.git


The following commit(s) were added to refs/heads/main by this push:
     new dda9e830f5 ci: discover staged Codecov flags instead of a hardcoded 
matrix (#6824)
dda9e830f5 is described below

commit dda9e830f54e0fa74dcf0fd5d41a3c2e1a0c989c
Author: Xinyuan Lin <[email protected]>
AuthorDate: Thu Jul 23 04:58:13 2026 -0700

    ci: discover staged Codecov flags instead of a hardcoded matrix (#6824)
    
    ### What changes were proposed in this PR?
    
    Follow-up to #6730. The deferred `Codecov Upload` workflow hardcoded all
    11 flags in its matrix and tried to download each from the source run.
    On a label-gated PR — e.g. a frontend-only PR that only stages
    `codecov-frontend` — the other 10 legs hit `##[error] Artifact not found
    for name: codecov-<flag>`, which looks like a name mismatch; and because
    the download used `continue-on-error: true`, a genuinely failed download
    was swallowed, the leg went green, and `notify-failure` never fired.
    
    **Fix:** add a `discover` job that lists the source run's *actual*
    `codecov-*` artifacts and drives the upload matrix from that
    (`fromJSON`), and drop `continue-on-error` on the download.
    
    - **No more spurious errors** — only the flags the source run actually
    built are processed; a frontend-only PR runs one clean leg.
    - **Failures surface** — a listed artifact that then fails to download
    or upload fails the leg, so `notify-failure` fires (previously it went
    silently green).
    - Also removes the hardcoded flag list, so adding a `platform` service
    no longer needs a matching matrix row here.
    
    Observed on the first live run after #6730 merged ([run
    29984668372](https://github.com/apache/texera/actions/runs/29984668372),
    triggered by a frontend-only PR's checks): `frontend` uploaded fine, but
    10 legs logged `Artifact not found` for the label-gated flags and the
    run still went green.
    
    ### Any related issues, documentation, discussions?
    
    Follow-up to #6730 (reported on #6685).
    
    ### How was this PR tested?
    
    - `workflow_run` only activates from the copy of the file on the default
    branch, so this can't run on its own PR. Validated by YAML lint and an
    adversarial review of the discover → upload → notify flow: empty-flags
    cleanly skips (no failure, no notify), the dynamic `fromJSON` matrix
    handles one or many flags, a download/upload failure now fails the leg
    and fires `notify-failure`, `amber-integration` (results-only) skips the
    coverage step via `hashFiles`, and the `workflow_dispatch` path resolves
    the run id.
    - The `workflow_dispatch` (`run_id`) entry remains for manual post-merge
    validation against a finished *Required Checks* run.
    
    ### Was this PR authored or co-authored using generative AI tooling?
    
    Yes.
    
    Generated-by: Claude Code (Opus 4.8 [1M context])
---
 .github/workflows/build.yml          |   5 +-
 .github/workflows/codecov-upload.yml | 148 ++++++++++++++++++++---------------
 2 files changed, 85 insertions(+), 68 deletions(-)

diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 34eeff6ae3..5084638c0e 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -787,9 +787,8 @@ jobs:
           CC_BRANCH: ${{ github.event.pull_request.head.ref || github.ref_name 
}}
         run: bash .github/scripts/stage-codecov.sh
       - name: Upload ${{ matrix.service }} Codecov artifact
-        # Adding a service to the matrix above also needs a matching
-        # { flag: <service>, coverage: true } row in codecov-upload.yml, or 
this
-        # artifact is downloaded by nothing and the service's coverage is 
dropped.
+        # codecov-upload.yml discovers codecov-* artifacts dynamically, so a 
new
+        # platform service here is picked up automatically — no matrix row to 
sync.
         if: inputs.backport_target_branch == '' && !cancelled()
         uses: actions/upload-artifact@v4
         with:
diff --git a/.github/workflows/codecov-upload.yml 
b/.github/workflows/codecov-upload.yml
index b43e22906e..fbe442cd38 100644
--- a/.github/workflows/codecov-upload.yml
+++ b/.github/workflows/codecov-upload.yml
@@ -27,17 +27,20 @@
 #
 # Each build job stages its reports as a `codecov-<flag>` artifact with a 
uniform
 # layout — coverage/ (coverage reports) and results/ (JUnit XMLs) plus 
pr-number /
-# commit-sha / branch identifier files. The matrix below re-uploads each flag 
with
-# `directory:` scoping so Codecov's search is confined to that flag's files 
(this is
-# what lets the amber flag's ~8 same-basename module jacoco.xml reports upload 
without
-# a fragile explicit file list). Codecov requires one upload per flag (flags 
in a
-# single upload apply to all its files, and carryforward needs the flag at 
upload
-# time), so the flags cannot be collapsed into fewer calls — the win here is 
the
-# token, not fewer uploads.
+# commit-sha / branch identifier files. The `discover` job lists which 
codecov-*
+# artifacts the source run ACTUALLY produced (a label-gated PR only builds 
some of
+# them) and drives the upload matrix from that, so we never chase a flag that 
wasn't
+# built — that avoids the spurious "artifact not found" errors and lets the 
download
+# fail loudly for a flag that should be there. Each flag re-uploads with 
`directory:`
+# scoping so Codecov's search is confined to that flag's files (this is what 
lets the
+# amber flag's ~8 same-basename module jacoco.xml reports upload without a 
fragile
+# explicit file list). Codecov requires one upload per flag (flags in a single 
upload
+# apply to all its files, and carryforward needs the flag at upload time), so 
the
+# flags cannot be collapsed into fewer calls — the win is the token, not fewer 
uploads.
 #
 # NOTE: `workflow_run` only fires from the copy of this file on the default 
branch, so
-# this has NO effect until merged to main. After merge, exercise it manually 
via the
-# workflow_dispatch entry below, passing a finished "Required Checks" run id.
+# a change here has NO effect until merged to main. After merge, exercise it 
manually
+# via the workflow_dispatch entry below, passing a finished "Required Checks" 
run id.
 
 name: Codecov Upload
 
@@ -55,66 +58,80 @@ on:
 
 permissions:
   contents: read
-  actions: read # download-artifact needs this to read artifacts from another 
run
+  actions: read # list + download artifacts from the source run
 
 jobs:
-  upload:
-    # Skip only a cancelled/skipped upstream; still run on failure so partial
-    # coverage is reported (mirrors the build's always()/!cancelled() staging).
+  discover:
+    # Which codecov-<flag> artifacts did the source run actually build? A 
label-gated
+    # PR only stages some of them, so we list what exists and upload exactly 
those —
+    # rather than a hardcoded flag set that throws "artifact not found" for 
the rest.
+    # Skip only a cancelled/skipped upstream; still run on failure so partial 
coverage
+    # is reported (mirrors the build's always()/!cancelled() staging).
     if: >-
       github.event_name == 'workflow_dispatch' ||
       (github.event.workflow_run.conclusion != 'cancelled' &&
        github.event.workflow_run.conclusion != 'skipped')
     runs-on: ubuntu-latest
-    # Job-level so the group can key on matrix.flag: one upload pass per source
-    # run + flag, and a re-run of the source supersedes the previous attempt.
+    outputs:
+      flags: ${{ steps.list.outputs.flags }}
+    steps:
+      - name: List codecov-* artifacts in the source run
+        id: list
+        uses: actions/github-script@v9
+        env:
+          # Passed via env (not interpolated into the script body) so a
+          # workflow_dispatch-supplied run_id can't inject into the JS.
+          SOURCE_RUN_ID: ${{ github.event.workflow_run.id || inputs.run_id }}
+        with:
+          script: |
+            const runId = Number(process.env.SOURCE_RUN_ID);
+            if (!Number.isInteger(runId) || runId <= 0) {
+              core.setFailed(`Invalid source run id: 
'${process.env.SOURCE_RUN_ID}' (expected a positive integer)`);
+              return;
+            }
+            const arts = await 
github.paginate(github.rest.actions.listWorkflowRunArtifacts, {
+              owner: context.repo.owner, repo: context.repo.repo, run_id: 
runId, per_page: 100,
+            });
+            const flags = [...new Set(
+              arts.map(a => a.name)
+                  .filter(n => n.startsWith('codecov-'))
+                  .map(n => n.slice('codecov-'.length))
+            )].sort();
+            core.info(`Source run ${runId} staged codecov flags: 
${flags.join(', ') || '(none)'}`);
+            core.setOutput('flags', JSON.stringify(flags));
+
+  upload:
+    needs: discover
+    # Nothing to do when the source produced no codecov-* artifacts (also 
avoids an
+    # empty matrix, which is an error).
+    if: ${{ needs.discover.outputs.flags != '' && needs.discover.outputs.flags 
!= '[]' }}
+    runs-on: ubuntu-latest
+    # Job-level so the group can key on matrix.flag: one upload pass per 
source run +
+    # flag, and a re-run of the source supersedes the previous attempt.
     concurrency:
       group: codecov-upload-${{ github.event.workflow_run.id || inputs.run_id 
}}-${{ matrix.flag }}
       cancel-in-progress: false
     strategy:
       fail-fast: false
       matrix:
-        # flag = artifact suffix (codecov-<flag>); coverage=false for the
-        # test-results-only flag (amber-integration has no coverage report).
-        #
-        # KEEP IN SYNC with the coverage-producing jobs in build.yml: one row 
per
-        # codecov-<flag> artifact staged there — frontend, amber, 
amber-integration,
-        # pyamber, agent-service, and every service in build.yml's `platform` 
matrix.
-        # A flag staged in build.yml but missing here is downloaded by nothing 
and
-        # its coverage is silently dropped (the reverse — a row here with no 
artifact
-        # — is safe: the download continue-on-errors and the leg skips).
-        include:
-          - { flag: frontend, coverage: true }
-          - { flag: amber, coverage: true }
-          - { flag: amber-integration, coverage: false }
-          - { flag: pyamber, coverage: true }
-          - { flag: agent-service, coverage: true }
-          - { flag: config-service, coverage: true }
-          - { flag: access-control-service, coverage: true }
-          - { flag: file-service, coverage: true }
-          - { flag: computing-unit-managing-service, coverage: true }
-          - { flag: workflow-compiling-service, coverage: true }
-          - { flag: notebook-migration-service, coverage: true }
+        # `|| '[]'` guards matrix evaluation if the output is ever an empty 
string
+        # (discover skipped/failed); the job `if` above still skips the empty 
case.
+        flag: ${{ fromJSON(needs.discover.outputs.flags || '[]') }}
     steps:
-      - name: Resolve source run id
-        id: src
-        shell: bash
-        run: echo "run_id=${{ github.event.workflow_run.id || inputs.run_id 
}}" >> "$GITHUB_OUTPUT"
-      - name: Download staged coverage
-        id: dl
-        continue-on-error: true # absent when this flag's job was label-gated 
out
-        # Layout: stage-codecov.sh roots the artifact at cc/ (coverage/, 
results/,
-        # and the pr-number/commit-sha/branch txt files), so downloading to 
path: cc
-        # restores cc/coverage/**, cc/results/**, and cc/pr-number.txt — the 
paths
-        # the steps below reference. Keep that rooting if the staging script 
changes.
+      - name: Download staged coverage for ${{ matrix.flag }}
+        # No continue-on-error: discover confirmed this artifact exists, so a 
download
+        # failure here is a real problem that must fail the leg and trip 
notify-failure
+        # (the old design swallowed the failure and went green with no signal).
+        # Layout: stage-codecov.sh roots the artifact at cc/ (coverage/, 
results/, and
+        # the pr-number/commit-sha/branch txt files), so downloading to path: 
cc
+        # restores cc/coverage/**, cc/results/**, and cc/pr-number.txt.
         uses: actions/download-artifact@v4
         with:
           name: codecov-${{ matrix.flag }}
           path: cc
-          run-id: ${{ steps.src.outputs.run_id }}
+          run-id: ${{ github.event.workflow_run.id || inputs.run_id }}
           github-token: ${{ github.token }}
       - name: Read coverage metadata
-        if: steps.dl.outcome == 'success'
         id: meta
         shell: bash
         # Artifact content comes from a (possibly fork) PR build, so sanitize 
each
@@ -124,12 +141,10 @@ jobs:
           echo "sha=$(cat cc/commit-sha.txt 2>/dev/null | tr -cd '0-9a-fA-F')" 
>> "$GITHUB_OUTPUT"
           echo "branch=$(cat cc/branch.txt 2>/dev/null | tr -cd 
'[:alnum:]._/:-')" >> "$GITHUB_OUTPUT"
       - name: Upload ${{ matrix.flag }} coverage to Codecov
-        # fail_ci_if_error: true so a genuine upload failure fails this leg and
-        # trips the notify-failure job below (Codecov posts its own comment 
only on
-        # success, so a failed coverage upload would otherwise leave the PR 
with no
-        # report and no signal). A label-gated-absent flag is not a failure — 
the
-        # download continue-on-errors and this step is skipped via the guards 
above.
-        if: steps.dl.outcome == 'success' && matrix.coverage && 
hashFiles('cc/coverage/**') != ''
+        # Every flag has coverage except the results-only amber-integration; 
hashFiles
+        # skips it there (its artifact has no coverage/ dir). 
fail_ci_if_error: true so
+        # a genuine upload failure fails the leg and trips notify-failure.
+        if: hashFiles('cc/coverage/**') != ''
         uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f 
# v7.0.0
         with:
           token: ${{ secrets.CODECOV_TOKEN }}
@@ -140,10 +155,7 @@ jobs:
           override_branch: ${{ steps.meta.outputs.branch }}
           override_pr: ${{ steps.meta.outputs.pr }}
       - name: Upload ${{ matrix.flag }} test results to Codecov
-        # fail_ci_if_error: true here too (per review) so a test-results 
upload error
-        # also trips notify-failure instead of being silently dropped. Still 
guarded
-        # on hashFiles, so an absent / label-gated flag simply skips.
-        if: steps.dl.outcome == 'success' && hashFiles('cc/results/**') != ''
+        if: hashFiles('cc/results/**') != ''
         uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f 
# v7.0.0
         with:
           token: ${{ secrets.CODECOV_TOKEN }}
@@ -156,11 +168,11 @@ jobs:
           override_pr: ${{ steps.meta.outputs.pr }}
 
   notify-failure:
-    # If any upload above failed (coverage or test-results), leave a comment 
on the
-    # PR so a human can re-dispatch it. Codecov posts its own comment on 
SUCCESS, so
-    # we only cover the failure case (per #6730 review). A delayed / queued 
upload
-    # posts nothing — the absence of a Codecov comment then signals a stuck 
run.
-    needs: upload
+    # If discovery or any upload above failed, comment on the PR so a human can
+    # re-dispatch it. Codecov posts its own comment on SUCCESS, so we only 
cover the
+    # failure case (per #6730 review). A delayed / queued upload posts nothing 
— the
+    # absence of a Codecov comment then signals a stuck run.
+    needs: [discover, upload]
     if: ${{ failure() && github.event_name == 'workflow_run' }}
     runs-on: ubuntu-latest
     permissions:
@@ -169,7 +181,13 @@ jobs:
       pull-requests: write # comment on the PR
     steps:
       - name: Download coverage metadata
-        id: dl
+        # Best-effort (opposite of the upload job's download, which fails 
hard): this
+        # only fetches a codecov-* artifact to read the PR number for the 
comment. If
+        # it can't (e.g. the source run produced no artifacts, so the pattern 
matches
+        # nothing and download-artifact errors), continue-on-error lets the 
job fall
+        # through to the `if: pr != ''` guard and skip commenting, rather than
+        # hard-failing notify-failure itself — which isn't a required check, 
so its
+        # failure would be invisible noise rather than a signal.
         continue-on-error: true
         uses: actions/download-artifact@v4
         with:

Reply via email to