@mean-weasel/lineage 0.1.13 → 0.1.15
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/CHANGELOG.md +12 -0
- package/README.md +37 -3
- package/dist/cli/lineage-channel.js +61 -34
- package/dist/cli/lineage-channel.js.map +3 -3
- package/dist/cli/lineage-dev.js +161 -15
- package/dist/cli/lineage-dev.js.map +3 -3
- package/dist/cli/lineage-preview.js +161 -15
- package/dist/cli/lineage-preview.js.map +3 -3
- package/dist/cli/lineage.js +161 -15
- package/dist/cli/lineage.js.map +3 -3
- package/dist/runtime-build.json +4 -4
- package/dist/server.js +13 -7
- package/dist/server.js.map +3 -3
- package/dist/web/assets/{index-3uJbXqPS.css → app-CDxSvoD3.css} +1 -1
- package/dist/web/assets/app-CYUgf6rX.js +16 -0
- package/dist/web/assets/jsx-runtime-_Rdg6et1.js +8 -0
- package/dist/web/assets/landing-D3Y679_B.css +1 -0
- package/dist/web/assets/landing-ii8AH_DG.js +1 -0
- package/dist/web/index.html +3 -2
- package/dist/web/landing/index.html +19 -0
- package/package.json +2 -1
- package/dist/web/assets/index-BrGIkUus.js +0 -23
|
@@ -17,7 +17,7 @@ import { existsSync as existsSync6, mkdirSync as mkdirSync5 } from "node:fs";
|
|
|
17
17
|
import { join as join6 } from "node:path";
|
|
18
18
|
|
|
19
19
|
// src/server/assetCore.ts
|
|
20
|
-
import { existsSync as existsSync3, mkdirSync as mkdirSync2, readdirSync as readdirSync2, readFileSync as readFileSync2, unlinkSync, writeFileSync } from "node:fs";
|
|
20
|
+
import { chmodSync, existsSync as existsSync3, mkdirSync as mkdirSync2, readdirSync as readdirSync2, readFileSync as readFileSync2, unlinkSync, writeFileSync } from "node:fs";
|
|
21
21
|
import { dirname as dirname2, join as join3, resolve as resolve2 } from "node:path";
|
|
22
22
|
import { spawnSync } from "node:child_process";
|
|
23
23
|
import { fileURLToPath } from "node:url";
|
|
@@ -590,21 +590,26 @@ function validateProject(project = defaultProject) {
|
|
|
590
590
|
|
|
591
591
|
// src/server/profileWriterLease.ts
|
|
592
592
|
import { createHash as createHash3, randomUUID as randomUUID2, timingSafeEqual } from "node:crypto";
|
|
593
|
-
import { existsSync as existsSync5, lstatSync, mkdirSync as mkdirSync4, readFileSync as readFileSync4, renameSync, rmSync as rmSync2, writeFileSync as writeFileSync3 } from "node:fs";
|
|
593
|
+
import { existsSync as existsSync5, lstatSync as lstatSync2, mkdirSync as mkdirSync4, readFileSync as readFileSync4, renameSync as renameSync2, rmSync as rmSync2, writeFileSync as writeFileSync3 } from "node:fs";
|
|
594
594
|
import { dirname as dirname4, join as join5, resolve as resolve4 } from "node:path";
|
|
595
595
|
|
|
596
596
|
// src/server/lineageProfiles.ts
|
|
597
597
|
import { createHash as createHash2, randomUUID } from "node:crypto";
|
|
598
598
|
import { createRequire } from "node:module";
|
|
599
599
|
import {
|
|
600
|
-
chmodSync,
|
|
600
|
+
chmodSync as chmodSync2,
|
|
601
|
+
closeSync,
|
|
601
602
|
constants as fsConstants,
|
|
602
603
|
copyFileSync as copyFileSync2,
|
|
603
604
|
existsSync as existsSync4,
|
|
605
|
+
fsyncSync,
|
|
604
606
|
linkSync,
|
|
607
|
+
lstatSync,
|
|
605
608
|
mkdirSync as mkdirSync3,
|
|
609
|
+
openSync,
|
|
606
610
|
readFileSync as readFileSync3,
|
|
607
611
|
realpathSync,
|
|
612
|
+
renameSync,
|
|
608
613
|
rmSync,
|
|
609
614
|
statSync as statSync3,
|
|
610
615
|
writeFileSync as writeFileSync2
|
|
@@ -617,6 +622,7 @@ var lineageProfileSchemaVersion = "lineage.profile.v1";
|
|
|
617
622
|
var lineageProfileDoctorSchemaVersion = "lineage.profile_doctor.v1";
|
|
618
623
|
var lineageProfileCloneReceiptSchemaVersion = "lineage.profile_clone_receipt.v1";
|
|
619
624
|
var lineageProfileAssetsCloneReceiptSchemaVersion = "lineage.profile_assets_clone_receipt.v1";
|
|
625
|
+
var lineageProfileRuntimeRepinReceiptSchemaVersion = "lineage.profile_runtime_repin_receipt.v1";
|
|
620
626
|
|
|
621
627
|
// src/server/lineageProfiles.ts
|
|
622
628
|
var require2 = createRequire(import.meta.url);
|
|
@@ -761,6 +767,129 @@ function lineageProfileFingerprint(profile) {
|
|
|
761
767
|
service_origin: profile.service_origin
|
|
762
768
|
})).digest("hex");
|
|
763
769
|
}
|
|
770
|
+
function sha256(value) {
|
|
771
|
+
return createHash2("sha256").update(value).digest("hex");
|
|
772
|
+
}
|
|
773
|
+
function assertOwnerOnlyPath(path, kind) {
|
|
774
|
+
const stats = lstatSync(path);
|
|
775
|
+
const expectedType = kind === "manifest" ? stats.isFile() : stats.isDirectory();
|
|
776
|
+
if (!expectedType || stats.isSymbolicLink()) throw new Error(`Profile ${kind} must be a nonsymlink ${kind === "manifest" ? "regular file" : "directory"}: ${path}`);
|
|
777
|
+
if ((stats.mode & 63) !== 0) throw new Error(`Profile ${kind} must be owner-only: ${path}`);
|
|
778
|
+
if (typeof process.getuid === "function" && stats.uid !== process.getuid()) throw new Error(`Profile ${kind} must be owned by the current user: ${path}`);
|
|
779
|
+
return stats;
|
|
780
|
+
}
|
|
781
|
+
function runtimeRepinInvariant(profile) {
|
|
782
|
+
return JSON.stringify({
|
|
783
|
+
asset_root: profile.asset_root,
|
|
784
|
+
database_path: profile.database_path,
|
|
785
|
+
environment: profile.environment,
|
|
786
|
+
profile_fingerprint: profile.profile_fingerprint,
|
|
787
|
+
profile_id: profile.profile_id,
|
|
788
|
+
required_schema_migrations: profile.required_schema_migrations || [],
|
|
789
|
+
schema_version: profile.schema_version,
|
|
790
|
+
service_origin: profile.service_origin
|
|
791
|
+
});
|
|
792
|
+
}
|
|
793
|
+
function sameFileIdentity(left, right) {
|
|
794
|
+
return left.dev === right.dev && left.ino === right.ino && left.size === right.size && left.mtimeMs === right.mtimeMs && left.ctimeMs === right.ctimeMs;
|
|
795
|
+
}
|
|
796
|
+
function repinLineageDevelopmentProfileRuntime(selector, checkoutRoot, runtime, confirmWrite) {
|
|
797
|
+
if (!confirmWrite) throw new Error("Profile runtime repin requires --confirm-write");
|
|
798
|
+
if (runtime.channel !== "dev") throw new Error(`Profile runtime repin requires dev code, not ${runtime.channel}`);
|
|
799
|
+
if (!runtime.code?.verified) throw new Error("Profile runtime repin requires a verified checkout runtime");
|
|
800
|
+
if (runtime.code.origin !== "checkout") throw new Error(`Profile runtime repin requires checkout code, not ${runtime.code.origin}`);
|
|
801
|
+
if (!runtime.code.fingerprint || !/^[a-f0-9]{64}$/i.test(runtime.code.fingerprint)) {
|
|
802
|
+
throw new Error("Profile runtime repin requires a valid executing code fingerprint");
|
|
803
|
+
}
|
|
804
|
+
if (!checkoutRoot.trim()) throw new Error("Profile runtime repin requires --checkout-root");
|
|
805
|
+
const intendedRoot = realpathSync(resolve3(checkoutRoot));
|
|
806
|
+
if (!statSync3(intendedRoot).isDirectory()) throw new Error(`Profile runtime repin checkout root is not a directory: ${intendedRoot}`);
|
|
807
|
+
const executingRoot = realpathSync(runtime.code.root);
|
|
808
|
+
if (intendedRoot !== executingRoot) {
|
|
809
|
+
throw new Error(`Profile runtime repin checkout root ${intendedRoot} does not match executing code root ${executingRoot}`);
|
|
810
|
+
}
|
|
811
|
+
const profile = resolveLineageProfile(selector);
|
|
812
|
+
if (profile.environment !== "development") throw new Error(`Profile runtime repin requires a development profile, not ${profile.environment}`);
|
|
813
|
+
if (profile.expected_runtime?.channel !== "dev") throw new Error("Development profile runtime repin requires an existing dev channel pin");
|
|
814
|
+
if (profile.expected_runtime.code_origin !== "checkout") throw new Error("Development profile runtime repin requires an existing checkout origin pin");
|
|
815
|
+
if (!profile.expected_runtime.code_fingerprint) throw new Error("Development profile runtime repin requires an existing code fingerprint pin");
|
|
816
|
+
const manifestPath = profile.manifest_path;
|
|
817
|
+
assertOwnerOnlyPath(dirname3(manifestPath), "profile directory");
|
|
818
|
+
const beforeStats = assertOwnerOnlyPath(manifestPath, "manifest");
|
|
819
|
+
const beforeText = readFileSync3(manifestPath, "utf8");
|
|
820
|
+
const beforeHash = sha256(beforeText);
|
|
821
|
+
let raw;
|
|
822
|
+
try {
|
|
823
|
+
raw = JSON.parse(beforeText);
|
|
824
|
+
} catch (error) {
|
|
825
|
+
throw new Error(`Could not parse profile manifest ${manifestPath}: ${error instanceof Error ? error.message : String(error)}`, { cause: error });
|
|
826
|
+
}
|
|
827
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) throw new Error("Profile manifest must be a JSON object");
|
|
828
|
+
const previousFingerprint = profile.expected_runtime.code_fingerprint;
|
|
829
|
+
const nextExpectedRuntime = {
|
|
830
|
+
channel: "dev",
|
|
831
|
+
code_fingerprint: runtime.code.fingerprint,
|
|
832
|
+
code_origin: "checkout",
|
|
833
|
+
...runtime.gitSha ? { git_sha: runtime.gitSha } : {},
|
|
834
|
+
version: runtime.version
|
|
835
|
+
};
|
|
836
|
+
const updated = { ...raw, expected_runtime: nextExpectedRuntime };
|
|
837
|
+
const afterText = `${JSON.stringify(updated, null, 2)}
|
|
838
|
+
`;
|
|
839
|
+
const afterHash = sha256(afterText);
|
|
840
|
+
const result = {
|
|
841
|
+
changed: beforeText !== afterText,
|
|
842
|
+
checkout_root: intendedRoot,
|
|
843
|
+
manifest_after_sha256: afterHash,
|
|
844
|
+
manifest_before_sha256: beforeHash,
|
|
845
|
+
manifest_path: manifestPath,
|
|
846
|
+
new_code_fingerprint: runtime.code.fingerprint,
|
|
847
|
+
previous_code_fingerprint: previousFingerprint,
|
|
848
|
+
profile_fingerprint: profile.profile_fingerprint,
|
|
849
|
+
profile_id: profile.profile_id,
|
|
850
|
+
schema_version: lineageProfileRuntimeRepinReceiptSchemaVersion
|
|
851
|
+
};
|
|
852
|
+
if (!result.changed) return result;
|
|
853
|
+
const temporaryPath = join4(dirname3(manifestPath), `.profile.runtime-repin-${randomUUID()}.tmp`);
|
|
854
|
+
let temporaryExists = false;
|
|
855
|
+
try {
|
|
856
|
+
writeFileSync2(temporaryPath, afterText, { encoding: "utf8", flag: "wx", mode: 384 });
|
|
857
|
+
temporaryExists = true;
|
|
858
|
+
chmodSync2(temporaryPath, 384);
|
|
859
|
+
const temporaryFd = openSync(temporaryPath, "r");
|
|
860
|
+
try {
|
|
861
|
+
fsyncSync(temporaryFd);
|
|
862
|
+
} finally {
|
|
863
|
+
closeSync(temporaryFd);
|
|
864
|
+
}
|
|
865
|
+
const preparedReplacement = resolveLineageProfile(temporaryPath);
|
|
866
|
+
if (runtimeRepinInvariant(preparedReplacement) !== runtimeRepinInvariant(profile)) {
|
|
867
|
+
throw new Error("Profile runtime repin would change immutable profile routing identity");
|
|
868
|
+
}
|
|
869
|
+
const currentStats = assertOwnerOnlyPath(manifestPath, "manifest");
|
|
870
|
+
const currentText = readFileSync3(manifestPath, "utf8");
|
|
871
|
+
if (!sameFileIdentity(beforeStats, currentStats) || sha256(currentText) !== beforeHash) {
|
|
872
|
+
throw new Error("Profile manifest changed while runtime repin was being prepared; refusing replacement");
|
|
873
|
+
}
|
|
874
|
+
renameSync(temporaryPath, manifestPath);
|
|
875
|
+
temporaryExists = false;
|
|
876
|
+
chmodSync2(manifestPath, 384);
|
|
877
|
+
const directoryFd = openSync(dirname3(manifestPath), "r");
|
|
878
|
+
try {
|
|
879
|
+
fsyncSync(directoryFd);
|
|
880
|
+
} finally {
|
|
881
|
+
closeSync(directoryFd);
|
|
882
|
+
}
|
|
883
|
+
const replacement = resolveLineageProfile(manifestPath);
|
|
884
|
+
if (runtimeRepinInvariant(replacement) !== runtimeRepinInvariant(profile)) {
|
|
885
|
+
throw new Error("Profile runtime repin changed immutable profile routing identity");
|
|
886
|
+
}
|
|
887
|
+
if (readFileSync3(manifestPath, "utf8") !== afterText) throw new Error("Profile runtime repin replacement bytes do not match the prepared manifest");
|
|
888
|
+
return result;
|
|
889
|
+
} finally {
|
|
890
|
+
if (temporaryExists) rmSync(temporaryPath, { force: true });
|
|
891
|
+
}
|
|
892
|
+
}
|
|
764
893
|
function assertProfileRuntimePin(profile, runtime) {
|
|
765
894
|
if (!profile.expected_runtime?.code_fingerprint || !profile.expected_runtime.code_origin) {
|
|
766
895
|
throw new Error(`Profile ${profile.profile_id} must pin expected_runtime.code_fingerprint and expected_runtime.code_origin before binding or writing`);
|
|
@@ -1051,6 +1180,7 @@ async function cloneLineageProfileDatabase(sourceDatabasePath, targetSelector, r
|
|
|
1051
1180
|
const source = new DatabaseSync(sourcePath, { readOnly: true });
|
|
1052
1181
|
try {
|
|
1053
1182
|
const pagesCopied = await backup(source, temporaryPath);
|
|
1183
|
+
chmodSync2(temporaryPath, 384);
|
|
1054
1184
|
const cloned = new DatabaseSync(temporaryPath);
|
|
1055
1185
|
let identity;
|
|
1056
1186
|
try {
|
|
@@ -1188,7 +1318,7 @@ function cloneLineageProfileAssets(sourceAssetRoot, targetSelector, runtime, con
|
|
|
1188
1318
|
const destinationPath = join4(targetRoot, relativePath);
|
|
1189
1319
|
mkdirSync3(dirname3(destinationPath), { recursive: true, mode: 448 });
|
|
1190
1320
|
copyFileSync2(sourcePath, destinationPath, fsConstants.COPYFILE_EXCL);
|
|
1191
|
-
|
|
1321
|
+
chmodSync2(destinationPath, 384);
|
|
1192
1322
|
const sourceHash = fileSha2562(sourcePath);
|
|
1193
1323
|
const destinationHash = fileSha2562(destinationPath);
|
|
1194
1324
|
if (sourceHash !== destinationHash) throw new Error("A source asset changed or failed checksum verification during clone");
|
|
@@ -1198,7 +1328,7 @@ function cloneLineageProfileAssets(sourceAssetRoot, targetSelector, runtime, con
|
|
|
1198
1328
|
treeHash.update(destinationHash);
|
|
1199
1329
|
treeHash.update("\0");
|
|
1200
1330
|
}
|
|
1201
|
-
|
|
1331
|
+
chmodSync2(targetRoot, 448);
|
|
1202
1332
|
const receiptPath = join4(targetRoot, ".lineage-profile-assets.json");
|
|
1203
1333
|
const result = {
|
|
1204
1334
|
asset_root: targetRoot,
|
|
@@ -1251,7 +1381,7 @@ function errorCode(error) {
|
|
|
1251
1381
|
return error && typeof error === "object" && "code" in error ? String(error.code) : void 0;
|
|
1252
1382
|
}
|
|
1253
1383
|
function readOwner(lockPath) {
|
|
1254
|
-
const stat =
|
|
1384
|
+
const stat = lstatSync2(lockPath);
|
|
1255
1385
|
if (!stat.isDirectory() || stat.isSymbolicLink()) {
|
|
1256
1386
|
throw new Error(`Writer lease path is not a safe directory: ${lockPath}; manual inspection is required`);
|
|
1257
1387
|
}
|
|
@@ -1300,7 +1430,7 @@ function reclaimDeadOwner(lockPath, owner) {
|
|
|
1300
1430
|
const tokenFence = createHash3("sha256").update(owner.token).digest("hex").slice(0, 24);
|
|
1301
1431
|
const tombstone = `${lockPath}.stale-${owner.pid}-${tokenFence}`;
|
|
1302
1432
|
try {
|
|
1303
|
-
|
|
1433
|
+
renameSync2(lockPath, tombstone);
|
|
1304
1434
|
} catch (error) {
|
|
1305
1435
|
if (errorCode(error) === "ENOENT") return;
|
|
1306
1436
|
throw error;
|
|
@@ -4783,7 +4913,7 @@ function getLineageSelectionPacket(project, options = {}) {
|
|
|
4783
4913
|
import { createHash as createHash6 } from "node:crypto";
|
|
4784
4914
|
import { spawnSync as spawnSync2 } from "node:child_process";
|
|
4785
4915
|
import { createRequire as createRequire4 } from "node:module";
|
|
4786
|
-
import { existsSync as existsSync10, lstatSync as
|
|
4916
|
+
import { existsSync as existsSync10, lstatSync as lstatSync3, readFileSync as readFileSync5, readdirSync as readdirSync3, readlinkSync, realpathSync as realpathSync3, statSync as statSync6 } from "node:fs";
|
|
4787
4917
|
import { dirname as dirname5, isAbsolute as isAbsolute3, join as join9, resolve as resolve7 } from "node:path";
|
|
4788
4918
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
4789
4919
|
|
|
@@ -4810,7 +4940,7 @@ function executingCodeRoot() {
|
|
|
4810
4940
|
return canonicalRoot(root);
|
|
4811
4941
|
}
|
|
4812
4942
|
var codeRoot = executingCodeRoot();
|
|
4813
|
-
function
|
|
4943
|
+
function sha2562(value) {
|
|
4814
4944
|
return createHash6("sha256").update(value).digest("hex");
|
|
4815
4945
|
}
|
|
4816
4946
|
function canonicalRoot(root) {
|
|
@@ -4849,7 +4979,7 @@ function untrackedFingerprint(root, status) {
|
|
|
4849
4979
|
hash.update(relativePath);
|
|
4850
4980
|
const path = join9(root, relativePath);
|
|
4851
4981
|
try {
|
|
4852
|
-
const stat =
|
|
4982
|
+
const stat = lstatSync3(path);
|
|
4853
4983
|
if (stat.isSymbolicLink()) hash.update(readlinkSync(path));
|
|
4854
4984
|
else if (stat.isFile()) hash.update(readFileSync5(path));
|
|
4855
4985
|
else hash.update(`[${stat.mode}:${stat.size}]`);
|
|
@@ -4871,12 +5001,12 @@ function checkoutCodeIdentity(root, channel, version) {
|
|
|
4871
5001
|
if (status === void 0 || diff === void 0) errors.push("Checkout dirty state could not be inspected");
|
|
4872
5002
|
if (channel !== "dev") errors.push(`Checkout code may run only as dev, not ${channel}`);
|
|
4873
5003
|
const dirty = Boolean(status);
|
|
4874
|
-
const sourceFingerprint =
|
|
5004
|
+
const sourceFingerprint = sha2562(`${gitSha || "unknown"}\0${sha2562(diff || "")}\0${untrackedFingerprint(root, status || "")}`);
|
|
4875
5005
|
return {
|
|
4876
5006
|
channel,
|
|
4877
5007
|
dirty,
|
|
4878
5008
|
errors,
|
|
4879
|
-
fingerprint:
|
|
5009
|
+
fingerprint: sha2562(JSON.stringify({ channel, dirty, git_sha: gitSha, origin: "checkout", root, source_fingerprint: sourceFingerprint, version })),
|
|
4880
5010
|
git_sha: gitSha,
|
|
4881
5011
|
origin: "checkout",
|
|
4882
5012
|
package_version: version,
|
|
@@ -4886,7 +5016,7 @@ function checkoutCodeIdentity(root, channel, version) {
|
|
|
4886
5016
|
};
|
|
4887
5017
|
}
|
|
4888
5018
|
function buildFingerprint(build) {
|
|
4889
|
-
return
|
|
5019
|
+
return sha2562(JSON.stringify({
|
|
4890
5020
|
package_name: build.package_name,
|
|
4891
5021
|
package_version: build.package_version,
|
|
4892
5022
|
schema_version: build.schema_version,
|
|
@@ -4986,7 +5116,7 @@ function packageCodeIdentity(root, channel, info, receiptPath) {
|
|
|
4986
5116
|
channel,
|
|
4987
5117
|
dirty: build?.source_dirty,
|
|
4988
5118
|
errors,
|
|
4989
|
-
fingerprint:
|
|
5119
|
+
fingerprint: sha2562(JSON.stringify({
|
|
4990
5120
|
build_fingerprint: build?.build_fingerprint,
|
|
4991
5121
|
channel,
|
|
4992
5122
|
install_integrity: install?.package_integrity,
|
|
@@ -5014,7 +5144,7 @@ function getLineageCodeIdentity(channel, options = {}) {
|
|
|
5014
5144
|
return {
|
|
5015
5145
|
channel,
|
|
5016
5146
|
errors,
|
|
5017
|
-
fingerprint:
|
|
5147
|
+
fingerprint: sha2562(JSON.stringify({ channel, origin: "unknown", root, version: info.version })),
|
|
5018
5148
|
origin: "unknown",
|
|
5019
5149
|
package_version: info.version,
|
|
5020
5150
|
root,
|
|
@@ -5309,6 +5439,7 @@ Usage:
|
|
|
5309
5439
|
${config.binName} profile bind --profile <id-or-manifest> --confirm-write [--json]
|
|
5310
5440
|
${config.binName} profile clone --source-db <snapshot-source> --target-profile <id-or-manifest> --confirm-write [--json]
|
|
5311
5441
|
${config.binName} profile clone-assets --source-asset-root <path> --target-profile <id-or-manifest> --confirm-write [--json]
|
|
5442
|
+
${config.binName} profile repin-runtime --profile <development-profile> --checkout-root <path> --confirm-write [--json]
|
|
5312
5443
|
${config.binName} runtime info [--json]
|
|
5313
5444
|
${config.binName} runtime doctor [--json]
|
|
5314
5445
|
${config.binName} next [--project <project>] [--root <asset-id>] [--db <path>] [--json]
|
|
@@ -5715,6 +5846,18 @@ function runLineageProfileCommand(config, command, args) {
|
|
|
5715
5846
|
const selector = profileSelector(args);
|
|
5716
5847
|
if (!selector) throw new Error(`lineage profile ${command} requires --profile or LINEAGE_PROFILE`);
|
|
5717
5848
|
if (command === "doctor") return doctorLineageProfile(selector, runtimeIdentity);
|
|
5849
|
+
if (command === "repin-runtime") {
|
|
5850
|
+
const checkoutRoot = readOption(args, "--checkout-root");
|
|
5851
|
+
if (!checkoutRoot) throw new Error("Profile runtime repin requires --checkout-root");
|
|
5852
|
+
if (!args.includes("--confirm-write")) throw new Error("Profile runtime repin requires --confirm-write");
|
|
5853
|
+
const profile = resolveLineageProfile(selector);
|
|
5854
|
+
const writerLease = acquireProfileWriterLease(profile, config.channel, "cli");
|
|
5855
|
+
try {
|
|
5856
|
+
return repinLineageDevelopmentProfileRuntime(selector, checkoutRoot, runtimeIdentity, true);
|
|
5857
|
+
} finally {
|
|
5858
|
+
writerLease.release();
|
|
5859
|
+
}
|
|
5860
|
+
}
|
|
5718
5861
|
if (command === "bind") {
|
|
5719
5862
|
if (!args.includes("--confirm-write")) throw new Error("Profile bind requires --confirm-write");
|
|
5720
5863
|
const profile = resolveLineageProfile(selector);
|
|
@@ -5740,6 +5883,9 @@ function printProfileResult(result, json) {
|
|
|
5740
5883
|
} else if (result.schema_version === "lineage.profile_clone_receipt.v1") {
|
|
5741
5884
|
console.log(`Cloned ${result.source_database_path} to ${result.database_path} for ${result.target_identity.profile_id}`);
|
|
5742
5885
|
console.log(`Receipt: ${result.receipt_path}`);
|
|
5886
|
+
} else if (result.schema_version === "lineage.profile_runtime_repin_receipt.v1") {
|
|
5887
|
+
console.log(`${result.changed ? "Repinned" : "Already pinned"} ${result.profile_id} to checkout ${result.checkout_root}`);
|
|
5888
|
+
console.log(`Code fingerprint: ${result.new_code_fingerprint}`);
|
|
5743
5889
|
} else {
|
|
5744
5890
|
console.log(`Cloned ${result.files_copied} referenced asset file(s) into ${result.asset_root}`);
|
|
5745
5891
|
console.log(`Receipt: ${result.receipt_path}`);
|