Skip to content

Commit b6c377c

Browse files
ayeshurunAlon Yeshurun
andauthored
ci: changie-existence workflow (#49)
Co-authored-by: Alon Yeshurun <alonyeshurun+microsoft@microsoft.com>
1 parent 10dee00 commit b6c377c

File tree

1 file changed

+214
-0
lines changed

1 file changed

+214
-0
lines changed
Lines changed: 214 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,214 @@
1+
# yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json
2+
---
3+
name: 🔄 Changelog Existence
4+
5+
on:
6+
pull_request:
7+
branches:
8+
- main
9+
types:
10+
- opened
11+
- reopened
12+
- labeled
13+
- unlabeled
14+
- synchronize
15+
workflow_dispatch:
16+
17+
concurrency:
18+
group: ${{ format('{0}-{1}-{2}-{3}-{4}', github.workflow, github.event_name, github.ref, github.base_ref || null, github.head_ref || null) }}
19+
cancel-in-progress: true
20+
21+
permissions:
22+
pull-requests: write
23+
contents: read
24+
issues: write
25+
26+
jobs:
27+
changelog-existence:
28+
name: 🔄 Check Changelog
29+
if: ${{ !contains(github.event.pull_request.labels.*.name, 'skip-changelog') && github.actor != 'dependabot[bot]' }}
30+
runs-on: ubuntu-latest
31+
env:
32+
BASE_SHA: ${{ github.event.pull_request.base.sha }}
33+
HEAD_SHA: ${{ github.event.pull_request.head.sha }}
34+
steps:
35+
- name: ⤵️ Checkout
36+
uses: actions/checkout@v4
37+
with:
38+
fetch-depth: 0 # Required to access full commit history
39+
40+
- name: ✔️ Check for changelog changes
41+
id: changelog_check
42+
uses: actions/github-script@v6
43+
with:
44+
script: |
45+
const { execSync } = require('child_process');
46+
const base = process.env.BASE_SHA;
47+
const head = process.env.HEAD_SHA;
48+
console.log(`Comparing changes from ${base} to ${head}`)
49+
const output = execSync(`git diff --name-only --no-renames --diff-filter=AM ${base} ${head}`).toString();
50+
const files = output.split('\n').filter(Boolean);
51+
const changelogExists = files.some(file => file.startsWith('.changes/unreleased/') && file.endsWith('.yaml'));
52+
core.setOutput('exists', changelogExists);
53+
54+
- name: 🚧 Setup Node
55+
if: steps.changelog_check.outputs.exists == 'true'
56+
uses: actions/setup-node@v6
57+
with:
58+
node-version: '20'
59+
60+
- name: 🚧 Install Changie
61+
if: steps.changelog_check.outputs.exists == 'true'
62+
run: npm i -g changie
63+
64+
- name: 🔎 Find comment
65+
id: fc
66+
uses: actions/github-script@v6
67+
with:
68+
script: |
69+
const commentMarker = '<!-- changelog -->';
70+
console.log('Searching for existing changelog comment...');
71+
console.log(`PR Number: ${context.issue.number}`);
72+
console.log(`Repo: ${context.repo.repo}`);
73+
console.log(`Owner: ${context.repo.owner}`);
74+
const { data: comments } = await github.rest.issues.listComments({
75+
issue_number: context.issue.number,
76+
owner: context.repo.owner,
77+
repo: context.repo.repo,
78+
});
79+
80+
const comment = comments?.find(c => c.user.login === 'github-actions[bot]' && c.body.includes(commentMarker));
81+
if (comment) {
82+
console.log(`Found existing changelog comment: ${comment.id}`);
83+
return comment.id;
84+
}
85+
console.log('No existing changelog comment found.');
86+
return null;
87+
88+
- name: 🔄 Prepare comment (changelog)
89+
if: steps.changelog_check.outputs.exists == 'true'
90+
run: |
91+
echo -e "# Changelog Preview\n" > changie.md
92+
changie batch patch --dry-run --prerelease 'dev' >> changie.md
93+
cat changie.md >> $GITHUB_STEP_SUMMARY
94+
echo -e "\n<!-- changelog -->" >> changie.md
95+
96+
- name: 📝 Create or Update comment (changelog)
97+
if: steps.changelog_check.outputs.exists == 'true'
98+
uses: actions/github-script@v6
99+
with:
100+
script: |
101+
const fs = require('fs');
102+
const body = fs.readFileSync('changie.md', 'utf8');
103+
console.log('Preparing to create or update changelog comment...');
104+
console.log(`body is: \n\n '${body}'`);
105+
const comment_id = ${{ steps.fc.outputs.result }};
106+
console.log(`Comment ID: ${comment_id}`);
107+
if (comment_id) {
108+
await github.rest.issues.updateComment({
109+
owner: context.repo.owner,
110+
repo: context.repo.repo,
111+
comment_id: comment_id,
112+
body: body,
113+
});
114+
} else {
115+
await github.rest.issues.createComment({
116+
owner: context.repo.owner,
117+
repo: context.repo.repo,
118+
issue_number: context.issue.number,
119+
body: body,
120+
});
121+
}
122+
123+
- name: 🔄 Prepare comment (missing)
124+
if: steps.changelog_check.outputs.exists == 'false'
125+
run: |
126+
echo -e "# 🛑 Changelog entry required to merge\n" > changie.md
127+
echo "Run \`changie new\` to add a new changelog entry" >> changie.md
128+
cat changie.md >> $GITHUB_STEP_SUMMARY
129+
echo -e "\n<!-- changelog -->" >> changie.md
130+
131+
- name: 📝 Create or Update comment (missing)
132+
if: steps.changelog_check.outputs.exists == 'false'
133+
uses: actions/github-script@v6
134+
with:
135+
script: |
136+
const fs = require('fs');
137+
const body = fs.readFileSync('changie.md', 'utf8');
138+
console.log('Preparing to create or update missing changelog comment...');
139+
console.log(`body is: \n\n '${body}'`);
140+
const comment_id = ${{ steps.fc.outputs.result }};
141+
console.log(`Comment ID: ${comment_id}`);
142+
if (comment_id) {
143+
await github.rest.issues.updateComment({
144+
owner: context.repo.owner,
145+
repo: context.repo.repo,
146+
comment_id: comment_id,
147+
body: body,
148+
});
149+
} else {
150+
await github.rest.issues.createComment({
151+
owner: context.repo.owner,
152+
repo: context.repo.repo,
153+
issue_number: context.issue.number,
154+
body: body,
155+
});
156+
}
157+
158+
- name: ✅ Pass if changelog entry exists
159+
if: steps.changelog_check.outputs.exists == 'true'
160+
run: |
161+
echo "✅ Changelog entry exists."
162+
exit 0
163+
164+
- name: 🛑 Fail if changelog entry is missing and required
165+
if: steps.changelog_check.outputs.exists == 'false'
166+
run: |
167+
echo "🛑 Changelog entry required to merge."
168+
exit 1
169+
170+
changelog-skip:
171+
name: ⏭️ Skip Changelog
172+
if: ${{ contains(github.event.pull_request.labels.*.name, 'skip-changelog') || github.actor == 'dependabot[bot]' }}
173+
runs-on: ubuntu-latest
174+
steps:
175+
- name: 🔎 Find comment
176+
if: github.actor != 'dependabot[bot]'
177+
id: fc
178+
uses: actions/github-script@v6
179+
with:
180+
script: |
181+
const commentMarker = '<!-- changelog -->';
182+
const { data: comments } = await github.rest.issues.listComments({
183+
issue_number: context.issue.number,
184+
owner: context.repo.owner,
185+
repo: context.repo.repo,
186+
});
187+
if (!comments) {
188+
return null;
189+
}
190+
const comment = comments.find(c => c.user.login === 'github-actions[bot]' && c.body.includes(commentMarker));
191+
if (comment) {
192+
console.log(`Found existing changelog comment: ${comment.id}`);
193+
return comment.id;
194+
}
195+
console.log('No existing changelog comment found.');
196+
return null;
197+
198+
- name: 🗑️ Delete comment
199+
uses: actions/github-script@v6
200+
if: github.actor != 'dependabot[bot]' && steps.fc.outputs.result != ''
201+
with:
202+
script: |
203+
const commentId = ${{ steps.fc.outputs.result }};
204+
console.log(`Deleting existing changelog comment due to skip label... ID: ${commentId}`);
205+
if (commentId) {
206+
await github.rest.issues.deleteComment({
207+
owner: context.repo.owner,
208+
repo: context.repo.repo,
209+
comment_id: commentId,
210+
});
211+
}
212+
213+
- name: ✅ Pass (skip)
214+
run: exit 0

0 commit comments

Comments
 (0)