@rightkit/release 0.2.28 → 0.2.29

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.
@@ -135,24 +135,26 @@ export function assertLegalReleaseContract(root, legal, appName, platform) {
135
135
  appendices.push({ ...appendix, absolutePath: realFile });
136
136
  }
137
137
 
138
- if (platform === "win") {
139
- if (typeof legal.tauriConfig !== "string") fail(appName, "Windows legal gate requires legal.tauriConfig");
140
- const tauriPath = path.resolve(appRoot, legal.tauriConfig);
141
- if (!within(appRoot, tauriPath) || !existsSync(tauriPath)) fail(appName, `missing Tauri config: ${legal.tauriConfig}`);
142
- const tauri = readJson(tauriPath, appName, "Tauri config");
143
- // The in-app gate is the SINGLE assent surface (Adrian, 2026-07-17). An
144
- // installer/DMG can only PRESENT the text: it cannot record who agreed, cannot
145
- // ask the individual-vs-enterprise basis, and passive updaters reach neither
146
- // so a license page is duplicate presentation that captures nothing. This gate
147
- // previously REQUIRED bundle.licenseFile; it now requires its absence.
148
- const configured = tauri.bundle?.licenseFile;
149
- if (typeof configured === "string" && configured.trim()) {
150
- fail(
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 (Adrian, 2026-07-17). An
139
+ // installer/DMG can only PRESENT the text: it cannot record who agreed, cannot ask
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.
147
+ if (typeof legal.tauriConfig !== "string") fail(appName, "the legal gate requires legal.tauriConfig");
148
+ const tauriPath = path.resolve(appRoot, legal.tauriConfig);
149
+ if (!within(appRoot, tauriPath) || !existsSync(tauriPath)) fail(appName, `missing Tauri config: ${legal.tauriConfig}`);
150
+ const tauri = readJson(tauriPath, appName, "Tauri config");
151
+ const configured = tauri.bundle?.licenseFile;
152
+ if (typeof configured === "string" && configured.trim()) {
153
+ fail(
154
+ appName,
155
+ "Tauri bundle.licenseFile must be absent: the in-app legal gate is the only assent surface, and an " +
156
+ "installer license page (NSIS) or DMG SLA duplicates it without recording acceptance",
157
+ );
156
158
  }
157
159
 
158
160
  return {
@@ -100,12 +100,27 @@ test("rejects an explicitly unresolved release blocker even without a checkbox",
100
100
  assert.throws(() => assertLegalReleaseContract(root, legal, "fixture", "win"), /release blocker/i);
101
101
  });
102
102
 
103
- test("requires Windows installer clickthrough to use the exact EULA snapshot", () => {
103
+ test("rejects a Windows installer license page: the in-app gate is the only assent surface", () => {
104
104
  const { root, legal } = fixture();
105
105
  writeFileSync(path.join(root, "src-tauri", "tauri.conf.json"), JSON.stringify({ bundle: { licenseFile: "../legal/EULA.md" } }));
106
106
  assert.throws(() => assertLegalReleaseContract(root, legal, "fixture", "win"), /licenseFile must be absent/i);
107
107
  });
108
108
 
109
+ 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
+ const { root, legal } = fixture();
113
+ writeFileSync(path.join(root, "src-tauri", "tauri.conf.json"), JSON.stringify({ bundle: { licenseFile: "../legal/EULA.md" } }));
114
+ assert.throws(() => assertLegalReleaseContract(root, legal, "fixture", "mac"), /licenseFile must be absent/i);
115
+ });
116
+
117
+ test("accepts an app whose installer carries no license page", () => {
118
+ const { root, legal } = fixture();
119
+ for (const platform of ["win", "mac"]) {
120
+ assert.doesNotThrow(() => assertLegalReleaseContract(root, legal, "fixture", platform));
121
+ }
122
+ });
123
+
109
124
  test("requires third-party build provenance", () => {
110
125
  const { root, legal, manifest } = fixture();
111
126
  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.28",
3
+ "version": "0.2.29",
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": {
@@ -327,7 +327,7 @@ test("RightKit exposes one current version manifest", () => {
327
327
  "@rightkit/legal": "0.3.0",
328
328
  "@rightkit/legal-ui": "0.1.0",
329
329
  "@rightkit/license": "0.1.6",
330
- "@rightkit/release": "0.2.28",
330
+ "@rightkit/release": "0.2.29",
331
331
  });
332
332
  const licensePackage = JSON.parse(readFileSync(
333
333
  path.join(workspace, "tools/rightkit/packages/license/package.json"),
@@ -14,7 +14,7 @@
14
14
  "stagedNpm": {
15
15
  "@rightkit/legal": "0.3.0",
16
16
  "@rightkit/license": "0.1.6",
17
- "@rightkit/release": "0.2.28",
17
+ "@rightkit/release": "0.2.29",
18
18
  "@rightkit/legal-ui": "0.1.0"
19
19
  },
20
20
  "cargo": {