@aidc-toolkit/dev 0.9.19-beta → 0.9.21-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.
@@ -1,4 +1,5 @@
1
1
  import * as fs from "node:fs";
2
+ import type { Repository } from "./configuration";
2
3
  import { PACKAGE_CONFIGURATION_PATH, PACKAGE_LOCK_CONFIGURATION_PATH, Publish } from "./publish.js";
3
4
  import { logger } from "./logger.js";
4
5
 
@@ -30,27 +31,54 @@ class PublishAlpha extends Publish {
30
31
  this._updateAll = updateAll;
31
32
  }
32
33
 
34
+ /**
35
+ * @inheritDoc
36
+ */
37
+ protected dependencyVersionFor(): string {
38
+ // Dependency version is always "alpha".
39
+ return "alpha";
40
+ }
41
+
42
+ /**
43
+ * @inheritDoc
44
+ */
45
+ protected getPhaseDateTime(repository: Repository, phaseDateTime: Date): Date {
46
+ // If beta or production has been published since the last alpha, use that instead.
47
+ return this.latestDateTime(phaseDateTime, repository.phaseStates.beta?.dateTime, repository.phaseStates.production?.dateTime);
48
+ }
49
+
50
+ /**
51
+ * @inheritDoc
52
+ */
53
+ protected isValidBranch(): boolean {
54
+ // Any branch is valid for alpha publication.
55
+ return true;
56
+ }
57
+
33
58
  /**
34
59
  * @inheritDoc
35
60
  */
36
61
  protected publish(): void {
37
- let anyDependencyUpdates = false;
62
+ let anyExternalUpdates = false;
63
+
64
+ const repositoryState = this.repositoryState;
65
+ const packageConfiguration = repositoryState.packageConfiguration;
38
66
 
39
- // Check for external dependency updates, even if there are no changes.
40
- for (const currentDependencies of [this.packageConfiguration.devDependencies, this.packageConfiguration.dependencies]) {
67
+ // Check for external updates, even if there are no changes.
68
+ for (const currentDependencies of [packageConfiguration.devDependencies, packageConfiguration.dependencies]) {
41
69
  if (currentDependencies !== undefined) {
42
- for (const [dependency, version] of Object.entries(currentDependencies)) {
70
+ for (const [dependencyPackageName, version] of Object.entries(currentDependencies)) {
43
71
  // Ignore organization dependencies.
44
- if (this.dependencyRepositoryName(dependency) === null && version.startsWith("^")) {
45
- const [latestVersion] = this.run(true, true, "npm", "view", dependency, "version");
72
+ if (this.dependencyRepositoryName(dependencyPackageName) === null && version.startsWith("^")) {
73
+ const [latestVersion] = this.run(true, true, "npm", "view", dependencyPackageName, "version");
46
74
 
47
75
  if (latestVersion !== version.substring(1)) {
48
- logger.info(`Dependency ${dependency}@${version} ${!this._updateAll ? "pending update" : "updating"} to version ${latestVersion}.`);
76
+ logger.info(`Dependency ${dependencyPackageName}@${version} ${!this._updateAll ? "pending update" : "updating"} to version ${latestVersion}.`);
49
77
 
50
78
  if (this._updateAll) {
51
- currentDependencies[dependency] = `^${latestVersion}`;
79
+ currentDependencies[dependencyPackageName] = `^${latestVersion}`;
52
80
 
53
- anyDependencyUpdates = true;
81
+ anyExternalUpdates = true;
54
82
  }
55
83
  }
56
84
  }
@@ -58,7 +86,7 @@ class PublishAlpha extends Publish {
58
86
  }
59
87
  }
60
88
 
61
- if (anyDependencyUpdates) {
89
+ if (anyExternalUpdates) {
62
90
  // Save the dependency updates; this will be detected by call to anyChanges().
63
91
  this.savePackageConfiguration();
64
92
  }
@@ -67,28 +95,24 @@ class PublishAlpha extends Publish {
67
95
  logger.debug("Updating all dependencies");
68
96
 
69
97
  // Running this even if there are no dependency updates will update dependencies of dependencies.
70
- this.run(false, false, "npm", "update", ...this.npmPlatformArgs);
98
+ this.run(false, false, "npm", "update", ...repositoryState.npmPlatformArgs);
71
99
  }
72
100
 
73
- const anyChanges = this.anyChanges(this.repository.lastAlphaPublished, true);
101
+ const anyChanges = this.anyChanges(repositoryState.phaseDateTime, true) || repositoryState.anyDependenciesUpdated;
74
102
 
75
103
  if (anyChanges) {
76
- const switchToAlpha = this.preReleaseIdentifier !== "alpha";
104
+ const switchToAlpha = repositoryState.preReleaseIdentifier !== "alpha";
77
105
 
78
106
  if (switchToAlpha) {
79
107
  // Previous publication was beta or production.
80
- this.updatePackageVersion(undefined, undefined, this.patchVersion + 1, "alpha");
108
+ this.updatePackageVersion(undefined, undefined, repositoryState.patchVersion + 1, "alpha");
81
109
 
82
110
  // Use specified registry for organization until no longer in alpha mode.
83
- this.run(false, false, "npm", "set", this.atOrganizationRegistry, "--location", "project");
111
+ this.run(false, false, "npm", "config", "set", this.atOrganizationRegistry, "--location", "project");
84
112
  }
85
113
 
86
- if (this.organizationDependenciesUpdated && (switchToAlpha || !this._updateAll)) {
87
- const updateOrganizationDependencies = Object.values(this.organizationDependencies).filter(updateOrganizationDependency => updateOrganizationDependency !== null);
88
-
89
- logger.debug(`Updating organization dependencies [${updateOrganizationDependencies.join(", ")}]`);
90
-
91
- this.run(false, false, "npm", "update", ...updateOrganizationDependencies, ...this.npmPlatformArgs);
114
+ if (repositoryState.anyDependenciesUpdated && (switchToAlpha || !this._updateAll)) {
115
+ this.updateOrganizationDependencies();
92
116
  }
93
117
  }
94
118
 
@@ -99,10 +123,11 @@ class PublishAlpha extends Publish {
99
123
  this.run(false, false, "npm", "run", "build:dev", "--if-present");
100
124
 
101
125
  if (anyChanges) {
102
- const nowISOString = new Date().toISOString();
126
+ const now = new Date();
127
+ const nowISOString = now.toISOString();
103
128
 
104
129
  // Nothing further required if this repository is not a dependency of others.
105
- if (this.repository.dependencyType !== "none") {
130
+ if (repositoryState.repository.dependencyType !== "none") {
106
131
  if (!this.dryRun) {
107
132
  // Backup the package configuration file.
108
133
  fs.renameSync(PACKAGE_CONFIGURATION_PATH, BACKUP_PACKAGE_CONFIGURATION_PATH);
@@ -117,9 +142,9 @@ class PublishAlpha extends Publish {
117
142
 
118
143
  // Unpublish all prior alpha versions.
119
144
  // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- Output is a JSON array.
120
- for (const version of JSON.parse(this.run(true, true, "npm", "view", this.packageConfiguration.name, "versions", "--json").join("\n")) as string[]) {
121
- if (/^\d+.\d+.\d+-alpha.\d+$/.test(version) && version !== this.packageConfiguration.version) {
122
- this.run(false, false, "npm", "unpublish", `${this.packageConfiguration.name}@${version}`);
145
+ for (const version of JSON.parse(this.run(true, true, "npm", "view", packageConfiguration.name, "versions", "--json").join("\n")) as string[]) {
146
+ if (/^\d+.\d+.\d+-alpha.\d+$/.test(version) && version !== packageConfiguration.version) {
147
+ this.run(false, false, "npm", "unpublish", `${packageConfiguration.name}@${version}`);
123
148
  }
124
149
  }
125
150
  } finally {
@@ -133,7 +158,9 @@ class PublishAlpha extends Publish {
133
158
 
134
159
  this.commitUpdatedPackageVersion(PACKAGE_CONFIGURATION_PATH, PACKAGE_LOCK_CONFIGURATION_PATH);
135
160
 
136
- this.repository.lastAlphaPublished = nowISOString;
161
+ this.updatePhaseState({
162
+ dateTime: now
163
+ });
137
164
  }
138
165
  }
139
166
  }
@@ -4,6 +4,7 @@ import { setTimeout } from "node:timers/promises";
4
4
  import { Octokit } from "octokit";
5
5
  import { parse as yamlParse } from "yaml";
6
6
  import secureConfigurationJSON from "../../config/publish.secure.json";
7
+ import type { Repository } from "./configuration";
7
8
  import { Publish } from "./publish.js";
8
9
  import { logger } from "./logger.js";
9
10
 
@@ -35,7 +36,7 @@ interface WorkflowConfiguration {
35
36
  * Push branches.
36
37
  */
37
38
  branches?: string[];
38
- };
39
+ } | null;
39
40
 
40
41
  /**
41
42
  * Release trigger.
@@ -45,14 +46,14 @@ interface WorkflowConfiguration {
45
46
  * Release types.
46
47
  */
47
48
  types?: string[];
48
- };
49
+ } | null;
49
50
  };
50
51
  }
51
52
 
52
53
  /**
53
54
  * Publish steps.
54
55
  */
55
- type Step = "install" | "build" | "commit" | "tag" | "push" | "workflow (push)" | "release" | "workflow (release)";
56
+ type Step = "update" | "build" | "commit" | "tag" | "push" | "workflow (push)" | "release" | "workflow (release)" | "complete";
56
57
 
57
58
  /**
58
59
  * Publish beta versions.
@@ -83,6 +84,52 @@ class PublishBeta extends Publish {
83
84
  });
84
85
  }
85
86
 
87
+ /**
88
+ * @inheritDoc
89
+ */
90
+ protected dependencyVersionFor(dependencyRepositoryName: string, dependencyRepository: Repository): string {
91
+ let dependencyVersion: string;
92
+
93
+ switch (dependencyRepository.dependencyType) {
94
+ case "external":
95
+ dependencyVersion = "beta";
96
+ break;
97
+
98
+ case "internal": {
99
+ const betaTag = dependencyRepository.phaseStates.beta?.tag;
100
+
101
+ if (betaTag === undefined) {
102
+ throw new Error(`*** Internal error *** Beta tag not set for ${dependencyRepositoryName}`);
103
+ }
104
+
105
+ dependencyVersion = `${this.configuration.organization}/${dependencyRepositoryName}#${betaTag}`;
106
+ }
107
+ break;
108
+
109
+ default:
110
+ throw new Error(`Invalid dependency type "${dependencyRepository.dependencyType}" for dependency ${dependencyRepositoryName}`);
111
+ }
112
+
113
+ return dependencyVersion;
114
+ }
115
+
116
+ /**
117
+ * @inheritDoc
118
+ */
119
+ protected getPhaseDateTime(repository: Repository, phaseDateTime: Date): Date {
120
+ return this.latestDateTime(phaseDateTime, repository.phaseStates.production?.dateTime);
121
+ }
122
+
123
+ /**
124
+ * @inheritDoc
125
+ */
126
+ protected isValidBranch(): boolean {
127
+ const repositoryState = this.repositoryState;
128
+
129
+ // Branch for beta phase must match version.
130
+ return repositoryState.branch === `v${repositoryState.majorVersion}.${repositoryState.minorVersion}`;
131
+ }
132
+
86
133
  /**
87
134
  * Run a step.
88
135
  *
@@ -95,14 +142,20 @@ class PublishBeta extends Publish {
95
142
  * Callback to execute step.
96
143
  */
97
144
  private async runStep(step: Step, stepRunner: () => (void | Promise<void>)): Promise<void> {
98
- if (this.repository.publishBetaStep === undefined || this.repository.publishBetaStep === step) {
145
+ const phaseStateStep = this.repositoryState.phaseState.step;
146
+
147
+ if (phaseStateStep === undefined || phaseStateStep === step) {
99
148
  logger.debug(`Running step ${step}`);
100
149
 
101
- this.repository.publishBetaStep = step;
150
+ this.updatePhaseState({
151
+ step
152
+ });
102
153
 
103
154
  await stepRunner();
104
155
 
105
- this.repository.publishBetaStep = undefined;
156
+ this.updatePhaseState({
157
+ step: undefined
158
+ });
106
159
  } else {
107
160
  logger.debug(`Skipping step ${step}`);
108
161
  }
@@ -117,7 +170,7 @@ class PublishBeta extends Publish {
117
170
  if (this.dryRun) {
118
171
  logger.info("Dry run: Validate workflow");
119
172
  } else {
120
- const commitSHA = this.run(true, true, "git", "rev-parse", this.branch)[0];
173
+ const commitSHA = this.run(true, true, "git", "rev-parse", this.repositoryState.branch)[0];
121
174
 
122
175
  let completed = false;
123
176
  let queryCount = 0;
@@ -128,7 +181,7 @@ class PublishBeta extends Publish {
128
181
  const response = await setTimeout(2000).then(
129
182
  async () => this._octokit.rest.actions.listWorkflowRunsForRepo({
130
183
  owner: this.configuration.organization,
131
- repo: this.repositoryName,
184
+ repo: this.repositoryState.repositoryName,
132
185
  head_sha: commitSHA
133
186
  })
134
187
  );
@@ -169,34 +222,42 @@ class PublishBeta extends Publish {
169
222
  protected async publish(): Promise<void> {
170
223
  let publish: boolean;
171
224
 
225
+ const repositoryState = this.repositoryState;
226
+
172
227
  // Scrap any incomplete publishing if pre-release identifier is not beta.
173
- if (this.preReleaseIdentifier !== "beta") {
174
- this.repository.publishBetaStep = undefined;
228
+ if (repositoryState.preReleaseIdentifier !== "beta") {
229
+ repositoryState.phaseState.step = undefined;
175
230
  }
176
231
 
177
- if (this.preReleaseIdentifier === "alpha") {
178
- if (this.anyChanges(this.repository.lastAlphaPublished, true)) {
232
+ if (repositoryState.preReleaseIdentifier === "alpha") {
233
+ if (this.anyChanges(repositoryState.repository.phaseStates.alpha?.dateTime, false)) {
179
234
  throw new Error("Repository has changed since last alpha published");
180
235
  }
181
236
 
182
237
  publish = true;
183
238
 
184
239
  this.updatePackageVersion(undefined, undefined, undefined, "beta");
240
+
241
+ // Revert to default registry for organization.
242
+ this.run(false, false, "npm", "config", "delete", this.atOrganizationRegistry, "--location", "project");
185
243
  } else {
186
- // Publish beta step is defined if previous attempt failed at that step.
187
- publish = this.repository.publishBetaStep !== undefined;
244
+ const step = repositoryState.phaseState.step;
245
+ const startingPublication = step === undefined;
246
+
247
+ // Step is defined and not "complete" if previous attempt failed at that step.
248
+ publish = !startingPublication && step !== "complete";
188
249
 
189
250
  // Ignore changes after publication process has started.
190
- if (!publish && this.anyChanges(this.repository.lastBetaPublished, false)) {
191
- throw new Error("Internal error, repository has changed without intermediate alpha publication");
251
+ if (startingPublication && this.anyChanges(repositoryState.repository.phaseStates.alpha?.dateTime, false)) {
252
+ throw new Error("Repository has changed since last alpha published");
192
253
  }
193
254
  }
194
255
 
195
256
  if (publish) {
196
- const tag = `v${this.packageConfiguration.version}`;
257
+ const tag = `v${repositoryState.packageConfiguration.version}`;
197
258
 
198
- if (this.repository.publishBetaStep !== undefined) {
199
- logger.debug(`Repository failed at step "${this.repository.publishBetaStep}" on prior run`);
259
+ if (repositoryState.phaseState.step !== undefined) {
260
+ logger.debug(`Repository failed at step "${repositoryState.phaseState.step}" on prior run`);
200
261
  }
201
262
 
202
263
  const workflowsPath = ".github/workflows/";
@@ -211,13 +272,13 @@ class PublishBeta extends Publish {
211
272
  // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- Workflow configuration format is known.
212
273
  const workflowOn = (yamlParse(fs.readFileSync(path.resolve(workflowsPath, workflowFile)).toString()) as WorkflowConfiguration).on;
213
274
 
214
- if (workflowOn.push !== undefined && (workflowOn.push.branches === undefined || workflowOn.push.branches.includes("v*"))) {
275
+ if (workflowOn.push !== undefined && (workflowOn.push?.branches === undefined || workflowOn.push.branches.includes("v*"))) {
215
276
  logger.debug("Repository has push workflow");
216
277
 
217
278
  hasPushWorkflow = true;
218
279
  }
219
280
 
220
- if (workflowOn.release !== undefined && (workflowOn.release.types === undefined || workflowOn.release.types.includes("published"))) {
281
+ if (workflowOn.release !== undefined && (workflowOn.release?.types === undefined || workflowOn.release.types.includes("published"))) {
221
282
  logger.debug("Repository has release workflow");
222
283
 
223
284
  hasReleaseWorkflow = true;
@@ -225,12 +286,15 @@ class PublishBeta extends Publish {
225
286
  }
226
287
  }
227
288
 
228
- await this.runStep("install", () => {
229
- this.run(false, false, "npm", "install", ...this.npmPlatformArgs);
289
+ await this.runStep("update", () => {
290
+ this.updateOrganizationDependencies();
230
291
  });
231
292
 
232
293
  await this.runStep("build", () => {
233
294
  this.run(false, false, "npm", "run", "build:release", "--if-present");
295
+
296
+ // Run test if present; must be part of build as correcting errors will require rebuild.
297
+ this.run(false, false, "npm", "run", "test", "--if-present");
234
298
  });
235
299
 
236
300
  await this.runStep("commit", () => {
@@ -242,7 +306,7 @@ class PublishBeta extends Publish {
242
306
  });
243
307
 
244
308
  await this.runStep("push", () => {
245
- this.run(false, false, "git", "push", "--atomic", "origin", this.branch, tag);
309
+ this.run(false, false, "git", "push", "--atomic", "origin", repositoryState.branch, tag);
246
310
  });
247
311
 
248
312
  if (hasPushWorkflow) {
@@ -257,7 +321,7 @@ class PublishBeta extends Publish {
257
321
  } else {
258
322
  await this._octokit.rest.repos.createRelease({
259
323
  owner: this.configuration.organization,
260
- repo: this.repositoryName,
324
+ repo: repositoryState.repositoryName,
261
325
  tag_name: tag,
262
326
  name: `Release ${tag}`,
263
327
  prerelease: true
@@ -271,8 +335,11 @@ class PublishBeta extends Publish {
271
335
  });
272
336
  }
273
337
 
274
- this.repository.lastBetaPublished = new Date().toISOString();
275
- this.repository.publishBetaStep = undefined;
338
+ this.updatePhaseState({
339
+ dateTime: new Date(),
340
+ tag,
341
+ step: "complete"
342
+ });
276
343
  }
277
344
  }
278
345
 
@@ -282,7 +349,8 @@ class PublishBeta extends Publish {
282
349
  protected override finalizeAll(): void {
283
350
  // Publication complete; reset steps to undefined for next run.
284
351
  for (const repository of Object.values(this.configuration.repositories)) {
285
- repository.publishBetaStep = undefined;
352
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- All beta phase states are defined by this point.
353
+ repository.phaseStates.beta!.step = undefined;
286
354
  }
287
355
  }
288
356
  }