authorgravatar for 107218376+Prgm-code@users.noreply.github.comPatricio Gómez Meneses <107218376+Prgm-code@users.noreply.github.com> 2026-07-08 23:55:33-04:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-07-09 03:55:33+00:00
log18a41388eb694a8590c2239424bf8ddec55c1c72
treefee8719334fd0aec0c197ec2735a96ca5d3bb572
parent0c665658544c9d60b29b59699631d7f496e914e6
signature Signed by PGP key B5690EEEBB952194

Fix desktop native optional dependency packaging (#3816)


2 files changed, 45 insertions(+), 11 deletions(-)

scripts/build-desktop-artifact.test.ts+15
......@@ -33,6 +33,7 @@ import {
3333 resolveGitHubPublishConfig,
3434 resolveMockUpdateServerPort,
3535 resolveMockUpdateServerUrl,
36 resolvePackageManagerUserAgent,
3637 stageLinuxIconSize,
3738 STAGE_INSTALL_ARGS,
3839} from "./build-desktop-artifact.ts";
......@@ -208,6 +209,13 @@ it.layer(NodeServices.layer)("build-desktop-artifact", (it) => {
208209 cpu: ["x64"],
209210 },
210211 });
212 assert.deepStrictEqual(createStageWorkspaceConfig({ platform: "linux", arch: "x64" }), {
213 supportedArchitectures: {
214 os: ["linux"],
215 cpu: ["x64"],
216 libc: ["glibc"],
217 },
218 });
211219 // Windows artifacts also bundle the same-architecture WSL (Linux, glibc) backend, so the
212220 // staged install must fetch its native optional deps (e.g. ffi-rs) too.
213221 assert.deepStrictEqual(createStageWorkspaceConfig({ platform: "win", arch: "x64" }), {
......@@ -253,6 +261,7 @@ it.layer(NodeServices.layer)("build-desktop-artifact", (it) => {
253261 supportedArchitectures: {
254262 os: ["linux"],
255263 cpu: ["x64"],
264 libc: ["glibc"],
256265 },
257266 allowBuilds: {
258267 electron: true,
......@@ -529,6 +538,12 @@ it.layer(NodeServices.layer)("build-desktop-artifact", (it) => {
529538 assert.equal(resolveMockUpdateServerUrl(4123), "http://localhost:4123");
530539 });
531540
541 it("derives the electron-builder package manager user agent from packageManager", () => {
542 assert.equal(resolvePackageManagerUserAgent("pnpm@11.10.0"), "pnpm/11.10.0");
543 assert.equal(resolvePackageManagerUserAgent(" yarn@4.9.2 "), "yarn/4.9.2");
544 assert.equal(resolvePackageManagerUserAgent("pnpm"), "pnpm");
545 });
546
532547 it.effect("normalizes mock update server ports from env-style strings", () =>
533548 Effect.gen(function* () {
534549 assert.equal(yield* resolveMockUpdateServerPort(undefined), undefined);
scripts/build-desktop-artifact.ts+30-11
......@@ -890,22 +890,27 @@ export function createStageWorkspaceConfig(input: {
890890 const { platform, arch, allowBuilds, patchedDependencies, overrides } = input;
891891 const hostOs = platform === "mac" ? "darwin" : platform === "win" ? "win32" : "linux";
892892 const hostCpu = arch === "universal" ? ["arm64", "x64"] : [arch];
893 // Windows artifacts also bundle the same-architecture WSL Linux backend, which loads
894 // Linux-native optional deps at runtime (e.g. @yuuang/ffi-rs-linux-x64-gnu).
895 // Pull the Linux (glibc) variants in addition to the host platform's so
896 // they ship in the asar; without them the WSL backend crash-loops on require
897 // ("Cannot find module '@yuuang/ffi-rs-linux-x64-gnu'").
893 // Linux AppImages and Windows WSL backends both execute a Linux/glibc Node
894 // process that loads Linux-native optional deps at runtime (e.g.
895 // @yuuang/ffi-rs-linux-x64-gnu). Keep libc explicit so pnpm includes those
896 // optional packages in the staged production install.
898897 const supportedArchitectures =
899 platform === "win"
898 platform === "linux"
900899 ? {
901 os: Array.from(new Set([hostOs, "linux"])),
900 os: [hostOs],
902901 cpu: hostCpu,
903902 libc: ["glibc"],
904903 }
905 : {
906 os: [hostOs],
907 cpu: hostCpu,
908 };
904 : platform === "win"
905 ? {
906 os: Array.from(new Set([hostOs, "linux"])),
907 cpu: hostCpu,
908 libc: ["glibc"],
909 }
910 : {
911 os: [hostOs],
912 cpu: hostCpu,
913 };
909914
910915 return {
911916 supportedArchitectures,
......@@ -1339,6 +1344,19 @@ export function resolveMockUpdateServerUrl(mockUpdateServerPort: number | undefi
13391344 return `http://localhost:${mockUpdateServerPort ?? 3000}`;
13401345}
13411346
1347// Electron Builder detects pnpm from npm_config_user_agent, whose value uses
1348// user-agent syntax (pnpm/11.10.0) rather than packageManager syntax
1349// (pnpm@11.10.0).
1350export function resolvePackageManagerUserAgent(packageManager: string): string {
1351 const trimmed = packageManager.trim();
1352 const versionSeparator = trimmed.lastIndexOf("@");
1353 if (versionSeparator <= 0 || versionSeparator === trimmed.length - 1) {
1354 return trimmed;
1355 }
1356
1357 return `${trimmed.slice(0, versionSeparator)}/${trimmed.slice(versionSeparator + 1)}`;
1358}
1359
13421360export function resolveDesktopProductName(version: string): string {
13431361 return resolveDesktopUpdateChannel(version) === "nightly"
13441362 ? "T3 Code (Nightly)"
......@@ -1799,6 +1817,7 @@ const buildDesktopArtifact = Effect.fn("buildDesktopArtifact")(function* (
17991817 const buildEnv: NodeJS.ProcessEnv = {
18001818 ...process.env,
18011819 };
1820 buildEnv.npm_config_user_agent = resolvePackageManagerUserAgent(rootPackageJson.packageManager);
18021821 for (const [key, value] of Object.entries(buildEnv)) {
18031822 if (value === "") {
18041823 delete buildEnv[key];