@rightkit/release 0.2.28 → 0.2.30
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/create-mac-updater.mjs +9 -1
- package/create-mac-updater.test.mjs +2 -1
- package/legal-contract.mjs +13 -18
- package/legal-contract.test.mjs +14 -3
- package/package.json +1 -1
- package/publish-update.test.mjs +1 -1
- package/release-token.mjs +5 -1
- package/release.mjs +12 -4
- package/right-suite-contract.test.mjs +545 -530
- package/rightkit-versions.json +39 -36
- package/pub-upload.mjs +0 -100
package/create-mac-updater.mjs
CHANGED
|
@@ -38,7 +38,15 @@ env.TAURI_SIGNING_PRIVATE_KEY_PASSWORD ??= "";
|
|
|
38
38
|
|
|
39
39
|
rmSync(outputPath, { force: true });
|
|
40
40
|
rmSync(`${outputPath}.sig`, { force: true });
|
|
41
|
-
|
|
41
|
+
// Tauri 2.10 strips the first path component while extracting. Omitting the explicit
|
|
42
|
+
// App.app/ directory entry prevents it from unpacking that entry onto its existing temp dir.
|
|
43
|
+
run("tar", [
|
|
44
|
+
"-czf",
|
|
45
|
+
outputPath,
|
|
46
|
+
"-C",
|
|
47
|
+
path.dirname(appPath),
|
|
48
|
+
path.join(path.basename(appPath), "Contents"),
|
|
49
|
+
]);
|
|
42
50
|
run("pnpm", ["exec", "tauri", "signer", "sign", outputPath]);
|
|
43
51
|
|
|
44
52
|
function run(command, commandArgs) {
|
|
@@ -8,6 +8,7 @@ const helper = fileURLToPath(new URL("./create-mac-updater.mjs", import.meta.url
|
|
|
8
8
|
test("documents the final signed-app to signed-updater sequence in dry-run mode", () => {
|
|
9
9
|
const result = spawnSync(process.execPath, [helper, "--app", "bundle/Test.app", "--output", "bundle/Test.app.tar.gz", "--dry-run"], { encoding: "utf8" });
|
|
10
10
|
assert.equal(result.status, 0, result.stderr);
|
|
11
|
-
assert.match(result.stdout, /tar .*Test\.app\.tar\.gz.*Test\.app/);
|
|
11
|
+
assert.match(result.stdout, /tar .*Test\.app\.tar\.gz.*Test\.app\/Contents/);
|
|
12
|
+
assert.doesNotMatch(result.stdout, / Test\.app$/m);
|
|
12
13
|
assert.match(result.stdout, /tauri signer sign .*Test\.app\.tar\.gz/);
|
|
13
14
|
});
|
package/legal-contract.mjs
CHANGED
|
@@ -135,24 +135,19 @@ export function assertLegalReleaseContract(root, legal, appName, platform) {
|
|
|
135
135
|
appendices.push({ ...appendix, absolutePath: realFile });
|
|
136
136
|
}
|
|
137
137
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
appName,
|
|
152
|
-
"Windows Tauri bundle.licenseFile must be absent: the in-app legal gate is the only assent surface, " +
|
|
153
|
-
"and an installer license page duplicates it without recording acceptance",
|
|
154
|
-
);
|
|
155
|
-
}
|
|
138
|
+
// The in-app gate is the single assent surface. An installer or DMG can only
|
|
139
|
+
// present text; it cannot record who agreed or the individual/enterprise basis.
|
|
140
|
+
if (typeof legal.tauriConfig !== "string") fail(appName, "the legal gate requires legal.tauriConfig");
|
|
141
|
+
const tauriPath = path.resolve(appRoot, legal.tauriConfig);
|
|
142
|
+
if (!within(appRoot, tauriPath) || !existsSync(tauriPath)) fail(appName, `missing Tauri config: ${legal.tauriConfig}`);
|
|
143
|
+
const tauri = readJson(tauriPath, appName, "Tauri config");
|
|
144
|
+
const configured = tauri.bundle?.licenseFile;
|
|
145
|
+
if (typeof configured === "string" && configured.trim()) {
|
|
146
|
+
fail(
|
|
147
|
+
appName,
|
|
148
|
+
"Tauri bundle.licenseFile must be absent: the in-app legal gate is the only assent surface, and an " +
|
|
149
|
+
"installer license page (NSIS) or DMG SLA duplicates it without recording acceptance",
|
|
150
|
+
);
|
|
156
151
|
}
|
|
157
152
|
|
|
158
153
|
return {
|
package/legal-contract.test.mjs
CHANGED
|
@@ -46,8 +46,6 @@ function fixture() {
|
|
|
46
46
|
})),
|
|
47
47
|
};
|
|
48
48
|
writeFileSync(path.join(legalDir, "legal-manifest.json"), `${JSON.stringify(manifest, null, 2)}\n`);
|
|
49
|
-
// No bundle.licenseFile: the in-app gate is the single assent surface, so a
|
|
50
|
-
// compliant app ships an installer with no license page (Adrian, 2026-07-17).
|
|
51
49
|
writeFileSync(
|
|
52
50
|
path.join(tauriDir, "tauri.conf.json"),
|
|
53
51
|
`${JSON.stringify({ bundle: { publisher: "Damned Ventures LLC" } }, null, 2)}\n`,
|
|
@@ -100,12 +98,25 @@ test("rejects an explicitly unresolved release blocker even without a checkbox",
|
|
|
100
98
|
assert.throws(() => assertLegalReleaseContract(root, legal, "fixture", "win"), /release blocker/i);
|
|
101
99
|
});
|
|
102
100
|
|
|
103
|
-
test("
|
|
101
|
+
test("rejects a Windows installer license page: the in-app gate is the only assent surface", () => {
|
|
104
102
|
const { root, legal } = fixture();
|
|
105
103
|
writeFileSync(path.join(root, "src-tauri", "tauri.conf.json"), JSON.stringify({ bundle: { licenseFile: "../legal/EULA.md" } }));
|
|
106
104
|
assert.throws(() => assertLegalReleaseContract(root, legal, "fixture", "win"), /licenseFile must be absent/i);
|
|
107
105
|
});
|
|
108
106
|
|
|
107
|
+
test("rejects a macOS DMG SLA too — one licenseFile drives both bundlers", () => {
|
|
108
|
+
const { root, legal } = fixture();
|
|
109
|
+
writeFileSync(path.join(root, "src-tauri", "tauri.conf.json"), JSON.stringify({ bundle: { licenseFile: "../legal/EULA.md" } }));
|
|
110
|
+
assert.throws(() => assertLegalReleaseContract(root, legal, "fixture", "mac"), /licenseFile must be absent/i);
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
test("accepts an app whose installer carries no license page", () => {
|
|
114
|
+
const { root, legal } = fixture();
|
|
115
|
+
for (const platform of ["win", "mac"]) {
|
|
116
|
+
assert.doesNotThrow(() => assertLegalReleaseContract(root, legal, "fixture", platform));
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
|
|
109
120
|
test("requires third-party build provenance", () => {
|
|
110
121
|
const { root, legal, manifest } = fixture();
|
|
111
122
|
const notice = "Third-party software is included.\n";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rightkit/release",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.30",
|
|
4
4
|
"description": "Portable Right Suite release CLI/SDK: signed installers, updater artifacts, patch/update routing, hardening, R2 publish, and RightApps registration.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
package/publish-update.test.mjs
CHANGED
|
@@ -193,10 +193,10 @@ test("requires the durable release credential before the first patch upload", ()
|
|
|
193
193
|
...process.env,
|
|
194
194
|
APPDATA: path.join(path.dirname(config), "empty-appdata"),
|
|
195
195
|
RIGHT_RELEASE_TIER: "patch",
|
|
196
|
+
RIGHTAPPS_RELEASE_TOKEN_FILE: path.join(path.dirname(config), "missing-release-token"),
|
|
196
197
|
RIGHTAPPS_ADMIN_TOKEN: "expired-admin-session",
|
|
197
198
|
};
|
|
198
199
|
delete env.RIGHTAPPS_RELEASE_TOKEN;
|
|
199
|
-
delete env.RIGHTAPPS_RELEASE_TOKEN_FILE;
|
|
200
200
|
const result = spawnSync(process.execPath, [publisher, "--config", config, "--platform", "win"], {
|
|
201
201
|
encoding: "utf8",
|
|
202
202
|
env,
|
package/release-token.mjs
CHANGED
|
@@ -23,7 +23,11 @@ export async function loadReleaseToken({ env = process.env, platform = process.p
|
|
|
23
23
|
if (direct) return direct;
|
|
24
24
|
|
|
25
25
|
if (env.RIGHTAPPS_RELEASE_TOKEN_FILE) {
|
|
26
|
-
|
|
26
|
+
try {
|
|
27
|
+
return await readTokenFile(path.resolve(env.RIGHTAPPS_RELEASE_TOKEN_FILE));
|
|
28
|
+
} catch (error) {
|
|
29
|
+
throw new Error(`RIGHTAPPS_RELEASE_TOKEN is unavailable (${error.message})`);
|
|
30
|
+
}
|
|
27
31
|
}
|
|
28
32
|
|
|
29
33
|
if (platform === "win32") {
|
package/release.mjs
CHANGED
|
@@ -16,7 +16,11 @@ const SIGN_UPDATER = path.resolve(TOOL_ROOT, "sign-updater.mjs");
|
|
|
16
16
|
const RIGHTKIT_VERSIONS = JSON.parse(readFileSync(path.resolve(TOOL_ROOT, "rightkit-versions.json"), "utf8"));
|
|
17
17
|
const VERSION = RIGHTKIT_VERSIONS.stagedNpm?.["@rightkit/release"] ?? RIGHTKIT_VERSIONS.npm["@rightkit/release"];
|
|
18
18
|
const TIERS = new Set(["patch", "update"]);
|
|
19
|
-
const RIGHTKIT_ALLOWED = buildAllowedVersions(
|
|
19
|
+
const RIGHTKIT_ALLOWED = buildAllowedVersions(
|
|
20
|
+
RIGHTKIT_VERSIONS.npm,
|
|
21
|
+
RIGHTKIT_VERSIONS.stagedNpm,
|
|
22
|
+
RIGHTKIT_VERSIONS.legacyNpm,
|
|
23
|
+
);
|
|
20
24
|
const RIGHTKIT_CARGO_ALLOWED = buildAllowedVersions(RIGHTKIT_VERSIONS.cargo, RIGHTKIT_VERSIONS.stagedCargo);
|
|
21
25
|
const FORBIDDEN_RIGHTKIT_SPEC = /^(?:git|file|link|workspace):|github|github\.com/i;
|
|
22
26
|
|
|
@@ -249,11 +253,15 @@ async function validateRightKitPackageContract(root, appName) {
|
|
|
249
253
|
}
|
|
250
254
|
}
|
|
251
255
|
|
|
252
|
-
function buildAllowedVersions(published = {}, staged = {}) {
|
|
256
|
+
function buildAllowedVersions(published = {}, staged = {}, legacy = {}) {
|
|
253
257
|
const allowed = new Map();
|
|
254
|
-
for (const [name,
|
|
258
|
+
for (const [name, value] of [
|
|
259
|
+
...Object.entries(published),
|
|
260
|
+
...Object.entries(staged),
|
|
261
|
+
...Object.entries(legacy),
|
|
262
|
+
]) {
|
|
255
263
|
const versions = allowed.get(name) ?? new Set();
|
|
256
|
-
versions.add(version);
|
|
264
|
+
for (const version of Array.isArray(value) ? value : [value]) versions.add(version);
|
|
257
265
|
allowed.set(name, versions);
|
|
258
266
|
}
|
|
259
267
|
return allowed;
|