@rightkit/release 0.2.29 → 0.2.31
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 +15 -2
- package/create-mac-updater.test.mjs +2 -1
- package/legal-contract.mjs +2 -9
- package/legal-contract.test.mjs +0 -4
- 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
|
@@ -23,6 +23,10 @@ const outputPath = path.resolve(cwd, output);
|
|
|
23
23
|
if (!dryRun && !existsSync(appPath)) fail(`signed app not found: ${appPath}`);
|
|
24
24
|
|
|
25
25
|
const env = { ...process.env };
|
|
26
|
+
// bsdtar otherwise serializes macOS extended attributes as AppleDouble `._*`
|
|
27
|
+
// entries. Tauri's Rust extractor writes those entries as real bundle files,
|
|
28
|
+
// invalidating the Developer ID seal and causing syspolicyd to trash the app.
|
|
29
|
+
env.COPYFILE_DISABLE = "1";
|
|
26
30
|
if (!dryRun && !env.TAURI_SIGNING_PRIVATE_KEY) {
|
|
27
31
|
try {
|
|
28
32
|
env.TAURI_SIGNING_PRIVATE_KEY = execFileSync(
|
|
@@ -38,11 +42,20 @@ env.TAURI_SIGNING_PRIVATE_KEY_PASSWORD ??= "";
|
|
|
38
42
|
|
|
39
43
|
rmSync(outputPath, { force: true });
|
|
40
44
|
rmSync(`${outputPath}.sig`, { force: true });
|
|
41
|
-
|
|
45
|
+
// Tauri 2.10 strips the first path component while extracting. Omitting the explicit
|
|
46
|
+
// App.app/ directory entry prevents it from unpacking that entry onto its existing temp dir.
|
|
47
|
+
run("tar", [
|
|
48
|
+
"-czf",
|
|
49
|
+
outputPath,
|
|
50
|
+
"-C",
|
|
51
|
+
path.dirname(appPath),
|
|
52
|
+
path.join(path.basename(appPath), "Contents"),
|
|
53
|
+
]);
|
|
42
54
|
run("pnpm", ["exec", "tauri", "signer", "sign", outputPath]);
|
|
43
55
|
|
|
44
56
|
function run(command, commandArgs) {
|
|
45
|
-
|
|
57
|
+
const envPrefix = command === "tar" ? "COPYFILE_DISABLE=1 " : "";
|
|
58
|
+
console.log(`${dryRun ? "[dry-run] " : ""}${envPrefix}${command} ${commandArgs.join(" ")}`);
|
|
46
59
|
if (dryRun) return;
|
|
47
60
|
const result = spawnSync(command, commandArgs, { cwd, env, stdio: "inherit" });
|
|
48
61
|
if (result.status !== 0) process.exit(result.status ?? 1);
|
|
@@ -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, /COPYFILE_DISABLE=1 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,15 +135,8 @@ export function assertLegalReleaseContract(root, legal, appName, platform) {
|
|
|
135
135
|
appendices.push({ ...appendix, absolutePath: realFile });
|
|
136
136
|
}
|
|
137
137
|
|
|
138
|
-
// The in-app gate is the
|
|
139
|
-
//
|
|
140
|
-
// the individual-vs-enterprise basis, and passive updaters reach neither — so a
|
|
141
|
-
// license page is duplicate presentation that captures nothing. This gate
|
|
142
|
-
// previously REQUIRED bundle.licenseFile; it now requires its absence.
|
|
143
|
-
//
|
|
144
|
-
// Checked on EVERY platform, not just Windows: one bundle.licenseFile drives BOTH
|
|
145
|
-
// the NSIS license page and the macOS DMG SLA (branded-dmg.mjs derives its --eula
|
|
146
|
-
// from it), so a win-only check would let a mac build reintroduce the SLA.
|
|
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.
|
|
147
140
|
if (typeof legal.tauriConfig !== "string") fail(appName, "the legal gate requires legal.tauriConfig");
|
|
148
141
|
const tauriPath = path.resolve(appRoot, legal.tauriConfig);
|
|
149
142
|
if (!within(appRoot, tauriPath) || !existsSync(tauriPath)) fail(appName, `missing Tauri config: ${legal.tauriConfig}`);
|
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`,
|
|
@@ -107,8 +105,6 @@ test("rejects a Windows installer license page: the in-app gate is the only asse
|
|
|
107
105
|
});
|
|
108
106
|
|
|
109
107
|
test("rejects a macOS DMG SLA too — one licenseFile drives both bundlers", () => {
|
|
110
|
-
// branded-dmg.mjs derives its --eula from bundle.licenseFile, so a Windows-only
|
|
111
|
-
// check would let a mac release quietly reintroduce the mount-time SLA.
|
|
112
108
|
const { root, legal } = fixture();
|
|
113
109
|
writeFileSync(path.join(root, "src-tauri", "tauri.conf.json"), JSON.stringify({ bundle: { licenseFile: "../legal/EULA.md" } }));
|
|
114
110
|
assert.throws(() => assertLegalReleaseContract(root, legal, "fixture", "mac"), /licenseFile must be absent/i);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rightkit/release",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.31",
|
|
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;
|