From 2527e1fbbb3cec0361337991a8a7b7661c486b5a 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:32:05 -0500 Subject: [PATCH] fix(workflows): remove literal '${{ }}' from run-block comments (the actual bug) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Root cause for the workflow_dispatch parse failures that defeated PRs #24, #25, #26, and #27: GitHub Actions substitutes '\${{ ... }}' expressions inside run: blocks *before* the shell sees them, including sequences inside shell comments. Three comments in this file contained the literal string '\${{ }}' (empty expression). GHA's queue-time parser tried to evaluate an empty expression and bailed with 'An expression was expected', always pointing at 'run: |' (col 14) because that's the parent scope. Diagnostic that confirmed this: the reported line numbers shifted between my earlier attempts (55/111 -> 53/126 after the jq refactor). The error position is file-structure-dependent; col 14 is always the '|' of 'run: |'. Meanwhile the literal '\${{ }}' was identical across every version of the file since the security-hardening cascade (mcp-clipboard#87), which is why every '|| X' fallback experiment missed — the fallbacks were on the wrong expressions. Fix: rewrite the three comments to describe the concept without the literal '\${{ }}' characters. Zero logic change. Why this only surfaced on workflow_dispatch (not on normal workflow_run firings): the normal workflow_run code path apparently tolerates empty expressions where the queue-time parser for workflow_dispatch does not. mcp-clipboard has the same latent bug — it just never tripped it because nobody dispatched manually. Cascade: the same fix needs to land on cmeans/mcp-clipboard's file to preserve verbatim parity and to unblock manual dispatch there as well. Co-Authored-By: Claude Opus 4.7 (1M context) --- .github/workflows/pr-labels-ci.yml | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/.github/workflows/pr-labels-ci.yml b/.github/workflows/pr-labels-ci.yml index 3dc5db0..feedfbf 100644 --- a/.github/workflows/pr-labels-ci.yml +++ b/.github/workflows/pr-labels-ci.yml @@ -53,9 +53,12 @@ jobs: 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. + # Going through jq -r avoids the GitHub Actions expression surface + # entirely (which previously broke queue-time parsing on + # workflow_dispatch / push validation because GHA substitutes + # dollar-brace-brace sequences in run blocks *before* the shell + # sees them — including sequences inside shell comments). + # 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. @@ -79,7 +82,7 @@ jobs: # Fallback: pull_requests array is empty for dependabot PRs. # Search by head branch instead. HEAD_BRANCH came from jq (above), - # not direct ${{ }} interpolation — fork PR branch names are + # not a direct GHA expression — 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 \ @@ -138,7 +141,7 @@ jobs: API_OUT=$(gh api "repos/$REPO/actions/runs/$RUN_ID/pull_requests" \ --jq '.[0].number // empty' 2>&1) && PR="$API_OUT" || true - # HEAD_BRANCH came from jq (above), not direct ${{ }} interpolation — + # HEAD_BRANCH came from jq (above), not a direct GHA expression — # fork PR branch names are contributor-controlled. if [ -z "$PR" ]; then PR=$(gh pr list --repo "$REPO" --head "$HEAD_BRANCH" --state open \