Merge pull request #277 from maralcbr/omacom/aarch64-host-pipeline

Make aarch64 a first-class architecture in the scheduled pipeline
This commit is contained in:
Ryan Hughes authored and GitHub committed 2026-09-05 02:24:36 -04:00
commit bceab72f8c
49 files changed
+2153 -520

No files matched your search

+1
View File
@@ -27,6 +27,7 @@ jobs:
set -euo pipefail set -euo pipefail
pacman -Syu --noconfirm jq pacman -Syu --noconfirm jq
./bin/sync-upstream self-test ./bin/sync-upstream self-test
./bin/sync-rebuilds --self-test
./bin/omarchy-pkgs self-test ./bin/omarchy-pkgs self-test
./bin/omarchy-release self-test ./bin/omarchy-release self-test
' '
+1
View File
@@ -1,3 +1,4 @@
.firecrawl/
src/ src/
logs/ logs/
build-output/ build-output/
+131 -28
View File
@@ -30,18 +30,57 @@ The filesystem no longer encodes release policy. Instead:
## Prerequisites ## Prerequisites
### aarch64 Builds (Optional) ### aarch64 Builds (Optional)
To build ARM64 packages on x86_64, enable QEMU emulation: The repository host builds every architecture it publishes on the same
machine. A foreign architecture runs under QEMU user emulation, which
`bin/build` checks by actually running a container for the target platform.
Rootful Docker registers QEMU on first use. Rootless Podman uses the host's
registration and prints the one-time Arch setup commands when it is missing or
lacks the credential flag required by `sudo` inside the builder:
```bash ```bash
# Run after each reboot
docker run --privileged --rm tonistiigi/binfmt --install arm64
# Verify # Verify
docker run --rm --platform linux/arm64 alpine:latest uname -m podman run --rm --platform linux/arm64 docker.io/library/alpine:latest uname -m
# Should output: aarch64 # Should output: aarch64
``` ```
**Note**: aarch64 builds use QEMU and slower than native x86_64 builds. **Note**: emulated builds are much slower than native ones.
### Published architectures
`helpers/paths.sh` names the architectures this repository publishes:
```bash
PUBLISHED_ARCHES="${OMARCHY_ARCHES:-x86_64}"
```
That list drives the whole scheduled pipeline. `check-versions` compares
PKGBUILDs against each architecture's channel databases and writes one queue
file per channel and architecture (`.sync-needed-<channel>-<arch>`);
`auto-release <channel>` works through the queues one architecture at a
time, each with its own backoff (`.build-failed-<channel>-<arch>`), so a
failing build on one architecture never holds up the other; and the release
train advances channels with `--arch all`: it takes one host-wide lock and
verifies every architecture's source database before moving any of them. The
first entry is the reference architecture the release train observes channels
through. A remote sync failure can still leave a promotion temporarily partial;
rerunning the same advance completes it safely.
Adding an architecture to the scheduled pipeline is therefore one checked-in
change to that list: the next `check-versions` tick queues everything the new
architecture lacks, and the next `auto-release` tick starts building it. A
checked-in list also means the rebuild workflow and release host cannot drift
onto different architecture sets. For a one-off run, override it directly:
```bash
OMARCHY_ARCHES=x86_64 bin/check-versions
OMARCHY_ARCHES=aarch64 bin/check-versions
OMARCHY_ARCHES="x86_64 aarch64" bin/check-versions
```
The builder image bootstraps
`omarchy-keyring` from the x86_64 tree for every architecture, so the first
build of a new architecture does not depend on a repository that only it can
create.
## Quick Start ## Quick Start
@@ -300,7 +339,8 @@ bin/sync-upstream openai-codex-desktop # Update specific packages
Some vendors publish a release feed of their own that is faster and more precise Some vendors publish a release feed of their own that is faster and more precise
than the AUR packaging of it. Those packages are `source: local` — Omarchy owns than the AUR packaging of it. Those packages are `source: local` — Omarchy owns
the PKGBUILD — and declare where releases come from in one of two ways. the PKGBUILD — and declare where releases come from either as data or, for an
unusual feed, a small hook.
A vendor shipping tagged GitHub releases is pure data, declared as `upstream` A vendor shipping tagged GitHub releases is pure data, declared as `upstream`
in `.omarchy/package.json` with no code at all: in `.omarchy/package.json` with no code at all:
@@ -322,6 +362,32 @@ digest GitHub's release API reports for every asset — see
`pkgbuilds/schist-bin/.omarchy/package.json`. Either way the artifacts `pkgbuilds/schist-bin/.omarchy/package.json`. Either way the artifacts
themselves are never downloaded. themselves are never downloaded.
An architecture may map to an ordered array when its PKGBUILD downloads more
than one release asset. Small versioned files outside the release assets can be
listed under `sources` and are downloaded and hashed when a new version appears:
```json
"upstream": {
"github": "owner/project",
"digests": true,
"assets": {
"x86_64": ["tool-{pkgver}-x86_64", "tool-{pkgver}-x86_64.asc"],
"aarch64": ["tool-{pkgver}-aarch64", "tool-{pkgver}-aarch64.asc"]
},
"sources": {
"any": ["https://raw.githubusercontent.com/owner/project/{tag}/LICENSE"]
}
}
```
Asset and source keys must be disjoint because each key maps to one PKGBUILD
checksum array (`any` means the unsuffixed `sha256sums`).
Repositories whose historical releases use incompatible tag schemes may set
`"latest_only": true`. The provider then considers only the newest stable
GitHub release, while retaining all validation for that release. A quarantine
will wait for that release to age instead of falling back to an older one.
`{tag}` and `{pkgver}` interpolate into asset names; a leading `v` on the tag is `{tag}` and `{pkgver}` interpolate into asset names; a leading `v` on the tag is
stripped for `pkgver`; drafts and prereleases are ignored. Only the 100 most stripped for `pkgver`; drafts and prereleases are ignored. Only the 100 most
recent releases are considered. The provider fails closed on anything it cannot recent releases are considered. The provider fails closed on anything it cannot
@@ -364,14 +430,33 @@ the tarball named by the selected dist-tag:
`dist_tag` defaults to `latest`. The registry's publication timestamp is `dist_tag` defaults to `latest`. The registry's publication timestamp is
carried into the provider result, so `min_release_age` works for npm packages. carried into the provider result, so `min_release_age` works for npm packages.
Exactly one of `github`, `git_tags`, or `npm` may appear in a declaration.
A vendor with a plain-text Debian `Packages` index can use it to discover the
newest exact package version, then hash immutable source URLs:
```json
"upstream": {
"debian": "https://example.com/debian/dists/stable/main/binary-amd64/Packages",
"package": "example-app",
"sources": {
"x86_64": ["https://example.com/tool-{pkgver}-x64.tar.gz"],
"aarch64": ["https://example.com/tool-{pkgver}-arm64.tar.gz"]
}
}
```
This deliberately accepts only Debian versions that are already valid Arch
`pkgver` values. Feeds needing epoch, revision, or filename translation retain
a hook. Exactly one of `github`, `git_tags`, `npm`, or `debian` may appear in a
declaration.
A timestamped provider may also declare `"min_release_age": "24h"` A timestamped provider may also declare `"min_release_age": "24h"`
(`s`/`m`/`h`/`d` suffix or bare seconds) to quarantine fresh releases until (`s`/`m`/`h`/`d` suffix or bare seconds) to quarantine fresh releases until
maintainers have had time to pull a bad or compromised one. GitHub Releases and maintainers have had time to pull a bad or compromised one. GitHub Releases and
npm provide publication times; raw git tags do not, so combining `git_tags` npm provide publication times; raw git tags and Debian Packages indexes do not,
with this policy fails closed. The newest release that has cleared the window so combining either with this policy fails closed. The newest release that has
ships, so a fast release cadence cannot starve updates. The window is enforced cleared the window ships, so a fast release cadence cannot starve updates. The
window is enforced
centrally: whatever reports the release must prove its age via `published_at`, centrally: whatever reports the release must prove its age via `published_at`,
or the sync fails. A maintainer deliberately shipping inside the window runs or the sync fails. A maintainer deliberately shipping inside the window runs
`BYPASS_MIN_RELEASE_AGE=1 bin/sync-upstream <package>` locally and merges the `BYPASS_MIN_RELEASE_AGE=1 bin/sync-upstream <package>` locally and merges the
@@ -424,7 +509,12 @@ A package names those dependencies in `.omarchy/package.json`:
{ "source": "aur", "sync": false, "rebuild_on": ["qt6-base", "qt6-declarative", "qt6-wayland"] } { "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. pkgrel is bumped unless every name in `rebuild_on` is recorded and still matches, so a name the record does not carry reads as changed rather than going unexamined forever. Opting a package in therefore buys one rebuild: what its published build actually linked against is not knowable from here, and a record written without a rebuild would certify a build nobody checked. `bin/sync-rebuilds` reads each named package's version from the official
repositories for every published architecture the package supports and compares
it to `rebuilt_against`. Records are kept per architecture because Arch and
Arch Linux ARM can carry different dependency versions. pkgrel is bumped once
when any recorded version moves; that one source revision is then rebuilt by
each architecture's normal queue.
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. 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.
@@ -432,9 +522,12 @@ For an AUR-synced package the bump is expressed as the dotted Omarchy pkgrel suf
The bumped version is checked against the published one as well as the checked-in one, and refused when pacman would not order it higher. The checked-in version is not the floor; what a user already has is, and a checkout that has fallen behind the repository can otherwise be bumped to something that loses to the package it means to replace. That check is skipped with a warning when the published database cannot be read. The bumped version is checked against the published one as well as the checked-in one, and refused when pacman would not order it higher. The checked-in version is not the floor; what a user already has is, and a checkout that has fallen behind the repository can otherwise be bumped to something that loses to the package it means to replace. That check is skipped with a warning when the published database cannot be read.
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. The workflow points that database at `mirror.omarchy.org`, the mirror the x86_64 builder itself uses, because a mirror running ahead of the builder would record a version the build never linked against and nothing re-fires once the record matches. x86_64 versions are read from the local pacman database, so the workflow runs
in an Arch container pointed at `mirror.omarchy.org`, the same mirror as the
aarch64 is not covered. Those builds resolve Qt from Arch Linux ARM, which can lag Arch, so one record cannot describe both architectures. Only x86_64 is published today, so nothing currently ships from the untracked side; if ARM publishing starts, `rebuilt_against` has to become per-architecture before this can be trusted there. x86_64 builder. aarch64 versions are read directly from the live Arch Linux ARM
repository database, which is also what the ARM builder uses. Testing and
staging repositories do not count. A legacy flat `rebuilt_against` record is
read as x86_64 and is migrated naturally the next time a rebuild is needed.
### Other ### Other
@@ -632,7 +725,7 @@ Minimal examples:
Fields: Fields:
- `source`: `aur` or `local`. A `local` package can still follow an upstream release, either declaratively via `upstream` or with an `.omarchy/upstream.sh` hook. - `source`: `aur` or `local`. A `local` package can still follow an upstream release, either declaratively via `upstream` or with an `.omarchy/upstream.sh` hook.
- `upstream`: optional for `local` packages whose vendor ships tagged GitHub releases. `{ "github": "owner/repo", "checksums": "SHASUMS256.txt", "assets": { "<arch>": "name-{tag}.tar.xz" } }`, or `"digests": true` in place of `checksums` to use the release API's per-asset digests — see [Sync Upstream Releases](#sync-upstream-releases). Mutually exclusive with `.omarchy/upstream.sh`. - `upstream`: optional for `local` packages following GitHub releases, git tags, npm dist-tags, or a Debian `Packages` index. GitHub architecture assets may be a string or an ordered array, and can be combined with disjoint versioned `sources` — see [Sync Upstream Releases](#sync-upstream-releases). Mutually exclusive with `.omarchy/upstream.sh`.
- `min_release_age`: optional quarantine for upstream releases (`"24h"`, `"2d"`, or bare seconds). The newest release older than the window ships; anything younger waits, and a release whose age cannot be proven fails the sync. Bypass deliberately with `BYPASS_MIN_RELEASE_AGE=1 bin/sync-upstream <package>`. - `min_release_age`: optional quarantine for upstream releases (`"24h"`, `"2d"`, or bare seconds). The newest release older than the window ships; anything younger waits, and a release whose age cannot be proven fails the sync. Bypass deliberately with `BYPASS_MIN_RELEASE_AGE=1 bin/sync-upstream <package>`.
- `sync`: optional for AUR packages; defaults to `true`. Set `false` for AUR-origin packages that Omarchy maintains manually. - `sync`: optional for AUR packages; defaults to `true`. Set `false` for AUR-origin packages that Omarchy maintains manually.
- `aur`: optional AUR package name when it differs from the local package directory, usually for split packages. - `aur`: optional AUR package name when it differs from the local package directory, usually for split packages.
@@ -642,7 +735,7 @@ Fields:
- `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`. - `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. - `rebuilt_against`: written by `bin/sync-rebuilds`. Maps each published architecture to the versions of its `rebuild_on` packages 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
@@ -737,10 +830,15 @@ bin/repo release --package my-package
- Mirrors: mirror.omarchy.org, rackspace, pkgbuild.com - Mirrors: mirror.omarchy.org, rackspace, pkgbuild.com
### aarch64 ### aarch64
- QEMU emulation required on x86_64 hosts (slower) - Built on the repository host like x86_64; under QEMU when the host is x86_64
- Uses Arch Linux ARM repositories - On an ARM host, package builds and the signing/database utility containers
run natively; only an explicitly requested x86_64 package build is emulated
- Uses Arch Linux ARM repositories through the same HTTPS mirror for every
channel (Arch Linux ARM publishes no dated snapshots to pin a channel's base)
- Additional repos: `[alarm]`, `[aur]` - Additional repos: `[alarm]`, `[aur]`
- Same workflow, just add `--arch aarch64` - Same workflow, just add `--arch aarch64`; the scheduled pipeline runs it
automatically once `aarch64` is in `PUBLISHED_ARCHES`
- Packages whose `arch=()` lacks `aarch64` are skipped, not failed
### Building for Both Architectures ### Building for Both Architectures
@@ -791,8 +889,8 @@ The repository includes GitHub workflows and systemd services for automated rele
All four units run **every 5 minutes**, staggered by a minute each, so a push All four units run **every 5 minutes**, staggered by a minute each, so a push
reaches the mirror in minutes rather than hours: reaches the mirror in minutes rather than hours:
1. **check-versions** (`*:0/5`): Pulls latest from git, compares PKGBUILD versions to published versions, creates state files if builds are needed 1. **check-versions** (`*:0/5`): Pulls latest from git, compares PKGBUILD versions to published versions for every published architecture, creates one state file per channel and architecture if builds are needed
2. **auto-release-edge** (`*:1/5`): If a state file exists, builds all edge packages that need updates 2. **auto-release-edge** (`*:1/5`): For each published architecture with a state file, builds all edge packages that need updates
3. **auto-release-rc** (`*:2/5`): Builds fast-ring packages for rc, from the main checkout like the other two — natively in the rc image, not copied from another channel. The pinned release pair is built separately by `omarchy-release rc` in the `rc` branch worktree 3. **auto-release-rc** (`*:2/5`): Builds fast-ring packages for rc, from the main checkout like the other two — natively in the rc image, not copied from another channel. The pinned release pair is built separately by `omarchy-release rc` in the `rc` branch worktree
4. **auto-release-stable** (`*:3/5`): If a state file exists, builds `release_ring=fast` packages for stable and replicates them to rc 4. **auto-release-stable** (`*:3/5`): If a state file exists, builds `release_ring=fast` packages for stable and replicates them to rc
@@ -806,10 +904,11 @@ That cadence is only safe because of three guards:
an operator expects. `check-versions` takes it too — its `git pull` would an operator expects. `check-versions` takes it too — its `git pull` would
otherwise swap PKGBUILDs out from under a running build. otherwise swap PKGBUILDs out from under a running build.
- **Backoff on failure.** A failed release records the attempt in - **Backoff on failure.** A failed release records the attempt in
`.build-failed-<channel>` and backs off exponentially — 10m, 20m, 40m, up to `.build-failed-<channel>-<arch>` and backs off exponentially — 10m, 20m, 40m, up to
a 6h ceiling — instead of rebuilding the same broken tree every 5 minutes. a 6h ceiling — instead of rebuilding the same broken tree every 5 minutes.
**Any new commit clears the backoff immediately**, since a push is the most **Any new commit clears the backoff immediately**, since a push is the most
likely fix. Clear it by hand with `rm /root/.state/.build-failed-<channel>`. likely fix. Clear it by hand with
`rm /root/.state/.build-failed-<channel>-<arch>`.
- **Quiet when idle.** With nothing queued a tick exits without output, so the - **Quiet when idle.** With nothing queued a tick exits without output, so the
journal shows the runs that mattered rather than 288 no-ops a day. journal shows the runs that mattered rather than 288 no-ops a day.
@@ -862,10 +961,14 @@ bin/repo timers --local # inspect this machine instead
``` ```
State files are stored in `/root/.state/`: State files are stored in `/root/.state/`:
- `.sync-needed-<channel>` — the packages queued for that channel, one per - `.sync-needed-<channel>-<arch>` — the packages queued for that channel and
line; the release run reads them to name what it is building architecture, one per line; the release run reads them to name what it is
- `.build-failed-<channel>` — consecutive failure count, timestamp, and the building
commit it failed on (drives the backoff; removing it forces a retry) - `.build-failed-<channel>-<arch>` — consecutive failure count, timestamp, and
the commit it failed on (drives the backoff; removing it forces a retry)
Legacy files without the architecture suffix are consumed once as x86_64
state, so upgrading the host does not lose an in-flight build.
### Schedule (America/New_York) ### Schedule (America/New_York)
+51 -3
View File
@@ -25,6 +25,22 @@ SKIP_PROD_CHECK=false
FAST_RING_ONLY=false FAST_RING_ONLY=false
BOOTSTRAP=false BOOTSTRAP=false
PACKAGES="" PACKAGES=""
ALL_ARCHES=false
# Kept for --arch all, which re-invokes this script per architecture with the
# other arguments unchanged (minus the --arch all pair itself).
ORIGINAL_ARGS=()
arch_option=false
for arg in "$@"; do
if [[ "$arch_option" == true ]]; then
[[ "$arg" != "all" ]] && ORIGINAL_ARGS+=("--arch" "$arg")
arch_option=false
elif [[ "$arg" == "--arch" ]]; then
arch_option=true
else
ORIGINAL_ARGS+=("$arg")
fi
done
usage() { usage() {
echo "Usage: $0 --from <channel> --to <channel> [OPTIONS]" echo "Usage: $0 --from <channel> --to <channel> [OPTIONS]"
@@ -36,7 +52,8 @@ usage() {
echo " or --bootstrap (one-time initial seed)" echo " or --bootstrap (one-time initial seed)"
echo "" echo ""
echo "Options:" echo "Options:"
echo " --arch <arch> Target architecture (x86_64 or aarch64, default: x86_64)" echo " --arch <arch>|all Target architecture (x86_64 or aarch64, default: x86_64);"
echo " all = every published architecture, one after another"
echo " --package <names...> Advance only the named package(s)" echo " --package <names...> Advance only the named package(s)"
echo " --fast-ring Restrict to fast-ring packages (stable -> rc parity copy)" echo " --fast-ring Restrict to fast-ring packages (stable -> rc parity copy)"
echo " --bootstrap One-time stable -> rc seed before forward-only enforcement" echo " --bootstrap One-time stable -> rc seed before forward-only enforcement"
@@ -65,8 +82,14 @@ while [[ $# -gt 0 ]]; do
shift 2 shift 2
;; ;;
--arch) --arch)
ARCH="$2" if [[ "$2" == "all" ]]; then
update_arch_paths ALL_ARCHES=true
ARCH=all
else
require_valid_arch "$2"
ARCH="$2"
update_arch_paths
fi
shift 2 shift 2
;; ;;
--package) --package)
@@ -134,6 +157,31 @@ case "$FROM->$TO" in
;; ;;
esac esac
if [[ "$ALL_ARCHES" == true ]]; then
# Hold one lock across the whole operation so a timer cannot mutate a
# channel between architectures. Check every source database before moving
# the first one; a failed sync can still require an idempotent retry, but a
# missing architecture never creates a knowingly partial advance.
if [[ "$DRY_RUN" != true ]]; then
acquire_release_lock || exit 1
fi
ARCHES=$(published_arches)
for arch in $ARCHES; do
source_db="$REPO_ROOT/$FROM/$arch/omarchy.db.tar.zst"
if [[ ! -f "$source_db" ]]; then
print_error "Source database not found: $source_db"
echo "Nothing has been published to the $FROM channel for $arch."
exit 1
fi
done
for arch in $ARCHES; do
"$0" --arch "$arch" "${ORIGINAL_ARGS[@]}" || exit $?
done
exit 0
fi
SOURCE_DIR="$REPO_ROOT/$FROM/$ARCH" SOURCE_DIR="$REPO_ROOT/$FROM/$ARCH"
TARGET_DIR="$REPO_ROOT/$TO/$ARCH" TARGET_DIR="$REPO_ROOT/$TO/$ARCH"
SOURCE_DB="$SOURCE_DIR/omarchy.db.tar.zst" SOURCE_DB="$SOURCE_DIR/omarchy.db.tar.zst"
+104 -57
View File
@@ -1,6 +1,10 @@
#!/bin/bash #!/bin/bash
# Run the release workflow for a channel when work is queued. # Run the release workflow for a channel when work is queued.
# Usage: auto-release <edge|rc|stable> # Usage: auto-release <edge|rc|stable> [<arch>|all]
#
# With no architecture (what the timers pass), every published architecture
# is processed in turn, each against its own queue and its own backoff, so a
# failing aarch64 build never holds up x86_64 or the other way round.
# #
# Safe to run on a tight schedule. Three guards make that true: # Safe to run on a tight schedule. Three guards make that true:
# #
@@ -21,7 +25,7 @@ source "$BUILD_ROOT/helpers/paths.sh"
source "$BUILD_ROOT/helpers/lock-helpers.sh" source "$BUILD_ROOT/helpers/lock-helpers.sh"
MIRROR="${1:-}" MIRROR="${1:-}"
STATE_DIR="${OMARCHY_STATE_DIR:-/root/.state}" ARCH_ARG="${2:-all}"
# Backoff schedule: 10m, 20m, 40m, 80m, 160m, 320m, then hourly-ish forever # Backoff schedule: 10m, 20m, 40m, 80m, 160m, 320m, then hourly-ish forever
# (capped at 6h, the cadence this system ran at before frequent timers). # (capped at 6h, the cadence this system ran at before frequent timers).
@@ -29,8 +33,9 @@ BACKOFF_BASE_SECONDS="${OMARCHY_BACKOFF_BASE:-600}"
BACKOFF_MAX_SECONDS="${OMARCHY_BACKOFF_MAX:-21600}" BACKOFF_MAX_SECONDS="${OMARCHY_BACKOFF_MAX:-21600}"
if [[ -z "$MIRROR" ]]; then if [[ -z "$MIRROR" ]]; then
print_error "Usage: $0 <mirror>" print_error "Usage: $0 <mirror> [<arch>|all]"
echo " mirror: edge, rc, or stable" echo " mirror: edge, rc, or stable"
echo " arch: one of $VALID_ARCHES, or all (default) for every published architecture"
exit 1 exit 1
fi fi
@@ -39,17 +44,13 @@ if [[ "$MIRROR" != "edge" && "$MIRROR" != "rc" && "$MIRROR" != "stable" ]]; then
exit 1 exit 1
fi fi
STATE_FILE="$STATE_DIR/.sync-needed-$MIRROR" if [[ "$ARCH_ARG" == "all" ]]; then
FAIL_FILE="$STATE_DIR/.build-failed-$MIRROR" ARCHES=$(published_arches)
else
# Nothing queued: stay quiet. At a 5-minute cadence this is most invocations, require_valid_arch "$ARCH_ARG"
# and a header for each would bury the runs that matter in the journal. ARCHES="$ARCH_ARG"
if [[ ! -f "$STATE_FILE" ]]; then
exit 0
fi fi
print_header "Processing Sync for $MIRROR"
# The build inputs are this checkout's contents; its HEAD identifies them. # The build inputs are this checkout's contents; its HEAD identifies them.
current_fingerprint() { current_fingerprint() {
git -C "$BUILD_ROOT" rev-parse HEAD 2>/dev/null || echo "unknown" git -C "$BUILD_ROOT" rev-parse HEAD 2>/dev/null || echo "unknown"
@@ -65,57 +66,103 @@ backoff_seconds() {
echo "$delay" echo "$delay"
} }
FAIL_COUNT=0 # Returns 0 when this architecture's queue was processed (or was empty), 1 when
if [[ -f "$FAIL_FILE" ]]; then # the release failed. Backoff and "someone else holds the lock" are not
# shellcheck disable=SC1090 # failures: they are this tick deciding to do nothing.
source "$FAIL_FILE" 2>/dev/null || true release_arch() {
FAIL_COUNT="${FAILURE_COUNT:-0}" local arch="$1"
failed_at="${FAILURE_AT:-0}" local state_file fail_file
failed_fingerprint="${FAILURE_FINGERPRINT:-}" state_file=$(sync_queue_file "$MIRROR" "$arch")
fail_file=$(sync_fail_file "$MIRROR" "$arch")
if [[ "$failed_fingerprint" != "$(current_fingerprint)" ]]; then # A queue written under the pre-architecture name belongs to x86_64.
print_info "Repository changed since the last failure — clearing backoff and retrying" if [[ "$arch" == "x86_64" && ! -f "$state_file" && -f "$(legacy_sync_queue_file "$MIRROR")" ]]; then
rm -f "$FAIL_FILE" state_file=$(legacy_sync_queue_file "$MIRROR")
FAIL_COUNT=0 fi
else if [[ "$arch" == "x86_64" && ! -f "$fail_file" && -f "$(legacy_sync_fail_file "$MIRROR")" ]]; then
delay=$(backoff_seconds "$FAIL_COUNT") fail_file=$(legacy_sync_fail_file "$MIRROR")
now=$(date +%s)
retry_at=$((failed_at + delay))
if ((now < retry_at)); then
print_warning "$MIRROR has failed $FAIL_COUNT time(s) on this tree — not retrying until $(date -d "@$retry_at" '+%H:%M:%S' 2>/dev/null || echo "+$((retry_at - now))s")"
echo " Push a fix (any new commit clears this), or: rm $FAIL_FILE"
exit 0
fi
print_info "Backoff elapsed — retrying $MIRROR (failure #$((FAIL_COUNT + 1)) if this fails)"
fi fi
fi
# Non-blocking: a build in progress means this tick has nothing to do. # Nothing queued: stay quiet. At a 5-minute cadence this is most
if ! try_release_lock; then # invocations, and a header for each would bury the runs that matter in
holder=$(release_lock_holder) # the journal.
print_info "A release is already running (${holder:-holder unknown}) — skipping this tick" [[ -f "$state_file" ]] || return 0
exit 0
fi
print_info "State file found: $STATE_FILE" print_header "Processing Sync for $MIRROR ($arch)"
print_info "Starting release workflow for $MIRROR..."
if "$BUILD_ROOT/bin/repo" release --mirror "$MIRROR" --skip-prod-check; then local fail_count=0 failed_at failed_fingerprint delay now retry_at
print_success "Release completed successfully for $MIRROR" if [[ -f "$fail_file" ]]; then
rm -f "$STATE_FILE" FAILURE_COUNT=0 FAILURE_AT=0 FAILURE_FINGERPRINT=""
rm -f "$FAIL_FILE" # shellcheck disable=SC1090
print_success "State file removed: $STATE_FILE" source "$fail_file" 2>/dev/null || true
else fail_count="${FAILURE_COUNT:-0}"
status=$? failed_at="${FAILURE_AT:-0}"
FAIL_COUNT=$((FAIL_COUNT + 1)) failed_fingerprint="${FAILURE_FINGERPRINT:-}"
cat >"$FAIL_FILE" <<EOF
FAILURE_COUNT=$FAIL_COUNT if [[ "$failed_fingerprint" != "$(current_fingerprint)" ]]; then
print_info "Repository changed since the last failure — clearing backoff and retrying"
rm -f "$fail_file"
fail_count=0
else
delay=$(backoff_seconds "$fail_count")
now=$(date +%s)
retry_at=$((failed_at + delay))
if ((now < retry_at)); then
print_warning "$MIRROR ($arch) has failed $fail_count time(s) on this tree — not retrying until $(date -d "@$retry_at" '+%H:%M:%S' 2>/dev/null || echo "+$((retry_at - now))s")"
echo " Push a fix (any new commit clears this), or: rm $fail_file"
return 0
fi
print_info "Backoff elapsed — retrying $MIRROR ($arch) (failure #$((fail_count + 1)) if this fails)"
fi
fi
# Non-blocking: a build in progress means this tick has nothing to do. The
# lock is reentrant, so once this run holds it the remaining architectures
# run under the same acquisition.
if ! try_release_lock; then
local holder
holder=$(release_lock_holder)
print_info "A release is already running (${holder:-holder unknown}) — skipping this tick"
return 0
fi
print_info "State file found: $state_file"
print_info "Starting release workflow for $MIRROR ($arch)..."
if "$BUILD_ROOT/bin/repo" release --mirror "$MIRROR" --arch "$arch" --skip-prod-check; then
print_success "Release completed successfully for $MIRROR ($arch)"
local completed_state=("$state_file" "$fail_file")
if [[ "$arch" == "x86_64" ]]; then
completed_state+=("$(legacy_sync_queue_file "$MIRROR")" "$(legacy_sync_fail_file "$MIRROR")")
fi
if ! rm -f "${completed_state[@]}"; then
print_error "Release succeeded, but its queue state could not be cleared"
return 1
fi
print_success "State file removed: $state_file"
return 0
fi
fail_count=$((fail_count + 1))
if ! cat >"$fail_file" <<EOF
FAILURE_COUNT=$fail_count
FAILURE_AT=$(date +%s) FAILURE_AT=$(date +%s)
FAILURE_FINGERPRINT=$(current_fingerprint) FAILURE_FINGERPRINT=$(current_fingerprint)
EOF EOF
next=$(backoff_seconds "$FAIL_COUNT") then
print_error "Release failed for $MIRROR (attempt $FAIL_COUNT)" print_error "Could not record failure state: $fail_file"
print_warning "State file retained for retry: $STATE_FILE" return 1
fi
local next
next=$(backoff_seconds "$fail_count")
print_error "Release failed for $MIRROR ($arch) (attempt $fail_count)"
print_warning "State file retained for retry: $state_file"
print_warning "Backing off $((next / 60))m before the next attempt; a new commit retries sooner" print_warning "Backing off $((next / 60))m before the next attempt; a new commit retries sooner"
exit "$status" return 1
fi }
status=0
for arch in $ARCHES; do
release_arch "$arch" || status=1
done
exit "$status"
+45 -15
View File
@@ -102,6 +102,8 @@ while [[ $# -gt 0 ]]; do
esac esac
done done
require_valid_arch "$ARCH"
if [[ $DEFER_RUNTIME_DEPS != "false" && $DEFER_RUNTIME_DEPS != "true" ]]; then if [[ $DEFER_RUNTIME_DEPS != "false" && $DEFER_RUNTIME_DEPS != "true" ]]; then
print_error "OMARCHY_DEFER_RUNTIME_DEPS must be true or false" print_error "OMARCHY_DEFER_RUNTIME_DEPS must be true or false"
exit 1 exit 1
@@ -143,7 +145,7 @@ if [[ $DEFER_RUNTIME_DEPS == "true" ]]; then
fi fi
if [[ "$DRY_RUN" == true ]]; then if [[ "$DRY_RUN" == true ]]; then
print_warning "DRY RUN MODE - build plan only; no Docker or makepkg will run" print_warning "DRY RUN MODE - build plan only; no container or makepkg will run"
ARCH="$ARCH" \ ARCH="$ARCH" \
MIRROR="$MIRROR" \ MIRROR="$MIRROR" \
PACKAGES="$PACKAGES" \ PACKAGES="$PACKAGES" \
@@ -161,15 +163,34 @@ fi
# Create directories if they don't exist # Create directories if they don't exist
mkdir -p "$BUILD_OUTPUT_DIR" "$REPO_DIR" "$SRC_DIR" mkdir -p "$BUILD_OUTPUT_DIR" "$REPO_DIR" "$SRC_DIR"
# Check Docker is available # Check the selected container engine is available
check_docker check_engine
# Setup QEMU for aarch64 builds on x86_64 hosts # A foreign target architecture runs under QEMU user emulation. Probe by
if [[ "$(uname -m)" == "x86_64" && "$ARCH" == "aarch64" ]]; then # actually running a container for the target platform: that is the only
# Check if QEMU is already working # test that covers both "binfmt not registered" and "registered but broken".
if ! docker run --rm --platform linux/arm64 alpine:3.21 /bin/true >/dev/null 2>&1; then HOST_ARCH=$(uname -m)
print_info "Setting up QEMU for ARM64 emulation..." [[ "$HOST_ARCH" == "arm64" ]] && HOST_ARCH=aarch64
setup_qemu if [[ "$HOST_ARCH" != "$ARCH" ]]; then
PROBE_IMAGE="alpine:3.21"
[[ "$CONTAINER_ENGINE" == "podman" ]] && PROBE_IMAGE="docker.io/library/alpine:3.21"
# Rootless Podman cannot repair host binfmt state itself. Validate the flags
# before the basic probe, because an F-only registration can start an ARM
# container but silently breaks sudo inside it.
if [[ "$CONTAINER_ENGINE" == "podman" ]]; then
setup_qemu "$ARCH"
fi
if ! "$CONTAINER_ENGINE" run "${CONTAINER_RUN_ARGS[@]}" --rm "$(get_platform_arg "$ARCH")" "$PROBE_IMAGE" /bin/true >/dev/null 2>&1; then
if [[ "$CONTAINER_ENGINE" == "podman" ]]; then
print_error "QEMU $ARCH is registered, but the container probe failed"
print_info "Refresh the registration with: sudo systemctl restart systemd-binfmt"
exit 1
else
print_info "Setting up QEMU for $ARCH emulation on this $HOST_ARCH host..."
setup_qemu "$ARCH"
fi
fi fi
fi fi
@@ -179,7 +200,7 @@ if [[ $KEEP_BUILD_WORKSPACE == "1" ]]; then
print_info "Keeping existing build workspace..." print_info "Keeping existing build workspace..."
else else
print_info "Cleaning build workspace..." print_info "Cleaning build workspace..."
rm -rf "$BUILD_OUTPUT_DIR"/* rm -rf "${BUILD_OUTPUT_DIR:?}"/*
fi fi
mkdir -p "$BUILD_OUTPUT_DIR" mkdir -p "$BUILD_OUTPUT_DIR"
@@ -196,7 +217,7 @@ fi
# flag is that every job runs the same bytes. # flag is that every job runs the same bytes.
IMAGE_TAG="omarchy-pkg-builder:latest-$ARCH-$MIRROR" IMAGE_TAG="omarchy-pkg-builder:latest-$ARCH-$MIRROR"
if [[ $SKIP_BUILDER_IMAGE == "1" ]]; then if [[ $SKIP_BUILDER_IMAGE == "1" ]]; then
if ! docker image inspect "$IMAGE_TAG" >/dev/null 2>&1; then if ! "$CONTAINER_ENGINE" image inspect "$IMAGE_TAG" >/dev/null 2>&1; then
print_error "Prepared builder image is unavailable: $IMAGE_TAG" print_error "Prepared builder image is unavailable: $IMAGE_TAG"
exit 1 exit 1
fi fi
@@ -211,9 +232,12 @@ print_info "Running package build..."
mkdir -p "$BUILD_OUTPUT_DIR" mkdir -p "$BUILD_OUTPUT_DIR"
mkdir -p "$REPO_DIR" mkdir -p "$REPO_DIR"
# Ensure output directories are writable by container user # Rootful Docker writes as the image uid, so retain its existing permission
make_dir_writable "$BUILD_OUTPUT_DIR" # workaround. Rootless Podman uses keep-id and must leave ownership/modes alone.
make_dir_writable "$REPO_DIR" if [[ "$CONTAINER_ENGINE" == "docker" ]]; then
make_dir_writable "$BUILD_OUTPUT_DIR"
make_dir_writable "$REPO_DIR"
fi
# Build Docker arguments # Build Docker arguments
DOCKER_ARGS=( DOCKER_ARGS=(
@@ -230,10 +254,16 @@ DOCKER_ARGS=(
-v "$BUILD_ROOT/pkgbuilds:/pkgbuilds:ro" -v "$BUILD_ROOT/pkgbuilds:/pkgbuilds:ro"
) )
# Podman-created images can leave WORKDIR owned by a remapped uid. Mount the
# existing host-user-owned workspace so the builder can write there.
if [[ "$CONTAINER_ENGINE" == "podman" ]]; then
DOCKER_ARGS+=(-v "$SRC_DIR:/src")
fi
# Run the builder with assembled args # Run the builder with assembled args
PLATFORM_ARG=$(get_platform_arg "$ARCH") PLATFORM_ARG=$(get_platform_arg "$ARCH")
docker run $PLATFORM_ARG "${DOCKER_ARGS[@]}" "$IMAGE_TAG" /build/build.sh "$CONTAINER_ENGINE" run "${CONTAINER_RUN_ARGS[@]}" "$PLATFORM_ARG" "${DOCKER_ARGS[@]}" "$IMAGE_TAG" /build/build.sh
BUILD_RESULT=$? BUILD_RESULT=$?
+38 -12
View File
@@ -1,6 +1,10 @@
#!/bin/bash #!/bin/bash
# Check PKGBUILD versions against published repo versions # Check PKGBUILD versions against published repo versions
# Creates state files for edge and/or stable if any packages need building # Creates a state file per channel and architecture when packages need building
#
# Usage: check-versions [--pull] [--arch <arch>]
# --pull pull the repository first (the scheduled run does this)
# --arch <arch> check one architecture; default: every published one
set -e set -e
@@ -10,11 +14,27 @@ source "$BUILD_ROOT/helpers/paths.sh"
source "$BUILD_ROOT/helpers/package-metadata.sh" source "$BUILD_ROOT/helpers/package-metadata.sh"
source "$BUILD_ROOT/helpers/lock-helpers.sh" source "$BUILD_ROOT/helpers/lock-helpers.sh"
STATE_DIR="${OMARCHY_STATE_DIR:-/root/.state}"
ARCH="${ARCH:-x86_64}"
PULL=false PULL=false
ARCHES=""
[[ "${1:-}" == "--pull" ]] && PULL=true while [[ $# -gt 0 ]]; do
case $1 in
--pull)
PULL=true
shift
;;
--arch)
require_valid_arch "$2"
ARCHES="$2"
shift 2
;;
*)
print_error "Unknown option: $1"
exit 1
;;
esac
done
[[ -n "$ARCHES" ]] || ARCHES=$(published_arches)
mkdir -p "$STATE_DIR" mkdir -p "$STATE_DIR"
@@ -147,11 +167,12 @@ check_package() {
check_mirror() { check_mirror() {
local mirror="$1" local mirror="$1"
local state_file="$STATE_DIR/.sync-needed-$mirror" local state_file
state_file=$(sync_queue_file "$mirror" "$ARCH")
local needs_build=false local needs_build=false
local packages=() local packages=()
print_info "Checking $mirror packages..." print_info "Checking $mirror packages for $ARCH..."
while IFS= read -r pkg; do while IFS= read -r pkg; do
local pkgdir="$PKGBUILDS_DIR/$pkg" local pkgdir="$PKGBUILDS_DIR/$pkg"
@@ -159,7 +180,7 @@ check_mirror() {
needs_build=true needs_build=true
packages+=("$pkg") packages+=("$pkg")
fi fi
done < <(packages_for_unscoped_build "$mirror") done < <(packages_for_unscoped_build "$mirror" "$ARCH")
echo "" echo ""
@@ -168,18 +189,23 @@ check_mirror() {
# this to name the packages in its start report, which is the difference # this to name the packages in its start report, which is the difference
# between "a build is running" and "your package is in this build". # between "a build is running" and "your package is in this build".
printf '%s\n' "${packages[@]}" >"$state_file" printf '%s\n' "${packages[@]}" >"$state_file"
print_success "${mirror^} needs building (${#packages[@]} packages)" print_success "${mirror^} ($ARCH) needs building (${#packages[@]} packages)"
print_info "Packages: ${packages[*]}" print_info "Packages: ${packages[*]}"
print_info "State file created: $state_file" print_info "State file created: $state_file"
else else
print_info "${mirror^} is up to date" print_info "${mirror^} ($ARCH) is up to date"
fi fi
echo "" echo ""
} }
check_mirror edge # One pass per published architecture: the version comparison reads that
check_mirror rc # architecture's channel databases, and each queue is its own file.
check_mirror stable for ARCH in $ARCHES; do
update_arch_paths
check_mirror edge
check_mirror rc
check_mirror stable
done
print_success "Version check complete!" print_success "Version check complete!"
+13 -7
View File
@@ -1,21 +1,27 @@
#!/bin/bash #!/bin/bash
# Clean Docker builder images and cache # Clean builder images and cache for the selected container engine
# Forces a fresh image build on next run # Forces a fresh image build on next run
set -e set -e
BUILD_ROOT=$(realpath "${BASH_SOURCE[0]%/*}/..") BUILD_ROOT=$(realpath "${BASH_SOURCE[0]%/*}/..")
source "$BUILD_ROOT/helpers/message-helpers.sh" source "$BUILD_ROOT/helpers/message-helpers.sh"
source "$BUILD_ROOT/helpers/docker-helpers.sh"
print_header "Cleaning Docker Builder Images" check_engine
print_header "Cleaning Container Builder Images"
# Remove all omarchy-pkg-builder images # Remove all omarchy-pkg-builder images
print_info "Removing omarchy-pkg-builder images..." print_info "Removing omarchy-pkg-builder images..."
docker images omarchy-pkg-builder -q | xargs -r docker rmi -f 2>/dev/null || true "$CONTAINER_ENGINE" images omarchy-pkg-builder -q | xargs -r "$CONTAINER_ENGINE" rmi -f 2>/dev/null || true
# Clear buildx cache for these builds # Clear this engine's build cache.
print_info "Clearing buildx cache..." print_info "Clearing build cache..."
docker buildx prune -a -f if [[ "$CONTAINER_ENGINE" == "docker" ]]; then
"$CONTAINER_ENGINE" buildx prune -a -f
else
"$CONTAINER_ENGINE" image prune --build-cache -f
fi
print_success "Docker builder cache cleared" print_success "Container builder cache cleared"
print_info "Next build will create fresh images" print_info "Next build will create fresh images"
+7 -3
View File
@@ -31,9 +31,13 @@ clean_packages() {
# Skip signature files # Skip signature files
[[ "$pkg" == *.sig ]] && continue [[ "$pkg" == *.sig ]] && continue
# Extract package name (remove version and architecture) # Format: name-version-release-arch.pkg.tar.*. Work from the right because
# Format: name-version-release-arch.pkg.tar.* # package names can themselves contain version-like pieces (qt6-5compat,
local pkgname=$(echo "$pkg" | sed -E 's/-[0-9]+.*-(any|x86_64|i686)\.pkg\.tar\..*//') # nvidia-580xx-utils), while pkgver and pkgrel cannot contain hyphens.
local stem="${pkg%%.pkg.tar.*}"
stem="${stem%-*}" # architecture
stem="${stem%-*}" # pkgrel
local pkgname="${stem%-*}" # pkgver
# Add to array # Add to array
if [[ -n "${packages[$pkgname]}" ]]; then if [[ -n "${packages[$pkgname]}" ]]; then
+12 -6
View File
@@ -23,7 +23,7 @@ source "$BUILD_ROOT/helpers/paths.sh"
source "$BUILD_ROOT/helpers/host-helpers.sh" source "$BUILD_ROOT/helpers/host-helpers.sh"
UPSTREAM_URL="${OMARCHY_UPSTREAM_URL:-https://github.com/basecamp/omarchy.git}" UPSTREAM_URL="${OMARCHY_UPSTREAM_URL:-https://github.com/basecamp/omarchy.git}"
EDGE_DB_URL="${OMARCHY_EDGE_DB_URL:-https://pkgs.omarchy.org/edge/x86_64/omarchy.db.tar.zst}" EDGE_DB_URL="${OMARCHY_EDGE_DB_URL:-https://pkgs.omarchy.org/edge/$(reference_arch)/omarchy.db.tar.zst}"
RELEASE_PACKAGES=(omarchy omarchy-settings) RELEASE_PACKAGES=(omarchy omarchy-settings)
DEFAULT_RC_REF="quattro" DEFAULT_RC_REF="quattro"
SRCDEST_DIR="$BUILD_ROOT/.srcdest" SRCDEST_DIR="$BUILD_ROOT/.srcdest"
@@ -327,13 +327,19 @@ regenerate_checksums() {
trigger_build_host() { trigger_build_host() {
local host local host
queue_edge_builds() {
mkdir -p "$STATE_DIR" || return 1
local arch
for arch in $(published_arches); do
touch "$(sync_queue_file edge "$arch")" || return 1
done
}
# Explicit host configuration outranks the local-host inference (a # Explicit host configuration outranks the local-host inference (a
# workstation that ran a full local release carries the db marker too). # workstation that ran a full local release carries the db marker too).
if ! resolve_repo_host "${REPO_HOST_OVERRIDE:-}" >/dev/null && on_repo_host; then if ! resolve_repo_host "${REPO_HOST_OVERRIDE:-}" >/dev/null && on_repo_host; then
print_info "Triggering edge build locally (this is the build host)..." print_info "Triggering edge build locally (this is the build host)..."
if mkdir -p "${OMARCHY_STATE_DIR:-/root/.state}" && if queue_edge_builds && systemctl start --no-block omarchy-auto-release-edge.service; then
touch "${OMARCHY_STATE_DIR:-/root/.state}/.sync-needed-edge" &&
systemctl start --no-block omarchy-auto-release-edge.service; then
print_success "Edge build triggered" print_success "Edge build triggered"
else else
print_warning "Could not start the edge release service — the 6-hourly timer will pick it up" print_warning "Could not start the edge release service — the 6-hourly timer will pick it up"
@@ -343,11 +349,11 @@ trigger_build_host() {
if ! host=$(resolve_repo_host "${REPO_HOST_OVERRIDE:-}"); then if ! host=$(resolve_repo_host "${REPO_HOST_OVERRIDE:-}"); then
print_info "No repository host configured (set OMARCHY_REPO_HOST or $BUILD_ROOT/.repo-host — any ssh destination, e.g. root@<host> or an ssh-config alias)." print_info "No repository host configured (set OMARCHY_REPO_HOST or $BUILD_ROOT/.repo-host — any ssh destination, e.g. root@<host> or an ssh-config alias)."
print_info "The 6-hourly auto-release timer will pick this up, or trigger manually:" print_info "The 6-hourly auto-release timer will pick this up, or trigger manually:"
echo " ssh <build-host> 'git -C /root/omarchy-pkgs pull --ff-only && touch /root/.state/.sync-needed-edge && systemctl start omarchy-auto-release-edge.service'" echo " ssh <build-host> 'git -C /root/omarchy-pkgs pull --ff-only && systemctl start omarchy-check-versions.service omarchy-auto-release-edge.service'"
return 0 return 0
fi fi
print_info "Triggering edge build on $host..." print_info "Triggering edge build on $host..."
if ssh "$host" 'git -C /root/omarchy-pkgs pull --ff-only && mkdir -p /root/.state && touch /root/.state/.sync-needed-edge && systemctl start --no-block omarchy-auto-release-edge.service'; then if ssh "$host" 'git -C /root/omarchy-pkgs pull --ff-only && cd /root/omarchy-pkgs && export BUILD_ROOT=/root/omarchy-pkgs && source helpers/paths.sh && mkdir -p "$STATE_DIR" && for arch in $(published_arches); do touch "$(sync_queue_file edge "$arch")"; done && systemctl start --no-block omarchy-auto-release-edge.service'; then
print_success "Edge build triggered on $host" print_success "Edge build triggered on $host"
else else
print_warning "Could not trigger $host — the 6-hourly timer will pick it up" print_warning "Could not trigger $host — the 6-hourly timer will pick it up"
+93 -55
View File
@@ -18,6 +18,7 @@ set -e
BUILD_ROOT=$(realpath "${BASH_SOURCE[0]%/*}/..") BUILD_ROOT=$(realpath "${BASH_SOURCE[0]%/*}/..")
source "$BUILD_ROOT/helpers/message-helpers.sh" source "$BUILD_ROOT/helpers/message-helpers.sh"
source "$BUILD_ROOT/helpers/docker-helpers.sh"
source "$BUILD_ROOT/helpers/paths.sh" source "$BUILD_ROOT/helpers/paths.sh"
source "$BUILD_ROOT/helpers/host-helpers.sh" source "$BUILD_ROOT/helpers/host-helpers.sh"
@@ -28,7 +29,11 @@ ISO_REPO="${OMARCHY_ISO_REPO:-omacom-io/omarchy-iso}"
DEV_BRANCH="${OMARCHY_DEV_BRANCH:-quattro}" DEV_BRANCH="${OMARCHY_DEV_BRANCH:-quattro}"
PKGS_DB_BASE="${OMARCHY_PKGS_DB_BASE:-https://pkgs.omarchy.org}" PKGS_DB_BASE="${OMARCHY_PKGS_DB_BASE:-https://pkgs.omarchy.org}"
RC_DB_URL="$PKGS_DB_BASE/rc/x86_64/omarchy.db.tar.zst" # The first published architecture supplies the version-ordering floor when
# cutting pins. Readiness checks below still verify every published
# architecture before an RC or final release can move forward.
OBSERVED_ARCH=$(reference_arch)
RC_DB_URL="$PKGS_DB_BASE/rc/$OBSERVED_ARCH/omarchy.db.tar.zst"
SRCDEST_DIR="$BUILD_ROOT/.srcdest" SRCDEST_DIR="$BUILD_ROOT/.srcdest"
MIRROR_CLONE="$SRCDEST_DIR/omarchy" # bare mirror (shared with bin/omarchy-pkgs) MIRROR_CLONE="$SRCDEST_DIR/omarchy" # bare mirror (shared with bin/omarchy-pkgs)
@@ -181,12 +186,12 @@ ensure_work_clone() {
# Prints omarchy's published version in a channel; empty when absent, rc 2 when # Prints omarchy's published version in a channel; empty when absent, rc 2 when
# the database cannot be read (callers must not mistake an outage for absence). # the database cannot be read (callers must not mistake an outage for absence).
published_version() { published_version() {
local channel="$1" tmp descs local channel="$1" arch="${2:-$OBSERVED_ARCH}" tmp descs
tmp=$(mktemp) || return 2 tmp=$(mktemp) || return 2
# A unique query string busts the CDN cache: right after a sync the plain # A unique query string busts the CDN cache: right after a sync the plain
# URL can keep serving the previous db for a while, which reads as "not # URL can keep serving the previous db for a while, which reads as "not
# published yet" to status, the wait loop, and ship's pre-checks. # published yet" to status, the wait loop, and ship's pre-checks.
if ! curl -sf "$PKGS_DB_BASE/$channel/x86_64/omarchy.db.tar.zst?$(date +%s%N)" -o "$tmp"; then if ! curl -sf "$PKGS_DB_BASE/$channel/$arch/omarchy.db.tar.zst?$(date +%s%N)" -o "$tmp"; then
rm -f "$tmp" rm -f "$tmp"
return 2 return 2
fi fi
@@ -207,6 +212,14 @@ published_version() {
' <<<"$descs" ' <<<"$descs"
} }
all_arches_at_version() { # all_arches_at_version <channel> <pkgver>
local channel="$1" want="$2" arch got
for arch in $(published_arches); do
got=$(published_version "$channel" "$arch" 2>/dev/null) || return 1
[[ "${got%-*}" == "$want" ]] || return 1
done
}
# The rc branch of THIS repo carries the current pins. Read them without # The rc branch of THIS repo carries the current pins. Read them without
# touching the working tree. # touching the working tree.
rc_branch_pin() { # prints "pkgver commit", empty when no rc branch rc_branch_pin() { # prints "pkgver commit", empty when no rc branch
@@ -251,8 +264,12 @@ fi
git -C /root/omarchy-pkgs-rc fetch origin rc git -C /root/omarchy-pkgs-rc fetch origin rc
git -C /root/omarchy-pkgs-rc reset --hard origin/rc git -C /root/omarchy-pkgs-rc reset --hard origin/rc
cd /root/omarchy-pkgs-rc cd /root/omarchy-pkgs-rc
OMARCHY_RC_PINS=1 OMARCHY_REPO_ROOT=/root/omarchy-pkgs/pkgs.omarchy.org \ # The pair is built once per published architecture (helpers/paths.sh on the
bin/repo release --mirror rc --package omarchy omarchy-settings --skip-prod-check # host decides which), so every architecture'"'"'s rc channel carries the pins.
for arch in $(BUILD_ROOT=/root/omarchy-pkgs-rc bash -c "source helpers/paths.sh; published_arches"); do
OMARCHY_RC_PINS=1 OMARCHY_REPO_ROOT=/root/omarchy-pkgs/pkgs.omarchy.org \
bin/repo release --mirror rc --arch "$arch" --package omarchy omarchy-settings --skip-prod-check
done
' '
trigger_rc_build() { trigger_rc_build() {
@@ -278,19 +295,25 @@ host_advance() { # host_advance <from> <to> [extra args...]
# against this machine's (likely stale) local tree would be wrong. # against this machine's (likely stale) local tree would be wrong.
if ! resolve_repo_host "$REPO_HOST_OVERRIDE" >/dev/null && ! on_repo_host; then if ! resolve_repo_host "$REPO_HOST_OVERRIDE" >/dev/null && ! on_repo_host; then
print_no_host_help "advance $from -> $to" \ print_no_host_help "advance $from -> $to" \
" cd /root/omarchy-pkgs && bin/repo advance --from $from --to $to --skip-prod-check $*" " cd /root/omarchy-pkgs && bin/repo advance --from $from --to $to --arch all --skip-prod-check $*"
return 1 return 1
fi fi
"$BUILD_ROOT/bin/repo" advance --from "$from" --to "$to" --skip-prod-check "$@" # --arch all: the host advances every architecture it publishes, so a train
# never moves a channel for one architecture and not another.
"$BUILD_ROOT/bin/repo" advance --from "$from" --to "$to" --arch all --skip-prod-check "$@"
} }
wait_for_published() { # wait_for_published <channel> <version> [timeout-seconds] wait_for_published() { # wait_for_published <channel> <version> [timeout-seconds]
local channel="$1" want="$2" timeout="${3:-3600}" waited=0 got local channel="$1" want="$2" timeout="${3:-3600}" waited=0 arch got pending
print_info "Waiting for $want to appear in the $channel channel (up to $((timeout / 60))m)..." print_info "Waiting for $want in $channel for: $(published_arches | tr '\n' ' ')"
while ((waited < timeout)); do while ((waited < timeout)); do
got=$(published_version "$channel" 2>/dev/null) || got="" pending=""
if [[ "${got%-*}" == "$want" ]]; then for arch in $(published_arches); do
print_success "$channel now serves omarchy $got" got=$(published_version "$channel" "$arch" 2>/dev/null) || got=""
[[ "${got%-*}" == "$want" ]] || pending+=" $arch=${got:-unreachable}"
done
if [[ -z "$pending" ]]; then
print_success "$channel now serves omarchy $want on every published architecture"
return 0 return 0
fi fi
sleep 60 sleep 60
@@ -298,7 +321,7 @@ wait_for_published() { # wait_for_published <channel> <version> [timeout-seconds
printf '.' >&2 printf '.' >&2
done done
echo "" >&2 echo "" >&2
print_warning "Timed out waiting for $want in $channel (currently: ${got:-unknown})" print_warning "Timed out waiting for $want in $channel (pending:$pending)"
print_info "The build may still be running — re-run this command to resume." print_info "The build may still be running — re-run this command to resume."
return 1 return 1
} }
@@ -352,8 +375,8 @@ iso_checkout() { # prints a usable omarchy-iso checkout, cloning to tmp if neede
build_iso() { # build_iso <version> [--rc] build_iso() { # build_iso <version> [--rc]
local version="$1" rc_flag="${2:-}" dir local version="$1" rc_flag="${2:-}" dir
dir=$(iso_checkout) || return 1 dir=$(iso_checkout) || return 1
if ! command -v docker >/dev/null || ! docker info >/dev/null 2>&1; then if [[ -z "$CONTAINER_ENGINE" ]] || ! "$CONTAINER_ENGINE" info >/dev/null 2>&1; then
print_warning "Docker unavailable — cannot build the ISO here. Run on a Docker machine:" print_warning "No container engine is available — cannot build the ISO here. Run on a Docker or Podman machine:"
echo " cd $dir && bin/omarchy-iso-release ${rc_flag:+$rc_flag }$version" echo " cd $dir && bin/omarchy-iso-release ${rc_flag:+$rc_flag }$version"
return 1 return 1
fi fi
@@ -662,15 +685,13 @@ cmd_rc() {
if [[ -n "$pin" ]]; then if [[ -n "$pin" ]]; then
local pin_ver="${pin%% *}" pin_commit="${pin##* }" local pin_ver="${pin%% *}" pin_commit="${pin##* }"
if [[ "$pin_commit" == "$head" && "$pin_ver" =~ ^${version//./\\.}rc[0-9]+$ ]]; then if [[ "$pin_commit" == "$head" && "$pin_ver" =~ ^${version//./\\.}rc[0-9]+$ ]]; then
local pub if all_arches_at_version rc "$pin_ver"; then
pub=$(published_version rc 2>/dev/null) || pub="" print_success "$pin_ver is already cut from this head and published to rc on every architecture"
if [[ "${pub%-*}" == "$pin_ver" ]]; then
print_success "$pin_ver is already cut from this head and published to rc"
maybe_iso "$pin_ver" rc "$iso_mode" maybe_iso "$pin_ver" rc "$iso_mode"
return 0 return 0
fi fi
print_info "$pin_ver is pinned from this head but not published yet — re-triggering the build" print_info "$pin_ver is pinned from this head but not published yet — re-triggering the build"
trigger_rc_build || true trigger_rc_build || return 1
[[ "$wait" == true ]] && wait_for_published rc "$pin_ver" [[ "$wait" == true ]] && wait_for_published rc "$pin_ver"
maybe_iso "$pin_ver" rc "$iso_mode" maybe_iso "$pin_ver" rc "$iso_mode"
return 0 return 0
@@ -686,7 +707,7 @@ cmd_rc() {
new_ver="${new_pin%% *}" new_ver="${new_pin%% *}"
print_success "Pinned $new_ver (rc branch pushed)" print_success "Pinned $new_ver (rc branch pushed)"
trigger_rc_build || true trigger_rc_build || return 1
if [[ "$wait" == true ]]; then if [[ "$wait" == true ]]; then
wait_for_published rc "$new_ver" || return 1 wait_for_published rc "$new_ver" || return 1
fi fi
@@ -708,9 +729,7 @@ cmd_ship() {
exit 1 exit 1
fi fi
version=$(branch_to_version "$branch") version=$(branch_to_version "$branch")
local stable_now if all_arches_at_version stable "$version" &&
stable_now=$(published_version stable 2>/dev/null) || stable_now=""
if [[ "${stable_now%-*}" == "$version" ]] &&
gh release view "v$version" --repo "$UPSTREAM_REPO" >/dev/null 2>&1; then gh release view "v$version" --repo "$UPSTREAM_REPO" >/dev/null 2>&1; then
print_success "Nothing to ship — $version is tagged, released, and live on stable" print_success "Nothing to ship — $version is tagged, released, and live on stable"
exit 0 exit 0
@@ -752,10 +771,8 @@ cmd_ship() {
echo " omarchy-release rc" echo " omarchy-release rc"
exit 1 exit 1
fi fi
local pub if ! all_arches_at_version rc "$pin_ver"; then
pub=$(published_version rc 2>/dev/null) || pub="" print_error "$pin_ver is pinned but is not published for every architecture"
if [[ "${pub%-*}" != "$pin_ver" ]]; then
print_error "$pin_ver is pinned but rc serves '${pub:-nothing}' — the candidate build hasn't published"
echo "Wait for it (or re-run: omarchy-release rc), then ship." echo "Wait for it (or re-run: omarchy-release rc), then ship."
exit 1 exit 1
fi fi
@@ -799,10 +816,9 @@ cmd_ship() {
# 2. Final pins into rc. Resolve the tag we just established so the final # 2. Final pins into rc. Resolve the tag we just established so the final
# PKGBUILDs record both its provenance and its exact commit. # PKGBUILDs record both its provenance and its exact commit.
local rc_pub stable_pub local stable_pub
rc_pub=$(published_version rc 2>/dev/null) || rc_pub="" if all_arches_at_version rc "$version"; then
if [[ "${rc_pub%-*}" == "$version" ]]; then print_success "2/7 Final $version already published to rc on every architecture"
print_success "2/7 Final $version already published to rc"
else else
if [[ "$pin_ver" != "$version" ]]; then if [[ "$pin_ver" != "$version" ]]; then
print_info "2/7 Pinning final $version from tag v$version..." print_info "2/7 Pinning final $version from tag v$version..."
@@ -810,22 +826,21 @@ cmd_ship() {
else else
print_info "2/7 Final $version pinned — re-triggering build" print_info "2/7 Final $version pinned — re-triggering build"
fi fi
trigger_rc_build || true trigger_rc_build || exit 1
wait_for_published rc "$version" || exit 1 wait_for_published rc "$version" || exit 1
fi fi
# 3. Promote rc -> stable # 3. Promote rc -> stable
stable_pub=$(published_version stable 2>/dev/null) || stable_pub="" if all_arches_at_version stable "$version"; then
if [[ "${stable_pub%-*}" == "$version" ]]; then print_success "3/7 Stable already serves $version on every architecture"
print_success "3/7 Stable already serves $version"
else else
host_advance rc stable || exit 1 host_advance rc stable || exit 1
stable_pub=$(published_version stable 2>/dev/null) || stable_pub="" if ! all_arches_at_version stable "$version"; then
if [[ "${stable_pub%-*}" != "$version" ]]; then print_error "Promotion ran but stable does not serve $version on every architecture"
print_error "Promotion ran but stable serves '${stable_pub:-nothing}' — investigate before continuing"
exit 1 exit 1
fi fi
print_success "3/7 Promoted to stable: omarchy $stable_pub" stable_pub=$(published_version stable 2>/dev/null) || stable_pub="$version"
print_success "3/7 Promoted to stable: omarchy $stable_pub on every architecture"
fi fi
# 4. Final pins onto master (keeps edge overlap publishing and the repo record) # 4. Final pins onto master (keeps edge overlap publishing and the repo record)
@@ -908,7 +923,7 @@ next_step() { # prints "<command>|<description>"
last=$(newest_release_branch 2>/dev/null) || last="" last=$(newest_release_branch 2>/dev/null) || last=""
if [[ -n "$last" && "$STABLE_VER" != "<unreachable>" ]]; then if [[ -n "$last" && "$STABLE_VER" != "<unreachable>" ]]; then
last_ver=$(branch_to_version "$last") last_ver=$(branch_to_version "$last")
if [[ "${STABLE_VER%-*}" != "$last_ver" ]] || if ! all_arches_at_version stable "$last_ver" ||
{ command -v gh >/dev/null && ! gh release view "v$last_ver" --repo "$UPSTREAM_REPO" >/dev/null 2>&1; }; then { command -v gh >/dev/null && ! gh release view "v$last_ver" --repo "$UPSTREAM_REPO" >/dev/null 2>&1; }; then
echo "ship|$last_ver is tagged but not fully shipped — resume ship" echo "ship|$last_ver is tagged but not fully shipped — resume ship"
return return
@@ -924,7 +939,7 @@ next_step() { # prints "<command>|<description>"
echo "ship|Final $TRAIN_VER is pinned — finish shipping (re-runs are safe)" echo "ship|Final $TRAIN_VER is pinned — finish shipping (re-runs are safe)"
elif [[ "$pin_commit" != "$TRAIN_HEAD" ]]; then elif [[ "$pin_commit" != "$TRAIN_HEAD" ]]; then
echo "rc|$TRAIN has commits newer than $pin_ver — cut the next candidate" echo "rc|$TRAIN has commits newer than $pin_ver — cut the next candidate"
elif [[ "${RC_VER%-*}" != "$pin_ver" ]]; then elif ! all_arches_at_version rc "$pin_ver"; then
echo "rc|$pin_ver is pinned but not published — re-run rc to re-trigger/wait" echo "rc|$pin_ver is pinned but not published — re-run rc to re-trigger/wait"
else else
echo "ship|$pin_ver is published to rc — test it, then ship" echo "ship|$pin_ver is published to rc — test it, then ship"
@@ -994,20 +1009,24 @@ cmd_doctor() {
check "makepkg available (checksums)" command -v makepkg check "makepkg available (checksums)" command -v makepkg
check "curl available" command -v curl check "curl available" command -v curl
check "upstream reachable ($UPSTREAM_URL)" git ls-remote "$UPSTREAM_URL" HEAD check "upstream reachable ($UPSTREAM_URL)" git ls-remote "$UPSTREAM_URL" HEAD
local ch local ch arch
for ch in edge stable; do for ch in edge stable; do
if published_version "$ch" >/dev/null 2>&1; then for arch in $(published_arches); do
print_success "$ch channel db readable" if published_version "$ch" "$arch" >/dev/null 2>&1; then
print_success "$ch/$arch channel db readable"
else
print_error "$ch/$arch channel db readable"
failures=$((failures + 1))
fi
done
done
for arch in $(published_arches); do
if published_version rc "$arch" >/dev/null 2>&1; then
print_success "rc/$arch channel db readable"
else else
print_error "$ch channel db readable" print_warning "rc/$arch channel db not readable — bootstrap it first: bin/repo bootstrap-rc (on the build host)"
failures=$((failures + 1))
fi fi
done done
if published_version rc >/dev/null 2>&1; then
print_success "rc channel db readable"
else
print_warning "rc channel db not readable — bootstrap it first: bin/repo bootstrap-rc (on the build host)"
fi
local host local host
if host=$(repo_host); then if host=$(repo_host); then
check "build host ssh ($host)" ssh -o ConnectTimeout=10 "$host" true check "build host ssh ($host)" ssh -o ConnectTimeout=10 "$host" true
@@ -1016,10 +1035,10 @@ cmd_doctor() {
else else
print_warning "no build host configured — set OMARCHY_REPO_HOST, --host, or write an ssh destination (root@<host> or an ssh-config alias) to $BUILD_ROOT/.repo-host; until then builds trigger on the 6h timer only" print_warning "no build host configured — set OMARCHY_REPO_HOST, --host, or write an ssh destination (root@<host> or an ssh-config alias) to $BUILD_ROOT/.repo-host; until then builds trigger on the 6h timer only"
fi fi
if command -v docker >/dev/null && docker info >/dev/null 2>&1; then if [[ -n "$CONTAINER_ENGINE" ]] && "$CONTAINER_ENGINE" info >/dev/null 2>&1; then
print_success "docker available (ISO builds possible here)" print_success "$CONTAINER_ENGINE available (ISO builds possible here)"
else else
print_warning "docker unavailable — ISO builds will print instructions instead" print_warning "container engine unavailable — ISO builds will print instructions instead"
fi fi
echo "" echo ""
if ((failures == 0)); then if ((failures == 0)); then
@@ -1054,6 +1073,25 @@ cmd_self_test() {
expect "version_is_patch 5.0.0" "$(version_is_patch 5.0.0 && echo yes || echo no)" "no" expect "version_is_patch 5.0.0" "$(version_is_patch 5.0.0 && echo yes || echo no)" "no"
expect "previous_patch_tag 4.0.2" "$(previous_patch_tag 4.0.2)" "v4.0.1" expect "previous_patch_tag 4.0.2" "$(previous_patch_tag 4.0.2)" "v4.0.1"
expect "previous_patch_tag 4.0.10" "$(previous_patch_tag 4.0.10)" "v4.0.9" expect "previous_patch_tag 4.0.10" "$(previous_patch_tag 4.0.10)" "v4.0.9"
# Keep release readiness fail-closed when only one architecture has reached
# the requested version. This replaces the network reader for this process;
# self-test exits immediately afterwards.
published_version() {
case "$2" in
x86_64) echo "${TEST_X86_VERSION:-4.0.2-1}" ;;
aarch64) echo "${TEST_ARM_VERSION:-4.0.2-1}" ;;
esac
}
PUBLISHED_ARCHES=x86_64
expect "x86-only readiness" "$(all_arches_at_version rc 4.0.2 && echo ready || echo blocked)" "ready"
PUBLISHED_ARCHES=aarch64
expect "ARM-only readiness" "$(all_arches_at_version rc 4.0.2 && echo ready || echo blocked)" "ready"
PUBLISHED_ARCHES="x86_64 aarch64"
TEST_ARM_VERSION=4.0.1-1
expect "mixed versions block release" "$(all_arches_at_version rc 4.0.2 && echo ready || echo blocked)" "blocked"
TEST_ARM_VERSION=4.0.2-1
expect "both architectures ready" "$(all_arches_at_version rc 4.0.2 && echo ready || echo blocked)" "ready"
echo "" echo ""
if ((failures == 0)); then if ((failures == 0)); then
print_success "Self-test passed" print_success "Self-test passed"
+19 -8
View File
@@ -149,11 +149,16 @@ fi
# leave nvidia-580xx-dkms and opencl-nvidia-580xx behind. An output's own name # leave nvidia-580xx-dkms and opencl-nvidia-580xx behind. An output's own name
# still matches, for pushing just one of them on purpose. # still matches, for pushing just one of them on purpose.
# #
# pkgbase comes from .PKGINFO rather than the PKGBUILD: it is what makepkg # Package identity comes from .PKGINFO rather than the filename or PKGBUILD: it
# actually recorded, and it needs no guessing about which directory built what. # is what makepkg actually recorded, and it handles epochs and split packages.
pkgbase_of() { package_identity_of() {
bsdtar -xOf "$1" .PKGINFO 2>/dev/null | bsdtar -xOf "$1" .PKGINFO 2>/dev/null |
awk -F ' = ' '$1 == "pkgbase" { print $2; exit }' awk -F ' = ' '
$1 == "pkgname" { name = $2 }
$1 == "pkgbase" { base = $2 }
$1 == "pkgver" { version = $2 }
END { print name "\t" base "\t" version }
'
} }
FILES=() FILES=()
@@ -161,14 +166,18 @@ if [[ -z "$PACKAGES" ]]; then
FILES=("${ALL_FILES[@]}") FILES=("${ALL_FILES[@]}")
else else
declare -A MATCHED=() declare -A MATCHED=()
declare -A LATEST_FILE=()
declare -A LATEST_VERSION=()
for file in "${ALL_FILES[@]}"; do for file in "${ALL_FILES[@]}"; do
# name-version-release-arch.pkg.tar.zst -> name IFS=$'\t' read -r pkgname pkgbase pkgver < <(package_identity_of "$BUILD_OUTPUT_DIR/$file")
pkgname="${file%-*-*-*.pkg.tar.*}"
pkgbase=$(pkgbase_of "$BUILD_OUTPUT_DIR/$file")
for wanted in $PACKAGES; do for wanted in $PACKAGES; do
if [[ "$pkgname" == "$wanted" || "$pkgbase" == "$wanted" ]]; then if [[ "$pkgname" == "$wanted" || "$pkgbase" == "$wanted" ]]; then
FILES+=("$file")
MATCHED["$wanted"]=1 MATCHED["$wanted"]=1
if [[ -z "${LATEST_FILE[$pkgname]:-}" ]] ||
[[ $(vercmp "$pkgver" "${LATEST_VERSION[$pkgname]}") -gt 0 ]]; then
LATEST_FILE["$pkgname"]="$file"
LATEST_VERSION["$pkgname"]="$pkgver"
fi
break break
fi fi
done done
@@ -181,6 +190,8 @@ else
exit 1 exit 1
fi fi
done done
mapfile -t FILES < <(printf '%s\n' "${LATEST_FILE[@]}" | sort)
fi fi
if [[ ${#FILES[@]} -eq 0 ]]; then if [[ ${#FILES[@]} -eq 0 ]]; then
+5 -1
View File
@@ -114,7 +114,11 @@ BUILT_FILES=""
# What the scheduled version check queued, when it was the one that asked for # What the scheduled version check queued, when it was the one that asked for
# this run. Absent for a manual run, which is fine — the report just omits it. # this run. Absent for a manual run, which is fine — the report just omits it.
QUEUED_PACKAGES="" QUEUED_PACKAGES=""
QUEUE_FILE="${OMARCHY_STATE_DIR:-/root/.state}/.sync-needed-$MIRROR" QUEUE_FILE=$(sync_queue_file "$MIRROR" "$ARCH")
# A queue written under the pre-architecture name belongs to x86_64.
if [[ "$ARCH" == "x86_64" && ! -s "$QUEUE_FILE" && -s "$(legacy_sync_queue_file "$MIRROR")" ]]; then
QUEUE_FILE=$(legacy_sync_queue_file "$MIRROR")
fi
[[ -s "$QUEUE_FILE" ]] && QUEUED_PACKAGES=$(grep -c '' "$QUEUE_FILE") [[ -s "$QUEUE_FILE" ]] && QUEUED_PACKAGES=$(grep -c '' "$QUEUE_FILE")
if [[ "$DRY_RUN" != true ]]; then if [[ "$DRY_RUN" != true ]]; then
+15 -10
View File
@@ -62,8 +62,8 @@ if [[ ! -d "$REPO_DIR" ]]; then
exit 1 exit 1
fi fi
# Check Docker is available # Check the selected container engine is available
check_docker check_engine
# Find package files to confirm before running Docker # Find package files to confirm before running Docker
cd "$REPO_DIR" cd "$REPO_DIR"
@@ -88,24 +88,29 @@ if [[ ! $REPLY =~ ^[Yy]$ ]]; then
exit 0 exit 0
fi fi
# Build/update the Docker image (always use x86_64 for removal - it's architecture independent) # repo-remove is architecture-independent, so use a host-native edge image.
# repo-remove is mirror-independent — always use the edge x86_64 image TOOL_ARCH=$(docker_native_arch) || {
build_docker_image "$BUILD_DIR" "x86_64" "edge" print_error "Unsupported host architecture: $(uname -m)"
exit 1
}
build_docker_image "$BUILD_DIR" "$TOOL_ARCH" "edge"
acquire_release_lock || exit 1 acquire_release_lock || exit 1
print_info "Removing package..." print_info "Removing package..."
# Ensure directory is writable by container user # Rootless Podman uses keep-id and leaves host ownership/modes intact.
make_dir_writable "$REPO_DIR" if [[ "$CONTAINER_ENGINE" == "docker" ]]; then
make_dir_writable "$REPO_DIR"
fi
# Run the removal script in Docker (always use x86_64 image) # Run the removal script in the host-native image.
docker run --rm --platform linux/amd64 \ "$CONTAINER_ENGINE" run "${CONTAINER_RUN_ARGS[@]}" --rm "$(get_platform_arg "$TOOL_ARCH")" \
-e ARCH="$ARCH" \ -e ARCH="$ARCH" \
-e MIRROR="$MIRROR" \ -e MIRROR="$MIRROR" \
-v "$REPO_ROOT:/pkgs.omarchy.org" \ -v "$REPO_ROOT:/pkgs.omarchy.org" \
-v "$BUILD_DIR:/build:ro" \ -v "$BUILD_DIR:/build:ro" \
omarchy-pkg-builder:latest-x86_64-edge /build/remove-package.sh "$PACKAGE_NAME" "omarchy-pkg-builder:latest-$TOOL_ARCH-edge" /build/remove-package.sh "$PACKAGE_NAME"
RESULT=$? RESULT=$?
+59 -22
View File
@@ -15,6 +15,8 @@ set -e
BUILD_ROOT=$(realpath "${BASH_SOURCE[0]%/*}/..") BUILD_ROOT=$(realpath "${BASH_SOURCE[0]%/*}/..")
source "$BUILD_ROOT/helpers/message-helpers.sh" source "$BUILD_ROOT/helpers/message-helpers.sh"
source "$BUILD_ROOT/helpers/docker-helpers.sh"
source "$BUILD_ROOT/helpers/paths.sh"
CHECK_ONLY=false CHECK_ONLY=false
SKIP_TIMERS=false SKIP_TIMERS=false
@@ -61,10 +63,12 @@ if command -v apt-get >/dev/null 2>&1; then
DISTRO="debian" DISTRO="debian"
PKG_BSDTAR="libarchive-tools" PKG_BSDTAR="libarchive-tools"
PKG_DOCKER="docker.io" PKG_DOCKER="docker.io"
PKG_PODMAN="podman"
elif command -v pacman >/dev/null 2>&1; then elif command -v pacman >/dev/null 2>&1; then
DISTRO="arch" DISTRO="arch"
PKG_BSDTAR="libarchive" PKG_BSDTAR="libarchive"
PKG_DOCKER="docker" PKG_DOCKER="docker"
PKG_PODMAN="podman"
else else
print_error "Unsupported distribution — need apt-get or pacman" print_error "Unsupported distribution — need apt-get or pacman"
exit 1 exit 1
@@ -72,18 +76,36 @@ fi
print_info "Distribution: $DISTRO" print_info "Distribution: $DISTRO"
# A fresh repository host still defaults to Docker. An explicit Podman choice,
# or a working Podman selected by the shared helper, is left alone.
if [[ -z "$CONTAINER_ENGINE" ]]; then
CONTAINER_ENGINE=docker
export CONTAINER_ENGINE
fi
if ! container_engine_supported; then
print_error "Unsupported CONTAINER_ENGINE: $CONTAINER_ENGINE (use docker or podman)"
exit 1
fi
if [[ "$CONTAINER_ENGINE" == "docker" ]]; then
PKG_ENGINE="$PKG_DOCKER"
ENGINE_NAME="Docker"
else
PKG_ENGINE="$PKG_PODMAN"
ENGINE_NAME="Podman"
fi
if [[ "$CHECK_ONLY" != true && $EUID -ne 0 ]]; then if [[ "$CHECK_ONLY" != true && $EUID -ne 0 ]]; then
print_error "Run as root (installing packages and systemd units)" print_error "Run as root (installing packages and systemd units)"
exit 1 exit 1
fi fi
# Docker and the release timers are both systemd units. Say so plainly rather # The release timers are systemd units. Say so plainly rather than failing
# than failing later on a missing command — a container is the usual way to end # later on a missing command — a container is the usual way to end up here,
# up here, and it cannot be a repository host. # and it cannot be a repository host.
if [[ "$CHECK_ONLY" != true ]] && ! command -v systemctl >/dev/null 2>&1; then if [[ "$CHECK_ONLY" != true ]] && ! command -v systemctl >/dev/null 2>&1; then
print_error "systemctl not found — the repository host must run systemd" print_error "systemctl not found — the repository host must run systemd"
echo "" echo ""
echo "Docker and the release timers are systemd units. This looks like a" echo "The release timers are systemd units. This looks like a"
echo "container; run setup on the host itself." echo "container; run setup on the host itself."
exit 1 exit 1
fi fi
@@ -139,24 +161,27 @@ else
fi fi
echo "" echo ""
# --- docker ------------------------------------------------------------------ # --- container engine --------------------------------------------------------
# Docker is left alone when it already works. A host may well be running a # The selected engine is left alone when it already works. A host may be running a
# version from Docker's own repository rather than the distribution's, and # version from Docker's own repository rather than the distribution's, and
# replacing that underneath a working builder would be a poor trade for # replacing that underneath a working builder would be a poor trade for
# tidiness. # tidiness.
print_info "Checking Docker..." print_info "Checking $ENGINE_NAME..."
if command -v docker >/dev/null 2>&1; then if command -v "$CONTAINER_ENGINE" >/dev/null 2>&1; then
print_step "docker present: $(docker --version 2>/dev/null | head -1)" print_step "$CONTAINER_ENGINE present: $("$CONTAINER_ENGINE" --version 2>/dev/null | head -1)"
if docker info >/dev/null 2>&1; then if "$CONTAINER_ENGINE" info >/dev/null 2>&1; then
print_success "Docker is installed and running — leaving it alone" print_success "$ENGINE_NAME is installed and available — leaving it alone"
elif [[ "$CHECK_ONLY" == true ]]; then elif [[ "$CHECK_ONLY" == true ]]; then
print_warning "Docker is installed but not running; would start it" print_warning "$ENGINE_NAME is installed but unavailable"
elif [[ "$CONTAINER_ENGINE" == "podman" ]]; then
print_error "Podman is installed but unavailable to the current user"
exit 1
else else
print_info "Docker is installed but not running — starting it" print_info "Docker is installed but not running — starting it"
systemctl enable --now docker.service systemctl enable --now docker.service
if docker info >/dev/null 2>&1; then if "$CONTAINER_ENGINE" info >/dev/null 2>&1; then
print_success "Docker started" print_success "Docker started"
else else
print_error "Docker is installed but still not responding" print_error "Docker is installed but still not responding"
@@ -165,16 +190,24 @@ if command -v docker >/dev/null 2>&1; then
fi fi
fi fi
elif [[ "$CHECK_ONLY" == true ]]; then elif [[ "$CHECK_ONLY" == true ]]; then
print_warning "Would install $PKG_DOCKER and enable it" if [[ "$CONTAINER_ENGINE" == "docker" ]]; then
else print_warning "Would install $PKG_ENGINE and enable it"
print_info "Installing $PKG_DOCKER..."
install_packages "$PKG_DOCKER"
systemctl enable --now docker.service
if docker info >/dev/null 2>&1; then
print_success "Docker installed and running"
else else
print_error "Docker installed but not responding" print_warning "Would install $PKG_ENGINE"
echo " Check 'systemctl status docker' — builds cannot run without it." fi
else
print_info "Installing $PKG_ENGINE..."
install_packages "$PKG_ENGINE"
if [[ "$CONTAINER_ENGINE" == "docker" ]]; then
systemctl enable --now docker.service
fi
if "$CONTAINER_ENGINE" info >/dev/null 2>&1; then
print_success "$ENGINE_NAME installed and available"
else
print_error "$ENGINE_NAME installed but not responding"
if [[ "$CONTAINER_ENGINE" == "docker" ]]; then
echo " Check 'systemctl status docker' — builds cannot run without it."
fi
exit 1 exit 1
fi fi
fi fi
@@ -237,6 +270,10 @@ echo ""
# --- release timers ---------------------------------------------------------- # --- release timers ----------------------------------------------------------
print_info "Published architectures: $(published_arches | tr '\n' ' ')"
echo " (PUBLISHED_ARCHES in helpers/paths.sh; OMARCHY_ARCHES overrides a one-off command)"
echo ""
TIMERS=(omarchy-check-versions omarchy-auto-release-edge omarchy-auto-release-rc omarchy-auto-release-stable) TIMERS=(omarchy-check-versions omarchy-auto-release-edge omarchy-auto-release-rc omarchy-auto-release-stable)
if [[ "$SKIP_TIMERS" == true ]]; then if [[ "$SKIP_TIMERS" == true ]]; then
+16 -9
View File
@@ -52,8 +52,8 @@ if [[ ! -d "$BUILD_OUTPUT_DIR" ]]; then
exit 1 exit 1
fi fi
# Check Docker is available # Check the selected container engine is available
check_docker check_engine
# Check GPG credentials are in environment # Check GPG credentials are in environment
if [[ -z "$GPG_PRIVATE_KEY" ]]; then if [[ -z "$GPG_PRIVATE_KEY" ]]; then
@@ -66,23 +66,30 @@ if [[ -z "$GPG_PASSPHRASE" ]]; then
exit 1 exit 1
fi fi
# Build/update the Docker image (always use x86_64 for signing - it's architecture independent) # Signing is architecture-independent, so run its utility container natively
build_docker_image "$BUILD_DIR" "x86_64" "$MIRROR" # on either an x86_64 or ARM host.
TOOL_ARCH=$(docker_native_arch) || {
print_error "Unsupported host architecture: $(uname -m)"
exit 1
}
build_docker_image "$BUILD_DIR" "$TOOL_ARCH" "$MIRROR"
print_info "Running package signing..." print_info "Running package signing..."
# Ensure output directory is writable by container user # Rootless Podman uses keep-id and leaves host ownership/modes intact.
make_dir_writable "$BUILD_OUTPUT_DIR" if [[ "$CONTAINER_ENGINE" == "docker" ]]; then
make_dir_writable "$BUILD_OUTPUT_DIR"
fi
# Run the signing script in Docker (always use x86_64 image) # Run the signing script in the host-native image.
docker run --rm --platform linux/amd64 \ "$CONTAINER_ENGINE" run "${CONTAINER_RUN_ARGS[@]}" --rm "$(get_platform_arg "$TOOL_ARCH")" \
-e ARCH="$ARCH" \ -e ARCH="$ARCH" \
-e MIRROR="$MIRROR" \ -e MIRROR="$MIRROR" \
-e GPG_PRIVATE_KEY="$GPG_PRIVATE_KEY" \ -e GPG_PRIVATE_KEY="$GPG_PRIVATE_KEY" \
-e GPG_PASSPHRASE="$GPG_PASSPHRASE" \ -e GPG_PASSPHRASE="$GPG_PASSPHRASE" \
-v "$BUILD_ROOT/build-output:/build-output" \ -v "$BUILD_ROOT/build-output:/build-output" \
-v "$BUILD_DIR:/build:ro" \ -v "$BUILD_DIR:/build:ro" \
omarchy-pkg-builder:latest-x86_64-$MIRROR /build/sign.sh "omarchy-pkg-builder:latest-$TOOL_ARCH-$MIRROR" /build/sign.sh
SIGN_RESULT=$? SIGN_RESULT=$?
+228 -59
View File
@@ -17,11 +17,14 @@ SELF_TEST=false
# rebuilding for it would ship a package built against the wrong ABI. # rebuilding for it would ship a package built against the wrong ABI.
OFFICIAL_REPOS=" core extra multilib core-debug extra-debug " OFFICIAL_REPOS=" core extra multilib core-debug extra-debug "
# The published repository, used as the floor a bumped pkgrel has to clear. # The published repositories are the floor a bumped pkgrel has to clear.
# Only x86_64 is published today; aarch64 has no repository to compare against.
PUBLISHED_BASE_URL="${OMARCHY_PUBLISHED_BASE_URL:-https://pkgs.omarchy.org}" PUBLISHED_BASE_URL="${OMARCHY_PUBLISHED_BASE_URL:-https://pkgs.omarchy.org}"
PUBLISHED_MIRRORS=(edge stable) PUBLISHED_MIRRORS=(edge stable)
PUBLISHED_ARCH=x86_64
# Arch Linux ARM has no dated snapshots. This is the same live repository the
# aarch64 builder resolves; override it only when the builder mirror changes.
ALARM_BASE_URL="${OMARCHY_ALARM_BASE_URL:-https://fl.us.mirror.archlinuxarm.org/aarch64}"
ALARM_REPOS=(core extra alarm aur)
usage() { usage() {
cat <<EOF cat <<EOF
@@ -34,10 +37,13 @@ A package opts in by naming those dependencies in .omarchy/package.json:
{ "source": "aur", "sync": false, "rebuild_on": ["qt6-base"] } { "source": "aur", "sync": false, "rebuild_on": ["qt6-base"] }
The versions the current pkgrel was bumped for are recorded alongside, in The versions the current pkgrel was bumped for are recorded per published
rebuilt_against, and written by this command: architecture in rebuilt_against, and written by this command:
{ "rebuild_on": ["qt6-base"], "rebuilt_against": { "qt6-base": "6.11.2-2" } } { "rebuild_on": ["qt6-base"], "rebuilt_against": {
"x86_64": { "qt6-base": "6.11.2-3" },
"aarch64": { "qt6-base": "6.11.2-2" }
} }
pkgrel is bumped unless every package named in rebuild_on is recorded and still pkgrel is bumped unless every package named in rebuild_on is recorded and still
matches. A name that is missing from the record counts as changed, so opting a matches. A name that is missing from the record counts as changed, so opting a
@@ -60,8 +66,9 @@ Examples:
$0 # Update every package that declares rebuild_on $0 # Update every package that declares rebuild_on
$0 quickshell-git # Update specific packages $0 quickshell-git # Update specific packages
Trigger versions are read from the local pacman database, so sync it first x86_64 trigger versions come from the local pacman database; aarch64 versions
(pacman -Sy) or this reports whatever that database last saw. come from the live Arch Linux ARM repository database. Only architectures in
PUBLISHED_ARCHES that the package supports are considered.
EOF EOF
} }
@@ -94,7 +101,7 @@ SPECIFIC_MODE=false
# The version of a trigger package as the build container would resolve it. # 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 # 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. # gets to say which package was left alone instead of the run dying here.
repo_version() { native_repo_version() {
local package="$1" local package="$1"
local info local info
@@ -108,9 +115,59 @@ repo_version() {
' <<<"$info" ' <<<"$info"
} }
load_alarm_repo() {
local repo="$1" db index
index="$TEMP_DIR/alarm-$repo.index"
[[ -f "$index" ]] && return 0
db="$TEMP_DIR/alarm-$repo.db"
if ! curl -fsSL --max-time 120 -o "$db" "$ALARM_BASE_URL/$repo/$repo.db" 2>/dev/null; then
print_error "Could not read the aarch64 $repo repository database"
return 1
fi
if ! tar -tf "$db" >/dev/null 2>&1; then
print_error "Unreadable aarch64 $repo repository database"
return 1
fi
tar -xOf "$db" --wildcards '*/desc' 2>/dev/null | awk '
function emit() {
if (name != "" && version != "") {
print name "\t" version
if (base != "" && base != name) print base "\t" version
}
name=""; base=""; version=""
}
$0 == "%FILENAME%" { emit(); next }
$0 == "%NAME%" { if (name != "" && version != "") emit(); getline; name=$0; next }
$0 == "%BASE%" { getline; base=$0; next }
$0 == "%VERSION%" { getline; version=$0; next }
END { emit() }
' >"$index"
}
alarm_repo_version() {
local package="$1" repo version
for repo in "${ALARM_REPOS[@]}"; do
load_alarm_repo "$repo" || return 1
version=$(awk -F '\t' -v package="$package" '$1 == package { print $2; exit }' "$TEMP_DIR/alarm-$repo.index")
if [[ -n "$version" ]]; then
echo "$version"
return 0
fi
done
}
repo_version() { # repo_version <arch> <package>
case "$1" in
x86_64) native_repo_version "$2" ;;
aarch64) alarm_repo_version "$2" ;;
*) return 1 ;;
esac
}
declare -A PUBLISHED_VERSION=() declare -A PUBLISHED_VERSION=()
PUBLISHED_LOADED=false PUBLISHED_LOADED=false
PUBLISHED_AVAILABLE=true
remember_published() { remember_published() {
local name="$1" local name="$1"
@@ -131,34 +188,35 @@ load_published_versions() {
[[ "$PUBLISHED_LOADED" == true ]] && return 0 [[ "$PUBLISHED_LOADED" == true ]] && return 0
PUBLISHED_LOADED=true PUBLISHED_LOADED=true
local mirror db name base version local arch mirror db name base version
for mirror in "${PUBLISHED_MIRRORS[@]}"; do for arch in $(published_arches); do
db="$TEMP_DIR/published-$mirror.db.tar.zst" for mirror in "${PUBLISHED_MIRRORS[@]}"; do
db="$TEMP_DIR/published-$mirror-$arch.db.tar.zst"
if ! curl -fsSL --max-time 120 -o "$db" \ if ! curl -fsSL --max-time 120 -o "$db" \
"$PUBLISHED_BASE_URL/$mirror/$PUBLISHED_ARCH/omarchy.db.tar.zst" 2>/dev/null; then "$PUBLISHED_BASE_URL/$mirror/$arch/omarchy.db.tar.zst" 2>/dev/null; then
print_warning "Could not read the published $mirror database; bumps are not checked against it this run" print_warning "Could not read the published $mirror/$arch database; bumps are not checked against it this run"
PUBLISHED_AVAILABLE=false continue
continue fi
fi
while IFS=$'\t' read -r name base version; do while IFS=$'\t' read -r name base version; do
[[ -n "$name" && -n "$version" ]] && remember_published "$name" "$version" [[ -n "$name" && -n "$version" ]] && remember_published "$name" "$version"
[[ -n "$base" && -n "$version" ]] && remember_published "$base" "$version" [[ -n "$base" && -n "$version" ]] && remember_published "$base" "$version"
done < <( done < <(
tar -xOf "$db" --wildcards '*/desc' 2>/dev/null | awk ' tar -xOf "$db" --wildcards '*/desc' 2>/dev/null | awk '
function emit() { function emit() {
if (name != "" && version != "") print name "\t" base "\t" version if (name != "" && version != "") print name "\t" base "\t" version
name=""; base=""; version="" name=""; base=""; version=""
} }
$0 == "%FILENAME%" { emit(); next } $0 == "%FILENAME%" { emit(); next }
$0 == "%NAME%" { if (name != "" && version != "") emit(); getline; name=$0; next } $0 == "%NAME%" { if (name != "" && version != "") emit(); getline; name=$0; next }
$0 == "%BASE%" { getline; base=$0; next } $0 == "%BASE%" { getline; base=$0; next }
$0 == "%VERSION%" { getline; version=$0; next } $0 == "%VERSION%" { getline; version=$0; next }
END { emit() } END { emit() }
' '
) )
done
done done
} }
@@ -252,7 +310,17 @@ record_triggers() {
local package_dir="$1" local package_dir="$1"
local current="$2" local current="$2"
write_metadata "$package_dir" '.rebuilt_against = $current' --argjson current "$current" # A flat record is the legacy x86_64 shape. Preserve records for
# architectures outside this run, then replace the ones just rebuilt.
write_metadata "$package_dir" '
(.rebuilt_against // {}) as $old |
(if ($old | length) == 0 then {}
elif ($old | to_entries | all(.value | type == "string"))
then {x86_64: $old}
else $old
end) as $by_arch |
.rebuilt_against = ($by_arch * $current)
' --argjson current "$current"
} }
# Metadata that does not parse would otherwise drop its package out of the run # Metadata that does not parse would otherwise drop its package out of the run
@@ -300,21 +368,41 @@ sync_package() {
print_info "Checking $package against ${triggers[*]}..." print_info "Checking $package against ${triggers[*]}..."
local current="{}" trigger version local current="{}" arch arch_current trigger version considered=0
for trigger in "${triggers[@]}"; do for arch in $(published_arches); do
version=$(repo_version "$trigger") package_supports_arch "$package_dir" "$arch" || continue
if [[ -z "$version" ]]; then considered=$((considered + 1))
print_error " $trigger is in no official repository; leaving $package alone" arch_current="{}"
((++FAILED)) for trigger in "${triggers[@]}"; do
return 0 if ! version=$(repo_version "$arch" "$trigger"); then
fi print_error " Could not read $arch repository versions; leaving $package alone"
if ! current=$(jq -c --arg name "$trigger" --arg version "$version" '.[$name] = $version' <<<"$current"); then ((++FAILED))
print_error " Could not record $trigger $version for $package" return 0
fi
if [[ -z "$version" ]]; then
print_error " $trigger is in no $arch repository; leaving $package alone"
((++FAILED))
return 0
fi
if ! arch_current=$(jq -c --arg name "$trigger" --arg version "$version" '.[$name] = $version' <<<"$arch_current"); then
print_error " Could not record $arch/$trigger $version for $package"
((++FAILED))
return 0
fi
done
if ! current=$(jq -c --arg arch "$arch" --argjson versions "$arch_current" '.[$arch] = $versions' <<<"$current"); then
print_error " Could not record $arch trigger versions for $package"
((++FAILED)) ((++FAILED))
return 0 return 0
fi fi
done done
if ((considered == 0)); then
print_info " Skipping: not built for any published architecture"
((++SKIPPED))
return 0
fi
local recorded local recorded
if ! recorded=$(package_metadata_value "$package_dir" '.rebuilt_against' ""); then if ! recorded=$(package_metadata_value "$package_dir" '.rebuilt_against' ""); then
print_error " Could not read .omarchy/package.json for $package" print_error " Could not read .omarchy/package.json for $package"
@@ -323,13 +411,20 @@ sync_package() {
fi fi
[[ -n "$recorded" && "$recorded" != "null" ]] || recorded="{}" [[ -n "$recorded" && "$recorded" != "null" ]] || recorded="{}"
# Before architecture-specific records existed, rebuilt_against described
# x86_64. Read it that way without forcing a metadata-only migration.
if jq -e 'to_entries | all(.value | type == "string")' >/dev/null <<<"$recorded"; then
recorded=$(jq -c '{x86_64: .}' <<<"$recorded")
fi
# Walk the declared triggers rather than the record, so a name the record does # Walk the declared triggers rather than the record, so a name the record does
# not carry reads as changed instead of going unexamined forever. # not carry reads as changed instead of going unexamined forever.
local moved local moved
if ! moved=$(jq -r --argjson recorded "$recorded" ' if ! moved=$(jq -r --argjson recorded "$recorded" '
to_entries [to_entries[] as $arch
| map(select($recorded[.key] != .value) | $arch.value | to_entries[] as $trigger
| "\(.key) \($recorded[.key] // "unrecorded") -> \(.value)") | select($recorded[$arch.key][$trigger.key] != $trigger.value)
| "\($arch.key)/\($trigger.key) \($recorded[$arch.key][$trigger.key] // "unrecorded") -> \($trigger.value)"]
| join(", ") | join(", ")
' <<<"$current"); then ' <<<"$current"); then
print_error " Could not compare recorded trigger versions for $package" print_error " Could not compare recorded trigger versions for $package"
@@ -338,7 +433,7 @@ sync_package() {
fi fi
if [[ -z "$moved" ]]; then if [[ -z "$moved" ]]; then
print_info " Already rebuilt against $(jq -r 'to_entries | map("\(.key) \(.value)") | join(", ")' <<<"$current")" print_info " Already rebuilt against every published architecture"
((++SKIPPED)) ((++SKIPPED))
return 0 return 0
fi fi
@@ -466,11 +561,11 @@ selftest_root() {
} }
selftest_package() { selftest_package() {
local root="$1" name="$2" pkgrel="$3" metadata="$4" pkgver="${5:-1.0}" local root="$1" name="$2" pkgrel="$3" metadata="$4" pkgver="${5:-1.0}" arches="${6:-x86_64}"
local dir="$root/pkgbuilds/$name" local dir="$root/pkgbuilds/$name"
mkdir -p "$dir/.omarchy" mkdir -p "$dir/.omarchy"
printf 'pkgname=%s\npkgver=%s\npkgrel=%s\narch=(x86_64)\n' "$name" "$pkgver" "$pkgrel" > "$dir/PKGBUILD" printf 'pkgname=%s\npkgver=%s\npkgrel=%s\narch=(%s)\n' "$name" "$pkgver" "$pkgrel" "$arches" > "$dir/PKGBUILD"
printf '%s\n' "$metadata" > "$dir/.omarchy/package.json" printf '%s\n' "$metadata" > "$dir/.omarchy/package.json"
} }
@@ -514,19 +609,51 @@ selftest_published() {
cat > "$root/stub/curl" <<'STUB' cat > "$root/stub/curl" <<'STUB'
#!/bin/bash #!/bin/bash
out="" out=""
url=""
while [[ $# -gt 0 ]]; do while [[ $# -gt 0 ]]; do
case "$1" in case "$1" in
-o) out="$2"; shift 2 ;; -o) out="$2"; shift 2 ;;
*) shift ;; *) url="$1"; shift ;;
esac esac
done done
db="$(dirname "$0")/omarchy.db.tar.zst" if [[ "$url" == */aarch64/* ]]; then
repo="${url%/*}"
repo="${repo##*/}"
db="$(dirname "$0")/alarm-$repo.db"
else
db="$(dirname "$0")/omarchy.db.tar.zst"
fi
[[ -f "$db" && -n "$out" ]] || exit 22 [[ -f "$db" && -n "$out" ]] || exit 22
cp "$db" "$out" cp "$db" "$out"
STUB STUB
chmod +x "$root/stub/curl" chmod +x "$root/stub/curl"
} }
selftest_alarm() {
local root="$1"
shift
local repo staging="$root/stub/alarm-db" entry name version
for repo in core extra; do
rm -rf "$staging"
mkdir -p "$staging"
if [[ "$repo" == "core" ]]; then
mkdir -p "$staging/unrelated-1-1"
printf '%%FILENAME%%\nunrelated-1-1-aarch64.pkg.tar.zst\n\n%%NAME%%\nunrelated\n\n%%BASE%%\nunrelated\n\n%%VERSION%%\n1-1\n' \
> "$staging/unrelated-1-1/desc"
else
for entry in "$@"; do
name="${entry%=*}"
version="${entry#*=}"
mkdir -p "$staging/$name-$version"
printf '%%FILENAME%%\n%s-%s-aarch64.pkg.tar.zst\n\n%%NAME%%\n%s\n\n%%BASE%%\n%s\n\n%%VERSION%%\n%s\n' \
"$name" "$version" "$name" "$name" "$version" > "$staging/$name-$version/desc"
done
fi
tar -czf "$root/stub/alarm-$repo.db" -C "$staging" .
done
}
cmd_self_test() { cmd_self_test() {
local failures=0 local failures=0
local root local root
@@ -545,7 +672,8 @@ cmd_self_test() {
local root="$1" local root="$1"
shift shift
local status=0 local status=0
PATH="$root/stub:$PATH" "$root/bin/sync-rebuilds" "$@" > "$root/output" 2>&1 || status=$? OMARCHY_ARCHES="${SELFTEST_ARCHES:-x86_64}" \
PATH="$root/stub:$PATH" "$root/bin/sync-rebuilds" "$@" > "$root/output" 2>&1 || status=$?
echo "$status" echo "$status"
} }
@@ -563,7 +691,7 @@ cmd_self_test() {
check "run succeeds" 0 "$(run_case "$root")" check "run succeeds" 0 "$(run_case "$root")"
check "pkgrel bumped" 2 "$(pkgrel_of "$root/pkgbuilds/t-partial")" check "pkgrel bumped" 2 "$(pkgrel_of "$root/pkgbuilds/t-partial")"
check "unrecorded trigger now recorded" "2-2" \ check "unrecorded trigger now recorded" "2-2" \
"$(jq -r '.rebuilt_against["dep-b"]' "$root/pkgbuilds/t-partial/.omarchy/package.json")" "$(jq -r '.rebuilt_against.x86_64["dep-b"]' "$root/pkgbuilds/t-partial/.omarchy/package.json")"
echo "Opting a package in buys a rebuild rather than a bare record:" echo "Opting a package in buys a rebuild rather than a bare record:"
root=$(selftest_root fresh) root=$(selftest_root fresh)
@@ -573,7 +701,7 @@ cmd_self_test() {
check "run succeeds" 0 "$(run_case "$root")" check "run succeeds" 0 "$(run_case "$root")"
check "pkgrel bumped" 2 "$(pkgrel_of "$root/pkgbuilds/t-fresh")" check "pkgrel bumped" 2 "$(pkgrel_of "$root/pkgbuilds/t-fresh")"
check "trigger recorded" "1-1" \ check "trigger recorded" "1-1" \
"$(jq -r '.rebuilt_against["dep-a"]' "$root/pkgbuilds/t-fresh/.omarchy/package.json")" "$(jq -r '.rebuilt_against.x86_64["dep-a"]' "$root/pkgbuilds/t-fresh/.omarchy/package.json")"
echo "An unchanged package is left alone:" echo "An unchanged package is left alone:"
root=$(selftest_root current) root=$(selftest_root current)
@@ -612,6 +740,47 @@ cmd_self_test() {
check "suffix recorded for the next AUR sync" 1 \ check "suffix recorded for the next AUR sync" 1 \
"$(jq -r '.pkgrel.suffix' "$root/pkgbuilds/t-aur/.omarchy/package.json")" "$(jq -r '.pkgrel.suffix' "$root/pkgbuilds/t-aur/.omarchy/package.json")"
echo "A dependency is tracked independently for both published architectures:"
root=$(selftest_root multiarch)
selftest_package "$root" t-multi 1 '{"source":"local","rebuild_on":["dep-a"],"rebuilt_against":{"dep-a":"1-1"}}' 1.0 'x86_64 aarch64'
selftest_pacman "$root" dep-a=1-1
selftest_published "$root"
selftest_alarm "$root" dep-a=2-1
SELFTEST_ARCHES="x86_64 aarch64"
check "run succeeds" 0 "$(run_case "$root")"
unset SELFTEST_ARCHES
check "pkgrel bumped once" 2 "$(pkgrel_of "$root/pkgbuilds/t-multi")"
check "x86_64 trigger recorded" "1-1" \
"$(jq -r '.rebuilt_against.x86_64["dep-a"]' "$root/pkgbuilds/t-multi/.omarchy/package.json")"
check "aarch64 trigger recorded" "2-1" \
"$(jq -r '.rebuilt_against.aarch64["dep-a"]' "$root/pkgbuilds/t-multi/.omarchy/package.json")"
echo "An ARM-only run records only the ARM dependency state:"
root=$(selftest_root arm-only)
selftest_package "$root" t-arm 1 '{"source":"local","rebuild_on":["dep-a"]}' 1.0 'x86_64 aarch64'
selftest_pacman "$root"
selftest_published "$root"
selftest_alarm "$root" dep-a=2-1
SELFTEST_ARCHES=aarch64
check "run succeeds" 0 "$(run_case "$root")"
unset SELFTEST_ARCHES
check "pkgrel bumped" 2 "$(pkgrel_of "$root/pkgbuilds/t-arm")"
check "ARM trigger recorded" "2-1" \
"$(jq -r '.rebuilt_against.aarch64["dep-a"]' "$root/pkgbuilds/t-arm/.omarchy/package.json")"
check "x86_64 was not consulted" "false" \
"$(jq -r '.rebuilt_against | has("x86_64")' "$root/pkgbuilds/t-arm/.omarchy/package.json")"
echo "An x86-only package ignores ARM during a dual-architecture run:"
root=$(selftest_root x86-package)
selftest_package "$root" t-x86 1 '{"source":"local","rebuild_on":["dep-a"],"rebuilt_against":{"dep-a":"1-1"}}'
selftest_pacman "$root" dep-a=1-1
selftest_published "$root"
selftest_alarm "$root" dep-a=2-1
SELFTEST_ARCHES="x86_64 aarch64"
check "run succeeds" 0 "$(run_case "$root")"
unset SELFTEST_ARCHES
check "pkgrel untouched" 1 "$(pkgrel_of "$root/pkgbuilds/t-x86")"
echo "" echo ""
if [[ "$failures" -eq 0 ]]; then if [[ "$failures" -eq 0 ]]; then
print_success "Self-test passed" print_success "Self-test passed"
@@ -626,9 +795,9 @@ if [[ "$SELF_TEST" == true ]]; then
exit $? exit $?
fi fi
for tool in pacman vercmp jq curl; do for tool in pacman vercmp jq curl tar; do
if ! command -v "$tool" >/dev/null 2>&1; then if ! command -v "$tool" >/dev/null 2>&1; then
print_error "$tool not found: reading trigger versions and ordering pkgrels both need pacman" print_error "$tool not found: rebuild trigger sync cannot run"
exit 1 exit 1
fi fi
done done
+139 -2
View File
@@ -20,8 +20,8 @@ Update packages that track an upstream vendor release feed instead of the AUR.
Packages opt in declaratively through "upstream" in .omarchy/package.json. Packages opt in declaratively through "upstream" in .omarchy/package.json.
Providers cover GitHub Releases with checksum manifests or API asset digests, Providers cover GitHub Releases with checksum manifests or API asset digests,
semver-shaped git tags whose source URLs can be hashed, and npm dist-tags. See README.md for the semver-shaped git tags, npm dist-tags, and plain Debian Packages indexes. See
schemas. Anything outside those conventions may provide README.md for the schemas. Anything outside those conventions may provide
pkgbuilds/<package>/.omarchy/upstream.sh, a hook that reports JSON on stdout: pkgbuilds/<package>/.omarchy/upstream.sh, a hook that reports JSON on stdout:
{ {
@@ -378,6 +378,7 @@ sync_package() {
github) release=$(github_upstream_release "$package_dir" "$min_age") || release_status=$? ;; github) release=$(github_upstream_release "$package_dir" "$min_age") || release_status=$? ;;
git_tags) release=$(git_tags_upstream_release "$package_dir") || release_status=$? ;; git_tags) release=$(git_tags_upstream_release "$package_dir") || release_status=$? ;;
npm) release=$(npm_upstream_release "$package_dir") || release_status=$? ;; npm) release=$(npm_upstream_release "$package_dir") || release_status=$? ;;
debian) release=$(debian_upstream_release "$package_dir") || release_status=$? ;;
esac esac
if [[ ${release_status:-0} -ne 0 ]]; then if [[ ${release_status:-0} -ne 0 ]]; then
print_error "$provider upstream provider failed for $package" print_error "$provider upstream provider failed for $package"
@@ -544,6 +545,31 @@ EOF
check "bypass lifts the quarantine" "2.0.0" "$(jq -r '.pkgver // "<none>"' <<<"$out")" check "bypass lifts the quarantine" "2.0.0" "$(jq -r '.pkgver // "<none>"' <<<"$out")"
check "x86_64 checksum via * binary-mode prefix" "$sum_x20" "$(jq -r '.sha256sums.x86_64[0] // "<none>"' <<<"$out")" check "x86_64 checksum via * binary-mode prefix" "$sum_x20" "$(jq -r '.sha256sums.x86_64[0] // "<none>"' <<<"$out")"
cp "$pkg/.omarchy/package.json" "$pkg/normal.json"
jq '.upstream.latest_only = true' "$pkg/normal.json" > "$pkg/.omarchy/package.json"
FIXTURE_RELEASES=$(jq -n --arg old "$old2d" '[
{tag_name: "v1.9.0", published_at: $old, draft: false, prerelease: false},
{tag_name: "v0.4.1-8", published_at: $old, draft: false, prerelease: false}
]')
FIXTURE_CHECKSUMS=$(printf '%s\n' \
"$sum_x19 ./tool-v1.9.0-x64.tar.xz" \
"$sum_a19 ./tool-v1.9.0-arm64.tar.xz")
out=$(github_upstream_release "$pkg" 0 2>/dev/null) || out="<error>"
check "latest_only ignores incompatible historical tags" "1.9.0" "$(jq -r '.pkgver // "<none>"' <<<"$out")"
cp "$pkg/normal.json" "$pkg/.omarchy/package.json"
FIXTURE_RELEASES=$(jq -n --arg young "$young" --arg old2 "$old2d" --arg old3 "$old3d" '[
{tag_name: "v2.0.0", published_at: $young, draft: false, prerelease: false},
{tag_name: "v1.9.9", published_at: $old2, draft: false, prerelease: true},
{tag_name: "v1.9.8", published_at: $old2, draft: true, prerelease: false},
{tag_name: "v1.9.0", published_at: $old2, draft: false, prerelease: false},
{tag_name: "v1.8.0", published_at: $old3, draft: false, prerelease: false}
]')
FIXTURE_CHECKSUMS=$(printf '%s\n' \
"$sum_x19 ./tool-v1.9.0-x64.tar.xz" \
"$sum_a19 tool-v1.9.0-arm64.tar.xz" \
"$sum_x20 *tool-v2.0.0-x64.tar.xz" \
"$sum_a20 tool-v2.0.0-arm64.tar.xz")
out=$(github_upstream_release "$pkg" 8640000 2>/dev/null) || out="<error>" out=$(github_upstream_release "$pkg" 8640000 2>/dev/null) || out="<error>"
check "everything quarantined reports no update" "{}" "$(jq -c . <<<"$out")" check "everything quarantined reports no update" "{}" "$(jq -c . <<<"$out")"
@@ -637,6 +663,117 @@ EOF
check "provider rejects a non-boolean digests" "1" "$rc" check "provider rejects a non-boolean digests" "1" "$rc"
cp "$digpkg/good.json" "$digpkg/.omarchy/package.json" cp "$digpkg/good.json" "$digpkg/.omarchy/package.json"
echo "Ordered GitHub assets with supplemental sources:"
local multipkg="$TEMP_DIR/selftest-multi-assets" multi_sum support_sum multi_vst
mkdir -p "$multipkg/.omarchy"
printf 'pkgver=1.0.0\npkgrel=1\n' > "$multipkg/PKGBUILD"
cat > "$multipkg/.omarchy/package.json" <<'EOF'
{
"source": "local",
"upstream": {
"github": "example/tool",
"digests": true,
"assets": {
"x86_64": ["tool-{pkgver}-x86_64", "tool-{pkgver}-x86_64.asc"],
"aarch64": ["tool-{pkgver}-aarch64", "tool-{pkgver}-aarch64.asc"]
},
"sources": {
"any": ["https://example.test/tool/{tag}/support.txt"]
}
}
}
EOF
local sum_x19_sig sum_a19_sig
sum_x19_sig=$(printf '1%.0s' {1..64})
sum_a19_sig=$(printf '2%.0s' {1..64})
FIXTURE_RELEASES=$(jq -n --arg old "$old2d" --arg x "$sum_x19" --arg xs "$sum_x19_sig" \
--arg a "$sum_a19" --arg as "$sum_a19_sig" '[
{tag_name: "v1.9.0", published_at: $old, draft: false, prerelease: false, assets: [
{name: "tool-1.9.0-x86_64", digest: ("sha256:" + $x)},
{name: "tool-1.9.0-x86_64.asc", digest: ("sha256:" + $xs)},
{name: "tool-1.9.0-aarch64", digest: ("sha256:" + $a)},
{name: "tool-1.9.0-aarch64.asc", digest: ("sha256:" + $as)}
]}
]')
upstream_fetch_source() { printf 'remote fixture for %s\n' "$1" > "$2"; }
multi_sum=$(github_upstream_release "$multipkg" 0 2>/dev/null) || multi_sum="<error>"
support_sum=$(printf 'remote fixture for %s\n' 'https://example.test/tool/v1.9.0/support.txt' | sha256sum | cut -d' ' -f1)
check "ordered GitHub assets produce an ordered checksum array" "$sum_x19 $sum_x19_sig" \
"$(jq -r '.sha256sums.x86_64 | join(" ")' <<<"$multi_sum")"
check "GitHub supplemental source is downloaded and hashed" "$support_sum" \
"$(jq -r '.sha256sums.any[0]' <<<"$multi_sum")"
multi_vst=0; validate_package_metadata "$multipkg" >/dev/null || multi_vst=$?
check "GitHub asset lists and disjoint sources validate" "0" "$multi_vst"
jq '.upstream.sources.x86_64 = ["https://example.test/duplicate"]' \
"$multipkg/.omarchy/package.json" > "$multipkg/overlap.json"
cp "$multipkg/.omarchy/package.json" "$multipkg/good.json"
cp "$multipkg/overlap.json" "$multipkg/.omarchy/package.json"
multi_vst=0; validate_package_metadata "$multipkg" >/dev/null || multi_vst=$?
check "GitHub assets and sources cannot target the same checksum array" "1" "$multi_vst"
cp "$multipkg/good.json" "$multipkg/.omarchy/package.json"
echo "Debian Packages provider:"
local debpkg="$TEMP_DIR/selftest-debian" deb_sum deb_x_sum deb_a_sum deb_vst
mkdir -p "$debpkg/.omarchy"
printf 'pkgver=1.0.0\npkgrel=1\nsha256sums_x86_64=("old")\nsha256sums_aarch64=("old")\n' > "$debpkg/PKGBUILD"
cat > "$debpkg/.omarchy/package.json" <<'EOF'
{
"source": "local",
"upstream": {
"debian": "https://packages.example.test/dists/stable/main/binary-amd64/Packages",
"package": "example-app",
"sources": {
"x86_64": ["https://downloads.example.test/app-{pkgver}-x64.tar.gz"],
"aarch64": ["https://downloads.example.test/app-{pkgver}-arm64.tar.gz"]
}
}
}
EOF
debian_fetch_packages() {
cat <<'EOF'
Package: unrelated
Version: 99.0.0
Package: example-app
Version: 1.9.0
Package: example-app
Version: 1.10.0
EOF
}
deb_sum=$(debian_upstream_release "$debpkg")
deb_x_sum=$(printf 'remote fixture for %s\n' 'https://downloads.example.test/app-1.10.0-x64.tar.gz' | sha256sum | cut -d' ' -f1)
deb_a_sum=$(printf 'remote fixture for %s\n' 'https://downloads.example.test/app-1.10.0-arm64.tar.gz' | sha256sum | cut -d' ' -f1)
check "Debian provider selects the newest exact package stanza" "1.10.0" "$(jq -r '.pkgver' <<<"$deb_sum")"
check "Debian x86_64 source is hashed" "$deb_x_sum" "$(jq -r '.sha256sums.x86_64[0]' <<<"$deb_sum")"
check "Debian aarch64 source is hashed" "$deb_a_sum" "$(jq -r '.sha256sums.aarch64[0]' <<<"$deb_sum")"
deb_vst=0; validate_package_metadata "$debpkg" >/dev/null || deb_vst=$?
check "Debian declaration validates" "0" "$deb_vst"
printf 'pkgver=1.10.0\npkgrel=1\nsha256sums_x86_64=("old")\nsha256sums_aarch64=("old")\n' > "$debpkg/PKGBUILD"
check "checked-in Debian version avoids source downloads" "{}" "$(debian_upstream_release "$debpkg" | jq -c .)"
echo "End-to-end Debian sync with the checked-in 1password recipe:"
local one_root="$TEMP_DIR/e2e-1password" one_dir one_version=8.12.35
mkdir -p "$one_root"
cp -a "$BUILD_ROOT/pkgbuilds/1password" "$one_root/1password"
one_dir="$one_root/1password"
debian_fetch_packages() {
printf 'Package: 1password\nVersion: %s\n' "$one_version"
}
upstream_fetch_source() { printf 'remote fixture for %s\n' "$1" > "$2"; }
local one_prev_updated=$UPDATED one_prev_failed=$FAILED
PKGBUILDS_DIR="$one_root" sync_package 1password >/dev/null 2>&1 || true
check "1password sync updates without failures" "updated=1 failed=0" \
"updated=$((UPDATED - one_prev_updated)) failed=$((FAILED - one_prev_failed))"
check "1password pkgver is the single download version source" "$one_version" \
"$(grep -m1 '^pkgver=' "$one_dir/PKGBUILD" | cut -d= -f2-)"
check "1password pkgrel resets to 1" "1" \
"$(grep -m1 '^pkgrel=' "$one_dir/PKGBUILD" | cut -d= -f2-)"
check "1password x86 URL follows the rewritten pkgver" "1password-${one_version}.x64.tar.gz" \
"$(CARCH=x86_64 bash -c 'source "$1"; basename "${source_x86_64[0]}"' _ "$one_dir/PKGBUILD")"
check "1password ARM URL follows the rewritten pkgver" "1password-${one_version}.arm64.tar.gz" \
"$(CARCH=aarch64 bash -c 'source "$1"; basename "${source_aarch64[0]}"' _ "$one_dir/PKGBUILD")"
echo "Quarantine backstop:" echo "Quarantine backstop:"
local rel st local rel st
rel=$(jq -n --arg p "$old2d" '{pkgver: "1.9.0", published_at: $p, sha256sums: {}}') rel=$(jq -n --arg p "$old2d" '{pkgver: "1.9.0", published_at: $p, sha256sums: {}}')
+38 -32
View File
@@ -84,15 +84,18 @@ echo ""
# --- queued work ------------------------------------------------------------- # --- queued work -------------------------------------------------------------
print_info "Queued builds (state files in $STATE_DIR)" print_info "Queued builds (state files in $STATE_DIR; architectures: $(published_arches | tr '\n' ' '))"
queued=false queued=false
for channel in edge rc stable; do for channel in edge rc stable; do
state_file="$STATE_DIR/.sync-needed-$channel" for arch in $(published_arches); do
if [[ -f "$state_file" ]]; then state_file=$(sync_queue_file "$channel" "$arch")
queued=true [[ "$arch" == "x86_64" && ! -f "$state_file" ]] && state_file=$(legacy_sync_queue_file "$channel")
since=$(date -r "$state_file" '+%Y-%m-%d %H:%M:%S' 2>/dev/null || echo "unknown") if [[ -f "$state_file" ]]; then
printf ' • %-7s queued since %s\n' "$channel" "$since" queued=true
fi since=$(date -r "$state_file" '+%Y-%m-%d %H:%M:%S' 2>/dev/null || echo "unknown")
printf ' • %-7s %-8s queued since %s\n' "$channel" "$arch" "$since"
fi
done
done done
[[ "$queued" == false ]] && echo " (nothing queued — all channels up to date)" [[ "$queued" == false ]] && echo " (nothing queued — all channels up to date)"
echo "" echo ""
@@ -101,34 +104,37 @@ echo ""
# say so plainly: it is queued but deliberately not being retried yet. # say so plainly: it is queued but deliberately not being retried yet.
paused=false paused=false
for channel in edge rc stable; do for channel in edge rc stable; do
fail_file="$STATE_DIR/.build-failed-$channel" for arch in $(published_arches); do
[[ -f "$fail_file" ]] || continue fail_file=$(sync_fail_file "$channel" "$arch")
if [[ "$paused" == false ]]; then [[ "$arch" == "x86_64" && ! -f "$fail_file" ]] && fail_file=$(legacy_sync_fail_file "$channel")
print_error "Failing builds (backoff active)" [[ -f "$fail_file" ]] || continue
paused=true if [[ "$paused" == false ]]; then
fi print_error "Failing builds (backoff active)"
FAILURE_COUNT=0 FAILURE_AT=0 FAILURE_FINGERPRINT="" paused=true
# shellcheck disable=SC1090 fi
source "$fail_file" 2>/dev/null || true FAILURE_COUNT=0 FAILURE_AT=0 FAILURE_FINGERPRINT=""
delay=600 # shellcheck disable=SC1090
for ((i = 1; i < FAILURE_COUNT; i++)); do source "$fail_file" 2>/dev/null || true
delay=$((delay * 2)) delay=600
((delay >= 21600)) && { delay=21600; break; } for ((i = 1; i < FAILURE_COUNT; i++)); do
delay=$((delay * 2))
((delay >= 21600)) && { delay=21600; break; }
done
retry_at=$((FAILURE_AT + delay))
now=$(date +%s)
if ((now < retry_at)); then
when="retries at $(date -d "@$retry_at" '+%H:%M:%S' 2>/dev/null || echo "+$((retry_at - now))s")"
else
when="retries on the next tick"
fi
printf ' ✗ %-7s %-8s %s consecutive failure(s), %s\n' "$channel" "$arch" "$FAILURE_COUNT" "$when"
printf ' last attempt %s on commit %s\n' \
"$(date -d "@$FAILURE_AT" '+%Y-%m-%d %H:%M:%S' 2>/dev/null || echo "$FAILURE_AT")" \
"${FAILURE_FINGERPRINT:0:12}"
done done
retry_at=$((FAILURE_AT + delay))
now=$(date +%s)
if ((now < retry_at)); then
when="retries at $(date -d "@$retry_at" '+%H:%M:%S' 2>/dev/null || echo "+$((retry_at - now))s")"
else
when="retries on the next tick"
fi
printf ' ✗ %-7s %s consecutive failure(s), %s\n' "$channel" "$FAILURE_COUNT" "$when"
printf ' last attempt %s on commit %s\n' \
"$(date -d "@$FAILURE_AT" '+%Y-%m-%d %H:%M:%S' 2>/dev/null || echo "$FAILURE_AT")" \
"${FAILURE_FINGERPRINT:0:12}"
done done
if [[ "$paused" == true ]]; then if [[ "$paused" == true ]]; then
echo " Any new commit clears the backoff; or: rm $STATE_DIR/.build-failed-<channel>" echo " Any new commit clears the backoff; or: rm $STATE_DIR/.build-failed-<channel>-<arch>"
echo "" echo ""
fi fi
+16 -12
View File
@@ -11,12 +11,11 @@ source "$BUILD_ROOT/helpers/docker-helpers.sh"
source "$BUILD_ROOT/helpers/paths.sh" source "$BUILD_ROOT/helpers/paths.sh"
source "$BUILD_ROOT/helpers/lock-helpers.sh" source "$BUILD_ROOT/helpers/lock-helpers.sh"
# Function to update repository database using Docker # Function to update repository database using the selected container engine
update_database() { update_database() {
print_info "Updating repository database in Docker container..." print_info "Updating repository database in a container..."
# Check Docker is available check_engine
check_docker
# Ensure output directory exists # Ensure output directory exists
if [[ ! -d "$REPO_DIR" ]]; then if [[ ! -d "$REPO_DIR" ]]; then
@@ -25,20 +24,25 @@ update_database() {
exit 1 exit 1
fi fi
# Make output directory writable for container if [[ "$CONTAINER_ENGINE" == "docker" ]]; then
make_dir_writable "$REPO_DIR" make_dir_writable "$REPO_DIR"
fi
# repo-add is architecture- and mirror-independent, so always use the edge # repo-add is architecture- and mirror-independent. Use the host-native edge
# x86_64 image. This also lets bootstrap-rc build the rc database before the # image, which also lets bootstrap-rc run before that channel exists remotely.
# rc channel exists remotely (an rc image can only build after it does). local tool_arch
build_docker_image "$BUILD_DIR" "x86_64" "edge" tool_arch=$(docker_native_arch) || {
print_error "Unsupported host architecture: $(uname -m)"
exit 1
}
build_docker_image "$BUILD_DIR" "$tool_arch" "edge"
docker run --rm --platform linux/amd64 \ "$CONTAINER_ENGINE" run "${CONTAINER_RUN_ARGS[@]}" --rm "$(get_platform_arg "$tool_arch")" \
-e ARCH="$ARCH" \ -e ARCH="$ARCH" \
-e MIRROR="$MIRROR" \ -e MIRROR="$MIRROR" \
-v "$REPO_ROOT:/output" \ -v "$REPO_ROOT:/output" \
-v "$BUILD_DIR:/build:ro" \ -v "$BUILD_DIR:/build:ro" \
omarchy-pkg-builder:latest-x86_64-edge /build/update-repo.sh "omarchy-pkg-builder:latest-$tool_arch-edge" /build/update-repo.sh
} }
# Main execution # Main execution
+14 -6
View File
@@ -45,9 +45,7 @@ RUN if [ "${TARGETARCH}" = "amd64" ]; then \
printf 'Server = https://mirror.omarchy.org/$repo/os/$arch\n' > /etc/pacman.d/mirrorlist; \ printf 'Server = https://mirror.omarchy.org/$repo/os/$arch\n' > /etc/pacman.d/mirrorlist; \
fi; \ fi; \
else \ else \
curl -L "https://raw.githubusercontent.com/archlinuxarm/PKGBUILDs/master/core/pacman-mirrorlist/mirrorlist" 2>/dev/null | \ printf 'Server = https://fl.us.mirror.archlinuxarm.org/aarch64/$repo\n' > /etc/pacman.d/mirrorlist; \
sed -E 's/^\s*#\s*Server\s*=/Server =/g' > /etc/pacman.d/mirrorlist && \
sed -i 's/\$arch/aarch64/g' /etc/pacman.d/mirrorlist; \
fi fi
# Bootstrap keyrings (required before pacstrap can verify packages) # Bootstrap keyrings (required before pacstrap can verify packages)
@@ -108,11 +106,16 @@ RUN ln -sf /usr/lib/os-release /etc/os-release && \
# Setup Omarchy keyring manually before adding repo (avoids keyserver trust issues) # Setup Omarchy keyring manually before adding repo (avoids keyserver trust issues)
# Note: Repository is removed at the end since build scripts add it dynamically. # Note: Repository is removed at the end since build scripts add it dynamically.
# The keyring comes from this image's own channel (the bare /$arch path is a # The keyring comes from this image's own channel (the bare /$arch path is a
# stale legacy layout); %s keeps pacman's $arch literal while MIRROR expands. # stale legacy layout). It is always taken from the x86_64 tree, whatever the
# image's own architecture: omarchy-keyring is an arch=any package, and the
# x86_64 tree is the one that exists before a new architecture has published
# anything. Bootstrapping from the target's own tree would make the first
# aarch64 build depend on an aarch64 repository that only that build can
# create.
ARG MIRROR=edge ARG MIRROR=edge
RUN pacman-key --recv-keys 40DFB630FF42BCFFB047046CF0134EE680CAC571 --keyserver keys.openpgp.org && \ RUN pacman-key --recv-keys 40DFB630FF42BCFFB047046CF0134EE680CAC571 --keyserver keys.openpgp.org && \
pacman-key --lsign-key 40DFB630FF42BCFFB047046CF0134EE680CAC571 && \ pacman-key --lsign-key 40DFB630FF42BCFFB047046CF0134EE680CAC571 && \
printf '\n[omarchy]\nSigLevel = Optional\nServer = https://pkgs.omarchy.org/%s/$arch\n' "${MIRROR}" >> /etc/pacman.conf && \ printf '\n[omarchy]\nSigLevel = Optional\nServer = https://pkgs.omarchy.org/%s/x86_64\n' "${MIRROR}" >> /etc/pacman.conf && \
pacman -Sy --noconfirm && \ pacman -Sy --noconfirm && \
pacman -S --noconfirm omarchy-keyring && \ pacman -S --noconfirm omarchy-keyring && \
pacman-key --populate omarchy && \ pacman-key --populate omarchy && \
@@ -137,8 +140,13 @@ RUN useradd -m -G wheel -s /bin/bash builder && \
chmod 700 /home/builder/.gnupg && \ chmod 700 /home/builder/.gnupg && \
chown -R builder:builder /home/builder chown -R builder:builder /home/builder
# Arch Linux ARM's makepkg.conf still defaults PKGEXT to .pkg.tar.xz; every
# tool downstream of the build (sign.sh, push-build, sync-rebuilds, the
# notifier) expects .pkg.tar.zst, so an aarch64 package would build and then
# be skipped at signing. Pin the extension so both architectures match.
RUN sed -i 's/^#MAKEFLAGS=.*/MAKEFLAGS="-j$(nproc)"/' /etc/makepkg.conf && \ RUN sed -i 's/^#MAKEFLAGS=.*/MAKEFLAGS="-j$(nproc)"/' /etc/makepkg.conf && \
sed -i 's/^COMPRESSZST=.*/COMPRESSZST=(zstd -c -z -q --threads=0 -)/' /etc/makepkg.conf sed -i 's/^COMPRESSZST=.*/COMPRESSZST=(zstd -c -z -q --threads=0 -)/' /etc/makepkg.conf && \
sed -i "s|^PKGEXT=.*|PKGEXT='.pkg.tar.zst'|" /etc/makepkg.conf
# Pacman wrapper that auto-resolves package conflicts (e.g. rustup vs rust). # Pacman wrapper that auto-resolves package conflicts (e.g. rustup vs rust).
# makepkg only passes --noconfirm to pacman, which defaults to 'N' on conflict # makepkg only passes --noconfirm to pacman, which defaults to 'N' on conflict
+69 -47
View File
@@ -5,6 +5,10 @@
# Setup directories # Setup directories
ARCH=${ARCH:-x86_64} ARCH=${ARCH:-x86_64}
# ARCH selects the repository target for this script, but make and Kbuild also
# interpret an exported ARCH themselves (Linux calls this target "arm64").
# Keep the shell variable local to the orchestrator so PKGBUILDs see CARCH only.
export -n ARCH
MIRROR=${MIRROR:-edge} MIRROR=${MIRROR:-edge}
DRY_RUN=${DRY_RUN:-false} DRY_RUN=${DRY_RUN:-false}
PKGBUILDS_DIR=${PKGBUILDS_DIR:-/pkgbuilds} PKGBUILDS_DIR=${PKGBUILDS_DIR:-/pkgbuilds}
@@ -60,14 +64,11 @@ if [[ "$DRY_RUN" != true ]]; then
# Configure Omarchy repositories for dependency resolution # Configure Omarchy repositories for dependency resolution
echo "==> Configuring Omarchy repositories for dependency resolution..." echo "==> Configuring Omarchy repositories for dependency resolution..."
# Always add omarchy-build repo (for incremental builds) # Always add omarchy-build repo first (for incremental builds). Repository
# Packages in build-output are unsigned, so use SigLevel = Never # order is pacman's priority order, so this must precede the official repos;
sudo tee -a /etc/pacman.conf > /dev/null <<EOF # otherwise pacman can select an older official package with the same name.
# Packages in build-output are unsigned, so use SigLevel = Never.
[omarchy-build] sudo sed -i "/^\[core\]$/i [omarchy-build]\nSigLevel = Never\nServer = file://$BUILD_OUTPUT_DIR\n" /etc/pacman.conf
SigLevel = Never
Server = file://$BUILD_OUTPUT_DIR
EOF
echo " -> omarchy-build (priority 1): $BUILD_OUTPUT_DIR" echo " -> omarchy-build (priority 1): $BUILD_OUTPUT_DIR"
# Initialize empty build database if it doesn't exist # Initialize empty build database if it doesn't exist
@@ -90,12 +91,7 @@ EOF
# Add omarchy repo if it has a database (stable packages) # Add omarchy repo if it has a database (stable packages)
if [[ -f "$FINAL_OUTPUT_DIR/omarchy.db.tar.zst" ]] || [[ -f "$FINAL_OUTPUT_DIR/omarchy.db" ]]; then if [[ -f "$FINAL_OUTPUT_DIR/omarchy.db.tar.zst" ]] || [[ -f "$FINAL_OUTPUT_DIR/omarchy.db" ]]; then
sudo tee -a /etc/pacman.conf > /dev/null <<EOF sudo sed -i "/^\[core\]$/i [omarchy]\nSigLevel = Optional TrustAll\nServer = file://$FINAL_OUTPUT_DIR\n" /etc/pacman.conf
[omarchy]
SigLevel = Optional TrustAll
Server = file://$FINAL_OUTPUT_DIR
EOF
echo " -> omarchy (priority 2): $FINAL_OUTPUT_DIR" echo " -> omarchy (priority 2): $FINAL_OUTPUT_DIR"
fi fi
@@ -186,26 +182,9 @@ get_local_version() {
# Returns 0 (success) if should build, 1 if should skip # Returns 0 (success) if should build, 1 if should skip
should_build_for_arch() { should_build_for_arch() {
local pkg="$1" local pkg="$1"
local current_arch="$ARCH" local pkgdir
local pkgdir=$(find_package_dir "$pkg") pkgdir=$(find_package_dir "$pkg")
local pkgbuild="$pkgdir/PKGBUILD" [[ -n "$pkgdir" ]] && package_supports_arch "$pkgdir" "$ARCH"
[[ ! -f "$pkgbuild" ]] && return 1
# Check PKGBUILD arch=() array
local pkgbuild_archs=$(cd "$pkgdir" && bash -c 'source PKGBUILD 2>/dev/null; echo "${arch[@]}"')
# If arch=('any'), build for all architectures
if [[ "$pkgbuild_archs" == "any" ]]; then
return 0
fi
# Check if current arch is in PKGBUILD arch=()
if echo "$pkgbuild_archs" | grep -qw "$current_arch"; then
return 0 # Build
else
return 1 # Skip
fi
} }
# For VCS packages, makepkg recalculates pkgver() before the build. If the # For VCS packages, makepkg recalculates pkgver() before the build. If the
@@ -353,15 +332,47 @@ build_package() {
# Ensure output directory exists # Ensure output directory exists
mkdir -p "$BUILD_OUTPUT_DIR" mkdir -p "$BUILD_OUTPUT_DIR"
for pkg_file in *.pkg.tar.*; do # Copy only the artifacts makepkg declares as outputs. A PKGBUILD may use
[[ -f "$pkg_file" ]] && cp "$pkg_file" "$BUILD_OUTPUT_DIR/" # another pacman package as a source (schist-bin does); a *.pkg.tar.* glob
# would mistake that source archive for one of our freshly built packages.
local -a package_files=()
mapfile -t package_files < <(makepkg --packagelist)
if [[ ${#package_files[@]} -eq 0 ]]; then
echo " Makepkg produced no package files for $pkg"
FAILED_PACKAGES="$FAILED_PACKAGES $pkg"
return 1
fi
local dependency_pkg_file=""
local -a new_pkgs=()
local pkg_path pkg_file
for pkg_path in "${package_files[@]}"; do
pkg_file=${pkg_path##*/}
if [[ ! -f "$pkg_file" ]]; then
# makepkg predicts an automatic -debug output whenever debug is
# enabled, but data-only packages may contain no symbols and therefore
# legitimately produce no debug archive.
if [[ "$pkg_file" == *-debug-*.pkg.tar.* ]]; then
continue
fi
echo " Expected package file was not produced: $pkg_file"
FAILED_PACKAGES="$FAILED_PACKAGES $pkg"
return 1
fi
cp "$pkg_file" "$BUILD_OUTPUT_DIR/"
new_pkgs+=("$pkg_file")
if [[ "$(bsdtar -xOf "$pkg_file" .PKGINFO 2>/dev/null | sed -n 's/^pkgname = //p')" == "$pkg" ]]; then
dependency_pkg_file="$BUILD_OUTPUT_DIR/$pkg_file"
fi
done done
cd "$BUILD_OUTPUT_DIR" cd "$BUILD_OUTPUT_DIR"
# Find ALL package files (handles split packages) # Add every output from this build, including split packages.
local new_pkgs=($(ls -t ${pkg}-*.pkg.tar.* 2>/dev/null | grep -v '\.sig$' | grep -v 'omarchy-build\.db'))
if [[ ${#new_pkgs[@]} -gt 0 ]]; then if [[ ${#new_pkgs[@]} -gt 0 ]]; then
repo-add omarchy-build.db.tar.zst "${new_pkgs[@]}" >/dev/null 2>&1 repo-add omarchy-build.db.tar.zst "${new_pkgs[@]}" >/dev/null 2>&1
ln -sf omarchy-build.db.tar.zst omarchy-build.db ln -sf omarchy-build.db.tar.zst omarchy-build.db
@@ -370,6 +381,24 @@ build_package() {
cd /src/$pkg cd /src/$pkg
# A lower-priority official repository may contain an older package with
# the same name. Install the exact artifact we just built before building
# its consumers, so pacman cannot select that older provider instead.
if [[ "${INSTALL_PACKAGES[$pkg]:-}" == "1" ]]; then
if [[ -z "$dependency_pkg_file" ]]; then
echo " Could not find the built $pkg package to install as a dependency"
FAILED_PACKAGES="$FAILED_PACKAGES $pkg"
return 1
fi
echo " Installing freshly built $pkg for dependent packages..."
if ! sudo /usr/local/bin/pacman-for-makepkg -U --needed --noconfirm "$dependency_pkg_file"; then
echo " Failed to install freshly built dependency $pkg"
FAILED_PACKAGES="$FAILED_PACKAGES $pkg"
return 1
fi
fi
echo " Successfully built $pkg" echo " Successfully built $pkg"
SUCCESSFUL_PACKAGES="$SUCCESSFUL_PACKAGES $pkg" SUCCESSFUL_PACKAGES="$SUCCESSFUL_PACKAGES $pkg"
return 0 return 0
@@ -488,7 +517,7 @@ check_needs_build() {
# Collect packages that should be built for the selected mirror # Collect packages that should be built for the selected mirror
collect_packages() { collect_packages() {
packages_for_unscoped_build "$MIRROR" packages_for_unscoped_build "$MIRROR" "$ARCH"
} }
# Main execution # Main execution
@@ -540,13 +569,6 @@ if [[ -n "$PACKAGES" ]]; then
else else
# Build all packages that need updates from the relevant directories # Build all packages that need updates from the relevant directories
while IFS= read -r pkg; do while IFS= read -r pkg; do
# Check if package should be built for this architecture
if ! should_build_for_arch "$pkg"; then
echo " - $pkg - not built for $ARCH"
SKIPPED_PACKAGES="$SKIPPED_PACKAGES $pkg"
continue
fi
if check_needs_build "$pkg"; then if check_needs_build "$pkg"; then
PACKAGES_TO_BUILD+=("$pkg") PACKAGES_TO_BUILD+=("$pkg")
else else
+108 -22
View File
@@ -1,25 +1,102 @@
# Docker helper functions for Omarchy package build system # Container engine helpers for Omarchy package build system
check_docker() { container_engine_supported() {
if ! command -v docker &>/dev/null; then [[ "$CONTAINER_ENGINE" == "docker" || "$CONTAINER_ENGINE" == "podman" ]]
print_error "Docker is not installed" }
if [[ -z "${CONTAINER_ENGINE:-}" ]]; then
for engine in docker podman; do
if command -v "$engine" >/dev/null 2>&1 && "$engine" info >/dev/null 2>&1; then
CONTAINER_ENGINE="$engine"
break
fi
done
fi
export CONTAINER_ENGINE
# Rootless Podman otherwise maps the image's builder uid 1000 to a subordinate
# host uid. keep-id makes files written through bind mounts belong to the user
# who invoked the build.
CONTAINER_RUN_ARGS=()
if [[ "$CONTAINER_ENGINE" == "podman" ]]; then
CONTAINER_RUN_ARGS+=("--userns=keep-id:uid=1000,gid=1000")
fi
check_engine() {
if [[ -n "$CONTAINER_ENGINE" ]] && ! container_engine_supported; then
print_error "Unsupported CONTAINER_ENGINE: $CONTAINER_ENGINE (use docker or podman)"
exit 1 exit 1
fi fi
if ! docker info &>/dev/null; then if [[ -z "$CONTAINER_ENGINE" ]]; then
print_error "Docker daemon is not running" print_error "No working container engine found (tried Docker, then Podman)"
print_warning "Start Docker with: sudo systemctl start docker" if command -v docker >/dev/null 2>&1; then
print_warning "Docker is installed but unavailable. Start it with: sudo systemctl start docker"
elif command -v podman >/dev/null 2>&1; then
print_warning "Podman is installed but 'podman info' failed"
else
print_warning "Install Docker or Podman"
fi
exit 1
fi
if ! command -v "$CONTAINER_ENGINE" >/dev/null 2>&1; then
print_error "$CONTAINER_ENGINE is not installed"
exit 1
fi
if ! "$CONTAINER_ENGINE" info >/dev/null 2>&1; then
if [[ "$CONTAINER_ENGINE" == "docker" ]]; then
print_error "Docker daemon is not running or is not accessible"
print_warning "Start Docker with: sudo systemctl start docker"
else
print_error "Podman is not available to the current user"
fi
exit 1 exit 1
fi fi
} }
docker_native_arch() {
case "$(uname -m)" in
x86_64) echo x86_64 ;;
aarch64 | arm64) echo aarch64 ;;
*) return 1 ;;
esac
}
setup_qemu() { setup_qemu() {
# Setup QEMU for building ARM64 packages on x86_64 hosts local target_arch="${1:-aarch64}"
if ! docker run --rm --privileged multiarch/qemu-user-static --reset -p yes --credential yes >/dev/null 2>&1; then
print_error "Failed to setup QEMU for ARM64 emulation" if [[ "$CONTAINER_ENGINE" == "podman" ]]; then
local registration="/proc/sys/fs/binfmt_misc/qemu-$target_arch"
local packaged_registration="/usr/lib/binfmt.d/qemu-$target_arch-static.conf"
local flags=""
if [[ -r "$registration" ]]; then
flags=$(sed -n 's/^flags: //p' "$registration")
fi
if [[ "$flags" == *F* && "$flags" == *C* ]]; then
print_success "QEMU $target_arch emulation is registered"
return 0
fi
print_error "Rootless Podman requires QEMU binfmt registration with the F and C flags"
print_info "F keeps the emulator available inside containers; C lets container sudo preserve credentials."
print_info "Configure it once with:"
echo " sudo pacman -S --needed qemu-user-static qemu-user-static-binfmt"
echo " sudo mkdir -p /etc/binfmt.d"
echo " sed 's/:FP$/:FPC/' $packaged_registration | sudo tee /etc/binfmt.d/qemu-$target_arch-static.conf >/dev/null"
echo " sudo systemctl restart systemd-binfmt"
exit 1 exit 1
fi fi
print_success "QEMU ARM64 emulation enabled"
# Register emulators for builds whose target differs from the host.
if ! "$CONTAINER_ENGINE" run --rm --privileged docker.io/multiarch/qemu-user-static --reset -p yes --credential yes >/dev/null 2>&1; then
print_error "Failed to set up QEMU emulation"
exit 1
fi
print_success "QEMU emulation enabled"
} }
build_docker_image() { build_docker_image() {
@@ -39,23 +116,32 @@ build_docker_image() {
;; ;;
esac esac
print_info "Building Docker image for $arch ($platform) using $mirror mirror..." print_info "Building container image for $arch ($platform) using $mirror mirror..."
docker buildx build \ if [[ "$CONTAINER_ENGINE" == "docker" ]]; then
--platform "$platform" \ "$CONTAINER_ENGINE" buildx build \
--build-arg MIRROR="$mirror" \ --platform "$platform" \
--load \ --build-arg MIRROR="$mirror" \
-t "$image_tag" \ --load \
-f "$build_dir/Dockerfile" \ -t "$image_tag" \
"$build_dir" -f "$build_dir/Dockerfile" \
"$build_dir"
else
"$CONTAINER_ENGINE" build \
--platform "$platform" \
--build-arg MIRROR="$mirror" \
-t "$image_tag" \
-f "$build_dir/Dockerfile" \
"$build_dir"
fi
} }
get_platform_arg() { get_platform_arg() {
local arch="$1" local arch="$1"
case "$arch" in case "$arch" in
x86_64) echo "--platform linux/amd64" ;; x86_64) echo "--platform=linux/amd64" ;;
aarch64) echo "--platform linux/arm64" ;; aarch64) echo "--platform=linux/arm64" ;;
*) echo "" ;; *) return 1 ;;
esac esac
} }
+71 -11
View File
@@ -15,10 +15,11 @@
# { "source": "local", "channels": ["edge"] } # { "source": "local", "channels": ["edge"] }
# { "source": "local", "channels": ["edge", "rc", "stable"] } # { "source": "local", "channels": ["edge", "rc", "stable"] }
# { "source": "local", "min_release_age": "24h" } # { "source": "local", "min_release_age": "24h" }
# { "source": "local", "upstream": { "github": "owner/repo", "checksums": "SHASUMS256.txt", "assets": { "x86_64": "name-{tag}-x64.tar.xz" } } } # { "source": "local", "upstream": { "github": "owner/repo", "checksums": "SHASUMS256.txt", "assets": { "x86_64": ["name-{tag}-x64.tar.xz"] } } }
# { "source": "local", "upstream": { "github": "owner/repo", "digests": true, "assets": { "x86_64": "name-{tag}-x64.tar.xz" } } } # { "source": "local", "upstream": { "github": "owner/repo", "digests": true, "assets": { "x86_64": "name-{tag}-x64.tar.xz" } } }
# { "source": "local", "upstream": { "git_tags": "https://example/repo.git", "tag_pattern": "v{pkgver}", "sources": { "any": ["https://example/archive/{tag}.tar.gz"] } } } # { "source": "local", "upstream": { "git_tags": "https://example/repo.git", "tag_pattern": "v{pkgver}", "sources": { "any": ["https://example/archive/{tag}.tar.gz"] } } }
# { "source": "local", "upstream": { "npm": "@scope/package", "sources": { "any": ["{npm_tarball}"] } } } # { "source": "local", "upstream": { "npm": "@scope/package", "sources": { "any": ["{npm_tarball}"] } } }
# { "source": "local", "upstream": { "debian": "https://example/debian/dists/stable/main/binary-amd64/Packages", "package": "example", "sources": { "any": ["https://example/releases/{pkgver}.tar.gz"] } } }
# #
# bin/sync-aur also writes upstream_commit for AUR-backed packages, and # bin/sync-aur also writes upstream_commit for AUR-backed packages, and
# bin/sync-rebuilds writes rebuilt_against for packages declaring rebuild_on. # bin/sync-rebuilds writes rebuilt_against for packages declaring rebuild_on.
@@ -143,6 +144,31 @@ package_has_pkgbuild() {
[[ -f "$pkgdir/PKGBUILD" ]] [[ -f "$pkgdir/PKGBUILD" ]]
} }
# The architectures declared by a PKGBUILD. Set CARCH while reading it so a
# conditional arch=() assignment is evaluated for the architecture we are
# actually checking, even when the repository host is a different one.
package_arches() {
local pkgdir="$1"
local arch="${2:-${ARCH:-x86_64}}"
(cd "$pkgdir" && env -u OMARCHY_SRC CARCH="$arch" bash -c '
source PKGBUILD >/dev/null 2>&1
printf "%s\n" "${arch[*]}"
')
}
package_supports_arch() {
local pkgdir="$1"
local target="${2:-${ARCH:-x86_64}}"
local arches
arches=$(package_arches "$pkgdir" "$target") || return 1
case " $arches " in
*" any "* | *" $target "*) return 0 ;;
*) return 1 ;;
esac
}
# Channel membership: where a package may be published. Packages without a # Channel membership: where a package may be published. Packages without a
# `channels` key are members of every channel (they flow edge -> rc -> stable). # `channels` key are members of every channel (they flow edge -> rc -> stable).
package_has_channels() { package_has_channels() {
@@ -301,9 +327,12 @@ packages_for_mirror() {
packages_for_unscoped_build() { packages_for_unscoped_build() {
local mirror="$1" local mirror="$1"
local arch="${2:-${ARCH:-x86_64}}"
package_dirs | while IFS= read -r pkgdir; do package_dirs | while IFS= read -r pkgdir; do
if package_builds_for_mirror "$pkgdir" "$mirror" && ! package_build_skipped "$pkgdir"; then if package_builds_for_mirror "$pkgdir" "$mirror" &&
! package_build_skipped "$pkgdir" &&
package_supports_arch "$pkgdir" "$arch"; then
basename "$pkgdir" basename "$pkgdir"
fi fi
done done
@@ -454,30 +483,45 @@ validate_package_metadata() {
(.key | test("\\A[a-z0-9_]+\\z")) (.key | test("\\A[a-z0-9_]+\\z"))
and (.value | type == "array" and length > 0 and all(type == "string" and length > 0)) and (.value | type == "array" and length > 0 and all(type == "string" and length > 0))
)); ));
def valid_assets:
type == "object" and length > 0 and (to_entries | all(
(.key | test("\\A[a-z0-9_]+\\z"))
and (.value |
(type == "string" and length > 0)
or (type == "array" and length > 0 and all(type == "string" and length > 0) and (unique | length) == length)
)
));
if has("upstream") | not then true if has("upstream") | not then true
elif (.upstream | type) != "object" then false elif (.upstream | type) != "object" then false
else .upstream | else .upstream |
([has("github"), has("git_tags"), has("npm")] | map(select(.)) | length) == 1 ([has("github"), has("git_tags"), has("npm"), has("debian")] | map(select(.)) | length) == 1
and if has("github") then and if has("github") then
(.github | type == "string" and test("\\A[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+\\z")) (.github | type == "string" and test("\\A[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+\\z"))
and (if has("checksums") then (.checksums | type == "string" and length > 0) else true end) and (if has("checksums") then (.checksums | type == "string" and length > 0) else true end)
and (if has("digests") then (.digests | type == "boolean") else true end) and (if has("digests") then (.digests | type == "boolean") else true end)
and (if has("latest_only") then (.latest_only | type == "boolean") else true end)
and (has("checksums") != (has("digests") and .digests == true)) and (has("checksums") != (has("digests") and .digests == true))
and (.assets | type == "object" and length > 0 and (to_entries | all( and (.assets | valid_assets)
(.key | test("\\A[a-z0-9_]+\\z")) and (.value | type == "string" and length > 0) and (if has("sources") then
))) (.sources | valid_sources)
and ((.assets | keys) as $assets | (.sources | keys) as $sources | ($assets - $sources | length) == ($assets | length))
else true end)
elif has("git_tags") then elif has("git_tags") then
(.git_tags | type == "string" and test("\\Ahttps://[^[:space:]]+\\.git\\z")) (.git_tags | type == "string" and test("\\Ahttps://[^[:space:]]+\\.git\\z"))
and (.tag_pattern | type == "string" and (split("{pkgver}") | length) == 2) and (.tag_pattern | type == "string" and (split("{pkgver}") | length) == 2)
and (.sources | valid_sources) and (.sources | valid_sources)
else elif has("npm") then
(.npm | type == "string" and test("\\A(@[a-z0-9_.-]+/)?[a-z0-9_.-]+\\z")) (.npm | type == "string" and test("\\A(@[a-z0-9_.-]+/)?[a-z0-9_.-]+\\z"))
and ((.dist_tag // "latest") | type == "string" and test("\\A[a-z0-9_.-]+\\z")) and ((.dist_tag // "latest") | type == "string" and test("\\A[a-z0-9_.-]+\\z"))
and (.sources | valid_sources) and (.sources | valid_sources)
else
(.debian | type == "string" and test("\\Ahttps://[^[:space:]]+\\z"))
and (.package | type == "string" and test("\\A[a-z0-9][a-z0-9+.-]*\\z"))
and (.sources | valid_sources)
end end
end end
' "$metadata" >/dev/null; then ' "$metadata" >/dev/null; then
echo "invalid upstream for $(basename "$pkgdir"): configure exactly one valid github, git_tags, or npm provider" echo "invalid upstream for $(basename "$pkgdir"): configure exactly one valid github, git_tags, npm, or debian provider"
return 1 return 1
fi fi
@@ -502,12 +546,28 @@ validate_package_metadata() {
return 1 return 1
fi fi
if ! jq -e '(.rebuilt_against // {}) | type == "object" and (to_entries | all(.value | type == "string" and length > 0))' "$metadata" >/dev/null; then if ! jq -e '
echo "invalid rebuilt_against for $(basename "$pkgdir"): must be an object mapping package names to versions" def version_map:
type == "object" and (to_entries | all(.value | type == "string" and length > 0));
(.rebuilt_against // {}) as $record |
($record | version_map) or
(($record | type) == "object"
and ((($record | keys) - ["x86_64", "aarch64"]) | length == 0)
and ($record | to_entries | all(.value | version_map)))
' "$metadata" >/dev/null; then
echo "invalid rebuilt_against for $(basename "$pkgdir"): must map architectures to package-version maps"
return 1 return 1
fi fi
if ! jq -e '((.rebuilt_against // {}) | keys) - (.rebuild_on // []) | length == 0' "$metadata" >/dev/null; then if ! jq -e '
(.rebuild_on // []) as $triggers |
(.rebuilt_against // {}) as $record |
if ($record | to_entries | all(.value | type == "string")) then
((($record | keys) - $triggers) | length == 0)
else
($record | to_entries | all((((.value | keys) - $triggers) | length) == 0))
end
' "$metadata" >/dev/null; then
echo "invalid rebuilt_against for $(basename "$pkgdir"): records a package that rebuild_on does not name" echo "invalid rebuilt_against for $(basename "$pkgdir"): records a package that rebuild_on does not name"
return 1 return 1
fi fi
+56
View File
@@ -5,6 +5,62 @@
ARCH=${ARCH:-x86_64} ARCH=${ARCH:-x86_64}
MIRROR=${MIRROR:-edge} MIRROR=${MIRROR:-edge}
# Architectures the tooling knows how to build.
VALID_ARCHES="x86_64 aarch64"
# Architectures this repository PUBLISHES. The scheduled pipeline (version
# check, auto-release, channel advance with --arch all) runs once per entry,
# in this order; the first entry is the reference architecture that the
# release train observes channels through. Adding an architecture here is the
# enablement step: the next check-versions tick queues its packages and the
# next auto-release tick builds them. OMARCHY_ARCHES overrides it for a
# one-off run.
PUBLISHED_ARCHES="${OMARCHY_ARCHES:-x86_64}"
validate_arch() {
case " $VALID_ARCHES " in
*" $1 "*) return 0 ;;
*) return 1 ;;
esac
}
require_valid_arch() {
if ! validate_arch "$1"; then
echo "Invalid architecture: $1 (must be one of: $VALID_ARCHES)" >&2
exit 1
fi
}
published_arches() {
local arch
for arch in $PUBLISHED_ARCHES; do
require_valid_arch "$arch"
echo "$arch"
done
}
reference_arch() {
published_arches | head -1
}
# Scheduled-pipeline state, one file per channel and architecture, so one
# architecture's queue or backoff never gates another's.
STATE_DIR="${OMARCHY_STATE_DIR:-/root/.state}"
sync_queue_file() { # sync_queue_file <mirror> <arch>
echo "$STATE_DIR/.sync-needed-$1-$2"
}
sync_fail_file() { # sync_fail_file <mirror> <arch>
echo "$STATE_DIR/.build-failed-$1-$2"
}
# The pre-architecture names, .sync-needed-<mirror> and .build-failed-<mirror>,
# meant x86_64. A host upgraded mid-cycle may still hold one; readers treat
# it as the x86_64 file until it is consumed.
legacy_sync_queue_file() { echo "$STATE_DIR/.sync-needed-$1"; }
legacy_sync_fail_file() { echo "$STATE_DIR/.build-failed-$1"; }
# Valid package channels, in pipeline order: packages move edge -> rc -> stable # Valid package channels, in pipeline order: packages move edge -> rc -> stable
VALID_MIRRORS="edge rc stable" VALID_MIRRORS="edge rc stable"
+142 -32
View File
@@ -20,8 +20,8 @@
# "v", which is stripped for pkgver. Drafts and prereleases are ignored. The # "v", which is stripped for pkgver. Drafts and prereleases are ignored. The
# provider emits the same JSON contract as an upstream.sh hook, so # provider emits the same JSON contract as an upstream.sh hook, so
# bin/sync-upstream's validation and min_release_age backstop apply # bin/sync-upstream's validation and min_release_age backstop apply
# unchanged. Git-tag and npm providers below cover projects without GitHub # unchanged. Git-tag, npm, and Debian Packages providers below cover projects
# releases; a feed that fits no convention keeps a bespoke hook. # without GitHub releases; a feed that fits no convention keeps a bespoke hook.
# Return the single declarative provider selected by a package. An empty # Return the single declarative provider selected by a package. An empty
# result means either no provider or an invalid/ambiguous declaration; the # result means either no provider or an invalid/ambiguous declaration; the
@@ -31,7 +31,7 @@ package_upstream_provider() {
metadata=$(metadata_file_for_dir "$pkgdir") metadata=$(metadata_file_for_dir "$pkgdir")
jq -r ' jq -r '
(.upstream? | objects) as $u (.upstream? | objects) as $u
| [$u | keys[] | select(. == "github" or . == "git_tags" or . == "npm")] | [$u | keys[] | select(. == "github" or . == "git_tags" or . == "npm" or . == "debian")]
| if length == 1 then .[0] else "" end | if length == 1 then .[0] else "" end
' "$metadata" ' "$metadata"
} }
@@ -64,6 +64,11 @@ npm_fetch_metadata() {
curl -fsSL "https://registry.npmjs.org/$encoded" curl -fsSL "https://registry.npmjs.org/$encoded"
} }
debian_fetch_packages() {
local url="$1"
curl --proto '=https' --proto-redir '=https' -fsSL "$url"
}
upstream_fetch_source() { upstream_fetch_source() {
local url="$1" output="$2" local url="$1" output="$2"
curl --proto '=https' --proto-redir '=https' -fsSL -o "$output" "$url" curl --proto '=https' --proto-redir '=https' -fsSL -o "$output" "$url"
@@ -198,6 +203,71 @@ npm_upstream_release() {
printf '%s\n' "$release" printf '%s\n' "$release"
} }
# Discover a package version from a plain-text Debian Packages index, then
# hash the declared immutable sources. This intentionally supports only
# versions that are already valid Arch pkgver values; feeds needing Debian
# epoch/revision translation keep a package-specific hook.
debian_upstream_release() {
local package_dir="$1" metadata index_url package packages
metadata=$(metadata_file_for_dir "$package_dir")
index_url=$(jq -r '.upstream.debian // ""' "$metadata")
package=$(jq -r '.upstream.package // ""' "$metadata")
if [[ ! "$index_url" =~ ^https://[^[:space:]]+$ || ! "$package" =~ ^[a-z0-9][a-z0-9+.-]*$ ]]; then
echo "invalid Debian Packages provider configuration" >&2
return 1
fi
if ! jq -e '
.upstream.sources | type == "object" and length > 0 and (to_entries | all(
(.key | test("\\A[a-z0-9_]+\\z"))
and (.value | type == "array" and length > 0 and all(type == "string" and length > 0))
))
' "$metadata" >/dev/null; then
echo "invalid Debian Packages source mapping" >&2
return 1
fi
if ! packages=$(debian_fetch_packages "$index_url"); then
echo "could not fetch Debian Packages index: $index_url" >&2
return 1
fi
packages=${packages//$'\r'/}
local best_pkgver="" candidate
while IFS= read -r candidate; do
if [[ ! "$candidate" =~ ^[A-Za-z0-9][A-Za-z0-9._+]*$ ]]; then
echo "$package has an unusable Debian version: ${candidate:-<empty>}" >&2
return 1
fi
if [[ -z "$best_pkgver" || $(vercmp "$candidate" "$best_pkgver") -gt 0 ]]; then
best_pkgver="$candidate"
fi
done < <(awk -v target="$package" '
BEGIN { RS = ""; FS = "\n" }
{
name = version = ""
for (i = 1; i <= NF; i++) {
if ($i ~ /^Package: /) name = substr($i, 10)
if ($i ~ /^Version: /) version = substr($i, 10)
}
if (name == target) print version
}
' <<<"$packages")
[[ -n "$best_pkgver" ]] || {
echo "no usable $package release found in $index_url" >&2
return 1
}
local current_pkgver
current_pkgver=$(grep -m1 '^pkgver=' "$package_dir/PKGBUILD" | cut -d= -f2- | tr -d "\"'")
if [[ $(vercmp "$best_pkgver" "$current_pkgver") -le 0 ]]; then
echo '{}'
return 0
fi
upstream_hash_sources "$package_dir" "$best_pkgver" "$best_pkgver"
}
# Emits the newest qualifying release as hook-contract JSON. min_release_age # Emits the newest qualifying release as hook-contract JSON. min_release_age
# is honored during selection (newest release older than the window wins, # is honored during selection (newest release older than the window wins,
# even when a younger one exists) and BYPASS_MIN_RELEASE_AGE=1 lifts it. # even when a younger one exists) and BYPASS_MIN_RELEASE_AGE=1 lifts it.
@@ -206,7 +276,7 @@ npm_upstream_release() {
# not silently choose from. # not silently choose from.
github_upstream_release() { github_upstream_release() {
local package_dir="$1" min_age="${2:-0}" local package_dir="$1" min_age="${2:-0}"
local metadata repo checksums_name use_digests local metadata repo checksums_name use_digests latest_only
metadata=$(metadata_file_for_dir "$package_dir") metadata=$(metadata_file_for_dir "$package_dir")
repo=$(jq -r '(.upstream? | objects | .github) // ""' "$metadata") repo=$(jq -r '(.upstream? | objects | .github) // ""' "$metadata")
@@ -218,10 +288,15 @@ github_upstream_release() {
# reaches this provider without running the validator first. # reaches this provider without running the validator first.
checksums_name=$(jq -r '(.upstream? | objects | .checksums) | strings' "$metadata") checksums_name=$(jq -r '(.upstream? | objects | .checksums) | strings' "$metadata")
use_digests=$(jq -r '(.upstream? | objects | .digests) | if . == null then "false" elif type == "boolean" then tostring else "invalid" end' "$metadata") use_digests=$(jq -r '(.upstream? | objects | .digests) | if . == null then "false" elif type == "boolean" then tostring else "invalid" end' "$metadata")
latest_only=$(jq -r '(.upstream? | objects | .latest_only) | if . == null then "false" elif type == "boolean" then tostring else "invalid" end' "$metadata")
if [[ "$use_digests" == "invalid" ]]; then if [[ "$use_digests" == "invalid" ]]; then
echo "upstream.digests must be true or false" >&2 echo "upstream.digests must be true or false" >&2
return 1 return 1
fi fi
if [[ "$latest_only" == "invalid" ]]; then
echo "upstream.latest_only must be true or false" >&2
return 1
fi
if [[ -n "$checksums_name" && "$use_digests" == "true" ]]; then if [[ -n "$checksums_name" && "$use_digests" == "true" ]]; then
echo "upstream sets both checksums and digests; keep exactly one" >&2 echo "upstream sets both checksums and digests; keep exactly one" >&2
return 1 return 1
@@ -230,6 +305,27 @@ github_upstream_release() {
echo "upstream needs either checksums (a manifest asset name) or digests: true" >&2 echo "upstream needs either checksums (a manifest asset name) or digests: true" >&2
return 1 return 1
fi fi
if ! jq -e '
def valid_sources:
type == "object" and length > 0 and (to_entries | all(
(.key | test("\\A[a-z0-9_]+\\z"))
and (.value | type == "array" and length > 0 and all(type == "string" and length > 0))
));
(.upstream.assets | type == "object" and length > 0 and (to_entries | all(
(.key | test("\\A[a-z0-9_]+\\z"))
and (.value |
(type == "string" and length > 0)
or (type == "array" and length > 0 and all(type == "string" and length > 0) and (unique | length) == length)
)
)))
and (if .upstream | has("sources") then
(.upstream.sources | valid_sources)
and ((.upstream.assets | keys) as $assets | (.upstream.sources | keys) as $sources | ($assets - $sources | length) == ($assets | length))
else true end)
' "$metadata" >/dev/null; then
echo "invalid upstream.assets or upstream.sources mapping" >&2
return 1
fi
local arches local arches
mapfile -t arches < <(jq -r '(.upstream? | objects | .assets) // {} | keys[]' "$metadata") mapfile -t arches < <(jq -r '(.upstream? | objects | .assets) // {} | keys[]' "$metadata")
if [[ ${#arches[@]} -eq 0 ]]; then if [[ ${#arches[@]} -eq 0 ]]; then
@@ -243,6 +339,9 @@ github_upstream_release() {
echo "could not fetch the release feed for $repo" >&2 echo "could not fetch the release feed for $repo" >&2
return 1 return 1
fi fi
if [[ "$latest_only" == "true" ]]; then
releases=$(jq '[.[] | select((.draft or .prerelease) | not)][0:1]' <<<"$releases")
fi
local candidates=0 best_tag="" best_pkgver="" best_published_at="" local candidates=0 best_tag="" best_pkgver="" best_published_at=""
local tag published_at pkgver published_epoch local tag published_at pkgver published_epoch
@@ -302,39 +401,50 @@ github_upstream_release() {
return 1 return 1
fi fi
local jq_args=(--arg pkgver "$best_pkgver" --arg published_at "$best_published_at") local result
local jq_filter='{pkgver: $pkgver, published_at: $published_at, sha256sums: {}}' result=$(jq -n --arg pkgver "$best_pkgver" --arg published_at "$best_published_at" \
local arch template filename checksum checksum_source '{pkgver: $pkgver, published_at: $published_at, sha256sums: {}}')
local arch template filename checksum checksum_source sums
for arch in "${arches[@]}"; do for arch in "${arches[@]}"; do
if [[ ! "$arch" =~ ^[a-z0-9_]+$ ]]; then if [[ ! "$arch" =~ ^[a-z0-9_]+$ ]]; then
echo "invalid architecture key in upstream.assets: '$arch'" >&2 echo "invalid architecture key in upstream.assets: '$arch'" >&2
return 1 return 1
fi fi
template=$(jq -r --arg arch "$arch" '.upstream.assets[$arch]' "$metadata") sums='[]'
filename=${template//\{pkgver\}/$best_pkgver} while IFS= read -r template; do
filename=${filename//\{tag\}/$best_tag} filename=${template//\{pkgver\}/$best_pkgver}
if [[ "$use_digests" == "true" ]]; then filename=${filename//\{tag\}/$best_tag}
# Only a "sha256:<hex>" digest is stripped to its hex; any other shape if [[ "$use_digests" == "true" ]]; then
# falls through empty and fails the check below. # Only a "sha256:<hex>" digest is stripped to its hex; any other shape
checksum=$(jq -r --arg tag "$best_tag" --arg name "$filename" ' # falls through empty and fails the check below.
first(.[] | select(.tag_name == $tag)) | (.assets // [])[] checksum=$(jq -r --arg tag "$best_tag" --arg name "$filename" '
| select(.name == $name) | (.digest // "") first(.[] | select(.tag_name == $tag)) | (.assets // [])[]
| if type == "string" and test("\\Asha256:[0-9a-f]{64}\\z") then ltrimstr("sha256:") else "" end | select(.name == $name) | (.digest // "")
' <<<"$releases") | if type == "string" and test("\\Asha256:[0-9a-f]{64}\\z") then ltrimstr("sha256:") else "" end
checksum_source="the release API digest" ' <<<"$releases")
else checksum_source="the release API digest"
# Manifest lines are "<sha256> <name>", with the name sometimes prefixed else
# "./" (sha256sum of a local path) or "*" (binary-mode marker). # Manifest lines are "<sha256> <name>", with the name sometimes prefixed
checksum=$(awk -v f="$filename" '$2 == f || $2 == "./" f || $2 == "*" f { print $1; exit }' <<<"$checksums") # "./" (sha256sum of a local path) or "*" (binary-mode marker).
checksum_source="$checksums_name" checksum=$(awk -v f="$filename" '$2 == f || $2 == "./" f || $2 == "*" f { print $1; exit }' <<<"$checksums")
fi checksum_source="$checksums_name"
if [[ ! "$checksum" =~ ^[0-9a-f]{64}$ ]]; then fi
echo "no valid checksum for $filename in $repo $best_tag $checksum_source" >&2 if [[ ! "$checksum" =~ ^[0-9a-f]{64}$ ]]; then
return 1 echo "no valid checksum for $filename in $repo $best_tag $checksum_source" >&2
fi return 1
jq_args+=(--arg "sum_$arch" "$checksum") fi
jq_filter+=" | .sha256sums[\"$arch\"] = [\$sum_$arch]" sums=$(jq -c --arg checksum "$checksum" '. + [$checksum]' <<<"$sums")
done < <(jq -r --arg arch "$arch" '
.upstream.assets[$arch] | if type == "array" then .[] else . end
' "$metadata")
result=$(jq -c --arg arch "$arch" --argjson sums "$sums" '.sha256sums[$arch] = $sums' <<<"$result")
done done
jq -n "${jq_args[@]}" "$jq_filter" if jq -e '.upstream | has("sources")' "$metadata" >/dev/null; then
local source_release
source_release=$(upstream_hash_sources "$package_dir" "$best_pkgver" "$best_tag") || return 1
result=$(jq -c --argjson source "$source_release" '.sha256sums += $source.sha256sums' <<<"$result")
fi
printf '%s\n' "$result"
} }
+15 -2
View File
@@ -1,5 +1,18 @@
{ {
"source": "aur", "source": "local",
"release_ring": "fast", "release_ring": "fast",
"upstream_commit": "e323d0d1f8dea6b75bb651ce14acc73904cd0326" "upstream": {
"debian": "https://downloads.1password.com/linux/debian/amd64/dists/stable/main/binary-amd64/Packages",
"package": "1password",
"sources": {
"x86_64": [
"https://downloads.1password.com/linux/tar/stable/x86_64/1password-{pkgver}.x64.tar.gz",
"https://downloads.1password.com/linux/tar/stable/x86_64/1password-{pkgver}.x64.tar.gz.sig"
],
"aarch64": [
"https://downloads.1password.com/linux/tar/stable/aarch64/1password-{pkgver}.arm64.tar.gz",
"https://downloads.1password.com/linux/tar/stable/aarch64/1password-{pkgver}.arm64.tar.gz.sig"
]
}
}
} }
+24 -10
View File
@@ -1,19 +1,33 @@
pkgname=1password pkgname=1password
_tarver=8.12.34 case "$CARCH" in
_tar="1password-${_tarver}.x64.tar.gz" x86_64) _tararch=x64 ;;
pkgver=${_tarver//-/_} aarch64) _tararch=arm64 ;;
pkgrel=34 *) return 1 ;;
esac
pkgver=8.12.34
pkgrel=35
conflicts=('1password-beta' '1password-beta-bin') conflicts=('1password-beta' '1password-beta-bin')
pkgdesc="Password manager and secure wallet" pkgdesc="Password manager and secure wallet"
arch=('x86_64') arch=('x86_64' 'aarch64')
url='https://1password.com' url='https://1password.com'
license=('LicenseRef-1Password-Proprietary') license=('LicenseRef-1Password-Proprietary')
options=(!strip) options=(!strip)
install="1password.install" install="1password.install"
source=(https://downloads.1password.com/linux/tar/stable/${CARCH}/${_tar}{,.sig}) source_x86_64=(
sha256sums=('297784aa66770b645607a7f04c9ba2c4aebed4f46d21202487f521ba572b7b13' "https://downloads.1password.com/linux/tar/stable/x86_64/1password-${pkgver}.x64.tar.gz"
'ec085bef60de748895d3c51a8208301ba2ac8fb47db99334539ba4bd1d3260d7' "https://downloads.1password.com/linux/tar/stable/x86_64/1password-${pkgver}.x64.tar.gz.sig"
)
source_aarch64=(
"https://downloads.1password.com/linux/tar/stable/aarch64/1password-${pkgver}.arm64.tar.gz"
"https://downloads.1password.com/linux/tar/stable/aarch64/1password-${pkgver}.arm64.tar.gz.sig"
)
sha256sums_x86_64=('297784aa66770b645607a7f04c9ba2c4aebed4f46d21202487f521ba572b7b13'
'ec085bef60de748895d3c51a8208301ba2ac8fb47db99334539ba4bd1d3260d7'
)
sha256sums_aarch64=('ea5102363d6cf3442b96a7abd6743da8c1d261f56a628e1a3c183d84fa65fdcb'
'f44db73fa44c3f68c3ab78a9cce140be9355de1dd9be4ce731c9d6597960c907'
) )
validpgpkeys=('3FEF9748469ADBE15DA7CA80AC2D62742012EA22') validpgpkeys=('3FEF9748469ADBE15DA7CA80AC2D62742012EA22')
@@ -21,7 +35,7 @@ package() {
depends=('hicolor-icon-theme' 'libgtk-3.so=0' 'nss' 'xdg-utils') depends=('hicolor-icon-theme' 'libgtk-3.so=0' 'nss' 'xdg-utils')
# Go to source directory # Go to source directory
cd "1password-${_tarver}.x64" cd "1password-${pkgver}.${_tararch}"
# Install icons # Install icons
resolutions=(32x32 64x64 256x256 512x512) resolutions=(32x32 64x64 256x256 512x512)
@@ -49,7 +63,7 @@ EOF" > ./com.1password.1Password.policy
# Move package contents to /opt/1Password # Move package contents to /opt/1Password
cd "${srcdir}" cd "${srcdir}"
install -dm0755 "${pkgdir}"/opt install -dm0755 "${pkgdir}"/opt
mv "1password-${_tarver}.x64" "${pkgdir}/opt/1Password" mv "1password-${pkgver}.${_tararch}" "${pkgdir}/opt/1Password"
# Cleanup un-needed files # Cleanup un-needed files
rm "${pkgdir}"/opt/1Password/com.1password.1Password.policy "${pkgdir}"/opt/1Password/com.1password.1Password.policy.tpl "${pkgdir}"/opt/1Password/install_biometrics_policy.sh rm "${pkgdir}"/opt/1Password/com.1password.1Password.policy "${pkgdir}"/opt/1Password/com.1password.1Password.policy.tpl "${pkgdir}"/opt/1Password/install_biometrics_policy.sh
@@ -0,0 +1,11 @@
--- a/PKGBUILD
+++ b/PKGBUILD
@@ -11,7 +11,7 @@ pkgname=dropbox-cli
pkgver=2024.04.17
pkgrel=2
pkgdesc="Command line interface for Dropbox"
-arch=("any")
+arch=("x86_64")
url="https://www.dropbox.com"
license=("GPL-3.0-or-later")
makedepends=("gdk-pixbuf2")
+2 -3
View File
@@ -7,9 +7,9 @@
pkgname=dropbox-cli pkgname=dropbox-cli
pkgver=2024.04.17 pkgver=2024.04.17
pkgrel=2 pkgrel=2.1
pkgdesc="Command line interface for Dropbox" pkgdesc="Command line interface for Dropbox"
arch=("any") arch=("x86_64")
url="https://www.dropbox.com" url="https://www.dropbox.com"
license=("GPL-3.0-or-later") license=("GPL-3.0-or-later")
makedepends=("gdk-pixbuf2") makedepends=("gdk-pixbuf2")
@@ -36,4 +36,3 @@ package() {
cd "nautilus-dropbox-${pkgver}" cd "nautilus-dropbox-${pkgver}"
install -m 755 -D "${pkgname}" "${pkgdir}/usr/bin/${pkgname}" install -m 755 -D "${pkgname}" "${pkgdir}/usr/bin/${pkgname}"
} }
@@ -0,0 +1,21 @@
{
"source": "local",
"release_ring": "fast",
"upstream": {
"git_tags": "https://github.com/hyprwm/hyprland-guiutils.git",
"tag_pattern": "v{pkgver}",
"sources": {
"any": [
"https://github.com/hyprwm/hyprland-guiutils/archive/v{pkgver}/hyprland-guiutils-{pkgver}.tar.gz"
]
}
},
"rebuild_on": [
"aquamarine"
],
"rebuilt_against": {
"aarch64": {
"aquamarine": "0.15.0-2"
}
}
}
+40
View File
@@ -0,0 +1,40 @@
# Maintainer: Caleb Maclennan <caleb@alerque.com>
pkgname=hyprland-guiutils
pkgver=0.2.2
pkgrel=3
pkgdesc='Hyprland GUI utilities'
arch=(aarch64)
url="https://github.com/hyprwm/$pkgname"
license=(BSD-3-Clause)
depends=(
libgcc
libstdc++
glibc # libc.so libm.so
hyprlang
hyprtoolkit libhyprtoolkit.so
hyprutils libhyprutils.so
libdrm
pixman
)
makedepends=(cmake)
replaces=(hyprland-qtutils)
_archive="$pkgname-$pkgver"
source=("$url/archive/v$pkgver/$_archive.tar.gz")
sha256sums=('16f92a6c5a22ac58e1fc313f6b202c188da45e804e1f21ff57dfd0da5c1a01b7')
build() {
cd "$_archive"
local cmake_flags=(
-D CMAKE_BUILD_TYPE=Release
-D CMAKE_INSTALL_PREFIX=/usr
)
cmake -B build ${cmake_flags[@]}
cmake --build build
}
package() {
cd "$_archive"
DESTDIR="$pkgdir" cmake --install build
install -Dm0644 -t "$pkgdir/usr/share/licenses/$pkgname" LICENSE
}
+21
View File
@@ -0,0 +1,21 @@
{
"source": "local",
"release_ring": "fast",
"upstream": {
"git_tags": "https://github.com/hyprwm/Hyprland.git",
"tag_pattern": "v{pkgver}",
"sources": {
"any": [
"https://github.com/hyprwm/Hyprland/releases/download/v{pkgver}/source-v{pkgver}.tar.gz"
]
}
},
"rebuild_on": [
"aquamarine"
],
"rebuilt_against": {
"aarch64": {
"aquamarine": "0.15.0-2"
}
}
}
+105
View File
@@ -0,0 +1,105 @@
# Maintainer: Caleb Maclennan <caleb@alerque.com>
# Contributor: ThatOneCalculator <kainoa@t1c.dev>
# Contributor: Brenno Lemos <brenno@syndel.is>
# Contributor: Gabriel Fox <inbox@gabrielfox.dev>
pkgname=(hyprland hyprpm)
pkgver=0.56.2
pkgrel=3
pkgdesc='a highly customizable dynamic tiling Wayland compositor'
arch=(aarch64)
url="https://github.com/hyprwm/${pkgname^}"
license=(BSD-3-Clause)
depends=(cairo # libcairo.so
aquamarine libaquamarine.so
libgcc # libgcc_s.so
'libstdc++' # libstdc++.so
glibc # libc.so libm.so
glib2 libgio-2.0.so libgobject-2.0.so
glslang # libglslang-default-resource-limits.so libglslang.so
hyprcursor libhyprcursor.so
hyprgraphics libhyprgraphics.so
hyprland-guiutils
hyprlang libhyprlang.so
hyprutils libhyprutils.so
hyprwire libhyprwire.so
lcms2 liblcms2.so
libdrm # libdrm.so
libglvnd libEGL.so libGLESv2.so
libinput # libinput.so
libx11
libxcb # libxcb-dri3.so libxcb-present.so libxcb-render.so libxcb-res.so libxcb-shm.so libxcb.so libxcb-xfixes.so libxcb-xinput.so
libxcomposite # libxcb-composite.so
libxcursor # libXcursor.so
libxfixes
libxkbcommon libxkbcommon.so
libxrender
lua # liblua.so
mesa # libgbm.so
muparser # libmuparser.so
opengl-driver
pango libpango-1.0.so libpangocairo-1.0.so
pixman libpixman-1.so
re2 libre2.so
tomlplusplus libtomlplusplus.so
util-linux-libs libuuid.so
wayland libwayland-server.so
wayland-protocols
xcb-proto
xcb-util
xcb-util-errors # libxcb-errors.so
xcb-util-image
xcb-util-keysyms
xcb-util-renderutil # libxcb-render-util.so
xcb-util-wm # libxcb-ewmh.so libxcb-icccm.so
xorg-xwayland)
makedepends=(cmake
glaze
hyprland-protocols
hyprwayland-scanner
meson
ninja
xorgproto)
optdepends=('hyprpm: build and install plugins'
'hyprshutdown: clean logout and shutdown helper'
'xdg-desktop-portal-hyprland: xdg-desktop-portal backend for hyprland')
provides=(wayland-compositor)
_archive="${pkgname^}-$pkgver"
source=("$_archive.tar.gz::$url/releases/download/v$pkgver/source-v$pkgver.tar.gz")
sha256sums=('03ad3f5ef152ff44116ffd56fcf808486211ecabf4f0ba567108ee746ba5cd2e')
prepare() {
ln -sf hyprland-source "$_archive"
cd "$_archive"
sed -i -e '/^release:/{n;s/-D/-DCMAKE_SKIP_RPATH=ON -D/}' Makefile
sed -i -e '/find_package.glaze/s/7...<8 //' {.,hyprpm,start}/CMakeLists.txt
}
build() {
cd "$_archive"
make release PREFIX=/usr
}
package_hyprland() {
cd "$_archive"
make DESTDIR="$pkgdir" install
rm -fv "$pkgdir/usr/include/hyprland/src/version.h.in"
find $pkgdir -name '*hyprpm*' -delete
install -Dm0644 -t "$pkgdir/usr/share/licenses/$pkgname/" LICENSE
}
package_hyprpm() {
pkgdesc='Plugin manager for Hyprland'
depends=(cmake
cpio
glaze
hyprland
hyprland-protocols
hyprwayland-scanner
meson)
cd "$_archive"
make DESTDIR="$pkgdir" install
find $pkgdir -type f,l -not -name '*hyprpm*' -delete
find $pkgdir -type d -empty -delete
install -Dm0644 -t "$pkgdir/usr/share/licenses/$pkgname/" LICENSE
}
@@ -0,0 +1,21 @@
{
"source": "local",
"release_ring": "fast",
"upstream": {
"git_tags": "https://github.com/hyprwm/hyprtoolkit.git",
"tag_pattern": "v{pkgver}",
"sources": {
"any": [
"https://github.com/hyprwm/hyprtoolkit/archive/v{pkgver}/hyprtoolkit-{pkgver}.tar.gz"
]
}
},
"rebuild_on": [
"aquamarine"
],
"rebuilt_against": {
"aarch64": {
"aquamarine": "0.15.0-2"
}
}
}
+50
View File
@@ -0,0 +1,50 @@
# Maintainer: Caleb Maclennan <caleb@alerque.com>
pkgname=hyprtoolkit
pkgver=0.5.4
pkgrel=5.1
pkgdesc='A modern C++ Wayland-native GUI toolkit'
arch=(aarch64)
url="https://github.com/hyprwm/$pkgname"
license=(BSD-3-Clause)
depends=(
libgcc
libstdc++
aquamarine libaquamarine.so
cairo libcairo.so
glib2 libglib-2.0.so
glibc # libc.so libm.so
hyprgraphics libhyprgraphics.so
hyprlang libhyprlang.so
hyprutils libhyprutils.so
iniparser libiniparser.so
libdrm # libdrm.so
libglvnd libEGL.so libOpenGL.so
libxkbcommon libxkbcommon.so
mesa # libgbm.so
pango libpango-1.0.so # libpango.so
pixman libpixman-1.so
wayland libwayland-client.so
)
makedepends=(cmake
hyprwayland-scanner)
provides=(libhyprtoolkit.so)
_archive="$pkgname-$pkgver"
source=("$url/archive/v$pkgver/$_archive.tar.gz")
sha256sums=('2fb59789f231c1c4e9154ceffc1e7524c0cae154807c0d57e6166806255b570f')
build() {
cd "$_archive"
local cmake_options=(
-D CMAKE_BUILD_TYPE=None
-D CMAKE_INSTALL_PREFIX=/usr
)
cmake -B build -W no-dev ${cmake_options[@]}
cmake --build build
}
package() {
cd "$_archive"
DESTDIR="$pkgdir" cmake --install build
install -Dm0644 -t "$pkgdir/usr/share/licenses/$pkgname/" LICENSE
}
@@ -0,0 +1,48 @@
--- a/PKGBUILD
+++ b/PKGBUILD
@@ -3,6 +3,7 @@
pkgname="limine-mkinitcpio-hook"
_pkgver=1.38.0
_extver=""
+_gradle_version=9.7.1
pkgver="${_pkgver}${_extver}"
pkgrel=1
pkgdesc="Install kernels for the Limine bootloader."
@@ -10,7 +11,8 @@
url="https://gitlab.com/Zesko/limine-entry-tool"
source=("${_pkgname}::git+${url}.git#tag=${pkgver}")
source_x86_64=("https://github.com/graalvm/graalvm-ce-builds/releases/download/graal-25.2.4/graalvm-community-jdk-25i2-25.0.4_linux-x64_bin.tar.gz")
-source_aarch64=("https://github.com/graalvm/graalvm-ce-builds/releases/download/graal-25.2.4/graalvm-community-jdk-25i2-25.0.4_linux-aarch64_bin.tar.gz")
+source_aarch64=("https://github.com/graalvm/graalvm-ce-builds/releases/download/graal-25.2.4/graalvm-community-jdk-25i2-25.0.4_linux-aarch64_bin.tar.gz"
+ "https://services.gradle.org/distributions/gradle-${_gradle_version}-bin.zip")
license=("GPL3")
provides=('limine-entry-tool')
options=(!debug !strip)
@@ -27,12 +29,14 @@
'sbctl: Signs UEFI boot files for Secure Boot when enabled'
'journalctl-desktop-notification: Sends desktop notifications when errors occur'
)
-makedepends=('git' 'gradle')
+makedepends=('git')
+makedepends_x86_64=('gradle')
backup=(etc/limine-entry-tool.conf)
conflicts=('limine-entry-tool')
sha256sums=('11dd8211898585f79d8361d97606965bb2ca214dccace5b133dc5121e4bb4d01')
sha256sums_x86_64=('3f4a89de8eaa96f2ed677f09957c7e872cd8467aad3537f8b5394c1b8c4b942e')
-sha256sums_aarch64=('22286f7ecd21b9aedb3226b9bf797469e1bd3eefc491e12ef3dd49b452d230b7')
+sha256sums_aarch64=('22286f7ecd21b9aedb3226b9bf797469e1bd3eefc491e12ef3dd49b452d230b7'
+ 'acd53f1edaf02f1a8ff99879f8a34b302661a057d9b063ae9e35b552f804d20a')
prepare() {
[[ -d "${_graalvm_version}" ]] && rm -rf "${_graalvm_version}"
@@ -48,7 +52,9 @@
export GRAALVM_HOME="$srcdir/${_graalvm_version}"
export JAVA_HOME="${GRAALVM_HOME}"
export NATIVE_IMAGE_OPTIONS="-march=compatibility --future-defaults=all"
- /usr/bin/gradle clean nativeCompile -Dorg.gradle.java.home="${JAVA_HOME}"
+ local gradle=/usr/bin/gradle
+ [[ $CARCH == aarch64 ]] && gradle="$srcdir/gradle-${_gradle_version}/bin/gradle"
+ "$gradle" clean nativeCompile -Dorg.gradle.java.home="${JAVA_HOME}"
}
package() {
+11 -5
View File
@@ -3,14 +3,16 @@ _pkgname="limine-entry-tool"
pkgname="limine-mkinitcpio-hook" pkgname="limine-mkinitcpio-hook"
_pkgver=1.38.0 _pkgver=1.38.0
_extver="" _extver=""
_gradle_version=9.7.1
pkgver="${_pkgver}${_extver}" pkgver="${_pkgver}${_extver}"
pkgrel=1 pkgrel=1.1
pkgdesc="Install kernels for the Limine bootloader." pkgdesc="Install kernels for the Limine bootloader."
arch=('x86_64' 'aarch64') arch=('x86_64' 'aarch64')
url="https://gitlab.com/Zesko/limine-entry-tool" url="https://gitlab.com/Zesko/limine-entry-tool"
source=("${_pkgname}::git+${url}.git#tag=${pkgver}") source=("${_pkgname}::git+${url}.git#tag=${pkgver}")
source_x86_64=("https://github.com/graalvm/graalvm-ce-builds/releases/download/graal-25.2.4/graalvm-community-jdk-25i2-25.0.4_linux-x64_bin.tar.gz") source_x86_64=("https://github.com/graalvm/graalvm-ce-builds/releases/download/graal-25.2.4/graalvm-community-jdk-25i2-25.0.4_linux-x64_bin.tar.gz")
source_aarch64=("https://github.com/graalvm/graalvm-ce-builds/releases/download/graal-25.2.4/graalvm-community-jdk-25i2-25.0.4_linux-aarch64_bin.tar.gz") source_aarch64=("https://github.com/graalvm/graalvm-ce-builds/releases/download/graal-25.2.4/graalvm-community-jdk-25i2-25.0.4_linux-aarch64_bin.tar.gz"
"https://services.gradle.org/distributions/gradle-${_gradle_version}-bin.zip")
license=("GPL3") license=("GPL3")
provides=('limine-entry-tool') provides=('limine-entry-tool')
options=(!debug !strip) options=(!debug !strip)
@@ -27,12 +29,14 @@ optdepends=(
'sbctl: Signs UEFI boot files for Secure Boot when enabled' 'sbctl: Signs UEFI boot files for Secure Boot when enabled'
'journalctl-desktop-notification: Sends desktop notifications when errors occur' 'journalctl-desktop-notification: Sends desktop notifications when errors occur'
) )
makedepends=('git' 'gradle') makedepends=('git')
makedepends_x86_64=('gradle')
backup=(etc/limine-entry-tool.conf) backup=(etc/limine-entry-tool.conf)
conflicts=('limine-entry-tool') conflicts=('limine-entry-tool')
sha256sums=('11dd8211898585f79d8361d97606965bb2ca214dccace5b133dc5121e4bb4d01') sha256sums=('11dd8211898585f79d8361d97606965bb2ca214dccace5b133dc5121e4bb4d01')
sha256sums_x86_64=('3f4a89de8eaa96f2ed677f09957c7e872cd8467aad3537f8b5394c1b8c4b942e') sha256sums_x86_64=('3f4a89de8eaa96f2ed677f09957c7e872cd8467aad3537f8b5394c1b8c4b942e')
sha256sums_aarch64=('22286f7ecd21b9aedb3226b9bf797469e1bd3eefc491e12ef3dd49b452d230b7') sha256sums_aarch64=('22286f7ecd21b9aedb3226b9bf797469e1bd3eefc491e12ef3dd49b452d230b7'
'acd53f1edaf02f1a8ff99879f8a34b302661a057d9b063ae9e35b552f804d20a')
prepare() { prepare() {
[[ -d "${_graalvm_version}" ]] && rm -rf "${_graalvm_version}" [[ -d "${_graalvm_version}" ]] && rm -rf "${_graalvm_version}"
@@ -48,7 +52,9 @@ build() {
export GRAALVM_HOME="$srcdir/${_graalvm_version}" export GRAALVM_HOME="$srcdir/${_graalvm_version}"
export JAVA_HOME="${GRAALVM_HOME}" export JAVA_HOME="${GRAALVM_HOME}"
export NATIVE_IMAGE_OPTIONS="-march=compatibility --future-defaults=all" export NATIVE_IMAGE_OPTIONS="-march=compatibility --future-defaults=all"
/usr/bin/gradle clean nativeCompile -Dorg.gradle.java.home="${JAVA_HOME}" local gradle=/usr/bin/gradle
[[ $CARCH == aarch64 ]] && gradle="$srcdir/gradle-${_gradle_version}/bin/gradle"
"$gradle" clean nativeCompile -Dorg.gradle.java.home="${JAVA_HOME}"
} }
package() { package() {
@@ -0,0 +1,46 @@
--- a/PKGBUILD
+++ b/PKGBUILD
@@ -2,12 +2,14 @@
pkgname="limine-snapper-sync"
pkgver=1.31.0
pkgrel=1
+_gradle_version=9.7.1
pkgdesc="Integrates Limine boot entries with Snapper snapshots."
arch=('x86_64' 'aarch64')
url="https://gitlab.com/Zesko/limine-snapper-sync"
source=("${pkgname}::git+${url}.git#tag=${pkgver}")
source_x86_64=("https://github.com/graalvm/graalvm-ce-builds/releases/download/jdk-25.0.2/graalvm-community-jdk-25.0.2_linux-x64_bin.tar.gz")
-source_aarch64=("https://github.com/graalvm/graalvm-ce-builds/releases/download/jdk-25.0.2/graalvm-community-jdk-25.0.2_linux-aarch64_bin.tar.gz")
+source_aarch64=("https://github.com/graalvm/graalvm-ce-builds/releases/download/jdk-25.0.2/graalvm-community-jdk-25.0.2_linux-aarch64_bin.tar.gz"
+ "https://services.gradle.org/distributions/gradle-${_gradle_version}-bin.zip")
license=("GPL3")
options=(!debug !strip)
_graalvm_version=graalvm_ce_jdk25
@@ -25,12 +27,14 @@
'b3sum: Fast Blake3 hash function to prevent duplication.'
'xxhash: Fast hashing utility for deduplication with shorter hashes.'
)
-makedepends=('git' 'gradle')
+makedepends=('git')
+makedepends_x86_64=('gradle')
backup=(etc/limine-snapper-sync.conf)
conflicts=('limine-snapper-cli' 'limine-snapper-sync-git')
sha256sums=('ed236f1bbab966950bf11ba5a7958e97a76e66db7fd647b7737bab4b48c9fc40')
sha256sums_x86_64=('e0be791c8fda4d03b6b0a0cb824fef3149736170057b3a515252b44419606af0')
-sha256sums_aarch64=('b4580d9f223d0a4b3a1757e58b18ff4c1db950e67e105fc5cb741457d2384a71')
+sha256sums_aarch64=('b4580d9f223d0a4b3a1757e58b18ff4c1db950e67e105fc5cb741457d2384a71'
+ 'acd53f1edaf02f1a8ff99879f8a34b302661a057d9b063ae9e35b552f804d20a')
prepare() {
[[ -d "${_graalvm_version}" ]] && rm -rf "${_graalvm_version}"
@@ -46,7 +50,9 @@
export GRAALVM_HOME="$srcdir/${_graalvm_version}"
export JAVA_HOME="${GRAALVM_HOME}"
export NATIVE_IMAGE_OPTIONS="-march=compatibility"
- /usr/bin/gradle clean nativeCompile -Dorg.gradle.java.home="${JAVA_HOME}"
+ local gradle=/usr/bin/gradle
+ [[ $CARCH == aarch64 ]] && gradle="$srcdir/gradle-${_gradle_version}/bin/gradle"
+ "$gradle" clean nativeCompile -Dorg.gradle.java.home="${JAVA_HOME}"
}
package() {
+11 -5
View File
@@ -1,13 +1,15 @@
# Maintainer: Zesko # Maintainer: Zesko
pkgname="limine-snapper-sync" pkgname="limine-snapper-sync"
pkgver=1.31.0 pkgver=1.31.0
pkgrel=1 pkgrel=1.1
_gradle_version=9.7.1
pkgdesc="Integrates Limine boot entries with Snapper snapshots." pkgdesc="Integrates Limine boot entries with Snapper snapshots."
arch=('x86_64' 'aarch64') arch=('x86_64' 'aarch64')
url="https://gitlab.com/Zesko/limine-snapper-sync" url="https://gitlab.com/Zesko/limine-snapper-sync"
source=("${pkgname}::git+${url}.git#tag=${pkgver}") source=("${pkgname}::git+${url}.git#tag=${pkgver}")
source_x86_64=("https://github.com/graalvm/graalvm-ce-builds/releases/download/jdk-25.0.2/graalvm-community-jdk-25.0.2_linux-x64_bin.tar.gz") source_x86_64=("https://github.com/graalvm/graalvm-ce-builds/releases/download/jdk-25.0.2/graalvm-community-jdk-25.0.2_linux-x64_bin.tar.gz")
source_aarch64=("https://github.com/graalvm/graalvm-ce-builds/releases/download/jdk-25.0.2/graalvm-community-jdk-25.0.2_linux-aarch64_bin.tar.gz") source_aarch64=("https://github.com/graalvm/graalvm-ce-builds/releases/download/jdk-25.0.2/graalvm-community-jdk-25.0.2_linux-aarch64_bin.tar.gz"
"https://services.gradle.org/distributions/gradle-${_gradle_version}-bin.zip")
license=("GPL3") license=("GPL3")
options=(!debug !strip) options=(!debug !strip)
_graalvm_version=graalvm_ce_jdk25 _graalvm_version=graalvm_ce_jdk25
@@ -25,12 +27,14 @@ optdepends=(
'b3sum: Fast Blake3 hash function to prevent duplication.' 'b3sum: Fast Blake3 hash function to prevent duplication.'
'xxhash: Fast hashing utility for deduplication with shorter hashes.' 'xxhash: Fast hashing utility for deduplication with shorter hashes.'
) )
makedepends=('git' 'gradle') makedepends=('git')
makedepends_x86_64=('gradle')
backup=(etc/limine-snapper-sync.conf) backup=(etc/limine-snapper-sync.conf)
conflicts=('limine-snapper-cli' 'limine-snapper-sync-git') conflicts=('limine-snapper-cli' 'limine-snapper-sync-git')
sha256sums=('ed236f1bbab966950bf11ba5a7958e97a76e66db7fd647b7737bab4b48c9fc40') sha256sums=('ed236f1bbab966950bf11ba5a7958e97a76e66db7fd647b7737bab4b48c9fc40')
sha256sums_x86_64=('e0be791c8fda4d03b6b0a0cb824fef3149736170057b3a515252b44419606af0') sha256sums_x86_64=('e0be791c8fda4d03b6b0a0cb824fef3149736170057b3a515252b44419606af0')
sha256sums_aarch64=('b4580d9f223d0a4b3a1757e58b18ff4c1db950e67e105fc5cb741457d2384a71') sha256sums_aarch64=('b4580d9f223d0a4b3a1757e58b18ff4c1db950e67e105fc5cb741457d2384a71'
'acd53f1edaf02f1a8ff99879f8a34b302661a057d9b063ae9e35b552f804d20a')
prepare() { prepare() {
[[ -d "${_graalvm_version}" ]] && rm -rf "${_graalvm_version}" [[ -d "${_graalvm_version}" ]] && rm -rf "${_graalvm_version}"
@@ -46,7 +50,9 @@ build() {
export GRAALVM_HOME="$srcdir/${_graalvm_version}" export GRAALVM_HOME="$srcdir/${_graalvm_version}"
export JAVA_HOME="${GRAALVM_HOME}" export JAVA_HOME="${GRAALVM_HOME}"
export NATIVE_IMAGE_OPTIONS="-march=compatibility" export NATIVE_IMAGE_OPTIONS="-march=compatibility"
/usr/bin/gradle clean nativeCompile -Dorg.gradle.java.home="${JAVA_HOME}" local gradle=/usr/bin/gradle
[[ $CARCH == aarch64 ]] && gradle="$srcdir/gradle-${_gradle_version}/bin/gradle"
"$gradle" clean nativeCompile -Dorg.gradle.java.home="${JAVA_HOME}"
} }
package() { package() {
@@ -0,0 +1,57 @@
--- a/PKGBUILD
+++ b/PKGBUILD
@@ -36,7 +36,6 @@ depends=(
'libcap'
'libdrm'
'libevdev'
- 'libmfx'
'libnotify'
'libpipewire'
'libpulse'
@@ -53,6 +52,10 @@ depends=(
'which'
)
+depends_x86_64=(
+ 'libmfx'
+)
+
makedepends=(
'appstream'
'appstream-glib'
@@ -69,6 +72,10 @@ makedepends=(
'shaderc'
)
+makedepends_aarch64=(
+ 'vulkan-headers'
+)
+
checkdepends=(
'gcovr'
)
@@ -111,7 +118,12 @@ if [ -n "${optdepends+x}" ]; then
prepare() {
cd "$pkgname"
- git submodule update --recursive --init
+ if [[ $CARCH == aarch64 ]]; then
+ git submodule update --init
+ git -C third-party/moonlight-common-c submodule update --init enet
+ else
+ git submodule update --recursive --init
+ fi
# Backport https://github.com/LizardByte/Sunshine/commit/060b6b07d376e9ef4c4ce4a5c6d648d08fe0eab9
patch -Np1 -i "${srcdir}/2026.516.143833-060b6b07-remove-setuptools-from-glad-dependencies.patch"
@@ -145,6 +157,10 @@ build() {
-D SUNSHINE_PUBLISHER_ISSUE_URL='https://app.lizardbyte.dev/support'
)
+ if [[ $CARCH == aarch64 ]]; then
+ _cmake_options+=(-D SUNSHINE_SYSTEM_VULKAN_HEADERS=ON)
+ fi
+
if [[ "${_use_cuda::1}" != "t" ]]; then
_cmake_options+=(-DSUNSHINE_ENABLE_CUDA=OFF -DCUDA_FAIL_ON_MISSING=OFF)
else
+19 -3
View File
@@ -10,7 +10,7 @@
pkgname='sunshine' pkgname='sunshine'
pkgver=2026.516.143833 pkgver=2026.516.143833
pkgrel=4 pkgrel=4.1
pkgdesc="Self-hosted game stream host for Moonlight" pkgdesc="Self-hosted game stream host for Moonlight"
arch=('x86_64' 'aarch64') arch=('x86_64' 'aarch64')
url=https://app.lizardbyte.dev/Sunshine url=https://app.lizardbyte.dev/Sunshine
@@ -36,7 +36,6 @@ depends=(
'libcap' 'libcap'
'libdrm' 'libdrm'
'libevdev' 'libevdev'
'libmfx'
'libnotify' 'libnotify'
'libpipewire' 'libpipewire'
'libpulse' 'libpulse'
@@ -55,6 +54,10 @@ depends=(
'which' 'which'
) )
depends_x86_64=(
'libmfx'
)
makedepends=( makedepends=(
'appstream' 'appstream'
'appstream-glib' 'appstream-glib'
@@ -69,6 +72,10 @@ makedepends=(
'shaderc' 'shaderc'
) )
makedepends_aarch64=(
'vulkan-headers'
)
checkdepends=( checkdepends=(
'gcovr' 'gcovr'
) )
@@ -123,7 +130,12 @@ fi
prepare() { prepare() {
cd "$pkgname" cd "$pkgname"
git submodule update --recursive --init if [[ $CARCH == aarch64 ]]; then
git submodule update --init
git -C third-party/moonlight-common-c submodule update --init enet
else
git submodule update --recursive --init
fi
# Backport https://github.com/LizardByte/Sunshine/commit/060b6b07d376e9ef4c4ce4a5c6d648d08fe0eab9 # Backport https://github.com/LizardByte/Sunshine/commit/060b6b07d376e9ef4c4ce4a5c6d648d08fe0eab9
patch -Np1 -i "${srcdir}/2026.516.143833-060b6b07-remove-setuptools-from-glad-dependencies.patch" patch -Np1 -i "${srcdir}/2026.516.143833-060b6b07-remove-setuptools-from-glad-dependencies.patch"
@@ -159,6 +171,10 @@ build() {
-D SUNSHINE_PUBLISHER_ISSUE_URL='https://app.lizardbyte.dev/support' -D SUNSHINE_PUBLISHER_ISSUE_URL='https://app.lizardbyte.dev/support'
) )
if [[ $CARCH == aarch64 ]]; then
_cmake_options+=(-D SUNSHINE_SYSTEM_VULKAN_HEADERS=ON)
fi
if [[ "${_use_cuda::1}" != "t" ]]; then if [[ "${_use_cuda::1}" != "t" ]]; then
_cmake_options+=(-DSUNSHINE_ENABLE_CUDA=OFF -DCUDA_FAIL_ON_MISSING=OFF) _cmake_options+=(-DSUNSHINE_ENABLE_CUDA=OFF -DCUDA_FAIL_ON_MISSING=OFF)
else else
+79 -2
View File
@@ -1,5 +1,82 @@
{ {
"source": "aur", "source": "local",
"release_ring": "fast", "release_ring": "fast",
"upstream_commit": "6dbf4bedbba0a2f90d8e813becfa5ba289653562" "min_release_age": "24h",
"upstream": {
"github": "peteonrails/voxtype",
"digests": true,
"latest_only": true,
"assets": {
"x86_64": [
"voxtype-{pkgver}-linux-x86_64-avx2",
"voxtype-{pkgver}-linux-x86_64-avx2.asc",
"voxtype-{pkgver}-linux-x86_64-avx512",
"voxtype-{pkgver}-linux-x86_64-avx512.asc",
"voxtype-{pkgver}-linux-x86_64-vulkan",
"voxtype-{pkgver}-linux-x86_64-vulkan.asc",
"voxtype-{pkgver}-linux-x86_64-onnx-avx2",
"voxtype-{pkgver}-linux-x86_64-onnx-avx2.asc",
"voxtype-{pkgver}-linux-x86_64-onnx-avx512",
"voxtype-{pkgver}-linux-x86_64-onnx-avx512.asc",
"voxtype-{pkgver}-linux-x86_64-onnx-cuda-12",
"voxtype-{pkgver}-linux-x86_64-onnx-cuda-12.asc",
"voxtype-{pkgver}-linux-x86_64-onnx-cuda-12.libonnxruntime_providers_cuda.so",
"voxtype-{pkgver}-linux-x86_64-onnx-cuda-12.libonnxruntime_providers_shared.so",
"voxtype-{pkgver}-linux-x86_64-onnx-cuda-13",
"voxtype-{pkgver}-linux-x86_64-onnx-cuda-13.asc",
"voxtype-{pkgver}-linux-x86_64-onnx-cuda-13.libonnxruntime_providers_cuda.so",
"voxtype-{pkgver}-linux-x86_64-onnx-cuda-13.libonnxruntime_providers_shared.so",
"voxtype-{pkgver}-linux-x86_64-onnx-cuda-13.libonnxruntime.so.1.24.4",
"voxtype-{pkgver}-linux-x86_64-onnx-migraphx",
"voxtype-{pkgver}-linux-x86_64-onnx-migraphx.asc",
"voxtype-{pkgver}-linux-x86_64-onnx-migraphx.libonnxruntime_providers_migraphx.so",
"voxtype-{pkgver}-linux-x86_64-onnx-migraphx.libonnxruntime_providers_shared.so",
"voxtype-{pkgver}-linux-x86_64-osd",
"voxtype-{pkgver}-linux-x86_64-osd.asc",
"voxtype-{pkgver}-linux-x86_64-osd-gtk4",
"voxtype-{pkgver}-linux-x86_64-osd-gtk4.asc",
"voxtype-{pkgver}-linux-x86_64-osd-quickshell",
"voxtype-{pkgver}-linux-x86_64-osd-quickshell.asc",
"voxtype-{pkgver}-linux-x86_64-audio-bridge",
"voxtype-{pkgver}-linux-x86_64-audio-bridge.asc"
],
"aarch64": [
"voxtype-{pkgver}-linux-aarch64-cpu",
"voxtype-{pkgver}-linux-aarch64-cpu.asc",
"voxtype-{pkgver}-linux-aarch64-onnx",
"voxtype-{pkgver}-linux-aarch64-onnx.asc",
"voxtype-{pkgver}-linux-aarch64-osd",
"voxtype-{pkgver}-linux-aarch64-osd.asc",
"voxtype-{pkgver}-linux-aarch64-osd-gtk4",
"voxtype-{pkgver}-linux-aarch64-osd-gtk4.asc",
"voxtype-{pkgver}-linux-aarch64-osd-quickshell",
"voxtype-{pkgver}-linux-aarch64-osd-quickshell.asc",
"voxtype-{pkgver}-linux-aarch64-audio-bridge",
"voxtype-{pkgver}-linux-aarch64-audio-bridge.asc"
]
},
"sources": {
"any": [
"https://raw.githubusercontent.com/peteonrails/voxtype/{tag}/config/default.toml",
"https://raw.githubusercontent.com/peteonrails/voxtype/{tag}/packaging/systemd/voxtype.service",
"https://raw.githubusercontent.com/peteonrails/voxtype/{tag}/packaging/completions/voxtype.bash",
"https://raw.githubusercontent.com/peteonrails/voxtype/{tag}/packaging/completions/voxtype.zsh",
"https://raw.githubusercontent.com/peteonrails/voxtype/{tag}/packaging/completions/voxtype.fish",
"https://raw.githubusercontent.com/peteonrails/voxtype/{tag}/LICENSE",
"https://raw.githubusercontent.com/peteonrails/voxtype/{tag}/README.md",
"https://raw.githubusercontent.com/peteonrails/voxtype/{tag}/packaging/voxtype-configure.desktop",
"https://raw.githubusercontent.com/peteonrails/voxtype/{tag}/packaging/scripts/voxtype-configure-launcher",
"https://raw.githubusercontent.com/peteonrails/voxtype/{tag}/quickshell/shell.qml",
"https://raw.githubusercontent.com/peteonrails/voxtype/{tag}/quickshell/OsdSurface.qml",
"https://raw.githubusercontent.com/peteonrails/voxtype/{tag}/quickshell/EnginePicker.qml",
"https://raw.githubusercontent.com/peteonrails/voxtype/{tag}/quickshell/MeetingControls.qml",
"https://raw.githubusercontent.com/peteonrails/voxtype/{tag}/quickshell/voxtype-shared/Theme.qml",
"https://raw.githubusercontent.com/peteonrails/voxtype/{tag}/quickshell/voxtype-shared/StateReader.qml",
"https://raw.githubusercontent.com/peteonrails/voxtype/{tag}/quickshell/voxtype-shared/AudioBridge.qml",
"https://raw.githubusercontent.com/peteonrails/voxtype/{tag}/quickshell/voxtype-shared/StyleLoader.qml",
"https://raw.githubusercontent.com/peteonrails/voxtype/{tag}/quickshell/voxtype-shared/RecipeRenderer.qml",
"https://raw.githubusercontent.com/peteonrails/voxtype/{tag}/quickshell/voxtype-shared/qmldir"
]
}
}
} }
+63 -26
View File
@@ -3,7 +3,7 @@ pkgname=voxtype-bin
pkgver=1.0.1 pkgver=1.0.1
pkgrel=1 pkgrel=1
pkgdesc="Push-to-talk voice-to-text for Linux (pre-built binaries)" pkgdesc="Push-to-talk voice-to-text for Linux (pre-built binaries)"
arch=('x86_64') arch=('x86_64' 'aarch64')
url="https://voxtype.io" url="https://voxtype.io"
license=('MIT') license=('MIT')
depends=( depends=(
@@ -47,7 +47,7 @@ validpgpkeys=(
'9CCF7915B750CAE8B095ED1AA3FC9F33FD209279' '9CCF7915B750CAE8B095ED1AA3FC9F33FD209279'
) )
_github="https://github.com/peteonrails/voxtype/releases/download/v$pkgver" _github="https://github.com/peteonrails/voxtype/releases/download/v$pkgver"
source=( source_x86_64=(
# Whisper binaries # Whisper binaries
"voxtype-$pkgver-avx2::$_github/voxtype-$pkgver-linux-x86_64-avx2" "voxtype-$pkgver-avx2::$_github/voxtype-$pkgver-linux-x86_64-avx2"
"voxtype-$pkgver-avx2.asc::$_github/voxtype-$pkgver-linux-x86_64-avx2.asc" "voxtype-$pkgver-avx2.asc::$_github/voxtype-$pkgver-linux-x86_64-avx2.asc"
@@ -97,7 +97,23 @@ source=(
"voxtype-$pkgver-osd-quickshell.asc::$_github/voxtype-$pkgver-linux-x86_64-osd-quickshell.asc" "voxtype-$pkgver-osd-quickshell.asc::$_github/voxtype-$pkgver-linux-x86_64-osd-quickshell.asc"
"voxtype-$pkgver-audio-bridge::$_github/voxtype-$pkgver-linux-x86_64-audio-bridge" "voxtype-$pkgver-audio-bridge::$_github/voxtype-$pkgver-linux-x86_64-audio-bridge"
"voxtype-$pkgver-audio-bridge.asc::$_github/voxtype-$pkgver-linux-x86_64-audio-bridge.asc" "voxtype-$pkgver-audio-bridge.asc::$_github/voxtype-$pkgver-linux-x86_64-audio-bridge.asc"
# Config and support files )
source_aarch64=(
"voxtype-$pkgver-cpu::$_github/voxtype-$pkgver-linux-aarch64-cpu"
"voxtype-$pkgver-cpu.asc::$_github/voxtype-$pkgver-linux-aarch64-cpu.asc"
"voxtype-$pkgver-onnx::$_github/voxtype-$pkgver-linux-aarch64-onnx"
"voxtype-$pkgver-onnx.asc::$_github/voxtype-$pkgver-linux-aarch64-onnx.asc"
"voxtype-$pkgver-osd::$_github/voxtype-$pkgver-linux-aarch64-osd"
"voxtype-$pkgver-osd.asc::$_github/voxtype-$pkgver-linux-aarch64-osd.asc"
"voxtype-$pkgver-osd-gtk4::$_github/voxtype-$pkgver-linux-aarch64-osd-gtk4"
"voxtype-$pkgver-osd-gtk4.asc::$_github/voxtype-$pkgver-linux-aarch64-osd-gtk4.asc"
"voxtype-$pkgver-osd-quickshell::$_github/voxtype-$pkgver-linux-aarch64-osd-quickshell"
"voxtype-$pkgver-osd-quickshell.asc::$_github/voxtype-$pkgver-linux-aarch64-osd-quickshell.asc"
"voxtype-$pkgver-audio-bridge::$_github/voxtype-$pkgver-linux-aarch64-audio-bridge"
"voxtype-$pkgver-audio-bridge.asc::$_github/voxtype-$pkgver-linux-aarch64-audio-bridge.asc"
)
source=(
# Architecture-independent config and support files
"config-$pkgver.toml::https://raw.githubusercontent.com/peteonrails/voxtype/v$pkgver/config/default.toml" "config-$pkgver.toml::https://raw.githubusercontent.com/peteonrails/voxtype/v$pkgver/config/default.toml"
"voxtype-$pkgver.service::https://raw.githubusercontent.com/peteonrails/voxtype/v$pkgver/packaging/systemd/voxtype.service" "voxtype-$pkgver.service::https://raw.githubusercontent.com/peteonrails/voxtype/v$pkgver/packaging/systemd/voxtype.service"
"voxtype-$pkgver.bash::https://raw.githubusercontent.com/peteonrails/voxtype/v$pkgver/packaging/completions/voxtype.bash" "voxtype-$pkgver.bash::https://raw.githubusercontent.com/peteonrails/voxtype/v$pkgver/packaging/completions/voxtype.bash"
@@ -125,46 +141,62 @@ source=(
"quickshell-voxtype-shared-RecipeRenderer-$pkgver.qml::https://raw.githubusercontent.com/peteonrails/voxtype/v$pkgver/quickshell/voxtype-shared/RecipeRenderer.qml" "quickshell-voxtype-shared-RecipeRenderer-$pkgver.qml::https://raw.githubusercontent.com/peteonrails/voxtype/v$pkgver/quickshell/voxtype-shared/RecipeRenderer.qml"
"quickshell-voxtype-shared-qmldir-$pkgver::https://raw.githubusercontent.com/peteonrails/voxtype/v$pkgver/quickshell/voxtype-shared/qmldir" "quickshell-voxtype-shared-qmldir-$pkgver::https://raw.githubusercontent.com/peteonrails/voxtype/v$pkgver/quickshell/voxtype-shared/qmldir"
) )
sha256sums=( sha256sums_x86_64=(
# Whisper binaries # Whisper binaries
'cb3843a894ef47aca230b30bb1c45c2ef8e0d015adf2fa754d60e55123165fd0' # voxtype-avx2 'cb3843a894ef47aca230b30bb1c45c2ef8e0d015adf2fa754d60e55123165fd0' # voxtype-avx2
'SKIP' # voxtype-avx2.asc '21357d39412619e8a3fc473153e6e2a28b219aa02007682737afedd73a08bdbe' # voxtype-avx2.asc
'77d49275ae4c3a6d93671014278b0dabd69ba52e65d2ee6ab3ec1bff0af34d55' # voxtype-avx512 '77d49275ae4c3a6d93671014278b0dabd69ba52e65d2ee6ab3ec1bff0af34d55' # voxtype-avx512
'SKIP' # voxtype-avx512.asc 'e3421641b4b5fa5d42ee0698eb43552e177f8b095a1acea32445693404b4fd54' # voxtype-avx512.asc
'c569d038057464aa60290296794bcbd79b928ee0efd038e33062a4c015558ed8' # voxtype-vulkan 'c569d038057464aa60290296794bcbd79b928ee0efd038e33062a4c015558ed8' # voxtype-vulkan
'SKIP' # voxtype-vulkan.asc 'a82bd4108dc70d7d2b6e25a83a6744170502f5110780d4a2e97a3e76047ea60d' # voxtype-vulkan.asc
# ONNX CPU binaries # ONNX CPU binaries
'19b8716ff7bd388ffb18843fc0ee5e614faa84544d22918ecf72a7c3d6c02929' # voxtype-onnx-avx2 '19b8716ff7bd388ffb18843fc0ee5e614faa84544d22918ecf72a7c3d6c02929' # voxtype-onnx-avx2
'SKIP' # voxtype-onnx-avx2.asc '3021aa3a13555bdab0ecccfb522db8391d7e05810d218608a6372477f11cb2af' # voxtype-onnx-avx2.asc
'69a6e6fc1c04a55eeb62cf1eaa5fe81873c70eabffff120ce1a4ff7ef62fd559' # voxtype-onnx-avx512 '69a6e6fc1c04a55eeb62cf1eaa5fe81873c70eabffff120ce1a4ff7ef62fd559' # voxtype-onnx-avx512
'SKIP' # voxtype-onnx-avx512.asc '305d33b6f4dc8ac7a38a5f522c1af95b462355fa057b2eb39d3add59303145e1' # voxtype-onnx-avx512.asc
# ONNX CUDA 12 binary + companion .so # ONNX CUDA 12 binary + companion .so
'e7180f17add10bb441368d8495fc99bde0ba1a1978ea5f7a52d7e626779eca16' # voxtype-onnx-cuda-12 'e7180f17add10bb441368d8495fc99bde0ba1a1978ea5f7a52d7e626779eca16' # voxtype-onnx-cuda-12
'SKIP' # voxtype-onnx-cuda-12.asc '34f7242fe16867ab029af9ed4854d3930aa2ed91bc37e062962edbfc65522af5' # voxtype-onnx-cuda-12.asc
'a8584727d51ba646ac63fc991c2f36ad6cd5b8cc8b1141896e46938700b888d0' # cuda-12.libonnxruntime_providers_cuda.so 'a8584727d51ba646ac63fc991c2f36ad6cd5b8cc8b1141896e46938700b888d0' # cuda-12.libonnxruntime_providers_cuda.so
'1b028afc079628d76a28d7eb09700a4baead4a27f9634ba82c35398486134114' # cuda-12.libonnxruntime_providers_shared.so '1b028afc079628d76a28d7eb09700a4baead4a27f9634ba82c35398486134114' # cuda-12.libonnxruntime_providers_shared.so
# ONNX CUDA 13 binary + companion .so + dlopen-target libonnxruntime # ONNX CUDA 13 binary + companion .so + dlopen-target libonnxruntime
'4de2aac42b67a05c34ac0b23f771fc83968670fd77d14781cbf4483524304cd1' # voxtype-onnx-cuda-13 '4de2aac42b67a05c34ac0b23f771fc83968670fd77d14781cbf4483524304cd1' # voxtype-onnx-cuda-13
'SKIP' # voxtype-onnx-cuda-13.asc 'adb768be0b115f38bc28ea35614ae59e1c1adfb6e11ae08a252c45d52932fab2' # voxtype-onnx-cuda-13.asc
'b6cb7744d0efd2faced5c83ead374c13e7f2630b5a249ffaf393cdb1e092c92b' # cuda-13.libonnxruntime_providers_cuda.so 'b6cb7744d0efd2faced5c83ead374c13e7f2630b5a249ffaf393cdb1e092c92b' # cuda-13.libonnxruntime_providers_cuda.so
'c6a12593396095f5670160e284c35d1700b7708cf3037b7042e2a5200ccae772' # cuda-13.libonnxruntime_providers_shared.so 'c6a12593396095f5670160e284c35d1700b7708cf3037b7042e2a5200ccae772' # cuda-13.libonnxruntime_providers_shared.so
'1aacefdf0b4afa145d410b2381bbc3db3d978c485fb182c42a2b0b09f91f5310' # cuda-13.libonnxruntime.so.1.24.4 '1aacefdf0b4afa145d410b2381bbc3db3d978c485fb182c42a2b0b09f91f5310' # cuda-13.libonnxruntime.so.1.24.4
# ONNX MIGraphX binary + companion .so # ONNX MIGraphX binary + companion .so
'f7a4cc0deaf35110b5106f1fc1f83dded41c0b1780f9fb5aa9cccc3c62e5eb6a' # voxtype-onnx-migraphx 'f7a4cc0deaf35110b5106f1fc1f83dded41c0b1780f9fb5aa9cccc3c62e5eb6a' # voxtype-onnx-migraphx
'SKIP' # voxtype-onnx-migraphx.asc '9e9c767605923c084c97659326cb0f03ad4e5db0aded4da052fa208b4de9cf45' # voxtype-onnx-migraphx.asc
'ddd67e6193ade819ee21f1706d1b03b9151f1d2d2843701e2d19d8b183631707' # migraphx.libonnxruntime_providers_migraphx.so 'ddd67e6193ade819ee21f1706d1b03b9151f1d2d2843701e2d19d8b183631707' # migraphx.libonnxruntime_providers_migraphx.so
'17f7cf47ad0d7b5ac895ae588fd62c7f85a13842588161b6a24c7d480f062be4' # migraphx.libonnxruntime_providers_shared.so '17f7cf47ad0d7b5ac895ae588fd62c7f85a13842588161b6a24c7d480f062be4' # migraphx.libonnxruntime_providers_shared.so
# OSD launcher + GTK4 frontend # OSD launcher + GTK4 frontend
'7250027b1672507a6d584f795731c87e1d3b5c1de891438bd55e34b136a2d5cc' # voxtype-osd '7250027b1672507a6d584f795731c87e1d3b5c1de891438bd55e34b136a2d5cc' # voxtype-osd
'SKIP' # voxtype-osd.asc '1a944200fbac1e773c70d036075745b0e868885443e29c30797f262d44928cc1' # voxtype-osd.asc
'74fb0f6ad87feb0c1c9e06a8b28a7f7ecee101caef4248f77dcf613b5271238e' # voxtype-osd-gtk4 '74fb0f6ad87feb0c1c9e06a8b28a7f7ecee101caef4248f77dcf613b5271238e' # voxtype-osd-gtk4
'SKIP' # voxtype-osd-gtk4.asc '60224685717fea0582d570160282e9e215cd4713b3bdb695edc7bae8b6860cac' # voxtype-osd-gtk4.asc
# Quickshell OSD launcher + audio-bridge sidecar (new in v0.7.5) # Quickshell OSD launcher + audio-bridge sidecar (new in v0.7.5)
'b809c5140e844a6add801d7e592775cd89af8cce73fa399b6a3aec15dfd09533' # voxtype-osd-quickshell 'b809c5140e844a6add801d7e592775cd89af8cce73fa399b6a3aec15dfd09533' # voxtype-osd-quickshell
'SKIP' # voxtype-osd-quickshell.asc '39412691ab1f8ffe4fa6bf1eb9763c29f94b8892d1ba59fd49dc65dd0d80297a' # voxtype-osd-quickshell.asc
'45776290e364194d83a8b89445166406c278e890507bf07ec52a5f0e8fa57720' # voxtype-audio-bridge '45776290e364194d83a8b89445166406c278e890507bf07ec52a5f0e8fa57720' # voxtype-audio-bridge
'SKIP' # voxtype-audio-bridge.asc '5be1174a78d70b78fd8d923c88e3406962443b39a1c452eb985b1e2484ef5668' # voxtype-audio-bridge.asc
# Config and support files )
sha256sums_aarch64=(
'b5e31a85aaa952d1a78c12b8a16ba5cbdcd92eb31adc7d1a908f3c9d06edd4f1' # voxtype-cpu
'3cd2f3fafca40e394a42b90dac031c67160256898a7f4a52893585b99f8f74bb' # voxtype-cpu.asc
'c3771f3e568629178201990976520f88da6d7599ec2d9e404a137570d6c1e108' # voxtype-onnx
'cd58773349eac0108cdad0efcc78d13f5d7c21623117381afa99b912c397b94a' # voxtype-onnx.asc
'ea910d4fd1fe331d38dbed1c3a639cb7e0c04542919192ff6f74be2139afe3c6' # voxtype-osd
'e079ebdd567e318502b710af95e4987fe13623d65673e21877afe88fe18bda55' # voxtype-osd.asc
'0d2148e0cd32bac538692470edc06aa9a2f5c6a891aaa373f59fbe78c247fae3' # voxtype-osd-gtk4
'0ce841f2caa9a1a7e8294a521bef0bab8823dddacd36fff66012b1127e0c1957' # voxtype-osd-gtk4.asc
'097bd518d5e2eac2c3cbad714b65dd8058c818dcb4d900b9a16e442af7d65b8a' # voxtype-osd-quickshell
'f48b8071f78fde0b2d20072e875a8b8e0d8fab4caf6c8ec91cd8e2aa0b031063' # voxtype-osd-quickshell.asc
'35170ad89fea2874fce0f08758ccc2164892ed643aacae632bcfbc6f10433976' # voxtype-audio-bridge
'787965900647ee9b04c8b65123d04b63e38636bfc671fa3b53360823f886b42c' # voxtype-audio-bridge.asc
)
sha256sums=(
# Architecture-independent config and support files
'f4b2bccd56b31a6a50e1c0a8b6b72383dc1e636f9ccb8cb070442d58b7314579' # config/default.toml 'f4b2bccd56b31a6a50e1c0a8b6b72383dc1e636f9ccb8cb070442d58b7314579' # config/default.toml
'531c3658e229619e56bb01659fb81f401767b85e1d6e2acd1ac67ee3414a168c' # voxtype.service '531c3658e229619e56bb01659fb81f401767b85e1d6e2acd1ac67ee3414a168c' # voxtype.service
'65c95805d9b03ccc2fadb9d63a03ab79974b00091df8457ee8ef290ec6bd5b12' # voxtype.bash '65c95805d9b03ccc2fadb9d63a03ab79974b00091df8457ee8ef290ec6bd5b12' # voxtype.bash
@@ -189,16 +221,20 @@ sha256sums=(
) )
package() { package() {
# Install Whisper CPU binaries to /usr/lib/voxtype/ if [[ "$CARCH" == aarch64 ]]; then
install -Dm755 "$srcdir/voxtype-$pkgver-avx2" "$pkgdir/usr/lib/voxtype/voxtype-avx2" install -Dm755 "$srcdir/voxtype-$pkgver-cpu" "$pkgdir/usr/lib/voxtype/voxtype-cpu"
install -Dm755 "$srcdir/voxtype-$pkgver-avx512" "$pkgdir/usr/lib/voxtype/voxtype-avx512" install -Dm755 "$srcdir/voxtype-$pkgver-onnx" "$pkgdir/usr/lib/voxtype/voxtype-onnx"
else
# Install Whisper CPU binaries to /usr/lib/voxtype/
install -Dm755 "$srcdir/voxtype-$pkgver-avx2" "$pkgdir/usr/lib/voxtype/voxtype-avx2"
install -Dm755 "$srcdir/voxtype-$pkgver-avx512" "$pkgdir/usr/lib/voxtype/voxtype-avx512"
# Install Whisper Vulkan GPU binary # Install Whisper Vulkan GPU binary
install -Dm755 "$srcdir/voxtype-$pkgver-vulkan" "$pkgdir/usr/lib/voxtype/voxtype-vulkan" install -Dm755 "$srcdir/voxtype-$pkgver-vulkan" "$pkgdir/usr/lib/voxtype/voxtype-vulkan"
# Install ONNX CPU binaries (no GPU EP, no companion .so files) # Install ONNX CPU binaries (no GPU EP, no companion .so files)
install -Dm755 "$srcdir/voxtype-$pkgver-onnx-avx2" "$pkgdir/usr/lib/voxtype/voxtype-onnx-avx2" install -Dm755 "$srcdir/voxtype-$pkgver-onnx-avx2" "$pkgdir/usr/lib/voxtype/voxtype-onnx-avx2"
install -Dm755 "$srcdir/voxtype-$pkgver-onnx-avx512" "$pkgdir/usr/lib/voxtype/voxtype-onnx-avx512" install -Dm755 "$srcdir/voxtype-$pkgver-onnx-avx512" "$pkgdir/usr/lib/voxtype/voxtype-onnx-avx512"
# GPU-using ONNX binaries each live in their own subdirectory alongside # GPU-using ONNX binaries each live in their own subdirectory alongside
# the EP companion shared libs they dlopen at runtime. ort 2.0.0-rc.12's # the EP companion shared libs they dlopen at runtime. ort 2.0.0-rc.12's
@@ -248,7 +284,8 @@ package() {
# voxtype-onnx-rocm name. The AMD GPU EP changed from ROCm to MIGraphX # voxtype-onnx-rocm name. The AMD GPU EP changed from ROCm to MIGraphX
# in v0.7.0; ship one release with both names to soften the transition. # in v0.7.0; ship one release with both names to soften the transition.
# Drop in v0.8.0. # Drop in v0.8.0.
ln -sf "voxtype-onnx-migraphx" "$pkgdir/usr/lib/voxtype/voxtype-onnx-rocm" ln -sf "voxtype-onnx-migraphx" "$pkgdir/usr/lib/voxtype/voxtype-onnx-rocm"
fi
# /usr/bin/voxtype symlink and the unversioned voxtype-onnx-cuda symlink # /usr/bin/voxtype symlink and the unversioned voxtype-onnx-cuda symlink
# are created by the .install script's post_install/post_upgrade hooks # are created by the .install script's post_install/post_upgrade hooks
@@ -0,0 +1,11 @@
--- a/PKGBUILD
+++ b/PKGBUILD
@@ -14,7 +14,9 @@ arch=(any)
license=('GPL-2.0-only AND GPL-3.0-or-later')
-checkdepends=('dkms' 'fakeroot' 'LINUX-HEADERS')
+checkdepends=('dkms' 'fakeroot')
+checkdepends_x86_64=('LINUX-HEADERS')
+checkdepends_aarch64=('linux-aarch64-headers')
source=("xpadneo-v${pkgver}.tar.gz::${url}/archive/v${pkgver}.tar.gz")
b2sums=('2858e466c5fde7e3d06d878dd7fb695751375e11a112bd3d389f9db5a82ef3ffcc1e3431b213f4807aeb8f05fd4280c7ca2a0a56305b0e984eb9b70df4843735')
+4 -2
View File
@@ -9,11 +9,13 @@
pkgname=xpadneo-dkms pkgname=xpadneo-dkms
pkgdesc='Advanced Linux Driver for Xbox One Wireless Gamepad' pkgdesc='Advanced Linux Driver for Xbox One Wireless Gamepad'
pkgver=0.10.4 pkgver=0.10.4
pkgrel=1 pkgrel=1.1
url='https://github.com/atar-axis/xpadneo' url='https://github.com/atar-axis/xpadneo'
arch=(any) arch=(any)
license=('GPL-2.0-only AND GPL-3.0-or-later') license=('GPL-2.0-only AND GPL-3.0-or-later')
checkdepends=('dkms' 'fakeroot' 'LINUX-HEADERS') checkdepends=('dkms' 'fakeroot')
checkdepends_x86_64=('LINUX-HEADERS')
checkdepends_aarch64=('linux-aarch64-headers')
source=("xpadneo-v${pkgver}.tar.gz::${url}/archive/v${pkgver}.tar.gz") source=("xpadneo-v${pkgver}.tar.gz::${url}/archive/v${pkgver}.tar.gz")
b2sums=('2858e466c5fde7e3d06d878dd7fb695751375e11a112bd3d389f9db5a82ef3ffcc1e3431b213f4807aeb8f05fd4280c7ca2a0a56305b0e984eb9b70df4843735') b2sums=('2858e466c5fde7e3d06d878dd7fb695751375e11a112bd3d389f9db5a82ef3ffcc1e3431b213f4807aeb8f05fd4280c7ca2a0a56305b0e984eb9b70df4843735')
+1 -1
View File
@@ -7,7 +7,7 @@ Wants=network-online.target
Type=oneshot Type=oneshot
ExecStart=/bin/bash -c 'source /root/.omarchy/build-credentials && /root/omarchy-pkgs/bin/auto-release edge' ExecStart=/bin/bash -c 'source /root/.omarchy/build-credentials && /root/omarchy-pkgs/bin/auto-release edge'
Environment=OMARCHY_STATE_DIR=/root/.state Environment=OMARCHY_STATE_DIR=/root/.state
TimeoutStartSec=7200 TimeoutStartSec=43200
[Install] [Install]
WantedBy=multi-user.target WantedBy=multi-user.target
+1 -1
View File
@@ -12,7 +12,7 @@ Type=oneshot
# branch's worktree with OMARCHY_RC_PINS=1. # branch's worktree with OMARCHY_RC_PINS=1.
ExecStart=/bin/bash -c 'source /root/.omarchy/build-credentials && /root/omarchy-pkgs/bin/auto-release rc' ExecStart=/bin/bash -c 'source /root/.omarchy/build-credentials && /root/omarchy-pkgs/bin/auto-release rc'
Environment=OMARCHY_STATE_DIR=/root/.state Environment=OMARCHY_STATE_DIR=/root/.state
TimeoutStartSec=7200 TimeoutStartSec=43200
[Install] [Install]
WantedBy=multi-user.target WantedBy=multi-user.target
+1 -1
View File
@@ -7,7 +7,7 @@ Wants=network-online.target
Type=oneshot Type=oneshot
ExecStart=/bin/bash -c 'source /root/.omarchy/build-credentials && /root/omarchy-pkgs/bin/auto-release stable' ExecStart=/bin/bash -c 'source /root/.omarchy/build-credentials && /root/omarchy-pkgs/bin/auto-release stable'
Environment=OMARCHY_STATE_DIR=/root/.state Environment=OMARCHY_STATE_DIR=/root/.state
TimeoutStartSec=7200 TimeoutStartSec=43200
[Install] [Install]
WantedBy=multi-user.target WantedBy=multi-user.target