chore: sync workflow templates - #569
Conversation
Automated sync from stranske/Workflows Template hash: c6b722d74cfd Changes synced from sync-manifest.yml
📝 WalkthroughWalkthroughThe agents-guard script gains a dependency-upgrade bypass path: new constants identify trusted bot logins and author associations, new patch-diff helpers detect whether a workflow file diff contains only ChangesAgents-guard dependency-PR bypass
claude-code-action SHA bump
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
|
Workflow state fingerprint for Keepalive Loop Reporter. Do not edit. |
|
Workflow state fingerprint for Agents Gate Followups. Do not edit. |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 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/scripts/agents-guard.js:
- Around line 401-410: The regex pattern in the parseActionReferenceLine
function accepts expression-based refs like ${{...}} in the second capture group
(the ref part), which should be rejected to ensure only literal static refs are
parsed. Modify the regex pattern to exclude refs containing expression syntax,
or add validation logic after the match to reject any refs that contain $ or {{
patterns, ensuring that only literal version tags and commit SHAs are treated as
static dependency refs rather than workflow logic expressions.
- Around line 616-620: The hasProtectedChanges variable only considers files
with status 'modified', allowing newly added protected workflow files to bypass
protection checks. Update the logic that sets hasProtectedChanges to also
include files with status 'added' that match protected paths using
matchProtectedPath. Additionally, update the protectedChangesAreDependencyOnly
filter condition to include both 'modified' and 'added' files when checking if
protected changes only contain dependency references via
patchChangesOnlyActionReferences.
🪄 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: 3c3757ad-716f-4286-91ca-0d99d31cbf91
📒 Files selected for processing (4)
.github/scripts/agents-guard.js.github/workflows/agents-guard.yml.github/workflows/maint-76-claude-code-review.ymlWORKFLOW_USER_GUIDE.md
🔗 Linked repositories identified
CodeRabbit considers these linked repositories for cross-repo context during reviews:
stranske/Workflows(auto-detected)
📜 Review details
🧰 Additional context used
📓 Path-based instructions (3)
.github/workflows/**/*.yml
📄 CodeRabbit inference engine (CLAUDE.md)
First-party consumers should reference reusable workflows with
@mainunless intentionally pinning to an exact commit SHA for a controlled reason.
Files:
.github/workflows/agents-guard.yml.github/workflows/maint-76-claude-code-review.yml
.github/workflows/agents-*.yml
📄 CodeRabbit inference engine (CLAUDE.md)
Agent workflows (
agents-*.yml) should be fixed instranske/Workflows, not locally.
Files:
.github/workflows/agents-guard.yml
.github/workflows/*.yml
📄 CodeRabbit inference engine (AGENTS.md)
First-party consumers currently reference reusable workflows with
@main. Match that unless you are intentionally pinning to an exact commit SHA for a controlled reason.
Files:
.github/workflows/agents-guard.yml.github/workflows/maint-76-claude-code-review.yml
🔀 Multi-repo context stranske/Workflows
Perfect! I now have sufficient cross-repository context to complete my analysis.
Linked repositories findings
stranske/Workflows (Template Repository)
agents-guard.js changes are already tested in source:
The source template repository shows tests for the new authorAssociation parameter in .github/scripts/__tests__/agents-guard.test.js (lines 329, 345, 362, 377), confirming that:
- Tests already validate
authorAssociation: 'OWNER'usage within theevaluateGuard()function - The parameter is tested as part of the dependency-upgrade bypass logic
- Tests confirm the function works with the new parameter [::stranske/Workflows::]
Template consumer-repo files are synced versions:
The consumer template located at ./templates/consumer-repo/.github/workflows/agents-guard.yml already contains:
- Extraction of
context.payload.pull_request.author_association - Passing
authorAssociationtoevaluateGuard()invocation (lines matching the PR's changes) - This is the source template that the PR is syncing to Pension-Data [::stranske/Workflows::]
Sync manifest controls the distribution:
The .github/sync-manifest.yml in stranske/Workflows explicitly lists:
.github/scripts/agents-guard.jsas a synced script.github/workflows/agents-guard.ymlas a synced workflowWORKFLOW_USER_GUIDE.mdas synced documentation- These files are distributed from
templates/consumer-repo/to all consumer repositories [::stranske/Workflows::]
Cross-repo impact assessment:
All consumer repositories (including stranske/Pension-Data) that receive this sync will get:
- The new
authorAssociationparameter handling in the agents-guard.yml workflow - The updated
evaluateGuard()function that uses author association for dependency-upgrade bypass - The new
patchChangesOnlyActionReferences()exported function - Updated documentation clarifying the dependency-only change policy
No breaking changes for consumers: The authorAssociation parameter has a default value in the function signature, so workflows that don't pass it will continue to work with the existing behavior.
🔇 Additional comments (4)
.github/scripts/agents-guard.js (1)
13-14: LGTM!Also applies to: 486-487, 723-727, 741-741
.github/workflows/agents-guard.yml (1)
211-212: LGTM!Also applies to: 425-425
WORKFLOW_USER_GUIDE.md (1)
168-168: LGTM!Also applies to: 853-853
.github/workflows/maint-76-claude-code-review.yml (1)
192-192: LGTM!
| function parseActionReferenceLine(line) { | ||
| const match = String(line || '').match(/^\s*(?:-\s*)?uses:\s*["']?([^@\s#'"]+)@([^\s#'"]+)["']?(?:\s*(?:#.*)?)?$/i); | ||
| if (!match) { | ||
| return null; | ||
| } | ||
|
|
||
| return { | ||
| action: match[1].toLowerCase(), | ||
| ref: match[2], | ||
| }; |
There was a problem hiding this comment.
Reject expression-based refs in dependency-only parsing.
The current regex accepts non-literal refs (for example ${{...}}), which can be treated as dependency-only and bypass CODEOWNER even though that is workflow logic, not a static ref bump.
Suggested hardening
function parseActionReferenceLine(line) {
const match = String(line || '').match(/^\s*(?:-\s*)?uses:\s*["']?([^@\s#'"]+)@([^\s#'"]+)["']?(?:\s*(?:#.*)?)?$/i);
if (!match) {
return null;
}
+ const action = match[1];
+ const ref = match[2];
+ if (ref.includes('${{') || ref.includes('}}')) {
+ return null;
+ }
+
return {
- action: match[1].toLowerCase(),
- ref: match[2],
+ action: action.toLowerCase(),
+ ref,
};
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| function parseActionReferenceLine(line) { | |
| const match = String(line || '').match(/^\s*(?:-\s*)?uses:\s*["']?([^@\s#'"]+)@([^\s#'"]+)["']?(?:\s*(?:#.*)?)?$/i); | |
| if (!match) { | |
| return null; | |
| } | |
| return { | |
| action: match[1].toLowerCase(), | |
| ref: match[2], | |
| }; | |
| function parseActionReferenceLine(line) { | |
| const match = String(line || '').match(/^\s*(?:-\s*)?uses:\s*["']?([^@\s#'"]+)@([^\s#'"]+)["']?(?:\s*(?:#.*)?)?$/i); | |
| if (!match) { | |
| return null; | |
| } | |
| const action = match[1]; | |
| const ref = match[2]; | |
| if (ref.includes('${{') || ref.includes('}}')) { | |
| return null; | |
| } | |
| return { | |
| action: action.toLowerCase(), | |
| ref, | |
| }; | |
| } |
🤖 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 @.github/scripts/agents-guard.js around lines 401 - 410, The regex pattern in
the parseActionReferenceLine function accepts expression-based refs like
${{...}} in the second capture group (the ref part), which should be rejected to
ensure only literal static refs are parsed. Modify the regex pattern to exclude
refs containing expression syntax, or add validation logic after the match to
reject any refs that contain $ or {{ patterns, ensuring that only literal
version tags and commit SHAs are treated as static dependency refs rather than
workflow logic expressions.
| const hasProtectedChanges = modifiedProtectedPaths.size > 0; | ||
| // Security note: Allow `agents:allow-change` label to bypass CODEOWNER approval | ||
| // ONLY for automated dependency PRs from known bots (dependabot, renovate). | ||
| // Human PRs or other bot PRs still require CODEOWNER approval even with label. | ||
| const isAutomatedPR = normalizedAuthor && (normalizedAuthor === 'dependabot[bot]' || normalizedAuthor === 'renovate[bot]'); | ||
| const needsApproval = hasProtectedChanges && !hasCodeownerApproval && !(hasAllowLabel && isAutomatedPR); | ||
| const protectedChangesAreDependencyOnly = hasProtectedChanges && relevantFiles | ||
| .filter((file) => file.status === 'modified' && matchProtectedPath(file.filename || '')) | ||
| .every((file) => patchChangesOnlyActionReferences(file.patch || '')); | ||
| const isDependencyUpdateBot = Boolean( |
There was a problem hiding this comment.
Treat added protected workflows as protected changes.
hasProtectedChanges only tracks status === 'modified', so adding a new .github/workflows/agents-*.yml file can bypass both label and CODEOWNER gating entirely.
Suggested fix
- const hasProtectedChanges = modifiedProtectedPaths.size > 0;
- const protectedChangesAreDependencyOnly = hasProtectedChanges && relevantFiles
- .filter((file) => file.status === 'modified' && matchProtectedPath(file.filename || ''))
- .every((file) => patchChangesOnlyActionReferences(file.patch || ''));
+ const changedProtectedFiles = relevantFiles.filter((file) => {
+ const current = file.filename || '';
+ const previous = file.previous_filename || '';
+ const isProtected = Boolean(
+ matchProtectedPath(current) || (previous ? matchProtectedPath(previous) : null),
+ );
+ return isProtected && (file.status === 'modified' || file.status === 'added');
+ });
+ const hasProtectedChanges = changedProtectedFiles.length > 0;
+ const protectedChangesAreDependencyOnly = hasProtectedChanges && changedProtectedFiles.every(
+ (file) => file.status === 'modified' && patchChangesOnlyActionReferences(file.patch || ''),
+ );📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const hasProtectedChanges = modifiedProtectedPaths.size > 0; | |
| // Security note: Allow `agents:allow-change` label to bypass CODEOWNER approval | |
| // ONLY for automated dependency PRs from known bots (dependabot, renovate). | |
| // Human PRs or other bot PRs still require CODEOWNER approval even with label. | |
| const isAutomatedPR = normalizedAuthor && (normalizedAuthor === 'dependabot[bot]' || normalizedAuthor === 'renovate[bot]'); | |
| const needsApproval = hasProtectedChanges && !hasCodeownerApproval && !(hasAllowLabel && isAutomatedPR); | |
| const protectedChangesAreDependencyOnly = hasProtectedChanges && relevantFiles | |
| .filter((file) => file.status === 'modified' && matchProtectedPath(file.filename || '')) | |
| .every((file) => patchChangesOnlyActionReferences(file.patch || '')); | |
| const isDependencyUpdateBot = Boolean( | |
| const changedProtectedFiles = relevantFiles.filter((file) => { | |
| const current = file.filename || ''; | |
| const previous = file.previous_filename || ''; | |
| const isProtected = Boolean( | |
| matchProtectedPath(current) || (previous ? matchProtectedPath(previous) : null), | |
| ); | |
| return isProtected && (file.status === 'modified' || file.status === 'added'); | |
| }); | |
| const hasProtectedChanges = changedProtectedFiles.length > 0; | |
| const protectedChangesAreDependencyOnly = hasProtectedChanges && changedProtectedFiles.every( | |
| (file) => file.status === 'modified' && patchChangesOnlyActionReferences(file.patch || ''), | |
| ); | |
| const isDependencyUpdateBot = Boolean( |
🤖 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 @.github/scripts/agents-guard.js around lines 616 - 620, The
hasProtectedChanges variable only considers files with status 'modified',
allowing newly added protected workflow files to bypass protection checks.
Update the logic that sets hasProtectedChanges to also include files with status
'added' that match protected paths using matchProtectedPath. Additionally,
update the protectedChangesAreDependencyOnly filter condition to include both
'modified' and 'added' files when checking if protected changes only contain
dependency references via patchChangesOnlyActionReferences.
Sync Summary
Files Updated
Files Skipped
Review Checklist
Source: stranske/Workflows
Source SHA:
5d306dbf622f81baba35684f8f03fdad013da942Template hash:
c6b722d74cfdSync branch:
sync/workflows-c6b722d74cfdConsumer repo:
stranske/Pension-DataManifest:
.github/sync-manifest.ymlSummary by CodeRabbit
Improvements
agents:allow-changelabel is present from trusted authors or dependency bot accounts.Documentation
agents:allow-changelabel, including auto-application for dependency updates.