fix(build): preserve login home for macOS signing

This commit is contained in:
ethernet
2026-09-13 21:02:15 -04:00
parent 59013e8247
commit 1df2d86262
4 changed files with 37 additions and 2 deletions

View File

@@ -140,6 +140,11 @@ Timestamp services, Azure signing, Apple notarization and publication remain
online operations. Validate unsigned packaging with dependency networking denied
on each target, then verify signed installers and launchers on their native hosts.
macOS packaging retains the caller's login `HOME` for keychain import and signing.
An explicit keychain path does not make Security.framework work under a scratch
home. Dependency preparation and product compilation still use the isolated home.
Hermes state and explicit dependency-cache paths remain build-owned during packaging.
## Commit-only builds
To preview a build for a pushed revision, run:

View File

@@ -59,7 +59,7 @@ def build_prepared(path: Path, builder_args: list[str], variant: str | None = No
def _build_prepared(prepared, builder_args: list[str], variant: str | None) -> None:
prepared.validate()
from scripts.bundles.desktop_inputs import build_environment, select_variant
from scripts.bundles.desktop_inputs import build_environment, packaging_environment, select_variant
from scripts.bundles.native import finish_native
request = prepared.request
@@ -109,7 +109,8 @@ def _build_prepared(prepared, builder_args: list[str], variant: str | None) -> N
if metadata["file"]:
version_args = [f'-c.extraMetadata.shortVersion={metadata["file"]}', f'-c.extraMetadata.shortVersionWindows={metadata["file"]}']
require_source(repo, request.commit)
run([node, "scripts/run-electron-builder.mjs", *package_args, *version_args, *builder_args], cwd=desktop, env=env)
run([node, "scripts/run-electron-builder.mjs", *package_args, *version_args, *builder_args], cwd=desktop,
env=packaging_environment(env, os.environ, request.target))
def main() -> None:

View File

@@ -54,6 +54,16 @@ def build_environment(prepared: PreparedDesktop, variant: str, inherited: Mappin
return env
def packaging_environment(build: Mapping[str, str], inherited: Mapping[str, str],
target: str) -> dict[str, str]:
env = dict(build)
if target.startswith("darwin-"):
# Security.framework needs the login HOME for both key import and signing,
# even with an explicit keychain. Keep dependency preparation isolated.
env["HOME"] = inherited.get("HERMES_REAL_HOME") or inherited.get("HOME") or str(Path.home())
return env
def select_variant(prepared: PreparedDesktop, variant: str | None) -> str:
from scripts.termux.deb_version import channel_for_tag

View File

@@ -150,6 +150,25 @@ def test_bootstrap_real_pm_resolves_only_build_owned_state(tmp_path):
assert all(path.is_relative_to(work) for path in (partials, uv_default, state))
@pytest.mark.parametrize("target", ["darwin-arm64", "darwin-x64", "linux-x64", "win32-arm64"])
def test_packaging_preserves_keychain_home_without_retargeting_build_state(tmp_path, target):
from scripts.bundles.desktop_inputs import packaging_environment
from scripts.bundles.desktop_toolchain import bootstrap_environment
source, work, cache = (tmp_path / name for name in ("source", "work", "cache"))
login_home = str(tmp_path / "login")
inherited = {"HOME": login_home, "CSC_KEYCHAIN": "explicit.keychain"}
isolated = bootstrap_environment(source, work, cache, inherited)
before = isolated.copy()
for caller in (inherited, {**inherited, "HOME": str(work / "launcher-home"),
"HERMES_REAL_HOME": login_home}, {}):
packaged = packaging_environment(isolated, caller, target)
expected_home = (caller.get("HERMES_REAL_HOME") or caller.get("HOME") or str(Path.home())
if target.startswith("darwin-") else isolated["HOME"])
assert packaged == {**isolated, "HOME": expected_home}
assert isolated == before
@pytest.mark.platforms("linux", "macos", "windows")
def test_prepare_tools_uses_pm_native_pins_and_separate_cache(tmp_path, monkeypatch):
import pm