Give each tracked package its own PR, and let a fresh branch tip fall back
The branch tracker stalled omarchy-dev and omarchy-settings-dev twice in a week: - All ~50 auto_merge packages rode one PR, so omasnap-git's failing smoke suite kept the quattro pins unmerged from Oct 4 to Oct 8 (#788). Each package now gets its own PR on auto/track-branches-<package>; packages pinned from the same branch share one, since they move in lockstep. A run closes tracker PRs that master has moved past, and the old single PR. - A branch tip younger than min_release_age held the whole branch, so a run that landed shortly after a quattro commit pinned nothing. The watch now falls back along first-parent history to the newest commit that has cleared the window, measured from clone time so siblings agree. - The every-2-hours schedule fired every 6 to 9 hours in practice; run it hourly.
This commit is contained in:
1 parent
1cef67f336
commit
a7f730d1e6
7 files changed
+187
-50
No files matched your search
@@ -186,9 +186,9 @@ def matches(watch, text, extra=None, full=False):
|
||||
yield candidate(watch, {**(extra or {}), **match.groupdict()})
|
||||
|
||||
|
||||
def git_branch_tip(url, branch, tag_pattern, cache):
|
||||
"""Describe the current tip of an upstream branch:
|
||||
commit, total count, date, and with a tag_pattern
|
||||
def git_branch_tip(url, branch, tag_pattern, cache, min_age=0):
|
||||
"""Describe the newest commit on an upstream branch that has cleared
|
||||
min_age: commit, total count, date, and with a tag_pattern
|
||||
the newest release tag reachable from it plus the distance from that tag,
|
||||
so a branch build can be versioned <tag>.r<n>.g<sha>, above the release it
|
||||
follows and below the next one, the way a pkgver() function would.
|
||||
@@ -196,20 +196,33 @@ def git_branch_tip(url, branch, tag_pattern, cache):
|
||||
One blobless single-branch clone per (url, branch) per run, shared by
|
||||
every package that tracks it, so two recipes pinned from one clone always
|
||||
see the same commit. The clone is read with git only; nothing in it runs.
|
||||
select_release applies the age hold to this tip, without walking back
|
||||
into history (which could select a commit from a merged side branch).
|
||||
A tip younger than min_age falls back along first-parent history, the
|
||||
commits that were themselves the branch tip, never into a merged side
|
||||
branch. Holding the whole branch instead stalled a busy branch for as
|
||||
long as it kept getting commits. Age is measured from when the clone was
|
||||
made, so siblings synced seconds apart cannot straddle the window.
|
||||
"""
|
||||
https(url)
|
||||
key = hashlib.sha256(f"{url}#{branch}".encode()).hexdigest()
|
||||
work = Path(cache) / f"{key}.branch.git"
|
||||
cloned_at = work.with_name(f"{key}.branch.time")
|
||||
if not work.exists():
|
||||
scratch = work.with_name(f"{work.name}.{os.getpid()}.tmp")
|
||||
started = dt.datetime.now(dt.timezone.utc).isoformat()
|
||||
subprocess.run(["git", "clone", "--quiet", "--bare", "--filter=blob:none", "--single-branch", "--branch", branch, url, str(scratch)], check=True)
|
||||
cloned_at.write_text(started)
|
||||
scratch.replace(work)
|
||||
git = ["git", "-C", str(work)]
|
||||
commit = run([*git, "rev-parse", "HEAD"], text=True).strip()
|
||||
if not re.fullmatch(r"[0-9a-f]{40}", commit):
|
||||
raise ValueError("branch tip is not a commit")
|
||||
if min_age:
|
||||
now = dt.datetime.fromisoformat(cloned_at.read_text().strip())
|
||||
for line in run([*git, "log", "--first-parent", "--format=%H %cI", commit], text=True).splitlines():
|
||||
sha, committed = line.split()
|
||||
if (now - dt.datetime.fromisoformat(committed)).total_seconds() >= min_age:
|
||||
commit = sha
|
||||
break
|
||||
count = run([*git, "rev-list", "--count", commit], text=True).strip()
|
||||
date = run([*git, "show", "-s", "--format=%cs", commit], text=True).strip().replace("-", "")
|
||||
timestamp = run([*git, "show", "-s", "--format=%cI", commit], text=True).strip()
|
||||
@@ -233,7 +246,7 @@ def git_branch_tip(url, branch, tag_pattern, cache):
|
||||
return values
|
||||
|
||||
|
||||
def discover(watch, fetch):
|
||||
def discover(watch, fetch, min_age=0):
|
||||
provider = validate(watch)
|
||||
feed = watch[provider]
|
||||
results = []
|
||||
@@ -260,7 +273,7 @@ def discover(watch, fetch):
|
||||
for tag, commit in tags.items():
|
||||
results.extend(matches(watch, tag, {"tag": tag, "commit": commit}, full=True))
|
||||
elif provider == "git_branch":
|
||||
tip = git_branch_tip(feed, watch["branch"], watch.get("tag_pattern"), fetch.cache)
|
||||
tip = git_branch_tip(feed, watch["branch"], watch.get("tag_pattern"), fetch.cache, min_age)
|
||||
results.append(candidate(watch, tip))
|
||||
elif provider == "npm":
|
||||
data = fetch.json("https://registry.npmjs.org/" + quote(feed, safe=""))
|
||||
@@ -544,7 +557,7 @@ def sync(package, fetch, min_age=0, check=False):
|
||||
original = path.read_text()
|
||||
before = read_recipe(path)
|
||||
bypass = os.environ.get("BYPASS_MIN_RELEASE_AGE") == "1"
|
||||
release = select_release(discover(watch, fetch), min_age, bypass=bypass)
|
||||
release = select_release(discover(watch, fetch, 0 if bypass else min_age), min_age, bypass=bypass)
|
||||
if release is None:
|
||||
return {"status": "skipped", "reason": "minimum release age"}
|
||||
current = scalar(before, "pkgver")
|
||||
|
||||
Reference in new issue
Block a user