#!/usr/bin/env python3 import locale locale.setlocale(locale.LC_ALL, "en_US.UTF-8") import os import subprocess as sp import re import shutil import tempfile import yaml def load_config(): with open("packages.yml", "r") as config_file: return yaml.load(config_file) PKG_NAME_PATTERN = r"[a-zA-Z0-9.+-]+" EPOCH_PATTERN = r"[0-9]+:" UPSTREAM_VERSION_PATTERN = r"[a-zA-Z0-9.+-:]+" DEBIAN_VERSION_PATTERN = r"[a-zA-Z0-9.+~]+" EXTRACT_PATTERN = (r"extracting (?P{pkg_name}) in (?P{pkg_name}-{version})" .format(pkg_name=PKG_NAME_PATTERN, version=UPSTREAM_VERSION_PATTERN)) def run_checked(command, **kwargs): try: return sp.run( command, check=True, stdout=sp.PIPE, stderr=sp.PIPE, universal_newlines=True, **kwargs) except sp.CalledProcessError as error: print(error.stdout) print(error.stderr) raise def build_package(repo_dir, package_dir, name, patch_dir=None, version=None, changelog=None): with tempfile.TemporaryDirectory(dir=os.path.abspath(".")) as tempdir: os.chdir(tempdir) result = run_checked(["apt-get", "source", name]) source_dir_candidates = [ line for line in result.stdout.splitlines() if "extracting" in line] if len(source_dir_candidates) != 1: raise ValueError("Got inconclusive candidate directories: {}".format( source_dir_candidates)) source_dir_match = re.search(EXTRACT_PATTERN, source_dir_candidates[0]) if source_dir_match is None: raise ValueError("Cannot get extraction directory from {}".format( source_dir_candidates[0])) pkg_dir = source_dir_match.group("dir") pkg_name = source_dir_match.group("name") if pkg_name != name: raise ValueError( "Extracted package {} is not expected package {}".format( pkg_name, name)) debian_workdir = os.path.join(tempdir, pkg_dir, "debian") os.chdir(debian_workdir) if patch_dir is not None: abs_patch_dir = os.path.join(repo_dir, patch_dir) for patch in sorted(os.listdir(abs_patch_dir)): patch_file = os.path.join(abs_patch_dir, patch) run_checked(["quilt", "import", patch_file]) command = ["debchange", "--preserve"] if version is None: command.append("--nmu") else: command.extend(["--newversion", version]) def _get_log_entries(): if changelog: yield changelog yield "Non-maintainer upload" for entry in _get_log_entries(): run_checked(command + [entry]) command = ["debuild", "-b", "-uc", "-us"] run_checked(command) deb_packages = [ filename for filename in os.listdir(tempdir) if filename.endswith(".deb") ] for filename in os.listdir(tempdir): if not filename.endswith(".deb"): continue shutil.move(os.path.join(tempdir, filename), package_dir) def main(): repo_dir = os.getcwd() config = load_config() maintainer = config.get("maintainer", None) if maintainer is not None: name = maintainer.get("name", None) mail = maintainer.get("mail", None) if name: os.environ["DEBFULLNAME"] = name if mail: os.environ["DEBEMAIL"] = mail package_dir = "packages" os.makedirs(package_dir, exist_ok=True) for package in config["packages"]: build_package(repo_dir=repo_dir, package_dir=package_dir, **package) os.chdir(repo_dir) if __name__ == "__main__": main()