repomatic.release.binary module

Binary build targets and the names of their release assets.

Defines the Nuitka compilation targets for all supported platforms, the executable format each one compiles to, and the naming of the binaries a release publishes. The header parsing that verifies a compiled binary lives in repomatic.release.verify.

repomatic.release.binary.BINARY_ASSET_SUFFIXES = ('.bin', '.exe')

File extensions identifying compiled binaries among release assets.

The one definition of “a compiled release asset”: scan-virustotal uploads this set, the release workflow downloads it (--pattern flags in _release-engine.yaml), docs/binaries.md lists it, and the dev-release asset globs derive from it.

repomatic.release.binary.PYTHON_DIST_SUFFIXES = ('.tar.gz', '.whl')

File extensions identifying Python distributions among release assets.

The counterpart of BINARY_ASSET_SUFFIXES, and the same kind of single definition: pack_binary_assets() excludes this set from the binary upload list (create-release already attached those), and the dev-release asset globs add it on top of the compiled binaries.

repomatic.release.binary.NUITKA_DISABLE_CCACHE_FLAG: Final[str] = '--disable-ccache'

The Nuitka option that keeps its ccache wrapper out of a build.

repomatic.release.binary.MACHO_MAGIC_64: Final[int] = 4277009103

Magic of a 64-bit Mach-O header, in the file’s own (little) endianness.

repomatic.release.binary.MACHO_FAT_MAGICS: Final[frozenset[int]] = frozenset({3405691582, 3405691583})

Big-endian magics of universal (fat) Mach-O containers, 32- and 64-bit.

class repomatic.release.binary.BinaryFormat(label, extension, floor_label)[source]

Bases: Enum

Executable container format a compiled binary uses.

Each member owns what varies by format: the name messages print, the file extension its executables take, and what a target’s floor measures. The floor label doubles as the enforceability flag, since a format recording no measurable floor has nothing to label. The header parser for each format is read_header().

Note

Members carry no magic bytes of their own: detect() needs to read them in a fixed order (a fat Mach-O and a PE both fail an equality test on the first four bytes), so the probe stays one method rather than a table.

ELF = ('elf', 'bin', 'glibc')
MACHO = ('macho', 'bin', 'macOS')
PE = ('pe', 'exe', None)
label

Lowercase format name, as printed in verification messages.

extension

File extension executables of this format take.

floor_label

What a target’s floor measures for this format, or None.

None marks a format whose headers record nothing a scan can check, so BuildTarget.enforced_floor reports no floor to enforce. Only PE is in that case: its version headers are nominal, and the Windows floor is CPython’s own support policy, not a linker artifact.

classmethod detect(path)[source]

Identify a file’s executable format from its magic bytes.

Parameters:

path (Path) – Path to the file to probe.

Return type:

BinaryFormat | None

Returns:

The matching format, or None for anything unrecognized.

repomatic.release.binary.PLATFORM_FORMATS: Final[dict[Platform | Group, BinaryFormat]] = {Group(id='linux', name='Linux distributions'): BinaryFormat.ELF, Platform(id='macos', name='macOS'): BinaryFormat.MACHO, Platform(id='windows', name='Windows'): BinaryFormat.PE}

Executable format each build platform compiles to.

Read through BuildTarget.binary_format, which is how every caller reaches it.

repomatic.release.binary.MACHINE_IDS: Final[dict[tuple[BinaryFormat, Architecture], int]] = {(BinaryFormat.ELF, Architecture(id='aarch64', name='ARM64 (AArch64)')): 183, (BinaryFormat.ELF, Architecture(id='x86_64', name='x86-64 (AMD64)')): 62, (BinaryFormat.MACHO, Architecture(id='aarch64', name='ARM64 (AArch64)')): 16777228, (BinaryFormat.MACHO, Architecture(id='x86_64', name='x86-64 (AMD64)')): 16777223, (BinaryFormat.PE, Architecture(id='aarch64', name='ARM64 (AArch64)')): 43620, (BinaryFormat.PE, Architecture(id='x86_64', name='x86-64 (AMD64)')): 34404}

Machine identifier a binary’s header carries, per format and architecture.

One table rather than three, keyed the way PlatformKey keys the tool registry: the value is the raw header integer of each format, so a caller compares it against whatever the matching parser returns.

class repomatic.release.binary.BuildTarget(id, runner, platform, arch, floor, container=None)[source]

Bases: object

One Nuitka compile target: a runner image, a platform and an architecture.

Carries the metadata every consumer branches on, and the methods that interpret it, so a caller asks the target what its binaries must look like instead of re-deriving it from a platform name.

Platform and architecture are Extra Platforms traits rather than free strings, which is the same vocabulary PlatformKey keys the downloaded-binary registry on. as_matrix_entry() renders both back to their ids for the workflow matrix.

Every field is irreducible: anything a format decides (the file extension, what the floor measures, which header field to read) lives on BinaryFormat instead.

id: str

Short target identifier, chosen for user-friendliness.

It names the published release asset, so it must stay stable: download URLs, docs/install.md and the binaries.csv catalog all match on it.

Note

It is deliberately not derived from platform and arch, even though all six targets currently read {platform}-{short arch}. The asset names froze on the short x64 and arm64 spellings while those fields carry the canonical extra-platforms ids, and a future target splitting an existing pair (a musl Linux, say) would need a name the derivation cannot produce. tests/test_binary.py pins the pairing.

runner: str

Runner image, as named in GitHub-hosted runners.

Named for what it holds, not for the os matrix key it renders to: ubuntu-26.04-arm is an image, and platform is the operating system.

Hint

One compile job per target, each on one of the six runners the test matrix already covers, so a published binary is built on an image the suite is validated against. The targets are exactly KNOWN_RUNNERS, not a separate selection: an image is added here by widening the test axes, never on its own.

platform: Platform | Group

Operating system the binary runs on.

arch: Architecture

CPU architecture the binary is compiled for.

floor: str

Oldest runtime the binary is built to run on.

What the version counts is the format’s business, named by BinaryFormat.floor_label: a glibc symbol version on ELF, a macOS deployment target on Mach-O, a Windows release on PE. The first two are measured out of the compiled files by verify_binary_floor(); the third is documentation, since PE version headers record nothing to measure.

On macOS, build_env also exports it as MACOSX_DEPLOYMENT_TARGET for the compile.

container: str | None = None

OCI image the Linux compile and self-test jobs run in.

Passed to the container: key of the release workflow. Compiling inside manylinux_2_28 caps the toolchain at glibc 2.28, so binaries stop inheriting the floor of whatever glibc the current runner image ships.

Declared per target rather than derived from platform, which would assume every Linux target is a manylinux one. Absent on macOS and Windows: GitHub Actions containers do not exist for those runners.

property binary_format: BinaryFormat

Executable format the compiler emits for this target.

property extension: str

File extension of the compiled binary.

property expected_machine: int

Machine identifier this target’s binaries carry in their header.

property enforced_floor: str | None

floor, or None when the format records nothing to measure.

property nuitka_flags: tuple[str, ...]

Nuitka options the compile of this target adds to the shared ones.

macOS turns ccache off. Nuitka downloads its own ccache binary there, with no checksum at the call site (Nuitka#3997), and runs it under Rosetta on arm at a measured cost. The download is supply-chain surface for no gain: see compile_cache. docs/nuitka.md says what re-enabling it would take.

property compile_cache: bool

Whether a persisted Nuitka compile cache speeds up this target.

A target compiling with ccache off writes no object a later run could reuse, so the release workflow neither restores nor saves its cache.

property build_env: dict[str, str]

Environment variables the compile of this target needs.

macOS pins MACOSX_DEPLOYMENT_TARGET to floor. Without it, compiled objects and processed dylibs inherit the build runner’s own macOS version as their minimum.

as_matrix_entry()[source]

Flat, JSON-safe mapping of this target, for GitHub matrix inclusion.

Renders platform and arch back to their extra-platforms ids, the spelling the workflow expressions (matrix.platform_id) and tests.yaml’s runner check compare against, and runner back to the os key runs-on: reads. A target with no container omits the key, so an entry carries only what applies to it.

Return type:

dict[str, str]

repomatic.release.binary.NUITKA_BUILD_TARGETS: Final[dict[str, BuildTarget]] = {'linux-arm64': BuildTarget(id='linux-arm64', runner='ubuntu-26.04-arm', platform=Group(id='linux', name='Linux distributions'), arch=Architecture(id='aarch64', name='ARM64 (AArch64)'), floor='2.28', container='quay.io/pypa/manylinux_2_28_aarch64@sha256:e7035406e58d96b7407246af1f6514a3cbd753a0025b42b9adfbeadd3b29ba80'), 'linux-x64': BuildTarget(id='linux-x64', runner='ubuntu-26.04', platform=Group(id='linux', name='Linux distributions'), arch=Architecture(id='x86_64', name='x86-64 (AMD64)'), floor='2.28', container='quay.io/pypa/manylinux_2_28_x86_64@sha256:fdb9a9c223b215604dc7b6f7e8fff4b39bfea5fbaa7777a2e5544a60dfa437f8'), 'macos-arm64': BuildTarget(id='macos-arm64', runner='macos-26', platform=Platform(id='macos', name='macOS'), arch=Architecture(id='aarch64', name='ARM64 (AArch64)'), floor='11.0', container=None), 'macos-x64': BuildTarget(id='macos-x64', runner='macos-26-intel', platform=Platform(id='macos', name='macOS'), arch=Architecture(id='x86_64', name='x86-64 (AMD64)'), floor='10.15', container=None), 'windows-arm64': BuildTarget(id='windows-arm64', runner='windows-11-arm', platform=Platform(id='windows', name='Windows'), arch=Architecture(id='aarch64', name='ARM64 (AArch64)'), floor='11', container=None), 'windows-x64': BuildTarget(id='windows-x64', runner='windows-2025', platform=Platform(id='windows', name='Windows'), arch=Architecture(id='x86_64', name='x86-64 (AMD64)'), floor='10', container=None)}

GitHub-hosted runner matrix for Nuitka builds, keyed by target name.

The roster is closed: every entry compiles on a runner the test matrix already covers, and the key doubles as the compiled binary’s published identifier. See BuildTarget for what each field means and which of them are frozen.

Note

A target leaves the roster when its runner image does

A published binary needs a runner to compile on, so GitHub’s Available Images table sets how long an architecture lives here. macos-x64 therefore ships for as long as that table offers an Intel macOS label, on GitHub’s schedule rather than Apple’s. The two diverge: Apple lets Mac App Store apps go arm64-only and drops Intel in macOS 27, while an Intel Mac stays on macOS 26 Tahoe and keeps running it, so those users still want the binary.

Drop the target once sync-runner-images reports the image deprecated, and not before. Removing one is a single change across two files: this roster and TEST_RUNNERS_FULL are held equal by tests/test_workflows.py::test_binary_build_targets_are_the_test_axes. A retirement onto a successor image needs no edit here: each target reads its runner from a slot constant of repomatic.matrix.axes, which is what sync-runner-images rewrites.

repomatic.release.binary.FLAT_BUILD_TARGETS: Final[list[dict[str, str]]] = [{'arch': 'aarch64', 'container': 'quay.io/pypa/manylinux_2_28_aarch64@sha256:e7035406e58d96b7407246af1f6514a3cbd753a0025b42b9adfbeadd3b29ba80', 'extension': 'bin', 'floor': '2.28', 'os': 'ubuntu-26.04-arm', 'platform_id': 'linux', 'target': 'linux-arm64'}, {'arch': 'x86_64', 'container': 'quay.io/pypa/manylinux_2_28_x86_64@sha256:fdb9a9c223b215604dc7b6f7e8fff4b39bfea5fbaa7777a2e5544a60dfa437f8', 'extension': 'bin', 'floor': '2.28', 'os': 'ubuntu-26.04', 'platform_id': 'linux', 'target': 'linux-x64'}, {'arch': 'aarch64', 'extension': 'bin', 'floor': '11.0', 'os': 'macos-26', 'platform_id': 'macos', 'target': 'macos-arm64'}, {'arch': 'x86_64', 'extension': 'bin', 'floor': '10.15', 'os': 'macos-26-intel', 'platform_id': 'macos', 'target': 'macos-x64'}, {'arch': 'aarch64', 'extension': 'exe', 'floor': '11', 'os': 'windows-11-arm', 'platform_id': 'windows', 'target': 'windows-arm64'}, {'arch': 'x86_64', 'extension': 'exe', 'floor': '10', 'os': 'windows-2025', 'platform_id': 'windows', 'target': 'windows-x64'}]

List of build targets in a flat format, suitable for matrix inclusion.

repomatic.release.binary.current_build_target()[source]

The build target of the machine this runs on.

Matches the running platform and architecture against NUITKA_BUILD_TARGETS. A Linux distribution extra-platforms cannot name still matches the Linux targets, as the tool registry treats it.

Return type:

BuildTarget | None

Returns:

The target, or None when no target builds on this machine.

repomatic.release.binary.binary_name(package, target, version=None)[source]

Compose a compiled binary’s release-asset filename.

The one definition of the naming convention: {package}-{version}-{target}.{ext} for the versioned upload, and with no version the stable alias ({package}-{target}.{ext}) backing the releases/latest/download URLs. The extension comes from NUITKA_BUILD_TARGETS.

Return type:

str

repomatic.release.binary.versionless_alias(filename, version)[source]

Map a versioned binary filename to its stable alias, or None.

Strips the -{version}- segment (papaya-1.2.3-linux-arm64.bin becomes papaya-linux-arm64.bin). Returns None for filenames that carry no such segment or are not compiled binaries, so callers can filter and map in one pass.

Return type:

str | None

repomatic.release.binary.binary_filename_re(package)[source]

Match a package binary filename, versioned or versionless.

Captures target and ext, both alternations derived from NUITKA_BUILD_TARGETS so a new build target extends the pattern without anyone editing a regex. The release freeze rewrites both spellings onto the versioned form through this; tests/test_platform_keys.py pins the pattern against every target.

Return type:

Pattern[str]

repomatic.release.binary.pack_binary_assets(dist_dir, version)[source]

Pack a release’s upload list, materializing the versionless aliases.

Mirrors what the release engine’s upload step needs: every file in dist_dir except the Python distributions (create-release already uploaded those), plus a byte-identical versionless alias copied beside each versioned binary so the stable releases/latest/download URLs always resolve. Aliases share their sibling’s digest, which is what lets artifact attestations verify them unchanged and the binaries catalog collapse them (see binaries_page._binary_assets).

Idempotent: re-running overwrites the same aliases with the same bytes.

Parameters:
  • dist_dir (Path) – Directory holding the compiled binaries and attestation bundles downloaded from the build jobs.

  • version (str) – The release version whose binaries earn aliases.

Return type:

list[Path]

Returns:

Sorted paths to upload, aliases included.