Repository navigation
ci: pink-labo 自動化を追加し Devin Slack トリガーに !swe を付与
#2
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 9 commits
cc70e58
9f3b03b
c3d67f8
cf223e9
bbb46bb
3e29fd8
9a2525b
31f639f
6f7b1d8
95664dc
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,90 @@ | ||
| name: CI Failure Slack Notify | ||
|
|
||
| on: | ||
| workflow_run: | ||
| workflows: | ||
| - CI | ||
| - Playwright E2E Tests | ||
| - TruffleHog Secret Scan | ||
| - Lighthouse CI | ||
|
Comment on lines
+5
to
+9
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
The existing CI workflow is named Useful? React with 👍 / 👎. |
||
| types: [completed] | ||
| branches: [main] | ||
|
|
||
| jobs: | ||
| notify-failure: | ||
| name: Notify Slack on failure | ||
| runs-on: ubuntu-latest | ||
| if: ${{ github.event.workflow_run.conclusion == 'failure' }} | ||
| steps: | ||
| - name: Send Slack notification | ||
| env: | ||
| SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }} | ||
| SLACK_CHANNEL_ID: ${{ vars.SLACK_CHANNEL_ID }} | ||
| WORKFLOW_NAME: ${{ github.event.workflow_run.name }} | ||
| RUN_URL: ${{ github.event.workflow_run.html_url }} | ||
| ACTOR: ${{ github.event.workflow_run.actor.login }} | ||
| COMMIT_SHA: ${{ github.event.workflow_run.head_sha }} | ||
| SERVER_URL: ${{ github.server_url }} | ||
| REPOSITORY: ${{ github.repository }} | ||
| run: | | ||
| SHORT_SHA="${COMMIT_SHA:0:7}" | ||
| COMMIT_URL="${SERVER_URL}/${REPOSITORY}/commit/${COMMIT_SHA}" | ||
|
|
||
| PAYLOAD=$(jq -n \ | ||
| --arg channel "$SLACK_CHANNEL_ID" \ | ||
| --arg text "🚨 ${WORKFLOW_NAME} が main ブランチで失敗しました" \ | ||
| --arg workflow "$WORKFLOW_NAME" \ | ||
| --arg actor "$ACTOR" \ | ||
| --arg commit_url "$COMMIT_URL" \ | ||
| --arg short_sha "$SHORT_SHA" \ | ||
| --arg run_url "$RUN_URL" \ | ||
| '{ | ||
| channel: $channel, | ||
| text: $text, | ||
| blocks: [ | ||
| { | ||
| type: "header", | ||
| text: { | ||
| type: "plain_text", | ||
| text: "🚨 CI/CD 失敗通知(main ブランチ)", | ||
| emoji: true | ||
| } | ||
| }, | ||
| { | ||
| type: "section", | ||
| fields: [ | ||
| { type: "mrkdwn", text: ("*ワークフロー:*\n" + $workflow) }, | ||
| { type: "mrkdwn", text: "*ブランチ:*\n`main`" }, | ||
| { type: "mrkdwn", text: ("*実行者:*\n" + $actor) }, | ||
| { type: "mrkdwn", text: ("*コミット:*\n<" + $commit_url + "|" + $short_sha + ">") } | ||
| ] | ||
| }, | ||
| { | ||
| type: "actions", | ||
| elements: [ | ||
| { | ||
| type: "button", | ||
| text: { type: "plain_text", text: "📋 ログを確認", emoji: true }, | ||
| url: $run_url, | ||
| style: "danger" | ||
| } | ||
| ] | ||
| } | ||
| ] | ||
| }') | ||
|
|
||
| RESPONSE=$(curl -sf -X POST "https://slack.com/api/chat.postMessage" \ | ||
| -H "Authorization: Bearer ${SLACK_BOT_TOKEN}" \ | ||
| -H "Content-Type: application/json" \ | ||
| -d "$PAYLOAD") || true | ||
|
|
||
| if [ -z "$RESPONSE" ]; then | ||
| echo "::error::Slack API request failed (network error or empty response)" | ||
| exit 1 | ||
| fi | ||
|
|
||
| if ! echo "$RESPONSE" | jq -e '.ok == true' > /dev/null; then | ||
| ERROR=$(echo "$RESPONSE" | jq -r '.error // "unknown_error"') | ||
| echo "::error::Slack API error: ${ERROR}" | ||
| exit 1 | ||
| fi | ||
|
Comment on lines
+76
to
+90
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win Duplicated Slack POST error-handling gap: swallowed error bodies and no request timeout. Both workflows use the identical
📍 Affects 2 files
🤖 Prompt for AI Agents |
||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,50 @@ | ||||||
| name: Dependabot自動マージ | ||||||
|
|
||||||
| on: pull_request | ||||||
|
|
||||||
| permissions: | ||||||
| contents: write | ||||||
| pull-requests: write | ||||||
|
|
||||||
| jobs: | ||||||
| dependabot-automerge: | ||||||
| runs-on: ubuntu-latest | ||||||
| if: github.actor == 'dependabot[bot]' | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "== workflow files mentioning dependabot =="
git ls-files .github/workflows | xargs -r rg -n "dependabot|automerge|github\.actor|pull_request\.user\.login|pull_request\.user" || true
echo
echo "== target workflow =="
if [ -f .github/workflows/dependabot-automerge.yml ]; then
nl -ba .github/workflows/dependabot-automerge.yml | sed -n '1,120p'
else
echo ".github/workflows/dependabot-automerge.yml not found"
fi
echo
echo "== relevant event context definitions in repository if any =="
rg -n "pull_request|pull_request_target|Dependabot|dependabot|automerge" .github README.md || trueRepository: kouiso/sns-app Length of output: 976 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "== target workflow =="
awk '{printf "%6d\t%s\n", NR, $0}' .github/workflows/dependabot-automerge.yml | sed -n '1,140p'
echo
echo "== parsed workflow triggers and if conditions =="
python3 - <<'PY'
from pathlib import Path
text = Path('.github/workflows/dependabot-automerge.yml').read_text()
for i, line in enumerate(text.splitlines(), 1):
for key in ('triggers:', 'github.actor', 'github.event.pull_request.user.login'):
if key in line:
print(f'{i}: {line}')
PY
echo
echo "== static check: pull_request.author_login used?"
python3 - <<'PY'
import re
text = open('.github/workflows/dependabot-automerge.yml', encoding='utf-8').read()
for expr in ['github.actor ==', 'pull_request.user.login ==', 'pull_request.author_login ==', 'github.event.pull_request.user.login ==']:
print(expr, expr in text)
PYRepository: kouiso/sns-app Length of output: 2366 🌐 Web query:
💡 Result: Using github.actor to verify if a pull request was opened by Dependabot is insecure and can be bypassed by malicious actors [1][2][3]. The github.actor context identifies the user who triggered the specific workflow run, which can be altered by events such as a user forcing a re-run or interacting with the pull request (e.g., adding a label or triggering a synchronize event) [4][1][5]. For secure automerge workflows, you must verify the immutable author of the pull request instead [2][5]. Use the following conditional check to ensure the pull request was truly created by Dependabot [4][2]: if: github.event.pull_request.user.login == 'dependabot[bot]' For even higher assurance, you can also verify the immutable numeric ID of the Dependabot account (49699333) [6][7]: if: github.event.pull_request.user.id == 49699333 && github.event.pull_request.user.login == 'dependabot[bot]' Example Automerge Workflow Implementation: name: Dependabot auto-merge on: pull_request permissions: contents: write pull-requests: write jobs: dependabot: runs-on: ubuntu-latest # Secure check: Verify the pull request author, not the triggering actor if: github.event.pull_request.user.login == 'dependabot[bot]' steps: - name: Fetch Dependabot metadata id: metadata uses: dependabot/fetch-metadata@v3 with: github-token: "${{ secrets.GITHUB_TOKEN }}" - name: Enable auto-merge if: steps.metadata.outputs.update-type == 'version-update:semver-patch' run: gh pr merge --auto --merge "$PR_URL" env: PR_URL: ${{ github.event.pull_request.html_url }} GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} Key Security Considerations: 1. Never rely on github.actor or github.triggering_actor as a security gate [1][2]. 2. Workflows triggered by Dependabot on pull_request events operate with read-only GITHUB_TOKEN permissions by default [8][9][10]. You must explicitly define the required permissions (e.g., contents: write, pull-requests: write) in your workflow file [9][10]. 3. For workflows requiring advanced security, consider using environment protection rules with required human reviewers instead of purely automated logic [1][6]. Citations:
Check the Dependabot pull request author, not the workflow trigger actor.
Proposed fix- if: github.actor == 'dependabot[bot]'
+ if: github.event.pull_request.user.login == 'dependabot[bot]'📝 Committable suggestion
Suggested change
🧰 Tools🪛 zizmor (1.28.0)[error] 12-12: spoofable bot actor check (bot-conditions): actor context may be spoofable (bot-conditions) 🤖 Prompt for AI AgentsSource: Linters/SAST tools |
||||||
|
|
||||||
| steps: | ||||||
| - name: Dependabotメタデータ取得 | ||||||
| id: metadata | ||||||
| uses: dependabot/fetch-metadata@21025c705c08248db411dc16f3619e6b5f9ea21a # v2 | ||||||
| with: | ||||||
| github-token: "${{ secrets.GITHUB_TOKEN }}" | ||||||
|
|
||||||
| - name: 更新タイプをチェック | ||||||
| id: check-update-type | ||||||
| run: | | ||||||
| # グループ化PRの場合、updated-dependencies-jsonから各依存関係の更新タイプを確認 | ||||||
| DEPENDENCIES='${{ steps.metadata.outputs.updated-dependencies-json }}' | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "== workflow excerpt =="
if [ -f .github/workflows/dependabot-automerge.yml ]; then
nl -ba .github/workflows/dependabot-automerge.yml | sed -n '1,140p'
else
echo "missing .github/workflows/dependabot-automerge.yml"
fi
echo
echo "== permissions and dependency env/action context =="
rg -n "permissions:|contents:|pull-requests:|updated-dependencies-json|DEPENDENCIES|dependabot|dependabot-automerge|metadata" .github/workflows/dependabot-automerge.yml .github/workflows || true
echo
echo "== shell quoting/expansion probe for malicious output shape =="
python3 - <<'PY'
payload = "'; :; echo INJECTED >&2"
# Simulate GitHub expression replacement before bash sees the literal line.
line = f" DEPENDENCIES='{payload}'"
print("generated line:")
print(line)
PY
bash -c "DEPENDENCIES='';:'; echo INJECTED >&2" || trueRepository: kouiso/sns-app Length of output: 215 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "== workflow excerpt =="
if [ -f .github/workflows/dependabot-automerge.yml ]; then
cat -n .github/workflows/dependabot-automerge.yml | sed -n '1,140p'
else
echo "missing .github/workflows/dependabot-automerge.yml"
fi
echo
echo "== permissions and dependency env/action context =="
grep -RInE "permissions:|contents:|pull-requests:|updated-dependencies-json|DEPENDENCIES|dependabot|metadata" .github/workflows/dependabot-automerge.yml .github/workflows 2>/dev/null || true
echo
echo "== shell quoting/expansion probe for malicious output shape =="
payload="'; :; echo INJECTED >&2"
generated="DEPENDENCIES='${payload}'"
printf 'generated output line:\n%s\n' "$generated"
bash -c "DEPENDENCIES='';:'; echo INJECTED >&2" || true
echo
echo "== safe assignment with environment variable probe =="
payload="'; :; echo INJECTED >&2"
bash -c 'export DEPENDENCIES="'"$payload"'"' 2>&1 || true
bash -c 'echo "$DEPENDENCIES"' 2>&1 || trueRepository: kouiso/sns-app Length of output: 4832 Do not expand metadata output into shell source. This job runs with write access for Proposed fix - name: 更新タイプをチェック
id: check-update-type
+ env:
+ DEPENDENCIES: ${{ steps.metadata.outputs.updated-dependencies-json }}
run: |
# グループ化PRの場合、updated-dependencies-jsonから各依存関係の更新タイプを確認
- DEPENDENCIES='${{ steps.metadata.outputs.updated-dependencies-json }}'🧰 Tools🪛 zizmor (1.28.0)[info] 25-25: code injection via template expansion (template-injection): may expand into attacker-controllable code (template-injection) 🤖 Prompt for AI AgentsSource: Linters/SAST tools |
||||||
|
|
||||||
| # 依存関係が空の場合はスキップ | ||||||
| if [ -z "$DEPENDENCIES" ] || [ "$DEPENDENCIES" = "[]" ]; then | ||||||
| echo "can-automerge=false" >> $GITHUB_OUTPUT | ||||||
| exit 0 | ||||||
| fi | ||||||
|
|
||||||
| # すべての依存関係がpatchまたはminorかチェック | ||||||
| CAN_MERGE=$(echo "$DEPENDENCIES" | jq -r ' | ||||||
| if length == 0 then | ||||||
| false | ||||||
| else | ||||||
| all(.[]; .["update-type"] == "version-update:semver-patch" or .["update-type"] == "version-update:semver-minor") | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Useful? React with 👍 / 👎. |
||||||
| end | ||||||
| ') | ||||||
|
|
||||||
| echo "can-automerge=$CAN_MERGE" >> $GITHUB_OUTPUT | ||||||
| echo "依存関係の更新タイプ確認完了: can-automerge=$CAN_MERGE" | ||||||
|
|
||||||
| - name: minor/patchを自動マージ | ||||||
| if: steps.check-update-type.outputs.can-automerge == 'true' | ||||||
| run: gh pr merge --auto --squash "$PR_URL" | ||||||
| env: | ||||||
| PR_URL: ${{ github.event.pull_request.html_url }} | ||||||
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,84 @@ | ||||||||||||||||||
| name: Devin Slack Trigger | ||||||||||||||||||
|
|
||||||||||||||||||
| on: | ||||||||||||||||||
| issues: | ||||||||||||||||||
| types: [labeled] | ||||||||||||||||||
|
|
||||||||||||||||||
| permissions: | ||||||||||||||||||
| issues: write | ||||||||||||||||||
|
|
||||||||||||||||||
| jobs: | ||||||||||||||||||
| trigger-devin: | ||||||||||||||||||
| name: Trigger Devin via Slack | ||||||||||||||||||
| runs-on: ${{ vars.RUNNER_LINUX || 'ubuntu-latest' }} | ||||||||||||||||||
| # Slack への通知トリガのみの小さなジョブなので短めに打ち切る | ||||||||||||||||||
| timeout-minutes: 10 | ||||||||||||||||||
| if: github.event.label.name == 'devin' | ||||||||||||||||||
| concurrency: | ||||||||||||||||||
| group: devin-trigger-${{ github.event.issue.number }} | ||||||||||||||||||
| cancel-in-progress: false | ||||||||||||||||||
|
|
||||||||||||||||||
| steps: | ||||||||||||||||||
| - name: Check for duplicate trigger | ||||||||||||||||||
| id: duplicate-check | ||||||||||||||||||
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | ||||||||||||||||||
| with: | ||||||||||||||||||
| script: | | ||||||||||||||||||
| const comments = await github.paginate( | ||||||||||||||||||
| github.rest.issues.listComments, | ||||||||||||||||||
| { | ||||||||||||||||||
| owner: context.repo.owner, | ||||||||||||||||||
| repo: context.repo.repo, | ||||||||||||||||||
| issue_number: context.payload.issue.number, | ||||||||||||||||||
| } | ||||||||||||||||||
| ); | ||||||||||||||||||
| const alreadyTriggered = comments.some(c => | ||||||||||||||||||
| c.body?.includes('<!-- devin-slack-triggered -->') | ||||||||||||||||||
| ); | ||||||||||||||||||
|
Comment on lines
+35
to
+37
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "Repo files matching workflow:"
fd -a 'devin-slack-trigger\.yml$' . || true
file="$(fd 'devin-slack-trigger\.yml$' . | head -n1 || true)"
if [ -n "${file:-}" ]; then
echo "=== $file ($(wc -l < "$file") lines) ==="
cat -n "$file"
fi
echo "=== Search for marker and GitHub App token usage ==="
rg -n --hidden --glob '!**/.git/**' 'devin-slack-triggered|github-app-token|get-token|issues:labeled|comment' .github . 2>/dev/null | head -n 200Repository: kouiso/sns-app Length of output: 1311 🏁 Script executed: #!/bin/bash
set -euo pipefail
file=".github/workflows/devin-slack-trigger.yml"
echo "=== workflow context ==="
cat -n "$file" | sed -n '1,130p'
echo "=== read-only behavioral probe mirroring the duplicate check ==="
python3 - <<'PY'
comments = [
{"user": {"login": "regular-commenter"}, "body": "<!-- devin-slack-triggered -->"},
]
alreadyTriggered = any(
c.get("body") and "<!-- devin-slack-triggered -->" in c["body"]
for c in comments
)
print("current accepts_commenter_marker =", alreadyTriggered)
comments = [
{"user": {"login": "github-actions[bot]"}, "body": "<!-- devin-slack-triggered -->"},
{"user": {"login": "regular-commenter"}, "body": "<!-- devin-slack-triggered -->"},
]
filtered = any(
c.get("user", {}).get("login") == "github-actions[bot]"
and c.get("body") and "<!-- devin-slack-triggered -->" in c["body"]
for c in comments
)
print("current_with_commenter_and_bot = True unless comment order changes, but first caller only",
filtered)
PYRepository: kouiso/sns-app Length of output: 4082 Require the duplicate marker to be posted by Users who can comment can add Proposed fix- const alreadyTriggered = comments.some(c =>
- c.body?.includes('<!-- devin-slack-triggered -->')
+ const marker = '<!-- devin-slack-triggered -->';
+ const alreadyTriggered = comments.some(c =>
+ c.user?.login === 'github-actions[bot]' &&
+ c.body?.includes(marker)
);📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents
Comment on lines
+35
to
+37
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
In this public repository, any issue commenter can include the hidden marker in a comment before a maintainer applies the Useful? React with 👍 / 👎. |
||||||||||||||||||
| if (alreadyTriggered) { | ||||||||||||||||||
| core.notice('Devin session already triggered for this issue. Skipping.'); | ||||||||||||||||||
| } | ||||||||||||||||||
| core.setOutput('skip', String(alreadyTriggered)); | ||||||||||||||||||
|
|
||||||||||||||||||
| - name: Post message to Slack | ||||||||||||||||||
| if: steps.duplicate-check.outputs.skip == 'false' | ||||||||||||||||||
| id: slack-post | ||||||||||||||||||
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | ||||||||||||||||||
| env: | ||||||||||||||||||
| SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }} | ||||||||||||||||||
| SLACK_CHANNEL_ID: ${{ vars.SLACK_CHANNEL_ID }} | ||||||||||||||||||
| DEVIN_USER_ID: ${{ vars.SLACK_DEVIN_USER_ID }} | ||||||||||||||||||
| with: | ||||||||||||||||||
| script: | | ||||||||||||||||||
| const issue = context.payload.issue; | ||||||||||||||||||
|
|
||||||||||||||||||
| const message = `!swe\n[${issue.title}]\n<@${process.env.DEVIN_USER_ID}> Please handle this issue. First, create a plan and share it before implementing. Think in English, respond in Japanese.\n${issue.html_url}`; | ||||||||||||||||||
|
|
||||||||||||||||||
| const resp = await fetch('https://slack.com/api/chat.postMessage', { | ||||||||||||||||||
| method: 'POST', | ||||||||||||||||||
| headers: { | ||||||||||||||||||
| 'Authorization': `Bearer ${process.env.SLACK_BOT_TOKEN}`, | ||||||||||||||||||
| 'Content-Type': 'application/json; charset=utf-8', | ||||||||||||||||||
| }, | ||||||||||||||||||
| body: JSON.stringify({ | ||||||||||||||||||
| channel: process.env.SLACK_CHANNEL_ID, | ||||||||||||||||||
| text: message, | ||||||||||||||||||
| }), | ||||||||||||||||||
| }); | ||||||||||||||||||
|
|
||||||||||||||||||
| const data = await resp.json(); | ||||||||||||||||||
| if (!data.ok) { | ||||||||||||||||||
| core.setFailed(`Slack API error: ${data.error || 'unknown'}`); | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| - name: Post marker comment | ||||||||||||||||||
| if: success() && steps.duplicate-check.outputs.skip == 'false' | ||||||||||||||||||
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | ||||||||||||||||||
| with: | ||||||||||||||||||
| script: | | ||||||||||||||||||
| await github.rest.issues.createComment({ | ||||||||||||||||||
|
Comment on lines
+74
to
+79
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
If Slack accepts the message but the runner is interrupted or Useful? React with 👍 / 👎. |
||||||||||||||||||
| owner: context.repo.owner, | ||||||||||||||||||
| repo: context.repo.repo, | ||||||||||||||||||
| issue_number: context.payload.issue.number, | ||||||||||||||||||
| body: ':robot_face: Devin session triggered via Slack <!-- devin-slack-triggered -->', | ||||||||||||||||||
| }); | ||||||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,142 @@ | ||||||||||||||||||||||||
| name: Merge Quality Gate | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| on: | ||||||||||||||||||||||||
| pull_request: | ||||||||||||||||||||||||
| branches: [main] | ||||||||||||||||||||||||
| types: [closed] | ||||||||||||||||||||||||
|
Comment on lines
+3
to
+6
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
When a PR originates from a fork, GitHub withholds repository secrets from workflows triggered by Useful? React with 👍 / 👎. |
||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| jobs: | ||||||||||||||||||||||||
| check-quality: | ||||||||||||||||||||||||
| name: Post-merge quality check | ||||||||||||||||||||||||
| runs-on: ubuntu-latest | ||||||||||||||||||||||||
| if: github.event.pull_request.merged == true | ||||||||||||||||||||||||
| permissions: | ||||||||||||||||||||||||
| contents: read | ||||||||||||||||||||||||
| checks: read | ||||||||||||||||||||||||
| pull-requests: read | ||||||||||||||||||||||||
| steps: | ||||||||||||||||||||||||
| - name: Check for unresolved quality issues | ||||||||||||||||||||||||
| id: check | ||||||||||||||||||||||||
| uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7 | ||||||||||||||||||||||||
| with: | ||||||||||||||||||||||||
| script: | | ||||||||||||||||||||||||
| const prNumber = context.payload.pull_request.number; | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| // PRコメントを取得してLighthouseスコアを確認 | ||||||||||||||||||||||||
| const { data: comments } = await github.rest.issues.listComments({ | ||||||||||||||||||||||||
| owner: context.repo.owner, | ||||||||||||||||||||||||
| repo: context.repo.repo, | ||||||||||||||||||||||||
| issue_number: prNumber, | ||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||
|
Comment on lines
+26
to
+30
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win Add pagination to
🛠️ Proposed fix- const { data: comments } = await github.rest.issues.listComments({
- owner: context.repo.owner,
- repo: context.repo.repo,
- issue_number: prNumber,
- });
+ const comments = await github.paginate(github.rest.issues.listComments, {
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ issue_number: prNumber,
+ per_page: 100,
+ });📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents
Comment on lines
+26
to
+30
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
When a merged PR has more than the REST endpoint's first page of comments, this request examines only that page, so a later Useful? React with 👍 / 👎. |
||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| const issues = []; | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| // Lighthouseスコア確認 | ||||||||||||||||||||||||
| const lhComment = comments.find(c => c.body?.includes('<!-- lighthouse-ci -->')); | ||||||||||||||||||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
In the reviewed commit, a repo-wide search of Useful? React with 👍 / 👎. |
||||||||||||||||||||||||
| if (lhComment) { | ||||||||||||||||||||||||
| // 🔴🟠のみマッチ(🟢=良好スコアは品質問題として検出しない) | ||||||||||||||||||||||||
| const perfMatch = lhComment.body.match(/Performance\s*\|\s*(?:🔴|🟠)\s*(\d+)/); | ||||||||||||||||||||||||
| if (perfMatch) { | ||||||||||||||||||||||||
| const perfScore = parseInt(perfMatch[1]); | ||||||||||||||||||||||||
| issues.push({ | ||||||||||||||||||||||||
| type: 'lighthouse', | ||||||||||||||||||||||||
| message: `Lighthouse Performance: ${perfScore}/100`, | ||||||||||||||||||||||||
| score: perfScore, | ||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| // protect-main警告が残っていないか確認 | ||||||||||||||||||||||||
| const protectComment = comments.find(c => c.body?.includes('<!-- protect-main-bot -->')); | ||||||||||||||||||||||||
| if (protectComment) { | ||||||||||||||||||||||||
| issues.push({ | ||||||||||||||||||||||||
| type: 'protect-main', | ||||||||||||||||||||||||
| message: 'mainブランチ保護ポリシー警告が未解消', | ||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| // チェック結果を出力 | ||||||||||||||||||||||||
| if (issues.length > 0) { | ||||||||||||||||||||||||
| core.setOutput('has_issues', 'true'); | ||||||||||||||||||||||||
| core.setOutput('issues', JSON.stringify(issues)); | ||||||||||||||||||||||||
| core.info(`未解消の品質問題: ${issues.length}件`); | ||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||
| core.setOutput('has_issues', 'false'); | ||||||||||||||||||||||||
| core.info('品質問題なし'); | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| - name: Notify Slack if unresolved issues | ||||||||||||||||||||||||
| if: steps.check.outputs.has_issues == 'true' | ||||||||||||||||||||||||
| env: | ||||||||||||||||||||||||
| SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }} | ||||||||||||||||||||||||
| SLACK_CHANNEL_ID: ${{ vars.SLACK_CHANNEL_ID }} | ||||||||||||||||||||||||
| ISSUES_JSON: ${{ steps.check.outputs.issues }} | ||||||||||||||||||||||||
| PR_NUMBER: ${{ github.event.pull_request.number }} | ||||||||||||||||||||||||
| PR_TITLE: ${{ github.event.pull_request.title }} | ||||||||||||||||||||||||
| PR_URL: ${{ github.event.pull_request.html_url }} | ||||||||||||||||||||||||
| ACTOR: ${{ github.event.pull_request.merged_by.login }} | ||||||||||||||||||||||||
| run: | | ||||||||||||||||||||||||
| ISSUE_LIST=$(echo "$ISSUES_JSON" | jq -r '[.[] | "• " + .message] | join("\n")') | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| PAYLOAD=$(jq -n \ | ||||||||||||||||||||||||
| --arg channel "$SLACK_CHANNEL_ID" \ | ||||||||||||||||||||||||
| --arg text "⚠️ 品質問題が未解消のままマージされました: #${PR_NUMBER}" \ | ||||||||||||||||||||||||
| --arg pr_url "$PR_URL" \ | ||||||||||||||||||||||||
| --arg pr_number "$PR_NUMBER" \ | ||||||||||||||||||||||||
| --arg pr_title "$PR_TITLE" \ | ||||||||||||||||||||||||
| --arg actor "$ACTOR" \ | ||||||||||||||||||||||||
| --arg issues "$ISSUE_LIST" \ | ||||||||||||||||||||||||
| '{ | ||||||||||||||||||||||||
| channel: $channel, | ||||||||||||||||||||||||
| text: $text, | ||||||||||||||||||||||||
| blocks: [ | ||||||||||||||||||||||||
| { | ||||||||||||||||||||||||
| type: "header", | ||||||||||||||||||||||||
| text: { | ||||||||||||||||||||||||
| type: "plain_text", | ||||||||||||||||||||||||
| text: "⚠️ 品質問題未解消マージ通知", | ||||||||||||||||||||||||
| emoji: true | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||
| { | ||||||||||||||||||||||||
| type: "section", | ||||||||||||||||||||||||
| fields: [ | ||||||||||||||||||||||||
| { type: "mrkdwn", text: ("*PR:*\n<" + $pr_url + "|#" + $pr_number + " " + $pr_title + ">") }, | ||||||||||||||||||||||||
| { type: "mrkdwn", text: ("*マージ実行者:*\n" + $actor) } | ||||||||||||||||||||||||
| ] | ||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||
| { | ||||||||||||||||||||||||
| type: "section", | ||||||||||||||||||||||||
| text: { | ||||||||||||||||||||||||
| type: "mrkdwn", | ||||||||||||||||||||||||
| text: ("*未解消の問題:*\n" + $issues) | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||
| { | ||||||||||||||||||||||||
| type: "actions", | ||||||||||||||||||||||||
| elements: [ | ||||||||||||||||||||||||
| { | ||||||||||||||||||||||||
| type: "button", | ||||||||||||||||||||||||
| text: { type: "plain_text", text: "📋 PRを確認", emoji: true }, | ||||||||||||||||||||||||
| url: $pr_url | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
| ] | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
| ] | ||||||||||||||||||||||||
| }') | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| RESPONSE=$(curl -sf -X POST "https://slack.com/api/chat.postMessage" \ | ||||||||||||||||||||||||
| -H "Authorization: Bearer ${SLACK_BOT_TOKEN}" \ | ||||||||||||||||||||||||
| -H "Content-Type: application/json" \ | ||||||||||||||||||||||||
| -d "$PAYLOAD") || true | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| if [ -z "$RESPONSE" ]; then | ||||||||||||||||||||||||
| echo "::error::Slack API request failed (network error or empty response)" | ||||||||||||||||||||||||
| exit 1 | ||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| if ! echo "$RESPONSE" | jq -e '.ok == true' > /dev/null; then | ||||||||||||||||||||||||
| ERROR=$(echo "$RESPONSE" | jq -r '.error // "unknown_error"') | ||||||||||||||||||||||||
| echo "::error::Slack API error: ${ERROR}" | ||||||||||||||||||||||||
| exit 1 | ||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,25 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| name: TruffleHog Secret Scan | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| on: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| pull_request: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| push: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| branches: [main] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| permissions: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| contents: read | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| security-events: write | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| jobs: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| trufflehog: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| name: Secret Scan | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| runs-on: ubuntu-latest | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| steps: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| - name: Checkout | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| with: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fetch-depth: 0 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+8
to
+20
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "== workflow excerpt =="
sed -n '1,80p' .github/workflows/trufflehog.yml 2>/dev/null || true
echo
echo "== workflow file metadata =="
git ls-files .github/workflows/trufflehog.yml
echo
echo "== search for trufflehog workflow references elsewhere =="
rg -n "trufflehog|security-events|persist-credentials|actions/checkout" .github/workflows || true
echo
echo "== inspect target action references from workflow (not executing repo file) =="
python3 - <<'PY'
from pathlib import Path
p=Path('.github/workflows/trufflehog.yml')
if p.exists():
text=p.read_text()
print("contains actions/checkout:", "actions/checkout" in text)
print("contains persist-credentials:", "persist-credentials" in text)
print("contains security-events: write:", "security-events: write" in text)
PYRepository: kouiso/sns-app Length of output: 1429 🌐 Web query:
💡 Result: The TruffleHog GitHub Action, which can be configured via action.yml [1][2], is designed to scan local Git repositories within a CI environment [3]. When using this action, it is standard practice to use the actions/checkout step to prepare the repository for scanning [4][3][5]. Regarding your specific configuration requirements: 1. persist-credentials: When using the actions/checkout action in a GitHub workflow, it is common to set persist-credentials: false for security purposes [6][7][8][9]. This prevents the checkout action from persisting the default GitHub token in the local git configuration, which is a security best practice for CI/CD pipelines [8][9]. 2. Local Git Scanning: The TruffleHog GitHub Action performs scanning by running the TruffleHog CLI within a Docker container [10][2]. It mounts the current directory (the local git repository) into the container [1][2]. For the action to scan correctly, especially when checking ranges (such as in pull requests), you must ensure that the repository has been checked out with sufficient history [4][3]. Setting fetch-depth: 0 in the actions/checkout step is typically required to provide full git history for the scanner to compare branches or commits [4][3][9]. 3. Configuration Example: A typical implementation in a workflow file looks like this: steps: - name: Checkout code uses: actions/checkout@v4 with: fetch-depth: 0 persist-credentials: false - name: TruffleHog OSS uses: trufflesecurity/trufflehog@main with: extra_args: --results=verified,unknown In this setup, the TruffleHog action automatically handles the necessary git file:/// protocol to scan the local repository contents [1][2]. The use of persist-credentials: false is independent of the TruffleHog action itself; it ensures that your workflow does not inadvertently leave credentials behind in the environment where TruffleHog is scanning [6][8][9]. Citations:
🌐 Web query:
💡 Result: To perform a local git scan without triggering GitHub Security events (such as automatic code scanning alerts or pull request annotations), you should avoid using the trufflesecurity/trufflehog GitHub Action wrapper. The official TruffleHog GitHub Action is designed specifically to integrate with GitHub's CI features by automatically formatting output for GitHub Actions, which generates code annotations and interacts with GitHub security interfaces [1][2][3]. There is no native configuration parameter within the Action itself to fully disable this behavior [2][3]. For a local scan that avoids these GitHub-specific integrations, the recommended approach is to run the TruffleHog CLI directly via a standard Docker container execution in your workflow [3][4]. Recommended approach: Instead of using the 'uses: trufflesecurity/trufflehog' syntax, invoke the official Docker image directly in your workflow step [1][3]: - name: Run TruffleHog Scan run: | docker run --rm -v "${{ github.workspace }}:/workdir" trufflesecurity/trufflehog:latest \ git file:///workdir --results=verified,unknown --json > scan-results.json This approach provides several advantages for your requirements: 1. No GitHub Security Integration: By running the CLI directly, you bypass the wrapper's logic that forces GitHub-specific output formats [1][2]. 2. Custom Handling: You can pipe the output to a file (as shown above), store it as a build artifact, or process it using your own custom scripts without the Action interfering with your PR or security tab [3][4]. 3. Local Git Scanning: When scanning a local repository within a container, ensure you volume mount the directory correctly (e.g., to /workdir) and scan that target to avoid git configuration errors [5][1]. If you prefer to continue using the Action but want to minimize its impact, some users have attempted to override default behavior by passing alternative output flags (e.g., --json) in extra_args, though this may not fully suppress all side effects related to the Action's integration [2][6]. Direct CLI execution remains the most reliable method for your use case [3][4]. Citations:
🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "== fetch pinned checkout metadata =="
if command -v gh >/dev/null 2>&1; then
gh api repos/actions/checkout/git/refs/remotes/origin/v6 \
--jq '{ref: .ref, object: {type: .object.type, sha: .object.sha}}' || true
gh api repos/actions/checkout/git/refs/tags/v6 \
--jq '{ref: .ref, object: {type: .object.type, sha: .object.sha}}' || true
else
python3 - <<'PY'
import urllib.request
urls = [
"https://raw.githubusercontent.com/actions/checkout/v6/action.yml",
"https://api.github.com/repos/actions/checkout/git/refs/tags/v6",
]
for u in urls:
try:
print("\n-- " + u)
data = urllib.request.urlopen(u, timeout=15).read().decode()
lines = data.splitlines()
for line in lines[:120]:
print(line)
except Exception as e:
print("ERROR", e)
PY
fi
echo
echo "== fetch pinned TruffleHog action metadata =="
if command -v gh >/dev/null 2>&1; then
gh api repos/trufflesecurity/trufflehog/git/refs/tags/v3.92.2 \
--jq '{ref: .ref, object: {type: .object.type, sha: .object.sha}}'
sha="$(gh api repos/trufflesecurity/trufflehog/git/refs/tags/v3.92.2 --jq '.object.type')"
if [ "$sha" = "tag" ]; then
target="$(gh api repos/trufflesecurity/trufflehog/git/tags/v3.92.2 --jq '.object.sha')"
gh api "repos/trufflesecurity/trufflehog/git/tags/$target" \
--jq '.message, .target.sha'
else
sha="$(gh api repos/trufflesecurity/trufflehog/git/refs/tags/v3.92.2 --jq '.object.sha')"
gh api "repos/trufflesecurity/trufflehog/git/commits/$sha" \
--jq '.sha, .message'
fi
else
python3 - <<'PY'
import json, urllib.request
ref=json.load(urllib.request.urlopen("https://api.github.com/repos/trufflesecurity трufflehog/git/refs/tags/v3.92.2", timeout=15))
print(json.dumps(ref, indent=2)[:1200])
try:
obj=json.load(urllib.request.urlopen("https://api.github.com/repos/trufflesecurity/trufflehog/git/commits/702bbefa350895c57f0e303eeb2855a1c637908f", timeout=15))
print(obj["sha"], obj["message"])
except Exception as e:
print("ERROR", e)
PY
fiRepository: kouiso/sns-app Length of output: 953 🌐 Web query:
💡 Result: In the actions/checkout action, the persist-credentials input controls whether the authentication token or SSH key used for the checkout is configured in the local git configuration [1][2][3]. By default, this setting is true [1][4][3]. While there have been discussions and open pull requests proposing to change the default value of persist-credentials to false to enhance security and reduce the risk of exposing credentials [5], as of August 2, 2026, it remains true by default in the official repository [1][3]. When persist-credentials is set to true (the default), the credentials are made available to your workflow, enabling subsequent git commands to run with authentication [1][2]. The action removes these credentials during the post-job cleanup [1][2]. If you wish to disable this behavior, you must explicitly set persist-credentials: false in your workflow file [1][2]. Citations:
🌐 Web query:
💡 Result: To upload analysis results (such as SARIF files) to GitHub Code Scanning using GitHub Actions, your workflow job must have the following permissions configured [1][2][3]: 1. security-events: write: This permission is mandatory for all workflows to authorize the upload of security analysis results to the repository [1][2][3]. 2. actions: read: While often omitted in simple examples, this permission is explicitly required for workflows running in private repositories [1][2][4]. It allows the action to access necessary metadata about the workflow run required during the upload process [4][5]. 3. contents: read: This is generally required to allow the workflow to check out the repository code [1][2], which is a standard step in most analysis workflows. A recommended minimal permissions block for a job performing this task is [1][4]: permissions: security-events: write actions: read contents: read If you encounter "Bad credentials" or "Resource not accessible" errors, especially in private repositories, ensure that the Citations:
🌐 Web query:
💡 Result: To integrate TruffleHog findings into GitHub Code Scanning (GHAS), you must configure your GitHub Actions workflow with the Citations:
🌐 Web query:
💡 Result: As of August 2026, the official TruffleHog GitHub Action does not natively support generating SARIF output or uploading results directly to GitHub Code Scanning [1][2][3]. While users have requested SARIF output support to facilitate integration with GitHub's security features [1][2], the current official action is designed primarily to run scans and block workflows (or report results) based on discovered credentials, typically outputting to logs or standard JSON via container-based workarounds [4][2][5][6]. If you wish to integrate TruffleHog results into GitHub Code Scanning, you must implement a custom workaround [2][6]: 1. Run TruffleHog: Execute the TruffleHog CLI (often via a Docker container) in your workflow, ensuring you output the results to a file (e.g., using --json) [4][6]. 2. Convert to SARIF: Use a third-party tool or a custom script to convert the TruffleHog JSON output into the SARIF 2.1.0 format required by GitHub [1][6]. 3. Upload to Code Scanning: Once you have a valid SARIF file, use the official github/codeql-action/upload-sarif action to upload the results to your repository's security tab [7]. Example workflow snippet for the upload step: - name: Upload SARIF file uses: github/codeql-action/upload-sarif@v4 with: sarif_file: path/to/your-converted-results.sarif [7] Note that users have noted difficulties in using automated upload features for secret scanning because secrets exist in git history, which can lead to issues with GitHub's automatic vulnerability resolution logic [1]. Always ensure your workflow includes the necessary permissions (security-events: write) to perform the upload [7]. Citations:
Remove This workflow only scans existing code, with no code-to-secret history scan or SARIF upload step. Drop Proposed fix permissions:
contents: read
- security-events: write
...
with:
fetch-depth: 0
+ persist-credentials: false📝 Committable suggestion
Suggested change
🧰 Tools🪛 zizmor (1.28.0)[warning] 17-20: credential persistence through GitHub Actions artifacts (artipacked): does not set persist-credentials: false (artipacked) 🤖 Prompt for AI AgentsSource: Linters/SAST tools |
||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| - name: TruffleHog OSS | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| uses: trufflesecurity/trufflehog@702bbefa350895c57f0e303eeb2855a1c637908f # v3.92.2 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| with: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| extra_args: --only-verified | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
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.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
Repository: kouiso/sns-app
Length of output: 571
🏁 Script executed:
Repository: kouiso/sns-app
Length of output: 3677
Align the failure notify workflow list with actual workflow
name:declarations.Only
TruffleHog Secret Scanhas a matching declaration in.github/workflows/trufflehog.yml.CI,Playwright E2E Tests, andLighthouse CIhave noname:matches, so failures from those workflows will not fire this notification. Add the missing workflowname:fields or update thisworkflows:list to match existing declarations.🤖 Prompt for AI Agents