You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@maka.apache.org by as...@apache.org on 2026/08/31 17:09:24 UTC

(maka) branch main updated: fix(desktop): resolve packaged license and electron paths without assuming hoisted node_modules (#4373)

This is an automated email from the ASF dual-hosted git repository.

Astro-Han pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/maka.git


The following commit(s) were added to refs/heads/main by this push:
     new 21bdf2348f fix(desktop): resolve packaged license and electron paths without assuming hoisted node_modules (#4373)
21bdf2348f is described below

commit 21bdf2348ffab71843c9942b06eb864b960c6a3b
Author: Xiao Liu <li...@qq.com>
AuthorDate: Tue Sep 1 01:09:19 2026 +0800

    fix(desktop): resolve packaged license and electron paths without assuming hoisted node_modules (#4373)
    
    Packaging and install scripts located third-party packages by walking to the workspace root (`../../node_modules/<pkg>`), which only resolves because npm hoists everything up. `electron` and `@fontsource-variable/geist*` are declared in `apps/desktop`, not the root, so under a layout that does not hoist the four `extraResources` license entries silently miss — electron-builder logs `file source doesn't exist` and still exits 0, dropping Electron's `LICENSE` and `LICENSES.chromium.html [...]
    
    Resolve each package by its manifest (`createRequire(...).resolve('<pkg>/package.json')`) instead of assuming its `node_modules` location. The release pre-flight guards in `package-macos-arm64.mjs` and `package-windows-x64.mjs` asserted the same hoisted electron dist path and are fixed the same way, so the assumption does not resurface in the release path.
    
    No behavior change under the committed npm install: resolution anchored at `apps/desktop` still walks up to the root `node_modules`, so the same files are found and the packaged artifacts are unchanged.
    
    Closes #4367
    
    Generated-by: Claude Code
---
 apps/desktop/electron-builder.config.mjs | 25 +++++++++++++++++++-----
 scripts/install-electron-with-retry.mjs  |  6 +++++-
 scripts/package-macos-arm64.mjs          | 12 +++++++++++-
 scripts/package-windows-x64.mjs          | 12 +++++++++++-
 scripts/product-release.test.mjs         | 33 +++++++++++++++++++++++++++++++-
 5 files changed, 79 insertions(+), 9 deletions(-)

diff --git a/apps/desktop/electron-builder.config.mjs b/apps/desktop/electron-builder.config.mjs
index 269d57267a..652e0cd08d 100644
--- a/apps/desktop/electron-builder.config.mjs
+++ b/apps/desktop/electron-builder.config.mjs
@@ -18,7 +18,8 @@
  */
 
 import { mkdirSync, readFileSync, writeFileSync } from 'node:fs';
-import { join } from 'node:path';
+import { createRequire } from 'node:module';
+import { dirname, join } from 'node:path';
 import {
   resolveDesktopBuildVersion,
   resolveRuntimeHostSetupPackage,
@@ -30,6 +31,20 @@ function readManifest(relativePath) {
   return JSON.parse(readFileSync(new URL(relativePath, import.meta.url), 'utf8'));
 }
 
+// Some license files below ship inside third-party packages that apps/desktop
+// depends on (electron, @fontsource-variable/geist*). Locate each package by
+// resolving its manifest rather than assuming its node_modules location:
+// `../../node_modules/<pkg>` only resolves when the installer hoists these
+// packages to the workspace root, but they are declared in apps/desktop, not
+// the root. electron-builder logs a warning and still exits 0 when a `from`
+// path is missing, so a non-hoisting layout would silently drop the notices
+// (verify-packaged-app.mjs then fails far from the cause). resolve() finds the
+// package wherever the installer placed it — hoisted or nested.
+const require = createRequire(import.meta.url);
+function resolvePackageFile(packageName, relativePath) {
+  return join(dirname(require.resolve(`${packageName}/package.json`)), relativePath);
+}
+
 async function stageReleaseManifests({ packager }) {
   const stage = await packager.info.tempDirManager.createTempDir({
     prefix: 'maka-release-manifests',
@@ -150,11 +165,11 @@ const baseDesktopBuilderConfig = {
       to: 'licenses/maka/DISCLAIMER-WIP',
     },
     {
-      from: '../../node_modules/electron/dist/LICENSE',
+      from: resolvePackageFile('electron', 'dist/LICENSE'),
       to: 'licenses/electron/LICENSE',
     },
     {
-      from: '../../node_modules/electron/dist/LICENSES.chromium.html',
+      from: resolvePackageFile('electron', 'dist/LICENSES.chromium.html'),
       to: 'licenses/electron/LICENSES.chromium.html',
     },
     {
@@ -166,11 +181,11 @@ const baseDesktopBuilderConfig = {
       to: 'licenses/renderer/THIRD_PARTY_LICENSES.txt',
     },
     {
-      from: '../../node_modules/@fontsource-variable/geist/LICENSE',
+      from: resolvePackageFile('@fontsource-variable/geist', 'LICENSE'),
       to: 'licenses/renderer/GEIST_LICENSE.txt',
     },
     {
-      from: '../../node_modules/@fontsource-variable/geist-mono/LICENSE',
+      from: resolvePackageFile('@fontsource-variable/geist-mono', 'LICENSE'),
       to: 'licenses/renderer/GEIST_MONO_LICENSE.txt',
     },
     {
diff --git a/scripts/install-electron-with-retry.mjs b/scripts/install-electron-with-retry.mjs
index 139b280e84..4b77170ee8 100644
--- a/scripts/install-electron-with-retry.mjs
+++ b/scripts/install-electron-with-retry.mjs
@@ -95,7 +95,11 @@ export function electronInstallerSpawnOptions(repositoryRoot) {
 }
 
 async function main() {
-  const require = createRequire(import.meta.url);
+  // electron is declared by apps/desktop, not the workspace root, so anchor
+  // resolution at that package. Resolving from this script (under scripts/)
+  // only reaches electron when the installer hoists it to the root
+  // node_modules; anchoring at apps/desktop works under any layout.
+  const require = createRequire(new URL('../apps/desktop/package.json', import.meta.url));
   const installer = require.resolve('electron/install.js');
   const launcher = fileURLToPath(new URL('./run-electron-installer.cjs', import.meta.url));
   const repositoryRoot = dirname(dirname(fileURLToPath(import.meta.url)));
diff --git a/scripts/package-macos-arm64.mjs b/scripts/package-macos-arm64.mjs
index c7b97fba7e..6b096d9be3 100644
--- a/scripts/package-macos-arm64.mjs
+++ b/scripts/package-macos-arm64.mjs
@@ -19,6 +19,7 @@
 
 import { spawn } from 'node:child_process';
 import { access, readFile, rm } from 'node:fs/promises';
+import { createRequire } from 'node:module';
 import { dirname, join } from 'node:path';
 import { fileURLToPath, pathToFileURL } from 'node:url';
 import { resolveDesktopBuildVersion } from './desktop-nightly.mjs';
@@ -26,7 +27,16 @@ import { resolveDesktopBuildVersion } from './desktop-nightly.mjs';
 const repoRoot = dirname(dirname(fileURLToPath(import.meta.url)));
 const desktopRoot = join(repoRoot, 'apps', 'desktop');
 const releaseDirectory = join(desktopRoot, 'release');
-const electronDistributionDirectory = join(repoRoot, 'node_modules', 'electron', 'dist');
+// electron is declared by apps/desktop, so resolve its install directory from
+// there rather than assuming node_modules hoisted it to the repo root. This
+// pre-flight guard exists to catch a missing electron dist before packaging;
+// pinning it to the hoisted path would make it fail at the wrong location the
+// moment an installer nests electron under apps/desktop.
+const require = createRequire(join(desktopRoot, 'package.json'));
+const electronDistributionDirectory = join(
+  dirname(require.resolve('electron/package.json')),
+  'dist',
+);
 const requiredElectronLicensePaths = [
   join(electronDistributionDirectory, 'LICENSE'),
   join(electronDistributionDirectory, 'LICENSES.chromium.html'),
diff --git a/scripts/package-windows-x64.mjs b/scripts/package-windows-x64.mjs
index 28bdef8fe1..c7d02cd526 100644
--- a/scripts/package-windows-x64.mjs
+++ b/scripts/package-windows-x64.mjs
@@ -19,6 +19,7 @@
 
 import { spawn } from 'node:child_process';
 import { access, copyFile, mkdir, readFile, rm } from 'node:fs/promises';
+import { createRequire } from 'node:module';
 import { dirname, join } from 'node:path';
 import { fileURLToPath, pathToFileURL } from 'node:url';
 import { resolveDesktopBuildVersion } from './desktop-nightly.mjs';
@@ -27,7 +28,16 @@ import { npmSpawnOptions } from './npm-spawn.mjs';
 const repoRoot = dirname(dirname(fileURLToPath(import.meta.url)));
 const desktopRoot = join(repoRoot, 'apps', 'desktop');
 const releaseDirectory = join(desktopRoot, 'release');
-const electronDistributionDirectory = join(repoRoot, 'node_modules', 'electron', 'dist');
+// electron is declared by apps/desktop, so resolve its install directory from
+// there rather than assuming node_modules hoisted it to the repo root. This
+// pre-flight guard exists to catch a missing electron dist before packaging;
+// pinning it to the hoisted path would make it fail at the wrong location the
+// moment an installer nests electron under apps/desktop.
+const require = createRequire(join(desktopRoot, 'package.json'));
+const electronDistributionDirectory = join(
+  dirname(require.resolve('electron/package.json')),
+  'dist',
+);
 const sandboxManifestPath = join(
   repoRoot,
   'experiments',
diff --git a/scripts/product-release.test.mjs b/scripts/product-release.test.mjs
index 04023366bc..1c695d2f28 100644
--- a/scripts/product-release.test.mjs
+++ b/scripts/product-release.test.mjs
@@ -21,7 +21,7 @@ import assert from 'node:assert/strict';
 import { execFile } from 'node:child_process';
 import { access, chmod, mkdir, mkdtemp, readFile, rm, writeFile } from 'node:fs/promises';
 import { tmpdir } from 'node:os';
-import { join } from 'node:path';
+import { isAbsolute, join } from 'node:path';
 import test from 'node:test';
 import { promisify } from 'node:util';
 import { parse as parseYaml } from 'yaml';
@@ -221,6 +221,37 @@ test('Desktop packaging does not distribute the retired bundled Git runtime', ()
   );
 });
 
+test('packaged third-party license sources are resolved, not assumed hoisted', () => {
+  // electron and @fontsource-variable/geist* are declared by apps/desktop, so
+  // `../../node_modules/<pkg>` only resolves when the installer hoists them to
+  // the workspace root. electron-builder logs a warning and still exits 0 on a
+  // missing `extraResources` source, so a non-hoisting layout would silently
+  // drop these notices. The config resolves each package instead; assert the
+  // sources are absolute paths that end at the intended in-package file.
+  const expectedSuffixes = new Map([
+    ['licenses/electron/LICENSE', join('electron', 'dist', 'LICENSE')],
+    [
+      'licenses/electron/LICENSES.chromium.html',
+      join('electron', 'dist', 'LICENSES.chromium.html'),
+    ],
+    ['licenses/renderer/GEIST_LICENSE.txt', join('@fontsource-variable', 'geist', 'LICENSE')],
+    [
+      'licenses/renderer/GEIST_MONO_LICENSE.txt',
+      join('@fontsource-variable', 'geist-mono', 'LICENSE'),
+    ],
+  ]);
+  const byTarget = new Map(desktopBuilderConfig.extraResources.map(({ from, to }) => [to, from]));
+  for (const [to, suffix] of expectedSuffixes) {
+    const from = byTarget.get(to);
+    assert.ok(from, `missing extraResources entry for ${to}`);
+    assert.ok(
+      isAbsolute(from),
+      `${to} source must be a resolved absolute path, not a '../../node_modules' hoisting assumption`,
+    );
+    assert.ok(from.endsWith(suffix), `${to} source must resolve to ${suffix}, got ${from}`);
+  }
+});
+
 test('macOS DMG ships correctly sized background assets', async () => {
   const backgroundDirectory = join(repoRoot, 'apps', 'desktop', 'build');
   const pngSignature = Buffer.from([137, 80, 78, 71, 13, 10, 26, 10]);


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@maka.apache.org
For additional commands, e-mail: commits-help@maka.apache.org