aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to 'tasks/package.py')
-rwxr-xr-xtasks/package.py224+0 −224
1 files changed, 0 insertions, 224 deletions
diff --git a/tasks/package.py b/tasks/package.py
deleted file mode 100755
--- a/tasks/package.py
+++ /dev/null
@@ -1,224 +0,0 @@
-#!/usr/bin/env -S pipx run --backend pip
-# SPDX-FileCopyrightText: 2026 Nikolay Govorov
-# SPDX-License-Identifier: Apache-2.0
-# fmt: off
-#MISE description="Build release archives and signed Linux packages"
-#MISE tools={"pipx"="1.16.7","python"="3.14.7","nfpm"="2.47.0"}
-# fmt: on
-# /// script
-# requires-python = ">=3.11"
-# dependencies = ["shellous==0.42.0"]
-# ///
-
-from __future__ import annotations
-
-import argparse
-import os
-import re
-import shutil
-import sys
-import tempfile
-from collections.abc import Sequence
-from pathlib import Path
-
-sys.dont_write_bytecode = True
-
-from libs.common import (
- APKSigning,
- GPGSigning,
- TaskError,
- require_command,
- run,
- task_main,
-)
-
-TASK = "package"
-SYSTEM_FORMATS = {"deb", "rpm", "apk"}
-ARCHIVE_FORMATS = {"tar.gz", "zip"}
-SAFE_NAME = re.compile(r"^[A-Za-z0-9][A-Za-z0-9._-]*$")
-SAFE_KEY_VERSION = re.compile(r"^[0-9]{4}$")
-
-
-def validate(arguments: argparse.Namespace, command: argparse.ArgumentParser) -> None:
- formats = set(arguments.formats)
- if formats & SYSTEM_FORMATS:
- if not arguments.version or not arguments.arch:
- command.error("--version and --arch are required for deb, rpm, and apk")
- if not arguments.config.is_file():
- raise TaskError(f"{TASK}: {arguments.config} not found")
- if formats & ARCHIVE_FORMATS:
- if arguments.archive_root is None or not arguments.archive_name:
- command.error(
- "--archive-root and --archive-name are required for tar.gz and zip"
- )
- if not arguments.archive_root.is_dir():
- raise TaskError(f"{TASK}: {arguments.archive_root} not found")
- if not SAFE_NAME.fullmatch(arguments.archive_name):
- raise TaskError(f"{TASK}: invalid archive name: {arguments.archive_name}")
- if arguments.apk_public_key and not SAFE_NAME.fullmatch(arguments.apk_public_key):
- raise TaskError(
- f"{TASK}: invalid APK public key name: {arguments.apk_public_key}"
- )
-
-
-async def create_archive(
- archive_format: str,
- output: Path,
- root: Path,
- name: str,
-) -> None:
- if archive_format == "tar.gz":
- require_command("tar", TASK)
- await run("tar", "-czf", output / f"{name}.tar.gz", "-C", root, ".")
- return
- require_command("zip", TASK)
- destination = output / f"{name}.zip"
- destination.unlink(missing_ok=True)
- await run.set(cwd=root)("zip", "-qry", destination, ".")
-
-
-async def sign_package(
- package_format: str, package: Path, signing: GPGSigning | None
-) -> None:
- if signing is None or package_format not in {"deb", "rpm"}:
- return
- command = run.set(env=signing.environment, inherit_env=False)
- if package_format == "deb":
- require_command("debsigs", TASK)
- await command(
- "debsigs",
- "--sign=origin",
- f"--default-key={signing.key_id}",
- package,
- )
- else:
- require_command("rpmsign", TASK)
- await command(
- "rpmsign",
- "--define",
- f"_gpg_name {signing.key_id}",
- "--addsign",
- package,
- )
-
-
-async def main(args: Sequence[str]) -> None:
- command = argparse.ArgumentParser(
- prog="mise run package --",
- usage=(
- "%(prog)s --output DIR [--config FILE] "
- "[--version VERSION --arch ARCH] [--apk-public-key FILE] "
- "[--archive-root DIR --archive-name NAME] "
- "deb|rpm|apk|tar.gz|zip..."
- ),
- )
- command.add_argument("--config", default="nfpm.yaml", type=Path)
- command.add_argument("--version")
- command.add_argument("--arch")
- command.add_argument("--output", required=True, type=Path)
- command.add_argument("--archive-root", type=Path)
- command.add_argument("--archive-name")
- command.add_argument("--apk-public-key")
- command.add_argument(
- "formats", nargs="+", choices=sorted(SYSTEM_FORMATS | ARCHIVE_FORMATS)
- )
- arguments = command.parse_args(args)
- validate(arguments, command)
-
- arguments.output.mkdir(parents=True, exist_ok=True)
- output = arguments.output.resolve()
- formats = set(arguments.formats)
-
- with tempfile.TemporaryDirectory(prefix="package-") as directory:
- work = Path(directory)
- environment = dict(os.environ)
- for name in (
- "GPG_PRIVATE_KEY",
- "APK_PRIVATE_KEY",
- "SIGNING_PRIVATE_KEY",
- "NFPM_PASSPHRASE",
- "NFPM_DEB_PASSPHRASE",
- "NFPM_RPM_PASSPHRASE",
- ):
- environment.pop(name, None)
- config = arguments.config
- if "apk" in formats and "${PACKAGE_KEY_VERSION}" in config.read_text():
- key_version = os.environ.get("PACKAGE_KEY_VERSION", "")
- if not SAFE_KEY_VERSION.fullmatch(key_version):
- raise TaskError(
- f"{TASK}: invalid PACKAGE_KEY_VERSION: {key_version or '<empty>'}"
- )
- config = work / "nfpm.yaml"
- config.write_text(
- arguments.config.read_text().replace(
- "${PACKAGE_KEY_VERSION}", key_version
- )
- )
-
- gpg_signing: GPGSigning | None = None
- if formats & {"deb", "rpm"} and os.environ.get("GPG_PRIVATE_KEY"):
- gpg_signing = await GPGSigning.create(TASK, work)
- await gpg_signing.prime_agent()
- environment = gpg_signing.package_environment()
-
- apk_signing: APKSigning | None = None
- if "apk" in formats and os.environ.get("APK_PRIVATE_KEY"):
- apk_signing = APKSigning(TASK, work)
- environment["APK_SIGNING_KEY"] = str(apk_signing.private_key_file)
- elif os.environ.get("APK_SIGNING_KEY"):
- environment["APK_SIGNING_KEY"] = os.environ["APK_SIGNING_KEY"]
-
- if "apk" in formats and arguments.apk_public_key:
- public_key = output / arguments.apk_public_key
- if apk_signing is not None:
- await apk_signing.export_public_key(public_key)
- elif environment.get("APK_SIGNING_KEY"):
- require_command("openssl", TASK)
- await run.set(env=environment, inherit_env=False)(
- "openssl",
- "rsa",
- "-in",
- environment["APK_SIGNING_KEY"],
- "-pubout",
- "-out",
- public_key,
- )
-
- for package_format in arguments.formats:
- if package_format in SYSTEM_FORMATS:
- require_command("nfpm", TASK)
- package_environment = dict(environment)
- package_environment.update(
- ARCH=arguments.arch,
- VERSION=arguments.version,
- )
- package_output = work / f"output-{package_format}"
- package_output.mkdir()
- await run.set(env=package_environment, inherit_env=False)(
- "nfpm",
- "package",
- "--config",
- config,
- "--packager",
- package_format,
- "--target",
- f"{package_output}/",
- )
- packages = list(package_output.iterdir())
- if len(packages) != 1 or not packages[0].is_file():
- raise TaskError(
- f"{TASK}: nFPM produced an unexpected number of packages"
- )
- await sign_package(package_format, packages[0], gpg_signing)
- shutil.move(packages[0], output / packages[0].name)
- else:
- await create_archive(
- package_format,
- output,
- arguments.archive_root,
- arguments.archive_name,
- )
-
-
-if __name__ == "__main__":
- task_main(TASK, main, sys.argv[1:])