@git.zone/cli 6.7.3 → 6.9.0
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/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/mod_release/helpers.npmartifact.d.ts +8 -4
- package/dist_ts/mod_release/helpers.npmartifact.js +51 -9
- package/dist_ts/mod_release/helpers.releasepreparation.d.ts +21 -0
- package/dist_ts/mod_release/helpers.releasepreparation.js +196 -0
- package/dist_ts/mod_release/helpers.releasepublication.d.ts +2 -1
- package/dist_ts/mod_release/helpers.releasepublication.js +5 -3
- package/dist_ts/mod_release/index.d.ts +4 -0
- package/dist_ts/mod_release/index.js +133 -11
- package/package.json +4 -4
- package/readme.md +57 -2
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/mod_release/helpers.npmartifact.ts +88 -12
- package/ts/mod_release/helpers.releasepreparation.ts +308 -0
- package/ts/mod_release/helpers.releasepublication.ts +6 -0
- package/ts/mod_release/index.ts +191 -10
package/ts/mod_release/index.ts
CHANGED
|
@@ -49,8 +49,14 @@ import {
|
|
|
49
49
|
normalizeNpmRegistryUrl,
|
|
50
50
|
packNpmArtifact,
|
|
51
51
|
verifyStoredNpmArtifact,
|
|
52
|
+
type IPnpmReleaseCapability,
|
|
52
53
|
} from "./helpers.npmartifact.js";
|
|
53
54
|
import { executeReleasePublication } from "./helpers.releasepublication.js";
|
|
55
|
+
import {
|
|
56
|
+
assertPackageReleaseRecovery,
|
|
57
|
+
assertRecoveryUnpublished,
|
|
58
|
+
withReleasePreparationRecovery,
|
|
59
|
+
} from "./helpers.releasepreparation.js";
|
|
54
60
|
import {
|
|
55
61
|
TsdockerReleaseClient,
|
|
56
62
|
canonicalReleaseJson,
|
|
@@ -87,6 +93,10 @@ const runInternal = async (argvArg: any): Promise<void> => {
|
|
|
87
93
|
await runReleaseResume(argvArg, mode);
|
|
88
94
|
return;
|
|
89
95
|
}
|
|
96
|
+
if (subcommand === "recover") {
|
|
97
|
+
await runReleaseRecover(argvArg, mode);
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
90
100
|
if (subcommand !== undefined) {
|
|
91
101
|
throw new Error(`Unknown release subcommand: ${subcommand}`);
|
|
92
102
|
}
|
|
@@ -134,7 +144,7 @@ const runInternal = async (argvArg: any): Promise<void> => {
|
|
|
134
144
|
const preflightTsdocker = workflow.targets.includes("docker")
|
|
135
145
|
? new TsdockerReleaseClient(smartshellInstance, branchContext.sourceCwd)
|
|
136
146
|
: undefined;
|
|
137
|
-
await assertFreshReleaseCapabilities(
|
|
147
|
+
const pnpmCapability = await assertFreshReleaseCapabilities(
|
|
138
148
|
smartshellInstance,
|
|
139
149
|
workflow,
|
|
140
150
|
branchContext.sourceCwd,
|
|
@@ -284,6 +294,7 @@ const runInternal = async (argvArg: any): Promise<void> => {
|
|
|
284
294
|
)
|
|
285
295
|
: undefined;
|
|
286
296
|
const journalContext = await createReleaseJournal({
|
|
297
|
+
pnpmCapability,
|
|
287
298
|
smartshell: smartshellInstance,
|
|
288
299
|
workflow,
|
|
289
300
|
releaseCwd,
|
|
@@ -296,6 +307,7 @@ const runInternal = async (argvArg: any): Promise<void> => {
|
|
|
296
307
|
ensurePublicationState,
|
|
297
308
|
});
|
|
298
309
|
const finalJournal = await executeReleasePublication({
|
|
310
|
+
pnpmCapability,
|
|
299
311
|
smartshell: smartshellInstance,
|
|
300
312
|
cwd: releaseCwd,
|
|
301
313
|
store: journalContext.store,
|
|
@@ -313,7 +325,7 @@ const assertFreshReleaseCapabilities = async (
|
|
|
313
325
|
cwdArg: string,
|
|
314
326
|
pushUrlArg?: string,
|
|
315
327
|
tsdockerArg?: ITsdockerReleaseClient,
|
|
316
|
-
): Promise<
|
|
328
|
+
): Promise<IPnpmReleaseCapability | undefined> => {
|
|
317
329
|
if (workflowArg.targets.includes("docker")) {
|
|
318
330
|
if (!tsdockerArg) {
|
|
319
331
|
throw new Error("Docker release target requires project-local tSDocker.");
|
|
@@ -332,7 +344,7 @@ const assertFreshReleaseCapabilities = async (
|
|
|
332
344
|
await assertNoLegacyNpmPublisher(cwdArg);
|
|
333
345
|
}
|
|
334
346
|
if (workflowArg.targets.includes("npm")) {
|
|
335
|
-
|
|
347
|
+
return assertPnpmReleaseCapability(smartshellArg, cwdArg);
|
|
336
348
|
}
|
|
337
349
|
};
|
|
338
350
|
|
|
@@ -399,9 +411,12 @@ const assertReleaseConfiguration = (
|
|
|
399
411
|
};
|
|
400
412
|
|
|
401
413
|
interface ICreateReleaseJournalOptions {
|
|
414
|
+
pnpmCapability?: IPnpmReleaseCapability;
|
|
402
415
|
smartshell: plugins.smartshell.Smartshell;
|
|
403
416
|
workflow: IResolvedReleaseWorkflow;
|
|
404
417
|
releaseCwd: string;
|
|
418
|
+
/** Exact detached checkout used only while recovering pre-journal packaging. */
|
|
419
|
+
npmSourceCwd?: string;
|
|
405
420
|
releasePushUrl?: string;
|
|
406
421
|
expectedRemoteMainOid?: string;
|
|
407
422
|
version: string;
|
|
@@ -447,6 +462,7 @@ export const planReleasePackages = async (
|
|
|
447
462
|
export const createReleaseJournal = async (
|
|
448
463
|
optionsArg: ICreateReleaseJournalOptions,
|
|
449
464
|
): Promise<{ store: ReleaseJournalStore; journal: TReleaseJournal }> => {
|
|
465
|
+
const npmSourceCwd = optionsArg.npmSourceCwd ?? optionsArg.releaseCwd;
|
|
450
466
|
const gitCommonDirectory = await resolveGitCommonDirectory(
|
|
451
467
|
optionsArg.smartshell,
|
|
452
468
|
optionsArg.releaseCwd,
|
|
@@ -474,7 +490,7 @@ export const createReleaseJournal = async (
|
|
|
474
490
|
const packages: IReleasePackage[] = [];
|
|
475
491
|
if (optionsArg.workflow.targets.includes("npm")) {
|
|
476
492
|
if (optionsArg.workflow.npmPackageSource === "tspublish") {
|
|
477
|
-
const plan = await planReleasePackages(
|
|
493
|
+
const plan = await planReleasePackages(npmSourceCwd);
|
|
478
494
|
if (plan.version !== optionsArg.version)
|
|
479
495
|
throw new Error("tspublish plan version differs from the release.");
|
|
480
496
|
const outputDirectory = plugins.path.join(
|
|
@@ -482,7 +498,7 @@ export const createReleaseJournal = async (
|
|
|
482
498
|
"prepared",
|
|
483
499
|
);
|
|
484
500
|
const prepared = await new plugins.tspublish.TsPublish().prepare(
|
|
485
|
-
|
|
501
|
+
npmSourceCwd,
|
|
486
502
|
outputDirectory,
|
|
487
503
|
);
|
|
488
504
|
if (
|
|
@@ -497,6 +513,7 @@ export const createReleaseJournal = async (
|
|
|
497
513
|
module.directory,
|
|
498
514
|
temporaryDirectory,
|
|
499
515
|
optionsArg.version,
|
|
516
|
+
optionsArg.pnpmCapability,
|
|
500
517
|
releasePackageArtifactFile(module.name),
|
|
501
518
|
);
|
|
502
519
|
if (packed.packageName !== module.name)
|
|
@@ -509,14 +526,15 @@ export const createReleaseJournal = async (
|
|
|
509
526
|
} else {
|
|
510
527
|
artifact = await packNpmArtifact(
|
|
511
528
|
optionsArg.smartshell,
|
|
512
|
-
|
|
529
|
+
npmSourceCwd,
|
|
513
530
|
temporaryDirectory,
|
|
514
531
|
optionsArg.version,
|
|
532
|
+
optionsArg.pnpmCapability,
|
|
515
533
|
);
|
|
516
534
|
}
|
|
517
535
|
await verifyCleanTree(
|
|
518
536
|
optionsArg.smartshell,
|
|
519
|
-
|
|
537
|
+
npmSourceCwd,
|
|
520
538
|
"npm pack lifecycle scripts changed the release worktree. Aborting release.",
|
|
521
539
|
);
|
|
522
540
|
await optionsArg.ensurePublicationState();
|
|
@@ -650,6 +668,155 @@ const runReleaseInspect = async (
|
|
|
650
668
|
}
|
|
651
669
|
};
|
|
652
670
|
|
|
671
|
+
const runReleaseRecover = async (
|
|
672
|
+
argvArg: any,
|
|
673
|
+
mode: ICliMode,
|
|
674
|
+
): Promise<void> => {
|
|
675
|
+
const allowed = new Set([
|
|
676
|
+
"_",
|
|
677
|
+
"$0",
|
|
678
|
+
"y",
|
|
679
|
+
"yes",
|
|
680
|
+
"plan",
|
|
681
|
+
"h",
|
|
682
|
+
"help",
|
|
683
|
+
"plain",
|
|
684
|
+
"quiet",
|
|
685
|
+
"agent",
|
|
686
|
+
"interactive",
|
|
687
|
+
"check-updates",
|
|
688
|
+
"checkUpdates",
|
|
689
|
+
]);
|
|
690
|
+
if (
|
|
691
|
+
argvArg._?.length !== 3 ||
|
|
692
|
+
Object.keys(argvArg).some((key) => !allowed.has(key)) ||
|
|
693
|
+
(argvArg.plan !== undefined && typeof argvArg.plan !== "boolean")
|
|
694
|
+
) {
|
|
695
|
+
throw new Error(
|
|
696
|
+
"Usage: gitzone release recover <version> [-y|--plan]. Target, version, build and test overrides are not allowed.",
|
|
697
|
+
);
|
|
698
|
+
}
|
|
699
|
+
const version = normalizeReleaseVersion(argvArg._[2]);
|
|
700
|
+
const shell = new plugins.smartshell.Smartshell({
|
|
701
|
+
executor: "bash",
|
|
702
|
+
sourceFilePaths: [],
|
|
703
|
+
});
|
|
704
|
+
const store = new ReleaseJournalStore(
|
|
705
|
+
await resolveGitCommonDirectory(shell, paths.cwd),
|
|
706
|
+
);
|
|
707
|
+
const existing = await plugins.fs
|
|
708
|
+
.lstat(store.getReleaseDirectory(version))
|
|
709
|
+
.then(() => true)
|
|
710
|
+
.catch((error: NodeJS.ErrnoException) => {
|
|
711
|
+
if (error.code === "ENOENT") return false;
|
|
712
|
+
throw error;
|
|
713
|
+
});
|
|
714
|
+
if (existing) {
|
|
715
|
+
if (argvArg.plan) {
|
|
716
|
+
printReleaseJournalSummary(await store.read(version));
|
|
717
|
+
return;
|
|
718
|
+
}
|
|
719
|
+
await runReleaseResume(
|
|
720
|
+
{ _: ["release", "resume", version], yes: mode.yes },
|
|
721
|
+
mode,
|
|
722
|
+
);
|
|
723
|
+
return;
|
|
724
|
+
}
|
|
725
|
+
const workflow = await resolveReleaseWorkflow({ yes: true });
|
|
726
|
+
assertReleaseConfiguration(workflow, false);
|
|
727
|
+
let branch = await inspectReleaseBranch({
|
|
728
|
+
smartshell: shell,
|
|
729
|
+
cwd: paths.cwd,
|
|
730
|
+
gitRemote: workflow.gitRemote,
|
|
731
|
+
gitTargetActive: true,
|
|
732
|
+
pushBranch: workflow.pushBranch,
|
|
733
|
+
pushTags: workflow.pushTags,
|
|
734
|
+
mergeRequested: false,
|
|
735
|
+
planMode: argvArg.plan === true,
|
|
736
|
+
});
|
|
737
|
+
const refs = await captureReleaseRefs(
|
|
738
|
+
shell,
|
|
739
|
+
paths.cwd,
|
|
740
|
+
version,
|
|
741
|
+
branch.sourceOid,
|
|
742
|
+
);
|
|
743
|
+
const options = { smartshell: shell, workflow, branch, refs, version, store };
|
|
744
|
+
const packageName = await assertPackageReleaseRecovery(options);
|
|
745
|
+
if (!branch.planAncestryVerified) {
|
|
746
|
+
console.log(
|
|
747
|
+
"Remote ancestry is deferred until execution fetches the missing commit.",
|
|
748
|
+
);
|
|
749
|
+
}
|
|
750
|
+
console.log(
|
|
751
|
+
`Recover existing ${packageName}@${version}\ncommit: ${refs.mainOid}\ntag object: ${refs.tagOid}\nGit destination: ${branch.gitRemote}\nnpm registries: ${workflow.npmRegistries.join(", ")}\nBuild and pack an isolated checkout, install its journal, then publish configured targets.`,
|
|
752
|
+
);
|
|
753
|
+
if (argvArg.plan) return;
|
|
754
|
+
const pnpmCapability = await assertFreshReleaseCapabilities(
|
|
755
|
+
shell,
|
|
756
|
+
workflow,
|
|
757
|
+
paths.cwd,
|
|
758
|
+
branch.pushUrl,
|
|
759
|
+
);
|
|
760
|
+
if (!mode.yes) {
|
|
761
|
+
if (!mode.interactive)
|
|
762
|
+
throw new Error(
|
|
763
|
+
"Release recovery requires an interactive terminal or -y.",
|
|
764
|
+
);
|
|
765
|
+
if (
|
|
766
|
+
!(await plugins.smartinteract.SmartInteract.getCliConfirmation(
|
|
767
|
+
`Recover and publish existing v${version}?`,
|
|
768
|
+
false,
|
|
769
|
+
))
|
|
770
|
+
)
|
|
771
|
+
return;
|
|
772
|
+
}
|
|
773
|
+
branch = await prepareReleaseIntegration(shell, branch);
|
|
774
|
+
options.branch = branch;
|
|
775
|
+
const ensureState = async (remote = branch.remoteMainOid) => {
|
|
776
|
+
await verifyReleaseRefs(shell, paths.cwd, version, refs);
|
|
777
|
+
await revalidateReleasePublicationState(
|
|
778
|
+
shell,
|
|
779
|
+
branch,
|
|
780
|
+
refs.mainOid,
|
|
781
|
+
remote,
|
|
782
|
+
);
|
|
783
|
+
};
|
|
784
|
+
const ensureUnpublished = async () => {
|
|
785
|
+
await ensureState();
|
|
786
|
+
await assertRecoveryUnpublished(options, packageName);
|
|
787
|
+
};
|
|
788
|
+
const context = await withReleasePreparationRecovery(
|
|
789
|
+
options,
|
|
790
|
+
ensureUnpublished,
|
|
791
|
+
async (checkout, ensureCheckout) =>
|
|
792
|
+
createReleaseJournal({
|
|
793
|
+
pnpmCapability,
|
|
794
|
+
smartshell: shell,
|
|
795
|
+
workflow,
|
|
796
|
+
releaseCwd: paths.cwd,
|
|
797
|
+
npmSourceCwd: checkout,
|
|
798
|
+
releasePushUrl: branch.pushUrl,
|
|
799
|
+
expectedRemoteMainOid: branch.remoteMainOid,
|
|
800
|
+
version,
|
|
801
|
+
refs,
|
|
802
|
+
ensurePublicationState: async () => {
|
|
803
|
+
await ensureCheckout();
|
|
804
|
+
await ensureUnpublished();
|
|
805
|
+
},
|
|
806
|
+
}),
|
|
807
|
+
);
|
|
808
|
+
const result = await executeReleasePublication({
|
|
809
|
+
pnpmCapability,
|
|
810
|
+
smartshell: shell,
|
|
811
|
+
cwd: paths.cwd,
|
|
812
|
+
store: context.store,
|
|
813
|
+
journal: context.journal,
|
|
814
|
+
pushUrl: branch.pushUrl,
|
|
815
|
+
ensurePublicationState: ensureState,
|
|
816
|
+
});
|
|
817
|
+
printReleaseJournalSummary(result);
|
|
818
|
+
};
|
|
819
|
+
|
|
653
820
|
const assertResumeArguments = (argvArg: any): void => {
|
|
654
821
|
const forbiddenKeys = [
|
|
655
822
|
"allow-empty",
|
|
@@ -754,8 +921,12 @@ const runReleaseResume = async (
|
|
|
754
921
|
await tsdocker.assertCapabilities();
|
|
755
922
|
await tsdocker.validate(journal.docker.request);
|
|
756
923
|
}
|
|
924
|
+
let pnpmCapability: IPnpmReleaseCapability | undefined;
|
|
757
925
|
if (getReleaseArtifacts(journal).length) {
|
|
758
|
-
await assertPnpmReleaseCapability(
|
|
926
|
+
pnpmCapability = await assertPnpmReleaseCapability(
|
|
927
|
+
smartshellInstance,
|
|
928
|
+
paths.cwd,
|
|
929
|
+
);
|
|
759
930
|
await assertNoLegacyNpmPublisher(paths.cwd);
|
|
760
931
|
for (const artifact of getReleaseArtifacts(journal)) {
|
|
761
932
|
await verifyStoredNpmArtifact(
|
|
@@ -825,6 +996,7 @@ const runReleaseResume = async (
|
|
|
825
996
|
);
|
|
826
997
|
};
|
|
827
998
|
const finalJournal = await executeReleasePublication({
|
|
999
|
+
pnpmCapability,
|
|
828
1000
|
smartshell: smartshellInstance,
|
|
829
1001
|
cwd: paths.cwd,
|
|
830
1002
|
store,
|
|
@@ -1284,7 +1456,8 @@ export function showHelp(mode?: ICliMode): void {
|
|
|
1284
1456
|
if (mode?.json) {
|
|
1285
1457
|
printJson({
|
|
1286
1458
|
command: "release",
|
|
1287
|
-
usage:
|
|
1459
|
+
usage:
|
|
1460
|
+
"gitzone release [inspect [version]|resume <version>|recover <version>] [options]",
|
|
1288
1461
|
description:
|
|
1289
1462
|
"Creates a versioned release from pending changelog entries and publishes configured artifacts.",
|
|
1290
1463
|
flags: [
|
|
@@ -1338,6 +1511,11 @@ export function showHelp(mode?: ICliMode): void {
|
|
|
1338
1511
|
description:
|
|
1339
1512
|
"Resume exact journaled publication without rebuilding or repacking npm artifacts",
|
|
1340
1513
|
},
|
|
1514
|
+
{
|
|
1515
|
+
flag: "recover <version>",
|
|
1516
|
+
description:
|
|
1517
|
+
"Recover a local root npm release tag after build or pack failure before journal creation",
|
|
1518
|
+
},
|
|
1341
1519
|
{
|
|
1342
1520
|
flag: "--recover-attempt <id>",
|
|
1343
1521
|
description:
|
|
@@ -1350,7 +1528,7 @@ export function showHelp(mode?: ICliMode): void {
|
|
|
1350
1528
|
|
|
1351
1529
|
console.log("");
|
|
1352
1530
|
console.log(
|
|
1353
|
-
"Usage: gitzone release [inspect [version]|resume <version>] [options]",
|
|
1531
|
+
"Usage: gitzone release [inspect [version]|resume <version>|recover <version>] [options]",
|
|
1354
1532
|
);
|
|
1355
1533
|
console.log("");
|
|
1356
1534
|
console.log("Creates a versioned release from changelog Pending entries.");
|
|
@@ -1386,6 +1564,9 @@ export function showHelp(mode?: ICliMode): void {
|
|
|
1386
1564
|
console.log(
|
|
1387
1565
|
" resume <version> Resume exact journaled publication without rebuilding or repacking npm artifacts",
|
|
1388
1566
|
);
|
|
1567
|
+
console.log(
|
|
1568
|
+
" recover <version> Recover an unpublished root npm release from its existing local tag",
|
|
1569
|
+
);
|
|
1389
1570
|
console.log(
|
|
1390
1571
|
" --recover-attempt Recover one exact 32-character hexadecimal attempt ID after proving its publisher stopped",
|
|
1391
1572
|
);
|