Skip to content

Commit 5654f28

Browse files
refactor: simplify license-check to auto-commit approach
Much simpler workflow: 1. Always try to auto-commit fix directly to PR branch 2. If push fails (fork without permissions), comment once with instructions 3. Don't create child PRs - just fix in place or give instructions 4. Only comment if not already commented (prevent spam) 5. Always fail check if licenses need updating Benefits: - Much simpler - single job - No child PR management complexity - Clear UX: either fixed or instructed - Works for all PRs (internal/fork/dependabot)
1 parent 8781409 commit 5654f28

File tree

1 file changed

+36
-304
lines changed

1 file changed

+36
-304
lines changed
Lines changed: 36 additions & 304 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# Automatically fix license files on PRs that need updates
2-
# Instead of just failing, this workflow pushes the fix and comments on the PR
2+
# Tries to auto-commit the fix, or comments with instructions if push fails
33

44
name: License Check
55
on:
@@ -21,17 +21,8 @@ permissions:
2121
jobs:
2222
license-check:
2323
runs-on: ubuntu-latest
24-
outputs:
25-
needs_fix: ${{ steps.changes.outputs.changed }}
26-
is_fork: ${{ steps.fork_check.outputs.is_fork }}
2724

2825
steps:
29-
- name: Check if fork PR
30-
id: fork_check
31-
run: |
32-
IS_FORK=${{ github.event.pull_request.head.repo.full_name != github.repository }}
33-
echo "is_fork=${IS_FORK}" >> $GITHUB_OUTPUT
34-
3526
- name: Check out code
3627
uses: actions/checkout@v6
3728
with:
@@ -55,325 +46,66 @@ jobs:
5546
5647
- name: Check for changes
5748
id: changes
58-
run: |
59-
if git diff --exit-code; then
60-
echo "changed=false" >> $GITHUB_OUTPUT
61-
echo "✅ License files are up to date"
62-
else
63-
echo "changed=true" >> $GITHUB_OUTPUT
64-
echo "📝 License files need updating"
65-
git diff --stat
66-
fi
49+
continue-on-error: true
50+
run: script/licenses-check
6751

6852
- name: Commit and push fixes
69-
if: steps.changes.outputs.changed == 'true'
70-
continue-on-error: true # Don't fail if push fails (e.g., on forks)
53+
if: steps.changes.outcome == 'failure'
54+
continue-on-error: true
7155
id: push
7256
run: |
73-
git config --local user.name "github-actions[bot]"
74-
git config --local user.email "41898282+github-actions[bot]@users.noreply.github.com"
75-
git add third-party third-party-licenses.*.md
76-
git commit -m "chore: regenerate third-party licenses"
77-
git push
78-
79-
- name: Comment on PR
80-
if: steps.changes.outputs.changed == 'true'
81-
uses: actions/github-script@v7
82-
with:
83-
script: |
84-
const isFork = context.payload.pull_request.head.repo.full_name !== context.repo.owner + '/' + context.repo.repo;
85-
const pushFailed = '${{ steps.push.outcome }}' === 'failure';
86-
87-
let body;
88-
if (isFork || pushFailed) {
89-
body = `## 📜 License files need updating
90-
91-
License files are out of date. Since this is from a fork, I can't push the fix directly.
92-
93-
**Please run locally:**
94-
\`\`\`bash
95-
./script/licenses
96-
git add third-party third-party-licenses.*.md third-party/
97-
git commit -m "chore: update license files"
98-
git push
99-
\`\`\`
57+
git config user.name "github-actions[bot]"
58+
git config user.email "github-actions[bot]@users.noreply.github.com"
59+
git add third-party-licenses.*.md third-party/
60+
git commit -m "chore: regenerate license files
10061
101-
Or a maintainer can push to your branch after approving the workflow.`;
102-
} else {
103-
body = `## 📜 License files updated
104-
105-
I noticed the third-party license files were out of date and pushed a fix to this PR.
106-
107-
**What changed:** Dependencies were added, removed, or updated, which requires regenerating the license documentation.
108-
109-
**What I did:** Ran \`./script/licenses\` and committed the result.
110-
111-
Please pull the latest changes before pushing again.`;
112-
}
113-
114-
github.rest.issues.createComment({
115-
owner: context.repo.owner,
116-
repo: context.repo.repo,
117-
issue_number: context.issue.number,
118-
body
119-
})
120-
121-
- name: Fail check if licenses not fixed
122-
if: steps.changes.outputs.changed == 'true' && steps.push.outcome == 'failure'
123-
run: |
124-
echo "::error::License files are out of date. Please run ./script/licenses locally."
125-
exit 1
126-
127-
auto-create-fix-pr:
128-
runs-on: ubuntu-latest
129-
needs: license-check
130-
if: needs.license-check.outputs.needs_fix == 'true' && needs.license-check.outputs.is_fork == 'false'
131-
132-
steps:
133-
- name: Check out base PR branch
134-
uses: actions/checkout@v6
135-
with:
136-
ref: ${{ github.event.pull_request.head.ref }}
137-
fetch-depth: 0
138-
139-
- name: Set up Go
140-
uses: actions/setup-go@v6
141-
with:
142-
go-version-file: "go.mod"
143-
144-
- name: Regenerate licenses
145-
id: regen
146-
env:
147-
CI: "true"
148-
run: |
149-
export GOROOT=$(go env GOROOT)
150-
export PATH=${GOROOT}/bin:$PATH
151-
./script/licenses
152-
153-
# Compute hash of license changes only
154-
LICENSE_HASH=$(git diff third-party-licenses.*.md third-party/ | sha256sum | cut -c1-8)
155-
echo "license_hash=${LICENSE_HASH}" >> $GITHUB_OUTPUT
156-
echo "License changes hash: ${LICENSE_HASH}"
62+
Auto-generated by license-check workflow"
63+
git push
15764

158-
- name: Find existing license fix PR
159-
id: find_pr
65+
- name: Check if already commented
66+
if: steps.changes.outcome == 'failure' && steps.push.outcome == 'failure'
67+
id: check_comment
16068
uses: actions/github-script@v7
16169
with:
16270
script: |
163-
const basePR = context.payload.pull_request.number;
164-
const baseBranch = context.payload.pull_request.head.ref;
165-
166-
// Search for existing auto-fix PR
167-
const { data: prs } = await github.rest.pulls.list({
71+
const { data: comments } = await github.rest.issues.listComments({
16872
owner: context.repo.owner,
16973
repo: context.repo.repo,
170-
state: 'open',
171-
base: baseBranch,
172-
sort: 'created',
173-
direction: 'desc'
74+
issue_number: context.issue.number
17475
});
17576
176-
// Find PR with our marker in the title or body
177-
const existingPR = prs.find(pr =>
178-
pr.title.includes('🤖 Auto-fix licenses') &&
179-
pr.body?.includes(`<!-- auto-fix-for-pr:${basePR} -->`)
77+
const alreadyCommented = comments.some(comment =>
78+
comment.user.login === 'github-actions[bot]' &&
79+
comment.body.includes('## ⚠️ License files need updating')
18080
);
18181
182-
if (existingPR) {
183-
core.setOutput('exists', 'true');
184-
core.setOutput('pr_number', existingPR.number);
185-
core.setOutput('pr_branch', existingPR.head.ref);
186-
187-
// Extract hash from PR body
188-
const hashMatch = existingPR.body?.match(/<!-- license-hash:(\w+) -->/);
189-
const oldHash = hashMatch ? hashMatch[1] : '';
190-
core.setOutput('old_hash', oldHash);
191-
192-
core.info(`Found existing PR #${existingPR.number} with hash ${oldHash}`);
193-
} else {
194-
core.setOutput('exists', 'false');
195-
core.info('No existing auto-fix PR found');
196-
}
82+
core.setOutput('already_commented', alreadyCommented ? 'true' : 'false');
19783
198-
- name: Close PR if hash changed (dependencies changed)
199-
id: check_hash_changed
200-
if: |
201-
steps.find_pr.outputs.exists == 'true' &&
202-
steps.find_pr.outputs.old_hash != '' &&
203-
steps.find_pr.outputs.old_hash != steps.regen.outputs.license_hash
84+
- name: Comment with instructions if cannot push
85+
if: steps.changes.outcome == 'failure' && steps.push.outcome == 'failure' && steps.check_comment.outputs.already_commented == 'false'
20486
uses: actions/github-script@v7
20587
with:
20688
script: |
207-
const prNumber = ${{ steps.find_pr.outputs.pr_number }};
208-
const oldHash = '${{ steps.find_pr.outputs.old_hash }}';
209-
const newHash = '${{ steps.regen.outputs.license_hash }}';
210-
21189
await github.rest.issues.createComment({
21290
owner: context.repo.owner,
21391
repo: context.repo.repo,
214-
issue_number: prNumber,
215-
body: `## 🔄 Closing - dependencies changed\n\nThe base PR #${context.payload.pull_request.number} has different license requirements now.\n\n- Old hash: \`${oldHash}\`\n- New hash: \`${newHash}\`\n\nA new auto-fix PR will be created.`
216-
});
217-
218-
await github.rest.pulls.update({
219-
owner: context.repo.owner,
220-
repo: context.repo.repo,
221-
pull_number: prNumber,
222-
state: 'closed'
223-
});
224-
225-
core.setOutput('create_new', 'true');
226-
core.info(`Closed PR #${prNumber} due to hash change`);
227-
228-
- name: Create or update license fix PR
229-
if: steps.find_pr.outputs.exists == 'false' || steps.check_hash_changed.outputs.create_new == 'true'
230-
uses: actions/github-script@v7
231-
with:
232-
script: |
233-
const basePR = context.payload.pull_request.number;
234-
const baseBranch = context.payload.pull_request.head.ref;
235-
const licenseHash = '${{ steps.regen.outputs.license_hash }}';
236-
const branchName = `auto-fix/licenses-for-pr-${basePR}`;
237-
238-
// Create new branch from base PR
239-
const { data: baseRef } = await github.rest.git.getRef({
240-
owner: context.repo.owner,
241-
repo: context.repo.repo,
242-
ref: `heads/${baseBranch}`
243-
});
244-
245-
try {
246-
await github.rest.git.createRef({
247-
owner: context.repo.owner,
248-
repo: context.repo.repo,
249-
ref: `refs/heads/${branchName}`,
250-
sha: baseRef.object.sha
251-
});
252-
} catch (error) {
253-
// Branch might exist, update it
254-
await github.rest.git.updateRef({
255-
owner: context.repo.owner,
256-
repo: context.repo.repo,
257-
ref: `heads/${branchName}`,
258-
sha: baseRef.object.sha,
259-
force: true
260-
});
261-
}
262-
263-
// Checkout the new branch and commit license changes
264-
await exec.exec('git', ['fetch', 'origin', branchName]);
265-
await exec.exec('git', ['checkout', '-B', branchName, `origin/${branchName}`]);
266-
await exec.exec('git', ['config', 'user.name', 'github-actions[bot]']);
267-
await exec.exec('git', ['config', 'user.email', '41898282+github-actions[bot]@users.noreply.github.com']);
268-
269-
// Regenerate licenses on this branch
270-
process.env.CI = 'true';
271-
const goRoot = (await exec.getExecOutput('go', ['env', 'GOROOT'])).stdout.trim();
272-
process.env.GOROOT = goRoot;
273-
process.env.PATH = `${goRoot}/bin:${process.env.PATH}`;
274-
await exec.exec('./script/licenses');
275-
276-
await exec.exec('git', ['add', 'third-party', 'third-party-licenses.*.md']);
277-
await exec.exec('git', ['commit', '-m', `chore: auto-fix license files for PR #${basePR}`]);
278-
await exec.exec('git', ['push', 'origin', branchName, '--force']);
279-
280-
// Create PR
281-
const { data: newPR } = await github.rest.pulls.create({
282-
owner: context.repo.owner,
283-
repo: context.repo.repo,
284-
title: `🤖 Auto-fix licenses for PR #${basePR}`,
285-
head: branchName,
286-
base: baseBranch,
287-
body: `## Automated License Update
288-
289-
This PR automatically updates license files for PR #${basePR}.
290-
291-
### What happened
292-
Dependencies were added/updated in #${basePR}, which requires regenerating license documentation.
293-
294-
### What to do
295-
- **Option 1:** Merge this PR to add the license updates to #${basePR}
296-
- **Option 2:** Manually run \`./script/licenses\` in #${basePR} and push (this PR will auto-close)
92+
issue_number: context.issue.number,
93+
body: `## ⚠️ License files need updating
29794
298-
This PR will automatically close if:
299-
- License files in #${basePR} are updated manually
300-
- Dependencies in #${basePR} change (a new PR will be created)
95+
The license files are out of date. I tried to fix them automatically but don't have permission to push to this branch.
30196

302-
<!-- auto-fix-for-pr:${basePR} -->
303-
<!-- license-hash:${licenseHash} -->`
304-
});
305-
306-
core.info(`Created auto-fix PR #${newPR.number}`);
97+
**Please run:**
98+
\`\`\`bash
99+
script/licenses
100+
git add third-party-licenses.*.md third-party/
101+
git commit -m "chore: regenerate license files"
102+
git push
103+
\`\`\`
307104

308-
# After pushing the fix, check if PR now has no functional changes (just license updates)
309-
# This handles the case where the PR only needed license updates and nothing else
310-
- name: Check if PR is now empty
311-
if: steps.changes.outputs.changed == 'true'
312-
id: empty_check
313-
uses: actions/github-script@v7
314-
with:
315-
script: |
316-
const { data: pr } = await github.rest.pulls.get({
317-
owner: context.repo.owner,
318-
repo: context.repo.repo,
319-
pull_number: context.issue.number
105+
Alternatively, enable "Allow edits by maintainers" in the PR settings so I can fix it automatically.`
320106
});
321-
322-
const { data: files } = await github.rest.pulls.listFiles({
323-
owner: context.repo.owner,
324-
repo: context.repo.repo,
325-
pull_number: context.issue.number
326-
});
327-
328-
// Check if ALL changes are just license files
329-
const nonLicenseFiles = files.filter(f =>
330-
!f.filename.startsWith('third-party-licenses.') &&
331-
!f.filename.startsWith('third-party/')
332-
);
333-
334-
const isEmpty = nonLicenseFiles.length === 0;
335-
336-
// Only close if the PR title/body suggests it wasn't meant to be about licenses
337-
// or if it was created by a bot (which might auto-update dependencies)
338-
const isLicenseFocused =
339-
pr.title.toLowerCase().includes('licen') ||
340-
pr.title.toLowerCase().includes('third-party') ||
341-
pr.body?.toLowerCase().includes('update.*licen');
342-
343-
const shouldClose = isEmpty && !isLicenseFocused && pr.user.type !== 'Bot';
344-
345-
core.setOutput('should_close', shouldClose);
346-
347-
if (isEmpty && isLicenseFocused) {
348-
core.info('PR only has license files but appears to be intentionally about licenses - keeping open');
349-
} else if (isEmpty && pr.user.type === 'Bot') {
350-
core.info('PR is from a bot and only has license files - keeping open (might be dependabot)');
351-
} else if (shouldClose) {
352-
core.info('PR only contains license file changes and appears stale - will close');
353-
} else {
354-
core.info(`PR has ${nonLicenseFiles.length} non-license file changes - keeping open`);
355-
}
356-
357-
- name: Close stale license-only PR
358-
if: steps.changes.outputs.changed == 'true' && steps.empty_check.outputs.should_close == 'true'
359-
uses: actions/github-script@v7
360-
with:
361-
script: |
362-
await github.rest.issues.createComment({
363-
owner: context.repo.owner,
364-
repo: context.repo.repo,
365-
issue_number: context.issue.number,
366-
body: `## 🤖 Auto-closing stale PR
367107

368-
This PR now only contains license file updates with no other functional changes. The license updates have been applied, so closing this PR as complete.
369-
370-
If this PR should have had other changes, please reopen it and add the intended changes.`
371-
});
372-
373-
await github.rest.pulls.update({
374-
owner: context.repo.owner,
375-
repo: context.repo.repo,
376-
pull_number: context.issue.number,
377-
state: 'closed'
378-
});
108+
- name: Fail check if changes needed
109+
if: steps.changes.outcome == 'failure'
110+
run: exit 1
379111

0 commit comments

Comments
 (0)