diff --git a/.github/workflows/generate-baseline.yml b/.github/workflows/generate-baseline.yml new file mode 100644 index 00000000000..c6fe84d53c9 --- /dev/null +++ b/.github/workflows/generate-baseline.yml @@ -0,0 +1,33 @@ +name: Generate Ubuntu Baseline + +on: + workflow_dispatch: + +jobs: + generate: + if: github.actor != 'github-actions[bot]' + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - name: Install Python deps + run: pip install PyYAML + + - name: Generate baseline + run: | + python3 scripts/generate_ubuntu_baseline.py \ + --lts 22.04 24.04 + + - name: Commit if changed + run: | + git config user.name github-actions + git config user.email github-actions@github.com + git add _data/ubuntu-baseline.yml + + if git diff --cached --quiet; then + echo "No changes" + else + git commit -m "Update Ubuntu baseline" + git push + fi \ No newline at end of file diff --git a/_data/ubuntu-baseline.yml b/_data/ubuntu-baseline.yml new file mode 100644 index 00000000000..b92f127186e --- /dev/null +++ b/_data/ubuntu-baseline.yml @@ -0,0 +1,26 @@ +generated_at: '2026-02-28T11:28:15.149816+00:00' +ubuntu: + '22.04': + optional: + libopenmpi-dev: 4.1.2 + petsc-dev: 3.15.5+dfsg1 + python3-dev: 3.10.6 + python3-numpy: 1.21.5 + required: + build-essential: 12.9ubuntu3 + cmake: 3.22.1 + libboost-all-dev: 1.74.0.3ubuntu7 + libeigen3-dev: 3.4.0 + libxml2-dev: 2.9.13+dfsg + '24.04': + optional: + libopenmpi-dev: 4.1.6 + petsc-dev: 3.19.6+dfsg1 + python3-dev: 3.12.3 + python3-numpy: 1.26.4+ds + required: + build-essential: 12.10ubuntu1 + cmake: 3.28.3 + libboost-all-dev: 1.83.0.1ubuntu2 + libeigen3-dev: 3.4.0 + libxml2-dev: 2.9.14+dfsg diff --git a/scripts/dependencies.yml b/scripts/dependencies.yml new file mode 100644 index 00000000000..5934789287a --- /dev/null +++ b/scripts/dependencies.yml @@ -0,0 +1,13 @@ +packages: + required: + - build-essential + - cmake + - libeigen3-dev + - libxml2-dev + - libboost-all-dev + + optional: + - libopenmpi-dev + - petsc-dev + - python3-dev + - python3-numpy \ No newline at end of file diff --git a/scripts/generate_ubuntu_baseline.py b/scripts/generate_ubuntu_baseline.py new file mode 100644 index 00000000000..b483fb4b967 --- /dev/null +++ b/scripts/generate_ubuntu_baseline.py @@ -0,0 +1,124 @@ +#!/usr/bin/env python3 + +import subprocess +import yaml +import argparse +from pathlib import Path +from datetime import datetime, UTC + +ROOT = Path(__file__).resolve().parent.parent +DEPENDENCIES_FILE = ROOT / "scripts" / "dependencies.yml" +OUTPUT_FILE = ROOT / "_data" / "ubuntu-baseline.yml" + + +def load_dependencies(): + with open(DEPENDENCIES_FILE, "r") as f: + data = yaml.safe_load(f) + + packages = data.get("packages", {}) + + return { + "required": sorted(packages.get("required", [])), + "optional": sorted(packages.get("optional", [])), + } + + +def run_command(cmd): + result = subprocess.run( + cmd, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + text=True, + ) + if result.returncode != 0: + print(result.stderr) + return None + return result.stdout.strip() + + +def get_version_docker(ubuntu_version, package): + cmd = [ + "docker", + "run", + "--rm", + f"ubuntu:{ubuntu_version}", + "bash", + "-c", + f"apt-get update -qq && apt-cache policy {package} | grep Candidate | awk '{{print $2}}'", + ] + raw = run_command(cmd) + + if not raw or raw == "(none)": + return None + + # Remove epoch (e.g. "1:") + if ":" in raw: + raw = raw.split(":", 1)[1] + + # Remove Debian revision (e.g. "-1ubuntu1") + raw = raw.split("-", 1)[0] + + return raw + + +def get_version_mock(package): + return "0.0.0-mock" + + +def generate_data(lts_versions, mock=False): + package_groups = load_dependencies() + + result = { + "generated_at": datetime.now(UTC).isoformat(), + "ubuntu": {}, + } + + for version in lts_versions: + result["ubuntu"][version] = { + "required": {}, + "optional": {}, + } + + for group in ["required", "optional"]: + for pkg in package_groups[group]: + if mock: + ver = get_version_mock(pkg) + else: + ver = get_version_docker(version, pkg) + + result["ubuntu"][version][group][pkg] = ver or "not-found" + + return result + + +def write_yaml(data): + OUTPUT_FILE.parent.mkdir(parents=True, exist_ok=True) + with open(OUTPUT_FILE, "w") as f: + yaml.dump(data, f, sort_keys=True) + + +def main(): + parser = argparse.ArgumentParser( + description="Generate Ubuntu baseline package versions using Docker." + ) + parser.add_argument( + "--mock", + action="store_true", + help="Use mock versions instead of querying Docker (useful for testing the pipeline).", + ) + parser.add_argument( + "--lts", + nargs="+", + required=True, + help="Ubuntu LTS versions to query (e.g. 22.04 24.04)", + ) + args = parser.parse_args() + + data = generate_data(args.lts, mock=args.mock) + write_yaml(data) + + print(f"Generated {OUTPUT_FILE}") + + +if __name__ == "__main__": + main() \ No newline at end of file