#!/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<name>{pkg_name}) in (?P<dir>{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 apply_patches(abs_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]) def build_package(repo_dir, package_dir, name, patch_dir=None, version=None, changelog=None, additional_content=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)) workdir = os.path.join(tempdir, pkg_dir) os.chdir(workdir) print(os.listdir()) if additional_content is not None: print("applying additional content") os.chdir(workdir) print(os.listdir()) for content in additional_content: print("applying {}".format(content)) os.chdir(content["target_dir"]) print(os.listdir()) if "patch_dir" in content: abs_patch_dir = os.path.join( repo_dir, content["patch_dir"]) apply_patches(abs_patch_dir) elif "git_url" in content: run_checked(["git", "clone", content["git_url"]]) else: print( "Unknown kind of additional content: {}".format( content)) 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 = ["apt-get", "build-dep", "-y", name] run_checked(command) 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) print(os.listdir(package_dir)) if __name__ == "__main__": main()