Repository navigation
130 lines (118 loc) · 5.01 KB
/
Copy pathsquad-impact.yml
File metadata and controls
130 lines (118 loc) · 5.01 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
name: Squad Impact Analysis
# Privileged half of the impact report. The github org blocks
# pull_request_target, so squad-impact-request.yml runs on `pull_request`
# (read-only token, no secrets, no checkout) and uploads only a claim: the PR
# number and head SHA it ran for. workflow_run runs THIS file from the default
# branch with a write token.
#
# Trust boundary:
# - The claim is untrusted data: a fork controls the request workflow. It is
# parsed strictly and bound to the workflow_run payload and the live PR (same
# head SHA, same head repository, this base repository, still open) before
# anything is written. See scripts/workflow-run-pr-binding.mjs.
# - Only default-branch scripts run. PR code is never checked out or executed;
# the report is computed from the GitHub API file list.
on:
workflow_run:
workflows: ["Squad Impact Analysis Request"]
types: [completed]
permissions:
actions: read
contents: read
pull-requests: write
concurrency:
group: ${{ github.workflow }}-${{ github.event.workflow_run.head_repository.full_name }}-${{ github.event.workflow_run.head_branch }}
cancel-in-progress: true
jobs:
impact:
runs-on: ubuntu-latest
timeout-minutes: 5
if: >-
github.event.workflow_run.event == 'pull_request'
&& github.event.workflow_run.conclusion == 'success'
&& github.event.workflow_run.path == '.github/workflows/squad-impact-request.yml'
&& github.event.workflow_run.actor.login != 'dependabot[bot]'
steps:
- name: Checkout scripts (default branch only)
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 #v7
with:
persist-credentials: false
sparse-checkout: |
scripts/analyze-impact.mjs
scripts/impact-utils/parse-diff.mjs
scripts/impact-utils/risk-scorer.mjs
scripts/impact-utils/report-generator.mjs
scripts/workflow-run-pr-binding.mjs
sparse-checkout-cone-mode: false
- name: Download PR claim (untrusted data)
uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 #v8
with:
name: squad-impact-request
path: ${{ runner.temp }}/impact-request
run-id: ${{ github.event.workflow_run.id }}
github-token: ${{ github.token }}
- name: Bind claim to the workflow run and live PR
id: bind
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 #v9
env:
CLAIM_DIR: ${{ runner.temp }}/impact-request
with:
script: |
const { bindWorkflowRun } = await import(`${process.env.GITHUB_WORKSPACE}/scripts/workflow-run-pr-binding.mjs`);
await bindWorkflowRun({
github,
context,
core,
claimDir: process.env.CLAIM_DIR,
claimWorkflowPath: '.github/workflows/squad-impact-request.yml',
});
- name: Setup Node.js
if: steps.bind.outputs.bound == 'true'
uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 #v6
with:
node-version: 22
- name: Run impact analysis
if: steps.bind.outputs.bound == 'true'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GITHUB_REPOSITORY: ${{ github.repository }}
PR_NUMBER: ${{ steps.bind.outputs.pr_number }}
run: node scripts/analyze-impact.mjs "$PR_NUMBER"
- name: Post impact report
if: steps.bind.outputs.bound == 'true'
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 #v9
env:
PR_NUMBER: ${{ steps.bind.outputs.pr_number }}
with:
script: |
const fs = require('fs');
const marker = '<!-- squad-impact-report -->';
const report = fs.readFileSync('impact-report.md', 'utf8');
const body = `${marker}\n${report}`;
const prNumber = Number(process.env.PR_NUMBER);
// Upsert: find existing comment by marker, update or create.
// paginate() follows Link headers so we never miss an existing marker.
const comments = await github.paginate(github.rest.issues.listComments, {
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: prNumber,
per_page: 100,
});
const existing = comments.find(c => c.body && c.body.includes(marker));
if (existing) {
await github.rest.issues.updateComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: existing.id,
body,
});
core.info('Updated existing impact report comment');
} else {
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: prNumber,
body,
});
core.info('Created new impact report comment');
}