initial commit
This commit is contained in:
+147
@@ -0,0 +1,147 @@
|
||||
#!/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()
|
||||
Reference in New Issue
Block a user