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
8 changes: 8 additions & 0 deletions .github/workflows/release-desktop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,14 @@ jobs:
native/hyprland-snap-shot/target/${{ inputs.rust_target }}/release/t3-hyprland-snap-shot
key: linux-capture-helpers-${{ inputs.rust_target }}-${{ hashFiles('native/kde-snap-shot/Cargo.lock', 'native/kde-snap-shot/Cargo.toml', 'native/kde-snap-shot/src/**', 'native/hyprland-snap-shot/Cargo.lock', 'native/hyprland-snap-shot/Cargo.toml', 'native/hyprland-snap-shot/src/**', 'native/hyprland-snap-shot/protocols/**') }}

- name: Ensure Rosetta for macOS x64 bytecode generation
if: inputs.platform == 'mac' && inputs.arch == 'x64'
shell: bash
run: |
if [[ "$(uname -m)" == "arm64" ]] && ! /usr/bin/arch -x86_64 /usr/bin/true; then
sudo /usr/sbin/softwareupdate --install-rosetta --agree-to-license
fi
- name: Setup Rust
if: steps.resource_monitor_cache.outputs.cache-hit != 'true' || (inputs.platform == 'linux' && steps.capture_helper_cache.outputs.cache-hit != 'true')
uses: dtolnay/rust-toolchain@6bed0761d98439e5a578e2877258200ad565ba87 # stable
Expand Down
5 changes: 3 additions & 2 deletions docs/operations/development.md
Original file line number Diff line number Diff line change
Expand Up @@ -265,8 +265,9 @@ options.

Native macOS and Windows desktop artifact builds generate server bytecode with the packaged
Electron executable, after ASAR creation and before signing. Linux builds skip generation until
native packaging and relocated AppImage reuse have been validated. Cross-platform and
cross-architecture builds also skip generation explicitly. CLI, SEA and WSL runtimes keep their
native packaging and relocated AppImage reuse have been validated. macOS x64 builds on ARM64
hosts run the packaged x64 Electron under Rosetta to generate the cache. Other cross-platform and
cross-architecture builds skip generation explicitly. CLI, SEA and WSL runtimes keep their
existing behavior; only desktop backend imports covered by generation receive prepared bytecode.
Worker-only modules compile normally.

Expand Down
61 changes: 61 additions & 0 deletions scripts/lib/server-compile-cache.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,67 @@ it.each(["x64", "arm64"])("skips prepared bytecode generation on Linux %s", asyn
expect(result.stdout).toContain("Skipping unsupported target linux");
});

it("prepares macOS x64 cache on an ARM64 build host", async () => {
const { spawnSync } = await import("node:child_process");
const root = NodeFS.realpathSync(
NodeFS.mkdtempSync(NodePath.join(NodeOS.tmpdir(), "t3-rosetta-cache-")),
);
try {
const resources = NodePath.join(root, "Fixture.app", "Contents", "Resources");
const archive = NodePath.join(resources, "app.asar");
const dist = NodePath.join(archive, "apps/server/dist");
const helper = NodePath.join(archive, "apps/desktop/dist-electron/serverCompileCache.cjs");
NodeFS.mkdirSync(dist, { recursive: true });
NodeFS.mkdirSync(NodePath.dirname(helper), { recursive: true });
NodeFS.writeFileSync(NodePath.join(dist, "binCli-fixture.mjs"), "export const answer = 42;");
NodeFS.writeFileSync(
helper,
`module.exports = require(${JSON.stringify(NodeURL.fileURLToPath(new URL("./server-compile-cache.ts", import.meta.url)))});`,
);
const hook = NodeURL.fileURLToPath(
new URL("../prepare-server-compile-cache.cjs", import.meta.url),
);
// The fixture uses Node for generation; the hook still sees an ARM64 macOS build host.
const result = spawnSync(
process.execPath,
[
"-e",
`
const childProcess = require("node:child_process");
const spawn = childProcess.spawnSync;
childProcess.spawnSync = (executable, args, options) => {
require("node:assert/strict").equal(executable, ${JSON.stringify(NodePath.join(root, "Fixture.app", "Contents", "MacOS", "Fixture"))});
return spawn(process.execPath, args, options);
};
Object.defineProperty(process, "platform", { value: "darwin" });
Object.defineProperty(process, "arch", { value: "arm64" });
require(${JSON.stringify(hook)})({
appOutDir: ${JSON.stringify(root)}, electronPlatformName: "darwin", arch: 1,
packager: { appInfo: { productFilename: "Fixture" } }
}).catch(error => { console.error(error); process.exitCode = 1; });
`,
],
{ encoding: "utf8" },
);
expect(result.status, result.stderr).toBe(0);
const manifest = JSON.parse(
NodeFS.readFileSync(NodePath.join(resources, "server-compile-cache/manifest.json"), "utf8"),
);
expect(manifest.entries).toEqual([
{
module: "app.asar/apps/server/dist/binCli-fixture.mjs",
file: expect.stringMatching(/^[a-f0-9]{8}$/),
},
]);
expect(
NodeFS.statSync(NodePath.join(resources, "server-compile-cache", manifest.entries[0].file))
.size,
).toBeGreaterThan(0);
} finally {
NodeFS.rmSync(root, { recursive: true, force: true });
}
});

it("seeds relocated cache entries without allowing writes into shipped resources", () => {
const root = NodeFS.realpathSync(
NodeFS.mkdtempSync(NodePath.join(NodeOS.tmpdir(), "t3-prepared-test-")),
Expand Down
10 changes: 9 additions & 1 deletion scripts/prepare-server-compile-cache.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,15 @@ module.exports = async ({ appOutDir, electronPlatformName, arch, packager }) =>
return;
}
const targetArch = ["ia32", "x64", "armv7l", "arm64", "universal"][arch];
if (electronPlatformName !== process.platform || targetArch !== process.arch) {
const runsWithRosetta =
electronPlatformName === "darwin" &&
process.platform === "darwin" &&
process.arch === "arm64" &&
targetArch === "x64";
if (
electronPlatformName !== process.platform ||
(targetArch !== process.arch && !runsWithRosetta)
) {
console.log(
`[server-compile-cache] Skipping unexecutable target ${electronPlatformName}/${targetArch}`,
);
Expand Down
Loading