Skip to content

Commit 8311b20

Browse files
zardusclaude
andcommitted
safelibs.py: add --filter-tag to skip ports lacking a phase tag
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent d56ec9f commit 8311b20

2 files changed

Lines changed: 91 additions & 2 deletions

File tree

README.md

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ python3 safelibs.py port libyaml --from upgrade
3232
python3 safelibs.py port libyaml --from-last
3333
python3 safelibs.py port libyaml --dry-run
3434
python3 safelibs.py port libyaml --filter-upgradeable
35+
python3 safelibs.py port libyaml --filter-tag 04-test
3536
python3 safelibs.py port libyaml --create-github --github-repo OWNER/libyaml-safe
3637
python3 safelibs.py port libyaml --push-github
3738
python3 safelibs.py port libyaml --github-repo OWNER/libyaml-safe --push-github
@@ -48,8 +49,10 @@ The runner always uses `PORTS_DIR/port-LIBNAME` as the workdir. By default
4849
override it. The workdir is treated as a managed checkout for
4950
`https://github.com/safelibs/port-LIBNAME.git`. Managed checkouts are cloned
5051
when the GitHub repo exists, fetched before use, and fast-forward pulled when
51-
the worktree is clean. If the GitHub repo does not exist yet, the runner
52-
initializes a fresh local git repo in that path. Use `--no-auto-pull` to skip
52+
the worktree is clean. If the GitHub repo does not exist yet, the runner falls
53+
back to cloning `safelibs/port-template` as the scaffold and rewrites `origin`
54+
to the eventual `port-LIBNAME` URL; if the template clone also fails, it
55+
initializes a fresh local git repo. Use `--no-auto-pull` to skip
5356
clone/fetch/pull, and `--github-owner` or `--github-prefix` for non-default port
5457
repo namespaces.
5558

safelibs.py

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
safelibs.py port [libname ...]
88
safelibs.py port --jobs 4
99
safelibs.py port [libname ...] --filter-upgradeable
10+
safelibs.py port [libname ...] --filter-tag 04-test
1011
safelibs.py port --from-validator
1112
safelibs.py port [libname ...] -L [logfile]
1213
safelibs.py port [libname ...] --from 01-recon
@@ -1418,6 +1419,27 @@ def _find_last_tagged_phase_index(workdir, libname, phases, log_handle=None):
14181419
return matches[-1]
14191420

14201421

1422+
def _tag_in_history(workdir, libname, phase, log_handle=None):
1423+
"""Return True if libname/phase is reachable from HEAD in workdir."""
1424+
if not _workdir_has_git_history(workdir, log_handle=log_handle):
1425+
return False
1426+
tag = f"{libname}/{phase}"
1427+
result = subprocess.run(
1428+
["git", "tag", "--merged", "HEAD", "--list", tag],
1429+
cwd=workdir,
1430+
capture_output=True,
1431+
text=True,
1432+
)
1433+
if result.returncode != 0:
1434+
_emit(
1435+
f"Failed to check tag {tag} in {workdir}: {result.stderr.strip()}",
1436+
log_handle=log_handle,
1437+
stream=sys.stderr,
1438+
)
1439+
return False
1440+
return tag in result.stdout.splitlines()
1441+
1442+
14211443
def _workdir_has_git_history(workdir, log_handle=None):
14221444
if not os.path.exists(os.path.join(workdir, ".git")):
14231445
return False
@@ -1945,6 +1967,16 @@ def _build_parser():
19451967
"when it does not."
19461968
),
19471969
)
1970+
parser.add_argument(
1971+
"--filter-tag",
1972+
dest="filter_tag",
1973+
metavar="PHASE",
1974+
default=None,
1975+
help=(
1976+
"For 'port', skip libraries whose port checkout does not have a "
1977+
"<libname>/PHASE tag reachable from HEAD (e.g. '04-test')."
1978+
),
1979+
)
19481980
parser.add_argument(
19491981
"--from-validator",
19501982
action="store_true",
@@ -2871,6 +2903,26 @@ def _round_robin_ports(args, scripts, phases, log_handle=None, log_path=None):
28712903
)
28722904
sys.exit(1)
28732905

2906+
filter_tag = getattr(args, "filter_tag", None)
2907+
if filter_tag:
2908+
kept_repos = []
2909+
for repo in repos:
2910+
if _tag_in_history(repo["workdir"], repo["libname"], filter_tag, log_handle=log_handle):
2911+
kept_repos.append(repo)
2912+
else:
2913+
_emit(
2914+
f"--filter-tag: skipping {repo['libname']} "
2915+
f"(no {repo['libname']}/{filter_tag} tag reachable from HEAD).",
2916+
log_handle=log_handle,
2917+
)
2918+
_emit(
2919+
f"--filter-tag {filter_tag}: kept {len(kept_repos)} of {len(repos)} known port(s).",
2920+
log_handle=log_handle,
2921+
)
2922+
if not kept_repos:
2923+
return
2924+
repos = kept_repos
2925+
28742926
if jobs <= 1:
28752927
_emit(
28762928
f"Round-robin porting {len(repos)} known port(s).",
@@ -3076,6 +3128,38 @@ def _run_pipeline(args, log_handle=None, log_path=None):
30763128
)
30773129
sys.exit(1)
30783130

3131+
filter_tag = getattr(args, "filter_tag", None)
3132+
if filter_tag and _find_phase_index(phases, filter_tag) is None:
3133+
_emit(
3134+
f"Unknown phase '{filter_tag}' for --filter-tag. Available: {phases}",
3135+
log_handle=log_handle,
3136+
stream=sys.stderr,
3137+
)
3138+
sys.exit(1)
3139+
3140+
if filter_tag and libnames:
3141+
ports_dir = os.path.abspath(getattr(args, "ports_dir", DEFAULT_PORTS_DIR))
3142+
github_prefix = getattr(args, "github_prefix", DEFAULT_PORT_REPO_PREFIX)
3143+
kept = []
3144+
for libname in libnames:
3145+
workdir = _default_port_workdir(libname, ports_dir, github_prefix)
3146+
if _tag_in_history(workdir, libname, filter_tag, log_handle=log_handle):
3147+
kept.append(libname)
3148+
else:
3149+
_emit(
3150+
f"--filter-tag: skipping {libname} "
3151+
f"(no {libname}/{filter_tag} tag reachable from HEAD).",
3152+
log_handle=log_handle,
3153+
)
3154+
_emit(
3155+
f"--filter-tag {filter_tag}: kept {len(kept)} of {len(libnames)} library(ies).",
3156+
log_handle=log_handle,
3157+
)
3158+
if not kept:
3159+
return
3160+
libnames = kept
3161+
args.libname = list(libnames)
3162+
30793163
if not libnames:
30803164
_round_robin_ports(args, scripts, phases, log_handle=log_handle, log_path=log_path)
30813165
return
@@ -3122,6 +3206,8 @@ def main():
31223206
)
31233207
if args.filter_upgradeable:
31243208
parser.error("--filter-upgradeable can only be used with action 'port'")
3209+
if args.filter_tag:
3210+
parser.error("--filter-tag can only be used with action 'port'")
31253211
if args.create_github:
31263212
parser.error("--create-github can only be used with action 'port'")
31273213
if args.push_github:

0 commit comments

Comments
 (0)