forked from fossabot/trivy
-
Notifications
You must be signed in to change notification settings - Fork 1
178 lines (159 loc) · 6.95 KB
/
Copy pathapidiff.yaml
File metadata and controls
178 lines (159 loc) · 6.95 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
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
name: API Diff Check
on:
# SECURITY: Using pull_request_target to support fork PRs with write permissions.
# PR code is checked out but only for static analysis - it is never executed.
# If modifying this workflow, ensure PR code is never executed and user inputs are not used unsafely.
pull_request_target:
types: [opened, synchronize]
paths:
- 'pkg/**/*.go'
- 'rpc/**/*.go'
permissions:
contents: read
pull-requests: write
issues: write
jobs:
apidiff:
runs-on: ubuntu-24.04
name: API Diff Check
steps:
# Check if PR has conflicts. When conflicts exist, the merge commit becomes
# frozen at an old state and apidiff cannot run correctly.
- name: Check for merge conflicts
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
PR_NUMBER: ${{ github.event.pull_request.number }}
# pull_request_target and mergeability are processed asynchronously.
# As a result, it’s possible that we start the check before GitHub has finished calculating the mergeability.
# To handle this, a retry mechanism has been added — it waits for 2 seconds after each attempt.
# If mergeable_state isn’t obtained after 5 attempts, an error is returned.
run: |
MAX=5
for i in $(seq 1 "$MAX"); do
state=$(gh api "repos/$GITHUB_REPOSITORY/pulls/$PR_NUMBER" --jq .mergeable_state)
echo "mergeable_state=$state"
if [ "$state" = "dirty" ]; then
echo "::error::This PR has merge conflicts. Please resolve conflicts before running apidiff."
exit 1
fi
if [ -n "$state" ] && [ "$state" != "unknown" ] && [ "$state" != "null" ]; then
break
fi
if [ "$i" -lt "$MAX" ] && { [ -z "$state" ] || [ "$state" = "unknown" ] || [ "$state" = "null" ]; }; then
echo "::error::Could not determine mergeability after $i tries."
exit 1
fi
sleep 2
done
# Checkout PR merge commit to compare against base branch
# This ensures we compare the actual merge result with the base branch,
# avoiding false positives when PR is not rebased with latest main
- name: Checkout
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
with:
ref: refs/pull/${{ github.event.pull_request.number }}/merge
- name: Set up Go
uses: ./.github/actions/setup-go
# Ensure the base commit exists locally for go-apidiff to compare against.
# Even though we checkout the merge commit, go-apidiff needs the base ref to exist.
# Use base.ref instead of base.sha, since base.sha is outdated (not updated after every commit).
# cf. https://github.com/orgs/community/discussions/59677
- name: Fetch base commit
id: fetch_base
run: |
set -euo pipefail
BASE_REF="${{ github.event.pull_request.base.ref || github.event.merge_group.base_ref }}"
if [ -z "${BASE_REF:-}" ]; then
echo "::error::BASE_REF is empty (no base ref in event payload)"; exit 1
fi
git fetch --depth=1 origin "$BASE_REF"
BASE_SHA="$(git rev-parse "origin/$BASE_REF")"
if [ -z "${BASE_SHA:-}" ]; then
echo "::error::BASE_SHA is empty (failed to resolve origin/$BASE_REF)"; exit 1
fi
echo "base_sha=$BASE_SHA" >> "$GITHUB_OUTPUT"
# NOTE: go-apidiff is not managed in go.mod because installing it via `go get -tool`
# would cause `mage tool:install` to attempt building it on Windows, which currently
# fails due to platform-specific issues.
- name: Run go-apidiff
id: apidiff
continue-on-error: true
uses: joelanford/go-apidiff@60c4206be8f84348ebda2a3e0c3ac9cb54b8f685 # v0.8.3
with:
base-ref: ${{ steps.fetch_base.outputs.base_sha }}
version: v0.8.3
- name: Add apidiff label
if: ${{ steps.apidiff.outputs.semver-type == 'major' }}
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
with:
script: |
const label = 'apidiff';
await github.rest.issues.addLabels({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
labels: [label],
});
- name: Comment API diff
if: ${{ steps.apidiff.outputs.semver-type == 'major' }}
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
env:
APIDIFF_OUTPUT: ${{ steps.apidiff.outputs.output }}
SEMVER_TYPE: ${{ steps.apidiff.outputs.semver-type }}
with:
script: |
const header = '## 📊 API Changes Detected';
const diff = process.env.APIDIFF_OUTPUT.trim();
const semver = process.env.SEMVER_TYPE || 'unknown';
const body = [
header,
'',
`Semver impact: \`${semver}\``,
'',
'```',
diff,
'```',
].join('\n');
const { data: comments } = await github.rest.issues.listComments({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
});
const existing = comments.find(comment =>
comment.user.type === 'Bot' &&
comment.body.startsWith(header),
);
if (existing) {
await github.rest.issues.updateComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: existing.id,
body,
});
} else {
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
body,
});
}
# Attempt to request the premium reviewers; needs org-scoped token because GITHUB_TOKEN lacks read:org.
- name: Request trivy-premium review
if: ${{ steps.apidiff.outputs.semver-type == 'major' }}
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
with:
github-token: ${{ secrets.ORG_REPO_TOKEN }}
script: |
try {
await github.rest.pulls.requestReviewers({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: context.issue.number,
team_reviewers: ['trivy-premium'],
});
console.log('Requested review from aquasecurity/trivy-premium team');
} catch (error) {
core.error(`Failed to request trivy-premium reviewers: ${error.message}`);
throw error;
}