Merge pull request 'Resolve labels from the org, and never touch a held issue' (#4) from fix/org-labels-and-hands-off into main
This commit was merged in pull request #4.
This commit is contained in:
@@ -45,6 +45,18 @@ import urllib.request
|
|||||||
DEFAULT_LABEL = "Status/Blocked"
|
DEFAULT_LABEL = "Status/Blocked"
|
||||||
DEFAULT_HOST = "https://git.chaosbit.de"
|
DEFAULT_HOST = "https://git.chaosbit.de"
|
||||||
|
|
||||||
|
#: Statuses that mean a human has decided something about this issue which
|
||||||
|
#: outranks the dependency graph. bit, 2026-09-07: *"the reconciler must not
|
||||||
|
#: touch issues that are already on hold or abandoned"*.
|
||||||
|
#:
|
||||||
|
#: "Not touch" is literal - neither add nor remove. Adding would be actively
|
||||||
|
#: destructive once `Status/*` is exclusive again, because the add would
|
||||||
|
#: *replace* the human's label rather than sit beside it, and `Status/On Hold`
|
||||||
|
#: is precisely what makes the backlog sweep skip an issue. A parked issue
|
||||||
|
#: would silently become an available one, every fifteen minutes, with nothing
|
||||||
|
#: in the log to say so.
|
||||||
|
HANDS_OFF = ("Status/On Hold", "Status/Abandoned")
|
||||||
|
|
||||||
|
|
||||||
class Forge:
|
class Forge:
|
||||||
def __init__(self, base, token):
|
def __init__(self, base, token):
|
||||||
@@ -75,13 +87,27 @@ class Forge:
|
|||||||
|
|
||||||
|
|
||||||
def label_id(forge, repo, name):
|
def label_id(forge, repo, name):
|
||||||
"""The label's id, or None if this repo has no such label.
|
"""The label's id, or None if neither the repo nor its org has one.
|
||||||
|
|
||||||
|
Repo first, then the organisation. Labels moved to the org on 2026-09-07
|
||||||
|
(weblib-archive#63) and `repos/<r>/labels` now returns `[]` in all five
|
||||||
|
repos, which made this return None everywhere and turned the whole script
|
||||||
|
into a silent no-op - every run printed "skipped" and reported success.
|
||||||
|
Checking both means it does not care how a given instance is arranged.
|
||||||
|
|
||||||
Returning None rather than exiting matters when several repos are passed:
|
Returning None rather than exiting matters when several repos are passed:
|
||||||
aborting on the third would leave the first two already modified, which is
|
aborting on the third would leave the first two already modified, which is
|
||||||
a worse state than doing nothing. weblib-viewer has no labels at all.
|
a worse state than doing nothing.
|
||||||
"""
|
"""
|
||||||
for label in forge.get(f"repos/{repo}/labels?limit=100"):
|
owner = repo.split("/")[0]
|
||||||
|
for path in (f"repos/{repo}/labels?limit=100",
|
||||||
|
f"orgs/{owner}/labels?limit=100"):
|
||||||
|
try:
|
||||||
|
labels = forge.get(path) or []
|
||||||
|
except urllib.error.HTTPError:
|
||||||
|
# A user-owned repo has no org endpoint; not an error worth dying on.
|
||||||
|
continue
|
||||||
|
for label in labels:
|
||||||
if label["name"] == name:
|
if label["name"] == name:
|
||||||
return label["id"]
|
return label["id"]
|
||||||
return None
|
return None
|
||||||
@@ -110,6 +136,15 @@ def reconcile(forge, repo, label_name, dry_run):
|
|||||||
changed = []
|
changed = []
|
||||||
for item in open_items(forge, repo):
|
for item in open_items(forge, repo):
|
||||||
number = item["number"]
|
number = item["number"]
|
||||||
|
names = {l["name"] for l in item.get("labels") or []}
|
||||||
|
# A human has already ruled on this one. Leave it entirely alone -
|
||||||
|
# neither add nor remove - rather than letting the dependency graph
|
||||||
|
# overwrite a deliberate decision. See HANDS_OFF.
|
||||||
|
held = names.intersection(HANDS_OFF)
|
||||||
|
if held:
|
||||||
|
print(f" hands off {repo}#{number} ({', '.join(sorted(held))})",
|
||||||
|
flush=True)
|
||||||
|
continue
|
||||||
deps = forge.get(f"repos/{repo}/issues/{number}/dependencies") or []
|
deps = forge.get(f"repos/{repo}/issues/{number}/dependencies") or []
|
||||||
if not deps:
|
if not deps:
|
||||||
# No edges, no opinion. See the docstring.
|
# No edges, no opinion. See the docstring.
|
||||||
|
|||||||
Reference in New Issue
Block a user