@aidc-toolkit/dev 0.9.16-beta → 0.9.17-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.
@@ -0,0 +1,53 @@
1
+ {
2
+ "organization": "aidc-toolkit",
3
+ "repositories": {
4
+ "dev": {
5
+ "dependencyType": "external",
6
+ "excludeFiles": [
7
+ "src/publish.ts",
8
+ "src/publish-internal.ts",
9
+ "src/publish-external.ts",
10
+ "config/publish.json",
11
+ "config/publish.secure.json"
12
+ ],
13
+ "lastExternalVersion": "0.9.16-beta",
14
+ "lastInternalPublished": "2025-03-02T19:42:30.219Z"
15
+ },
16
+ "core": {
17
+ "dependencyType": "external",
18
+ "lastExternalVersion": "0.9.16-beta",
19
+ "lastInternalPublished": "2025-03-02T19:46:27.439Z"
20
+ },
21
+ "utility": {
22
+ "dependencyType": "external",
23
+ "lastExternalVersion": "0.9.16-beta",
24
+ "lastInternalPublished": "2025-03-02T19:46:37.087Z"
25
+ },
26
+ "gs1": {
27
+ "dependencyType": "external",
28
+ "lastExternalVersion": "0.9.16-beta",
29
+ "lastInternalPublished": "2025-03-02T19:46:50.266Z"
30
+ },
31
+ "demo": {
32
+ "dependencyType": "none",
33
+ "lastExternalVersion": "0.9.16-beta"
34
+ },
35
+ "app-extension": {
36
+ "dependencyType": "internal",
37
+ "lastExternalVersion": "0.9.16-beta",
38
+ "internal": true,
39
+ "lastInternalPublished": "2025-03-02T19:47:50.192Z"
40
+ },
41
+ "app-generator": {
42
+ "dependencyType": "internal",
43
+ "lastExternalVersion": "0.9.16-beta",
44
+ "internal": true,
45
+ "lastInternalPublished": "2025-03-02T19:47:58.982Z"
46
+ },
47
+ "aidc-toolkit.github.io": {
48
+ "directory": "doc",
49
+ "dependencyType": "none",
50
+ "lastExternalVersion": "0.9.16-beta"
51
+ }
52
+ }
53
+ }
package/dev.iml ADDED
@@ -0,0 +1,9 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <module type="WEB_MODULE" version="4">
3
+ <component name="NewModuleRootManager" inherit-compiler-output="true">
4
+ <exclude-output />
5
+ <content url="file://$MODULE_DIR$" />
6
+ <orderEntry type="inheritedJdk" />
7
+ <orderEntry type="sourceFolder" forTests="false" />
8
+ </component>
9
+ </module>
package/dist/index.d.ts CHANGED
@@ -15,5 +15,5 @@
15
15
  * limitations under the License.
16
16
  */
17
17
  export * from "./eslint-config-template.js";
18
- export * from "./publish-dev.js";
18
+ export * from "./utility.js";
19
19
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,cAAc,6BAA6B,CAAC;AAC5C,cAAc,kBAAkB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,cAAc,6BAA6B,CAAC;AAC5C,cAAc,cAAc,CAAC"}
package/dist/index.js CHANGED
@@ -15,5 +15,5 @@
15
15
  * limitations under the License.
16
16
  */
17
17
  export * from "./eslint-config-template.js";
18
- export * from "./publish-dev.js";
18
+ export * from "./utility.js";
19
19
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,cAAc,6BAA6B,CAAC;AAC5C,cAAc,kBAAkB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,cAAc,6BAA6B,CAAC;AAC5C,cAAc,cAAc,CAAC"}
@@ -1,3 +1,8 @@
1
+ import { Logger } from "tslog";
2
+ /**
3
+ * Logger with a default minimum level of Info.
4
+ */
5
+ export declare const logger: Logger<unknown>;
1
6
  /**
2
7
  * Run a command and optionally capture its output.
3
8
  *
@@ -14,4 +19,4 @@
14
19
  * Output if captured or empty array if not.
15
20
  */
16
21
  export declare function run(captureOutput: boolean, command: string, ...args: string[]): string[];
17
- //# sourceMappingURL=command-util.d.ts.map
22
+ //# sourceMappingURL=utility.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utility.d.ts","sourceRoot":"","sources":["../src/utility.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,MAAM,OAAO,CAAC;AAS/B;;GAEG;AACH,eAAO,MAAM,MAAM,iBAEjB,CAAC;AAEH;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,GAAG,CAAC,aAAa,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,CA0BxF"}
@@ -0,0 +1,57 @@
1
+ import { spawnSync } from "child_process";
2
+ import { Logger } from "tslog";
3
+ /**
4
+ * Log level.
5
+ */
6
+ var LogLevel;
7
+ (function (LogLevel) {
8
+ LogLevel[LogLevel["Silly"] = 0] = "Silly";
9
+ LogLevel[LogLevel["Trace"] = 1] = "Trace";
10
+ LogLevel[LogLevel["Debug"] = 2] = "Debug";
11
+ LogLevel[LogLevel["Info"] = 3] = "Info";
12
+ LogLevel[LogLevel["Warn"] = 4] = "Warn";
13
+ LogLevel[LogLevel["Error"] = 5] = "Error";
14
+ LogLevel[LogLevel["Fatal"] = 6] = "Fatal";
15
+ })(LogLevel || (LogLevel = {}));
16
+ /**
17
+ * Logger with a default minimum level of Info.
18
+ */
19
+ export const logger = new Logger({
20
+ minLevel: LogLevel.Info
21
+ });
22
+ /**
23
+ * Run a command and optionally capture its output.
24
+ *
25
+ * @param captureOutput
26
+ * If true, output is captured and returned.
27
+ *
28
+ * @param command
29
+ * Command to run.
30
+ *
31
+ * @param args
32
+ * Arguments to command.
33
+ *
34
+ * @returns
35
+ * Output if captured or empty array if not.
36
+ */
37
+ export function run(captureOutput, command, ...args) {
38
+ logger.debug(`Running command "${command}" with arguments ${JSON.stringify(args)}.`);
39
+ const spawnResult = spawnSync(command, args, {
40
+ stdio: ["inherit", captureOutput ? "pipe" : "inherit", "inherit"]
41
+ });
42
+ if (spawnResult.error !== undefined) {
43
+ throw spawnResult.error;
44
+ }
45
+ if (spawnResult.status === null) {
46
+ throw new Error(`Terminated by signal ${spawnResult.signal}`);
47
+ }
48
+ if (spawnResult.status !== 0) {
49
+ throw new Error(`Failed with status ${spawnResult.status}`);
50
+ }
51
+ const output = captureOutput ? spawnResult.stdout.toString().split("\n").slice(0, -1) : [];
52
+ if (captureOutput) {
53
+ logger.debug(`Output is ${JSON.stringify(output)}.`);
54
+ }
55
+ return output;
56
+ }
57
+ //# sourceMappingURL=utility.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utility.js","sourceRoot":"","sources":["../src/utility.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAC1C,OAAO,EAAE,MAAM,EAAE,MAAM,OAAO,CAAC;AAE/B;;GAEG;AACH,IAAK,QAEJ;AAFD,WAAK,QAAQ;IACT,yCAAK,CAAA;IAAE,yCAAK,CAAA;IAAE,yCAAK,CAAA;IAAE,uCAAI,CAAA;IAAE,uCAAI,CAAA;IAAE,yCAAK,CAAA;IAAE,yCAAK,CAAA;AACjD,CAAC,EAFI,QAAQ,KAAR,QAAQ,QAEZ;AAED;;GAEG;AACH,MAAM,CAAC,MAAM,MAAM,GAAG,IAAI,MAAM,CAAC;IAC7B,QAAQ,EAAE,QAAQ,CAAC,IAAI;CAC1B,CAAC,CAAC;AAEH;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,GAAG,CAAC,aAAsB,EAAE,OAAe,EAAE,GAAG,IAAc;IAC1E,MAAM,CAAC,KAAK,CAAC,oBAAoB,OAAO,oBAAoB,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAErF,MAAM,WAAW,GAAG,SAAS,CAAC,OAAO,EAAE,IAAI,EAAE;QACzC,KAAK,EAAE,CAAC,SAAS,EAAE,aAAa,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,EAAE,SAAS,CAAC;KACpE,CAAC,CAAC;IAEH,IAAI,WAAW,CAAC,KAAK,KAAK,SAAS,EAAE,CAAC;QAClC,MAAM,WAAW,CAAC,KAAK,CAAC;IAC5B,CAAC;IAED,IAAI,WAAW,CAAC,MAAM,KAAK,IAAI,EAAE,CAAC;QAC9B,MAAM,IAAI,KAAK,CAAC,wBAAwB,WAAW,CAAC,MAAM,EAAE,CAAC,CAAC;IAClE,CAAC;IAED,IAAI,WAAW,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC3B,MAAM,IAAI,KAAK,CAAC,sBAAsB,WAAW,CAAC,MAAM,EAAE,CAAC,CAAC;IAChE,CAAC;IAED,MAAM,MAAM,GAAG,aAAa,CAAC,CAAC,CAAC,WAAW,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IAE3F,IAAI,aAAa,EAAE,CAAC;QAChB,MAAM,CAAC,KAAK,CAAC,aAAa,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IACzD,CAAC;IAED,OAAO,MAAM,CAAC;AAClB,CAAC"}
package/eslint.config.ts CHANGED
@@ -1,3 +1,3 @@
1
- import { esLintConfigAIDCToolkit } from "./dist/index.js";
1
+ import { esLintConfigAIDCToolkit } from "./dist";
2
2
 
3
3
  export default esLintConfigAIDCToolkit;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aidc-toolkit/dev",
3
- "version": "0.9.16-beta",
3
+ "version": "0.9.17-beta",
4
4
  "description": "Shared development artefacts for AIDC Toolkit",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -25,11 +25,8 @@
25
25
  "build:dev": "npm run build:core -- tsconfig-build-dev-local.json",
26
26
  "build:release": "npm run build:core -- tsconfig-build-local.json",
27
27
  "build:doc": "npm run build:dev",
28
- "publish-dev": "bin/publish-dev-local",
29
- "release": "tsx src/release.ts"
30
- },
31
- "bin": {
32
- "publish-dev": "bin/publish-dev"
28
+ "publish-internal": "tsx src/publish-internal.ts",
29
+ "publish-external": "tsx src/publish-external.ts"
33
30
  },
34
31
  "devDependencies": {
35
32
  "copy-files-from-to": "^3.12.1"
@@ -45,6 +42,7 @@
45
42
  "octokit": "^4.1.2",
46
43
  "rimraf": "^6.0.1",
47
44
  "ts-node": "^10.9.2",
45
+ "tslog": "^4.9.3",
48
46
  "tsx": "^4.19.3",
49
47
  "typescript": "^5.7.3",
50
48
  "typescript-eslint": "^8.25.0",
package/src/index.ts CHANGED
@@ -15,4 +15,4 @@
15
15
  * limitations under the License.
16
16
  */
17
17
  export * from "./eslint-config-template.js";
18
- export * from "./publish-dev.js";
18
+ export * from "./utility.js";
@@ -0,0 +1,332 @@
1
+ import * as fs from "fs";
2
+ import * as path from "node:path";
3
+ import * as util from "node:util";
4
+ import { Octokit } from "octokit";
5
+ import { parse as yamlParse } from "yaml";
6
+ import {
7
+ anyChanges,
8
+ configuration,
9
+ organizationRepository, type PackageConfiguration,
10
+ publishRepositories,
11
+ type Repository,
12
+ saveConfiguration,
13
+ secureConfiguration
14
+ } from "./publish";
15
+ import { logger, run } from "./utility.js";
16
+
17
+ /**
18
+ * Configuration layout of release.yml workflow (relevant attributes only).
19
+ */
20
+ interface WorkflowConfiguration {
21
+ /**
22
+ * Workflow name.
23
+ */
24
+ name: string;
25
+
26
+ /**
27
+ * Workflow trigger.
28
+ */
29
+ on: {
30
+ /**
31
+ * Push trigger.
32
+ */
33
+ push?: {
34
+ /**
35
+ * Push branches.
36
+ */
37
+ branches?: string[];
38
+ };
39
+
40
+ /**
41
+ * Release trigger.
42
+ */
43
+ release?: {
44
+ /**
45
+ * Release types.
46
+ */
47
+ types?: string[];
48
+ };
49
+ };
50
+ }
51
+
52
+ /**
53
+ * Supported steps.
54
+ */
55
+ type Step =
56
+ "skipped" | "install" | "build" | "commit" | "tag" | "push" | "workflow (push)" | "release" | "workflow (release)" | "restore alpha" | "complete";
57
+
58
+ /**
59
+ * Execute a step.
60
+ *
61
+ * @param repository
62
+ * Repository.
63
+ *
64
+ * @param step
65
+ * State at which step takes place.
66
+ *
67
+ * @param callback
68
+ * Callback to execute step.
69
+ *
70
+ * @returns
71
+ * Promise.
72
+ */
73
+ async function runStep(repository: Repository, step: Step, callback: () => (void | Promise<void>)): Promise<void> {
74
+ if (repository.publishExternalStep === undefined || repository.publishExternalStep === step) {
75
+ logger.debug(`Running step ${step}`);
76
+
77
+ repository.publishExternalStep = step;
78
+
79
+ try {
80
+ const result = callback();
81
+
82
+ if (result instanceof Promise) {
83
+ await result;
84
+ }
85
+
86
+ repository.publishExternalStep = undefined;
87
+ } finally {
88
+ saveConfiguration();
89
+ }
90
+ } else {
91
+ logger.debug(`Skipping step ${step}`);
92
+ }
93
+ }
94
+
95
+ /**
96
+ * Update dependencies from the organization.
97
+ *
98
+ * @param restoreAlpha
99
+ * If true, restore "alpha" as the version for development.
100
+ *
101
+ * @param development
102
+ * True if updating development dependencies.
103
+ *
104
+ * @param internal
105
+ * True if the package is for internal use only and its version should not be used in dependencies.
106
+ *
107
+ * @param dependencies
108
+ * Dependencies.
109
+ *
110
+ * @returns
111
+ * True if any dependencies were updated.
112
+ */
113
+ function updateDependencies(restoreAlpha: boolean, development: boolean, internal: boolean | undefined, dependencies: Record<string, string> | undefined): boolean {
114
+ let anyUpdated = false;
115
+
116
+ if (dependencies !== undefined) {
117
+ // eslint-disable-next-line guard-for-in -- Dependency record type is shallow.
118
+ for (const dependency in dependencies) {
119
+ const dependencyRepositoryName = organizationRepository(dependency);
120
+
121
+ if (dependencyRepositoryName !== null) {
122
+ const dependencyRepository = configuration.repositories[dependencyRepositoryName];
123
+
124
+ // Set to explicit version for external dependency.
125
+ if (dependencyRepository.dependencyType === "external") {
126
+ dependencies[dependency] = !restoreAlpha ? `^${dependencyRepository.lastExternalVersion}` : "alpha";
127
+ anyUpdated = true;
128
+ } else if (!restoreAlpha && !development && internal !== true) {
129
+ throw new Error("Internal dependency specified for external package");
130
+ }
131
+ }
132
+ }
133
+ }
134
+
135
+ return anyUpdated;
136
+ }
137
+
138
+ const octokit = new Octokit({
139
+ auth: secureConfiguration.token,
140
+ userAgent: `${configuration.organization} release`
141
+ });
142
+
143
+ await publishRepositories(async (name, repository) => {
144
+ // Repository must be on main branch.
145
+ if (run(true, "git", "branch", "--show-current")[0] !== "main") {
146
+ throw new Error("Repository is not on main branch");
147
+ }
148
+
149
+ const packageConfigurationPath = "package.json";
150
+
151
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment -- Package configuration format is known.
152
+ const packageConfiguration: PackageConfiguration = JSON.parse(fs.readFileSync(packageConfigurationPath).toString());
153
+
154
+ let publish: boolean;
155
+
156
+ if (repository.publishExternalStep === undefined) {
157
+ publish = anyChanges(repository, true);
158
+ } else {
159
+ publish = true;
160
+ }
161
+
162
+ if (packageConfiguration.version !== repository.lastExternalVersion) {
163
+ // Package version has already been updated, either manually or by previous failed run.
164
+ publish = true;
165
+ } else if (publish) {
166
+ const packageVersionSplits = packageConfiguration.version.split("-");
167
+
168
+ // Extract semantic version and pre-release identifier.
169
+ const semanticVersion = packageVersionSplits[0];
170
+ const preReleaseIdentifier = packageVersionSplits.length !== 1 ? `-${packageVersionSplits[1]}` : "";
171
+
172
+ // Parse semantic version into its components.
173
+ const [majorVersion, minorVersion, patchVersion] = semanticVersion.split(".").map(versionString => Number(versionString));
174
+
175
+ // Increment patch version number.
176
+ packageConfiguration.version = `${majorVersion}.${minorVersion}.${patchVersion + 1}${preReleaseIdentifier}`;
177
+ }
178
+
179
+ const tag = `v${packageConfiguration.version}`;
180
+
181
+ const octokitParameterBase = {
182
+ owner: configuration.organization,
183
+ repo: name
184
+ };
185
+
186
+ const internal = repository.dependencyType === "internal";
187
+
188
+ if (publish) {
189
+ if (repository.publishExternalStep === undefined) {
190
+ updateDependencies(false, true, internal, packageConfiguration.devDependencies);
191
+ updateDependencies(false, false, internal, packageConfiguration.dependencies);
192
+
193
+ fs.writeFileSync(packageConfigurationPath, `${JSON.stringify(packageConfiguration, null, 2)}\n`);
194
+ } else {
195
+ logger.debug(`Repository failed at step ${repository.publishExternalStep} on prior run`);
196
+ }
197
+
198
+ const workflowsPath = ".github/workflows/";
199
+
200
+ let hasPushWorkflow = false;
201
+ let hasReleaseWorkflow = false;
202
+
203
+ if (fs.existsSync(workflowsPath)) {
204
+ logger.debug("Checking workflows");
205
+
206
+ for (const workflowFile of fs.readdirSync(workflowsPath).filter(workflowFile => workflowFile.endsWith(".yml"))) {
207
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- Workflow configuration format is known.
208
+ const workflowOn = (yamlParse(fs.readFileSync(path.resolve(workflowsPath, workflowFile)).toString()) as WorkflowConfiguration).on;
209
+
210
+ if (workflowOn.push !== undefined && (workflowOn.push.branches === undefined || workflowOn.push.branches.includes("main"))) {
211
+ logger.debug("Repository has push workflow");
212
+
213
+ hasPushWorkflow = true;
214
+ }
215
+
216
+ if (workflowOn.release !== undefined && (workflowOn.release.types === undefined || workflowOn.release.types.includes("published"))) {
217
+ logger.debug("Repository has release workflow");
218
+
219
+ hasReleaseWorkflow = true;
220
+ }
221
+ }
222
+ }
223
+
224
+ /**
225
+ * Validate the workflow by waiting for it to complete.
226
+ */
227
+ async function validateWorkflow(): Promise<void> {
228
+ const commitSHA = run(true, "git", "rev-parse", "HEAD")[0];
229
+
230
+ let completed = false;
231
+ let queryCount = 0;
232
+ let workflowRunID = -1;
233
+
234
+ do {
235
+ await util.promisify(setTimeout)(2000);
236
+
237
+ const response = await octokit.rest.actions.listWorkflowRunsForRepo({
238
+ ...octokitParameterBase,
239
+ head_sha: commitSHA
240
+ });
241
+
242
+ for (const workflowRun of response.data.workflow_runs) {
243
+ if (workflowRun.status !== "completed") {
244
+ if (workflowRun.id === workflowRunID) {
245
+ process.stdout.write(".");
246
+ } else if (workflowRunID === -1) {
247
+ workflowRunID = workflowRun.id;
248
+
249
+ logger.info(`Workflow run ID ${workflowRunID}`);
250
+ } else {
251
+ throw new Error(`Parallel workflow runs for SHA ${commitSHA}`);
252
+ }
253
+ } else if (workflowRun.id === workflowRunID) {
254
+ process.stdout.write("\n");
255
+
256
+ if (workflowRun.conclusion !== "success") {
257
+ throw new Error(`Workflow ${workflowRun.conclusion}`);
258
+ }
259
+
260
+ completed = true;
261
+ }
262
+ }
263
+
264
+ // Abort if workflow run not started after 10 queries.
265
+ if (++queryCount === 10 && workflowRunID === -1) {
266
+ throw new Error(`Workflow run not started for SHA ${commitSHA}`);
267
+ }
268
+ } while (!completed);
269
+ }
270
+
271
+ await runStep(repository, "install", () => {
272
+ run(false, "npm", "install");
273
+ });
274
+
275
+ await runStep(repository, "build", () => {
276
+ run(false, "npm", "run", "build", "--if-present");
277
+ });
278
+
279
+ await runStep(repository, "commit", () => {
280
+ run(false, "git", "commit", "--all", `--message=Updated to version ${packageConfiguration.version}.`);
281
+ });
282
+
283
+ await runStep(repository, "tag", () => {
284
+ run(false, "git", "tag", tag);
285
+ });
286
+
287
+ await runStep(repository, "push", () => {
288
+ run(false, "git", "push", "--atomic", "origin", "main", tag);
289
+ });
290
+
291
+ if (hasPushWorkflow) {
292
+ await runStep(repository, "workflow (push)", async () => {
293
+ await validateWorkflow();
294
+ });
295
+ }
296
+
297
+ await runStep(repository, "release", async () => {
298
+ const versionSplit = packageConfiguration.version.split("-");
299
+ const prerelease = versionSplit.length !== 1;
300
+
301
+ await octokit.rest.repos.createRelease({
302
+ ...octokitParameterBase,
303
+ tag_name: tag,
304
+ name: `${prerelease ? `${versionSplit[1].substring(0, 1).toUpperCase()}${versionSplit[1].substring(1)}` : "Production"} release ${versionSplit[0]}`,
305
+ prerelease
306
+ });
307
+ });
308
+
309
+ if (hasReleaseWorkflow) {
310
+ await runStep(repository, "workflow (release)", async () => {
311
+ await validateWorkflow();
312
+ });
313
+ }
314
+
315
+ await runStep(repository, "restore alpha", () => {
316
+ // Restore dependencies to "alpha" version for development.
317
+ const devDependenciesUpdated = updateDependencies(true, true, internal, packageConfiguration.devDependencies);
318
+ const dependenciesUpdated = updateDependencies(true, false, internal, packageConfiguration.dependencies);
319
+
320
+ if (devDependenciesUpdated || dependenciesUpdated) {
321
+ fs.writeFileSync(packageConfigurationPath, `${JSON.stringify(packageConfiguration, null, 2)}\n`);
322
+ run(false, "git", "commit", "--all", "--message=Restored alpha version.");
323
+ }
324
+ });
325
+
326
+ repository.lastExternalPublished = new Date().toISOString();
327
+ repository.lastExternalVersion = packageConfiguration.version;
328
+ repository.publishExternalStep = undefined;
329
+ }
330
+ }).catch((e: unknown) => {
331
+ logger.error(e);
332
+ });
@@ -0,0 +1,111 @@
1
+ import * as fs from "fs";
2
+ import { anyChanges, organizationRepository, type PackageConfiguration, publishRepositories } from "./publish";
3
+ import { logger, run } from "./utility.js";
4
+
5
+ /**
6
+ * Check dependencies for belonging to the organization; if not, check for updates and log a message if an update is
7
+ * available.
8
+ *
9
+ * @param dependencies
10
+ * Dependencies.
11
+ *
12
+ * @returns
13
+ * Dependencies belonging to the organization.
14
+ */
15
+ function checkDependencyUpdates(dependencies?: Record<string, string>): string[] {
16
+ const organizationDependencies = [];
17
+
18
+ if (dependencies !== undefined) {
19
+ for (const [dependency, version] of Object.entries(dependencies)) {
20
+ if (organizationRepository(dependency) !== null) {
21
+ organizationDependencies.push(dependency);
22
+ } else if (version.startsWith("^")) {
23
+ const [latestVersion] = run(true, "npm", "view", dependency, "version");
24
+
25
+ if (latestVersion !== version.substring(1)) {
26
+ logger.info(`Dependency ${dependency}@${version} pending update to version ${latestVersion}.`);
27
+ }
28
+ }
29
+ }
30
+ }
31
+
32
+ return organizationDependencies;
33
+ }
34
+
35
+ /**
36
+ * Convert a number to a zero-padded string.
37
+ *
38
+ * @param n
39
+ * Number.
40
+ *
41
+ * @param length
42
+ * Length of required string.
43
+ *
44
+ * @returns
45
+ * Zero-padded string.
46
+ */
47
+ function zeroPadded(n: number, length: number): string {
48
+ return `${"0".repeat(length - 1)}${n}`.slice(-length);
49
+ }
50
+
51
+ await publishRepositories((_name, repository) => {
52
+ const packageConfigurationPath = "package.json";
53
+
54
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment -- Package configuration format is known.
55
+ const packageConfiguration: PackageConfiguration = JSON.parse(fs.readFileSync(packageConfigurationPath).toString());
56
+
57
+ // Check dependency updates, even if there are no changes.
58
+ const organizationDependencies = [...checkDependencyUpdates(packageConfiguration.devDependencies), ...checkDependencyUpdates(packageConfiguration.dependencies)];
59
+
60
+ if (organizationDependencies.length !== 0) {
61
+ logger.debug(`Updating organization dependencies ${JSON.stringify(organizationDependencies)}`);
62
+
63
+ run(false, "npm", "update", ...organizationDependencies);
64
+ }
65
+
66
+ // Nothing further required if this repository is not a dependency.
67
+ if (repository.dependencyType !== "none" && anyChanges(repository, false)) {
68
+ const backupPackageConfigurationPath = ".package.json";
69
+
70
+ // Backup the package configuration file.
71
+ fs.renameSync(packageConfigurationPath, backupPackageConfigurationPath);
72
+
73
+ try {
74
+ const now = new Date();
75
+
76
+ // Strip pre-release identifier if any.
77
+ const [semanticVersion] = packageConfiguration.version.split("-");
78
+
79
+ // Parse semantic version into its components.
80
+ const [majorVersion, minorVersion, patchVersion] = semanticVersion.split(".").map(versionString => Number(versionString));
81
+
82
+ // Set version to alpha version with incremental patch version number.
83
+ packageConfiguration.version = `${majorVersion}.${minorVersion}.${patchVersion + 1}-alpha.${now.getFullYear()}${zeroPadded(now.getMonth() + 1, 2)}${zeroPadded(now.getDate(), 2)}${zeroPadded(now.getHours(), 2)}${zeroPadded(now.getMinutes(), 2)}`;
84
+
85
+ // Update the package configuration for the build.
86
+ fs.writeFileSync(packageConfigurationPath, `${JSON.stringify(packageConfiguration, null, 2)}\n`);
87
+
88
+ // Run development build.
89
+ run(false, "npm", "run", "build:dev");
90
+
91
+ // Publish to development npm registry.
92
+ run(false, "npm", "publish", "--tag", "alpha");
93
+
94
+ // Unpublish all prior alpha versions.
95
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- Output is a JSON array.
96
+ for (const version of JSON.parse(run(true, "npm", "view", packageConfiguration.name, "versions", "--json").join("\n")) as string[]) {
97
+ if (/^[0-9]+.[0-9]+.[0-9]+-alpha.[0-9]+$/.test(version) && version !== packageConfiguration.version) {
98
+ run(false, "npm", "unpublish", `${packageConfiguration.name}@${version}`);
99
+ }
100
+ }
101
+
102
+ repository.lastInternalPublished = now.toISOString();
103
+ } finally {
104
+ // Restore the package configuration file.
105
+ fs.rmSync(packageConfigurationPath);
106
+ fs.renameSync(backupPackageConfigurationPath, packageConfigurationPath);
107
+ }
108
+ }
109
+ }).catch((e: unknown) => {
110
+ logger.error(e);
111
+ });