@gadmin2n/cli 0.0.111 → 0.0.113
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/actions/new.action.js +30 -0
- package/actions/update.action.js +159 -100
- package/commands/update.command.js +17 -8
- package/lib/executable-paths.d.ts +27 -0
- package/lib/executable-paths.js +55 -0
- package/lib/schematics-root.d.ts +20 -0
- package/lib/schematics-root.js +51 -0
- package/lib/template-merge/materialize.d.ts +27 -0
- package/lib/template-merge/materialize.js +58 -1
- package/package.json +1 -1
package/actions/new.action.js
CHANGED
|
@@ -25,6 +25,10 @@ const schematics_1 = require("../lib/schematics");
|
|
|
25
25
|
const ui_1 = require("../lib/ui");
|
|
26
26
|
const formatting_1 = require("../lib/utils/formatting");
|
|
27
27
|
const abstract_action_1 = require("./abstract.action");
|
|
28
|
+
const schematics_root_1 = require("../lib/schematics-root");
|
|
29
|
+
const base_store_1 = require("../lib/template-merge/base-store");
|
|
30
|
+
const types_1 = require("../lib/template-merge/types");
|
|
31
|
+
const executable_paths_1 = require("../lib/executable-paths");
|
|
28
32
|
class NewAction extends abstract_action_1.AbstractAction {
|
|
29
33
|
handle(inputs, options) {
|
|
30
34
|
return __awaiter(this, void 0, void 0, function* () {
|
|
@@ -42,6 +46,32 @@ class NewAction extends abstract_action_1.AbstractAction {
|
|
|
42
46
|
yield applyEnvConfig(projectDirectory, options).catch((err) => {
|
|
43
47
|
console.error(chalk.yellow(`[warn] Failed to apply env config: ${err && err.message ? err.message : err}`));
|
|
44
48
|
});
|
|
49
|
+
// Pin the 3-way merge base at project init time. Recording the exact
|
|
50
|
+
// schematics version that scaffolded this project means the FIRST
|
|
51
|
+
// `gadmin2 update` can immediately do a real 3-way merge — no
|
|
52
|
+
// bootstrap-then-rerun dance. Failure is non-fatal: if meta can't be
|
|
53
|
+
// written here, `gadmin2 update` still has its bootstrap fallback.
|
|
54
|
+
try {
|
|
55
|
+
const projectAbsDir = (0, path_1.join)(process.cwd(), projectDirectory);
|
|
56
|
+
const schematicsVersion = (0, schematics_root_1.getSchematicsVersion)();
|
|
57
|
+
(0, base_store_1.writeMeta)(projectAbsDir, {
|
|
58
|
+
schematicsVersion,
|
|
59
|
+
schemaVersion: types_1.CURRENT_META_SCHEMA_VERSION,
|
|
60
|
+
});
|
|
61
|
+
(0, base_store_1.writeReadme)(projectAbsDir);
|
|
62
|
+
}
|
|
63
|
+
catch (err) {
|
|
64
|
+
console.warn(chalk.yellow(`[warn] Failed to write .gadmin/.meta.json (first \`gadmin2 update\` will bootstrap instead): ${err && err.message ? err.message : err}`));
|
|
65
|
+
}
|
|
66
|
+
// @angular-devkit/schematics drops the source mode bits when writing
|
|
67
|
+
// generated files, so executable scripts (compose-ctl.sh, etc.) come
|
|
68
|
+
// out as 0644. Re-apply +x for known entry points.
|
|
69
|
+
try {
|
|
70
|
+
(0, executable_paths_1.ensureExecutableBits)((0, path_1.join)(process.cwd(), projectDirectory));
|
|
71
|
+
}
|
|
72
|
+
catch (_a) {
|
|
73
|
+
// best-effort, never fatal
|
|
74
|
+
}
|
|
45
75
|
}
|
|
46
76
|
if (!shouldSkipInstall) {
|
|
47
77
|
console.info('Begin to install server dependence ...');
|
package/actions/update.action.js
CHANGED
|
@@ -19,6 +19,7 @@ const child_process_1 = require("child_process");
|
|
|
19
19
|
const abstract_action_1 = require("./abstract.action");
|
|
20
20
|
const self_update_action_1 = require("./self-update.action");
|
|
21
21
|
const version_check_1 = require("../lib/version-check");
|
|
22
|
+
const executable_paths_1 = require("../lib/executable-paths");
|
|
22
23
|
const merge3way_1 = require("../lib/template-merge/merge3way");
|
|
23
24
|
const binary_detect_1 = require("../lib/template-merge/binary-detect");
|
|
24
25
|
const residual_scan_1 = require("../lib/template-merge/residual-scan");
|
|
@@ -46,58 +47,6 @@ function formatMaterializeError(schematicsVersion, err) {
|
|
|
46
47
|
const m = (_a = err === null || err === void 0 ? void 0 : err.message) !== null && _a !== void 0 ? _a : String(err);
|
|
47
48
|
return `\n❌ Failed to materialize base @ schematics ${schematicsVersion}: ${m}\n`;
|
|
48
49
|
}
|
|
49
|
-
/* ------------------------------------------------------------------ *
|
|
50
|
-
* Template config / path resolution
|
|
51
|
-
* ------------------------------------------------------------------ */
|
|
52
|
-
/**
|
|
53
|
-
* Locate the @gadmin2n/schematics install root, searching:
|
|
54
|
-
* 1. the instance project dir
|
|
55
|
-
* 2. the instance project's server/ subdir (where deps usually live)
|
|
56
|
-
* 3. the CLI's own node_modules (covers globally installed CLI where
|
|
57
|
-
* schematics ships alongside @gadmin2n/cli but is NOT installed in
|
|
58
|
-
* the user's project)
|
|
59
|
-
* Throws a helpful error if none of these resolve.
|
|
60
|
-
*/
|
|
61
|
-
function findSchematicsRoot(instanceDir = process.cwd()) {
|
|
62
|
-
const searchPaths = [
|
|
63
|
-
instanceDir,
|
|
64
|
-
path.join(instanceDir, 'server'),
|
|
65
|
-
path.resolve(__dirname, '..'),
|
|
66
|
-
];
|
|
67
|
-
for (const searchPath of searchPaths) {
|
|
68
|
-
try {
|
|
69
|
-
const pkgPath = require.resolve('@gadmin2n/schematics/package.json', {
|
|
70
|
-
paths: [searchPath],
|
|
71
|
-
});
|
|
72
|
-
return path.dirname(pkgPath);
|
|
73
|
-
}
|
|
74
|
-
catch (_a) {
|
|
75
|
-
// continue
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
throw new Error('Cannot resolve @gadmin2n/schematics package. ' +
|
|
79
|
-
'Make sure it is installed globally (alongside @gadmin2n/cli) ' +
|
|
80
|
-
'or as a dependency of the project.');
|
|
81
|
-
}
|
|
82
|
-
function resolveTemplatePath(templateName) {
|
|
83
|
-
const schematicsRoot = findSchematicsRoot();
|
|
84
|
-
const nameVariants = [templateName];
|
|
85
|
-
if (templateName.startsWith('gadmin2n-')) {
|
|
86
|
-
nameVariants.push(templateName.replace('gadmin2n-', 'gadmin2-'));
|
|
87
|
-
}
|
|
88
|
-
else if (templateName.startsWith('gadmin2-')) {
|
|
89
|
-
nameVariants.push(templateName.replace('gadmin2-', 'gadmin2n-'));
|
|
90
|
-
}
|
|
91
|
-
for (const name of nameVariants) {
|
|
92
|
-
const distPath = path.join(schematicsRoot, 'dist/lib/application/files', name);
|
|
93
|
-
const srcPath = path.join(schematicsRoot, 'src/lib/application/files', name);
|
|
94
|
-
if (fs.existsSync(distPath))
|
|
95
|
-
return distPath;
|
|
96
|
-
if (fs.existsSync(srcPath))
|
|
97
|
-
return srcPath;
|
|
98
|
-
}
|
|
99
|
-
throw new Error(`Template directory not found for "${templateName}" in ${schematicsRoot}`);
|
|
100
|
-
}
|
|
101
50
|
/* ------------------------------------------------------------------ *
|
|
102
51
|
* .claude context sync (from remote git repo)
|
|
103
52
|
* ------------------------------------------------------------------ */
|
|
@@ -584,19 +533,79 @@ function applyPlan(plan, baseRoot, oursRoot, theirsRoot, opts) {
|
|
|
584
533
|
/* ------------------------------------------------------------------ *
|
|
585
534
|
* Print summary
|
|
586
535
|
* ------------------------------------------------------------------ */
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
536
|
+
/**
|
|
537
|
+
* Unified file-list printer for a `ClassifiedPlan`. Used by both pre-apply
|
|
538
|
+
* preview / dry-run output AND post-apply summary so the wording stays
|
|
539
|
+
* consistent across the three call sites.
|
|
540
|
+
*
|
|
541
|
+
* Sections that have zero files are skipped entirely (no "0 files added"
|
|
542
|
+
* noise). Structural conflicts (add/add, modify/delete, delete/modify) are
|
|
543
|
+
* shown together because they're all "needs decision" situations the user
|
|
544
|
+
* should see before applying.
|
|
545
|
+
*
|
|
546
|
+
* The "kept" footer (`plan.keep + plan.noop`) is shown by default; pass
|
|
547
|
+
* `{ showKept: false }` to suppress it.
|
|
548
|
+
*/
|
|
549
|
+
function printPlanFileLists(plan, opts) {
|
|
550
|
+
var _a;
|
|
551
|
+
const showKept = (_a = opts === null || opts === void 0 ? void 0 : opts.showKept) !== null && _a !== void 0 ? _a : true;
|
|
552
|
+
const plural = (n) => (n === 1 ? '' : 's');
|
|
553
|
+
if (plan.update.length > 0) {
|
|
554
|
+
console.info(chalk.yellow(`📝 Updated (3-way merged) — ${plan.update.length} file${plural(plan.update.length)}`));
|
|
555
|
+
for (const f of plan.update)
|
|
556
|
+
console.info(` ${f}`);
|
|
557
|
+
console.info();
|
|
558
|
+
}
|
|
559
|
+
if (plan.add.length > 0) {
|
|
560
|
+
console.info(chalk.green(`➕ Added (template-only) — ${plan.add.length} file${plural(plan.add.length)}`));
|
|
561
|
+
for (const f of plan.add)
|
|
562
|
+
console.info(` ${f}`);
|
|
563
|
+
console.info();
|
|
564
|
+
}
|
|
565
|
+
if (plan.delete.length > 0) {
|
|
566
|
+
console.info(chalk.red(`🗑 Deleted (template removed) — ${plan.delete.length} file${plural(plan.delete.length)}`));
|
|
567
|
+
for (const f of plan.delete)
|
|
568
|
+
console.info(` ${f}`);
|
|
569
|
+
console.info();
|
|
570
|
+
}
|
|
571
|
+
const structural = [
|
|
572
|
+
...plan.addAddConflict.map((p) => ['add/add', p]),
|
|
573
|
+
...plan.modifyDeleteConflict.map((p) => ['modify/delete', p]),
|
|
574
|
+
...plan.deleteModifyConflict.map((p) => ['delete/modify', p]),
|
|
575
|
+
];
|
|
576
|
+
if (structural.length > 0) {
|
|
577
|
+
console.info(chalk.magenta(`⚠️ Structural conflicts — ${structural.length} file${plural(structural.length)}`));
|
|
578
|
+
const padKind = (k) => k.padEnd(16);
|
|
579
|
+
for (const [kind, p] of structural) {
|
|
580
|
+
console.info(` ${chalk.magenta(padKind(`[${kind}]`))} ${p}`);
|
|
581
|
+
}
|
|
582
|
+
console.info();
|
|
583
|
+
}
|
|
584
|
+
if (showKept) {
|
|
585
|
+
const keptCount = plan.keep.length + plan.noop.length;
|
|
586
|
+
if (keptCount > 0) {
|
|
587
|
+
console.info(chalk.gray(` ${keptCount} file${plural(keptCount)} kept (instance-only or identical)`));
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
/**
|
|
592
|
+
* Print post-apply conflict records (output of `applyPlan`). These differ
|
|
593
|
+
* from a `ClassifiedPlan` because they include `kind: 'content' | 'binary'`
|
|
594
|
+
* which is only known after the merge actually runs.
|
|
595
|
+
*/
|
|
596
|
+
function printConflictRecords(conflicts) {
|
|
597
|
+
const padKind = (k) => k.padEnd(16);
|
|
598
|
+
for (const c of conflicts) {
|
|
599
|
+
const kindLabel = `[${c.kind}]`;
|
|
600
|
+
console.info(` ${chalk.yellow(padKind(kindLabel))} ${c.path}` +
|
|
601
|
+
(c.detail ? chalk.gray(` (${c.detail})`) : ''));
|
|
593
602
|
}
|
|
594
603
|
}
|
|
595
604
|
function printSummary(plan, conflicts, templateName, schematicsVersion) {
|
|
596
605
|
console.info(chalk.bold('\n═══════════════════════════════════════════════════'));
|
|
597
606
|
console.info(chalk.bold(' Template Update Summary'));
|
|
598
607
|
console.info(chalk.bold('═══════════════════════════════════════════════════\n'));
|
|
599
|
-
|
|
608
|
+
printPlanFileLists(plan);
|
|
600
609
|
console.info();
|
|
601
610
|
if (conflicts.length === 0) {
|
|
602
611
|
console.info(chalk.green('✅ Template update complete — no conflicts.'));
|
|
@@ -604,12 +613,7 @@ function printSummary(plan, conflicts, templateName, schematicsVersion) {
|
|
|
604
613
|
return;
|
|
605
614
|
}
|
|
606
615
|
console.info(chalk.yellow(`⚠️ ${conflicts.length} conflict(s) need your attention:\n`));
|
|
607
|
-
|
|
608
|
-
for (const c of conflicts) {
|
|
609
|
-
const kindLabel = `[${c.kind}]`;
|
|
610
|
-
console.info(` ${chalk.yellow(padKind(kindLabel))} ${c.path}` +
|
|
611
|
-
(c.detail ? chalk.gray(` (${c.detail})`) : ''));
|
|
612
|
-
}
|
|
616
|
+
printConflictRecords(conflicts);
|
|
613
617
|
console.info();
|
|
614
618
|
console.info(chalk.gray(' Recommended workflow:'));
|
|
615
619
|
console.info(chalk.gray(' $ git status # see all modified files'));
|
|
@@ -762,7 +766,17 @@ class UpdateAction extends abstract_action_1.AbstractAction {
|
|
|
762
766
|
...((_b = cfg.templateExcludes) !== null && _b !== void 0 ? _b : []),
|
|
763
767
|
...((_d = (_c = cfg.templateUpdate) === null || _c === void 0 ? void 0 : _c.excludes) !== null && _d !== void 0 ? _d : []),
|
|
764
768
|
];
|
|
765
|
-
|
|
769
|
+
let theirsHandle;
|
|
770
|
+
try {
|
|
771
|
+
theirsHandle = (0, materialize_1.materializeLatest)(cfg.template);
|
|
772
|
+
}
|
|
773
|
+
catch (err) {
|
|
774
|
+
console.error(chalk.red(`\n❌ ${err.message}\n`));
|
|
775
|
+
process.exit(1);
|
|
776
|
+
return; // unreachable; satisfies TS
|
|
777
|
+
}
|
|
778
|
+
const theirsRoot = theirsHandle.dir;
|
|
779
|
+
const theirsVersion = theirsHandle.version;
|
|
766
780
|
const metaExists = fs.existsSync((0, base_store_1.metaPath)(instanceDir));
|
|
767
781
|
let baseHandle = null;
|
|
768
782
|
let baseRoot;
|
|
@@ -774,6 +788,7 @@ class UpdateAction extends abstract_action_1.AbstractAction {
|
|
|
774
788
|
baseRoot = baseHandle.dir;
|
|
775
789
|
}
|
|
776
790
|
catch (err) {
|
|
791
|
+
theirsHandle.cleanup();
|
|
777
792
|
console.error(chalk.red(formatMaterializeError((_e = meta === null || meta === void 0 ? void 0 : meta.schematicsVersion) !== null && _e !== void 0 ? _e : '?', err)));
|
|
778
793
|
process.exit(1);
|
|
779
794
|
}
|
|
@@ -784,7 +799,7 @@ class UpdateAction extends abstract_action_1.AbstractAction {
|
|
|
784
799
|
baseRoot = path.join(instanceDir, '.gadmin/__nonexistent__');
|
|
785
800
|
}
|
|
786
801
|
try {
|
|
787
|
-
console.info(`\n${chalk.gray('Template:')}
|
|
802
|
+
console.info(`\n${chalk.gray('Template:')} @gadmin2n/schematics@${theirsVersion} (latest)`);
|
|
788
803
|
console.info(`${chalk.gray('Instance:')} ${instanceDir}`);
|
|
789
804
|
console.info(`${chalk.gray('Base: ')} ${metaExists
|
|
790
805
|
? `(materialized from schematics ${meta.schematicsVersion})`
|
|
@@ -830,6 +845,7 @@ class UpdateAction extends abstract_action_1.AbstractAction {
|
|
|
830
845
|
finally {
|
|
831
846
|
if (baseHandle)
|
|
832
847
|
baseHandle.cleanup();
|
|
848
|
+
theirsHandle.cleanup();
|
|
833
849
|
}
|
|
834
850
|
});
|
|
835
851
|
}
|
|
@@ -863,13 +879,14 @@ class UpdateAction extends abstract_action_1.AbstractAction {
|
|
|
863
879
|
const allExcludes = [...excludes_1.HARDCODED_EXCLUDES, ...excludes_1.DEFAULT_EXCLUDES, ...userExcludes];
|
|
864
880
|
const instanceFiles = (0, excludes_1.collectFiles)(instanceDir, allExcludes);
|
|
865
881
|
const residual = (0, residual_scan_1.scanResidualMarkers)(instanceDir, instanceFiles);
|
|
866
|
-
let
|
|
882
|
+
let theirsHandle;
|
|
867
883
|
try {
|
|
868
|
-
|
|
884
|
+
theirsHandle = (0, materialize_1.materializeLatest)(cfg.template);
|
|
869
885
|
}
|
|
870
886
|
catch (err) {
|
|
871
887
|
console.error(chalk.red(err.message + '\n'));
|
|
872
888
|
process.exit(2);
|
|
889
|
+
return; // unreachable
|
|
873
890
|
}
|
|
874
891
|
let baseHandle = null;
|
|
875
892
|
try {
|
|
@@ -880,7 +897,7 @@ class UpdateAction extends abstract_action_1.AbstractAction {
|
|
|
880
897
|
console.error(chalk.red(formatMaterializeError(meta.schematicsVersion, err)));
|
|
881
898
|
process.exit(2);
|
|
882
899
|
}
|
|
883
|
-
const plan = (0, classify_1.classifyAll)(baseHandle.dir, instanceDir,
|
|
900
|
+
const plan = (0, classify_1.classifyAll)(baseHandle.dir, instanceDir, theirsHandle.dir, userExcludes);
|
|
884
901
|
const structural = [
|
|
885
902
|
...plan.addAddConflict.map((p) => ({ kind: 'add/add', path: p })),
|
|
886
903
|
...plan.modifyDeleteConflict.map((p) => ({ kind: 'modify/delete', path: p })),
|
|
@@ -916,6 +933,7 @@ class UpdateAction extends abstract_action_1.AbstractAction {
|
|
|
916
933
|
finally {
|
|
917
934
|
if (baseHandle)
|
|
918
935
|
baseHandle.cleanup();
|
|
936
|
+
theirsHandle.cleanup();
|
|
919
937
|
}
|
|
920
938
|
});
|
|
921
939
|
}
|
|
@@ -929,13 +947,15 @@ class UpdateAction extends abstract_action_1.AbstractAction {
|
|
|
929
947
|
const templateEnabled = ((_e = options.find((o) => o.name === 'template')) === null || _e === void 0 ? void 0 : _e.value) !== false;
|
|
930
948
|
const claudeEnabled = ((_f = options.find((o) => o.name === 'coding-context')) === null || _f === void 0 ? void 0 : _f.value) !== false;
|
|
931
949
|
if (!templateEnabled && !claudeEnabled) {
|
|
932
|
-
|
|
950
|
+
// Should never happen under the opt-in selector — kept as a safety net.
|
|
951
|
+
console.error(chalk.red('\nNothing to do: neither stage was selected.\n'));
|
|
933
952
|
process.exit(1);
|
|
934
953
|
}
|
|
935
954
|
const mergeOpts = { ours, theirs };
|
|
936
955
|
let pkgSnapshots = null;
|
|
937
956
|
let preflight = null;
|
|
938
957
|
let baseHandle = null;
|
|
958
|
+
let theirsHandle = null;
|
|
939
959
|
try {
|
|
940
960
|
if (templateEnabled) {
|
|
941
961
|
try {
|
|
@@ -946,8 +966,20 @@ class UpdateAction extends abstract_action_1.AbstractAction {
|
|
|
946
966
|
process.exit(1);
|
|
947
967
|
}
|
|
948
968
|
const { instanceDir, templateName, userExcludes } = preflight;
|
|
949
|
-
|
|
950
|
-
|
|
969
|
+
// Pull the latest schematics tarball from npm to use as `theirs`.
|
|
970
|
+
// Fail-hard: if the registry is unreachable, abort the update — we
|
|
971
|
+
// don't want to silently fall back to a stale local copy.
|
|
972
|
+
try {
|
|
973
|
+
theirsHandle = (0, materialize_1.materializeLatest)(templateName);
|
|
974
|
+
}
|
|
975
|
+
catch (err) {
|
|
976
|
+
console.error(chalk.red(`\n❌ ${err.message}\n`));
|
|
977
|
+
process.exit(1);
|
|
978
|
+
return; // unreachable
|
|
979
|
+
}
|
|
980
|
+
const theirsRoot = theirsHandle.dir;
|
|
981
|
+
const schematicsVersion = theirsHandle.version;
|
|
982
|
+
console.info(`\n${chalk.gray('Template:')} @gadmin2n/schematics@${schematicsVersion} (latest)`);
|
|
951
983
|
console.info(`${chalk.gray('Instance:')} ${instanceDir}\n`);
|
|
952
984
|
// Snapshot package.json before any writes (for stage 3 install)
|
|
953
985
|
pkgSnapshots = [
|
|
@@ -955,13 +987,10 @@ class UpdateAction extends abstract_action_1.AbstractAction {
|
|
|
955
987
|
snapshotPackageJson(path.join(instanceDir, 'web'), 'web'),
|
|
956
988
|
snapshotPackageJson(path.join(instanceDir, 'server'), 'server'),
|
|
957
989
|
];
|
|
958
|
-
const schematicsRoot = findSchematicsRoot(instanceDir);
|
|
959
|
-
const schematicsPkg = require(path.join(schematicsRoot, 'package.json'));
|
|
960
|
-
const schematicsVersion = schematicsPkg.version || '0.0.0';
|
|
961
990
|
// First-time bootstrap: meta absent → write meta + README, exit.
|
|
962
991
|
if (!fs.existsSync((0, base_store_1.metaPath)(instanceDir))) {
|
|
963
992
|
if (dryRun) {
|
|
964
|
-
console.info(chalk.gray('(dry-run: would write .gadmin/.meta.json pinned to
|
|
993
|
+
console.info(chalk.gray('(dry-run: would write .gadmin/.meta.json pinned to latest schematics version, then exit)\n'));
|
|
965
994
|
}
|
|
966
995
|
else {
|
|
967
996
|
(0, base_store_1.writeMeta)(instanceDir, {
|
|
@@ -970,7 +999,7 @@ class UpdateAction extends abstract_action_1.AbstractAction {
|
|
|
970
999
|
});
|
|
971
1000
|
(0, base_store_1.writeReadme)(instanceDir);
|
|
972
1001
|
console.info(chalk.green('✅ Baseline established at .gadmin/.meta.json'));
|
|
973
|
-
console.info(chalk.gray(` Pinned to schematics ${schematicsVersion}.`));
|
|
1002
|
+
console.info(chalk.gray(` Pinned to schematics ${schematicsVersion} (latest from npm).`));
|
|
974
1003
|
console.info(chalk.gray(' The next `gadmin2 update` will perform a real 3-way merge.'));
|
|
975
1004
|
}
|
|
976
1005
|
// Skip the rest of stage 1 — nothing to merge yet.
|
|
@@ -1006,33 +1035,61 @@ class UpdateAction extends abstract_action_1.AbstractAction {
|
|
|
1006
1035
|
console.info(chalk.bold('═══════════════════════════════════════════════════'));
|
|
1007
1036
|
console.info(chalk.bold(' Template Update Plan (dry-run)'));
|
|
1008
1037
|
console.info(chalk.bold('═══════════════════════════════════════════════════\n'));
|
|
1009
|
-
|
|
1010
|
-
console.info(chalk.green(` add ${f}`));
|
|
1011
|
-
for (const f of plan.delete)
|
|
1012
|
-
console.info(chalk.red(` delete ${f}`));
|
|
1013
|
-
for (const f of plan.update)
|
|
1014
|
-
console.info(chalk.yellow(` update ${f}`));
|
|
1015
|
-
for (const f of plan.addAddConflict)
|
|
1016
|
-
console.info(chalk.magenta(` add/add ${f}`));
|
|
1017
|
-
for (const f of plan.modifyDeleteConflict)
|
|
1018
|
-
console.info(chalk.magenta(` modify/delete ${f}`));
|
|
1019
|
-
for (const f of plan.deleteModifyConflict)
|
|
1020
|
-
console.info(chalk.magenta(` delete/modify ${f}`));
|
|
1038
|
+
printPlanFileLists(plan);
|
|
1021
1039
|
console.info();
|
|
1022
1040
|
}
|
|
1023
1041
|
else {
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1042
|
+
// Pre-apply preview + confirm (skipped under --yes).
|
|
1043
|
+
const applyAndReport = () => {
|
|
1044
|
+
const conflicts = applyPlan(plan, baseRoot, instanceDir, theirsRoot, mergeOpts);
|
|
1045
|
+
// Always advance meta to new version, even when conflicts present (invariant #2).
|
|
1046
|
+
(0, base_store_1.writeMeta)(instanceDir, {
|
|
1047
|
+
schematicsVersion,
|
|
1048
|
+
schemaVersion: types_1.CURRENT_META_SCHEMA_VERSION,
|
|
1049
|
+
});
|
|
1050
|
+
// Re-apply +x to known script entry points: applyPlan uses
|
|
1051
|
+
// fs.copyFileSync for newly-added files, which does not preserve
|
|
1052
|
+
// the source template's mode bits.
|
|
1053
|
+
try {
|
|
1054
|
+
(0, executable_paths_1.ensureExecutableBits)(instanceDir);
|
|
1055
|
+
}
|
|
1056
|
+
catch (_a) {
|
|
1057
|
+
// best-effort, never fatal
|
|
1058
|
+
}
|
|
1059
|
+
printSummary(plan, conflicts, templateName, schematicsVersion);
|
|
1060
|
+
};
|
|
1061
|
+
if (autoYes) {
|
|
1062
|
+
applyAndReport();
|
|
1063
|
+
}
|
|
1064
|
+
else {
|
|
1065
|
+
console.info(chalk.bold('\n═══════════════════════════════════════════════════'));
|
|
1066
|
+
console.info(chalk.bold(' Template Update Preview'));
|
|
1067
|
+
console.info(chalk.bold('═══════════════════════════════════════════════════\n'));
|
|
1068
|
+
printPlanFileLists(plan);
|
|
1069
|
+
console.info();
|
|
1070
|
+
const { proceedTemplate } = yield inquirer.prompt([
|
|
1071
|
+
{
|
|
1072
|
+
type: 'confirm',
|
|
1073
|
+
name: 'proceedTemplate',
|
|
1074
|
+
message: 'Apply this template update to your instance?',
|
|
1075
|
+
default: true,
|
|
1076
|
+
},
|
|
1077
|
+
]);
|
|
1078
|
+
if (proceedTemplate) {
|
|
1079
|
+
applyAndReport();
|
|
1080
|
+
}
|
|
1081
|
+
else {
|
|
1082
|
+
console.info(chalk.gray('\nTemplate update skipped — base & files unchanged.\n'));
|
|
1083
|
+
// Fall through to stage 2 (.claude sync) and stage 3 (install).
|
|
1084
|
+
// pkgSnapshots are already captured but package.json is unchanged,
|
|
1085
|
+
// so maybeInstallChangedDeps will naturally find no diff and skip.
|
|
1086
|
+
}
|
|
1087
|
+
}
|
|
1031
1088
|
}
|
|
1032
1089
|
}
|
|
1033
1090
|
}
|
|
1034
1091
|
else {
|
|
1035
|
-
console.info(chalk.gray('\nSkipping template update (
|
|
1092
|
+
console.info(chalk.gray('\nSkipping template update (not selected).\n'));
|
|
1036
1093
|
}
|
|
1037
1094
|
// Stage 2: .claude context sync — UNCHANGED.
|
|
1038
1095
|
if (claudeEnabled) {
|
|
@@ -1043,11 +1100,11 @@ class UpdateAction extends abstract_action_1.AbstractAction {
|
|
|
1043
1100
|
yield syncClaudeContext(process.cwd(), dryRun, claudeSource);
|
|
1044
1101
|
}
|
|
1045
1102
|
else {
|
|
1046
|
-
console.info(chalk.gray('\nSkipping .claude context sync (per user choice
|
|
1103
|
+
console.info(chalk.gray('\nSkipping .claude context sync (per user choice).\n'));
|
|
1047
1104
|
}
|
|
1048
1105
|
}
|
|
1049
1106
|
else {
|
|
1050
|
-
console.info(chalk.gray('\nSkipping .claude context sync (
|
|
1107
|
+
console.info(chalk.gray('\nSkipping .claude context sync (not selected).\n'));
|
|
1051
1108
|
}
|
|
1052
1109
|
// Stage 3: Install changed deps — UNCHANGED.
|
|
1053
1110
|
if (pkgSnapshots) {
|
|
@@ -1057,6 +1114,8 @@ class UpdateAction extends abstract_action_1.AbstractAction {
|
|
|
1057
1114
|
finally {
|
|
1058
1115
|
if (baseHandle)
|
|
1059
1116
|
baseHandle.cleanup();
|
|
1117
|
+
if (theirsHandle)
|
|
1118
|
+
theirsHandle.cleanup();
|
|
1060
1119
|
if (preflight)
|
|
1061
1120
|
preflight.releaseLock();
|
|
1062
1121
|
}
|
|
@@ -25,23 +25,32 @@ class UpdateCommand extends abstract_command_1.AbstractCommand {
|
|
|
25
25
|
' Conflict resolution:\n' +
|
|
26
26
|
' Conflicts are written into source files as <<<<<<< INSTANCE / >>>>>>> TEMPLATE markers.\n' +
|
|
27
27
|
' Resolve them in your editor, verify with `gadmin2 update status`, then commit.\n\n' +
|
|
28
|
-
' Stage
|
|
29
|
-
' --
|
|
30
|
-
' --
|
|
28
|
+
' Stage selection (opt-in selector — when omitted, both stages run):\n' +
|
|
29
|
+
' --template Run only the template merge + deps install stage.\n' +
|
|
30
|
+
' --coding-context Run only the .claude context sync stage.\n' +
|
|
31
|
+
' (pass both / neither) Run both stages.\n')
|
|
31
32
|
.option('--no-content', '[diff] Only show file list, skip inline diff content.')
|
|
32
33
|
.option('-d, --dry-run', '[update] Show what would be updated without making changes.')
|
|
33
|
-
.option('-y, --yes', '[update] Skip interactive prompts (
|
|
34
|
+
.option('-y, --yes', '[update] Skip interactive prompts (template confirm, .claude protocol).')
|
|
34
35
|
.option('--ours', '[update] Resolve all conflicts by keeping the instance side (mutually exclusive with --theirs).')
|
|
35
36
|
.option('--theirs', '[update] Resolve all conflicts by taking the template side (mutually exclusive with --ours).')
|
|
36
37
|
.option('--no-version-check', '[update | diff] Skip the npm registry version check at startup.')
|
|
37
38
|
.option('--coding-context-repo <url>', '[update] Fully override the .claude context repo URL.')
|
|
38
39
|
.option('--coding-context-protocol <proto>', '[update] One of: https | ssh | skip.')
|
|
39
|
-
.option('--
|
|
40
|
-
.option('--
|
|
40
|
+
.option('--template', '[update] Run only the template merge + deps install stage.')
|
|
41
|
+
.option('--coding-context', '[update] Run only the .claude context sync stage.')
|
|
41
42
|
.action((subcommand, arg, command) => __awaiter(this, void 0, void 0, function* () {
|
|
42
43
|
const inputs = [];
|
|
43
44
|
inputs.push({ name: 'subcommand', value: subcommand || '' });
|
|
44
45
|
inputs.push({ name: 'subcommand-arg', value: arg || '' });
|
|
46
|
+
// Opt-in stage selector: when neither --template nor --coding-context
|
|
47
|
+
// is passed, run both stages (default). When at least one is passed,
|
|
48
|
+
// run only the stage(s) explicitly requested.
|
|
49
|
+
const tplFlag = !!command.template;
|
|
50
|
+
const ctxFlag = !!command.codingContext;
|
|
51
|
+
const anySelector = tplFlag || ctxFlag;
|
|
52
|
+
const templateEnabled = anySelector ? tplFlag : true;
|
|
53
|
+
const codingContextEnabled = anySelector ? ctxFlag : true;
|
|
45
54
|
const options = [];
|
|
46
55
|
options.push({ name: 'no-content', value: !command.content });
|
|
47
56
|
options.push({ name: 'dry-run', value: !!command.dryRun });
|
|
@@ -51,8 +60,8 @@ class UpdateCommand extends abstract_command_1.AbstractCommand {
|
|
|
51
60
|
options.push({ name: 'version-check', value: command.versionCheck !== false });
|
|
52
61
|
options.push({ name: 'coding-context-repo', value: command.codingContextRepo || '' });
|
|
53
62
|
options.push({ name: 'coding-context-protocol', value: command.codingContextProtocol || '' });
|
|
54
|
-
options.push({ name: 'coding-context', value:
|
|
55
|
-
options.push({ name: 'template', value:
|
|
63
|
+
options.push({ name: 'coding-context', value: codingContextEnabled });
|
|
64
|
+
options.push({ name: 'template', value: templateEnabled });
|
|
56
65
|
yield this.action.handle(inputs, options);
|
|
57
66
|
}));
|
|
58
67
|
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Files (relative to instance / project root) that must carry the executable
|
|
3
|
+
* bit. Both `gadmin2 n` and `gadmin2 update` re-apply this set after writing
|
|
4
|
+
* files because:
|
|
5
|
+
*
|
|
6
|
+
* - @angular-devkit/schematics writes generated files via fs.writeFile, which
|
|
7
|
+
* does NOT preserve the source template's mode.
|
|
8
|
+
* - update.action's applyPlan uses fs.copyFileSync for "added" files, which
|
|
9
|
+
* also does not preserve mode.
|
|
10
|
+
*
|
|
11
|
+
* Without this step the user has to `chmod +x compose-ctl.sh` manually after
|
|
12
|
+
* every `gadmin2 n` / `gadmin2 update`.
|
|
13
|
+
*
|
|
14
|
+
* KEEP IN SYNC with the schematics templates: any new shell entry point added
|
|
15
|
+
* to schematics that the user is expected to invoke directly should be listed
|
|
16
|
+
* here too.
|
|
17
|
+
*/
|
|
18
|
+
export declare const EXECUTABLE_PATHS: ReadonlyArray<string>;
|
|
19
|
+
/**
|
|
20
|
+
* Best-effort: add the executable bit to known script entry points under
|
|
21
|
+
* `projectDir`. Only files that actually exist are touched. Failures are
|
|
22
|
+
* silently swallowed — chmod is a no-op on Windows and in containers without
|
|
23
|
+
* permission, but neither is a reason to abort the broader command.
|
|
24
|
+
*
|
|
25
|
+
* Returns the list of paths that were updated, suitable for verbose logging.
|
|
26
|
+
*/
|
|
27
|
+
export declare function ensureExecutableBits(projectDir: string): string[];
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ensureExecutableBits = exports.EXECUTABLE_PATHS = void 0;
|
|
4
|
+
const fs = require("fs");
|
|
5
|
+
const path = require("path");
|
|
6
|
+
/**
|
|
7
|
+
* Files (relative to instance / project root) that must carry the executable
|
|
8
|
+
* bit. Both `gadmin2 n` and `gadmin2 update` re-apply this set after writing
|
|
9
|
+
* files because:
|
|
10
|
+
*
|
|
11
|
+
* - @angular-devkit/schematics writes generated files via fs.writeFile, which
|
|
12
|
+
* does NOT preserve the source template's mode.
|
|
13
|
+
* - update.action's applyPlan uses fs.copyFileSync for "added" files, which
|
|
14
|
+
* also does not preserve mode.
|
|
15
|
+
*
|
|
16
|
+
* Without this step the user has to `chmod +x compose-ctl.sh` manually after
|
|
17
|
+
* every `gadmin2 n` / `gadmin2 update`.
|
|
18
|
+
*
|
|
19
|
+
* KEEP IN SYNC with the schematics templates: any new shell entry point added
|
|
20
|
+
* to schematics that the user is expected to invoke directly should be listed
|
|
21
|
+
* here too.
|
|
22
|
+
*/
|
|
23
|
+
exports.EXECUTABLE_PATHS = [
|
|
24
|
+
'compose-ctl.sh',
|
|
25
|
+
'server/start-prod.sh',
|
|
26
|
+
];
|
|
27
|
+
/**
|
|
28
|
+
* Best-effort: add the executable bit to known script entry points under
|
|
29
|
+
* `projectDir`. Only files that actually exist are touched. Failures are
|
|
30
|
+
* silently swallowed — chmod is a no-op on Windows and in containers without
|
|
31
|
+
* permission, but neither is a reason to abort the broader command.
|
|
32
|
+
*
|
|
33
|
+
* Returns the list of paths that were updated, suitable for verbose logging.
|
|
34
|
+
*/
|
|
35
|
+
function ensureExecutableBits(projectDir) {
|
|
36
|
+
const updated = [];
|
|
37
|
+
for (const rel of exports.EXECUTABLE_PATHS) {
|
|
38
|
+
const abs = path.join(projectDir, rel);
|
|
39
|
+
if (!fs.existsSync(abs))
|
|
40
|
+
continue;
|
|
41
|
+
try {
|
|
42
|
+
const cur = fs.statSync(abs).mode;
|
|
43
|
+
const next = cur | 0o111; // owner+group+other +x
|
|
44
|
+
if (next !== cur) {
|
|
45
|
+
fs.chmodSync(abs, next);
|
|
46
|
+
updated.push(rel);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
catch (_a) {
|
|
50
|
+
// ignore — Windows / restricted FS
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return updated;
|
|
54
|
+
}
|
|
55
|
+
exports.ensureExecutableBits = ensureExecutableBits;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Locate the @gadmin2n/schematics install root, searching:
|
|
3
|
+
* 1. the supplied instance dir (defaults to process.cwd())
|
|
4
|
+
* 2. <instanceDir>/server (where deps are usually hoisted)
|
|
5
|
+
* 3. the CLI's own node_modules — covers globally installed CLI where
|
|
6
|
+
* schematics ships alongside @gadmin2n/cli but is NOT installed in
|
|
7
|
+
* the user's project.
|
|
8
|
+
*
|
|
9
|
+
* Throws a helpful error if none of these resolve.
|
|
10
|
+
*
|
|
11
|
+
* Used by:
|
|
12
|
+
* - update.action (resolveTemplatePath, schematics version pin)
|
|
13
|
+
* - new.action (meta bootstrap at project init)
|
|
14
|
+
*/
|
|
15
|
+
export declare function findSchematicsRoot(instanceDir?: string): string;
|
|
16
|
+
/**
|
|
17
|
+
* Convenience wrapper: returns the version field of @gadmin2n/schematics's
|
|
18
|
+
* package.json. Falls back to '0.0.0' if the field is missing.
|
|
19
|
+
*/
|
|
20
|
+
export declare function getSchematicsVersion(instanceDir?: string): string;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getSchematicsVersion = exports.findSchematicsRoot = void 0;
|
|
4
|
+
const path = require("path");
|
|
5
|
+
/**
|
|
6
|
+
* Locate the @gadmin2n/schematics install root, searching:
|
|
7
|
+
* 1. the supplied instance dir (defaults to process.cwd())
|
|
8
|
+
* 2. <instanceDir>/server (where deps are usually hoisted)
|
|
9
|
+
* 3. the CLI's own node_modules — covers globally installed CLI where
|
|
10
|
+
* schematics ships alongside @gadmin2n/cli but is NOT installed in
|
|
11
|
+
* the user's project.
|
|
12
|
+
*
|
|
13
|
+
* Throws a helpful error if none of these resolve.
|
|
14
|
+
*
|
|
15
|
+
* Used by:
|
|
16
|
+
* - update.action (resolveTemplatePath, schematics version pin)
|
|
17
|
+
* - new.action (meta bootstrap at project init)
|
|
18
|
+
*/
|
|
19
|
+
function findSchematicsRoot(instanceDir = process.cwd()) {
|
|
20
|
+
const searchPaths = [
|
|
21
|
+
instanceDir,
|
|
22
|
+
path.join(instanceDir, 'server'),
|
|
23
|
+
path.resolve(__dirname, '..'),
|
|
24
|
+
];
|
|
25
|
+
for (const searchPath of searchPaths) {
|
|
26
|
+
try {
|
|
27
|
+
const pkgPath = require.resolve('@gadmin2n/schematics/package.json', {
|
|
28
|
+
paths: [searchPath],
|
|
29
|
+
});
|
|
30
|
+
return path.dirname(pkgPath);
|
|
31
|
+
}
|
|
32
|
+
catch (_a) {
|
|
33
|
+
// continue
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
throw new Error('Cannot resolve @gadmin2n/schematics package. ' +
|
|
37
|
+
'Make sure it is installed globally (alongside @gadmin2n/cli) ' +
|
|
38
|
+
'or as a dependency of the project.');
|
|
39
|
+
}
|
|
40
|
+
exports.findSchematicsRoot = findSchematicsRoot;
|
|
41
|
+
/**
|
|
42
|
+
* Convenience wrapper: returns the version field of @gadmin2n/schematics's
|
|
43
|
+
* package.json. Falls back to '0.0.0' if the field is missing.
|
|
44
|
+
*/
|
|
45
|
+
function getSchematicsVersion(instanceDir) {
|
|
46
|
+
const root = findSchematicsRoot(instanceDir);
|
|
47
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
48
|
+
const pkg = require(path.join(root, 'package.json'));
|
|
49
|
+
return (pkg && pkg.version) || '0.0.0';
|
|
50
|
+
}
|
|
51
|
+
exports.getSchematicsVersion = getSchematicsVersion;
|
|
@@ -4,6 +4,10 @@ export interface MaterializedBase {
|
|
|
4
4
|
/** Idempotent cleanup of any temp dirs created. */
|
|
5
5
|
cleanup: () => void;
|
|
6
6
|
}
|
|
7
|
+
export interface MaterializedLatest extends MaterializedBase {
|
|
8
|
+
/** Resolved version string (whatever the registry / env override returned). */
|
|
9
|
+
version: string;
|
|
10
|
+
}
|
|
7
11
|
/**
|
|
8
12
|
* Materialize a base reference for 3-way merge.
|
|
9
13
|
*
|
|
@@ -16,3 +20,26 @@ export interface MaterializedBase {
|
|
|
16
20
|
* Returned `cleanup()` removes any tmp dir we created.
|
|
17
21
|
*/
|
|
18
22
|
export declare function materializeBase(schematicsVersion: string, templateName: string): MaterializedBase;
|
|
23
|
+
/**
|
|
24
|
+
* Resolve the latest published version of `@gadmin2n/schematics`.
|
|
25
|
+
*
|
|
26
|
+
* Sources, in priority order:
|
|
27
|
+
* 1. `GADMIN2_LATEST_VERSION` env var — used by tests and `--use-version=X`
|
|
28
|
+
* style overrides.
|
|
29
|
+
* 2. `npm view @gadmin2n/schematics version --json` — production. Hard
|
|
30
|
+
* timeout via npm's own behaviour; we do not catch ENOENT here because a
|
|
31
|
+
* missing `npm` is a fatal config error.
|
|
32
|
+
*
|
|
33
|
+
* Throws on any failure (fail-hard per design decision: theirs must always be
|
|
34
|
+
* truly latest, callers should not silently fall back to a stale local copy).
|
|
35
|
+
*/
|
|
36
|
+
export declare function resolveLatestVersion(): string;
|
|
37
|
+
/**
|
|
38
|
+
* Materialize the LATEST published template as the `theirs` side of a
|
|
39
|
+
* 3-way merge. Equivalent to `materializeBase(resolveLatestVersion(), ...)`
|
|
40
|
+
* with the resolved version returned alongside the handle.
|
|
41
|
+
*
|
|
42
|
+
* Per design (fail-hard on network failure): if either step throws, the
|
|
43
|
+
* caller is expected to surface the error and abort the update.
|
|
44
|
+
*/
|
|
45
|
+
export declare function materializeLatest(templateName: string): MaterializedLatest;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.materializeBase = void 0;
|
|
3
|
+
exports.materializeLatest = exports.resolveLatestVersion = exports.materializeBase = void 0;
|
|
4
4
|
// cli/lib/template-merge/materialize.ts
|
|
5
5
|
const fs = require("fs");
|
|
6
6
|
const os = require("os");
|
|
@@ -127,3 +127,60 @@ function copyTreeRecursive(src, dst) {
|
|
|
127
127
|
// symlinks / sockets: skip
|
|
128
128
|
}
|
|
129
129
|
}
|
|
130
|
+
/**
|
|
131
|
+
* Resolve the latest published version of `@gadmin2n/schematics`.
|
|
132
|
+
*
|
|
133
|
+
* Sources, in priority order:
|
|
134
|
+
* 1. `GADMIN2_LATEST_VERSION` env var — used by tests and `--use-version=X`
|
|
135
|
+
* style overrides.
|
|
136
|
+
* 2. `npm view @gadmin2n/schematics version --json` — production. Hard
|
|
137
|
+
* timeout via npm's own behaviour; we do not catch ENOENT here because a
|
|
138
|
+
* missing `npm` is a fatal config error.
|
|
139
|
+
*
|
|
140
|
+
* Throws on any failure (fail-hard per design decision: theirs must always be
|
|
141
|
+
* truly latest, callers should not silently fall back to a stale local copy).
|
|
142
|
+
*/
|
|
143
|
+
function resolveLatestVersion() {
|
|
144
|
+
var _a, _b, _c, _d;
|
|
145
|
+
const envOverride = process.env.GADMIN2_LATEST_VERSION;
|
|
146
|
+
if (envOverride)
|
|
147
|
+
return envOverride.trim();
|
|
148
|
+
try {
|
|
149
|
+
const out = (0, child_process_1.execFileSync)('npm', ['view', SCHEMATICS_PKG, 'version', '--json'], { encoding: 'utf-8', stdio: ['ignore', 'pipe', 'pipe'] });
|
|
150
|
+
const trimmed = out.trim();
|
|
151
|
+
// `--json` wraps the value in quotes for plain strings.
|
|
152
|
+
const parsed = trimmed.startsWith('"') ? JSON.parse(trimmed) : trimmed;
|
|
153
|
+
if (typeof parsed !== 'string' || parsed.length === 0) {
|
|
154
|
+
throw new Error(`unexpected npm view output: ${out}`);
|
|
155
|
+
}
|
|
156
|
+
return parsed;
|
|
157
|
+
}
|
|
158
|
+
catch (err) {
|
|
159
|
+
if (err && err.code === 'ENOENT') {
|
|
160
|
+
throw new Error('npm not in PATH — required to resolve the latest @gadmin2n/schematics version. ' +
|
|
161
|
+
'Install Node.js / npm and retry.');
|
|
162
|
+
}
|
|
163
|
+
const stderr = (_c = (_b = (_a = err === null || err === void 0 ? void 0 : err.stderr) === null || _a === void 0 ? void 0 : _a.toString) === null || _b === void 0 ? void 0 : _b.call(_a)) !== null && _c !== void 0 ? _c : '';
|
|
164
|
+
const message = (_d = err === null || err === void 0 ? void 0 : err.message) !== null && _d !== void 0 ? _d : String(err);
|
|
165
|
+
throw new Error(`Failed to resolve latest @gadmin2n/schematics version from npm registry.\n` +
|
|
166
|
+
` ${message.split('\n').join('\n ')}` +
|
|
167
|
+
(stderr ? `\n ${stderr.trim().split('\n').join('\n ')}` : '') +
|
|
168
|
+
`\n Check your network / npm registry config and retry, ` +
|
|
169
|
+
`or set GADMIN2_LATEST_VERSION=<version> to pin a specific version.`);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
exports.resolveLatestVersion = resolveLatestVersion;
|
|
173
|
+
/**
|
|
174
|
+
* Materialize the LATEST published template as the `theirs` side of a
|
|
175
|
+
* 3-way merge. Equivalent to `materializeBase(resolveLatestVersion(), ...)`
|
|
176
|
+
* with the resolved version returned alongside the handle.
|
|
177
|
+
*
|
|
178
|
+
* Per design (fail-hard on network failure): if either step throws, the
|
|
179
|
+
* caller is expected to surface the error and abort the update.
|
|
180
|
+
*/
|
|
181
|
+
function materializeLatest(templateName) {
|
|
182
|
+
const version = resolveLatestVersion();
|
|
183
|
+
const handle = materializeBase(version, templateName);
|
|
184
|
+
return Object.assign(Object.assign({}, handle), { version });
|
|
185
|
+
}
|
|
186
|
+
exports.materializeLatest = materializeLatest;
|