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
4 changes: 4 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,10 @@ The updater module (`updater.rs`) checks for new releases by hitting `https://ap

## Suggested Tests Before Committing

> **Canonical gates:** see [`docs/optimization-roadmap/local-gates.md`](docs/optimization-roadmap/local-gates.md)
> for the full local gate set, including the D3D12 Metal SDK probes CI cannot
> run and the Phase 1–8 backend diagnostic routes.

### Rust changes
```bash
cd app/src-rust
Expand Down
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
cmake_minimum_required(VERSION 3.24)
project(metalsharp VERSION 0.46.7 LANGUAGES C CXX OBJC OBJCXX)
project(metalsharp VERSION 0.46.9 LANGUAGES C CXX OBJC OBJCXX)

set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
Expand Down
116 changes: 116 additions & 0 deletions app/build/adhoc-deep-sign.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
const fs = require("node:fs");
const path = require("node:path");
const { spawnSync } = require("node:child_process");

function run(command, args, options = {}) {
const result = spawnSync(command, args, {
encoding: "utf8",
stdio: options.capture ? "pipe" : "inherit",
});
if (result.error) {
throw result.error;
}
if (result.status !== 0) {
const output = [result.stdout, result.stderr].filter(Boolean).join("\n");
throw new Error(`${command} ${args.join(" ")} failed with status ${result.status}${output ? `\n${output}` : ""}`);
}
return result.stdout || "";
}

function shouldSkipAdhocSigning() {
if (process.env.METALSHARP_SKIP_ADHOC_DEEP_SIGN === "1") {
return "METALSHARP_SKIP_ADHOC_DEEP_SIGN=1";
}

const developerIdSigning =
Boolean(process.env.CSC_KEYCHAIN) || Boolean(process.env.CSC_LINK) || Boolean(process.env.CSC_NAME);
if (developerIdSigning && process.env.METALSHARP_UNSIGNED_DMG !== "1") {
return "Developer ID signing is active";
}

return "";
}

function isMachO(filePath) {
if (!fs.statSync(filePath).isFile()) {
return false;
}
const output = run("file", ["-b", filePath], { capture: true });
return output.includes("Mach-O");
}

function collectSignTargets(root) {
const files = [];
const bundles = [];
const stack = [root];

while (stack.length > 0) {
const current = stack.pop();
for (const entry of fs.readdirSync(current, { withFileTypes: true })) {
const fullPath = path.join(current, entry.name);
if (entry.isSymbolicLink()) {
continue;
}
if (entry.isDirectory()) {
if (/\.(app|appex|framework|xpc)$/i.test(entry.name)) {
bundles.push(fullPath);
}
stack.push(fullPath);
} else if (entry.isFile() && isMachO(fullPath)) {
files.push(fullPath);
}
}
}

bundles.sort((a, b) => b.split(path.sep).length - a.split(path.sep).length);
files.sort((a, b) => b.split(path.sep).length - a.split(path.sep).length);
return { files, bundles };
}

function signTarget(target, entitlementsPath = "") {
const args = ["--force", "--sign", "-", "--timestamp=none"];
if (entitlementsPath) {
args.push("--entitlements", entitlementsPath);
}
args.push(target);
run("codesign", args);
}

exports.default = async function adhocDeepSignMetalSharp(context) {
if (context.electronPlatformName !== "darwin") {
return;
}

const skipReason = shouldSkipAdhocSigning();
if (skipReason) {
console.log(`MetalSharp ad-hoc deep sign skipped: ${skipReason}.`);
return;
}

const appName = context.packager.appInfo.productFilename;
const appPath = path.join(context.appOutDir, `${appName}.app`);
if (!fs.existsSync(appPath)) {
throw new Error(`MetalSharp app bundle was not found for ad-hoc signing: ${appPath}`);
}

const entitlementsPath = path.join(__dirname, "entitlements.mac.plist");
const { files, bundles } = collectSignTargets(appPath);
console.log(`MetalSharp ad-hoc deep sign: ${files.length} Mach-O file(s), ${bundles.length} bundle(s).`);

for (const file of files) {
signTarget(file);
}
for (const bundle of bundles) {
signTarget(bundle);
}

const finalArgs = ["--force", "--deep", "--strict", "--sign", "-", "--timestamp=none"];
if (fs.existsSync(entitlementsPath)) {
finalArgs.push("--entitlements", entitlementsPath);
}
finalArgs.push(appPath);
run("codesign", finalArgs);

run("codesign", ["--verify", "--deep", "--strict", "--verbose=2", appPath]);
console.log("MetalSharp ad-hoc deep sign complete.");
};
4 changes: 2 additions & 2 deletions app/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion app/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "metalsharp",
"version": "0.46.7",
"version": "0.46.9",
"description": "MetalSharp — D3D→Metal translation layer frontend",
"author": "MetalSharp",
"repository": {
Expand Down Expand Up @@ -44,6 +44,7 @@
"appId": "com.metalsharp.app",
"productName": "MetalSharp",
"copyright": "Copyright © 2026 MetalSharp",
"afterPack": "build/adhoc-deep-sign.cjs",
"afterSign": "build/notarize.cjs",
"mac": {
"category": "public.app-category.games",
Expand Down
68 changes: 67 additions & 1 deletion app/src-rust/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion app/src-rust/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "metalsharp-backend"
version = "0.46.7"
version = "0.46.9"
edition = "2021"

[dependencies]
Expand All @@ -16,3 +16,4 @@ rusqlite = { version = "0.40", features = ["bundled"] }
ctrlc = "3"
libc = "0.2"
zip = { version = "8", default-features = false, features = ["deflate"] }
sha2 = "0.10"
Loading
Loading