authorgravatar for saiansh2525@gmail.comYash Singh <saiansh2525@gmail.com> 2026-10-11 02:31:06-05:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-10-11 02:31:06-05:00
log20254307084f1101ced00dfadeddc7804a7de45d
tree6822ff33bebf8a798f8986449a527ec569a4a38d
parent5584d21688fc0e4cf6cd8057167df2514b759d3b
signature Signed by PGP key B5690EEEBB952194

fix(mobile): license generation works with filtered installs (#18127)


7 files changed, 95 insertions(+), 15 deletions(-)

apps/server/src/device/DeviceToolchain.ts+1-1
......@@ -1,5 +1,4 @@
11import type { DeviceToolVersions } from "@t3tools/contracts";
2import { DEVICE_TOOL_VERSIONS } from "@t3tools/shared/deviceToolVersions";
32/**
43 * Pinned installs of the two external tools device support is built on.
54 *
......@@ -25,6 +24,7 @@ import * as Schema from "effect/Schema";
2524import * as Semaphore from "effect/Semaphore";
2625
2726import * as ProcessRunner from "../processRunner.ts";
27import DEVICE_TOOL_VERSIONS from "./deviceToolVersions.json" with { type: "json" };
2828
2929const DEVICE_HUB_PACKAGE = "expo-device-hub";
3030export const DEVICE_HUB_VERSION = DEVICE_TOOL_VERSIONS[DEVICE_HUB_PACKAGE];
apps/server/src/device/deviceToolVersions.json created+4
......@@ -0,0 +1,4 @@
1{
2 "agent-device": "0.21.24",
3 "expo-device-hub": "0.15.3"
4}
docs/internals/open-source-licenses.md+1-1
......@@ -61,7 +61,7 @@ supplies the label shown to users. Use `includeInBundles` when those differ, suc
6161server tool that should appear in both client manifests but is not bundled into either client.
6262
6363The `agent-device` and `expo-device-hub` custom notices take their versions from the
64[shared installer pins](../../packages/shared/src/deviceToolVersions.ts). Keep their package names
64[installer pins](../../apps/server/src/device/deviceToolVersions.json). Keep their package names
6565in the config; their versions do not need manual updates.
6666
6767Use `packageOverrides` only when an installed npm archive omits its notice or has incorrect
packages/shared/package.json-4
......@@ -359,10 +359,6 @@
359359 "types": "./src/thirdPartyLicenses.ts",
360360 "import": "./src/thirdPartyLicenses.ts"
361361 },
362 "./deviceToolVersions": {
363 "types": "./src/deviceToolVersions.ts",
364 "import": "./src/deviceToolVersions.ts"
365 },
366362 "./claudeCompaction": {
367363 "types": "./src/claudeCompaction.ts",
368364 "import": "./src/claudeCompaction.ts"
packages/shared/src/deviceToolVersions.ts deleted-5
......@@ -1,5 +0,0 @@
1// The installer and third-party notices share these pins for tools installed outside the workspace.
2export const DEVICE_TOOL_VERSIONS = {
3 "agent-device": "0.21.24",
4 "expo-device-hub": "0.15.3",
5} as const;
scripts/lib/third-party-licenses.test.ts+78-1
......@@ -1,11 +1,11 @@
11// @effect-diagnostics nodeBuiltinImport:off - Tests exercise the Node filesystem build boundary.
22
33import * as NodeFSP from "node:fs/promises";
4import * as NodeChildProcess from "node:child_process";
45import * as NodeOS from "node:os";
56import * as NodePath from "node:path";
67import * as NodeURL from "node:url";
78
8import { DEVICE_TOOL_VERSIONS } from "@t3tools/shared/deviceToolVersions";
99import { afterEach, describe, expect, it } from "vite-plus/test";
1010
1111import {
......@@ -19,6 +19,12 @@ const REPOSITORY_ROOT = NodePath.resolve(
1919 NodePath.dirname(NodeURL.fileURLToPath(import.meta.url)),
2020 "../..",
2121);
22const DEVICE_TOOL_VERSIONS = JSON.parse(
23 await NodeFSP.readFile(
24 NodePath.join(REPOSITORY_ROOT, "apps/server/src/device/deviceToolVersions.json"),
25 "utf8",
26 ),
27) as { readonly "agent-device": string; readonly "expo-device-hub": string };
2228
2329async function writeJson(path: string, value: unknown): Promise<void> {
2430 await NodeFSP.mkdir(NodePath.dirname(path), { recursive: true });
......@@ -78,6 +84,77 @@ afterEach(async () => {
7884});
7985
8086describe("third-party license generation", () => {
87 it("generates mobile notices without scripts workspace dependency links", async () => {
88 const fixture = await createFixture();
89 const generatorPath = NodePath.join(fixture.root, "scripts/lib/third-party-licenses.ts");
90 const versionsPath = NodePath.join(
91 fixture.root,
92 "apps/server/src/device/deviceToolVersions.json",
93 );
94 await Promise.all([
95 NodeFSP.mkdir(NodePath.dirname(generatorPath), { recursive: true }),
96 NodeFSP.mkdir(NodePath.dirname(versionsPath), { recursive: true }),
97 ]);
98 await Promise.all([
99 writeJson(NodePath.join(fixture.root, "scripts/package.json"), { type: "module" }),
100 NodeFSP.copyFile(
101 NodePath.join(REPOSITORY_ROOT, "scripts/lib/third-party-licenses.ts"),
102 generatorPath,
103 ),
104 NodeFSP.copyFile(
105 NodePath.join(REPOSITORY_ROOT, "apps/server/src/device/deviceToolVersions.json"),
106 versionsPath,
107 ),
108 writeJson(fixture.configFile, {
109 customNotices: Object.keys(DEVICE_TOOL_VERSIONS).map((name) => ({
110 name,
111 license: "MIT",
112 noticeFile: "asset-notice.txt",
113 bundles: ["device-tools"],
114 includeInBundles: ["mobile", "web"],
115 })),
116 }),
117 ]);
118
119 const output = NodeChildProcess.execFileSync(
120 process.execPath,
121 [
122 "--input-type=module",
123 "--eval",
124 `
125 import { pathToFileURL } from "node:url";
126 const { generateThirdPartyLicenseManifest } = await import(
127 pathToFileURL(process.argv[1]).href + "?version=1"
128 );
129 const manifest = await generateThirdPartyLicenseManifest({
130 configFile: process.argv[2],
131 packageManifests: [{ bundle: "mobile", path: process.argv[3] }],
132 });
133 process.stdout.write(JSON.stringify(manifest));
134 `,
135 generatorPath,
136 fixture.configFile,
137 fixture.appManifest,
138 ],
139 { cwd: fixture.root, encoding: "utf8" },
140 );
141 const manifest = JSON.parse(output) as {
142 entries: ReadonlyArray<{ name: string; version: string }>;
143 };
144 expect(manifest.entries).toEqual(
145 expect.arrayContaining([
146 expect.objectContaining({
147 name: "agent-device",
148 version: DEVICE_TOOL_VERSIONS["agent-device"],
149 }),
150 expect.objectContaining({
151 name: "expo-device-hub",
152 version: DEVICE_TOOL_VERSIONS["expo-device-hub"],
153 }),
154 ]),
155 );
156 });
157
81158 it.each(["web", "mobile"])(
82159 "uses installer pins for device tool notices in the %s manifest",
83160 async (bundle) => {
scripts/lib/third-party-licenses.ts+11-3
......@@ -5,14 +5,12 @@ import * as NodePath from "node:path";
55import * as NodeURL from "node:url";
66import * as NodeModule from "node:module";
77
8import { DEVICE_TOOL_VERSIONS } from "@t3tools/shared/deviceToolVersions";
98import type { Plugin } from "vite-plus";
109
1110export const THIRD_PARTY_LICENSES_FILE_NAME = "third-party-licenses.json";
1211const SPDX_LICENSE_LIST_VERSION = "v3.28.0";
1312const SPDX_LICENSE_LIST_REVISION = "c4a7237ec8f4654e867546f9f409749300f1bf4c";
1413const GENERATED_NOTICE_CACHE_DIRECTORY = ".generated/third-party-licenses/spdx";
15const DEVICE_TOOL_NOTICE_VERSIONS = new Map(Object.entries(DEVICE_TOOL_VERSIONS));
1614
1715export interface ThirdPartyLicenseEntry {
1816 readonly bundles: ReadonlyArray<string>;
......@@ -825,6 +823,13 @@ async function customEntries(
825823 includedBundles: ReadonlySet<string>,
826824 allowMissingGeneratedNotices: boolean,
827825): Promise<ReadonlyArray<ThirdPartyLicenseEntry>> {
826 const deviceToolVersions: unknown = JSON.parse(
827 await NodeFSP.readFile(
828 new URL("../../apps/server/src/device/deviceToolVersions.json", import.meta.url),
829 "utf8",
830 ),
831 );
832 if (!isRecord(deviceToolVersions)) throw new Error("Device tool versions must be an object.");
828833 const entries = await Promise.all(
829834 config.customNotices
830835 .filter(
......@@ -870,7 +875,10 @@ async function customEntries(
870875 name: notice.name,
871876 noticeText,
872877 sourceUrl: notice.sourceUrl ?? null,
873 version: DEVICE_TOOL_NOTICE_VERSIONS.get(notice.name) ?? notice.version ?? null,
878 version:
879 readOptionalString(deviceToolVersions, notice.name, "Device tool versions") ??
880 notice.version ??
881 null,
874882 };
875883 }),
876884 );