@git.zone/cli 6.6.2 → 6.7.2

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.
Files changed (50) hide show
  1. package/.smartconfig.json +2 -1
  2. package/dist_ts/00_commitinfo_data.js +1 -1
  3. package/dist_ts/gitzone.cli.js +2 -2
  4. package/dist_ts/helpers.workflow.d.ts +3 -0
  5. package/dist_ts/helpers.workflow.js +14 -1
  6. package/dist_ts/mod_config/index.js +4 -1
  7. package/dist_ts/mod_deprecate/helpers.deprecation.d.ts +12 -0
  8. package/dist_ts/mod_deprecate/helpers.deprecation.js +87 -0
  9. package/dist_ts/mod_deprecate/index.d.ts +1 -1
  10. package/dist_ts/mod_deprecate/index.js +77 -40
  11. package/dist_ts/mod_format/classes.baseformatter.d.ts +6 -0
  12. package/dist_ts/mod_format/classes.baseformatter.js +61 -1
  13. package/dist_ts/mod_format/classes.formatplanner.js +14 -3
  14. package/dist_ts/mod_format/classes.formatstats.d.ts +4 -1
  15. package/dist_ts/mod_format/classes.formatstats.js +11 -1
  16. package/dist_ts/mod_format/formatters/readme.formatter.d.ts +2 -1
  17. package/dist_ts/mod_format/formatters/readme.formatter.js +80 -14
  18. package/dist_ts/mod_format/index.js +2 -1
  19. package/dist_ts/mod_format/interfaces.format.d.ts +6 -2
  20. package/dist_ts/mod_format/interfaces.format.js +1 -1
  21. package/dist_ts/mod_release/classes.releasejournal.d.ts +17 -3
  22. package/dist_ts/mod_release/classes.releasejournal.js +144 -12
  23. package/dist_ts/mod_release/helpers.npmartifact.d.ts +3 -1
  24. package/dist_ts/mod_release/helpers.npmartifact.js +64 -5
  25. package/dist_ts/mod_release/helpers.releasepublication.js +72 -20
  26. package/dist_ts/mod_release/index.d.ts +24 -0
  27. package/dist_ts/mod_release/index.js +82 -22
  28. package/dist_ts/plugins.d.ts +2 -1
  29. package/dist_ts/plugins.js +3 -2
  30. package/package.json +2 -2
  31. package/readme.md +86 -3
  32. package/ts/00_commitinfo_data.ts +1 -1
  33. package/ts/gitzone.cli.ts +1 -1
  34. package/ts/helpers.workflow.ts +17 -0
  35. package/ts/mod_config/index.ts +3 -0
  36. package/ts/mod_deprecate/helpers.deprecation.ts +151 -0
  37. package/ts/mod_deprecate/index.ts +92 -41
  38. package/ts/mod_format/classes.baseformatter.ts +71 -0
  39. package/ts/mod_format/classes.formatplanner.ts +16 -3
  40. package/ts/mod_format/classes.formatstats.ts +14 -1
  41. package/ts/mod_format/formatters/readme.formatter.ts +89 -14
  42. package/ts/mod_format/index.ts +1 -0
  43. package/ts/mod_format/interfaces.format.ts +7 -2
  44. package/ts/mod_release/classes.releasejournal.ts +219 -20
  45. package/ts/mod_release/helpers.npmartifact.ts +120 -23
  46. package/ts/mod_release/helpers.releasepublication.ts +131 -16
  47. package/ts/mod_release/index.ts +127 -30
  48. package/ts/plugins.ts +2 -0
  49. package/readme.hints.md +0 -596
  50. package/readme.plan.md +0 -176
@@ -1,5 +1,5 @@
1
1
  import { BaseFormatter } from '../classes.baseformatter.js';
2
- import type { IPlannedChange } from '../interfaces.format.js';
2
+ import type { IFormatWarning, IPlannedChange } from '../interfaces.format.js';
3
3
  import * as plugins from '../mod.plugins.js';
4
4
  import { logger } from '../../gitzone.logging.js';
5
5
 
@@ -7,9 +7,33 @@ const DEFAULT_README_CONTENT = `# Project Readme
7
7
 
8
8
  This is the initial readme file.`;
9
9
 
10
- const DEFAULT_README_HINTS_CONTENT = `# Project Readme Hints
10
+ const DEFAULT_PLAN_CONTENT = `# Project Plan
11
11
 
12
- This is the initial readme hints file.`;
12
+ This is the initial plan file. It holds implementation plans and is not published.`;
13
+
14
+ const DEFAULT_HINTS_CONTENT = `# Project Hints
15
+
16
+ This is the initial hints file. It holds development findings and is not published.`;
17
+
18
+ /**
19
+ * Development documents used to live in readme.plan.md and readme.hints.md.
20
+ * npm-packlist force-includes every root file whose name starts with "readme",
21
+ * regardless of the package.json files array or .npmignore, so those documents
22
+ * shipped inside every published tarball. plan.md and hints.md carry the same
23
+ * content without being force-included.
24
+ */
25
+ const DEVELOPMENT_DOCUMENTS = [
26
+ {
27
+ legacyPath: 'readme.plan.md',
28
+ path: 'plan.md',
29
+ defaultContent: DEFAULT_PLAN_CONTENT,
30
+ },
31
+ {
32
+ legacyPath: 'readme.hints.md',
33
+ path: 'hints.md',
34
+ defaultContent: DEFAULT_HINTS_CONTENT,
35
+ },
36
+ ] as const;
13
37
 
14
38
  export class ReadmeFormatter extends BaseFormatter {
15
39
  get name(): string {
@@ -19,7 +43,7 @@ export class ReadmeFormatter extends BaseFormatter {
19
43
  async analyze(): Promise<IPlannedChange[]> {
20
44
  const changes: IPlannedChange[] = [];
21
45
 
22
- // Check readme.md
46
+ // Check readme.md - this one is meant to be published, keep it as is
23
47
  const readmeExists = await plugins.smartfs.file('readme.md').exists();
24
48
  if (!readmeExists) {
25
49
  changes.push({
@@ -31,22 +55,73 @@ export class ReadmeFormatter extends BaseFormatter {
31
55
  });
32
56
  }
33
57
 
34
- // Check readme.hints.md
35
- const hintsExists = await plugins.smartfs.file('readme.hints.md').exists();
36
- if (!hintsExists) {
37
- changes.push({
38
- type: 'create',
39
- path: 'readme.hints.md',
40
- module: this.name,
41
- description: 'Create readme.hints.md',
42
- content: DEFAULT_README_HINTS_CONTENT,
43
- });
58
+ for (const document of DEVELOPMENT_DOCUMENTS) {
59
+ const legacyExists = await plugins.smartfs
60
+ .file(document.legacyPath)
61
+ .exists();
62
+ const currentExists = await plugins.smartfs.file(document.path).exists();
63
+
64
+ if (legacyExists && currentExists) {
65
+ // Conflict: reported by validate(), never resolved by clobbering.
66
+ continue;
67
+ }
68
+
69
+ if (legacyExists) {
70
+ changes.push({
71
+ type: 'rename',
72
+ path: document.path,
73
+ fromPath: document.legacyPath,
74
+ module: this.name,
75
+ description: `Rename ${document.legacyPath} to ${document.path} so it is not published to npm`,
76
+ });
77
+ continue;
78
+ }
79
+
80
+ if (!currentExists) {
81
+ changes.push({
82
+ type: 'create',
83
+ path: document.path,
84
+ module: this.name,
85
+ description: `Create ${document.path}`,
86
+ content: document.defaultContent,
87
+ });
88
+ }
44
89
  }
45
90
 
46
91
  return changes;
47
92
  }
48
93
 
94
+ async validate(): Promise<IFormatWarning[]> {
95
+ const warnings: IFormatWarning[] = [];
96
+
97
+ for (const document of DEVELOPMENT_DOCUMENTS) {
98
+ const legacyExists = await plugins.smartfs
99
+ .file(document.legacyPath)
100
+ .exists();
101
+ const currentExists = await plugins.smartfs.file(document.path).exists();
102
+ if (legacyExists && currentExists) {
103
+ warnings.push({
104
+ level: 'error',
105
+ module: this.name,
106
+ message:
107
+ `Both ${document.legacyPath} and ${document.path} exist. ` +
108
+ `${document.legacyPath} is force-included in every npm tarball and must go away, ` +
109
+ `but ${document.path} would be overwritten. Merge them by hand, then remove ${document.legacyPath}.`,
110
+ });
111
+ }
112
+ }
113
+
114
+ return warnings;
115
+ }
116
+
49
117
  async applyChange(change: IPlannedChange): Promise<void> {
118
+ if (change.type === 'rename') {
119
+ if (!change.fromPath) return;
120
+ await this.renameFile(change.fromPath, change.path);
121
+ logger.log('info', `Renamed ${change.fromPath} to ${change.path}`);
122
+ return;
123
+ }
124
+
50
125
  if (change.type !== 'create' || !change.content) return;
51
126
 
52
127
  await this.createFile(change.path, change.content);
@@ -206,6 +206,7 @@ const serializePlan = (plan: any) => {
206
206
  changes: plan.changes.map((change: any) => ({
207
207
  type: change.type,
208
208
  path: change.path,
209
+ fromPath: change.fromPath,
209
210
  module: change.module,
210
211
  description: change.description,
211
212
  mode: change.mode,
@@ -4,35 +4,40 @@ export type IFormatWarning = {
4
4
  module: string;
5
5
  };
6
6
 
7
+ export type TPlannedChangeType = 'create' | 'modify' | 'delete' | 'rename';
8
+
7
9
  export type IFormatPlan = {
8
10
  summary: {
9
11
  totalFiles: number;
10
12
  filesAdded: number;
11
13
  filesModified: number;
12
14
  filesRemoved: number;
15
+ filesRenamed: number;
13
16
  };
14
17
  changes: IPlannedChange[];
15
18
  warnings: IFormatWarning[];
16
19
  };
17
20
 
18
21
  export type IPlannedChange = {
19
- type: 'create' | 'modify' | 'delete';
22
+ type: TPlannedChangeType;
20
23
  path: string;
21
24
  module: string;
22
25
  description: string;
23
26
  content?: string; // New content for create/modify operations
24
27
  mode?: string; // Optional file mode, e.g. 0755 for generated executables
28
+ fromPath?: string; // Source path for rename operations, path is the destination
25
29
  };
26
30
 
27
31
  export interface ICheckResult {
28
32
  hasDiff: boolean;
29
33
  diffs: Array<{
30
34
  path: string;
31
- type: 'create' | 'modify' | 'delete';
35
+ type: TPlannedChangeType;
32
36
  before?: string;
33
37
  after?: string;
34
38
  modeBefore?: string;
35
39
  modeAfter?: string;
40
+ fromPath?: string;
36
41
  }>;
37
42
  }
38
43
 
@@ -47,7 +47,7 @@ export interface IReleaseTargetStatus {
47
47
 
48
48
  export interface IReleaseArtifact {
49
49
  kind: "npm-tarball";
50
- file: typeof releaseArtifactFileName;
50
+ file: string;
51
51
  packageName: string;
52
52
  version: string;
53
53
  size: number;
@@ -64,6 +64,7 @@ export interface IReleaseGitJournal extends IReleaseTargetStatus {
64
64
 
65
65
  export interface IReleaseNpmRegistryJournal extends IReleaseTargetStatus {
66
66
  registry: string;
67
+ packageName?: string;
67
68
  }
68
69
 
69
70
  export interface IReleaseJournalV1 {
@@ -135,7 +136,49 @@ export interface IReleaseJournalV2 {
135
136
  completedAt: string | null;
136
137
  }
137
138
 
138
- export type TReleaseJournal = IReleaseJournalV1 | IReleaseJournalV2;
139
+ /** Ordered, independently packed modules from one tspublish repository release. */
140
+ export interface IReleasePackage {
141
+ folder: string;
142
+ artifact: IReleaseArtifact;
143
+ }
144
+
145
+ export interface IReleaseJournalV3 extends Omit<
146
+ IReleaseJournalV1,
147
+ "schemaVersion" | "artifact"
148
+ > {
149
+ schemaVersion: 3;
150
+ artifact: null;
151
+ packages: IReleasePackage[];
152
+ }
153
+
154
+ export type TReleaseJournal =
155
+ IReleaseJournalV1 | IReleaseJournalV2 | IReleaseJournalV3;
156
+
157
+ export const releasePackageArtifactFile = (packageNameArg: string): string =>
158
+ `package.${plugins.crypto.createHash("sha256").update(packageNameArg).digest("hex")}.tgz`;
159
+
160
+ export const getReleaseArtifacts = (
161
+ journalArg: TReleaseJournal,
162
+ ): IReleaseArtifact[] =>
163
+ journalArg.schemaVersion === 3
164
+ ? journalArg.packages.map((entryArg) => entryArg.artifact)
165
+ : journalArg.artifact
166
+ ? [journalArg.artifact]
167
+ : [];
168
+
169
+ export const getReleaseNpmArtifact = (
170
+ journalArg: TReleaseJournal,
171
+ packageNameArg?: string,
172
+ ): IReleaseArtifact => {
173
+ const artifact =
174
+ journalArg.schemaVersion === 3
175
+ ? journalArg.packages.find(
176
+ (entryArg) => entryArg.artifact.packageName === packageNameArg,
177
+ )?.artifact
178
+ : journalArg.artifact;
179
+ if (!artifact) throw new Error("npm release target has no exact artifact.");
180
+ return artifact;
181
+ };
139
182
 
140
183
  const releaseVersionRegex = /^(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)$/;
141
184
  const gitOidRegex = /^(?:[0-9a-f]{40}|[0-9a-f]{64})$/;
@@ -356,7 +399,7 @@ const isComplete = (journalArg: TReleaseJournal): boolean => {
356
399
  ].every((stateArg) => stateArg === "verified" || stateArg === "skipped");
357
400
  if (
358
401
  !commonComplete ||
359
- journalArg.schemaVersion === legacyReleaseJournalSchemaVersion
402
+ journalArg.schemaVersion !== releaseJournalSchemaVersion
360
403
  ) {
361
404
  return commonComplete;
362
405
  }
@@ -375,6 +418,9 @@ const immutableJournalIdentity = (journalArg: TReleaseJournal): string =>
375
418
  schemaVersion: journalArg.schemaVersion,
376
419
  release: journalArg.release,
377
420
  artifact: journalArg.artifact,
421
+ ...(journalArg.schemaVersion === 3
422
+ ? { packages: journalArg.packages }
423
+ : {}),
378
424
  git: {
379
425
  remote: journalArg.git.remote,
380
426
  destinationHash: journalArg.git.destinationHash,
@@ -384,8 +430,10 @@ const immutableJournalIdentity = (journalArg: TReleaseJournal): string =>
384
430
  access: journalArg.npm.access,
385
431
  tag: journalArg.npm.tag,
386
432
  alreadyPublished: journalArg.npm.alreadyPublished,
387
- registries: journalArg.npm.registries.map(
388
- (registryArg) => registryArg.registry,
433
+ registries: journalArg.npm.registries.map((registryArg) =>
434
+ journalArg.schemaVersion === 3
435
+ ? [registryArg.packageName, registryArg.registry]
436
+ : registryArg.registry,
389
437
  ),
390
438
  },
391
439
  docker:
@@ -994,6 +1042,138 @@ const assertReleaseJournalV2 = (valueArg: unknown): IReleaseJournalV2 => {
994
1042
  return valueArg as unknown as IReleaseJournalV2;
995
1043
  };
996
1044
 
1045
+ const assertReleaseJournalV3 = (
1046
+ valueArg: Record<string, unknown>,
1047
+ ): IReleaseJournalV3 => {
1048
+ assertExactKeys(
1049
+ valueArg,
1050
+ [
1051
+ "kind",
1052
+ "schemaVersion",
1053
+ "revision",
1054
+ "release",
1055
+ "artifact",
1056
+ "packages",
1057
+ "git",
1058
+ "npm",
1059
+ "createdAt",
1060
+ "updatedAt",
1061
+ "completedAt",
1062
+ ],
1063
+ "Release journal",
1064
+ );
1065
+ if (
1066
+ valueArg.artifact !== null ||
1067
+ !Array.isArray(valueArg.packages) ||
1068
+ !valueArg.packages.length ||
1069
+ valueArg.packages.length > 1000 ||
1070
+ !isPlainObject(valueArg.npm) ||
1071
+ !Array.isArray(valueArg.npm.registries)
1072
+ ) {
1073
+ throw new Error("Schema 3 requires an ordered npm package set.");
1074
+ }
1075
+ const names = new Set<string>();
1076
+ const folders = new Set<string>();
1077
+ const targets = valueArg.npm.registries;
1078
+ let expectedRegistries: string[] | undefined;
1079
+ let targetOffset = 0;
1080
+ for (const entry of valueArg.packages) {
1081
+ if (!isPlainObject(entry) || !isPlainObject(entry.artifact))
1082
+ throw new Error("Invalid release package.");
1083
+ assertExactKeys(entry, ["folder", "artifact"], "Release package");
1084
+ if (
1085
+ typeof entry.folder !== "string" ||
1086
+ !/^ts(?:_[a-z0-9_]+)?$/.test(entry.folder) ||
1087
+ folders.has(entry.folder)
1088
+ ) {
1089
+ throw new Error(
1090
+ "Release package folders must be unique canonical source folders.",
1091
+ );
1092
+ }
1093
+ folders.add(entry.folder);
1094
+ const artifact = assertArtifact({
1095
+ ...entry.artifact,
1096
+ file: releaseArtifactFileName,
1097
+ })!;
1098
+ if (
1099
+ names.has(artifact.packageName) ||
1100
+ entry.artifact.file !== releasePackageArtifactFile(artifact.packageName)
1101
+ ) {
1102
+ throw new Error(
1103
+ "Release packages require unique names and canonical artifact filenames.",
1104
+ );
1105
+ }
1106
+ names.add(artifact.packageName);
1107
+ const registries: IReleaseNpmRegistryJournal[] = [];
1108
+ while (
1109
+ targetOffset < targets.length &&
1110
+ isPlainObject(targets[targetOffset]) &&
1111
+ targets[targetOffset].packageName === artifact.packageName
1112
+ ) {
1113
+ const target = targets[targetOffset++];
1114
+ assertExactKeys(
1115
+ target,
1116
+ ["state", "attempts", "attempt", "error", "registry", "packageName"],
1117
+ "Release package target",
1118
+ );
1119
+ const { packageName, ...registry } = target;
1120
+ registries.push(registry as unknown as IReleaseNpmRegistryJournal);
1121
+ }
1122
+ const registryUrls = registries.map((target) => target.registry);
1123
+ if (
1124
+ !registries.length ||
1125
+ (expectedRegistries &&
1126
+ JSON.stringify(expectedRegistries) !== JSON.stringify(registryUrls))
1127
+ ) {
1128
+ throw new Error(
1129
+ "Release package targets must retain one complete, ordered registry matrix.",
1130
+ );
1131
+ }
1132
+ expectedRegistries = registryUrls;
1133
+ // Reuse the established identity, destination, state and timestamp validators.
1134
+ // Each projection is validation-only; schema 1/2 data is never rewritten.
1135
+ const { packages, ...common } = valueArg;
1136
+ const complete = [
1137
+ valueArg.git as IReleaseTargetStatus,
1138
+ ...registries,
1139
+ ].every(
1140
+ (target) => target?.state === "verified" || target?.state === "skipped",
1141
+ );
1142
+ assertReleaseJournalV1({
1143
+ ...common,
1144
+ schemaVersion: 1,
1145
+ artifact,
1146
+ npm: { ...valueArg.npm, registries },
1147
+ completedAt: complete ? valueArg.updatedAt : null,
1148
+ });
1149
+ }
1150
+ if (targetOffset !== targets.length)
1151
+ throw new Error("Unexpected or out-of-order release package target.");
1152
+ const statuses = [
1153
+ valueArg.git as IReleaseTargetStatus,
1154
+ ...(targets as IReleaseTargetStatus[]),
1155
+ ];
1156
+ if (statuses.filter((target) => target.state === "publishing").length > 1) {
1157
+ throw new Error("Schema 3 permits at most one active publication owner.");
1158
+ }
1159
+ const journal = valueArg as unknown as IReleaseJournalV3;
1160
+ if (valueArg.completedAt !== null) {
1161
+ const completedAt = assertIsoTimestamp(
1162
+ valueArg.completedAt,
1163
+ "Release journal completedAt",
1164
+ );
1165
+ if (
1166
+ completedAt < journal.createdAt ||
1167
+ completedAt > journal.updatedAt ||
1168
+ !isComplete(journal)
1169
+ ) {
1170
+ throw new Error("Release journal completion state is inconsistent.");
1171
+ }
1172
+ } else if (isComplete(journal))
1173
+ throw new Error("Complete release journal must record completedAt.");
1174
+ return journal;
1175
+ };
1176
+
997
1177
  export const assertReleaseJournal = (valueArg: unknown): TReleaseJournal => {
998
1178
  if (!isPlainObject(valueArg)) {
999
1179
  throw new Error("Release journal root must be a JSON object.");
@@ -1004,6 +1184,7 @@ export const assertReleaseJournal = (valueArg: unknown): TReleaseJournal => {
1004
1184
  if (valueArg.schemaVersion === releaseJournalSchemaVersion) {
1005
1185
  return assertReleaseJournalV2(valueArg);
1006
1186
  }
1187
+ if (valueArg.schemaVersion === 3) return assertReleaseJournalV3(valueArg);
1007
1188
  throw new Error("Release journal header is invalid or unsupported.");
1008
1189
  };
1009
1190
 
@@ -1014,7 +1195,7 @@ const prepareReleaseJournalSerialization = (
1014
1195
  journalArg: TReleaseJournal,
1015
1196
  ): TReleaseJournal => {
1016
1197
  const journal = assertReleaseJournal(journalArg);
1017
- if (journal.schemaVersion === legacyReleaseJournalSchemaVersion) {
1198
+ if (journal.schemaVersion !== releaseJournalSchemaVersion) {
1018
1199
  return journal;
1019
1200
  }
1020
1201
  return {
@@ -1292,11 +1473,17 @@ export class ReleaseJournalStore {
1292
1473
  return plugins.path.join(this.rootPath, `v${version}`);
1293
1474
  }
1294
1475
 
1295
- public getArtifactPath(versionArg: string): string {
1296
- return plugins.path.join(
1297
- this.getReleaseDirectory(versionArg),
1298
- releaseArtifactFileName,
1299
- );
1476
+ public getArtifactPath(
1477
+ versionArg: string,
1478
+ fileArg: string = releaseArtifactFileName,
1479
+ ): string {
1480
+ if (
1481
+ fileArg !== releaseArtifactFileName &&
1482
+ !/^package\.[0-9a-f]{64}\.tgz$/.test(fileArg)
1483
+ ) {
1484
+ throw new Error("Invalid release artifact filename.");
1485
+ }
1486
+ return plugins.path.join(this.getReleaseDirectory(versionArg), fileArg);
1300
1487
  }
1301
1488
 
1302
1489
  public async createTemporaryDirectory(versionArg: string): Promise<string> {
@@ -1379,6 +1566,21 @@ export class ReleaseJournalStore {
1379
1566
  if (journal.revision !== 1) {
1380
1567
  throw new Error("New release journals must start at revision 1.");
1381
1568
  }
1569
+ if (
1570
+ journal.schemaVersion === 3 &&
1571
+ (journal.completedAt !== null ||
1572
+ [journal.git, ...journal.npm.registries].some(
1573
+ (target) =>
1574
+ !["pending", "skipped"].includes(target.state) ||
1575
+ target.attempts !== 0 ||
1576
+ target.attempt !== null ||
1577
+ target.error !== null,
1578
+ ))
1579
+ ) {
1580
+ throw new Error(
1581
+ "New schema 3 journals require unclaimed publication targets.",
1582
+ );
1583
+ }
1382
1584
  if (journal.schemaVersion === releaseJournalSchemaVersion) {
1383
1585
  const commonTargets = [journal.git, ...journal.npm.registries];
1384
1586
  const commonTargetsUnclaimed = commonTargets.every(
@@ -1419,28 +1621,25 @@ export class ReleaseJournalStore {
1419
1621
  "Prepared release directory is outside canonical storage.",
1420
1622
  );
1421
1623
  }
1422
- if (journal.artifact) {
1423
- const artifactPath = plugins.path.join(
1424
- temporaryDirectory,
1425
- releaseArtifactFileName,
1426
- );
1624
+ for (const artifact of getReleaseArtifacts(journal)) {
1625
+ const artifactPath = plugins.path.join(temporaryDirectory, artifact.file);
1427
1626
  const artifactStat = await plugins.fs.lstat(artifactPath);
1428
1627
  if (!artifactStat.isFile() || artifactStat.isSymbolicLink()) {
1429
1628
  throw new Error("Prepared release artifact must be a regular file.");
1430
1629
  }
1431
1630
  const artifactBytes = await plugins.fs.readFile(artifactPath);
1432
1631
  if (
1433
- artifactBytes.byteLength !== journal.artifact.size ||
1632
+ artifactBytes.byteLength !== artifact.size ||
1434
1633
  plugins.crypto
1435
1634
  .createHash("sha1")
1436
1635
  .update(artifactBytes)
1437
- .digest("hex") !== journal.artifact.sha1 ||
1636
+ .digest("hex") !== artifact.sha1 ||
1438
1637
  plugins.crypto
1439
1638
  .createHash("sha256")
1440
1639
  .update(artifactBytes)
1441
- .digest("hex") !== journal.artifact.sha256 ||
1640
+ .digest("hex") !== artifact.sha256 ||
1442
1641
  `sha512-${plugins.crypto.createHash("sha512").update(artifactBytes).digest("base64")}` !==
1443
- journal.artifact.integrity
1642
+ artifact.integrity
1444
1643
  ) {
1445
1644
  throw new Error(
1446
1645
  "Prepared release artifact does not match its journal identity.",