#!/usr/bin/env python3 """Render thin ci-fleet-publish-based pipelines for the quartet family (oleks/ci-scripts#17) — PREP/PLANNING script, not wired into anything yet. SCOPE NOTE (surfaced to team-lead, not silently assumed): #17 lists 6 repos, but manifests/*.yaml shows only 2 are actually `kind: matrix` (real buildx per-arch-build + manifest-consolidate quartets): xonsh-image, csi-s3. The other 4 are shaped differently and don't map onto the plugin's buildx-build/buildx-manifest flow at all: - the-eye, emdash.kotkanagrilli.fi: kind: single — one native-arch build, no manifest step. These want `flow: publish` (single-step, like the fleet family), not the buildx quartet flow. - flake-hub, woodpecker-peek: kind: split — separate per-arch files using `nix run .#publish-*`, no docker buildx involved at all. Same as above, `flow: publish` per arch file, not buildx-build/buildx-manifest. This script only renders the 2 true matrix-kind repos. The other 4 need a render_thin_fleet.py-style treatment instead — flagged as follow-up, not done here. For xonsh-image/csi-s3, migrating to the plugin's buildx-build/buildx-manifest flow means the plugin's generic `docker buildx build --push ` (building the repo's own Dockerfile directly) REPLACES what `ci/local.sh --arch/--manifest` used to do internally. This is a bigger behavioral swap than the fleet family's migration (which just moves inline shell into `run:`) — needs explicit confirmation that ci/local.sh's Dockerfile build is the ONLY thing it does before this ships (see report). Usage: python3 generator/render_thin_quartet.py --out /path/to/scratch """ import argparse import pathlib import yaml HERE = pathlib.Path(__file__).parent.parent PLUGIN_IMAGE = "git.oleks.space/oleks/ci-fleet-publish:v1" GENERATED_HEADER = ( "# GENERATED by oleks/ci-scripts — do not hand-edit.\n" "# Source: oleks/ci-scripts manifests/{repo}.yaml + " "generator/render_thin_quartet.py (oleks/ci-scripts#17).\n" "# Re-render with: python3 generator/render_thin_quartet.py --out /.woodpecker\n" "\n" ) def render_build(manifest: dict, arch: str, image_name: str) -> str: node_selector = manifest.get("node_selector") backend_options_block = "" if node_selector: lines = "\n".join(f" {k}: {v}" for k, v in node_selector.items()) backend_options_block = ( " backend_options:\n kubernetes:\n nodeSelector:\n" + lines + "\n" ) return f"""{GENERATED_HEADER}labels: arch: {arch} when: - event: tag ref: "refs/tags/v*" steps: - name: build-{arch} image: {PLUGIN_IMAGE} environment: REGISTRY_TOKEN: from_secret: registry_token settings: flow: buildx-build image_name: "{image_name}" tag_scheme: "{{version}}-{{arch}}" {backend_options_block}""" def render_manifest(manifest: dict, arches: list, image_name: str) -> str: platforms = ",".join(arches) return f"""{GENERATED_HEADER}when: - event: tag ref: "refs/tags/v*" depends_on: {chr(10).join(f" - build-{a}" for a in arches)} # oleks/ci-scripts#9: run even if a per-arch build failed — the plugin's # buildx-manifest flow itself probes the registry for which per-arch tags # actually landed and builds the manifest from whatever exists. runs_on: - success - failure steps: - name: manifest image: {PLUGIN_IMAGE} environment: REGISTRY_TOKEN: from_secret: registry_token settings: flow: buildx-manifest image_name: "{image_name}" platforms: "{platforms}" tag_scheme: "{{version}}-{{arch}}" """ def main(): ap = argparse.ArgumentParser() ap.add_argument("--out", type=pathlib.Path, required=True) args = ap.parse_args() manifests_dir = HERE / "manifests" for path in sorted(manifests_dir.glob("*.yaml")): manifest = yaml.safe_load(path.read_text()) if manifest.get("kind") != "matrix": print(f"skip {manifest['repo']} (kind={manifest.get('kind')}, not a buildx quartet)") continue repo = manifest["repo"] image_name = f"git.oleks.space/oleks/{repo}" arches = manifest["arches"] out_dir = args.out / repo out_dir.mkdir(parents=True, exist_ok=True) for arch in arches: target = out_dir / f"build-{arch}.yaml" target.write_text(render_build(manifest, arch, image_name)) print(f"wrote {target}") target = out_dir / "manifest.yaml" target.write_text(render_manifest(manifest, arches, image_name)) print(f"wrote {target}") if __name__ == "__main__": main()