diff --git a/.github/workflows/sync-upstream.yml b/.github/workflows/sync-upstream.yml index 0083ec39..c19c9ae3 100644 --- a/.github/workflows/sync-upstream.yml +++ b/.github/workflows/sync-upstream.yml @@ -39,7 +39,7 @@ jobs: archlinux:base-devel bash -lc ' set -euo pipefail - pacman -Syu --noconfirm jq + pacman -Syu --noconfirm git jq groupadd -g "$HOST_GID" runner useradd -m -u "$HOST_UID" -g "$HOST_GID" runner diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 4aaea2b0..9fc270f3 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -25,8 +25,9 @@ jobs: -w /workspace \ archlinux:base-devel bash -lc ' set -euo pipefail - pacman -Syu --noconfirm jq + pacman -Syu --noconfirm git jq ./bin/sync-upstream self-test + ./bin/sync-rebuilds --self-test ./bin/omarchy-pkgs self-test ./bin/omarchy-release self-test ' diff --git a/.gitignore b/.gitignore index d5f201f4..3f0c128d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +.firecrawl/ src/ logs/ build-output/ diff --git a/README.md b/README.md index 34e76149..d57fcb1a 100644 --- a/README.md +++ b/README.md @@ -30,18 +30,57 @@ The filesystem no longer encodes release policy. Instead: ## Prerequisites ### 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 -# Run after each reboot -docker run --privileged --rm tonistiigi/binfmt --install arm64 - # 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 ``` -**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--`); +`auto-release ` works through the queues one architecture at a +time, each with its own backoff (`.build-failed--`), 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 @@ -300,10 +339,11 @@ bin/sync-upstream openai-codex-desktop # Update specific packages 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 -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 with a checksum manifest asset is pure -data, declared as `upstream` in `.omarchy/package.json` with no code at all: +A vendor shipping tagged GitHub releases is pure data, declared as `upstream` +in `.omarchy/package.json` with no code at all: ```json "upstream": { @@ -316,16 +356,107 @@ data, declared as `upstream` in `.omarchy/package.json` with no code at all: } ``` +`checksums` names the manifest asset the vendor publishes. A vendor publishing +none sets `"digests": true` instead, and the checksums come from the SHA-256 +digest GitHub's release API reports for every asset — see +`pkgbuilds/schist-bin/.omarchy/package.json`. Either way the artifacts +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 stripped for `pkgver`; drafts and prereleases are ignored. Only the 100 most recent releases are considered. The provider fails closed on anything it cannot read — an unusable tag, timestamp, or checksum stops the sync rather than being skipped. -A package may also declare `"min_release_age": "24h"` (`s`/`m`/`h`/`d` suffix or -bare seconds) to quarantine fresh releases until maintainers have had time to -pull a bad or compromised one. The newest release that has cleared the window -ships, so a fast release cadence cannot starve updates. The window is enforced +Projects that publish version tags but no checksum manifest can declare the +tag repository, the exact tag shape, and every source that should be hashed: + +```json +"upstream": { + "git_tags": "https://github.com/owner/project.git", + "tag_pattern": "v{pkgver}", + "sources": { + "any": ["https://github.com/owner/project/archive/refs/tags/{tag}.tar.gz"] + } +} +``` + +The newest matching tag is selected with pacman's `vercmp`; unrelated tags are +ignored. `tag_pattern` must contain exactly one `{pkgver}`. Source templates may +use `{tag}` and `{pkgver}`. Each expanded URL must be HTTPS and is downloaded +only when the discovered version is newer. A checked-in patch or other local +source can be included as `file:patch-name.patch`; it is hashed from the package +directory. Keys such as `any`, `x86_64`, and `aarch64` select the corresponding +`sha256sums` array. + +npm packages use the same source mapping, with `{npm_tarball}` available for +the tarball named by the selected dist-tag: + +```json +"upstream": { + "npm": "@scope/package", + "dist_tag": "latest", + "sources": { + "any": ["{npm_tarball}", "https://example.com/v{pkgver}/CHANGELOG.md"] + } +} +``` + +`dist_tag` defaults to `latest`. The registry's publication timestamp is +carried into the provider result, so `min_release_age` works for npm packages. + +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"` +(`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 +npm provide publication times; raw git tags and Debian Packages indexes do not, +so combining either with this policy fails closed. The newest release that has +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`, or the sync fails. A maintainer deliberately shipping inside the window runs `BYPASS_MIN_RELEASE_AGE=1 bin/sync-upstream ` locally and merges the @@ -378,7 +509,12 @@ A package names those dependencies in `.omarchy/package.json`: { "source": "aur", "sync": false, "rebuild_on": ["qt6-base", "qt6-declarative", "qt6-wayland"] } ``` -`bin/sync-rebuilds` reads each named package's version from the official repositories and compares it to `rebuilt_against`, the record of what the checked-in pkgrel was last bumped for. 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. @@ -386,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. -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. - -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 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 +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 @@ -586,7 +725,7 @@ Minimal examples: 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. -- `upstream`: optional for `local` packages whose vendor ships tagged GitHub releases with a checksum manifest asset. `{ "github": "owner/repo", "checksums": "SHASUMS256.txt", "assets": { "": "name-{tag}.tar.xz" } }` — 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 `. - `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. @@ -596,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 `. - `pkgrel`: optional Omarchy pkgrel suffix for a version-pinned rebuild bump. This emits `.` instead of replacing AUR's pkgrel. `offset` can be used only when preserving monotonic upgrades from old absolute pkgrel bumps. The metadata is removed automatically when AUR sync changes `pkgver`; the current package version is read from the checked-in PKGBUILD, so the version is not duplicated in JSON. - `rebuild_on`: optional array of package names this package links against closely enough that it must be rebuilt when they change, independent of its own source. Read by `bin/sync-rebuilds`. -- `rebuilt_against`: written by `bin/sync-rebuilds`. Records the version of each `rebuild_on` package that the current pkgrel was bumped for. +- `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. ### Build Matrix @@ -691,10 +830,15 @@ bin/repo release --package my-package - Mirrors: mirror.omarchy.org, rackspace, pkgbuild.com ### aarch64 -- QEMU emulation required on x86_64 hosts (slower) -- Uses Arch Linux ARM repositories +- Built on the repository host like x86_64; under QEMU when the host is x86_64 +- 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]` -- 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 @@ -745,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 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 -2. **auto-release-edge** (`*:1/5`): If a state file exists, builds all edge packages that need updates +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`): 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 4. **auto-release-stable** (`*:3/5`): If a state file exists, builds `release_ring=fast` packages for stable and replicates them to rc @@ -760,10 +904,11 @@ That cadence is only safe because of three guards: an operator expects. `check-versions` takes it too — its `git pull` would otherwise swap PKGBUILDs out from under a running build. - **Backoff on failure.** A failed release records the attempt in - `.build-failed-` and backs off exponentially — 10m, 20m, 40m, up to + `.build-failed--` and backs off exponentially — 10m, 20m, 40m, up to 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 - likely fix. Clear it by hand with `rm /root/.state/.build-failed-`. + likely fix. Clear it by hand with + `rm /root/.state/.build-failed--`. - **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. @@ -816,10 +961,14 @@ bin/repo timers --local # inspect this machine instead ``` State files are stored in `/root/.state/`: -- `.sync-needed-` — the packages queued for that channel, one per - line; the release run reads them to name what it is building -- `.build-failed-` — consecutive failure count, timestamp, and the - commit it failed on (drives the backoff; removing it forces a retry) +- `.sync-needed--` — the packages queued for that channel and + architecture, one per line; the release run reads them to name what it is + building +- `.build-failed--` — 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) diff --git a/bin/advance-channel b/bin/advance-channel index 5581a8e2..1effbc64 100755 --- a/bin/advance-channel +++ b/bin/advance-channel @@ -25,6 +25,22 @@ SKIP_PROD_CHECK=false FAST_RING_ONLY=false BOOTSTRAP=false 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() { echo "Usage: $0 --from --to [OPTIONS]" @@ -36,7 +52,8 @@ usage() { echo " or --bootstrap (one-time initial seed)" echo "" echo "Options:" - echo " --arch Target architecture (x86_64 or aarch64, default: x86_64)" + echo " --arch |all Target architecture (x86_64 or aarch64, default: x86_64);" + echo " all = every published architecture, one after another" echo " --package Advance only the named package(s)" echo " --fast-ring Restrict to fast-ring packages (stable -> rc parity copy)" echo " --bootstrap One-time stable -> rc seed before forward-only enforcement" @@ -65,8 +82,14 @@ while [[ $# -gt 0 ]]; do shift 2 ;; --arch) - ARCH="$2" - update_arch_paths + if [[ "$2" == "all" ]]; then + ALL_ARCHES=true + ARCH=all + else + require_valid_arch "$2" + ARCH="$2" + update_arch_paths + fi shift 2 ;; --package) @@ -134,6 +157,31 @@ case "$FROM->$TO" in ;; 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" TARGET_DIR="$REPO_ROOT/$TO/$ARCH" SOURCE_DB="$SOURCE_DIR/omarchy.db.tar.zst" diff --git a/bin/auto-release b/bin/auto-release index 4d6d0648..b7094bb9 100755 --- a/bin/auto-release +++ b/bin/auto-release @@ -1,6 +1,10 @@ #!/bin/bash # Run the release workflow for a channel when work is queued. -# Usage: auto-release +# Usage: auto-release [|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: # @@ -21,7 +25,7 @@ source "$BUILD_ROOT/helpers/paths.sh" source "$BUILD_ROOT/helpers/lock-helpers.sh" 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 # (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}" if [[ -z "$MIRROR" ]]; then - print_error "Usage: $0 " + print_error "Usage: $0 [|all]" echo " mirror: edge, rc, or stable" + echo " arch: one of $VALID_ARCHES, or all (default) for every published architecture" exit 1 fi @@ -39,17 +44,13 @@ if [[ "$MIRROR" != "edge" && "$MIRROR" != "rc" && "$MIRROR" != "stable" ]]; then exit 1 fi -STATE_FILE="$STATE_DIR/.sync-needed-$MIRROR" -FAIL_FILE="$STATE_DIR/.build-failed-$MIRROR" - -# Nothing queued: stay quiet. At a 5-minute cadence this is most invocations, -# and a header for each would bury the runs that matter in the journal. -if [[ ! -f "$STATE_FILE" ]]; then - exit 0 +if [[ "$ARCH_ARG" == "all" ]]; then + ARCHES=$(published_arches) +else + require_valid_arch "$ARCH_ARG" + ARCHES="$ARCH_ARG" fi -print_header "Processing Sync for $MIRROR" - # The build inputs are this checkout's contents; its HEAD identifies them. current_fingerprint() { git -C "$BUILD_ROOT" rev-parse HEAD 2>/dev/null || echo "unknown" @@ -65,57 +66,103 @@ backoff_seconds() { echo "$delay" } -FAIL_COUNT=0 -if [[ -f "$FAIL_FILE" ]]; then - # shellcheck disable=SC1090 - source "$FAIL_FILE" 2>/dev/null || true - FAIL_COUNT="${FAILURE_COUNT:-0}" - failed_at="${FAILURE_AT:-0}" - failed_fingerprint="${FAILURE_FINGERPRINT:-}" +# Returns 0 when this architecture's queue was processed (or was empty), 1 when +# the release failed. Backoff and "someone else holds the lock" are not +# failures: they are this tick deciding to do nothing. +release_arch() { + local arch="$1" + local state_file fail_file + state_file=$(sync_queue_file "$MIRROR" "$arch") + fail_file=$(sync_fail_file "$MIRROR" "$arch") - 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 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)" + # A queue written under the pre-architecture name belongs to x86_64. + if [[ "$arch" == "x86_64" && ! -f "$state_file" && -f "$(legacy_sync_queue_file "$MIRROR")" ]]; then + state_file=$(legacy_sync_queue_file "$MIRROR") + fi + if [[ "$arch" == "x86_64" && ! -f "$fail_file" && -f "$(legacy_sync_fail_file "$MIRROR")" ]]; then + fail_file=$(legacy_sync_fail_file "$MIRROR") fi -fi -# Non-blocking: a build in progress means this tick has nothing to do. -if ! try_release_lock; then - holder=$(release_lock_holder) - print_info "A release is already running (${holder:-holder unknown}) — skipping this tick" - exit 0 -fi + # Nothing queued: stay quiet. At a 5-minute cadence this is most + # invocations, and a header for each would bury the runs that matter in + # the journal. + [[ -f "$state_file" ]] || return 0 -print_info "State file found: $STATE_FILE" -print_info "Starting release workflow for $MIRROR..." + print_header "Processing Sync for $MIRROR ($arch)" -if "$BUILD_ROOT/bin/repo" release --mirror "$MIRROR" --skip-prod-check; then - print_success "Release completed successfully for $MIRROR" - rm -f "$STATE_FILE" - rm -f "$FAIL_FILE" - print_success "State file removed: $STATE_FILE" -else - status=$? - FAIL_COUNT=$((FAIL_COUNT + 1)) - cat >"$FAIL_FILE" </dev/null || true + fail_count="${FAILURE_COUNT:-0}" + failed_at="${FAILURE_AT:-0}" + failed_fingerprint="${FAILURE_FINGERPRINT:-}" + + 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" </dev/null 2>&1; then - print_info "Setting up QEMU for ARM64 emulation..." - setup_qemu +# A foreign target architecture runs under QEMU user emulation. Probe by +# actually running a container for the target platform: that is the only +# test that covers both "binfmt not registered" and "registered but broken". +HOST_ARCH=$(uname -m) +[[ "$HOST_ARCH" == "arm64" ]] && HOST_ARCH=aarch64 +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 -# Clean build-output directory to start fresh -print_info "Cleaning build workspace..." -rm -rf "$BUILD_OUTPUT_DIR"/* +# Clean build-output directory to start fresh, unless the caller seeded it +# with packages from an earlier job or is resuming an interrupted run. +if [[ $KEEP_BUILD_WORKSPACE == "1" ]]; then + print_info "Keeping existing build workspace..." +else + print_info "Cleaning build workspace..." + rm -rf "${BUILD_OUTPUT_DIR:?}"/* +fi mkdir -p "$BUILD_OUTPUT_DIR" # Show package info @@ -124,8 +211,20 @@ else print_info "Building unscoped packages for $MIRROR mirror" fi -# Build/update the Docker image -build_docker_image "$BUILD_DIR" "$ARCH" "$MIRROR" +# Build/update the Docker image, unless the caller prepared the exact image +# already (a workflow building it once with an external BuildKit cache). A +# missing image is an error rather than a silent rebuild: the point of the +# flag is that every job runs the same bytes. +IMAGE_TAG="omarchy-pkg-builder:latest-$ARCH-$MIRROR" +if [[ $SKIP_BUILDER_IMAGE == "1" ]]; then + if ! "$CONTAINER_ENGINE" image inspect "$IMAGE_TAG" >/dev/null 2>&1; then + print_error "Prepared builder image is unavailable: $IMAGE_TAG" + exit 1 + fi + print_info "Using prepared builder image: $IMAGE_TAG" +else + build_docker_image "$BUILD_DIR" "$ARCH" "$MIRROR" +fi print_info "Running package build..." @@ -133,9 +232,12 @@ print_info "Running package build..." mkdir -p "$BUILD_OUTPUT_DIR" mkdir -p "$REPO_DIR" -# Ensure output directories are writable by container user -make_dir_writable "$BUILD_OUTPUT_DIR" -make_dir_writable "$REPO_DIR" +# Rootful Docker writes as the image uid, so retain its existing permission +# workaround. Rootless Podman uses keep-id and must leave ownership/modes alone. +if [[ "$CONTAINER_ENGINE" == "docker" ]]; then + make_dir_writable "$BUILD_OUTPUT_DIR" + make_dir_writable "$REPO_DIR" +fi # Build Docker arguments DOCKER_ARGS=( @@ -144,6 +246,7 @@ DOCKER_ARGS=( -e MIRROR="$MIRROR" -e PACKAGES="$PACKAGES" -e OMARCHY_RC_PINS="${OMARCHY_RC_PINS:-}" + -e DEFER_RUNTIME_DEPS="$DEFER_RUNTIME_DEPS" -v "$BUILD_ROOT/build-output:/build-output" -v "$REPO_ROOT:/pkgs.omarchy.org" -v "$BUILD_DIR:/build:ro" @@ -151,11 +254,16 @@ DOCKER_ARGS=( -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 -IMAGE_TAG="omarchy-pkg-builder:latest-$ARCH-$MIRROR" 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=$? diff --git a/bin/check-versions b/bin/check-versions index fe765839..925570f6 100755 --- a/bin/check-versions +++ b/bin/check-versions @@ -1,6 +1,10 @@ #!/bin/bash # 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 ] +# --pull pull the repository first (the scheduled run does this) +# --arch check one architecture; default: every published one set -e @@ -10,11 +14,27 @@ source "$BUILD_ROOT/helpers/paths.sh" source "$BUILD_ROOT/helpers/package-metadata.sh" source "$BUILD_ROOT/helpers/lock-helpers.sh" -STATE_DIR="${OMARCHY_STATE_DIR:-/root/.state}" -ARCH="${ARCH:-x86_64}" 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" @@ -59,10 +79,26 @@ get_repo_version() { ' } +# The PKGBUILD's full version (epoch:pkgver-pkgrel) for the architecture being +# checked. Prints nothing when pkgver or pkgrel cannot be read: a partial +# version like "-" would compare unequal to everything published and queue a +# rebuild on every tick, so an unreadable PKGBUILD must not queue at all. get_pkgbuild_version() { local pkgdir="$1" - if [[ -f "$pkgdir/PKGBUILD" ]]; then - (cd "$pkgdir" && bash -c 'source PKGBUILD 2>/dev/null; if [[ -n "$epoch" ]]; then echo "${epoch}:${pkgver}-${pkgrel}"; else echo "${pkgver}-${pkgrel}"; fi') + local epoch pkgver pkgrel + + [[ -f "$pkgdir/PKGBUILD" ]] || return 1 + + epoch=$(package_pkgbuild_var "$pkgdir" epoch "$ARCH") + pkgver=$(package_pkgbuild_var "$pkgdir" pkgver "$ARCH") + pkgrel=$(package_pkgbuild_var "$pkgdir" pkgrel "$ARCH") + + [[ -n "$pkgver" && -n "$pkgrel" ]] || return 1 + + if [[ -n "$epoch" ]]; then + echo "${epoch}:${pkgver}-${pkgrel}" + else + echo "${pkgver}-${pkgrel}" fi } @@ -77,8 +113,8 @@ check_vcs_unchanged() { # If epoch or pkgrel changed in PKGBUILD, release even if upstream is unchanged. local pkgbuild_epoch pkgbuild_pkgrel - pkgbuild_epoch=$(cd "$pkgdir" && bash -c 'source PKGBUILD 2>/dev/null; echo "${epoch:-}"') - pkgbuild_pkgrel=$(cd "$pkgdir" && bash -c 'source PKGBUILD 2>/dev/null; echo "${pkgrel}"') + pkgbuild_epoch=$(package_pkgbuild_var "$pkgdir" epoch "$ARCH") + pkgbuild_pkgrel=$(package_pkgbuild_var "$pkgdir" pkgrel "$ARCH") local repo_pkgrel="${repo_version##*-}" local repo_no_pkgrel="${repo_version%-*}" @@ -106,12 +142,11 @@ check_package() { local mirror="$3" local pkgbuild_version repo_version - pkgbuild_version=$(get_pkgbuild_version "$pkgdir") - repo_version=$(get_repo_version "$pkg" "$mirror") - - if [[ -z "$pkgbuild_version" ]]; then + if ! pkgbuild_version=$(get_pkgbuild_version "$pkgdir") || [[ -z "$pkgbuild_version" ]]; then + print_warning "$pkg: could not read pkgver/pkgrel from its PKGBUILD for $ARCH — not queueing" return 1 fi + repo_version=$(get_repo_version "$pkg" "$mirror") if grep -qE '^pkgver[[:space:]]*\(\)' "$pkgdir/PKGBUILD"; then if [[ -n "$repo_version" && -n "$(package_extract_vcs_hash_from_version "$repo_version")" ]]; then @@ -147,11 +182,12 @@ check_package() { check_mirror() { 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 packages=() - print_info "Checking $mirror packages..." + print_info "Checking $mirror packages for $ARCH..." while IFS= read -r pkg; do local pkgdir="$PKGBUILDS_DIR/$pkg" @@ -159,7 +195,7 @@ check_mirror() { needs_build=true packages+=("$pkg") fi - done < <(packages_for_unscoped_build "$mirror") + done < <(packages_for_unscoped_build "$mirror" "$ARCH") echo "" @@ -168,18 +204,23 @@ check_mirror() { # 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". 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 "State file created: $state_file" else - print_info "${mirror^} is up to date" + print_info "${mirror^} ($ARCH) is up to date" fi echo "" } -check_mirror edge -check_mirror rc -check_mirror stable +# One pass per published architecture: the version comparison reads that +# architecture's channel databases, and each queue is its own file. +for ARCH in $ARCHES; do + update_arch_paths + check_mirror edge + check_mirror rc + check_mirror stable +done print_success "Version check complete!" diff --git a/bin/clean-docker b/bin/clean-docker index 4a5fc945..93c9a659 100755 --- a/bin/clean-docker +++ b/bin/clean-docker @@ -1,21 +1,27 @@ #!/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 set -e BUILD_ROOT=$(realpath "${BASH_SOURCE[0]%/*}/..") 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 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 -print_info "Clearing buildx cache..." -docker buildx prune -a -f +# Clear this engine's build cache. +print_info "Clearing build cache..." +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" diff --git a/bin/clean-repo b/bin/clean-repo index 493b85a6..8543a8a7 100755 --- a/bin/clean-repo +++ b/bin/clean-repo @@ -31,9 +31,13 @@ clean_packages() { # Skip signature files [[ "$pkg" == *.sig ]] && continue - # Extract package name (remove version and architecture) - # Format: name-version-release-arch.pkg.tar.* - local pkgname=$(echo "$pkg" | sed -E 's/-[0-9]+.*-(any|x86_64|i686)\.pkg\.tar\..*//') + # Format: name-version-release-arch.pkg.tar.*. Work from the right because + # package names can themselves contain version-like pieces (qt6-5compat, + # 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 if [[ -n "${packages[$pkgname]}" ]]; then diff --git a/bin/omarchy-pkgs b/bin/omarchy-pkgs index 58ff646b..b35016fb 100755 --- a/bin/omarchy-pkgs +++ b/bin/omarchy-pkgs @@ -21,9 +21,10 @@ BUILD_ROOT=$(realpath "${BASH_SOURCE[0]%/*}/..") source "$BUILD_ROOT/helpers/message-helpers.sh" source "$BUILD_ROOT/helpers/paths.sh" source "$BUILD_ROOT/helpers/host-helpers.sh" +source "$BUILD_ROOT/helpers/package-metadata.sh" 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) DEFAULT_RC_REF="quattro" SRCDEST_DIR="$BUILD_ROOT/.srcdest" @@ -92,7 +93,7 @@ version_is_rc() { version_is_prerelease "$1"; } pkgbuild_var() { local pkg="$1" var="$2" - (cd "$BUILD_ROOT/pkgbuilds/$pkg" && bash -c "source PKGBUILD 2>/dev/null; echo \"\${$var}\"") + package_pkgbuild_var "$BUILD_ROOT/pkgbuilds/$pkg" "$var" } # Prints the published version of $pkg from the edge DB. Distinguishes @@ -327,13 +328,19 @@ regenerate_checksums() { trigger_build_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 # 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 print_info "Triggering edge build locally (this is the build host)..." - if mkdir -p "${OMARCHY_STATE_DIR:-/root/.state}" && - touch "${OMARCHY_STATE_DIR:-/root/.state}/.sync-needed-edge" && - systemctl start --no-block omarchy-auto-release-edge.service; then + if queue_edge_builds && systemctl start --no-block omarchy-auto-release-edge.service; then print_success "Edge build triggered" else print_warning "Could not start the edge release service — the 6-hourly timer will pick it up" @@ -343,11 +350,11 @@ trigger_build_host() { 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@ or an ssh-config alias)." print_info "The 6-hourly auto-release timer will pick this up, or trigger manually:" - echo " ssh 'git -C /root/omarchy-pkgs pull --ff-only && touch /root/.state/.sync-needed-edge && systemctl start omarchy-auto-release-edge.service'" + echo " ssh 'git -C /root/omarchy-pkgs pull --ff-only && systemctl start omarchy-check-versions.service omarchy-auto-release-edge.service'" return 0 fi 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" else print_warning "Could not trigger $host — the 6-hourly timer will pick it up" diff --git a/bin/omarchy-release b/bin/omarchy-release index c64d2ed5..12e689bd 100755 --- a/bin/omarchy-release +++ b/bin/omarchy-release @@ -18,6 +18,7 @@ set -e BUILD_ROOT=$(realpath "${BASH_SOURCE[0]%/*}/..") source "$BUILD_ROOT/helpers/message-helpers.sh" +source "$BUILD_ROOT/helpers/docker-helpers.sh" source "$BUILD_ROOT/helpers/paths.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}" 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" 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 # the database cannot be read (callers must not mistake an outage for absence). published_version() { - local channel="$1" tmp descs + local channel="$1" arch="${2:-$OBSERVED_ARCH}" tmp descs tmp=$(mktemp) || return 2 # 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 # 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" return 2 fi @@ -207,6 +212,14 @@ published_version() { ' <<<"$descs" } +all_arches_at_version() { # all_arches_at_version + 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 # touching the working tree. 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 reset --hard origin/rc cd /root/omarchy-pkgs-rc -OMARCHY_RC_PINS=1 OMARCHY_REPO_ROOT=/root/omarchy-pkgs/pkgs.omarchy.org \ - bin/repo release --mirror rc --package omarchy omarchy-settings --skip-prod-check +# The pair is built once per published architecture (helpers/paths.sh on the +# 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() { @@ -278,19 +295,25 @@ host_advance() { # host_advance [extra args...] # against this machine's (likely stale) local tree would be wrong. if ! resolve_repo_host "$REPO_HOST_OVERRIDE" >/dev/null && ! on_repo_host; then 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 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 [timeout-seconds] - local channel="$1" want="$2" timeout="${3:-3600}" waited=0 got - print_info "Waiting for $want to appear in the $channel channel (up to $((timeout / 60))m)..." + local channel="$1" want="$2" timeout="${3:-3600}" waited=0 arch got pending + print_info "Waiting for $want in $channel for: $(published_arches | tr '\n' ' ')" while ((waited < timeout)); do - got=$(published_version "$channel" 2>/dev/null) || got="" - if [[ "${got%-*}" == "$want" ]]; then - print_success "$channel now serves omarchy $got" + pending="" + for arch in $(published_arches); do + 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 fi sleep 60 @@ -298,7 +321,7 @@ wait_for_published() { # wait_for_published [timeout-seconds printf '.' >&2 done 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." return 1 } @@ -352,8 +375,8 @@ iso_checkout() { # prints a usable omarchy-iso checkout, cloning to tmp if neede build_iso() { # build_iso [--rc] local version="$1" rc_flag="${2:-}" dir dir=$(iso_checkout) || return 1 - if ! command -v docker >/dev/null || ! docker info >/dev/null 2>&1; then - print_warning "Docker unavailable — cannot build the ISO here. Run on a Docker machine:" + if [[ -z "$CONTAINER_ENGINE" ]] || ! "$CONTAINER_ENGINE" info >/dev/null 2>&1; then + 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" return 1 fi @@ -662,15 +685,13 @@ cmd_rc() { if [[ -n "$pin" ]]; then local pin_ver="${pin%% *}" pin_commit="${pin##* }" if [[ "$pin_commit" == "$head" && "$pin_ver" =~ ^${version//./\\.}rc[0-9]+$ ]]; then - local pub - pub=$(published_version rc 2>/dev/null) || pub="" - if [[ "${pub%-*}" == "$pin_ver" ]]; then - print_success "$pin_ver is already cut from this head and published to rc" + if all_arches_at_version rc "$pin_ver"; then + print_success "$pin_ver is already cut from this head and published to rc on every architecture" maybe_iso "$pin_ver" rc "$iso_mode" return 0 fi 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" maybe_iso "$pin_ver" rc "$iso_mode" return 0 @@ -686,7 +707,7 @@ cmd_rc() { new_ver="${new_pin%% *}" print_success "Pinned $new_ver (rc branch pushed)" - trigger_rc_build || true + trigger_rc_build || return 1 if [[ "$wait" == true ]]; then wait_for_published rc "$new_ver" || return 1 fi @@ -708,9 +729,7 @@ cmd_ship() { exit 1 fi version=$(branch_to_version "$branch") - local stable_now - stable_now=$(published_version stable 2>/dev/null) || stable_now="" - if [[ "${stable_now%-*}" == "$version" ]] && + if all_arches_at_version stable "$version" && 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" exit 0 @@ -752,10 +771,8 @@ cmd_ship() { echo " omarchy-release rc" exit 1 fi - local pub - pub=$(published_version rc 2>/dev/null) || pub="" - if [[ "${pub%-*}" != "$pin_ver" ]]; then - print_error "$pin_ver is pinned but rc serves '${pub:-nothing}' — the candidate build hasn't published" + if ! all_arches_at_version rc "$pin_ver"; then + print_error "$pin_ver is pinned but is not published for every architecture" echo "Wait for it (or re-run: omarchy-release rc), then ship." exit 1 fi @@ -799,10 +816,9 @@ cmd_ship() { # 2. Final pins into rc. Resolve the tag we just established so the final # PKGBUILDs record both its provenance and its exact commit. - local rc_pub stable_pub - rc_pub=$(published_version rc 2>/dev/null) || rc_pub="" - if [[ "${rc_pub%-*}" == "$version" ]]; then - print_success "2/7 Final $version already published to rc" + local stable_pub + if all_arches_at_version rc "$version"; then + print_success "2/7 Final $version already published to rc on every architecture" else if [[ "$pin_ver" != "$version" ]]; then print_info "2/7 Pinning final $version from tag v$version..." @@ -810,22 +826,21 @@ cmd_ship() { else print_info "2/7 Final $version pinned — re-triggering build" fi - trigger_rc_build || true + trigger_rc_build || exit 1 wait_for_published rc "$version" || exit 1 fi # 3. Promote rc -> stable - stable_pub=$(published_version stable 2>/dev/null) || stable_pub="" - if [[ "${stable_pub%-*}" == "$version" ]]; then - print_success "3/7 Stable already serves $version" + if all_arches_at_version stable "$version"; then + print_success "3/7 Stable already serves $version on every architecture" else host_advance rc stable || exit 1 - stable_pub=$(published_version stable 2>/dev/null) || stable_pub="" - if [[ "${stable_pub%-*}" != "$version" ]]; then - print_error "Promotion ran but stable serves '${stable_pub:-nothing}' — investigate before continuing" + if ! all_arches_at_version stable "$version"; then + print_error "Promotion ran but stable does not serve $version on every architecture" exit 1 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 # 4. Final pins onto master (keeps edge overlap publishing and the repo record) @@ -908,7 +923,7 @@ next_step() { # prints "|" last=$(newest_release_branch 2>/dev/null) || last="" if [[ -n "$last" && "$STABLE_VER" != "" ]]; then 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 echo "ship|$last_ver is tagged but not fully shipped — resume ship" return @@ -924,7 +939,7 @@ next_step() { # prints "|" echo "ship|Final $TRAIN_VER is pinned — finish shipping (re-runs are safe)" elif [[ "$pin_commit" != "$TRAIN_HEAD" ]]; then 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" else 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 "curl available" command -v curl check "upstream reachable ($UPSTREAM_URL)" git ls-remote "$UPSTREAM_URL" HEAD - local ch + local ch arch for ch in edge stable; do - if published_version "$ch" >/dev/null 2>&1; then - print_success "$ch channel db readable" + for arch in $(published_arches); do + 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 - print_error "$ch channel db readable" - failures=$((failures + 1)) + print_warning "rc/$arch channel db not readable — bootstrap it first: bin/repo bootstrap-rc (on the build host)" fi 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 if host=$(repo_host); then check "build host ssh ($host)" ssh -o ConnectTimeout=10 "$host" true @@ -1016,10 +1035,10 @@ cmd_doctor() { else print_warning "no build host configured — set OMARCHY_REPO_HOST, --host, or write an ssh destination (root@ or an ssh-config alias) to $BUILD_ROOT/.repo-host; until then builds trigger on the 6h timer only" fi - if command -v docker >/dev/null && docker info >/dev/null 2>&1; then - print_success "docker available (ISO builds possible here)" + if [[ -n "$CONTAINER_ENGINE" ]] && "$CONTAINER_ENGINE" info >/dev/null 2>&1; then + print_success "$CONTAINER_ENGINE available (ISO builds possible here)" else - print_warning "docker unavailable — ISO builds will print instructions instead" + print_warning "container engine unavailable — ISO builds will print instructions instead" fi echo "" 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 "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" + + # 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 "" if ((failures == 0)); then print_success "Self-test passed" diff --git a/bin/push-build b/bin/push-build index 5b850126..a4abae99 100755 --- a/bin/push-build +++ b/bin/push-build @@ -149,11 +149,16 @@ fi # leave nvidia-580xx-dkms and opencl-nvidia-580xx behind. An output's own name # still matches, for pushing just one of them on purpose. # -# pkgbase comes from .PKGINFO rather than the PKGBUILD: it is what makepkg -# actually recorded, and it needs no guessing about which directory built what. -pkgbase_of() { +# Package identity comes from .PKGINFO rather than the filename or PKGBUILD: it +# is what makepkg actually recorded, and it handles epochs and split packages. +package_identity_of() { 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=() @@ -161,14 +166,18 @@ if [[ -z "$PACKAGES" ]]; then FILES=("${ALL_FILES[@]}") else declare -A MATCHED=() + declare -A LATEST_FILE=() + declare -A LATEST_VERSION=() for file in "${ALL_FILES[@]}"; do - # name-version-release-arch.pkg.tar.zst -> name - pkgname="${file%-*-*-*.pkg.tar.*}" - pkgbase=$(pkgbase_of "$BUILD_OUTPUT_DIR/$file") + IFS=$'\t' read -r pkgname pkgbase pkgver < <(package_identity_of "$BUILD_OUTPUT_DIR/$file") for wanted in $PACKAGES; do if [[ "$pkgname" == "$wanted" || "$pkgbase" == "$wanted" ]]; then - FILES+=("$file") 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 fi done @@ -181,6 +190,8 @@ else exit 1 fi done + + mapfile -t FILES < <(printf '%s\n' "${LATEST_FILE[@]}" | sort) fi if [[ ${#FILES[@]} -eq 0 ]]; then diff --git a/bin/release b/bin/release index d40fa0f8..193bfb52 100755 --- a/bin/release +++ b/bin/release @@ -114,7 +114,11 @@ BUILT_FILES="" # 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. 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") if [[ "$DRY_RUN" != true ]]; then diff --git a/bin/remove-package b/bin/remove-package index 6c798377..4700402b 100755 --- a/bin/remove-package +++ b/bin/remove-package @@ -62,8 +62,8 @@ if [[ ! -d "$REPO_DIR" ]]; then exit 1 fi -# Check Docker is available -check_docker +# Check the selected container engine is available +check_engine # Find package files to confirm before running Docker cd "$REPO_DIR" @@ -88,24 +88,29 @@ if [[ ! $REPLY =~ ^[Yy]$ ]]; then exit 0 fi -# Build/update the Docker image (always use x86_64 for removal - it's architecture independent) -# repo-remove is mirror-independent — always use the edge x86_64 image -build_docker_image "$BUILD_DIR" "x86_64" "edge" +# repo-remove is architecture-independent, so use a host-native edge image. +TOOL_ARCH=$(docker_native_arch) || { + print_error "Unsupported host architecture: $(uname -m)" + exit 1 +} +build_docker_image "$BUILD_DIR" "$TOOL_ARCH" "edge" acquire_release_lock || exit 1 print_info "Removing package..." -# Ensure directory is writable by container user -make_dir_writable "$REPO_DIR" +# Rootless Podman uses keep-id and leaves host ownership/modes intact. +if [[ "$CONTAINER_ENGINE" == "docker" ]]; then + make_dir_writable "$REPO_DIR" +fi -# Run the removal script in Docker (always use x86_64 image) -docker run --rm --platform linux/amd64 \ +# Run the removal script in the host-native image. +"$CONTAINER_ENGINE" run "${CONTAINER_RUN_ARGS[@]}" --rm "$(get_platform_arg "$TOOL_ARCH")" \ -e ARCH="$ARCH" \ -e MIRROR="$MIRROR" \ -v "$REPO_ROOT:/pkgs.omarchy.org" \ -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=$? diff --git a/bin/setup b/bin/setup index 5467be64..69dfe0e9 100755 --- a/bin/setup +++ b/bin/setup @@ -15,6 +15,8 @@ set -e BUILD_ROOT=$(realpath "${BASH_SOURCE[0]%/*}/..") source "$BUILD_ROOT/helpers/message-helpers.sh" +source "$BUILD_ROOT/helpers/docker-helpers.sh" +source "$BUILD_ROOT/helpers/paths.sh" CHECK_ONLY=false SKIP_TIMERS=false @@ -61,10 +63,12 @@ if command -v apt-get >/dev/null 2>&1; then DISTRO="debian" PKG_BSDTAR="libarchive-tools" PKG_DOCKER="docker.io" + PKG_PODMAN="podman" elif command -v pacman >/dev/null 2>&1; then DISTRO="arch" PKG_BSDTAR="libarchive" PKG_DOCKER="docker" + PKG_PODMAN="podman" else print_error "Unsupported distribution — need apt-get or pacman" exit 1 @@ -72,18 +76,36 @@ fi 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 print_error "Run as root (installing packages and systemd units)" exit 1 fi -# Docker and the release timers are both systemd units. Say so plainly rather -# than failing later on a missing command — a container is the usual way to end -# up here, and it cannot be a repository host. +# The release timers are systemd units. Say so plainly rather than failing +# later on a missing command — a container is the usual way to end up here, +# and it cannot be a repository host. if [[ "$CHECK_ONLY" != true ]] && ! command -v systemctl >/dev/null 2>&1; then print_error "systemctl not found — the repository host must run systemd" 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." exit 1 fi @@ -139,24 +161,27 @@ else fi 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 # replacing that underneath a working builder would be a poor trade for # tidiness. -print_info "Checking Docker..." +print_info "Checking $ENGINE_NAME..." -if command -v docker >/dev/null 2>&1; then - print_step "docker present: $(docker --version 2>/dev/null | head -1)" - if docker info >/dev/null 2>&1; then - print_success "Docker is installed and running — leaving it alone" +if command -v "$CONTAINER_ENGINE" >/dev/null 2>&1; then + print_step "$CONTAINER_ENGINE present: $("$CONTAINER_ENGINE" --version 2>/dev/null | head -1)" + if "$CONTAINER_ENGINE" info >/dev/null 2>&1; then + print_success "$ENGINE_NAME is installed and available — leaving it alone" 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 print_info "Docker is installed but not running — starting it" 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" else print_error "Docker is installed but still not responding" @@ -165,16 +190,24 @@ if command -v docker >/dev/null 2>&1; then fi fi elif [[ "$CHECK_ONLY" == true ]]; then - print_warning "Would install $PKG_DOCKER and enable it" -else - 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" + if [[ "$CONTAINER_ENGINE" == "docker" ]]; then + print_warning "Would install $PKG_ENGINE and enable it" else - print_error "Docker installed but not responding" - echo " Check 'systemctl status docker' — builds cannot run without it." + print_warning "Would install $PKG_ENGINE" + 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 fi fi @@ -237,6 +270,10 @@ echo "" # --- 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) if [[ "$SKIP_TIMERS" == true ]]; then diff --git a/bin/sign b/bin/sign index 70779e8c..e1e99cf3 100755 --- a/bin/sign +++ b/bin/sign @@ -52,8 +52,8 @@ if [[ ! -d "$BUILD_OUTPUT_DIR" ]]; then exit 1 fi -# Check Docker is available -check_docker +# Check the selected container engine is available +check_engine # Check GPG credentials are in environment if [[ -z "$GPG_PRIVATE_KEY" ]]; then @@ -66,23 +66,30 @@ if [[ -z "$GPG_PASSPHRASE" ]]; then exit 1 fi -# Build/update the Docker image (always use x86_64 for signing - it's architecture independent) -build_docker_image "$BUILD_DIR" "x86_64" "$MIRROR" +# Signing is architecture-independent, so run its utility container natively +# 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..." -# Ensure output directory is writable by container user -make_dir_writable "$BUILD_OUTPUT_DIR" +# Rootless Podman uses keep-id and leaves host ownership/modes intact. +if [[ "$CONTAINER_ENGINE" == "docker" ]]; then + make_dir_writable "$BUILD_OUTPUT_DIR" +fi -# Run the signing script in Docker (always use x86_64 image) -docker run --rm --platform linux/amd64 \ +# Run the signing script in the host-native image. +"$CONTAINER_ENGINE" run "${CONTAINER_RUN_ARGS[@]}" --rm "$(get_platform_arg "$TOOL_ARCH")" \ -e ARCH="$ARCH" \ -e MIRROR="$MIRROR" \ -e GPG_PRIVATE_KEY="$GPG_PRIVATE_KEY" \ -e GPG_PASSPHRASE="$GPG_PASSPHRASE" \ -v "$BUILD_ROOT/build-output:/build-output" \ -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=$? diff --git a/bin/sync-rebuilds b/bin/sync-rebuilds index 7a0336b4..3009b898 100755 --- a/bin/sync-rebuilds +++ b/bin/sync-rebuilds @@ -17,11 +17,14 @@ SELF_TEST=false # rebuilding for it would ship a package built against the wrong ABI. OFFICIAL_REPOS=" core extra multilib core-debug extra-debug " -# The published repository, used as the floor a bumped pkgrel has to clear. -# Only x86_64 is published today; aarch64 has no repository to compare against. +# The published repositories are the floor a bumped pkgrel has to clear. PUBLISHED_BASE_URL="${OMARCHY_PUBLISHED_BASE_URL:-https://pkgs.omarchy.org}" 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() { cat </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 + case "$1" in + x86_64) native_repo_version "$2" ;; + aarch64) alarm_repo_version "$2" ;; + *) return 1 ;; + esac +} + declare -A PUBLISHED_VERSION=() PUBLISHED_LOADED=false -PUBLISHED_AVAILABLE=true remember_published() { local name="$1" @@ -131,34 +188,35 @@ load_published_versions() { [[ "$PUBLISHED_LOADED" == true ]] && return 0 PUBLISHED_LOADED=true - local mirror db name base version + local arch mirror db name base version - for mirror in "${PUBLISHED_MIRRORS[@]}"; do - db="$TEMP_DIR/published-$mirror.db.tar.zst" + for arch in $(published_arches); do + for mirror in "${PUBLISHED_MIRRORS[@]}"; do + db="$TEMP_DIR/published-$mirror-$arch.db.tar.zst" - if ! curl -fsSL --max-time 120 -o "$db" \ - "$PUBLISHED_BASE_URL/$mirror/$PUBLISHED_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" - PUBLISHED_AVAILABLE=false - continue - fi + if ! curl -fsSL --max-time 120 -o "$db" \ + "$PUBLISHED_BASE_URL/$mirror/$arch/omarchy.db.tar.zst" 2>/dev/null; then + print_warning "Could not read the published $mirror/$arch database; bumps are not checked against it this run" + continue + fi - while IFS=$'\t' read -r name base version; do - [[ -n "$name" && -n "$version" ]] && remember_published "$name" "$version" - [[ -n "$base" && -n "$version" ]] && remember_published "$base" "$version" - done < <( - tar -xOf "$db" --wildcards '*/desc' 2>/dev/null | awk ' - function emit() { - if (name != "" && version != "") print name "\t" 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() } - ' - ) + while IFS=$'\t' read -r name base version; do + [[ -n "$name" && -n "$version" ]] && remember_published "$name" "$version" + [[ -n "$base" && -n "$version" ]] && remember_published "$base" "$version" + done < <( + tar -xOf "$db" --wildcards '*/desc' 2>/dev/null | awk ' + function emit() { + if (name != "" && version != "") print name "\t" 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() } + ' + ) + done done } @@ -179,7 +237,7 @@ pkgbuild_field() { local package_dir="$1" local field="$2" - (cd "$package_dir" && env -u OMARCHY_SRC bash -c "source PKGBUILD 2>/dev/null; echo \"\${$field:-}\"") + package_pkgbuild_var "$package_dir" "$field" } # 2 -> 3, and 1.1 -> 1.2. Anything else is a pkgrel this command has no business @@ -252,7 +310,17 @@ record_triggers() { local package_dir="$1" 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 @@ -300,21 +368,41 @@ sync_package() { print_info "Checking $package against ${triggers[*]}..." - local current="{}" trigger version - for trigger in "${triggers[@]}"; do - version=$(repo_version "$trigger") - if [[ -z "$version" ]]; then - print_error " $trigger is in no official repository; leaving $package alone" - ((++FAILED)) - return 0 - fi - if ! current=$(jq -c --arg name "$trigger" --arg version "$version" '.[$name] = $version' <<<"$current"); then - print_error " Could not record $trigger $version for $package" + local current="{}" arch arch_current trigger version considered=0 + for arch in $(published_arches); do + package_supports_arch "$package_dir" "$arch" || continue + considered=$((considered + 1)) + arch_current="{}" + for trigger in "${triggers[@]}"; do + if ! version=$(repo_version "$arch" "$trigger"); then + print_error " Could not read $arch repository versions; leaving $package alone" + ((++FAILED)) + 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)) return 0 fi done + if ((considered == 0)); then + print_info " Skipping: not built for any published architecture" + ((++SKIPPED)) + return 0 + fi + local recorded if ! recorded=$(package_metadata_value "$package_dir" '.rebuilt_against' ""); then print_error " Could not read .omarchy/package.json for $package" @@ -323,13 +411,20 @@ sync_package() { fi [[ -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 # not carry reads as changed instead of going unexamined forever. local moved if ! moved=$(jq -r --argjson recorded "$recorded" ' - to_entries - | map(select($recorded[.key] != .value) - | "\(.key) \($recorded[.key] // "unrecorded") -> \(.value)") + [to_entries[] as $arch + | $arch.value | to_entries[] as $trigger + | select($recorded[$arch.key][$trigger.key] != $trigger.value) + | "\($arch.key)/\($trigger.key) \($recorded[$arch.key][$trigger.key] // "unrecorded") -> \($trigger.value)"] | join(", ") ' <<<"$current"); then print_error " Could not compare recorded trigger versions for $package" @@ -338,7 +433,7 @@ sync_package() { fi 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)) return 0 fi @@ -466,11 +561,11 @@ selftest_root() { } 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" 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" } @@ -514,19 +609,51 @@ selftest_published() { cat > "$root/stub/curl" <<'STUB' #!/bin/bash out="" +url="" while [[ $# -gt 0 ]]; do case "$1" in -o) out="$2"; shift 2 ;; - *) shift ;; + *) url="$1"; shift ;; esac 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 cp "$db" "$out" STUB 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() { local failures=0 local root @@ -545,7 +672,8 @@ cmd_self_test() { local root="$1" shift 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" } @@ -563,7 +691,7 @@ cmd_self_test() { check "run succeeds" 0 "$(run_case "$root")" check "pkgrel bumped" 2 "$(pkgrel_of "$root/pkgbuilds/t-partial")" 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:" root=$(selftest_root fresh) @@ -573,7 +701,7 @@ cmd_self_test() { check "run succeeds" 0 "$(run_case "$root")" check "pkgrel bumped" 2 "$(pkgrel_of "$root/pkgbuilds/t-fresh")" 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:" root=$(selftest_root current) @@ -612,6 +740,67 @@ cmd_self_test() { check "suffix recorded for the next AUR sync" 1 \ "$(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 "A PKGBUILD that branches on CARCH at file scope still reads its version:" + root=$(selftest_root carch-branch) + mkdir -p "$root/pkgbuilds/t-carch/.omarchy" + cat > "$root/pkgbuilds/t-carch/PKGBUILD" <<'PKG' +pkgname=t-carch +case "$CARCH" in + x86_64) _suffix=x64 ;; + aarch64) _suffix=arm64 ;; + *) return 1 ;; +esac +pkgver=1.0 +pkgrel=3 +arch=(x86_64 aarch64) +PKG + echo '{"source":"local"}' > "$root/pkgbuilds/t-carch/.omarchy/package.json" + check "pkgver read for x86_64" "1.0" "$(package_pkgbuild_var "$root/pkgbuilds/t-carch" pkgver x86_64)" + check "pkgrel read for x86_64" "3" "$(package_pkgbuild_var "$root/pkgbuilds/t-carch" pkgrel x86_64)" + check "arch-specific value follows CARCH" "arm64" "$(package_pkgbuild_var "$root/pkgbuilds/t-carch" _suffix aarch64)" + check "unsupported arch reports failure" 1 "$(package_pkgbuild_var "$root/pkgbuilds/t-carch" pkgver armv7h >/dev/null; echo $?)" + echo "" if [[ "$failures" -eq 0 ]]; then print_success "Self-test passed" @@ -626,9 +815,9 @@ if [[ "$SELF_TEST" == true ]]; then exit $? 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 - 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 fi done diff --git a/bin/sync-upstream b/bin/sync-upstream index a0da5f3c..8b2864d2 100755 --- a/bin/sync-upstream +++ b/bin/sync-upstream @@ -18,11 +18,11 @@ Usage: $0 [PACKAGE...] Update packages that track an upstream vendor release feed instead of the AUR. -A package whose upstream ships tagged GitHub releases with a checksum manifest -opts in declaratively, via "upstream" in .omarchy/package.json (see -helpers/upstream-github.sh for the schema); no code needed. Anything with a -bespoke feed provides pkgbuilds//.omarchy/upstream.sh instead, a hook -that reports the newest upstream release as JSON on stdout: +Packages opt in declaratively through "upstream" in .omarchy/package.json. +Providers cover GitHub Releases with checksum manifests or API asset digests, +semver-shaped git tags, npm dist-tags, and plain Debian Packages indexes. See +README.md for the schemas. Anything outside those conventions may provide +pkgbuilds//.omarchy/upstream.sh, a hook that reports JSON on stdout: { "pkgver": "1.2.3", @@ -332,27 +332,27 @@ sync_package() { return 0 fi - local github_repo has_upstream=false - github_repo=$(package_upstream_github_repo "$package_dir") + local provider has_upstream=false + provider=$(package_upstream_provider "$package_dir") if package_has_upstream_provider "$package_dir"; then has_upstream=true fi # A present-but-unusable declaration fails loudly; treating it like "no # upstream source" would silently drop the package from scheduled runs. - if [[ "$has_upstream" == true && -z "$github_repo" ]]; then - print_error "Package $package has an unusable upstream declaration (needs a github owner/repo)" + if [[ "$has_upstream" == true && -z "$provider" ]]; then + print_error "Package $package has an unusable or ambiguous upstream declaration" ((++FAILED)) return 0 fi - if [[ -n "$github_repo" && -f "$hook" ]]; then - print_error "Package $package declares both upstream.github and an upstream.sh hook; keep exactly one" + if [[ -n "$provider" && -f "$hook" ]]; then + print_error "Package $package declares both an upstream provider and an upstream.sh hook; keep exactly one" ((++FAILED)) return 0 fi - if [[ -z "$github_repo" && ! -f "$hook" ]]; then + if [[ -z "$provider" && ! -f "$hook" ]]; then if [[ "$SPECIFIC_MODE" == true ]]; then print_error "Package $package has neither an upstream declaration nor .omarchy/upstream.sh" ((++FAILED)) @@ -372,10 +372,16 @@ sync_package() { print_info "Checking $package for upstream releases..." - local release - if [[ -n "$github_repo" ]]; then - if ! release=$(github_upstream_release "$package_dir" "$min_age"); then - print_error "GitHub release provider failed for $package" + local release release_status=0 + if [[ -n "$provider" ]]; then + case "$provider" in + github) release=$(github_upstream_release "$package_dir" "$min_age") || release_status=$? ;; + git_tags) release=$(git_tags_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 + if [[ ${release_status:-0} -ne 0 ]]; then + print_error "$provider upstream provider failed for $package" ((++FAILED)) return 0 fi @@ -461,8 +467,8 @@ sync_package() { # paths. Covers release selection (fallback past quarantined releases, # draft/prerelease filtering, bypass, unchanged version), failure paths # (unusable tags/timestamps, missing checksums), checksum template mapping -# for both architectures, the min_release_age backstop, the duration parser, -# and manifest validation. +# for both architectures, release API digests, the min_release_age backstop, +# the duration parser, and manifest validation. cmd_self_test() { local failures=0 @@ -539,6 +545,31 @@ EOF check "bypass lifts the quarantine" "2.0.0" "$(jq -r '.pkgver // ""' <<<"$out")" check "x86_64 checksum via * binary-mode prefix" "$sum_x20" "$(jq -r '.sha256sums.x86_64[0] // ""' <<<"$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="" + check "latest_only ignores incompatible historical tags" "1.9.0" "$(jq -r '.pkgver // ""' <<<"$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="" check "everything quarantined reports no update" "{}" "$(jq -c . <<<"$out")" @@ -562,6 +593,187 @@ EOF rc=0; github_upstream_release "$pkg" 86400 >/dev/null 2>&1 || rc=$? check "missing aarch64 checksum fails the sync" "1" "$rc" + # A vendor publishing no manifest: checksums come from the digests the + # release API reports per asset, with nothing fetched beyond the feed. + echo "Release API digests:" + local digpkg="$TEMP_DIR/selftest-digests" + mkdir -p "$digpkg/.omarchy" + printf 'pkgver=1.0.0\npkgrel=1\n' > "$digpkg/PKGBUILD" + cat > "$digpkg/.omarchy/package.json" <<'EOF' +{ + "source": "local", + "upstream": { + "github": "example/tool", + "digests": true, + "assets": { + "x86_64": "tool-{pkgver}-1-x86_64.pkg.tar.zst", + "aarch64": "tool-{pkgver}-1-aarch64.pkg.tar.zst" + } + } +} +EOF + FIXTURE_RELEASES=$(jq -n --arg old "$old2d" --arg x "$sum_x19" --arg a "$sum_a19" '[ + {tag_name: "v1.9.0", published_at: $old, draft: false, prerelease: false, assets: [ + {name: "tool-1.9.0-1-x86_64.pkg.tar.zst", digest: ("sha256:" + $x)}, + {name: "tool-1.9.0-1-aarch64.pkg.tar.zst", digest: ("sha256:" + $a)}, + {name: "tool-1.9.0-1-x86_64.rpm", digest: "sha256:0000000000000000000000000000000000000000000000000000000000000000"} + ]} + ]') + FIXTURE_CHECKSUMS="manifest must not be consulted" + out=$(github_upstream_release "$digpkg" 0 2>/dev/null) || out="" + check "x86_64 checksum via the asset digest" "$sum_x19" "$(jq -r '.sha256sums.x86_64[0] // ""' <<<"$out")" + check "aarch64 checksum via the asset digest" "$sum_a19" "$(jq -r '.sha256sums.aarch64[0] // ""' <<<"$out")" + + FIXTURE_RELEASES=$(jq -n --arg old "$old2d" --arg x "$sum_x19" '[ + {tag_name: "v1.9.0", published_at: $old, draft: false, prerelease: false, assets: [ + {name: "tool-1.9.0-1-x86_64.pkg.tar.zst", digest: ("sha256:" + $x)}, + {name: "tool-1.9.0-1-aarch64.pkg.tar.zst"} + ]} + ]') + rc=0; github_upstream_release "$digpkg" 0 >/dev/null 2>&1 || rc=$? + check "asset without a digest fails the sync" "1" "$rc" + + FIXTURE_RELEASES=$(jq -n --arg old "$old2d" --arg x "$sum_x19" '[ + {tag_name: "v1.9.0", published_at: $old, draft: false, prerelease: false, assets: [ + {name: "tool-1.9.0-1-x86_64.pkg.tar.zst", digest: ("sha256:" + $x)}, + {name: "tool-1.9.0-2-aarch64.pkg.tar.zst", digest: ("sha256:" + $x)} + ]} + ]') + rc=0; github_upstream_release "$digpkg" 0 >/dev/null 2>&1 || rc=$? + check "asset re-cut under another release number fails the sync" "1" "$rc" + + FIXTURE_RELEASES=$(jq -n --arg old "$old2d" --arg x "$sum_x19" --arg a "$sum_a19" '[ + {tag_name: "v1.9.0", published_at: $old, draft: false, prerelease: false, assets: [ + {name: "tool-1.9.0-1-x86_64.pkg.tar.zst", digest: $x}, + {name: "tool-1.9.0-1-aarch64.pkg.tar.zst", digest: ("sha256:" + $a)} + ]} + ]') + rc=0; github_upstream_release "$digpkg" 0 >/dev/null 2>&1 || rc=$? + check "digest without the sha256: prefix fails the sync" "1" "$rc" + + # The provider enforces the declaration shape itself: scheduled runs reach + # it without validate_package_metadata. + jq '.upstream.checksums = "SHASUMS256.txt"' "$digpkg/.omarchy/package.json" > "$digpkg/both.json" + cp "$digpkg/.omarchy/package.json" "$digpkg/good.json" + cp "$digpkg/both.json" "$digpkg/.omarchy/package.json" + rc=0; github_upstream_release "$digpkg" 0 >/dev/null 2>&1 || rc=$? + check "provider rejects checksums and digests together" "1" "$rc" + jq '.upstream.digests = "true"' "$digpkg/good.json" > "$digpkg/.omarchy/package.json" + rc=0; github_upstream_release "$digpkg" 0 >/dev/null 2>&1 || rc=$? + check "provider rejects a non-boolean digests" "1" "$rc" + 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="" + 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:" local rel st rel=$(jq -n --arg p "$old2d" '{pkgver: "1.9.0", published_at: $p, sha256sums: {}}') @@ -614,10 +826,100 @@ EOF echo '{"source": "local", "upstream": {"github": "example/tool"}}' > "$agepkg/.omarchy/package.json" vst=0; validate_package_metadata "$agepkg" >/dev/null || vst=$? check "upstream without checksums/assets is rejected" "1" "$vst" + echo '{"source": "local", "upstream": {"github": "example/tool", "checksums": "SHASUMS256.txt", "digests": true, "assets": {"x86_64": "a"}}}' > "$agepkg/.omarchy/package.json" + vst=0; validate_package_metadata "$agepkg" >/dev/null || vst=$? + check "upstream with both checksums and digests is rejected" "1" "$vst" + echo '{"source": "local", "upstream": {"github": "example/tool", "digests": "yes", "assets": {"x86_64": "a"}}}' > "$agepkg/.omarchy/package.json" + vst=0; validate_package_metadata "$agepkg" >/dev/null || vst=$? + check "non-boolean digests is rejected" "1" "$vst" + echo '{"source": "local", "upstream": {"github": "example/tool", "checksums": false, "digests": true, "assets": {"x86_64": "a"}}}' > "$agepkg/.omarchy/package.json" + vst=0; validate_package_metadata "$agepkg" >/dev/null || vst=$? + check "checksums: false alongside digests is rejected" "1" "$vst" + echo '{"source": "local", "upstream": {"github": "example/tool", "checksums": "SUMS", "digests": null, "assets": {"x86_64": "a"}}}' > "$agepkg/.omarchy/package.json" + vst=0; validate_package_metadata "$agepkg" >/dev/null || vst=$? + check "digests: null is rejected" "1" "$vst" + echo '{"source": "local", "upstream": {"github": "example/tool", "checksums": "SUMS", "digests": false, "assets": {"x86_64": "a"}}}' > "$agepkg/.omarchy/package.json" + vst=0; validate_package_metadata "$agepkg" >/dev/null || vst=$? + check "digests: false beside a checksums manifest is accepted" "0" "$vst" + echo '{"source": "local", "upstream": {"github": "example/tool", "digests": false, "assets": {"x86_64": "a"}}}' > "$agepkg/.omarchy/package.json" + vst=0; validate_package_metadata "$agepkg" >/dev/null || vst=$? + check "digests: false alone is rejected" "1" "$vst" + cp "$digpkg/.omarchy/package.json" "$agepkg/.omarchy/package.json" + vst=0; validate_package_metadata "$agepkg" >/dev/null || vst=$? + check "the digests declaration shape is accepted" "0" "$vst" + echo '{"source":"local","upstream":{"github":"example/tool","git_tags":"https://example/tool.git","checksums":"sums","assets":{"any":"tool"}}}' > "$agepkg/.omarchy/package.json" + vst=0; validate_package_metadata "$agepkg" >/dev/null || vst=$? + check "multiple provider types are rejected" "1" "$vst" cp "$pkg/.omarchy/package.json" "$agepkg/.omarchy/package.json" vst=0; validate_package_metadata "$agepkg" >/dev/null || vst=$? check "the real declaration shape is accepted" "0" "$vst" + echo "Git-tag provider:" + local tagpkg="$TEMP_DIR/selftest-tags" tag_sum remote_sum local_sum + mkdir -p "$tagpkg/.omarchy" + printf 'pkgver=1.0.0\npkgrel=4\nsha256sums=("old" "old")\n' > "$tagpkg/PKGBUILD" + printf 'local fixture\n' > "$tagpkg/local.patch" + cat > "$tagpkg/.omarchy/package.json" <<'EOF' +{ + "source": "local", + "upstream": { + "git_tags": "https://example.test/tool.git", + "tag_pattern": "release/{pkgver}", + "sources": { + "any": ["https://downloads.example.test/tool-{pkgver}.tar.gz", "file:local.patch"] + } + } +} +EOF + git_tags_fetch_refs() { + printf '%s\n' \ + 'aaaa refs/tags/release/1.9.0' \ + 'bbbb refs/tags/release/1.10.0' \ + 'cccc refs/tags/not-a-release' + } + upstream_fetch_source() { printf 'remote fixture for %s\n' "$1" > "$2"; } + tag_sum=$(git_tags_upstream_release "$tagpkg") + remote_sum=$(printf 'remote fixture for %s\n' 'https://downloads.example.test/tool-1.10.0.tar.gz' | sha256sum | cut -d' ' -f1) + local_sum=$(sha256sum "$tagpkg/local.patch" | cut -d' ' -f1) + check "pacman ordering selects 1.10.0 over 1.9.0" "1.10.0" "$(jq -r '.pkgver' <<<"$tag_sum")" + check "remote source template is downloaded and hashed" "$remote_sum" "$(jq -r '.sha256sums.any[0]' <<<"$tag_sum")" + check "local source entry is hashed" "$local_sum" "$(jq -r '.sha256sums.any[1]' <<<"$tag_sum")" + vst=0; validate_package_metadata "$tagpkg" >/dev/null || vst=$? + check "git-tags declaration validates" "0" "$vst" + + echo "npm provider:" + local npmpkg="$TEMP_DIR/selftest-npm" npm_sum npm_tar_sum npm_notes_sum + mkdir -p "$npmpkg/.omarchy" + printf 'pkgver=1.0.0\npkgrel=1\nsha256sums=("old" "old")\n' > "$npmpkg/PKGBUILD" + cat > "$npmpkg/.omarchy/package.json" <<'EOF' +{ + "source": "local", + "upstream": { + "npm": "@example/tool", + "dist_tag": "latest", + "sources": { + "any": ["{npm_tarball}", "https://example.test/tool/{pkgver}/notes"] + } + } +} +EOF + npm_fetch_metadata() { + jq -n '{ + "dist-tags": {latest: "2.0.0"}, + versions: {"2.0.0": {dist: {tarball: "https://registry.npmjs.org/@example/tool/-/tool-2.0.0.tgz"}}}, + time: {"2.0.0": "2024-01-02T03:04:05.000Z"} + }' + } + npm_sum=$(npm_upstream_release "$npmpkg") + npm_tar_sum=$(printf 'remote fixture for %s\n' 'https://registry.npmjs.org/@example/tool/-/tool-2.0.0.tgz' | sha256sum | cut -d' ' -f1) + npm_notes_sum=$(printf 'remote fixture for %s\n' 'https://example.test/tool/2.0.0/notes' | sha256sum | cut -d' ' -f1) + check "npm dist-tag selects its version" "2.0.0" "$(jq -r '.pkgver' <<<"$npm_sum")" + check "npm tarball placeholder is hashed" "$npm_tar_sum" "$(jq -r '.sha256sums.any[0]' <<<"$npm_sum")" + check "npm pkgver template is hashed" "$npm_notes_sum" "$(jq -r '.sha256sums.any[1]' <<<"$npm_sum")" + check "npm publication time is preserved" "2024-01-02T03:04:05.000Z" "$(jq -r '.published_at' <<<"$npm_sum")" + vst=0; validate_package_metadata "$npmpkg" >/dev/null || vst=$? + check "npm declaration validates" "0" "$vst" + # End to end over the real mise-bin package: its checked-in metadata and # PKGBUILD, the full sync_package path (selection, validation, backstop, # rewrite, read-back verification), with only the two network fetches diff --git a/bin/timers b/bin/timers index 619a821e..4b39a292 100755 --- a/bin/timers +++ b/bin/timers @@ -84,15 +84,18 @@ echo "" # --- 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 for channel in edge rc stable; do - state_file="$STATE_DIR/.sync-needed-$channel" - if [[ -f "$state_file" ]]; then - queued=true - since=$(date -r "$state_file" '+%Y-%m-%d %H:%M:%S' 2>/dev/null || echo "unknown") - printf ' • %-7s queued since %s\n' "$channel" "$since" - fi + for arch in $(published_arches); do + state_file=$(sync_queue_file "$channel" "$arch") + [[ "$arch" == "x86_64" && ! -f "$state_file" ]] && state_file=$(legacy_sync_queue_file "$channel") + if [[ -f "$state_file" ]]; then + queued=true + 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 [[ "$queued" == false ]] && echo " (nothing queued — all channels up to date)" echo "" @@ -101,34 +104,37 @@ echo "" # say so plainly: it is queued but deliberately not being retried yet. paused=false for channel in edge rc stable; do - fail_file="$STATE_DIR/.build-failed-$channel" - [[ -f "$fail_file" ]] || continue - if [[ "$paused" == false ]]; then - print_error "Failing builds (backoff active)" - paused=true - fi - FAILURE_COUNT=0 FAILURE_AT=0 FAILURE_FINGERPRINT="" - # shellcheck disable=SC1090 - source "$fail_file" 2>/dev/null || true - delay=600 - for ((i = 1; i < FAILURE_COUNT; i++)); do - delay=$((delay * 2)) - ((delay >= 21600)) && { delay=21600; break; } + for arch in $(published_arches); do + fail_file=$(sync_fail_file "$channel" "$arch") + [[ "$arch" == "x86_64" && ! -f "$fail_file" ]] && fail_file=$(legacy_sync_fail_file "$channel") + [[ -f "$fail_file" ]] || continue + if [[ "$paused" == false ]]; then + print_error "Failing builds (backoff active)" + paused=true + fi + FAILURE_COUNT=0 FAILURE_AT=0 FAILURE_FINGERPRINT="" + # shellcheck disable=SC1090 + source "$fail_file" 2>/dev/null || true + delay=600 + 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 - 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 if [[ "$paused" == true ]]; then - echo " Any new commit clears the backoff; or: rm $STATE_DIR/.build-failed-" + echo " Any new commit clears the backoff; or: rm $STATE_DIR/.build-failed--" echo "" fi diff --git a/bin/update-repo b/bin/update-repo index 34d9f512..2fda5493 100755 --- a/bin/update-repo +++ b/bin/update-repo @@ -11,12 +11,11 @@ source "$BUILD_ROOT/helpers/docker-helpers.sh" source "$BUILD_ROOT/helpers/paths.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() { - print_info "Updating repository database in Docker container..." + print_info "Updating repository database in a container..." - # Check Docker is available - check_docker + check_engine # Ensure output directory exists if [[ ! -d "$REPO_DIR" ]]; then @@ -25,20 +24,25 @@ update_database() { exit 1 fi - # Make output directory writable for container - make_dir_writable "$REPO_DIR" + if [[ "$CONTAINER_ENGINE" == "docker" ]]; then + make_dir_writable "$REPO_DIR" + fi - # repo-add is architecture- and mirror-independent, so always use the edge - # x86_64 image. This also lets bootstrap-rc build the rc database before the - # rc channel exists remotely (an rc image can only build after it does). - build_docker_image "$BUILD_DIR" "x86_64" "edge" + # repo-add is architecture- and mirror-independent. Use the host-native edge + # image, which also lets bootstrap-rc run before that channel exists remotely. + local tool_arch + 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 MIRROR="$MIRROR" \ -v "$REPO_ROOT:/output" \ -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 diff --git a/build/Dockerfile b/build/Dockerfile index fc1e66b7..af1bb5e9 100644 --- a/build/Dockerfile +++ b/build/Dockerfile @@ -45,9 +45,7 @@ RUN if [ "${TARGETARCH}" = "amd64" ]; then \ printf 'Server = https://mirror.omarchy.org/$repo/os/$arch\n' > /etc/pacman.d/mirrorlist; \ fi; \ else \ - curl -L "https://raw.githubusercontent.com/archlinuxarm/PKGBUILDs/master/core/pacman-mirrorlist/mirrorlist" 2>/dev/null | \ - sed -E 's/^\s*#\s*Server\s*=/Server =/g' > /etc/pacman.d/mirrorlist && \ - sed -i 's/\$arch/aarch64/g' /etc/pacman.d/mirrorlist; \ + printf 'Server = https://fl.us.mirror.archlinuxarm.org/aarch64/$repo\n' > /etc/pacman.d/mirrorlist; \ fi # 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) # 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 -# 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 RUN pacman-key --recv-keys 40DFB630FF42BCFFB047046CF0134EE680CAC571 --keyserver keys.openpgp.org && \ 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 -S --noconfirm omarchy-keyring && \ pacman-key --populate omarchy && \ @@ -137,8 +140,13 @@ RUN useradd -m -G wheel -s /bin/bash builder && \ chmod 700 /home/builder/.gnupg && \ 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 && \ - 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). # makepkg only passes --noconfirm to pacman, which defaults to 'N' on conflict diff --git a/build/build.sh b/build/build.sh index 42f9f2ea..9be27162 100755 --- a/build/build.sh +++ b/build/build.sh @@ -5,6 +5,10 @@ # Setup directories 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} DRY_RUN=${DRY_RUN:-false} PKGBUILDS_DIR=${PKGBUILDS_DIR:-/pkgbuilds} @@ -12,9 +16,37 @@ BUILD_OUTPUT_DIR=${BUILD_OUTPUT_DIR:-/build-output/$MIRROR/$ARCH} FINAL_OUTPUT_DIR=${FINAL_OUTPUT_DIR:-/pkgs.omarchy.org/$MIRROR/$ARCH} HELPERS_DIR=${HELPERS_DIR:-/helpers} SRC_DIR=${SRC_DIR:-/src} +# Set by bin/build from OMARCHY_DEFER_RUNTIME_DEPS after it has checked the +# request; re-checked here so the container never trusts a stray value. +DEFER_RUNTIME_DEPS=${DEFER_RUNTIME_DEPS:-false} source "$HELPERS_DIR/package-metadata.sh" +if [[ $DEFER_RUNTIME_DEPS != "false" && $DEFER_RUNTIME_DEPS != "true" ]]; then + echo "DEFER_RUNTIME_DEPS must be true or false" >&2 + exit 1 +fi +if [[ $DEFER_RUNTIME_DEPS == "true" ]]; then + deferred_runtime=0 + deferred_settings=0 + deferred_count=0 + for package in $PACKAGES; do + ((deferred_count += 1)) + case $package in + omarchy|omarchy-dev) deferred_runtime=1 ;; + omarchy-settings|omarchy-settings-dev) deferred_settings=1 ;; + *) + echo "Runtime dependency deferral only applies to the omarchy pair, not $package" >&2 + exit 1 + ;; + esac + done + if (( deferred_runtime != 1 || deferred_settings != 1 || deferred_count != 2 )); then + echo "Runtime dependency deferral requires exactly the omarchy pair" >&2 + exit 1 + fi +fi + if [[ "$DRY_RUN" != true ]]; then # Import GPG keys /build/import-gpg-keys.sh || exit 1 @@ -32,14 +64,11 @@ if [[ "$DRY_RUN" != true ]]; then # Configure Omarchy repositories for dependency resolution echo "==> Configuring Omarchy repositories for dependency resolution..." - # Always add omarchy-build repo (for incremental builds) - # Packages in build-output are unsigned, so use SigLevel = Never - sudo tee -a /etc/pacman.conf > /dev/null < omarchy-build (priority 1): $BUILD_OUTPUT_DIR" # Initialize empty build database if it doesn't exist @@ -48,23 +77,21 @@ EOF # Create an empty database repo-add omarchy-build.db.tar.zst >/dev/null 2>&1 ln -sf omarchy-build.db.tar.zst omarchy-build.db - else - # Database exists, check if we need to rebuild it from packages - if ls *.pkg.tar.* 2>/dev/null | grep -v '\.sig$' | grep -v 'omarchy-build\.db' | grep -q .; then - echo "==> Rebuilding build database from existing packages..." - ls *.pkg.tar.* | grep -v '\.sig$' | grep -v 'omarchy-build\.db' | xargs -r repo-add omarchy-build.db.tar.zst >/dev/null 2>&1 - ln -sf omarchy-build.db.tar.zst omarchy-build.db - fi + fi + # Fold any packages already in the workspace into the database, whether + # they came with an existing database or were dropped in by an earlier + # workflow job (OMARCHY_KEEP_BUILD_WORKSPACE). Without this a seeded + # workspace with no database would leave those packages invisible to + # dependency resolution. + if ls *.pkg.tar.* 2>/dev/null | grep -v '\.sig$' | grep -v 'omarchy-build\.db' | grep -q .; then + echo "==> Rebuilding build database from existing packages..." + ls *.pkg.tar.* | grep -v '\.sig$' | grep -v 'omarchy-build\.db' | xargs -r repo-add omarchy-build.db.tar.zst >/dev/null 2>&1 + ln -sf omarchy-build.db.tar.zst omarchy-build.db fi # 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 - sudo tee -a /etc/pacman.conf > /dev/null < omarchy (priority 2): $FINAL_OUTPUT_DIR" fi @@ -155,26 +182,9 @@ get_local_version() { # Returns 0 (success) if should build, 1 if should skip should_build_for_arch() { local pkg="$1" - local current_arch="$ARCH" - local pkgdir=$(find_package_dir "$pkg") - local pkgbuild="$pkgdir/PKGBUILD" - - [[ ! -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 + local pkgdir + pkgdir=$(find_package_dir "$pkg") + [[ -n "$pkgdir" ]] && package_supports_arch "$pkgdir" "$ARCH" } # For VCS packages, makepkg recalculates pkgver() before the build. If the @@ -224,6 +234,31 @@ refresh_vcs_pkgver_preserving_local_pkgrel() { fi } +# With runtime dependency checks deferred, makepkg runs --nodeps, so the +# build-time dependencies it would normally install with -s have to be +# installed explicitly: makedepends and checkdepends, including the +# architecture-suffixed variants for the current CARCH. +install_deferred_build_dependencies() { + local pkg="$1" + local -a build_deps=() + + mapfile -t build_deps < <( + CARCH="$ARCH" bash -c ' + source PKGBUILD + arch_makedepends="makedepends_${CARCH}[@]" + arch_checkdepends="checkdepends_${CARCH}[@]" + printf "%s\n" \ + "${makedepends[@]}" "${!arch_makedepends}" \ + "${checkdepends[@]}" "${!arch_checkdepends}" + ' | awk 'NF && !seen[$0]++' + ) + + if (( ${#build_deps[@]} )); then + echo " Installing build-only dependencies for $pkg..." + sudo pacman -S --needed --noconfirm -- "${build_deps[@]}" + fi +} + # Build a package build_package() { local pkg="$1" @@ -280,21 +315,64 @@ build_package() { # Build package without signing (signing is done separately) # PACMAN override uses a wrapper that adds --ask 4 to auto-resolve conflicts # (e.g. rustup replacing rust) since --noconfirm defaults to 'N' on those prompts - MAKEPKG_FLAGS="-scf --noconfirm" + local -a makepkg_flags=(-scf --noconfirm) + if [[ $DEFER_RUNTIME_DEPS == "true" ]]; then + # The pair's runtime dependencies (each other, and packages other jobs + # of the same pipeline build) are not resolvable here; the assembled set + # is installed in one verified transaction downstream. Only the + # build-time dependencies are installed, then makepkg skips the check. + install_deferred_build_dependencies "$pkg" || { + FAILED_PACKAGES="$FAILED_PACKAGES $pkg" + return 1 + } + makepkg_flags=(-cf --noconfirm --nodeps) + fi - if PACMAN=/usr/local/bin/pacman-for-makepkg makepkg $MAKEPKG_FLAGS; then + if PACMAN=/usr/local/bin/pacman-for-makepkg makepkg "${makepkg_flags[@]}"; then # Ensure output directory exists mkdir -p "$BUILD_OUTPUT_DIR" - - for pkg_file in *.pkg.tar.*; do - [[ -f "$pkg_file" ]] && cp "$pkg_file" "$BUILD_OUTPUT_DIR/" + + # Copy only the artifacts makepkg declares as outputs. A PKGBUILD may use + # 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 cd "$BUILD_OUTPUT_DIR" - # Find ALL package files (handles split packages) - local new_pkgs=($(ls -t ${pkg}-*.pkg.tar.* 2>/dev/null | grep -v '\.sig$' | grep -v 'omarchy-build\.db')) - + # Add every output from this build, including split packages. if [[ ${#new_pkgs[@]} -gt 0 ]]; then repo-add omarchy-build.db.tar.zst "${new_pkgs[@]}" >/dev/null 2>&1 ln -sf omarchy-build.db.tar.zst omarchy-build.db @@ -303,6 +381,24 @@ build_package() { 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" SUCCESSFUL_PACKAGES="$SUCCESSFUL_PACKAGES $pkg" return 0 @@ -421,7 +517,7 @@ check_needs_build() { # Collect packages that should be built for the selected mirror collect_packages() { - packages_for_unscoped_build "$MIRROR" + packages_for_unscoped_build "$MIRROR" "$ARCH" } # Main execution @@ -473,13 +569,6 @@ if [[ -n "$PACKAGES" ]]; then else # Build all packages that need updates from the relevant directories 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 PACKAGES_TO_BUILD+=("$pkg") else diff --git a/helpers/docker-helpers.sh b/helpers/docker-helpers.sh index 53f63065..cc18222a 100644 --- a/helpers/docker-helpers.sh +++ b/helpers/docker-helpers.sh @@ -1,25 +1,102 @@ -# Docker helper functions for Omarchy package build system +# Container engine helpers for Omarchy package build system -check_docker() { - if ! command -v docker &>/dev/null; then - print_error "Docker is not installed" +container_engine_supported() { + [[ "$CONTAINER_ENGINE" == "docker" || "$CONTAINER_ENGINE" == "podman" ]] +} + +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 fi - if ! docker info &>/dev/null; then - print_error "Docker daemon is not running" - print_warning "Start Docker with: sudo systemctl start docker" + if [[ -z "$CONTAINER_ENGINE" ]]; then + print_error "No working container engine found (tried Docker, then Podman)" + 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 fi } +docker_native_arch() { + case "$(uname -m)" in + x86_64) echo x86_64 ;; + aarch64 | arm64) echo aarch64 ;; + *) return 1 ;; + esac +} + setup_qemu() { - # Setup QEMU for building ARM64 packages on x86_64 hosts - 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" + local target_arch="${1:-aarch64}" + + 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 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() { @@ -39,31 +116,45 @@ build_docker_image() { ;; esac - print_info "Building Docker image for $arch ($platform) using $mirror mirror..." - - docker buildx build \ - --platform "$platform" \ - --build-arg MIRROR="$mirror" \ - --load \ - -t "$image_tag" \ - -f "$build_dir/Dockerfile" \ - "$build_dir" + print_info "Building container image for $arch ($platform) using $mirror mirror..." + + if [[ "$CONTAINER_ENGINE" == "docker" ]]; then + "$CONTAINER_ENGINE" buildx build \ + --platform "$platform" \ + --build-arg MIRROR="$mirror" \ + --load \ + -t "$image_tag" \ + -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() { local arch="$1" case "$arch" in - x86_64) echo "--platform linux/amd64" ;; - aarch64) echo "--platform linux/arm64" ;; - *) echo "" ;; + x86_64) echo "--platform=linux/amd64" ;; + aarch64) echo "--platform=linux/arm64" ;; + *) return 1 ;; esac } make_dir_writable() { local dir="$1" - if [ "$(id -u)" -eq 0 ]; then + if (( EUID == 0 )); then chmod -R 777 "$dir" else - sudo chown -R $(id -u):$(id -g) "$dir" 2>/dev/null || chmod -R 777 "$dir" + # chown can succeed on part of the tree and fail on the rest (files a + # previous container left behind as another uid); the old `|| chmod` + # fallback only ran when chown failed outright, leaving those files + # unwritable. Always follow with chmod so the whole tree is usable. + sudo chown -R "$(id -u):$(id -g)" "$dir" 2>/dev/null || true + chmod -R 777 "$dir" fi } diff --git a/helpers/package-metadata.sh b/helpers/package-metadata.sh index c121b46a..564cd2d6 100644 --- a/helpers/package-metadata.sh +++ b/helpers/package-metadata.sh @@ -15,7 +15,11 @@ # { "source": "local", "channels": ["edge"] } # { "source": "local", "channels": ["edge", "rc", "stable"] } # { "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": { "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": { "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-rebuilds writes rebuilt_against for packages declaring rebuild_on. @@ -140,6 +144,55 @@ package_has_pkgbuild() { [[ -f "$pkgdir/PKGBUILD" ]] } +# Read one variable from a PKGBUILD the way makepkg would see it. +# +# makepkg always exports CARCH, so PKGBUILDs may branch on it at file scope +# (per-architecture sources, tarball suffixes, even `return` for an +# unsupported architecture). Sourcing without CARCH takes the wrong branch or +# aborts partway, which leaves pkgver and pkgrel empty — and an empty version +# never equals the published one, so the package is queued for a rebuild that +# promotion then refuses. Every read of a PKGBUILD goes through here. +# +# Prints the value; exit status is that of `source PKGBUILD` itself, so a +# caller can tell "variable empty" from "PKGBUILD could not be read". +package_pkgbuild_var() { + local pkgdir="$1" + local var="$2" + local arch="${3:-${ARCH:-x86_64}}" + + (cd "$pkgdir" && env -u OMARCHY_SRC CARCH="$arch" bash -c ' + source PKGBUILD >/dev/null 2>&1 + rc=$? + printf "%s\n" "${!1:-}" + exit "$rc" + ' _ "$var") +} + +# 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 # `channels` key are members of every channel (they flow edge -> rc -> stable). package_has_channels() { @@ -298,9 +351,12 @@ packages_for_mirror() { packages_for_unscoped_build() { local mirror="$1" + local arch="${2:-${ARCH:-x86_64}}" 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" fi done @@ -446,17 +502,50 @@ validate_package_metadata() { # `has` rather than `// {}`: jq's // treats false as absent, which would # let "upstream": false slip through as an empty declaration. 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)) + )); + 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 elif (.upstream | type) != "object" then false else .upstream | - ((.github // "") | type == "string" and test("\\A[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+\\z")) - and ((.checksums // "") | type == "string" and length > 0) - and ((.assets // {}) | type == "object" and length > 0 and (to_entries | all( - (.key | test("\\A[a-z0-9_]+\\z")) and (.value | type == "string" and length > 0) - ))) + ([has("github"), has("git_tags"), has("npm"), has("debian")] | map(select(.)) | length) == 1 + and if has("github") then + (.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("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 (.assets | valid_assets) + 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 + (.git_tags | type == "string" and test("\\Ahttps://[^[:space:]]+\\.git\\z")) + and (.tag_pattern | type == "string" and (split("{pkgver}") | length) == 2) + and (.sources | valid_sources) + elif has("npm") then + (.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 (.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 ' "$metadata" >/dev/null; then - echo "invalid upstream for $(basename "$pkgdir"): needs github owner/repo, checksums asset name, and an assets arch->name map" + echo "invalid upstream for $(basename "$pkgdir"): configure exactly one valid github, git_tags, npm, or debian provider" return 1 fi @@ -481,12 +570,28 @@ validate_package_metadata() { return 1 fi - if ! jq -e '(.rebuilt_against // {}) | type == "object" and (to_entries | all(.value | type == "string" and length > 0))' "$metadata" >/dev/null; then - echo "invalid rebuilt_against for $(basename "$pkgdir"): must be an object mapping package names to versions" + if ! jq -e ' + 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 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" return 1 fi diff --git a/helpers/paths.sh b/helpers/paths.sh index 42fb44bd..5d6191c3 100644 --- a/helpers/paths.sh +++ b/helpers/paths.sh @@ -5,6 +5,62 @@ ARCH=${ARCH:-x86_64} 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 + echo "$STATE_DIR/.sync-needed-$1-$2" +} + +sync_fail_file() { # sync_fail_file + echo "$STATE_DIR/.build-failed-$1-$2" +} + +# The pre-architecture names, .sync-needed- and .build-failed-, +# 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_MIRRORS="edge rc stable" diff --git a/helpers/upstream-github.sh b/helpers/upstream-github.sh index e37e6ab4..6c7e1ba4 100644 --- a/helpers/upstream-github.sh +++ b/helpers/upstream-github.sh @@ -1,8 +1,7 @@ -# GitHub-releases upstream provider for bin/sync-upstream. +# Declarative upstream providers for bin/sync-upstream. # -# A package whose upstream ships tagged GitHub releases with a checksum -# manifest asset needs no upstream.sh hook: the whole feed is data, declared -# in .omarchy/package.json -- +# A package whose upstream ships tagged GitHub releases needs no upstream.sh +# hook: the whole feed is data, declared in .omarchy/package.json -- # # "upstream": { # "github": "jdx/mise", @@ -13,17 +12,28 @@ # } # } # +# "checksums" names the vendor's manifest asset. A vendor publishing none can +# set "digests": true instead, which reads the SHA-256 digest GitHub's release +# API reports for every asset, so the sync never downloads the artifacts. +# # {tag} and {pkgver} interpolate into asset names; tags may carry a leading # "v", which is stripped for pkgver. Drafts and prereleases are ignored. The # provider emits the same JSON contract as an upstream.sh hook, so # bin/sync-upstream's validation and min_release_age backstop apply -# unchanged; a feed that fits no convention keeps a bespoke upstream.sh. +# unchanged. Git-tag, npm, and Debian Packages providers below cover projects +# without GitHub releases; a feed that fits no convention keeps a bespoke hook. -package_upstream_github_repo() { - local pkgdir="$1" - # `objects` drops a non-object upstream value (validation rejects those - # separately) instead of erroring the jq pipeline. - package_metadata_value "$pkgdir" '(.upstream? | objects | .github)' "" +# Return the single declarative provider selected by a package. An empty +# result means either no provider or an invalid/ambiguous declaration; the +# caller distinguishes those through package_has_upstream_provider(). +package_upstream_provider() { + local pkgdir="$1" metadata + metadata=$(metadata_file_for_dir "$pkgdir") + jq -r ' + (.upstream? | objects) as $u + | [$u | keys[] | select(. == "github" or . == "git_tags" or . == "npm" or . == "debian")] + | if length == 1 then .[0] else "" end + ' "$metadata" } # Fetches sit behind functions so the self-test can replace them with fixture @@ -43,6 +53,221 @@ github_fetch_checksums() { curl -fsSL "https://github.com/$repo/releases/download/$tag/$asset" } +git_tags_fetch_refs() { + local repo="$1" + git ls-remote --tags "$repo" +} + +npm_fetch_metadata() { + local package="$1" encoded + encoded=$(jq -rn --arg package "$package" '$package | @uri') + curl -fsSL "https://registry.npmjs.org/$encoded" +} + +debian_fetch_packages() { + local url="$1" + curl --proto '=https' --proto-redir '=https' -fsSL "$url" +} + +upstream_fetch_source() { + local url="$1" output="$2" + curl --proto '=https' --proto-redir '=https' -fsSL -o "$output" "$url" +} + +# Hash every URL template in upstream.sources and emit hook-contract JSON. +# Templates may use {pkgver}, {tag}, and (for npm) {npm_tarball}. Downloads +# happen only after discovery reports a version newer than the PKGBUILD. +upstream_hash_sources() { + local package_dir="$1" pkgver="$2" tag="${3:-}" npm_tarball="${4:-}" + local metadata sources work result arch template url file sum sums index=0 + metadata=$(metadata_file_for_dir "$package_dir") + sources=$(jq -c '.upstream.sources' "$metadata") + work=$(mktemp -d) + result=$(jq -n --arg pkgver "$pkgver" '{pkgver: $pkgver, sha256sums: {}}') + + while IFS= read -r arch; do + sums='[]' + while IFS= read -r template; do + if [[ "$template" == file:* ]]; then + file=${template#file:} + if [[ ! "$file" =~ ^[A-Za-z0-9._+-]+$ || ! -f "$package_dir/$file" ]]; then + echo "upstream source names an unsafe or missing local file: '$file'" >&2 + rm -rf "$work" + return 1 + fi + sum=$(sha256sum "$package_dir/$file" | cut -d' ' -f1) + sums=$(jq -c --arg sum "$sum" '. + [$sum]' <<<"$sums") + continue + fi + url=${template//\{pkgver\}/$pkgver} + url=${url//\{tag\}/$tag} + url=${url//\{npm_tarball\}/$npm_tarball} + if [[ ! "$url" =~ ^https://[^[:space:]{}]+$ ]]; then + echo "upstream source template produced an unsafe URL: '$url'" >&2 + rm -rf "$work" + return 1 + fi + file="$work/source-$((index += 1))" + if ! upstream_fetch_source "$url" "$file"; then + echo "could not fetch upstream source: $url" >&2 + rm -rf "$work" + return 1 + fi + sum=$(sha256sum "$file" | cut -d' ' -f1) + sums=$(jq -c --arg sum "$sum" '. + [$sum]' <<<"$sums") + done < <(jq -r --arg arch "$arch" '.[$arch][]' <<<"$sources") + result=$(jq -c --arg arch "$arch" --argjson sums "$sums" '.sha256sums[$arch] = $sums' <<<"$result") + done < <(jq -r 'keys[]' <<<"$sources") + + rm -rf "$work" + printf '%s\n' "$result" +} + +git_tags_upstream_release() { + local package_dir="$1" metadata repo pattern prefix suffix refs + metadata=$(metadata_file_for_dir "$package_dir") + repo=$(jq -r '.upstream.git_tags // ""' "$metadata") + pattern=$(jq -r '.upstream.tag_pattern // ""' "$metadata") + prefix=${pattern%%\{pkgver\}*} + suffix=${pattern#*\{pkgver\}} + + if [[ ! "$repo" =~ ^https://[^[:space:]]+\.git$ || "$pattern" != *'{pkgver}'* || "$suffix" == *'{pkgver}'* ]]; then + echo "invalid git_tags provider configuration" >&2 + return 1 + fi + if ! refs=$(git_tags_fetch_refs "$repo"); then + echo "could not fetch tags from $repo" >&2 + return 1 + fi + + local best_pkgver="" best_tag="" tag candidate + declare -A version_tags=() + while read -r tag; do + tag=${tag%\^\{\}} + [[ "$tag" == "$prefix"*"$suffix" ]] || continue + candidate=${tag#"$prefix"} + [[ -z "$suffix" ]] || candidate=${candidate%"$suffix"} + [[ "$candidate" =~ ^[A-Za-z0-9][A-Za-z0-9._+]*$ ]] || continue + if [[ -n "${version_tags[$candidate]:-}" && "${version_tags[$candidate]}" != "$tag" ]]; then + echo "multiple tags map to pkgver $candidate: ${version_tags[$candidate]} and $tag" >&2 + return 1 + fi + version_tags[$candidate]="$tag" + if [[ -z "$best_pkgver" || $(vercmp "$candidate" "$best_pkgver") -gt 0 ]]; then + best_pkgver="$candidate" + best_tag="$tag" + fi + done < <(sed -n 's#^.*refs/tags/##p' <<<"$refs") + + [[ -n "$best_pkgver" ]] || { echo "no usable tags found at $repo" >&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_tag" +} + +npm_upstream_release() { + local package_dir="$1" metadata package dist_tag npm_metadata pkgver tarball published_at release + metadata=$(metadata_file_for_dir "$package_dir") + package=$(jq -r '.upstream.npm // ""' "$metadata") + dist_tag=$(jq -r '.upstream.dist_tag // "latest"' "$metadata") + if [[ ! "$package" =~ ^(@[a-z0-9_.-]+/)?[a-z0-9_.-]+$ || ! "$dist_tag" =~ ^[a-z0-9_.-]+$ ]]; then + echo "invalid npm provider configuration" >&2 + return 1 + fi + if ! npm_metadata=$(npm_fetch_metadata "$package"); then + echo "could not fetch npm metadata for $package" >&2 + return 1 + fi + pkgver=$(jq -r --arg tag "$dist_tag" '."dist-tags"[$tag] // ""' <<<"$npm_metadata") + tarball=$(jq -r --arg version "$pkgver" '.versions[$version].dist.tarball // ""' <<<"$npm_metadata") + published_at=$(jq -r --arg version "$pkgver" '.time[$version] // ""' <<<"$npm_metadata") + if [[ ! "$pkgver" =~ ^[A-Za-z0-9][A-Za-z0-9._+]*$ || ! "$tarball" =~ ^https://registry\.npmjs\.org/ ]]; then + echo "npm returned an unusable $package release" >&2 + return 1 + fi + + local current_pkgver + current_pkgver=$(grep -m1 '^pkgver=' "$package_dir/PKGBUILD" | cut -d= -f2- | tr -d "\"'") + if [[ $(vercmp "$pkgver" "$current_pkgver") -le 0 ]]; then + echo '{}' + return 0 + fi + release=$(upstream_hash_sources "$package_dir" "$pkgver" "$pkgver" "$tarball") || return 1 + if [[ -n "$published_at" ]]; then + release=$(jq -c --arg published_at "$published_at" '.published_at = $published_at' <<<"$release") + fi + 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:-}" >&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 # 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. @@ -51,7 +276,7 @@ github_fetch_checksums() { # not silently choose from. github_upstream_release() { local package_dir="$1" min_age="${2:-0}" - local metadata repo checksums_name + local metadata repo checksums_name use_digests latest_only metadata=$(metadata_file_for_dir "$package_dir") repo=$(jq -r '(.upstream? | objects | .github) // ""' "$metadata") @@ -59,9 +284,46 @@ github_upstream_release() { echo "invalid upstream.github repository: '${repo:-}'" >&2 return 1 fi - checksums_name=$(jq -r '(.upstream? | objects | .checksums) // ""' "$metadata") - if [[ -z "$checksums_name" ]]; then - echo "upstream.checksums names the checksum manifest asset and is required" >&2 + # Enforced here as well as in validate_package_metadata: the scheduled sync + # reaches this provider without running the validator first. + 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") + 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 + echo "upstream.digests must be true or false" >&2 + return 1 + 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 + echo "upstream sets both checksums and digests; keep exactly one" >&2 + return 1 + fi + if [[ -z "$checksums_name" && "$use_digests" != "true" ]]; then + echo "upstream needs either checksums (a manifest asset name) or digests: true" >&2 + return 1 + 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 @@ -77,6 +339,9 @@ github_upstream_release() { echo "could not fetch the release feed for $repo" >&2 return 1 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 tag published_at pkgver published_epoch @@ -129,33 +394,57 @@ github_upstream_release() { return 0 fi - local checksums - if ! checksums=$(github_fetch_checksums "$repo" "$best_tag" "$checksums_name"); then + local checksums="" + if [[ "$use_digests" != "true" ]] \ + && ! checksums=$(github_fetch_checksums "$repo" "$best_tag" "$checksums_name"); then echo "could not fetch $checksums_name for $repo $best_tag" >&2 return 1 fi - local jq_args=(--arg pkgver "$best_pkgver" --arg published_at "$best_published_at") - local jq_filter='{pkgver: $pkgver, published_at: $published_at, sha256sums: {}}' - local arch template filename checksum + local result + result=$(jq -n --arg pkgver "$best_pkgver" --arg published_at "$best_published_at" \ + '{pkgver: $pkgver, published_at: $published_at, sha256sums: {}}') + local arch template filename checksum checksum_source sums for arch in "${arches[@]}"; do if [[ ! "$arch" =~ ^[a-z0-9_]+$ ]]; then echo "invalid architecture key in upstream.assets: '$arch'" >&2 return 1 fi - template=$(jq -r --arg arch "$arch" '.upstream.assets[$arch]' "$metadata") - filename=${template//\{pkgver\}/$best_pkgver} - filename=${filename//\{tag\}/$best_tag} - # Manifest lines are " ", with the name sometimes prefixed - # "./" (sha256sum of a local path) or "*" (binary-mode marker). - checksum=$(awk -v f="$filename" '$2 == f || $2 == "./" f || $2 == "*" f { print $1; exit }' <<<"$checksums") - if [[ ! "$checksum" =~ ^[0-9a-f]{64}$ ]]; then - echo "no valid checksum for $filename in $repo $best_tag $checksums_name" >&2 - return 1 - fi - jq_args+=(--arg "sum_$arch" "$checksum") - jq_filter+=" | .sha256sums[\"$arch\"] = [\$sum_$arch]" + sums='[]' + while IFS= read -r template; do + filename=${template//\{pkgver\}/$best_pkgver} + filename=${filename//\{tag\}/$best_tag} + if [[ "$use_digests" == "true" ]]; then + # Only a "sha256:" digest is stripped to its hex; any other shape + # falls through empty and fails the check below. + checksum=$(jq -r --arg tag "$best_tag" --arg name "$filename" ' + first(.[] | select(.tag_name == $tag)) | (.assets // [])[] + | select(.name == $name) | (.digest // "") + | if type == "string" and test("\\Asha256:[0-9a-f]{64}\\z") then ltrimstr("sha256:") else "" end + ' <<<"$releases") + checksum_source="the release API digest" + else + # Manifest lines are " ", with the name sometimes prefixed + # "./" (sha256sum of a local path) or "*" (binary-mode marker). + checksum=$(awk -v f="$filename" '$2 == f || $2 == "./" f || $2 == "*" f { print $1; exit }' <<<"$checksums") + checksum_source="$checksums_name" + fi + if [[ ! "$checksum" =~ ^[0-9a-f]{64}$ ]]; then + echo "no valid checksum for $filename in $repo $best_tag $checksum_source" >&2 + return 1 + fi + 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 - 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" } diff --git a/pkgbuilds/1password/.omarchy/package.json b/pkgbuilds/1password/.omarchy/package.json index b3137453..663db8e5 100644 --- a/pkgbuilds/1password/.omarchy/package.json +++ b/pkgbuilds/1password/.omarchy/package.json @@ -1,5 +1,18 @@ { - "source": "aur", + "source": "local", "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" + ] + } + } } diff --git a/pkgbuilds/1password/PKGBUILD b/pkgbuilds/1password/PKGBUILD index 80d90d80..2479352a 100644 --- a/pkgbuilds/1password/PKGBUILD +++ b/pkgbuilds/1password/PKGBUILD @@ -1,27 +1,45 @@ pkgname=1password - -_tarver=8.12.34 -_tar="1password-${_tarver}.x64.tar.gz" -pkgver=${_tarver//-/_} -pkgrel=34 +pkgver=8.12.34 +pkgrel=36 conflicts=('1password-beta' '1password-beta-bin') pkgdesc="Password manager and secure wallet" -arch=('x86_64') +arch=('x86_64' 'aarch64') url='https://1password.com' license=('LicenseRef-1Password-Proprietary') options=(!strip) install="1password.install" -source=(https://downloads.1password.com/linux/tar/stable/${CARCH}/${_tar}{,.sig}) -sha256sums=('297784aa66770b645607a7f04c9ba2c4aebed4f46d21202487f521ba572b7b13' - 'ec085bef60de748895d3c51a8208301ba2ac8fb47db99334539ba4bd1d3260d7' +source_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" +) +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') package() { depends=('hicolor-icon-theme' 'libgtk-3.so=0' 'nss' 'xdg-utils') + # 1Password names its tarballs by a vendor architecture suffix. arch=() + # above already limits which architectures makepkg builds, so no fallback + # branch is needed here; keeping this inside package() means sourcing the + # PKGBUILD without CARCH (as the version check does) still reads the + # version correctly. + local _tararch + case "$CARCH" in + x86_64) _tararch=x64 ;; + aarch64) _tararch=arm64 ;; + esac + # Go to source directory - cd "1password-${_tarver}.x64" + cd "1password-${pkgver}.${_tararch}" # Install icons resolutions=(32x32 64x64 256x256 512x512) @@ -33,6 +51,12 @@ package() { # Install desktop file install -Dm0644 resources/1password.desktop -t "${pkgdir}"/usr/share/applications/ + # 1Password reads the display scale itself, the way Electron apps do, and + # comes up oversized next to every other window on a scaled monitor. Pin it + # and let the compositor do the scaling. + sed -i 's|^Exec=.*|Exec=/opt/1Password/1password --force-device-scale-factor=1 %U|' \ + "${pkgdir}/usr/share/applications/1password.desktop" + # Fill in policy kit file with a list of (the first 10) human users of the system. export POLICY_OWNERS POLICY_OWNERS="$(cut -d: -f1,3 /etc/passwd | grep -E ':[0-9]{4}$' | cut -d: -f1 | head -n 10 | sed 's/^/unix-user:/' | tr '\n' ' ')" @@ -49,7 +73,7 @@ EOF" > ./com.1password.1Password.policy # Move package contents to /opt/1Password cd "${srcdir}" install -dm0755 "${pkgdir}"/opt - mv "1password-${_tarver}.x64" "${pkgdir}/opt/1Password" + mv "1password-${pkgver}.${_tararch}" "${pkgdir}/opt/1Password" # 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 diff --git a/pkgbuilds/asdcontrol/PKGBUILD b/pkgbuilds/asdcontrol/PKGBUILD index eb67e1c8..6ae49791 100644 --- a/pkgbuilds/asdcontrol/PKGBUILD +++ b/pkgbuilds/asdcontrol/PKGBUILD @@ -4,7 +4,7 @@ epoch=1 pkgver=0.6.0 pkgrel=2 pkgdesc="Control brightness on Apple Displays connected via USB-C" -arch=('x86_64') +arch=('x86_64' 'aarch64') url="https://github.com/omakasui/asdcontrol" license=('GPL2') options=('!debug') diff --git a/pkgbuilds/asusctl/.nvchecker.toml b/pkgbuilds/asusctl/.nvchecker.toml index d1a0c7f1..e98ca307 100644 --- a/pkgbuilds/asusctl/.nvchecker.toml +++ b/pkgbuilds/asusctl/.nvchecker.toml @@ -1,4 +1,3 @@ [asusctl] source = "git" -git = "https://gitlab.com/asus-linux/asusctl.git" - +git = "https://github.com/OpenGamingCollective/asusctl.git" diff --git a/pkgbuilds/asusctl/.omarchy/package.json b/pkgbuilds/asusctl/.omarchy/package.json index f2bc93fa..1b2f22af 100644 --- a/pkgbuilds/asusctl/.omarchy/package.json +++ b/pkgbuilds/asusctl/.omarchy/package.json @@ -1,4 +1,4 @@ { "source": "aur", - "upstream_commit": "ed336f7dbd5e8bee333aeaa6246da89241f1c8e6" + "upstream_commit": "b0ec6ca495eb331a684db91b0fe12085a868b632" } diff --git a/pkgbuilds/asusctl/.omarchy/patches/6.4.0-github-source.patch b/pkgbuilds/asusctl/.omarchy/patches/6.4.0-github-source.patch deleted file mode 100644 index af9cfcf3..00000000 --- a/pkgbuilds/asusctl/.omarchy/patches/6.4.0-github-source.patch +++ /dev/null @@ -1,18 +0,0 @@ -diff --git a/PKGBUILD b/PKGBUILD -index 55d0c22..8897aec 100644 ---- a/PKGBUILD -+++ b/PKGBUILD -@@ -6,7 +6,7 @@ pkgname=( - asusctl - rog-control-center - ) --pkgver=6.3.8 -+pkgver=6.4.0 - pkgrel=1 - pkgdesc="A control daemon, tools, and a collection of crates for interacting with ASUS ROG laptops" - arch=('x86_64') -@@ -28,2 +28,2 @@ --source=("git+https://gitlab.com/asus-linux/asusctl.git#tag=$pkgver") --b2sums=('01269bc9fe63c1ce37568f9085c25dbcaaaa5cd9f51339d4c6904600d179ec826dace289d6e4e3501988fce5800da98e9cfa18b21c40da152d91c4f7fe821ffe') -+source=("git+https://github.com/opengamingcollective/asusctl.git#tag=$pkgver") -+b2sums=('0cfdc7f792fc6499cde9e4c9c6d82e484605e1b2878bb9f3a0e0696f5910b9ba7898f124fecd5fb3f39a0787f81e5163721606290099d757365dc3d6715a1bcf') diff --git a/pkgbuilds/asusctl/PKGBUILD b/pkgbuilds/asusctl/PKGBUILD index 3d39c41b..470e7e60 100644 --- a/pkgbuilds/asusctl/PKGBUILD +++ b/pkgbuilds/asusctl/PKGBUILD @@ -1,108 +1,59 @@ -# Maintainer: Fabian Bornschein +# Maintainer: Mahdi Sarikhani +# Contributor: Fabian Bornschein # Contributor: Static_Rocket pkgbase=asusctl -pkgname=( - asusctl - rog-control-center -) +pkgname=(asusctl rog-control-center) pkgver=6.4.0 -pkgrel=1.1 -pkgdesc="A control daemon, tools, and a collection of crates for interacting with ASUS ROG laptops" +pkgrel=1 +pkgdesc="Daemon and tools to control your ASUS ROG laptop" arch=('x86_64') url="https://asus-linux.org" license=('MPL-2.0') -makedepends=( - clang - cmake - fontconfig - git - hicolor-icon-theme - libayatana-appindicator - libinput - libusb - rust - seatd - systemd -) -source=("git+https://github.com/opengamingcollective/asusctl.git#tag=$pkgver") -b2sums=('0cfdc7f792fc6499cde9e4c9c6d82e484605e1b2878bb9f3a0e0696f5910b9ba7898f124fecd5fb3f39a0787f81e5163721606290099d757365dc3d6715a1bcf') +makedepends=('cargo' 'fontconfig') +source=("${pkgbase}-${pkgver}.tar.gz::https://github.com/OpenGamingCollective/asusctl/archive/${pkgver}.tar.gz") +b2sums=('e90074e904f364386ad661784bd9fc2e929e83ea06ac62fd1d34eb03490cefe8aae3bed2722162f1e8ea5d69248138cb5fd9f90c3a18443d1d8c04cf732b928a') prepare() { - cd "${pkgbase}" - - # Keep rust/cargo build-dependency management inside the build directory - export CARGO_HOME="${srcdir}/cargo" - - # Follow Rust package guidelines - ## https://wiki.archlinux.org/title/Rust_package_guidelines - export RUSTUP_TOOLCHAIN=stable - cargo fetch --locked --target "$(rustc -vV | sed -n 's/host: //p')" + cd "${pkgbase}-${pkgver}" + export RUSTUP_TOOLCHAIN=stable + cargo fetch --locked --target host-tuple } build() { - cd "${pkgbase}" - - # Keep rust/cargo build-dependency management inside the build directory - export CARGO_HOME="${srcdir}/cargo" - - # Follow Rust package guidelines - ## https://wiki.archlinux.org/title/Rust_package_guidelines - export RUSTUP_TOOLCHAIN=stable - export CARGO_TARGET_DIR=target - - make build + cd "${pkgbase}-${pkgver}" + export RUSTUP_TOOLCHAIN=stable + export CARGO_TARGET_DIR=target + make build } package_asusctl() { - pkgdesc="${pkgdesc/tools/CLI tools}" - depends=( - glibc - libgcc - libusb - systemd - systemd-libs - ) - conflicts=(gnome-shell-extension-asusctl-gnome) - install=asusctl.install - optdepends=( - 'acpi_call: fan control' - 'asusctltray: tray profile switcher' - 'rog-control-center: app to control asusctl' - 'supergfxctl: hybrid GPU control' - ) + pkgdesc="An utility for Linux to control many aspects of various ASUS laptops" + depends=('glibc' 'libgcc' 'libusb' 'systemd-libs') + optdepends=( + 'acpi_call: fan control' + 'asusctltray: tray profile switcher' + 'rog-control-center: graphical user interface for asusctl' + 'supergfxctl: hybrid GPU control' + ) + install=asusctl.install - cd "${pkgbase}" - export CARGO_HOME="${srcdir}/cargo" - make DESTDIR="${pkgdir}" \ - install-asusctl \ - install-asusd \ - install-asusd_user \ - install-asus-shutdown \ - install-data-asusd \ - install-data-asusd_user + cd "${pkgbase}-${pkgver}" + make DESTDIR="${pkgdir}" \ + install-asusctl \ + install-asusd \ + install-asusd_user \ + install-asus-shutdown \ + install-data-asusd \ + install-data-asusd_user } package_rog-control-center() { - depends=( - asusctl - fontconfig - freetype2 - glibc - hicolor-icon-theme - libayatana-appindicator - libgcc - libinput - libxkbcommon - mesa - seatd - systemd-libs - ) - pkgdesc="App to control asusctl" + pkgdesc="Graphical user interface for asusctl" + depends=('asusctl' 'fontconfig' 'glibc' 'hicolor-icon-theme' 'libgcc' 'systemd-libs') - cd "${pkgbase}" - export CARGO_HOME="${srcdir}/cargo" - make DESTDIR="${pkgdir}" \ - install-data-rog_gui \ - install-rog_gui + cd "${pkgbase}-${pkgver}" + make DESTDIR="${pkgdir}" \ + install-rog_gui \ + install-data-rog_gui } diff --git a/pkgbuilds/asusctl/asusctl.install b/pkgbuilds/asusctl/asusctl.install index 2f5a1c36..c6ccfe76 100644 --- a/pkgbuilds/asusctl/asusctl.install +++ b/pkgbuilds/asusctl/asusctl.install @@ -1,19 +1,18 @@ post_install() { - printf ":: asusd provides a service that is activated by an udev rule on\n" - printf ":: startup. Please reboot the system or run\n" - printf ":: # systemctl start asusd.service\n" - printf ":: to make it work.\n" - printf ":: See https://gitlab.com/asus-linux/asusctl#kernel-support.\n" - printf ":: for latest required kernel patches/versions\n" + printf ":: asusd provides a service that is activated by an udev rule on\n" + printf ":: startup. Please reboot the system or run\n" + printf ":: # systemctl start asusd.service\n" + printf ":: to make it work.\n" + printf ":: See https://github.com/OpenGamingCollective/asusctl#kernel-requirements\n" + printf ":: for latest required kernel patches/versions\n" } post_upgrade() { - if systemctl is-active asusd.service --quiet - then - printf ":: asusd service will be restarted…\n" - systemctl daemon-reload - systemctl restart asusd.service - fi - printf ":: See https://gitlab.com/asus-linux/asusctl#kernel-support.\n" - printf ":: for latest required kernel patches/versions\n" + if systemctl is-active asusd.service --quiet; then + printf ":: asusd service will be restarted…\n" + systemctl daemon-reload + systemctl restart asusd.service + fi + printf ":: See https://github.com/OpenGamingCollective/asusctl#kernel-requirements\n" + printf ":: for latest required kernel patches/versions\n" } diff --git a/pkgbuilds/basecamp-cli/.omarchy/package.json b/pkgbuilds/basecamp-cli/.omarchy/package.json index 2b88c4f5..0beb986e 100644 --- a/pkgbuilds/basecamp-cli/.omarchy/package.json +++ b/pkgbuilds/basecamp-cli/.omarchy/package.json @@ -1,5 +1,5 @@ { "source": "aur", "release_ring": "fast", - "upstream_commit": "fe2a6345e271f618dc3adf31e48557a717866395" + "upstream_commit": "1e7bd48ef192b3d6a2854916e75e14ea43f6f0b7" } diff --git a/pkgbuilds/basecamp-cli/PKGBUILD b/pkgbuilds/basecamp-cli/PKGBUILD index ba35e3a4..b7ed938c 100644 --- a/pkgbuilds/basecamp-cli/PKGBUILD +++ b/pkgbuilds/basecamp-cli/PKGBUILD @@ -1,6 +1,6 @@ # Maintainer: Basecamp pkgname=basecamp-cli -pkgver=0.9.1 +pkgver=0.10.0 pkgrel=1 pkgdesc="CLI for Basecamp project management" arch=('x86_64' 'aarch64') @@ -13,10 +13,10 @@ optdepends=( 'zsh: for zsh shell completions' 'fish: for fish shell completions' ) -source_x86_64=("https://github.com/basecamp/basecamp-cli/releases/download/v0.9.1/basecamp_${pkgver}_linux_amd64.tar.gz") -source_aarch64=("https://github.com/basecamp/basecamp-cli/releases/download/v0.9.1/basecamp_${pkgver}_linux_arm64.tar.gz") -sha256sums_x86_64=('bcb1fa3a03e702bbf81a3004ded4bc7808605e41106cbc768ba4006b89e0db2f') -sha256sums_aarch64=('ff50313024b6ca14e40146e30e1c9c9c00a4e4103fe55615eeee3e7c359d88d1') +source_x86_64=("https://github.com/basecamp/basecamp-cli/releases/download/v0.10.0/basecamp_${pkgver}_linux_amd64.tar.gz") +source_aarch64=("https://github.com/basecamp/basecamp-cli/releases/download/v0.10.0/basecamp_${pkgver}_linux_arm64.tar.gz") +sha256sums_x86_64=('ca8b3a085f90323c8ab1694b83ff624cf10c459618ca6010ce204534f3f01987') +sha256sums_aarch64=('dd1b5db88b9b309e95ae1ec0d642d23f4955a3abe159bffe00796a0ef6030c1d') package() { install -Dm755 "basecamp" "${pkgdir}/usr/bin/basecamp" diff --git a/pkgbuilds/claude-code/.omarchy/package.json b/pkgbuilds/claude-code/.omarchy/package.json index 47a3b893..5ce322c2 100644 --- a/pkgbuilds/claude-code/.omarchy/package.json +++ b/pkgbuilds/claude-code/.omarchy/package.json @@ -1,5 +1,5 @@ { "source": "aur", "release_ring": "fast", - "upstream_commit": "29ca1941149bbf080d7eb6d2bb53cddc76478a1a" + "upstream_commit": "87aa5d5f2771c83dae737fe1bcfbc129110291fb" } diff --git a/pkgbuilds/claude-code/PKGBUILD b/pkgbuilds/claude-code/PKGBUILD index 3f92f5b3..ca3c84a6 100644 --- a/pkgbuilds/claude-code/PKGBUILD +++ b/pkgbuilds/claude-code/PKGBUILD @@ -4,7 +4,7 @@ # Automation repository: https://github.com/fabifont/claude-code-aur pkgname=claude-code -pkgver=2.1.252 +pkgver=2.1.263 pkgrel=1 pkgdesc="An agentic coding tool that lives in your terminal" arch=('x86_64' 'aarch64') @@ -29,8 +29,8 @@ source_x86_64=("claude-${pkgver}-x86_64::https://downloads.claude.ai/claude-code source_aarch64=("claude-${pkgver}-aarch64::https://downloads.claude.ai/claude-code-releases/${pkgver}/linux-arm64/claude") sha256sums=('SKIP') -sha256sums_x86_64=('a715a45105e593fc9808d035d77781f88480b9897975a9df41837f0c591bd4b3') -sha256sums_aarch64=('6c0b32eaa936954a0f4d4ad2595f8416af288d9b3cbaf19e3cdd9a95d7c8853e') +sha256sums_x86_64=('26d020351e8112f4006790f3cfce43b4c9df0c1bb1d0e542364d64151b81d5ba') +sha256sums_aarch64=('7d25d7c8ae6c6e009cc7dae4e817f674179fd31fb7761bcd56fee4c2902b4c03') package() { install -Dm755 "${srcdir}/claude-${pkgver}-${CARCH}" "${pkgdir}/opt/claude-code/bin/claude" diff --git a/pkgbuilds/cliamp/.omarchy/package.json b/pkgbuilds/cliamp/.omarchy/package.json index d9084bbe..78c29884 100644 --- a/pkgbuilds/cliamp/.omarchy/package.json +++ b/pkgbuilds/cliamp/.omarchy/package.json @@ -1,4 +1,4 @@ { "source": "aur", - "upstream_commit": "76b9ce02837a49de0fa50cfa3da5d1c0c692ece5" + "upstream_commit": "0e012cbfe1ea6cbbf15e2ada9cf93cca7aaa9ca0" } diff --git a/pkgbuilds/cliamp/PKGBUILD b/pkgbuilds/cliamp/PKGBUILD index 34e938f7..bc972e01 100644 --- a/pkgbuilds/cliamp/PKGBUILD +++ b/pkgbuilds/cliamp/PKGBUILD @@ -1,17 +1,17 @@ # Maintainer: bjarneo pkgname=cliamp -pkgver=1.63.2 +pkgver=2.0.1 pkgrel=1 pkgdesc='A retro terminal music player inspired by Winamp 2.x' arch=('x86_64' 'aarch64') url='https://github.com/bjarneo/cliamp' license=('MIT') -depends=('alsa-lib' 'flac' 'libvorbis' 'libogg' 'ffmpeg' 'yt-dlp') +depends=('alsa-lib' 'flac' 'libvorbis' 'libogg' 'mpg123' 'ffmpeg' 'yt-dlp') optdepends=('pipewire-alsa: audio output on PipeWire systems' 'pulseaudio-alsa: audio output on PulseAudio systems') makedepends=('go') source=("${pkgname}-${pkgver}.tar.gz::https://github.com/bjarneo/cliamp/archive/refs/tags/v${pkgver}.tar.gz") -sha256sums=('968ff98c1e49bae8a0ce63acf5c77a9621ef70756a048e3df5e79454b82a9eef') +sha256sums=('2c5885665dba5ed2e8dc156bce64751199a92efed7f63959c65e985759b73732') build() { cd "${pkgname}-${pkgver}" diff --git a/pkgbuilds/cursor-bin/.omarchy/package.json b/pkgbuilds/cursor-bin/.omarchy/package.json index 77bb17de..8461d51b 100644 --- a/pkgbuilds/cursor-bin/.omarchy/package.json +++ b/pkgbuilds/cursor-bin/.omarchy/package.json @@ -1,5 +1,5 @@ { "source": "aur", "release_ring": "fast", - "upstream_commit": "00756d82c31b0056800bb98220c83800ebb85e72" + "upstream_commit": "c5073d62f60f3973a14340eb1b40fc5b5a4ea9f0" } diff --git a/pkgbuilds/cursor-bin/PKGBUILD b/pkgbuilds/cursor-bin/PKGBUILD index e4088b65..14325f67 100644 --- a/pkgbuilds/cursor-bin/PKGBUILD +++ b/pkgbuilds/cursor-bin/PKGBUILD @@ -1,18 +1,18 @@ # Maintainer: Gunther Schulz pkgname=cursor-bin -pkgver=3.18.25 +pkgver=3.19.13 pkgrel=1 pkgdesc='AI-first coding environment' arch=('x86_64') url="https://www.cursor.com" license=('LicenseRef-Cursor_EULA') # upstream uses Electron newer than internal VSCode -_electron=electron40 +_electron=electron42 depends=(xdg-utils ripgrep $_electron nodejs 'gcc-libs' 'hicolor-icon-theme' 'libxkbfile') options=(!strip !debug) # Don't break ext of VSCode -_commit=280eca2911f1774689696e5f1efa5a4f97a87af3 +_commit=dd066f332fcea7382764400fde902f61920648d5 source=("https://downloads.cursor.com/production/${_commit}/linux/x64/deb/amd64/deb/cursor_${pkgver}_amd64.deb" "https://gitlab.archlinux.org/archlinux/packaging/packages/code/-/raw/main/code."{sh,mjs} rg.sh) @@ -20,7 +20,7 @@ sha512sums=('SKIP' '937299c6cb6be2f8d25f7dbc95cf77423875c5f8353b8bd6cd7cc8e5603cbf8405b14dbf8bd615db2e3b36ed680fc8e1909410815f7f8587b7267a699e00ab37' '793f9ff6306e3992ac89802d98110cba288ea1181a901467333293b7d76182ef9792c2a39ff49d9347a18a174b1f42bc58862091dff583f4146c2704eea28033' 'e79fe7659f59d1ae02fc68816399bfd31587315df6cdb6ccf1d0ca76f7cdc692c2a42b30591c0091147bd97ef14b1c7745dc26bd7cb3ea6bba45698e5044fa2a') -sha512sums[0]=5f25c9dccf75c764b6266e74136ad5cf6e99330804c7ae3ed22d4210304791b1ac1233efb07b291da490fbfe3d4801cb46680a171ddf6b2815e0267741ec339a +sha512sums[0]=3c952035fa1809caded7059b7addbb01a9143cd248ecff27cdb3cb3af47d4bc824ed60dfd9757e105a342e53280e075d81f775790295a3272818bab5a994b305 noextract=(cursor_${pkgver}_amd64.deb) # avoid double tarball _app=usr/share/cursor/resources/app package() { diff --git a/pkgbuilds/dropbox-cli/.omarchy/patches/x86-only.patch b/pkgbuilds/dropbox-cli/.omarchy/patches/x86-only.patch new file mode 100644 index 00000000..2448ae46 --- /dev/null +++ b/pkgbuilds/dropbox-cli/.omarchy/patches/x86-only.patch @@ -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") diff --git a/pkgbuilds/dropbox-cli/PKGBUILD b/pkgbuilds/dropbox-cli/PKGBUILD index 7cc84b95..1372a2cf 100644 --- a/pkgbuilds/dropbox-cli/PKGBUILD +++ b/pkgbuilds/dropbox-cli/PKGBUILD @@ -7,9 +7,9 @@ pkgname=dropbox-cli pkgver=2024.04.17 -pkgrel=2 +pkgrel=2.1 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") diff --git a/pkgbuilds/flea/.omarchy/package.json b/pkgbuilds/flea/.omarchy/package.json new file mode 100644 index 00000000..bbe9ae19 --- /dev/null +++ b/pkgbuilds/flea/.omarchy/package.json @@ -0,0 +1,5 @@ +{ + "source": "local", + "release_ring": "fast", + "min_release_age": "24h" +} diff --git a/pkgbuilds/flea/.omarchy/upstream.sh b/pkgbuilds/flea/.omarchy/upstream.sh new file mode 100755 index 00000000..5f512b26 --- /dev/null +++ b/pkgbuilds/flea/.omarchy/upstream.sh @@ -0,0 +1,100 @@ +#!/bin/bash +# Flea publishes signed source tags but no release artifacts or checksum manifest. +# Check the small GitHub source tarball only when a newer stable release exists, +# validate its expected root, then report its hash alongside the fixed hashes of +# the four upstream security patches carried until a release includes them. +set -euo pipefail + +REPO='thisisgm/flea' +RELEASES_URL="https://api.github.com/repos/$REPO/releases?per_page=100" +PATCH_2BD7CC2='c610a9f44294b67341940203943c9c567003b65cc436d6013cd36754379183d8' +PATCH_27D19CA='e457ef17e26f70057b1184eeb938fccc5906f48a7c77f1df573d55d13a845425' +PATCH_23290CE='f8381456a3b5f39d341cc6610bf27beb8354892b17a1c8c6d7882db4e40824c3' +PATCH_B4B7EE4='46bdbe43f135a052c893b1987f8b0928736445616a84b0c65d2181ee74b21b25' + +current=$(awk -F= '/^pkgver=/ { print $2; exit }' PKGBUILD) +releases=$(curl -fsSL "$RELEASES_URL") +now=$(date +%s) +min_age=${MIN_RELEASE_AGE_SECONDS:-0} + +candidates=0 +best_version='' +best_tag='' +best_published_at='' + +while IFS=$'\t' read -r tag published_at; do + if [[ ! $tag =~ ^v([0-9]+\.[0-9]+\.[0-9]+)$ ]]; then + printf '%s has an unusable stable release tag: %s\n' "$REPO" "${tag:-}" >&2 + exit 1 + fi + version=${BASH_REMATCH[1]} + + if [[ -z $published_at ]] || ! published_epoch=$(date --date="$published_at" +%s 2>/dev/null); then + printf '%s release %s has an invalid publication time\n' "$REPO" "$tag" >&2 + exit 1 + fi + candidates=$((candidates + 1)) + + if (( now - published_epoch < min_age )) && [[ ${BYPASS_MIN_RELEASE_AGE:-} != 1 ]]; then + continue + fi + + if [[ -z $best_version ]] || (( $(vercmp "$version" "$best_version") > 0 )); then + best_version=$version + best_tag=$tag + best_published_at=$published_at + fi +done < <(jq -r '.[] | select((.draft or .prerelease) | not) | [.tag_name // "", .published_at // ""] | @tsv' <<<"$releases") + +if (( candidates == 0 )); then + printf 'No stable releases found for %s\n' "$REPO" >&2 + exit 1 +fi + +if [[ -z $best_version ]] || (( $(vercmp "$best_version" "$current") <= 0 )); then + echo '{}' + exit 0 +fi + +tarball=$(mktemp) +trap 'rm -f "$tarball"' EXIT +curl -fsSL -o "$tarball" "https://github.com/$REPO/archive/refs/tags/$best_tag.tar.gz" + +expected_root="flea-$best_version" +served_roots=$(tar -tzf "$tarball" | cut -d/ -f1 | sort -u) +if [[ $served_roots != "$expected_root" ]]; then + printf 'Release %s contains root %s, expected %s\n' "$best_tag" "$served_roots" "$expected_root" >&2 + exit 1 +fi + +archive_rs=$(tar -xOzf "$tarball" "$expected_root/src/backend/archive.rs") +archiveops_rs=$(tar -xOzf "$tarball" "$expected_root/src/backend/archiveops.rs") +run_rs=$(tar -xOzf "$tarball" "$expected_root/src/backend/run.rs") +archivereq_rs=$(tar -xOzf "$tarball" "$expected_root/src/backend/archivereq.rs") +archivework_rs=$(tar -xOzf "$tarball" "$expected_root/src/backend/archivework.rs") +mediaprobe_rs=$(tar -xOzf "$tarball" "$expected_root/src/backend/mediaprobe.rs") +metareq_rs=$(tar -xOzf "$tarball" "$expected_root/src/backend/metareq.rs") +sharelink_qml=$(tar -xOzf "$tarball" "$expected_root/ui/ShareLink.qml") +copyfile_rs=$(tar -xOzf "$tarball" "$expected_root/src/backend/copyfile.rs") + +if ! grep -Fq 'a.push("--".to_string());' <<<"$archive_rs" || + ! grep -Fq 'let input = std::fs::canonicalize(input)' <<<"$archiveops_rs" || + ! grep -Fq 'if op != "compress" && op != "extract"' <<<"$run_rs$archivereq_rs" || + ! grep -Fq 'the sandbox is unavailable: bwrap or prlimit is not on PATH' <<<"$archivework_rs" || + ! grep -Fq 'if !sandbox::available()' <<<"$mediaprobe_rs" || + ! grep -Fq 'if !sandbox::available()' <<<"$metareq_rs" || + ! grep -Fq 'copyToClipboard.command = ["wl-copy", url]' <<<"$sharelink_qml" || + ! grep -Fq '.custom_flags(O_NOFOLLOW)' <<<"$copyfile_rs"; then + printf 'Release %s does not contain every required upstream security fix\n' "$best_tag" >&2 + exit 1 +fi + +jq -n \ + --arg pkgver "$best_version" \ + --arg published_at "$best_published_at" \ + --arg source "$(sha256sum "$tarball" | cut -d' ' -f1)" \ + --arg patch_2bd7cc2 "$PATCH_2BD7CC2" \ + --arg patch_27d19ca "$PATCH_27D19CA" \ + --arg patch_23290ce "$PATCH_23290CE" \ + --arg patch_b4b7ee4 "$PATCH_B4B7EE4" \ + '{pkgver: $pkgver, published_at: $published_at, sha256sums: {any: [$source, $patch_2bd7cc2, $patch_27d19ca, $patch_23290ce, $patch_b4b7ee4]}}' diff --git a/pkgbuilds/flea/PKGBUILD b/pkgbuilds/flea/PKGBUILD new file mode 100644 index 00000000..c03a00a5 --- /dev/null +++ b/pkgbuilds/flea/PKGBUILD @@ -0,0 +1,163 @@ +# Maintainer: GM + +pkgname=flea +pkgver=0.1.3 +pkgrel=1 +pkgdesc='Fast, keyboard-first file manager for Omarchy' +arch=('x86_64') +url='https://github.com/thisisgm/flea' +license=('MIT') + +depends=( + 'bubblewrap' + 'gcc-libs' + 'glib2' + 'glibc' + 'hicolor-icon-theme' + 'omarchy' + 'quickshell' + 'qt6-multimedia' + 'qt6-webengine' + 'shared-mime-info' + 'util-linux' + 'xdg-utils' +) +makedepends=('cargo') +optdepends=( + '7zip: 7z archive support' + 'dropbox-cli: Dropbox browsing and share links' + 'ffmpeg: media metadata previews' + 'imagemagick: image conversion' + 'libarchive: archive listing and extraction' + 'tailscale: Taildrop sharing' + 'wl-clipboard: copy Dropbox share links to the clipboard' +) +conflicts=('flea-git') +options=('!debug') + +_security_patches=( + '2bd7cc207c110ae3b3ea61a4d4e42336f1815111' + '27d19ca4a38ef4a0a920fb78441ce662a0101327' + '23290ce1917b0d7f0392dd09d04bf2a7504ec93a' + 'b4b7ee47244b52457eec2874e21a8656d8ace647' +) +source=( + "$pkgname-$pkgver.tar.gz::$url/archive/refs/tags/v$pkgver.tar.gz" + "security-2bd7cc2.patch::$url/commit/${_security_patches[0]}.patch" + "security-27d19ca.patch::$url/commit/${_security_patches[1]}.patch" + "security-23290ce.patch::$url/commit/${_security_patches[2]}.patch" + "security-b4b7ee4.patch::$url/commit/${_security_patches[3]}.patch" +) +sha256sums=( + '3599ab76253c444dea027f1ebe85b32612d9a174e60eb8aca33ee21d9e2d6973' + 'c610a9f44294b67341940203943c9c567003b65cc436d6013cd36754379183d8' + 'e457ef17e26f70057b1184eeb938fccc5906f48a7c77f1df573d55d13a845425' + 'f8381456a3b5f39d341cc6610bf27beb8354892b17a1c8c6d7882db4e40824c3' + '46bdbe43f135a052c893b1987f8b0928736445616a84b0c65d2181ee74b21b25' +) + +prepare() { + cd "$pkgname-$pkgver" + + security_patch_present() { + case "$1" in + 2bd7cc207c110ae3b3ea61a4d4e42336f1815111) + grep -Fq 'a.push("--".to_string());' src/backend/archive.rs && + grep -Fq 'let input = std::fs::canonicalize(input)' src/backend/archiveops.rs && + grep -Fq 'if op != "compress" && op != "extract"' \ + src/backend/run.rs src/backend/archivereq.rs + ;; + 27d19ca4a38ef4a0a920fb78441ce662a0101327) + grep -Fq 'the sandbox is unavailable: bwrap or prlimit is not on PATH' src/backend/archivework.rs && + grep -Fq 'if !sandbox::available()' src/backend/mediaprobe.rs && + grep -Fq 'if !sandbox::available()' src/backend/metareq.rs + ;; + 23290ce1917b0d7f0392dd09d04bf2a7504ec93a) + grep -Fq 'copyToClipboard.command = ["wl-copy", url]' ui/ShareLink.qml + ;; + b4b7ee47244b52457eec2874e21a8656d8ace647) + grep -Fq '.custom_flags(O_NOFOLLOW)' src/backend/copyfile.rs + ;; + *) + return 1 + ;; + esac + } + + local commit patch_file + for commit in "${_security_patches[@]}"; do + patch_file="$srcdir/security-${commit:0:7}.patch" + + if patch --batch --forward --dry-run -Np1 -i "$patch_file" >/dev/null 2>&1; then + patch --batch --forward -Np1 -i "$patch_file" + elif security_patch_present "$commit"; then + printf 'Security patch %s is already present upstream\n' "$commit" + else + printf 'Security patch %s no longer applies and is not present upstream\n' "$commit" >&2 + return 1 + fi + done +} + +build() { + cd "$pkgname-$pkgver" + + export CARGO_TARGET_DIR=target + cargo build --frozen --release +} + +check() { + cd "$pkgname-$pkgver" + + export CARGO_TARGET_DIR=target + + local -a test_args=() + if ! /usr/bin/prlimit --cpu=30 --as=1073741824 \ + /usr/bin/bwrap --unshare-all --die-with-parent --new-session --clearenv \ + --ro-bind /usr /usr --ro-bind /etc /etc \ + --symlink usr/lib /lib --symlink usr/lib /lib64 \ + --symlink usr/bin /bin --symlink usr/bin /sbin \ + --proc /proc --dev /dev --tmpfs /tmp /usr/bin/true >/dev/null 2>&1; then + printf 'Skipping sandbox integration tests: this builder cannot create an unprivileged namespace\n' + test_args+=( + --skip backend::archiveops::tests::a_tool_that_exits_zero_writing_nothing_is_caught_by_the_predicate + --skip backend::archiveops::tests::an_empty_archive_extracts_to_an_empty_directory_and_that_is_success + --skip backend::archiveops::tests::only_the_archive_root_extracts_to_nothing_while_nested_directories_do_not + --skip backend::archiveops::tests::the_emptiness_check_reads_the_destination_and_the_index_separately + --skip backend::thumbs::tests::a_cancelled_job_never_runs + --skip backend::thumbs::tests::a_real_file_round_trips_to_a_stamped_cache_entry + --skip backend::thumbs::tests::cancel_all_empties_the_queue + --skip backend::thumbs::tests::the_queue_is_bounded_and_drops_the_oldest_rather_than_growing + ) + fi + + if [[ ! -x /usr/bin/evince-thumbnailer ]] || + [[ ! -x /usr/bin/ffmpegthumbnailer ]] || + [[ ! -x /usr/bin/glycin-thumbnailer ]]; then + printf 'Skipping thumbnailer fixture tests: this builder lacks their optional host programs\n' + test_args+=( + --skip backend::thumbspec::tests::a_type_two_files_declare_goes_to_the_first_in_path_order + --skip backend::thumbspec::tests::the_heaviest_shipped_field_parses + ) + fi + + cargo test --frozen --release -- "${test_args[@]}" + ./tests/js.sh + ./tests/keymap-gen.sh +} + +package() { + cd "$pkgname-$pkgver" + + install -Dm755 target/release/flea "$pkgdir/usr/bin/flea" + install -Dm644 packaging/com.thisisgm.flea.desktop \ + "$pkgdir/usr/share/applications/com.thisisgm.flea.desktop" + install -Dm644 packaging/com.thisisgm.flea.svg \ + "$pkgdir/usr/share/icons/hicolor/scalable/apps/com.thisisgm.flea.svg" + install -Dm644 LICENSE "$pkgdir/usr/share/licenses/$pkgname/LICENSE" + + install -Dm644 ui/qmldir ui/*.qml -t "$pkgdir/usr/share/flea/ui" + install -Dm644 ui/js/*.js -t "$pkgdir/usr/share/flea/ui/js" + ln -s /usr/share/omarchy/shell/Commons "$pkgdir/usr/share/flea/ui/Commons" + ln -s /usr/share/omarchy/shell/Ui "$pkgdir/usr/share/flea/ui/Ui" +} diff --git a/pkgbuilds/github-copilot-cli/.omarchy/package.json b/pkgbuilds/github-copilot-cli/.omarchy/package.json index 39cf2ffe..29481cfd 100644 --- a/pkgbuilds/github-copilot-cli/.omarchy/package.json +++ b/pkgbuilds/github-copilot-cli/.omarchy/package.json @@ -1,5 +1,13 @@ { - "source": "aur", + "source": "local", "release_ring": "fast", - "upstream_commit": "fc4db00ecaf6c2669422b32c023a16bf6eb4e6c7" + "upstream": { + "npm": "@github/copilot", + "sources": { + "any": [ + "{npm_tarball}", + "https://raw.githubusercontent.com/github/copilot-cli/v{pkgver}/changelog.md" + ] + } + } } diff --git a/pkgbuilds/github-copilot-cli/PKGBUILD b/pkgbuilds/github-copilot-cli/PKGBUILD index dc61d75a..917dca58 100644 --- a/pkgbuilds/github-copilot-cli/PKGBUILD +++ b/pkgbuilds/github-copilot-cli/PKGBUILD @@ -6,7 +6,7 @@ _npmmodule=@github/copilot pkgname=github-copilot-cli _pkgexec=copilot -pkgver=1.0.82 +pkgver=1.0.83 pkgrel=1 pkgdesc="GitHub Copilot CLI brings the power of Copilot coding agent directly to your terminal." @@ -14,7 +14,7 @@ pkgdesc="GitHub Copilot CLI brings the power of Copilot coding agent directly to url="https://github.com/github/copilot-cli" _urlraw="https://raw.githubusercontent.com/github/copilot-cli/v${pkgver}" -arch=("x86_64") +arch=("x86_64" "aarch64") license=("LicenseRef-GitHub-Copilot") @@ -30,8 +30,10 @@ source=("https://registry.npmjs.org/${_npmmodule}/-/copilot-${pkgver}.tgz" "CHANGELOG-${pkgver}.md::${_urlraw}/changelog.md") noextract=("copilot-${pkgver}.tgz") -b2sums=('41c8306b58a142afe162bad9822b150babb0f3903fe7392aa7106fcb0ffce4483c0a2ebda8bb1d2036199deb52dc83d9a0e9408b8e11f7b0bc2a8f57c6841425' - '5d2dfc870356f83b9b890d351b21bc9339100b0bfdce405ee77751e934dbeab0f8656f5eee85119a06aa3b01dcf3c91224ce28c24ff318eb1b13fdb6082037cd') +sha256sums=( + '135506fc2b13163ab55dbf76a06e2fbcbad04ecac76b4e9c6659f0ba309e6a86' + '0c0064a10effac8adf9ad97338bafaa0d7d7d5bf191cc1c0384e05ff4366d36c' +) # Document: https://wiki.archlinux.org/title/Node.js_package_guidelines package() { @@ -124,4 +126,3 @@ package() { ln -sf "/usr/lib/node_modules/${_npmmodule}/LICENSE.md" "${pkgdir}/usr/share/licenses/${pkgname}/LICENSE" } - diff --git a/pkgbuilds/hermes-desktop/PKGBUILD b/pkgbuilds/hermes-desktop/PKGBUILD index 472c804c..1f48dd9d 100644 --- a/pkgbuilds/hermes-desktop/PKGBUILD +++ b/pkgbuilds/hermes-desktop/PKGBUILD @@ -1,25 +1,11 @@ # Maintainer: David Heinemeier Hansson -# Nous builds Hermes Desktop for macOS and Windows only -- their download page -# offers a .dmg and an .exe and tells Linux users to install from a terminal -- -# so there is no vendor binary to repackage. Their electron-builder config does -# carry a Linux target, though, and it works; this builds it. -# -# The app only runs against a Hermes runtime built from its own commit, so it -# provisions one itself under ~/.hermes on first launch and the package stays -# on the newest tag. Pairing it with the mise CLI instead was tried and does -# not work: PyPI trails the tags, and the version gap fails the readiness probe -# with a 401. Pinning back to the tag behind PyPI's release does not rescue it -# either -- v2026.7.20's desktop hangs after "backend is ready" without ever -# opening a window, against its own matching runtime. -# -# The app is only a shell: it runs `hermes serve` against a Hermes CLI it does -# not ship, and clones its own copy with the upstream install script when it -# finds none. /usr/bin/hermes-desktop heads that off. See hermes-desktop.sh. +# Build a prebuilt release; Omarchy seeds it into the user's Hermes checkout +# so the upstream updater can rebuild and relaunch it in place. pkgname=hermes-desktop -pkgver=2026.8.18 -pkgrel=2 +pkgver=2026.8.31 +pkgrel=3 pkgdesc='Native desktop shell for Hermes Agent' arch=('x86_64') url='https://github.com/NousResearch/hermes-agent' @@ -32,7 +18,7 @@ depends=( 'dbus' 'expat' 'curl' - 'gcc-libs' + 'gcc' 'gdk-pixbuf2' 'git' 'glib2' @@ -52,19 +38,24 @@ depends=( 'libxfixes' 'libxkbcommon' 'libxrandr' + 'make' 'mesa' + 'nodejs' + 'npm' 'nspr' 'nss' 'pango' + 'python' 'systemd-libs' + 'util-linux' 'xdg-utils' ) -optdepends=('omarchy: installs the Hermes CLI the app needs on first launch') +optdepends=('omarchy: sets up the user installation for in-app updates') # The build runs the repo's own npm workspace install, which fetches Electron # and rebuilds node-pty against it. -makedepends=('git' 'imagemagick' 'nodejs' 'npm' 'python') +makedepends=('imagemagick') # Electron bundles prebuilt binaries that stripping corrupts. options=('!strip' '!debug') @@ -74,23 +65,27 @@ options=('!strip' '!debug') # before falling back to `git rev-parse`. That fallback is wrong here: makepkg # builds inside this repository, so git ascends out of srcdir and stamps the # app with an omarchy-pkgs commit that means nothing upstream. -_commit=e624e9fde561e1add9388384012b295fde669ade +_commit=29112bef099274229cadff79cdff7bf7b99c4b77 _srcdir="hermes-agent-${pkgver}" source=("${pkgname}-${pkgver}.tar.gz::${url}/archive/refs/tags/v${pkgver}.tar.gz" 'hermes-desktop.sh' 'hermes-desktop.desktop' - 'hermes-desktop.png') -sha256sums=('1e3d39d3638ec15fa9d31af262568a953e9272090deb1c50c44cd401175f5b80' - 'f5833b969ce451aadee9f08d92db55cce3c7c9213175080590bf37444854d676' + 'hermes-desktop.png' + 'runtime.patch' + 'runtime-test.py') +sha256sums=('78fb3ff707ec1d17044b875ecac8bef28aa39d44242824f6871ca40afe7bf217' + '094d5f3191109a80eea9f23053b78a2e00dbecf90d62d1ca04c8e48866251469' '3ef685bfcf366776b025d26c37d32854d8d4aa2023b2bd07c8e08b001ef1e8c4' - 'd60d164e24fdcf6532133b8ea43c77a201e4b9e9dbc396187b58d51d8590ef52') + 'd60d164e24fdcf6532133b8ea43c77a201e4b9e9dbc396187b58d51d8590ef52' + '03b67e26c234c797a6b1d4c9f34a57502dba37f462540e47ce6c88f6ea79302a' + '461e1120e7e6779f531c114d9926479e47fab79113d1b4646efea36bc771b3c5') build() { cd "${srcdir}/${_srcdir}" export GITHUB_SHA="${_commit}" - export GITHUB_REF_NAME="v${pkgver}" + export GITHUB_REF_NAME="main" # The desktop workspace resolves against the repo root, so the install has to # happen there rather than in apps/desktop. @@ -100,6 +95,10 @@ build() { npm run pack } +check() { + python "${srcdir}/runtime-test.py" "${srcdir}/${_srcdir}" "${srcdir}/runtime.patch" "${srcdir}/hermes-desktop.sh" +} + package() { cd "${srcdir}/${_srcdir}/apps/desktop/release/linux-unpacked" @@ -108,6 +107,12 @@ package() { install -Dm755 "${srcdir}/hermes-desktop.sh" "${pkgdir}/usr/bin/${pkgname}" + install -Dm644 "${srcdir}/${_srcdir}/scripts/install.sh" \ + "${pkgdir}/usr/share/${pkgname}/install.sh" + # Let the release's first updater relaunch with the user-namespace sandbox. + install -Dm644 "${srcdir}/runtime.patch" "${pkgdir}/usr/share/${pkgname}/runtime.patch" + + install -Dm644 "${srcdir}/hermes-desktop.desktop" \ "${pkgdir}/usr/share/applications/${pkgname}.desktop" @@ -125,11 +130,6 @@ package() { install -Dm644 "${pkgdir}/opt/${pkgname}/LICENSE.electron.txt" \ "${pkgdir}/usr/share/licenses/${pkgname}/LICENSE.electron.txt" - # Chromium's setuid sandbox is only needed where unprivileged user namespaces - # are unavailable; where they work, setuid root is the worse of the two. - if ! { [[ -L /proc/self/ns/user ]] && unshare --user true; }; then - chmod 4755 "${pkgdir}/opt/${pkgname}/chrome-sandbox" - else - chmod 0755 "${pkgdir}/opt/${pkgname}/chrome-sandbox" - fi + # The launcher requires the namespace sandbox on the target host. + chmod 0755 "${pkgdir}/opt/${pkgname}/chrome-sandbox" } diff --git a/pkgbuilds/hermes-desktop/hermes-desktop.sh b/pkgbuilds/hermes-desktop/hermes-desktop.sh index 5c4d35ef..40822b88 100644 --- a/pkgbuilds/hermes-desktop/hermes-desktop.sh +++ b/pkgbuilds/hermes-desktop/hermes-desktop.sh @@ -1,41 +1,74 @@ #!/bin/bash set -euo pipefail -# Hermes Desktop is a shell around a Hermes runtime, and it only works against -# one built from its own commit. A CLI from PyPI is always a different release -# -- PyPI trails the tags -- and the mismatch fails the app's readiness probe -# with 401 Unauthorized. So keep it away from whatever `hermes` is on PATH, -# which on Omarchy is the mise CLI installed for the terminal agent, and let -# the app provision and manage its own runtime under ~/.hermes. That is the -# arrangement upstream ships, and the only one that starts. +unset ELECTRON_RUN_AS_NODE PYTHONPATH PYTHONHOME export HERMES_DESKTOP_IGNORE_EXISTING=1 -# Chromium cannot reliably infer the Secret Service password-store backend -# from a Hyprland session, even when GNOME Keyring is already providing it. -# Keep an explicit user choice (such as KWallet), otherwise select the -# libsecret backend that this package depends on. -export HERMES_DESKTOP_PASSWORD_STORE="${HERMES_DESKTOP_PASSWORD_STORE:-gnome-libsecret}" - -# Reconcile every launch rather than trusting whatever installed us. A plain -# `pacman -S hermes-desktop`, or an install interrupted partway, leaves any -# Hermes the terminal agent had built still sitting there, and by then the -# menu entry that would have tidied it up is disabled because we are present. +# Reconcile direct package installs and interrupted Omarchy setup as well. if command -v omarchy-install-hermes-cli >/dev/null 2>&1; then omarchy-install-hermes-cli >/dev/null 2>&1 || true fi -# Chromium's own Ozone detection falls back to XWayland often enough to matter, -# and the result is a blurry window on every scaled display. Ask for Wayland -# directly, unless the user has already picked a platform themselves. -platform_flags=() -if [[ -n "${WAYLAND_DISPLAY:-}" || ${XDG_SESSION_TYPE:-} == wayland ]]; then - platform_flags=(--ozone-platform=wayland) - - for flag in "$@"; do - case "$flag" in - --ozone-platform=* | --ozone-platform-hint=*) platform_flags=() ;; - esac - done +hermes_home=$(realpath -ms -- "${HERMES_HOME:-$HOME/.hermes}") +parent=${hermes_home%/*} +if [[ ${parent##*/} == [Pp][Rr][Oo][Ff][Ii][Ll][Ee][Ss] ]]; then + hermes_home=${parent%/*} + hermes_home=${hermes_home:-/} +fi +export HERMES_HOME="$hermes_home" +runtime="$hermes_home/hermes-agent" +native="$runtime/apps/desktop/release/linux-unpacked/Hermes" +if [[ ! -x $native || ! -x $runtime/venv/bin/hermes ]]; then + native=/opt/hermes-desktop/Hermes fi -exec /opt/hermes-desktop/Hermes "${platform_flags[@]}" "$@" +# Both app locations use namespaces, never a user-writable setuid helper. +if ! timeout 5 unshare --user --map-root-user true 2>/dev/null; then + echo "Hermes Desktop requires working unprivileged user namespaces for its sandbox." >&2 + exit 1 +fi + +python=/usr/bin/python +if [[ -x $runtime/venv/bin/python && -f $runtime/hermes_cli/main.py ]]; then + python="$runtime/venv/bin/python" +else + runtime="" +fi +exec "$python" - "$native" "$runtime" "$@" <<'PY' +import os +from pathlib import Path +import sys + +native, runtime, *args = sys.argv[1:] +env = os.environ.copy() +flags, gpu, store, ozone = [], "auto", "auto", "auto" +if runtime: + sys.path.insert(0, runtime) + try: + # Upstream moved the helper out of main after the packaged release. + if Path(runtime, "hermes_cli/main_desktop.py").is_file(): + from hermes_cli.main_desktop import _desktop_launch_options + else: + from hermes_cli.main import _desktop_launch_options + from hermes_constants import with_hermes_node_path + + flags, gpu, store, ozone = _desktop_launch_options() + env = with_hermes_node_path(env) + except ImportError: + print("Could not load Hermes desktop settings; using launch defaults.", file=sys.stderr) + +env["HERMES_DESKTOP_CWD"] = os.getcwd() +if gpu != "auto": + env.setdefault("HERMES_DESKTOP_DISABLE_GPU", gpu) +if ozone != "auto": + env.setdefault("ELECTRON_OZONE_PLATFORM_HINT", ozone) +env.setdefault("HERMES_DESKTOP_PASSWORD_STORE", store if store != "auto" else "gnome-libsecret") + +# Explicit config, environment and command-line choices override the Wayland default. +if (env.get("WAYLAND_DISPLAY") or env.get("XDG_SESSION_TYPE") == "wayland") and ( + "ELECTRON_OZONE_PLATFORM_HINT" not in env + and not any(arg.startswith(("--ozone-platform=", "--ozone-platform-hint=")) for arg in flags + args) +): + flags.insert(0, "--ozone-platform=wayland") +os.execve(native, [native, "--disable-setuid-sandbox", *flags, *args], env) +PY diff --git a/pkgbuilds/hermes-desktop/runtime-test.py b/pkgbuilds/hermes-desktop/runtime-test.py new file mode 100644 index 00000000..79f0a43d --- /dev/null +++ b/pkgbuilds/hermes-desktop/runtime-test.py @@ -0,0 +1,162 @@ +"""Check launch settings and sandbox gates without running Hermes or sudo.""" +import ast +import json +import os +from pathlib import Path +import shutil +import subprocess +import sys +import tempfile + +source, patch_file, launcher = map(Path, sys.argv[1:]) +with tempfile.TemporaryDirectory(prefix="hermes-runtime-check-") as temporary: + root = Path(temporary) + destination = root / "scripts/desktop-update/posix.sh" + destination.parent.mkdir(parents=True) + shutil.copyfile(source / "scripts/desktop-update/posix.sh", destination) + subprocess.run(["git", "apply", str(patch_file.resolve())], cwd=root, check=True) + + home = root / "home with spaces" + runtime = home / ".hermes/hermes-agent" + native = runtime / "apps/desktop/release/linux-unpacked" + native.mkdir(parents=True) + executable = native / "Hermes" + executable.write_text(f"#!{sys.executable}\n" + '''import json, os, sys +from pathlib import Path +Path(os.environ["TEST_OUTPUT"]).write_text(json.dumps({ + "args": sys.argv[1:], "home": os.environ["HERMES_HOME"], + "store": os.environ["HERMES_DESKTOP_PASSWORD_STORE"], + "gpu": os.environ.get("HERMES_DESKTOP_DISABLE_GPU"), + "ozone": os.environ.get("ELECTRON_OZONE_PLATFORM_HINT"), + "cwd": os.environ.get("HERMES_DESKTOP_CWD"), + "inherited": [name for name in ("ELECTRON_RUN_AS_NODE", "PYTHONPATH", "PYTHONHOME") if name in os.environ], +})) +''') + executable.chmod(0o755) + sandbox = native / "chrome-sandbox" + sandbox.write_text("fixture") + sandbox.chmod(0o755) + + # Execute the release's real option parser, isolating config I/O and avoiding + # unrelated CLI imports, startup hooks and third-party dependencies. + module = runtime / "hermes_cli" + module.mkdir() + (module / "__init__.py").touch() + upstream = ast.parse((source / "hermes_cli/main.py").read_text()) + option_parser = next(node for node in upstream.body + if isinstance(node, ast.FunctionDef) and node.name == "_desktop_launch_options") + stores = next(node for node in upstream.body if isinstance(node, ast.Assign) + and any(isinstance(target, ast.Name) and target.id == "_LINUX_PASSWORD_STORES" + for target in node.targets)) + helper = "import os, shlex\n" + ast.unparse(stores) + "\n" + ast.unparse(option_parser) + "\n" + (module / "main.py").write_text(helper) + (module / "config.py").write_text('''import json, os +from pathlib import Path +def load_config(): + path = Path(os.environ["HERMES_HOME"], "config.yaml") + return json.loads(path.read_text()) if path.exists() else {} +''') + (runtime / "hermes_constants.py").write_text('''import os +def with_hermes_node_path(env=None): + return (os.environ if env is None else env).copy() +''') + + mock_bin = root / "bin" + mock_bin.mkdir() + unshare = mock_bin / "unshare" + unshare.write_text('#!/bin/bash\nexit "${TEST_NAMESPACE_RESULT:-0}"\n') + unshare.chmod(0o755) + forbidden = '#!/bin/bash\ntouch "$TEST_FORBIDDEN"\nexit 99\n' + cli = runtime / "venv/bin/hermes" + cli.parent.mkdir(parents=True) + cli.write_text(forbidden) + cli.chmod(0o755) + (cli.parent / "python").symlink_to(sys.executable) + for command in ("sudo", "omarchy-install-hermes-cli"): + target = mock_bin / command + target.write_text(forbidden if command == "sudo" else '#!/bin/bash\nexit 0\n') + target.chmod(0o755) + + output = root / "launch.json" + forbidden_output = root / "forbidden" + env = {"HOME": str(home), "PATH": f"{mock_bin}:/usr/bin:/bin", + "TEST_OUTPUT": str(output), "TEST_FORBIDDEN": str(forbidden_output)} + launch = ["bash", str(launcher.resolve())] + + def check_launch(args=(), overrides=None, expected_args=(), store="gnome-libsecret", gpu=None, ozone=None): + subprocess.run(launch + list(args), env={**env, **(overrides or {})}, cwd=home, check=True) + result = json.loads(output.read_text()) + assert result == {"args": ["--disable-setuid-sandbox", *expected_args], + "home": str(home / ".hermes"), "store": store, "gpu": gpu, + "ozone": ozone, "cwd": str(home), "inherited": []}, result + assert not forbidden_output.exists(), "launcher invoked CLI or sudo" + output.unlink() + + wayland = {"WAYLAND_DISPLAY": "wayland-1"} + check_launch(overrides=wayland, expected_args=["--ozone-platform=wayland"]) + url = "hermes://open?text=a%20b" + check_launch(["--ozone-platform=x11", url], {**wayland, "HERMES_DESKTOP_PASSWORD_STORE": "kwallet6"}, + ["--ozone-platform=x11", url], store="kwallet6") + check_launch(overrides={"HERMES_HOME": str(home / ".hermes/profiles/work"), + "HERMES_DESKTOP_DISABLE_GPU": "1"}, gpu="1") + check_launch(overrides={"ELECTRON_RUN_AS_NODE": "1", "PYTHONPATH": "/invalid", "PYTHONHOME": "/invalid"}) + + config = home / ".hermes/config.yaml" + config.write_text(json.dumps({"desktop": {"disable_gpu": True, "password_store": "kwallet5", + "ozone_platform_hint": "x11", + "electron_flags": '--force-device-scale-factor=1.5 "--test=a b"'}})) + config_flags = ["--force-device-scale-factor=1.5", "--test=a b"] + for args in ([], [url]): + check_launch(args, wayland, config_flags + args, store="kwallet5", gpu="1", ozone="x11") + check_launch(overrides={**wayland, "HERMES_DESKTOP_DISABLE_GPU": "0", + "HERMES_DESKTOP_PASSWORD_STORE": "basic", "ELECTRON_OZONE_PLATFORM_HINT": "wayland"}, + expected_args=config_flags, store="basic", gpu="0", ozone="wayland") + config.write_text(json.dumps({"desktop": {"electron_flags": ["--ozone-platform=x11"], "disable_gpu": False}})) + check_launch(overrides=wayland, expected_args=["--ozone-platform=x11"], gpu="0") + # New upstream revisions expose the same helper from main_desktop instead. + (module / "main_desktop.py").write_text(helper) + (module / "main.py").write_text('raise AssertionError("old helper import after update")\n') + check_launch([url], wayland, ["--ozone-platform=x11", url], gpu="0") + config.unlink() + (module / "main_desktop.py").write_text(helper + '\nimport os\nos.environ.update(' + repr({ + "ELECTRON_RUN_AS_NODE": "1", "PYTHONPATH": "/invalid", "PYTHONHOME": "/invalid", + "HERMES_HOME": "/invalid", "HERMES_DESKTOP_DISABLE_GPU": "1", + "HERMES_DESKTOP_PASSWORD_STORE": "basic", "ELECTRON_OZONE_PLATFORM_HINT": "x11", + }) + ')\n') + check_launch(overrides={**wayland, "HERMES_DESKTOP_DISABLE_GPU": "0"}, + expected_args=["--ozone-platform=wayland"], gpu="0") + (module / "main_desktop.py").write_text(helper) + + def check_namespace_failure(): + for code in ("1", "127"): + for args in ([], [url]): + result = subprocess.run(launch + args, env={**env, "TEST_NAMESPACE_RESULT": code}, + capture_output=True, text=True) + assert result.returncode != 0 and "user namespaces" in result.stderr, result + assert not output.exists() and not forbidden_output.exists() + + check_namespace_failure() + # Exercise the real fallback branch with only its absolute app path redirected. + fallback = root / "packaged-Hermes" + shutil.copyfile(executable, fallback) + fallback.chmod(0o755) + fallback_launcher = root / "fallback-launcher" + fallback_launcher.write_text(launcher.read_text().replace("/opt/hermes-desktop/Hermes", str(fallback))) + launch = ["bash", str(fallback_launcher)] + executable.unlink() + check_launch([url], wayland, ["--ozone-platform=wayland", url]) + (module / "main_desktop.py").write_text('raise ImportError("incomplete Python dependencies")\n') + check_launch([url], wayland, ["--ozone-platform=wayland", url]) + shutil.rmtree(runtime / "venv") + check_launch(overrides={"ELECTRON_RUN_AS_NODE": "1", "PYTHONPATH": "/invalid", "PYTHONHOME": "/invalid"}) + check_namespace_failure() + + gate = ["bash", str(destination), "--self-test-gate", "--install-root", str(runtime), + "--relaunch-target", str(executable)] + executable.touch() + executable.chmod(0o755) + assert subprocess.check_output(gate, env=env, text=True).strip() == "relaunch" + assert subprocess.check_output(gate, env={**env, "TEST_NAMESPACE_RESULT": "1"}, text=True).startswith("manual:") + gate[-1] = "/opt/hermes-desktop/Hermes" + assert subprocess.check_output(gate, env=env, text=True).startswith("skew:") +print("PASS: desktop settings, environment, native/fallback namespace sandbox and updater gate") diff --git a/pkgbuilds/hermes-desktop/runtime.patch b/pkgbuilds/hermes-desktop/runtime.patch new file mode 100644 index 00000000..8b7ed51e --- /dev/null +++ b/pkgbuilds/hermes-desktop/runtime.patch @@ -0,0 +1,11 @@ +--- a/scripts/desktop-update/posix.sh ++++ b/scripts/desktop-update/posix.sh +@@ -317,6 +317,8 @@ + if [ ! -e "$sb" ]; then GATE=relaunch; return; fi + if [ -u "$sb" ] && [ "$(stat -c %u "$sb" 2>/dev/null)" = "0" ]; then GATE=relaunch; return; fi + ++ if unshare --user --map-root-user true 2>/dev/null; then GATE=relaunch; return; fi ++ + case "${ELECTRON_DISABLE_SANDBOX:-}" in 1|true|TRUE|True) GATE=relaunch; return ;; esac + [ "$SANDBOX_FALLBACK" -eq 1 ] && { GATE=relaunch; return; } + for arg in ${RELAUNCH_ARGS[@]+"${RELAUNCH_ARGS[@]}"}; do diff --git a/pkgbuilds/hyprland-guiutils/.omarchy/package.json b/pkgbuilds/hyprland-guiutils/.omarchy/package.json new file mode 100644 index 00000000..d87fa12f --- /dev/null +++ b/pkgbuilds/hyprland-guiutils/.omarchy/package.json @@ -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" + } + } +} diff --git a/pkgbuilds/hyprland-guiutils/PKGBUILD b/pkgbuilds/hyprland-guiutils/PKGBUILD new file mode 100644 index 00000000..036e6bf4 --- /dev/null +++ b/pkgbuilds/hyprland-guiutils/PKGBUILD @@ -0,0 +1,40 @@ +# Maintainer: Caleb Maclennan + +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 +} diff --git a/pkgbuilds/hyprland-preview-share-picker/PKGBUILD b/pkgbuilds/hyprland-preview-share-picker/PKGBUILD index ab85617e..b3d80bef 100644 --- a/pkgbuilds/hyprland-preview-share-picker/PKGBUILD +++ b/pkgbuilds/hyprland-preview-share-picker/PKGBUILD @@ -4,7 +4,7 @@ pkgname="hyprland-preview-share-picker" pkgver=0.2.1 pkgrel=1 pkgdesc="An alternative share picker for hyprland with window and monitor previews" -arch=(x86_64) +arch=(x86_64 aarch64) url="https://github.com/WhySoBad/hyprland-preview-share-picker" license=(MIT) depends=('gtk4' 'gtk4-layer-shell' 'xdg-desktop-portal-hyprland' 'hyprland') diff --git a/pkgbuilds/hyprland/.omarchy/package.json b/pkgbuilds/hyprland/.omarchy/package.json new file mode 100644 index 00000000..9dd97453 --- /dev/null +++ b/pkgbuilds/hyprland/.omarchy/package.json @@ -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" + } + } +} diff --git a/pkgbuilds/hyprland/PKGBUILD b/pkgbuilds/hyprland/PKGBUILD new file mode 100644 index 00000000..cbb70ed8 --- /dev/null +++ b/pkgbuilds/hyprland/PKGBUILD @@ -0,0 +1,105 @@ +# Maintainer: Caleb Maclennan +# Contributor: ThatOneCalculator +# Contributor: Brenno Lemos +# Contributor: Gabriel Fox + +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 +} diff --git a/pkgbuilds/hyprtoolkit/.omarchy/package.json b/pkgbuilds/hyprtoolkit/.omarchy/package.json new file mode 100644 index 00000000..ee5e6cbf --- /dev/null +++ b/pkgbuilds/hyprtoolkit/.omarchy/package.json @@ -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" + } + } +} diff --git a/pkgbuilds/hyprtoolkit/PKGBUILD b/pkgbuilds/hyprtoolkit/PKGBUILD new file mode 100644 index 00000000..c6f1b490 --- /dev/null +++ b/pkgbuilds/hyprtoolkit/PKGBUILD @@ -0,0 +1,50 @@ +# Maintainer: Caleb Maclennan + +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 +} diff --git a/pkgbuilds/libfprint-git/PKGBUILD b/pkgbuilds/libfprint-git/PKGBUILD index 26e4a71e..07fd2b9c 100644 --- a/pkgbuilds/libfprint-git/PKGBUILD +++ b/pkgbuilds/libfprint-git/PKGBUILD @@ -1,22 +1,18 @@ # Maintainer: Mikko Nyman # Based on the Arch libfprint package by Jan Alexander Steffens (heftig). # -# Carried the focaltech_moc driver and the FocalTech FT9349 device ID -# (USB 2808:a97a, ASUS ExpertBook Ultra B9406CAA) while mainline libfprint -# releases (1.94.x) lacked them. libfprint 1.94.100 ships both, so Omarchy -# has moved back to stock libfprint and no longer installs this package. -# -# Kept around pinned to the last known good commit as an escape hatch; -# the fallback patch stays for the conditional in prepare(). +# Omarchy's fingerprint setup installs this in place of stock libfprint, so +# readers upstream supports ahead of an Arch release reach users by bumping +# the pin here. Pinned to the commit adding the Synaptics reader USB +# 06cb:010b, missing from stock libfprint 1.94.100; the FocalTech MOC support +# carried by the previous pin is retained. pkgname=libfprint-git -pkgver=1.94.10.r12.gd79f157 -pkgrel=1.1 -# The repo already carries an untested 1.94.10.r220.g695663f build that sorts -# above the pinned pkgver; the epoch makes the pinned build win the version -# comparison and gives it a fresh filename that can be promoted. +pkgver=1.94.100.r10.g6f9479c +pkgrel=1 +# Retain the epoch used to supersede the previously published untested build. epoch=1 -pkgdesc="Library for fingerprint readers (libfprint master with FocalTech MOC updates)" +pkgdesc="Library for fingerprint readers (pinned upstream snapshot with FocalTech and Synaptics 06cb:010b support)" url="https://fprint.freedesktop.org/" arch=(x86_64) license=(LGPL-2.1-or-later) @@ -47,7 +43,7 @@ provides=(libfprint libfprint-2.so) conflicts=(libfprint) groups=(fprint) source=( - "git+https://gitlab.freedesktop.org/libfprint/libfprint.git#commit=d79f157282085738ea8ffbe8c2ae96fb8b3ad831" + "git+https://gitlab.freedesktop.org/libfprint/libfprint.git#commit=6f9479c3d55f847c1b3769f28ceb99227f9858cf" "focaltech_moc-add-pid-0xA97A.patch" ) b2sums=( @@ -79,9 +75,6 @@ build() { ) arch-meson libfprint build "${meson_options[@]}" meson compile -C build - # Regenerate autosuspend.hwdb from the patched id_table[] so the - # udev-hwdb test in check() doesn't fail on the new PID 0xA97A. - ninja -C build sync-udev-hwdb } check() { diff --git a/pkgbuilds/limine-mkinitcpio-hook/.omarchy/patches/arm-gradle.patch b/pkgbuilds/limine-mkinitcpio-hook/.omarchy/patches/arm-gradle.patch new file mode 100644 index 00000000..d7af3e8a --- /dev/null +++ b/pkgbuilds/limine-mkinitcpio-hook/.omarchy/patches/arm-gradle.patch @@ -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() { diff --git a/pkgbuilds/limine-mkinitcpio-hook/PKGBUILD b/pkgbuilds/limine-mkinitcpio-hook/PKGBUILD index e7a24b7d..b2e63c8c 100644 --- a/pkgbuilds/limine-mkinitcpio-hook/PKGBUILD +++ b/pkgbuilds/limine-mkinitcpio-hook/PKGBUILD @@ -3,14 +3,16 @@ _pkgname="limine-entry-tool" pkgname="limine-mkinitcpio-hook" _pkgver=1.38.0 _extver="" +_gradle_version=9.7.1 pkgver="${_pkgver}${_extver}" -pkgrel=1 +pkgrel=1.1 pkgdesc="Install kernels for the Limine bootloader." arch=('x86_64' 'aarch64') 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 @@ optdepends=( '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 @@ build() { 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() { diff --git a/pkgbuilds/limine-snapper-sync/.omarchy/patches/arm-gradle.patch b/pkgbuilds/limine-snapper-sync/.omarchy/patches/arm-gradle.patch new file mode 100644 index 00000000..76ad31a0 --- /dev/null +++ b/pkgbuilds/limine-snapper-sync/.omarchy/patches/arm-gradle.patch @@ -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() { diff --git a/pkgbuilds/limine-snapper-sync/PKGBUILD b/pkgbuilds/limine-snapper-sync/PKGBUILD index c8f19624..97d9add2 100644 --- a/pkgbuilds/limine-snapper-sync/PKGBUILD +++ b/pkgbuilds/limine-snapper-sync/PKGBUILD @@ -1,13 +1,15 @@ # Maintainer: Zesko pkgname="limine-snapper-sync" pkgver=1.31.0 -pkgrel=1 +pkgrel=1.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 @@ optdepends=( '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 @@ build() { 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() { diff --git a/pkgbuilds/link-studio/PKGBUILD b/pkgbuilds/link-studio/PKGBUILD index 034b49cc..361f4403 100644 --- a/pkgbuilds/link-studio/PKGBUILD +++ b/pkgbuilds/link-studio/PKGBUILD @@ -1,6 +1,6 @@ # Maintainer: Jake Weaver pkgname=link-studio -pkgver=1.0.2 +pkgver=1.0.4 pkgrel=1 pkgdesc="Native Linux controller for Insta360 Link webcams" arch=('x86_64') @@ -28,7 +28,7 @@ optdepends=( 'whisper-cpp: offline meeting transcription' ) source=("${pkgname}-${pkgver}.tar.gz::https://github.com/jweaver60/link-studio/releases/download/v${pkgver}/${pkgname}-${pkgver}.tar.gz") -sha256sums=('b0fc912d8b7924661b57e427280c9766cbb0f01b7a2b3f3cdb1885969f8cfdc6') +sha256sums=('f04156d34ecf5e858bd259dca824f970af53ff177185516fafff15766a0880e9') build() { cd "link_studio-${pkgver}" diff --git a/pkgbuilds/mise-bin/PKGBUILD b/pkgbuilds/mise-bin/PKGBUILD index dc92f591..9d59d455 100644 --- a/pkgbuilds/mise-bin/PKGBUILD +++ b/pkgbuilds/mise-bin/PKGBUILD @@ -1,6 +1,6 @@ # Maintainer: Jeff Dickey pkgname=mise-bin -pkgver=2026.8.15 +pkgver=2026.9.1 pkgrel=1 pkgdesc="dev tools, env vars, task runner" arch=('x86_64' 'aarch64') @@ -14,8 +14,8 @@ provides=('mise') conflicts=('mise') source_x86_64=("https://github.com/jdx/mise/releases/download/v${pkgver}/mise-v${pkgver}-linux-x64.tar.xz") source_aarch64=("https://github.com/jdx/mise/releases/download/v${pkgver}/mise-v${pkgver}-linux-arm64.tar.xz") -sha256sums_x86_64=('0ed4ee8bcf229718bead0d0aa83de865803f3fe25cc6c71a41e3a8bd57c40a33') -sha256sums_aarch64=('d5f9b7e6b36b5cf5aaab0dc4d7e29ee74224281b1a81a228bdc1cd1f3edf9d49') +sha256sums_x86_64=('bbc21f8fe9b2aa09f74250be15f9bf32e87562e1039d858ad91d7944f057c8e2') +sha256sums_aarch64=('7d0c28d44bd7e1c5444a7d867146afabe43592c728329bc0ed5ec59ca7106c48') package() { install -Dm755 "${srcdir}/mise/bin/mise" "${pkgdir}/usr/bin/mise" diff --git a/pkgbuilds/omakade/.omarchy/package.json b/pkgbuilds/omakade/.omarchy/package.json index 584e2235..482bc952 100644 --- a/pkgbuilds/omakade/.omarchy/package.json +++ b/pkgbuilds/omakade/.omarchy/package.json @@ -2,7 +2,7 @@ "source": "local", "release_ring": "fast", "upstream": { - "github": "tsouth89/omakade", + "github": "btsouth/omakade", "checksums": "SHA256SUMS", "assets": { "any": "omakade-{pkgver}.tar.gz" diff --git a/pkgbuilds/omakade/PKGBUILD b/pkgbuilds/omakade/PKGBUILD index 0ebaf89f..5186e465 100644 --- a/pkgbuilds/omakade/PKGBUILD +++ b/pkgbuilds/omakade/PKGBUILD @@ -1,15 +1,17 @@ pkgname=omakade -pkgver=1.2.3 +pkgver=1.7.0 pkgrel=1 pkgdesc='A beautiful, local-first game library for Omarchy' -arch=('x86_64') -url='https://tsouth89.github.io/omakade/' +arch=('x86_64' 'aarch64') +url='https://btsouth.github.io/omakade/' license=('GPL-3.0-or-later') -depends=('glib2' 'libsecret' 'qt6-base' 'qt6-declarative' 'qt6-wayland' 'sdl3') -makedepends=('cmake' 'ninja' 'pkgconf') +depends=('glib2' 'hicolor-icon-theme' 'libsecret' 'libzip' 'openssl' 'qt6-base' 'qt6-declarative' 'qt6-imageformats' 'qt6-svg' 'qt6-wayland' 'sdl3' 'wayland' 'zstd') +# wayland-protocols turns the optional idle-inhibit feature on +# deterministically instead of leaving it to what the chroot happens to carry. +makedepends=('cmake' 'ninja' 'pkgconf' 'wayland-protocols') options=('!debug') -source=("$pkgname-$pkgver.tar.gz::https://github.com/tsouth89/omakade/releases/download/v$pkgver/$pkgname-$pkgver.tar.gz") -sha256sums=('dcbb57caf982317ae0fd75a1a97c48b80d2b02f92d65b38030876a172eb25606') +source=("$pkgname-$pkgver.tar.gz::https://github.com/btsouth/omakade/releases/download/v$pkgver/$pkgname-$pkgver.tar.gz") +sha256sums=('715cd5e0cb860514276b1af57fe08ced71c1c69a6aeebd93aa51f1ca367eefa8') build() { cmake -S "$pkgname-$pkgver" -B build -G Ninja \ diff --git a/pkgbuilds/omarchy-dev/PKGBUILD b/pkgbuilds/omarchy-dev/PKGBUILD index fac61dba..58630edd 100644 --- a/pkgbuilds/omarchy-dev/PKGBUILD +++ b/pkgbuilds/omarchy-dev/PKGBUILD @@ -5,7 +5,10 @@ pkgrel=1 _pkgver_base=4.0.0 _pkgver_base_tag=v3.8.2 pkgdesc='Beautiful, modern, and opinionated Arch Linux by DHH (quattro branch tip)' -arch=('any') +# The payload is architecture-independent, but the dependency set is not: the +# boot stack differs per architecture (see depends_x86_64 / depends_aarch64), +# and makepkg only honours arch-suffixed arrays on arch-specific packages. +arch=('x86_64' 'aarch64') url='https://github.com/basecamp/omarchy' license=('MIT') provides=('omarchy') @@ -17,18 +20,6 @@ depends=( 'omarchy-keyring' 'omarchy-settings-dev' - # Bootloader stack. Lives here rather than on omarchy-settings because - # omarchy-settings is also installed into the live ISO env (for plymouth - # / /etc/skel seeding), where the limine-mkinitcpio-hook would fail with - # 'Cannot detect an ESP path' inside the chroot. The omarchy-settings - # config drop-ins under /etc/mkinitcpio.conf.d/, /etc/limine-entry-tool.d/, - # and /etc/snapper/config-templates/ only do anything when these are - # installed alongside. - 'limine' - 'limine-mkinitcpio-hook' - 'limine-snapper-sync' - 'snapper' - # Wayland / Hyprland core 'hyprland' 'quickshell' @@ -55,6 +46,29 @@ depends=( 'ttf-jetbrains-mono-nerd-basic' ) +# Bootloader stack. Lives here rather than on omarchy-settings because +# omarchy-settings is also installed into the live ISO env (for plymouth +# / /etc/skel seeding), where the limine-mkinitcpio-hook would fail with +# 'Cannot detect an ESP path' inside the chroot. The omarchy-settings +# config drop-ins under /etc/mkinitcpio.conf.d/, /etc/limine-entry-tool.d/, +# and /etc/snapper/config-templates/ only do anything when these are +# installed alongside. +depends_x86_64=( + 'limine' + 'limine-mkinitcpio-hook' + 'limine-snapper-sync' + 'snapper' +) + +# Apple Silicon boots through m1n1 + GRUB from the Asahi packages and keeps +# Arch Linux ARM's kernel and boot layout, so the Limine/Snapper stack does not +# apply. Wi-Fi on the Broadcom parts only scans reliably through the iwd +# backend, which install/hardware/network.sh selects on Apple Silicon. +depends_aarch64=( + 'iwd' + 'networkmanager' +) + makedepends=( 'git' ) diff --git a/pkgbuilds/omarchy-settings-dev/PKGBUILD b/pkgbuilds/omarchy-settings-dev/PKGBUILD index 9fe2d8af..6c7f8d98 100644 --- a/pkgbuilds/omarchy-settings-dev/PKGBUILD +++ b/pkgbuilds/omarchy-settings-dev/PKGBUILD @@ -5,7 +5,12 @@ pkgrel=1 _pkgver_base=4.0.0 _pkgver_base_tag=v3.8.2 pkgdesc='Omarchy user defaults, /etc/skel content, fonts, plymouth theme, and support helpers (quattro branch tip)' -arch=('any') +# Arch-specific because the shipped /etc tree is not the same on every +# architecture: the Limine, mkinitcpio, zram and oomd drop-ins belong to the +# x86_64 boot and memory stack and are left out of the aarch64 package (see +# package()). makepkg only honours the arch-suffixed arrays below on +# arch-specific packages. +arch=('x86_64' 'aarch64') url='https://github.com/basecamp/omarchy' license=('MIT') provides=('omarchy-settings') @@ -44,15 +49,10 @@ makedepends=( # scriptlet WILL clobber user edits on each upgrade (documented limitation). backup=( 'etc/fastfetch/config.jsonc' - 'etc/mkinitcpio.conf.d/omarchy_hooks.conf' - 'etc/mkinitcpio.conf.d/thunderbolt_module.conf' - 'etc/limine-entry-tool.d/omarchy-defaults.conf' - 'etc/limine-entry-tool.d/omarchy-uki.conf' 'etc/docker/daemon.json' 'etc/gnupg/dirmngr.conf' 'etc/sysctl.d/99-omarchy-sysctl.conf' 'etc/sysctl.d/90-omarchy-file-watchers.conf' - 'etc/modprobe.d/omarchy-usb-autosuspend.conf' 'etc/systemd/logind.conf.d/10-ignore-power-button.conf' 'etc/systemd/logind.conf.d/20-inhibit-delay.conf' 'etc/systemd/resolved.conf.d/10-disable-multicast.conf' @@ -63,18 +63,31 @@ backup=( 'etc/systemd/system/plocate-updatedb.service.d/ac-only.conf' 'etc/systemd/system.conf.d/20-omarchy-nofile.conf' 'etc/systemd/user.conf.d/20-omarchy-nofile.conf' - 'etc/systemd/oomd.conf.d/10-omarchy.conf' - 'etc/systemd/zram-generator.conf' 'etc/sddm.conf.d/10-theme.conf' 'etc/sddm.conf.d/10-wayland.conf' 'etc/udev/rules.d/99-omarchy-power-profile.rules' 'etc/udev/rules.d/99-omarchy-wifi-powersave.rules' ) +# backup has no arch-suffixed form, so the x86_64-only drop-ins join it here. +# Each of these paths is removed from the aarch64 package in package(). +if [[ $CARCH == x86_64 ]]; then + backup+=( + 'etc/mkinitcpio.conf.d/omarchy_hooks.conf' + 'etc/mkinitcpio.conf.d/thunderbolt_module.conf' + 'etc/limine-entry-tool.d/omarchy-defaults.conf' + 'etc/limine-entry-tool.d/omarchy-uki.conf' + 'etc/modprobe.d/omarchy-usb-autosuspend.conf' + 'etc/systemd/oomd.conf.d/10-omarchy.conf' + 'etc/systemd/zram-generator.conf' + ) +fi optdepends=( 'omarchy-dev: Full Omarchy desktop (quattro branch tip)' 'inxi: System information for omarchy-debug' 'fastfetch: System information for omarchy-upload-log' +) +optdepends_x86_64=( 'limine: Bootloader the shipped /etc/mkinitcpio.conf.d/ and /etc/limine-entry-tool.d/ drop-ins are for' 'limine-mkinitcpio-hook: Picks up the shipped /etc/mkinitcpio.conf.d/omarchy_hooks.conf' 'limine-snapper-sync: Boot-entry sync for snapper snapshots' @@ -147,6 +160,14 @@ package() { install -d "$pkgdir/usr/share/omarchy/config" cp -a config/. "$pkgdir/usr/share/omarchy/config/" + # The Limine/Snapper notifier has nothing to notify about without the x86_64 + # boot stack; drop it from both seeds so aarch64 users don't autostart a + # helper whose backing tool is not installed. + if [[ $CARCH == aarch64 ]]; then + rm -f "$pkgdir/etc/skel/.config/autostart/limine-snapper-notify.desktop" \ + "$pkgdir/usr/share/omarchy/config/autostart/limine-snapper-notify.desktop" + fi + # Package-owned defaults with real system/XDG locations. User config remains # higher priority: ~/.config/uwsm/default, ~/.config/uwsm/env.d, # ~/.config/environment.d, ~/.config/fontconfig, ~/.config/xdg-terminals.list, @@ -174,9 +195,14 @@ package() { install -Dm644 default/systemd/user/omarchy-crash-watch.service "$pkgdir/usr/lib/systemd/user/omarchy-crash-watch.service" # Marks app.slice (and only app.slice) as a systemd-oomd kill candidate, so # memory pressure costs one app scope instead of the compositor session. - # Thresholds live in etc/systemd/oomd.conf.d/10-omarchy.conf. - install -Dm644 default/systemd/user/app.slice.d/10-oomd.conf "$pkgdir/usr/lib/systemd/user/app.slice.d/10-oomd.conf" - install -Dm644 default/systemd/zram-generator.conf.d/90-omarchy.conf "$pkgdir/usr/lib/systemd/zram-generator.conf.d/90-omarchy.conf" + # Thresholds live in etc/systemd/oomd.conf.d/10-omarchy.conf. Both this and + # the zram vendor drop-in belong to the x86_64 memory stack: the aarch64 + # install neither enables systemd-oomd nor configures zram swap. + if [[ $CARCH == x86_64 ]]; then + install -Dm644 default/systemd/user/app.slice.d/10-oomd.conf "$pkgdir/usr/lib/systemd/user/app.slice.d/10-oomd.conf" + install -Dm644 default/systemd/zram-generator.conf.d/90-omarchy.conf "$pkgdir/usr/lib/systemd/zram-generator.conf.d/90-omarchy.conf" + fi + install -Dm644 default/systemd/system/plocate-updatedb.service.d/10-omarchy.conf "$pkgdir/usr/lib/systemd/system/plocate-updatedb.service.d/10-omarchy.conf" # Same for applications/** — used by omarchy-refresh-applications, # omarchy-install-gaming-*, etc. to copy .desktop files into ~/.local/share. @@ -190,6 +216,25 @@ package() { # stage separately below). install -d "$pkgdir/etc" cp -a etc/. "$pkgdir/etc/" + if [[ $CARCH == aarch64 ]]; then + # The x86_64 boot stack's drop-ins must not ship on aarch64: mkinitcpio + # reads every file under /etc/mkinitcpio.conf.d/, so omarchy_hooks.conf + # would inject the Limine hooks into the Asahi kernel's initramfs, and the + # Limine entry-tool config has no consumer without Limine. + rm -rf "$pkgdir/etc/limine-entry-tool.d" "$pkgdir/etc/mkinitcpio.conf.d" + # Memory stack: no zram device or zswap on the aarch64 install, and + # systemd-oomd is not enabled there, so the vm.* reclaim tuning written + # for zram would be wrong for it. Keep only the network tuning. + rm -rf "$pkgdir/etc/systemd/oomd.conf.d" + rm -f "$pkgdir/etc/systemd/zram-generator.conf" "$pkgdir/etc/tmpfiles.d/omarchy-zswap.conf" + cat >"$pkgdir/etc/sysctl.d/99-omarchy-sysctl.conf" <<'EOF' +# Solve common flakiness with SSH (MTU discovery on flaky links). +net.ipv4.tcp_mtu_probing=1 +EOF + # USB autosuspend is left at the kernel default on Apple Silicon. + rm -f "$pkgdir/etc/modprobe.d/omarchy-usb-autosuspend.conf" + rmdir "$pkgdir/etc/modprobe.d" "$pkgdir/etc/tmpfiles.d" 2>/dev/null || true + fi # sudoers.d files must be mode 0440 or sudo will refuse them, and the # directory must be 0750 to match the Arch sudo package's filesystem # convention (otherwise pacman warns about dir-mode drift on every install). @@ -280,6 +325,10 @@ EOF # live root config. install -d "$pkgdir/usr/share/omarchy/default" cp -a default/. "$pkgdir/usr/share/omarchy/default/" + # The Limine template is read by the x86_64 ISO orchestrator only. + if [[ $CARCH == aarch64 ]]; then + rm -rf "$pkgdir/usr/share/omarchy/default/limine" + fi # Snapper config template used by the install-time `snapper create-config`. install -Dm644 default/snapper/root \ diff --git a/pkgbuilds/omarchy-settings-dev/omarchy-settings-dev.install b/pkgbuilds/omarchy-settings-dev/omarchy-settings-dev.install index fd977b16..19f54e70 100644 --- a/pkgbuilds/omarchy-settings-dev/omarchy-settings-dev.install +++ b/pkgbuilds/omarchy-settings-dev/omarchy-settings-dev.install @@ -1,6 +1,31 @@ +_apple_silicon() { + [[ $(uname -m) == aarch64 ]] && grep -aq 'apple,' /proc/device-tree/compatible 2>/dev/null +} + _etc_overrides_apply() { echo "Applying Omarchy etc-overrides..." + # The hardened CUPS authorization applies on every platform. It waits for the + # file the cups package owns: an ISO bootstrap without cups installed is left + # alone and picks it up once cups lands. + if [[ -f /etc/cups/cups-files.conf && -f /usr/share/omarchy/etc-overrides/cups-cups-files.conf ]]; then + systemd-sysusers /etc/sysusers.d/omarchy-cups-browsed.conf + install -m 0640 -o root -g cups /usr/share/omarchy/etc-overrides/cups-cups-files.conf /etc/cups/cups-files.conf + fi + + # Apple Silicon installs run on Arch Linux ARM's base with the Asahi + # packages, and keep that system identity: /etc/os-release stays the + # symlink to the distribution's /usr/lib/os-release, and the PAM, NSS, + # plymouth and skel overrides below are not applied there. + if _apple_silicon; then + echo "Preserving Arch Linux ARM system configuration on Apple Silicon..." + if [[ -f /usr/lib/os-release ]]; then + rm -f /etc/os-release + ln -s ../usr/lib/os-release /etc/os-release + fi + return 0 + fi + # Ensure parent dirs exist. cups-browsed may not be installed yet during ISO # bootstrap; do not create /etc/cups/cups-browsed.conf before the package is # installed or pacman will preserve its packaged file as a noisy .pacnew. @@ -21,10 +46,6 @@ _etc_overrides_apply() { install -d /etc/cups cp -f /usr/share/omarchy/etc-overrides/cups-cups-browsed.conf /etc/cups/cups-browsed.conf fi - if [[ -f /etc/cups/cups-files.conf && -f /usr/share/omarchy/etc-overrides/cups-cups-files.conf ]]; then - systemd-sysusers /etc/sysusers.d/omarchy-cups-browsed.conf - install -m 0640 -o root -g cups /usr/share/omarchy/etc-overrides/cups-cups-files.conf /etc/cups/cups-files.conf - fi cp -f /usr/share/omarchy/etc-overrides/plymouth-plymouthd.conf /etc/plymouth/plymouthd.conf cp -f /usr/share/omarchy/etc-overrides/dot.bashrc /etc/skel/.bashrc chmod 0644 /etc/os-release /etc/security/faillock.conf /etc/nsswitch.conf \ diff --git a/pkgbuilds/omarchy-settings/PKGBUILD b/pkgbuilds/omarchy-settings/PKGBUILD index e48f36bc..f466293f 100644 --- a/pkgbuilds/omarchy-settings/PKGBUILD +++ b/pkgbuilds/omarchy-settings/PKGBUILD @@ -15,7 +15,12 @@ _commit='346e69e1cec6c4e8924531874af6ba010a1bc99e' pkgver=4.0.2 pkgrel=1 pkgdesc='Omarchy user defaults, /etc/skel content, fonts, plymouth theme, and support helpers' -arch=('any') +# Arch-specific because the shipped /etc tree is not the same on every +# architecture: the Limine, mkinitcpio, zram and oomd drop-ins belong to the +# x86_64 boot and memory stack and are left out of the aarch64 package (see +# package()). makepkg only honours the arch-suffixed arrays below on +# arch-specific packages. +arch=('x86_64' 'aarch64') url='https://github.com/basecamp/omarchy' license=('MIT') conflicts=('omarchy-settings-dev') @@ -53,15 +58,10 @@ makedepends=( # scriptlet WILL clobber user edits on each upgrade (documented limitation). backup=( 'etc/fastfetch/config.jsonc' - 'etc/mkinitcpio.conf.d/omarchy_hooks.conf' - 'etc/mkinitcpio.conf.d/thunderbolt_module.conf' - 'etc/limine-entry-tool.d/omarchy-defaults.conf' - 'etc/limine-entry-tool.d/omarchy-uki.conf' 'etc/docker/daemon.json' 'etc/gnupg/dirmngr.conf' 'etc/sysctl.d/99-omarchy-sysctl.conf' 'etc/sysctl.d/90-omarchy-file-watchers.conf' - 'etc/modprobe.d/omarchy-usb-autosuspend.conf' 'etc/systemd/logind.conf.d/10-ignore-power-button.conf' 'etc/systemd/logind.conf.d/20-inhibit-delay.conf' 'etc/systemd/resolved.conf.d/10-disable-multicast.conf' @@ -72,18 +72,31 @@ backup=( 'etc/systemd/system/plocate-updatedb.service.d/ac-only.conf' 'etc/systemd/system.conf.d/20-omarchy-nofile.conf' 'etc/systemd/user.conf.d/20-omarchy-nofile.conf' - 'etc/systemd/oomd.conf.d/10-omarchy.conf' - 'etc/systemd/zram-generator.conf' 'etc/sddm.conf.d/10-theme.conf' 'etc/sddm.conf.d/10-wayland.conf' 'etc/udev/rules.d/99-omarchy-power-profile.rules' 'etc/udev/rules.d/99-omarchy-wifi-powersave.rules' ) +# backup has no arch-suffixed form, so the x86_64-only drop-ins join it here. +# Each of these paths is removed from the aarch64 package in package(). +if [[ $CARCH == x86_64 ]]; then + backup+=( + 'etc/mkinitcpio.conf.d/omarchy_hooks.conf' + 'etc/mkinitcpio.conf.d/thunderbolt_module.conf' + 'etc/limine-entry-tool.d/omarchy-defaults.conf' + 'etc/limine-entry-tool.d/omarchy-uki.conf' + 'etc/modprobe.d/omarchy-usb-autosuspend.conf' + 'etc/systemd/oomd.conf.d/10-omarchy.conf' + 'etc/systemd/zram-generator.conf' + ) +fi optdepends=( 'omarchy: Full Omarchy desktop' 'inxi: System information for omarchy-debug' 'fastfetch: System information for omarchy-upload-log' +) +optdepends_x86_64=( 'limine: Bootloader the shipped /etc/mkinitcpio.conf.d/ and /etc/limine-entry-tool.d/ drop-ins are for' 'limine-mkinitcpio-hook: Picks up the shipped /etc/mkinitcpio.conf.d/omarchy_hooks.conf' 'limine-snapper-sync: Boot-entry sync for snapper snapshots' @@ -142,6 +155,14 @@ package() { install -d "$pkgdir/usr/share/omarchy/config" cp -a config/. "$pkgdir/usr/share/omarchy/config/" + # The Limine/Snapper notifier has nothing to notify about without the x86_64 + # boot stack; drop it from both seeds so aarch64 users don't autostart a + # helper whose backing tool is not installed. + if [[ $CARCH == aarch64 ]]; then + rm -f "$pkgdir/etc/skel/.config/autostart/limine-snapper-notify.desktop" \ + "$pkgdir/usr/share/omarchy/config/autostart/limine-snapper-notify.desktop" + fi + # Package-owned defaults with real system/XDG locations. User config remains # higher priority: ~/.config/uwsm/default, ~/.config/uwsm/env.d, # ~/.config/environment.d, ~/.config/fontconfig, ~/.config/xdg-terminals.list, @@ -169,9 +190,17 @@ package() { install -Dm644 default/systemd/user/omarchy-crash-watch.service "$pkgdir/usr/lib/systemd/user/omarchy-crash-watch.service" # Marks app.slice (and only app.slice) as a systemd-oomd kill candidate, so # memory pressure costs one app scope instead of the compositor session. - # Thresholds live in etc/systemd/oomd.conf.d/10-omarchy.conf. - install -Dm644 default/systemd/user/app.slice.d/10-oomd.conf "$pkgdir/usr/lib/systemd/user/app.slice.d/10-oomd.conf" - install -Dm644 default/systemd/zram-generator.conf.d/90-omarchy.conf "$pkgdir/usr/lib/systemd/zram-generator.conf.d/90-omarchy.conf" + # Thresholds live in etc/systemd/oomd.conf.d/10-omarchy.conf. Both this and + # the zram vendor drop-in belong to the x86_64 memory stack: the aarch64 + # install neither enables systemd-oomd nor configures zram swap. + if [[ $CARCH == x86_64 ]]; then + install -Dm644 default/systemd/user/app.slice.d/10-oomd.conf "$pkgdir/usr/lib/systemd/user/app.slice.d/10-oomd.conf" + install -Dm644 default/systemd/zram-generator.conf.d/90-omarchy.conf "$pkgdir/usr/lib/systemd/zram-generator.conf.d/90-omarchy.conf" + fi + # Older release pins still configure locate through install/config/locate.sh. + if [[ -f default/systemd/system/plocate-updatedb.service.d/10-omarchy.conf ]]; then + install -Dm644 default/systemd/system/plocate-updatedb.service.d/10-omarchy.conf "$pkgdir/usr/lib/systemd/system/plocate-updatedb.service.d/10-omarchy.conf" + fi # Same for applications/** — used by omarchy-refresh-applications, # omarchy-install-gaming-*, etc. to copy .desktop files into ~/.local/share. @@ -185,6 +214,25 @@ package() { # stage separately below). install -d "$pkgdir/etc" cp -a etc/. "$pkgdir/etc/" + if [[ $CARCH == aarch64 ]]; then + # The x86_64 boot stack's drop-ins must not ship on aarch64: mkinitcpio + # reads every file under /etc/mkinitcpio.conf.d/, so omarchy_hooks.conf + # would inject the Limine hooks into the Asahi kernel's initramfs, and the + # Limine entry-tool config has no consumer without Limine. + rm -rf "$pkgdir/etc/limine-entry-tool.d" "$pkgdir/etc/mkinitcpio.conf.d" + # Memory stack: no zram device or zswap on the aarch64 install, and + # systemd-oomd is not enabled there, so the vm.* reclaim tuning written + # for zram would be wrong for it. Keep only the network tuning. + rm -rf "$pkgdir/etc/systemd/oomd.conf.d" + rm -f "$pkgdir/etc/systemd/zram-generator.conf" "$pkgdir/etc/tmpfiles.d/omarchy-zswap.conf" + cat >"$pkgdir/etc/sysctl.d/99-omarchy-sysctl.conf" <<'EOF' +# Solve common flakiness with SSH (MTU discovery on flaky links). +net.ipv4.tcp_mtu_probing=1 +EOF + # USB autosuspend is left at the kernel default on Apple Silicon. + rm -f "$pkgdir/etc/modprobe.d/omarchy-usb-autosuspend.conf" + rmdir "$pkgdir/etc/modprobe.d" "$pkgdir/etc/tmpfiles.d" 2>/dev/null || true + fi # sudoers.d files must be mode 0440 or sudo will refuse them, and the # directory must be 0750 to match the Arch sudo package's filesystem # convention (otherwise pacman warns about dir-mode drift on every install). @@ -275,6 +323,10 @@ EOF # live root config. install -d "$pkgdir/usr/share/omarchy/default" cp -a default/. "$pkgdir/usr/share/omarchy/default/" + # The Limine template is read by the x86_64 ISO orchestrator only. + if [[ $CARCH == aarch64 ]]; then + rm -rf "$pkgdir/usr/share/omarchy/default/limine" + fi # Snapper config template used by the install-time `snapper create-config`. install -Dm644 default/snapper/root \ diff --git a/pkgbuilds/omarchy-settings/omarchy-settings.install b/pkgbuilds/omarchy-settings/omarchy-settings.install index c02cc276..4ebb226d 100644 --- a/pkgbuilds/omarchy-settings/omarchy-settings.install +++ b/pkgbuilds/omarchy-settings/omarchy-settings.install @@ -1,6 +1,31 @@ +_apple_silicon() { + [[ $(uname -m) == aarch64 ]] && grep -aq 'apple,' /proc/device-tree/compatible 2>/dev/null +} + _etc_overrides_apply() { echo "Applying Omarchy etc-overrides..." + # The hardened CUPS authorization applies on every platform. It waits for the + # file the cups package owns: an ISO bootstrap without cups installed is left + # alone and picks it up once cups lands. + if [[ -f /etc/cups/cups-files.conf && -f /usr/share/omarchy/etc-overrides/cups-cups-files.conf ]]; then + systemd-sysusers /etc/sysusers.d/omarchy-cups-browsed.conf + install -m 0640 -o root -g cups /usr/share/omarchy/etc-overrides/cups-cups-files.conf /etc/cups/cups-files.conf + fi + + # Apple Silicon installs run on Arch Linux ARM's base with the Asahi + # packages, and keep that system identity: /etc/os-release stays the + # symlink to the distribution's /usr/lib/os-release, and the PAM, NSS, + # plymouth and skel overrides below are not applied there. + if _apple_silicon; then + echo "Preserving Arch Linux ARM system configuration on Apple Silicon..." + if [[ -f /usr/lib/os-release ]]; then + rm -f /etc/os-release + ln -s ../usr/lib/os-release /etc/os-release + fi + return 0 + fi + # Ensure parent dirs exist. cups-browsed may not be installed yet during ISO # bootstrap; do not create /etc/cups/cups-browsed.conf before the package is # installed or pacman will preserve its packaged file as a noisy .pacnew. @@ -21,10 +46,6 @@ _etc_overrides_apply() { install -d /etc/cups cp -f /usr/share/omarchy/etc-overrides/cups-cups-browsed.conf /etc/cups/cups-browsed.conf fi - if [[ -f /etc/cups/cups-files.conf && -f /usr/share/omarchy/etc-overrides/cups-cups-files.conf ]]; then - systemd-sysusers /etc/sysusers.d/omarchy-cups-browsed.conf - install -m 0640 -o root -g cups /usr/share/omarchy/etc-overrides/cups-cups-files.conf /etc/cups/cups-files.conf - fi cp -f /usr/share/omarchy/etc-overrides/plymouth-plymouthd.conf /etc/plymouth/plymouthd.conf cp -f /usr/share/omarchy/etc-overrides/dot.bashrc /etc/skel/.bashrc chmod 0644 /etc/os-release /etc/security/faillock.conf /etc/nsswitch.conf \ diff --git a/pkgbuilds/omarchy/PKGBUILD b/pkgbuilds/omarchy/PKGBUILD index a3af2e87..2e20a38d 100644 --- a/pkgbuilds/omarchy/PKGBUILD +++ b/pkgbuilds/omarchy/PKGBUILD @@ -15,7 +15,10 @@ _commit='346e69e1cec6c4e8924531874af6ba010a1bc99e' pkgver=4.0.2 pkgrel=1 pkgdesc='Beautiful, modern, and opinionated Arch Linux by DHH' -arch=('any') +# The payload is architecture-independent, but the dependency set is not: the +# boot stack differs per architecture (see depends_x86_64 / depends_aarch64), +# and makepkg only honours arch-suffixed arrays on arch-specific packages. +arch=('x86_64' 'aarch64') url='https://github.com/basecamp/omarchy' license=('MIT') conflicts=('omarchy-dev') @@ -31,18 +34,6 @@ depends=( # from the same _commit, and forces the pair to upgrade together. "omarchy-settings=${pkgver}" - # Bootloader stack. Lives here rather than on omarchy-settings because - # omarchy-settings is also installed into the live ISO env (for plymouth - # / /etc/skel seeding), where the limine-mkinitcpio-hook would fail with - # 'Cannot detect an ESP path' inside the chroot. The omarchy-settings - # config drop-ins under /etc/mkinitcpio.conf.d/, /etc/limine-entry-tool.d/, - # and /etc/snapper/config-templates/ only do anything when these are - # installed alongside. - 'limine' - 'limine-mkinitcpio-hook' - 'limine-snapper-sync' - 'snapper' - # Wayland / Hyprland core 'hyprland' 'quickshell' @@ -69,6 +60,29 @@ depends=( 'ttf-jetbrains-mono-nerd-basic' ) +# Bootloader stack. Lives here rather than on omarchy-settings because +# omarchy-settings is also installed into the live ISO env (for plymouth +# / /etc/skel seeding), where the limine-mkinitcpio-hook would fail with +# 'Cannot detect an ESP path' inside the chroot. The omarchy-settings +# config drop-ins under /etc/mkinitcpio.conf.d/, /etc/limine-entry-tool.d/, +# and /etc/snapper/config-templates/ only do anything when these are +# installed alongside. +depends_x86_64=( + 'limine' + 'limine-mkinitcpio-hook' + 'limine-snapper-sync' + 'snapper' +) + +# Apple Silicon boots through m1n1 + GRUB from the Asahi packages and keeps +# Arch Linux ARM's kernel and boot layout, so the Limine/Snapper stack does not +# apply. Wi-Fi on the Broadcom parts only scans reliably through the iwd +# backend, which install/hardware/network.sh selects on Apple Silicon. +depends_aarch64=( + 'iwd' + 'networkmanager' +) + makedepends=( 'git' ) diff --git a/pkgbuilds/omareel/.omarchy/package.json b/pkgbuilds/omareel/.omarchy/package.json new file mode 100644 index 00000000..fed23784 --- /dev/null +++ b/pkgbuilds/omareel/.omarchy/package.json @@ -0,0 +1,6 @@ +{ + "source": "local", + "rebuild_on": [ + "hyprland" + ] +} diff --git a/pkgbuilds/omareel/PKGBUILD b/pkgbuilds/omareel/PKGBUILD new file mode 100644 index 00000000..e43106e0 --- /dev/null +++ b/pkgbuilds/omareel/PKGBUILD @@ -0,0 +1,61 @@ +# Maintainer: Ryan + +pkgname=omareel +pkgver=0.1.0 +pkgrel=1 +pkgdesc='Screen recorder and editor for Omarchy with a synthetic cursor, auto zooms, and a camera bubble' +arch=('x86_64' 'aarch64') +url='https://github.com/omacom/omareel' +license=('MIT') +install='omareel.install' +options=('!debug') +depends=( + 'ffmpeg' + 'gpu-screen-recorder' + 'hicolor-icon-theme' + 'hyprland' + 'layer-shell-qt' + 'libevdev' + 'libjpeg-turbo' + 'qt6-base' + 'qt6-declarative' + 'qt6-multimedia' + 'qt6-svg' + 'slurp' + 'xdg-desktop-portal' +) +makedepends=( + 'cmake' + 'ninja' + 'pkgconf' + 'qt6-shadertools' + 'wayland' +) +optdepends=('gifsicle: optimize exported GIF files') +conflicts=('omarecord') +source=("$pkgname-$pkgver.tar.gz::$url/archive/refs/tags/v$pkgver.tar.gz") +sha256sums=('330d8e2805135af33bf35378e2e961094fc375075d38120c8270bd42fe04ce5b') + +build() { + cd "$srcdir/$pkgname-$pkgver" + ./bin/build +} + +package() { + cd "$srcdir/$pkgname-$pkgver" + install -Dm755 build/omareel "$pkgdir/usr/bin/omareel" + ln -s omareel "$pkgdir/usr/bin/omarecord" + # The capture-exclusion plugin is bound to the Hyprland commit it was built + # against; rebuild_on hyprland keeps this package in step with the compositor. + install -Dm755 build/plugin/omareel-capture-exclude.so "$pkgdir/usr/lib/omareel/omareel-capture-exclude.so" + install -Dm644 build/plugin/omareel-capture-exclude.so.hash "$pkgdir/usr/lib/omareel/omareel-capture-exclude.so.hash" + install -Dm644 LICENSE "$pkgdir/usr/share/licenses/$pkgname/LICENSE" + install -Dm644 LICENSES/lucide.txt "$pkgdir/usr/share/licenses/$pkgname/lucide.txt" + install -Dm644 pkg/omareel.svg "$pkgdir/usr/share/icons/hicolor/scalable/apps/omareel.svg" + local size + for size in 16 32 48 64 128 256 512; do + install -Dm644 "pkg/icons/omareel-$size.png" \ + "$pkgdir/usr/share/icons/hicolor/${size}x${size}/apps/omareel.png" + done + install -Dm644 pkg/omareel.desktop "$pkgdir/usr/share/applications/omareel.desktop" +} diff --git a/pkgbuilds/omareel/omareel.install b/pkgbuilds/omareel/omareel.install new file mode 100644 index 00000000..3fa0c2a1 --- /dev/null +++ b/pkgbuilds/omareel/omareel.install @@ -0,0 +1,13 @@ +post_install() { + command -v update-desktop-database >/dev/null 2>&1 && update-desktop-database -q + command -v gtk-update-icon-cache >/dev/null 2>&1 && gtk-update-icon-cache -q -t -f usr/share/icons/hicolor + return 0 +} + +post_upgrade() { + post_install +} + +post_remove() { + post_install +} diff --git a/pkgbuilds/omasnap/PKGBUILD b/pkgbuilds/omasnap/PKGBUILD index 8404121e..24b7e086 100644 --- a/pkgbuilds/omasnap/PKGBUILD +++ b/pkgbuilds/omasnap/PKGBUILD @@ -4,7 +4,7 @@ pkgname=omasnap pkgver=1.20.1 pkgrel=1 pkgdesc="Native Wayland screenshot and annotation overlay for Hyprland" -arch=('x86_64') +arch=('x86_64' 'aarch64') url="https://github.com/tobi/omasnap" license=('MIT' 'OFL-1.1') depends=( diff --git a/pkgbuilds/omazed/.omarchy/package.json b/pkgbuilds/omazed/.omarchy/package.json index 9f738bb8..4d2f6593 100644 --- a/pkgbuilds/omazed/.omarchy/package.json +++ b/pkgbuilds/omazed/.omarchy/package.json @@ -1,4 +1,4 @@ { "source": "aur", - "upstream_commit": "e2a85e53e2a73cec9170a5fa631805f62b98bb36" + "upstream_commit": "cbc25a60a791ccda07ed5e8300e8acb267d23007" } diff --git a/pkgbuilds/omazed/PKGBUILD b/pkgbuilds/omazed/PKGBUILD index 8d821897..9557018d 100644 --- a/pkgbuilds/omazed/PKGBUILD +++ b/pkgbuilds/omazed/PKGBUILD @@ -1,5 +1,5 @@ pkgname=omazed -pkgver=2.1.1 +pkgver=2.1.2 pkgrel=1 pkgdesc="Live theme switching for Zed in Omarchy - automatically synchronize your Zed editor theme with your Omarchy system theme" arch=('any') @@ -10,7 +10,7 @@ makedepends=('git') backup=() install=omazed.install source=("$pkgname-$pkgver.tar.gz::https://github.com/aps6/$pkgname/archive/refs/tags/v$pkgver.tar.gz") -sha256sums=('083757c5385179f241ac0c6e9dde05ba0c7f465c1fed617779d1ebe2a6dfe036') +sha256sums=('ca0708c86969547b14958a307ae3df89f63d92ffa68af3b5c6bf79c036b8cab9') package() { cd "$srcdir/$pkgname-$pkgver" diff --git a/pkgbuilds/omazed/omazed.install b/pkgbuilds/omazed/omazed.install index 05164413..b0376c67 100644 --- a/pkgbuilds/omazed/omazed.install +++ b/pkgbuilds/omazed/omazed.install @@ -17,11 +17,6 @@ post_upgrade() { echo "" echo "==> Omazed updated to $(pacman -Q omazed | cut -d' ' -f2)" echo "" - echo " Complete the update!" - echo "" - echo " To complete this update, run:" - echo "" - echo " omazed setup" echo "" } diff --git a/pkgbuilds/openai-codex-bin/.omarchy/package.json b/pkgbuilds/openai-codex-bin/.omarchy/package.json index cbdb2338..75046fbf 100644 --- a/pkgbuilds/openai-codex-bin/.omarchy/package.json +++ b/pkgbuilds/openai-codex-bin/.omarchy/package.json @@ -1,5 +1,5 @@ { "source": "aur", "release_ring": "fast", - "upstream_commit": "7298bbbde771f2f33d0942fc429bd27643a9986a" + "upstream_commit": "134a0e93624d39306f94dc4384dbd102b8d37bb2" } diff --git a/pkgbuilds/openai-codex-bin/PKGBUILD b/pkgbuilds/openai-codex-bin/PKGBUILD index a08a92f9..ef7f8519 100644 --- a/pkgbuilds/openai-codex-bin/PKGBUILD +++ b/pkgbuilds/openai-codex-bin/PKGBUILD @@ -2,7 +2,7 @@ # shellcheck disable=SC2034 # Maintainer: Chmouel Boudjnah pkgname=openai-codex-bin -pkgver=0.152.0 +pkgver=0.153.4 pkgrel=1 pkgdesc="Arch Linux package for OpenAI's Codex CLI - Auto Updated" arch=('x86_64' 'aarch64') @@ -21,10 +21,10 @@ source_x86_64=( "codex-${pkgver}-x86_64.tar.gz::https://github.com/openai/codex/releases/download/rust-v${pkgver}/codex-x86_64-unknown-linux-musl.tar.gz" "codex-code-mode-host-${pkgver}-x86_64.tar.gz::https://github.com/openai/codex/releases/download/rust-v${pkgver}/codex-code-mode-host-x86_64-unknown-linux-musl.tar.gz" ) -sha256sums_x86_64=('05f942d3d3c5b5acd9edad56ce2797b6fe72dbb1462b24e5c9bf7dcec9a28a11' - '449cefe35b9f347e3fdbf121e816339b37825eb0bfee7de8298a0a61b6687cba') -sha256sums_aarch64=('37da6b486503c8a42cc4604d2a3d80d388df896dd251e9225f4f3d49b08c2e8c' - '134183c7f5bb2245ed4bb15cfcf00d1a64950adbdd089d33c003c8bcf13267e9') +sha256sums_x86_64=('f479424eca092484dc40d87ae28c44f4cc40234a60045d6131e493800d814a30' + 'f95830a869590957664bbfc67bccb08773806b693670baf15908176f89b4cd31') +sha256sums_aarch64=('5cda6182bd94c3a30f2eb63a495489ebf7f691fddb14d70f48c6c1a5071b6cde' + 'd8047b8d33370d6090e729d27eb76de60a2686baa1c143c138c9b05dc70d813b') source_aarch64=( "codex-${pkgver}-aarch64.tar.gz::https://github.com/openai/codex/releases/download/rust-v${pkgver}/codex-aarch64-unknown-linux-musl.tar.gz" diff --git a/pkgbuilds/openai-codex-desktop/PKGBUILD b/pkgbuilds/openai-codex-desktop/PKGBUILD index a2ca833f..9e6a496b 100644 --- a/pkgbuilds/openai-codex-desktop/PKGBUILD +++ b/pkgbuilds/openai-codex-desktop/PKGBUILD @@ -5,7 +5,7 @@ # the version and checksums below from that repository's package index. pkgname=openai-codex-desktop -pkgver=26.825.51511 +pkgver=26.901.51231 pkgrel=1 pkgdesc="Official ChatGPT desktop app with Codex" arch=('x86_64' 'aarch64') @@ -71,8 +71,8 @@ source_x86_64=("${_deb_x86_64}::${_pool}/${_deb_x86_64}") source_aarch64=("${_deb_aarch64}::${_pool}/${_deb_aarch64}") noextract=("${_deb_x86_64}" "${_deb_aarch64}") sha256sums=('b3a4503b5931f102444bc7015c3cf4e40266cf034e0d682bd2a407dc5b3ee58c') -sha256sums_x86_64=('3554b0022c6cfb513326f43fd11f718835a77086ac4d7ca2ff3ebbba2d4c7f45') -sha256sums_aarch64=('125e36522d43c75bd7958477846ba6b1cddf2eb19cefcb57dda80be1742f917f') +sha256sums_x86_64=('62580188d87c3d3a9369dab7c73b42a8a32518d4df8a2d5bae6466ddeac5c05e') +sha256sums_aarch64=('02a2f5c6cb69509c62abcbdd13c76b139cdb2ca9edde7537239ddde024077ea0') package() { cd "${srcdir}" diff --git a/pkgbuilds/openclaw/.omarchy/package.json b/pkgbuilds/openclaw/.omarchy/package.json new file mode 100644 index 00000000..1c25fbcd --- /dev/null +++ b/pkgbuilds/openclaw/.omarchy/package.json @@ -0,0 +1,11 @@ +{ + "source": "local", + "release_ring": "fast", + "min_release_age": "24h", + "upstream": { + "npm": "openclaw", + "sources": { + "any": ["{npm_tarball}"] + } + } +} diff --git a/pkgbuilds/openclaw/PKGBUILD b/pkgbuilds/openclaw/PKGBUILD new file mode 100644 index 00000000..2da0c56a --- /dev/null +++ b/pkgbuilds/openclaw/PKGBUILD @@ -0,0 +1,72 @@ +# Based on the AUR openclaw package: +# Maintainer: Ralph Torres +# Contributor: Evert Vorster +# Contributor: Emir-Eins +# Contributor: Wuxxin +# Followed here straight from the npm registry (see .omarchy/package.json), since +# upstream's release cadence outruns the AUR. + +pkgname=openclaw +pkgver=2026.9.2 +pkgrel=1 +pkgdesc='Multi-channel AI gateway with extensible messaging integrations' +arch=(x86_64 aarch64) +url=https://github.com/openclaw/openclaw +license=(MIT) + +depends=('nodejs>=22') +makedepends=(npm) +optdepends=( + '1password-cli: 1password skill' + 'curl: weather, openai-whisper-api skills' + 'ffmpeg: video-frames skill' + 'github-cli: github, gh-issues skills' + 'jq: session-logs, trello skills' + 'python-openai-whisper: openai-whisper skill' + 'ripgrep: session-logs skill' + 'tmux: tmux skill' + 'uv: nano-pdf skill' + 'go: for installing skill tools not packaged for Arch' +) +source=($pkgname-$pkgver.tgz::https://registry.npmjs.org/$pkgname/-/$pkgname-$pkgver.tgz) +sha256sums=('3431f4cd2d8dbd6b936def2694ac27e19fa0256295cf4ada0f652ecf1c9ee520') +options=(!debug !strip) +install=$pkgname.install +noextract=($pkgname-$pkgver.tgz) + +package() { + export SHARP_IGNORE_GLOBAL_LIBVIPS=1 + # npm 12 blocks install-time lifecycle scripts unless the package is + # allow-listed, and for a local tarball the allow-list key is the tarball's + # own file: spec, not the package name (arborist matches node.resolved). + # Upstream's postinstall prunes stale dist files and clears the + # .openclaw-lifecycle-pending marker the tarball ships with; without it, + # every openclaw invocation tries to finish that lifecycle itself and dies + # with EACCES on the root-owned install. + # That postinstall also migrates legacy state under whatever home it sees + # (pruning plugin-runtime-deps in ~/.openclaw and friends), so it gets a + # scratch home: a maintainer's own OpenClaw is not the build's to touch. + mkdir -p "$srcdir"/home + env -u OPENCLAW_HOME -u OPENCLAW_STATE_DIR -u OPENCLAW_CONFIG_PATH HOME="$srcdir"/home \ + npm install --silent --global --cache "$srcdir"/npm-cache \ + --allow-scripts="file:$srcdir/$pkgname-$pkgver.tgz" \ + --prefix "$pkgdir"/usr "$srcdir"/$pkgname-$pkgver.tgz + if [[ -e "$pkgdir"/usr/lib/node_modules/$pkgname/.openclaw-lifecycle-pending ]]; then + error "openclaw's postinstall did not run; the package would fail on first use" + return 1 + fi + + cat > $pkgname <<'EOF' +#!/bin/sh +export SHARP_IGNORE_GLOBAL_LIBVIPS=1 +exec node /usr/lib/node_modules/openclaw/openclaw.mjs "$@" +EOF + install -Dm755 -t "$pkgdir"/usr/bin $pkgname + + cd "$pkgdir"/usr/lib/node_modules/$pkgname + install -Dm644 -t "$pkgdir"/usr/share/licenses/$pkgname LICENSE + install -Dm644 -t "$pkgdir"/usr/share/doc/$pkgname README.md CHANGELOG.md + for f in docs/* + do ln -s /usr/lib/node_modules/$pkgname/"$f" "$pkgdir"/usr/share/doc/$pkgname/ + done +} diff --git a/pkgbuilds/openclaw/openclaw.install b/pkgbuilds/openclaw/openclaw.install new file mode 100644 index 00000000..dccd8c6e --- /dev/null +++ b/pkgbuilds/openclaw/openclaw.install @@ -0,0 +1,17 @@ +post_install() { + printf "%s\n" \ + "==> Install gateway service: openclaw gateway install --force" \ + "==> Perform onboarding once: openclaw onboard" +} + +post_upgrade() { + # The gateway runs as a systemd user unit (openclaw-gateway.service in + # ~/.config/systemd/user), which a root pacman hook has no session bus + # to reach, so it cannot be restarted from here and keeps running the + # old code until the user restarts it. + printf "%s\n" \ + "==> Restart the gateway to apply updates:" \ + " openclaw gateway restart" \ + "==> Run doctor to migrate configs, check gateway health, etc.:" \ + " openclaw doctor" +} diff --git a/pkgbuilds/t3code-patched-bin/.omarchy/package.json b/pkgbuilds/perplexity/.omarchy/package.json similarity index 100% rename from pkgbuilds/t3code-patched-bin/.omarchy/package.json rename to pkgbuilds/perplexity/.omarchy/package.json diff --git a/pkgbuilds/perplexity/.omarchy/upstream.sh b/pkgbuilds/perplexity/.omarchy/upstream.sh new file mode 100755 index 00000000..0e3997e0 --- /dev/null +++ b/pkgbuilds/perplexity/.omarchy/upstream.sh @@ -0,0 +1,82 @@ +#!/bin/bash +# Perplexity ships its desktop app from its own Debian repository. The +# per-architecture package index carries the filename and SHA256 of every deb, +# so an update costs two small HTTP requests instead of a 175 MB download. The +# index's Version field drops the build number the pool filename carries, and +# upstream rebuilds under the same marketing version, so the version of record +# here is the full string embedded in the filename. +set -euo pipefail + +BASE_URL="https://packages.perplexity.ai/deb" +declare -A DEB_ARCHES=([x86_64]=amd64 [aarch64]=arm64) + +# Print " " for the newest stanza in a Packages index, version +# meaning the full string from the pool filename. Newest is vercmp's opinion, +# which is the one bin/sync-upstream and pacman both use; sort -V disagrees +# with it over versions like 1.0a. +newest_release() { + local index="$1" + local debarch="$2" + local filename sha256 version best_version="" best_sha256="" + local prefix="pool/main/p/perplexity/perplexity_" + local suffix="_${debarch}.deb" + + while read -r filename sha256; do + # The PKGBUILD reconstructs the pool URL from pkgver alone, so a stanza + # naming anything else -- a rename, or a new pool layout -- has to stop + # the sync rather than pin a checksum to a URL nobody will fetch. + if [[ "$filename" != "${prefix}"*"${suffix}" ]]; then + echo "Unexpected pool filename in the upstream index: $filename" >&2 + return 1 + fi + version="${filename#"$prefix"}" + version="${version%"$suffix"}" + + if [[ -z "$best_version" ]] || [[ "$(vercmp "$version" "$best_version")" -gt 0 ]]; then + best_version="$version" + best_sha256="$sha256" + fi + done < <(awk ' + { sub(/\r$/, "") } + /^Filename:/ { filename = $2 } + /^SHA256:/ { sha256 = $2 } + /^$/ { if (filename && sha256) print filename, sha256; filename = sha256 = "" } + END { if (filename && sha256) print filename, sha256 } + ' <<<"$index") + + [[ -n "$best_version" ]] || return 1 + echo "$best_version $best_sha256" +} + +versions=() +declare -A checksums=() + +for arch in "${!DEB_ARCHES[@]}"; do + index=$(curl -fsSL "$BASE_URL/dists/stable/main/binary-${DEB_ARCHES[$arch]}/Packages") + + read -r version sha256 <<<"$(newest_release "$index" "${DEB_ARCHES[$arch]}")" + if [[ -z "${version:-}" || -z "${sha256:-}" ]]; then + echo "No usable release found for $arch in the upstream package index" >&2 + exit 1 + fi + + versions+=("$version") + checksums[$arch]="$sha256" +done + +# A release lands one architecture at a time, and a single pkgver has to cover +# both -- including the build number, since that is what the download URLs are +# built from. Report no update until they agree; the next run picks it up. +for version in "${versions[@]}"; do + if [[ "$version" != "${versions[0]}" ]]; then + echo "Upstream architectures are mid-release (${versions[*]}); skipping" >&2 + echo '{}' + exit 0 + fi +done + +jq -n \ + --arg pkgver "${versions[0]}" \ + --arg x86_64 "${checksums[x86_64]}" \ + --arg aarch64 "${checksums[aarch64]}" \ + '{pkgver: $pkgver, sha256sums: {x86_64: [$x86_64], aarch64: [$aarch64]}}' diff --git a/pkgbuilds/perplexity/PKGBUILD b/pkgbuilds/perplexity/PKGBUILD new file mode 100644 index 00000000..bbfb0586 --- /dev/null +++ b/pkgbuilds/perplexity/PKGBUILD @@ -0,0 +1,139 @@ +# Maintainer: Spencer Bull + +# Omarchy tracks Perplexity's own Debian repository, the feed the app updates +# itself from. .omarchy/upstream.sh rewrites the version and checksums below +# from that repository's package index. + +pkgname=perplexity +pkgver=26.9.1+build61614 +pkgrel=2 +pkgdesc="Official Perplexity desktop app" +arch=('x86_64' 'aarch64') +url="https://www.perplexity.ai" +# Upstream ships no license text with the app; its terms live at +# https://www.perplexity.ai/hub/legal/terms-of-service +license=('custom') + +depends=( + 'alsa-lib' + 'at-spi2-core' + 'bash' + 'bubblewrap' + 'cairo' + 'dbus' + 'expat' + 'gcc-libs' + 'gdk-pixbuf2' + 'glib2' + 'glibc' + 'gtk3' + 'hicolor-icon-theme' + 'libcups' + 'libdrm' + 'libglvnd' + 'libnotify' + 'libsecret' + 'libx11' + 'libxcb' + 'libxcomposite' + 'libxdamage' + 'libxext' + 'libxfixes' + 'libxkbcommon' + 'libxrandr' + 'libxss' + 'libxtst' + 'mesa' + 'nspr' + 'nss' + 'pango' + 'systemd-libs' + 'util-linux-libs' + 'vulkan-icd-loader' + 'xdg-utils' +) + +optdepends=( + 'apparmor: confine the app with the bundled user-namespace profile' + 'libappindicator-gtk3: tray icon support' + 'docker: containerized local GPU runtime for on-device models' + 'nvidia-container-toolkit: containerized local GPU runtime for on-device models' +) + +makedepends=('libarchive') +options=('!debug' '!strip') + +# Omarchy: the build image compresses with zstd at its default level. Maximum +# zstd buys roughly a third off this ~600 MB Electron tree for a few extra +# minutes of build time -- worth it for a package this large on the fast ring, +# same reasoning as openai-codex-desktop. +COMPRESSZST=(zstd -c -z -q --ultra -22 --threads=0 -) + +# The pool filename carries a build number the index's Version field drops, and +# upstream rebuilds under the same marketing version, so pkgver is the full +# string from the filename. The pool's storage treats a literal '+' in a URL as +# a space and answers 403, hence the %2B in the download URL only. +_deb_x86_64="perplexity_${pkgver}_amd64.deb" +_deb_aarch64="perplexity_${pkgver}_arm64.deb" +_pool="https://packages.perplexity.ai/deb/pool/main/p/perplexity" +source=('perplexity-launcher.sh') +source_x86_64=("${_deb_x86_64}::${_pool}/${_deb_x86_64//+/%2B}") +source_aarch64=("${_deb_aarch64}::${_pool}/${_deb_aarch64//+/%2B}") +noextract=("${_deb_x86_64}" "${_deb_aarch64}") +sha256sums=('b180ca6fbd268712a277a34d4215f176001c0031a4f6360686458623f47fad65') +sha256sums_x86_64=('2fc95f5500335e661af6901bbad5c95e8805cc9f43d9f0b979787c7249c15c33') +sha256sums_aarch64=('917562bad5ab319612c22fca49773cdee163b2e7c9f0c3dd5569fc4878f10237') + +package() { + cd "${srcdir}" + + local deb_var="_deb_${CARCH}" + local deb="${!deb_var}" + + bsdtar -xOf "${deb}" data.tar.xz | + bsdtar --no-same-owner -xf - -C "${pkgdir}" + + # 26.9.1 started shipping a systemd unit under /lib, and owning /lib -- a + # symlink owned by the filesystem package -- makes pacman refuse the whole + # transaction. The unit is also a no-op here: its setup script apt-installs + # Docker and the NVIDIA Container Toolkit and exits on any distro but Ubuntu + # (install those yourself; see optdepends). Delete it, and its parents once + # emptied; then anything else left outside opt/ and usr/ -- whatever its + # type, an empty bin/ or lib64/ included, since those are filesystem-owned + # symlinks too -- stops the build rather than shipping the next conflict. + ( + cd "${pkgdir}" + rm -f lib/systemd/system/perplexity-local-runtime-setup.service + rmdir -p lib/systemd/system 2>/dev/null || true + ) + + local unexpected + unexpected=$(cd "${pkgdir}" && find . -mindepth 1 \ + -path ./opt -prune -o -path ./usr -prune -o -print) + if [[ -n "${unexpected}" ]]; then + echo "Unexpected entries outside opt/ and usr/ in the upstream deb:" >&2 + echo "${unexpected}" >&2 + return 1 + fi + + # pacman never runs the deb's postinst, so the /usr/bin entry it would have + # symlinked is a launcher here instead, and the menu entry goes through it so + # the flags file applies there too. The scheme handler stays upstream's. + install -Dm755 perplexity-launcher.sh "${pkgdir}/usr/bin/perplexity" + sed -i 's|^Exec=.*|Exec=perplexity %U|' \ + "${pkgdir}/usr/share/applications/perplexity.desktop" + + # Arch ships Chromium's and Electron's own sandbox helper setuid, which is + # what keeps the sandbox up on a kernel that denies unprivileged user + # namespaces -- linux-hardened, mainly -- instead of aborting the app. The + # deb leaves this bit to its postinst. + chmod 4755 "${pkgdir}/opt/Perplexity/chrome-sandbox" + + # The profile upstream's postinst would install. It names /opt/Perplexity + # paths, which this package keeps. + install -Dm644 "${pkgdir}/opt/Perplexity/resources/apparmor-profile" \ + "${pkgdir}/etc/apparmor.d/perplexity" + + # Debian package-policy files are not used on Arch Linux. + rm -rf "${pkgdir}/usr/share/doc" +} diff --git a/pkgbuilds/t3code-patched-bin/t3code-launcher.sh b/pkgbuilds/perplexity/perplexity-launcher.sh old mode 100644 new mode 100755 similarity index 73% rename from pkgbuilds/t3code-patched-bin/t3code-launcher.sh rename to pkgbuilds/perplexity/perplexity-launcher.sh index 2157d1c9..2ff179fd --- a/pkgbuilds/t3code-patched-bin/t3code-launcher.sh +++ b/pkgbuilds/perplexity/perplexity-launcher.sh @@ -4,7 +4,7 @@ set -euo pipefail user_flags=() config_home="${XDG_CONFIG_HOME:-}" [[ -n "$config_home" || -z "${HOME:-}" ]] || config_home="$HOME/.config" -flags_file="${config_home:+$config_home/t3code-flags.conf}" +flags_file="${config_home:+$config_home/perplexity-flags.conf}" if [[ -n "$flags_file" && -f "$flags_file" && -r "$flags_file" ]]; then while IFS= read -r line || [[ -n "$line" ]]; do @@ -16,7 +16,8 @@ if [[ -n "$flags_file" && -f "$flags_file" && -r "$flags_file" ]]; then fi # Chromium's own Ozone detection falls back to XWayland often enough to matter, -# and the result is a blurry window on every scaled display. +# and the result is a blurry window on every scaled display. Ask for Wayland +# directly, unless the user has already picked a platform themselves. platform_flags=() if [[ -n "${WAYLAND_DISPLAY:-}" || "${XDG_SESSION_TYPE:-}" == wayland ]]; then platform_flags=(--ozone-platform=wayland) @@ -28,4 +29,4 @@ if [[ -n "${WAYLAND_DISPLAY:-}" || "${XDG_SESSION_TYPE:-}" == wayland ]]; then done fi -exec /usr/lib/t3code/t3code "${platform_flags[@]}" "${user_flags[@]}" "$@" +exec /opt/Perplexity/perplexity "${platform_flags[@]}" "${user_flags[@]}" "$@" diff --git a/pkgbuilds/qmk-hid/.omarchy/package.json b/pkgbuilds/qmk-hid/.omarchy/package.json index d00eb135..aa2fb82a 100644 --- a/pkgbuilds/qmk-hid/.omarchy/package.json +++ b/pkgbuilds/qmk-hid/.omarchy/package.json @@ -1,4 +1,10 @@ { - "source": "aur", - "upstream_commit": "a850f2d07e825f61cb01b87931d2ae5f1802dde1" + "source": "local", + "upstream": { + "git_tags": "https://github.com/FrameworkComputer/qmk_hid.git", + "tag_pattern": "v{pkgver}", + "sources": { + "any": ["https://github.com/FrameworkComputer/qmk_hid/archive/refs/tags/{tag}.tar.gz"] + } + } } diff --git a/pkgbuilds/qmk-hid/PKGBUILD b/pkgbuilds/qmk-hid/PKGBUILD index c71f3af1..f2508dfb 100644 --- a/pkgbuilds/qmk-hid/PKGBUILD +++ b/pkgbuilds/qmk-hid/PKGBUILD @@ -2,16 +2,16 @@ pkgname=qmk-hid _srcname=qmk_hid -pkgver=0.1.11 -pkgrel=3 +pkgver=0.1.13 +pkgrel=1 pkgdesc="Commandline tool for interacting with QMK devices over HID" -arch=('x86_64') +arch=('x86_64' 'aarch64') url="https://github.com/FrameworkComputer/qmk_hid" license=('BSD') depends=('gcc-libs' 'systemd-libs' 'libcap') makedepends=('cargo') source=("$pkgname-$pkgver.tar.gz::$url/archive/v$pkgver.tar.gz") -b2sums=('e898d27cfe8350f4a8cd2938a3d066935093daf1292f41fc10b8f1937059f2b59e4ac34a987f26c8258a5d3b15a900059024fe3a18ed793f7e3b524939ab4c71') +sha256sums=('8a058395b8ba56ff54308a681223f11a6032c54df42b4d0223143ccf09d0c858') options=('!lto') prepare() { diff --git a/pkgbuilds/quickshell-git/.omarchy/package.json b/pkgbuilds/quickshell-git/.omarchy/package.json index 4a7a06e4..2bb526a5 100644 --- a/pkgbuilds/quickshell-git/.omarchy/package.json +++ b/pkgbuilds/quickshell-git/.omarchy/package.json @@ -8,7 +8,7 @@ "qt6-wayland" ], "rebuilt_against": { - "qt6-base": "6.11.2-2", + "qt6-base": "6.11.2-3", "qt6-declarative": "6.11.2-1", "qt6-wayland": "6.11.2-1" } diff --git a/pkgbuilds/quickshell-git/PKGBUILD b/pkgbuilds/quickshell-git/PKGBUILD index 9338de2d..5d1dec64 100644 --- a/pkgbuilds/quickshell-git/PKGBUILD +++ b/pkgbuilds/quickshell-git/PKGBUILD @@ -3,7 +3,7 @@ _pkgname=quickshell pkgname="$_pkgname-git" pkgver=0.3.0.r20.g28771c7 -pkgrel=2 +pkgrel=3 pkgdesc='Flexible toolkit for making desktop shells with QtQuick' arch=(x86_64 aarch64) url='https://git.outfoxxed.me/quickshell/quickshell' diff --git a/pkgbuilds/retroarch-joypad-autoconfig-git/.omarchy/package.json b/pkgbuilds/retroarch-joypad-autoconfig-git/.omarchy/package.json index d2ffaee8..2a9719d3 100644 --- a/pkgbuilds/retroarch-joypad-autoconfig-git/.omarchy/package.json +++ b/pkgbuilds/retroarch-joypad-autoconfig-git/.omarchy/package.json @@ -1,4 +1,3 @@ { - "source": "aur", - "upstream_commit": "80c65c27a9d5511d2b4ce2ca7ad2396ad528f0da" + "source": "local" } diff --git a/pkgbuilds/retroarch-joypad-autoconfig-git/PKGBUILD b/pkgbuilds/retroarch-joypad-autoconfig-git/PKGBUILD index 83cdd485..1b859182 100644 --- a/pkgbuilds/retroarch-joypad-autoconfig-git/PKGBUILD +++ b/pkgbuilds/retroarch-joypad-autoconfig-git/PKGBUILD @@ -1,7 +1,10 @@ # Maintainer: Alexandre Bouvier _pkgname=retroarch-joypad-autoconfig +# Pinned so every architecture packages the same tree and a rebuild of the +# same pkgver reproduces the same contents; bump both together. +_commit=033151045d378b64e712a92592467800d7924227 pkgname=$_pkgname-git -pkgver=1.22.0.r24.g739cee1 +pkgver=1.22.0.r96.g0331510 pkgrel=1 pkgdesc="RetroArch joypad autoconfig files" arch=('any') @@ -12,7 +15,7 @@ makedepends=('git') optdepends=('retroarch') provides=("$_pkgname") conflicts=("$_pkgname") -source=("$_pkgname::git+$url.git") +source=("$_pkgname::git+$url.git#commit=$_commit") b2sums=('SKIP') pkgver() { diff --git a/pkgbuilds/schist-bin/.omarchy/package.json b/pkgbuilds/schist-bin/.omarchy/package.json new file mode 100644 index 00000000..8d64039c --- /dev/null +++ b/pkgbuilds/schist-bin/.omarchy/package.json @@ -0,0 +1,13 @@ +{ + "source": "local", + "release_ring": "fast", + "min_release_age": "24h", + "upstream": { + "github": "Infrawrench/schist", + "digests": true, + "assets": { + "x86_64": "schist-{pkgver}-1-x86_64.pkg.tar.zst", + "aarch64": "schist-{pkgver}-1-aarch64.pkg.tar.zst" + } + } +} diff --git a/pkgbuilds/schist-bin/PKGBUILD b/pkgbuilds/schist-bin/PKGBUILD new file mode 100644 index 00000000..06ee0b3c --- /dev/null +++ b/pkgbuilds/schist-bin/PKGBUILD @@ -0,0 +1,44 @@ +# Maintainer: Infrawrench LLC +pkgname=schist-bin +pkgver=0.12.0 +pkgrel=1 +# Upstream's own package release, embedded in the asset name. It is +# packages.sh's "release=" and only moves when the packaging changes under +# a version that has already shipped; pkgrel above is this package's own. +# The asset templates in .omarchy/package.json carry the same number. +_relver=1 +pkgdesc="Layered image editor with PSD and Affinity support (binary release)" +arch=(x86_64 aarch64) +url="https://github.com/Infrawrench/schist" +license=(MIT) +# The list the payload's own .PKGINFO carries: fontconfig/wayland/ +# vulkan-icd-loader are dlopen'd, so namcap flags them "may not be needed" +# — they are. +depends=(fontconfig freetype2 hicolor-icon-theme libxcb libxkbcommon + libxkbcommon-x11 vulkan-icd-loader wayland) +# A Vulkan ICD is needed to draw, but as a hard dependency makepkg -s would +# resolve it to nvidia-utils in the build container; Omarchy installs the right +# driver per machine. libheif is dlopen'd; everything else opens without it. +optdepends=('vulkan-driver: GPU rendering (any Vulkan ICD, vulkan-swrast in software)' + 'libheif: HEIC import') +provides=(schist) +conflicts=(schist) +# The released binary ships byte-exact: it is already stripped by the +# release workflow, and its build id is what Sentry symbolication matches. +options=(!strip !debug) +# The release asset is already a pacman-format payload, assembled by +# packaging/linux/packages.sh from the CI build — this just re-wraps its +# usr/ tree, so the dependency lists here have to stay in step with that +# script. +source_x86_64=("$url/releases/download/v$pkgver/schist-$pkgver-$_relver-x86_64.pkg.tar.zst") +source_aarch64=("$url/releases/download/v$pkgver/schist-$pkgver-$_relver-aarch64.pkg.tar.zst") +sha256sums_x86_64=('baff8848274f0121911a6408fd3dd082c5ab023c0ae5c9e5be35968c80beb069') +sha256sums_aarch64=('a7a4f062289a140ff0aa5c0c9c4b3cc9f386759e3bfe929a0382de2d20c04eda') + +package() { + # makepkg has already extracted the payload into srcdir; its .PKGINFO + # and .MTREE are upstream's and stay behind, only usr/ is packaged. + cp -a "$srcdir/usr" "$pkgdir/" + # The payload keeps the licence under the upstream pkgname. + mv "$pkgdir/usr/share/licenses/schist" "$pkgdir/usr/share/licenses/$pkgname" +} diff --git a/pkgbuilds/slap-notes-bin/.omarchy/package.json b/pkgbuilds/slap-notes-bin/.omarchy/package.json new file mode 100644 index 00000000..a77bdb3a --- /dev/null +++ b/pkgbuilds/slap-notes-bin/.omarchy/package.json @@ -0,0 +1,12 @@ +{ + "source": "local", + "release_ring": "fast", + "min_release_age": "24h", + "upstream": { + "github": "Onefailatatime/slap-notes", + "checksums": "SHA256SUMS", + "assets": { + "x86_64": "slap-notes-{tag}-linux-x64.tar.zst" + } + } +} diff --git a/pkgbuilds/slap-notes-bin/PKGBUILD b/pkgbuilds/slap-notes-bin/PKGBUILD new file mode 100644 index 00000000..b19a7e72 --- /dev/null +++ b/pkgbuilds/slap-notes-bin/PKGBUILD @@ -0,0 +1,82 @@ +# Maintainer: FunnelFix + +# Slap Notes publishes a plain Electron tree rather than an AppImage, so this +# repackages the vendor tarball as-is. .omarchy/upstream.sh rewrites the version +# and checksum below from the release feed the app updates itself from. + +pkgname=slap-notes-bin +_appname=slap-notes +pkgver=0.2.3 +pkgrel=1 +pkgdesc="Local-first block notes with a wiki-link graph and a built-in AI research agent" +arch=('x86_64') +url="https://slapnotes.com" +license=('LicenseRef-proprietary') +depends=( + 'alsa-lib' + 'at-spi2-core' + 'cairo' + 'dbus' + 'expat' + 'gcc-libs' + 'glib2' + 'glibc' + 'gtk3' + 'hicolor-icon-theme' + 'libcups' + 'libdrm' + # Both are opened by name at runtime rather than linked, so nothing else in + # this list pulls them in: libnotify carries desktop notifications and + # libsecret backs Electron's encrypted storage. + 'libnotify' + 'libsecret' + 'libx11' + 'libxcb' + 'libxcomposite' + 'libxdamage' + 'libxext' + 'libxfixes' + 'libxkbcommon' + 'libxrandr' + 'mesa' + 'nspr' + 'nss' + 'pango' + 'systemd-libs' + 'xdg-utils' +) +provides=("${_appname}=${pkgver}") +conflicts=("${_appname}") +# Electron ships prebuilt binaries: stripping them breaks the bundle. +options=('!strip' '!debug') +install="${pkgname}.install" +_tarball="${_appname}-${pkgver}-linux-x64.tar.zst" +source=("${_appname}-launcher.sh") +source_x86_64=("${pkgname}-${pkgver}.tar.zst::https://github.com/Onefailatatime/slap-notes/releases/download/${pkgver}/${_tarball}") +sha256sums=('9d208c06717906b74c41874bdd3fdba0f0dc5b31d239ae00d19472e6eab2ea29') +sha256sums_x86_64=('56cb7606f41051ba10d088ed8e1ae85a17b20692f4b45c0c6a6e6cec6669a5e2') + +package() { + cd "${srcdir}/${_appname}-${pkgver}" + + install -dm755 "${pkgdir}/opt/${_appname}" + cp -a app/. "${pkgdir}/opt/${_appname}/" + chmod -R a+rX "${pkgdir}/opt/${_appname}" + + install -Dm755 "${srcdir}/${_appname}-launcher.sh" "${pkgdir}/usr/bin/${_appname}" + + # Upstream's entry points straight into /opt, which would step around the + # launcher and hand every desktop-started window to XWayland. + sed -e "s|^Exec=.*|Exec=${_appname} %U|" "${_appname}.desktop" \ + > "${srcdir}/${_appname}.desktop.arch" + install -Dm644 "${srcdir}/${_appname}.desktop.arch" \ + "${pkgdir}/usr/share/applications/${_appname}.desktop" + + local size + for size in 16 32 48 64 128 256 512 1024; do + install -Dm644 "icons/${size}.png" \ + "${pkgdir}/usr/share/icons/hicolor/${size}x${size}/apps/${_appname}.png" + done + + install -Dm644 LICENSE "${pkgdir}/usr/share/licenses/${pkgname}/LICENSE" +} diff --git a/pkgbuilds/slap-notes-bin/slap-notes-bin.install b/pkgbuilds/slap-notes-bin/slap-notes-bin.install new file mode 100755 index 00000000..77fb3b20 --- /dev/null +++ b/pkgbuilds/slap-notes-bin/slap-notes-bin.install @@ -0,0 +1,3 @@ +post_install() { + echo "Extra Chromium flags go in ~/.config/slap-notes-flags.conf, one per line." +} diff --git a/pkgbuilds/slap-notes-bin/slap-notes-launcher.sh b/pkgbuilds/slap-notes-bin/slap-notes-launcher.sh new file mode 100755 index 00000000..ade5c08b --- /dev/null +++ b/pkgbuilds/slap-notes-bin/slap-notes-launcher.sh @@ -0,0 +1,32 @@ +#!/bin/bash +set -euo pipefail + +user_flags=() +config_home="${XDG_CONFIG_HOME:-}" +[[ -n "$config_home" || -z "${HOME:-}" ]] || config_home="$HOME/.config" +flags_file="${config_home:+$config_home/slap-notes-flags.conf}" + +if [[ -n "$flags_file" && -f "$flags_file" && -r "$flags_file" ]]; then + while IFS= read -r line || [[ -n "$line" ]]; do + line="${line%%#*}" + [[ -n "${line//[[:space:]]/}" ]] || continue + read -r -a flags <<<"$line" + user_flags+=("${flags[@]}") + done <"$flags_file" +fi + +# Chromium's own Ozone detection falls back to XWayland often enough to matter, +# and the result is a blurry window on every scaled display. Ask for Wayland +# directly, unless the user has already picked a platform themselves. +platform_flags=() +if [[ -n "${WAYLAND_DISPLAY:-}" || "${XDG_SESSION_TYPE:-}" == wayland ]]; then + platform_flags=(--ozone-platform=wayland) + + for flag in "${user_flags[@]}" "$@"; do + case "$flag" in + --ozone-platform=* | --ozone-platform-hint=*) platform_flags=() ;; + esac + done +fi + +exec /opt/slap-notes/slap-notes "${platform_flags[@]}" "${user_flags[@]}" "$@" diff --git a/pkgbuilds/strata/.omarchy/package.json b/pkgbuilds/strata/.omarchy/package.json new file mode 100644 index 00000000..bbe9ae19 --- /dev/null +++ b/pkgbuilds/strata/.omarchy/package.json @@ -0,0 +1,5 @@ +{ + "source": "local", + "release_ring": "fast", + "min_release_age": "24h" +} diff --git a/pkgbuilds/strata/.omarchy/upstream.sh b/pkgbuilds/strata/.omarchy/upstream.sh new file mode 100755 index 00000000..6c638d9b --- /dev/null +++ b/pkgbuilds/strata/.omarchy/upstream.sh @@ -0,0 +1,106 @@ +#!/bin/bash +# Strata publishes annotated tags and GitHub source archives, but no checksum +# manifest. Download the small archive only when a newer stable release exists, +# then verify that its embedded commit matches the release tag before hashing it. +set -euo pipefail + +REPO='lgse/strata' +API_URL="https://api.github.com/repos/$REPO" + +current=$(awk -F= '/^pkgver=/ { print $2; exit }' PKGBUILD) +releases=$(curl -fsSL "$API_URL/releases?per_page=100") +now=$(date +%s) +min_age=${MIN_RELEASE_AGE_SECONDS:-0} + +candidates=0 +best_version='' +best_tag='' +best_published_at='' + +while IFS=$'\t' read -r tag published_at; do + if [[ ! $tag =~ ^v([0-9]+\.[0-9]+\.[0-9]+)$ ]]; then + printf '%s has an unusable stable release tag: %s\n' "$REPO" "${tag:-}" >&2 + exit 1 + fi + version=${BASH_REMATCH[1]} + + if [[ -z $published_at ]] || ! published_epoch=$(date --date="$published_at" +%s 2>/dev/null); then + printf '%s release %s has an invalid publication time\n' "$REPO" "$tag" >&2 + exit 1 + fi + candidates=$((candidates + 1)) + + if ((now - published_epoch < min_age)) && [[ ${BYPASS_MIN_RELEASE_AGE:-} != 1 ]]; then + continue + fi + + if [[ -z $best_version ]] || (( $(vercmp "$version" "$best_version") > 0 )); then + best_version=$version + best_tag=$tag + best_published_at=$published_at + fi +done < <(jq -r '.[] | select((.draft or .prerelease) | not) | [.tag_name // "", .published_at // ""] | @tsv' <<<"$releases") + +if ((candidates == 0)); then + printf 'No stable releases found for %s\n' "$REPO" >&2 + exit 1 +fi + +if [[ -z $best_version ]] || (( $(vercmp "$best_version" "$current") <= 0 )); then + echo '{}' + exit 0 +fi + +tag_ref=$(curl -fsSL "$API_URL/git/ref/tags/$best_tag") +tag_type=$(jq -r '.object.type // empty' <<<"$tag_ref") +tag_object=$(jq -r '.object.sha // empty' <<<"$tag_ref") +if [[ ! $tag_object =~ ^[0-9a-f]{40}$ ]]; then + printf 'Release %s has an invalid tag object\n' "$best_tag" >&2 + exit 1 +fi + +case "$tag_type" in + commit) + expected_commit=$tag_object + ;; + tag) + tag_data=$(curl -fsSL "$API_URL/git/tags/$tag_object") + if [[ $(jq -r '.object.type // empty' <<<"$tag_data") != commit ]]; then + printf 'Release %s does not resolve to a commit\n' "$best_tag" >&2 + exit 1 + fi + expected_commit=$(jq -r '.object.sha // empty' <<<"$tag_data") + ;; + *) + printf 'Release %s has unsupported tag object type %s\n' "$best_tag" "${tag_type:-}" >&2 + exit 1 + ;; +esac + +if [[ ! $expected_commit =~ ^[0-9a-f]{40}$ ]]; then + printf 'Release %s resolves to an invalid commit\n' "$best_tag" >&2 + exit 1 +fi + +tarball=$(mktemp) +trap 'rm -f "$tarball"' EXIT +curl -fsSL -o "$tarball" "https://github.com/$REPO/archive/refs/tags/$best_tag.tar.gz" + +expected_root="strata-$best_version" +served_roots=$(tar -tzf "$tarball" | cut -d/ -f1 | sort -u) +if [[ $served_roots != "$expected_root" ]]; then + printf 'Release %s contains root %s, expected %s\n' "$best_tag" "$served_roots" "$expected_root" >&2 + exit 1 +fi + +if ! archive_commit=$(git get-tar-commit-id < <(gzip -dc "$tarball")) || + [[ $archive_commit != "$expected_commit" ]]; then + printf 'Release %s archive commit does not match its tag\n' "$best_tag" >&2 + exit 1 +fi + +jq -n \ + --arg pkgver "$best_version" \ + --arg published_at "$best_published_at" \ + --arg source "$(sha256sum "$tarball" | cut -d' ' -f1)" \ + '{pkgver: $pkgver, published_at: $published_at, sha256sums: {any: [$source]}}' diff --git a/pkgbuilds/strata/PKGBUILD b/pkgbuilds/strata/PKGBUILD new file mode 100644 index 00000000..a9313a45 --- /dev/null +++ b/pkgbuilds/strata/PKGBUILD @@ -0,0 +1,95 @@ +pkgname=strata +pkgver=0.11.2 +pkgrel=1 +pkgdesc='Fast, keyboard-first file manager for modern Linux desktops' +arch=('x86_64' 'aarch64') +url='https://github.com/lgse/strata' +license=('GPL-3.0-or-later') + +depends=( + 'bash' + 'bubblewrap' + 'cairo' + 'ffmpeg' + 'ffmpegthumbnailer' + 'fontconfig' + 'gcc-libs' + 'gdk-pixbuf2' + 'glib2' + 'glibc' + 'graphene' + 'gst-libav' + 'gst-plugins-good' + 'gtk4' + 'gtksourceview5' + 'hicolor-icon-theme' + 'pango' + 'poppler-glib' + 'util-linux' + 'xdg-terminal-exec' +) +makedepends=('cargo' 'git' 'glib2-devel' 'pkgconf') +optdepends=( + 'gvfs-smb: browse SMB network shares' + 'imagemagick: additional camera RAW preview support' + 'libraw: additional camera RAW preview support' +) +conflicts=('strata-git') +options=('!debug' '!lto') + +source=("$pkgname-$pkgver.tar.gz::$url/archive/refs/tags/v$pkgver.tar.gz") +sha256sums=('cfc8b1c3a9a5fde518c5862b58af7722e942b5f394c60fd53822dd642c1f30a0') + +prepare() { + cd "$pkgname-$pkgver" + + local commit + if ! commit=$(git get-tar-commit-id < <(gzip -dc "$srcdir/$pkgname-$pkgver.tar.gz")) || + [[ ! $commit =~ ^[0-9a-f]{40}$ ]]; then + printf 'Could not recover the release commit from the source archive\n' >&2 + return 1 + fi + printf '%s\n' "$commit" >.build-commit + + cargo fetch --locked --target "$CARCH-unknown-linux-gnu" +} + +build() { + cd "$pkgname-$pkgver" + + export CARGO_TARGET_DIR=target + export STRATA_BUILD_COMMIT + STRATA_BUILD_COMMIT=$(<.build-commit) + export STRATA_RELEASE_TAG="v$pkgver" + export STRATA_BUILD_KIND=stable + cargo build --frozen --release +} + +check() { + cd "$pkgname-$pkgver" + + export CARGO_TARGET_DIR=target + export STRATA_BUILD_COMMIT + STRATA_BUILD_COMMIT=$(<.build-commit) + export STRATA_RELEASE_TAG="v$pkgver" + export STRATA_BUILD_KIND=stable + cargo test --frozen --release --all-targets --all-features +} + +package() { + cd "$pkgname-$pkgver" + + install -Dm755 target/release/strata "$pkgdir/usr/bin/strata" + install -Dm644 data/io.github.lgse.Strata.desktop \ + "$pkgdir/usr/share/applications/io.github.lgse.Strata.desktop" + install -Dm644 data/icons/scalable/apps/io.github.lgse.Strata.svg \ + "$pkgdir/usr/share/icons/hicolor/scalable/apps/io.github.lgse.Strata.svg" + # Payload for the app's per-user FileManager1 opt-in; upstream's own + # packaging stages it here, not in /usr/share/dbus-1. + install -Dm644 data/io.github.lgse.Strata.FileManager1.service \ + "$pkgdir/usr/share/strata/io.github.lgse.Strata.FileManager1.service" + install -Dm644 LICENSE "$pkgdir/usr/share/licenses/$pkgname/LICENSE" + install -Dm644 THIRD_PARTY_LICENSES.md \ + "$pkgdir/usr/share/licenses/$pkgname/THIRD_PARTY_LICENSES.md" + install -Dm644 README.md "$pkgdir/usr/share/doc/$pkgname/README.md" +} diff --git a/pkgbuilds/sunshine/.omarchy/patches/arm-dependencies.patch b/pkgbuilds/sunshine/.omarchy/patches/arm-dependencies.patch new file mode 100644 index 00000000..8345fc50 --- /dev/null +++ b/pkgbuilds/sunshine/.omarchy/patches/arm-dependencies.patch @@ -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 diff --git a/pkgbuilds/sunshine/PKGBUILD b/pkgbuilds/sunshine/PKGBUILD index e6100a62..a6b3d23d 100644 --- a/pkgbuilds/sunshine/PKGBUILD +++ b/pkgbuilds/sunshine/PKGBUILD @@ -10,7 +10,7 @@ pkgname='sunshine' pkgver=2026.516.143833 -pkgrel=4 +pkgrel=4.1 pkgdesc="Self-hosted game stream host for Moonlight" arch=('x86_64' 'aarch64') url=https://app.lizardbyte.dev/Sunshine @@ -36,7 +36,6 @@ depends=( 'libcap' 'libdrm' 'libevdev' - 'libmfx' 'libnotify' 'libpipewire' 'libpulse' @@ -55,6 +54,10 @@ depends=( 'which' ) +depends_x86_64=( + 'libmfx' +) + makedepends=( 'appstream' 'appstream-glib' @@ -69,6 +72,10 @@ makedepends=( 'shaderc' ) +makedepends_aarch64=( + 'vulkan-headers' +) + checkdepends=( 'gcovr' ) @@ -123,7 +130,12 @@ fi 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" @@ -159,6 +171,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 diff --git a/pkgbuilds/symfony-cli/.omarchy/package.json b/pkgbuilds/symfony-cli/.omarchy/package.json index 3dc28470..b9e29500 100644 --- a/pkgbuilds/symfony-cli/.omarchy/package.json +++ b/pkgbuilds/symfony-cli/.omarchy/package.json @@ -1,5 +1,11 @@ { - "source": "aur", + "source": "local", "release_ring": "fast", - "upstream_commit": "39667376addf3916b7bed3ecbe3ec6ed2af367e4" + "upstream": { + "github": "symfony-cli/symfony-cli", + "checksums": "checksums.txt", + "assets": { + "any": "symfony-cli-{pkgver}.tar.gz" + } + } } diff --git a/pkgbuilds/symfony-cli/PKGBUILD b/pkgbuilds/symfony-cli/PKGBUILD index 28cba8e9..5c09eb21 100644 --- a/pkgbuilds/symfony-cli/PKGBUILD +++ b/pkgbuilds/symfony-cli/PKGBUILD @@ -1,23 +1,27 @@ # Maintainer: Florian Moser pkgname=symfony-cli -pkgrel=8 +pkgrel=9 pkgver=5.20.0 pkgdesc="The Symfony client helps developers create and manage Symfony applications." url="https://symfony.com/" -arch=('x86_64') +arch=('x86_64' 'aarch64') license=('AGPL3') depends=('glibc') -makedepends=('git' 'go') +makedepends=('go') provides=('symfony-cli') conflicts=('symfony-cli') -source=( - "git+https://github.com/symfony-cli/symfony-cli#tag=v${pkgver}" -) -sha256sums=('SKIP') +source=("$pkgname-$pkgver.tar.gz::https://github.com/symfony-cli/symfony-cli/releases/download/v${pkgver}/symfony-cli-${pkgver}.tar.gz") +sha256sums=('11354f7206740dc8b31410bf7fbcb20debccb4a92935da84729fd7d754b2082f') +noextract=("$pkgname-$pkgver.tar.gz") + +prepare() { + mkdir "$pkgname-$pkgver" + bsdtar -xf "$pkgname-$pkgver.tar.gz" -C "$pkgname-$pkgver" +} build() { - cd "symfony-cli" + cd "$pkgname-$pkgver" DATE=$(date -u +"%Y-%m-%dT%H:%M:%SZ") PKGVER=${pkgver} @@ -26,6 +30,6 @@ build() { go build -trimpath -ldflags '-s -w -X main.channel=stable -X main.buildDate='"${DATE}"' -X main.version='"${PKGVER}"'' } -package() { - install -D -m 755 "${srcdir}/symfony-cli/symfony-cli" "${pkgdir}/usr/bin/symfony" +package() { + install -D -m 755 "${srcdir}/${pkgname}-${pkgver}/symfony-cli" "${pkgdir}/usr/bin/symfony" } diff --git a/pkgbuilds/t3code-bin/PKGBUILD b/pkgbuilds/t3code-bin/PKGBUILD index fbb9abe9..fdff11f7 100644 --- a/pkgbuilds/t3code-bin/PKGBUILD +++ b/pkgbuilds/t3code-bin/PKGBUILD @@ -7,7 +7,7 @@ # from the release feed the app updates itself from. pkgname=t3code-bin -pkgver=0.0.37 +pkgver=0.0.39 pkgrel=1 pkgdesc="Open-source control plane for coding agents" arch=('x86_64') @@ -61,7 +61,7 @@ noextract=("${_appimage}") sha256sums=('cb905ff341372ef2ef6e402cf485959f8bd1df8f0efebee4cda1afdd5e6abc0a' 'c5b3f2a9f0b14b12cfd973b79319f0f018b7ae49a1d43d8ca346100c3f7de28f' '935d8f2af0c703f9c39517ee57cc4930b19d02d533be930b63f0e82f93614b43') -sha256sums_x86_64=('ea88807ccc4b18b2c3f85726ef1235e695b656ab37c8e29c9289f7a28de336c2') +sha256sums_x86_64=('4f78c9939c0386f9be5a2d8e9baf9644af497344015817a36fea8be84981bda8') prepare() { chmod +x "${srcdir}/${_appimage}" diff --git a/pkgbuilds/t3code-patched-bin/LICENSE b/pkgbuilds/t3code-patched-bin/LICENSE deleted file mode 100644 index 55ee675b..00000000 --- a/pkgbuilds/t3code-patched-bin/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2026 T3 Tools Inc. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/pkgbuilds/t3code-patched-bin/PKGBUILD b/pkgbuilds/t3code-patched-bin/PKGBUILD deleted file mode 100644 index 1facc566..00000000 --- a/pkgbuilds/t3code-patched-bin/PKGBUILD +++ /dev/null @@ -1,134 +0,0 @@ -# Maintainer: Ryan Hughes - -# T3 Code built from the environment-theme branch of ryanrhughes/t3code, until -# upstream ships it: the desktop follows the machine's published themes -# (Omarchy retints it live) and `t3 theme set` works. Packaging matches -# t3code-bin exactly, so this installs in its place and the AppImage is -# unpacked the same way. -# -# Shipping a new artifact, every time: -# 1. The t3code worktree needs a repo-root .env carrying the public T3 -# Connect config (relay URL, Clerk publishable key, CLI OAuth client id) -# or Connect ships broken -- the 0.0.35-2 mistake. The values are public -# and recoverable from the published npm `t3` package if lost. -# 2. Build: `pnpm dist:desktop:linux` with --build-version . -# 3. Tag the built commit v-omarchy., create a GitHub release on -# the fork with the AppImage as its asset. -# 4. Update _patchtag and sha256sums_x86_64 here, and bump pkgrel (or, on a -# rebase to a new upstream release, bump pkgver and reset pkgrel) -- the -# version change is what makes the build server rebuild at all; a new -# release asset with an untouched PKGBUILD is invisible to it. - -pkgname=t3code-patched-bin -pkgver=0.0.35 -pkgrel=5 -pkgdesc="Open-source control plane for coding agents, with Omarchy environment-theme patches" -arch=('x86_64') -url="https://t3.codes" -license=('MIT') - -depends=( - 'alsa-lib' - 'at-spi2-core' - 'cairo' - 'dbus' - 'expat' - 'gcc-libs' - 'glib2' - 'glibc' - 'gtk3' - 'hicolor-icon-theme' - 'libcups' - 'libnotify' - 'libx11' - 'libxcb' - 'libxcomposite' - 'libxdamage' - 'libxext' - 'libxfixes' - 'libxkbcommon' - 'libxrandr' - 'mesa' - 'nspr' - 'nss' - 'pango' - 'systemd-libs' - 'xdg-utils' -) - -optdepends=( - 'claude-code: drive Claude Code from the app' - 'cursor-cli: drive Cursor from the app' - 'github-copilot-cli: drive Copilot from the app' - 'openai-codex: drive Codex from the app' -) - -provides=("t3code=${pkgver}") -conflicts=('t3code' 't3code-bin') -options=('!debug' '!strip') - -_patchtag="v${pkgver}-omarchy.5" -_appimage="T3-Code-${pkgver}-x86_64.AppImage" -source=('t3code-launcher.sh' 't3-launcher.sh' 'LICENSE') -source_x86_64=("${_appimage}::https://github.com/ryanrhughes/t3code/releases/download/${_patchtag}/${_appimage}") -noextract=("${_appimage}") -sha256sums=('cb905ff341372ef2ef6e402cf485959f8bd1df8f0efebee4cda1afdd5e6abc0a' - 'c5b3f2a9f0b14b12cfd973b79319f0f018b7ae49a1d43d8ca346100c3f7de28f' - '935d8f2af0c703f9c39517ee57cc4930b19d02d533be930b63f0e82f93614b43') -sha256sums_x86_64=('6f58306d54525ca695c7b902aac610d111f85a1f3ba8a9bf04e6e1950337b1f6') - -prepare() { - chmod +x "${srcdir}/${_appimage}" - rm -rf "${srcdir}/squashfs-root" - "${srcdir}/${_appimage}" --appimage-extract >/dev/null -} - -package() { - cd "${srcdir}/squashfs-root" - - # The AppImage's usr/ tree is compatibility libraries for distributions that - # lack them, and Arch is not one; its icons are the only part worth keeping. - # A release that starts shipping something else there stops the build rather - # than having it deleted quietly on the way past. - local unexpected - unexpected=$(find usr \( -type f -o -type l \) | grep -vE \ - '^usr/share/icons/hicolor/[0-9]+x[0-9]+/apps/t3code\.png$|^usr/lib/lib(Xss\.so\.1|Xtst\.so\.6|appindicator\.so\.1|gconf-2\.so\.4|indicator\.so\.7|notify\.so\.4)$' || true) - if [[ -n "${unexpected}" ]]; then - echo "Unexpected files in the AppImage's usr/ tree:" >&2 - echo "${unexpected}" >&2 - return 1 - fi - - local icon size - for icon in usr/share/icons/hicolor/*/apps/t3code.png; do - size="${icon#usr/share/icons/hicolor/}" - install -Dm644 "${icon}" "${pkgdir}/usr/share/icons/hicolor/${size%%/*}/apps/t3code.png" - done - - # Upstream's own entry rather than a hand-written one: it carries the - # t3code:// scheme handlers that make the app's deep links resolve. - # The name drops the "(Alpha)" upstream's electron-builder still appends; the - # app itself now calls that string legacy and keeps its data under t3code. - sed -e 's|^Exec=.*|Exec=t3code %U|' -e 's|^Name=.*|Name=T3 Code|' \ - -e '/^X-AppImage-Version=/d' t3code.desktop \ - > "${srcdir}/t3code.desktop.arch" - install -Dm644 "${srcdir}/t3code.desktop.arch" \ - "${pkgdir}/usr/share/applications/t3code.desktop" - - rm -rf AppRun .DirIcon usr t3code.desktop t3code.png - install -d "${pkgdir}/usr/lib/t3code" - cp -a . "${pkgdir}/usr/lib/t3code/" - chmod -R a+rX "${pkgdir}/usr/lib/t3code" - - # Arch ships Chromium's and Electron's own sandbox helper setuid, which is - # what keeps the sandbox up on a kernel that denies unprivileged user - # namespaces -- linux-hardened, mainly -- instead of aborting the app. - chmod 4755 "${pkgdir}/usr/lib/t3code/chrome-sandbox" - - install -Dm755 "${srcdir}/t3code-launcher.sh" "${pkgdir}/usr/bin/t3code" - # The server CLI, run through the bundled Electron as Node. Same entrypoint - # npm's `t3` package runs, so `t3 serve`, `t3 pair`, and the rest work - # without a separate install. - install -Dm755 "${srcdir}/t3-launcher.sh" "${pkgdir}/usr/bin/t3" - install -Dm644 "${srcdir}/LICENSE" "${pkgdir}/usr/share/licenses/${pkgname}/LICENSE" -} diff --git a/pkgbuilds/t3code-patched-bin/t3-launcher.sh b/pkgbuilds/t3code-patched-bin/t3-launcher.sh deleted file mode 100755 index af1988f7..00000000 --- a/pkgbuilds/t3code-patched-bin/t3-launcher.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/bash -# The T3 Code server CLI (`t3`) ships inside the app bundle; upstream only -# distributes it separately through npm. The bundled Electron doubles as the -# Node runtime for it, so the desktop package can put the CLI on PATH without -# shipping a second runtime. Electron's fs layer reads app.asar transparently -# in this mode. -set -euo pipefail - -export ELECTRON_RUN_AS_NODE=1 -exec /usr/lib/t3code/t3code /usr/lib/t3code/resources/app.asar/apps/server/dist/bin.mjs "$@" diff --git a/pkgbuilds/tensaku/.omarchy/package.json b/pkgbuilds/tensaku/.omarchy/package.json index 8995d643..eb35b0ba 100644 --- a/pkgbuilds/tensaku/.omarchy/package.json +++ b/pkgbuilds/tensaku/.omarchy/package.json @@ -1,4 +1,10 @@ { - "source": "aur", - "upstream_commit": "c6fe8f2c6dc43a5c2f0c95df5656037b351dacb7" + "source": "local", + "upstream": { + "git_tags": "https://github.com/jondkinney/tensaku.git", + "tag_pattern": "v{pkgver}", + "sources": { + "any": ["https://github.com/jondkinney/tensaku/archive/refs/tags/{tag}.tar.gz"] + } + } } diff --git a/pkgbuilds/tensaku/PKGBUILD b/pkgbuilds/tensaku/PKGBUILD index 1f8bd234..0e6353be 100644 --- a/pkgbuilds/tensaku/PKGBUILD +++ b/pkgbuilds/tensaku/PKGBUILD @@ -6,10 +6,10 @@ # pushes to the AUR. Edit depends / package() etc. here — never in the # AUR repo directly. pkgname=tensaku -pkgver=0.28.0 +pkgver=0.29.0 pkgrel=1 pkgdesc='Modern screenshot annotation tool for Wayland' -arch=('x86_64') +arch=('x86_64' 'aarch64') url='https://github.com/jondkinney/tensaku' license=('MPL-2.0') depends=('gtk4' 'gtk4-layer-shell' 'libadwaita' 'libepoxy' 'fontconfig') @@ -20,7 +20,7 @@ makedepends=('rust') optdepends=('hyprland: window snapping and pin placement' 'sway: window snapping and pin placement') source=("$pkgname-$pkgver.tar.gz::$url/archive/refs/tags/v$pkgver.tar.gz") -sha256sums=('08f61dce1a4b629a1229c0b2cead642e165319e4b61a6f605fc3568d9f6183ee') +sha256sums=('31595a8b4600107a63c1e5889f50f40e1ef832e51cfc038c1a7750b987fd34d0') prepare() { cd "$pkgname-$pkgver" diff --git a/pkgbuilds/ttf-jetbrains-mono-nerd-basic/.omarchy/package.json b/pkgbuilds/ttf-jetbrains-mono-nerd-basic/.omarchy/package.json index 2a9719d3..eeacccb2 100644 --- a/pkgbuilds/ttf-jetbrains-mono-nerd-basic/.omarchy/package.json +++ b/pkgbuilds/ttf-jetbrains-mono-nerd-basic/.omarchy/package.json @@ -1,3 +1,11 @@ { - "source": "local" + "source": "local", + "min_release_age": "24h", + "upstream": { + "github": "ryanoasis/nerd-fonts", + "checksums": "SHA-256.txt", + "assets": { + "any": "JetBrainsMono.zip" + } + } } diff --git a/pkgbuilds/ttf-jetbrains-mono-nerd-basic/PKGBUILD b/pkgbuilds/ttf-jetbrains-mono-nerd-basic/PKGBUILD index c74acb32..1accc2ed 100644 --- a/pkgbuilds/ttf-jetbrains-mono-nerd-basic/PKGBUILD +++ b/pkgbuilds/ttf-jetbrains-mono-nerd-basic/PKGBUILD @@ -1,7 +1,7 @@ # Maintainer: Omarchy pkgname=ttf-jetbrains-mono-nerd-basic -pkgver=3.5.0 +pkgver=3.5.1 pkgrel=1 pkgdesc="Basic JetBrains Mono Nerd Font family" arch=("any") @@ -11,7 +11,7 @@ groups=("nerd-fonts") provides=("ttf-font-nerd" "ttf-jetbrains-mono-nerd=$pkgver") conflicts=("ttf-jetbrains-mono-nerd" "nerd-fonts-jetbrains-mono") source=("JetBrainsMono-${pkgver}.zip::https://github.com/ryanoasis/nerd-fonts/releases/download/v${pkgver}/JetBrainsMono.zip") -sha256sums=("9577de1ae84ec523df16fc69bac5338b89497a5b4fb91489e2dcb79dc06ac2b5") +sha256sums=('fab782a66f7d3019da64f6572db9fc5d3a4bcb19f9fa13e2d8a62e3693d6396e') package() { local font diff --git a/pkgbuilds/tzupdate/.omarchy/package.json b/pkgbuilds/tzupdate/.omarchy/package.json index cf4a8c65..31bfd105 100644 --- a/pkgbuilds/tzupdate/.omarchy/package.json +++ b/pkgbuilds/tzupdate/.omarchy/package.json @@ -1,4 +1,10 @@ { - "source": "aur", - "upstream_commit": "1b3c3bdf6289a348da09eed57d653d3f6dce1956" + "source": "local", + "upstream": { + "git_tags": "https://github.com/cdown/tzupdate.git", + "tag_pattern": "{pkgver}", + "sources": { + "any": ["https://github.com/cdown/tzupdate/archive/refs/tags/{tag}.tar.gz"] + } + } } diff --git a/pkgbuilds/tzupdate/PKGBUILD b/pkgbuilds/tzupdate/PKGBUILD index a8d05c73..c9aeb3a2 100644 --- a/pkgbuilds/tzupdate/PKGBUILD +++ b/pkgbuilds/tzupdate/PKGBUILD @@ -5,7 +5,7 @@ pkgver=3.1.0 pkgrel=1 pkgdesc='Set the system timezone based on IP geolocation' url=https://github.com/cdown/tzupdate -arch=('x86_64') +arch=('x86_64' 'aarch64') options=(!lto) license=('MIT') makedepends=('cargo') diff --git a/pkgbuilds/v4l2-relayd/.omarchy/package.json b/pkgbuilds/v4l2-relayd/.omarchy/package.json index 4c423964..daea8850 100644 --- a/pkgbuilds/v4l2-relayd/.omarchy/package.json +++ b/pkgbuilds/v4l2-relayd/.omarchy/package.json @@ -1,9 +1,14 @@ { - "source": "aur", - "sync": false, - "pkgrel": { - "suffix": 2, - "offset": 1 - }, - "upstream_commit": "086fa284d7f5ad562ea6ba79b3cb4fd798e759d3" + "source": "local", + "upstream": { + "git_tags": "https://gitlab.com/vicamo/v4l2-relayd.git", + "tag_pattern": "upstream/{pkgver}", + "sources": { + "any": [ + "https://gitlab.com/vicamo/v4l2-relayd/-/archive/upstream/{pkgver}/v4l2-relayd-upstream-{pkgver}.tar.gz", + "file:0001-reset-output-on-idle.patch", + "file:0002-escape-optional-splashsrc-expansion.patch" + ] + } + } } diff --git a/pkgbuilds/v4l2-relayd/PKGBUILD b/pkgbuilds/v4l2-relayd/PKGBUILD index b276a5f8..fbe9bbb9 100644 --- a/pkgbuilds/v4l2-relayd/PKGBUILD +++ b/pkgbuilds/v4l2-relayd/PKGBUILD @@ -4,7 +4,7 @@ pkgname=v4l2-relayd pkgver=0.2.0 pkgrel=2.2 pkgdesc="Plays gstreamer sources into v4l2loopback devices." -arch=('x86_64') +arch=('x86_64' 'aarch64') url="https://gitlab.com/vicamo/v4l2-relayd" license=('GPL') depends=( @@ -19,8 +19,8 @@ source=("https://gitlab.com/vicamo/v4l2-relayd/-/archive/upstream/${pkgver}/v4l2 "0001-reset-output-on-idle.patch" "0002-escape-optional-splashsrc-expansion.patch") sha256sums=('0c063edf18dcc6edcdef46e695128cfc2b2d60964ea8538c7e79a2454310c53d' - 'SKIP' - 'SKIP') + '07722a8708ced48d2db9575f3eae5b1266868d259d260635e06e9a10baddec78' + '3cb89056af276eed7a45dfc96435e8e48558e4e11c96560360d46d631b1c0b6c') prepare() { cd "$srcdir/${pkgname}-upstream-${pkgver}" diff --git a/pkgbuilds/vi/.omarchy/package.json b/pkgbuilds/vi/.omarchy/package.json new file mode 100644 index 00000000..a07823fd --- /dev/null +++ b/pkgbuilds/vi/.omarchy/package.json @@ -0,0 +1,4 @@ +{ + "source": "aur", + "upstream_commit": "9aa84b0998874261e94bbbe25e0dcf3de7286b94" +} diff --git a/pkgbuilds/vi/LICENSE b/pkgbuilds/vi/LICENSE new file mode 100644 index 00000000..0e259d42 --- /dev/null +++ b/pkgbuilds/vi/LICENSE @@ -0,0 +1,121 @@ +Creative Commons Legal Code + +CC0 1.0 Universal + + CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE + LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN + ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS + INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES + REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS + PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM + THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED + HEREUNDER. + +Statement of Purpose + +The laws of most jurisdictions throughout the world automatically confer +exclusive Copyright and Related Rights (defined below) upon the creator +and subsequent owner(s) (each and all, an "owner") of an original work of +authorship and/or a database (each, a "Work"). + +Certain owners wish to permanently relinquish those rights to a Work for +the purpose of contributing to a commons of creative, cultural and +scientific works ("Commons") that the public can reliably and without fear +of later claims of infringement build upon, modify, incorporate in other +works, reuse and redistribute as freely as possible in any form whatsoever +and for any purposes, including without limitation commercial purposes. +These owners may contribute to the Commons to promote the ideal of a free +culture and the further production of creative, cultural and scientific +works, or to gain reputation or greater distribution for their Work in +part through the use and efforts of others. + +For these and/or other purposes and motivations, and without any +expectation of additional consideration or compensation, the person +associating CC0 with a Work (the "Affirmer"), to the extent that he or she +is an owner of Copyright and Related Rights in the Work, voluntarily +elects to apply CC0 to the Work and publicly distribute the Work under its +terms, with knowledge of his or her Copyright and Related Rights in the +Work and the meaning and intended legal effect of CC0 on those rights. + +1. Copyright and Related Rights. A Work made available under CC0 may be +protected by copyright and related or neighboring rights ("Copyright and +Related Rights"). Copyright and Related Rights include, but are not +limited to, the following: + + i. the right to reproduce, adapt, distribute, perform, display, + communicate, and translate a Work; + ii. moral rights retained by the original author(s) and/or performer(s); +iii. publicity and privacy rights pertaining to a person's image or + likeness depicted in a Work; + iv. rights protecting against unfair competition in regards to a Work, + subject to the limitations in paragraph 4(a), below; + v. rights protecting the extraction, dissemination, use and reuse of data + in a Work; + vi. database rights (such as those arising under Directive 96/9/EC of the + European Parliament and of the Council of 11 March 1996 on the legal + protection of databases, and under any national implementation + thereof, including any amended or successor version of such + directive); and +vii. other similar, equivalent or corresponding rights throughout the + world based on applicable law or treaty, and any national + implementations thereof. + +2. Waiver. To the greatest extent permitted by, but not in contravention +of, applicable law, Affirmer hereby overtly, fully, permanently, +irrevocably and unconditionally waives, abandons, and surrenders all of +Affirmer's Copyright and Related Rights and associated claims and causes +of action, whether now known or unknown (including existing as well as +future claims and causes of action), in the Work (i) in all territories +worldwide, (ii) for the maximum duration provided by applicable law or +treaty (including future time extensions), (iii) in any current or future +medium and for any number of copies, and (iv) for any purpose whatsoever, +including without limitation commercial, advertising or promotional +purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each +member of the public at large and to the detriment of Affirmer's heirs and +successors, fully intending that such Waiver shall not be subject to +revocation, rescission, cancellation, termination, or any other legal or +equitable action to disrupt the quiet enjoyment of the Work by the public +as contemplated by Affirmer's express Statement of Purpose. + +3. Public License Fallback. Should any part of the Waiver for any reason +be judged legally invalid or ineffective under applicable law, then the +Waiver shall be preserved to the maximum extent permitted taking into +account Affirmer's express Statement of Purpose. In addition, to the +extent the Waiver is so judged Affirmer hereby grants to each affected +person a royalty-free, non transferable, non sublicensable, non exclusive, +irrevocable and unconditional license to exercise Affirmer's Copyright and +Related Rights in the Work (i) in all territories worldwide, (ii) for the +maximum duration provided by applicable law or treaty (including future +time extensions), (iii) in any current or future medium and for any number +of copies, and (iv) for any purpose whatsoever, including without +limitation commercial, advertising or promotional purposes (the +"License"). The License shall be deemed effective as of the date CC0 was +applied by Affirmer to the Work. Should any part of the License for any +reason be judged legally invalid or ineffective under applicable law, such +partial invalidity or ineffectiveness shall not invalidate the remainder +of the License, and in such case Affirmer hereby affirms that he or she +will not (i) exercise any of his or her remaining Copyright and Related +Rights in the Work or (ii) assert any associated claims and causes of +action with respect to the Work, in either case contrary to Affirmer's +express Statement of Purpose. + +4. Limitations and Disclaimers. + + a. No trademark or patent rights held by Affirmer are waived, abandoned, + surrendered, licensed or otherwise affected by this document. + b. Affirmer offers the Work as-is and makes no representations or + warranties of any kind concerning the Work, express, implied, + statutory or otherwise, including without limitation warranties of + title, merchantability, fitness for a particular purpose, non + infringement, or the absence of latent or other defects, accuracy, or + the present or absence of errors, whether or not discoverable, all to + the greatest extent permissible under applicable law. + c. Affirmer disclaims responsibility for clearing rights of other persons + that may apply to the Work or any use thereof, including without + limitation any person's Copyright and Related Rights in the Work. + Further, Affirmer disclaims responsibility for obtaining any necessary + consents, permissions or other rights required for any use of the + Work. + d. Affirmer understands and acknowledges that Creative Commons is not a + party to this document and has no duty or obligation with respect to + this CC0 or use of the Work. diff --git a/pkgbuilds/vi/PKGBUILD b/pkgbuilds/vi/PKGBUILD new file mode 100644 index 00000000..4483e826 --- /dev/null +++ b/pkgbuilds/vi/PKGBUILD @@ -0,0 +1,66 @@ +# Maintainer: libele +# Contributor: Evangelos Foutras +# Contributor: Eric Bélanger + +pkgname=vi +pkgver=070224 +pkgrel=9 +epoch=1 +pkgdesc="The original ex/vi text editor" +arch=('aarch64' 'arm' 'armv6h' 'armv7h' 'i486' 'i686' 'pentium4' 'x86_64') +url="https://ex-vi.sourceforge.net/" +license=('BSD-4-Clause-UC AND Caldera-no-preamble') +depends=('ncurses') +optdepends=('s-nail: used by the preserve command for notification') +provides=('vi') +conflicts=('ex-vi-compat' 'heirloom-ex-vi') +source=(https://sources.archlinux.org/other/$pkgname/ex-$pkgver.tar.xz{,.sig} + fix-tubesize-short-overflow.patch + navkeys.patch + format-security.patch + linenum.patch + preserve-dir.patch) +sha256sums=('c3e52dd44edd1f6bf0b52207f717c56149dc50aac0d131fff3851d589727f52f' + 'SKIP' + '42167fabebe30a13d594346b1d254db82090ba41742a9f35b8895d37092053f0' + '0e9e2d381f1d8cb86daae68462b3849825b003c08007725c0db9939d3d5bf58d' + 'ec50373ab48a9b327c5d02b10406bc3d04310d692de6c17098a59b1afeb08e03' + '4539f46cd839475c168a9c463f041354463e90e810b973a10c499c2727b008e7' + 'f0ef9ed2ccea98bf47b08dd3faa2abb911b4e6c0579f5294e9d0e8742282ff65') +validpgpkeys=('86CFFCA918CF3AF47147588051E8B148A9999C34') # Evangelos Foutras + +prepare() { + # Extract specific license files. + sed -n '1,36p' ex-$pkgver/LICENSE > BSD-4-Clause-UC.txt + sed -n '39,69p' ex-$pkgver/LICENSE > Caldera-no-preamble.txt + + cd ex-$pkgver + + patch -Np1 -i ../fix-tubesize-short-overflow.patch + patch -Np1 -i ../navkeys.patch + + # https://bugs.archlinux.org/task/76121 + patch -Np1 -i ../format-security.patch + + # https://bugs.archlinux.org/task/70770 + patch -Np1 -i ../linenum.patch + + # https://bugs.archlinux.org/task/20653 + patch -Np1 -i ../preserve-dir.patch +} + +build() { + cd ex-$pkgver + + export CFLAGS=-std=gnu90 + make PREFIX=/usr LIBEXECDIR=/usr/lib/ex PRESERVEDIR=/var/lib/ex \ + TERMLIB=ncurses FEATURES="-DCHDIR -DFASTTAG -DUCVISUAL -DMB -DBIT8" +} + +package() { + cd ex-$pkgver + + make PREFIX=/usr LIBEXECDIR=/usr/lib/ex PRESERVEDIR=/var/lib/ex \ + INSTALL=/usr/bin/install DESTDIR="$pkgdir" install + install -vDm 644 ../*.txt -t "$pkgdir/usr/share/licenses/$pkgname/" +} diff --git a/pkgbuilds/vi/fix-tubesize-short-overflow.patch b/pkgbuilds/vi/fix-tubesize-short-overflow.patch new file mode 100644 index 00000000..46cc2af2 --- /dev/null +++ b/pkgbuilds/vi/fix-tubesize-short-overflow.patch @@ -0,0 +1,12 @@ +diff -upr ex-070224.orig/ex_vis.h ex-070224/ex_vis.h +--- ex-070224.orig/ex_vis.h 2005-08-06 14:41:15.000000000 +0300 ++++ ex-070224/ex_vis.h 2015-05-02 20:41:13.938665281 +0300 +@@ -104,7 +104,7 @@ var enum { + */ + var short TUBELINES; + var short TUBECOLS; +-var short TUBESIZE; ++var int TUBESIZE; + + /* + * The screen in visual and crtopen is of varying size; the basic diff --git a/pkgbuilds/vi/format-security.patch b/pkgbuilds/vi/format-security.patch new file mode 100644 index 00000000..3676bae1 --- /dev/null +++ b/pkgbuilds/vi/format-security.patch @@ -0,0 +1,26 @@ +diff --git a/ex_unix.c b/ex_unix.c +index 4ad17f9..80815d1 100644 +--- a/ex_unix.c ++++ b/ex_unix.c +@@ -179,7 +179,7 @@ uexp: + if (warn && hush == 0 && chng && xchng != chng && value(WARN) && dol > zero) { + xchng = chng; + vnfl(); +- printf(mesg(catgets(catd, 1, 197, ++ printf("%s", mesg(catgets(catd, 1, 197, + "[No write]|[No write since last change]"))); + noonl(); + flush(); +diff --git a/ex_vops2.c b/ex_vops2.c +index 48050ff..e59727a 100644 +--- a/ex_vops2.c ++++ b/ex_vops2.c +@@ -211,7 +211,7 @@ showmode(int mode) + if (value(TERSE)) + putchar(str[0]); + else +- printf(&str[1]); ++ printf("%s", &str[1]); + vgoto(sdl, sdc); + cursor = ocurs; + splitw = 0; diff --git a/pkgbuilds/vi/keys/pgp/86CFFCA918CF3AF47147588051E8B148A9999C34.asc b/pkgbuilds/vi/keys/pgp/86CFFCA918CF3AF47147588051E8B148A9999C34.asc new file mode 100644 index 00000000..22af2c00 --- /dev/null +++ b/pkgbuilds/vi/keys/pgp/86CFFCA918CF3AF47147588051E8B148A9999C34.asc @@ -0,0 +1,52 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- + +mQENBEzdbBcBCAC8QJ+5x2TjAlM8lL+6SKlj2BV3SS5YcLN3zL27ax8Li98VTn3G +lacdavKCQKUNbeHxSNKILuzMnx74kL5LOl9pSaDB6XgLMGu4RGhFFzKv3wO9cuBN +sxfHQFdXdJ4hY/Kiji0zH6XPyl8BGiUxXqjth+hoo0siJOiME2t/SL0U9KRnJyPt +fOngeUlKIcnBr+/0wboTgQpO3fa502CNV98mRVCY6/3gJY2kJP8G5rNWa6wywRtr +N94SiD0NL7R1ozTqIy4/K/7dB7nJOnQQxxmElal7bFr7k69L0cuc3XKkyhTrAnzM +bMp2vcXbgpcSoi/zLNgqDo0wq1CbmZxfgmbNABEBAAG0J0V2YW5nZWxvcyBGb3V0 +cmFzIDxmb3V0cmVsaXNAZ21haWwuY29tPokBkgQTAQgAhgWCYslmrAQLCQgHCRBR +6LFIqZmcNEcUAAAAAAAeACBzYWx0QG5vdGF0aW9ucy5zZXF1b2lhLXBncC5vcmdG +FjAlcBwlLXsuKdWKZbbHWcAUXvnvjaY6ZeOCCVu9hwMVCAoEFgIDAQIXgAIbAwIe +ARYhBIbP/KkYzzr0cUdYgFHosUipmZw0AACV5wf9EmIB+G4SXx2OLgEtL3ohZfhk +/wKwh6AaOT94SWhAllGtLNtCCSUgaHgz05oVdLkGrYBBcgkvcr6K7TH2T7v7tuop +VeX+qNI+EKYkL+fPdpgb6wAqLeLiULZa0XM1kxfp1xFNFPIfXcjuhbRYbkzrj/MH +0P1XZYoBDO3yo+cWJt7z3fFw06tA9KZxn/iOKGblVPMZS2UGlA8UCIj01NRZWeNY +TD9JtcEPiRHt6Xnodfpvm5Tdp86Uo6BXeIjmpZXYPfR7NXcmwLFQMjOqb7roqd8+ +66pzMDJ/onYO+DZZs3EEcIDL0LykoK64cts6vwPj8FzdM4Z2nOn/wT7QRdq6lrQr +RXZhbmdlbG9zIEZvdXRyYXMgPGV2YW5nZWxvc0Bmb3V0cmVsaXMuY29tPokBlQQT +AQgAiQWCYslmqwQLCQgHCRBR6LFIqZmcNEcUAAAAAAAeACBzYWx0QG5vdGF0aW9u +cy5zZXF1b2lhLXBncC5vcmfCF18eejk+B+7x5ZO1TGdelYn1jDioOzy+sqxgQCyR +WAMVCAoEFgIDAQIXgAIZAQIbAwIeARYhBIbP/KkYzzr0cUdYgFHosUipmZw0AABl +rQf+LMOENiR6NLyG5b7ut+d3ATilp7ECuwLE/9vQDaDQa5nkvQixsjomHklpdA5Y +UPer6J6WKf88iAU21H7qW9gqw4K5OjVXHZYI+RLaS+1qEz7Y5FFN/Axdu2GuvZvp +9B0frJ7zPVtM7+TbIkXqjYMiFbw24e5s3+y1CO3yEKc4dbHKQb493Axx/xCMtGzi +9rrebFuIQVJw+IaguGAk8tV/Jf/jsA7GjjrHhWHCcnY8HqCnPtWgUqxW15Yql5VS +pGcOAQMx/xeMHWqzSpE0NXTvBzuA81l4ovngBfy3Vjia7z/n/XxxLV3ehO8d6gir +yuEEdkkBPG48ysS9NxqxpWjCnLQrRXZhbmdlbG9zIEZvdXRyYXMgPGZvdXRyZWxp +c0BhcmNobGludXgub3JnPokBkgQTAQgAhgWCYslmrAQLCQgHCRBR6LFIqZmcNEcU +AAAAAAAeACBzYWx0QG5vdGF0aW9ucy5zZXF1b2lhLXBncC5vcmd4ABJkF/uaJLUS +de0Gt8Tr94eHgbzWjcuTeOp+7VARHQMVCAoEFgIDAQIXgAIbAwIeARYhBIbP/KkY +zzr0cUdYgFHosUipmZw0AABvDwf+NxwZ805EPlYZ53+rvvTLE1SqT8b7EaqVflff +HRoJoT+2XajrpPeT2cN0ePjoRtZgYEhj3qPCIz3R4uaGu7uWkX4NmqfJGDsmOAdN +XSibWKcz0cnbqwUmFsS6QmfcsAZxCD+LYt6C7KYhzGT0xvhP2OVHVOFDIj/I+iwH +MK41swTiQ101OtKPuqW1uLXzYDfbdTOTQ6RrOgtWlLtF94mF7sGNPnyKpTksgdw0 +4vnxbvQWSPuAsGwXN8hQk1Zy6I35Z+/qeXqh6AfPasUb0i/8PO5gZkcqW/pccOpy +7kFIKkjvAIKMl/ILR7Epd0LJlJHLctB+g06eGW3jYoH2E+c1x7kBDQRM3WwXAQgA +niGcmhZ5gty8/k0Nh5ov7MAnwcUtPQ7rXDb7iRsNyHkbx/o5FBDlU/zFdqE/drqs +oRYxI/4VqnDXBJPr9Pk7d2f4iUdO/pSa0XzY+q8vOtWDky67K5Bl2YkdX9F7audf +uggM75IJo2y1EF2F4D0pcWiDjT9kkjNpVBBCuKqcM4yWaf4pmYHihgC4tvBhJx8f +6YrnL8N/wCPpvqyvYJaL0kg+hcnVgYyOnbFcrlkeuGMDrF6aoKaN6Sl54dtnxkep +4/dCyW5A2w/V6ubaK5XRsN1yeY1bLvz4iRAy0CbtAvZuUsb+DEVDQ40aPvTw52Um +6hfCN8bBOTQs8t4sGM12DwARAQABiQF+BBgBCAByBYJiyWasCRBR6LFIqZmcNEcU +AAAAAAAeACBzYWx0QG5vdGF0aW9ucy5zZXF1b2lhLXBncC5vcmc3jDQWZ2z5IqgI +XRUrrTn72ljmV28aDWVAt3ozjarlLAIbDBYhBIbP/KkYzzr0cUdYgFHosUipmZw0 +AACo2AgAktLNEDb6geHvgsWWw0vklq4UxTIedKP7iQfg20v/fHrtY22V2SKE67Sb +bl8xezEr0NfHF/M8r2zUpHdI+r7HE2QrgE2NEBeWGoY54vyaN5wYxTJxJmUsOrQj +ujJqwoBcj2DIiTprNROzdjWuPY7JR/Vw80TrfAwFwcikAb6QJvc3i6oqant06Um6 +ycmH1zSFX1SMte/gXd0RJ8gyQmbgpo12HDof92/13ZRrdUG8Rk33fHAK2hN9pr+l +f29yxIMXFh5z9mwEkLCd7bz85k+VNOC/pcURJhXjH3n+ssK0pEHZ684OctKn6At8 +CxByRzRqssZbq46oABvbsGeZq1Qw7A== +=UALx +-----END PGP PUBLIC KEY BLOCK----- diff --git a/pkgbuilds/vi/linenum.patch b/pkgbuilds/vi/linenum.patch new file mode 100644 index 00000000..6821b3b3 --- /dev/null +++ b/pkgbuilds/vi/linenum.patch @@ -0,0 +1,61 @@ +From 891c4022e24063608b554b6b3e4b5e2533e5443b Mon Sep 17 00:00:00 2001 +From: n-t-roff +Date: Fri, 22 Apr 2016 19:08:05 +0200 +Subject: [PATCH] set nu bug fixed + +--- + ex_put.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/ex_put.c b/ex_put.c +index fb7446e..0760747 100644 +--- a/ex_put.c ++++ b/ex_put.c +@@ -298,7 +298,7 @@ normline(int unused, int max) + + (void)unused; + if (max > 0) +- vcntcol = 0; ++ vcntcol = value(NUMBER) << 3; + if (shudclob) + slobber(linebuf[0]); + /* pdp-11 doprnt is not reentrant so can't use "printf" here + +From ad7f1e567324494021135bd2ec680b656a3f1cab Mon Sep 17 00:00:00 2001 +From: n-t-roff +Date: Wed, 27 Jul 2016 18:58:47 +0200 +Subject: [PATCH] Number mode command line bugfix + +The prev. number mode bugfix did introduce a bug in the command line +cursor position when using backspace in number mode. Should be +fixed now. +--- + ex_put.c | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +diff --git a/ex_put.c b/ex_put.c +index 26236d4..09ad403 100644 +--- a/ex_put.c ++++ b/ex_put.c +@@ -277,9 +277,12 @@ int + numbline(int i, int max) + { + ++ extern short vcntcol; + if (shudclob) + slobber(' '); + max -= printf("%6d ", i); ++ if (max > 0) ++ vcntcol = value(NUMBER) << 3; + return normline(0, max); + } + +@@ -297,8 +300,6 @@ normline(int unused, int max) + int c, n; + int ret = 0; + +- if (max > 0) +- vcntcol = value(NUMBER) << 3; + if (shudclob) + slobber(linebuf[0]); + /* pdp-11 doprnt is not reentrant so can't use "printf" here diff --git a/pkgbuilds/vi/navkeys.patch b/pkgbuilds/vi/navkeys.patch new file mode 100644 index 00000000..46924379 --- /dev/null +++ b/pkgbuilds/vi/navkeys.patch @@ -0,0 +1,55 @@ +diff -ru ex-050325/ex_tty.c ex-050325-du/ex_tty.c +--- ex-050325/ex_tty.c 2005-03-04 13:42:58.000000000 +0100 ++++ ex-050325-du/ex_tty.c 2009-06-25 20:06:57.000000000 +0200 +@@ -132,7 +132,8 @@ + &ED, &EI, &F0, &F1, &F2, &F3, &F4, &F5, &F6, &F7, &F8, &F9, + &HO, &IC, &IM, &IP, &KD, &KE, &KH, &KL, &KR, &KS, &KU, &LL, &ND, &xNL, + &xPC, &RC, &SC, &SE, &SF, &SO, &SR, &TA, &TE, &TI, &UP, &VB, &VS, &VE, +- &AL_PARM, &DL_PARM, &UP_PARM, &DOWN_PARM, &LEFT_PARM, &RIGHT_PARM ++ &AL_PARM, &DL_PARM, &UP_PARM, &DOWN_PARM, &LEFT_PARM, &RIGHT_PARM, ++ &kI, &kD, &kh, &at7, &kP, &kN + }; + bool *sflags[] = { + &AM, &BS, &DA, &DB, &EO, &HC, +@@ -170,7 +171,12 @@ + addmac1(KD, "j", "down", arrows, 1); + addmac1(KL, "h", "left", arrows, 1); + addmac1(KR, "l", "right", arrows, 1); +- addmac1(KH, "H", "home", arrows, 1); ++ addmac1(kI, "i", "insert", arrows, 1); ++ addmac1(kD, "x", "delete", arrows, 1); ++ addmac1(kh, "^", "home", arrows, 1); ++ addmac1(at7, "$", "end", arrows, 1); ++ addmac1(kP, "", "pgup", arrows, 1); ++ addmac1(kN, "", "pgdn", arrows, 1); + + /* + * Handle funny termcap capabilities +@@ -341,7 +347,7 @@ + *(*fp++) = flag; + namp += 2; + } while (*namp); +- namp = "albcbtcdceclcmcrcsdcdldmdoedeik0k1k2k3k4k5k6k7k8k9hoicimipkdkekhklkrkskullndnlpcrcscsesfsosrtatetiupvbvsveALDLUPDOLERI"; ++ namp = "albcbtcdceclcmcrcsdcdldmdoedeik0k1k2k3k4k5k6k7k8k9hoicimipkdkekhklkrkskullndnlpcrcscsesfsosrtatetiupvbvsveALDLUPDOLERIkIkDkh@7kPkN"; + sp = sstrs; + do { + string = tgetstr(namp, &aoftspace); +diff -ru ex-050325/ex_tty.h ex-050325-du/ex_tty.h +--- ex-050325/ex_tty.h 2004-12-01 19:21:06.000000000 +0100 ++++ ex-050325-du/ex_tty.h 2009-06-25 20:06:05.000000000 +0200 +@@ -183,6 +183,15 @@ + var bool XX; /* Tektronix 4025 insert line */ + /* X? is reserved for severely nauseous glitches */ + /* If there are enough of these we may need bit masks! */ ++/* ++ * Insert, Delete, Home, End, PgUp an PgDown keys ++ */ ++var char *kI; ++var char *kD; ++var char *kh; ++var char *at7; ++var char *kP; ++var char *kN; + + /* + * From the tty modes... diff --git a/pkgbuilds/vi/preserve-dir.patch b/pkgbuilds/vi/preserve-dir.patch new file mode 100644 index 00000000..ddfe8783 --- /dev/null +++ b/pkgbuilds/vi/preserve-dir.patch @@ -0,0 +1,99 @@ +diff -Naur ex-050325.ori/ex.1 ex-050325/ex.1 +--- ex-050325.ori/ex.1 2005-02-17 07:39:36.000000000 -0500 ++++ ex-050325/ex.1 2011-02-01 16:21:51.496666674 -0500 +@@ -1950,7 +1950,7 @@ + .B /var/tmp/Rx\fInnnnnnnnnn\fP + named buffer temporary + .TP +-.B /var/preserve ++.B /var/lib/ex + preservation directory + .SH DOCUMENTATION + The document +diff -Naur ex-050325.ori/expreserve.c ex-050325/expreserve.c +--- ex-050325.ori/expreserve.c 2005-03-04 07:44:46.000000000 -0500 ++++ ex-050325/expreserve.c 2011-02-01 16:22:06.583333342 -0500 +@@ -272,7 +272,7 @@ + #ifdef notdef + char pattern[] = "/usr/preserve/Exaa`XXXXX"; + #else +-char pattern[] = "/var/preserve/Exa`XXXXXXXXXX"; ++char pattern[] = "/var/lib/ex/Exa`XXXXXXXXXX"; + #endif + + /* +@@ -296,7 +296,7 @@ + timestamp = ctime(&time); + timestamp[16] = 0; /* blast from seconds on */ + putenv("MAILRC=/dev/null"); +- sprintf(cmd, "/bin/mail %s", pp->pw_name); ++ sprintf(cmd, "/usr/bin/mail %s", pp->pw_name); + setuid(getuid()); + mf = popen(cmd, "w"); + if (mf == NULL) +diff -Naur ex-050325.ori/exrecover.c ex-050325/exrecover.c +--- ex-050325.ori/exrecover.c 2005-03-04 07:44:46.000000000 -0500 ++++ ex-050325/exrecover.c 2011-02-01 16:21:51.496666674 -0500 +@@ -167,7 +167,7 @@ + #ifdef notdef + char mydir[] = "/usr/preserve"; + #else +-char mydir[] = "/var/preserve"; ++char mydir[] = "/var/lib/ex"; + #endif + + /* +diff -Naur ex-050325.ori/Makefile ex-050325/Makefile +--- ex-050325.ori/Makefile 2005-03-24 18:50:09.000000000 -0500 ++++ ex-050325/Makefile 2011-02-01 16:21:51.496666674 -0500 +@@ -83,7 +83,7 @@ + BINDIR = $(PREFIX)/bin + LIBEXECDIR = $(PREFIX)/libexec + MANDIR = $(PREFIX)/share/man +-PRESERVEDIR = /var/preserve ++PRESERVEDIR = /var/lib/ex + + # + # DESTDIR is prepended to the installation paths. It is mostly useful +diff -Naur ex-050325.ori/vi.1 ex-050325/vi.1 +--- ex-050325.ori/vi.1 2005-02-17 07:39:36.000000000 -0500 ++++ ex-050325/vi.1 2011-02-01 16:21:51.496666674 -0500 +@@ -979,7 +979,7 @@ + .B /var/tmp/Rx\fInnnnnnnnnn\fP + named buffer temporary + .TP +-.B /var/preserve ++.B /var/lib/ex + preservation directory + .SH SEE ALSO + ex(1), +--- ex-050325.original/ex.1 2012-01-27 20:43:00.602119283 +0100 ++++ ex-050325/ex.1 2012-01-27 20:47:24.535436516 +0100 +@@ -1932,10 +1932,10 @@ + Determines the terminal type. + .SH FILES + .TP +-.B /usr/libexec/expreserve ++.B /usr/lib/ex/expreserve + preserve command + .TP +-.B /usr/libexec/exrecover ++.B /usr/lib/ex/exrecover + recover command + .TP + .B /etc/termcap +--- ex-050325.original/vi.1 2012-01-27 20:43:00.602119283 +0100 ++++ ex-050325/vi.1 2012-01-27 20:46:33.815439611 +0100 +@@ -961,10 +961,10 @@ + Determines the terminal type. + .SH FILES + .TP +-.B /usr/libexec/expreserve ++.B /usr/lib/ex/expreserve + preserve command + .TP +-.B /usr/libexec/exrecover ++.B /usr/lib/ex/exrecover + recover command + .TP + .B /etc/termcap diff --git a/pkgbuilds/visual-studio-code-bin/.omarchy/package.json b/pkgbuilds/visual-studio-code-bin/.omarchy/package.json index e70b1af8..4d00cbf9 100644 --- a/pkgbuilds/visual-studio-code-bin/.omarchy/package.json +++ b/pkgbuilds/visual-studio-code-bin/.omarchy/package.json @@ -1,5 +1,5 @@ { "source": "aur", "release_ring": "fast", - "upstream_commit": "69b361eca02a84aa97c43c885e23d463eec96770" + "upstream_commit": "70a29667af6ac26dc2631a1910ac356924066b48" } diff --git a/pkgbuilds/visual-studio-code-bin/PKGBUILD b/pkgbuilds/visual-studio-code-bin/PKGBUILD index 210e18e3..1124e28e 100644 --- a/pkgbuilds/visual-studio-code-bin/PKGBUILD +++ b/pkgbuilds/visual-studio-code-bin/PKGBUILD @@ -2,7 +2,7 @@ pkgname=visual-studio-code-bin _pkgname=visual-studio-code -pkgver=1.135.0 +pkgver=1.136.1 pkgrel=1 pkgdesc="Visual Studio Code (vscode): Editor for building and debugging modern web and cloud applications (official binary version)" arch=('x86_64' 'aarch64' 'armv7h') @@ -27,9 +27,9 @@ source_x86_64=(code_${pkgver}_amd64.deb::https://update.code.visualstudio.com/${ source_aarch64=(code_${pkgver}_arm64.deb::https://update.code.visualstudio.com/${pkgver}/linux-deb-arm64/stable) source_armv7h=(code_${pkgver}_armhf.deb::https://update.code.visualstudio.com/${pkgver}/linux-deb-armhf/stable) sha256sums=('bd0d9edf69283ebdf4e73e0a7b168d2fcf50acbd01f63674cad93ed4fe42fdad') -sha256sums_x86_64=('e8101720f0a48f8e6ae5a4112d10ffea5f13d298e7d0a7012b4e36eebc53022c') -sha256sums_aarch64=('20f1717805ed723c8846e14df3280c6132a663ff71e32068aaa70f3e4c8b1ca4') -sha256sums_armv7h=('78e64befb925f85a34239db6e9c9c7c28d368df2ad42e7ff6b2f2876c394a22a') +sha256sums_x86_64=('4463c4e249a19d7746603f2ec3745c345d3cd55b009d36e824158d15c35509ef') +sha256sums_aarch64=('baa72f92d3feaa76d015202c57271475ea15809e635587279171a972cdd612a4') +sha256sums_armv7h=('02ddb5c5cbc2d5f4db25a3d7bb365c60eef0a017fa64b1994b131544677319e3') package() { bsdtar -xf data.tar.xz -C "${pkgdir}/" diff --git a/pkgbuilds/voxtype-bin/.omarchy/package.json b/pkgbuilds/voxtype-bin/.omarchy/package.json index f9b00a03..778fbb52 100644 --- a/pkgbuilds/voxtype-bin/.omarchy/package.json +++ b/pkgbuilds/voxtype-bin/.omarchy/package.json @@ -1,5 +1,82 @@ { - "source": "aur", + "source": "local", "release_ring": "fast", - "upstream_commit": "436615cc12ff968819fec3b2bb5ea87b3b185108" + "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" + ] + } + } } diff --git a/pkgbuilds/voxtype-bin/PKGBUILD b/pkgbuilds/voxtype-bin/PKGBUILD index 42e415a0..5333cd28 100644 --- a/pkgbuilds/voxtype-bin/PKGBUILD +++ b/pkgbuilds/voxtype-bin/PKGBUILD @@ -1,9 +1,9 @@ # Maintainer: Peter Jackson pkgname=voxtype-bin -pkgver=1.0.0 -pkgrel=2 +pkgver=1.0.1 +pkgrel=1 pkgdesc="Push-to-talk voice-to-text for Linux (pre-built binaries)" -arch=('x86_64') +arch=('x86_64' 'aarch64') url="https://voxtype.io" license=('MIT') depends=( @@ -47,7 +47,7 @@ validpgpkeys=( '9CCF7915B750CAE8B095ED1AA3FC9F33FD209279' ) _github="https://github.com/peteonrails/voxtype/releases/download/v$pkgver" -source=( +source_x86_64=( # Whisper binaries "voxtype-$pkgver-avx2::$_github/voxtype-$pkgver-linux-x86_64-avx2" "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-audio-bridge::$_github/voxtype-$pkgver-linux-x86_64-audio-bridge" "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" "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" @@ -121,80 +137,104 @@ source=( "quickshell-voxtype-shared-Theme-$pkgver.qml::https://raw.githubusercontent.com/peteonrails/voxtype/v$pkgver/quickshell/voxtype-shared/Theme.qml" "quickshell-voxtype-shared-StateReader-$pkgver.qml::https://raw.githubusercontent.com/peteonrails/voxtype/v$pkgver/quickshell/voxtype-shared/StateReader.qml" "quickshell-voxtype-shared-AudioBridge-$pkgver.qml::https://raw.githubusercontent.com/peteonrails/voxtype/v$pkgver/quickshell/voxtype-shared/AudioBridge.qml" + "quickshell-voxtype-shared-StyleLoader-$pkgver.qml::https://raw.githubusercontent.com/peteonrails/voxtype/v$pkgver/quickshell/voxtype-shared/StyleLoader.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" ) -sha256sums=( +sha256sums_x86_64=( # Whisper binaries - '7323514a48aee6a73c0124463bd0276aad2d77f61c537ae753ca527700860572' # voxtype-avx2 - 'SKIP' # voxtype-avx2.asc - 'bf7d6c0cb9573ed49a23b17b3846e6adcc36f59bca56ba1ebd71ca55f3ffe6ef' # voxtype-avx512 - 'SKIP' # voxtype-avx512.asc - 'b3d5b64b9e5ef07278966e429dfc49f52df990c637a9425110b9ed6a3b498a62' # voxtype-vulkan - 'SKIP' # voxtype-vulkan.asc + 'cb3843a894ef47aca230b30bb1c45c2ef8e0d015adf2fa754d60e55123165fd0' # voxtype-avx2 + '21357d39412619e8a3fc473153e6e2a28b219aa02007682737afedd73a08bdbe' # voxtype-avx2.asc + '77d49275ae4c3a6d93671014278b0dabd69ba52e65d2ee6ab3ec1bff0af34d55' # voxtype-avx512 + 'e3421641b4b5fa5d42ee0698eb43552e177f8b095a1acea32445693404b4fd54' # voxtype-avx512.asc + 'c569d038057464aa60290296794bcbd79b928ee0efd038e33062a4c015558ed8' # voxtype-vulkan + 'a82bd4108dc70d7d2b6e25a83a6744170502f5110780d4a2e97a3e76047ea60d' # voxtype-vulkan.asc # ONNX CPU binaries - '99757afc3e1507ecf54ef4fc1c2fe5d940d671a0931e0ec88cd28025e110034b' # voxtype-onnx-avx2 - 'SKIP' # voxtype-onnx-avx2.asc - 'ba225151b93e6f4e5d741347e3fb592eede3b726f91d692c812abf78330d2e88' # voxtype-onnx-avx512 - 'SKIP' # voxtype-onnx-avx512.asc + '19b8716ff7bd388ffb18843fc0ee5e614faa84544d22918ecf72a7c3d6c02929' # voxtype-onnx-avx2 + '3021aa3a13555bdab0ecccfb522db8391d7e05810d218608a6372477f11cb2af' # voxtype-onnx-avx2.asc + '69a6e6fc1c04a55eeb62cf1eaa5fe81873c70eabffff120ce1a4ff7ef62fd559' # voxtype-onnx-avx512 + '305d33b6f4dc8ac7a38a5f522c1af95b462355fa057b2eb39d3add59303145e1' # voxtype-onnx-avx512.asc # ONNX CUDA 12 binary + companion .so - 'b92790a76dd989444d5f1f5e796895f158fe603005a0b2c7fa5171403dfa4153' # voxtype-onnx-cuda-12 - 'SKIP' # voxtype-onnx-cuda-12.asc + 'e7180f17add10bb441368d8495fc99bde0ba1a1978ea5f7a52d7e626779eca16' # voxtype-onnx-cuda-12 + '34f7242fe16867ab029af9ed4854d3930aa2ed91bc37e062962edbfc65522af5' # voxtype-onnx-cuda-12.asc 'a8584727d51ba646ac63fc991c2f36ad6cd5b8cc8b1141896e46938700b888d0' # cuda-12.libonnxruntime_providers_cuda.so '1b028afc079628d76a28d7eb09700a4baead4a27f9634ba82c35398486134114' # cuda-12.libonnxruntime_providers_shared.so # ONNX CUDA 13 binary + companion .so + dlopen-target libonnxruntime - '3fe24444779c15b0755bb66841e225aa69594614c95b8f2bddf3c2300736e254' # voxtype-onnx-cuda-13 - 'SKIP' # voxtype-onnx-cuda-13.asc + '4de2aac42b67a05c34ac0b23f771fc83968670fd77d14781cbf4483524304cd1' # voxtype-onnx-cuda-13 + 'adb768be0b115f38bc28ea35614ae59e1c1adfb6e11ae08a252c45d52932fab2' # voxtype-onnx-cuda-13.asc 'b6cb7744d0efd2faced5c83ead374c13e7f2630b5a249ffaf393cdb1e092c92b' # cuda-13.libonnxruntime_providers_cuda.so 'c6a12593396095f5670160e284c35d1700b7708cf3037b7042e2a5200ccae772' # cuda-13.libonnxruntime_providers_shared.so '1aacefdf0b4afa145d410b2381bbc3db3d978c485fb182c42a2b0b09f91f5310' # cuda-13.libonnxruntime.so.1.24.4 # ONNX MIGraphX binary + companion .so - '954ca9a9fe98cfe3e6da93568e41a63472051800616a267340d9e8b8cffa9a96' # voxtype-onnx-migraphx - 'SKIP' # voxtype-onnx-migraphx.asc + 'f7a4cc0deaf35110b5106f1fc1f83dded41c0b1780f9fb5aa9cccc3c62e5eb6a' # voxtype-onnx-migraphx + '9e9c767605923c084c97659326cb0f03ad4e5db0aded4da052fa208b4de9cf45' # voxtype-onnx-migraphx.asc 'ddd67e6193ade819ee21f1706d1b03b9151f1d2d2843701e2d19d8b183631707' # migraphx.libonnxruntime_providers_migraphx.so '17f7cf47ad0d7b5ac895ae588fd62c7f85a13842588161b6a24c7d480f062be4' # migraphx.libonnxruntime_providers_shared.so # OSD launcher + GTK4 frontend - 'ec78f39c7381ea8bbd974e0d49c35618152557c2b3c2b9e2a9f2e34a8ab68598' # voxtype-osd - 'SKIP' # voxtype-osd.asc - '6c51add26a3329b66ff98ddeeeb9ccc6a6a2bff428027aa2a6627e526d743720' # voxtype-osd-gtk4 - 'SKIP' # voxtype-osd-gtk4.asc + '7250027b1672507a6d584f795731c87e1d3b5c1de891438bd55e34b136a2d5cc' # voxtype-osd + '1a944200fbac1e773c70d036075745b0e868885443e29c30797f262d44928cc1' # voxtype-osd.asc + '74fb0f6ad87feb0c1c9e06a8b28a7f7ecee101caef4248f77dcf613b5271238e' # voxtype-osd-gtk4 + '60224685717fea0582d570160282e9e215cd4713b3bdb695edc7bae8b6860cac' # voxtype-osd-gtk4.asc # Quickshell OSD launcher + audio-bridge sidecar (new in v0.7.5) - '0944cd78972bdabec0fff30aeef0470643ef0a661c174c1074452c26cee449c6' # voxtype-osd-quickshell - 'SKIP' # voxtype-osd-quickshell.asc - '4e822d393a6a005296ae1fb31f1276fc242bb8454c80df3d443fa56c50e02a38' # voxtype-audio-bridge - 'SKIP' # voxtype-audio-bridge.asc - # Config and support files - '0b91367d2088e8850a37b47a0cba0bd9107695fc81a00ab1933465ada2a16a2f' # config/default.toml + 'b809c5140e844a6add801d7e592775cd89af8cce73fa399b6a3aec15dfd09533' # voxtype-osd-quickshell + '39412691ab1f8ffe4fa6bf1eb9763c29f94b8892d1ba59fd49dc65dd0d80297a' # voxtype-osd-quickshell.asc + '45776290e364194d83a8b89445166406c278e890507bf07ec52a5f0e8fa57720' # voxtype-audio-bridge + '5be1174a78d70b78fd8d923c88e3406962443b39a1c452eb985b1e2484ef5668' # voxtype-audio-bridge.asc +) +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 '531c3658e229619e56bb01659fb81f401767b85e1d6e2acd1ac67ee3414a168c' # voxtype.service '65c95805d9b03ccc2fadb9d63a03ab79974b00091df8457ee8ef290ec6bd5b12' # voxtype.bash 'e5e63b3c7f48238cf719e4f2ef90c1f9c5c7e8cd25eaebc9f78bdd34b24b6605' # voxtype.zsh 'f720ddd24ee97c105b448323899c36bca7c63d00c2d42c4a3da70c3d157dccbb' # voxtype.fish '31123c45b4ff9cb5fd9e01083350fea6ccaf14969013fd48e4c95fdf89e6eb4b' # LICENSE - '11bf83d8cb2bfab0e978707fa80475d97be79723a6dc71adc3c357829ba9f347' # README.md + 'e5b2ec5da5eafe2ce8f7b84b81a889f2ff496ad6488d56b6b9b32b73d025ed53' # README.md # Desktop entry + launcher '32144a4a5210092b0aa909f6de7a43ebe8bbf82fa3dfb1f3519787512fdf8e4b' # voxtype-configure.desktop '044b1f7b52cc610ce57ba624111d882029b0ce4bc3e2c2c360f96d07f69e0e85' # voxtype-configure-launcher # Quickshell QML tree (new in v0.7.5) - '3f711c7cfca80773960e98a47361611497e584e56657052d675241daae84947e' # quickshell/shell.qml - '6e8b8aae6b64809ed5ca504a3171a4ddeadc91b1ed96eacbf67bbf056b2ffbdd' # quickshell/OsdSurface.qml + 'd3d0b0b24a3fc3e252623a6fad898c7d1d147fb8c2e49de90fbf452e95de63ab' # quickshell/shell.qml + '70a611fd4eabde43189aee0619899827de21e36695191648dc36470d4c333de6' # quickshell/OsdSurface.qml '4d6eef505ec161080ca92ae6a355ca00dcc7bb05d5b190534a1851fcbb726e55' # quickshell/EnginePicker.qml '54271a8f0e4b52f40505f32801f4c78399ac356ed1e80906ca8a068ecb7ee734' # quickshell/MeetingControls.qml 'aaa011682b92d8e25863a9ea34a469b897bac15ad939292d8144b012fa05b209' # quickshell/voxtype-shared/Theme.qml - 'd7d2fb11df25c3f30127a0d7c72693cdfb649b357888f4abd5b3fbbb2e7ce655' # quickshell/voxtype-shared/StateReader.qml + '74345f9d8b77de3f1d6d08a759e52506c606e8af3d04a97a5bfe7954d0f64604' # quickshell/voxtype-shared/StateReader.qml '2ad530f92f13fc7f1100e6f6c9910878c35d911e91dc60feb902c2542b619230' # quickshell/voxtype-shared/AudioBridge.qml + '45180488129f16a0568c217c743ce7e45831acb0cb287346d6da5b2fc097717b' # quickshell/voxtype-shared/StyleLoader.qml + 'd5e9a86946dc8dc2afabdcd00d79d973181e51b2e5e8aea2772960aaf1ff2283' # quickshell/voxtype-shared/RecipeRenderer.qml '41b894baa1487e47db5f9ec4baeea9c02b6a76d3c8198b4700a88e2cb7ec0b62' # quickshell/voxtype-shared/qmldir ) package() { - # 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" + if [[ "$CARCH" == aarch64 ]]; then + install -Dm755 "$srcdir/voxtype-$pkgver-cpu" "$pkgdir/usr/lib/voxtype/voxtype-cpu" + 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 -Dm755 "$srcdir/voxtype-$pkgver-vulkan" "$pkgdir/usr/lib/voxtype/voxtype-vulkan" + # Install Whisper Vulkan GPU binary + install -Dm755 "$srcdir/voxtype-$pkgver-vulkan" "$pkgdir/usr/lib/voxtype/voxtype-vulkan" - # 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-avx512" "$pkgdir/usr/lib/voxtype/voxtype-onnx-avx512" + # 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-avx512" "$pkgdir/usr/lib/voxtype/voxtype-onnx-avx512" # 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 @@ -244,7 +284,8 @@ package() { # 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. # 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 # are created by the .install script's post_install/post_upgrade hooks @@ -284,6 +325,10 @@ package() { "$pkgdir/usr/share/voxtype/quickshell/voxtype-shared/StateReader.qml" install -Dm644 "$srcdir/quickshell-voxtype-shared-AudioBridge-$pkgver.qml" \ "$pkgdir/usr/share/voxtype/quickshell/voxtype-shared/AudioBridge.qml" + install -Dm644 "$srcdir/quickshell-voxtype-shared-StyleLoader-$pkgver.qml" \ + "$pkgdir/usr/share/voxtype/quickshell/voxtype-shared/StyleLoader.qml" + install -Dm644 "$srcdir/quickshell-voxtype-shared-RecipeRenderer-$pkgver.qml" \ + "$pkgdir/usr/share/voxtype/quickshell/voxtype-shared/RecipeRenderer.qml" install -Dm644 "$srcdir/quickshell-voxtype-shared-qmldir-$pkgver" \ "$pkgdir/usr/share/voxtype/quickshell/voxtype-shared/qmldir" diff --git a/pkgbuilds/voxtype-bin/voxtype-bin.install b/pkgbuilds/voxtype-bin/voxtype-bin.install index 7b424464..a790fa3d 100644 --- a/pkgbuilds/voxtype-bin/voxtype-bin.install +++ b/pkgbuilds/voxtype-bin/voxtype-bin.install @@ -53,6 +53,14 @@ _install_active_binary() { } _set_default_backend() { + # aarch64 ships one generic CPU binary — no AVX2/AVX-512 split exists on + # that architecture, so there's nothing to detect. + if [ "$(uname -m)" = "aarch64" ]; then + _install_active_binary /usr/lib/voxtype/voxtype-cpu + echo "cpu" + return + fi + # Set the default CPU backend based on CPU capabilities if grep -q avx512f /proc/cpuinfo 2>/dev/null; then _install_active_binary /usr/lib/voxtype/voxtype-avx512 @@ -161,6 +169,14 @@ _set_onnx_cuda_symlink() { } _detect_gpu() { + # No GPU-accelerated binary variant ships for aarch64 yet — no mainstream + # ARM hardware has a supported Vulkan/CUDA/MIGraphX path (see + # voxtype#579) — so recommending `voxtype setup gpu --enable` here would + # send aarch64 users after a switch that doesn't exist. + if [ "$(uname -m)" = "aarch64" ]; then + return + fi + # Detect GPU for acceleration recommendation GPU_DETECTED="" GPU_VENDOR="" diff --git a/pkgbuilds/xpadneo-dkms/.omarchy/patches/arm-kernel-headers.patch b/pkgbuilds/xpadneo-dkms/.omarchy/patches/arm-kernel-headers.patch new file mode 100644 index 00000000..9dcc175f --- /dev/null +++ b/pkgbuilds/xpadneo-dkms/.omarchy/patches/arm-kernel-headers.patch @@ -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') + diff --git a/pkgbuilds/xpadneo-dkms/PKGBUILD b/pkgbuilds/xpadneo-dkms/PKGBUILD index d14f85cb..3117cde6 100644 --- a/pkgbuilds/xpadneo-dkms/PKGBUILD +++ b/pkgbuilds/xpadneo-dkms/PKGBUILD @@ -9,11 +9,13 @@ pkgname=xpadneo-dkms pkgdesc='Advanced Linux Driver for Xbox One Wireless Gamepad' pkgver=0.10.4 -pkgrel=1 +pkgrel=1.1 url='https://github.com/atar-axis/xpadneo' 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') diff --git a/systemd/omarchy-auto-release-edge.service b/systemd/omarchy-auto-release-edge.service index 7658dfb7..3537ed95 100644 --- a/systemd/omarchy-auto-release-edge.service +++ b/systemd/omarchy-auto-release-edge.service @@ -7,7 +7,7 @@ Wants=network-online.target Type=oneshot ExecStart=/bin/bash -c 'source /root/.omarchy/build-credentials && /root/omarchy-pkgs/bin/auto-release edge' Environment=OMARCHY_STATE_DIR=/root/.state -TimeoutStartSec=7200 +TimeoutStartSec=43200 [Install] WantedBy=multi-user.target diff --git a/systemd/omarchy-auto-release-rc.service b/systemd/omarchy-auto-release-rc.service index 0078b3d1..c57f5f73 100644 --- a/systemd/omarchy-auto-release-rc.service +++ b/systemd/omarchy-auto-release-rc.service @@ -12,7 +12,7 @@ Type=oneshot # branch's worktree with OMARCHY_RC_PINS=1. ExecStart=/bin/bash -c 'source /root/.omarchy/build-credentials && /root/omarchy-pkgs/bin/auto-release rc' Environment=OMARCHY_STATE_DIR=/root/.state -TimeoutStartSec=7200 +TimeoutStartSec=43200 [Install] WantedBy=multi-user.target diff --git a/systemd/omarchy-auto-release-stable.service b/systemd/omarchy-auto-release-stable.service index e1979e3a..110bad3b 100644 --- a/systemd/omarchy-auto-release-stable.service +++ b/systemd/omarchy-auto-release-stable.service @@ -7,7 +7,7 @@ Wants=network-online.target Type=oneshot ExecStart=/bin/bash -c 'source /root/.omarchy/build-credentials && /root/omarchy-pkgs/bin/auto-release stable' Environment=OMARCHY_STATE_DIR=/root/.state -TimeoutStartSec=7200 +TimeoutStartSec=43200 [Install] WantedBy=multi-user.target