Files
hermes-agent/scripts/bundles/desktop_inputs.py
2026-09-22 02:19:38 -04:00

107 lines
4.6 KiB
Python

"""Build identity and process environment from an admitted preparation."""
from __future__ import annotations
from collections.abc import Mapping
from contextlib import contextmanager
import json
import re
from pathlib import Path
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from scripts.bundles.desktop_prepare import BuildRequest, PreparedDesktop
@contextmanager
def build_lock(source: Path):
from hermes_cli.runtime_state import _lock
directory = source / ".build"
if directory.is_symlink():
raise ValueError("desktop build scratch must not be a symlink")
directory.mkdir(exist_ok=True)
with (directory / ".desktop-build.lock").open("a+b") as lock:
if not _lock(lock.fileno(), wait=False):
raise ValueError("another desktop build or preparation is using this source checkout")
yield
def build_environment(prepared: PreparedDesktop, variant: str, inherited: Mapping[str, str]) -> dict[str, str]:
from pm.build_operations import verified_tools
from pm.lock import Lockfile
from scripts.bundles.desktop_toolchain import bootstrap_environment
request = prepared.request
env = bootstrap_environment(request.source, request.work, request.cache, inherited)
selection = verified_tools(["python", "node", "npm"], source_store=request.cache / "tools",
target=request.target, lock=Lockfile(request.source / "pm/lock.json"))
if prepared.python != selection.entries["python"].binary or prepared.node != selection.entries["node"].binary:
raise ValueError("prepared Python/Node paths do not match selected tools; prepare again")
env = selection.environment(env)
env["HERMES_PYTHON"] = str(prepared.python)
return identity_environment(request, variant, env)
def identity_environment(request: BuildRequest, variant: str, inherited: Mapping[str, str]) -> dict[str, str]:
"""One build-only bridge shared by dependency workers, stamp and packagers."""
request.validate_channel()
if request.channel_request is not None and variant != "bundled":
raise ValueError("channel builds currently support only the bundled variant")
env = dict(inherited)
env.update(CI="true", PYTHONUTF8="1", GITHUB_SHA=request.commit,
HERMES_DESKTOP_VARIANT=variant,
HERMES_PAYLOAD_VERSION=request.version,
HERMES_BUNDLE_ENV_JSON=json.dumps(request.bundle_env, sort_keys=True))
if request.release_epoch is None:
env.pop("HERMES_RELEASE_EPOCH", None)
else:
env["HERMES_RELEASE_EPOCH"] = str(request.release_epoch)
env.pop("BUILD_NUMBER", None)
env.pop("GITHUB_HEAD_REF", None)
env.pop("_HERMES_CHANNEL_REQUEST_JSON", None)
if request.channel_request is not None:
env["_HERMES_CHANNEL_REQUEST_JSON"] = json.dumps(request.channel_request, sort_keys=True)
env["GITHUB_REPOSITORY"] = request.channel_request["repository"]
for key in ("HERMES_BUILD_COMMIT", "HERMES_PAYLOAD_TAG", "GITHUB_REF_NAME"):
env.pop(key, None)
elif request.tag is None:
env["HERMES_BUILD_COMMIT"] = request.commit
env.pop("HERMES_PAYLOAD_TAG", None)
env.pop("GITHUB_REF_NAME", None)
else:
env.pop("HERMES_BUILD_COMMIT", None)
env["HERMES_PAYLOAD_TAG"] = request.tag
env["GITHUB_REF_NAME"] = request.tag
return env
def validate_builder_identity(request: BuildRequest, args: list[str]) -> None:
if request.channel_request is not None and any(
re.match(r"(?:-c|--config)\.extraMetadata\.(?:version|shortVersion|shortVersionWindows)=", arg)
for arg in args
):
raise ValueError("channel package versions come only from the admitted request")
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
request = prepared.request
variant = variant or request.variant
if variant != request.variant and not (
{variant, request.variant} == {"bundled", "store"} and request.tag
and channel_for_tag(request.tag) == "stable"
):
raise ValueError("requested variant was not prepared")
return variant