Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion apps/server/src/device/DeviceToolchain.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import type { DeviceToolVersions } from "@t3tools/contracts";
import { DEVICE_TOOL_VERSIONS } from "@t3tools/shared/deviceToolVersions";
/**
* Pinned installs of the two external tools device support is built on.
*
Expand All @@ -25,6 +24,7 @@ import * as Schema from "effect/Schema";
import * as Semaphore from "effect/Semaphore";

import * as ProcessRunner from "../processRunner.ts";
import DEVICE_TOOL_VERSIONS from "./deviceToolVersions.json" with { type: "json" };

const DEVICE_HUB_PACKAGE = "expo-device-hub";
export const DEVICE_HUB_VERSION = DEVICE_TOOL_VERSIONS[DEVICE_HUB_PACKAGE];
Expand Down
4 changes: 4 additions & 0 deletions apps/server/src/device/deviceToolVersions.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"agent-device": "0.21.24",
"expo-device-hub": "0.15.3"
}
2 changes: 1 addition & 1 deletion docs/internals/open-source-licenses.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ supplies the label shown to users. Use `includeInBundles` when those differ, suc
server tool that should appear in both client manifests but is not bundled into either client.

The `agent-device` and `expo-device-hub` custom notices take their versions from the
[shared installer pins](../../packages/shared/src/deviceToolVersions.ts). Keep their package names
[installer pins](../../apps/server/src/device/deviceToolVersions.json). Keep their package names
in the config; their versions do not need manual updates.

Use `packageOverrides` only when an installed npm archive omits its notice or has incorrect
Expand Down
4 changes: 0 additions & 4 deletions packages/shared/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -359,10 +359,6 @@
"types": "./src/thirdPartyLicenses.ts",
"import": "./src/thirdPartyLicenses.ts"
},
"./deviceToolVersions": {
"types": "./src/deviceToolVersions.ts",
"import": "./src/deviceToolVersions.ts"
},
"./claudeCompaction": {
"types": "./src/claudeCompaction.ts",
"import": "./src/claudeCompaction.ts"
Expand Down
5 changes: 0 additions & 5 deletions packages/shared/src/deviceToolVersions.ts

This file was deleted.

79 changes: 78 additions & 1 deletion scripts/lib/third-party-licenses.test.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
// @effect-diagnostics nodeBuiltinImport:off - Tests exercise the Node filesystem build boundary.

import * as NodeFSP from "node:fs/promises";
import * as NodeChildProcess from "node:child_process";
import * as NodeOS from "node:os";
import * as NodePath from "node:path";
import * as NodeURL from "node:url";

import { DEVICE_TOOL_VERSIONS } from "@t3tools/shared/deviceToolVersions";
import { afterEach, describe, expect, it } from "vite-plus/test";

import {
Expand All @@ -19,6 +19,12 @@ const REPOSITORY_ROOT = NodePath.resolve(
NodePath.dirname(NodeURL.fileURLToPath(import.meta.url)),
"../..",
);
const DEVICE_TOOL_VERSIONS = JSON.parse(
await NodeFSP.readFile(
NodePath.join(REPOSITORY_ROOT, "apps/server/src/device/deviceToolVersions.json"),
"utf8",
),
) as { readonly "agent-device": string; readonly "expo-device-hub": string };

async function writeJson(path: string, value: unknown): Promise<void> {
await NodeFSP.mkdir(NodePath.dirname(path), { recursive: true });
Expand Down Expand Up @@ -78,6 +84,77 @@ afterEach(async () => {
});

describe("third-party license generation", () => {
it("generates mobile notices without scripts workspace dependency links", async () => {
const fixture = await createFixture();
const generatorPath = NodePath.join(fixture.root, "scripts/lib/third-party-licenses.ts");
const versionsPath = NodePath.join(
fixture.root,
"apps/server/src/device/deviceToolVersions.json",
);
await Promise.all([
NodeFSP.mkdir(NodePath.dirname(generatorPath), { recursive: true }),
NodeFSP.mkdir(NodePath.dirname(versionsPath), { recursive: true }),
]);
await Promise.all([
writeJson(NodePath.join(fixture.root, "scripts/package.json"), { type: "module" }),
NodeFSP.copyFile(
NodePath.join(REPOSITORY_ROOT, "scripts/lib/third-party-licenses.ts"),
generatorPath,
),
NodeFSP.copyFile(
NodePath.join(REPOSITORY_ROOT, "apps/server/src/device/deviceToolVersions.json"),
versionsPath,
),
writeJson(fixture.configFile, {
customNotices: Object.keys(DEVICE_TOOL_VERSIONS).map((name) => ({
name,
license: "MIT",
noticeFile: "asset-notice.txt",
bundles: ["device-tools"],
includeInBundles: ["mobile", "web"],
})),
}),
]);

const output = NodeChildProcess.execFileSync(
process.execPath,
[
"--input-type=module",
"--eval",
`
import { pathToFileURL } from "node:url";
const { generateThirdPartyLicenseManifest } = await import(
pathToFileURL(process.argv[1]).href + "?version=1"
);
const manifest = await generateThirdPartyLicenseManifest({
configFile: process.argv[2],
packageManifests: [{ bundle: "mobile", path: process.argv[3] }],
});
process.stdout.write(JSON.stringify(manifest));
`,
generatorPath,
fixture.configFile,
fixture.appManifest,
],
{ cwd: fixture.root, encoding: "utf8" },
);
const manifest = JSON.parse(output) as {
entries: ReadonlyArray<{ name: string; version: string }>;
};
expect(manifest.entries).toEqual(
expect.arrayContaining([
expect.objectContaining({
name: "agent-device",
version: DEVICE_TOOL_VERSIONS["agent-device"],
}),
expect.objectContaining({
name: "expo-device-hub",
version: DEVICE_TOOL_VERSIONS["expo-device-hub"],
}),
]),
);
});

it.each(["web", "mobile"])(
"uses installer pins for device tool notices in the %s manifest",
async (bundle) => {
Expand Down
14 changes: 11 additions & 3 deletions scripts/lib/third-party-licenses.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,12 @@ import * as NodePath from "node:path";
import * as NodeURL from "node:url";
import * as NodeModule from "node:module";

import { DEVICE_TOOL_VERSIONS } from "@t3tools/shared/deviceToolVersions";
import type { Plugin } from "vite-plus";

export const THIRD_PARTY_LICENSES_FILE_NAME = "third-party-licenses.json";
const SPDX_LICENSE_LIST_VERSION = "v3.28.0";
const SPDX_LICENSE_LIST_REVISION = "c4a7237ec8f4654e867546f9f409749300f1bf4c";
const GENERATED_NOTICE_CACHE_DIRECTORY = ".generated/third-party-licenses/spdx";
const DEVICE_TOOL_NOTICE_VERSIONS = new Map(Object.entries(DEVICE_TOOL_VERSIONS));

export interface ThirdPartyLicenseEntry {
readonly bundles: ReadonlyArray<string>;
Expand Down Expand Up @@ -825,6 +823,13 @@ async function customEntries(
includedBundles: ReadonlySet<string>,
allowMissingGeneratedNotices: boolean,
): Promise<ReadonlyArray<ThirdPartyLicenseEntry>> {
const deviceToolVersions: unknown = JSON.parse(
await NodeFSP.readFile(
new URL("../../apps/server/src/device/deviceToolVersions.json", import.meta.url),
"utf8",
),
);
if (!isRecord(deviceToolVersions)) throw new Error("Device tool versions must be an object.");
const entries = await Promise.all(
config.customNotices
.filter(
Expand Down Expand Up @@ -870,7 +875,10 @@ async function customEntries(
name: notice.name,
noticeText,
sourceUrl: notice.sourceUrl ?? null,
version: DEVICE_TOOL_NOTICE_VERSIONS.get(notice.name) ?? notice.version ?? null,
version:
readOptionalString(deviceToolVersions, notice.name, "Device tool versions") ??
notice.version ??
null,
};
}),
);
Expand Down
Loading