Files
2026-08-14 13:34:26 +03:00

148 lines
5.1 KiB
Python

#!/usr/bin/env python3
"""
Генерирует control-файл equivs (из Jinja2-шаблона) и собирает .deb
метапакет с зависимостями для кастомной сборки PostgreSQL под Astra Linux.
Использование:
./build_deb.py versions/14.10-2.1C.yml --os 1.7
./build_deb.py versions/14.10-2.1C.yml --os 1.8
./build_deb.py versions/14.10-2.1C.yml --all-os # под все ОС сразу
"""
import argparse
import shutil
import subprocess
import sys
import re
from pathlib import Path
import yaml # pip install pyyaml
from jinja2 import Environment, FileSystemLoader, StrictUndefined # pip install jinja2
SCRIPT_DIR = Path(__file__).resolve().parent
TEMPLATES_DIR = SCRIPT_DIR / "templates"
PKG_NAME_RE = re.compile(r"^[a-z0-9][a-z0-9+.-]*$")
def load_version_file(path: Path) -> dict:
with open(path, encoding="utf-8") as f:
return yaml.safe_load(f)
def render_control(context: dict) -> str:
env = Environment(
loader=FileSystemLoader(str(TEMPLATES_DIR)),
undefined=StrictUndefined, # упадёт с понятной ошибкой, если забыли передать переменную
trim_blocks=True,
lstrip_blocks=True,
)
template = env.get_template("control.j2")
return template.render(**context)
def sanitize_pkg_token(value: str) -> str:
"""
Приводит строку к виду, допустимому в имени deb-пакета:
только [a-z0-9+.-], первый символ - буква или цифра.
"""
value = value.lower()
value = re.sub(r"[^a-z0-9+.-]", "-", value) # всё запрещённое -> дефис
value = re.sub(r"-{2,}", "-", value) # схлопнуть повторяющиеся дефисы
value = value.strip("-.") # не должно начинаться/заканчиваться на - или .
return value
def validate_pkg_name(name: str):
if not PKG_NAME_RE.match(name):
sys.exit(
f"Некорректное имя пакета: '{name}' (должно соответствовать {PKG_NAME_RE.pattern})"
)
def build_for_os(spec: dict, os_version: str, build_dir: Path, dist_dir: Path) -> Path:
if os_version not in spec.get("per_os", {}):
sys.exit(
f"Ошибка: в {spec['pg_version']} нет секции per_os для ОС {os_version}"
)
depends = spec.get("common", []) + spec["per_os"][os_version]
pg_version_safe = sanitize_pkg_token(spec["pg_version"])
os_version_safe = sanitize_pkg_token(os_version)
pkg_name = f"pgsql-{pg_version_safe}-deps-astra{os_version_safe}"
validate_pkg_name(pkg_name)
control_content = render_control(
{
"pg_version": spec["pg_version"],
"pg_version_safe": pg_version_safe,
"package_revision": spec.get("package_revision", 1),
"maintainer": spec.get("maintainer", "unknown"),
"depends": depends,
"os_version": os_version,
"os_version_safe": os_version_safe,
}
)
work_dir = build_dir / f"{pg_version_safe}-astra{os_version}"
work_dir.mkdir(parents=True, exist_ok=True)
control_path = work_dir / "control"
control_path.write_text(control_content, encoding="utf-8")
print(f"--- Building: {control_path} ---")
result = subprocess.run(
["equivs-build", "control"],
cwd=work_dir,
capture_output=True,
text=True,
)
print(result.stdout)
if result.returncode != 0:
print(result.stderr, file=sys.stderr)
sys.exit(f"Сборка пакета для ОС {os_version} провалилась")
debs = list(work_dir.glob("*.deb"))
if not debs:
sys.exit(f"equivs-build не создал .deb в {work_dir}")
dist_dir.mkdir(parents=True, exist_ok=True)
dest = dist_dir / debs[0].name
shutil.copy2(debs[0], dest)
print(f"OK -> {dest}")
return dest
def main():
parser = argparse.ArgumentParser(
description=__doc__, formatter_class=argparse.RawTextHelpFormatter
)
parser.add_argument(
"version_file",
type=Path,
help="Путь к YAML-файлу версии, напр. versions/14.10-2.1C.yml",
)
parser.add_argument(
"--os", dest="os_version", help="Версия ОС Astra, напр. 1.7 или 1.8"
)
parser.add_argument(
"--all-os",
action="store_true",
help="Собрать под все ОС, перечисленные в per_os",
)
parser.add_argument("--build-dir", type=Path, default=Path("build"))
parser.add_argument("--dist-dir", type=Path, default=Path("dist"))
args = parser.parse_args()
if not args.os_version and not args.all_os:
sys.exit("Укажите --os <версия> или --all-os")
spec = load_version_file(args.version_file)
os_list = list(spec.get("per_os", {}).keys()) if args.all_os else [args.os_version]
for os_version in os_list:
build_for_os(spec, os_version, args.build_dir, args.dist_dir)
if __name__ == "__main__":
main()