Skip to content

harden pr-size reusable: batch check-attr, --source probe, git timeouts, comment-token scope#51

Merged
mattmillerai merged 6 commits into
mainfrom
matt/pr-size-checkattr-hardening
Jul 21, 2026
Merged

harden pr-size reusable: batch check-attr, --source probe, git timeouts, comment-token scope#51
mattmillerai merged 6 commits into
mainfrom
matt/pr-size-checkattr-hardening

Conversation

@mattmillerai

@mattmillerai mattmillerai commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

Hardens the reusable PR-size cap so every consumer inherits the improvements from one place. Three tool fixes + one workflow fix, all surfaced while migrating Comfy-Org/cloud onto this reusable (cloud#5335).

Tool: scripts/check-pr-size/ (Go)

Ports two follow-ups that were open against cloud's now-deleted local copy — they belong here, in the shared tool:

BE-3247 (was cloud#5163) — checkAttrSourceSupported previously treated any probe error as "flag unsupported," so a transient error or unreadable base ref on a modern git would drop every base linguist-generated exclusion and print a misleading "your git is too old" note. It now inspects git stderr and only treats git's own unknown option/unknown switch wording as unsupported (LC_ALL=C forces canonical English). Fail direction stays conservative: any other failure keeps --source trusted and the read fails closed → over-count, never under.

BE-3248 (was cloud#5164) — attrGenerated (one subprocess per file) becomes attrGeneratedBatch: a single git check-attr -z --stdin pass. Every git call now runs under a 60s context.WithTimeout (runGitStdin + runGitCapped), so a wedged git can't stall the job.

The two cloud PRs each rewrote runGit; here they're unified into one runGitStdin (stdin + timeout + bounded stderr capture + C locale), and the deadline extends to the reusable's runGitCapped blob path too. Base-only reading (BE-3220) preserved.

Workflow: pr-size.yml comment-token scope

The comment job minted the bot token requesting issues:write and pull-requests:write, but the sticky-comment upsert only calls the issues comments API. On an installation not granted pull-requests:write (cloud's Cloud Code Bot), the mint 422s and the comment job fails — a red check from the best-effort comment path alone. Now:

  • request only permission-issues: write (exactly what's used);
  • continue-on-error on the mint so a consumer whose bot lacks the scope degrades to status + step summary, never a red check;
  • the degraded-mode note fires on a failed mint too, not just absent creds.

Testing

gofmt -l, go vet, go build, go test ./..., actionlint pr-size.yml all clean locally on git 2.50.1 (--source exercised, not skipped). New/updated tests: TestAttrGeneratedBatchMultiplePaths, TestIsUnknownFlagError, TestCheckAttrSourceSupported, TestCapWriter, TestAttrGeneratedSourceIsolatesBase (→ batch API).

After this merges

Re-pin cloud#5335's uses: + workflows_ref to the resulting SHA, then close cloud#5163 / cloud#5164.

🤖 Generated with Claude Code

…alls

Ports two robustness fixes onto the reusable's check-pr-size tool. Both were
raised as follow-ups on the original implementation in Comfy-Org/cloud
(cloud#5163 / BE-3247 and cloud#5164 / BE-3248) and belong here, in the shared
tool, so every consuming repo inherits them instead of each carrying a fork.

BE-3247 — distinguish an unsupported `check-attr --source` from unrelated
probe failures. checkAttrSourceSupported previously treated ANY probe error as
"flag unsupported", so a transient error or an unreadable base ref on a modern
git would wrongly disable every base linguist-generated exclusion and print a
misleading "your git is too old" note. It now inspects git's stderr and only
treats git's own unknown-option/unknown-switch wording as unsupported; the
capture forces LC_ALL=C so the match sees canonical English. Fail direction
stays conservative (over-count, never under-count).

BE-3248 — resolve linguist-generated for all paths in ONE `git check-attr -z
--stdin` pass (attrGeneratedBatch) instead of one subprocess per file, and put
a 60s deadline on every git invocation (runGitStdin + runGitCapped via
exec.CommandContext) so a wedged git can't stall the job until the runner-wide
timeout.

The two cloud PRs each rewrote runGit independently; here they are unified into
a single runGitStdin (stdin + timeout + bounded stderr capture + C locale),
and the deadline is extended to the reusable's runGitCapped blob-read path too.
Base-only attribute reading (BE-3220) is preserved unchanged.

Tests: TestAttrGeneratedBatchMultiplePaths, TestIsUnknownFlagError,
TestCheckAttrSourceSupported, TestCapWriter; TestAttrGeneratedSourceIsolatesBase
updated to the batch API. gofmt/vet/build/test all clean (git 2.50.1, --source
exercised).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 21, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

You’ve reached a temporary PR review limit under our Fair Usage Limits Policy.

Your recent review volume is higher than typical usage, so adaptive limits are currently applied.

Next review available in: 35 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 4d6b4dcc-db60-485a-abfb-ca458247d986

📥 Commits

Reviewing files that changed from the base of the PR and between 64c4803 and a4e7e9e.

📒 Files selected for processing (4)
  • .github/workflows/pr-size.yml
  • scripts/check-pr-size/check-pr-size
  • scripts/check-pr-size/main.go
  • scripts/check-pr-size/main_test.go
📝 Walkthrough

Walkthrough

The PR hardens git subprocess execution with timeouts and capped stderr, batches linguist-generated attribute checks, expands coverage for these paths, and makes the PR-size workflow tolerate bot-token failures and ambient Go workspace files.

Changes

PR-size robustness

Layer / File(s) Summary
Bounded git execution
scripts/check-pr-size/main.go, scripts/check-pr-size/main_test.go
Git commands use timed execution, stable locale settings, separate capped stderr, and updated error handling validated by TestCapWriter.
Batched generated-file classification
scripts/check-pr-size/main.go, scripts/check-pr-size/main_test.go
Linguist-generated attributes are resolved for multiple paths in one command, with base-source isolation and explicit unknown-flag handling covered by tests.
Workflow build and degraded-mode handling
.github/workflows/pr-size.yml
The build disables ambient Go workspace discovery, while bot-token minting tolerates failure and reports all non-success outcomes using only issue-write permission.

Sequence Diagram(s)

sequenceDiagram
  participant classify
  participant attrGeneratedBatch
  participant runGitStdin
  participant git
  classify->>attrGeneratedBatch: provide non-binary paths
  attrGeneratedBatch->>runGitStdin: send NUL-separated paths
  runGitStdin->>git: run git check-attr --stdin
  git-->>runGitStdin: return attributes and diagnostics
  runGitStdin-->>attrGeneratedBatch: return command result
  attrGeneratedBatch-->>classify: return generated-path map
Loading
🚥 Pre-merge checks | ✅ 2
✅ Passed checks (2 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch matt/pr-size-checkattr-hardening
✨ Simplify code
  • Create PR with simplified code
  • Commit simplified code in branch matt/pr-size-checkattr-hardening

Comment @coderabbitai help to get the list of available commands.

@mattmillerai mattmillerai added the cursor-review Multi-model cursor review label Jul 21, 2026
…-effort

The comment job minted the bot token requesting both issues:write and
pull-requests:write, but the sticky-comment upsert only ever calls the issues
comments API (GET/POST/PATCH /issues/.../comments). On an installation not
granted pull-requests:write — e.g. Comfy-Org/cloud's Cloud Code Bot — the mint
returns 422 "permissions requested are not granted to this installation" and
the comment job fails, turning a check red purely on the best-effort comment
path even though the size verdict (in the pr-size job) is fine.

- Request only permission-issues: write — exactly what the upsert uses.
- Mark the mint continue-on-error so a consumer whose bot lacks even that scope
  (or has creds misconfigured) degrades to status + step summary instead of a
  red check. The size verdict never depended on this job.
- Broaden the degraded-mode note to fire on a failed mint too, not just absent
  creds, so the log always explains why no comment was posted.

Surfaced by cloud#5335 (the migration of Comfy-Org/cloud onto this reusable).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@mattmillerai mattmillerai changed the title perf(pr-size): batch check-attr + harden --source probe + bound git calls harden pr-size reusable: batch check-attr, --source probe, git timeouts, comment-token scope Jul 21, 2026
…sumers

The tool is checked out into the consumer's working tree (_pr_size_tool/) and
built with `go build`. When the consumer is a go.work monorepo — e.g.
Comfy-Org/cloud — `go build` walks up, discovers the consumer's go.work, and
fails: "current directory is contained in a module that is not one of the
workspace modules listed in go.work." The tool's module is not (and should not
be) part of the consumer's workspace.

Set GOWORK=off on the build step so the ambient workspace is ignored and the
tool builds as its own standalone module (it has its own go.mod). No effect on
non-workspace consumers.

Caught by cloud#5335: the reusable's first go.work-based consumer. Without this
the pr-size job never even evaluates size — it dies at the build step.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@mattmillerai
mattmillerai marked this pull request as ready for review July 21, 2026 18:37
@mattmillerai mattmillerai added cursor-review Multi-model cursor review and removed cursor-review Multi-model cursor review labels Jul 21, 2026

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔍 Cursor Review — Consolidated panel

Triggered by @mattmillerai.

Found 4 finding(s).

Severity Count
🟡 Medium 1
🟢 Low 2
⚪ Nit 1

Panel: 8/8 reviewers contributed findings.

Comment thread scripts/check-pr-size/main.go
Comment thread scripts/check-pr-size/main.go
Comment thread scripts/check-pr-size/main.go
Comment thread scripts/check-pr-size/main.go

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
scripts/check-pr-size/main_test.go (1)

82-227: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Add a regression test for the git timeout path
scripts/check-pr-size/main_test.go covers batching, probe detection, and base isolation, but not the gitTimeout/runGitStdin deadline behavior. Add one test that forces a hung git call and asserts the timeout error so this guard doesn’t go feral in CI.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/check-pr-size/main_test.go` around lines 82 - 227, Add a regression
test in scripts/check-pr-size/main_test.go that exercises the
gitTimeout/runGitStdin deadline path by forcing a git invocation to hang, then
assert it returns the expected timeout error. Keep the test deterministic and
bounded so it reliably detects deadline enforcement without risking a lingering
process or extended CI runtime.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/pr-size.yml:
- Around line 317-321: Update the degraded-mode reporting step around steps.dl
and steps.bot so it also runs when steps.dl.outcome != 'success'. Emit a
distinct note for download failures, while preserving the existing
credentials/token failure message when the download succeeds but
steps.bot.outcome is not successful.

---

Outside diff comments:
In `@scripts/check-pr-size/main_test.go`:
- Around line 82-227: Add a regression test in
scripts/check-pr-size/main_test.go that exercises the gitTimeout/runGitStdin
deadline path by forcing a git invocation to hang, then assert it returns the
expected timeout error. Keep the test deterministic and bounded so it reliably
detects deadline enforcement without risking a lingering process or extended CI
runtime.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: aa5b8c2b-4f21-424a-908f-2ecca5b5b70d

📥 Commits

Reviewing files that changed from the base of the PR and between eda264b and 64c4803.

📒 Files selected for processing (3)
  • .github/workflows/pr-size.yml
  • scripts/check-pr-size/main.go
  • scripts/check-pr-size/main_test.go

Comment thread .github/workflows/pr-size.yml Outdated
mattmillerai and others added 2 commits July 21, 2026 11:46
…iagnostics

Address cursor-review panel findings on the git helper refactor:

- Set cmd.WaitDelay (Go 1.20+) on runGitStdin and runGitCapped so a git
  descendant that inherited the stdout/stderr pipes (filter driver, textconv,
  pager, credential helper) can't hold them open and block Wait() past the
  SIGKILL deadline, keeping the BE-3248 timeout robust (Medium).
- runGit now folds runGitStdin's captured stderr into its returned error.
  runGitStdin sets cmd.Stderr, so *exec.ExitError no longer carries git's
  stderr; diffFiles and other callers were losing git's diagnostics (Low).
- Wrap runGitCapped's deadline hit as %w of ctx.Err() for parity with
  runGitStdin so errors.Is(err, context.DeadlineExceeded) works (Nit).
- Add TestRunGitFoldsStderrIntoError regression guard.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The comment job's "Note degraded mode" step only fired when the report
download succeeded but the bot token was absent/unmintable. When
actions/download-artifact itself failed (continue-on-error), every
downstream step — including this note — was skipped, so the job went green
with no explanation of why no comment was posted. Broaden the condition to
also cover steps.dl.outcome != 'success' and emit a distinct message
pointing at the pr-size job logs (CodeRabbit).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔍 Cursor Review — Consolidated panel

Triggered by @mattmillerai.

Found 3 finding(s).

Severity Count
🟠 High 1
🟡 Medium 1
⚪ Nit 1

Panel: 8/8 reviewers contributed findings.

Comment thread .github/workflows/pr-size.yml Outdated
Comment thread scripts/check-pr-size/main.go
Comment thread scripts/check-pr-size/main.go
…false

A bare `off` is a YAML boolean; GitHub Actions stringifies it into the env
as "false", and `go build` with GOWORK=false (neither off, empty, nor an
absolute path) aborts — breaking the build for every consumer, including the
go.work monorepo case this env was added to fix. Quote it as "off".

Flagged by cursor-review panel (2/8 reviewers, High).
@mattmillerai
mattmillerai merged commit 6158503 into main Jul 21, 2026
4 checks passed
@mattmillerai
mattmillerai deleted the matt/pr-size-checkattr-hardening branch July 21, 2026 19:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cursor-review Multi-model cursor review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant