From 0b0e8d89c4332bdda7efbdf8ab1308246ded3321 Mon Sep 17 00:00:00 2001 From: "cmeans-claude-dev[bot]" <3223881+cmeans-claude-dev[bot]@users.noreply.github.com> Date: Mon, 20 Apr 2026 13:13:44 -0500 Subject: [PATCH] fix(workflows): extract workflow_run fields via jq from $GITHUB_EVENT_PATH MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PR #26's 'none' fallback also didn't fix the queue-time parse error on workflow_dispatch. Confirmed via diagnostic: pr-labels-ci.yml's API metadata still reports name as the file path, not 'PR Label Automation (CI)' — meaning GitHub's workflow catalogue still considers it unregistered. Taking QA's suggested approach: eliminate the ${{ }} expression surface on workflow_run fields entirely. Read them from the event payload on disk via jq instead. Changes per job: - Remove RUN_ID and HEAD_BRANCH from step-level env: (the only remaining ${{ github.event.workflow_run.* }} sites outside the job-level if: guards). - Add at the top of run:: RUN_ID=$(jq -r '.workflow_run.id // empty' "$GITHUB_EVENT_PATH") HEAD_BRANCH=$(jq -r '.workflow_run.head_branch // empty' "$GITHUB_EVENT_PATH") - Add defensive early-exit if RUN_ID is empty — belt-and-suspenders for the existing if: guard in case someone loosens it later. Shell-injection safety: jq -r writes raw bytes into a shell variable via command substitution, and every subsequent use is double-quoted. Same guarantee as the prior env: pattern. jq and $GITHUB_EVENT_PATH are both available on all GitHub-hosted runners (ubuntu-latest / macos-latest) — documented, not a hack. Unchanged: - Job-level if: guards still reference github.event.workflow_run.*. These are identical to mcp-clipboard's working version and should short-circuit cleanly on non-workflow_run events via &&. If this PR still hits the same queue-time parse error, the root cause is in the if: surface rather than the env: surface, which points toward reverting the whole seeding-attempt chain (#24, #25, #26, this PR). Co-Authored-By: Claude Opus 4.7 (1M context) --- .github/workflows/pr-labels-ci.yml | 43 +++++++++++++++++++++++------- 1 file changed, 34 insertions(+), 9 deletions(-) diff --git a/.github/workflows/pr-labels-ci.yml b/.github/workflows/pr-labels-ci.yml index ccdb9a8..3dc5db0 100644 --- a/.github/workflows/pr-labels-ci.yml +++ b/.github/workflows/pr-labels-ci.yml @@ -50,9 +50,26 @@ jobs: env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} REPO: ${{ github.repository }} - RUN_ID: ${{ github.event.workflow_run.id || 'none' }} - HEAD_BRANCH: ${{ github.event.workflow_run.head_branch || 'none' }} run: | + # Read workflow_run fields directly from the event payload on disk. + # $GITHUB_EVENT_PATH is always set on GitHub-hosted runners. + # Going through jq -r avoids the ${{ }} expression surface entirely + # (which previously broke queue-time parsing on workflow_dispatch / + # push validation). jq is pre-installed on ubuntu-latest. + # Shell-injection safety: jq -r writes raw bytes into a shell + # variable via command substitution, and every subsequent use is + # double-quoted — same guarantee as the prior env: pattern. + RUN_ID=$(jq -r '.workflow_run.id // empty' "$GITHUB_EVENT_PATH") + HEAD_BRANCH=$(jq -r '.workflow_run.head_branch // empty' "$GITHUB_EVENT_PATH") + + # Defensive: the job-level if: already filters to workflow_run + # events with a usable payload, but if somehow we land here with + # no RUN_ID (e.g. someone loosens the if: later), early-exit. + if [ -z "$RUN_ID" ]; then + echo "No workflow_run payload — exiting" + exit 0 + fi + # Look up the associated PR. # The API can 404 after a force-push (orphaned run). # Capture output only on success so 404 body doesn't leak into $PR. @@ -61,9 +78,9 @@ jobs: --jq '.[0].number // empty' 2>&1) && PR="$API_OUT" || true # Fallback: pull_requests array is empty for dependabot PRs. - # Search by head branch instead. HEAD_BRANCH comes via env - # (not direct ${{ }} interpolation) because fork PR branch names - # are contributor-controlled and allow shell metacharacters. + # Search by head branch instead. HEAD_BRANCH came from jq (above), + # not direct ${{ }} interpolation — fork PR branch names are + # contributor-controlled and allow shell metacharacters. if [ -z "$PR" ]; then PR=$(gh pr list --repo "$REPO" --head "$HEAD_BRANCH" --state open \ --json number --jq '.[0].number // empty' 2>/dev/null) || true @@ -106,15 +123,23 @@ jobs: env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} REPO: ${{ github.repository }} - RUN_ID: ${{ github.event.workflow_run.id || 'none' }} - HEAD_BRANCH: ${{ github.event.workflow_run.head_branch || 'none' }} run: | + # See on-ci-pass for the rationale behind reading workflow_run + # fields from $GITHUB_EVENT_PATH via jq rather than env: expressions. + RUN_ID=$(jq -r '.workflow_run.id // empty' "$GITHUB_EVENT_PATH") + HEAD_BRANCH=$(jq -r '.workflow_run.head_branch // empty' "$GITHUB_EVENT_PATH") + + if [ -z "$RUN_ID" ]; then + echo "No workflow_run payload — exiting" + exit 0 + fi + PR="" API_OUT=$(gh api "repos/$REPO/actions/runs/$RUN_ID/pull_requests" \ --jq '.[0].number // empty' 2>&1) && PR="$API_OUT" || true - # HEAD_BRANCH comes via env (not direct ${{ }} interpolation) - # because fork PR branch names are contributor-controlled. + # HEAD_BRANCH came from jq (above), not direct ${{ }} interpolation — + # fork PR branch names are contributor-controlled. if [ -z "$PR" ]; then PR=$(gh pr list --repo "$REPO" --head "$HEAD_BRANCH" --state open \ --json number --jq '.[0].number // empty' 2>/dev/null) || true