Skip to content

fix: typed error for workflows-scope rejection in review/fallback branch push#42001

Merged
pelikhan merged 8 commits into
mainfrom
copilot/aw-failures-change-token-scope
Jun 28, 2026
Merged

fix: typed error for workflows-scope rejection in review/fallback branch push#42001
pelikhan merged 8 commits into
mainfrom
copilot/aw-failures-change-token-scope

Conversation

Copilot AI commented Jun 28, 2026

Copy link
Copy Markdown
Contributor

When threat detection forces a review-branch push and the branch contains .github/workflows/*.lock.yml changes, GitHub's pre-receive hook rejects the push for missing workflows scope. The previous code used exec.exec(), which swallows stderr — the agent received only git exit code 1, logged nothing actionable, and silently discarded the changeset commit.

Changes

push_to_pull_request_branch.cjs

  • isWorkflowsScopeRejection(stderr) — null-safe, case-insensitive helper matching GitHub's two rejection variants:
    • `workflows` scope may be required
    • workflow can be created or updated due to timeout
  • buildWorkflowsScopeError(context, core) — emits two core.error() messages with remediation steps and returns { success: false, error_type: "workflows_scope_required", error: "..." }
  • Both the threat-detection review branch push and the non-fast-forward fallback push are switched from exec.execexec.getExecOutput({ ignoreReturnCode: true }) so stderr is captured; matched rejections return buildWorkflowsScopeError(...) directly (bypasses the outer catch that would strip error_type)

push_to_pull_request_branch.test.cjs — 3 new tests in describe("threat detection: review branch push"):

  • Timeout variant → error_type: "workflows_scope_required"
  • Backtick variant → error_type: "workflows_scope_required"
  • Unrelated push failure → generic catch, no typed error
// before
await exec.exec("git", ["push", "origin", reviewBranchName], { env: gitAuthEnv });

// after
const pushResult = await exec.getExecOutput("git", ["push", "origin", reviewBranchName], {
  ignoreReturnCode: true,
  env: gitAuthEnv,
});
if (pushResult.exitCode !== 0) {
  if (isWorkflowsScopeRejection(pushResult.stderr)) {
    return buildWorkflowsScopeError("Review branch", core);
  }
  throw new Error(`Review branch push failed:\n${pushResult.stderr}`);
}

The proper long-term fix is push-to-pull-request-branch.allow-workflows: true with a GitHub App carrying workflows: write — the error messages now explicitly direct operators there.

Copilot AI and others added 6 commits June 28, 2026 04:10
- Fix review branch push path: use exec.getExecOutput(ignoreReturnCode:true) to capture stderr, detect GitHub's "workflows scope" rejection, return typed error_type:"workflows_scope_required" with actionable message pointing to allow-workflows config
- Fix fallback (non-fast-forward) branch push path: same detection and typed error for workflows scope rejection
- Add 3 unit tests in threat detection: review branch push describe block - covering the timeout variant, backtick variant, and generic push failure cases
- Ran make fmt-cjs, make lint-cjs, make agent-report-progress — all pass

Changes in:
- actions/setup/js/push_to_pull_request_branch.cjs
- actions/setup/js/push_to_pull_request_branch.test.cjs

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
…ror helpers

- Extracted detection logic into isWorkflowsScopeRejection() helper with case-insensitive matching
- Extracted error construction into buildWorkflowsScopeError() helper to eliminate duplication
- Both review branch and fallback branch push paths now use shared helpers
- Tests still pass (97/97), fmt-cjs and lint-cjs clean, make agent-report-progress passes

Changes in:
- actions/setup/js/push_to_pull_request_branch.cjs
- actions/setup/js/push_to_pull_request_branch.test.cjs

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
…or message

- Added null/falsy guard to isWorkflowsScopeRejection() to prevent potential TypeError on undefined input
- Updated generic push failure test to use a realistic git authentication error URL
- All 97 tests pass, fmt-cjs, lint-cjs, make agent-report-progress all pass

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
The fallback (non-fast-forward) branch push now returns the typed
error_type:'workflows_scope_required' object directly, consistent with
the review branch path. Previously it threw the error message string,
losing the structured type field through the catch handler.

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
…or error messages

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Copilot AI changed the title [WIP] Grant Changeset Generator safe-output push workflows scope fix: typed error for workflows-scope rejection in review/fallback branch push Jun 28, 2026
Copilot AI requested a review from pelikhan June 28, 2026 04:29
@github-actions

Copy link
Copy Markdown
Contributor

Great work on this fix! 🎉 The typed error propagation for workflows-scope rejections in push_to_pull_request_branch.cjs is exactly the kind of improvement that makes agent-run failures diagnosable instead of silently swallowed.

The PR is well-structured: the root cause is clearly explained, both the review-branch push and the non-fast-forward fallback paths are covered, and the three new test cases in push_to_pull_request_branch.test.cjs cover the two rejection message variants plus the unrelated-failure pass-through. The before/after code snippet and the pointer to the long-term workflows: write GitHub App fix are a nice touch for operators hitting this.

This looks ready for review. 🚀

Generated by ✅ Contribution Check · 113.9 AIC · ⌖ 19.9 AIC · ⊞ 6K ·

@github-actions

Copy link
Copy Markdown
Contributor

PR Triage — Run §28315307719

Category bug
Risk low
Priority medium
Score 47/100 — impact 28 · urgency 8 · quality 11
Action defer

⚠️ Draft PR. Fixes silent error swallowing when the workflows-scope rejection occurs during review/fallback branch push (+143/-3, 2 files). Ready to re-evaluate once marked ready for review.

Generated by 🔧 PR Triage Agent · 82.5 AIC · ⌖ 10.6 AIC · ⊞ 5.4K ·

@pelikhan pelikhan marked this pull request as ready for review June 28, 2026 08:56
Copilot AI review requested due to automatic review settings June 28, 2026 08:56
@github-actions

github-actions Bot commented Jun 28, 2026

Copy link
Copy Markdown
Contributor

PR Code Quality Reviewer completed the code quality review.

@github-actions

github-actions Bot commented Jun 28, 2026

Copy link
Copy Markdown
Contributor

Design Decision Gate 🏗️ completed the design decision gate check.

No ADR enforcement needed: PR #42001 does not have the 'implementation' label and has 0 new lines of code in business logic directories (threshold: 100).

@github-actions

github-actions Bot commented Jun 28, 2026

Copy link
Copy Markdown
Contributor

🧠 Matt Pocock Skills Reviewer has completed the skills-based review. ✅

@github-actions

github-actions Bot commented Jun 28, 2026

Copy link
Copy Markdown
Contributor

Test Quality Sentinel completed test quality analysis.

TQS analysis for PR #42001 already completed in this workflow run: comment posted and APPROVE review submitted (score 100/100, 3 design tests, 0 violations). No further action needed.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Improves push_to_pull_request_branch’s diagnostics when GitHub rejects a fallback/review-branch git push due to missing workflows permission, returning a typed error and logging actionable remediation steps instead of a generic push failure.

Changes:

  • Add isWorkflowsScopeRejection(stderr) and buildWorkflowsScopeError(...) helpers to detect and report missing-workflows-scope push rejections.
  • Switch review-branch and non-fast-forward fallback pushes from exec.exec() to exec.getExecOutput(..., { ignoreReturnCode: true }) to capture stderr and emit a typed failure.
  • Add tests covering both known rejection variants plus a generic push failure case.
Show a summary per file
File Description
actions/setup/js/push_to_pull_request_branch.cjs Detects “workflows scope required” push rejections and returns a typed, actionable error from review/fallback push paths.
actions/setup/js/push_to_pull_request_branch.test.cjs Adds regression tests for typed error behavior for review-branch push failures.

Review details

Tip

Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

  • Files reviewed: 2/2 changed files
  • Comments generated: 3
  • Review effort level: Low

Comment thread actions/setup/js/push_to_pull_request_branch.test.cjs
Comment thread actions/setup/js/push_to_pull_request_branch.test.cjs
Comment thread actions/setup/js/push_to_pull_request_branch.test.cjs
@github-actions github-actions Bot mentioned this pull request Jun 28, 2026
@github-actions

Copy link
Copy Markdown
Contributor

🧪 Test Quality Sentinel Report

Test Quality Score: 100/100 — Excellent

Analyzed 3 test(s): 3 design, 0 implementation, 0 guideline violation(s).

📊 Metrics & Test Classification (3 tests analyzed)
Metric Value
New/modified tests analyzed 3
✅ Design tests (behavioral contracts) 3 (100%)
⚠️ Implementation tests (low value) 0 (0%)
Tests with error/edge cases 3 (100%)
Duplicate test clusters 0
Test inflation detected No (test/prod ratio: 86/57 = 1.51:1)
🚨 Coding-guideline violations 0
Test File Classification Issues Detected
should return typed workflows_scope_required error ... (timeout variant) push_to_pull_request_branch.test.cjs ✅ Design
should return typed workflows_scope_required error ... (backtick variant) push_to_pull_request_branch.test.cjs ✅ Design Sparse (2 assertions; could also assert error message content)
should wrap generic review branch push failure in actionable error message push_to_pull_request_branch.test.cjs ✅ Design

Go: 0 (*_test.go); JavaScript: 3 (*.test.cjs). Other languages detected but not scored.

Notes on mocking: All three tests use vi.fn() to stub mockExec.getExecOutput and simulate GitHub's remote rejection responses. This mocks the external @actions/exec I/O layer — acceptable per project guidelines. Every test asserts on observable return values (result.success, result.error_type, result.error) and/or logger calls, not on internal call counts alone.

Verdict

Check passed. 0% implementation tests (threshold: 30%). All three new tests verify behavioral contracts: (1) the timeout variant of GitHub's workflows-scope rejection produces a typed workflows_scope_required error with actionable message content; (2) the backtick variant produces the same typed error; (3) a generic push failure is correctly not typed as workflows_scope_required. The negative test (case 3) is particularly valuable for preventing future regressions in error discrimination.

🧪 Test quality analysis by Test Quality Sentinel · 71.1 AIC · ⌖ 14.4 AIC · ⊞ 8.2K ·

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

✅ Test Quality Sentinel: 100/100. Test quality is excellent — 0% of new tests are implementation tests (threshold: 30%).

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Skills-Based Review 🧠

Applied /tdd and /diagnose — the core fix is sound, with a meaningful test coverage gap for the fallback branch path.

📋 Key Themes & Highlights

Key Themes

  • Fallback branch tests missing: isWorkflowsScopeRejection was added to both push paths, but only the review-branch path has new test coverage. The return buildWorkflowsScopeError("Fallback branch", core) code path is entirely untested.
  • Timeout pattern not isolated: The "timeout variant" test input matches both includes() branches simultaneously, so the timeout-only code path in isWorkflowsScopeRejection is never exclusively exercised.
  • Mock not restored: mockExec.getExecOutput is overridden in each test but never cleaned up — worth confirming or documenting vitest's reset behaviour for this shared object.
  • Timeout pattern breadth: The second includes() check omits the word workflows, making it slightly broader than the first; low practical risk but easy to tighten.

Positive Highlights

  • exec.execexec.getExecOutput + ignoreReturnCode: true is exactly the right pattern to capture stderr without relying on a thrown exception
  • ✅ Typed error_type: "workflows_scope_required" with a direct return that bypasses the outer catch is a clean, intentional design
  • buildWorkflowsScopeError actionable messages pointing operators to allow-workflows + GitHub App config are well-crafted
  • ✅ Null-safe, case-insensitive matching in isWorkflowsScopeRejection is defensive
  • ✅ Test 3 (generic auth failure → no typed error) provides solid negative coverage

🧠 Reviewed using Matt Pocock's skills by Matt Pocock Skills Reviewer · 86.9 AIC · ⌖ 10.2 AIC · ⊞ 6.6K

Comment thread actions/setup/js/push_to_pull_request_branch.test.cjs
Comment thread actions/setup/js/push_to_pull_request_branch.test.cjs
Comment thread actions/setup/js/push_to_pull_request_branch.test.cjs Outdated
Comment thread actions/setup/js/push_to_pull_request_branch.test.cjs Outdated
Comment thread actions/setup/js/push_to_pull_request_branch.cjs
@gh-aw-bot

Copy link
Copy Markdown
Collaborator

@copilot please run the pr-finisher skill, address the review feedback on fallback-branch test coverage, isolate the timeout-only case, and restore/reset the exec mock between tests. If you push a follow-up commit, refresh the branch and rerun checks.

Generated by 👨‍🍳 PR Sous Chef · 70.2 AIC · ⌖ 1.04 AIC · ⊞ 17.1K ·

… add fallback-branch scope test

- Forward `options` argument in all three getExecOutput wrappers in the
  threat-detection describe block so option-dependent behavior (env, cwd,
  ignoreReturnCode) is faithfully preserved when falling through to the
  original mock.

- Add an inner beforeEach/afterEach pair within describe("threat detection:
  review branch push") that saves and restores mockExec.getExecOutput, so
  the mock is explicitly reset between tests.

- Isolate the timeout-only variant: the first test now uses a stderr that
  matches only the "workflow can be created or updated due to timeout"
  pattern, not the combined timeout + backtick message, so the timeout path
  is tested in isolation.

- Add a new test in "repository state scenarios" that exercises the
  fallback-branch push path (non-fast-forward -> checkout -b -> getExecOutput
  push) returning `workflows_scope_required` when the fallback push is
  rejected for missing workflows scope.

Co-authored-by: gh-aw-bot <259018956+gh-aw-bot@users.noreply.github.com>
Copilot AI requested a review from gh-aw-bot June 28, 2026 09:36
@pelikhan pelikhan merged commit c6bc8c0 into main Jun 28, 2026
14 checks passed
@pelikhan pelikhan deleted the copilot/aw-failures-change-token-scope branch June 28, 2026 09:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants