"""
Generate Tekton PipelineRun configuration files for Fedora CoreOS builds.
This script uses Jinja2 templates to generate PipelineRun YAML files for each
stream (branch) and event type combination.
"""
import argparse
import os
import sys
from pathlib import Path
try:
import jinja2
import yaml
except ImportError as e:
print(f"Error: Required module not found: {e.name}", file=sys.stderr)
print("Please install required dependencies:", file=sys.stderr)
print(" pip install jinja2 pyyaml", file=sys.stderr)
sys.exit(1)
EVENT_CONFIGS = {
"on-push": {
"cancel_in_progress": False,
"output_image_suffix": ":{{revision}}",
"image_expires_after": None,
},
"on-pull-request": {
"cancel_in_progress": True,
"cel_expression_base": 'event == "pull_request" && !(files.all.all(f, f.matches("ci/buildroot/") || f.matches(".tekton/fcos-buildroot/"))) && !("manifest-lock.overrides*".pathChanged())',
"output_image_suffix": ":on-pr-{{revision}}",
"image_expires_after": "5d",
},
"on-pull-request-overrides": {
"cancel_in_progress": True,
"cel_expression_base": 'event == "pull_request" && "manifest-lock.overrides*".pathChanged()',
"output_image_suffix": ":on-pr-{{revision}}",
"image_expires_after": "5d",
},
}
def load_streams_config(config_path: Path) -> dict:
"""Load the streams configuration from YAML file."""
with open(config_path, encoding="utf-8") as f:
return yaml.safe_load(f)
def setup_jinja_env(template_dir: Path) -> jinja2.Environment:
"""Set up the Jinja2 environment."""
return jinja2.Environment(
loader=jinja2.FileSystemLoader(template_dir),
keep_trailing_newline=True,
trim_blocks=False,
lstrip_blocks=False,
)
def generate_pipelinerun(
env: jinja2.Environment,
stream_name: str,
stream_config: dict,
event_type: str,
pipeline_bundle: str,
) -> str:
"""Generate a PipelineRun YAML for a stream and event type."""
event_config = EVENT_CONFIGS[event_type]
if event_type == "on-push" and "on_push_cel_expression" in stream_config:
cel_expression = stream_config["on_push_cel_expression"]
elif "cel_expression_base" not in event_config:
cel_expression = f'"false"'
else:
cel_expression = f'{event_config["cel_expression_base"]} && target_branch == "{stream_name}"'
output_image = f"quay.io/konflux-fedora/coreos-tenant/fedora-coreos-{{{{target_branch}}}}{event_config['output_image_suffix']}"
hermetic = stream_config.get("hermetic", True)
template = env.get_template("pipelinerun.yaml.j2")
return template.render(
stream=stream_name,
event_type=event_type,
cancel_in_progress=event_config["cancel_in_progress"],
cel_expression=cel_expression,
output_image=output_image,
image_expires_after=event_config["image_expires_after"],
pipeline_bundle=pipeline_bundle,
hermetic=hermetic,
)
def main():
parser = argparse.ArgumentParser(
description="Generate Tekton PipelineRun configuration files"
)
parser.add_argument(
"--check",
action="store_true",
help="Check if generated files are up to date (exit 1 if not)",
)
args = parser.parse_args()
script_dir = Path(__file__).parent.resolve()
repo_root = script_dir.parent
tekton_dir = repo_root / ".tekton"
template_dir = tekton_dir / "templates"
config_path = template_dir / "streams.yaml"
config = load_streams_config(config_path)
pipeline_bundle = config["config"]["pipeline_bundle"]
streams = config["streams"]
env = setup_jinja_env(template_dir)
files_need_update = []
print("Generating pipelinerun configuration...")
for stream_config in streams:
stream_name = stream_config["name"]
has_overrides = stream_config.get("has_overrides", False)
event_types = ["on-push", "on-pull-request"]
if has_overrides:
event_types.append("on-pull-request-overrides")
for event_type in event_types:
content = generate_pipelinerun(
env, stream_name, stream_config, event_type, pipeline_bundle
)
output_dir = tekton_dir / stream_name / event_type
output_file = output_dir / f"fedora-coreos-{stream_name}-{event_type}.yaml"
if args.check:
if output_file.exists():
existing_content = output_file.read_text(encoding="utf-8")
if existing_content != content:
files_need_update.append(str(output_file))
print(f" {stream_name}-{event_type}: NEED UPDATE")
else:
print(f" {stream_name}-{event_type}: ok")
else:
files_need_update.append(str(output_file))
print(f" {stream_name}-{event_type}: MISSING")
else:
output_dir.mkdir(parents=True, exist_ok=True)
output_file.write_text(content, encoding="utf-8")
print(f" generating {stream_name}-{event_type} pipelinerun")
if args.check:
if files_need_update:
print(
f"\nError: {len(files_need_update)} file(s) need to be regenerated.",
file=sys.stderr,
)
print("Run './ci/generate-tekton-pipelinerun.py' to update them.", file=sys.stderr)
sys.exit(1)
else:
print("\nAll files are up to date.")
else:
print("Done.")
if __name__ == "__main__":
main()