Track upstream branches as pinned releases on an unattended lane
Since publishing moved to CI on merge, a package whose PKGBUILD never
changes while its source moves was never rebuilt: omarchy-dev and
omarchy-settings-dev followed quattro through "#branch=" and a pkgver()
function, and nothing in this repository changed when quattro did. The
host timers that used to notice are off, so edge fell days behind.
The rule now: no git source without a commit or tag pin
(tests/pinned-sources.sh, run in CI). A package that has to follow a
branch declares a git_branch upstream watch, and the pin moves through
the same PR/build/publish path as every other version bump.
Watch (helpers/upstream-watch.py)
git_branch gains tag_pattern: the newest release tag in the pinned
commit's own history, exposed as {tag}/{version}/{distance}, so a
branch build is versioned <tag>.r<n>.g<sha>, above the release it
follows and below the next one. One blobless clone per branch per
run, shared by every package on it. min_release_age selects the
newest commit older than the window, so a push burst builds once.
Lane (helpers/package-metadata.sh, bin/sync-upstream --lane)
"auto_merge": true moves a package from the reviewed 6-hourly sync
PR to the unattended lane. Packages pinned from the same branch move
together: a failure on one restores the others and fails the group,
so the dev pair can never ship from two quattro commits.
Tracker (.github/workflows/track-branches.yml)
Every two hours: pin, open one PR with a GitHub App token, enable
auto-merge. Branch protection still gates the merge on result,
self-tests and build-isolation. A tip that fails to build stays an
open red PR until the next tick supersedes it. The App is required:
a PR opened with GITHUB_TOKEN has its checks held for approval and
its auto-merge would not fire publish.yml.
The reviewed workflows (sync-upstream, sync-rebuilds) open their PRs
with the same App so their builds start without a maintainer clicking
"Approve workflows to run"; without the App they fall back to
GITHUB_TOKEN and behave as before.
Recipes
The dev pair pins _commit and a real sha256sum, keeps the OMARCHY_SRC
override, and drops pkgver(). Its r-number stays the branch's total
commit count because the published history used it and pacman must
never see the version go down. omasnap-git is new: omacom/omasnap
main, versioned <tag>.r<distance>.g<sha>, provides/conflicts omasnap.
This commit is contained in:
1 parent
e7da505280
commit
d87686ca4f
17 files changed
+746
-68
No files matched your search
+85
-2
@@ -12,6 +12,7 @@ trap 'rm -rf "$TEMP_DIR"' EXIT
|
||||
export UPSTREAM_CACHE_DIR="$TEMP_DIR/watch-cache"
|
||||
|
||||
SPECIFIC_PACKAGES=()
|
||||
LANE=all
|
||||
|
||||
usage() {
|
||||
cat <<EOF
|
||||
@@ -49,6 +50,14 @@ the bypass, so the resulting change still goes through a reviewed PR.
|
||||
Arguments:
|
||||
PACKAGE One or more package names to update (optional)
|
||||
|
||||
Options:
|
||||
--lane <reviewed|auto-merge|all>
|
||||
Which delivery lane to sync (default: all). Packages marked
|
||||
"auto_merge": true ride the unattended lane (the branch tracker
|
||||
opens and auto-merges their PR); everything else is reviewed.
|
||||
The scheduled workflows each pass their own lane so a moving
|
||||
branch tip never waits on a vendor release, or the reverse.
|
||||
|
||||
Commands:
|
||||
self-test Run the offline fixture tests for release selection, the
|
||||
quarantine backstop, and metadata parsing
|
||||
@@ -65,6 +74,14 @@ while [[ $# -gt 0 ]]; do
|
||||
usage
|
||||
exit 0
|
||||
;;
|
||||
--lane)
|
||||
LANE="${2:-}"
|
||||
case "$LANE" in
|
||||
reviewed|auto-merge|all) ;;
|
||||
*) print_error "Invalid --lane '$LANE' (expected reviewed, auto-merge, or all)"; exit 1 ;;
|
||||
esac
|
||||
shift 2
|
||||
;;
|
||||
--*)
|
||||
print_error "Unknown option: $1"
|
||||
exit 1
|
||||
@@ -1042,16 +1059,82 @@ if [[ ${#SPECIFIC_PACKAGES[@]} -gt 0 && "${SPECIFIC_PACKAGES[0]}" == "self-test"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Packages that pin the same upstream branch move together or not at all.
|
||||
# The watch reads one shared clone per run, so they only disagree when one
|
||||
# package's update failed after the tip was chosen (a checksum fetch, say).
|
||||
# Leaving the other one advanced would ship omarchy-dev and
|
||||
# omarchy-settings-dev from different commits, which is exactly the skew the
|
||||
# release pair's lockstep guard exists to prevent. Restore the packages that
|
||||
# moved and count the group as failed; the next run tries again.
|
||||
declare -A BEFORE_SYNC=()
|
||||
|
||||
snapshot_package() {
|
||||
local package="$1" pkgbuild="$PKGBUILDS_DIR/$1/PKGBUILD"
|
||||
[[ -f "$pkgbuild" ]] || return 0
|
||||
mkdir -p "$TEMP_DIR/before"
|
||||
cp "$pkgbuild" "$TEMP_DIR/before/$package"
|
||||
BEFORE_SYNC["$package"]=1
|
||||
}
|
||||
|
||||
branch_watch_key() {
|
||||
local package_dir="$1"
|
||||
jq -r '
|
||||
(.upstream.watch? | objects | select(has("git_branch")))
|
||||
| "\(.git_branch)#\(.branch)"
|
||||
' "$package_dir/.omarchy/package.json" 2>/dev/null
|
||||
}
|
||||
|
||||
enforce_branch_lockstep() {
|
||||
local package key
|
||||
declare -A groups=()
|
||||
for package in "${!BEFORE_SYNC[@]}"; do
|
||||
key=$(branch_watch_key "$PKGBUILDS_DIR/$package")
|
||||
[[ -n "$key" ]] || continue
|
||||
groups["$key"]+="$package "
|
||||
done
|
||||
for key in "${!groups[@]}"; do
|
||||
local members commits
|
||||
read -r -a members <<<"${groups[$key]}"
|
||||
(( ${#members[@]} > 1 )) || continue
|
||||
commits=$(for package in "${members[@]}"; do
|
||||
grep -m1 -E '^_commit=' "$PKGBUILDS_DIR/$package/PKGBUILD" | cut -d= -f2- | tr -d "\"'"
|
||||
done | sort -u | grep -c .)
|
||||
(( commits > 1 )) || continue
|
||||
print_error "Lockstep violation on $key: ${members[*]} pin different commits; restoring all of them"
|
||||
for package in "${members[@]}"; do
|
||||
if ! cmp -s "$TEMP_DIR/before/$package" "$PKGBUILDS_DIR/$package/PKGBUILD"; then
|
||||
cp "$TEMP_DIR/before/$package" "$PKGBUILDS_DIR/$package/PKGBUILD"
|
||||
# Not ((--UPDATED)): an arithmetic command that evaluates to zero
|
||||
# returns 1, and under errexit that would end the run right here.
|
||||
UPDATED=$((UPDATED > 0 ? UPDATED - 1 : 0))
|
||||
fi
|
||||
done
|
||||
((++FAILED))
|
||||
done
|
||||
}
|
||||
|
||||
sync_in_lane() {
|
||||
local package="$1" package_dir="$PKGBUILDS_DIR/$1"
|
||||
if [[ -d "$package_dir" ]] && ! package_in_lane "$package_dir" "$LANE"; then
|
||||
print_info "Skipping $package: not in the $LANE lane"
|
||||
((++SKIPPED))
|
||||
return 0
|
||||
fi
|
||||
snapshot_package "$package"
|
||||
sync_package "$package"
|
||||
}
|
||||
|
||||
if [[ ${#SPECIFIC_PACKAGES[@]} -gt 0 ]]; then
|
||||
SPECIFIC_MODE=true
|
||||
for package in "${SPECIFIC_PACKAGES[@]}"; do
|
||||
sync_package "$package"
|
||||
sync_in_lane "$package"
|
||||
done
|
||||
else
|
||||
while IFS= read -r package; do
|
||||
sync_package "$package"
|
||||
sync_in_lane "$package"
|
||||
done < <(packages_for_upstream_sync)
|
||||
fi
|
||||
enforce_branch_lockstep
|
||||
|
||||
echo ""
|
||||
if [[ $FAILED -gt 0 ]]; then
|
||||
|
||||
Reference in new issue
Block a user