From f8e1287c4fe7084a620a03d96da5cd8696a8e74b Mon Sep 17 00:00:00 2001 From: Matt Miller Date: Sat, 18 Jul 2026 15:01:21 -0700 Subject: [PATCH 1/3] ci: generalize bump-cursor-review-callers into a multi-workflow bump-callers dispatcher One dispatcher now serves every registered reusable workflow, each with its own caller-list Actions variable (cursor-review.yml keeps CURSOR_REVIEW_CALLERS unchanged; pr-size.yml registers against PR_SIZE_CALLERS for when it lands). Push events resolve which registered workflows actually changed via the compare API (falling back to a sweep of all registered workflows), and workflow_dispatch gains a workflows filter plus a read-only dry_run mode. Per-repo failures stay fault-isolated per workflow, and one workflow's bad caller variable cannot abort another's fan-out. Replaces bump-cursor-review-callers.yml. --- .github/workflows/bump-callers.yml | 337 ++++++++++++++++++ .../workflows/bump-cursor-review-callers.yml | 177 --------- 2 files changed, 337 insertions(+), 177 deletions(-) create mode 100644 .github/workflows/bump-callers.yml delete mode 100644 .github/workflows/bump-cursor-review-callers.yml diff --git a/.github/workflows/bump-callers.yml b/.github/workflows/bump-callers.yml new file mode 100644 index 0000000..7fbfa25 --- /dev/null +++ b/.github/workflows/bump-callers.yml @@ -0,0 +1,337 @@ +name: Bump callers + +# When a registered reusable workflow is updated on main, open a SHA-bump PR in +# every repo that pins a caller against it. PRs are opened by Cloud Code Bot so +# they are easy to filter and merge. +# +# ONE dispatcher serves ALL reusable workflows in this repo. Each registered +# workflow keeps its OWN caller list — the set of repos adopting one workflow is +# not the set adopting another. A push that touches several registered workflows +# bumps each one's callers independently (per-workflow branch + PR), and a +# failure in one workflow's fan-out never aborts another's. +# +# The caller lists are NOT hardcoded here. This repo is PUBLIC (workflow file +# and Actions run logs are both publicly viewable), and most callers are +# private, so their names must never appear in this file or its logs. Each +# registered workflow's list lives in its own repo-level Actions variable +# (config, not a credential — a variable, not a secret, since secrets are +# write-only via the API) as a JSON array of {"repo","file","label"} objects. +# Every repo name is `::add-mask::`ed out of the (public) run logs before it is +# ever echoed. +# +# Registered workflows: +# +# workflow file callers variable +# cursor-review.yml CURSOR_REVIEW_CALLERS +# pr-size.yml PR_SIZE_CALLERS +# +# To register a new reusable workflow, touch all three spots (then seed the +# variable): +# 1. add its path to `on.push.paths` below +# 2. add a `|` row to REGISTRY in the bump step +# 3. add the variable to the bump step's `env:` block +# +# Update flow — adding/removing a caller needs NO public commit: +# gh variable set --repo Comfy-Org/github-workflows \ +# --body "$(jq -c . callers.json)" +# Keep the canonical callers.json in a PRIVATE infra/ops repo so variable edits +# have a reviewed source of truth; the org audit log records each edit. (The +# specific home repo is intentionally not named here — this file is public.) + +on: + workflow_dispatch: + inputs: + workflows: + description: >- + Workflow files to bump (space/comma-separated, e.g. + "cursor-review.yml"). Empty = every registered workflow. + required: false + default: '' + type: string + dry_run: + description: Read-only — log what would be bumped, open no branches/PRs. + required: false + default: false + type: boolean + push: + branches: [main] + paths: + - .github/workflows/cursor-review.yml + - .github/workflows/pr-size.yml + +permissions: + contents: read + +jobs: + bump: + runs-on: ubuntu-latest + steps: + - name: Generate Cloud Code Bot token + uses: actions/create-github-app-token@d72941d797fd3113feb6b93fd0dec494b13a2547 # v1.12.0 + id: token + with: + app-id: ${{ vars.APP_ID }} + private-key: ${{ secrets.CLOUD_CODE_BOT_PRIVATE_KEY }} + owner: Comfy-Org + + - name: Bump SHA in caller repos + env: + GH_TOKEN: ${{ steps.token.outputs.token }} + NEW_SHA: ${{ github.sha }} + EVENT_NAME: ${{ github.event_name }} + PUSH_BEFORE: ${{ github.event.before }} + INPUT_WORKFLOWS: ${{ inputs.workflows }} + DRY_RUN: ${{ inputs.dry_run }} + # Per-workflow caller lists — each a JSON array of {"repo","file", + # "label"} objects; see the header comment for the update flow. Kept in + # variables (not the file) so private caller names never land in this + # public repo or its logs. + CURSOR_REVIEW_CALLERS: ${{ vars.CURSOR_REVIEW_CALLERS }} + PR_SIZE_CALLERS: ${{ vars.PR_SIZE_CALLERS }} + run: | + # NOTE: deliberately no `set -e`. Fault isolation is two-level: each + # caller repo is bumped independently in bump_one() (a failure there is + # downgraded to a per-repo warning), and each registered workflow's + # fan-out runs independently in bump_workflow() (one workflow's bad + # caller variable cannot abort another workflow's bumps). We still fail + # the job at the end if anything failed. + set -fuo pipefail # -f: no globbing (dispatch input is word-split below) + SHORT="${NEW_SHA:0:7}" + + # Static registry: workflow file → Actions variable holding its caller + # list. Keep in sync with `on.push.paths` and the step `env:` block + # (see the header comment for the registration steps). + REGISTRY=( + "cursor-review.yml|CURSOR_REVIEW_CALLERS" + "pr-size.yml|PR_SIZE_CALLERS" + ) + + registered_var() { # → its callers variable name + local ROW + for ROW in "${REGISTRY[@]}"; do + if [[ "${ROW%%|*}" == "$1" ]]; then + echo "${ROW##*|}" + return 0 + fi + done + return 1 + } + + # ---- Resolve which registered workflows this run bumps. ----------- + # TARGETS = workflow files to process. STRICT=1 when each target was + # explicitly requested (its file changed in the push, or it was named + # in the dispatch input) — then an empty caller list is an error, so a + # wiped variable can never turn the dispatcher into a silent no-op. + # STRICT=0 only when sweeping ALL registered workflows (dispatch with + # no input, or a push whose changed files can't be determined) — there + # a registered-but-not-yet-seeded workflow is an expected skip. + TARGETS=() + STRICT=0 + + all_registered() { + local ROW + for ROW in "${REGISTRY[@]}"; do + TARGETS+=("${ROW%%|*}") + done + } + + if [[ "$EVENT_NAME" == "push" ]]; then + CHANGED="" + # A well-formed, non-all-zero `before` SHA is comparable; an all-zero + # one (branch creation) or a malformed value is not. + if [[ "$PUSH_BEFORE" =~ ^[0-9a-f]{40}$ ]] && [[ "$PUSH_BEFORE" == *[^0]* ]]; then + CHANGED=$(gh api --paginate "repos/${GITHUB_REPOSITORY}/compare/${PUSH_BEFORE}...${NEW_SHA}" \ + --jq '.files[].filename' 2>/dev/null) || CHANGED="" + fi + if [[ -n "$CHANGED" ]]; then + STRICT=1 + for ROW in "${REGISTRY[@]}"; do + FILE="${ROW%%|*}" + if grep -qxF ".github/workflows/${FILE}" <<<"$CHANGED"; then + TARGETS+=("$FILE") + fi + done + fi + if (( ${#TARGETS[@]} == 0 )); then + # The paths filter fired but we couldn't pin down which registered + # file changed (compare unavailable, or force-push). Sweep them + # all — an already-pinned caller is a no-op skip, so over-bumping + # is at worst harmless churn, while under-bumping silently strands + # callers on an old SHA. + echo "::warning::could not determine which registered workflows changed in ${PUSH_BEFORE}...${NEW_SHA} — sweeping every registered workflow" + STRICT=0 + all_registered + fi + else + # workflow_dispatch + if [[ -n "${INPUT_WORKFLOWS//[[:space:],]/}" ]]; then + STRICT=1 + for W in ${INPUT_WORKFLOWS//,/ }; do + W="${W##*/}" # allow full paths + [[ "$W" == *.yml || "$W" == *.yaml ]] || W="${W}.yml" + if ! registered_var "$W" >/dev/null; then + echo "::error::'${W}' is not a registered workflow — see the REGISTRY list in this workflow file." + exit 1 + fi + [[ " ${TARGETS[*]} " == *" ${W} "* ]] || TARGETS+=("$W") + done + else + all_registered + fi + fi + + # ---- Per-caller bump (unchanged behavior). ------------------------ + # Bump a single caller repo. Every external call is guarded, so a bad + # repo returns non-zero here (caught by the loop) instead of killing + # the run. An expected skip (file missing / already pinned) is a + # successful `return 0`. + bump_one() { + local NAME="$1" BRANCH="$2" WF_FILE="$3" ENTRY="$4" REPO FILE LABEL FILE_ENC + REPO=$(cut -d'|' -f1 <<<"$ENTRY") + FILE=$(cut -d'|' -f2 <<<"$ENTRY") + LABEL=$(cut -d'|' -f3 <<<"$ENTRY") + FILE_ENC="${FILE//\//%2F}" + + local DEFAULT_BRANCH + DEFAULT_BRANCH=$(gh api "repos/${REPO}" --jq '.default_branch') || { + echo "::warning::${REPO}: cannot read repo metadata — skipping" + return 1 + } + + # Fetch current file; a missing file is an expected skip (success). + local CURRENT + CURRENT=$(gh api "repos/${REPO}/contents/${FILE_ENC}?ref=${DEFAULT_BRANCH}" 2>/dev/null) || { + echo "::warning::${REPO}: ${FILE} not found — skipping" + return 0 + } + + local BLOB_SHA OLD_CONTENT + BLOB_SHA=$(jq -r '.sha' <<<"$CURRENT") + OLD_CONTENT=$(jq -r '.content' <<<"$CURRENT" | base64 -d) + + # Already pinned to this SHA → nothing to do (success). + if grep -qF "$NEW_SHA" <<<"$OLD_CONTENT"; then + echo "${REPO}: already at ${SHORT} — skipping" + return 0 + fi + + # Replace every 40-char hex SHA and update the inline comment. + local NEW_CONTENT + NEW_CONTENT=$(sed -E "s/[0-9a-f]{40}/${NEW_SHA}/g; s|# github-workflows#[0-9]+|# github-workflows main (${SHORT})|g" <<<"$OLD_CONTENT") + + if [[ "$DRY_RUN" == "true" ]]; then + echo "${REPO}: would bump ${FILE} to ${SHORT} (dry run)" + return 0 + fi + + # Create the branch from the default-branch tip (ignore 422 if it exists). + local MAIN_SHA + MAIN_SHA=$(gh api "repos/${REPO}/git/refs/heads/${DEFAULT_BRANCH}" --jq '.object.sha') || { + echo "::warning::${REPO}: cannot read ${DEFAULT_BRANCH} ref — skipping" + return 1 + } + gh api --method POST "repos/${REPO}/git/refs" \ + --field ref="refs/heads/${BRANCH}" \ + --field sha="${MAIN_SHA}" 2>/dev/null || true + + # Commit the updated file onto the branch. `$(...)` capture above + # (base64 -d, then sed) strips the file's trailing newline, so + # printf '%s\n' restores exactly one — otherwise the committed + # caller loses its EOF newline and trips the receiver repo's + # formatter (oxfmt/prettier both require a trailing newline). + local ENCODED + ENCODED=$(printf '%s\n' "$NEW_CONTENT" | base64 | tr -d '\n') + gh api --method PUT "repos/${REPO}/contents/${FILE_ENC}" \ + --field message="ci: bump ${NAME} to github-workflows@${SHORT}" \ + --field content="${ENCODED}" \ + --field sha="${BLOB_SHA}" \ + --field branch="${BRANCH}" \ + > /dev/null || { + echo "::warning::${REPO}: commit to ${BRANCH} failed — skipping" + return 1 + } + + local LABEL_ARGS=() + [[ -n "$LABEL" ]] && LABEL_ARGS=(--label "$LABEL") + + # Open the PR (a pre-existing PR for this branch is not an error). + if gh pr create \ + --repo "${REPO}" \ + --head "${BRANCH}" \ + --base "${DEFAULT_BRANCH}" \ + --title "ci: bump ${NAME} to github-workflows@${SHORT}" \ + --body "Automatic SHA bump — \`${WF_FILE}\` was updated in \`Comfy-Org/github-workflows\` at [\`${SHORT}\`](https://github.com/Comfy-Org/github-workflows/commit/${NEW_SHA}). _Opened by the \`bump-callers\` workflow._" \ + "${LABEL_ARGS[@]}" 2>/dev/null; then + echo "${REPO}: PR opened" + else + echo "::warning::${REPO}: PR may already exist for ${BRANCH}" + fi + return 0 + } + + # ---- Per-workflow fan-out. ---------------------------------------- + FAILED=() + + bump_workflow() { + local WF_FILE="$1" VAR CALLERS_JSON NAME BRANCH + NAME="${WF_FILE%.yml}" + NAME="${NAME%.yaml}" + BRANCH="ci/bump-${NAME}-${SHORT}" + VAR=$(registered_var "$WF_FILE") + CALLERS_JSON="${!VAR-}" + + # Missing caller list. In STRICT mode (this workflow was explicitly + # targeted) that's a hard error — a silent no-op dispatcher (every + # caller left un-bumped without anyone noticing) must not be + # possible. In sweep mode it's an expected skip for a workflow + # that's registered but not yet adopted. An EXPLICIT empty array + # (`[]`) is always a benign skip — the deliberate "registered, + # intentionally zero callers" state. + if [[ -z "${CALLERS_JSON//[[:space:]]/}" ]]; then + if (( STRICT )); then + echo "::error::${NAME}: callers variable ${VAR} is missing or empty. Seed it with the caller list (or an explicit []) — see this workflow's header comment for the update flow." + return 1 + fi + echo "::warning::${NAME}: callers variable ${VAR} is missing or empty — no callers registered; skipping" + return 0 + fi + if jq -e 'type == "array" and length == 0' <<<"$CALLERS_JSON" >/dev/null 2>&1; then + echo "${NAME}: ${VAR} is an empty list — no callers to bump; skipping" + return 0 + fi + if ! jq -e 'type == "array" and all(.[]; (.repo | type == "string" and . != "") and (.file | type == "string" and . != ""))' <<<"$CALLERS_JSON" >/dev/null 2>&1; then + echo "::error::${NAME}: ${VAR} is not a JSON array of {repo,file,label} objects (each needing a non-empty repo and file). Fix the variable — see the workflow header." + return 1 + fi + + # Parse the JSON into repo|file|label tuples, and mask every repo + # name in the (publicly viewable) run logs BEFORE the loop that + # echoes it, so all per-repo output shows *** instead of a private + # repo name. + local CALLERS=() ENTRY + while IFS= read -r ENTRY; do + echo "::add-mask::${ENTRY%%|*}" + CALLERS+=("$ENTRY") + done < <(jq -r '.[] | "\(.repo)|\(.file)|\(.label // "")"' <<<"$CALLERS_JSON") + + echo "${NAME}: bumping ${#CALLERS[@]} caller(s) to ${SHORT}" + local RC=0 + for ENTRY in "${CALLERS[@]}"; do + bump_one "$NAME" "$BRANCH" "$WF_FILE" "$ENTRY" || { + FAILED+=("${NAME}:${ENTRY%%|*}") + RC=1 + } + done + return $RC + } + + OK=1 + for WF_FILE in "${TARGETS[@]}"; do + bump_workflow "$WF_FILE" || OK=0 + done + + if (( ${#FAILED[@]} )); then + printf '::error::bump failed for %d caller(s): %s\n' "${#FAILED[@]}" "${FAILED[*]}" + fi + (( OK )) || exit 1 + echo "bump complete for: ${TARGETS[*]}" diff --git a/.github/workflows/bump-cursor-review-callers.yml b/.github/workflows/bump-cursor-review-callers.yml deleted file mode 100644 index 073932d..0000000 --- a/.github/workflows/bump-cursor-review-callers.yml +++ /dev/null @@ -1,177 +0,0 @@ -name: Bump cursor-review callers - -# When cursor-review.yml is updated on main, open a SHA-bump PR in every repo -# that pins a caller against it. PRs are opened by Cloud Code Bot so they are -# easy to filter and merge. -# -# The caller list is NOT hardcoded here. This repo is PUBLIC (workflow file and -# Actions run logs are both publicly viewable), and most callers are private, so -# their names must never appear in this file or its logs. The list lives in the -# repo-level Actions variable `CURSOR_REVIEW_CALLERS` (config, not a credential — -# a variable, not a secret, since secrets are write-only via the API) as a JSON -# array of {"repo","file","label"} objects. Every repo name is `::add-mask::`ed -# out of the (public) run logs before it is ever echoed. -# -# Update flow — adding/removing a caller needs NO public commit: -# gh variable set CURSOR_REVIEW_CALLERS --repo Comfy-Org/github-workflows \ -# --body "$(jq -c . callers.json)" -# Keep the canonical callers.json in a PRIVATE infra/ops repo so variable edits -# have a reviewed source of truth; the org audit log records each edit. (The -# specific home repo is intentionally not named here — this file is public.) - -on: - workflow_dispatch: {} # allow on-demand runs (e.g. to re-bump callers) - push: - branches: [main] - paths: - - .github/workflows/cursor-review.yml - -jobs: - bump: - runs-on: ubuntu-latest - steps: - - name: Generate Cloud Code Bot token - uses: actions/create-github-app-token@d72941d797fd3113feb6b93fd0dec494b13a2547 # v1.12.0 - id: token - with: - app-id: ${{ vars.APP_ID }} - private-key: ${{ secrets.CLOUD_CODE_BOT_PRIVATE_KEY }} - owner: Comfy-Org - - - name: Bump SHA in caller repos - env: - GH_TOKEN: ${{ steps.token.outputs.token }} - NEW_SHA: ${{ github.sha }} - # JSON array of {"repo","file","label"} — see the header comment for the - # update flow. Kept in a variable (not the file) so private caller names - # never land in this public repo or its logs. - CALLERS_JSON: ${{ vars.CURSOR_REVIEW_CALLERS }} - run: | - # NOTE: deliberately no `set -e`. Each caller is bumped independently - # in bump_one(); a failure there returns non-zero and is downgraded to - # a per-repo warning so one bad repo cannot abort the whole fan-out. - # We still fail the job at the end if any repo failed. - set -uo pipefail - SHORT="${NEW_SHA:0:7}" - BRANCH="ci/bump-cursor-review-${SHORT}" - - # Load the caller list from the CURSOR_REVIEW_CALLERS variable. Hard-fail - # loudly on a missing/empty/invalid value — a silent no-op dispatcher - # (which would leave every caller un-bumped without anyone noticing) must - # not be possible. - if [[ -z "${CALLERS_JSON//[[:space:]]/}" ]]; then - echo "::error::CURSOR_REVIEW_CALLERS variable is missing or empty. Seed it with the caller list — see this workflow's header comment for the update flow." - exit 1 - fi - if ! jq -e 'type == "array" and length > 0 and all(.[]; (.repo | type == "string" and . != "") and (.file | type == "string" and . != ""))' <<<"$CALLERS_JSON" >/dev/null 2>&1; then - echo "::error::CURSOR_REVIEW_CALLERS is not a non-empty JSON array of {repo,file,label} objects (each needing a non-empty repo and file). Fix the variable — see the workflow header." - exit 1 - fi - - # Parse the JSON into repo|file|label tuples, and mask every repo name in - # the (publicly viewable) run logs BEFORE the loop that echoes it, so all - # per-repo output shows *** instead of a private repo name. - CALLERS=() - while IFS= read -r ENTRY; do - echo "::add-mask::${ENTRY%%|*}" - CALLERS+=("$ENTRY") - done < <(jq -r '.[] | "\(.repo)|\(.file)|\(.label // "")"' <<<"$CALLERS_JSON") - - if (( ${#CALLERS[@]} == 0 )); then - echo "::error::CURSOR_REVIEW_CALLERS parsed to zero callers — refusing to run a no-op dispatcher." - exit 1 - fi - - # Bump a single caller repo. Every external call is guarded, so a bad - # repo returns non-zero here (caught by the loop) instead of killing - # the run. An expected skip (file missing / already pinned) is a - # successful `return 0`. - bump_one() { - local ENTRY="$1" REPO FILE LABEL FILE_ENC - REPO=$(cut -d'|' -f1 <<<"$ENTRY") - FILE=$(cut -d'|' -f2 <<<"$ENTRY") - LABEL=$(cut -d'|' -f3 <<<"$ENTRY") - FILE_ENC="${FILE//\//%2F}" - - local DEFAULT_BRANCH - DEFAULT_BRANCH=$(gh api "repos/${REPO}" --jq '.default_branch') || { - echo "::warning::${REPO}: cannot read repo metadata — skipping" - return 1 - } - - # Fetch current file; a missing file is an expected skip (success). - local CURRENT - CURRENT=$(gh api "repos/${REPO}/contents/${FILE_ENC}?ref=${DEFAULT_BRANCH}" 2>/dev/null) || { - echo "::warning::${REPO}: ${FILE} not found — skipping" - return 0 - } - - local BLOB_SHA OLD_CONTENT - BLOB_SHA=$(jq -r '.sha' <<<"$CURRENT") - OLD_CONTENT=$(jq -r '.content' <<<"$CURRENT" | base64 -d) - - # Already pinned to this SHA → nothing to do (success). - if grep -qF "$NEW_SHA" <<<"$OLD_CONTENT"; then - echo "${REPO}: already at ${SHORT} — skipping" - return 0 - fi - - # Replace every 40-char hex SHA and update the inline comment. - local NEW_CONTENT - NEW_CONTENT=$(sed -E "s/[0-9a-f]{40}/${NEW_SHA}/g; s|# github-workflows#[0-9]+|# github-workflows main (${SHORT})|g" <<<"$OLD_CONTENT") - - # Create the branch from the default-branch tip (ignore 422 if it exists). - local MAIN_SHA - MAIN_SHA=$(gh api "repos/${REPO}/git/refs/heads/${DEFAULT_BRANCH}" --jq '.object.sha') || { - echo "::warning::${REPO}: cannot read ${DEFAULT_BRANCH} ref — skipping" - return 1 - } - gh api --method POST "repos/${REPO}/git/refs" \ - --field ref="refs/heads/${BRANCH}" \ - --field sha="${MAIN_SHA}" 2>/dev/null || true - - # Commit the updated file onto the branch. `$(...)` capture above - # (base64 -d, then sed) strips the file's trailing newline, so - # printf '%s\n' restores exactly one — otherwise the committed - # caller loses its EOF newline and trips the receiver repo's - # formatter (oxfmt/prettier both require a trailing newline). - local ENCODED - ENCODED=$(printf '%s\n' "$NEW_CONTENT" | base64 | tr -d '\n') - gh api --method PUT "repos/${REPO}/contents/${FILE_ENC}" \ - --field message="ci: bump cursor-review to github-workflows@${SHORT}" \ - --field content="${ENCODED}" \ - --field sha="${BLOB_SHA}" \ - --field branch="${BRANCH}" \ - > /dev/null || { - echo "::warning::${REPO}: commit to ${BRANCH} failed — skipping" - return 1 - } - - local LABEL_ARGS=() - [[ -n "$LABEL" ]] && LABEL_ARGS=(--label "$LABEL") - - # Open the PR (a pre-existing PR for this branch is not an error). - if gh pr create \ - --repo "${REPO}" \ - --head "${BRANCH}" \ - --base "${DEFAULT_BRANCH}" \ - --title "ci: bump cursor-review to github-workflows@${SHORT}" \ - --body "Automatic SHA bump — \`cursor-review.yml\` was updated in \`Comfy-Org/github-workflows\` at [\`${SHORT}\`](https://github.com/Comfy-Org/github-workflows/commit/${NEW_SHA}). _Opened by the \`bump-cursor-review-callers\` workflow._" \ - "${LABEL_ARGS[@]}" 2>/dev/null; then - echo "${REPO}: PR opened" - else - echo "::warning::${REPO}: PR may already exist for ${BRANCH}" - fi - return 0 - } - - FAILED=() - for ENTRY in "${CALLERS[@]}"; do - bump_one "$ENTRY" || FAILED+=("${ENTRY%%|*}") - done - - if (( ${#FAILED[@]} )); then - printf '::error::bump failed for %d repo(s): %s\n' "${#FAILED[@]}" "${FAILED[*]}" - exit 1 - fi - echo "cursor-review bump complete for all callers." From c26fe337503a88f236d3466598f4bdd40c0ef624 Mon Sep 17 00:00:00 2001 From: Matt Miller Date: Mon, 20 Jul 2026 23:33:02 -0700 Subject: [PATCH 2/3] fix: resolve review feedback MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Restore the explicit `permissions: contents: read` this PR originally added (as a zizmor excessive-permissions fix on the old bump-cursor-review-callers.yml) across all three bump-*-callers.yml entrypoints — main's #38 shared-script version and the two pre-existing sibling entrypoints (bump-agents-md-callers.yml, bump-cursor-review-callers.yml) hadn't gained it, only the new bump-pr-size- callers.yml would have otherwise. None of the jobs write via GITHUB_TOKEN (all writes go through the generated app token), so read-only is correct and matches every other workflow file in this repo, which all declare explicit permissions. --- .github/workflows/bump-agents-md-callers.yml | 3 +++ .github/workflows/bump-cursor-review-callers.yml | 3 +++ .github/workflows/bump-pr-size-callers.yml | 3 +++ 3 files changed, 9 insertions(+) diff --git a/.github/workflows/bump-agents-md-callers.yml b/.github/workflows/bump-agents-md-callers.yml index 772fa61..66d79de 100644 --- a/.github/workflows/bump-agents-md-callers.yml +++ b/.github/workflows/bump-agents-md-callers.yml @@ -39,6 +39,9 @@ on: - .github/workflows/agents-md-integrity.yml - .github/agents-md-integrity/** +permissions: + contents: read + jobs: bump: runs-on: ubuntu-latest diff --git a/.github/workflows/bump-cursor-review-callers.yml b/.github/workflows/bump-cursor-review-callers.yml index 489946f..fc7d86d 100644 --- a/.github/workflows/bump-cursor-review-callers.yml +++ b/.github/workflows/bump-cursor-review-callers.yml @@ -33,6 +33,9 @@ on: paths: - .github/workflows/cursor-review.yml +permissions: + contents: read + jobs: bump: runs-on: ubuntu-latest diff --git a/.github/workflows/bump-pr-size-callers.yml b/.github/workflows/bump-pr-size-callers.yml index 71e50aa..a6135bd 100644 --- a/.github/workflows/bump-pr-size-callers.yml +++ b/.github/workflows/bump-pr-size-callers.yml @@ -39,6 +39,9 @@ on: paths: - .github/workflows/pr-size.yml +permissions: + contents: read + jobs: bump: runs-on: ubuntu-latest From ed6b9ade349a83228461720caa7f76a98c807d81 Mon Sep 17 00:00:00 2001 From: Matt Miller Date: Tue, 21 Jul 2026 10:49:05 -0700 Subject: [PATCH 3/3] fix: resolve review feedback MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Drop the stale bump-pr-size-callers.yml comment claiming pr-size.yml hasn't landed yet — it merged via #36 in the conflict-resolution merge. --- .github/workflows/bump-pr-size-callers.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/bump-pr-size-callers.yml b/.github/workflows/bump-pr-size-callers.yml index 5d2d71b..2234dae 100644 --- a/.github/workflows/bump-pr-size-callers.yml +++ b/.github/workflows/bump-pr-size-callers.yml @@ -22,8 +22,8 @@ name: Bump pr-size callers # array of {"repo","file","label"} objects, same shape as the other fleets' # variables. Every repo name is `::add-mask::`ed out of the (public) run logs # before it is ever echoed. The variable is seeded EMPTY (`[]`) — an empty list -# is a clean no-op here (ALLOW_EMPTY below), since pr-size.yml itself hasn't -# landed yet; the rollout's per-repo caller tickets add entries as callers land. +# is a clean no-op here (ALLOW_EMPTY below); the rollout's per-repo caller +# tickets add entries as callers land. # # Update flow — adding/removing a caller needs NO public commit: # gh variable set PR_SIZE_CALLERS --repo Comfy-Org/github-workflows \