Conversation
- Auto-move to In Progress when assigned - Auto-label issues based on title keywords - Epic sub-task status sync - Stale issue management (weekly)
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Claude encountered an error —— View job I'll analyze this and get back to you. |
📝 WalkthroughSummary by CodeRabbit
WalkthroughIntroduces four new GitHub Actions workflows to automate repository management tasks: auto-labeling issues based on keywords, syncing epic and subtask statuses, updating project progress on issue state changes, and managing stale issues and pull requests via scheduled cleanup. Changes
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
| runs-on: ubuntu-latest | ||
| steps: | ||
| - name: Label based on title | ||
| uses: actions/github-script@v7 | ||
| with: | ||
| script: | | ||
| const title = context.payload.issue.title.toLowerCase(); | ||
| const labels = []; | ||
|
|
||
| if (title.includes('bug') || title.includes('fix') || title.includes('crash')) { | ||
| labels.push('bug'); | ||
| } else if (title.includes('feat') || title.includes('add') || title.includes('implement')) { | ||
| labels.push('enhancement'); | ||
| } else if (title.includes('docs') || title.includes('documentation')) { | ||
| labels.push('documentation'); | ||
| } else if (title.includes('test') || title.includes('testing')) { | ||
| labels.push('testing'); | ||
| } | ||
|
|
||
| if (labels.length > 0) { | ||
| github.rest.issues.addLabels({ | ||
| owner: context.repo.owner, | ||
| repo: context.repo.repo, | ||
| issue_number: context.issue.number, | ||
| labels: labels | ||
| }); | ||
| } |
Check warning
Code scanning / CodeQL
Workflow does not contain permissions Medium
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 7 days ago
To fix the problem, explicitly define a permissions block for the job (or at the workflow root) that grants only the scopes required. This workflow only needs to read the issue (title) and add labels. Reading the issue is covered by issues: read or broader contents: read, and adding labels requires issues: write. No other scopes (e.g., contents: write) are necessary.
The best minimal fix, without changing functionality, is to add a permissions section under the auto-label job (line 7) specifying contents: read and issues: write. Placing it at the job level keeps the change tightly scoped. Concretely, in .github/workflows/auto-label-issues.yml, under jobs: auto-label:, insert:
permissions:
contents: read
issues: writeindented to align with runs-on. No imports or additional methods are needed; we are only adjusting YAML configuration for GitHub Actions.
| @@ -6,6 +6,9 @@ | ||
| jobs: | ||
| auto-label: | ||
| runs-on: ubuntu-latest | ||
| permissions: | ||
| contents: read | ||
| issues: write | ||
| steps: | ||
| - name: Label based on title | ||
| uses: actions/github-script@v7 |
| runs-on: ubuntu-latest | ||
| if: contains(github.event.issue.body, 'Part of #412') || contains(github.event.issue.body, 'Parent Epic') | ||
| steps: | ||
| - name: Update epic status | ||
| uses: actions/github-script@v7 | ||
| with: | ||
| script: | | ||
| // Find parent epic from issue body | ||
| const body = context.payload.issue.body; | ||
| const epicMatch = body.match(/#(\d+)/); | ||
| if (epicMatch) { | ||
| const epicNumber = epicMatch[1]; | ||
|
|
||
| // Comment on epic about sub-task status change | ||
| const status = context.payload.action === 'closed' ? 'completed' : 'reopened'; | ||
| github.rest.issues.createComment({ | ||
| owner: context.repo.owner, | ||
| repo: context.repo.repo, | ||
| issue_number: epicNumber, | ||
| body: `📊 Sub-task #${context.issue.number} has been **${status}**` | ||
| }); | ||
| } |
Check warning
Code scanning / CodeQL
Workflow does not contain permissions Medium
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 7 days ago
In general, to fix this issue you should add a permissions block either at the root of the workflow (to affect all jobs) or inside the specific job that needs permissions. That block should request only the scopes actually required. This workflow needs to create comments on issues, so it requires issues: write. It doesn’t read or modify repository contents directly, so we can set contents: read as a conservative baseline and avoid broader write permissions.
The best targeted fix without changing behavior is to add a permissions block under the sync-epic-progress job. This keeps the change localized and clearly tied to the job that needs it. Concretely, in .github/workflows/epic-subtask-sync.yml, just below runs-on: ubuntu-latest on line 8, insert:
permissions:
contents: read
issues: writeNo imports or additional methods are needed because this is a YAML configuration change only. The rest of the workflow logic using actions/github-script@v7 remains unchanged.
| @@ -6,6 +6,9 @@ | ||
| jobs: | ||
| sync-epic-progress: | ||
| runs-on: ubuntu-latest | ||
| permissions: | ||
| contents: read | ||
| issues: write | ||
| if: contains(github.event.issue.body, 'Part of #412') || contains(github.event.issue.body, 'Parent Epic') | ||
| steps: | ||
| - name: Update epic status |
| runs-on: ubuntu-latest | ||
| steps: | ||
| - name: Move to In Progress on assignment | ||
| if: github.event_name == 'issues' && github.event.action == 'assigned' | ||
| uses: actions/github-script@v7 | ||
| with: | ||
| script: | | ||
| const projectId = 'PVT_kwDOAnuWxs4BRcst'; | ||
| const statusFieldId = 'PVTSSF_lADOAnuWxs4BRcstzg_RgWc'; | ||
| const inProgressOptionId = '47fc9ee4'; | ||
|
|
||
| // Update issue status in project | ||
| await github.graphql(` | ||
| mutation { | ||
| updateProjectV2ItemFieldValue: updateProjectV2ItemFieldValue( | ||
| input: { | ||
| projectId: "${projectId}" | ||
| itemId: "${{ github.event.issue.node_id }}" | ||
| fieldId: "${statusFieldId}" | ||
| value: { singleSelectOptionId: "${inProgressOptionId}" } | ||
| } | ||
| ) { clientMutationId } | ||
| } | ||
| `); |
Check warning
Code scanning / CodeQL
Workflow does not contain permissions Medium
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 7 days ago
In general, the fix is to add an explicit permissions block to the workflow (either at the root, under name:/on:, or specifically under the update-project-status job) that grants only the minimum required access to GITHUB_TOKEN. This documents the intended privileges and ensures they remain constrained even if repository or organization defaults change.
For this specific workflow, the job uses github.graphql to run a mutation that updates a Project V2 item field. That operation requires permission to write to the project resource (projects: write), but does not require write access to repository contents. The safest, least-privilege change that preserves existing behavior is to add a job-level permissions block under update-project-status: specifying projects: write and, if no other steps require repository contents, leaving out contents: write (GitHub will implicitly allow read access where needed; if you want to be explicit, you can also add contents: read). No imports or additional methods are needed; this is purely a YAML configuration change.
Concretely, update .github/workflows/project-auto-progress.yml so that under jobs: -> update-project-status: and before runs-on: ubuntu-latest, you insert:
permissions:
projects: writeThis ensures that the GITHUB_TOKEN used by actions/github-script@v7 has project write access while avoiding overly broad repository-wide write permissions.
| @@ -8,6 +8,8 @@ | ||
|
|
||
| jobs: | ||
| update-project-status: | ||
| permissions: | ||
| projects: write | ||
| runs-on: ubuntu-latest | ||
| steps: | ||
| - name: Move to In Progress on assignment |
| runs-on: ubuntu-latest | ||
| steps: | ||
| - uses: actions/stale@v9 | ||
| with: | ||
| repo-token: ${{ secrets.GITHUB_TOKEN }} | ||
| stale-issue-message: 'This issue has been inactive for 30 days. Please update or it will be closed in 7 days.' | ||
| stale-pr-message: 'This PR has been inactive for 30 days. Please update or it will be closed in 7 days.' | ||
| close-issue-message: 'Closing due to inactivity. Reopen if still needed.' | ||
| days-before-stale: 30 | ||
| days-before-close: 7 | ||
| exempt-issue-labels: 'epic,in-progress,priority-high' | ||
| exempt-pr-labels: 'in-review' |
Check warning
Code scanning / CodeQL
Workflow does not contain permissions Medium
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 7 days ago
To fix the problem, explicitly declare the minimal required permissions for the GITHUB_TOKEN in this workflow. Since actions/stale needs to read repository contents (to load issues/PRs) and write to issues and pull requests (to label, comment, and close them), we should set contents: read, issues: write, and pull-requests: write. These can be set at the workflow root so they apply to all jobs, which keeps the configuration simple and avoids changing functionality.
Concretely, in .github/workflows/stale-issue-management.yml, add a permissions: block near the top of the file, at the same indentation level as on: and jobs:. No imports or additional methods are needed; this is pure workflow configuration. The existing logic and steps remain unchanged, but the token will now have clearly defined, least‑privilege access.
| @@ -3,6 +3,10 @@ | ||
| schedule: | ||
| - cron: '0 0 * * 1' # Weekly on Monday | ||
| workflow_dispatch: | ||
| permissions: | ||
| contents: read | ||
| issues: write | ||
| pull-requests: write | ||
|
|
||
| jobs: | ||
| stale: |
❌ 9 blocking issues (9 total)
@qltysh one-click actions:
|
| @@ -0,0 +1,34 @@ | |||
| name: Auto-Label Issues | |||
| issue_number: context.issue.number, | ||
| labels: labels | ||
| }); | ||
| } |
| jobs: | ||
| sync-epic-progress: | ||
| runs-on: ubuntu-latest | ||
| if: contains(github.event.issue.body, 'Part of #412') || contains(github.event.issue.body, 'Parent Epic') |
| issue_number: epicNumber, | ||
| body: `📊 Sub-task #${context.issue.number} has been **${status}**` | ||
| }); | ||
| } |
| @@ -0,0 +1,34 @@ | |||
| name: Project Auto-Progress | |||
| updateProjectV2ItemFieldValue: updateProjectV2ItemFieldValue( | ||
| input: { | ||
| projectId: "${projectId}" | ||
| itemId: "${{ github.event.issue.node_id }}" |
| } | ||
| ) { clientMutationId } | ||
| } | ||
| `); |
| @@ -0,0 +1,20 @@ | |||
| name: Stale Issue Management | |||
| days-before-stale: 30 | ||
| days-before-close: 7 | ||
| exempt-issue-labels: 'epic,in-progress,priority-high' | ||
| exempt-pr-labels: 'in-review' |
|
| Filename | Overview |
|---|---|
| .github/workflows/auto-label-issues.yml | Auto-labels new issues by title keyword; missing await on the addLabels API call silently swallows errors. |
| .github/workflows/epic-subtask-sync.yml | Syncs parent epic on sub-task close/reopen; two logic bugs: null body crash and regex matching the wrong issue number if the body has other references. |
| .github/workflows/project-auto-progress.yml | Moves issues to "In Progress" in Project #4 via GraphQL; unused pull_request and reopened triggers waste CI minutes, and ${{ }} interpolation inside the script block is an injection anti-pattern. |
| .github/workflows/stale-issue-management.yml | Weekly stale issue/PR cleanup with reasonable defaults (30-day stale, 7-day close) and appropriate label exemptions; no issues found. |
Sequence Diagram
sequenceDiagram
participant GH as GitHub Event
participant AL as auto-label-issues
participant PP as project-auto-progress
participant ES as epic-subtask-sync
participant SM as stale-issue-management
GH->>AL: issue opened
AL->>AL: match title keywords
AL-->>GH: addLabels (bug/enhancement/docs/testing)
GH->>PP: issue assigned
PP->>GH: GraphQL mutation → Project #4 status = "In Progress"
GH->>ES: issue closed / reopened
ES->>ES: parse body for parent epic #
ES-->>GH: createComment on parent epic
Note over SM: runs weekly (Monday 00:00 UTC)
SM->>GH: mark issues stale after 30d
SM->>GH: close stale issues after 7d
Note over SM: exempt: epic, in-progress, priority-high
Last reviewed commit: 998efab
| // Find parent epic from issue body | ||
| const body = context.payload.issue.body; | ||
| const epicMatch = body.match(/#(\d+)/); | ||
| if (epicMatch) { |
There was a problem hiding this comment.
Regex matches first #number, not the parent epic reference
body.match(/#(\d+)/) matches the first #number occurrence anywhere in the issue body. If the body contains other issue references before the "Part of #…" line (e.g., Fixes #100 or Closes #200), the workflow will post the status comment on that wrong issue rather than the actual parent epic.
Consider anchoring the match to a specific prefix pattern:
| if (epicMatch) { | |
| const epicMatch = body.match(/[Pp]art of #(\d+)/); |
This ensures only the intended parent-epic reference is captured.
Prompt To Fix With AI
This is a comment left during a code review.
Path: .github/workflows/epic-subtask-sync.yml
Line: 18
Comment:
**Regex matches first `#number`, not the parent epic reference**
`body.match(/#(\d+)/)` matches the **first** `#number` occurrence anywhere in the issue body. If the body contains other issue references before the "Part of #…" line (e.g., `Fixes #100` or `Closes #200`), the workflow will post the status comment on that wrong issue rather than the actual parent epic.
Consider anchoring the match to a specific prefix pattern:
```suggestion
const epicMatch = body.match(/[Pp]art of #(\d+)/);
```
This ensures only the intended parent-epic reference is captured.
How can I resolve this? If you propose a fix, please make it concise.| uses: actions/github-script@v7 | ||
| with: | ||
| script: | | ||
| // Find parent epic from issue body |
There was a problem hiding this comment.
Null body causes runtime crash
context.payload.issue.body can be null when an issue is closed without a description. Calling .match() on null will throw TypeError: Cannot read properties of null, causing the entire workflow step to fail.
Add a null-guard before matching:
| // Find parent epic from issue body | |
| const body = context.payload.issue.body || ''; |
Prompt To Fix With AI
This is a comment left during a code review.
Path: .github/workflows/epic-subtask-sync.yml
Line: 15
Comment:
**Null body causes runtime crash**
`context.payload.issue.body` can be `null` when an issue is closed without a description. Calling `.match()` on `null` will throw `TypeError: Cannot read properties of null`, causing the entire workflow step to fail.
Add a null-guard before matching:
```suggestion
const body = context.payload.issue.body || '';
```
How can I resolve this? If you propose a fix, please make it concise.| const epicNumber = epicMatch[1]; | ||
|
|
||
| // Comment on epic about sub-task status change | ||
| const status = context.payload.action === 'closed' ? 'completed' : 'reopened'; |
There was a problem hiding this comment.
Missing await on API call
github.rest.issues.createComment(...) returns a Promise but it is not awaited. If the API call fails (e.g., rate-limit, network error), the error is silently swallowed and the workflow step exits successfully despite the failure.
| const status = context.payload.action === 'closed' ? 'completed' : 'reopened'; | |
| await github.rest.issues.createComment({ |
Prompt To Fix With AI
This is a comment left during a code review.
Path: .github/workflows/epic-subtask-sync.yml
Line: 22
Comment:
**Missing `await` on API call**
`github.rest.issues.createComment(...)` returns a Promise but it is not awaited. If the API call fails (e.g., rate-limit, network error), the error is silently swallowed and the workflow step exits successfully despite the failure.
```suggestion
await github.rest.issues.createComment({
```
How can I resolve this? If you propose a fix, please make it concise.|
|
||
| if (labels.length > 0) { | ||
| github.rest.issues.addLabels({ | ||
| owner: context.repo.owner, |
There was a problem hiding this comment.
Missing await on API call
github.rest.issues.addLabels(...) is not awaited. Errors (e.g., non-existent label, permission failure) will be silently swallowed and the step will appear to succeed.
| owner: context.repo.owner, | |
| await github.rest.issues.addLabels({ |
Prompt To Fix With AI
This is a comment left during a code review.
Path: .github/workflows/auto-label-issues.yml
Line: 29
Comment:
**Missing `await` on API call**
`github.rest.issues.addLabels(...)` is not awaited. Errors (e.g., non-existent label, permission failure) will be silently swallowed and the step will appear to succeed.
```suggestion
await github.rest.issues.addLabels({
```
How can I resolve this? If you propose a fix, please make it concise.| on: | ||
| issues: | ||
| types: [assigned, reopened] | ||
| pull_request: | ||
| types: [opened] | ||
| branches: [main, develop] | ||
|
|
There was a problem hiding this comment.
pull_request: opened trigger fires but no step handles it
The workflow declares a pull_request: types: [opened] trigger, but the only step inside the job is guarded by if: github.event_name == 'issues' && github.event.action == 'assigned'. When a PR is opened, the job is queued, consumes a runner, and then immediately exits because the condition is false — no work is done.
Similarly, the issues: reopened event is declared but there is no step that handles it either.
Either add the missing steps for these events or remove the unused triggers:
| on: | |
| issues: | |
| types: [assigned, reopened] | |
| pull_request: | |
| types: [opened] | |
| branches: [main, develop] | |
| on: | |
| issues: | |
| types: [assigned] |
Prompt To Fix With AI
This is a comment left during a code review.
Path: .github/workflows/project-auto-progress.yml
Line: 2-8
Comment:
**`pull_request: opened` trigger fires but no step handles it**
The workflow declares a `pull_request: types: [opened]` trigger, but the only step inside the job is guarded by `if: github.event_name == 'issues' && github.event.action == 'assigned'`. When a PR is opened, the job is queued, consumes a runner, and then immediately exits because the condition is false — no work is done.
Similarly, the `issues: reopened` event is declared but there is no step that handles it either.
Either add the missing steps for these events or remove the unused triggers:
```suggestion
on:
issues:
types: [assigned]
```
How can I resolve this? If you propose a fix, please make it concise.| with: | ||
| script: | | ||
| const projectId = 'PVT_kwDOAnuWxs4BRcst'; | ||
| const statusFieldId = 'PVTSSF_lADOAnuWxs4BRcstzg_RgWc'; | ||
| const inProgressOptionId = '47fc9ee4'; | ||
|
|
||
| // Update issue status in project | ||
| await github.graphql(` | ||
| mutation { | ||
| updateProjectV2ItemFieldValue: updateProjectV2ItemFieldValue( | ||
| input: { | ||
| projectId: "${projectId}" | ||
| itemId: "${{ github.event.issue.node_id }}" | ||
| fieldId: "${statusFieldId}" | ||
| value: { singleSelectOptionId: "${inProgressOptionId}" } | ||
| } | ||
| ) { clientMutationId } | ||
| } | ||
| `); |
There was a problem hiding this comment.
Script injection via ${{ }} expression inside script:
Interpolating ${{ github.event.issue.node_id }} directly into the GraphQL mutation string is a script-injection anti-pattern. Although GitHub-generated node IDs are safe today, the GitHub Actions security hardening guide explicitly recommends passing context values through environment variables to avoid any injection risk.
| with: | |
| script: | | |
| const projectId = 'PVT_kwDOAnuWxs4BRcst'; | |
| const statusFieldId = 'PVTSSF_lADOAnuWxs4BRcstzg_RgWc'; | |
| const inProgressOptionId = '47fc9ee4'; | |
| // Update issue status in project | |
| await github.graphql(` | |
| mutation { | |
| updateProjectV2ItemFieldValue: updateProjectV2ItemFieldValue( | |
| input: { | |
| projectId: "${projectId}" | |
| itemId: "${{ github.event.issue.node_id }}" | |
| fieldId: "${statusFieldId}" | |
| value: { singleSelectOptionId: "${inProgressOptionId}" } | |
| } | |
| ) { clientMutationId } | |
| } | |
| `); | |
| with: | |
| github-token: ${{ secrets.GITHUB_TOKEN }} | |
| script: | | |
| const projectId = 'PVT_kwDOAnuWxs4BRcst'; | |
| const statusFieldId = 'PVTSSF_lADOAnuWxs4BRcstzg_RgWc'; | |
| const inProgressOptionId = '47fc9ee4'; | |
| const itemId = process.env.ITEM_ID; | |
| await github.graphql(` | |
| mutation { | |
| updateProjectV2ItemFieldValue: updateProjectV2ItemFieldValue( | |
| input: { | |
| projectId: "${projectId}" | |
| itemId: "${itemId}" | |
| fieldId: "${statusFieldId}" | |
| value: { singleSelectOptionId: "${inProgressOptionId}" } | |
| } | |
| ) { clientMutationId } | |
| } | |
| `); | |
| env: | |
| ITEM_ID: ${{ github.event.issue.node_id }} |
Prompt To Fix With AI
This is a comment left during a code review.
Path: .github/workflows/project-auto-progress.yml
Line: 16-34
Comment:
**Script injection via `${{ }}` expression inside `script:`**
Interpolating `${{ github.event.issue.node_id }}` directly into the GraphQL mutation string is a script-injection anti-pattern. Although GitHub-generated node IDs are safe today, the GitHub Actions security hardening guide explicitly recommends passing context values through environment variables to avoid any injection risk.
```suggestion
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const projectId = 'PVT_kwDOAnuWxs4BRcst';
const statusFieldId = 'PVTSSF_lADOAnuWxs4BRcstzg_RgWc';
const inProgressOptionId = '47fc9ee4';
const itemId = process.env.ITEM_ID;
await github.graphql(`
mutation {
updateProjectV2ItemFieldValue: updateProjectV2ItemFieldValue(
input: {
projectId: "${projectId}"
itemId: "${itemId}"
fieldId: "${statusFieldId}"
value: { singleSelectOptionId: "${inProgressOptionId}" }
}
) { clientMutationId }
}
`);
env:
ITEM_ID: ${{ github.event.issue.node_id }}
```
How can I resolve this? If you propose a fix, please make it concise.There was a problem hiding this comment.
Actionable comments posted: 7
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In @.github/workflows/auto-label-issues.yml:
- Around line 17-25: The current title checks using title.includes(...) cause
false positives; update the conditional matching in the auto-labeling logic (the
checks referencing title and the includes(...) calls) to use whole-word,
case-insensitive matching (e.g., test with a word-boundary regex like /\bfix\b/i
or split the title into tokens) for each label category so substrings like
"contest", "fixed width", or "additional" do not trigger the wrong label;
replace each title.includes(...) condition with a proper word-boundary regex
test or token lookup for 'bug'/'fix'/'crash', 'feat'/'add'/'implement',
'docs'/'documentation', and 'test'/'testing'.
- Around line 6-34: Add an explicit permissions block granting write access to
issues and await the asynchronous GitHub API call: update the workflow job
"auto-label" (the step using actions/github-script@v7) to include permissions: {
issues: write } at the job or workflow level, and change the call to
github.rest.issues.addLabels(...) to await github.rest.issues.addLabels(...) so
the labels are applied before the step completes.
In @.github/workflows/epic-subtask-sync.yml:
- Around line 14-28: The script calls context.payload.issue.body.match(...)
without guarding against a null body and calls the async GitHub API
github.rest.issues.createComment(...) without awaiting it; fix by first checking
that context.payload.issue && context.payload.issue.body is non-null (or coerce
to empty string) before calling .match, only proceed if epicMatch is truthy, and
add await before github.rest.issues.createComment so the Promise is handled;
also add a workflow permissions block with "permissions: issues: write" to allow
creating comments.
- Line 9: The conditional expression using contains(github.event.issue.body,
'Part of `#412`') is failing due to the unescaped '#' being parsed as a YAML
comment; update the if condition to quote and escape the hash (e.g., use double
quotes and escape the pound sign) and add a null-safe fallback for issue.body
(use contains(github.event.issue.body || '', "Part of \#412") or similar) so
actionlint/YAML parsing succeeds; locate the contains(...) expression in the if
line and apply the quoted/escaped string and the || '' fallback, and optionally
generalize the pattern instead of hardcoding "#412".
In @.github/workflows/project-auto-progress.yml:
- Around line 5-7: The workflow defines a pull_request trigger but no job
handles PR events, so either remove the "pull_request:" trigger block or add a
job/step that runs when github.event_name == 'pull_request' (or update the
existing step condition from github.event_name == 'issues' to include
pull_request); locate the trigger named "pull_request" and the step condition
using "github.event_name == 'issues'" and either delete the pull_request trigger
or change the condition to handle 'pull_request' (or use a combined conditional
like github.event_name == 'issues' || github.event_name == 'pull_request') so PR
events actually execute a step.
- Around line 17-34: The mutation is using github.event.issue.node_id as itemId
which is incorrect; first query the ProjectV2Item ID for the issue (use the
issue's projectItems connection or call addProjectV2ItemById) and then pass that
PVTI_... ID into updateProjectV2ItemFieldValue (refer to
updateProjectV2ItemFieldValue, github.event.issue.node_id, projectItems,
addProjectV2ItemById to locate the code). Also replace the default GITHUB_TOKEN
or ensure you use a token with Projects V2 write scope (use a PAT or a GitHub
App installation token and store it in secrets) because the permissions:
projects: write block won’t grant Projects V2 mutation rights.
In @.github/workflows/stale-issue-management.yml:
- Around line 7-20: The workflow's stale job using actions/stale@v9 needs
explicit least-privilege permissions and should stop passing the deprecated
repo-token; add a top-level permissions block granting only issues: write and
pull-requests: write (or issues: write and checks: write if PR write isn’t
supported) and remove the repo-token: ${{ secrets.GITHUB_TOKEN }} line from the
stale step's with: block so actions/stale uses the default GITHUB_TOKEN with the
specified limited permissions.
🪄 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
Run ID: e4336414-7e3c-4c07-b363-849ff6976d23
📒 Files selected for processing (4)
.github/workflows/auto-label-issues.yml.github/workflows/epic-subtask-sync.yml.github/workflows/project-auto-progress.yml.github/workflows/stale-issue-management.yml
| jobs: | ||
| auto-label: | ||
| runs-on: ubuntu-latest | ||
| steps: | ||
| - name: Label based on title | ||
| uses: actions/github-script@v7 | ||
| with: | ||
| script: | | ||
| const title = context.payload.issue.title.toLowerCase(); | ||
| const labels = []; | ||
|
|
||
| if (title.includes('bug') || title.includes('fix') || title.includes('crash')) { | ||
| labels.push('bug'); | ||
| } else if (title.includes('feat') || title.includes('add') || title.includes('implement')) { | ||
| labels.push('enhancement'); | ||
| } else if (title.includes('docs') || title.includes('documentation')) { | ||
| labels.push('documentation'); | ||
| } else if (title.includes('test') || title.includes('testing')) { | ||
| labels.push('testing'); | ||
| } | ||
|
|
||
| if (labels.length > 0) { | ||
| github.rest.issues.addLabels({ | ||
| owner: context.repo.owner, | ||
| repo: context.repo.repo, | ||
| issue_number: context.issue.number, | ||
| labels: labels | ||
| }); | ||
| } |
There was a problem hiding this comment.
Add permissions block and await the async API call.
The addLabels call is async and should be awaited to ensure labels are applied before the workflow completes. Also add explicit permissions.
🐛 Proposed fix
on:
issues:
types: [opened]
+permissions:
+ issues: write
+
jobs:
auto-label:
runs-on: ubuntu-latest
steps:
- name: Label based on title
uses: actions/github-script@v7
with:
script: |
const title = context.payload.issue.title.toLowerCase();
const labels = [];
if (title.includes('bug') || title.includes('fix') || title.includes('crash')) {
labels.push('bug');
} else if (title.includes('feat') || title.includes('add') || title.includes('implement')) {
labels.push('enhancement');
} else if (title.includes('docs') || title.includes('documentation')) {
labels.push('documentation');
} else if (title.includes('test') || title.includes('testing')) {
labels.push('testing');
}
if (labels.length > 0) {
- github.rest.issues.addLabels({
+ await github.rest.issues.addLabels({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
labels: labels
});
}🧰 Tools
🪛 GitHub Check: CodeQL
[warning] 8-34: Workflow does not contain permissions
Actions job or workflow does not limit the permissions of the GITHUB_TOKEN. Consider setting an explicit permissions block, using the following as a minimal starting point: {{}}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In @.github/workflows/auto-label-issues.yml around lines 6 - 34, Add an explicit
permissions block granting write access to issues and await the asynchronous
GitHub API call: update the workflow job "auto-label" (the step using
actions/github-script@v7) to include permissions: { issues: write } at the job
or workflow level, and change the call to github.rest.issues.addLabels(...) to
await github.rest.issues.addLabels(...) so the labels are applied before the
step completes.
| if (title.includes('bug') || title.includes('fix') || title.includes('crash')) { | ||
| labels.push('bug'); | ||
| } else if (title.includes('feat') || title.includes('add') || title.includes('implement')) { | ||
| labels.push('enhancement'); | ||
| } else if (title.includes('docs') || title.includes('documentation')) { | ||
| labels.push('documentation'); | ||
| } else if (title.includes('test') || title.includes('testing')) { | ||
| labels.push('testing'); | ||
| } |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Consider false positive risks with substring matching.
Using includes() may produce unintended matches:
- "contest" matches "test" → gets
testinglabel - "fixed width" matches "fix" → gets
buglabel - "additional" matches "add" → gets
enhancementlabel
💡 Optional: Use word boundary regex for more precise matching
- if (title.includes('bug') || title.includes('fix') || title.includes('crash')) {
+ if (/\b(bug|fix|crash)\b/.test(title)) {
labels.push('bug');
- } else if (title.includes('feat') || title.includes('add') || title.includes('implement')) {
+ } else if (/\b(feat|feature|add|implement)\b/.test(title)) {
labels.push('enhancement');
- } else if (title.includes('docs') || title.includes('documentation')) {
+ } else if (/\b(docs|documentation)\b/.test(title)) {
labels.push('documentation');
- } else if (title.includes('test') || title.includes('testing')) {
+ } else if (/\b(test|testing)\b/.test(title)) {
labels.push('testing');
}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In @.github/workflows/auto-label-issues.yml around lines 17 - 25, The current
title checks using title.includes(...) cause false positives; update the
conditional matching in the auto-labeling logic (the checks referencing title
and the includes(...) calls) to use whole-word, case-insensitive matching (e.g.,
test with a word-boundary regex like /\bfix\b/i or split the title into tokens)
for each label category so substrings like "contest", "fixed width", or
"additional" do not trigger the wrong label; replace each title.includes(...)
condition with a proper word-boundary regex test or token lookup for
'bug'/'fix'/'crash', 'feat'/'add'/'implement', 'docs'/'documentation', and
'test'/'testing'.
| jobs: | ||
| sync-epic-progress: | ||
| runs-on: ubuntu-latest | ||
| if: contains(github.event.issue.body, 'Part of #412') || contains(github.event.issue.body, 'Parent Epic') |
There was a problem hiding this comment.
YAML syntax error: # character is interpreted as comment start.
The actionlint error confirms that the # in 'Part of #412' causes a YAML parsing issue. The string needs proper quoting to prevent # from being treated as a comment delimiter.
🐛 Proposed fix using double quotes and escaping
- if: contains(github.event.issue.body, 'Part of `#412`') || contains(github.event.issue.body, 'Parent Epic')
+ if: contains(github.event.issue.body || '', 'Part of #') || contains(github.event.issue.body || '', 'Parent Epic')Note: Also added || '' fallback since issue.body can be null, which would cause contains() to fail.
Consider making the pattern more generic rather than hardcoding #412.
📝 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.
| if: contains(github.event.issue.body, 'Part of #412') || contains(github.event.issue.body, 'Parent Epic') | |
| if: contains(github.event.issue.body || '', 'Part of #') || contains(github.event.issue.body || '', 'Parent Epic') |
🧰 Tools
🪛 actionlint (1.7.11)
[error] 9-9: got unexpected EOF while lexing end of string literal, expecting '''
(expression)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In @.github/workflows/epic-subtask-sync.yml at line 9, The conditional
expression using contains(github.event.issue.body, 'Part of `#412`') is failing
due to the unescaped '#' being parsed as a YAML comment; update the if condition
to quote and escape the hash (e.g., use double quotes and escape the pound sign)
and add a null-safe fallback for issue.body (use
contains(github.event.issue.body || '', "Part of \#412") or similar) so
actionlint/YAML parsing succeeds; locate the contains(...) expression in the if
line and apply the quoted/escaped string and the || '' fallback, and optionally
generalize the pattern instead of hardcoding "#412".
| script: | | ||
| // Find parent epic from issue body | ||
| const body = context.payload.issue.body; | ||
| const epicMatch = body.match(/#(\d+)/); | ||
| if (epicMatch) { | ||
| const epicNumber = epicMatch[1]; | ||
|
|
||
| // Comment on epic about sub-task status change | ||
| const status = context.payload.action === 'closed' ? 'completed' : 'reopened'; | ||
| github.rest.issues.createComment({ | ||
| owner: context.repo.owner, | ||
| repo: context.repo.repo, | ||
| issue_number: epicNumber, | ||
| body: `📊 Sub-task #${context.issue.number} has been **${status}**` | ||
| }); |
There was a problem hiding this comment.
Missing await on async API call and potential null reference.
github.rest.issues.createComment()returns a Promise that should be awaited.context.payload.issue.bodycan benullif no body was provided, causing.match()to throw.
🐛 Proposed fix
script: |
// Find parent epic from issue body
- const body = context.payload.issue.body;
+ const body = context.payload.issue.body || '';
const epicMatch = body.match(/#(\d+)/);
if (epicMatch) {
const epicNumber = epicMatch[1];
// Comment on epic about sub-task status change
const status = context.payload.action === 'closed' ? 'completed' : 'reopened';
- github.rest.issues.createComment({
+ await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: epicNumber,
body: `📊 Sub-task #${context.issue.number} has been **${status}**`
});
}Also add permissions block:
permissions:
issues: write🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In @.github/workflows/epic-subtask-sync.yml around lines 14 - 28, The script
calls context.payload.issue.body.match(...) without guarding against a null body
and calls the async GitHub API github.rest.issues.createComment(...) without
awaiting it; fix by first checking that context.payload.issue &&
context.payload.issue.body is non-null (or coerce to empty string) before
calling .match, only proceed if epicMatch is truthy, and add await before
github.rest.issues.createComment so the Promise is handled; also add a workflow
permissions block with "permissions: issues: write" to allow creating comments.
| pull_request: | ||
| types: [opened] | ||
| branches: [main, develop] |
There was a problem hiding this comment.
PR trigger defined but no corresponding step handles it.
The workflow triggers on pull_request events (lines 5-7), but the only step has a condition github.event_name == 'issues' (line 14), so PR events will trigger the workflow but execute nothing.
Either add a step to handle PR events or remove the unused trigger.
🛠️ Option A: Remove unused PR trigger
on:
issues:
types: [assigned, reopened]
- pull_request:
- types: [opened]
- branches: [main, develop]📝 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.
| pull_request: | |
| types: [opened] | |
| branches: [main, develop] | |
| on: | |
| issues: | |
| types: [assigned, reopened] |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In @.github/workflows/project-auto-progress.yml around lines 5 - 7, The workflow
defines a pull_request trigger but no job handles PR events, so either remove
the "pull_request:" trigger block or add a job/step that runs when
github.event_name == 'pull_request' (or update the existing step condition from
github.event_name == 'issues' to include pull_request); locate the trigger named
"pull_request" and the step condition using "github.event_name == 'issues'" and
either delete the pull_request trigger or change the condition to handle
'pull_request' (or use a combined conditional like github.event_name == 'issues'
|| github.event_name == 'pull_request') so PR events actually execute a step.
| script: | | ||
| const projectId = 'PVT_kwDOAnuWxs4BRcst'; | ||
| const statusFieldId = 'PVTSSF_lADOAnuWxs4BRcstzg_RgWc'; | ||
| const inProgressOptionId = '47fc9ee4'; | ||
|
|
||
| // Update issue status in project | ||
| await github.graphql(` | ||
| mutation { | ||
| updateProjectV2ItemFieldValue: updateProjectV2ItemFieldValue( | ||
| input: { | ||
| projectId: "${projectId}" | ||
| itemId: "${{ github.event.issue.node_id }}" | ||
| fieldId: "${statusFieldId}" | ||
| value: { singleSelectOptionId: "${inProgressOptionId}" } | ||
| } | ||
| ) { clientMutationId } | ||
| } | ||
| `); |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
GitHub Actions permissions required for Project V2 GraphQL mutations
💡 Result:
For Projects (Project V2) GraphQL mutations (e.g., addProjectV2ItemById, updateProjectV2ItemFieldValue) you can’t enable the needed access just by adding a permissions: block for GITHUB_TOKEN—those workflow permissions (like repository-projects) are not the Projects V2 auth scope and historically map to “projects (classic)” style permissions. [3], [4]
What you need instead
- Read-only Projects V2 queries: a token with
read:projectscope. [1] - Projects V2 queries + mutations (write): a token with
projectscope. [1], [2]
In GitHub Actions, that means you typically must provide a PAT (classic PAT with project, or a fine-grained PAT that includes Projects: Read and write) or use a GitHub App installation token that has Projects (organization/user) write permission, because the default GITHUB_TOKEN does not grant the project scope required for Projects V2 mutations. [1], [2]
Practical workflow shape
- Store the PAT (or App token) in a secret (e.g.
PROJECT_TOKEN) - Use it as
GH_TOKEN/GITHUB_TOKENfor your GraphQL call step (Octokit/gh api graphql/curl). [1], [2]
Sources: GitHub docs for Projects API scopes (read:project vs project) [1], GitHub blog noting the project scope requirement and that Actions must supply such a token separately [2], and GitHub Actions token-permissions material showing repository-projects is a different permission surface (not the Projects V2 project scope). [3], [4]
References:
[1] https://docs.github.com/enterprise-cloud%40latest/issues/planning-and-tracking-with-projects/automating-your-project/using-the-api-to-manage-projects
[2] https://github.blog/developer-skills/github/github-cli-project-command-is-now-generally-available/
[3] https://github.blog/changelog/2021-04-20-github-actions-control-permissions-for-github_token/
[4] https://github.com/orgs/community/discussions/54538
🌐 Web query:
GitHub GraphQL updateProjectV2ItemFieldValue itemId parameter issue node_id
💡 Result:
updateProjectV2ItemFieldValue’s itemId is not the Issue’s node_id. It must be the ProjectV2Item node ID (typically starts with PVTI_...). (docs.github.com)
How to get the right itemId from an Issue node_id:
- Add the Issue to the Project (or “add” it again) and read the returned Project item id:
mutation($projectId:ID!, $contentId:ID!) {
addProjectV2ItemById(input:{ projectId:$projectId, contentId:$contentId }) {
item { id }
}
}contentId= the Issuenode_id- Response
item.id= theitemIdyou must use - If the Issue is already in the Project, GitHub returns the existing item id. (docs.github.com)
- Then update the field value using that
itemId:
mutation($projectId:ID!, $itemId:ID!, $fieldId:ID!) {
updateProjectV2ItemFieldValue(input:{
projectId:$projectId, itemId:$itemId, fieldId:$fieldId, value:{ text:"Updated text" }
}) {
projectV2Item { id }
}
}You can’t add and update the item in the same call. (docs.github.com)
Citations:
- 1: https://docs.github.com/enterprise-cloud%40latest/issues/planning-and-tracking-with-projects/automating-your-project/using-the-api-to-manage-projects
- 2: https://docs.github.com/enterprise-cloud%40latest/issues/planning-and-tracking-with-projects/automating-your-project/using-the-api-to-manage-projects
- 3: https://docs.github.com/enterprise-cloud%40latest/issues/planning-and-tracking-with-projects/automating-your-project/using-the-api-to-manage-projects
Critical: Using issue node_id instead of project item ID will cause mutation to fail.
The GraphQL mutation uses github.event.issue.node_id as the itemId, but this is the issue's node ID, not the project item ID. The updateProjectV2ItemFieldValue mutation requires a ProjectV2Item ID (typically starts with PVTI_...).
You must first query for the project item ID associated with this issue using addProjectV2ItemById or by querying the issue's projectItems connection.
🐛 Proposed fix: Query for project item ID first
script: |
const projectId = 'PVT_kwDOAnuWxs4BRcst';
const statusFieldId = 'PVTSSF_lADOAnuWxs4BRcstzg_RgWc';
const inProgressOptionId = '47fc9ee4';
+ const issueNodeId = '${{ github.event.issue.node_id }}';
- // Update issue status in project
+ // First, find or create the project item for this issue
+ const addResult = await github.graphql(`
+ mutation($projectId: ID!, $contentId: ID!) {
+ addProjectV2ItemById(input: {
+ projectId: $projectId
+ contentId: $contentId
+ }) {
+ item { id }
+ }
+ }
+ `, { projectId: projectId, contentId: issueNodeId });
+
+ const itemId = addResult.addProjectV2ItemById.item.id;
+
+ // Update issue status in project
await github.graphql(`
mutation {
updateProjectV2ItemFieldValue: updateProjectV2ItemFieldValue(
input: {
projectId: "${projectId}"
- itemId: "${{ github.event.issue.node_id }}"
+ itemId: "${itemId}"
fieldId: "${statusFieldId}"
value: { singleSelectOptionId: "${inProgressOptionId}" }
}
) { clientMutationId }
}
`);Additionally, the workflow requires a token with Projects V2 write access, but the default GITHUB_TOKEN does not have the project scope required for Projects V2 mutations. You must use a Personal Access Token (PAT with project scope) or a GitHub App installation token with Projects write permission instead. The permissions: projects: write block is for classic projects and will not enable Projects V2 mutations.
🧰 Tools
🪛 GitHub Check: CodeQL
[warning] 11-34: Workflow does not contain permissions
Actions job or workflow does not limit the permissions of the GITHUB_TOKEN. Consider setting an explicit permissions block, using the following as a minimal starting point: {{}}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In @.github/workflows/project-auto-progress.yml around lines 17 - 34, The
mutation is using github.event.issue.node_id as itemId which is incorrect; first
query the ProjectV2Item ID for the issue (use the issue's projectItems
connection or call addProjectV2ItemById) and then pass that PVTI_... ID into
updateProjectV2ItemFieldValue (refer to updateProjectV2ItemFieldValue,
github.event.issue.node_id, projectItems, addProjectV2ItemById to locate the
code). Also replace the default GITHUB_TOKEN or ensure you use a token with
Projects V2 write scope (use a PAT or a GitHub App installation token and store
it in secrets) because the permissions: projects: write block won’t grant
Projects V2 mutation rights.
| jobs: | ||
| stale: | ||
| runs-on: ubuntu-latest | ||
| steps: | ||
| - uses: actions/stale@v9 | ||
| with: | ||
| repo-token: ${{ secrets.GITHUB_TOKEN }} | ||
| stale-issue-message: 'This issue has been inactive for 30 days. Please update or it will be closed in 7 days.' | ||
| stale-pr-message: 'This PR has been inactive for 30 days. Please update or it will be closed in 7 days.' | ||
| close-issue-message: 'Closing due to inactivity. Reopen if still needed.' | ||
| days-before-stale: 30 | ||
| days-before-close: 7 | ||
| exempt-issue-labels: 'epic,in-progress,priority-high' | ||
| exempt-pr-labels: 'in-review' |
There was a problem hiding this comment.
Add explicit permissions block for security hardening.
The workflow uses actions/stale@v9 which requires write access to issues and pull requests. Adding explicit permissions follows the principle of least privilege and addresses the CodeQL warning.
Also, repo-token is deprecated in newer versions of actions/stale; the action now uses GITHUB_TOKEN by default.
🔒 Proposed fix to add permissions and remove deprecated parameter
on:
schedule:
- cron: '0 0 * * 1' # Weekly on Monday
workflow_dispatch:
+permissions:
+ issues: write
+ pull-requests: write
+
jobs:
stale:
runs-on: ubuntu-latest
steps:
- uses: actions/stale@v9
with:
- repo-token: ${{ secrets.GITHUB_TOKEN }}
stale-issue-message: 'This issue has been inactive for 30 days. Please update or it will be closed in 7 days.'🧰 Tools
🪛 GitHub Check: CodeQL
[warning] 9-20: Workflow does not contain permissions
Actions job or workflow does not limit the permissions of the GITHUB_TOKEN. Consider setting an explicit permissions block, using the following as a minimal starting point: {{contents: write, issues: write, pull-requests: write}}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In @.github/workflows/stale-issue-management.yml around lines 7 - 20, The
workflow's stale job using actions/stale@v9 needs explicit least-privilege
permissions and should stop passing the deprecated repo-token; add a top-level
permissions block granting only issues: write and pull-requests: write (or
issues: write and checks: write if PR write isn’t supported) and remove the
repo-token: ${{ secrets.GITHUB_TOKEN }} line from the stale step's with: block
so actions/stale uses the default GITHUB_TOKEN with the specified limited
permissions.
There was a problem hiding this comment.
Pull request overview
Adds GitHub Actions automation to manage Sobers v2 project workflow hygiene (auto-progress, auto-labeling, epic/subtask notifications, and stale triage) to support issue/PR tracking for Project #4 and the Sobers Buddy epic (#412).
Changes:
- Add a scheduled stale-management workflow for issues/PRs.
- Add issue-title-based auto-labeling on issue open.
- Add workflows to auto-progress items and to notify/sync epic status when subtasks change.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 10 comments.
| File | Description |
|---|---|
| .github/workflows/stale-issue-management.yml | Scheduled stale triage/closure with label-based exemptions. |
| .github/workflows/project-auto-progress.yml | Attempts to move assigned issues into “In Progress” in Project V2. |
| .github/workflows/epic-subtask-sync.yml | On subtask close/reopen, identifies epic and posts an update comment. |
| .github/workflows/auto-label-issues.yml | Applies labels based on issue title keywords on issue creation. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| if: contains(github.event.issue.body, 'Part of #412') || contains(github.event.issue.body, 'Parent Epic') | ||
| steps: | ||
| - name: Update epic status | ||
| uses: actions/github-script@v7 | ||
| with: | ||
| script: | | ||
| // Find parent epic from issue body | ||
| const body = context.payload.issue.body; | ||
| const epicMatch = body.match(/#(\d+)/); | ||
| if (epicMatch) { | ||
| const epicNumber = epicMatch[1]; |
There was a problem hiding this comment.
The parent epic detection is hard-coded to Part of #412 and then uses body.match(/#(\d+)/) (first issue reference in the body). This can easily pick the wrong issue if the body contains other #123 references and makes the workflow non-reusable for future epics. Prefer parsing a specific marker like Part of #(\d+)/Parent Epic: #(\d+) and using the captured epic number.
| const status = context.payload.action === 'closed' ? 'completed' : 'reopened'; | ||
| github.rest.issues.createComment({ | ||
| owner: context.repo.owner, | ||
| repo: context.repo.repo, | ||
| issue_number: epicNumber, | ||
| body: `📊 Sub-task #${context.issue.number} has been **${status}**` | ||
| }); |
There was a problem hiding this comment.
The REST call to github.rest.issues.createComment(...) is not awaited/returned. If the request fails, the workflow may still report success or behave inconsistently. Use await (or return) so failures surface and execution waits for the API call to complete.
| if (labels.length > 0) { | ||
| github.rest.issues.addLabels({ | ||
| owner: context.repo.owner, | ||
| repo: context.repo.repo, | ||
| issue_number: context.issue.number, | ||
| labels: labels | ||
| }); |
There was a problem hiding this comment.
The REST call to github.rest.issues.addLabels(...) is not awaited/returned. If the API call fails (missing label, permission issue, rate limit), the workflow can still succeed silently. Use await (or return) so failures are surfaced and the step waits for completion.
|
|
||
| jobs: | ||
| stale: | ||
| runs-on: ubuntu-latest |
There was a problem hiding this comment.
actions/stale needs write permissions to apply labels/comments and close items. Add an explicit permissions: block (e.g., issues: write and pull-requests: write if PRs are included) so the workflow doesn't depend on repository-wide default token permissions.
| runs-on: ubuntu-latest | |
| runs-on: ubuntu-latest | |
| permissions: | |
| issues: write | |
| pull-requests: write |
| jobs: | ||
| sync-epic-progress: | ||
| runs-on: ubuntu-latest | ||
| if: contains(github.event.issue.body, 'Part of #412') || contains(github.event.issue.body, 'Parent Epic') | ||
| steps: | ||
| - name: Update epic status | ||
| uses: actions/github-script@v7 |
There was a problem hiding this comment.
This workflow creates comments via the GitHub API; add an explicit permissions: block (at least issues: write) so it works when default GITHUB_TOKEN permissions are read-only/restricted.
|
|
||
| // Update issue status in project | ||
| await github.graphql(` | ||
| mutation { | ||
| updateProjectV2ItemFieldValue: updateProjectV2ItemFieldValue( | ||
| input: { | ||
| projectId: "${projectId}" | ||
| itemId: "${{ github.event.issue.node_id }}" | ||
| fieldId: "${statusFieldId}" | ||
| value: { singleSelectOptionId: "${inProgressOptionId}" } | ||
| } | ||
| ) { clientMutationId } | ||
| } | ||
| `); |
There was a problem hiding this comment.
updateProjectV2ItemFieldValue expects a ProjectV2Item ID for itemId, but this uses the issue's node_id (content ID). This mutation will fail unless you first add the issue to the project (e.g., addProjectV2ItemById) or query the project's item by contentId and use that item ID.
| // Update issue status in project | |
| await github.graphql(` | |
| mutation { | |
| updateProjectV2ItemFieldValue: updateProjectV2ItemFieldValue( | |
| input: { | |
| projectId: "${projectId}" | |
| itemId: "${{ github.event.issue.node_id }}" | |
| fieldId: "${statusFieldId}" | |
| value: { singleSelectOptionId: "${inProgressOptionId}" } | |
| } | |
| ) { clientMutationId } | |
| } | |
| `); | |
| // Get the issue's node ID from the event payload | |
| const issueNodeId = context.payload.issue.node_id; | |
| // Ensure the issue is in the project and get the ProjectV2Item ID | |
| const addItemResult = await github.graphql( | |
| ` | |
| mutation ($projectId: ID!, $contentId: ID!) { | |
| addProjectV2ItemById( | |
| input: { projectId: $projectId, contentId: $contentId } | |
| ) { | |
| item { | |
| id | |
| } | |
| } | |
| } | |
| `, | |
| { | |
| projectId, | |
| contentId: issueNodeId, | |
| } | |
| ); | |
| const itemId = addItemResult.addProjectV2ItemById.item.id; | |
| // Update issue status in project | |
| await github.graphql( | |
| ` | |
| mutation ( | |
| $projectId: ID! | |
| $itemId: ID! | |
| $fieldId: ID! | |
| $optionId: String! | |
| ) { | |
| updateProjectV2ItemFieldValue( | |
| input: { | |
| projectId: $projectId | |
| itemId: $itemId | |
| fieldId: $fieldId | |
| value: { singleSelectOptionId: $optionId } | |
| } | |
| ) { | |
| clientMutationId | |
| } | |
| } | |
| `, | |
| { | |
| projectId, | |
| itemId, | |
| fieldId: statusFieldId, | |
| optionId: inProgressOptionId, | |
| } | |
| ); |
| issues: | ||
| types: [assigned, reopened] | ||
| pull_request: | ||
| types: [opened] | ||
| branches: [main, develop] | ||
|
|
||
| jobs: | ||
| update-project-status: | ||
| runs-on: ubuntu-latest | ||
| steps: | ||
| - name: Move to In Progress on assignment | ||
| if: github.event_name == 'issues' && github.event.action == 'assigned' | ||
| uses: actions/github-script@v7 |
There was a problem hiding this comment.
This workflow is configured to run on pull_request: opened and issues: reopened, but the only step is gated to issues + assigned, so PR-opened runs will be no-ops and reopened issues won't be handled. Either remove the unused triggers or add corresponding logic (and/or a job-level if) to match the intended behavior described in the PR.
|
|
||
| jobs: | ||
| update-project-status: | ||
| runs-on: ubuntu-latest |
There was a problem hiding this comment.
This job mutates Projects/Issues via GraphQL; without explicit permissions (e.g., projects: write and issues: write as needed) it may fail in repos where the default GITHUB_TOKEN permission is read-only. Consider adding a permissions: block to the job for least-privilege and reliability.
| runs-on: ubuntu-latest | |
| runs-on: ubuntu-latest | |
| permissions: | |
| issues: write | |
| projects: write |
|
|
||
| jobs: | ||
| auto-label: | ||
| runs-on: ubuntu-latest |
There was a problem hiding this comment.
This workflow writes labels via the GitHub API; consider adding an explicit permissions: block (at least issues: write) so it works reliably when repository default GITHUB_TOKEN permissions are restricted.
| runs-on: ubuntu-latest | |
| runs-on: ubuntu-latest | |
| permissions: | |
| issues: write |
| stale-issue-message: 'This issue has been inactive for 30 days. Please update or it will be closed in 7 days.' | ||
| stale-pr-message: 'This PR has been inactive for 30 days. Please update or it will be closed in 7 days.' | ||
| close-issue-message: 'Closing due to inactivity. Reopen if still needed.' | ||
| days-before-stale: 30 | ||
| days-before-close: 7 | ||
| exempt-issue-labels: 'epic,in-progress,priority-high' | ||
| exempt-pr-labels: 'in-review' |
There was a problem hiding this comment.
This workflow is named "Stale Issue Management" but it also configures stale PR handling (stale-pr-message and exempt-pr-labels) and days-before-close will close PRs as well unless explicitly limited. If the intent is issues-only (per PR description), set only-issues: true (or split into separate issue/PR configs) to avoid unexpectedly marking/closing PRs.
🤖 Morph Preview TestPreview URL: https://sobers-9zjy31mva-volvox-llc.vercel.app AI SummaryIssues:
Verified:
RecordingAutomated testing by Morph |

Automation Workflows
This PR adds GitHub Actions workflows for the Sobers v2 project:
Workflows Added
Project Integration
Part of #412