Rebuild packages when what they link against moves
A package that links Qt private API has to be rebuilt whenever qt6-base moves, because Qt_6_PRIVATE_API symbols are not covered by the soname and pacman upgrades Qt out from under the installed binary while the dependency stays unversioned. Nothing here noticed. Both version gates ask whether the package's own source moved, and for a VCS package pinned to a commit that answer stays no through every Qt release.
Unlocking the build gate would not have been enough on its own. A rebuild that reuses the published version string produces a package pacman never offers anyone, so the trigger has to edit git and bump pkgrel, which is why it sits beside sync-aur and sync-upstream rather than inside check-versions or the builder. Once pkgrel moves, both existing gates already do the right thing untouched.
Packages opt in with rebuild_on in .omarchy/package.json. bin/sync-rebuilds records what each was last bumped for in rebuilt_against and compares that to core, extra and multilib, ignoring testing and kde-unstable because those are not what the builder links against. A package with no record yet is only recorded, never bumped: what its published build linked against is not knowable from here, so the first run establishes the baseline. For an AUR-synced package the bump is written as the dotted Omarchy pkgrel suffix in the metadata as well, since the next sync replaces the PKGBUILD wholesale and would otherwise drop it.
🤖 Generated by Opus 5 in Claude Code.
This commit is contained in:
@@ -0,0 +1,99 @@
|
|||||||
|
name: Sync Rebuild Triggers
|
||||||
|
|
||||||
|
on:
|
||||||
|
schedule:
|
||||||
|
# Every 6 hours, off the hour to dodge the scheduling backlog at :00
|
||||||
|
- cron: '40 */6 * * *'
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
packages:
|
||||||
|
description: 'Specific packages to update (space-separated, leave empty for all)'
|
||||||
|
required: false
|
||||||
|
default: ''
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
sync:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
pull-requests: write
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
|
|
||||||
|
# Runs in an Arch container because the question being asked is what the
|
||||||
|
# build container will link against, and pacman's own view of core/extra is
|
||||||
|
# the only answer that matches.
|
||||||
|
- name: Bump pkgrel for packages whose dependencies moved
|
||||||
|
run: |
|
||||||
|
docker run --rm \
|
||||||
|
-e PACKAGES="$PACKAGES" \
|
||||||
|
-e HOST_UID="$(id -u)" \
|
||||||
|
-e HOST_GID="$(id -g)" \
|
||||||
|
-v "$PWD/bin:/workspace/bin:ro" \
|
||||||
|
-v "$PWD/helpers:/workspace/helpers:ro" \
|
||||||
|
-v "$PWD/pkgbuilds:/workspace/pkgbuilds" \
|
||||||
|
-w /workspace \
|
||||||
|
archlinux:base-devel bash -lc '
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
pacman -Syu --noconfirm jq
|
||||||
|
|
||||||
|
groupadd -g "$HOST_GID" runner
|
||||||
|
useradd -m -u "$HOST_UID" -g "$HOST_GID" runner
|
||||||
|
chown -R runner:runner /workspace/pkgbuilds
|
||||||
|
|
||||||
|
if [[ -n "${PACKAGES:-}" ]]; then
|
||||||
|
read -r -a package_args <<< "$PACKAGES"
|
||||||
|
runuser -u runner -- ./bin/sync-rebuilds "${package_args[@]}"
|
||||||
|
else
|
||||||
|
runuser -u runner -- ./bin/sync-rebuilds
|
||||||
|
fi
|
||||||
|
'
|
||||||
|
env:
|
||||||
|
PACKAGES: ${{ github.event.inputs.packages }}
|
||||||
|
|
||||||
|
- name: Check for changes
|
||||||
|
id: changes
|
||||||
|
run: |
|
||||||
|
if [ -z "$(git status --porcelain)" ]; then
|
||||||
|
echo "has_changes=false" >> "$GITHUB_OUTPUT"
|
||||||
|
else
|
||||||
|
echo "has_changes=true" >> "$GITHUB_OUTPUT"
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Create Pull Request
|
||||||
|
if: steps.changes.outputs.has_changes == 'true'
|
||||||
|
uses: peter-evans/create-pull-request@v7
|
||||||
|
with:
|
||||||
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
commit-message: 'chore: rebuild against updated dependencies'
|
||||||
|
title: 'chore: rebuild against updated dependencies'
|
||||||
|
body: |
|
||||||
|
Automated pkgrel bump for packages that link against a dependency
|
||||||
|
which has moved in the official repositories.
|
||||||
|
|
||||||
|
Each package names those dependencies in `rebuild_on` and carries the
|
||||||
|
versions its current pkgrel was bumped for in `rebuilt_against`. The
|
||||||
|
bump is what makes the rebuilt package an upgrade pacman will offer;
|
||||||
|
without it the build produces the version already published and no
|
||||||
|
one receives it.
|
||||||
|
branch: auto/sync-rebuilds
|
||||||
|
delete-branch: true
|
||||||
|
labels: automated
|
||||||
|
reviewers: ryanrhughes
|
||||||
|
|
||||||
|
- name: Notify Basecamp on failure
|
||||||
|
if: failure() && env.BASECAMP_CHATBOT_URL != ''
|
||||||
|
env:
|
||||||
|
BASECAMP_CHATBOT_URL: ${{ secrets.BASECAMP_CHATBOT_URL }}
|
||||||
|
run: |
|
||||||
|
curl -s -o /dev/null \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d "$(jq -n --arg content \
|
||||||
|
"🔴 <strong>Rebuild trigger sync failed</strong><br><a href=\"${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}\">View run</a>" \
|
||||||
|
'{content: $content}')" \
|
||||||
|
"$BASECAMP_CHATBOT_URL"
|
||||||
@@ -283,6 +283,29 @@ than downloading the artifacts — see `pkgbuilds/openai-codex-desktop/.omarchy/
|
|||||||
which reads OpenAI's Debian package index and never fetches the 750 MB of debs
|
which reads OpenAI's Debian package index and never fetches the 750 MB of debs
|
||||||
it describes.
|
it describes.
|
||||||
|
|
||||||
|
### Sync Rebuild Triggers
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bin/sync-rebuilds # Bump every package whose dependencies moved
|
||||||
|
bin/sync-rebuilds quickshell-git # Update specific packages
|
||||||
|
```
|
||||||
|
|
||||||
|
Some packages have to be rebuilt when something they link against changes, even though nothing in their own source moved. A Qt private-API consumer is the usual case: `Qt_6_PRIVATE_API` symbols are not covered by the soname, so a qt6-base point release can leave an installed binary unable to resolve a symbol at startup, and pacman upgrades Qt out from under it because the dependency is unversioned. The package still builds from the same git commit, so nothing in the normal version check notices.
|
||||||
|
|
||||||
|
A package names those dependencies in `.omarchy/package.json`:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{ "source": "aur", "sync": false, "rebuild_on": ["qt6-base", "qt6-declarative", "qt6-wayland"] }
|
||||||
|
```
|
||||||
|
|
||||||
|
`bin/sync-rebuilds` reads each named package's version from the official repositories and compares it to `rebuilt_against`, the record of what the checked-in pkgrel was last bumped for. When they differ it bumps pkgrel and rewrites the record. A package with no record yet is only recorded, never bumped: what its published build linked against is not knowable from here, so the first run establishes the baseline and the next change acts on it.
|
||||||
|
|
||||||
|
The bump is the point of the command, and it has to land in git rather than in the builder. A rebuild that reuses the published version string produces a package pacman will never offer anyone, so merely unlocking the build gate would ship nothing. Bumping pkgrel needs no other change: `bin/check-versions` and the builder both already rebuild when pkgrel moves.
|
||||||
|
|
||||||
|
For an AUR-synced package the bump is expressed as the dotted Omarchy pkgrel suffix in the metadata as well as in the PKGBUILD, because the next AUR sync replaces the PKGBUILD wholesale and would otherwise drop it.
|
||||||
|
|
||||||
|
Versions are read from the local pacman database, so this runs on Arch or in an Arch container against a synced database. Only `core`, `extra` and `multilib` count: a Qt release sitting in testing or kde-unstable is not what the builder will link against, and rebuilding for it would ship a package built against the wrong ABI.
|
||||||
|
|
||||||
### Other
|
### Other
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@@ -296,6 +319,7 @@ bin/add-package <package> # Add an AUR/local package with metadata
|
|||||||
bin/package-worktree <package> # Create upstream/patched/current scratch workspace
|
bin/package-worktree <package> # Create upstream/patched/current scratch workspace
|
||||||
bin/repo remove <package> # Remove package
|
bin/repo remove <package> # Remove package
|
||||||
bin/sync-upstream # Update packages that track a vendor release feed
|
bin/sync-upstream # Update packages that track a vendor release feed
|
||||||
|
bin/sync-rebuilds # Bump pkgrel for packages whose dependencies moved
|
||||||
bin/clean-docker # Clear Docker images/cache (forces fresh rebuild)
|
bin/clean-docker # Clear Docker images/cache (forces fresh rebuild)
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -439,6 +463,8 @@ Fields:
|
|||||||
- `release_ring`: optional. `fast` means the package is built directly for stable as well as edge. Packages without a ring build in edge and reach stable through tested artifact promotion (`bin/repo migrate`).
|
- `release_ring`: optional. `fast` means the package is built directly for stable as well as edge. Packages without a ring build in edge and reach stable through tested artifact promotion (`bin/repo migrate`).
|
||||||
- `skip_build`: optional boolean; defaults to `false`. Set `true` to exclude a package from scheduled version checks and unscoped builds. The package can still be built explicitly with `bin/repo release --package <name>`.
|
- `skip_build`: optional boolean; defaults to `false`. Set `true` to exclude a package from scheduled version checks and unscoped builds. The package can still be built explicitly with `bin/repo release --package <name>`.
|
||||||
- `pkgrel`: optional Omarchy pkgrel suffix for a version-pinned rebuild bump. This emits `<aur pkgrel>.<suffix>` instead of replacing AUR's pkgrel. `offset` can be used only when preserving monotonic upgrades from old absolute pkgrel bumps. The metadata is removed automatically when AUR sync changes `pkgver`; the current package version is read from the checked-in PKGBUILD, so the version is not duplicated in JSON.
|
- `pkgrel`: optional Omarchy pkgrel suffix for a version-pinned rebuild bump. This emits `<aur pkgrel>.<suffix>` instead of replacing AUR's pkgrel. `offset` can be used only when preserving monotonic upgrades from old absolute pkgrel bumps. The metadata is removed automatically when AUR sync changes `pkgver`; the current package version is read from the checked-in PKGBUILD, so the version is not duplicated in JSON.
|
||||||
|
- `rebuild_on`: optional array of package names this package links against closely enough that it must be rebuilt when they change, independent of its own source. Read by `bin/sync-rebuilds`.
|
||||||
|
- `rebuilt_against`: written by `bin/sync-rebuilds`. Records the version of each `rebuild_on` package that the current pkgrel was bumped for.
|
||||||
- `upstream_commit`: set by `bin/sync-aur` for AUR packages. Used by `bin/package-worktree` to recreate the exact raw AUR package that Omarchy last synced.
|
- `upstream_commit`: set by `bin/sync-aur` for AUR packages. Used by `bin/package-worktree` to recreate the exact raw AUR package that Omarchy last synced.
|
||||||
|
|
||||||
### Build Matrix
|
### Build Matrix
|
||||||
@@ -567,6 +593,8 @@ Packages are only rebuilt if:
|
|||||||
- PKGBUILD version is newer than repository version
|
- PKGBUILD version is newer than repository version
|
||||||
- Package doesn't exist in production
|
- Package doesn't exist in production
|
||||||
|
|
||||||
|
Neither notices a package that has to be rebuilt because something underneath it changed. That case is handled by turning it into a version change: `bin/sync-rebuilds` bumps pkgrel when a dependency named in `rebuild_on` moves.
|
||||||
|
|
||||||
## Automated Releases
|
## Automated Releases
|
||||||
|
|
||||||
The repository includes GitHub workflows and systemd services for automated releases.
|
The repository includes GitHub workflows and systemd services for automated releases.
|
||||||
@@ -577,6 +605,7 @@ The repository includes GitHub workflows and systemd services for automated rele
|
|||||||
|
|
||||||
1. **sync-aur.yml** (Every 6 hours): Syncs AUR packages according to `.omarchy/package.json` and opens a PR when changes are found.
|
1. **sync-aur.yml** (Every 6 hours): Syncs AUR packages according to `.omarchy/package.json` and opens a PR when changes are found.
|
||||||
2. **sync-upstream.yml** (Every 6 hours): Runs `.omarchy/upstream.sh` for packages that track a vendor release feed and opens a PR when a newer version is out.
|
2. **sync-upstream.yml** (Every 6 hours): Runs `.omarchy/upstream.sh` for packages that track a vendor release feed and opens a PR when a newer version is out.
|
||||||
|
3. **sync-rebuilds.yml** (Every 6 hours): Bumps pkgrel for packages whose `rebuild_on` dependencies have moved in the official repositories and opens a PR.
|
||||||
|
|
||||||
#### Systemd Services
|
#### Systemd Services
|
||||||
|
|
||||||
|
|||||||
Executable
+340
@@ -0,0 +1,340 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
BUILD_ROOT=$(realpath "${BASH_SOURCE[0]%/*}/..")
|
||||||
|
source "$BUILD_ROOT/helpers/message-helpers.sh"
|
||||||
|
source "$BUILD_ROOT/helpers/paths.sh"
|
||||||
|
source "$BUILD_ROOT/helpers/package-metadata.sh"
|
||||||
|
|
||||||
|
SPECIFIC_PACKAGES=()
|
||||||
|
|
||||||
|
# Only the repositories a user actually installs from. A Qt release sitting in
|
||||||
|
# testing or kde-unstable is not what the build container will link against, and
|
||||||
|
# rebuilding for it would ship a package built against the wrong ABI.
|
||||||
|
OFFICIAL_REPOS=" core extra multilib core-debug extra-debug "
|
||||||
|
|
||||||
|
usage() {
|
||||||
|
cat <<EOF
|
||||||
|
Usage: $0 [PACKAGE...]
|
||||||
|
|
||||||
|
Bump pkgrel for packages that have to be rebuilt when a dependency changes
|
||||||
|
underneath them, rather than when their own source moves.
|
||||||
|
|
||||||
|
A package opts in by naming those dependencies in .omarchy/package.json:
|
||||||
|
|
||||||
|
{ "source": "aur", "sync": false, "rebuild_on": ["qt6-base"] }
|
||||||
|
|
||||||
|
The versions the current pkgrel was last bumped for are recorded alongside, in
|
||||||
|
rebuilt_against, and written by this command:
|
||||||
|
|
||||||
|
{ "rebuild_on": ["qt6-base"], "rebuilt_against": { "qt6-base": "6.11.2-2" } }
|
||||||
|
|
||||||
|
When a named package in the official repositories no longer matches what is
|
||||||
|
recorded, pkgrel is bumped and the record is rewritten. A package with no record
|
||||||
|
yet is only recorded, never bumped: the versions its published build was linked
|
||||||
|
against are not knowable from here, so the first run establishes the baseline
|
||||||
|
and the next real change acts on it.
|
||||||
|
|
||||||
|
The bump has to happen in git rather than in the builder, because a rebuild that
|
||||||
|
reuses the published version string is a package pacman will never offer anyone.
|
||||||
|
|
||||||
|
Arguments:
|
||||||
|
PACKAGE One or more package names to update (optional)
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
$0 # Update every package that declares rebuild_on
|
||||||
|
$0 quickshell-git # Update specific packages
|
||||||
|
|
||||||
|
Trigger versions are read from the local pacman database, so sync it first
|
||||||
|
(pacman -Sy) or this reports whatever that database last saw.
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
while [[ $# -gt 0 ]]; do
|
||||||
|
case "$1" in
|
||||||
|
-h|--help)
|
||||||
|
usage
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
--*)
|
||||||
|
print_error "Unknown option: $1"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
SPECIFIC_PACKAGES+=("$1")
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
for tool in pacman vercmp jq; do
|
||||||
|
if ! command -v "$tool" >/dev/null 2>&1; then
|
||||||
|
print_error "$tool not found: reading trigger versions and ordering pkgrels both need pacman"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
print_header "Rebuild Trigger Sync"
|
||||||
|
|
||||||
|
UPDATED=0
|
||||||
|
SKIPPED=0
|
||||||
|
FAILED=0
|
||||||
|
SPECIFIC_MODE=false
|
||||||
|
|
||||||
|
# The version of a trigger package as the build container would resolve it.
|
||||||
|
# A name pacman does not know reports nothing rather than failing, so the caller
|
||||||
|
# gets to say which package was left alone instead of the run dying here.
|
||||||
|
repo_version() {
|
||||||
|
local package="$1"
|
||||||
|
local info
|
||||||
|
|
||||||
|
info=$(LC_ALL=C pacman -Si "$package" 2>/dev/null) || return 0
|
||||||
|
|
||||||
|
awk -v allowed="$OFFICIAL_REPOS" '
|
||||||
|
/^Repository[[:space:]]*:/ { repo = $3 }
|
||||||
|
/^Version[[:space:]]*:/ {
|
||||||
|
if (index(allowed, " " repo " ") > 0) { print $3; exit }
|
||||||
|
}
|
||||||
|
' <<<"$info"
|
||||||
|
}
|
||||||
|
|
||||||
|
pkgbuild_field() {
|
||||||
|
local package_dir="$1"
|
||||||
|
local field="$2"
|
||||||
|
|
||||||
|
(cd "$package_dir" && env -u OMARCHY_SRC bash -c "source PKGBUILD 2>/dev/null; echo \"\${$field:-}\"")
|
||||||
|
}
|
||||||
|
|
||||||
|
# 2 -> 3, and 1.1 -> 1.2. Anything else is a pkgrel this command has no business
|
||||||
|
# rewriting.
|
||||||
|
bump_pkgrel() {
|
||||||
|
local pkgrel="$1"
|
||||||
|
|
||||||
|
[[ "$pkgrel" =~ ^[0-9]+(\.[0-9]+)?$ ]] || return 1
|
||||||
|
|
||||||
|
local head tail
|
||||||
|
if [[ "$pkgrel" == *.* ]]; then
|
||||||
|
head="${pkgrel%.*}."
|
||||||
|
tail="${pkgrel##*.}"
|
||||||
|
else
|
||||||
|
head=""
|
||||||
|
tail="$pkgrel"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "${head}$((tail + 1))"
|
||||||
|
}
|
||||||
|
|
||||||
|
write_pkgrel() {
|
||||||
|
local package_dir="$1"
|
||||||
|
local pkgrel="$2"
|
||||||
|
local pkgbuild="$package_dir/PKGBUILD"
|
||||||
|
|
||||||
|
if [[ $(grep -c '^pkgrel=' "$pkgbuild") -ne 1 ]]; then
|
||||||
|
print_error "Expected exactly one pkgrel assignment in $pkgbuild"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Every edit lands on a scratch copy that replaces the PKGBUILD in one rename,
|
||||||
|
# so a failed rewrite leaves the original alone rather than half updated.
|
||||||
|
local scratch="$pkgbuild.sync-rebuilds"
|
||||||
|
cp "$pkgbuild" "$scratch" || return 1
|
||||||
|
sed -i "s/^pkgrel=.*/pkgrel=$pkgrel/" "$scratch"
|
||||||
|
|
||||||
|
if ! bash -n "$scratch" 2>/dev/null; then
|
||||||
|
print_error "Rewritten PKGBUILD is not valid shell"
|
||||||
|
rm -f "$scratch"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
local written
|
||||||
|
written=$(CARCH=x86_64 bash -c 'source "$1" >/dev/null 2>&1 || exit 1; echo "$pkgrel"' _ "$scratch" 2>/dev/null)
|
||||||
|
if [[ "$written" != "$pkgrel" ]]; then
|
||||||
|
print_error "Rewritten PKGBUILD reads back pkgrel=$written, not $pkgrel"
|
||||||
|
rm -f "$scratch"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
chmod --reference="$pkgbuild" "$scratch"
|
||||||
|
mv "$scratch" "$pkgbuild"
|
||||||
|
}
|
||||||
|
|
||||||
|
write_metadata() {
|
||||||
|
local package_dir="$1"
|
||||||
|
local filter="$2"
|
||||||
|
shift 2
|
||||||
|
local metadata
|
||||||
|
metadata=$(metadata_file_for_dir "$package_dir")
|
||||||
|
|
||||||
|
local scratch="$metadata.sync-rebuilds"
|
||||||
|
jq "$@" "$filter" "$metadata" > "$scratch" || { rm -f "$scratch"; return 1; }
|
||||||
|
chmod --reference="$metadata" "$scratch"
|
||||||
|
mv "$scratch" "$metadata"
|
||||||
|
}
|
||||||
|
|
||||||
|
record_triggers() {
|
||||||
|
local package_dir="$1"
|
||||||
|
local current="$2"
|
||||||
|
|
||||||
|
write_metadata "$package_dir" '.rebuilt_against = $current' --argjson current "$current"
|
||||||
|
}
|
||||||
|
|
||||||
|
sync_package() {
|
||||||
|
local package="$1"
|
||||||
|
local package_dir="$PKGBUILDS_DIR/$package"
|
||||||
|
|
||||||
|
if [[ ! -f "$package_dir/PKGBUILD" ]]; then
|
||||||
|
print_error "Package $package has no PKGBUILD"
|
||||||
|
((++FAILED))
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
local triggers=()
|
||||||
|
mapfile -t triggers < <(package_rebuild_triggers "$package_dir")
|
||||||
|
|
||||||
|
if [[ ${#triggers[@]} -eq 0 ]]; then
|
||||||
|
if [[ "$SPECIFIC_MODE" == true ]]; then
|
||||||
|
print_error "Package $package does not declare rebuild_on"
|
||||||
|
((++FAILED))
|
||||||
|
else
|
||||||
|
print_info "Skipping $package: no rebuild triggers"
|
||||||
|
((++SKIPPED))
|
||||||
|
fi
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
print_info "Checking $package against ${triggers[*]}..."
|
||||||
|
|
||||||
|
local current="{}" trigger version
|
||||||
|
for trigger in "${triggers[@]}"; do
|
||||||
|
version=$(repo_version "$trigger")
|
||||||
|
if [[ -z "$version" ]]; then
|
||||||
|
print_error " $trigger is in no official repository; leaving $package alone"
|
||||||
|
((++FAILED))
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
if ! current=$(jq -c --arg name "$trigger" --arg version "$version" '.[$name] = $version' <<<"$current"); then
|
||||||
|
print_error " Could not record $trigger $version for $package"
|
||||||
|
((++FAILED))
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
local recorded
|
||||||
|
if ! recorded=$(package_metadata_value "$package_dir" '.rebuilt_against' ""); then
|
||||||
|
print_error " Could not read .omarchy/package.json for $package"
|
||||||
|
((++FAILED))
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -z "$recorded" || "$recorded" == "null" ]]; then
|
||||||
|
if ! record_triggers "$package_dir" "$current"; then
|
||||||
|
print_error " Failed to record trigger versions for $package"
|
||||||
|
((++FAILED))
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
print_success " Recorded baseline: $(jq -r 'to_entries | map("\(.key) \(.value)") | join(", ")' <<<"$current")"
|
||||||
|
((++UPDATED))
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
local moved
|
||||||
|
if ! moved=$(jq -r --argjson current "$current" '
|
||||||
|
to_entries
|
||||||
|
| map(select($current[.key] != .value) | "\(.key) \(.value) -> \($current[.key])")
|
||||||
|
| join(", ")
|
||||||
|
' <<<"$recorded"); then
|
||||||
|
print_error " Could not compare recorded trigger versions for $package"
|
||||||
|
((++FAILED))
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -z "$moved" ]]; then
|
||||||
|
print_info " Already rebuilt against $(jq -r 'to_entries | map("\(.key) \(.value)") | join(", ")' <<<"$current")"
|
||||||
|
((++SKIPPED))
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
local pkgrel next_pkgrel
|
||||||
|
pkgrel=$(pkgbuild_field "$package_dir" pkgrel)
|
||||||
|
|
||||||
|
# An AUR-synced package gets its PKGBUILD replaced wholesale on the next sync,
|
||||||
|
# so the bump only survives as the dotted Omarchy suffix that sync-aur
|
||||||
|
# reapplies from .omarchy/package.json.
|
||||||
|
local suffix=""
|
||||||
|
if package_sync_enabled "$package_dir"; then
|
||||||
|
if [[ "$pkgrel" == *.* ]]; then
|
||||||
|
next_pkgrel=$(bump_pkgrel "$pkgrel") || next_pkgrel=""
|
||||||
|
suffix="${next_pkgrel##*.}"
|
||||||
|
else
|
||||||
|
next_pkgrel="$pkgrel.1"
|
||||||
|
suffix="1"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
next_pkgrel=$(bump_pkgrel "$pkgrel") || next_pkgrel=""
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -z "$next_pkgrel" ]]; then
|
||||||
|
print_error " Cannot bump pkgrel=$pkgrel for $package; bump it by hand"
|
||||||
|
((++FAILED))
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
local pkgver epoch old_version new_version
|
||||||
|
pkgver=$(pkgbuild_field "$package_dir" pkgver)
|
||||||
|
epoch=$(pkgbuild_field "$package_dir" epoch)
|
||||||
|
old_version="${epoch:+$epoch:}$pkgver-$pkgrel"
|
||||||
|
new_version="${epoch:+$epoch:}$pkgver-$next_pkgrel"
|
||||||
|
|
||||||
|
if [[ "$(vercmp "$new_version" "$old_version")" -le 0 ]]; then
|
||||||
|
print_error " pkgrel $pkgrel -> $next_pkgrel would not be an upgrade for $package"
|
||||||
|
((++FAILED))
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! write_pkgrel "$package_dir" "$next_pkgrel"; then
|
||||||
|
print_error " Failed to bump pkgrel for $package"
|
||||||
|
((++FAILED))
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -n "$suffix" ]] && ! write_metadata "$package_dir" '.pkgrel.suffix = ($suffix | tonumber)' --arg suffix "$suffix"; then
|
||||||
|
print_error " Failed to record pkgrel suffix for $package"
|
||||||
|
((++FAILED))
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! record_triggers "$package_dir" "$current"; then
|
||||||
|
print_error " Failed to record trigger versions for $package"
|
||||||
|
((++FAILED))
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
print_success " $moved; pkgrel $pkgrel -> $next_pkgrel"
|
||||||
|
((++UPDATED))
|
||||||
|
}
|
||||||
|
|
||||||
|
if [[ ${#SPECIFIC_PACKAGES[@]} -gt 0 ]]; then
|
||||||
|
SPECIFIC_MODE=true
|
||||||
|
for package in "${SPECIFIC_PACKAGES[@]}"; do
|
||||||
|
sync_package "$package"
|
||||||
|
done
|
||||||
|
else
|
||||||
|
while IFS= read -r package; do
|
||||||
|
sync_package "$package"
|
||||||
|
done < <(packages_for_rebuild_sync)
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
if [[ $FAILED -gt 0 ]]; then
|
||||||
|
print_error "Rebuild trigger sync completed with failures"
|
||||||
|
else
|
||||||
|
print_success "Rebuild trigger sync complete!"
|
||||||
|
fi
|
||||||
|
echo " Target: $PKGBUILDS_DIR"
|
||||||
|
echo " Updated: $UPDATED"
|
||||||
|
echo " Skipped: $SKIPPED"
|
||||||
|
echo " Failed: $FAILED"
|
||||||
|
|
||||||
|
if [[ $FAILED -gt 0 ]]; then
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
@@ -10,9 +10,11 @@
|
|||||||
# { "source": "aur", "release_ring": "fast" }
|
# { "source": "aur", "release_ring": "fast" }
|
||||||
# { "source": "aur", "skip_build": true }
|
# { "source": "aur", "skip_build": true }
|
||||||
# { "source": "aur", "pkgrel": { "suffix": 1, "offset": 1 } }
|
# { "source": "aur", "pkgrel": { "suffix": 1, "offset": 1 } }
|
||||||
|
# { "source": "aur", "rebuild_on": ["qt6-base"] }
|
||||||
# { "source": "local" }
|
# { "source": "local" }
|
||||||
#
|
#
|
||||||
# bin/sync-aur also writes upstream_commit for AUR-backed packages.
|
# bin/sync-aur also writes upstream_commit for AUR-backed packages, and
|
||||||
|
# bin/sync-rebuilds writes rebuilt_against for packages declaring rebuild_on.
|
||||||
|
|
||||||
if [[ -z "${PKGBUILDS_DIR:-}" ]]; then
|
if [[ -z "${PKGBUILDS_DIR:-}" ]]; then
|
||||||
if [[ -n "${BUILD_ROOT:-}" ]]; then
|
if [[ -n "${BUILD_ROOT:-}" ]]; then
|
||||||
@@ -148,6 +150,33 @@ packages_for_upstream_sync() {
|
|||||||
done
|
done
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Packages that must be rebuilt when a dependency they link against changes,
|
||||||
|
# even though nothing in their own source moved. `rebuild_on` names those
|
||||||
|
# dependencies; `rebuilt_against` records the versions the checked-in pkgrel was
|
||||||
|
# last bumped for.
|
||||||
|
package_rebuild_triggers() {
|
||||||
|
local pkgdir="$1"
|
||||||
|
local metadata
|
||||||
|
|
||||||
|
metadata=$(metadata_file_for_dir "$pkgdir")
|
||||||
|
[[ -f "$metadata" ]] || return 0
|
||||||
|
|
||||||
|
jq -r '(.rebuild_on // [])[]' "$metadata"
|
||||||
|
}
|
||||||
|
|
||||||
|
package_has_rebuild_triggers() {
|
||||||
|
local pkgdir="$1"
|
||||||
|
[[ -n "$(package_rebuild_triggers "$pkgdir")" ]]
|
||||||
|
}
|
||||||
|
|
||||||
|
packages_for_rebuild_sync() {
|
||||||
|
package_dirs | while IFS= read -r pkgdir; do
|
||||||
|
if package_has_rebuild_triggers "$pkgdir"; then
|
||||||
|
basename "$pkgdir"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
packages_for_mirror() {
|
packages_for_mirror() {
|
||||||
local mirror="$1"
|
local mirror="$1"
|
||||||
|
|
||||||
@@ -299,4 +328,19 @@ validate_package_metadata() {
|
|||||||
echo "invalid upstream_commit for $(basename "$pkgdir"): must be a string"
|
echo "invalid upstream_commit for $(basename "$pkgdir"): must be a string"
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if ! jq -e '(.rebuild_on // []) | type == "array" and all(type == "string" and length > 0)' "$metadata" >/dev/null; then
|
||||||
|
echo "invalid rebuild_on for $(basename "$pkgdir"): must be an array of package names"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! jq -e '(.rebuilt_against // {}) | type == "object" and (to_entries | all(.value | type == "string" and length > 0))' "$metadata" >/dev/null; then
|
||||||
|
echo "invalid rebuilt_against for $(basename "$pkgdir"): must be an object mapping package names to versions"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! jq -e '((.rebuilt_against // {}) | keys) - (.rebuild_on // []) | length == 0' "$metadata" >/dev/null; then
|
||||||
|
echo "invalid rebuilt_against for $(basename "$pkgdir"): records a package that rebuild_on does not name"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user