Initial commit
This commit is contained in:
		
						commit
						aa1173f7d9
					
				
							
								
								
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@ -0,0 +1 @@
 | 
			
		||||
__pycache__
 | 
			
		||||
							
								
								
									
										408
									
								
								build-packages
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										408
									
								
								build-packages
									
									
									
									
									
										Executable file
									
								
							@ -0,0 +1,408 @@
 | 
			
		||||
#!/usr/bin/env python3
 | 
			
		||||
 | 
			
		||||
import subprocess
 | 
			
		||||
import io
 | 
			
		||||
import os
 | 
			
		||||
import sys
 | 
			
		||||
import yaml
 | 
			
		||||
import argparse
 | 
			
		||||
import logging
 | 
			
		||||
from datetime import datetime
 | 
			
		||||
import tarfile
 | 
			
		||||
import shutil
 | 
			
		||||
from git import Repo, GitCommandError
 | 
			
		||||
import tempfile
 | 
			
		||||
from concurrent.futures import ThreadPoolExecutor, wait, FIRST_COMPLETED
 | 
			
		||||
import fnmatch
 | 
			
		||||
import re
 | 
			
		||||
from debian.copyright import Header, Copyright
 | 
			
		||||
import uuid
 | 
			
		||||
from common import clean_old_logs
 | 
			
		||||
 | 
			
		||||
BASE_DIR = "/srv/lubuntu-ci/repos"
 | 
			
		||||
DEBFULLNAME = "Lugito"
 | 
			
		||||
DEBEMAIL = "info@lubuntu.me"
 | 
			
		||||
OUTPUT_DIR = os.path.join(BASE_DIR, "build_output")
 | 
			
		||||
SUPPRESSED_LINTIAN_TAGS = [
 | 
			
		||||
    "orig-tarball-missing-upstream-signature",
 | 
			
		||||
    "package-has-long-file-name",
 | 
			
		||||
    "adopted-extended-field"
 | 
			
		||||
]
 | 
			
		||||
BASE_OUTPUT_DIR = "/srv/lubuntu-ci/output"
 | 
			
		||||
LOG_DIR = os.path.join(BASE_OUTPUT_DIR, "logs", "source_builds")
 | 
			
		||||
BASE_LINTIAN_DIR = os.path.join(BASE_OUTPUT_DIR, f"lintian.tmp.{str(uuid.uuid4())[:8]}")
 | 
			
		||||
REAL_LINTIAN_DIR = os.path.join(BASE_OUTPUT_DIR, "lintian")
 | 
			
		||||
 | 
			
		||||
os.makedirs(LOG_DIR, exist_ok=True)
 | 
			
		||||
os.makedirs(OUTPUT_DIR, exist_ok=True)
 | 
			
		||||
os.makedirs(BASE_LINTIAN_DIR, exist_ok=True)
 | 
			
		||||
 | 
			
		||||
logging.basicConfig(
 | 
			
		||||
    level=logging.INFO,
 | 
			
		||||
    format="%(asctime)s - %(levelname)s - %(message)s",
 | 
			
		||||
    handlers=[
 | 
			
		||||
        logging.FileHandler(log_file),
 | 
			
		||||
        logging.StreamHandler()
 | 
			
		||||
    ]
 | 
			
		||||
)
 | 
			
		||||
logger = logging.getLogger("TimeBasedLogger")
 | 
			
		||||
 | 
			
		||||
def run_command(cmd, cwd=None, env=None, show_output=False):
 | 
			
		||||
    logging.info(f"Executing: {' '.join(cmd)} in {cwd or 'current directory'}")
 | 
			
		||||
    try:
 | 
			
		||||
        result = subprocess.run(
 | 
			
		||||
            cmd,
 | 
			
		||||
            cwd=cwd,
 | 
			
		||||
            env=env,
 | 
			
		||||
            check=True,
 | 
			
		||||
            capture_output=True,
 | 
			
		||||
            text=True
 | 
			
		||||
        )
 | 
			
		||||
        if show_output:
 | 
			
		||||
            if result.stdout:
 | 
			
		||||
                logging.info(f"Output: {result.stdout.strip()}")
 | 
			
		||||
            if result.stderr:
 | 
			
		||||
                logging.warning(f"Output: {result.stderr.strip()}")
 | 
			
		||||
        logging.info(f"Command succeeded: {' '.join(cmd)}")
 | 
			
		||||
    except subprocess.CalledProcessError as e:
 | 
			
		||||
        logging.error(f"Command failed: {' '.join(cmd)}")
 | 
			
		||||
        logging.error(e.stderr)
 | 
			
		||||
        raise
 | 
			
		||||
 | 
			
		||||
def parse_version(changelog_path):
 | 
			
		||||
    try:
 | 
			
		||||
        with open(changelog_path, "r") as f:
 | 
			
		||||
            first_line = f.readline().strip()
 | 
			
		||||
        version_match = first_line.split("(")[1].split(")")[0]
 | 
			
		||||
        # Remove Debian revision
 | 
			
		||||
        upstream_version = version_match.split("-")[0]
 | 
			
		||||
        # Remove '+git...' and '~release' if present
 | 
			
		||||
        upstream_version = re.sub(r'(\+git[0-9]+)?(~[a-z]+)?$', '', upstream_version)
 | 
			
		||||
        logging.info(f"Upstream version extracted: {upstream_version}")
 | 
			
		||||
        current_date = datetime.now().strftime("%Y%m%d%H%M")
 | 
			
		||||
        version = f"{upstream_version}+git{current_date}"
 | 
			
		||||
        logging.info(f"Parsed VERSION: {version}")
 | 
			
		||||
        return version
 | 
			
		||||
    except (IndexError, FileNotFoundError) as e:
 | 
			
		||||
        logging.error(f"Error parsing version from {changelog_path}: {e}")
 | 
			
		||||
        raise
 | 
			
		||||
 | 
			
		||||
def get_exclusions(packaging):
 | 
			
		||||
    exclusions = []
 | 
			
		||||
    with io.open(os.path.join(packaging, "debian/copyright"), "rt", encoding="utf-8") as f:
 | 
			
		||||
        copyright_obj = Copyright(f)
 | 
			
		||||
        for paragraph in copyright_obj.all_paragraphs():
 | 
			
		||||
            if isinstance(paragraph, Header):
 | 
			
		||||
                if paragraph.files_excluded:
 | 
			
		||||
                    for file_name in paragraph.files_excluded:
 | 
			
		||||
                        exclusions.append(file_name)
 | 
			
		||||
                break
 | 
			
		||||
    return exclusions
 | 
			
		||||
 | 
			
		||||
def create_tarball(name, source_dir, exclusions=[]):
 | 
			
		||||
    tar_filename = f"{name}_MAIN.orig.tar.gz"
 | 
			
		||||
    logging.info(f"Creating tarball: {tar_filename}")
 | 
			
		||||
    exclusions.append(".git/")
 | 
			
		||||
 | 
			
		||||
    def exclusion_func(tarinfo):
 | 
			
		||||
        for exclusion in exclusions:
 | 
			
		||||
            if exclusion in tarinfo.name:
 | 
			
		||||
                return None
 | 
			
		||||
 | 
			
		||||
        return tarinfo
 | 
			
		||||
 | 
			
		||||
    with tarfile.open(tar_filename, "w:gz") as tar:
 | 
			
		||||
        tar.add(source_dir, arcname=os.path.basename(source_dir), filter=exclusion_func)
 | 
			
		||||
    logging.info(f"Tarball created and compressed: {tar_filename}")
 | 
			
		||||
 | 
			
		||||
def update_changelog(packaging_dir, release, version, env):
 | 
			
		||||
    name = os.path.basename(packaging_dir)
 | 
			
		||||
    logging.info(f"Updating changelog for {name} to version {version}-0ubuntu1~ppa1")
 | 
			
		||||
    run_command(["git", "checkout", "debian/changelog"], cwd=packaging_dir)
 | 
			
		||||
    cmd = [
 | 
			
		||||
        "dch",
 | 
			
		||||
        "--distribution", release,
 | 
			
		||||
        "--package", name,
 | 
			
		||||
        "--newversion", f"{version}-0ubuntu1~ppa1",
 | 
			
		||||
        "--urgency", "low",
 | 
			
		||||
        "CI upload."
 | 
			
		||||
    ]
 | 
			
		||||
    run_command(cmd, cwd=packaging_dir, env=env)
 | 
			
		||||
 | 
			
		||||
def build_package(packaging_dir, env):
 | 
			
		||||
    name = os.path.basename(packaging_dir)
 | 
			
		||||
    logging.info(f"Building source package for {name}")
 | 
			
		||||
 | 
			
		||||
    temp_dir = tempfile.mkdtemp()
 | 
			
		||||
    try:
 | 
			
		||||
        temp_packaging_dir = os.path.join(temp_dir, name)
 | 
			
		||||
        os.makedirs(temp_packaging_dir, exist_ok=True)
 | 
			
		||||
        shutil.copytree(packaging_dir + "/debian", temp_packaging_dir + "/debian")
 | 
			
		||||
 | 
			
		||||
        tarball_name = f"{name}_{env['VERSION']}.orig.tar.gz"
 | 
			
		||||
        tarball_source = os.path.join(BASE_DIR, tarball_name)
 | 
			
		||||
        tarball_dest = os.path.join(temp_dir, tarball_name)
 | 
			
		||||
        shutil.copyfile(tarball_source, tarball_dest)
 | 
			
		||||
 | 
			
		||||
        cmd_build = ["debuild", "--no-lintian", "-S", "-d", "-sa"]
 | 
			
		||||
        run_command(cmd_build, cwd=temp_packaging_dir, env=env)
 | 
			
		||||
        run_command(["git", "checkout", "debian/changelog"], cwd=packaging_dir)
 | 
			
		||||
 | 
			
		||||
        pattern = f"{name}_{env['VERSION']}*"
 | 
			
		||||
        for filename in os.listdir(temp_dir):
 | 
			
		||||
            if fnmatch.fnmatch(filename, pattern):
 | 
			
		||||
                source_file = os.path.join(temp_dir, filename)
 | 
			
		||||
                dest_file = os.path.join(OUTPUT_DIR, filename)
 | 
			
		||||
                shutil.copyfile(source_file, dest_file)
 | 
			
		||||
                logging.info(f"Copied {filename} to {OUTPUT_DIR}")
 | 
			
		||||
 | 
			
		||||
        changes_files = [f for f in os.listdir(OUTPUT_DIR) if f.startswith(f"{name}_{env['VERSION']}") and f.endswith("_source.changes")]
 | 
			
		||||
        if changes_files:
 | 
			
		||||
            changes_file = os.path.join(OUTPUT_DIR, changes_files[-1])
 | 
			
		||||
            logging.info(f"Built package, changes file: {changes_file}")
 | 
			
		||||
            return changes_file
 | 
			
		||||
        else:
 | 
			
		||||
            logging.error("No changes file found after build.")
 | 
			
		||||
            raise FileNotFoundError("Changes file not found.")
 | 
			
		||||
    finally:
 | 
			
		||||
        shutil.rmtree(temp_dir)
 | 
			
		||||
 | 
			
		||||
def load_config(config_path):
 | 
			
		||||
    try:
 | 
			
		||||
        with open(config_path, "r") as f:
 | 
			
		||||
            config = yaml.safe_load(f)
 | 
			
		||||
        if "packages" not in config or "releases" not in config:
 | 
			
		||||
            raise ValueError("Config file must contain 'packages' and 'releases' sections.")
 | 
			
		||||
        return config
 | 
			
		||||
    except Exception as e:
 | 
			
		||||
        logging.error(f"Error loading config file: {e}")
 | 
			
		||||
        sys.exit(1)
 | 
			
		||||
 | 
			
		||||
def clone_or_update_repo(destination, repo_url, repo_branch=None):
 | 
			
		||||
    if os.path.exists(destination):
 | 
			
		||||
        logging.info(f"Repository already exists at {destination}, checking branch and remote URL.")
 | 
			
		||||
        try:
 | 
			
		||||
            repo = Repo(destination)
 | 
			
		||||
 | 
			
		||||
            current_remote_url = repo.remotes.origin.url
 | 
			
		||||
            if current_remote_url != repo_url:
 | 
			
		||||
                logging.info(f"Remote URL differs for {destination}. Removing and recloning.")
 | 
			
		||||
                shutil.rmtree(destination)
 | 
			
		||||
            else:
 | 
			
		||||
                repo.git.reset("--hard", "HEAD")
 | 
			
		||||
                current_branch = repo.active_branch.name
 | 
			
		||||
                if repo_branch and current_branch != repo_branch:
 | 
			
		||||
                    logging.info(f"Branch differs for {destination}. Removing and recloning.")
 | 
			
		||||
                    shutil.rmtree(destination)
 | 
			
		||||
                else:
 | 
			
		||||
                    logging.info(f"Repository matches desired remote and branch, pulling updates.")
 | 
			
		||||
                    repo.git.checkout(repo_branch or current_branch)
 | 
			
		||||
                    try:
 | 
			
		||||
                        repo.remotes.origin.pull()
 | 
			
		||||
                        repo.submodule_update(recursive=True)
 | 
			
		||||
                        logging.info(f"Pulled latest changes for {destination}")
 | 
			
		||||
                    except GitCommandError as e:
 | 
			
		||||
                        if 'non-fast-forward' in str(e):
 | 
			
		||||
                            logging.error(f"Pull failed due to non-fast-forward update: {e}")
 | 
			
		||||
                            logging.info(f"Removing repository {destination} and cloning again.")
 | 
			
		||||
                            shutil.rmtree(destination)
 | 
			
		||||
                        else:
 | 
			
		||||
                            logging.error(f"Pull failed for {destination}: {e}")
 | 
			
		||||
                            raise
 | 
			
		||||
                    else:
 | 
			
		||||
                        return
 | 
			
		||||
        except Exception as e:
 | 
			
		||||
            logging.error(f"Error updating repository {destination}: {e}")
 | 
			
		||||
            logging.info(f"Removing repository {destination} and cloning again.")
 | 
			
		||||
            shutil.rmtree(destination)
 | 
			
		||||
    try:
 | 
			
		||||
        logging.info(f"Cloning repository {repo_url} into {destination}")
 | 
			
		||||
        repo = Repo.clone_from(repo_url, destination, recurse_submodules=True)
 | 
			
		||||
        if repo_branch:
 | 
			
		||||
            repo.git.checkout(repo_branch)
 | 
			
		||||
            logging.info(f"Checked out {repo_branch} in {destination}")
 | 
			
		||||
    except GitCommandError as e:
 | 
			
		||||
        logging.error(f"Git clone failed for {repo_url}: {e}")
 | 
			
		||||
        raise
 | 
			
		||||
 | 
			
		||||
def publish_lintian():
 | 
			
		||||
    if os.path.exists(BASE_LINTIAN_DIR):
 | 
			
		||||
        for root, dirs, files in os.walk(BASE_LINTIAN_DIR):
 | 
			
		||||
            for file in files:
 | 
			
		||||
                # Determine the source and destination paths
 | 
			
		||||
                src_path = os.path.join(root, file)
 | 
			
		||||
                rel_path = os.path.relpath(src_path, BASE_LINTIAN_DIR)
 | 
			
		||||
                dest_path = os.path.join(REAL_LINTIAN_DIR, rel_path)
 | 
			
		||||
 | 
			
		||||
                # Ensure the destination directory exists
 | 
			
		||||
                os.makedirs(os.path.dirname(dest_path), exist_ok=True)
 | 
			
		||||
 | 
			
		||||
                # Copy the file
 | 
			
		||||
                shutil.copy2(src_path, dest_path)
 | 
			
		||||
 | 
			
		||||
        # Remove the temporary directory
 | 
			
		||||
        shutil.rmtree(BASE_LINTIAN_DIR)
 | 
			
		||||
 | 
			
		||||
def run_source_lintian(name, sources_path):
 | 
			
		||||
    logging.info(f"Running Lintian for {name}")
 | 
			
		||||
    with tempfile.NamedTemporaryFile(mode='w+', suffix='.txt') as temp_file:
 | 
			
		||||
        temp_file.write("\n".join(SUPPRESSED_LINTIAN_TAGS))
 | 
			
		||||
        temp_file.flush()
 | 
			
		||||
        temp_file_path = temp_file.name
 | 
			
		||||
 | 
			
		||||
        cmd = [
 | 
			
		||||
            "lintian",
 | 
			
		||||
            "-EvIL",
 | 
			
		||||
            "+pedantic",
 | 
			
		||||
            "--suppress-tags-from-file",
 | 
			
		||||
            f"{temp_file_path}",
 | 
			
		||||
            sources_path
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
        result = subprocess.run(
 | 
			
		||||
            cmd,
 | 
			
		||||
            capture_output=True,
 | 
			
		||||
            text=True
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    stderr, stdout = None, None
 | 
			
		||||
    if result.stderr:
 | 
			
		||||
        stderr = result.stderr.strip()
 | 
			
		||||
    if result.stdout:
 | 
			
		||||
        stdout = result.stdout.strip()
 | 
			
		||||
 | 
			
		||||
    lintian_output = None
 | 
			
		||||
    if stderr == stdout:
 | 
			
		||||
        lintian_output = stderr
 | 
			
		||||
    else:
 | 
			
		||||
        lintian_output = f"{stderr}\n{stdout}".strip()
 | 
			
		||||
 | 
			
		||||
    if lintian_output:
 | 
			
		||||
        pkgdir = os.path.join(BASE_LINTIAN_DIR, name)
 | 
			
		||||
        if not os.path.exists(pkgdir):
 | 
			
		||||
            os.mkdir(pkgdir)
 | 
			
		||||
        output_file = os.path.join(pkgdir, "source.txt")
 | 
			
		||||
        with open(output_file, "a") as f:
 | 
			
		||||
            f.write(lintian_output)
 | 
			
		||||
 | 
			
		||||
    logging.info(f"Lintian run for {name} is complete")
 | 
			
		||||
 | 
			
		||||
def main():
 | 
			
		||||
    parser = argparse.ArgumentParser(description="Automate Lubuntu package builds.")
 | 
			
		||||
    parser.add_argument("config", help="Path to the YAML configuration file.")
 | 
			
		||||
    parser.add_argument("--skip-dput", action="store_true", help="Skip the dput upload step.")
 | 
			
		||||
    parser.add_argument("--skip-cleanup", action="store_true", help="Skip removal of build_output.")
 | 
			
		||||
    args = parser.parse_args()
 | 
			
		||||
 | 
			
		||||
    config = load_config(args.config)
 | 
			
		||||
    packages = config["packages"]
 | 
			
		||||
    releases = config["releases"]
 | 
			
		||||
 | 
			
		||||
    os.makedirs(BASE_DIR, exist_ok=True)
 | 
			
		||||
    logging.info(f"Using base directory: {BASE_DIR}")
 | 
			
		||||
    os.chdir(BASE_DIR)
 | 
			
		||||
 | 
			
		||||
    with ThreadPoolExecutor(max_workers=5) as executor:
 | 
			
		||||
        def dput_source(name, upload_target, changes_files, devel_changes_files):
 | 
			
		||||
            if changes_files:
 | 
			
		||||
                hr_changes = ", ".join(changes_files)
 | 
			
		||||
                logging.info(f"Uploading {hr_changes} to {upload_target} using dput")
 | 
			
		||||
                cmd_upload = ["dput", upload_target] + changes_files
 | 
			
		||||
                run_command(cmd_upload, cwd=OUTPUT_DIR)
 | 
			
		||||
                logging.info(f"Completed upload of {hr_changes} to {upload_target}")
 | 
			
		||||
 | 
			
		||||
                for file in devel_changes_files:
 | 
			
		||||
                    if file:
 | 
			
		||||
                        futures.add(executor.submit(run_source_lintian, name, file))
 | 
			
		||||
 | 
			
		||||
        def prepare_package(pkg):
 | 
			
		||||
            name = pkg.get("name")
 | 
			
		||||
            if not name:
 | 
			
		||||
                logging.warning(f"Skipping package due to missing name: {pkg}")
 | 
			
		||||
                return
 | 
			
		||||
            upstream_url = pkg.get("upstream_url") or f"https://github.com/lxqt/{name}.git"
 | 
			
		||||
            upstream_destination = os.path.join(BASE_DIR, f"upstream-{name}")
 | 
			
		||||
            clone_or_update_repo(upstream_destination, upstream_url)
 | 
			
		||||
            packaging_url = pkg.get("packaging_url") or f"https://git.lubuntu.me/Lubuntu/{name}-packaging.git"
 | 
			
		||||
            packaging_branch = pkg.get("packaging_branch") or f"ubuntu/{releases[0]}" if releases else None
 | 
			
		||||
            packaging_destination = os.path.join(BASE_DIR, name)
 | 
			
		||||
            clone_or_update_repo(packaging_destination, packaging_url, packaging_branch)
 | 
			
		||||
            exclusions = get_exclusions(packaging_destination)
 | 
			
		||||
            create_tarball(name, upstream_destination, exclusions)
 | 
			
		||||
            run_command(["update-maintainer"], cwd=packaging_destination)
 | 
			
		||||
            futures.add(executor.submit(process_package, pkg))
 | 
			
		||||
 | 
			
		||||
        def process_package(pkg):
 | 
			
		||||
            name = pkg.get("name")
 | 
			
		||||
            upload_target = pkg.get("upload_target", "ppa:lubuntu-ci/unstable-ci-proposed")
 | 
			
		||||
 | 
			
		||||
            if not name:
 | 
			
		||||
                logging.warning(f"Skipping package due to missing name: {pkg}")
 | 
			
		||||
                return []
 | 
			
		||||
 | 
			
		||||
            package_changes = []
 | 
			
		||||
 | 
			
		||||
            packaging_destination = os.path.join(BASE_DIR, name)
 | 
			
		||||
            changelog_path = os.path.join(packaging_destination, "debian", "changelog")
 | 
			
		||||
            version = parse_version(changelog_path)
 | 
			
		||||
 | 
			
		||||
            for release in releases:
 | 
			
		||||
                logging.info(f"Building {name} for {release}")
 | 
			
		||||
                try:
 | 
			
		||||
                    release_version = f"{version}~{release}"
 | 
			
		||||
                    tarball_name = f"{name}_{release_version}.orig.tar.gz"
 | 
			
		||||
                    tarball_source = os.path.join(BASE_DIR, f"{name}_MAIN.orig.tar.gz")
 | 
			
		||||
                    tarball_dest = os.path.join(BASE_DIR, tarball_name)
 | 
			
		||||
                    shutil.copyfile(tarball_source, tarball_dest)
 | 
			
		||||
 | 
			
		||||
                    env = os.environ.copy()
 | 
			
		||||
                    env["DEBFULLNAME"] = DEBFULLNAME
 | 
			
		||||
                    env["DEBEMAIL"] = DEBEMAIL
 | 
			
		||||
                    env["VERSION"] = release_version
 | 
			
		||||
                    env["UPLOAD_TARGET"] = upload_target
 | 
			
		||||
 | 
			
		||||
                    # Update changelog and build package
 | 
			
		||||
                    update_changelog(packaging_destination, release, release_version, env)
 | 
			
		||||
                    changes_file = build_package(packaging_destination, env)
 | 
			
		||||
                    if changes_file:
 | 
			
		||||
                        package_changes.append((changes_file, env))
 | 
			
		||||
                    os.remove(os.path.join(BASE_DIR, tarball_name))
 | 
			
		||||
 | 
			
		||||
                except Exception as e:
 | 
			
		||||
                    logging.error(f"Error processing package '{name}' for release '{release}': {e}")
 | 
			
		||||
 | 
			
		||||
            changes_files = [os.path.basename(cf) for cf, env in package_changes]
 | 
			
		||||
            devel_changes_files = set(os.path.join(OUTPUT_DIR, file) if releases[0] in file else None for file in changes_files)
 | 
			
		||||
            if args.skip_dput:
 | 
			
		||||
                for changes_file in devel_changes_files:
 | 
			
		||||
                    if changes_file:
 | 
			
		||||
                        futures.add(executor.submit(run_source_lintian, name, changes_file))
 | 
			
		||||
            else:
 | 
			
		||||
                upload_target = package_changes[0][1]["UPLOAD_TARGET"]
 | 
			
		||||
                futures.add(executor.submit(dput_source, name, upload_target, changes_files, devel_changes_files))
 | 
			
		||||
 | 
			
		||||
            os.remove(os.path.join(BASE_DIR, f"{name}_MAIN.orig.tar.gz"))
 | 
			
		||||
 | 
			
		||||
        futures = set(executor.submit(prepare_package, pkg) for pkg in packages)
 | 
			
		||||
 | 
			
		||||
        while futures:
 | 
			
		||||
            done, not_done = wait(futures, return_when=FIRST_COMPLETED)
 | 
			
		||||
 | 
			
		||||
            for future in done:
 | 
			
		||||
                try:
 | 
			
		||||
                    result = future.result()
 | 
			
		||||
                except Exception as e:
 | 
			
		||||
                    logging.exception("Task generated an exception")
 | 
			
		||||
                finally:
 | 
			
		||||
                    futures.remove(future)
 | 
			
		||||
 | 
			
		||||
    if not args.skip_cleanup:
 | 
			
		||||
        shutil.rmtree(OUTPUT_DIR)
 | 
			
		||||
    logging.info("Publishing Lintian output...")
 | 
			
		||||
    publish_lintian()
 | 
			
		||||
    clean_old_logs(LOG_DIR)
 | 
			
		||||
 | 
			
		||||
    logging.info("Script completed successfully.")
 | 
			
		||||
 | 
			
		||||
if __name__ == "__main__":
 | 
			
		||||
    main()
 | 
			
		||||
							
								
								
									
										28
									
								
								common.py
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										28
									
								
								common.py
									
									
									
									
									
										Executable file
									
								
							@ -0,0 +1,28 @@
 | 
			
		||||
#!/usr/bin/env python3
 | 
			
		||||
#
 | 
			
		||||
# Copyright (C) 2024 Simon Quigley <tsimonq2@ubuntu.com>
 | 
			
		||||
#
 | 
			
		||||
# This program is free software: you can redistribute it and/or modify
 | 
			
		||||
# it under the terms of the GNU General Public License as published by
 | 
			
		||||
# the Free Software Foundation, either version 3 of the License, or
 | 
			
		||||
# (at your option) any later version.
 | 
			
		||||
#
 | 
			
		||||
# This program is distributed in the hope that it will be useful,
 | 
			
		||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
# GNU General Public License for more details.
 | 
			
		||||
#
 | 
			
		||||
# You should have received a copy of the GNU General Public License
 | 
			
		||||
# along with this program.  If not, see <https://www.gnu.org/licenses/>.
 | 
			
		||||
 | 
			
		||||
import os
 | 
			
		||||
from datetime import datetime
 | 
			
		||||
 | 
			
		||||
def clean_old_logs(log_dir, max_age_seconds=86400):
 | 
			
		||||
    now = datetime.utcnow()
 | 
			
		||||
    for file_name in os.listdir(log_dir):
 | 
			
		||||
        file_path = os.path.join(log_dir, file_name)
 | 
			
		||||
        if os.path.isfile(file_path):
 | 
			
		||||
            file_age = now - os.path.getmtime(file_path)
 | 
			
		||||
            if file_age > max_age_seconds:
 | 
			
		||||
                os.remove(file_path)
 | 
			
		||||
							
								
								
									
										126
									
								
								configs/britney.conf
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										126
									
								
								configs/britney.conf
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,126 @@
 | 
			
		||||
# Configuration file for britney
 | 
			
		||||
 | 
			
		||||
# Paths for control files
 | 
			
		||||
TESTING           = data/%(SERIES)
 | 
			
		||||
UNSTABLE          = data/%(SERIES)-proposed
 | 
			
		||||
PARTIAL_UNSTABLE  = yes
 | 
			
		||||
 | 
			
		||||
# Output
 | 
			
		||||
NONINST_STATUS    = data/%(SERIES)/non-installable-status
 | 
			
		||||
EXCUSES_OUTPUT    = output/%(SERIES)/excuses.html
 | 
			
		||||
EXCUSES_YAML_OUTPUT = output/%(SERIES)/excuses.yaml.xz
 | 
			
		||||
UPGRADE_OUTPUT    = output/%(SERIES)/output.txt
 | 
			
		||||
HEIDI_OUTPUT      = output/%(SERIES)/HeidiResult
 | 
			
		||||
 | 
			
		||||
# External policy/constraints/faux-packages information that
 | 
			
		||||
# (presumably) rarely changes.  Examples include "constraints".
 | 
			
		||||
STATIC_INPUT_DIR = data/%(SERIES)/input
 | 
			
		||||
 | 
			
		||||
# Directory for input files that Britney will update herself
 | 
			
		||||
# (e.g. aging information) or will need regular updates
 | 
			
		||||
# (e.g. urgency information).
 | 
			
		||||
STATE_DIR          = data/%(SERIES)/state
 | 
			
		||||
 | 
			
		||||
# List of architectures that Britney should consider.
 | 
			
		||||
# - defaults to the value in testing's Release file (if it is present).
 | 
			
		||||
# - Required for the legacy layout.
 | 
			
		||||
ARCHITECTURES     = amd64 arm64 armhf ppc64el riscv64 s390x
 | 
			
		||||
 | 
			
		||||
# if you're not in this list, arch: all packages are allowed to break on you
 | 
			
		||||
NOBREAKALL_ARCHES = amd64
 | 
			
		||||
 | 
			
		||||
# primary architecture used for checking Build-Depends-Indep
 | 
			
		||||
ALL_BUILDARCH     = amd64
 | 
			
		||||
 | 
			
		||||
# is arch-all built separately? i.e. can it fail independently of another arch?
 | 
			
		||||
HAS_ARCH_ALL_BUILDDS = no
 | 
			
		||||
 | 
			
		||||
# if you're in this list, your packages may not stay in sync with the source
 | 
			
		||||
OUTOFSYNC_ARCHES  =
 | 
			
		||||
 | 
			
		||||
# if you're in this list, your uninstallability count may increase
 | 
			
		||||
BREAK_ARCHES      =
 | 
			
		||||
 | 
			
		||||
# if you're in this list, you are a new architecture
 | 
			
		||||
NEW_ARCHES        =
 | 
			
		||||
 | 
			
		||||
# priorities and delays
 | 
			
		||||
MINDAYS_LOW       = 0
 | 
			
		||||
MINDAYS_MEDIUM    = 0
 | 
			
		||||
MINDAYS_HIGH      = 0
 | 
			
		||||
MINDAYS_CRITICAL  = 0
 | 
			
		||||
MINDAYS_EMERGENCY = 0
 | 
			
		||||
DEFAULT_URGENCY   = medium
 | 
			
		||||
NO_PENALTIES      = high critical emergency
 | 
			
		||||
BOUNTY_MIN_AGE    = 2
 | 
			
		||||
 | 
			
		||||
HINTSDIR = data/%(SERIES)-proposed/Hints
 | 
			
		||||
 | 
			
		||||
# hint permissions
 | 
			
		||||
HINTS_LANEY        = ALL
 | 
			
		||||
HINTS_STEFANOR     = ALL
 | 
			
		||||
HINTS_STGRABER     = ALL
 | 
			
		||||
HINTS_VORLON       = ALL
 | 
			
		||||
HINTS_PITTI        = ALL
 | 
			
		||||
HINTS_UBUNTU-RELEASE = ALL
 | 
			
		||||
# Kernel team automated testing
 | 
			
		||||
HINTS_KERNEL-TESTING = block unblock
 | 
			
		||||
# SRU team
 | 
			
		||||
HINTS_APW          = ALL
 | 
			
		||||
HINTS_ARGES        = ALL
 | 
			
		||||
HINTS_BRIAN-MURRAY = ALL
 | 
			
		||||
HINTS_RACB         = ALL
 | 
			
		||||
HINTS_RAOF         = ALL
 | 
			
		||||
HINTS_SIL2100      = ALL
 | 
			
		||||
HINTS_TJAALTON     = ALL
 | 
			
		||||
HINTS_UBUNTU-SRU   = ALL
 | 
			
		||||
HINTS_FREEZE       = block block-all
 | 
			
		||||
 | 
			
		||||
# support for old libraries in testing (smooth update)
 | 
			
		||||
# use ALL to enable smooth updates for all the sections
 | 
			
		||||
#
 | 
			
		||||
# naming a non-existent section will effectively disable new smooth
 | 
			
		||||
# updates but still allow removals to occur
 | 
			
		||||
SMOOTH_UPDATES    = libs oldlibs
 | 
			
		||||
 | 
			
		||||
IGNORE_CRUFT      = 0
 | 
			
		||||
 | 
			
		||||
REMOVE_OBSOLETE   = no
 | 
			
		||||
 | 
			
		||||
CHECK_BUILDD      = nxo
 | 
			
		||||
 | 
			
		||||
IMPLICIT_DEPS     = no
 | 
			
		||||
 | 
			
		||||
ADT_ENABLE        = no
 | 
			
		||||
#ADT_ARCHES        = amd64 i386 armhf ppc64el arm64
 | 
			
		||||
#ADT_AMQP          = amqp://test_request:password@autopkgtest-amqp.internal
 | 
			
		||||
# space separate list of PPAs to add for test requests and for polling results;
 | 
			
		||||
# the *last* one determines the swift container name
 | 
			
		||||
ADT_PPAS          =
 | 
			
		||||
# set this to the path of a (r/o) autopkgtest-results.cache for running many parallel
 | 
			
		||||
# britney instances for PPAs without updating the cache
 | 
			
		||||
ADT_SHARED_RESULTS_CACHE =
 | 
			
		||||
# Swift base URL with the results (must be publicly readable and browsable)
 | 
			
		||||
# or file location if results are pre-fetched
 | 
			
		||||
#ADT_SWIFT_URL     = https://objectstorage.prodstack5.canonical.com/swift/v1/AUTH_0f9aae918d5b4744bf7b827671c86842/
 | 
			
		||||
# Base URL for autopkgtest site, used for links in the excuses
 | 
			
		||||
#ADT_CI_URL        = https://autopkgtest.ubuntu.com/
 | 
			
		||||
# URL for the autopkgtest database, if used
 | 
			
		||||
#ADT_DB_URL        = https://autopkgtest.ubuntu.com/static/autopkgtest.db
 | 
			
		||||
#ADT_HUGE          = 20
 | 
			
		||||
 | 
			
		||||
# Autopkgtest results can be used to influence the aging
 | 
			
		||||
ADT_REGRESSION_PENALTY =
 | 
			
		||||
ADT_SUCCESS_BOUNTY     =
 | 
			
		||||
ADT_BASELINE           = reference
 | 
			
		||||
ADT_RETRY_URL_MECH     =
 | 
			
		||||
ADT_RETRY_OLDER_THAN   =
 | 
			
		||||
ADT_REFERENCE_MAX_AGE  =
 | 
			
		||||
 | 
			
		||||
# email uploaders for stuck uploads
 | 
			
		||||
EMAIL_ENABLE     = no
 | 
			
		||||
# email SRU bugs when regressions are detected
 | 
			
		||||
SRUREGRESSIONEMAIL_ENABLE = no
 | 
			
		||||
 | 
			
		||||
# we don't run piuparts testing in Ubuntu
 | 
			
		||||
PIUPARTS_ENABLE = no
 | 
			
		||||
							
								
								
									
										10
									
								
								configs/cmake.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								configs/cmake.yaml
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,10 @@
 | 
			
		||||
packages:
 | 
			
		||||
  - name: cmake
 | 
			
		||||
    upstream_url: "https://gitlab.kitware.com/cmake/cmake.git"
 | 
			
		||||
    packaging_url: "https://git.lubuntu.me/Lubuntu/cmake.git"
 | 
			
		||||
    packaging_branch: "ci/unstable"
 | 
			
		||||
 | 
			
		||||
releases:
 | 
			
		||||
  - plucky
 | 
			
		||||
  - oracular
 | 
			
		||||
  - noble
 | 
			
		||||
							
								
								
									
										306
									
								
								configs/kde.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										306
									
								
								configs/kde.yaml
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,306 @@
 | 
			
		||||
packages:
 | 
			
		||||
  - name: kf6-extra-cmake-modules
 | 
			
		||||
    upstream_url: "https://invent.kde.org/frameworks/extra-cmake-modules.git"
 | 
			
		||||
    packaging_url: "https://git.launchpad.net/~kubuntu-packagers/kubuntu-packaging/+git/kf6-extra-cmake-modules"
 | 
			
		||||
    packaging_branch: "kubuntu_unstable"
 | 
			
		||||
  - name: kf6-attica
 | 
			
		||||
    upstream_url: "https://invent.kde.org/frameworks/attica.git"
 | 
			
		||||
    packaging_url: "https://git.launchpad.net/~kubuntu-packagers/kubuntu-packaging/+git/kf6-attica"
 | 
			
		||||
    packaging_branch: "kubuntu_unstable"
 | 
			
		||||
  - name: kf6-baloo
 | 
			
		||||
    upstream_url: "https://invent.kde.org/frameworks/baloo.git"
 | 
			
		||||
    packaging_url: "https://git.launchpad.net/~kubuntu-packagers/kubuntu-packaging/+git/kf6-baloo"
 | 
			
		||||
    packaging_branch: "kubuntu_unstable"
 | 
			
		||||
  - name: kf6-bluez-qt
 | 
			
		||||
    upstream_url: "https://invent.kde.org/frameworks/bluez-qt.git"
 | 
			
		||||
    packaging_url: "https://git.launchpad.net/~kubuntu-packagers/kubuntu-packaging/+git/kf6-bluez-qt"
 | 
			
		||||
    packaging_branch: "kubuntu_unstable"
 | 
			
		||||
  - name: kf6-breeze-icons
 | 
			
		||||
    upstream_url: "https://invent.kde.org/frameworks/breeze-icons.git"
 | 
			
		||||
    packaging_url: "https://git.launchpad.net/~kubuntu-packagers/kubuntu-packaging/+git/kf6-breeze-icons"
 | 
			
		||||
    packaging_branch: "kubuntu_unstable"
 | 
			
		||||
  - name: kf6-frameworkintegration
 | 
			
		||||
    upstream_url: "https://invent.kde.org/frameworks/frameworkintegration.git"
 | 
			
		||||
    packaging_url: "https://git.launchpad.net/~kubuntu-packagers/kubuntu-packaging/+git/kf6-frameworkintegration"
 | 
			
		||||
    packaging_branch: "kubuntu_unstable"
 | 
			
		||||
  - name: kf6-kapidox
 | 
			
		||||
    upstream_url: "https://invent.kde.org/frameworks/kapidox.git"
 | 
			
		||||
    packaging_url: "https://git.launchpad.net/~kubuntu-packagers/kubuntu-packaging/+git/kf6-kapidox"
 | 
			
		||||
    packaging_branch: "kubuntu_unstable"
 | 
			
		||||
  - name: kf6-karchive
 | 
			
		||||
    upstream_url: "https://invent.kde.org/frameworks/karchive.git"
 | 
			
		||||
    packaging_url: "https://git.launchpad.net/~kubuntu-packagers/kubuntu-packaging/+git/kf6-karchive"
 | 
			
		||||
    packaging_branch: "kubuntu_unstable"
 | 
			
		||||
  - name: kf6-kauth
 | 
			
		||||
    upstream_url: "https://invent.kde.org/frameworks/kauth.git"
 | 
			
		||||
    packaging_url: "https://git.launchpad.net/~kubuntu-packagers/kubuntu-packaging/+git/kf6-kauth"
 | 
			
		||||
    packaging_branch: "kubuntu_unstable"
 | 
			
		||||
  - name: kf6-kbookmarks
 | 
			
		||||
    upstream_url: "https://invent.kde.org/frameworks/kbookmarks.git"
 | 
			
		||||
    packaging_url: "https://git.launchpad.net/~kubuntu-packagers/kubuntu-packaging/+git/kf6-kbookmarks"
 | 
			
		||||
    packaging_branch: "kubuntu_unstable"
 | 
			
		||||
  - name: kf6-kcalendarcore
 | 
			
		||||
    upstream_url: "https://invent.kde.org/frameworks/kcalendarcore.git"
 | 
			
		||||
    packaging_url: "https://git.launchpad.net/~kubuntu-packagers/kubuntu-packaging/+git/kf6-kcalendarcore"
 | 
			
		||||
    packaging_branch: "kubuntu_unstable"
 | 
			
		||||
  - name: kf6-kcmutils
 | 
			
		||||
    upstream_url: "https://invent.kde.org/frameworks/kcmutils.git"
 | 
			
		||||
    packaging_url: "https://git.launchpad.net/~kubuntu-packagers/kubuntu-packaging/+git/kf6-kcmutils"
 | 
			
		||||
    packaging_branch: "kubuntu_unstable"
 | 
			
		||||
  - name: kf6-kcodecs
 | 
			
		||||
    upstream_url: "https://invent.kde.org/frameworks/kcodecs.git"
 | 
			
		||||
    packaging_url: "https://git.launchpad.net/~kubuntu-packagers/kubuntu-packaging/+git/kf6-kcodecs"
 | 
			
		||||
    packaging_branch: "kubuntu_unstable"
 | 
			
		||||
  - name: kf6-kcolorscheme
 | 
			
		||||
    upstream_url: "https://invent.kde.org/frameworks/kcolorscheme.git"
 | 
			
		||||
    packaging_url: "https://git.launchpad.net/~kubuntu-packagers/kubuntu-packaging/+git/kf6-kcolorscheme"
 | 
			
		||||
    packaging_branch: "kubuntu_unstable"
 | 
			
		||||
  - name: kf6-kcompletion
 | 
			
		||||
    upstream_url: "https://invent.kde.org/frameworks/kcompletion.git"
 | 
			
		||||
    packaging_url: "https://git.launchpad.net/~kubuntu-packagers/kubuntu-packaging/+git/kf6-kcompletion"
 | 
			
		||||
    packaging_branch: "kubuntu_unstable"
 | 
			
		||||
  - name: kf6-kconfig
 | 
			
		||||
    upstream_url: "https://invent.kde.org/frameworks/kconfig.git"
 | 
			
		||||
    packaging_url: "https://git.launchpad.net/~kubuntu-packagers/kubuntu-packaging/+git/kf6-kconfig"
 | 
			
		||||
    packaging_branch: "kubuntu_unstable"
 | 
			
		||||
  - name: kf6-kconfigwidgets
 | 
			
		||||
    upstream_url: "https://invent.kde.org/frameworks/kconfigwidgets.git"
 | 
			
		||||
    packaging_url: "https://git.launchpad.net/~kubuntu-packagers/kubuntu-packaging/+git/kf6-kconfigwidgets"
 | 
			
		||||
    packaging_branch: "kubuntu_unstable"
 | 
			
		||||
  - name: kf6-kcoreaddons
 | 
			
		||||
    upstream_url: "https://invent.kde.org/frameworks/kcoreaddons.git"
 | 
			
		||||
    packaging_url: "https://git.launchpad.net/~kubuntu-packagers/kubuntu-packaging/+git/kf6-kcoreaddons"
 | 
			
		||||
    packaging_branch: "kubuntu_unstable"
 | 
			
		||||
  - name: kf6-kcrash
 | 
			
		||||
    upstream_url: "https://invent.kde.org/frameworks/kcrash.git"
 | 
			
		||||
    packaging_url: "https://git.launchpad.net/~kubuntu-packagers/kubuntu-packaging/+git/kf6-kcrash"
 | 
			
		||||
    packaging_branch: "kubuntu_unstable"
 | 
			
		||||
  - name: kf6-kdbusaddons
 | 
			
		||||
    upstream_url: "https://invent.kde.org/frameworks/kdbusaddons.git"
 | 
			
		||||
    packaging_url: "https://git.launchpad.net/~kubuntu-packagers/kubuntu-packaging/+git/kf6-kdbusaddons"
 | 
			
		||||
    packaging_branch: "kubuntu_unstable"
 | 
			
		||||
  - name: kf6-kdeclarative
 | 
			
		||||
    upstream_url: "https://invent.kde.org/frameworks/kdeclarative.git"
 | 
			
		||||
    packaging_url: "https://git.launchpad.net/~kubuntu-packagers/kubuntu-packaging/+git/kf6-kdeclarative"
 | 
			
		||||
    packaging_branch: "kubuntu_unstable"
 | 
			
		||||
  - name: kf6-kded
 | 
			
		||||
    upstream_url: "https://invent.kde.org/frameworks/kded.git"
 | 
			
		||||
    packaging_url: "https://git.launchpad.net/~kubuntu-packagers/kubuntu-packaging/+git/kf6-kded"
 | 
			
		||||
    packaging_branch: "kubuntu_unstable"
 | 
			
		||||
  - name: kf6-kdesu
 | 
			
		||||
    upstream_url: "https://invent.kde.org/frameworks/kdesu.git"
 | 
			
		||||
    packaging_url: "https://git.launchpad.net/~kubuntu-packagers/kubuntu-packaging/+git/kf6-kdesu"
 | 
			
		||||
    packaging_branch: "kubuntu_unstable"
 | 
			
		||||
  - name: kf6-kdnssd
 | 
			
		||||
    upstream_url: "https://invent.kde.org/frameworks/kdnssd.git"
 | 
			
		||||
    packaging_url: "https://git.launchpad.net/~kubuntu-packagers/kubuntu-packaging/+git/kf6-kdnssd"
 | 
			
		||||
    packaging_branch: "kubuntu_unstable"
 | 
			
		||||
  - name: kf6-kdoctools
 | 
			
		||||
    upstream_url: "https://invent.kde.org/frameworks/kdoctools.git"
 | 
			
		||||
    packaging_url: "https://git.launchpad.net/~kubuntu-packagers/kubuntu-packaging/+git/kf6-kdoctools"
 | 
			
		||||
    packaging_branch: "kubuntu_unstable"
 | 
			
		||||
  - name: kf6-kfilemetadata
 | 
			
		||||
    upstream_url: "https://invent.kde.org/frameworks/kfilemetadata.git"
 | 
			
		||||
    packaging_url: "https://git.launchpad.net/~kubuntu-packagers/kubuntu-packaging/+git/kf6-kfilemetadata"
 | 
			
		||||
    packaging_branch: "kubuntu_unstable"
 | 
			
		||||
  - name: kf6-kglobalaccel
 | 
			
		||||
    upstream_url: "https://invent.kde.org/frameworks/kglobalaccel.git"
 | 
			
		||||
    packaging_url: "https://git.launchpad.net/~kubuntu-packagers/kubuntu-packaging/+git/kf6-kglobalaccel"
 | 
			
		||||
    packaging_branch: "kubuntu_unstable"
 | 
			
		||||
  - name: kf6-kguiaddons
 | 
			
		||||
    upstream_url: "https://invent.kde.org/frameworks/kguiaddons.git"
 | 
			
		||||
    packaging_url: "https://git.launchpad.net/~kubuntu-packagers/kubuntu-packaging/+git/kf6-kguiaddons"
 | 
			
		||||
    packaging_branch: "kubuntu_unstable"
 | 
			
		||||
  - name: kf6-kholidays
 | 
			
		||||
    upstream_url: "https://invent.kde.org/frameworks/kholidays.git"
 | 
			
		||||
    packaging_url: "https://git.launchpad.net/~kubuntu-packagers/kubuntu-packaging/+git/kf6-kholidays"
 | 
			
		||||
    packaging_branch: "kubuntu_unstable"
 | 
			
		||||
  - name: kf6-ki18n
 | 
			
		||||
    upstream_url: "https://invent.kde.org/frameworks/ki18n.git"
 | 
			
		||||
    packaging_url: "https://git.launchpad.net/~kubuntu-packagers/kubuntu-packaging/+git/kf6-ki18n"
 | 
			
		||||
    packaging_branch: "kubuntu_unstable"
 | 
			
		||||
  - name: kf6-kiconthemes
 | 
			
		||||
    upstream_url: "https://invent.kde.org/frameworks/kiconthemes.git"
 | 
			
		||||
    packaging_url: "https://git.launchpad.net/~kubuntu-packagers/kubuntu-packaging/+git/kf6-kiconthemes"
 | 
			
		||||
    packaging_branch: "kubuntu_unstable"
 | 
			
		||||
  - name: kf6-kidletime
 | 
			
		||||
    upstream_url: "https://invent.kde.org/frameworks/kidletime.git"
 | 
			
		||||
    packaging_url: "https://git.launchpad.net/~kubuntu-packagers/kubuntu-packaging/+git/kf6-kidletime"
 | 
			
		||||
    packaging_branch: "kubuntu_unstable"
 | 
			
		||||
  - name: kf6-kimageformats
 | 
			
		||||
    upstream_url: "https://invent.kde.org/frameworks/kimageformats.git"
 | 
			
		||||
    packaging_url: "https://git.launchpad.net/~kubuntu-packagers/kubuntu-packaging/+git/kf6-kimageformats"
 | 
			
		||||
    packaging_branch: "kubuntu_unstable"
 | 
			
		||||
  - name: kf6-kio
 | 
			
		||||
    upstream_url: "https://invent.kde.org/frameworks/kio.git"
 | 
			
		||||
    packaging_url: "https://git.launchpad.net/~kubuntu-packagers/kubuntu-packaging/+git/kf6-kio"
 | 
			
		||||
    packaging_branch: "kubuntu_unstable"
 | 
			
		||||
  - name: kf6-kirigami
 | 
			
		||||
    upstream_url: "https://invent.kde.org/frameworks/kirigami.git"
 | 
			
		||||
    packaging_url: "https://git.launchpad.net/~kubuntu-packagers/kubuntu-packaging/+git/kf6-kirigami"
 | 
			
		||||
    packaging_branch: "kubuntu_unstable"
 | 
			
		||||
  - name: kf6-kitemmodels
 | 
			
		||||
    upstream_url: "https://invent.kde.org/frameworks/kitemmodels.git"
 | 
			
		||||
    packaging_url: "https://git.launchpad.net/~kubuntu-packagers/kubuntu-packaging/+git/kf6-kitemmodels"
 | 
			
		||||
    packaging_branch: "kubuntu_unstable"
 | 
			
		||||
  - name: kf6-kitemviews
 | 
			
		||||
    upstream_url: "https://invent.kde.org/frameworks/kitemviews.git"
 | 
			
		||||
    packaging_url: "https://git.launchpad.net/~kubuntu-packagers/kubuntu-packaging/+git/kf6-kitemviews"
 | 
			
		||||
    packaging_branch: "kubuntu_unstable"
 | 
			
		||||
  - name: kf6-kjobwidgets
 | 
			
		||||
    upstream_url: "https://invent.kde.org/frameworks/kjobwidgets.git"
 | 
			
		||||
    packaging_url: "https://git.launchpad.net/~kubuntu-packagers/kubuntu-packaging/+git/kf6-kjobwidgets"
 | 
			
		||||
    packaging_branch: "kubuntu_unstable"
 | 
			
		||||
  - name: kf6-knewstuff
 | 
			
		||||
    upstream_url: "https://invent.kde.org/frameworks/knewstuff.git"
 | 
			
		||||
    packaging_url: "https://git.launchpad.net/~kubuntu-packagers/kubuntu-packaging/+git/kf6-knewstuff"
 | 
			
		||||
    packaging_branch: "kubuntu_unstable"
 | 
			
		||||
  - name: kf6-knotifications
 | 
			
		||||
    upstream_url: "https://invent.kde.org/frameworks/knotifications.git"
 | 
			
		||||
    packaging_url: "https://git.launchpad.net/~kubuntu-packagers/kubuntu-packaging/+git/kf6-knotifications"
 | 
			
		||||
    packaging_branch: "kubuntu_unstable"
 | 
			
		||||
  - name: kf6-knotifyconfig
 | 
			
		||||
    upstream_url: "https://invent.kde.org/frameworks/knotifyconfig.git"
 | 
			
		||||
    packaging_url: "https://git.launchpad.net/~kubuntu-packagers/kubuntu-packaging/+git/kf6-knotifyconfig"
 | 
			
		||||
    packaging_branch: "kubuntu_unstable"
 | 
			
		||||
  - name: kf6-kpackage
 | 
			
		||||
    upstream_url: "https://invent.kde.org/frameworks/kpackage.git"
 | 
			
		||||
    packaging_url: "https://git.launchpad.net/~kubuntu-packagers/kubuntu-packaging/+git/kf6-kpackage"
 | 
			
		||||
    packaging_branch: "kubuntu_unstable"
 | 
			
		||||
  - name: kf6-kparts
 | 
			
		||||
    upstream_url: "https://invent.kde.org/frameworks/kparts.git"
 | 
			
		||||
    packaging_url: "https://git.launchpad.net/~kubuntu-packagers/kubuntu-packaging/+git/kf6-kparts"
 | 
			
		||||
    packaging_branch: "kubuntu_unstable"
 | 
			
		||||
  - name: kf6-kpeople
 | 
			
		||||
    upstream_url: "https://invent.kde.org/frameworks/kpeople.git"
 | 
			
		||||
    packaging_url: "https://git.launchpad.net/~kubuntu-packagers/kubuntu-packaging/+git/kf6-kpeople"
 | 
			
		||||
    packaging_branch: "kubuntu_unstable"
 | 
			
		||||
  - name: kf6-kplotting
 | 
			
		||||
    upstream_url: "https://invent.kde.org/frameworks/kplotting.git"
 | 
			
		||||
    packaging_url: "https://git.launchpad.net/~kubuntu-packagers/kubuntu-packaging/+git/kf6-kplotting"
 | 
			
		||||
    packaging_branch: "kubuntu_unstable"
 | 
			
		||||
  - name: kf6-kpty
 | 
			
		||||
    upstream_url: "https://invent.kde.org/frameworks/kpty.git"
 | 
			
		||||
    packaging_url: "https://git.launchpad.net/~kubuntu-packagers/kubuntu-packaging/+git/kf6-kpty"
 | 
			
		||||
    packaging_branch: "kubuntu_unstable"
 | 
			
		||||
  - name: kf6-kquickcharts
 | 
			
		||||
    upstream_url: "https://invent.kde.org/frameworks/kquickcharts.git"
 | 
			
		||||
    packaging_url: "https://git.launchpad.net/~kubuntu-packagers/kubuntu-packaging/+git/kf6-kquickcharts"
 | 
			
		||||
    packaging_branch: "kubuntu_unstable"
 | 
			
		||||
  - name: kf6-krunner
 | 
			
		||||
    upstream_url: "https://invent.kde.org/frameworks/krunner.git"
 | 
			
		||||
    packaging_url: "https://git.launchpad.net/~kubuntu-packagers/kubuntu-packaging/+git/kf6-krunner"
 | 
			
		||||
    packaging_branch: "kubuntu_unstable"
 | 
			
		||||
  - name: kf6-kservice
 | 
			
		||||
    upstream_url: "https://invent.kde.org/frameworks/kservice.git"
 | 
			
		||||
    packaging_url: "https://git.launchpad.net/~kubuntu-packagers/kubuntu-packaging/+git/kf6-kservice"
 | 
			
		||||
    packaging_branch: "kubuntu_unstable"
 | 
			
		||||
  - name: kf6-kstatusnotifieritem
 | 
			
		||||
    upstream_url: "https://invent.kde.org/frameworks/kstatusnotifieritem.git"
 | 
			
		||||
    packaging_url: "https://git.launchpad.net/~kubuntu-packagers/kubuntu-packaging/+git/kf6-kstatusnotifieritem"
 | 
			
		||||
    packaging_branch: "kubuntu_unstable"
 | 
			
		||||
  - name: kf6-ksvg
 | 
			
		||||
    upstream_url: "https://invent.kde.org/frameworks/ksvg.git"
 | 
			
		||||
    packaging_url: "https://git.launchpad.net/~kubuntu-packagers/kubuntu-packaging/+git/kf6-ksvg"
 | 
			
		||||
    packaging_branch: "kubuntu_unstable"
 | 
			
		||||
  - name: kf6-ktexteditor
 | 
			
		||||
    upstream_url: "https://invent.kde.org/frameworks/ktexteditor.git"
 | 
			
		||||
    packaging_url: "https://git.launchpad.net/~kubuntu-packagers/kubuntu-packaging/+git/kf6-ktexteditor"
 | 
			
		||||
    packaging_branch: "kubuntu_unstable"
 | 
			
		||||
  - name: kf6-ktexttemplate
 | 
			
		||||
    upstream_url: "https://invent.kde.org/frameworks/ktexttemplate.git"
 | 
			
		||||
    packaging_url: "https://git.launchpad.net/~kubuntu-packagers/kubuntu-packaging/+git/kf6-ktexttemplate"
 | 
			
		||||
    packaging_branch: "kubuntu_unstable"
 | 
			
		||||
  - name: kf6-ktextwidgets
 | 
			
		||||
    upstream_url: "https://invent.kde.org/frameworks/ktextwidgets.git"
 | 
			
		||||
    packaging_url: "https://git.launchpad.net/~kubuntu-packagers/kubuntu-packaging/+git/kf6-ktextwidgets"
 | 
			
		||||
    packaging_branch: "kubuntu_unstable"
 | 
			
		||||
  - name: kf6-kunitconversion
 | 
			
		||||
    upstream_url: "https://invent.kde.org/frameworks/kunitconversion.git"
 | 
			
		||||
    packaging_url: "https://git.launchpad.net/~kubuntu-packagers/kubuntu-packaging/+git/kf6-kunitconversion"
 | 
			
		||||
    packaging_branch: "kubuntu_unstable"
 | 
			
		||||
  - name: kf6-kuserfeedback
 | 
			
		||||
    upstream_url: "https://invent.kde.org/frameworks/kuserfeedback.git"
 | 
			
		||||
    packaging_url: "https://git.launchpad.net/~kubuntu-packagers/kubuntu-packaging/+git/kf6-kuserfeedback"
 | 
			
		||||
    packaging_branch: "kubuntu_unstable"
 | 
			
		||||
  - name: kf6-kwallet
 | 
			
		||||
    upstream_url: "https://invent.kde.org/frameworks/kwallet.git"
 | 
			
		||||
    packaging_url: "https://git.launchpad.net/~kubuntu-packagers/kubuntu-packaging/+git/kf6-kwallet"
 | 
			
		||||
    packaging_branch: "kubuntu_unstable"
 | 
			
		||||
  - name: kf6-kwidgetsaddons
 | 
			
		||||
    upstream_url: "https://invent.kde.org/frameworks/kwidgetsaddons.git"
 | 
			
		||||
    packaging_url: "https://git.launchpad.net/~kubuntu-packagers/kubuntu-packaging/+git/kf6-kwidgetsaddons"
 | 
			
		||||
    packaging_branch: "kubuntu_unstable"
 | 
			
		||||
  - name: kf6-kwindowsystem
 | 
			
		||||
    upstream_url: "https://invent.kde.org/frameworks/kwindowsystem.git"
 | 
			
		||||
    packaging_url: "https://git.launchpad.net/~kubuntu-packagers/kubuntu-packaging/+git/kf6-kwindowsystem"
 | 
			
		||||
    packaging_branch: "kubuntu_unstable"
 | 
			
		||||
  - name: kf6-kxmlgui
 | 
			
		||||
    upstream_url: "https://invent.kde.org/frameworks/kxmlgui.git"
 | 
			
		||||
    packaging_url: "https://git.launchpad.net/~kubuntu-packagers/kubuntu-packaging/+git/kf6-kxmlgui"
 | 
			
		||||
    packaging_branch: "kubuntu_unstable"
 | 
			
		||||
  - name: kf6-modemmanager-qt
 | 
			
		||||
    upstream_url: "https://invent.kde.org/frameworks/modemmanager-qt.git"
 | 
			
		||||
    packaging_url: "https://git.launchpad.net/~kubuntu-packagers/kubuntu-packaging/+git/kf6-modemmanager-qt"
 | 
			
		||||
    packaging_branch: "kubuntu_unstable"
 | 
			
		||||
  - name: kf6-networkmanager-qt
 | 
			
		||||
    upstream_url: "https://invent.kde.org/frameworks/networkmanager-qt.git"
 | 
			
		||||
    packaging_url: "https://git.launchpad.net/~kubuntu-packagers/kubuntu-packaging/+git/kf6-networkmanager-qt"
 | 
			
		||||
    packaging_branch: "kubuntu_unstable"
 | 
			
		||||
  - name: kf6-prison
 | 
			
		||||
    upstream_url: "https://invent.kde.org/frameworks/prison.git"
 | 
			
		||||
    packaging_url: "https://git.launchpad.net/~kubuntu-packagers/kubuntu-packaging/+git/kf6-prison"
 | 
			
		||||
    packaging_branch: "kubuntu_unstable"
 | 
			
		||||
  - name: kf6-purpose
 | 
			
		||||
    upstream_url: "https://invent.kde.org/frameworks/purpose.git"
 | 
			
		||||
    packaging_url: "https://git.launchpad.net/~kubuntu-packagers/kubuntu-packaging/+git/kf6-purpose"
 | 
			
		||||
    packaging_branch: "kubuntu_unstable"
 | 
			
		||||
  - name: kf6-qqc2-desktop-style
 | 
			
		||||
    upstream_url: "https://invent.kde.org/frameworks/qqc2-desktop-style.git"
 | 
			
		||||
    packaging_url: "https://git.launchpad.net/~kubuntu-packagers/kubuntu-packaging/+git/kf6-qqc2-desktop-style"
 | 
			
		||||
    packaging_branch: "kubuntu_unstable"
 | 
			
		||||
  - name: kf6-solid
 | 
			
		||||
    upstream_url: "https://invent.kde.org/frameworks/solid.git"
 | 
			
		||||
    packaging_url: "https://git.launchpad.net/~kubuntu-packagers/kubuntu-packaging/+git/kf6-solid"
 | 
			
		||||
    packaging_branch: "kubuntu_unstable"
 | 
			
		||||
  - name: kf6-sonnet
 | 
			
		||||
    upstream_url: "https://invent.kde.org/frameworks/sonnet.git"
 | 
			
		||||
    packaging_url: "https://git.launchpad.net/~kubuntu-packagers/kubuntu-packaging/+git/kf6-sonnet"
 | 
			
		||||
    packaging_branch: "kubuntu_unstable"
 | 
			
		||||
  - name: kf6-syndication
 | 
			
		||||
    upstream_url: "https://invent.kde.org/frameworks/syndication.git"
 | 
			
		||||
    packaging_url: "https://git.launchpad.net/~kubuntu-packagers/kubuntu-packaging/+git/kf6-syndication"
 | 
			
		||||
    packaging_branch: "kubuntu_unstable"
 | 
			
		||||
  - name: kf6-syntax-highlighting
 | 
			
		||||
    upstream_url: "https://invent.kde.org/frameworks/syntax-highlighting.git"
 | 
			
		||||
    packaging_url: "https://git.launchpad.net/~kubuntu-packagers/kubuntu-packaging/+git/kf6-syntax-highlighting"
 | 
			
		||||
    packaging_branch: "kubuntu_unstable"
 | 
			
		||||
  - name: kf6-threadweaver
 | 
			
		||||
    upstream_url: "https://invent.kde.org/frameworks/threadweaver.git"
 | 
			
		||||
    packaging_url: "https://git.launchpad.net/~kubuntu-packagers/kubuntu-packaging/+git/kf6-threadweaver"
 | 
			
		||||
    packaging_branch: "kubuntu_unstable"
 | 
			
		||||
  - name: kpmcore
 | 
			
		||||
    upstream_url: "https://invent.kde.org/system/kpmcore.git"
 | 
			
		||||
    packaging_url: "https://git.launchpad.net/~kubuntu-packagers/kubuntu-packaging/+git/kpmcore"
 | 
			
		||||
    packaging_branch: "kubuntu_unstable"
 | 
			
		||||
  - name: plasma-discover
 | 
			
		||||
    upstream_url: "https://invent.kde.org/plasma/discover.git"
 | 
			
		||||
    packaging_url: "https://git.launchpad.net/~kubuntu-packagers/kubuntu-packaging/+git/discover"
 | 
			
		||||
    packaging_branch: "kubuntu_unstable"
 | 
			
		||||
  - name: skanlite
 | 
			
		||||
    upstream_url: "https://invent.kde.org/graphics/skanlite.git"
 | 
			
		||||
    packaging_url: "https://git.launchpad.net/~kubuntu-packagers/kubuntu-packaging/+git/skanlite"
 | 
			
		||||
    packaging_branch: "kubuntu_unstable"
 | 
			
		||||
  - name: libksane
 | 
			
		||||
    upstream_url: "https://invent.kde.org/graphics/libksane.git"
 | 
			
		||||
    packaging_url: "https://git.launchpad.net/~kubuntu-packagers/kubuntu-packaging/+git/libksane"
 | 
			
		||||
    packaging_branch: "kubuntu_unstable"
 | 
			
		||||
  - name: ksanecore
 | 
			
		||||
    upstream_url: "https://invent.kde.org/libraries/ksanecore.git"
 | 
			
		||||
    packaging_url: "https://git.launchpad.net/~kubuntu-packagers/kubuntu-packaging/+git/ksanecore"
 | 
			
		||||
    packaging_branch: "kubuntu_unstable"
 | 
			
		||||
 | 
			
		||||
releases:
 | 
			
		||||
  - plucky
 | 
			
		||||
  - oracular
 | 
			
		||||
  - noble
 | 
			
		||||
							
								
								
									
										45
									
								
								configs/lxqt.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								configs/lxqt.yaml
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,45 @@
 | 
			
		||||
packages:
 | 
			
		||||
  - name: lxqt-build-tools
 | 
			
		||||
  - name: libdbusmenu-lxqt
 | 
			
		||||
    packaging_branch: "ci/unstable"
 | 
			
		||||
  - name: libqtxdg
 | 
			
		||||
  - name: lxqt-menu-data
 | 
			
		||||
  - name: liblxqt
 | 
			
		||||
  - name: libsysstat
 | 
			
		||||
  - name: qtxdg-tools
 | 
			
		||||
  - name: libfm-qt
 | 
			
		||||
  - name: lxqt-globalkeys
 | 
			
		||||
  - name: lxqt-qtplugin
 | 
			
		||||
  - name: qtermwidget
 | 
			
		||||
  - name: lxqt-panel
 | 
			
		||||
  - name: pcmanfm-qt
 | 
			
		||||
  - name: qterminal
 | 
			
		||||
  - name: lxqt-powermanagement
 | 
			
		||||
  - name: lxqt-runner
 | 
			
		||||
  - name: lxqt-themes
 | 
			
		||||
  - name: lxqt-admin
 | 
			
		||||
  - name: lxqt-notificationd
 | 
			
		||||
  - name: lxqt-about
 | 
			
		||||
  - name: lxqt-config
 | 
			
		||||
  - name: lxqt-policykit
 | 
			
		||||
  - name: lxqt-sudo
 | 
			
		||||
  - name: lxqt-openssh-askpass
 | 
			
		||||
  - name: lxqt-session
 | 
			
		||||
  - name: pavucontrol-qt
 | 
			
		||||
  - name: xdg-desktop-portal-lxqt
 | 
			
		||||
  - name: lxqt-archiver
 | 
			
		||||
  - name: screengrab
 | 
			
		||||
  - name: lximage-qt
 | 
			
		||||
  - name: qps
 | 
			
		||||
  - name: obconf-qt
 | 
			
		||||
  - name: nm-tray
 | 
			
		||||
    upstream_url: "https://github.com/palinek/nm-tray.git"
 | 
			
		||||
  - name: calamares
 | 
			
		||||
    upstream_url: "https://github.com/calamares/calamares.git"
 | 
			
		||||
    packaging_url: "https://git.launchpad.net/~ubuntu-qt-code/ubuntu/+source/calamares/+git/calamares"
 | 
			
		||||
    packaging_branch: "ci/unstable"
 | 
			
		||||
 | 
			
		||||
releases:
 | 
			
		||||
  - plucky
 | 
			
		||||
  - oracular
 | 
			
		||||
  - noble
 | 
			
		||||
							
								
								
									
										110
									
								
								configs/qt6.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										110
									
								
								configs/qt6.yaml
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,110 @@
 | 
			
		||||
packages:
 | 
			
		||||
  - name: qt6-base
 | 
			
		||||
    upstream_url: "https://code.qt.io/qt/qtbase.git"
 | 
			
		||||
    packaging_url: "https://git.launchpad.net/~ubuntu-qt-code/+git/qt6-base"
 | 
			
		||||
    packaging_branch: "ci/unstable"
 | 
			
		||||
  - name: qt6-imageformats
 | 
			
		||||
    upstream_url: "https://code.qt.io/qt/qtimageformats.git"
 | 
			
		||||
    packaging_url: "https://git.launchpad.net/~ubuntu-qt-code/+git/qt6-imageformats"
 | 
			
		||||
    packaging_branch: "ci/unstable"
 | 
			
		||||
  - name: qt6-languageserver
 | 
			
		||||
    upstream_url: "https://code.qt.io/qt/qtlanguageserver.git"
 | 
			
		||||
    packaging_url: "https://git.launchpad.net/~ubuntu-qt-code/+git/qt6-languageserver"
 | 
			
		||||
    packaging_branch: "ci/unstable"
 | 
			
		||||
  - name: qt6-shadertools
 | 
			
		||||
    upstream_url: "https://code.qt.io/qt/qtshadertools.git"
 | 
			
		||||
    packaging_url: "https://git.launchpad.net/~ubuntu-qt-code/+git/qt6-shadertools"
 | 
			
		||||
    packaging_branch: "ci/unstable"
 | 
			
		||||
  - name: qt6-svg
 | 
			
		||||
    upstream_url: "https://code.qt.io/qt/qtsvg.git"
 | 
			
		||||
    packaging_url: "https://git.launchpad.net/~ubuntu-qt-code/+git/qt6-svg"
 | 
			
		||||
    packaging_branch: "ci/unstable"
 | 
			
		||||
  - name: qt6-networkauth
 | 
			
		||||
    upstream_url: "https://code.qt.io/qt/qtnetworkauth.git"
 | 
			
		||||
    packaging_url: "https://git.launchpad.net/~ubuntu-qt-code/+git/qt6-networkauth"
 | 
			
		||||
    packaging_branch: "ci/unstable"
 | 
			
		||||
  - name: qt6-serialport
 | 
			
		||||
    upstream_url: "https://code.qt.io/qt/qtserialport.git"
 | 
			
		||||
    packaging_url: "https://git.launchpad.net/~ubuntu-qt-code/+git/qt6-serialport"
 | 
			
		||||
    packaging_branch: "ci/unstable"
 | 
			
		||||
  - name: qt6-declarative
 | 
			
		||||
    upstream_url: "https://code.qt.io/qt/qtdeclarative.git"
 | 
			
		||||
    packaging_url: "https://git.launchpad.net/~ubuntu-qt-code/+git/qt6-declarative"
 | 
			
		||||
    packaging_branch: "ci/unstable"
 | 
			
		||||
  - name: qt6-lottie
 | 
			
		||||
    upstream_url: "https://code.qt.io/qt/qtlottie.git"
 | 
			
		||||
    packaging_url: "https://git.launchpad.net/~ubuntu-qt-code/+git/qt6-lottie"
 | 
			
		||||
    packaging_branch: "ci/unstable"
 | 
			
		||||
  - name: qt6-websockets
 | 
			
		||||
    upstream_url: "https://code.qt.io/qt/qtwebsockets.git"
 | 
			
		||||
    packaging_url: "https://git.launchpad.net/~ubuntu-qt-code/+git/qt6-websockets"
 | 
			
		||||
    packaging_branch: "ci/unstable"
 | 
			
		||||
  - name: qt6-5compat
 | 
			
		||||
    upstream_url: "https://code.qt.io/qt/qt5compat.git"
 | 
			
		||||
    packaging_url: "https://git.launchpad.net/~ubuntu-qt-code/+git/qt6-5compat"
 | 
			
		||||
    packaging_branch: "ci/unstable"
 | 
			
		||||
  - name: qt6-connectivity
 | 
			
		||||
    upstream_url: "https://code.qt.io/qt/qtconnectivity.git"
 | 
			
		||||
    packaging_url: "https://git.launchpad.net/~ubuntu-qt-code/+git/qt6-connectivity"
 | 
			
		||||
    packaging_branch: "ci/unstable"
 | 
			
		||||
  - name: qt6-scxml
 | 
			
		||||
    upstream_url: "https://code.qt.io/qt/qtscxml.git"
 | 
			
		||||
    packaging_url: "https://git.launchpad.net/~ubuntu-qt-code/+git/qt6-scxml"
 | 
			
		||||
    packaging_branch: "ci/unstable"
 | 
			
		||||
  - name: qt6-sensors
 | 
			
		||||
    upstream_url: "https://code.qt.io/qt/qtsensors.git"
 | 
			
		||||
    packaging_url: "https://git.launchpad.net/~ubuntu-qt-code/+git/qt6-sensors"
 | 
			
		||||
    packaging_branch: "ci/unstable"
 | 
			
		||||
  - name: qt6-wayland
 | 
			
		||||
    upstream_url: "https://code.qt.io/qt/qtwayland.git"
 | 
			
		||||
    packaging_url: "https://git.launchpad.net/~ubuntu-qt-code/+git/qt6-wayland"
 | 
			
		||||
    packaging_branch: "ci/unstable"
 | 
			
		||||
  - name: qt6-datavis3d
 | 
			
		||||
    upstream_url: "https://code.qt.io/qt/qtdatavis3d.git"
 | 
			
		||||
    packaging_url: "https://git.launchpad.net/~ubuntu-qt-code/+git/qt6-datavis3d"
 | 
			
		||||
    packaging_branch: "ci/unstable"
 | 
			
		||||
  - name: qt6-grpc
 | 
			
		||||
    upstream_url: "https://code.qt.io/qt/qtgrpc.git"
 | 
			
		||||
    packaging_url: "https://git.launchpad.net/~ubuntu-qt-code/+git/qt6-grpc"
 | 
			
		||||
    packaging_branch: "ci/unstable"
 | 
			
		||||
  - name: qt6-positioning
 | 
			
		||||
    upstream_url: "https://code.qt.io/qt/qtpositioning.git"
 | 
			
		||||
    packaging_url: "https://git.launchpad.net/~ubuntu-qt-code/+git/qt6-positioning"
 | 
			
		||||
    packaging_branch: "ci/unstable"
 | 
			
		||||
  - name: qt6-quicktimeline
 | 
			
		||||
    upstream_url: "https://code.qt.io/qt/qtquicktimeline.git"
 | 
			
		||||
    packaging_url: "https://git.launchpad.net/~ubuntu-qt-code/+git/qt6-quicktimeline"
 | 
			
		||||
    packaging_branch: "ci/unstable"
 | 
			
		||||
  - name: qt6-serialbus
 | 
			
		||||
    upstream_url: "https://code.qt.io/qt/qtserialbus.git"
 | 
			
		||||
    packaging_url: "https://git.launchpad.net/~ubuntu-qt-code/+git/qt6-serialbus"
 | 
			
		||||
    packaging_branch: "ci/unstable"
 | 
			
		||||
  - name: qt6-tools
 | 
			
		||||
    upstream_url: "https://code.qt.io/qt/qttools.git"
 | 
			
		||||
    packaging_url: "https://git.launchpad.net/~ubuntu-qt-code/+git/qt6-tools"
 | 
			
		||||
    packaging_branch: "ci/unstable"
 | 
			
		||||
  - name: qt6-webchannel
 | 
			
		||||
    upstream_url: "https://code.qt.io/qt/qtwebchannel.git"
 | 
			
		||||
    packaging_url: "https://git.launchpad.net/~ubuntu-qt-code/+git/qt6-webchannel"
 | 
			
		||||
    packaging_branch: "ci/unstable"
 | 
			
		||||
  - name: qt6-httpserver
 | 
			
		||||
    upstream_url: "https://code.qt.io/qt/qthttpserver.git"
 | 
			
		||||
    packaging_url: "https://git.launchpad.net/~ubuntu-qt-code/+git/qt6-httpserver"
 | 
			
		||||
    packaging_branch: "ci/unstable"
 | 
			
		||||
  - name: qt6-remoteobjects
 | 
			
		||||
    upstream_url: "https://code.qt.io/qt/qtremoteobjects.git"
 | 
			
		||||
    packaging_url: "https://git.launchpad.net/~ubuntu-qt-code/+git/qt6-remoteobjects"
 | 
			
		||||
    packaging_branch: "ci/unstable"
 | 
			
		||||
  - name: qt6-location
 | 
			
		||||
    upstream_url: "https://code.qt.io/qt/qtlocation.git"
 | 
			
		||||
    packaging_url: "https://git.launchpad.net/~ubuntu-qt-code/+git/qt6-location"
 | 
			
		||||
    packaging_branch: "ci/unstable"
 | 
			
		||||
  - name: qt6-translations
 | 
			
		||||
    upstream_url: "https://code.qt.io/qt/qttranslations.git"
 | 
			
		||||
    packaging_url: "https://git.launchpad.net/~ubuntu-qt-code/+git/qt6-translations"
 | 
			
		||||
    packaging_branch: "ci/unstable"
 | 
			
		||||
 | 
			
		||||
releases:
 | 
			
		||||
  - plucky
 | 
			
		||||
  - oracular
 | 
			
		||||
  - noble
 | 
			
		||||
							
								
								
									
										161
									
								
								fetch-indexes
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										161
									
								
								fetch-indexes
									
									
									
									
									
										Executable file
									
								
							@ -0,0 +1,161 @@
 | 
			
		||||
#!/bin/bash
 | 
			
		||||
# download current package indexes to data/<series>{,-proposed}/ for running
 | 
			
		||||
# britney against a PPA. The PPA will play the role of "-proposed" (i. e.
 | 
			
		||||
# "unstable" in britney terms, containing the updated packages to test), the
 | 
			
		||||
# Ubuntu archive has the "-release" part (i. e. "testing" in britney terms, in
 | 
			
		||||
# which the -proposed packages are being landed).
 | 
			
		||||
#
 | 
			
		||||
# Copyright (C) 2019-2024 Simon Quigley <tsimonq2@ubuntu.com>
 | 
			
		||||
# Copyright (C) Canonical Ltd
 | 
			
		||||
# Author: Martin Pitt <martin.pitt@ubuntu.com>
 | 
			
		||||
# Author: Robert Bruce Park <robert.park@canonical.com>
 | 
			
		||||
# This program is free software: you can redistribute it and/or modify
 | 
			
		||||
# it under the terms of the GNU General Public License as published by
 | 
			
		||||
# the Free Software Foundation, either version 3 of the License, or
 | 
			
		||||
# (at your option) any later version.
 | 
			
		||||
#
 | 
			
		||||
# This program is distributed in the hope that it will be useful,
 | 
			
		||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
# GNU General Public License for more details.
 | 
			
		||||
#
 | 
			
		||||
# You should have received a copy of the GNU General Public License
 | 
			
		||||
# along with this program.  If not, see <https://www.gnu.org/licenses/>.
 | 
			
		||||
 | 
			
		||||
set -u
 | 
			
		||||
 | 
			
		||||
./pending-packages $RELEASE || exit 0
 | 
			
		||||
 | 
			
		||||
export MAIN_ARCHIVE="http://us.archive.ubuntu.com/ubuntu/dists/"
 | 
			
		||||
export PORTS_ARCHIVE="http://us.ports.ubuntu.com/ubuntu-ports/dists/"
 | 
			
		||||
export LP_TEAM="lubuntu-ci"
 | 
			
		||||
export SOURCE_PPA="unstable-ci-proposed"
 | 
			
		||||
export DEST_PPA="unstable-ci"
 | 
			
		||||
export SOURCE_PPA_URL="https://ppa.launchpadcontent.net/$LP_TEAM/$SOURCE_PPA/ubuntu/dists/$RELEASE/main";
 | 
			
		||||
export DEST_PPA_URL="https://ppa.launchpadcontent.net/$LP_TEAM/$DEST_PPA/ubuntu/dists/$RELEASE/main";
 | 
			
		||||
export ARCHES="amd64"
 | 
			
		||||
export PORTS_ARCHES="arm64 armhf ppc64el riscv64 s390x"
 | 
			
		||||
export BRITNEY_CACHE="cache/"
 | 
			
		||||
export BRITNEY_DATADIR="data/"
 | 
			
		||||
export BRITNEY_OUTDIR="output/"
 | 
			
		||||
export BRITNEY_HINTDIR="../hints-ubuntu/"
 | 
			
		||||
export BRITNEY_LOC="/srv/lubuntu-ci/repos/britney2-ubuntu/britney.py"
 | 
			
		||||
export BRITNEY_TIMESTAMP=$(date +"%Y-%m-%d_%H:%M:%S")
 | 
			
		||||
 | 
			
		||||
echo "Release: $RELEASE";
 | 
			
		||||
echo "Timestamp: $BRITNEY_TIMESTAMP"
 | 
			
		||||
 | 
			
		||||
# Download files in parallel in background, only if there is an update
 | 
			
		||||
refresh() {
 | 
			
		||||
    DIR=$BRITNEY_CACHE/$pocket/$(echo $1 | rev | cut --delimiter=/ --fields=2,3 | rev)
 | 
			
		||||
    mkdir --parents $DIR
 | 
			
		||||
    touch --no-create $BRITNEY_CACHE $BRITNEY_CACHE/$pocket "$(dirname $DIR)" $DIR  # Timestamp thwarts expire.sh
 | 
			
		||||
    wget --directory-prefix $DIR --timestamping $1 --append-output $DIR/$$-wget-log --no-verbose &
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
echo 'Refreshing package indexes...'
 | 
			
		||||
 | 
			
		||||
for pocket in $RELEASE $RELEASE-updates; do
 | 
			
		||||
    for component in main restricted universe multiverse; do
 | 
			
		||||
        for arch in $ARCHES; do
 | 
			
		||||
            refresh $MAIN_ARCHIVE/$pocket/$component/binary-$arch/Packages.gz
 | 
			
		||||
        done
 | 
			
		||||
        for arch in $PORTS_ARCHES; do
 | 
			
		||||
            refresh $PORTS_ARCHIVE/$pocket/$component/binary-$arch/Packages.gz
 | 
			
		||||
        done
 | 
			
		||||
        refresh $MAIN_ARCHIVE/$pocket/$component/source/Sources.gz
 | 
			
		||||
    done
 | 
			
		||||
done
 | 
			
		||||
 | 
			
		||||
# Treat the destination PPA as just another pocket
 | 
			
		||||
for pocket in $RELEASE-ppa-proposed; do
 | 
			
		||||
    for arch in $ARCHES $PORTS_ARCHES; do
 | 
			
		||||
        refresh $DEST_PPA_URL/source/Sources.gz
 | 
			
		||||
        refresh $DEST_PPA_URL/binary-$arch/Packages.gz
 | 
			
		||||
    done
 | 
			
		||||
done
 | 
			
		||||
 | 
			
		||||
# Get the source PPA
 | 
			
		||||
pocket=$SOURCE_PPA-$RELEASE
 | 
			
		||||
for arch in $ARCHES $PORTS_ARCHES; do
 | 
			
		||||
    refresh $SOURCE_PPA_URL/binary-$arch/Packages.gz
 | 
			
		||||
done
 | 
			
		||||
refresh $SOURCE_PPA_URL/source/Sources.gz
 | 
			
		||||
 | 
			
		||||
wait  # for wgets to finish
 | 
			
		||||
 | 
			
		||||
find $BRITNEY_DATADIR -name "$$-wget-log*" -exec cat '{}' \; -delete 1>&2
 | 
			
		||||
 | 
			
		||||
echo 'Building britney indexes...'
 | 
			
		||||
 | 
			
		||||
mkdir --parents "$BRITNEY_OUTDIR/$BRITNEY_TIMESTAMP/"
 | 
			
		||||
 | 
			
		||||
# "Unstable" is SOURCE_PPA
 | 
			
		||||
DEST=$BRITNEY_DATADIR/$RELEASE-proposed
 | 
			
		||||
mkdir --parents $DEST
 | 
			
		||||
mkdir -pv $BRITNEY_DATADIR/$RELEASE-proposed/state/
 | 
			
		||||
touch $BRITNEY_DATADIR/$RELEASE-proposed/state/age-policy-dates
 | 
			
		||||
touch --no-create $DEST
 | 
			
		||||
ln --verbose --symbolic --force --no-dereference $BRITNEY_HINTDIR $DEST/Hints
 | 
			
		||||
zcat $BRITNEY_CACHE/$SOURCE_PPA-$RELEASE/*/source/Sources.gz > $DEST/Sources
 | 
			
		||||
for arch in $ARCHES $PORTS_ARCHES; do
 | 
			
		||||
    zcat $BRITNEY_CACHE/$SOURCE_PPA-$RELEASE/*/binary-$arch/Packages.gz > $DEST/Packages_${arch}
 | 
			
		||||
done
 | 
			
		||||
touch $DEST/Blocks $DEST/Dates
 | 
			
		||||
 | 
			
		||||
# "Testing" is a combination of the archive and DEST_PPA
 | 
			
		||||
DEST=$BRITNEY_DATADIR/$RELEASE
 | 
			
		||||
mkdir --parents $DEST
 | 
			
		||||
mkdir -pv $BRITNEY_DATADIR/$RELEASE/state/
 | 
			
		||||
touch $BRITNEY_DATADIR/$RELEASE/state/age-policy-dates
 | 
			
		||||
touch --no-create $DEST
 | 
			
		||||
ln --verbose --symbolic --force --no-dereference $BRITNEY_HINTDIR $DEST/Hints
 | 
			
		||||
zcat $BRITNEY_CACHE/$RELEASE*/*/source/Sources.gz > $DEST/Sources
 | 
			
		||||
sed -i "s/Section: universe\//Section: /g" $DEST/Sources
 | 
			
		||||
for arch in $ARCHES $PORTS_ARCHES; do
 | 
			
		||||
    zcat $BRITNEY_CACHE/$RELEASE*/*/binary-$arch/Packages.gz > $DEST/Packages_${arch}
 | 
			
		||||
    sed -i "s/Section: universe\//Section: /g" $DEST/Packages_${arch}
 | 
			
		||||
done
 | 
			
		||||
touch $DEST/Blocks
 | 
			
		||||
touch "$BRITNEY_DATADIR/$SOURCE_PPA-$RELEASE/Dates"
 | 
			
		||||
 | 
			
		||||
# Create config file atomically.
 | 
			
		||||
CONFIG="britney.conf"
 | 
			
		||||
cp $CONFIG $CONFIG.bak
 | 
			
		||||
envsubst < "$CONFIG.bak" > "$CONFIG"
 | 
			
		||||
rm $CONFIG.bak
 | 
			
		||||
 | 
			
		||||
echo 'Running britney...'
 | 
			
		||||
$BRITNEY_LOC -v --config "$CONFIG" --series $RELEASE
 | 
			
		||||
 | 
			
		||||
echo 'Syncing output to frontend...'
 | 
			
		||||
rmdir output/;
 | 
			
		||||
rsync -da output/ ../../output/britney
 | 
			
		||||
 | 
			
		||||
echo "$0 done."
 | 
			
		||||
 | 
			
		||||
echo "Moving packages..."
 | 
			
		||||
 | 
			
		||||
egrep -v '^#' output/$RELEASE/HeidiResultDelta > candidates || echo "No candidates found.";
 | 
			
		||||
 | 
			
		||||
while read -r -a package; do
 | 
			
		||||
    # This only acts on sources; binaries require manual cleanup
 | 
			
		||||
    if [ ${#package[@]} = 2 ]; then
 | 
			
		||||
        COPY="../ubuntu-archive-tools/copy-package"
 | 
			
		||||
        REMOVE="../ubuntu-archive-tools/remove-package"
 | 
			
		||||
        if echo ${package[0]} | egrep -q "^-"; then
 | 
			
		||||
            PACKAGE=$(echo ${package[0]} | sed 's/-//')
 | 
			
		||||
            echo "Demoting $PACKAGE..."
 | 
			
		||||
            $COPY -y -b -s $RELEASE --from "ppa:$LP_TEAM/ubuntu/$DEST_PPA" --to "ppa:$LP_TEAM/ubuntu/$SOURCE_PPA" --version "${package[1]}" "$PACKAGE";
 | 
			
		||||
            $REMOVE -y -s $RELEASE --archive "ppa:$LP_TEAM/ubuntu/$DEST_PPA" --version "${package[1]}" --removal-comment="demoted to proposed" "$PACKAGE";
 | 
			
		||||
        else
 | 
			
		||||
            echo "Migrating ${package[0]}..."
 | 
			
		||||
            $COPY -y -b -s $RELEASE --from "ppa:$LP_TEAM/ubuntu/$SOURCE_PPA" --to "ppa:$LP_TEAM/ubuntu/$DEST_PPA" --version "${package[1]}" "${package[0]}";
 | 
			
		||||
            $REMOVE -y -s $RELEASE --archive "ppa:$LP_TEAM/ubuntu/$SOURCE_PPA" --version "${package[1]}" --removal-comment="moved to release" "${package[0]}";
 | 
			
		||||
        fi
 | 
			
		||||
    fi
 | 
			
		||||
done < candidates;
 | 
			
		||||
rm candidates;
 | 
			
		||||
 | 
			
		||||
echo "Run the grim reaper..."
 | 
			
		||||
./grim-reaper
 | 
			
		||||
							
								
								
									
										64
									
								
								grim-reaper
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										64
									
								
								grim-reaper
									
									
									
									
									
										Executable file
									
								
							@ -0,0 +1,64 @@
 | 
			
		||||
#!/usr/bin/env python3
 | 
			
		||||
#
 | 
			
		||||
# Copyright (C) 2024 Simon Quigley <tsimonq2@ubuntu.com>
 | 
			
		||||
#
 | 
			
		||||
# This program is free software: you can redistribute it and/or modify
 | 
			
		||||
# it under the terms of the GNU General Public License as published by
 | 
			
		||||
# the Free Software Foundation, either version 3 of the License, or
 | 
			
		||||
# (at your option) any later version.
 | 
			
		||||
#
 | 
			
		||||
# This program is distributed in the hope that it will be useful,
 | 
			
		||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
# GNU General Public License for more details.
 | 
			
		||||
#
 | 
			
		||||
# You should have received a copy of the GNU General Public License
 | 
			
		||||
# along with this program.  If not, see <https://www.gnu.org/licenses/>.
 | 
			
		||||
 | 
			
		||||
from concurrent.futures import ThreadPoolExecutor, as_completed
 | 
			
		||||
from datetime import datetime, timedelta
 | 
			
		||||
from launchpadlib.launchpad import Launchpad
 | 
			
		||||
 | 
			
		||||
now = datetime.now()
 | 
			
		||||
 | 
			
		||||
def print_log(string):
 | 
			
		||||
    global now
 | 
			
		||||
    old_now = now
 | 
			
		||||
    now = datetime.now()
 | 
			
		||||
    time_elapsed = now - old_now
 | 
			
		||||
    print(f"[{now}] (took {time_elapsed}) {string}")
 | 
			
		||||
 | 
			
		||||
print(f"[{now}] Logging into Launchpad...")
 | 
			
		||||
launchpad = Launchpad.login_with("grim-reaper", "production", version="devel")
 | 
			
		||||
 | 
			
		||||
print_log("Logged in. Initializing repositories...")
 | 
			
		||||
ubuntu = launchpad.distributions["ubuntu"]
 | 
			
		||||
lubuntu_ci = launchpad.people["lubuntu-ci"]
 | 
			
		||||
regular = lubuntu_ci.getPPAByName(distribution=ubuntu, name="unstable-ci")
 | 
			
		||||
proposed = lubuntu_ci.getPPAByName(distribution=ubuntu, name="unstable-ci-proposed")
 | 
			
		||||
 | 
			
		||||
print_log("IS THAT THE GRIM REAPER?!?!?!?!!!")
 | 
			
		||||
 | 
			
		||||
# Fetch packages once
 | 
			
		||||
two_weeks_ago = datetime.now() - timedelta(days=14)
 | 
			
		||||
packages = [proposed.getPublishedSources(status="Superseded"), regular.getPublishedSources(status="Superseded")]
 | 
			
		||||
total_removals = sum(len(packageset) for packageset in packages)
 | 
			
		||||
 | 
			
		||||
print_log(f"Total packages to remove: {total_removals}")
 | 
			
		||||
current_package = 1
 | 
			
		||||
current_percentage = 0
 | 
			
		||||
for packageset in packages:
 | 
			
		||||
    for pkg in packageset:
 | 
			
		||||
        # Cancel all running builds for the package:
 | 
			
		||||
        for build in pkg.getBuilds():
 | 
			
		||||
            if build.buildstate in ["Currently building", "Needs building"]:
 | 
			
		||||
                # Only cancel the build if we can
 | 
			
		||||
                if build.can_be_cancelled:
 | 
			
		||||
                    build.cancel()
 | 
			
		||||
        # Delete the source package
 | 
			
		||||
        pkg.requestDeletion(removal_comment="superseded")
 | 
			
		||||
        new_percentage = int(current_package / total_removals)
 | 
			
		||||
        if new_percentage > current_percentage:
 | 
			
		||||
            current_percentage = new_percentage
 | 
			
		||||
            print_log(f"{new_percentage}% complete ({current_package}/{total_removals})")
 | 
			
		||||
        current_package += 1
 | 
			
		||||
							
								
								
									
										167
									
								
								lintian-ppa
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										167
									
								
								lintian-ppa
									
									
									
									
									
										Executable file
									
								
							@ -0,0 +1,167 @@
 | 
			
		||||
#!/usr/bin/env python3
 | 
			
		||||
#
 | 
			
		||||
# Copyright (C) 2024 Simon Quigley <tsimonq2@ubuntu.com>
 | 
			
		||||
#
 | 
			
		||||
# This program is free software: you can redistribute it and/or modify
 | 
			
		||||
# it under the terms of the GNU General Public License as published by
 | 
			
		||||
# the Free Software Foundation, either version 3 of the License, or
 | 
			
		||||
# (at your option) any later version.
 | 
			
		||||
#
 | 
			
		||||
# This program is distributed in the hope that it will be useful,
 | 
			
		||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
# GNU General Public License for more details.
 | 
			
		||||
#
 | 
			
		||||
# You should have received a copy of the GNU General Public License
 | 
			
		||||
# along with this program.  If not, see <https://www.gnu.org/licenses/>.
 | 
			
		||||
 | 
			
		||||
import argparse
 | 
			
		||||
import logging
 | 
			
		||||
import os
 | 
			
		||||
import shutil
 | 
			
		||||
import subprocess
 | 
			
		||||
import tempfile
 | 
			
		||||
import uuid
 | 
			
		||||
from common import clean_old_logs
 | 
			
		||||
from concurrent.futures import ThreadPoolExecutor, wait, FIRST_COMPLETED
 | 
			
		||||
from datetime import datetime, timedelta, timezone
 | 
			
		||||
from debian.deb822 import Changes
 | 
			
		||||
from launchpadlib.launchpad import Launchpad
 | 
			
		||||
from pathlib import Path
 | 
			
		||||
 | 
			
		||||
BASE_OUTPUT_DIR = "/srv/lubuntu-ci/output/"
 | 
			
		||||
LOG_DIR = os.path.join(BASE_OUTPUT_DIR, "logs/lintian/")
 | 
			
		||||
 | 
			
		||||
parser = argparse.ArgumentParser(description="")
 | 
			
		||||
parser.add_argument("--user", "-u", required=True)
 | 
			
		||||
parser.add_argument("--ppa", "-p", required=True)
 | 
			
		||||
parser.add_argument("--ppa2", "-p2")
 | 
			
		||||
args = parser.parse_args()
 | 
			
		||||
 | 
			
		||||
os.makedirs(LOG_DIR, exist_ok=True)
 | 
			
		||||
current_time = datetime.utcnow().strftime("%H-%M-%S")
 | 
			
		||||
log_file = os.path.join(LOG_DIR, f"{current_time}.log")
 | 
			
		||||
logging.basicConfig(
 | 
			
		||||
    level=logging.INFO,
 | 
			
		||||
    format="%(asctime)s - %(levelname)s - %(message)s",
 | 
			
		||||
    handlers=[
 | 
			
		||||
        logging.FileHandler(log_file)
 | 
			
		||||
    ]
 | 
			
		||||
)
 | 
			
		||||
logger = logging.getLogger("TimeBasedLogger")
 | 
			
		||||
 | 
			
		||||
launchpad = Launchpad.login_with("lintian-ppa", "production", version="devel")
 | 
			
		||||
ubuntu = launchpad.distributions["ubuntu"]
 | 
			
		||||
current_series = ubuntu.current_series
 | 
			
		||||
 | 
			
		||||
user = launchpad.people[args.user]
 | 
			
		||||
ppa = user.getPPAByName(distribution=ubuntu, name=args.ppa)
 | 
			
		||||
if args.ppa2:
 | 
			
		||||
    ppa2 = user.getPPAByName(distribution=ubuntu, name=args.ppa)
 | 
			
		||||
 | 
			
		||||
if not os.path.exists(args.user):
 | 
			
		||||
    os.mkdir(args.user)
 | 
			
		||||
 | 
			
		||||
lintian = os.path.join(BASE_OUTPUT_DIR, "lintian")
 | 
			
		||||
lintian_tmp = os.path.join(BASE_OUTPUT_DIR, f".lintian.tmp.{str(uuid.uuid4())[:8]}")
 | 
			
		||||
if not os.path.exists(lintian):
 | 
			
		||||
    os.mkdir(lintian)
 | 
			
		||||
if os.path.exists(lintian_tmp):
 | 
			
		||||
    shutil.rmtree(lintian_tmp)
 | 
			
		||||
os.mkdir(lintian_tmp)
 | 
			
		||||
 | 
			
		||||
def rsync(source, destination):
 | 
			
		||||
    src = Path(source)
 | 
			
		||||
    dst = Path(destination)
 | 
			
		||||
    dst.mkdir(parents=True, exist_ok=True)
 | 
			
		||||
 | 
			
		||||
    for item in src.iterdir():
 | 
			
		||||
        src_path = item
 | 
			
		||||
        dst_path = dst / item.name
 | 
			
		||||
 | 
			
		||||
        if src_path.is_symlink():
 | 
			
		||||
            if dst_path.exists() or dst_path.is_symlink():
 | 
			
		||||
                dst_path.unlink()
 | 
			
		||||
            os.symlink(os.readlink(src_path), dst_path)
 | 
			
		||||
        elif src_path.is_dir():
 | 
			
		||||
            shutil.copytree(src_path, dst_path, symlinks=True, dirs_exist_ok=True)
 | 
			
		||||
        else:
 | 
			
		||||
            shutil.copy2(src_path, dst_path)
 | 
			
		||||
 | 
			
		||||
def process_sources(url):
 | 
			
		||||
    tmpdir = os.
 | 
			
		||||
    changes_file = url.split("/")[-1]
 | 
			
		||||
    logging.info(f"Downloading {changes_file} and friends via dget")
 | 
			
		||||
    dget_command = ["dget", "-u", url]
 | 
			
		||||
    result = subprocess.run(dget_command, cwd=tmpdir, capture_output=True)
 | 
			
		||||
 | 
			
		||||
    with open(os.path.join(tmpdir, changes_file), "r") as f:
 | 
			
		||||
        changes_obj = Changes(f)
 | 
			
		||||
    source = changes_obj["Source"]
 | 
			
		||||
    arch = changes_obj["Architecture"].replace("all", "").replace("_translations", "").split(" ")[0].strip()
 | 
			
		||||
 | 
			
		||||
    if arch == "":
 | 
			
		||||
        return
 | 
			
		||||
 | 
			
		||||
    logging.info(f"Running Lintian for {source} on {arch}")
 | 
			
		||||
    lintian_command = ["lintian", "-EvIL", "+pedantic", changes_file]
 | 
			
		||||
    result = subprocess.run(lintian_command, cwd=tmpdir, capture_output=True)
 | 
			
		||||
    stderr = result.stderr.decode("utf-8").strip()
 | 
			
		||||
    stdout = result.stdout.decode("utf-8").strip()
 | 
			
		||||
 | 
			
		||||
    if stderr == stdout:
 | 
			
		||||
        lintian_output = stderr
 | 
			
		||||
    elif stderr != "" and stdout == "":
 | 
			
		||||
        lintian_output = stderr
 | 
			
		||||
    elif stderr == "" and stdout != "":
 | 
			
		||||
        lintian_output = stdout
 | 
			
		||||
    else:
 | 
			
		||||
        lintian_output = f"{stderr}\n{stdout}"
 | 
			
		||||
 | 
			
		||||
    output_path = os.path.join(lintian_tmp, source)
 | 
			
		||||
    if not os.path.exists(output_path):
 | 
			
		||||
        os.mkdir(output_path)
 | 
			
		||||
 | 
			
		||||
    with open(os.path.join(output_path, f"{arch}.txt"), "w") as f:
 | 
			
		||||
        f.write(lintian_output)
 | 
			
		||||
 | 
			
		||||
with ThreadPoolExecutor(max_workers=30) as executor:
 | 
			
		||||
    futures = set()
 | 
			
		||||
 | 
			
		||||
    def main_source_iter():
 | 
			
		||||
        last_run_file = os.path.join(args.user, ".LAST_RUN")
 | 
			
		||||
        last_run_datetime = datetime.now(timezone.utc) - timedelta(days=365)
 | 
			
		||||
        if os.path.exists(last_run_file):
 | 
			
		||||
            with open(last_run_file, "r") as file:
 | 
			
		||||
                last_run_time = file.read().strip()
 | 
			
		||||
 | 
			
		||||
            last_run_datetime = datetime.fromisoformat(last_run_time)
 | 
			
		||||
            last_run_datetime = last_run_datetime.replace(tzinfo=timezone.utc)
 | 
			
		||||
            logging.info(f"Last run: {last_run_datetime}")
 | 
			
		||||
 | 
			
		||||
        with open(last_run_file, "w") as file:
 | 
			
		||||
            current_time = datetime.now(timezone.utc).isoformat()
 | 
			
		||||
            file.write(current_time)
 | 
			
		||||
 | 
			
		||||
        for source in ppa.getPublishedSources(status="Published", distro_series=current_series):
 | 
			
		||||
            for build in source.getBuilds():
 | 
			
		||||
                if build.buildstate == "Successfully built" and build.datebuilt >= last_run_datetime:
 | 
			
		||||
                    futures.add(executor.submit(process_sources, build.changesfile_url))
 | 
			
		||||
 | 
			
		||||
    futures.add(executor.submit(main_source_iter))
 | 
			
		||||
 | 
			
		||||
    while futures:
 | 
			
		||||
        done, not_done = wait(futures, return_when=FIRST_COMPLETED)
 | 
			
		||||
 | 
			
		||||
        for future in done:
 | 
			
		||||
            try:
 | 
			
		||||
                result = future.result()
 | 
			
		||||
            except Exception as e:
 | 
			
		||||
                logging.exception("Task generated an exception:")
 | 
			
		||||
            finally:
 | 
			
		||||
                futures.remove(future)
 | 
			
		||||
 | 
			
		||||
rsync(lintian_tmp, lintian)
 | 
			
		||||
shutil.rmtree(lintian_tmp)
 | 
			
		||||
clean_old_logs(LOG_DIR)
 | 
			
		||||
logging.info("Done")
 | 
			
		||||
							
								
								
									
										103
									
								
								pending-packages
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										103
									
								
								pending-packages
									
									
									
									
									
										Executable file
									
								
							@ -0,0 +1,103 @@
 | 
			
		||||
#!/usr/bin/env python3
 | 
			
		||||
#
 | 
			
		||||
# Copyright (C) 2024 Simon Quigley <tsimonq2@ubuntu.com>
 | 
			
		||||
#
 | 
			
		||||
# This program is free software: you can redistribute it and/or modify
 | 
			
		||||
# it under the terms of the GNU General Public License as published by
 | 
			
		||||
# the Free Software Foundation, either version 3 of the License, or
 | 
			
		||||
# (at your option) any later version.
 | 
			
		||||
#
 | 
			
		||||
# This program is distributed in the hope that it will be useful,
 | 
			
		||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
# GNU General Public License for more details.
 | 
			
		||||
#
 | 
			
		||||
# You should have received a copy of the GNU General Public License
 | 
			
		||||
# along with this program.  If not, see <https://www.gnu.org/licenses/>.
 | 
			
		||||
 | 
			
		||||
import argparse
 | 
			
		||||
from datetime import datetime, timedelta, timezone
 | 
			
		||||
from launchpadlib.launchpad import Launchpad
 | 
			
		||||
 | 
			
		||||
print(f"Logging into Launchpad...")
 | 
			
		||||
launchpad = Launchpad.login_with("pending-packages", "production", version="devel")
 | 
			
		||||
 | 
			
		||||
print("Logged in. Initializing repositories...")
 | 
			
		||||
ubuntu = launchpad.distributions["ubuntu"]
 | 
			
		||||
lubuntu_ci = launchpad.people["lubuntu-ci"]
 | 
			
		||||
regular = lubuntu_ci.getPPAByName(distribution=ubuntu, name="unstable-ci")
 | 
			
		||||
proposed = lubuntu_ci.getPPAByName(distribution=ubuntu, name="unstable-ci-proposed")
 | 
			
		||||
 | 
			
		||||
parser = argparse.ArgumentParser()
 | 
			
		||||
parser.add_argument("release")
 | 
			
		||||
args = parser.parse_args()
 | 
			
		||||
series = ubuntu.getSeries(name_or_version=args.release)
 | 
			
		||||
 | 
			
		||||
# First, check if any sources are still publishing
 | 
			
		||||
print("Repositories initialized. Checking for pending sources...")
 | 
			
		||||
records = [regular.getPublishedSources(status="Pending", distro_series=series),
 | 
			
		||||
           proposed.getPublishedSources(status="Pending", distro_series=series)]
 | 
			
		||||
total_pending = sum([len(i) for i in records])
 | 
			
		||||
has_pending = total_pending != 0
 | 
			
		||||
 | 
			
		||||
if has_pending:
 | 
			
		||||
    print(f"Total sources pending: {total_pending}")
 | 
			
		||||
    print("Sources are still pending, not running Britney")
 | 
			
		||||
    exit(1)
 | 
			
		||||
 | 
			
		||||
# Finally, check if any builds are still running/queued
 | 
			
		||||
print("No pending sources, continuing. Checking for pending builds...")
 | 
			
		||||
total_pending = 0
 | 
			
		||||
total_retried = 0
 | 
			
		||||
for archive in [proposed, regular]:
 | 
			
		||||
    one_hour_ago = datetime.now(timezone.utc) - timedelta(hours=1)
 | 
			
		||||
    for source in archive.getPublishedSources(status="Published", distro_series=series):
 | 
			
		||||
        for build in source.getBuilds():
 | 
			
		||||
            if build.buildstate == "Currently building":
 | 
			
		||||
                if build.date_started >= one_hour_ago:
 | 
			
		||||
                    total_pending += 1
 | 
			
		||||
            elif build.buildstate == "Needs building":
 | 
			
		||||
                total_pending += 1
 | 
			
		||||
            # This isn't technically related, but retry failed builds without logs
 | 
			
		||||
            elif build.buildstate == "Chroot problem" or (build.buildstate == "Failed to build" and not build.build_log_url):
 | 
			
		||||
                if build.can_be_retried:
 | 
			
		||||
                    build.retry()
 | 
			
		||||
                    total_pending += 1
 | 
			
		||||
                    total_retried += 1
 | 
			
		||||
 | 
			
		||||
if total_retried != 0:
 | 
			
		||||
    print(f"Total builds retried due to builder flakiness: {total_retried}")
 | 
			
		||||
 | 
			
		||||
if total_pending != 0:
 | 
			
		||||
    print(f"Total builds pending: {total_pending}")
 | 
			
		||||
    print("Builds are still running, not running Britney")
 | 
			
		||||
    exit(1)
 | 
			
		||||
 | 
			
		||||
print("No pending builds, continuing. Checking for pending binaries...")
 | 
			
		||||
has_pending = False
 | 
			
		||||
for pocket in [proposed, regular]:
 | 
			
		||||
    if has_pending:
 | 
			
		||||
        break
 | 
			
		||||
    three_hours_ago = datetime.now(timezone.utc) - timedelta(hours=3)
 | 
			
		||||
    check_builds = set()
 | 
			
		||||
    current_builds = set()
 | 
			
		||||
    source_packages = []
 | 
			
		||||
 | 
			
		||||
    for build in pocket.getBuildRecords(build_state="Successfully built"):
 | 
			
		||||
        if build.datebuilt < three_hours_ago:
 | 
			
		||||
            del source_packages
 | 
			
		||||
            break
 | 
			
		||||
        check_builds.add(build.title)
 | 
			
		||||
        source_package = build.current_source_publication
 | 
			
		||||
        if source_package and source_package.distro_series == series and source_package not in source_packages:
 | 
			
		||||
            source_packages.append(source_package)
 | 
			
		||||
            for binary in source_package.getPublishedBinaries():
 | 
			
		||||
                current_builds.add(binary.build.title)
 | 
			
		||||
 | 
			
		||||
    has_pending = not check_builds.issuperset(current_builds) or has_pending
 | 
			
		||||
 | 
			
		||||
if has_pending:
 | 
			
		||||
    print("Binaries are still pending, not running Britney")
 | 
			
		||||
    exit(1)
 | 
			
		||||
 | 
			
		||||
print("All clear. Starting Britney.")
 | 
			
		||||
							
								
								
									
										38
									
								
								run-britney
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										38
									
								
								run-britney
									
									
									
									
									
										Executable file
									
								
							@ -0,0 +1,38 @@
 | 
			
		||||
#!/bin/bash
 | 
			
		||||
#
 | 
			
		||||
# Copyright (C) 2024 Simon Quigley <tsimonq2@ubuntu.com>
 | 
			
		||||
#
 | 
			
		||||
# This program is free software: you can redistribute it and/or modify
 | 
			
		||||
# it under the terms of the GNU General Public License as published by
 | 
			
		||||
# the Free Software Foundation, either version 3 of the License, or
 | 
			
		||||
# (at your option) any later version.
 | 
			
		||||
#
 | 
			
		||||
# This program is distributed in the hope that it will be useful,
 | 
			
		||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
# GNU General Public License for more details.
 | 
			
		||||
#
 | 
			
		||||
# You should have received a copy of the GNU General Public License
 | 
			
		||||
# along with this program.  If not, see <https://www.gnu.org/licenses/>.
 | 
			
		||||
 | 
			
		||||
# Configuration
 | 
			
		||||
LOG_DIR="/srv/lubuntu-ci/output/logs/britney"
 | 
			
		||||
SCRIPT_PATH="fetch-indexes"
 | 
			
		||||
MAX_LOG_AGE=86400 # 24 hours in seconds
 | 
			
		||||
 | 
			
		||||
# Ensure the log directory exists
 | 
			
		||||
mkdir -p "$LOG_DIR"
 | 
			
		||||
 | 
			
		||||
# Log rotation: Remove logs older than MAX_LOG_AGE
 | 
			
		||||
find "$LOG_DIR" -type f -mtime +1 -exec rm -f {} \;
 | 
			
		||||
 | 
			
		||||
# Execute the fetch-indexes script for each release and log output
 | 
			
		||||
for release in plucky oracular noble; do
 | 
			
		||||
    export RELEASE="$release"
 | 
			
		||||
 | 
			
		||||
    # Log file named by current UTC time (HH-MM-SS)
 | 
			
		||||
    LOG_FILE="$LOG_DIR/$RELEASE_$(date -u +"%H-%M-%S").log"
 | 
			
		||||
 | 
			
		||||
    echo "$(date -u +"%Y-%m-%d %H:%M:%S") - Running Britney for $RELEASE" >> "$LOG_FILE"
 | 
			
		||||
    "$SCRIPT_PATH" >> "$LOG_FILE" 2>&1
 | 
			
		||||
done
 | 
			
		||||
							
								
								
									
										11
									
								
								systemd/britney.service
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								systemd/britney.service
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,11 @@
 | 
			
		||||
[Unit]
 | 
			
		||||
Description=Britney
 | 
			
		||||
Wants=britney.timer
 | 
			
		||||
After=network.target
 | 
			
		||||
 | 
			
		||||
[Service]
 | 
			
		||||
Type=simple
 | 
			
		||||
User=lugito
 | 
			
		||||
Group=lugito
 | 
			
		||||
WorkingDirectory=/srv/lubuntu-ci/repos/ci-tools
 | 
			
		||||
ExecStart=/usr/bin/python3 /srv/lubuntu-ci/repos/ci-tools/run-britney
 | 
			
		||||
							
								
								
									
										10
									
								
								systemd/britney.timer
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								systemd/britney.timer
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,10 @@
 | 
			
		||||
[Unit]
 | 
			
		||||
Description=Britney timer
 | 
			
		||||
 | 
			
		||||
[Timer]
 | 
			
		||||
OnBootSec=5min
 | 
			
		||||
OnUnitActiveSec=30min
 | 
			
		||||
Persistent=true
 | 
			
		||||
 | 
			
		||||
[Install]
 | 
			
		||||
WantedBy=timers.target
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user