4059 lines · 147.5 KB · TypeScript
RawDownload
#!/usr/bin/env node// @effect-diagnostics nodeBuiltinImport:off - Node's typed junction API avoids Windows symlink privileges while keeping the probe isolated. import * as NodeFSP from "node:fs/promises";import * as NodeCrypto from "node:crypto";import * as NodeModule from "node:module"; import {  createPackageWithOptions,  extractAll,  getRawHeader,  statFile,  type DirectoryRecord,} from "@electron/asar"; import { fromYaml } from "@t3tools/shared/schemaYaml";import { HostProcessArchitecture, HostProcessPlatform } from "@t3tools/shared/hostProcess";import { clerkFrontendApiHostnameFromPublishableKey } from "@t3tools/shared/relayAuth";import { resolveSpawnCommand } from "@t3tools/shared/shell";import rootPackageJson from "../package.json" with { type: "json" };import desktopPackageJson from "../apps/desktop/package.json" with { type: "json" };import gnomeCaptureBundle from "../apps/desktop/gnome-extension/bundle.json" with { type: "json" };import serverPackageJson from "../apps/server/package.json" with { type: "json" }; import { applyWebBrandAssets } from "./apply-web-brand-assets.ts";import {  BRAND_ASSET_PATHS,  resolveWebAssetBrandForChannel,  type WebAssetBrand,} from "./lib/brand-assets.ts";import { getDefaultBuildArch } from "./lib/build-target-arch.ts";import {  findInlinedExternalPackages,  selectCliRuntimeExternalDependencies,} from "./lib/cli-external-packages.ts";import { loadRepoEnv } from "./lib/public-config.ts";import { resolveCatalogDependencies } from "./lib/resolve-catalog.ts"; import * as NodeRuntime from "@effect/platform-node/NodeRuntime";import * as NodeServices from "@effect/platform-node/NodeServices";import * as Config from "effect/Config";import * as Duration from "effect/Duration";import * as Effect from "effect/Effect";import * as FileSystem from "effect/FileSystem";import * as Layer from "effect/Layer";import * as Logger from "effect/Logger";import * as Option from "effect/Option";import type { PlatformError } from "effect/PlatformError";import * as Path from "effect/Path";import * as Schema from "effect/Schema";import * as Stream from "effect/Stream";import { Command, Flag } from "effect/unstable/cli";import { ChildProcess, ChildProcessSpawner } from "effect/unstable/process"; const LINUX_ICON_SIZES = [16, 22, 24, 32, 48, 64, 128, 256, 512] as const;const DESKTOP_APP_ID = "com.t3tools.t3code";const APPLE_TEAM_ID_PATTERN = /^[A-Z0-9]{10}$/u; const BuildPlatform = Schema.Literals(["mac", "linux", "win"]);const BuildArch = Schema.Literals(["arm64", "x64", "universal"]); const WorkspaceConfig = Schema.Struct({  catalog: Schema.optional(Schema.Record(Schema.String, Schema.String)),  overrides: Schema.optional(Schema.Record(Schema.String, Schema.String)),  patchedDependencies: Schema.optional(Schema.Record(Schema.String, Schema.String)),  allowBuilds: Schema.optional(Schema.Record(Schema.String, Schema.Boolean)),});type WorkspaceConfig = typeof WorkspaceConfig.Type; const StageWorkspaceConfig = Schema.Struct({  supportedArchitectures: Schema.Struct({    os: Schema.Array(Schema.String),    cpu: Schema.Array(Schema.String),    libc: Schema.optional(Schema.Array(Schema.String)),  }),  // pnpm 11 only reads these from pnpm-workspace.yaml (not package.json#pnpm).  // Without allowBuilds the staged `vp install --prod` fails with  // ERR_PNPM_IGNORED_BUILDS for packages that have lifecycle scripts.  allowBuilds: Schema.optional(Schema.Record(Schema.String, Schema.Boolean)),  patchedDependencies: Schema.optional(Schema.Record(Schema.String, Schema.String)),  overrides: Schema.optional(Schema.Record(Schema.String, Schema.String)),  nodeLinker: Schema.optional(Schema.Literals(["hoisted"])),});type StageWorkspaceConfig = typeof StageWorkspaceConfig.Type; const RepoRoot = Effect.service(Path.Path).pipe(  Effect.flatMap((path) => path.fromFileUrl(new URL("..", import.meta.url))),);const encodeJsonString = Schema.encodeEffect(Schema.fromJsonString(Schema.Unknown));const decodeWorkspaceConfig = Schema.decodeEffect(fromYaml(WorkspaceConfig));const decodeNodePtyManifest = Schema.decodeUnknownEffect(  Schema.fromJsonString(Schema.Struct({ version: Schema.String })),);const encodeStageWorkspaceConfig = Schema.encodeEffect(fromYaml(StageWorkspaceConfig)); const readWorkspaceConfig = Effect.fn("readWorkspaceConfig")(function* () {  const fs = yield* FileSystem.FileSystem;  const path = yield* Path.Path;  const repoRoot = yield* RepoRoot;  const workspaceYaml = yield* fs.readFileString(path.join(repoRoot, "pnpm-workspace.yaml"));  return yield* decodeWorkspaceConfig(workspaceYaml);}); interface DesktopBuildIconAssets {  readonly macIconPng: string;  readonly linuxIconPng: string;  readonly windowsIconIco: string;} interface PlatformConfig {  readonly cliFlag: "--mac" | "--linux" | "--win";  readonly defaultTarget: string;  readonly archChoices: ReadonlyArray<typeof BuildArch.Type>;} export function resolveResourceMonitorRustTargets(  platform: typeof BuildPlatform.Type,  arch: typeof BuildArch.Type,): ReadonlyArray<string> {  if (platform === "mac") {    if (arch === "universal") {      return ["aarch64-apple-darwin", "x86_64-apple-darwin"];    }    return [arch === "arm64" ? "aarch64-apple-darwin" : "x86_64-apple-darwin"];  }  if (platform === "linux") {    return [arch === "arm64" ? "aarch64-unknown-linux-gnu" : "x86_64-unknown-linux-gnu"];  }  return [arch === "arm64" ? "aarch64-pc-windows-msvc" : "x86_64-pc-windows-msvc"];} export function resourceMonitorExecutableName(platform: typeof BuildPlatform.Type): string {  return platform === "win" ? "t3-resource-monitor.exe" : "t3-resource-monitor";} const PLATFORM_CONFIG: Record<typeof BuildPlatform.Type, PlatformConfig> = {  mac: {    cliFlag: "--mac",    defaultTarget: "dmg",    archChoices: ["arm64", "x64", "universal"],  },  linux: {    cliFlag: "--linux",    defaultTarget: "AppImage",    archChoices: ["x64", "arm64"],  },  win: {    cliFlag: "--win",    defaultTarget: "nsis",    archChoices: ["x64", "arm64"],  },}; interface BuildCliInput {  readonly platform: Option.Option<typeof BuildPlatform.Type>;  readonly target: Option.Option<string>;  readonly arch: Option.Option<typeof BuildArch.Type>;  readonly buildVersion: Option.Option<string>;  readonly outputDir: Option.Option<string>;  readonly skipBuild: Option.Option<boolean>;  readonly keepStage: Option.Option<boolean>;  readonly signed: Option.Option<boolean>;  readonly verbose: Option.Option<boolean>;  readonly mockUpdates: Option.Option<boolean>;  readonly mockUpdateServerPort: Option.Option<number>;  readonly wslPrebuild: Option.Option<string>;} function detectHostBuildPlatform(hostPlatform: string): typeof BuildPlatform.Type | undefined {  if (hostPlatform === "darwin") return "mac";  if (hostPlatform === "linux") return "linux";  if (hostPlatform === "win32") return "win";  return undefined;} const getDefaultArch = Effect.fn("getDefaultArch")(function* (platform: typeof BuildPlatform.Type) {  const config = PLATFORM_CONFIG[platform];  if (!config) {    return "x64";  }   return yield* getDefaultBuildArch(platform, config);}); export class MacPasskeySigningConfigurationResolutionError extends Schema.TaggedError<MacPasskeySigningConfigurationResolutionError>()(  "MacPasskeySigningConfigurationResolutionError",  {    cause: Schema.Defect(),  },) {  static fromCause(    cause: unknown,  ): MacPasskeySigningConfigurationError | MacPasskeySigningConfigurationResolutionError {    return isMacPasskeySigningConfigurationError(cause)      ? cause      : new MacPasskeySigningConfigurationResolutionError({ cause });  }   override get message(): string {    return "Failed to resolve macOS passkey signing configuration.";  }} export class KeyringNativePackageMissingError extends Schema.TaggedError<KeyringNativePackageMissingError>()(  "KeyringNativePackageMissingError",  {    packageName: Schema.String,    binaryFileName: Schema.String,    packageEntryPath: Schema.String,    platform: BuildPlatform,    arch: BuildArch,    cause: Schema.Defect(),  },) {  override get message(): string {    return `Keyring native package is missing: ${this.packageName}`;  }} export class ClerkPasskeyNativePackageMissingError extends Schema.TaggedError<ClerkPasskeyNativePackageMissingError>()(  "ClerkPasskeyNativePackageMissingError",  {    packageName: Schema.String,    binaryFileName: Schema.String,    packageEntryPath: Schema.String,    platform: BuildPlatform,    arch: BuildArch,    cause: Schema.Defect(),  },) {  override get message(): string {    return `Clerk passkey native package is missing: ${this.packageName}`;  }} export class UnsupportedHostBuildPlatformError extends Schema.TaggedError<UnsupportedHostBuildPlatformError>()(  "UnsupportedHostBuildPlatformError",  {    hostPlatform: Schema.String,  },) {  override get message(): string {    return `Unsupported host platform '${this.hostPlatform}'.`;  }} export class UnsupportedDesktopBuildArchitectureError extends Schema.TaggedError<UnsupportedDesktopBuildArchitectureError>()(  "UnsupportedDesktopBuildArchitectureError",  {    platform: BuildPlatform,    arch: BuildArch,    supportedArchitectures: Schema.Array(BuildArch),  },) {  override get message(): string {    return `Unsupported architecture '${this.arch}' for ${this.platform}.`;  }} const InvalidMockUpdateServerPortReason = Schema.Literals([  "not-numeric",  "not-integer",  "out-of-range",]); export class InvalidMockUpdateServerPortError extends Schema.TaggedError<InvalidMockUpdateServerPortError>()(  "InvalidMockUpdateServerPortError",  {    reason: InvalidMockUpdateServerPortReason,    inputLength: Schema.Int.check(Schema.isGreaterThanOrEqualTo(0)),    cause: Schema.Defect(),  },) {  override get message(): string {    return "Invalid mock update server port.";  }   static fromConfigValue(configuredPort: string, cause: unknown) {    return new InvalidMockUpdateServerPortError({      reason: invalidMockUpdateServerPortReason(configuredPort),      inputLength: configuredPort.length,      cause,    });  }} export class BuildCommandFailedError extends Schema.TaggedError<BuildCommandFailedError>()(  "BuildCommandFailedError",  {    command: Schema.String,    exitCode: Schema.Int,    stdoutTail: Schema.optionalKey(Schema.String),    stderrTail: Schema.optionalKey(Schema.String),  },) {  override get message(): string {    const outputSections = [      `Command: ${this.command}`,      formatOutputSection("stdout", this.stdoutTail ?? ""),      formatOutputSection("stderr", this.stderrTail ?? ""),    ].filter((section): section is string => section !== undefined);    const outputSuffix = outputSections.length > 0 ? `\n\n${outputSections.join("\n\n")}` : "";    return `Command exited with non-zero exit code (${this.exitCode})${outputSuffix}`;  }} export const LINUX_DESKTOP_BUILD_PREREQUISITES = [  { id: "cargo", description: "Rust compiler and Cargo", packages: ["cargo", "rustc"] },  { id: "rust-target", description: "Requested Rust standard library", packages: [] },  { id: "cc", description: "C/C++ build toolchain", packages: ["build-essential"] },  { id: "make", description: "Make", packages: ["build-essential"] },  {    id: "libsecret",    description: "libsecret development headers and pkg-config",    packages: ["libsecret-1-dev", "pkg-config"],  },  { id: "imagemagick", description: "ImageMagick", packages: ["imagemagick"] },] as const; export class LinuxDesktopBuildPrerequisitesMissingError extends Schema.TaggedError<LinuxDesktopBuildPrerequisitesMissingError>()(  "LinuxDesktopBuildPrerequisitesMissingError",  {    missing: Schema.Array(Schema.String),    rustTarget: Schema.String,  },) {  override get message(): string {    const missingRequirements = LINUX_DESKTOP_BUILD_PREREQUISITES.filter((requirement) =>      this.missing.includes(requirement.id),    );    const details = missingRequirements      .map((requirement) =>        requirement.packages.length > 0          ? `  - ${requirement.description} (${requirement.packages.join(", ")})`          : `  - ${requirement.description}`,      )      .join("\n");    const packages = [      ...new Set(missingRequirements.flatMap((requirement) => requirement.packages)),    ];    return [      "Linux desktop build prerequisites are missing:",      details,      "",      ...(packages.length > 0        ? ["On Ubuntu/Debian, install them with:", `  sudo apt-get install ${packages.join(" ")}`]        : []),      ...(this.missing.includes("rust-target")        ? ["Add the requested Rust target with:", `  rustup target add ${this.rustTarget}`]        : []),      "",      "For other distributions, see docs/operations/development.md#linux-appimage-prerequisites.",      "Then rerun `vp run dist:desktop:linux`.",    ].join("\n");  }} const MAC_DESKTOP_BUILD_PREREQUISITES = [  { id: "rust", description: "Rust/Cargo and the requested Rust target" },  { id: "clang", description: "Xcode Command Line Tools (clang)" },  { id: "make", description: "Xcode Command Line Tools (make)" },  { id: "sips", description: "macOS image tool (sips)" },  { id: "iconutil", description: "macOS icon tool (iconutil)" },  { id: "lipo", description: "Xcode universal-binary tool (lipo)" },] as const; export class MacDesktopBuildPrerequisitesMissingError extends Schema.TaggedError<MacDesktopBuildPrerequisitesMissingError>()(  "MacDesktopBuildPrerequisitesMissingError",  {    missing: Schema.Array(Schema.String),    rustTargets: Schema.Array(Schema.String),  },) {  override get message(): string {    const details = MAC_DESKTOP_BUILD_PREREQUISITES.filter((requirement) =>      this.missing.includes(requirement.id),    )      .map((requirement) => `  - ${requirement.description}`)      .join("\n");    return [      "macOS desktop build prerequisites are missing:",      details,      "",      "Install Apple's build tools with:",      "  xcode-select --install",      "Install Rust from https://rustup.rs, then add the requested target(s):",      `  rustup target add ${this.rustTargets.join(" ")}`,      "",      "Then rerun the desktop artifact command.",    ].join("\n");  }} const WINDOWS_DESKTOP_BUILD_PREREQUISITES = [  { id: "rust", description: "Rust/Cargo and the requested MSVC Rust target" },  { id: "python", description: "Python 3 for node-gyp" },  {    id: "msvc",    description: "Visual Studio Build Tools with C++, Windows SDK, and Spectre libraries",  },  { id: "tar", description: "tar for the bundled WSL runtime" },] as const; export class WindowsDesktopBuildPrerequisitesMissingError extends Schema.TaggedError<WindowsDesktopBuildPrerequisitesMissingError>()(  "WindowsDesktopBuildPrerequisitesMissingError",  {    missing: Schema.Array(Schema.String),    rustTarget: Schema.String,  },) {  override get message(): string {    const details = WINDOWS_DESKTOP_BUILD_PREREQUISITES.filter((requirement) =>      this.missing.includes(requirement.id),    )      .map((requirement) => `  - ${requirement.description}`)      .join("\n");    return [      "Windows desktop build prerequisites are missing:",      details,      "",      "Install Rust from https://rustup.rs and add the requested target:",      `  rustup target add ${this.rustTarget}`,      "Install Python 3 and the Visual Studio Build Tools components listed in",      "docs/operations/development.md#windows-installer-prerequisites.",      "",      "Then rerun the desktop artifact command.",    ].join("\n");  }} export class ResourceMonitorBuildOutputMissingError extends Schema.TaggedError<ResourceMonitorBuildOutputMissingError>()(  "ResourceMonitorBuildOutputMissingError",  {    binaryPath: Schema.String,    rustTarget: Schema.String,    platform: BuildPlatform,    arch: BuildArch,  },) {  override get message(): string {    return `Resource monitor build for ${this.rustTarget} did not produce ${this.binaryPath}.`;  }} const desktopIconPlatformNames = {  mac: "macOS",  linux: "Linux",  win: "Windows",} satisfies Record<typeof BuildPlatform.Type, string>; export class LinuxBrowserSecretHostError extends Schema.TaggedError<LinuxBrowserSecretHostError>()(  "LinuxBrowserSecretHostError",  { hostPlatform: Schema.String },) {  override get message(): string {    return `Linux desktop builds must run on a Linux host: the browser secret helper links against libsecret and cannot be built on '${this.hostPlatform}'.`;  }} export class DesktopIconSourceMissingError extends Schema.TaggedError<DesktopIconSourceMissingError>()(  "DesktopIconSourceMissingError",  {    platform: BuildPlatform,    sourcePath: Schema.String,  },) {  override get message(): string {    return `Desktop ${desktopIconPlatformNames[this.platform]} icon source is missing at ${this.sourcePath}`;  }} export class DesktopDmgBackgroundSourceMissingError extends Schema.TaggedError<DesktopDmgBackgroundSourceMissingError>()(  "DesktopDmgBackgroundSourceMissingError",  {    channel: Schema.Literals(["latest", "nightly"]),    sourcePath: Schema.String,  },) {  override get message(): string {    return `Desktop ${this.channel} DMG background source is missing at ${this.sourcePath}`;  }} export class BundledClientAssetsMissingError extends Schema.TaggedError<BundledClientAssetsMissingError>()(  "BundledClientAssetsMissingError",  {    indexPath: Schema.String,    missingFiles: Schema.Array(Schema.String),  },) {  override get message(): string {    const preview = this.missingFiles.slice(0, 6).join(", ");    const suffix = this.missingFiles.length > 6 ? ` (+${this.missingFiles.length - 6} more)` : "";    return `Bundled client references missing files in ${this.indexPath}: ${preview}${suffix}. Rebuild web/server artifacts.`;  }} export class UnsupportedDesktopBuildPlatformError extends Schema.TaggedError<UnsupportedDesktopBuildPlatformError>()(  "UnsupportedDesktopBuildPlatformError",  {

Lines 1–500 of 4059.