diff --git a/osbuild/main_cli.py b/osbuild/main_cli.py index d1ebfdb08..031a584f8 100644 --- a/osbuild/main_cli.py +++ b/osbuild/main_cli.py @@ -70,7 +70,7 @@ def parse_arguments(sys_argv): parser.add_argument("--cache-max-size", metavar="SIZE", type=parse_size, default=None, help="maximum size of the cache (bytes) or 'unlimited' for no restriction") parser.add_argument("--checkpoint", metavar="ID", action="append", type=str, default=None, - help="stage to commit to the object store during build (can be passed multiple times)") + help="stage to commit to the object store during build (can be passed multiple times), accepts globs") parser.add_argument("--export", metavar="ID", action="append", type=str, default=[], help="object to export, can be passed multiple times") parser.add_argument("--json", action="store_true", @@ -127,10 +127,9 @@ def osbuild_cli(): return 1 if args.checkpoint: - missed = manifest.mark_checkpoints(args.checkpoint) - if missed: - for checkpoint in missed: - print(f"Checkpoint {vt.bold}{checkpoint}{vt.reset} not found!") + marked = manifest.mark_checkpoints(args.checkpoint) + if not marked: + print("No checkpoints matched provided patterns!") print(f"{vt.reset}{vt.bold}{vt.red}Failed{vt.reset}") return 1 diff --git a/osbuild/pipeline.py b/osbuild/pipeline.py index ab43387f3..92b50dcff 100644 --- a/osbuild/pipeline.py +++ b/osbuild/pipeline.py @@ -3,6 +3,7 @@ import hashlib import json import os +from fnmatch import fnmatch from typing import Dict, Generator, Iterable, Iterator, List, Optional from . import buildroot, host, objectstore, remoteloop @@ -472,27 +473,30 @@ def build(self, store, pipelines, monitor, libdir, stage_timeout=None): return results - def mark_checkpoints(self, checkpoints): - points = set(checkpoints) + def mark_checkpoints(self, patterns): + """Match pipeline names, stage ids, and stage names against an iterable + of `fnmatch`-patterns.""" + selected = [] - def mark_stage(stage): - c = stage.id - if c in points: - stage.checkpoint = True - points.remove(c) + def matching(haystack): + return any(fnmatch(haystack, p) for p in patterns) - def mark_pipeline(pl): - if pl.name in points and pl.stages: - pl.stages[-1].checkpoint = True - points.remove(pl.name) + for pipeline in self.pipelines.values(): + # checkpoints are marked on stages, if a pipeline has no stages we + # can't mark it + if not pipeline.stages: + continue - for stage in pl.stages: - mark_stage(stage) + if matching(pipeline.name): + selected.append(pipeline.name) + pipeline.stages[-1].checkpoint = True - for pl in self.pipelines.values(): - mark_pipeline(pl) + for stage in pipeline.stages: + if matching(stage.id) or matching(stage.name): + selected.append(stage.id) + stage.checkpoint = True - return points + return selected def get(self, name_or_id: str) -> Optional[Pipeline]: pl = self.pipelines.get(name_or_id)