@rightkit/release 0.2.107 → 0.2.109

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.
@@ -0,0 +1,181 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import { createHash } from "node:crypto";
4
+
5
+ const CHUNK_BYTES = 1024 * 1024;
6
+
7
+ /** Install an unsigned development payload through an owned, versioned `current` pointer. */
8
+ export function installPortableDevelopment(descriptor, { root, platform = process.platform, env = process.env } = {}) {
9
+ const emit = (event, fields = {}) => process.stdout.write(`${JSON.stringify({ event: `DEV_INSTALL_${event}`, ...fields })}\n`);
10
+ let backup = null;
11
+ let switched = false;
12
+ let versionRoot;
13
+ let digest;
14
+ let current;
15
+ try {
16
+ validateDescriptor(descriptor, root, platform);
17
+ const installRoot = path.resolve(descriptor.installRoot);
18
+ if (!path.isAbsolute(descriptor.installRoot) || isFilesystemRoot(installRoot, platform)) {
19
+ throw new Error("development installRoot must be an absolute non-filesystem-root path");
20
+ }
21
+ const source = resolveContained(root, descriptor.sourceDirectory, "sourceDirectory");
22
+ const sourceReal = fs.realpathSync(source);
23
+ if (!isWithin(fs.realpathSync(root), sourceReal, platform)) throw new Error("development sourceDirectory resolves outside repository root");
24
+ const sourceInfo = fs.lstatSync(source);
25
+ if (!sourceInfo.isDirectory() || sourceInfo.isSymbolicLink()) throw new Error("development sourceDirectory must be a real directory");
26
+ const sourceEntrypoint = resolveContained(source, descriptor.entrypoint, "entrypoint");
27
+ const sourceEntrypointInfo = fs.lstatSync(sourceEntrypoint);
28
+ if (!sourceEntrypointInfo.isFile() || sourceEntrypointInfo.isSymbolicLink()) throw new Error("development entrypoint must be a regular file inside sourceDirectory");
29
+ const sourceDigest = digestTree(source);
30
+ digest = sourceDigest;
31
+ versionRoot = path.join(installRoot, "versions", `dev-${digest}`);
32
+ current = path.join(installRoot, "current");
33
+ emit("STAGE", { sourceDirectory: source, versionRoot, digest });
34
+
35
+ fs.mkdirSync(path.dirname(versionRoot), { recursive: true });
36
+ if (exists(versionRoot)) {
37
+ if (digestTree(versionRoot) !== digest) throw new Error(`development version digest collision: ${versionRoot}`);
38
+ } else {
39
+ const staging = path.join(path.dirname(versionRoot), `.stage-${process.pid}-${randomSuffix()}`);
40
+ try {
41
+ copyTree(source, staging);
42
+ if (digestTree(staging) !== digest) throw new Error("staged development payload did not match source digest");
43
+ fs.renameSync(staging, versionRoot);
44
+ } catch (error) {
45
+ removeIfExists(staging);
46
+ throw error;
47
+ }
48
+ }
49
+
50
+ validateCurrent(current, installRoot, platform);
51
+ if (exists(current)) {
52
+ backup = path.join(installRoot, `.current-backup-${process.pid}-${randomSuffix()}`);
53
+ fs.renameSync(current, backup);
54
+ }
55
+ try {
56
+ fs.symlinkSync(versionRoot, current, platform === "win32" ? "junction" : "dir");
57
+ switched = true;
58
+ emit("ACTIVATE", { current, versionRoot });
59
+ const activeRoot = fs.realpathSync(current);
60
+ if (!samePath(activeRoot, fs.realpathSync(versionRoot), platform)) throw new Error("development current pointer did not resolve to staged version");
61
+ const entrypoint = resolveContained(versionRoot, descriptor.entrypoint, "entrypoint");
62
+ const entryDigest = hashFile(entrypoint);
63
+ if (entryDigest !== hashFile(path.join(source, descriptor.entrypoint))) throw new Error("development entrypoint readback did not match source");
64
+ if (backup) removeIfExists(backup);
65
+ emit("SUCCEEDED", { installRoot, current, versionRoot, digest, entrypoint: descriptor.entrypoint, entrypointDigest: entryDigest });
66
+ return { installRoot, current, versionRoot, digest, entrypoint: path.join(current, descriptor.entrypoint), entrypointDigest: entryDigest };
67
+ } catch (error) {
68
+ if (switched) removeIfExists(current);
69
+ if (backup && exists(backup)) fs.renameSync(backup, current);
70
+ throw error;
71
+ }
72
+ } catch (error) {
73
+ emit("FAILED", { installRoot: descriptor?.installRoot, versionRoot, digest, message: error?.message ?? String(error) });
74
+ throw error;
75
+ }
76
+ }
77
+
78
+ function validateDescriptor(descriptor, root, platform) {
79
+ if (!descriptor || descriptor.kind !== "portable" || descriptor.activation?.kind !== "current") throw new Error("unsupported portable development descriptor");
80
+ if (typeof root !== "string" || !path.isAbsolute(root)) throw new Error("repository root must be absolute");
81
+ if (typeof descriptor.installRoot !== "string" || !path.isAbsolute(descriptor.installRoot)) throw new Error("development installRoot must be absolute");
82
+ resolveContained(root, descriptor.sourceDirectory, "sourceDirectory");
83
+ resolveContained(root, descriptor.entrypoint, "entrypoint");
84
+ if (!isRelativePath(descriptor.sourceDirectory) || !isRelativePath(descriptor.entrypoint)) throw new Error("development sourceDirectory and entrypoint must be relative paths");
85
+ if (platform !== "win32" && platform !== "darwin" && platform !== "linux") throw new Error(`unsupported development install platform: ${platform}`);
86
+ }
87
+
88
+ function isRelativePath(value) {
89
+ return typeof value === "string" && value.length > 0 && !path.isAbsolute(value) && !path.win32.isAbsolute(value) && !value.split(/[\\/]/).includes("..");
90
+ }
91
+
92
+ function resolveContained(base, relative, label) {
93
+ if (!isRelativePath(relative)) throw new Error(`${label} must be a contained relative path`);
94
+ const resolved = path.resolve(base, relative);
95
+ if (!isWithin(path.resolve(base), resolved, process.platform)) throw new Error(`${label} escapes its parent directory`);
96
+ return resolved;
97
+ }
98
+
99
+ function isFilesystemRoot(value, platform) {
100
+ const parsed = path.parse(value);
101
+ return samePath(parsed.root, value, platform);
102
+ }
103
+
104
+ function isWithin(parent, child, platform) {
105
+ const relative = path.relative(parent, child);
106
+ return relative === "" || (!relative.startsWith(`..${path.sep}`) && relative !== ".." && !path.isAbsolute(relative));
107
+ }
108
+
109
+ function samePath(left, right, platform) {
110
+ const a = path.normalize(left);
111
+ const b = path.normalize(right);
112
+ return platform === "win32" ? a.toLowerCase() === b.toLowerCase() : a === b;
113
+ }
114
+
115
+ function exists(file) {
116
+ try { fs.lstatSync(file); return true; } catch (error) { if (error?.code === "ENOENT") return false; throw error; }
117
+ }
118
+
119
+ function digestTree(directory) {
120
+ const hash = createHash("sha256");
121
+ const visit = (dir, relative = "") => {
122
+ const entries = fs.readdirSync(dir, { withFileTypes: true }).sort((a, b) => a.name < b.name ? -1 : a.name > b.name ? 1 : 0);
123
+ for (const entry of entries) {
124
+ const rel = relative ? `${relative}/${entry.name}` : entry.name;
125
+ const absolute = path.join(dir, entry.name);
126
+ const stat = fs.lstatSync(absolute);
127
+ if (stat.isSymbolicLink()) throw new Error(`development payload contains a symbolic link: ${rel}`);
128
+ if (stat.isDirectory()) { hash.update(`D\0${rel}\0`); visit(absolute, rel); }
129
+ else if (stat.isFile()) hash.update(`F\0${rel}\0${stat.size}\0${hashFile(absolute)}\0`);
130
+ else throw new Error(`development payload contains a non-regular entry: ${rel}`);
131
+ }
132
+ };
133
+ visit(directory);
134
+ return hash.digest("hex");
135
+ }
136
+
137
+ function hashFile(file) {
138
+ const fd = fs.openSync(file, "r");
139
+ const hash = createHash("sha256");
140
+ const buffer = Buffer.allocUnsafe(CHUNK_BYTES);
141
+ try {
142
+ let bytes;
143
+ while ((bytes = fs.readSync(fd, buffer, 0, buffer.length, null)) > 0) hash.update(buffer.subarray(0, bytes));
144
+ } finally { fs.closeSync(fd); }
145
+ return hash.digest("hex");
146
+ }
147
+
148
+ function copyTree(source, target) {
149
+ fs.mkdirSync(target, { recursive: true });
150
+ for (const entry of fs.readdirSync(source, { withFileTypes: true })) {
151
+ const from = path.join(source, entry.name);
152
+ const to = path.join(target, entry.name);
153
+ const stat = fs.lstatSync(from);
154
+ if (stat.isSymbolicLink()) throw new Error(`development payload contains a symbolic link: ${from}`);
155
+ if (stat.isDirectory()) copyTree(from, to);
156
+ else if (stat.isFile()) fs.copyFileSync(from, to, fs.constants.COPYFILE_EXCL);
157
+ else throw new Error(`development payload contains a non-regular entry: ${from}`);
158
+ }
159
+ }
160
+
161
+ function validateCurrent(current, installRoot, platform) {
162
+ if (!exists(current)) return;
163
+ const stat = fs.lstatSync(current);
164
+ if (!stat.isSymbolicLink()) throw new Error(`development current pointer is not a symlink or junction; refusing to replace: ${current}`);
165
+ let target;
166
+ try { target = fs.realpathSync(current); } catch (error) { throw new Error(`development current pointer is dangling or unreadable: ${current}`, { cause: error }); }
167
+ const versionsRoot = path.join(installRoot, "versions");
168
+ const versionName = path.basename(target);
169
+ if (!isWithin(fs.realpathSync(installRoot), target, platform)
170
+ || !samePath(path.dirname(target), fs.realpathSync(versionsRoot), platform)
171
+ || !versionName || versionName === "." || versionName === "..") {
172
+ throw new Error(`development current pointer does not resolve to an owned version: ${current}`);
173
+ }
174
+ }
175
+
176
+ function removeIfExists(target) {
177
+ if (!exists(target)) return;
178
+ fs.rmSync(target, { recursive: true, force: true });
179
+ }
180
+
181
+ function randomSuffix() { return `${Date.now()}-${Math.random().toString(16).slice(2)}`; }
package/preflight.mjs CHANGED
@@ -174,6 +174,8 @@ export function collectPreflight({
174
174
  targetLink,
175
175
  minFreeGb = 25,
176
176
  env = process.env,
177
+ versionProbe = commandVersion,
178
+ isBrokerManagedHost = brokerManagedHost,
177
179
  } = {}) {
178
180
  const checks = [];
179
181
  const windows = platform === "win" || platform === "win32";
@@ -257,12 +259,16 @@ export function collectPreflight({
257
259
  }
258
260
  }
259
261
 
260
- const sccache = commandVersion("sccache");
261
- checks.push(
262
- sccache
263
- ? ok("sccache", sccache)
264
- : warn("sccache", "sccache not found; the shared cache will compile without a compiler cache", "cargo install sccache --locked"),
265
- );
262
+ if (isBrokerManagedHost(env)) {
263
+ checks.push(ok("compiler-cache", "broker-managed; compiler-cache selection and status are recorded in build receipts"));
264
+ } else {
265
+ const sccache = versionProbe("sccache");
266
+ checks.push(
267
+ sccache
268
+ ? ok("sccache", `${sccache} visible on shell PATH`)
269
+ : warn("sccache", "sccache was not found by this shell PATH probe; cache availability is not determined here", null),
270
+ );
271
+ }
266
272
 
267
273
  if (appRoot) {
268
274
  const free = freeDiskGb(appRoot);
@@ -1,142 +1,144 @@
1
- {
2
- "schema": 2,
3
- "packageManager": "pnpm@11.24.0",
4
- "npm": {
5
- "@rightkit/git": "0.2.0",
6
- "@rightkit/legal": "0.2.0",
7
- "@rightkit/license": "0.1.5",
8
- "@rightkit/logs": "0.1.3",
9
- "@rightkit/platform-ui": "0.1.0",
10
- "@rightkit/qa": "0.1.0",
11
- "@rightkit/release": "0.2.47",
12
- "@rightkit/tauri": "0.1.0",
13
- "@rightkit/updates": "0.2.3"
14
- },
15
- "stagedNpm": {
16
- "@rightkit/ax": "0.2.4",
17
- "@rightkit/git": "0.2.25",
18
- "@rightkit/hooks": "0.1.1",
19
- "@rightkit/legal": "0.3.1",
20
- "@rightkit/legal-ui": "0.1.1",
21
- "@rightkit/license": "0.1.7",
22
- "@rightkit/logs": "0.1.4",
23
- "@rightkit/platform-ui": "0.1.1",
24
- "@rightkit/qa": "0.2.1",
25
- "@rightkit/release": "0.2.107",
26
- "@rightkit/tauri": "0.1.1",
27
- "@rightkit/updates": "0.2.4"
28
- },
29
- "legacyNpm": {
30
- "@rightkit/ax": [
31
- "0.2.0",
32
- "0.2.1",
33
- "0.2.2",
34
- "0.2.3"
35
- ],
36
- "@rightkit/git": [
37
- "0.2.1",
38
- "0.2.2",
39
- "0.2.3",
40
- "0.2.4",
41
- "0.2.5",
42
- "0.2.6",
43
- "0.2.7",
44
- "0.2.8",
45
- "0.2.9",
46
- "0.2.10",
47
- "0.2.11",
48
- "0.2.12",
49
- "0.2.13",
50
- "0.2.14",
51
- "0.2.15",
52
- "0.2.16",
53
- "0.2.18",
54
- "0.2.19",
55
- "0.2.20",
56
- "0.2.21",
57
- "0.2.22",
58
- "0.2.23",
59
- "0.2.24"
60
- ],
61
- "@rightkit/legal-ui": [
62
- "0.1.0"
63
- ],
64
- "@rightkit/hooks": [
65
- "0.1.0"
66
- ],
67
- "@rightkit/legal": [
68
- "0.3.0"
69
- ],
70
- "@rightkit/license": [
71
- "0.1.6"
72
- ],
73
- "@rightkit/release": [
74
- "0.2.22",
75
- "0.2.29",
76
- "0.2.30",
77
- "0.2.31",
78
- "0.2.41",
79
- "0.2.42",
80
- "0.2.43",
81
- "0.2.44",
82
- "0.2.45",
83
- "0.2.46",
84
- "0.2.49",
85
- "0.2.50",
86
- "0.2.51",
87
- "0.2.53",
88
- "0.2.54",
89
- "0.2.55",
90
- "0.2.56",
91
- "0.2.61",
92
- "0.2.62",
93
- "0.2.63",
94
- "0.2.64",
95
- "0.2.65",
96
- "0.2.66",
97
- "0.2.67",
98
- "0.2.68",
99
- "0.2.69",
100
- "0.2.70",
101
- "0.2.71",
102
- "0.2.72",
103
- "0.2.73",
104
- "0.2.74",
105
- "0.2.75",
106
- "0.2.76",
107
- "0.2.82",
108
- "0.2.83",
109
- "0.2.91",
110
- "0.2.92",
111
- "0.2.94",
112
- "0.2.95",
113
- "0.2.96",
114
- "0.2.97",
115
- "0.2.98",
116
- "0.2.99",
117
- "0.2.104",
118
- "0.2.105",
119
- "0.2.106"
120
- ],
121
- "@rightkit/qa": [
122
- "0.1.0",
123
- "0.2.0"
124
- ]
125
- },
126
- "cargo": {
127
- "rightkit-license": "0.1.2",
128
- "rightkit-logs": "0.1.0",
129
- "rightkit-process": "0.1.0",
130
- "rightkit-tauri": "0.1.0"
131
- },
132
- "stagedCargo": {
133
- "rightkit-license": "0.1.3",
134
- "rightkit-tauri": "0.1.1"
135
- },
136
- "swift": {
137
- "rightkit-swift": {
138
- "url": "https://github.com/bogusyogi/rightkit-swift.git",
139
- "version": "0.1.1"
140
- }
141
- }
142
- }
1
+ {
2
+ "schema": 2,
3
+ "packageManager": "pnpm@11.24.0",
4
+ "npm": {
5
+ "@rightkit/git": "0.2.0",
6
+ "@rightkit/legal": "0.2.0",
7
+ "@rightkit/license": "0.1.5",
8
+ "@rightkit/logs": "0.1.3",
9
+ "@rightkit/platform-ui": "0.1.0",
10
+ "@rightkit/qa": "0.1.0",
11
+ "@rightkit/release": "0.2.47",
12
+ "@rightkit/tauri": "0.1.0",
13
+ "@rightkit/updates": "0.2.3"
14
+ },
15
+ "stagedNpm": {
16
+ "@rightkit/ax": "0.2.4",
17
+ "@rightkit/git": "0.2.25",
18
+ "@rightkit/hooks": "0.1.1",
19
+ "@rightkit/legal": "0.3.1",
20
+ "@rightkit/legal-ui": "0.1.1",
21
+ "@rightkit/license": "0.1.7",
22
+ "@rightkit/logs": "0.1.4",
23
+ "@rightkit/platform-ui": "0.1.1",
24
+ "@rightkit/qa": "0.2.1",
25
+ "@rightkit/release": "0.2.109",
26
+ "@rightkit/tauri": "0.1.1",
27
+ "@rightkit/updates": "0.2.4"
28
+ },
29
+ "legacyNpm": {
30
+ "@rightkit/ax": [
31
+ "0.2.0",
32
+ "0.2.1",
33
+ "0.2.2",
34
+ "0.2.3"
35
+ ],
36
+ "@rightkit/git": [
37
+ "0.2.1",
38
+ "0.2.2",
39
+ "0.2.3",
40
+ "0.2.4",
41
+ "0.2.5",
42
+ "0.2.6",
43
+ "0.2.7",
44
+ "0.2.8",
45
+ "0.2.9",
46
+ "0.2.10",
47
+ "0.2.11",
48
+ "0.2.12",
49
+ "0.2.13",
50
+ "0.2.14",
51
+ "0.2.15",
52
+ "0.2.16",
53
+ "0.2.18",
54
+ "0.2.19",
55
+ "0.2.20",
56
+ "0.2.21",
57
+ "0.2.22",
58
+ "0.2.23",
59
+ "0.2.24"
60
+ ],
61
+ "@rightkit/legal-ui": [
62
+ "0.1.0"
63
+ ],
64
+ "@rightkit/hooks": [
65
+ "0.1.0"
66
+ ],
67
+ "@rightkit/legal": [
68
+ "0.3.0"
69
+ ],
70
+ "@rightkit/license": [
71
+ "0.1.6"
72
+ ],
73
+ "@rightkit/release": [
74
+ "0.2.22",
75
+ "0.2.29",
76
+ "0.2.30",
77
+ "0.2.31",
78
+ "0.2.41",
79
+ "0.2.42",
80
+ "0.2.43",
81
+ "0.2.44",
82
+ "0.2.45",
83
+ "0.2.46",
84
+ "0.2.49",
85
+ "0.2.50",
86
+ "0.2.51",
87
+ "0.2.53",
88
+ "0.2.54",
89
+ "0.2.55",
90
+ "0.2.56",
91
+ "0.2.61",
92
+ "0.2.62",
93
+ "0.2.63",
94
+ "0.2.64",
95
+ "0.2.65",
96
+ "0.2.66",
97
+ "0.2.67",
98
+ "0.2.68",
99
+ "0.2.69",
100
+ "0.2.70",
101
+ "0.2.71",
102
+ "0.2.72",
103
+ "0.2.73",
104
+ "0.2.74",
105
+ "0.2.75",
106
+ "0.2.76",
107
+ "0.2.82",
108
+ "0.2.83",
109
+ "0.2.91",
110
+ "0.2.92",
111
+ "0.2.94",
112
+ "0.2.95",
113
+ "0.2.96",
114
+ "0.2.97",
115
+ "0.2.98",
116
+ "0.2.99",
117
+ "0.2.104",
118
+ "0.2.105",
119
+ "0.2.106",
120
+ "0.2.107",
121
+ "0.2.108"
122
+ ],
123
+ "@rightkit/qa": [
124
+ "0.1.0",
125
+ "0.2.0"
126
+ ]
127
+ },
128
+ "cargo": {
129
+ "rightkit-license": "0.1.2",
130
+ "rightkit-logs": "0.1.0",
131
+ "rightkit-process": "0.1.0",
132
+ "rightkit-tauri": "0.1.0"
133
+ },
134
+ "stagedCargo": {
135
+ "rightkit-license": "0.1.3",
136
+ "rightkit-tauri": "0.1.1"
137
+ },
138
+ "swift": {
139
+ "rightkit-swift": {
140
+ "url": "https://github.com/bogusyogi/rightkit-swift.git",
141
+ "version": "0.1.1"
142
+ }
143
+ }
144
+ }