From ee76fdb57871ff94a22ba07415930cc296284840 Mon Sep 17 00:00:00 2001
From: hututuQQQ <245432843+hututuQQQ@users.noreply.github.com>
Date: Thu, 6 Aug 2026 07:38:16 +0800
Subject: [PATCH] feat: add macOS ARM64 desktop release
---
apps/desktop/package.json | 2 +-
apps/server/package.json | 2 +-
apps/web/package.json | 2 +-
packages/contracts/package.json | 2 +-
pnpm-workspace.yaml | 4 +-
scripts/build-desktop-artifact.test.ts | 88 ++++++++++-
scripts/build-desktop-artifact.ts | 180 +++++++++++++++++++---
scripts/desktop-release-evidence.test.ts | 71 +++++++++
scripts/desktop-release-evidence.ts | 187 +++++++++++++++++++++++
9 files changed, 502 insertions(+), 36 deletions(-)
create mode 100644 scripts/desktop-release-evidence.test.ts
create mode 100644 scripts/desktop-release-evidence.ts
diff --git a/apps/desktop/package.json b/apps/desktop/package.json
index 2725e7ade..c0e740d71 100644
--- a/apps/desktop/package.json
+++ b/apps/desktop/package.json
@@ -1,6 +1,6 @@
{
"name": "@t3tools/desktop",
- "version": "0.1.5",
+ "version": "0.1.6",
"private": true,
"type": "module",
"main": "dist-electron/main.cjs",
diff --git a/apps/server/package.json b/apps/server/package.json
index bca383219..62ac68f62 100644
--- a/apps/server/package.json
+++ b/apps/server/package.json
@@ -1,6 +1,6 @@
{
"name": "t3",
- "version": "0.1.5",
+ "version": "0.1.6",
"private": true,
"license": "MIT",
"bin": {
diff --git a/apps/web/package.json b/apps/web/package.json
index 5f2b5affa..91bb74474 100644
--- a/apps/web/package.json
+++ b/apps/web/package.json
@@ -1,6 +1,6 @@
{
"name": "@t3tools/web",
- "version": "0.1.5",
+ "version": "0.1.6",
"private": true,
"type": "module",
"scripts": {
diff --git a/packages/contracts/package.json b/packages/contracts/package.json
index cf7329118..135a0fdb2 100644
--- a/packages/contracts/package.json
+++ b/packages/contracts/package.json
@@ -1,6 +1,6 @@
{
"name": "@t3tools/contracts",
- "version": "0.1.5",
+ "version": "0.1.6",
"private": true,
"files": [
"dist"
diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml
index 42529f56e..18155d8e5 100644
--- a/pnpm-workspace.yaml
+++ b/pnpm-workspace.yaml
@@ -145,6 +145,6 @@ peerDependencyRules:
vite: "*"
supportedArchitectures:
- cpu: [current, x64]
+ cpu: [current, x64, arm64]
libc: [current, glibc]
- os: [current, linux]
+ os: [current, linux, darwin]
diff --git a/scripts/build-desktop-artifact.test.ts b/scripts/build-desktop-artifact.test.ts
index 146ad986a..89b49a075 100644
--- a/scripts/build-desktop-artifact.test.ts
+++ b/scripts/build-desktop-artifact.test.ts
@@ -17,6 +17,7 @@ import {
createStagePatchedDependencies,
createBuildConfig,
DESKTOP_ASAR_UNPACK,
+ IncompleteMacSigningCredentialsError,
InvalidMacPasskeyRpDomainError,
InvalidMacPasskeyPublishableKeyError,
InvalidMockUpdateServerPortError,
@@ -25,8 +26,10 @@ import {
MacPasskeySigningConfigurationResolutionError,
MissingMacPasskeyProvisioningProfileError,
renderMacPasskeyEntitlements,
+ renderMacSigningEntitlements,
resolveClerkPasskeyNativeArtifacts,
resolveMacPasskeySigningConfiguration,
+ resolveMacSigningCredentialState,
resolveDesktopRuntimeDependencies,
resolveFffNativeDependencies,
resolveBuildOptions,
@@ -405,6 +408,32 @@ it.layer(NodeServices.layer)("build-desktop-artifact", (it) => {
assert.include(entitlements, "com.apple.security.cs.allow-jit");
});
+ it("enables macOS signing only for the complete fixed credential interface", () => {
+ assert.deepStrictEqual(resolveMacSigningCredentialState({}), { mode: "unsigned" });
+ assert.deepStrictEqual(
+ resolveMacSigningCredentialState({
+ CSC_LINK: "certificate",
+ CSC_KEY_PASSWORD: "password",
+ APPLE_API_KEY: "private-key",
+ APPLE_API_KEY_ID: "KEY123",
+ APPLE_API_ISSUER: "issuer",
+ APPLE_TEAM_ID: "abc1234567",
+ }),
+ { mode: "signed", teamId: "ABC1234567" },
+ );
+ assert.throws(
+ () => resolveMacSigningCredentialState({ CSC_LINK: "certificate" }),
+ IncompleteMacSigningCredentialsError,
+ );
+ });
+
+ it("renders hardened-runtime JIT entitlements without requiring Clerk provisioning", () => {
+ const entitlements = renderMacSigningEntitlements("ABC1234567");
+ assert.include(entitlements, "com.apple.security.cs.allow-jit");
+ assert.notInclude(entitlements, "com.apple.application-identifier");
+ assert.notInclude(entitlements, "com.apple.developer.associated-domains");
+ });
+
it("rejects incomplete macOS passkey signing configuration", () => {
const captureError = (env: Readonly>) => {
try {
@@ -495,17 +524,32 @@ it.layer(NodeServices.layer)("build-desktop-artifact", (it) => {
true,
false,
undefined,
- ["node_modules/tr46/lib/.gitkeep"],
+ [
+ "bin/node",
+ "bin/sigma-exec",
+ "node_modules/native/addon.node",
+ "node_modules/tr46/lib/.gitkeep",
+ ],
{
entitlementsPath: "/tmp/entitlements.mac.plist",
provisioningProfilePath: "/tmp/sigma-code.provisionprofile",
},
+ ["bin/node", "bin/sigma-exec", "node_modules/native/addon.node"],
);
const mac = config.mac as Record;
assert.equal(config.appId, "io.github.hututuqqq.sigmacode");
assert.equal(mac.entitlements, "/tmp/entitlements.mac.plist");
+ assert.equal(mac.entitlementsInherit, "/tmp/entitlements.mac.plist");
assert.equal(mac.provisioningProfile, "/tmp/sigma-code.provisionprofile");
+ assert.equal(mac.minimumSystemVersion, "13.5");
+ assert.equal(mac.hardenedRuntime, true);
+ assert.equal(mac.notarize, true);
+ assert.deepStrictEqual(mac.binaries, [
+ "Contents/Resources/sigma-runtime/bin/node",
+ "Contents/Resources/sigma-runtime/bin/sigma-exec",
+ "Contents/Resources/sigma-runtime/node_modules/native/addon.node",
+ ]);
assert.equal(mac.executableName, "sigma-code");
assert.deepStrictEqual(config.protocols, [
{ name: "Sigma Code", schemes: ["sigmacode", "sigmacode-dev"] },
@@ -513,6 +557,12 @@ it.layer(NodeServices.layer)("build-desktop-artifact", (it) => {
assert.deepStrictEqual(config.extraResources, [
{ from: "legal", to: "legal" },
{ from: ".", to: ".", filter: ["sigma-runtime/**/*"] },
+ { from: "sigma-runtime/bin/node", to: "sigma-runtime/bin/node" },
+ { from: "sigma-runtime/bin/sigma-exec", to: "sigma-runtime/bin/sigma-exec" },
+ {
+ from: "sigma-runtime/node_modules/native/addon.node",
+ to: "sigma-runtime/node_modules/native/addon.node",
+ },
{
from: "sigma-runtime/node_modules/tr46/lib/.gitkeep",
to: "sigma-runtime/node_modules/tr46/lib/.gitkeep",
@@ -521,6 +571,26 @@ it.layer(NodeServices.layer)("build-desktop-artifact", (it) => {
}).pipe(Effect.provide(ConfigProvider.layer(ConfigProvider.fromEnv({ env: {} })))),
);
+ it.effect("forces unsigned macOS previews to disable identity, hardening, and notarization", () =>
+ Effect.gen(function* () {
+ const config = yield* createBuildConfig(
+ "mac",
+ "dmg",
+ "1.2.3",
+ false,
+ false,
+ undefined,
+ [],
+ undefined,
+ );
+ const mac = config.mac as Record;
+ assert.strictEqual(mac.identity, null);
+ assert.equal(mac.hardenedRuntime, false);
+ assert.equal(mac.notarize, false);
+ assert.equal(mac.minimumSystemVersion, "13.5");
+ }).pipe(Effect.provide(ConfigProvider.layer(ConfigProvider.fromEnv({ env: {} })))),
+ );
+
it.effect("stages the verified Sigma Runtime bundle as an opaque resource", () =>
Effect.gen(function* () {
const fileSystem = yield* FileSystem.FileSystem;
@@ -531,7 +601,10 @@ it.layer(NodeServices.layer)("build-desktop-artifact", (it) => {
const runtimePath = path.join(tempDir, "runtime");
const stageAppDir = path.join(tempDir, "app");
const requiredFiles = [
- "bin/sigma.cmd",
+ "bin/sigma",
+ "bin/node",
+ "bin/sigma-exec",
+ "node_modules/native/addon.node",
"LICENSE",
"integrity-manifest.json",
"package-metadata.json",
@@ -556,12 +629,17 @@ it.layer(NodeServices.layer)("build-desktop-artifact", (it) => {
integrityManifest,
);
- const exactCopyPaths = yield* stageBundledSigmaRuntime({
+ const stagedPaths = yield* stageBundledSigmaRuntime({
stageAppDir,
- platform: "win",
+ platform: "mac",
runtimePath,
});
- assert.deepStrictEqual(exactCopyPaths, ["node_modules/tr46/lib/.gitkeep"]);
+ assert.deepStrictEqual(stagedPaths.exactCopyPaths, ["node_modules/tr46/lib/.gitkeep"]);
+ assert.deepStrictEqual(stagedPaths.signingBinaryPaths, [
+ "bin/node",
+ "bin/sigma-exec",
+ "node_modules/native/addon.node",
+ ]);
for (const relativePath of requiredFiles) {
assert.isTrue(
diff --git a/scripts/build-desktop-artifact.ts b/scripts/build-desktop-artifact.ts
index 82f1db378..b434c8bf9 100644
--- a/scripts/build-desktop-artifact.ts
+++ b/scripts/build-desktop-artifact.ts
@@ -3,7 +3,7 @@
import * as NodeModule from "node:module";
import { fromYaml } from "@t3tools/shared/schemaYaml";
-import { HostProcessPlatform } from "@t3tools/shared/hostProcess";
+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" };
@@ -37,7 +37,16 @@ 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 = "io.github.hututuqqq.sigmacode";
+export const MACOS_MINIMUM_SYSTEM_VERSION = "13.5";
const APPLE_TEAM_ID_PATTERN = /^[A-Z0-9]{10}$/u;
+export const MAC_SIGNING_CREDENTIAL_NAMES = [
+ "CSC_LINK",
+ "CSC_KEY_PASSWORD",
+ "APPLE_API_KEY",
+ "APPLE_API_KEY_ID",
+ "APPLE_API_ISSUER",
+ "APPLE_TEAM_ID",
+] as const;
const BuildPlatform = Schema.Literals(["mac", "linux", "win"]);
const BuildArch = Schema.Literals(["arm64", "x64", "universal"]);
@@ -292,6 +301,18 @@ export class UnsupportedDesktopBuildPlatformError extends Schema.TaggedErrorClas
}
}
+export class NativeMacArm64BuildRequiredError extends Schema.TaggedErrorClass()(
+ "NativeMacArm64BuildRequiredError",
+ {
+ hostPlatform: Schema.String,
+ hostArch: Schema.String,
+ },
+) {
+ override get message(): string {
+ return `macOS ARM64 desktop artifacts require a native darwin/arm64 host, got ${this.hostPlatform}/${this.hostArch}.`;
+ }
+}
+
const dependencyResolutionDescriptions = {
"server-production": "production dependencies",
"workspace-overrides": "overrides",
@@ -656,7 +677,7 @@ export class InvalidAppleTeamIdError extends Schema.TaggedErrorClass()(
+ "IncompleteMacSigningCredentialsError",
+ {
+ configuredNames: Schema.Array(Schema.String),
+ missingNames: Schema.Array(Schema.String),
+ },
+) {
+ override get message(): string {
+ return `macOS signing credentials are partially configured; missing: ${this.missingNames.join(", ")}.`;
+ }
+}
+
+export type MacSigningCredentialState =
+ | { readonly mode: "unsigned" }
+ | { readonly mode: "signed"; readonly teamId: string };
+
+export function resolveMacSigningCredentialState(
+ env: Readonly>,
+): MacSigningCredentialState {
+ const configuredNames = MAC_SIGNING_CREDENTIAL_NAMES.filter(
+ (name) => (env[name] ?? "").trim().length > 0,
+ );
+ if (configuredNames.length === 0) return { mode: "unsigned" };
+ const missingNames = MAC_SIGNING_CREDENTIAL_NAMES.filter(
+ (name) => !configuredNames.includes(name),
+ );
+ if (missingNames.length > 0) {
+ throw new IncompleteMacSigningCredentialsError({
+ configuredNames: [...configuredNames],
+ missingNames: [...missingNames],
+ });
+ }
+ const teamId = env.APPLE_TEAM_ID!.trim().toUpperCase();
+ if (!APPLE_TEAM_ID_PATTERN.test(teamId)) {
+ throw new InvalidAppleTeamIdError({ teamId });
+ }
+ return { mode: "signed", teamId };
+}
+
export class InvalidMacPasskeyPublishableKeyError extends Schema.TaggedErrorClass()(
"InvalidMacPasskeyPublishableKeyError",
{
@@ -753,7 +813,7 @@ function normalizePasskeyRpDomain(value: string): string {
export function resolveMacPasskeySigningConfiguration(
env: Readonly>,
): MacPasskeySigningConfiguration {
- const teamId = env.SIGMACODE_APPLE_TEAM_ID?.trim().toUpperCase() ?? "";
+ const teamId = (env.APPLE_TEAM_ID ?? env.SIGMACODE_APPLE_TEAM_ID)?.trim().toUpperCase() ?? "";
if (!APPLE_TEAM_ID_PATTERN.test(teamId)) {
throw new InvalidAppleTeamIdError({ teamId });
}
@@ -833,6 +893,26 @@ ${associatedDomains}
`;
}
+export function renderMacSigningEntitlements(
+ _teamId: string,
+ passkeys?: MacPasskeySigningConfiguration,
+): string {
+ if (passkeys) return renderMacPasskeyEntitlements(passkeys);
+ return `
+
+
+
+ com.apple.security.cs.allow-jit
+
+ com.apple.security.cs.allow-unsigned-executable-memory
+
+ com.apple.security.cs.disable-library-validation
+
+
+
+`;
+}
+
export function resolveFffNativeDependencies(
platform: typeof BuildPlatform.Type,
arch: typeof BuildArch.Type,
@@ -1422,12 +1502,13 @@ export const createBuildConfig = Effect.fn("createBuildConfig")(function* (
mockUpdates: boolean,
mockUpdateServerPort: number | undefined,
sigmaRuntimeExactCopyPaths: ReadonlyArray,
- macPasskeySigning:
+ macSigning:
| {
readonly entitlementsPath: string;
- readonly provisioningProfilePath: string;
+ readonly provisioningProfilePath?: string;
}
| undefined,
+ sigmaRuntimeSigningBinaryPaths: ReadonlyArray = [],
) {
const buildConfig: Record = {
appId: DESKTOP_APP_ID,
@@ -1486,15 +1567,27 @@ export const createBuildConfig = Effect.fn("createBuildConfig")(function* (
}
if (platform === "mac") {
+ const runtimeBinaries = sigmaRuntimeSigningBinaryPaths.map(
+ (relativePath) => `Contents/Resources/sigma-runtime/${relativePath}`,
+ );
buildConfig.mac = {
target: target === "dmg" ? [target, "zip"] : [target],
executableName: "sigma-code",
icon: "icon.icns",
category: "public.app-category.developer-tools",
- ...(macPasskeySigning
+ minimumSystemVersion: MACOS_MINIMUM_SYSTEM_VERSION,
+ hardenedRuntime: signed,
+ notarize: signed,
+ gatekeeperAssess: false,
+ ...(signed ? {} : { identity: null }),
+ ...(signed ? { binaries: runtimeBinaries } : {}),
+ ...(macSigning
? {
- entitlements: macPasskeySigning.entitlementsPath,
- provisioningProfile: macPasskeySigning.provisioningProfilePath,
+ entitlements: macSigning.entitlementsPath,
+ entitlementsInherit: macSigning.entitlementsPath,
+ ...(macSigning.provisioningProfilePath
+ ? { provisioningProfile: macSigning.provisioningProfilePath }
+ : {}),
}
: {}),
};
@@ -1668,14 +1761,24 @@ export const stageBundledSigmaRuntime = Effect.fn("stageBundledSigmaRuntime")(fu
path.join(input.runtimePath, "integrity-manifest.json"),
);
const decodedIntegrityManifest = yield* decodeBundledRuntimeIntegrityManifest(integrityManifest);
- const exactCopyPaths = decodedIntegrityManifest.entries
- .map((entry) => entry.path.replaceAll("\\", "/"))
+ const manifestPaths = decodedIntegrityManifest.entries.map((entry) =>
+ entry.path.replaceAll("\\", "/"),
+ );
+ const exactCopyPaths = manifestPaths
.filter((relativePath) => path.basename(relativePath) === ".gitkeep")
.sort();
+ const signingBinaryPaths = manifestPaths
+ .filter(
+ (relativePath) =>
+ relativePath === "bin/node" ||
+ relativePath === "bin/sigma-exec" ||
+ relativePath.endsWith(".node"),
+ )
+ .sort();
yield* fs.copy(input.runtimePath, path.join(input.stageAppDir, "sigma-runtime"));
yield* Effect.log(`[desktop-artifact] Staged bundled Sigma Runtime from ${input.runtimePath}.`);
- return exactCopyPaths;
+ return { exactCopyPaths, signingBinaryPaths };
});
const buildDesktopArtifact = Effect.fn("buildDesktopArtifact")(function* (
@@ -1685,11 +1788,27 @@ const buildDesktopArtifact = Effect.fn("buildDesktopArtifact")(function* (
const path = yield* Path.Path;
const fs = yield* FileSystem.FileSystem;
const hostPlatform = yield* HostProcessPlatform;
+ const hostArchitecture = yield* HostProcessArchitecture;
const workspaceConfig = yield* readWorkspaceConfig();
const workspaceCatalog = workspaceConfig.catalog ?? {};
const workspaceOverrides = workspaceConfig.overrides ?? {};
const workspacePatchedDependencies = workspaceConfig.patchedDependencies ?? {};
const workspaceAllowBuilds = workspaceConfig.allowBuilds ?? {};
+ if (
+ options.platform === "mac" &&
+ options.arch === "arm64" &&
+ (hostPlatform !== "darwin" || hostArchitecture !== "arm64")
+ ) {
+ return yield* new NativeMacArm64BuildRequiredError({
+ hostPlatform,
+ hostArch: hostArchitecture,
+ });
+ }
+ const repoEnv = loadRepoEnv({ repoRoot });
+ const macSigningCredentialState =
+ options.platform === "mac" ? resolveMacSigningCredentialState(repoEnv) : undefined;
+ const signed =
+ options.platform === "mac" ? macSigningCredentialState?.mode === "signed" : options.signed;
const platformConfig = PLATFORM_CONFIG[options.platform];
if (!platformConfig) {
@@ -1810,7 +1929,7 @@ const buildDesktopArtifact = Effect.fn("buildDesktopArtifact")(function* (
path.join(legalDir, legalResource.targetFileName),
);
}
- const sigmaRuntimeExactCopyPaths = yield* stageBundledSigmaRuntime({
+ const sigmaRuntimePaths = yield* stageBundledSigmaRuntime({
stageAppDir,
platform: options.platform,
runtimePath: options.sigmaRuntime,
@@ -1830,10 +1949,15 @@ const buildDesktopArtifact = Effect.fn("buildDesktopArtifact")(function* (
// electron-builder is filtering out stageResourcesDir directory in the AppImage for production
yield* fs.copy(stageResourcesDir, path.join(stageAppDir, "apps/desktop/prod-resources"));
+ const hasMacPasskeyConfiguration = [
+ "SIGMACODE_MACOS_PROVISIONING_PROFILE",
+ "SIGMACODE_CLERK_PASSKEY_RP_DOMAINS",
+ "SIGMACODE_CLERK_PUBLISHABLE_KEY",
+ ].some((name) => (repoEnv[name] ?? "").trim().length > 0);
const configuredMacPasskeySigning =
- options.platform === "mac" && options.signed
+ options.platform === "mac" && signed && hasMacPasskeyConfiguration
? yield* Effect.try({
- try: () => resolveMacPasskeySigningConfiguration(loadRepoEnv({ repoRoot })),
+ try: () => resolveMacPasskeySigningConfiguration(repoEnv),
catch: MacPasskeySigningConfigurationResolutionError.fromCause,
})
: undefined;
@@ -1846,16 +1970,19 @@ const buildDesktopArtifact = Effect.fn("buildDesktopArtifact")(function* (
),
}
: undefined;
- const macEntitlementsPath = macPasskeySigning
- ? path.join(stageAppDir, "entitlements.mac.plist")
- : undefined;
- if (macPasskeySigning && macEntitlementsPath) {
+ const macEntitlementsPath = signed ? path.join(stageAppDir, "entitlements.mac.plist") : undefined;
+ if (macPasskeySigning) {
if (!(yield* fs.exists(macPasskeySigning.provisioningProfilePath))) {
return yield* new MacProvisioningProfileNotFoundError({
provisioningProfilePath: macPasskeySigning.provisioningProfilePath,
});
}
- yield* fs.writeFileString(macEntitlementsPath, renderMacPasskeyEntitlements(macPasskeySigning));
+ }
+ if (macEntitlementsPath && macSigningCredentialState?.mode === "signed") {
+ yield* fs.writeFileString(
+ macEntitlementsPath,
+ renderMacSigningEntitlements(macSigningCredentialState.teamId, macPasskeySigning),
+ );
}
const stageDependencies = {
@@ -1896,16 +2023,19 @@ const buildDesktopArtifact = Effect.fn("buildDesktopArtifact")(function* (
options.platform,
options.target,
appVersion,
- options.signed,
+ signed,
options.mockUpdates,
options.mockUpdateServerPort,
- sigmaRuntimeExactCopyPaths,
- macPasskeySigning && macEntitlementsPath
+ sigmaRuntimePaths.exactCopyPaths,
+ macEntitlementsPath
? {
entitlementsPath: macEntitlementsPath,
- provisioningProfilePath: macPasskeySigning.provisioningProfilePath,
+ ...(macPasskeySigning
+ ? { provisioningProfilePath: macPasskeySigning.provisioningProfilePath }
+ : {}),
}
: undefined,
+ sigmaRuntimePaths.signingBinaryPaths,
),
dependencies: stageDependencies,
devDependencies: {
@@ -1965,7 +2095,7 @@ const buildDesktopArtifact = Effect.fn("buildDesktopArtifact")(function* (
delete buildEnv[key];
}
}
- if (!options.signed) {
+ if (!signed) {
buildEnv.CSC_IDENTITY_AUTO_DISCOVERY = "false";
delete buildEnv.CSC_LINK;
delete buildEnv.CSC_KEY_PASSWORD;
@@ -2092,7 +2222,7 @@ const buildDesktopArtifactCli = Command.make("build-desktop-artifact", {
),
signed: Flag.boolean("signed").pipe(
Flag.withDescription(
- "Enable signing/notarization discovery; Windows uses Azure Trusted Signing (env: SIGMACODE_DESKTOP_SIGNED).",
+ "Enable Windows Azure Trusted Signing. macOS signing is enabled only by the complete Apple credential set (env: SIGMACODE_DESKTOP_SIGNED).",
),
Flag.optional,
),
diff --git a/scripts/desktop-release-evidence.test.ts b/scripts/desktop-release-evidence.test.ts
new file mode 100644
index 000000000..e253a5113
--- /dev/null
+++ b/scripts/desktop-release-evidence.test.ts
@@ -0,0 +1,71 @@
+// @effect-diagnostics nodeBuiltinImport:off
+import { assert, it } from "@effect/vitest";
+import * as NodeFSP from "node:fs/promises";
+import * as NodeOS from "node:os";
+import * as NodePath from "node:path";
+
+import { writeDesktopReleaseEvidence } from "./desktop-release-evidence.ts";
+
+it("writes checksum, runtime provenance, and unsigned preview status", async () => {
+ const root = await NodeFSP.mkdtemp(
+ NodePath.join(NodeOS.tmpdir(), "sigma-desktop-evidence-test-"),
+ );
+ const artifactPath = NodePath.join(root, "Sigma-Code-0.1.6-arm64.dmg");
+ const runtimeProvenancePath = NodePath.join(root, "agent-cli-darwin-arm64.provenance.json");
+ await NodeFSP.writeFile(artifactPath, "dmg fixture", "utf8");
+ await NodeFSP.writeFile(
+ runtimeProvenancePath,
+ '{"predicate":{"buildDefinition":{"externalParameters":{"version":"0.1.6","targetPlatform":"darwin","targetArch":"arm64"}}}}\n',
+ "utf8",
+ );
+
+ const result = await writeDesktopReleaseEvidence({
+ artifactPath,
+ runtimeProvenancePath,
+ platform: "darwin",
+ arch: "arm64",
+ version: "0.1.6",
+ signingMode: "unsigned",
+ codesign: "not-signed",
+ gatekeeper: "not-applicable",
+ stapler: "not-applicable",
+ });
+ const provenance = JSON.parse(await NodeFSP.readFile(result.provenancePath, "utf8"));
+ const signing = JSON.parse(await NodeFSP.readFile(result.signingPath, "utf8"));
+ assert.equal(provenance.product.minimumSystemVersion, "13.5");
+ assert.equal(provenance.runtime.target, "darwin-arm64");
+ assert.equal(signing.mode, "unsigned");
+ assert.isFalse(signing.hardenedRuntime);
+});
+
+it("rejects signed evidence unless every notarization gate passed", async () => {
+ const root = await NodeFSP.mkdtemp(
+ NodePath.join(NodeOS.tmpdir(), "sigma-desktop-evidence-signed-test-"),
+ );
+ const artifactPath = NodePath.join(root, "Sigma-Code-0.1.6-arm64.dmg");
+ const runtimeProvenancePath = NodePath.join(root, "runtime.json");
+ await NodeFSP.writeFile(artifactPath, "dmg fixture", "utf8");
+ await NodeFSP.writeFile(
+ runtimeProvenancePath,
+ '{"predicate":{"buildDefinition":{"externalParameters":{"version":"0.1.6","targetPlatform":"darwin","targetArch":"arm64"}}}}\n',
+ "utf8",
+ );
+ let failure: unknown;
+ try {
+ await writeDesktopReleaseEvidence({
+ artifactPath,
+ runtimeProvenancePath,
+ platform: "darwin",
+ arch: "arm64",
+ version: "0.1.6",
+ signingMode: "signed",
+ codesign: "pass",
+ gatekeeper: "pass",
+ stapler: "not-applicable",
+ });
+ } catch (error) {
+ failure = error;
+ }
+ assert.instanceOf(failure, Error);
+ assert.match((failure as Error).message, /stapler=pass/u);
+});
diff --git a/scripts/desktop-release-evidence.ts b/scripts/desktop-release-evidence.ts
new file mode 100644
index 000000000..b54bfcfad
--- /dev/null
+++ b/scripts/desktop-release-evidence.ts
@@ -0,0 +1,187 @@
+#!/usr/bin/env node
+// @effect-diagnostics nodeBuiltinImport:off
+
+import * as NodeCrypto from "node:crypto";
+import * as NodeFSP from "node:fs/promises";
+import * as NodePath from "node:path";
+
+const APP_ID = "io.github.hututuqqq.sigmacode";
+const MACOS_MINIMUM_SYSTEM_VERSION = "13.5";
+
+type Platform = "darwin" | "win32";
+type Arch = "arm64" | "x64";
+type SigningMode = "signed" | "unsigned";
+type Verification = "pass" | "not-signed" | "not-applicable";
+
+export interface DesktopReleaseEvidenceOptions {
+ readonly artifactPath: string;
+ readonly runtimeProvenancePath: string;
+ readonly platform: Platform;
+ readonly arch: Arch;
+ readonly version: string;
+ readonly signingMode: SigningMode;
+ readonly codesign: Verification;
+ readonly gatekeeper: Verification;
+ readonly stapler: Verification;
+}
+
+async function sha256(filePath: string): Promise {
+ return NodeCrypto.createHash("sha256")
+ .update(await NodeFSP.readFile(filePath))
+ .digest("hex");
+}
+
+function validateOptions(options: DesktopReleaseEvidenceOptions): void {
+ if (!(["darwin", "win32"] as const).includes(options.platform)) {
+ throw new Error(`Unsupported desktop evidence platform: ${options.platform}`);
+ }
+ if (!(["arm64", "x64"] as const).includes(options.arch)) {
+ throw new Error(`Unsupported desktop evidence architecture: ${options.arch}`);
+ }
+ if (!(["signed", "unsigned"] as const).includes(options.signingMode)) {
+ throw new Error(`Unsupported desktop signing mode: ${options.signingMode}`);
+ }
+ if (!/^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/u.test(options.version)) {
+ throw new Error(`Invalid desktop release version: ${options.version}`);
+ }
+ const extension = options.platform === "darwin" ? "dmg" : "exe";
+ const expectedName = `Sigma-Code-${options.version}-${options.arch}.${extension}`;
+ if (NodePath.basename(options.artifactPath) !== expectedName) {
+ throw new Error(`Desktop artifact must be named ${expectedName}.`);
+ }
+ if (options.platform === "darwin" && options.arch !== "arm64") {
+ throw new Error("This release supports only macOS ARM64.");
+ }
+ const expected =
+ options.signingMode === "signed"
+ ? { codesign: "pass", gatekeeper: "pass", stapler: "pass" }
+ : { codesign: "not-signed", gatekeeper: "not-applicable", stapler: "not-applicable" };
+ for (const key of ["codesign", "gatekeeper", "stapler"] as const) {
+ if (options[key] !== expected[key]) {
+ throw new Error(`${options.signingMode} evidence requires ${key}=${expected[key]}.`);
+ }
+ }
+}
+
+export async function writeDesktopReleaseEvidence(options: DesktopReleaseEvidenceOptions): Promise<{
+ readonly checksumPath: string;
+ readonly provenancePath: string;
+ readonly signingPath: string;
+}> {
+ validateOptions(options);
+ const runtimeProvenanceRaw = await NodeFSP.readFile(options.runtimeProvenancePath, "utf8");
+ const runtimeProvenance = JSON.parse(runtimeProvenanceRaw) as Record;
+ const runtimeParameters = (
+ runtimeProvenance.predicate as
+ | { readonly buildDefinition?: { readonly externalParameters?: unknown } }
+ | undefined
+ )?.buildDefinition?.externalParameters as
+ | {
+ readonly version?: unknown;
+ readonly targetPlatform?: unknown;
+ readonly targetArch?: unknown;
+ }
+ | undefined;
+ if (
+ runtimeParameters?.version !== options.version ||
+ runtimeParameters.targetPlatform !== options.platform ||
+ runtimeParameters.targetArch !== options.arch
+ ) {
+ throw new Error(
+ `Runtime provenance does not match ${options.version}/${options.platform}/${options.arch}.`,
+ );
+ }
+ const artifactSha256 = await sha256(options.artifactPath);
+ const artifactName = NodePath.basename(options.artifactPath);
+ const checksumPath = `${options.artifactPath}.sha256`;
+ const provenancePath = `${options.artifactPath}.desktop-provenance.json`;
+ const signingPath = `${options.artifactPath}.signing.json`;
+ await NodeFSP.writeFile(checksumPath, `${artifactSha256} ${artifactName}\n`, "utf8");
+
+ const buildSource = {
+ repository: process.env.GITHUB_REPOSITORY ?? null,
+ runtimeCommit: process.env.GITHUB_SHA ?? null,
+ sigmaCodeCommit: process.env.SIGMACODE_SOURCE_COMMIT ?? null,
+ ref: process.env.GITHUB_REF ?? null,
+ workflowRunId: process.env.GITHUB_RUN_ID ?? null,
+ workflowRunAttempt: process.env.GITHUB_RUN_ATTEMPT ?? null,
+ };
+ const provenance = {
+ schemaVersion: 1,
+ kind: "sigma-code.desktop-provenance",
+ artifact: { name: artifactName, sha256: artifactSha256 },
+ product: {
+ appId: APP_ID,
+ version: options.version,
+ platform: options.platform,
+ arch: options.arch,
+ minimumSystemVersion: options.platform === "darwin" ? MACOS_MINIMUM_SYSTEM_VERSION : null,
+ },
+ runtime: {
+ provenanceFile: NodePath.basename(options.runtimeProvenancePath),
+ provenanceSha256: await sha256(options.runtimeProvenancePath),
+ target: `${options.platform}-${options.arch}`,
+ },
+ buildSource,
+ };
+ const signing = {
+ schemaVersion: 1,
+ kind: "sigma-code.desktop-signing-status",
+ artifact: { name: artifactName, sha256: artifactSha256 },
+ mode: options.signingMode,
+ hardenedRuntime: options.platform === "darwin" && options.signingMode === "signed",
+ notarized: options.platform === "darwin" && options.signingMode === "signed",
+ verification: {
+ codesign: options.codesign,
+ gatekeeper: options.gatekeeper,
+ stapler: options.stapler,
+ },
+ };
+ await Promise.all([
+ NodeFSP.writeFile(provenancePath, `${JSON.stringify(provenance, null, 2)}\n`, "utf8"),
+ NodeFSP.writeFile(signingPath, `${JSON.stringify(signing, null, 2)}\n`, "utf8"),
+ ]);
+ return { checksumPath, provenancePath, signingPath };
+}
+
+function parseArgs(argv: readonly string[]): DesktopReleaseEvidenceOptions {
+ const values = new Map();
+ for (let index = 0; index < argv.length; index += 2) {
+ const name = argv[index];
+ const value = argv[index + 1];
+ if (!name?.startsWith("--") || !value)
+ throw new Error(`Invalid argument: ${name ?? ""}`);
+ values.set(name.slice(2), value);
+ }
+ const required = [
+ "artifact",
+ "runtime-provenance",
+ "platform",
+ "arch",
+ "version",
+ "signing-mode",
+ "codesign",
+ "gatekeeper",
+ "stapler",
+ ];
+ const missing = required.filter((name) => !values.has(name));
+ if (missing.length > 0) throw new Error(`Missing arguments: ${missing.join(", ")}`);
+ return {
+ artifactPath: NodePath.resolve(values.get("artifact")!),
+ runtimeProvenancePath: NodePath.resolve(values.get("runtime-provenance")!),
+ platform: values.get("platform") as Platform,
+ arch: values.get("arch") as Arch,
+ version: values.get("version")!,
+ signingMode: values.get("signing-mode") as SigningMode,
+ codesign: values.get("codesign") as Verification,
+ gatekeeper: values.get("gatekeeper") as Verification,
+ stapler: values.get("stapler") as Verification,
+ };
+}
+
+if (import.meta.main) {
+ writeDesktopReleaseEvidence(parseArgs(process.argv.slice(2))).catch((error) => {
+ process.stderr.write(`${error instanceof Error ? error.message : String(error)}\n`);
+ process.exitCode = 1;
+ });
+}