-
-
Notifications
You must be signed in to change notification settings - Fork 16
295 lines (255 loc) · 13.1 KB
/
ci-sync.yml
File metadata and controls
295 lines (255 loc) · 13.1 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
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
# Updated ci-sync.yml - Safe, Non-Destructive Sync
name: CI - Sync Branches
on:
workflow_dispatch:
workflow_call:
secrets:
UNITY_PAT:
required: true
description: 'GitHub PAT with admin permissions'
jobs:
sync_dev_with_main:
name: 'Sync Dev with Main (Safe)'
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Safe sync dev with main
uses: actions/github-script@v8
with:
github-token: ${{ secrets.UNITY_PAT }}
script: |
const { owner, repo } = context.repo;
// Get main and dev branch info
const main = await github.rest.repos.getBranch({ owner, repo, branch: 'main' });
const mainSha = main.data.commit.sha;
let devSha;
try {
const dev = await github.rest.repos.getBranch({ owner, repo, branch: 'dev' });
devSha = dev.data.commit.sha;
} catch (e) {
if (e.status === 404) {
// Dev doesn't exist, safe to create from main
await github.rest.git.createRef({
owner,
repo,
ref: 'refs/heads/dev',
sha: mainSha,
});
console.log(`✅ Created dev branch at ${mainSha}`);
return;
}
throw e;
}
if (devSha === mainSha) {
console.log('✅ Dev is already up to date with main');
return;
}
// Compare branches to understand the relationship
const comparison = await github.rest.repos.compareCommits({
owner,
repo,
base: mainSha,
head: devSha
});
if (comparison.data.ahead_by === 0) {
// Dev is behind main - safe to fast-forward
console.log(`📈 Dev is ${comparison.data.behind_by} commits behind main`);
console.log('Safe to fast-forward dev to main');
await github.rest.git.updateRef({
owner,
repo,
ref: 'heads/dev',
sha: mainSha,
force: false,
});
console.log('✅ Fast-forwarded dev to match main');
} else {
// Dev has atomic commits that aren't in main yet
console.log(`📊 Dev status:`);
console.log(` - ${comparison.data.ahead_by} commits ahead of main`);
console.log(` - ${comparison.data.behind_by} commits behind main`);
console.log('🟡 Dev has unreleased atomic commits - preserving them');
// Write to job summary instead of creating an issue
await core.summary
.addHeading('Dev Sync Status', 2)
.addRaw(`Dev branch contains **${comparison.data.ahead_by} atomic commits** not yet released to main.\n\n`)
.addTable([
[{data: 'Metric', header: true}, {data: 'Value', header: true}],
['Dev ahead by', `${comparison.data.ahead_by} commits`],
['Dev behind by', `${comparison.data.behind_by} commits`],
['Last main commit', `\`${mainSha.substring(0, 7)}\``],
['Last dev commit', `\`${devSha.substring(0, 7)}\``],
])
.addRaw('\n**This is normal.** Dev contains work-in-progress atomic commits that will be released through the dev→main promotion process. No action needed.')
.write();
console.log('📝 Wrote status to job summary');
}
# Cleanup jobs
cleanup_orphaned_atomic_branches:
name: 'Cleanup Orphaned Atomic Branches'
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Cleanup merged atomic branches
uses: actions/github-script@v8
with:
github-token: ${{ secrets.UNITY_PAT }}
script: |
const { owner, repo } = context.repo;
// Get all branches
const branches = await github.rest.repos.listBranches({
owner,
repo,
per_page: 100
});
const atomicBranches = branches.data.filter(branch =>
branch.name.startsWith('atom-')
);
console.log(`Found ${atomicBranches.length} atomic branches`);
for (const branch of atomicBranches) {
try {
// Check if branch is merged into dev
const comparison = await github.rest.repos.compareCommits({
owner,
repo,
base: 'dev',
head: branch.name
});
if (comparison.data.ahead_by === 0) {
// Branch is fully merged into dev, safe to delete
await github.rest.git.deleteRef({
owner,
repo,
ref: `heads/${branch.name}`
});
console.log(`🗑️ Deleted merged atomic branch: ${branch.name}`);
} else {
console.log(`⚠️ Keeping unmerged atomic branch: ${branch.name}`);
}
} catch (error) {
console.log(`❌ Error processing ${branch.name}: ${error.message}`);
}
}
close_resolved_merge_conflict_issues:
name: 'Close Resolved Merge Conflict Issues'
runs-on: ubuntu-latest
permissions:
issues: write
steps:
- name: Close resolved merge conflict issues
uses: actions/github-script@v8
with:
github-token: ${{ secrets.UNITY_PAT }}
script: |
// Find open merge conflict issues for atomic branches
const openIssues = await github.rest.issues.listForRepo({
owner: context.repo.owner,
repo: context.repo.repo,
state: 'open',
labels: 'merge-conflict'
});
console.log(`Found ${openIssues.data.length} open merge conflict issues`);
for (const issue of openIssues.data) {
// Extract branch name from issue title
const branchMatch = issue.title.match(/Merge conflict: (atom-[a-zA-Z0-9-]+)/);
if (branchMatch) {
const branchName = branchMatch[1];
// Check if the atomic branch still exists
try {
await github.rest.repos.getBranch({
owner: context.repo.owner,
repo: context.repo.repo,
branch: branchName
});
console.log(`Branch ${branchName} still exists, keeping issue open`);
} catch (error) {
if (error.status === 404) {
// Branch doesn't exist anymore, close the issue
await github.rest.issues.update({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: issue.number,
state: 'closed'
});
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: issue.number,
body: `Merge conflict resolved! The atomic branch \`${branchName}\` was successfully merged and deleted.`
});
console.log(`Closed merge conflict issue #${issue.number} for ${branchName}`);
}
}
}
}
close_explicit_keyword_issues:
name: 'Close Issues with Explicit Keywords'
runs-on: ubuntu-latest
permissions:
issues: write
steps:
- name: Close issues with explicit keywords
uses: actions/github-script@v8
with:
github-token: ${{ secrets.UNITY_PAT }}
script: |
// Get recent commits on main
const commits = await github.rest.repos.listCommits({
owner: context.repo.owner,
repo: context.repo.repo,
sha: 'main',
per_page: 10
});
console.log(`Checking ${commits.data.length} recent commits for explicit issue closures...`);
const closedIssues = new Set();
for (const commit of commits.data) {
const message = commit.commit.message;
// ONLY explicit close keywords WITH exclamation mark prefix
const issuePatterns = [
/!(?:fix(?:es)?|close(?:s)?|resolve(?:s)?)\s+#(\d+)/gi
];
for (const pattern of issuePatterns) {
let match;
while ((match = pattern.exec(message)) !== null) {
const issueNumber = parseInt(match[1]);
if (!closedIssues.has(issueNumber)) {
closedIssues.add(issueNumber);
console.log(`Found explicit closure: ${match[0]} -> #${issueNumber}`);
}
}
}
}
// Close the explicitly referenced issues
for (const issueNumber of closedIssues) {
try {
const issue = await github.rest.issues.get({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: issueNumber
});
if (issue.data.state === 'open') {
await github.rest.issues.update({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: issueNumber,
state: 'closed'
});
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: issueNumber,
body: `This issue was automatically closed because it was explicitly resolved in the main branch.`
});
console.log(`Closed issue #${issueNumber}`);
}
} catch (error) {
console.log(`Error processing issue #${issueNumber}: ${error.message}`);
}
}
if (closedIssues.size === 0) {
console.log('No explicit issue closures found');
} else {
console.log(`Processed ${closedIssues.size} explicit issue closure(s)`);
}