@aidc-toolkit/dev 0.9.6-beta → 0.9.7-beta
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/config/release.json +6 -6
- package/copy-workflows.json +0 -20
- package/package.json +5 -8
- package/src/release.ts +191 -121
package/config/release.json
CHANGED
|
@@ -2,24 +2,24 @@
|
|
|
2
2
|
"organization": "aidc-toolkit",
|
|
3
3
|
"repositories": {
|
|
4
4
|
"dev": {
|
|
5
|
-
"version": "0.9.
|
|
5
|
+
"version": "0.9.7-beta"
|
|
6
6
|
},
|
|
7
7
|
"core": {
|
|
8
|
-
"version": "0.9.
|
|
8
|
+
"version": "0.9.7-beta"
|
|
9
9
|
},
|
|
10
10
|
"utility": {
|
|
11
|
-
"version": "0.9.
|
|
11
|
+
"version": "0.9.7-beta"
|
|
12
12
|
},
|
|
13
13
|
"gs1": {
|
|
14
14
|
"name": "gs1",
|
|
15
|
-
"version": "0.9.
|
|
15
|
+
"version": "0.9.7-beta"
|
|
16
16
|
},
|
|
17
17
|
"demo": {
|
|
18
|
-
"version": "0.9.
|
|
18
|
+
"version": "0.9.7-beta"
|
|
19
19
|
},
|
|
20
20
|
"aidc-toolkit.github.io": {
|
|
21
21
|
"directory": "doc",
|
|
22
|
-
"version": "0.9.
|
|
22
|
+
"version": "0.9.7-beta"
|
|
23
23
|
}
|
|
24
24
|
}
|
|
25
25
|
}
|
package/copy-workflows.json
CHANGED
|
@@ -1,25 +1,5 @@
|
|
|
1
1
|
{
|
|
2
2
|
"copyFiles": [
|
|
3
|
-
{
|
|
4
|
-
"from": ".github/workflows/tag.yml",
|
|
5
|
-
"to": "../core/.github/workflows/"
|
|
6
|
-
},
|
|
7
|
-
{
|
|
8
|
-
"from": ".github/workflows/tag.yml",
|
|
9
|
-
"to": "../utility/.github/workflows/"
|
|
10
|
-
},
|
|
11
|
-
{
|
|
12
|
-
"from": ".github/workflows/tag.yml",
|
|
13
|
-
"to": "../gs1/.github/workflows/"
|
|
14
|
-
},
|
|
15
|
-
{
|
|
16
|
-
"from": ".github/workflows/tag.yml",
|
|
17
|
-
"to": "../demo/.github/workflows/"
|
|
18
|
-
},
|
|
19
|
-
{
|
|
20
|
-
"from": ".github/workflows/tag.yml",
|
|
21
|
-
"to": "../doc/.github/workflows/"
|
|
22
|
-
},
|
|
23
3
|
{
|
|
24
4
|
"from": ".github/workflows/release.yml",
|
|
25
5
|
"to": "../core/.github/workflows/"
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aidc-toolkit/dev",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.7-beta",
|
|
4
4
|
"description": "Shared development artefacts for AIDC Toolkit",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -33,18 +33,15 @@
|
|
|
33
33
|
"tsx": "^4.19.2",
|
|
34
34
|
"typescript": "^5.7.2"
|
|
35
35
|
},
|
|
36
|
-
"peerDependencies": {
|
|
37
|
-
"eslint": ">=9"
|
|
38
|
-
},
|
|
39
36
|
"dependencies": {
|
|
40
37
|
"@eslint/js": "^9.16.0",
|
|
41
38
|
"@octokit/types": "^13.6.2",
|
|
42
|
-
"@rollup/rollup-linux-x64-gnu": "^4.28.
|
|
43
|
-
"@stylistic/eslint-plugin": "^2.
|
|
44
|
-
"eslint-config-love": "^
|
|
39
|
+
"@rollup/rollup-linux-x64-gnu": "^4.28.1",
|
|
40
|
+
"@stylistic/eslint-plugin": "^2.12.0",
|
|
41
|
+
"eslint-config-love": "^110.0.0",
|
|
45
42
|
"eslint-plugin-jsdoc": "^50.6.0",
|
|
46
43
|
"octokit": "^4.0.2",
|
|
47
|
-
"typescript-eslint": "^8.
|
|
44
|
+
"typescript-eslint": "^8.17.0",
|
|
48
45
|
"yaml": "^2.6.1"
|
|
49
46
|
}
|
|
50
47
|
}
|
package/src/release.ts
CHANGED
|
@@ -56,6 +56,11 @@ interface PackageConfiguration {
|
|
|
56
56
|
*/
|
|
57
57
|
version: string;
|
|
58
58
|
|
|
59
|
+
/**
|
|
60
|
+
* If true, package is private and not linked by others.
|
|
61
|
+
*/
|
|
62
|
+
private?: boolean;
|
|
63
|
+
|
|
59
64
|
/**
|
|
60
65
|
* Development dependencies.
|
|
61
66
|
*/
|
|
@@ -137,13 +142,18 @@ function run(captureOutput: boolean, command: string, ...args: string[]): string
|
|
|
137
142
|
return captureOutput ? spawnResult.stdout.toString().split("\n").slice(0, -1) : [];
|
|
138
143
|
}
|
|
139
144
|
|
|
145
|
+
/**
|
|
146
|
+
* Supported states.
|
|
147
|
+
*/
|
|
148
|
+
type State = "skipped" | "install" | "build" | "link" | "commit" | "tag" | "push" | "workflow (push)" | "release" | "workflow (release)" | "complete";
|
|
149
|
+
|
|
140
150
|
/**
|
|
141
151
|
* Release.
|
|
142
152
|
*/
|
|
143
153
|
async function release(): Promise<void> {
|
|
144
154
|
const statePath = path.resolve("config/release.state.json");
|
|
145
155
|
|
|
146
|
-
let state: Record<string,
|
|
156
|
+
let state: Record<string, State | undefined> = {};
|
|
147
157
|
|
|
148
158
|
if (fs.existsSync(statePath)) {
|
|
149
159
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment -- Format is controlled by this process.
|
|
@@ -172,7 +182,7 @@ async function release(): Promise<void> {
|
|
|
172
182
|
* @returns
|
|
173
183
|
* Promise.
|
|
174
184
|
*/
|
|
175
|
-
async function step(name: string, stepState:
|
|
185
|
+
async function step(name: string, stepState: State, callback: () => (void | Promise<void>)): Promise<void> {
|
|
176
186
|
const repositoryState = state[name];
|
|
177
187
|
|
|
178
188
|
if (repositoryState === undefined || repositoryState === stepState) {
|
|
@@ -187,7 +197,7 @@ async function release(): Promise<void> {
|
|
|
187
197
|
|
|
188
198
|
state[name] = undefined;
|
|
189
199
|
} finally {
|
|
190
|
-
|
|
200
|
+
saveState();
|
|
191
201
|
}
|
|
192
202
|
}
|
|
193
203
|
}
|
|
@@ -197,6 +207,8 @@ async function release(): Promise<void> {
|
|
|
197
207
|
userAgent: `${configuration.organization} release`
|
|
198
208
|
});
|
|
199
209
|
|
|
210
|
+
let allSkipped = true;
|
|
211
|
+
|
|
200
212
|
for (const name of Object.keys(configuration.repositories)) {
|
|
201
213
|
const repository = configuration.repositories[name];
|
|
202
214
|
|
|
@@ -215,26 +227,6 @@ async function release(): Promise<void> {
|
|
|
215
227
|
throw new Error("Repository has uncommitted changes");
|
|
216
228
|
}
|
|
217
229
|
|
|
218
|
-
const workflowsPath = ".github/workflows/";
|
|
219
|
-
|
|
220
|
-
let hasPushWorkflow = false;
|
|
221
|
-
let hasReleaseWorkflow = false;
|
|
222
|
-
|
|
223
|
-
for (const workflowFile of fs.readdirSync(workflowsPath)) {
|
|
224
|
-
if (workflowFile.endsWith(".yml")) {
|
|
225
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- Workflow configuration format is known.
|
|
226
|
-
const workflowOn = (yamlParse(fs.readFileSync(path.resolve(workflowsPath, workflowFile)).toString()) as WorkflowConfiguration).on;
|
|
227
|
-
|
|
228
|
-
if (workflowOn.push !== undefined && (workflowOn.push.branches === undefined || workflowOn.push.branches.includes("main"))) {
|
|
229
|
-
hasPushWorkflow = true;
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
if (workflowOn.release !== undefined && (workflowOn.release.types === undefined || workflowOn.release.types.includes("published"))) {
|
|
233
|
-
hasReleaseWorkflow = true;
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
|
|
238
230
|
const tag = `v${repository.version}`;
|
|
239
231
|
|
|
240
232
|
const octokitParameterBase = {
|
|
@@ -242,141 +234,219 @@ async function release(): Promise<void> {
|
|
|
242
234
|
repo: name
|
|
243
235
|
};
|
|
244
236
|
|
|
245
|
-
const
|
|
237
|
+
const packageConfigurationPath = "package.json";
|
|
246
238
|
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
*/
|
|
250
|
-
async function validateWorkflow(): Promise<void> {
|
|
251
|
-
while (!await new Promise<void>((resolve) => {
|
|
252
|
-
setTimeout(resolve, 2000);
|
|
253
|
-
}).then(async () => await octokit.rest.actions.listWorkflowRunsForRepo({
|
|
254
|
-
...octokitParameterBase,
|
|
255
|
-
head_sha: commitSHA
|
|
256
|
-
})).then((response) => {
|
|
257
|
-
let workflowRunID = -1;
|
|
239
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment -- Package configuration format is known.
|
|
240
|
+
const packageConfiguration: PackageConfiguration = JSON.parse(fs.readFileSync(packageConfigurationPath).toString());
|
|
258
241
|
|
|
259
|
-
|
|
260
|
-
let completed = false;
|
|
242
|
+
let skipRepository: boolean;
|
|
261
243
|
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
} else if (workflowRunID === -1) {
|
|
267
|
-
workflowRunID = workflowRun.id;
|
|
244
|
+
switch (state[name]) {
|
|
245
|
+
case undefined:
|
|
246
|
+
// No steps have yet been taken; skip if repository is already at the required version.
|
|
247
|
+
skipRepository = packageConfiguration.version === repository.version;
|
|
268
248
|
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
249
|
+
if (!skipRepository) {
|
|
250
|
+
allSkipped = false;
|
|
251
|
+
|
|
252
|
+
packageConfiguration.version = repository.version;
|
|
253
|
+
|
|
254
|
+
const atOrganization = `@${configuration.organization}`;
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* Update dependencies from the organization.
|
|
258
|
+
*
|
|
259
|
+
* @param dependencies
|
|
260
|
+
* Dependencies.
|
|
261
|
+
*
|
|
262
|
+
* @returns
|
|
263
|
+
* List of dependencies that require linking.
|
|
264
|
+
*/
|
|
265
|
+
function updateDependencies(dependencies: Record<string, string> | undefined): string[] {
|
|
266
|
+
const linkDependencies = new Array<string>();
|
|
275
267
|
|
|
276
|
-
if (
|
|
277
|
-
|
|
268
|
+
if (dependencies !== undefined) {
|
|
269
|
+
// eslint-disable-next-line guard-for-in -- Dependency record type is shallow.
|
|
270
|
+
for (const dependency in dependencies) {
|
|
271
|
+
const [dependencyAtOrganization, dependencyRepositoryName] = dependency.split("/");
|
|
272
|
+
|
|
273
|
+
if (dependencyAtOrganization === atOrganization) {
|
|
274
|
+
dependencies[dependency] = `^${configuration.repositories[dependencyRepositoryName].version}`;
|
|
275
|
+
|
|
276
|
+
linkDependencies.push(dependency);
|
|
277
|
+
}
|
|
278
|
+
}
|
|
278
279
|
}
|
|
279
280
|
|
|
280
|
-
|
|
281
|
+
return linkDependencies;
|
|
281
282
|
}
|
|
282
|
-
}
|
|
283
283
|
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
284
|
+
const linkDependencies = updateDependencies(packageConfiguration.devDependencies);
|
|
285
|
+
linkDependencies.push(...updateDependencies(packageConfiguration.dependencies));
|
|
286
|
+
|
|
287
|
+
fs.writeFileSync(packageConfigurationPath, `${JSON.stringify(packageConfiguration, null, 2)}\n`);
|
|
288
|
+
|
|
289
|
+
for (const dependency of linkDependencies) {
|
|
290
|
+
run(false, "npm", "link", dependency);
|
|
291
|
+
}
|
|
292
|
+
} else if (!allSkipped) {
|
|
293
|
+
throw new Error(`Repository ${name} is supposed to be skipped but at least one prior repository has been updated`);
|
|
287
294
|
}
|
|
295
|
+
break;
|
|
288
296
|
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
297
|
+
case "skipped":
|
|
298
|
+
// Repository was skipped on the prior run.
|
|
299
|
+
skipRepository = true;
|
|
300
|
+
break;
|
|
301
|
+
|
|
302
|
+
case "complete":
|
|
303
|
+
// Repository was fully updated on the prior run.
|
|
304
|
+
skipRepository = true;
|
|
305
|
+
|
|
306
|
+
allSkipped = false;
|
|
307
|
+
break;
|
|
308
|
+
|
|
309
|
+
default:
|
|
310
|
+
// Repository failed at some step on the prior run.
|
|
311
|
+
skipRepository = false;
|
|
312
|
+
|
|
313
|
+
allSkipped = false;
|
|
314
|
+
break;
|
|
293
315
|
}
|
|
294
316
|
|
|
295
|
-
|
|
317
|
+
if (!skipRepository) {
|
|
318
|
+
const workflowsPath = ".github/workflows/";
|
|
296
319
|
|
|
297
|
-
|
|
298
|
-
|
|
320
|
+
let hasPushWorkflow = false;
|
|
321
|
+
let hasReleaseWorkflow = false;
|
|
299
322
|
|
|
300
|
-
|
|
323
|
+
if (fs.existsSync(workflowsPath)) {
|
|
324
|
+
for (const workflowFile of fs.readdirSync(workflowsPath)) {
|
|
325
|
+
if (workflowFile.endsWith(".yml")) {
|
|
326
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- Workflow configuration format is known.
|
|
327
|
+
const workflowOn = (yamlParse(fs.readFileSync(path.resolve(workflowsPath, workflowFile)).toString()) as WorkflowConfiguration).on;
|
|
301
328
|
|
|
302
|
-
|
|
303
|
-
|
|
329
|
+
if (workflowOn.push !== undefined && (workflowOn.push.branches === undefined || workflowOn.push.branches.includes("main"))) {
|
|
330
|
+
hasPushWorkflow = true;
|
|
331
|
+
}
|
|
304
332
|
|
|
305
|
-
|
|
333
|
+
if (workflowOn.release !== undefined && (workflowOn.release.types === undefined || workflowOn.release.types.includes("published"))) {
|
|
334
|
+
hasReleaseWorkflow = true;
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
}
|
|
306
339
|
|
|
307
340
|
/**
|
|
308
|
-
*
|
|
309
|
-
*
|
|
310
|
-
* @param dependencies
|
|
311
|
-
* Dependencies.
|
|
341
|
+
* Validate the workflow by waiting for it to complete.
|
|
312
342
|
*/
|
|
313
|
-
function
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
343
|
+
async function validateWorkflow(): Promise<void> {
|
|
344
|
+
const commitSHA = run(true, "git", "rev-parse", "HEAD")[0];
|
|
345
|
+
|
|
346
|
+
let completed = false;
|
|
347
|
+
let queryCount = 0;
|
|
348
|
+
let workflowRunID = -1;
|
|
349
|
+
|
|
350
|
+
do {
|
|
351
|
+
await new Promise<void>((resolve) => {
|
|
352
|
+
setTimeout(resolve, 2000);
|
|
353
|
+
});
|
|
354
|
+
|
|
355
|
+
const response = await octokit.rest.actions.listWorkflowRunsForRepo({
|
|
356
|
+
...octokitParameterBase,
|
|
357
|
+
head_sha: commitSHA
|
|
358
|
+
});
|
|
317
359
|
|
|
318
|
-
|
|
319
|
-
|
|
360
|
+
for (const workflowRun of response.data.workflow_runs) {
|
|
361
|
+
if (workflowRun.status !== "completed") {
|
|
362
|
+
if (workflowRun.id === workflowRunID) {
|
|
363
|
+
process.stdout.write(".");
|
|
364
|
+
} else if (workflowRunID === -1) {
|
|
365
|
+
workflowRunID = workflowRun.id;
|
|
366
|
+
|
|
367
|
+
console.log(`Workflow run ID ${workflowRunID}`);
|
|
368
|
+
} else {
|
|
369
|
+
throw new Error(`Parallel workflow runs for SHA ${commitSHA}`);
|
|
370
|
+
}
|
|
371
|
+
} else if (workflowRun.id === workflowRunID) {
|
|
372
|
+
process.stdout.write("\n");
|
|
373
|
+
|
|
374
|
+
if (workflowRun.conclusion !== "success") {
|
|
375
|
+
throw new Error(`Workflow ${workflowRun.conclusion}`);
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
completed = true;
|
|
320
379
|
}
|
|
321
380
|
}
|
|
322
|
-
|
|
381
|
+
|
|
382
|
+
// Abort if workflow run not started after 10 queries.
|
|
383
|
+
if (++queryCount === 10 && workflowRunID === -1) {
|
|
384
|
+
throw new Error(`Workflow run not started for SHA ${commitSHA}`);
|
|
385
|
+
}
|
|
386
|
+
} while (!completed);
|
|
323
387
|
}
|
|
324
388
|
|
|
325
|
-
|
|
326
|
-
|
|
389
|
+
await step(name, "install", () => {
|
|
390
|
+
run(false, "npm", "install");
|
|
391
|
+
});
|
|
327
392
|
|
|
328
|
-
|
|
329
|
-
|
|
393
|
+
await step(name, "build", () => {
|
|
394
|
+
run(false, "npm", "run", "build", "--if-present");
|
|
395
|
+
});
|
|
330
396
|
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
}).then(async () => {
|
|
335
|
-
await step(name, "git commit", () => {
|
|
336
|
-
run(false, "git", "commit", "--all", `--message=Updated to version ${repository.version}`);
|
|
397
|
+
if (!(packageConfiguration.private ?? false)) {
|
|
398
|
+
await step(name, "link", () => {
|
|
399
|
+
run(false, "npm", "link");
|
|
337
400
|
});
|
|
338
|
-
}
|
|
339
|
-
await step(name, "git tag", () => {
|
|
340
|
-
run(false, "git", "tag", tag);
|
|
341
|
-
});
|
|
342
|
-
}).then(async () => {
|
|
343
|
-
await step(name, "git push", () => {
|
|
344
|
-
run(false, "git", "push", "--atomic", "origin", "main", tag);
|
|
345
|
-
});
|
|
346
|
-
}).then(async () => {
|
|
347
|
-
await step(name, "push workflow", async () => {
|
|
348
|
-
if (hasPushWorkflow) {
|
|
349
|
-
await validateWorkflow();
|
|
350
|
-
}
|
|
351
|
-
});
|
|
352
|
-
}).then(async () => {
|
|
353
|
-
await step(name, "release", async () => {
|
|
354
|
-
const versionSplit = repository.version.split("-");
|
|
355
|
-
const prerelease = versionSplit.length !== 1;
|
|
401
|
+
}
|
|
356
402
|
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
403
|
+
await step(name, "commit", () => {
|
|
404
|
+
run(false, "git", "commit", "--all", `--message=Updated to version ${repository.version}`);
|
|
405
|
+
});
|
|
406
|
+
|
|
407
|
+
await step(name, "tag", () => {
|
|
408
|
+
run(false, "git", "tag", tag);
|
|
409
|
+
});
|
|
410
|
+
|
|
411
|
+
await step(name, "push", () => {
|
|
412
|
+
run(false, "git", "push", "--atomic", "origin", "main", tag);
|
|
413
|
+
});
|
|
414
|
+
|
|
415
|
+
if (hasPushWorkflow) {
|
|
416
|
+
await step(name, "workflow (push)", async () => {
|
|
417
|
+
await validateWorkflow();
|
|
364
418
|
});
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
await step(name, "release", async () => {
|
|
422
|
+
const versionSplit = repository.version.split("-");
|
|
423
|
+
const prerelease = versionSplit.length !== 1;
|
|
424
|
+
|
|
425
|
+
await octokit.rest.repos.createRelease({
|
|
426
|
+
...octokitParameterBase,
|
|
427
|
+
tag_name: tag,
|
|
428
|
+
name: `${prerelease ? `${versionSplit[1].substring(0, 1).toUpperCase()}${versionSplit[1].substring(1)} r` : "R"}elease ${versionSplit[0]}`,
|
|
429
|
+
// TODO Remove "false" override.
|
|
430
|
+
prerelease: false
|
|
370
431
|
});
|
|
371
432
|
});
|
|
372
433
|
|
|
434
|
+
if (hasReleaseWorkflow) {
|
|
435
|
+
await step(name, "workflow (release)", async () => {
|
|
436
|
+
await validateWorkflow();
|
|
437
|
+
});
|
|
438
|
+
}
|
|
439
|
+
|
|
373
440
|
state[name] = "complete";
|
|
374
|
-
|
|
441
|
+
} else {
|
|
442
|
+
state[name] = "skipped";
|
|
375
443
|
}
|
|
444
|
+
|
|
445
|
+
saveState();
|
|
376
446
|
}
|
|
377
447
|
|
|
378
|
-
|
|
379
|
-
|
|
448
|
+
// All repositories released.
|
|
449
|
+
fs.rmSync(statePath);
|
|
380
450
|
}
|
|
381
451
|
|
|
382
452
|
await release().catch((e: unknown) => {
|