Merge current master into ARM settings boot fix

Preserve the v4.0.4 source pin and checksum while retaining the settings package revision bump. Keep Jim Martin’s boot-configuration fix and original commit intact.
This commit is contained in:
Birk Skyum committed 2026-09-16 20:30:59 +02:00
commit 6a6e170fe4
571 files changed
+196061 -1490

No files matched your search

-91
View File
@@ -1,91 +0,0 @@
name: Sync AUR Packages
on:
schedule:
# Every 6 hours
- cron: '0 */6 * * *'
workflow_dispatch:
inputs:
packages:
description: 'Specific packages to sync (space-separated, leave empty for all)'
required: false
default: ''
jobs:
sync:
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
persist-credentials: false
- name: Sync AUR packages
run: |
docker run --rm \
-e PACKAGES="$PACKAGES" \
-e HOST_UID="$(id -u)" \
-e HOST_GID="$(id -g)" \
-v "$PWD/bin:/workspace/bin:ro" \
-v "$PWD/helpers:/workspace/helpers:ro" \
-v "$PWD/pkgbuilds:/workspace/pkgbuilds" \
-w /workspace \
archlinux:base-devel bash -lc '
set -euo pipefail
pacman -Syu --noconfirm git jq
groupadd -g "$HOST_GID" runner
useradd -m -u "$HOST_UID" -g "$HOST_GID" runner
chown -R runner:runner /workspace/pkgbuilds
if [[ -n "${PACKAGES:-}" ]]; then
read -r -a package_args <<< "$PACKAGES"
runuser -u runner -- ./bin/sync-aur "${package_args[@]}"
else
runuser -u runner -- ./bin/sync-aur
fi
'
env:
PACKAGES: ${{ github.event.inputs.packages }}
- name: Check for changes
id: changes
run: |
if [ -z "$(git status --porcelain)" ]; then
echo "has_changes=false" >> "$GITHUB_OUTPUT"
else
echo "has_changes=true" >> "$GITHUB_OUTPUT"
fi
- name: Create Pull Request
if: steps.changes.outputs.has_changes == 'true'
uses: peter-evans/create-pull-request@v7
with:
token: ${{ secrets.GITHUB_TOKEN }}
commit-message: 'chore: sync AUR packages'
title: 'chore: sync AUR packages'
body: |
Automated AUR package sync.
Package sync behavior is controlled by `.omarchy/package.json`.
branch: auto/sync-aur
delete-branch: true
labels: automated
reviewers: ryanrhughes
- name: Notify Basecamp on failure
if: failure() && env.BASECAMP_CHATBOT_URL != ''
env:
BASECAMP_CHATBOT_URL: ${{ secrets.BASECAMP_CHATBOT_URL }}
run: |
curl -s -o /dev/null \
-H "Content-Type: application/json" \
-d "$(jq -n --arg content \
"🔴 <strong>AUR sync failed</strong><br><a href=\"${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}\">View run</a>" \
'{content: $content}')" \
"$BASECAMP_CHATBOT_URL"
+11 -4
View File
@@ -27,9 +27,11 @@ jobs:
# Runs in an Arch container for vercmp: whether a release is an upgrade has
# to be decided by the same comparator pacman will use on users' machines.
- name: Update packages from upstream release feeds
id: sync
run: |
docker run --rm \
-e PACKAGES="$PACKAGES" \
-e UPSTREAM_GITHUB_TOKEN="$UPSTREAM_GITHUB_TOKEN" \
-e HOST_UID="$(id -u)" \
-e HOST_GID="$(id -g)" \
-v "$PWD/bin:/workspace/bin:ro" \
@@ -39,7 +41,7 @@ jobs:
archlinux:base-devel bash -lc '
set -euo pipefail
pacman -Syu --noconfirm git jq
pacman -Syu --noconfirm git jq python libarchive
groupadd -g "$HOST_GID" runner
useradd -m -u "$HOST_UID" -g "$HOST_GID" runner
@@ -54,8 +56,12 @@ jobs:
'
env:
PACKAGES: ${{ github.event.inputs.packages }}
UPSTREAM_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# Failed feeds leave their recipes untouched; completed updates still
# reach review. The failed sync step keeps the workflow red and notifies.
- name: Check for changes
if: ${{ !cancelled() && steps.sync.outcome != 'skipped' }}
id: changes
run: |
if [ -z "$(git status --porcelain)" ]; then
@@ -65,7 +71,7 @@ jobs:
fi
- name: Create Pull Request
if: steps.changes.outputs.has_changes == 'true'
if: ${{ !cancelled() && steps.changes.outputs.has_changes == 'true' }}
uses: peter-evans/create-pull-request@v7
with:
token: ${{ secrets.GITHUB_TOKEN }}
@@ -75,8 +81,9 @@ jobs:
Automated update of packages that track an upstream vendor release
feed rather than the AUR.
Each package reports its newest release through
`.omarchy/upstream.sh`.
Release watches and providers are declared in `.omarchy/package.json`;
exceptional feeds use `.omarchy/upstream.sh`. Failed package updates
are left untouched; check the workflow result for outstanding failures.
branch: auto/sync-upstream
delete-branch: true
labels: automated
+4 -1
View File
@@ -39,9 +39,12 @@ jobs:
-w /workspace \
archlinux:base-devel bash -lc '
set -euo pipefail
pacman -Syu --noconfirm git jq
pacman -Syu --noconfirm git jq python libarchive
python tests/upstream-watch.py
./bin/sync-upstream self-test
./bin/sync-rebuilds --self-test
./bin/omarchy-pkgs self-test
./bin/omarchy-release self-test
./tests/partial-release.sh
./tests/published-build-plan.sh
'
+3
View File
@@ -37,3 +37,6 @@ pkgbuilds/yay/yay/
.srcdest/
.repo-host
.worktrees/
# Python helpers and offline tests
__pycache__/
+43 -105
View File
@@ -23,9 +23,9 @@ The filesystem no longer encodes release policy. Instead:
(`OMARCHY_RC_PINS=1`, which `omarchy-release rc` sets) may build it for rc — master's
shipped pins can never overwrite an in-flight RC. The dev pair
(`omarchy-dev`, `omarchy-settings-dev`) is pinned to `edge`
- AUR sync behavior is controlled by `source`, `sync`, `aur`, patches, and hooks in `.omarchy/`
- Omarchy owns every checked-in recipe; upstream watches update release metadata without replacing packaging or architecture support
- packages can opt out of unscoped builds with `skip_build`; explicit `--package` builds remain available
- packages that follow a vendor release feed instead of the AUR carry an `.omarchy/upstream.sh` hook
- packages follow direct upstream watches/providers in `.omarchy/package.json`, or a custom `.omarchy/upstream.sh` hook
## Prerequisites
### aarch64 Builds (Optional)
@@ -125,6 +125,14 @@ bin/repo advance --from edge --to rc
The release command is smart and **incremental** - it only builds packages that have changed or are missing. You generally don't need to specify a package manually unless you are debugging a specific failure.
When a package fails, a completed build run still signs and publishes the packages
that succeeded. Failed packages and their blocked dependents remain queued with
failure backoff; retries compare against the updated repository and skip the
published versions. Only artifacts recorded by fully completed package builds
are eligible for a partial release. An interrupted build, a failed publication
step, or an incomplete pair using deferred runtime dependencies still stops the
release. Reports distinguish partial publication from complete success.
```bash
# Build changed/new packages, sign, promote, clean, update, and sync
bin/repo release
@@ -321,14 +329,16 @@ push uploaded, so `push` stops when it finds packages already staged there —
usually leftovers from a failed run. Remove them on the host, or pass
`--include-staged` to publish them too.
### Sync AUR PKGBUILDs
### Import an initial AUR recipe
```bash
bin/sync-aur # Sync all AUR packages with sync enabled
bin/sync-aur yay v4l2-relayd # Sync specific packages
bin/add-package package-name --source aur
```
AUR sync is metadata-driven. It preserves `.omarchy/`, replaces the package root with AUR contents, applies `.omarchy/patches/*.patch`, runs `.omarchy/post-sync.sh` when present, applies pkgrel metadata, removes AUR-only `.SRCINFO` and `.gitignore` files, and records `upstream_commit`.
AUR is an optional source for an initial recipe. Imported packages become
Omarchy-owned immediately; subsequent updates use direct upstream releases.
There is no scheduled AUR sync. Edit the checked-in PKGBUILD to maintain
architecture support and packaging behavior.
### Sync Upstream Releases
@@ -542,8 +552,8 @@ bin/omarchy-release # Release front door (start / pick / rc / s
bin/repo list # List package metadata
bin/repo deploy # Build locally, then publish from the host
bin/repo push # Upload local builds to the host and publish
bin/add-package <package> # Add an AUR/local package with metadata
bin/package-worktree <package> # Create upstream/patched/current scratch workspace
bin/add-package <package> # Add an Omarchy-owned package with metadata
bin/package-worktree <package> # Inspect historical AUR provenance in a scratch workspace
bin/repo remove <package> # Remove package
bin/sync-upstream # Update packages that track a vendor release feed
bin/sync-rebuilds # Bump pkgrel for packages whose dependencies moved
@@ -562,7 +572,7 @@ bin/repo list # Table view of source package metadata
bin/repo list --json # Agent/script-friendly JSON
bin/repo list --repo --mirror stable # List packages in a published repo database
bin/package-worktree v4l2-relayd # Create upstream/patched/current scratch workspace
bin/package-worktree yay # Compare with the original imported AUR recipe
```
## Cutting an Omarchy Release
@@ -676,9 +686,7 @@ omarchy-pkgs/
│ ├── PKGBUILD
│ └── .omarchy/
│ ├── package.json # Source/sync/release metadata
│ ├── patches/ # Omarchy patches reapplied after AUR sync
│ ├── post-sync.sh # Optional dynamic post-sync customization hook
│ └── upstream.sh # Optional vendor release feed hook (non-AUR packages)
│ └── upstream.sh # Optional custom vendor release feed hook
├── build/
├── build-output/ # Unsigned packages (temporary)
│ ├── edge/ # (rc/ and stable/ alongside, each x86_64 + aarch64)
@@ -698,45 +706,28 @@ Each source package has Omarchy metadata at `pkgbuilds/<package>/.omarchy/packag
Minimal examples:
```json
{ "source": "aur" }
```
```json
{ "source": "aur", "sync": false }
```
```json
{ "source": "aur", "release_ring": "fast" }
```
```json
{ "source": "local" }
```
```json
{ "source": "local", "release_ring": "fast" }
{ "source": "local", "skip_build": true }
```
```json
{ "source": "aur", "pkgrel": { "suffix": 1 } }
{ "source": "local", "upstream": { "watch": { "github": "abenz1267/walker", "pattern": "v(?P<version>[0-9]+(?:\\.[0-9]+)*)" } } }
```
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 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`.
- `source`: `local` for maintained packages. The legacy `aur` value is used only during an initial import. A local recipe can follow an upstream watch, provider, or `.omarchy/upstream.sh` hook.
- `upstream`: optional direct release watch (see [Upstream watches](docs/upstream-sources.md)), or an existing GitHub, git-tag, npm, or Debian provider. GitHub architecture assets may be a string or an ordered array, and can be combined with disjoint versioned `sources` — see [Sync Upstream Releases](#sync-upstream-releases). Mutually exclusive with `.omarchy/upstream.sh`.
- `min_release_age`: optional quarantine for upstream releases (`"24h"`, `"2d"`, or bare seconds). The newest release older than the window ships; anything younger waits, and a release whose age cannot be proven fails the sync. Bypass deliberately with `BYPASS_MIN_RELEASE_AGE=1 bin/sync-upstream <package>`.
- `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.
- `sync`: `false` records an existing manual maintenance hold. Held packages have no upstream watch/provider/hook and are excluded from automatic updates.
- `origin`: optional historical import provenance, with `aur` (package name) and `commit`. It does not control updates.
- `release_ring`: optional. `fast` means the package is built directly for stable as well as edge, with the artifacts replicated into rc for parity. Packages without a ring build in edge and reach stable through the pipeline (`bin/repo advance`).
- `channels`: optional array bounding where the package may be built (`edge`, `rc`, `stable`). Without the key a package is a member of every channel and follows the default build rules above; `bin/repo advance` refuses to carry a package anywhere it isn't a member.
- `pinned`: optional boolean. A pinned package's version is set per release by `omarchy-release` on the `rc` branch, so it is never built for stable (promotion only) and is built for rc only from that branch's worktree (`OMARCHY_RC_PINS=1`). Used by `omarchy` and `omarchy-settings`.
- `skip_build`: optional boolean; defaults to `false`. Set `true` to exclude a package from scheduled version checks and unscoped builds. The package can still be built explicitly with `bin/repo release --package <name>`.
- `pkgrel`: optional Omarchy pkgrel suffix for a version-pinned rebuild bump. This emits `<aur pkgrel>.<suffix>` instead of replacing AUR's pkgrel. `offset` can be used only when preserving monotonic upgrades from old absolute pkgrel bumps. The metadata is removed automatically when AUR sync changes `pkgver`; the current package version is read from the checked-in PKGBUILD, so the version is not duplicated in JSON.
- `pkgrel`: legacy import customization metadata. Maintained recipes keep their complete package release directly in PKGBUILD; rebuilds increment it there.
- `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`. Maps each published architecture to the versions of its `rebuild_on` packages that the current pkgrel was bumped for.
- `upstream_commit`: set by `bin/sync-aur` for AUR packages. Used by `bin/package-worktree` to recreate the exact raw AUR package that Omarchy last synced.
- `upstream_commit`: legacy AUR metadata, superseded by `origin.commit`. `bin/package-worktree` can use historical provenance to inspect the original recipe.
### Build Matrix
@@ -748,78 +739,26 @@ Fields:
## Adding Packages
### From AUR
### Start from an existing recipe
```bash
bin/add-package package-name
bin/add-package package-name --source aur --fast
# Review the imported files, own any architecture/packaging changes directly,
# and declare an upstream watch/provider or hook in .omarchy/.
bin/sync-upstream package-name
bin/repo release --package package-name
```
### From AUR, fast release ring
The import records historical provenance in `origin`. It does not opt a package
into future AUR imports. Upstream watches update only release scalars and source
checksums; downstream build behavior stays in the recipe. Ordinary source-code
patches still belong beside PKGBUILD and are applied by `prepare()` as needed.
### Custom package
```bash
bin/add-package package-name --fast
bin/repo release --package package-name
bin/repo release --mirror stable --package package-name
```
### AUR-origin, manually maintained by Omarchy
```bash
bin/add-package package-name --no-sync
```
### Local Customizations for AUR Packages
For static changes, create `pkgbuilds/package-name/.omarchy/patches/*.patch` to maintain modifications across AUR syncs.
The recommended workflow is to use a scratch workspace:
```bash
bin/package-worktree package-name --dir /tmp/package-name-worktree
```
This creates:
```text
upstream/ # raw AUR package at upstream_commit
patched/ # AUR + existing Omarchy .omarchy customizations
current/ # current checked-in package directory
```
Patch-authoring flow:
```bash
# 1. Make the intended change in pkgbuilds/package-name/
# 2. Recreate the scratch workspace
bin/package-worktree package-name --dir /tmp/package-name-worktree
# 3. Inspect drift from patched -> current
# For multi-file changes, inspect this and split into focused patches.
diff -ruN /tmp/package-name-worktree/patched /tmp/package-name-worktree/current
# For a single PKGBUILD change, write a patch like this:
mkdir -p pkgbuilds/package-name/.omarchy/patches
(
cd /tmp/package-name-worktree/patched
diff -u --label a/PKGBUILD --label b/PKGBUILD \
PKGBUILD /tmp/package-name-worktree/current/PKGBUILD || true
) > pkgbuilds/package-name/.omarchy/patches/my-fix.patch
# 4. Verify the package is reproducible from AUR + .omarchy
bin/sync-aur package-name
bin/package-worktree package-name --dir /tmp/package-name-check
diff -ruN /tmp/package-name-check/patched /tmp/package-name-check/current
```
For dynamic changes that depend on the current upstream version, add `pkgbuilds/package-name/.omarchy/post-sync.sh`. The hook runs after the AUR package is copied into a temporary worktree and before the Omarchy pkgrel suffix is applied. After patches/hooks/metadata pkgrel overrides, `bin/sync-aur` removes AUR-only `.SRCINFO` and `.gitignore` files before writing the package back.
### Custom Package
```bash
bin/add-package my-package --local --scaffold
# Fill in PKGBUILD and package files
bin/add-package my-package --scaffold
# Fill in PKGBUILD, package files, and upstream metadata
bin/repo release --package my-package
```
@@ -902,9 +841,8 @@ The repository includes GitHub workflows and systemd services for automated rele
#### GitHub Workflows
1. **sync-aur.yml** (Every 6 hours): Syncs AUR packages according to `.omarchy/package.json` and opens a PR when changes are found.
2. **sync-upstream.yml** (Every 6 hours): Runs `.omarchy/upstream.sh` for packages that track a vendor release feed and opens a PR when a newer version is out.
3. **sync-rebuilds.yml** (Every 6 hours): Bumps pkgrel for packages whose `rebuild_on` dependencies have moved in the official repositories and opens a PR.
1. **sync-upstream.yml** (Every 6 hours): Watches direct upstream feeds and updates owned recipes. Successful package updates reach a PR even if another feed fails; failed recipes stay untouched and the workflow remains red.
2. **sync-rebuilds.yml** (Every 6 hours): Bumps pkgrel for packages whose `rebuild_on` dependencies have moved in the official repositories and opens a PR.
#### Systemd Services
+19 -20
View File
@@ -6,7 +6,7 @@ source "$BUILD_ROOT/helpers/message-helpers.sh"
source "$BUILD_ROOT/helpers/paths.sh"
PACKAGE=""
SOURCE="aur"
SOURCE="local"
SYNC="true"
RELEASE_RING=""
AUR_PACKAGE=""
@@ -17,14 +17,14 @@ usage() {
cat <<EOF
Usage: $0 <package> [OPTIONS]
Create pkgbuilds/<package> with Omarchy metadata. AUR packages are synced
immediately after metadata is written.
Create an Omarchy-owned package. --source aur imports a starting recipe once;
future releases must use an upstream watch, provider, or hook.
Options:
--source <aur|local> Package source (default: aur)
--source <aur|local> Initial recipe source (default: local)
--local Shortcut for --source local
--aur <name> AUR package name when different from local directory
--no-sync For AUR packages, mark sync disabled after initial setup
--no-sync Keep the imported recipe manually maintained
--fast Put package in the fast release ring
--release-ring <ring> Release ring (currently: fast)
--scaffold For local packages, create a starter PKGBUILD
@@ -32,9 +32,9 @@ Options:
-h, --help Show this help message
Examples:
$0 yay
$0 spotify --fast
$0 signal-desktop --no-sync
$0 yay --source aur
$0 spotify --source aur --fast
$0 signal-desktop --source aur --no-sync
$0 omarchy-zsh --local --scaffold
EOF
}
@@ -97,6 +97,10 @@ if [[ -z "$PACKAGE" ]]; then
usage
exit 1
fi
if [[ ! $PACKAGE =~ ^[a-zA-Z0-9@_+][a-zA-Z0-9@._+-]*$ ]]; then
print_error "Invalid package name: $PACKAGE"
exit 1
fi
case "$SOURCE" in
aur|local) ;;
@@ -112,6 +116,11 @@ PACKAGE_DIR="$PKGBUILDS_DIR/$PACKAGE"
OMARCHY_DIR="$PACKAGE_DIR/.omarchy"
METADATA_FILE="$OMARCHY_DIR/package.json"
if [[ "$SOURCE" == aur && -f "$PACKAGE_DIR/PKGBUILD" ]]; then
print_error "Refusing to replace the maintained recipe for $PACKAGE"
exit 1
fi
if [[ -f "$METADATA_FILE" && "$FORCE" != true ]]; then
print_error "Package metadata already exists: $METADATA_FILE"
print_info "Use --force to overwrite it"
@@ -146,18 +155,8 @@ jq -n "${jq_args[@]}" "$jq_filter" > "$METADATA_FILE"
print_success "Wrote $METADATA_FILE"
if [[ "$SOURCE" == "aur" ]]; then
if [[ "$SYNC" == "false" ]]; then
# Temporarily sync once, then restore sync=false so future automated syncs skip it.
tmpfile=$(mktemp)
jq 'del(.sync)' "$METADATA_FILE" > "$tmpfile"
mv "$tmpfile" "$METADATA_FILE"
"$BUILD_ROOT/bin/sync-aur" "$PACKAGE"
jq '. + {sync: false}' "$METADATA_FILE" > "$tmpfile"
mv "$tmpfile" "$METADATA_FILE"
print_info "AUR sync disabled for future runs"
else
"$BUILD_ROOT/bin/sync-aur" "$PACKAGE"
fi
"$BUILD_ROOT/bin/import-aur" "$PACKAGE"
else
if [[ "$SCAFFOLD" == true && ! -f "$PACKAGE_DIR/PKGBUILD" ]]; then
cat > "$PACKAGE_DIR/PKGBUILD" <<EOF
+20 -1
View File
@@ -238,6 +238,9 @@ PACKAGE_CACHE_DIR="$BUILD_ROOT/cache/pacman/$MIRROR/$ARCH"
mkdir -p "$PACKAGE_CACHE_DIR"
PLAN_DIR=$(mktemp -d "$SRC_DIR/build-plan.XXXXXX")
trap 'rm -rf "$PLAN_DIR"' EXIT
# Keep manifest directories host-owned so cleanup also works when Docker's
# builder uid differs from the caller (as on GitHub runners).
mkdir -p "$PLAN_DIR/artifacts"
# Rootful Docker writes as the image uid, so retain its existing permission
# workaround. Rootless Podman uses keep-id and must leave ownership/modes alone.
@@ -320,6 +323,20 @@ echo " Skipped: ${#SKIPPED_PACKAGES[@]} (up-to-date or excluded)"
echo " Failed: ${#FAILED_PACKAGES[@]}"
echo " Blocked: ${#BLOCKED_PACKAGES[@]}"
# The release caller supplies a fresh directory. A completed result
# distinguishes package failures from an interrupted/failed orchestrator;
# only artifacts belonging to fully successful builds may be published.
if [[ -n "${OMARCHY_BUILD_RESULT_DIR:-}" ]]; then
mkdir -p "$OMARCHY_BUILD_RESULT_DIR"
: > "$OMARCHY_BUILD_RESULT_DIR/artifacts"
for package in "${SUCCESSFUL_PACKAGES[@]}"; do
cat "$PLAN_DIR/artifacts/$package" >> "$OMARCHY_BUILD_RESULT_DIR/artifacts"
done
printf '%s\n' "${FAILED_PACKAGES[@]}" | sed '/^$/d' > "$OMARCHY_BUILD_RESULT_DIR/failed"
printf '%s\n' "${BLOCKED_PACKAGES[@]}" | sed '/^$/d' > "$OMARCHY_BUILD_RESULT_DIR/blocked"
touch "$OMARCHY_BUILD_RESULT_DIR/complete"
fi
if (( ${#FAILED_PACKAGES[@]} + ${#BLOCKED_PACKAGES[@]} )); then
if (( ${#FAILED_PACKAGES[@]} )); then
echo "Failed packages:"
@@ -330,7 +347,9 @@ if (( ${#FAILED_PACKAGES[@]} + ${#BLOCKED_PACKAGES[@]} )); then
printf ' - %s\n' "${BLOCKED_PACKAGES[@]}"
fi
print_warning "Some packages failed (see details above)"
exit 1
# Reserved for a completed run with unsuccessful packages. Other failures
# must not let release publish arbitrary files left in the workspace.
exit 2
fi
print_success "Build completed successfully!"
+2 -2
View File
@@ -172,8 +172,8 @@ check_package() {
# it because that exact filename is already published with different bytes.
# If the artifact for this version already exists in the channel, there is
# nothing to build regardless of which direction the versions differ.
if compgen -G "$REPO_ROOT/$mirror/$ARCH/${pkg}-${pkgbuild_version}-*.pkg.tar."[!s]* >/dev/null 2>&1; then
print_warning "$pkg $pkgbuild_version is already published — this checkout is behind the channel; not queueing"
if package_version_is_published "$REPO_ROOT/$mirror/$ARCH" "$pkg" "$pkgbuild_version" "$ARCH"; then
print_warning "$pkg $pkgbuild_version is already published; not queueing"
return 1
fi
Executable
+62
View File
@@ -0,0 +1,62 @@
#!/bin/bash
# Internal initial-recipe import used by add-package. Maintained recipes are
# never replaced; subsequent releases go through sync-upstream.
set -euo pipefail
BUILD_ROOT=$(realpath "${BASH_SOURCE[0]%/*}/..")
source "$BUILD_ROOT/helpers/message-helpers.sh"
source "$BUILD_ROOT/helpers/paths.sh"
if [[ ${1:-} == --help || ${1:-} == -h ]]; then
echo "Usage: bin/add-package <package> --source aur [--aur <upstream-name>]"
exit 0
fi
if [[ $# != 1 || ! $1 =~ ^[a-zA-Z0-9@_+][a-zA-Z0-9@._+-]*$ ]]; then
print_error "Use bin/add-package <package> --source aur for an initial import"
exit 1
fi
package=$1
package_dir="$PKGBUILDS_DIR/$package"
metadata="$package_dir/.omarchy/package.json"
if [[ -f "$package_dir/PKGBUILD" ]]; then
print_error "Refusing to replace the maintained recipe for $package"
exit 1
fi
if [[ ! -f "$metadata" ]] || [[ $(jq -r .source "$metadata") != aur ]]; then
print_error "Initial import requires metadata created by bin/add-package --source aur"
exit 1
fi
aur_package=$(jq -r --arg name "$package" '.aur // $name' "$metadata")
if [[ ! $aur_package =~ ^[a-zA-Z0-9@_+][a-zA-Z0-9@._+-]*$ ]]; then
print_error "Invalid AUR package name"
exit 1
fi
# Refuse unrelated package files: an import only starts from .omarchy metadata.
shopt -s dotglob nullglob
for item in "$package_dir"/*; do
if [[ ${item##*/} != .omarchy ]]; then
print_error "Initial import needs an empty package directory: $package_dir"
exit 1
fi
done
work=$(mktemp -d "$PKGBUILDS_DIR/.import-aur.XXXXXX")
trap 'rm -rf "$work"' EXIT
print_info "Importing $package from AUR package $aur_package..."
git clone --quiet "https://aur.archlinux.org/${aur_package}.git" "$work/recipe"
[[ -f "$work/recipe/PKGBUILD" ]] || { print_error "AUR package has no PKGBUILD"; exit 1; }
commit=$(git -C "$work/recipe" rev-parse HEAD)
rm -rf "$work/recipe/.git" "$work/recipe/.omarchy"
rm -f "$work/recipe/.SRCINFO" "$work/recipe/.gitignore"
cp -a "$package_dir/.omarchy" "$work/recipe/.omarchy"
jq --arg name "$aur_package" --arg commit "$commit" '
.source = "local" | .origin = {aur: $name, commit: $commit}
| del(.aur, .upstream_commit)
' "$metadata" > "$work/recipe/.omarchy/package.json"
# Stage on the same filesystem, restoring the metadata directory on failure.
mv "$package_dir" "$work/original"
if ! mv "$work/recipe" "$package_dir"; then
mv "$work/original" "$package_dir"
exit 1
fi
print_success "Imported $package; review the recipe and configure its direct upstream watch"
+6 -6
View File
@@ -17,13 +17,13 @@ Usage: $0 <package> [OPTIONS]
Create a scratch workspace for inspecting an AUR-backed package.
The workspace contains:
upstream/ Raw AUR package at .omarchy/package.json upstream_commit, or HEAD
upstream/ Raw AUR package at the recorded origin.commit, or HEAD
patched/ Raw AUR package with Omarchy .omarchy patches/hooks/pkgrel applied
current/ Current checked-in package directory
Options:
--dir <path> Workspace directory (default: mktemp under /tmp)
--commit <sha> Use a specific AUR commit instead of upstream_commit
--commit <sha> Use a specific AUR commit instead of origin.commit
-h, --help Show this help message
Examples:
@@ -75,13 +75,13 @@ if [[ ! -f "$METADATA" ]]; then
exit 1
fi
if [[ "$(jq -r '.source // ""' "$METADATA")" != "aur" ]]; then
if [[ "$(jq -r '.origin.aur // .aur // (if .source == "aur" then "legacy" else "" end)' "$METADATA")" == "" ]]; then
print_error "package-worktree only supports AUR-backed packages"
exit 1
fi
AUR_PACKAGE=$(jq -r --arg package "$PACKAGE" '.aur // $package' "$METADATA")
UPSTREAM_COMMIT=${COMMIT_OVERRIDE:-$(jq -r '.upstream_commit // ""' "$METADATA")}
AUR_PACKAGE=$(jq -r --arg package "$PACKAGE" '.origin.aur // .aur // $package' "$METADATA")
UPSTREAM_COMMIT=${COMMIT_OVERRIDE:-$(jq -r '.origin.commit // .upstream_commit // ""' "$METADATA")}
if [[ -z "$DEST_DIR" ]]; then
DEST_DIR=$(mktemp -d "${TMPDIR:-/tmp}/omarchy-${PACKAGE}.XXXXXX")
@@ -224,7 +224,7 @@ print_info "AUR package: $AUR_PACKAGE"
if [[ -n "$UPSTREAM_COMMIT" ]]; then
print_info "Upstream commit: $UPSTREAM_COMMIT"
else
print_warning "No upstream_commit recorded; using AUR HEAD"
print_warning "No origin.commit recorded; using AUR HEAD"
fi
rm -rf "$UPSTREAM_DIR" "$PATCHED_DIR" "$CURRENT_DIR"
+56 -3
View File
@@ -138,11 +138,43 @@ if [[ "$DRY_RUN" == true ]]; then
else
print_info "Step 1/6: Building packages..."
fi
"$BUILD_ROOT/bin/build" "${BUILD_ARGS[@]}" || {
BUILD_RESULT_DIR=$(mktemp -d)
trap 'rm -rf "$BUILD_RESULT_DIR"' EXIT
build_status=0
OMARCHY_BUILD_RESULT_DIR="$BUILD_RESULT_DIR" "$BUILD_ROOT/bin/build" "${BUILD_ARGS[@]}" || build_status=$?
partial=false
if [[ "$build_status" == 2 && -f "$BUILD_RESULT_DIR/complete" && "$DRY_RUN" != true ]]; then
if [[ "${OMARCHY_DEFER_RUNTIME_DEPS:-false}" == true ]]; then
print_error "The deferred release pair must succeed together; nothing will be published"
notify_error "Release failed: Incomplete release pair" "$RELEASE_CONTEXT"
exit 1
fi
partial=true
while IFS= read -r file; do
[[ -f "$BUILD_OUTPUT_DIR/$file" ]] || {
print_error "Completed build artifact is missing: $file"
notify_error "Release failed: Missing completed artifact" "$RELEASE_CONTEXT"
exit 1
}
done < "$BUILD_RESULT_DIR/artifacts"
# Exclude partial split outputs or leftovers from failed builds. Moving
# them out of the flat publication directory keeps them available for
# diagnosis without handing them to sign/promote.
unpublished=""
for path in "$BUILD_OUTPUT_DIR"/*.pkg.tar.*; do
[[ -f "$path" ]] || continue
file=${path##*/}
if ! grep -Fxq -- "${file%.sig}" "$BUILD_RESULT_DIR/artifacts"; then
[[ -n "$unpublished" ]] || unpublished=$(mktemp -d "$BUILD_OUTPUT_DIR/.unpublished.XXXXXX")
mv -- "$path" "$unpublished/"
fi
done
print_warning "Some packages failed; publishing the completed packages before retrying the failures"
elif [[ "$build_status" != 0 ]]; then
print_error "Build failed"
notify_error "Release failed: Build step failed" "$RELEASE_CONTEXT"
exit 1
}
fi
if [[ "$DRY_RUN" == true ]]; then
echo ""
@@ -155,6 +187,12 @@ BUILT_COUNT=$(grep -c '' <<<"$BUILT_FILES")
[[ -z "$BUILT_FILES" ]] && BUILT_COUNT=0
print_info "Built $BUILT_COUNT package(s) this run"
if [[ "$partial" == true && "$BUILT_COUNT" == 0 ]]; then
print_error "No completed packages to publish"
notify_error "Release failed: No completed packages" "$RELEASE_CONTEXT"
exit 1
fi
# Step 2: Sign
echo ""
print_info "Step 2/6: Signing packages..."
@@ -213,7 +251,16 @@ if ((BUILT_COUNT > 0)); then
summary+="<br><br><strong>$BUILT_COUNT package(s) published:</strong>"
summary+="$(format_package_list_html "$BUILT_FILES")"
summary+="<br><br>Live at https://pkgs.omarchy.org/$MIRROR/$ARCH/"
notify_success "Release published: $MIRROR" "$summary"
if [[ "$partial" == true ]]; then
failed=$(tr '\n' ' ' < "$BUILD_RESULT_DIR/failed" | basecamp_html_escape)
blocked=$(tr '\n' ' ' < "$BUILD_RESULT_DIR/blocked" | basecamp_html_escape)
[[ -z "$failed" ]] || summary+="<br><br>Failed: $failed"
[[ -z "$blocked" ]] || summary+="<br>Blocked by failed dependencies: $blocked"
summary+="<br>Published packages will be skipped on retry; failed packages remain queued."
notify_info "Partial release published: $MIRROR" "$summary"
else
notify_success "Release published: $MIRROR" "$summary"
fi
else
# Rare by construction: a release only runs when the version check queued
# work, so publishing nothing means the check and the builder disagreed
@@ -228,4 +275,10 @@ else
fi
echo ""
if [[ "$partial" == true ]]; then
print_warning "Completed packages published; unsuccessful packages remain for retry"
# Preserve the scheduled queue and failure backoff. The next version
# check/build compares against the updated repository and skips successes.
exit 1
fi
print_success "Release workflow completed successfully!"
-434
View File
@@ -1,434 +0,0 @@
#!/bin/bash
set -euo pipefail
BUILD_ROOT=$(realpath "${BASH_SOURCE[0]%/*}/..")
source "$BUILD_ROOT/helpers/message-helpers.sh"
source "$BUILD_ROOT/helpers/paths.sh"
source "$BUILD_ROOT/helpers/package-metadata.sh"
TEMP_DIR=$(mktemp -d)
trap 'rm -rf "$TEMP_DIR"' EXIT
SPECIFIC_PACKAGES=()
usage() {
cat <<EOF
Usage: $0 [PACKAGE...]
Sync AUR-backed packages into pkgbuilds/<package>/.
Package selection is driven by pkgbuilds/<package>/.omarchy/package.json:
{ "source": "aur" } # synced from matching AUR package name
{ "source": "aur", "aur": "yaru" } # synced from different AUR package name
{ "source": "aur", "sync": false } # AUR-origin, but not auto-synced
Arguments:
PACKAGE One or more package names to sync (optional)
Examples:
$0 # Sync all AUR packages with sync enabled
$0 yay cursor-bin # Sync specific packages
EOF
}
while [[ $# -gt 0 ]]; do
case "$1" in
-h|--help)
usage
exit 0
;;
--tier)
print_error "--tier is no longer supported; package metadata controls sync behavior"
exit 1
;;
--*)
print_error "Unknown option: $1"
exit 1
;;
*)
SPECIFIC_PACKAGES+=("$1")
shift
;;
esac
done
print_header "AUR Package Sync"
mkdir -p "$PKGBUILDS_DIR"
SYNCED=0
SKIPPED=0
FAILED=0
SYNCED_PACKAGES=()
SPECIFIC_MODE=false
get_pkgbuild_field() {
local package_dir="$1"
local field="$2"
local value=""
if [[ -f "$package_dir/PKGBUILD" ]]; then
value=$(grep -m1 "^${field}=" "$package_dir/PKGBUILD" | cut -d= -f2- | tr -d "\"'") || true
if [[ -n "$value" ]]; then
echo "$value"
return
fi
fi
if [[ -f "$package_dir/.SRCINFO" ]]; then
awk -F' = ' -v field="$field" '$1 ~ "^[[:space:]]*" field "$" { print $2; exit }' "$package_dir/.SRCINFO"
fi
}
set_pkgrel() {
local package_dir="$1"
local pkgrel="$2"
local pkgbuild="$package_dir/PKGBUILD"
if [[ -f "$pkgbuild" ]]; then
sed -i "s/^pkgrel=.*/pkgrel=$pkgrel/" "$pkgbuild"
fi
}
display_package_name() {
local package_dir="$1"
local name
name=$(basename "$package_dir")
echo "${name%.work}"
}
remove_aur_only_files() {
local package_dir="$1"
rm -f "$package_dir/.SRCINFO" "$package_dir/.gitignore"
}
copy_aur_contents() {
local aur_dir="$1"
local target_dir="$2"
mkdir -p "$target_dir"
shopt -s dotglob nullglob
local item base
for item in "$aur_dir"/*; do
base=$(basename "$item")
[[ "$base" == ".git" ]] && continue
cp -a "$item" "$target_dir/"
done
shopt -u dotglob nullglob
}
commit_synced_worktree() {
local work_dir="$1"
local target_dir="$2"
local parent base staged_dir backup_root backup_dir
parent=$(dirname "$target_dir")
base=$(basename "$target_dir")
staged_dir=$(mktemp -d "$parent/.${base}.staged.XXXXXX")
backup_root=$(mktemp -d "$parent/.${base}.backup.XXXXXX")
backup_dir="$backup_root/$base"
# Copy to the package filesystem before swapping. This keeps the original
# package directory intact if copying from /tmp fails or is interrupted.
if ! cp -a "$work_dir/." "$staged_dir/"; then
print_error "Failed to stage synced package for $base"
rm -rf "$staged_dir" "$backup_root"
return 1
fi
if [[ -e "$target_dir" ]]; then
if ! mv "$target_dir" "$backup_dir"; then
print_error "Failed to back up existing package directory: $target_dir"
rm -rf "$staged_dir" "$backup_root"
return 1
fi
fi
if ! mv "$staged_dir" "$target_dir"; then
print_error "Failed to install synced package directory: $target_dir"
if [[ -e "$backup_dir" ]]; then
mv "$backup_dir" "$target_dir" || true
fi
rm -rf "$staged_dir" "$backup_root"
return 1
fi
rm -rf "$backup_root" "$work_dir"
}
set_upstream_commit() {
local package_dir="$1"
local commit="$2"
local metadata="$package_dir/.omarchy/package.json"
local tmpfile
tmpfile=$(mktemp)
jq --arg commit "$commit" '.upstream_commit = $commit' "$metadata" > "$tmpfile"
mv "$tmpfile" "$metadata"
}
apply_omarchy_patches() {
local package_dir="$1"
local patches_dir="$package_dir/.omarchy/patches"
local applied=false
[[ -d "$patches_dir" ]] || return 1
shopt -s nullglob
local patch_files=("$patches_dir"/*.patch)
local patch_dir_files=("$patches_dir"/*)
shopt -u nullglob
if [[ ${#patch_files[@]} -eq 0 ]]; then
if [[ ${#patch_dir_files[@]} -gt 0 ]]; then
print_warning "No .patch files found in $patches_dir"
fi
return 1
fi
print_info "Applying Omarchy patches for $(display_package_name "$package_dir")..."
local patch_file
for patch_file in "${patch_files[@]}"; do
print_info " $(basename "$patch_file")"
if ! (cd "$package_dir" && patch -p1 --forward --batch --no-backup-if-mismatch < "$patch_file"); then
print_error "Failed to apply patch: $patch_file"
return 2
fi
applied=true
done
[[ "$applied" == true ]]
}
run_omarchy_post_sync_hook() {
local package_dir="$1"
local package="$2"
local aur_package="$3"
local aur_pkgrel="$4"
local hook="$package_dir/.omarchy/post-sync.sh"
[[ -f "$hook" ]] || return 1
print_info "Running Omarchy post-sync hook for $(display_package_name "$package_dir")..."
if ! (
cd "$package_dir"
PACKAGE_NAME="$package" \
AUR_PACKAGE_NAME="$aur_package" \
AUR_PKGREL="$aur_pkgrel" \
bash ".omarchy/post-sync.sh"
); then
print_error "Failed to run post-sync hook: $hook"
return 2
fi
return 0
}
apply_pkgrel_suffix_if_customized() {
local package_dir="$1"
local aur_pkgrel="$2"
[[ -n "$aur_pkgrel" ]] || return 0
print_info "Applying Omarchy pkgrel suffix for $(display_package_name "$package_dir"): pkgrel=$aur_pkgrel.1"
set_pkgrel "$package_dir" "$aur_pkgrel.1"
}
apply_pkgrel_override() {
local package_dir="$1"
local aur_pkgrel="$2"
local previous_pkgver="$3"
local metadata="$package_dir/.omarchy/package.json"
local pkgbuild="$package_dir/PKGBUILD"
[[ -f "$metadata" ]] || return 1
jq -e 'has("pkgrel")' "$metadata" >/dev/null || return 1
local current_pkgver suffix offset base rel tmpfile
# Read through the same accessor that produced previous_pkgver. Parsing it a
# second time here let a quoted pkgver= compare unequal to itself, which threw
# away the pkgrel metadata of an unchanged package on every sync.
current_pkgver=$(get_pkgbuild_field "$package_dir" pkgver)
if [[ -n "$previous_pkgver" && "$current_pkgver" != "$previous_pkgver" ]]; then
print_info "Removing stale pkgrel metadata for $(display_package_name "$package_dir") (pkgver changed: $previous_pkgver -> $current_pkgver)"
tmpfile=$(mktemp)
jq 'del(.pkgrel)' "$metadata" > "$tmpfile"
mv "$tmpfile" "$metadata"
return 1
fi
suffix=$(jq -r '.pkgrel.suffix // 1' "$metadata")
offset=$(jq -r '.pkgrel.offset // 0' "$metadata")
if [[ ! "$offset" =~ ^[0-9]+$ || ! "$aur_pkgrel" =~ ^[0-9]+$ ]]; then
print_error "pkgrel offset requires numeric AUR pkgrel for $(display_package_name "$package_dir")"
return 2
fi
base=$((aur_pkgrel + offset))
rel="$base.$suffix"
print_info "Applying pkgrel suffix for $(display_package_name "$package_dir"): AUR pkgrel=$aur_pkgrel, offset=$offset, suffix=$suffix -> pkgrel=$rel"
set_pkgrel "$package_dir" "$rel"
return 0
}
clone_aur_package() {
local aur_package="$1"
local dest="$2"
local clone_log="$TEMP_DIR/clone.log"
local attempt
for attempt in 1 2 3; do
rm -rf "$dest"
if git clone "https://aur.archlinux.org/${aur_package}.git" "$dest" >"$clone_log" 2>&1; then
return 0
fi
if [[ $attempt -lt 3 ]]; then
print_warning "Clone of $aur_package failed (attempt $attempt/3), retrying in 10s..."
sleep 10
fi
done
print_warning "Failed to clone $aur_package after 3 attempts: $(tail -n 1 "$clone_log")"
return 1
}
sync_package() {
local package="$1"
local package_dir="$PKGBUILDS_DIR/$package"
local metadata="$package_dir/.omarchy/package.json"
if [[ ! -f "$metadata" ]]; then
if [[ "$SPECIFIC_MODE" == true ]]; then
print_error "Package $package is missing .omarchy/package.json"
((++FAILED))
else
print_warning "Skipping $package: missing .omarchy/package.json"
((++SKIPPED))
fi
return 0
fi
if [[ "$(jq -r '.source // ""' "$metadata")" != "aur" ]]; then
print_info "Skipping $package: source is not AUR"
((++SKIPPED))
return 0
fi
if [[ "$(jq -r 'if has("sync") then .sync else true end' "$metadata")" == "false" ]]; then
print_info "Skipping $package: AUR sync disabled"
((++SKIPPED))
return 0
fi
local aur_package
aur_package=$(jq -r --arg package "$package" '.aur // $package' "$metadata")
if [[ "$aur_package" == "$package" ]]; then
print_info "Syncing $package from AUR..."
else
print_info "Syncing $package from AUR package $aur_package..."
fi
cd "$TEMP_DIR"
if ! clone_aur_package "$aur_package" "$TEMP_DIR/$aur_package"; then
((++FAILED))
return 0
fi
if [[ ! -f "$TEMP_DIR/$aur_package/PKGBUILD" ]]; then
print_warning "AUR repository for $aur_package is empty (package does not exist in AUR)"
((++FAILED))
return 0
fi
local aur_dir="$TEMP_DIR/$aur_package"
local work_dir="$TEMP_DIR/${package}.work"
local aur_pkgrel previous_pkgver upstream_commit
aur_pkgrel=$(get_pkgbuild_field "$aur_dir" pkgrel)
previous_pkgver=$(get_pkgbuild_field "$package_dir" pkgver || true)
upstream_commit=$(git -C "$aur_dir" rev-parse HEAD)
rm -rf "$work_dir"
copy_aur_contents "$aur_dir" "$work_dir"
rm -rf "$work_dir/.omarchy"
cp -a "$package_dir/.omarchy" "$work_dir/.omarchy"
local customized=false
if apply_omarchy_patches "$work_dir"; then
customized=true
else
local patch_status=$?
if [[ $patch_status -eq 2 ]]; then
((++FAILED))
return 0
fi
fi
if run_omarchy_post_sync_hook "$work_dir" "$package" "$aur_package" "$aur_pkgrel"; then
customized=true
else
local hook_status=$?
if [[ $hook_status -eq 2 ]]; then
((++FAILED))
return 0
fi
fi
local pkgrel_overridden=false
set +e
apply_pkgrel_override "$work_dir" "$aur_pkgrel" "$previous_pkgver"
local pkgrel_status=$?
set -e
case "$pkgrel_status" in
0) pkgrel_overridden=true ;;
1) ;;
*) ((++FAILED)); return 0 ;;
esac
if [[ "$customized" == true && "$pkgrel_overridden" == false ]]; then
apply_pkgrel_suffix_if_customized "$work_dir" "$aur_pkgrel"
fi
remove_aur_only_files "$work_dir"
set_upstream_commit "$work_dir" "$upstream_commit"
if ! commit_synced_worktree "$work_dir" "$package_dir"; then
((++FAILED))
return 0
fi
SYNCED_PACKAGES+=("$package")
((++SYNCED))
}
if [[ ${#SPECIFIC_PACKAGES[@]} -gt 0 ]]; then
SPECIFIC_MODE=true
for package in "${SPECIFIC_PACKAGES[@]}"; do
sync_package "$package"
done
else
while IFS= read -r package; do
sync_package "$package"
done < <(packages_for_aur_sync)
fi
echo ""
if [[ $FAILED -gt 0 ]]; then
print_error "Sync completed with failures"
else
print_success "Sync complete!"
fi
echo " Target: $PKGBUILDS_DIR"
echo " Synced: $SYNCED"
echo " Skipped: $SKIPPED"
echo " Failed: $FAILED"
if [[ $FAILED -gt 0 ]]; then
exit 1
fi
+25
View File
@@ -9,6 +9,7 @@ source "$BUILD_ROOT/helpers/upstream-github.sh"
TEMP_DIR=$(mktemp -d)
trap 'rm -rf "$TEMP_DIR"' EXIT
export UPSTREAM_CACHE_DIR="$TEMP_DIR/watch-cache"
SPECIFIC_PACKAGES=()
@@ -332,6 +333,12 @@ sync_package() {
return 0
fi
if jq -e '.sync == false' "$package_dir/.omarchy/package.json" >/dev/null 2>&1; then
print_info "Skipping $package: upstream updates held by sync=false"
((++SKIPPED))
return 0
fi
local provider has_upstream=false
provider=$(package_upstream_provider "$package_dir")
if package_has_upstream_provider "$package_dir"; then
@@ -372,6 +379,21 @@ sync_package() {
print_info "Checking $package for upstream releases..."
if [[ "$provider" == watch ]]; then
local result
if ! result=$(python3 "$BUILD_ROOT/helpers/upstream-watch.py" sync "$package_dir" --min-age "$min_age"); then
print_error "Upstream watch failed for $package"
((++FAILED))
elif [[ $(jq -r .status <<<"$result") == updated ]]; then
print_success " $(jq -r '.before + " -> " + .after' <<<"$result")"
((++UPDATED))
else
print_info " $(jq -r '.reason' <<<"$result")"
((++SKIPPED))
fi
return 0
fi
local release release_status=0
if [[ -n "$provider" ]]; then
case "$provider" in
@@ -757,6 +779,9 @@ EOF
mkdir -p "$one_root"
cp -a "$BUILD_ROOT/pkgbuilds/1password" "$one_root/1password"
one_dir="$one_root/1password"
# Keep the real recipe shape, but make the fixture's starting version stable.
# Otherwise a routine package update can outrun the mocked release below.
sed -i -e 's/^pkgver=.*/pkgver=8.12.34/' -e 's/^pkgrel=.*/pkgrel=2/' "$one_dir/PKGBUILD"
debian_fetch_packages() {
printf 'Package: 1password\nVersion: %s\n' "$one_version"
}
+15 -2
View File
@@ -411,6 +411,11 @@ build_package() {
ln -sf omarchy-build.db.tar.zst omarchy-build.db || return 1
fi
# A release may publish successful builds even when a peer fails. Record
# outputs only after this package's entire split build has completed.
mkdir -p "$BUILD_PLAN_DIR/artifacts" || return 1
printf '%s\n' "${new_pkgs[@]}" > "$BUILD_PLAN_DIR/artifacts/$pkg" || return 1
echo " Successfully built $pkg"
return 0
else
@@ -526,9 +531,17 @@ check_needs_build() {
if [[ "$local_version" == "$pkgbuild_version" ]]; then
return 1 # Already up to date
else
return 0 # Needs building
fi
# Match check-versions: a retained archive is already published even when
# the DB now indexes a newer release (for example, 4.0.4rc1 vs 4.0.3).
# Rebuilding it would produce different bytes under an immutable filename.
if package_version_is_published "$FINAL_OUTPUT_DIR" "$pkg" "$pkgbuild_version" "$ARCH"; then
echo " + $pkg $pkgbuild_version - archive already published; skipping rebuild"
return 1
fi
return 0 # Needs building
}
# Collect packages that should be built for the selected mirror
+178
View File
@@ -0,0 +1,178 @@
# Direct upstream watches
Omarchy owns the recipes in `pkgbuilds/`. `bin/sync-upstream` discovers new
releases directly from project/vendor feeds and updates versions, declared
release variables, and the source checksums already used by the recipe. It never
imports upstream PKGBUILDs or runs downloaded build scripts. Architecture support,
root install hooks, dependencies, and build functions remain ours to maintain.
`upstream.watch` complements the existing declarative providers and custom hooks:
```json
{
"source": "local",
"upstream": {
"watch": {
"github": "abenz1267/walker",
"pattern": "v(?P<version>[0-9]+(?:\\.[0-9]+)*)"
}
}
}
```
## Watch fields
Choose exactly one provider: `github` (owner/repository), `git_tags` (repository
HTTPS URL), `git_branch` (repository URL plus explicit `branch`), `npm` or `pypi`
(package name), `debian` (Packages index plus exact `package`), `json` (URL plus
version `path`), `regex` (text URL), `redirect` (final HTTPS download URL), or
`archive` (inspect archive metadata without extracting/executing code).
Tag, text, redirect and archive watches use an explicit `pattern` with a named
`version` capture. Tag patterns match the entire tag. `version` optionally formats
those captures into an Arch pkgver; e.g. Sublime uses `4.{version}`. JSON feeds can
expose additional capture values through `fields`, a name-to-JSON-path map.
`variables` maps recipe scalars such as `_commit` or `_build` to capture templates.
Only explicitly declared underscore-prefixed variables can change. GitHub
`{commit}` resolves the selected tag, not a moving target_commitish branch.
`submodules` can map a recipe variable to a gitlink in the selected GitHub tag;
RustDesk uses this for hbb_common. Downloaded repository code is never evaluated.
For upstreams that rebuild a release, declare a numeric `revision` template and
its `revision_variable`. With unchanged pkgver, only an increasing revision can
advance that variable, and the downstream pkgrel increments instead of resetting.
Cursor CLI uses `sequence` to preserve its date/counter/hash version convention
when the vendor publishes a second hash on the same day. A new pkgver resets
pkgrel to 1, but the complete epoch:pkgver-pkgrel must still increase.
GitHub releases exclude drafts and prereleases unless `allow_prerelease` is true.
Existing `min_release_age` policies apply: a feed without a verifiable publication
time cannot bypass a configured hold. Git branch watches derive a commit count
and date from the actual branch history and write an immutable source pin.
Checksums retain their algorithms (SHA256, SHA512, BLAKE2, etc.) and source order.
Changed git sources are hashed with makepkg's git-archive convention. Unchanged
sources retain their hashes; `mutable_sources` explicitly names entries such as
`source:0` that must be fetched again for a new version despite a stable URL.
Existing `SKIP` entries remain unchanged (including signed metadata verified by
the recipe); new skips are never introduced. Changed URLs are still fetched.
A matching GitHub release asset SHA256 digest avoids downloading large assets.
Missing architecture artifacts or malformed metadata fail the package atomically.
Every declared architecture must read back the same release and checksum values.
Archive watches use `member` to select a text member, or `filenames: true` to read
versions from archive member names. Debian archives are read through their control
metadata. `unescape_json` handles JSON strings embedded in a vendor's HTML page.
## Maintenance and validation
Running watches locally requires Python 3.11+, Bash, curl, git, jq, Arch's
`vercmp`, and `bsdtar`. CI installs these in its Arch container.
- Edit packaging and architecture changes directly in PKGBUILD. The old AUR
overlays have been folded into these recipes and removed.
- Keep source-code patches and install hooks checked in as ordinary package files.
- Bump pkgrel when changing a recipe at the same version. Removing a dotted AUR
suffix must never lower the complete version.
- Add a watch with each new package. `bin/add-package --source aur` is a one-time
import; it records historical `origin` metadata and leaves an owned recipe.
- `python helpers/upstream-watch.py check pkgbuilds/NAME` checks release discovery
without rewriting the recipe. `bin/sync-upstream NAME` performs the update.
- `python tests/upstream-watch.py` tests update atomicity, architecture coverage,
version ordering, source hashes and hostile metadata using offline fixtures.
The scheduled workflow continues reviewing completed updates if another package
fails. The failing recipe stays unchanged and the run still reports failure.
## Migrated package watches
68 active AUR packages now use direct watches. The nine previously disabled
packages retain manual maintenance holds. Historical AUR provenance is recorded
in `origin` and has no effect on release selection.
| Package | Provider | Upstream |
|---|---|---|
| `1password-beta` | debian | [https://downloads.1password.com/linux/debian/amd64/dists/beta/main/binary-amd64/Packages](https://downloads.1password.com/linux/debian/amd64/dists/beta/main/binary-amd64/Packages) |
| `1password-cli` | json | [https://app-updates.agilebits.com/check/1/0/CLI2/en/0](https://app-updates.agilebits.com/check/1/0/CLI2/en/0) |
| `aether` | github | [omacom/aether](https://github.com/omacom/aether) |
| `asusctl` | git_tags | [https://github.com/OpenGamingCollective/asusctl.git](https://github.com/OpenGamingCollective/asusctl.git) |
| `basecamp-cli` | github | [basecamp/basecamp-cli](https://github.com/basecamp/basecamp-cli) |
| `bun-bin` | github | [oven-sh/bun](https://github.com/oven-sh/bun) |
| `claude-code` | regex | [https://downloads.claude.ai/claude-code-releases/latest](https://downloads.claude.ai/claude-code-releases/latest) |
| `cliamp` | github | [bjarneo/cliamp](https://github.com/bjarneo/cliamp) |
| `crush-bin` | github | [charmbracelet/crush](https://github.com/charmbracelet/crush) |
| `cursor-bin` | json | [https://www.cursor.com/api/download?platform=linux-x64&releaseTrack=stable](https://www.cursor.com/api/download?platform=linux-x64&releaseTrack=stable) |
| `cursor-cli` | regex | [https://cursor.com/install](https://cursor.com/install) |
| `dbxcli-bin` | github | [dropbox/dbxcli](https://github.com/dropbox/dbxcli) |
| `dropbox` | redirect | [https://www.dropbox.com/download?plat=lnx.x86_64](https://www.dropbox.com/download?plat=lnx.x86_64) |
| `dropbox-cli` | regex | [https://linux.dropbox.com/packages/](https://linux.dropbox.com/packages/) |
| `elephant` | github | [abenz1267/elephant](https://github.com/abenz1267/elephant) |
| `elephant-all` | github | [abenz1267/elephant](https://github.com/abenz1267/elephant) |
| `elephant-archlinuxpkgs` | github | [abenz1267/elephant](https://github.com/abenz1267/elephant) |
| `elephant-bluetooth` | github | [abenz1267/elephant](https://github.com/abenz1267/elephant) |
| `elephant-calc` | github | [abenz1267/elephant](https://github.com/abenz1267/elephant) |
| `elephant-clipboard` | github | [abenz1267/elephant](https://github.com/abenz1267/elephant) |
| `elephant-desktopapplications` | github | [abenz1267/elephant](https://github.com/abenz1267/elephant) |
| `elephant-files` | github | [abenz1267/elephant](https://github.com/abenz1267/elephant) |
| `elephant-menus` | github | [abenz1267/elephant](https://github.com/abenz1267/elephant) |
| `elephant-providerlist` | github | [abenz1267/elephant](https://github.com/abenz1267/elephant) |
| `elephant-runner` | github | [abenz1267/elephant](https://github.com/abenz1267/elephant) |
| `elephant-symbols` | github | [abenz1267/elephant](https://github.com/abenz1267/elephant) |
| `elephant-todo` | github | [abenz1267/elephant](https://github.com/abenz1267/elephant) |
| `elephant-unicode` | github | [abenz1267/elephant](https://github.com/abenz1267/elephant) |
| `elephant-websearch` | github | [abenz1267/elephant](https://github.com/abenz1267/elephant) |
| `heroic-games-launcher-bin` | github | [Heroic-Games-Launcher/HeroicGamesLauncher](https://github.com/Heroic-Games-Launcher/HeroicGamesLauncher) |
| `hyprshade` | pypi | [hyprshade](https://pypi.org/project/hyprshade/) |
| `lib32-nvidia-580xx-utils` | regex | [https://download.nvidia.com/XFree86/Linux-x86_64/](https://download.nvidia.com/XFree86/Linux-x86_64/) |
| `limine-mkinitcpio-hook` | git_tags | [https://gitlab.com/Zesko/limine-entry-tool.git](https://gitlab.com/Zesko/limine-entry-tool.git) |
| `limine-snapper-sync` | git_tags | [https://gitlab.com/Zesko/limine-snapper-sync.git](https://gitlab.com/Zesko/limine-snapper-sync.git) |
| `lmstudio-bin` | regex | [https://lmstudio.ai/download](https://lmstudio.ai/download) |
| `localsend` | github | [localsend/localsend](https://github.com/localsend/localsend) |
| `localsend-bin` | github | [localsend/localsend](https://github.com/localsend/localsend) |
| `macbook12-spi-driver-dkms` | git_branch | [https://github.com/marc-git/macbook12-spi-driver.git](https://github.com/marc-git/macbook12-spi-driver.git) |
| `makima-bin` | github | [cyber-sushi/makima](https://github.com/cyber-sushi/makima) |
| `minecraft-launcher` | archive | [https://launcher.mojang.com/download/Minecraft.deb](https://launcher.mojang.com/download/Minecraft.deb) |
| `nautilus-dropbox` | github | [dropbox/nautilus-dropbox](https://github.com/dropbox/nautilus-dropbox) |
| `nautilus-open-any-terminal` | git_tags | [https://github.com/Stunkymonkey/nautilus-open-any-terminal.git](https://github.com/Stunkymonkey/nautilus-open-any-terminal.git) |
| `nordvpn-bin` | debian | [https://repo.nordvpn.com/deb/nordvpn/debian/dists/stable/main/binary-amd64/Packages](https://repo.nordvpn.com/deb/nordvpn/debian/dists/stable/main/binary-amd64/Packages) |
| `nvidia-580xx-utils` | regex | [https://download.nvidia.com/XFree86/Linux-x86_64/](https://download.nvidia.com/XFree86/Linux-x86_64/) |
| `omarchy-chromium-bin` | github | [omacom/omarchy-chromium](https://github.com/omacom/omarchy-chromium) |
| `omarchy-emacs` | git_tags | [https://github.com/scottjones/omarchy-emacs.git](https://github.com/scottjones/omarchy-emacs.git) |
| `omazed` | git_tags | [https://github.com/aps6/omazed.git](https://github.com/aps6/omazed.git) |
| `once-bin` | github | [basecamp/once](https://github.com/basecamp/once) |
| `openai-codex-bin` | github | [openai/codex](https://github.com/openai/codex) |
| `python-mediapipe` | github | [google-ai-edge/mediapipe](https://github.com/google-ai-edge/mediapipe) |
| `python-sounddevice` | pypi | [sounddevice](https://pypi.org/project/sounddevice/) |
| `python-terminaltexteffects` | pypi | [terminaltexteffects](https://pypi.org/project/terminaltexteffects/) |
| `rustdesk` | github | [rustdesk/rustdesk](https://github.com/rustdesk/rustdesk) |
| `spotify` | debian | [https://repository.spotify.com/dists/testing/non-free/binary-amd64/Packages](https://repository.spotify.com/dists/testing/non-free/binary-amd64/Packages) |
| `sublime-text-4` | json | [https://www.sublimetext.com/updates/4/stable_update_check](https://www.sublimetext.com/updates/4/stable_update_check) |
| `sunshine` | github | [LizardByte/Sunshine](https://github.com/LizardByte/Sunshine) |
| `ttf-ia-writer` | git_branch | [https://github.com/iaolo/iA-Fonts.git](https://github.com/iaolo/iA-Fonts.git) |
| `tuxedo-drivers-nocompatcheck-dkms` | git_tags | [https://gitlab.com/kronerm/tuxedo-drivers-nocompatcheck.git](https://gitlab.com/kronerm/tuxedo-drivers-nocompatcheck.git) |
| `typora` | debian | [https://downloads.typora.io/linux/Packages](https://downloads.typora.io/linux/Packages) |
| `ufw-docker` | git_tags | [https://github.com/chaifeng/ufw-docker.git](https://github.com/chaifeng/ufw-docker.git) |
| `vi` | regex | [https://sources.archlinux.org/other/vi/](https://sources.archlinux.org/other/vi/) |
| `visual-studio-code-bin` | json | [https://update.code.visualstudio.com/api/update/linux-deb-x64/stable/latest](https://update.code.visualstudio.com/api/update/linux-deb-x64/stable/latest) |
| `walker` | github | [abenz1267/walker](https://github.com/abenz1267/walker) |
| `xdg-terminal-exec` | git_tags | [https://gitlab.freedesktop.org/Vladimir-csp/xdg-terminal-exec.git](https://gitlab.freedesktop.org/Vladimir-csp/xdg-terminal-exec.git) |
| `xpadneo-dkms` | github | [atar-axis/xpadneo](https://github.com/atar-axis/xpadneo) |
| `yaru-icon-theme` | git_tags | [https://github.com/ubuntu/yaru.git](https://github.com/ubuntu/yaru.git) |
| `yay` | github | [Jguer/yay](https://github.com/Jguer/yay) |
| `yt6801-dkms` | archive | [https://www.motor-comm.com/Cn/Skippower/downloadFile.html?id=1817](https://www.motor-comm.com/Cn/Skippower/downloadFile.html?id=1817) |
## Existing manual holds
`grok-bot`, `libfprint-git`, `libretro-cap32-git`, `libretro-database-git`, `libretro-fbneo-git`, `libretro-uae-git`, `libretro-vice-git`, `quickshell-git`, `supergfxctl`.
These packages were already excluded from automatic AUR updates. The migration preserves that policy.
## Package-specific boundaries
- NVIDIA watches remain on the 580 driver branch.
- Hardware-specific packages keep their declared architectures; this migration does not invent ARM binaries for x86-only upstreams.
- iA Duospace was deleted upstream. Its four legacy font files retain their original immutable pin while the other families track the current repository.
- RustDesk reads hbb_common from the release gitlink; its existing build-time dependency/toolchain checks remain in force.
- Spotify uses HTTPS and retains its signed Release/Packages verification.
- Source and build compatibility still need review when upstream code changes. Direct watches remove AUR recipe churn, not the need to maintain packaging.
+27 -12
View File
@@ -3,15 +3,13 @@
# Expects package directories in $PKGBUILDS_DIR, each with:
# .omarchy/package.json
#
# Minimal schema:
# { "source": "aur" }
# { "source": "aur", "sync": false }
# { "source": "aur", "aur": "different-aur-name" }
# { "source": "aur", "release_ring": "fast" }
# { "source": "aur", "skip_build": true }
# { "source": "aur", "pkgrel": { "suffix": 1, "offset": 1 } }
# { "source": "aur", "rebuild_on": ["qt6-base"] }
# Minimal schema (legacy source:aur remains readable for initial imports):
# { "source": "local" }
# { "source": "local", "sync": false }
# { "source": "local", "release_ring": "fast" }
# { "source": "local", "skip_build": true }
# { "source": "local", "rebuild_on": ["qt6-base"] }
# { "source": "local", "upstream": { "watch": { "github": "owner/repo", "pattern": "v(?P<version>[0-9.]+)" } } }
# { "source": "local", "channels": ["edge"] }
# { "source": "local", "channels": ["edge", "rc", "stable"] }
# { "source": "local", "min_release_age": "24h" }
@@ -21,7 +19,7 @@
# { "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/import-aur records historical origin.aur and origin.commit;
# bin/sync-rebuilds writes rebuilt_against for packages declaring rebuild_on.
if [[ -z "${PKGBUILDS_DIR:-}" ]]; then
@@ -193,6 +191,18 @@ package_supports_arch() {
esac
}
# The channel DB indexes only its newest version, but older published archives
# remain immutable. Both the scheduler and build planner must skip an existing
# filename even when the checkout differs from the version currently indexed.
package_version_is_published() {
local repo_dir="$1" package="$2" version="$3" target="$4" path
for path in "$repo_dir/$package-$version-$target.pkg.tar."* \
"$repo_dir/$package-$version-any.pkg.tar."*; do
[[ -f "$path" && "$path" != *.sig ]] && return 0
done
return 1
}
# 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() {
@@ -523,8 +533,9 @@ validate_package_metadata() {
if has("upstream") | not then true
elif (.upstream | type) != "object" then false
else .upstream |
([has("github"), has("git_tags"), has("npm"), has("debian")] | map(select(.)) | length) == 1
and if has("github") then
([has("github"), has("git_tags"), has("npm"), has("debian"), has("watch")] | map(select(.)) | length) == 1
and if has("watch") then (.watch | type == "object")
elif 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)
@@ -550,10 +561,14 @@ validate_package_metadata() {
end
end
' "$metadata" >/dev/null; then
echo "invalid upstream for $(basename "$pkgdir"): configure exactly one valid github, git_tags, npm, or debian provider"
echo "invalid upstream for $(basename "$pkgdir"): configure exactly one valid github, git_tags, npm, debian, or watch provider"
return 1
fi
if jq -e '.upstream? | objects | has("watch")' "$metadata" >/dev/null; then
python3 "${BASH_SOURCE[0]%/*}/upstream-watch.py" validate "$pkgdir" || return 1
fi
pkgrel_type=$(jq -r 'if has("pkgrel") then .pkgrel | type else "missing" end' "$metadata")
case "$pkgrel_type" in
object|missing) ;;
+1 -1
View File
@@ -31,7 +31,7 @@ package_upstream_provider() {
metadata=$(metadata_file_for_dir "$pkgdir")
jq -r '
(.upstream? | objects) as $u
| [$u | keys[] | select(. == "github" or . == "git_tags" or . == "npm" or . == "debian")]
| [$u | keys[] | select(. == "github" or . == "git_tags" or . == "npm" or . == "debian" or . == "watch")]
| if length == 1 then .[0] else "" end
' "$metadata"
}
+566
View File
@@ -0,0 +1,566 @@
#!/usr/bin/env python3
"""Discover releases and update our own recipes; never import upstream build code.
The watch selects release metadata. Sources, supported architectures, integrity
algorithms and packaging behavior stay in the checked-in PKGBUILD. Only release
scalars and checksum arrays are replaced, atomically, after every source passes.
"""
import argparse
import datetime as dt
import gzip
import hashlib
import io
import json
import os
from pathlib import Path
import re
import subprocess
import sys
import tarfile
import tempfile
from urllib.parse import quote, urlsplit
import zipfile
PROVIDERS = {"github", "git_tags", "git_branch", "npm", "pypi", "debian", "json", "regex", "archive", "redirect"}
VERSION = re.compile(r"[A-Za-z0-9][A-Za-z0-9._+]*\Z")
SCALAR = re.compile(r"[A-Za-z0-9._+/-]+\Z")
SUM = re.compile(r"(md5|sha1|sha224|sha256|sha384|sha512|b2)sums(_[a-z0-9_]+)?\Z")
HASHES = {"b2": "blake2b"}
def run(args, **kwargs):
return subprocess.check_output(args, **kwargs)
def vercmp(a, b):
return int(run(["vercmp", a, b], text=True).strip())
def https(url):
parts = urlsplit(url)
if parts.scheme != "https" or not parts.hostname or parts.username or parts.password or re.search(r"[\s\x00-\x1f]", url):
raise ValueError(f"expected an HTTPS upstream URL: {url!r}")
return url
class Fetcher:
def __init__(self, cache):
self.cache = Path(cache)
self.cache.mkdir(parents=True, exist_ok=True)
def file(self, url):
https(url)
dest = self.cache / hashlib.sha256(url.encode()).hexdigest()
if not dest.exists():
scratch = dest.with_suffix(f".{os.getpid()}.tmp")
command = ["curl", "--proto", "=https", "--proto-redir", "=https", "-fsSL",
"--connect-timeout", "20", "--max-time", "300", "--retry", "2", "-o", str(scratch), url]
# Credentials only go to GitHub's API, never to release assets or vendors.
token = os.environ.get("UPSTREAM_GITHUB_TOKEN")
if token and urlsplit(url).hostname == "api.github.com":
command[1:1] = ["--config", "-"]
subprocess.run(command, input=f'header = "Authorization: Bearer {token}"\n', text=True, check=True)
else:
subprocess.run(command, check=True)
scratch.replace(dest)
return dest
def text(self, url):
data = self.file(url).read_bytes()
if data.startswith(b"\x1f\x8b"):
data = gzip.decompress(data)
return data.decode()
def json(self, url):
return json.loads(self.text(url))
def validate(watch):
if not isinstance(watch, dict) or len(PROVIDERS & watch.keys()) != 1:
raise ValueError("watch must select exactly one release provider")
provider = next(iter(PROVIDERS & watch.keys()))
allowed = PROVIDERS | {"pattern", "path", "package", "branch", "variables", "fields",
"submodules", "allow_prerelease", "unescape_json", "filenames",
"sequence", "version", "revision", "revision_variable",
"mutable_sources", "member", "dist_tag"}
if watch.keys() - allowed:
raise ValueError(f"unknown watch fields: {sorted(watch.keys() - allowed)}")
value = watch[provider]
if not isinstance(value, str) or not value:
raise ValueError(f"invalid watch.{provider}")
if provider == "github":
if not re.fullmatch(r"[\w.-]+/[\w.-]+", value):
raise ValueError("invalid GitHub repository")
elif provider in {"npm", "pypi"}:
if not re.fullmatch(r"(?:@[\w.-]+/)?[\w.-]+", value):
raise ValueError("invalid registry package")
else:
https(value)
if "pattern" in watch or provider in {"github", "git_tags", "regex", "archive", "redirect"}:
if not isinstance(watch.get("pattern"), str):
raise ValueError("watch needs an explicit release pattern")
pattern = re.compile(watch["pattern"])
if "version" not in pattern.groupindex:
raise ValueError("release pattern needs a named version group")
if provider == "json" and (not isinstance(watch.get("path"), str) or not watch["path"]):
raise ValueError("JSON watch needs a version path")
if provider == "debian":
name = watch.get("package", "")
if not isinstance(name, str) or not re.fullmatch(r"[a-z0-9][a-z0-9+.-]*", name):
raise ValueError("Debian watch needs an exact package name")
if provider == "git_branch":
branch = watch.get("branch", "")
if not isinstance(branch, str) or not branch or branch.startswith("-"):
raise ValueError("git branch watch needs an explicit branch")
run(["git", "check-ref-format", "refs/heads/" + branch])
for field in ("variables", "submodules", "fields"):
mapping = watch.get(field, {})
if not isinstance(mapping, dict):
raise ValueError(f"watch.{field} must be a string mapping")
for name, value in mapping.items():
pattern = r"[a-z][a-z0-9_]*" if field == "fields" else r"_[a-z][a-z0-9_]*"
if not re.fullmatch(pattern, name) or not isinstance(value, str) or not value:
raise ValueError(f"invalid watch.{field} mapping")
if "submodules" in watch and provider != "github":
raise ValueError("submodules require a GitHub watch")
if watch.get("variables", {}).keys() & watch.get("submodules", {}).keys():
raise ValueError("a release variable cannot also be a submodule")
for path in watch.get("submodules", {}).values():
if path.startswith("/") or any(part in {"", ".", ".."} for part in path.split("/")):
raise ValueError("submodule path must be relative to the release repository")
for field in ("allow_prerelease", "unescape_json", "filenames", "sequence"):
if field in watch and not isinstance(watch[field], bool):
raise ValueError(f"watch.{field} must be boolean")
for field in ("version", "revision", "member", "dist_tag"):
if field in watch and (not isinstance(watch[field], str) or not watch[field]):
raise ValueError(f"watch.{field} must be a string template")
if "revision_variable" in watch:
name = watch["revision_variable"]
if not isinstance(name, str) or name not in watch.get("variables", {}) or not watch.get("revision"):
raise ValueError("revision_variable requires a declared variable and revision template")
for field in ("mutable_sources",):
entries = watch.get(field, [])
if not isinstance(entries, list) or any(not isinstance(v, str) or not re.fullmatch(r"source(?:_[a-z0-9_]+)?:[0-9]+", v) for v in entries):
raise ValueError(f"watch.{field} must name source-array:index entries")
return provider
def json_path(data, path):
for key in path.split("."):
data = data[int(key)] if isinstance(data, list) else data[key]
return data
def candidate(watch, values):
values = {k: str(v) for k, v in values.items() if v is not None}
version = watch.get("version", "{version}").format_map(values)
if not VERSION.fullmatch(version):
raise ValueError(f"unusable upstream version: {version!r}")
revision = watch.get("revision", "").format_map(values)
if revision and not re.fullmatch(r"[0-9]+", revision):
raise ValueError("upstream release revision must be numeric")
return {"pkgver": version, "values": values,
"published_at": values.get("published_at"),
"revision": revision}
def matches(watch, text, extra=None, full=False):
pattern = re.compile(watch["pattern"])
found = [pattern.fullmatch(text)] if full else pattern.finditer(text)
for match in found:
if match:
yield candidate(watch, {**(extra or {}), **match.groupdict()})
def discover(watch, fetch):
provider = validate(watch)
feed = watch[provider]
results = []
if provider == "github":
releases = fetch.json(f"https://api.github.com/repos/{feed}/releases?per_page=100")
if not isinstance(releases, list):
raise ValueError("GitHub did not return a release list")
for release in releases:
if release.get("draft") or (release.get("prerelease") and not watch.get("allow_prerelease")):
continue
for item in matches(watch, release["tag_name"], {"tag": release["tag_name"], "published_at": release["published_at"]}, full=True):
item["assets"] = release.get("assets", [])
results.append(item)
elif provider == "git_tags":
refs = run(["git", "ls-remote", "--tags", feed], text=True)
tags = {}
for line in refs.splitlines():
commit, ref = line.split()
tag = ref.removeprefix("refs/tags/")
if tag.endswith("^{}"):
tags[tag[:-3]] = commit
else:
tags.setdefault(tag, commit)
for tag, commit in tags.items():
results.extend(matches(watch, tag, {"tag": tag, "commit": commit}, full=True))
elif provider == "git_branch":
with tempfile.TemporaryDirectory(prefix="upstream-git-") as work:
subprocess.run(["git", "clone", "--quiet", "--bare", "--filter=blob:none", "--single-branch", "--branch", watch["branch"], feed, work], check=True)
commit = run(["git", "-C", work, "rev-parse", "HEAD"], text=True).strip()
count = run(["git", "-C", work, "rev-list", "--count", "HEAD"], text=True).strip()
date = run(["git", "-C", work, "show", "-s", "--format=%cs", "HEAD"], text=True).strip().replace("-", "")
timestamp = run(["git", "-C", work, "show", "-s", "--format=%cI", "HEAD"], text=True).strip()
results.append(candidate(watch, {"version": date, "date": date, "count": count, "commit": commit, "published_at": timestamp}))
elif provider == "npm":
data = fetch.json("https://registry.npmjs.org/" + quote(feed, safe=""))
version = data["dist-tags"][watch.get("dist_tag", "latest")]
results.append(candidate(watch, {"version": version, "published_at": data.get("time", {}).get(version)}))
elif provider == "pypi":
data = fetch.json(f"https://pypi.org/pypi/{feed}/json")
version = data["info"]["version"]
dates = [r["upload_time_iso_8601"] for r in data["releases"].get(version, []) if not r.get("yanked")]
if not dates:
raise ValueError("PyPI release has no unyanked files")
results.append(candidate(watch, {"version": version, "published_at": max(dates)}))
elif provider == "debian":
for stanza in re.split(r"\n\s*\n", fetch.text(feed).replace("\r", "")):
fields = dict(re.findall(r"^([A-Za-z0-9-]+): (.*)$", stanza, re.M))
if fields.get("Package") != watch["package"]:
continue
if "pattern" in watch:
results.extend(matches(watch, fields["Version"], full=True))
else:
results.append(candidate(watch, {"version": fields["Version"]}))
elif provider == "json":
data = fetch.json(feed)
values = {"version": json_path(data, watch["path"])}
values.update({name: json_path(data, path) for name, path in watch.get("fields", {}).items()})
results.append(candidate(watch, values))
elif provider == "redirect":
final_url = run(["curl", "--proto", "=https", "--proto-redir", "=https", "-fsSLI", "--max-time", "60", "-o", "/dev/null", "-w", "%{url_effective}", feed], text=True)
results.extend(matches(watch, final_url))
elif provider == "regex":
text = fetch.text(feed)
if watch.get("unescape_json"):
text = text.replace('\\"', '"')
results.extend(matches(watch, text))
elif provider == "archive":
file = fetch.file(feed)
if zipfile.is_zipfile(file):
with zipfile.ZipFile(file) as archive:
names = archive.namelist()
if watch.get("filenames"):
results.extend(matches(watch, "\n".join(names)))
for name in ([] if watch.get("filenames") else names):
if re.fullmatch(watch.get("member", ".*"), name):
results.extend(matches(watch, archive.read(name).decode()))
elif file.read_bytes()[:8] == b"!<arch>\n":
names = run(["bsdtar", "-tf", str(file)], text=True).splitlines()
controls = [name for name in names if name.startswith("control.tar")]
if len(controls) != 1:
raise ValueError("deb does not contain exactly one control archive")
data = run(["bsdtar", "-xOf", str(file), controls[0]])
with tarfile.open(fileobj=io.BytesIO(data)) as archive:
members = [m for m in archive if m.name.removeprefix("./") == "control"]
if len(members) != 1:
raise ValueError("deb control file is missing or ambiguous")
results.extend(matches(watch, archive.extractfile(members[0]).read().decode()))
else:
with tarfile.open(file) as archive:
for member in archive:
if member.isfile() and re.fullmatch(watch.get("member", ".*"), member.name):
results.extend(matches(watch, archive.extractfile(member).read().decode()))
if not results:
raise ValueError(f"no matching releases in {feed}")
return results
def select_release(releases, min_age=0, now=None, bypass=False):
now = now or dt.datetime.now(dt.timezone.utc)
best = None
for release in releases:
if min_age and not bypass:
value = release.get("published_at")
if not value or not re.fullmatch(r"\d{4}-\d\d-\d\dT\d\d:\d\d:\d\d(?:\.\d+)?(?:Z|[+-]\d\d:?\d\d)", value):
raise ValueError("release age cannot be established")
if (now - dt.datetime.fromisoformat(value.replace("Z", "+00:00"))).total_seconds() < min_age:
continue
order = vercmp(release["pkgver"], best["pkgver"]) if best else 1
if best and order == 0:
order = vercmp(release["revision"] or "0", best["revision"] or "0")
if order > 0:
best = release
return best
DUMP = r'''
source "$1" >/dev/null || exit 1
set +u
for __watch_name in pkgver pkgrel epoch arch $(compgen -A variable | LC_ALL=C sort); do
case "$__watch_name" in
pkgver|pkgrel|epoch|arch|source|source_*|md5sums*|sha1sums*|sha224sums*|sha256sums*|sha384sums*|sha512sums*|b2sums*|_*)
[[ $__watch_name == __watch_* ]] && continue
declare -n __watch_value="$__watch_name"
printf '%s\0' "$__watch_name" "${#__watch_value[@]}" "${__watch_value[@]}"
unset -n __watch_value
;;
esac
done
'''
def read_recipe(path, arch="x86_64"):
with tempfile.TemporaryDirectory(prefix="recipe-read-") as work:
env = {**os.environ, "CARCH": arch, "SRCDEST": work, "srcdir": work, "pkgdir": work}
data = run(["bash", "-c", DUMP, "_", str(path.resolve())], cwd=path.parent, env=env).decode().split("\0")
result = {}
index = 0
while index < len(data) - 1:
name, size = data[index:index + 2]
index += 2
size = int(size)
result[name] = data[index:index + size]
index += size
return result
def scalar(recipe, name, default=""):
return recipe.get(name, [default])[0] if recipe.get(name) else default
def replace_scalar(text, name, value):
if not SCALAR.fullmatch(value):
raise ValueError(f"unsafe {name} value")
pattern = re.compile(r"^" + re.escape(name) + r"=.*$", re.M)
if len(pattern.findall(text)) != 1:
raise ValueError(f"expected one top-level {name}= assignment")
return pattern.sub(lambda _: f"{name}={value}", text)
def replace_array(text, name, values):
starts = list(re.finditer(r"^" + re.escape(name) + r"=\(", text, re.M))
if len(starts) != 1:
raise ValueError(f"expected one top-level {name}= array")
start = starts[0]
depth, quote_char, escaped, comment = 1, None, False, False
for index in range(start.end(), len(text)):
char = text[index]
if comment:
if char == "\n": comment = False
elif escaped:
escaped = False
elif char == "\\" and quote_char != "'":
escaped = True
elif quote_char:
if char == quote_char: quote_char = None
elif char in "\"'": quote_char = char
elif char == "#" and (index == 0 or text[index - 1].isspace()): comment = True
elif char == "(": depth += 1
elif char == ")":
depth -= 1
if depth == 0:
replacement = name + "=(" + " ".join("'" + value + "'" for value in values) + ")"
return text[:start.start()] + replacement + text[index + 1:]
raise ValueError(f"unclosed {name} array")
def bump_pkgrel(value):
if not re.fullmatch(r"[0-9]+(?:\.[0-9]+)?", value):
raise ValueError(f"invalid pkgrel: {value}")
components = value.split(".")
components[-1] = str(int(components[-1]) + 1)
return ".".join(components)
def complete_version(recipe):
return f"{scalar(recipe, 'epoch', '0')}:{scalar(recipe, 'pkgver')}-{scalar(recipe, 'pkgrel')}"
def hash_file(path, algorithm):
with path.open("rb") as stream:
return hashlib.file_digest(stream, HASHES.get(algorithm, algorithm)).hexdigest()
def source_url(source):
return source.split("::", 1)[-1]
def git_source_file(url, cache):
base, fragment = url.removeprefix("git+").split("#", 1)
kind, ref = fragment.split("=", 1)
https(base)
if kind not in {"tag", "commit"} or (kind == "commit" and not re.fullmatch(r"[0-9a-f]{40}", ref)):
raise ValueError("VCS sources must name an immutable commit or a checksummed tag")
if kind == "tag":
run(["git", "check-ref-format", "refs/tags/" + ref])
dest = cache / (hashlib.sha256(url.encode()).hexdigest() + ".git.tar")
if not dest.exists():
with tempfile.TemporaryDirectory(prefix="upstream-source-", dir=cache) as work:
subprocess.run(["git", "init", "--quiet", "--bare", work], check=True)
subprocess.run(["git", "-C", work, "fetch", "--quiet", "--depth=1", base, "refs/tags/" + ref if kind == "tag" else ref], check=True)
scratch = Path(work) / "source.tar"
with scratch.open("wb") as output:
subprocess.run(["git", "-c", "core.abbrev=no", "-C", work, "archive", "--format", "tar", "FETCH_HEAD"], stdout=output, check=True)
# The cache must never retain partial archives after a git failure.
scratch.replace(dest)
return dest
def updated_checksums(before, after, package, fetch, release, watch):
arrays = {}
source_names = {key for key in before if key == "source" or key.startswith("source_")}
if source_names != {key for key in after if key == "source" or key.startswith("source_")}:
raise ValueError("release changed the set of source architectures")
for source_name in sorted(source_names):
old_sources, sources = before[source_name], after[source_name]
suffix = source_name.removeprefix("source")
names = [name for name in before if SUM.fullmatch(name) and (SUM.fullmatch(name)[2] or "") == suffix]
if not sources:
continue
if len(sources) != len(old_sources) or not names:
raise ValueError(f"{source_name}: sources changed shape or have no checksums")
for name in names:
if len(before[name]) != len(sources):
raise ValueError(f"{name}: source/checksum count mismatch")
values = []
algorithm = SUM.fullmatch(name)[1]
for index, source in enumerate(sources):
old = before[name][index]
if source == old_sources[index] and f"{source_name}:{index}" not in watch.get("mutable_sources", []):
values.append(old)
continue
url = source_url(source)
# A release API digest can supply SHA256 without downloading a
# large asset, but only when its exact declared URL matches.
assets = [a for a in release.get("assets", []) if a.get("browser_download_url") == url]
if algorithm == "sha256" and len(assets) == 1 and re.fullmatch(r"sha256:[0-9a-f]{64}", assets[0].get("digest") or ""):
values.append("SKIP" if old == "SKIP" else assets[0]["digest"][7:])
continue
if url.startswith("git+https://"):
file = git_source_file(url, fetch.cache)
elif url.startswith("https://"):
file = fetch.file(url)
elif "://" not in url:
file = (package / url).resolve()
if not file.is_relative_to(package.resolve()) or not file.is_file():
raise ValueError(f"unsafe local source: {url}")
else:
raise ValueError(f"unsupported source transport: {url}")
# Preserve existing signature/prepare()-verified sources. Never
# introduce SKIP; still fetch changed URLs to verify availability.
values.append("SKIP" if old == "SKIP" else hash_file(file, algorithm))
if values != before[name]:
arrays[name] = values
return arrays
def resolve_release_fields(watch, release, fetch):
values = release["values"].copy()
if "github" in watch and any("{commit}" in value for value in watch.get("variables", {}).values()):
ref = fetch.json(f"https://api.github.com/repos/{watch['github']}/git/ref/tags/{quote(values['tag'], safe='')}")['object']
if ref['type'] == 'tag':
ref = fetch.json(f"https://api.github.com/repos/{watch['github']}/git/tags/{ref['sha']}")['object']
if ref['type'] != 'commit' or not re.fullmatch(r"[0-9a-f]{40}", ref['sha']):
raise ValueError("release tag does not resolve to a commit")
values['commit'] = ref['sha']
variables = {k: template.format_map(values) for k, template in watch.get('variables', {}).items()}
for name, path in watch.get('submodules', {}).items():
entry = fetch.json(f"https://api.github.com/repos/{watch['github']}/contents/{quote(path, safe='/')}?ref={quote(values['tag'], safe='')}")
if not entry.get('submodule_git_url') or not re.fullmatch(r"[0-9a-f]{40}", entry.get('sha', '')):
raise ValueError(f"release does not contain submodule {path}")
variables[name] = entry['sha']
if any(not SCALAR.fullmatch(value) for value in variables.values()):
raise ValueError("unsafe release variable value")
release['variables'] = variables
return release
def sync(package, fetch, min_age=0, check=False):
metadata = json.loads((package / ".omarchy/package.json").read_text())
if metadata.get("sync") is False:
return {"status": "skipped", "reason": "upstream updates held by sync=false"}
watch = metadata["upstream"]["watch"]
validate(watch)
path = package / "PKGBUILD"
original = path.read_text()
before = read_recipe(path)
release = select_release(discover(watch, fetch), min_age, bypass=os.environ.get("BYPASS_MIN_RELEASE_AGE") == "1")
if release is None:
return {"status": "skipped", "reason": "minimum release age"}
current = scalar(before, "pkgver")
if watch.get('sequence'):
prefix, counter, identity = current.rsplit('.', 2)
new_prefix, new_identity = release['values']['version'], release['values']['hash']
if new_prefix == prefix:
release['pkgver'] = current if new_identity == identity else f"{prefix}.{int(counter) + 1}.{new_identity}"
order = vercmp(release["pkgver"], current)
if order < 0:
return {"status": "skipped", "current": current, "available": release["pkgver"], "reason": "upstream is older"}
if order == 0 and not watch.get("revision_variable"):
return {"status": "skipped", "current": current, "reason": "already current"}
release = resolve_release_fields(watch, release, fetch)
changed_variables = {k: v for k, v in release["variables"].items() if scalar(before, k) != v}
if order == 0 and not changed_variables:
return {"status": "skipped", "current": current, "reason": "already current"}
if order == 0 and changed_variables:
# Only a declared, forward-moving release revision can rebuild the same
# version. A changed hash/commit alone is an immutable-release violation.
revision_field = watch.get("revision_variable")
if revision_field not in changed_variables or vercmp(changed_variables[revision_field], scalar(before, revision_field, "0")) <= 0:
raise ValueError("release metadata changed without a newer version/revision")
new_pkgrel = "1" if order > 0 else bump_pkgrel(scalar(before, "pkgrel"))
text = replace_scalar(original, "pkgver", release["pkgver"])
text = replace_scalar(text, "pkgrel", new_pkgrel)
for name, value in release["variables"].items():
text = replace_scalar(text, name, value)
if check:
return {"status": "available", "current": current, "release": release}
scratch = path.with_name("PKGBUILD.sync-upstream")
try:
scratch.write_text(text)
after = read_recipe(scratch)
if scalar(after, "pkgver") != release["pkgver"] or scalar(after, "pkgrel") != new_pkgrel:
raise ValueError("recipe did not retain the release version")
if vercmp(complete_version(after), complete_version(before)) <= 0:
raise ValueError("complete package version must increase")
if before["arch"] != after["arch"]:
raise ValueError("release changed supported architectures")
arrays = updated_checksums(before, after, package, fetch, release, watch)
for name, values in arrays.items():
text = replace_array(text, name, values)
scratch.write_text(text)
subprocess.run(["bash", "-n", str(scratch)], check=True)
for arch in before["arch"]:
result = read_recipe(scratch, "x86_64" if arch == "any" else arch)
if complete_version(result) != complete_version(after):
raise ValueError(f"{arch}: inconsistent release version")
for name, values in arrays.items():
if result.get(name) != values:
raise ValueError(f"{arch}: rewritten {name} differs from the checked source hashes")
for name in after:
if name == "source" or name.startswith("source_"):
if result.get(name) != after[name]:
raise ValueError(f"{arch}: conditional {name} differs from the checked sources; use source_<arch> arrays")
scratch.chmod(path.stat().st_mode)
scratch.replace(path)
return {"status": "updated", "before": complete_version(before), "after": complete_version(after)}
finally:
scratch.unlink(missing_ok=True)
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("command", choices=["sync", "check", "validate"])
parser.add_argument("package", type=Path)
parser.add_argument("--min-age", type=int, default=0)
args = parser.parse_args()
package = args.package.resolve()
if args.command == "validate":
validate(json.loads((package / ".omarchy/package.json").read_text())["upstream"]["watch"])
return
with tempfile.TemporaryDirectory(prefix="upstream-watch-") as cache:
fetch = Fetcher(os.environ.get("UPSTREAM_CACHE_DIR", cache))
print(json.dumps(sync(package, fetch, args.min_age, check=args.command == "check")))
if __name__ == "__main__":
try:
main()
except (ValueError, KeyError, TypeError, IndexError, re.error, OSError, subprocess.CalledProcessError) as error:
print(f"upstream watch failed: {error}", file=sys.stderr)
sys.exit(1)
+16 -2
View File
@@ -1,5 +1,19 @@
{
"source": "aur",
"source": "local",
"release_ring": "fast",
"upstream_commit": "a0f4262f94eb8a5b604146e71d42a3bb522feedf"
"upstream": {
"watch": {
"debian": "https://downloads.1password.com/linux/debian/amd64/dists/beta/main/binary-amd64/Packages",
"package": "1password",
"pattern": "(?P<version>[0-9]+(?:\\.[0-9]+)*)~(?P<build>[0-9]+)\\.BETA",
"version": "{version}_{build}.BETA",
"variables": {
"_tarver": "{version}-{build}.BETA"
}
}
},
"origin": {
"aur": "1password-beta",
"commit": "18d2de51dc01c9a58c1e8e96262f7afadcbf0648"
}
}
@@ -1,83 +0,0 @@
#!/bin/bash
set -euo pipefail
# Keep the AUR x86_64 checksums and add aarch64 sources. The aarch64
# artifacts are signed by 1Password's validpgpkeys, so we skip checksums there
# instead of baking version-specific hashes into Omarchy metadata.
set +u
CARCH=x86_64 source PKGBUILD
set -u
if declare -p sha256sums >/dev/null 2>&1 && [[ ${#sha256sums[@]} -ge 2 ]]; then
x86_sums=("${sha256sums[@]}")
elif declare -p sha256sums_x86_64 >/dev/null 2>&1 && [[ ${#sha256sums_x86_64[@]} -ge 2 ]]; then
x86_sums=("${sha256sums_x86_64[@]}")
else
echo "Unable to read x86_64 checksums from PKGBUILD" >&2
exit 1
fi
sha256_from_url() {
curl -fsSL "$1" | sha256sum | awk '{ print $1 }'
}
arm_url="https://downloads.1password.com/linux/tar/beta/aarch64/1password-${_tarver}.arm64.tar.gz"
arm_tar_sum=$(sha256_from_url "$arm_url")
arm_sig_sum=$(sha256_from_url "$arm_url.sig")
emit_archdir() {
cat <<'EOF'
case "${CARCH}" in
x86_64)
_archdir="x64"
;;
aarch64)
_archdir="arm64"
;;
esac
EOF
}
emit_sources() {
cat <<EOF
source=()
sha256sums=()
source_x86_64=(https://downloads.1password.com/linux/tar/beta/x86_64/1password-\${_tarver}.x64.tar.gz{,.sig})
source_aarch64=(https://downloads.1password.com/linux/tar/beta/aarch64/1password-\${_tarver}.arm64.tar.gz{,.sig})
sha256sums_x86_64=('${x86_sums[0]}'
'${x86_sums[1]}')
sha256sums_aarch64=('$arm_tar_sum'
'$arm_sig_sum')
EOF
}
tmpfile=$(mktemp)
skip_checksums=false
while IFS= read -r line || [[ -n "$line" ]]; do
if [[ "$skip_checksums" == true ]]; then
[[ "$line" == ")" ]] && skip_checksums=false
continue
fi
case "$line" in
'_tar="1password-${_tarver}.x64.tar.gz"')
emit_archdir >> "$tmpfile"
;;
"arch=('x86_64')")
echo "arch=('x86_64' 'aarch64')" >> "$tmpfile"
;;
source=\(*)
emit_sources >> "$tmpfile"
;;
sha256sums=\(*)
[[ "$line" == *")" ]] || skip_checksums=true
;;
*)
line=${line//1password-\$\{_tarver\}.x64/1password-\$\{_tarver\}.\$\{_archdir\}}
printf '%s\n' "$line" >> "$tmpfile"
;;
esac
done < PKGBUILD
mv "$tmpfile" PKGBUILD
+9 -10
View File
@@ -1,6 +1,6 @@
pkgname=1password-beta
_tarver=8.12.34-29.BETA
_tarver=8.12.38-25.BETA
case "${CARCH}" in
x86_64)
_archdir="x64"
@@ -9,8 +9,8 @@ case "${CARCH}" in
_archdir="arm64"
;;
esac
pkgver=${_tarver//-/_}
pkgrel=29.1
pkgver=8.12.38_25.BETA
pkgrel=25.2
conflicts=('1password' '1password-beta-bin')
pkgdesc="Password manager and secure wallet"
arch=('x86_64' 'aarch64')
@@ -22,10 +22,10 @@ source=()
sha256sums=()
source_x86_64=(https://downloads.1password.com/linux/tar/beta/x86_64/1password-${_tarver}.x64.tar.gz{,.sig})
source_aarch64=(https://downloads.1password.com/linux/tar/beta/aarch64/1password-${_tarver}.arm64.tar.gz{,.sig})
sha256sums_x86_64=('6894b283a534cf94b07903fb38966a0aab2e37f75ee3848ce340308819aadddb'
'8d4df4d0a80d2be7aad7d91ad964a750c8f32db5007261045003c191690c8246')
sha256sums_aarch64=('c77ce6ddf36dbd6054c64d91b7f644274d3218f465fd60e211d0296f6443124a'
'91c7249a1cf5e7924ef2810cb0cb1b893d8a9a424b373b433f45d7aaa5a8369b')
sha256sums_x86_64=('c6d302a2c7404a7ded34a3c4f1c401a43eafeed8b147d128dcb416284c2c2b71'
'cc0f00054749c32d77fba31a12a8dece812e409f4b9d81850d2f9b50fab55dca')
sha256sums_aarch64=('1fd62cd0df90098dd5e50d22e9a6c0a5221f9db6355b7c848db7af7076b395fd'
'4d273b71ab987dcadad9e4fa7cfac7e0173dc4dbe7908c9a3e76af274313dd76')
validpgpkeys=('3FEF9748469ADBE15DA7CA80AC2D62742012EA22')
package() {
@@ -42,7 +42,7 @@ package() {
"${pkgdir}/usr/share/icons/hicolor/${resolution}/apps/1password.png"
done
# Install desktop file
install -Dm0644 resources/1password.desktop -t "${pkgdir}"/usr/share/applications/
install -Dm0644 resources/com.onepassword.OnePassword.desktop -t "${pkgdir}"/usr/share/applications/
# Fill in policy kit file with a list of (the first 10) human users of the system.
export POLICY_OWNERS
@@ -65,8 +65,7 @@ EOF" > ./com.1password.1Password.policy
# Cleanup un-needed files
rm "${pkgdir}"/opt/1Password/com.1password.1Password.policy "${pkgdir}"/opt/1Password/com.1password.1Password.policy.tpl "${pkgdir}"/opt/1Password/install_biometrics_policy.sh
rm -r "${pkgdir}"/opt/1Password/resources/icons/
rm "${pkgdir}"/opt/1Password/resources/1password.desktop "${pkgdir}"/opt/1Password/resources/custom_allowed_browsers
rm "${pkgdir}"/opt/1Password/resources/com.onepassword.OnePassword.desktop "${pkgdir}"/opt/1Password/resources/custom_allowed_browsers
# Symlink /usr/bin executable to opt
install -dm0755 "${pkgdir}"/usr/bin
ln -s /opt/1Password/1password "${pkgdir}"/usr/bin/1password
+11 -2
View File
@@ -1,5 +1,14 @@
{
"source": "aur",
"source": "local",
"release_ring": "fast",
"upstream_commit": "b0d208821677a5dbb883a8b92f06a5c92b9e861a"
"upstream": {
"watch": {
"json": "https://app-updates.agilebits.com/check/1/0/CLI2/en/0",
"path": "version"
}
},
"origin": {
"aur": "1password-cli",
"commit": "b0d208821677a5dbb883a8b92f06a5c92b9e861a"
}
}
+11 -8
View File
@@ -1,6 +1,6 @@
pkgname=1password
pkgver=8.12.34
pkgrel=36
pkgver=8.12.36
pkgrel=2
conflicts=('1password-beta' '1password-beta-bin')
pkgdesc="Password manager and secure wallet"
arch=('x86_64' 'aarch64')
@@ -16,11 +16,13 @@ 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_x86_64=(
'393c93c8025fee5dda76a4d0f1e478e98526cc946e58a22efe26f540ea2b5729'
'251177694bfdc63c431021e2bd2ed64f241b8a0247d5dd1c46d4d2dadffe7b97'
)
sha256sums_aarch64=('ea5102363d6cf3442b96a7abd6743da8c1d261f56a628e1a3c183d84fa65fdcb'
'f44db73fa44c3f68c3ab78a9cce140be9355de1dd9be4ce731c9d6597960c907'
sha256sums_aarch64=(
'4b58851b3bf52a7bbc79243fc6b9cba9591cabe8b7eb2d0e271593bad2192af9'
'b5ba754ec22f9ecd980986582cb3ea0c6af45b276bfe8897724bf4abfa17e27f'
)
validpgpkeys=('3FEF9748469ADBE15DA7CA80AC2D62742012EA22')
@@ -49,7 +51,8 @@ package() {
"${pkgdir}/usr/share/icons/hicolor/${resolution}/apps/1password.png"
done
# Install desktop file
install -Dm0644 resources/1password.desktop -t "${pkgdir}"/usr/share/applications/
install -Dm0644 resources/com.onepassword.OnePassword.desktop \
"${pkgdir}/usr/share/applications/1password.desktop"
# 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
@@ -78,7 +81,7 @@ EOF" > ./com.1password.1Password.policy
# Cleanup un-needed files
rm "${pkgdir}"/opt/1Password/com.1password.1Password.policy "${pkgdir}"/opt/1Password/com.1password.1Password.policy.tpl "${pkgdir}"/opt/1Password/install_biometrics_policy.sh
rm -r "${pkgdir}"/opt/1Password/resources/icons/
rm "${pkgdir}"/opt/1Password/resources/1password.desktop "${pkgdir}"/opt/1Password/resources/custom_allowed_browsers
rm "${pkgdir}"/opt/1Password/resources/com.onepassword.OnePassword.desktop "${pkgdir}"/opt/1Password/resources/custom_allowed_browsers
# Symlink /usr/bin executable to opt
install -dm0755 "${pkgdir}"/usr/bin
+11 -2
View File
@@ -1,5 +1,14 @@
{
"source": "aur",
"source": "local",
"release_ring": "fast",
"upstream_commit": "0f047f40a200121075ca3cedce59ddf226f2a0f1"
"upstream": {
"watch": {
"github": "omacom/aether",
"pattern": "v(?P<version>[0-9]+(?:\\.[0-9]+)*)"
}
},
"origin": {
"aur": "aether",
"commit": "0f047f40a200121075ca3cedce59ddf226f2a0f1"
}
}
+11 -2
View File
@@ -1,4 +1,13 @@
{
"source": "aur",
"upstream_commit": "b0ec6ca495eb331a684db91b0fe12085a868b632"
"source": "local",
"upstream": {
"watch": {
"git_tags": "https://github.com/OpenGamingCollective/asusctl.git",
"pattern": "(?P<version>[0-9]+(?:\\.[0-9]+)*)"
}
},
"origin": {
"aur": "asusctl",
"commit": "b0ec6ca495eb331a684db91b0fe12085a868b632"
}
}
+2 -2
View File
@@ -4,7 +4,7 @@
pkgbase=asusctl
pkgname=(asusctl rog-control-center)
pkgver=6.4.0
pkgver=6.5.0
pkgrel=1
pkgdesc="Daemon and tools to control your ASUS ROG laptop"
arch=('x86_64')
@@ -12,7 +12,7 @@ url="https://asus-linux.org"
license=('MPL-2.0')
makedepends=('cargo' 'fontconfig')
source=("${pkgbase}-${pkgver}.tar.gz::https://github.com/OpenGamingCollective/asusctl/archive/${pkgver}.tar.gz")
b2sums=('e90074e904f364386ad661784bd9fc2e929e83ea06ac62fd1d34eb03490cefe8aae3bed2722162f1e8ea5d69248138cb5fd9f90c3a18443d1d8c04cf732b928a')
b2sums=('4179e08a60f9480b62e41d84faade1f46407140a213ca4d60c8699837a2486bda8e66b4ca43fa06149667d02e3d060c3efd792348f9a93a675f762d6ea2d05f8')
prepare() {
cd "${pkgbase}-${pkgver}"
+11 -2
View File
@@ -1,5 +1,14 @@
{
"source": "aur",
"source": "local",
"release_ring": "fast",
"upstream_commit": "1e7bd48ef192b3d6a2854916e75e14ea43f6f0b7"
"upstream": {
"watch": {
"github": "basecamp/basecamp-cli",
"pattern": "v(?P<version>[0-9]+(?:\\.[0-9]+)*)"
}
},
"origin": {
"aur": "basecamp-cli",
"commit": "89c4eb5a0ac9ffe98aecd4ea8b789cf1fee42bf1"
}
}
+6 -6
View File
@@ -1,7 +1,7 @@
# Maintainer: Basecamp <support@basecamp.com>
pkgname=basecamp-cli
pkgver=0.10.0
pkgrel=1
pkgver=0.11.0
pkgrel=2
pkgdesc="CLI for Basecamp project management"
arch=('x86_64' 'aarch64')
url="https://github.com/basecamp/basecamp-cli"
@@ -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.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')
source_x86_64=("https://github.com/basecamp/basecamp-cli/releases/download/v${pkgver}/basecamp_${pkgver}_linux_amd64.tar.gz")
source_aarch64=("https://github.com/basecamp/basecamp-cli/releases/download/v${pkgver}/basecamp_${pkgver}_linux_arm64.tar.gz")
sha256sums_x86_64=('425ffab1251c4315c5f731f8367c3c8c37b54b6b1050eafdbe369e11e1a1ce51')
sha256sums_aarch64=('9c433b12a704402a98b238abb3128a0844a0bc682064adb260b11bc228b3595e')
package() {
install -Dm755 "basecamp" "${pkgdir}/usr/bin/basecamp"
+11 -2
View File
@@ -1,5 +1,14 @@
{
"source": "aur",
"source": "local",
"release_ring": "fast",
"upstream_commit": "195b828d52ce9a181215f753927123e7ef2af252"
"upstream": {
"watch": {
"github": "oven-sh/bun",
"pattern": "bun-v(?P<version>[0-9]+(?:\\.[0-9]+)*)"
}
},
"origin": {
"aur": "bun-bin",
"commit": "195b828d52ce9a181215f753927123e7ef2af252"
}
}
+3 -6
View File
@@ -3,7 +3,7 @@
# Contributor: 37h4n (aarch64 support added by Ethan Reece <aur at ethanreece dot com>)
# Contributor: sh!zeeg (shizeeque@gmail.com) support for non-avx2 CPUs, shell completions.
pkgname=bun-bin
pkgver=1.3.11
pkgver=1.4.2
pkgrel=1
pkgdesc="All-in-one JavaScript runtime built for speed, with bundler, transpiler, test runner, and package manager. Includes bunx, shell completions and support for baseline CPUs"
arch=('x86_64' 'aarch64')
@@ -12,11 +12,8 @@ license=('MIT')
provides=('bun')
conflicts=('bun')
options=('!debug')
sha256sums_x86_64=('8611ba935af886f05a6f38740a15160326c15e5d5d07adef966130b4493607ed'
'abe346f63414547cdf6b35b7a649a490c728b93d006226156923918a84c0e59b'
'9b296bcc20090b5ea079dc1bca15913a32246121169bcf54cbdb7384d6f5b32b')
sha256sums_aarch64=('d13944da12a53ecc74bf6a720bd1d04c4555c038dfe422365356a7be47691fdf'
'9b296bcc20090b5ea079dc1bca15913a32246121169bcf54cbdb7384d6f5b32b')
sha256sums_x86_64=('36368faef7527875d5ffa52e53cd48021741f2a83eb6208a8dd64068d422a913' 'c678040f14fe0440eb839d37cbd0ce4c051a32da72806ac97de6a6aab6bf728f' '9b296bcc20090b5ea079dc1bca15913a32246121169bcf54cbdb7384d6f5b32b')
sha256sums_aarch64=('54328bbc2d9c8e0c9f892c544d66c57a83b84139e34909e5ee81758f1ac8fda7' '9b296bcc20090b5ea079dc1bca15913a32246121169bcf54cbdb7384d6f5b32b')
source_x86_64=(
"bun-x64.zip::https://github.com/oven-sh/bun/releases/download/bun-v${pkgver}/bun-linux-x64.zip"
"bun-x64-baseline.zip::https://github.com/oven-sh/bun/releases/download/bun-v${pkgver}/bun-linux-x64-baseline.zip"
+11 -2
View File
@@ -1,5 +1,14 @@
{
"source": "aur",
"source": "local",
"release_ring": "fast",
"upstream_commit": "87aa5d5f2771c83dae737fe1bcfbc129110291fb"
"upstream": {
"watch": {
"regex": "https://downloads.claude.ai/claude-code-releases/latest",
"pattern": "^(?P<version>[0-9]+(?:\\.[0-9]+)*)\\s*$"
}
},
"origin": {
"aur": "claude-code",
"commit": "27f61daa48ebdca87c9b2c7c83c162100d233ccc"
}
}
+3 -3
View File
@@ -4,7 +4,7 @@
# Automation repository: https://github.com/fabifont/claude-code-aur
pkgname=claude-code
pkgver=2.1.263
pkgver=2.1.273
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=('26d020351e8112f4006790f3cfce43b4c9df0c1bb1d0e542364d64151b81d5ba')
sha256sums_aarch64=('7d25d7c8ae6c6e009cc7dae4e817f674179fd31fb7761bcd56fee4c2902b4c03')
sha256sums_x86_64=('6c752e2cc7c110c9df15f26d8d134d438c5ae95dbd610efc1a308bf7f9c5f6c1')
sha256sums_aarch64=('103cfab4d6ae898b6af692336fb662ffcc607075cc6408892bd350b3f549ebee')
package() {
install -Dm755 "${srcdir}/claude-${pkgver}-${CARCH}" "${pkgdir}/opt/claude-code/bin/claude"
@@ -0,0 +1,12 @@
{
"source": "local",
"release_ring": "fast",
"upstream": {
"debian": "https://downloads.claude.ai/claude-desktop/apt/stable/dists/stable/main/binary-amd64/Packages",
"package": "claude-desktop",
"sources": {
"x86_64": ["https://downloads.claude.ai/claude-desktop/apt/stable/pool/main/c/claude-desktop/claude-desktop_{pkgver}_amd64.deb"],
"aarch64": ["https://downloads.claude.ai/claude-desktop/apt/stable/pool/main/c/claude-desktop/claude-desktop_{pkgver}_arm64.deb"]
}
}
}
+99
View File
@@ -0,0 +1,99 @@
# Maintainer: Spencer Bull <spencerbull2554@gmail.com>
# Omarchy tracks Anthropic's own Debian repository, the only channel the
# Linux beta ships through. The declarative "debian" upstream provider in
# .omarchy/package.json rewrites the version and checksums below from that
# repository's package index.
pkgname=claude-desktop
pkgver=2.110.0
pkgrel=1
pkgdesc="Official Claude desktop app with Claude Code"
arch=('x86_64' 'aarch64')
url="https://claude.ai"
license=('custom')
depends=(
'alsa-lib'
'at-spi2-core'
'bash'
'gcc-libs'
'glibc'
'gtk3'
'libdrm'
'libnotify'
'libsecret'
'libxcb'
'libxtst'
'mesa'
'nss'
'util-linux-libs'
'xdg-desktop-portal'
'xdg-utils'
)
optdepends=(
'gnome-keyring: store credentials in a system keyring'
'bubblewrap: Claude Code sandboxing'
'socat: Claude Code sandboxing'
)
# Cowork is x86_64-only here: Arch Linux ARM ships no UEFI firmware package,
# so on aarch64 the app's /usr/share/AAVMF probe has nothing to resolve to.
optdepends_x86_64=(
'qemu-system-x86: run Cowork tasks in a local virtual machine'
'edk2-ovmf: UEFI firmware for the Cowork virtual machine'
'virtiofsd: file sharing with the Cowork virtual machine'
)
makedepends=('libarchive')
options=('!debug' '!strip')
# Omarchy: same treatment as openai-codex-desktop. The build image compresses
# with zstd at its default level, which leaves this 560 MB Electron tree at
# 210 MB. Maximum zstd takes it to 160 MB for ~3 extra minutes of build time
# -- worth it for a package every user re-downloads on each of Anthropic's
# frequent releases.
COMPRESSZST=(zstd -c -z -q --ultra -22 --threads=0 -)
_deb_x86_64="claude-desktop_${pkgver}_amd64.deb"
_deb_aarch64="claude-desktop_${pkgver}_arm64.deb"
source=('claude-desktop-launcher.sh')
_pool="https://downloads.claude.ai/claude-desktop/apt/stable/pool/main/c/claude-desktop"
source_x86_64=("${_deb_x86_64}::${_pool}/${_deb_x86_64}")
source_aarch64=("${_deb_aarch64}::${_pool}/${_deb_aarch64}")
noextract=("${_deb_x86_64}" "${_deb_aarch64}")
sha256sums=('edfdbc63b65891ef7c481b07086c7e630fc102c042b6ed65331a52fcaf72b14a')
sha256sums_x86_64=('f44cb8b52f6e9171ac2e67cbcc8070c4974a2f0a9b9f141b430b32b4ff541109')
sha256sums_aarch64=('de9f24034f33dcadc53bedee92da6604e2c9268497ebff5447fedc092c884e71')
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}"
# Upstream's /usr/bin/claude-desktop is a bare symlink to the Electron
# binary; replace it with a launcher that picks the right Ozone platform.
rm "${pkgdir}/usr/bin/claude-desktop"
install -Dm755 claude-desktop-launcher.sh "${pkgdir}/usr/bin/claude-desktop"
install -Dm644 "${pkgdir}/usr/share/doc/claude-desktop/copyright" \
"${pkgdir}/usr/share/licenses/${pkgname}/copyright"
# Cowork probes Debian's paths for its VM stack; map them onto Arch's.
# The links dangle harmlessly until the matching optdepends are installed.
if [[ "${CARCH}" == x86_64 ]]; then
install -d "${pkgdir}/usr/libexec"
ln -s ../lib/virtiofsd "${pkgdir}/usr/libexec/virtiofsd"
# The app derives the VARS path from the CODE path, so both need a link.
install -d "${pkgdir}/usr/share/edk2"
ln -s x64/OVMF_CODE.4m.fd "${pkgdir}/usr/share/edk2/OVMF_CODE_4M.fd"
ln -s x64/OVMF_VARS.4m.fd "${pkgdir}/usr/share/edk2/OVMF_VARS_4M.fd"
fi
# Debian package-policy files are not used on Arch Linux.
rm -rf "${pkgdir}/usr/share/doc" "${pkgdir}/usr/share/lintian"
}
@@ -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/claude-desktop-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 /usr/lib/claude-desktop/claude-desktop "${platform_flags[@]}" "${user_flags[@]}" "$@"
+11 -2
View File
@@ -1,4 +1,13 @@
{
"source": "aur",
"upstream_commit": "0e012cbfe1ea6cbbf15e2ada9cf93cca7aaa9ca0"
"source": "local",
"upstream": {
"watch": {
"github": "bjarneo/cliamp",
"pattern": "v(?P<version>[0-9]+(?:\\.[0-9]+)*)"
}
},
"origin": {
"aur": "cliamp",
"commit": "92fb021a1c78f75043cbbd402aca8b515af069ec"
}
}
+2 -2
View File
@@ -1,6 +1,6 @@
# Maintainer: bjarneo <https://github.com/bjarneo>
pkgname=cliamp
pkgver=2.0.1
pkgver=2.2.0
pkgrel=1
pkgdesc='A retro terminal music player inspired by Winamp 2.x'
arch=('x86_64' 'aarch64')
@@ -11,7 +11,7 @@ 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=('2c5885665dba5ed2e8dc156bce64751199a92efed7f63959c65e985759b73732')
sha256sums=('54ffbba6983880c915d2c13c83ca1339de2d2a3c5af3bb0a176923faa9afc015')
build() {
cd "${pkgname}-${pkgver}"
+11 -2
View File
@@ -1,5 +1,14 @@
{
"source": "aur",
"source": "local",
"release_ring": "fast",
"upstream_commit": "1a56795cad77f703b8d0e5edefcd30926c6091c6"
"upstream": {
"watch": {
"github": "charmbracelet/crush",
"pattern": "v(?P<version>[0-9]+(?:\\.[0-9]+)*)"
}
},
"origin": {
"aur": "crush-bin",
"commit": "9c63847ccd3650646141f84384545c8fe2d68bb7"
}
}
+5 -5
View File
@@ -3,7 +3,7 @@
# Maintainer: caarlos0 <carlos@charm.sh>
pkgname='crush-bin'
pkgver=0.92.0
pkgver=0.95.0
pkgrel=1
pkgdesc='A powerful terminal-based AI assistant for developers, providing intelligent coding assistance directly in your terminal.'
url='https://charm.sh/crush'
@@ -13,16 +13,16 @@ provides=('crush')
conflicts=('crush')
source_aarch64=("${pkgname}_${pkgver}_aarch64.tar.gz::https://github.com/charmbracelet/crush/releases/download/v${pkgver}/crush_${pkgver}_Linux_arm64.tar.gz")
sha256sums_aarch64=('1b6f0384297a1e77d01df41db1f56fc0872f1e9c9d99634c4b2c208b7bb5d935')
sha256sums_aarch64=('b42291307abe5572afb972fba9b8780f63a2058f37fb0dc61ab4c7b435314a8a')
source_armv7h=("${pkgname}_${pkgver}_armv7h.tar.gz::https://github.com/charmbracelet/crush/releases/download/v${pkgver}/crush_${pkgver}_Linux_armv7.tar.gz")
sha256sums_armv7h=('dac6c6d57e3ffc84d24883ce63b84e9a940ca243d9ea6954a732f0f7da862a2a')
sha256sums_armv7h=('756363804b6475ab6bf811f175a93766f47372beddb6e4a7b6e365ecba3f90ae')
source_i686=("${pkgname}_${pkgver}_i686.tar.gz::https://github.com/charmbracelet/crush/releases/download/v${pkgver}/crush_${pkgver}_Linux_i386.tar.gz")
sha256sums_i686=('29f2586dfc6b065a30d5f740ff2ca1ed03fbccdcd84a8bf826f1882f0f3660a1')
sha256sums_i686=('ce7b0dec1f1d9aa5a6e339f04e835bc6b3a335caf816dcb6c83a2f808d9fcd3b')
source_x86_64=("${pkgname}_${pkgver}_x86_64.tar.gz::https://github.com/charmbracelet/crush/releases/download/v${pkgver}/crush_${pkgver}_Linux_x86_64.tar.gz")
sha256sums_x86_64=('7d9aca9ab7808ee828a0bb0fde01d96fc480ce0a66a5ff617aa27ac09e23cbd5')
sha256sums_x86_64=('edef832ff1fc03e420a0410b9653547feb816bda7e0458589434ea4164210f5e')
package() {
case "$CARCH" in
+17 -17
View File
@@ -11,14 +11,14 @@
# The binary carries its own updater: `cua-driver update --apply` pipes the
# vendor installer into bash, which would install a second copy under
# ~/.cua-driver and link it into ~/.local/bin, stepping around pacman and
# this repository's release gate. Upstream offers no switch for that path,
# and a /usr/bin wrapper would not cover it either, since the MCP
# configurations the binary generates record the resolved executable. So
# prepare() rewrites the installer URL inside the binary to point at pm.sh,
# a stand-in that declines and names pacman instead.
# this repository's release gate. Driver 0.27.0 detects pacman ownership, but
# deliberately treats failed and timed-out ownership queries as unmanaged.
# A /usr/bin wrapper would not cover MCP configurations that record the
# resolved executable, so prepare() rewrites the installer URL inside the
# binary to point at pm.sh, a stand-in that declines and names pacman instead.
pkgname=cua-driver-bin
pkgver=0.24.0
pkgver=0.28.1
pkgrel=1
pkgdesc="Computer-use driver for native GUI apps: accessibility-tree snapshots and input injection"
arch=('x86_64' 'aarch64')
@@ -46,8 +46,8 @@ source_x86_64=("https://github.com/trycua/cua/releases/download/cua-driver-rs-v$
source_aarch64=("https://github.com/trycua/cua/releases/download/cua-driver-rs-v${pkgver}/cua-driver-rs-${pkgver}-linux-arm64.tar.gz")
sha256sums=('c0779290c1d4783169aa3dbfb55feb505e563ef8a004bbf55298ceffcfbda8d9'
'c76e251c3ed424200eac52bec35ba534336307fabd83a175ab0b47e2084ab0d8')
sha256sums_x86_64=('e313e4072bde730f16466b90388c7602954ff25714bf73f701d7218eeb7747d2')
sha256sums_aarch64=('6d7969715e6be6e1d635fc0017040825d132142c8503130b1ce08fb6ee71c8c9')
sha256sums_x86_64=('a068b6e477893b77ced74bceccf7db7483cf140e8d54150ce5849b6252b90bcf')
sha256sums_aarch64=('a863951ef0699fd25091adb87bd114d69709b887fdfc059e795aca49ef8ac19c')
case "${CARCH}" in
x86_64) _platform="linux-x86_64" ;;
@@ -56,7 +56,7 @@ esac
_vendor_tree="cua-driver-rs-${pkgver}-${_platform}"
# Rust strings carry their length out of band, so the replacement has to be
# exactly as long as the original: 32 bytes, which is what fixes the
# exactly as long as the original, which is what fixes the
# stand-in's short name and location.
_vendor_installer='https://cua.ai/driver/install.sh'
_pacman_installer='file:///usr/lib/cua-driver/pm.sh'
@@ -69,14 +69,14 @@ prepare() {
return 1
fi
# The URL appears twice: once in the updater and once in the printed
# reinstall one-liner. Any other count means upstream moved the updater
# and this rewrite needs another look, so the build stops rather than
# shipping a live self-updater.
local found
# In 0.28.1 the URL appears in the updater, the printed reinstall command,
# and two embedded copies of Skills/cua-driver/README.md. Rewrite all four
# so the embedded instructions also defer to pacman. Any other count means
# the release layout changed and needs review before packaging.
local expected=4 found
found=$(grep -obUaF "${_vendor_installer}" cua-driver | wc -l)
if (( found != 2 )); then
echo "expected the vendor installer URL twice in cua-driver, found ${found}" >&2
if (( found != expected )); then
echo "expected the vendor installer URL ${expected} times in cua-driver, found ${found}" >&2
return 1
fi
@@ -87,7 +87,7 @@ prepare() {
if (( size_before != size_after )) \
|| grep -qUaF "${_vendor_installer}" cua-driver \
|| (( $(grep -obUaF "${_pacman_installer}" cua-driver | wc -l) != 2 )); then
|| (( $(grep -obUaF "${_pacman_installer}" cua-driver | wc -l) != expected )); then
echo "installer URL rewrite did not land cleanly in cua-driver" >&2
return 1
fi
@@ -0,0 +1,3 @@
{
"source": "local"
}
@@ -0,0 +1,57 @@
{
"files": {
"CMakeLists.txt": "7e874a595e1abd708cb0626bd9f0f58d79b4b4bbc6d99b45a0cbe1c5c53b43bc",
"LICENSE.md": "c0779290c1d4783169aa3dbfb55feb505e563ef8a004bbf55298ceffcfbda8d9",
"SOURCE-PROVENANCE.json": "54f514664c84e1358a435f29cd6befd5661b0b133d76997191c000b10f021a75",
"cmake/DetectHyprlandAPI.cmake": "216133ec0eb141c3696bf3770a23e63e46521c9e91a0245a7cb75c20a75ba2c5",
"cmake/VerifyRuntime.cmake": "5869f79a418e7aa6178d2b9166c36cd01c3093c2579b647624968244db57b761",
"include/cua_hyprland/protocol.hpp": "7051463b3c61a2dc93c388e66b6136b7bb524f8694350f249f9d2b5c55826493",
"include/cua_hyprland/session.hpp": "e6a968e4f2ac28122cb7413a0e318f6222d2a0a1b7b0c45f4ab419a78639ebea",
"include/cua_hyprland/status.hpp": "56a9656647c0f4eeb0c588cd4b98a77df198d1f90421f973e9a80e6802f61495",
"src/drag_geometry.hpp": "c5b783d15ff197f22938f08f8d176bab5d45544fe989d150f15cb99295acedb8",
"src/foreground_route.hpp": "4aa016c237b33c15e352a9f5f64bbbca7e1a9c1671ffd593a95a0d87994fb519",
"src/inject_server.cpp": "0935283580c50fcf0e4ad956f858885700536002b1d86d2f078da9c4404ee9e8",
"src/inject_server.hpp": "67de008b4d6983371207bb22a57bab154b1dedda9b38d1207d3ac8cb379382eb",
"src/input_client_deadline.hpp": "00a91a789ff698820607449ff7152e2fb50d0f315e0fa9c5c3855752bfffe2ef",
"src/input_experiment.cpp": "7017748c782b64b0bc1d257f4ade1a8d46fd19ce940ecc21f22d628614fcef37",
"src/input_experiment.hpp": "9da2ddab7f0de6e9cf02aea53e9119acef17ef8513af849bb5aa3d137ebd12c3",
"src/input_grant.hpp": "90b544b2f559bacd920b85ab5915f09ff201de3c05a052ce1b71ff71f767e6a6",
"src/keyboard_layout.hpp": "bc2ec039ac1974caebbb66fe4acb7a2a81832c9054b3aa644a9adcc8954a2467",
"src/owned_socket_path.hpp": "8e784656d944c700f3ded383c93a8cadf846dabdaa4cc12673bcb637c26d9fd1",
"src/passive_pointer_target.hpp": "ede36fd9fd6e95ae5923c751f12591084392be9d2270eb06ad64eb4f245169fa",
"src/plugin.cpp": "712fd73ef8e9046e0fd91531b7bf5da50ce37ccca9df174160137e1924a74b09",
"src/primary_trace.cpp": "e9468d1a3f3be2a90d47bf8c4a638ad8a60fe10b6297582ab7825751cb707aa9",
"src/primary_trace.hpp": "8d62535fb0b24a02bb80d9a8dcd540b39204afb2f3b4bcb5cabd5275c3b5eaa7",
"src/protocol.cpp": "bd083d65efb05e80946566dc535b1a6fadaa581c66ec327eff41796feba795b1",
"src/seat_lifetime.hpp": "386cf5c72c178f8eec0824f2a7d46fa755b0bb000861f9a6e00802f6b81fb779",
"src/session.cpp": "0105c7ba5f9e2dbdd9a21f48be0bc1f2bde930f6aa19f77e2216403d7790e4df",
"src/status.cpp": "e46e81e5e8ae3b1f50af5dcaa6c1776e236321c788fe61b402c9923c797b1270",
"tests/agent_keymap_test.py": "9b112f520a97a77a0d55f1009cd2594988d3c1e8e7bcaa39f213ffad2644dc56",
"tests/cmake-api/CMakeLists.txt": "6a66c8f98023f029998af917fcd3ec6b1388607bdf7acaf5bdfaef9141962685",
"tests/cmake-api/include/src/plugins/PluginAPI.hpp": "86c8ad51e668908d18928cef1b04e8e6d32a33894525640b52f0b31769c870a0",
"tests/desktop_fault_policy_fixture.cpp": "ac24d675ebc64cc98148e852eb5aba5858bffc06332678d14276d04b317aaf15",
"tests/desktop_fault_policy_test.py": "c3f624c0239036babd23eaaf1bb6b722f3a0c3321a5d3d6668ae7ab4910ca95c",
"tests/drag_geometry_test.cpp": "d32ea649d008fc051fe18555d6fbc54ba5d057b61880b648c5df8aa076a53fbc",
"tests/foreground_modifiers_test.py": "abf0ddde2d51c6639c8bdca8fdda51cdc8f922b57575a00fc8925c683ccc3bb2",
"tests/foreground_route_test.cpp": "1024168828b13ee6abd8242941e73c042e9381b39108e3ada74823039c7e7932",
"tests/input_client_deadline_test.cpp": "d62373a7815d531f1269c9a838773595f43e8bcef6482fa140edb162e59a6cac",
"tests/input_grant_test.cpp": "1f327b7ee678189ebad6a50bb1b9bd06767521cebc9cfb478d92de4a8bf7e7fe",
"tests/keyboard_layout_test.cpp": "3bb0fade4675d7ad92a81eb4a1c5201dd1d01bcc418b7ea59a4284dc235e5fb2",
"tests/mock-hyprland/mock.hpp": "3aeb1a4b9d6b83506b66c129d3fa812330fad4509fe218a0dbb99dd2bb5b6319",
"tests/mock-hyprland/src/config/values/types/BoolValue.hpp": "47cf2cca89f71a273573968cb9b8ba46a1496841d6c892756ebf123500a7ecb3",
"tests/mock-hyprland/src/plugins/PluginAPI.hpp": "5654d90ec9090a88bea3d31f8a79617d4c79742b09068648e64448319d395110",
"tests/owned_socket_path_test.cpp": "eaff6b5c6f148eca6c8650ee3dc212a5e892f002ee4f68b2a1290c7205ee7e42",
"tests/passive_pointer_target_test.cpp": "2aeef1de1dc8932b26ab8c41b83fb16a4289ff96c177a5816088f07b9a168948",
"tests/plugin_api_test.cpp": "1e7e200c309996ee945c88e172273ae942be2837e24564d422dee79d8b77d8a2",
"tests/plugin_input_lifetime_test.cpp": "82e57b335ea1216ea24cca07fe4feebafebdebb779785fde20b8dab6ee222e1b",
"tests/protocol_test.cpp": "119cfe0df81c0c00036a2d181764eda7601d6ee72459c2275a96226d4f670447",
"tests/seat_lifetime_test.cpp": "b07570edbe0a142f97c54560eeb93e8327c435ab3b8cbc7496d55175e387b78a",
"tests/status_test.cpp": "b8990efc53ec3820cfe498c920b9220c4b70615ad585468558e032e7619e32f3",
"tests/transport_test.cpp": "deef114a950a27eaff0a530165ddf7db0bfc0fe7e8fb55bdbb66135c8e0c04c9",
"verify.py": "fb35d62313ff4661f892f88666919b33b160f8b6d4fb2d5d52610708bf7f4a54"
},
"patch_sha256": "e74d9021111a5c217b12f3e13c1bbdd260e829c60db5fc2f7293ba4846e1a7d7",
"schema": 1,
"upstream_manifest_sha256": "54f514664c84e1358a435f29cd6befd5661b0b133d76997191c000b10f021a75",
"upstream_revision": "cc54254464c0c9aebfd6547fe7e4a0ceaf0456d7"
}
+207
View File
@@ -0,0 +1,207 @@
# Verified upstream kit plus a separately pinned Omarchy keyboard-remap patch.
# Normal reruns need a fresh build directory; makepkg -e reuses verified extracted trees.
# Profile kit: original source bytes and separately committed packaging tooling.
# shellcheck shell=bash disable=SC2034,SC2154
pkgname=cua-hyprland-plugin
pkgver=0.26.1
pkgrel=5
pkgdesc='Cua input candidate for reviewed profile omarchy-hyprland-0562r3-remaps'
arch=('x86_64')
url='https://github.com/trycua/cua'
license=('MIT')
depends=('hyprland=0.56.2-3' 'aquamarine=0.15.0-2' 'glibc=2.44+r24+g16be1518495f-1' 'hyprcursor=0.1.13-7' 'hyprgraphics=0.5.1-4' 'hyprlang=0.6.8-5' 'hyprutils=0.14.2-1' 'libgcc=16.2.1+r23+gd564253eb6c8-1' 'libstdc++=16.2.1+r23+gd564253eb6c8-1' 'libxkbcommon=1.13.2-1' 'wayland=1.26.0-1' 'python>=3.11' 'binutils')
makedepends=('cmake>=3.30' 'ninja' 'pkgconf' 'gcc' 'patch')
options=('!strip' '!debug' '!lto')
_stem='cua-hyprland-plugin-0.26.1-cc54254464c0c9aebfd6547fe7e4a0ceaf0456d7'
_archive_sha256='47bca9e018f32f4fcfe683f91c7475c60368f3b65d318cc35c1f2de88a4ee9ab'
_kit_sha256='089f447e11cacd8c2d3d6cd56528776417c51d353b9677c47c42bba1ef79c9f9'
_profile_sha256='fc3034649af98f7f81178ef575660143c249664b9ff5d0e1ccaf79d7580df17b'
_verifier_sha256='480f649cbd35a0ddc7f49dc0a3a44785402c9e0ec9653b0f8ea4965d52d7f900'
_cxx="${CUA_RELEASE_CXX:-/usr/bin/g++}"
_download_name='cua-hyprland-plugin-0.26.1-cc54254464c0c9aebfd6547fe7e4a0ceaf0456d7-profile-omarchy-stable-20260910-kit-1.1.0-5dacea79a6a5927e59fca7b51e6c04e94fa45133ac1b8fb7f4e1f442d113eb9a-67f89ceb47edd973aa748820eadd02ddbfb3d3e4.tar.gz'
_download_sha256='a89bfa7f6490f598719dfabdd2a5badc137e27b0764fd25e6a05e1f969433520'
source=('https://github.com/trycua/cua/releases/download/cua-hyprland-kit-v1.1.0-omarchy-stable-20260910/cua-hyprland-plugin-0.26.1-cc54254464c0c9aebfd6547fe7e4a0ceaf0456d7-profile-omarchy-stable-20260910-kit-1.1.0-5dacea79a6a5927e59fca7b51e6c04e94fa45133ac1b8fb7f4e1f442d113eb9a-67f89ceb47edd973aa748820eadd02ddbfb3d3e4.tar.gz'
'PROFILE.json')
noextract=("$_download_name")
sha256sums=('a89bfa7f6490f598719dfabdd2a5badc137e27b0764fd25e6a05e1f969433520'
'fc3034649af98f7f81178ef575660143c249664b9ff5d0e1ccaf79d7580df17b')
# Downstream inputs are also checked explicitly when makepkg integrity is skipped.
declare -gA _downstream_sha256=(
['independent-keymaps.patch']='e74d9021111a5c217b12f3e13c1bbdd260e829c60db5fc2f7293ba4846e1a7d7'
['DOWNSTREAM-PROVENANCE.json']='e0c95350ec3ff2dd54a05e9377d79ddffffdb1cf494d05553a90207a3a919f7e'
['downstream.py']='7c9725805ad038737bd4d346c85f27991ce9ddfff436a32ebf515cad1bf98ca1'
['downstream_test.py']='7b9a992979b22a5e061fdf011ac6be59a3f1ae6e0d7c0a8ff631f74e71984c5a'
)
source+=('independent-keymaps.patch' 'DOWNSTREAM-PROVENANCE.json' 'downstream.py' 'downstream_test.py')
sha256sums+=('e74d9021111a5c217b12f3e13c1bbdd260e829c60db5fc2f7293ba4846e1a7d7' 'e0c95350ec3ff2dd54a05e9377d79ddffffdb1cf494d05553a90207a3a919f7e' '7c9725805ad038737bd4d346c85f27991ce9ddfff436a32ebf515cad1bf98ca1' '7b9a992979b22a5e061fdf011ac6be59a3f1ae6e0d7c0a8ff631f74e71984c5a')
_verify_download() {
python3 -I - "$SRCDEST/$_download_name" "$_download_sha256" "$srcdir" "$1" "$SRCDEST/PROFILE.json" <<'CUA_DOWNLOAD_PY'
import hashlib
import io
import json
from pathlib import Path, PurePosixPath
import sys
import tarfile
expected = {'KIT-PROVENANCE.json': '7beb736adfd334eed52e84070177634269e3a09f8bb25971b38606933ff4c997', 'PKGBUILD': 'b945a6a6eda13d0e382770edd5419485e3196041956663eb38f5183b05d30db3', 'PROFILE-PKGBUILD.in': 'c350d1b2375946cb0166893d67a1fc01344ee5e3215bbe801b4d54bb361d6bce', 'PROFILE-USAGE.md': 'c14d8e8103fccab59e558758f4249f86bce700a8723cd4ba1b97b1102e02bbd2', 'PROFILE.json': '5dacea79a6a5927e59fca7b51e6c04e94fa45133ac1b8fb7f4e1f442d113eb9a', 'SHA256SUMS': '34a2126bcfab983f171382aafac3ef218475b652f4583c58f4a04088044cb935', 'SOURCE-PROVENANCE.json': '54f514664c84e1358a435f29cd6befd5661b0b133d76997191c000b10f021a75', 'cua-hyprland-plugin-0.26.1-cc54254464c0c9aebfd6547fe7e4a0ceaf0456d7.tar.gz': '47bca9e018f32f4fcfe683f91c7475c60368f3b65d318cc35c1f2de88a4ee9ab', 'lifecycle.py': 'b18dceb8b8e05b93586ddd3a2f1d90d70ae1c36088e54fc05c89e08585290990', 'profile_bundle.py': 'ac883883814787da477c037f017939ee92c9fb5f46f373af7de1331b24f1f6da', 'profile_verify.py': '480f649cbd35a0ddc7f49dc0a3a44785402c9e0ec9653b0f8ea4965d52d7f900'}
stem = 'cua-hyprland-plugin-0.26.1-cc54254464c0c9aebfd6547fe7e4a0ceaf0456d7'
def require(condition, message):
if not condition:
raise SystemExit(message)
def digest(data):
return hashlib.sha256(data).hexdigest()
archive, checksum, srcdir, mode, profile_path = sys.argv[1:]
archive, srcdir = Path(archive), Path(srcdir)
require(mode in {'check', 'extract'}, 'invalid kit verification mode')
require(archive.is_file() and not archive.is_symlink(), 'outer archive must be a regular file')
data = archive.read_bytes()
require(digest(data) == checksum, 'outer archive checksum mismatch')
payload = {}
with tarfile.open(fileobj=io.BytesIO(data), mode='r:gz') as contents:
for member in contents:
require(member.isfile() and not member.issparse() and not member.pax_headers,
'nonregular outer kit member')
require(member.name in expected and member.name not in payload, 'outer kit inventory mismatch')
content = contents.extractfile(member).read()
require(digest(content) == expected[member.name], 'outer kit member checksum mismatch')
payload[member.name] = content
require(payload.keys() == expected.keys(), 'outer kit inventory mismatch')
# Arch's -3 package has the same compositor and all 498 headers/pkg-config
# files as -2. Derive a version-only profile with the original source/tooling
# and byte checks intact; record its own profile and kit provenance digests.
profile_data = Path(profile_path).read_bytes()
require(digest(profile_data) == 'fc3034649af98f7f81178ef575660143c249664b9ff5d0e1ccaf79d7580df17b',
'local profile checksum mismatch')
profile = json.loads(payload['PROFILE.json'])
profile.update(profile_id='omarchy-hyprland-0562r3-remaps', package_release=5)
profile['hyprland']['package_version'] = '0.56.2-3'
require(json.loads(profile_data) == profile, 'local profile changes more than the reviewed package revision')
payload['PROFILE.json'] = profile_data
provenance = json.loads(payload['KIT-PROVENANCE.json'])
provenance['profile_sha256'] = digest(profile_data)
payload['KIT-PROVENANCE.json'] = (json.dumps(provenance, sort_keys=True, indent=2) + '\n').encode()
recipe = payload['PKGBUILD'].decode()
for old, new in [('pkgrel=2\n', 'pkgrel=5\n'), ('omarchy-stable-20260910', profile['profile_id']),
('hyprland=0.56.2-2', 'hyprland=0.56.2-3'),
('5dacea79a6a5927e59fca7b51e6c04e94fa45133ac1b8fb7f4e1f442d113eb9a', digest(profile_data)),
('7beb736adfd334eed52e84070177634269e3a09f8bb25971b38606933ff4c997', digest(payload['KIT-PROVENANCE.json']))]:
recipe = recipe.replace(old, new)
payload['PKGBUILD'] = recipe.encode()
payload['SHA256SUMS'] = ''.join(f'{digest(body)} {name}\n' for name, body in sorted(payload.items())
if name != 'SHA256SUMS').encode()
expected.update({'PROFILE.json': 'fc3034649af98f7f81178ef575660143c249664b9ff5d0e1ccaf79d7580df17b',
'KIT-PROVENANCE.json': '089f447e11cacd8c2d3d6cd56528776417c51d353b9677c47c42bba1ef79c9f9',
'PKGBUILD': '0cbf2cd34c3c5038a5ed51e6bf84acd81844e4c2bb08ad7203959201bba61da9',
'SHA256SUMS': 'd01b9e0be4c5bcf84cc2ecef9f11f44cedfc1ca5b31afbfcf52aa2efbd636a09'})
for name, content in payload.items():
require(digest(content) == expected[name], 'derived kit checksum mismatch: ' + name)
require(srcdir.is_dir() and not srcdir.is_symlink(), 'srcdir must be a real directory')
kit, source = srcdir / 'cua-profile-kit', srcdir / stem
if mode == 'extract':
require(not kit.exists() and not kit.is_symlink() and not source.exists() and not source.is_symlink(),
'prepare requires fresh kit and source destinations; use a clean srcdir')
source_payload = {}
with tarfile.open(fileobj=io.BytesIO(payload[stem + '.tar.gz']), mode='r:gz') as contents:
for member in contents:
require(member.isfile() and not member.issparse() and not member.pax_headers and
member.name.startswith(stem + '/'), 'invalid source member')
name = member.name[len(stem) + 1:]
path = PurePosixPath(name)
require(name and path.as_posix() == name and not path.is_absolute() and
'..' not in path.parts and '\\' not in name and name not in source_payload,
'unsafe or duplicate source path')
source_payload[name] = contents.extractfile(member).read()
kit.mkdir()
source.mkdir()
for name, content in payload.items():
(kit / name).write_bytes(content)
for name, content in source_payload.items():
destination = source / name
destination.parent.mkdir(parents=True, exist_ok=True)
destination.write_bytes(content)
require(kit.is_dir() and not kit.is_symlink(), 'kit must be a real directory')
require({path.name for path in kit.iterdir()} == expected.keys(), 'extracted kit inventory mismatch')
for name, checksum in expected.items():
path = kit / name
require(path.is_file() and not path.is_symlink() and digest(path.read_bytes()) == checksum,
'extracted kit checksum mismatch: ' + name)
CUA_DOWNLOAD_PY
}
_verify() {
printf '%s %s\n' "$_download_sha256" "$SRCDEST/$_download_name" | sha256sum -c - || return 1
_verify_download check || return 1
# Explicit checks still apply to --skipinteg, --noextract and --repackage.
printf '%s %s\n' "$_archive_sha256" "$srcdir/cua-profile-kit/${_stem}.tar.gz" | sha256sum -c - || return 1
printf '%s %s\n' "$_kit_sha256" "$srcdir/cua-profile-kit/KIT-PROVENANCE.json" | sha256sum -c - || return 1
printf '%s %s\n' "$_profile_sha256" "$srcdir/cua-profile-kit/PROFILE.json" | sha256sum -c - || return 1
printf '%s %s\n' "$_verifier_sha256" "$srcdir/cua-profile-kit/profile_verify.py" | sha256sum -c - || return 1
python3 "$srcdir/cua-profile-kit/profile_verify.py" --kit "$srcdir/cua-profile-kit" --kit-sha256 "$_kit_sha256" \
--archive "$srcdir/cua-profile-kit/${_stem}.tar.gz" --source "$srcdir/$_stem" --cxx "$_cxx"
}
_downstream() {
local name
for name in independent-keymaps.patch DOWNSTREAM-PROVENANCE.json downstream.py downstream_test.py; do
printf '%s %s\n' "${_downstream_sha256[$name]}" "$SRCDEST/$name" | sha256sum -c - || return 1
done
python3 -B "$SRCDEST/downstream.py" "$1" \
--pristine "$srcdir/$_stem" --source "$srcdir/omarchy-source" \
--patch "$SRCDEST/independent-keymaps.patch" --manifest "$SRCDEST/DOWNSTREAM-PROVENANCE.json" \
--kit "$srcdir/cua-profile-kit" --kit-sha256 "$_kit_sha256" \
--archive "$srcdir/cua-profile-kit/${_stem}.tar.gz" --cxx "$_cxx" "${@:2}"
}
prepare() {
_verify_download extract || return 1
_verify || return 1
_downstream prepare
}
build() {
_verify || return 1
_downstream check || return 1
cmake -S "$srcdir/omarchy-source" -B "$srcdir/build" -G Ninja \
-DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_COMPILER="$_cxx" \
-DPKG_CONFIG_EXECUTABLE=/usr/bin/pkgconf -DPKG_CONFIG_ARGN= \
-DPKG_CONFIG_USE_CMAKE_PREFIX_PATH=OFF -DCMAKE_PREFIX_PATH= \
-DBUILD_TESTING=ON -DCUA_HYPRLAND_BUILD_PLUGIN=ON \
-DCUA_HYPRLAND_EXPECTED_VERSION=0.56.2 \
-DCUA_HYPRLAND_INPUT=ON -DCUA_HYPRLAND_TEST_INPUT=OFF \
-DCUA_HYPRLAND_INPUT_TRACE=OFF -DCUA_HYPRLAND_TEST_OPERATOR_KEY= || return 1
cmake --build "$srcdir/build"
}
check() {
_verify || return 1
_downstream check || return 1
python3 -B "$SRCDEST/downstream_test.py" || return 1
(
unset LD_PRELOAD FAKEROOTKEY FAKED_MODE
ctest --test-dir "$srcdir/build" --output-on-failure --no-tests=error
)
}
package() {
check || return 1
_downstream build --build "$srcdir/build" --output "$srcdir/BUILD-PROVENANCE.json" || return 1
install -Dm755 "$srcdir/build/cua-hyprland-plugin.so" \
"$pkgdir/usr/lib/cua/hyprland/cua-hyprland-plugin.so" || return 1
install -Dm644 "$srcdir/$_stem/LICENSE.md" \
"$pkgdir/usr/share/licenses/$pkgname/LICENSE" || return 1
install -Dm644 "$srcdir/$_stem/SOURCE-PROVENANCE.json" \
"$pkgdir/usr/share/$pkgname/SOURCE-PROVENANCE.json" || return 1
local name
install -Dm644 "$srcdir/BUILD-PROVENANCE.json" "$pkgdir/usr/share/$pkgname/BUILD-PROVENANCE.json" || return 1
for name in KIT-PROVENANCE.json PROFILE.json profile_verify.py; do
install -Dm644 "$srcdir/cua-profile-kit/$name" "$pkgdir/usr/share/$pkgname/$name" || return 1
done
for name in DOWNSTREAM-PROVENANCE.json independent-keymaps.patch; do
install -Dm644 "$SRCDEST/$name" "$pkgdir/usr/share/$pkgname/$name" || return 1
done
}
@@ -0,0 +1,40 @@
{
"architecture": "x86_64",
"compiler": {
"comment": "GCC: (GNU) 16.2.1 20260810",
"sha256": "f04191f6a7b2cd7d9a62e1745872b8a6088791e5af6955488c69c9b2c4668bc9",
"version": "16.2.1 20260810"
},
"hyprland": {
"header_version": "0.56.2",
"headers_sha256": "88a6875af00203627b264a5c1f9908781be4ad8d9cee4e577fef174e72dd0e28",
"package_version": "0.56.2-3",
"sha256": "da8fcacf347bcbed83edc40108c6e2298da095e22246bd764e9bb382786cebb2"
},
"kit_version": "1.1.0",
"package_release": 5,
"profile_id": "omarchy-hyprland-0562r3-remaps",
"runtime": {
"basename": "libstdc++.so.6.0.36",
"packages": {
"aquamarine": "0.15.0-2",
"glibc": "2.44+r24+g16be1518495f-1",
"hyprcursor": "0.1.13-7",
"hyprgraphics": "0.5.1-4",
"hyprlang": "0.6.8-5",
"hyprutils": "0.14.2-1",
"libgcc": "16.2.1+r23+gd564253eb6c8-1",
"libstdc++": "16.2.1+r23+gd564253eb6c8-1",
"libxkbcommon": "1.13.2-1",
"wayland": "1.26.0-1"
},
"sha256": "f5fc7380f2ae46fa4053a64be04e7b98109f1066a4bbfff3c37042488aa0be0e"
},
"schema": 2,
"source": {
"archive_sha256": "47bca9e018f32f4fcfe683f91c7475c60368f3b65d318cc35c1f2de88a4ee9ab",
"driver_version": "0.26.1",
"manifest_sha256": "54f514664c84e1358a435f29cd6befd5661b0b133d76997191c000b10f021a75",
"revision": "cc54254464c0c9aebfd6547fe7e4a0ceaf0456d7"
}
}
+212
View File
@@ -0,0 +1,212 @@
# Optional Cua Hyprland plugin
This package targets **Omarchy stable x86_64**, with Inkscape `1.4.4-6` and two independent background-input lanes. Package release `5` includes the Omarchy patch for independent agent keymaps, operation-specific foreground checks, and compatible Num Lock state; the upstream native qualification below covers the unpatched source, not this change. Cua's native qualification is recorded in [the kit's qualification record](https://github.com/trycua/cua/releases/download/cua-hyprland-kit-v1.1.0-omarchy-stable-20260910/QUALIFICATION.md) and [Cua #3698](https://github.com/trycua/cua/pull/3698). Omabot replay and Omarchy's merge decision are recorded in [omarchy-pkgs #346](https://github.com/omacom/omarchy-pkgs/pull/346). Scheduling the recipe does not expand the qualified stable target.
The plugin is optional. Cua Driver works independently, and installation does not load the plugin or enable input. The package follows the normal edge-to-RC-to-stable promotion path instead of the fast release ring. Its PKGBUILD limits builds to x86_64; only stable x86_64 is a qualified target.
## Source and build profile
The package uses the [Driver 0.26.1 plugin source](https://github.com/trycua/cua/releases/tag/cua-driver-rs-v0.26.1),
including the [desktop-fault cleanup repair](https://github.com/trycua/cua/pull/3702).
It is not a repackaging of the unmodified 0.24.0 plugin.
The qualified upstream Driver pairing is `cua-driver-bin 0.27.0-1`, with input protocol v3. Driver 0.27.0 contains the bounded stale-geometry retry validated with the upstream module; its production plugin source is the base for the downstream patch used here. Discovery protocol v2 is separate. A newer Driver release is a changed pairing and requires affected replay before promotion.
Profile `omarchy-hyprland-0562r3-remaps`, kit tooling `1.1.0`, and package release `5` pin:
- Hyprland `0.56.2-3`, headers `0.56.2`, and measured executable/header hashes.
- GCC `16.2.1 20260810`, including compiler bytes and emitted ELF identity.
- Shared runtime `libstdc++.so.6.0.36`, its bytes, and exact ABI package versions.
This profile derives from Cua's `omarchy-stable-20260910` profile. Arch's
Hyprland `-3` package splits out `hyprpm` and changes package dependencies;
its compositor executable and all 498 header/pkg-config files are byte-identical
to `-2`. Both executables have SHA-256
`da8fcacf347bcbed83edc40108c6e2298da095e22246bd764e9bb382786cebb2`.
The checked-in `PROFILE.json` changes only the profile name, package release,
and exact Hyprland package version. Compiler, runtime, upstream source, executable,
and header identities remain unchanged; the separately recorded patch changes the build source. The download wrapper verifies the
original kit before deriving the updated profile, recipe, and provenance,
then verifies every derived member against its recorded digest.
The native qualification below was recorded with package release `2` and
Hyprland `-2`. The downstream keymap change needs its own application and Driver replay before promotion. The `-3` dependency must reach a destination channel before
this artifact can be installed there; publication still follows edge → RC → stable.
The generated `PKGBUILD` identifies the immutable kit download, outer checksum,
and member checksums. The kit records the full source and tooling revisions,
profile digest, and source archive/manifest digests. Do not infer compatibility
from a matching version label or substitute an unreviewed profile.
The download wrapper verifies its complete inventory before executing downloaded
tooling. It preserves the source archive and its historical embedded verifier,
but explicitly uses the new kit's `profile_verify.py`. Source integrity,
package-owned headers, pkg-config selection, compiler probes, runtime equality,
and production flags remain mandatory. Packaging runs all bundled CTests even
with `--nocheck` or `--repackage`; `--skipinteg` does not bypass recipe checks.
Production input is built in; experimental signed input and tracing are off.
The pristine upstream archive, manifest, and verifier remain unchanged. `independent-keymaps.patch` is applied to a separate source tree, and `DOWNSTREAM-PROVENANCE.json` pins the patch and every resulting source file. Build, check, and package revalidate both trees, including when makepkg integrity checks are skipped. `BUILD-PROVENANCE.json` records the upstream base under `source`, the applied change under `downstream`, and the final module digest; the downstream manifest and patch are installed beside it. This preserves the existing compiler, headers, runtime, and consumer checks without representing the modified module as an unmodified upstream build.
## Keyboard behavior
Each background lane owns a canonical US keymap and independent modifier state. The physical keyboard keeps its layout, Compose key, and remaps. No installation or activation step edits `input:kb_*`. Existing Driver keycodes are interpreted by the agent keyboard, so this does not add Unicode, IME, or new Driver text routes.
Plain click, scroll, drag, and foreground activation do not require a canonical keyboard layout. Foreground keys still use the primary seat: the plugin checks the requested key and modifier sequence against its actual XKB map before activation or input. Unrelated remaps are accepted; a sequence whose symbols or modifier/lock transitions differ from the canonical meaning is refused with `unsupported_layout`. Arbitrary foreground layout translation remains outside protocol v3.
Foreground typing preserves Num Lock and admits a requested key sequence only when its symbols and shortcut semantics still match the canonical meaning. Num Lock does not block unaffected letters, top-row digits, Enter, or compatible shortcuts; a keypad sequence whose meaning changes is refused. Caps Lock, other unsupported lock states, held or latched modifiers, and nonzero layout groups remain guarded.
Both routes retain target/conflict checks and cancellation on desktop/keymap changes. `hyprctl -j cua:status` exposes `keyboard_layout_independent: true` and `foreground_numlock_compatible: true` for installers to distinguish this implementation from an older mapped module. The marker does not identify every future package revision; plugin updates still require a fresh desktop session.
## Historical upstream qualification
The initial app scope is native Wayland Inkscape `1.4.4-6` with the canonical
US keymap. Two lanes require independent Driver processes and distinct native
application clients, not merely two windows. This is concurrency inside one
desktop account, not multi-user or mutually untrusted-agent isolation.
Cua's retained evidence covers background application effects, two-lane overlap,
third-owner refusal, primary-input preservation, conflicts, stale targets and
geometry, cancellation, desktop faults, recovery, and cold package transitions.
Production-package app checks and independent primary observers are separate
from trace-enabled diagnostics. Cua's retained canonical run
`433ce968ee164d5e8e3226e800db93a6` recorded all 128 required cells: 87
deliveries and 41 expected refusals, with no failures or skips; its completion
report has SHA-256
`1eda4cc008ea6b27d96c21d58f8041834398a43409642376bafe84ad38f0e112`.
That historical run used source-built released Driver 0.24.0; earlier real-app
checks separately used the then-published Omarchy `cua-driver-bin 0.24.0-1`
executable. A separate later Omabot replay reported 124 passes and four
failures, plus seven incomplete native cases. Cua subsequently passed those
four cells with the repair candidate shipped in Driver 0.27.0 and passed all
seven lifecycle cases. A final exact-release Fleet replay then passed all four
cells with Driver and harness source `082de4344b731ae4738ddc6a6f13f21bb3c49a85`,
released Driver binary SHA-256
`bb1b65394e912246220f9f758c9efbbf6260cec16e3562e62a361fa95329377f`,
and evidence SHA-256
`b6c47278a3db398ecbb4d7aed2bce44a467e2af37e6d4ccfc236d1e07aa70af7`.
See the linked qualification record and PR description for exact artifacts and observation limits. Omarchy replay of the 0.27.0 package pairing is recorded in #346; it does not qualify later Driver releases.
Duplicate motion notifications are retained and counted. They are acceptable
only when pointer identity, coordinates, focus, held input, and foreground
interaction remain unchanged. Actual motion—including moving away and back—
fails isolation. After cancellation, an inert agent pointer may remain parked
if held input is released and authority is revoked.
Current LibreOffice Calc `26.8`, Chromium/Electron raw background input,
XWayland, Unicode/IME, non-US layouts, and modified pointer gestures are outside
this profile. The plugin does not widen Driver's application admission.
Foreground input, capture, and accessibility have separate contracts; a
background refusal never authorizes a hidden foreground fallback or unlock.
## Omabot replay before promotion
Build the unsigned candidate in edge:
```sh
./bin/build --package cua-hyprland-plugin --arch x86_64 --mirror edge
```
In a fresh worker matching the reviewed profile:
1. Verify the downloaded kit and source identities against the reviewed recipe.
Record the actual channel snapshot, Driver, compiler, compositor, runtime,
applications, keymap, and resulting package/module hashes.
2. Require all bundled tests and native compatibility checks. Do not weaken
exact dependencies or replace the compositor to make the build pass.
3. Install through pacman and activate in a fresh session. Replay the declared
app, two-lane, refusal, primary-input, cancellation, and fault/recovery checks
against the actual packaged Driver and module. A Cua Fleet result is not an
Omabot result; matching source alone does not certify different binaries.
4. Verify restart-based upgrade, rollback, removal, and reinstallation. Retain
evidence that binds each result to the package and mapped module bytes.
After the exact package and Driver pairing passes, advance the signed artifact with `bin/repo advance --from edge --to rc --package cua-hyprland-plugin`, validate RC, and then use `bin/repo advance --from rc --to stable --package cua-hyprland-plugin`. Do not rebuild independently in RC or stable.
Portable tests, screenshots, health reports, and a successful build do not
replace native qualification. Recheck the published Driver package before
rollout and qualify any changed pairing explicitly.
## Activation, updates, and removal
The package installs the module at
`/usr/lib/cua/hyprland/cua-hyprland-plugin.so` and provenance plus the consumer
verifier under `/usr/share/cua-hyprland-plugin/`. There are no hooks, autoloading,
configuration edits, or hot replacement.
Save your work and exit Hyprland before installing, replacing, or removing the
package. Install the exact reviewed package from a text console, then start a
fresh session. Before loading, run the consumer check with this package's derived
kit-provenance digest:
```sh
python3 /usr/share/cua-hyprland-plugin/profile_verify.py \
--kit /usr/share/cua-hyprland-plugin \
--kit-sha256 089f447e11cacd8c2d3d6cd56528776417c51d353b9677c47c42bba1ef79c9f9 \
--consumer /usr/lib/cua/hyprland/cua-hyprland-plugin.so
```
This check requires Python 3.11+, binutils `readelf`, and system `ldd`/`pacman`,
not a compiler or headers. If it fails, leave the plugin unloaded. It verifies
installed compatibility, not runtime mapping or input effects.
After that check passes in the fresh session, load the module explicitly:
```sh
hyprctl plugin load /usr/lib/cua/hyprland/cua-hyprland-plugin.so
hyprctl -j cua:status
```
Loading alone does not enable input. Use Omarchy's explicit Cua Input toggle when available; it verifies the installed profile and loaded capability and removes the legacy copied toggle's keyboard override. If it reports an older mapped plugin, disable Cua Input and log out and back in before enabling it again. Never hot-unload and reload the module.
For manual activation, add only this plugin setting to a sourced Hyprland Lua configuration file, preserving all existing input settings:
```lua
hl.config({
plugin = { cua = { enabled = true } },
})
```
Then reload and inspect status:
```sh
hyprctl reload
hyprctl -j cua:status
```
Continue only when status reports `keyboard_layout_independent: true`, `foreground_numlock_compatible: true`, input protocol v3, input capability, socket paths, and the expected compositor identity. Do not change `kb_layout`, `kb_options`, or NumLock for background input. If you previously followed the stock-US override instructions, remove only that Cua-specific override and reload to restore your underlying personal settings.
Start Driver with `CUA_DRIVER_RS_ENABLE_WAYLAND=1`. In a new disposable Inkscape document, test an admitted background key operation and pointer operation, then verify the result in both a fresh snapshot and a saved/reopened SVG. Driver text-route restrictions still apply. Never test against an existing document or automatically replay an action with a partial or unknown outcome.
To disable input, turn the Cua Input toggle off, or remove the manual `plugin.cua.enabled` setting and reload. Confirm that status reports input disabled. Retained inert agent pointers can remain until the compositor exits; disabling input does not unload the mapped module.
Before an incompatible desktop update, remove operator-added plugin activation
settings, save work, and exit the graphical session. From a text console, run
`sudo pacman -R cua-hyprland-plugin`, then apply the normal desktop update and
verify a fresh session without the plugin. Declining removal preserves the
dependency refusal. Disabling input alone leaves exact dependencies installed;
do not force an upgrade past them.
Retain the previous package with its matching compositor, runtime, Driver, and
provenance as a rollback set. Restore a consistent set outside the graphical
session, then repeat the fresh-session consumer and app checks. Do not hot
unload/reload or replace a mapped module.
## Ownership and publication
The [agreed ownership split](https://github.com/omacom/omarchy-pkgs/pull/346#issuecomment-5612834061)
assigns profiles, build kits, plugin fixes, and native input evidence to Cua.
Francesco (@f-trycua) is the Cua contact through this PR. Omarchy owns package
integration, dependency-change detection, Omabot validation, and signing and
publication decisions. Spencer (@spencerbull) and Emir (@emirb) jointly own
that Omarchy package and release path. Maintenance is best effort, with no
turnaround commitment.
Edge detects upcoming incompatibilities; RC validates the intended stable
environment. Mirror/channel changes and changes to ABI dependencies, Driver,
or admitted apps request a new candidate and affected qualification. They do
not establish compatibility or authorize additional publication channels.
This package participates in scheduled builds, but has no upstream polling, AUR synchronization, or automatic rebuild bump. A checked-in version change or missing artifact can queue it for the normal release pipeline. Build selection, promotion, `push`, and `upload-prebuilt` do not supply native qualification or authorize a broader support claim. Do not silently substitute newly rebuilt bytes during signing or publication.
Before calling a release complete, install the signed published package on a fresh consumer, verify its signature and package/module digests, and perform a short activation, background-action, and cleanup smoke. Edge and RC artifacts are compatibility checkpoints, not qualified support for those environments.
@@ -0,0 +1,96 @@
#!/usr/bin/env python3
"""Verify the Omarchy patch separately from the unchanged upstream source kit."""
import argparse
import hashlib
import importlib.util
import json
from pathlib import Path, PurePosixPath
import shutil
import subprocess
def require(condition, message):
if not condition:
raise ValueError(message)
def digest(path):
return hashlib.sha256(path.read_bytes()).hexdigest()
def inventory(root):
require(root.is_dir() and not root.is_symlink(), "source must be a real directory")
result = {}
for path in root.rglob("*"):
require(not path.is_symlink() and (path.is_dir() or path.is_file()), "nonregular source entry")
if path.is_file():
result[path.relative_to(root).as_posix()] = digest(path)
return result
def verify_inputs(pristine, patch, manifest):
require(manifest["schema"] == 1, "unsupported downstream schema")
require(patch.is_file() and not patch.is_symlink() and digest(patch) == manifest["patch_sha256"],
"downstream patch checksum mismatch")
require(digest(pristine / "SOURCE-PROVENANCE.json") == manifest["upstream_manifest_sha256"],
"downstream base manifest mismatch")
require(manifest["files"], "empty downstream inventory")
for name in manifest["files"]:
path = PurePosixPath(name)
require(name and not path.is_absolute() and path.as_posix() == name and
".." not in path.parts and "\\" not in name, "invalid downstream path")
def verify_tree(source, manifest):
require(inventory(source) == manifest["files"], "patched source inventory/checksum mismatch")
def prepare(pristine, source, patch, manifest):
require(not source.exists() and not source.is_symlink(), "patched source requires a fresh destination")
shutil.copytree(pristine, source)
subprocess.run(["patch", "--batch", "--fuzz=0", "-p1", "-i", str(patch.resolve())], cwd=source, check=True)
verify_tree(source, manifest)
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("mode", choices=("prepare", "check", "build"))
parser.add_argument("--pristine", required=True, type=Path)
parser.add_argument("--source", required=True, type=Path)
parser.add_argument("--patch", required=True, type=Path)
parser.add_argument("--manifest", required=True, type=Path)
parser.add_argument("--kit", required=True, type=Path)
parser.add_argument("--kit-sha256", required=True)
parser.add_argument("--archive", required=True, type=Path)
parser.add_argument("--cxx", required=True, type=Path)
parser.add_argument("--build", type=Path)
parser.add_argument("--output", type=Path)
args = parser.parse_args()
try:
# PKGBUILD authenticates the verifier and this helper before execution.
spec = importlib.util.spec_from_file_location("upstream_profile", args.kit / "profile_verify.py")
upstream = importlib.util.module_from_spec(spec)
spec.loader.exec_module(upstream)
profile, kit = upstream.verify_kit(args.kit, args.kit_sha256)
base = upstream.verify_archive(args.archive, profile)
require(upstream.verify_source(args.pristine, profile) == base, "upstream source identity mismatch")
manifest = upstream.read_json(args.manifest.read_bytes())
verify_inputs(args.pristine, args.patch, manifest)
if args.mode == "prepare":
prepare(args.pristine, args.source, args.patch, manifest)
else:
verify_tree(args.source, manifest)
if args.mode == "build":
require(args.build is not None and args.output is not None, "build evidence requires output")
native = upstream.verify_native(args.cxx, profile)
native["module_sha256"] = upstream.verify_build(args.build, args.source, args.cxx, profile)
native["module_runtime_sha256"] = profile["runtime"]["sha256"]
args.output.write_bytes(upstream.json_bytes(dict(native, source=base, profile=profile,
kit=kit, downstream=manifest)))
except (ValueError, KeyError, TypeError, OSError, subprocess.CalledProcessError) as error:
parser.exit(1, f"error: {error}\n")
if __name__ == "__main__":
main()
@@ -0,0 +1,75 @@
#!/usr/bin/env python3
"""Exercise downstream integrity with real patch application and tampering."""
import hashlib
from pathlib import Path
import tempfile
import unittest
import downstream
class DownstreamTests(unittest.TestCase):
def setUp(self):
self.temp = tempfile.TemporaryDirectory()
self.addCleanup(self.temp.cleanup)
self.root = Path(self.temp.name)
self.pristine = self.root / "pristine"
self.pristine.mkdir()
(self.pristine / "SOURCE-PROVENANCE.json").write_text("upstream\n")
(self.pristine / "input.cpp").write_text("old\n")
self.patch = self.root / "change.patch"
self.patch.write_text("--- a/input.cpp\n+++ b/input.cpp\n@@ -1 +1 @@\n-old\n+new\n")
self.source = self.root / "patched"
self.manifest = {
"schema": 1,
"patch_sha256": downstream.digest(self.patch),
"upstream_manifest_sha256": downstream.digest(self.pristine / "SOURCE-PROVENANCE.json"),
"files": {"SOURCE-PROVENANCE.json": downstream.digest(self.pristine / "SOURCE-PROVENANCE.json"),
"input.cpp": hashlib.sha256(b"new\n").hexdigest()},
}
def test_applies_patch_without_changing_upstream(self):
downstream.verify_inputs(self.pristine, self.patch, self.manifest)
downstream.prepare(self.pristine, self.source, self.patch, self.manifest)
self.assertEqual((self.pristine / "input.cpp").read_text(), "old\n")
self.assertEqual((self.source / "input.cpp").read_text(), "new\n")
def test_changed_patch_refuses(self):
self.patch.write_text(self.patch.read_text().replace("+new", "+bad"))
with self.assertRaisesRegex(ValueError, "patch checksum"):
downstream.verify_inputs(self.pristine, self.patch, self.manifest)
def test_changed_base_manifest_refuses(self):
(self.pristine / "SOURCE-PROVENANCE.json").write_text("different\n")
with self.assertRaisesRegex(ValueError, "base manifest"):
downstream.verify_inputs(self.pristine, self.patch, self.manifest)
def test_tampered_missing_and_extra_files_refuse(self):
downstream.prepare(self.pristine, self.source, self.patch, self.manifest)
file = self.source / "input.cpp"
for content in ("tampered\n", None):
if content is None:
file.unlink()
else:
file.write_text(content)
with self.assertRaisesRegex(ValueError, "inventory/checksum"):
downstream.verify_tree(self.source, self.manifest)
file.write_text("new\n")
(self.source / "unexpected.cpp").write_text("extra\n")
with self.assertRaisesRegex(ValueError, "inventory/checksum"):
downstream.verify_tree(self.source, self.manifest)
def test_symlink_and_reused_destination_refuse(self):
downstream.prepare(self.pristine, self.source, self.patch, self.manifest)
with self.assertRaisesRegex(ValueError, "fresh destination"):
downstream.prepare(self.pristine, self.source, self.patch, self.manifest)
file = self.source / "input.cpp"
file.unlink()
file.symlink_to(self.pristine / "input.cpp")
with self.assertRaisesRegex(ValueError, "nonregular"):
downstream.verify_tree(self.source, self.manifest)
if __name__ == "__main__":
unittest.main()
File diff suppressed because it is too large. Load diff
+17 -2
View File
@@ -1,5 +1,20 @@
{
"source": "aur",
"source": "local",
"release_ring": "fast",
"upstream_commit": "c5073d62f60f3973a14340eb1b40fc5b5a4ea9f0"
"upstream": {
"watch": {
"json": "https://www.cursor.com/api/download?platform=linux-x64&releaseTrack=stable",
"path": "version",
"fields": {
"commit": "commitSha"
},
"variables": {
"_commit": "{commit}"
}
}
},
"origin": {
"aur": "cursor-bin",
"commit": "a87a0de17b0d8176ee4f046499b1e6e4b37cc422"
}
}
+4 -8
View File
@@ -1,8 +1,8 @@
# Maintainer: Gunther Schulz <dev@guntherschulz.de>
pkgname=cursor-bin
pkgver=3.19.13
pkgrel=1
pkgver=3.20.21
pkgrel=2
pkgdesc='AI-first coding environment'
arch=('x86_64')
url="https://www.cursor.com"
@@ -12,15 +12,11 @@ _electron=electron42
depends=(xdg-utils ripgrep $_electron nodejs
'gcc-libs' 'hicolor-icon-theme' 'libxkbfile')
options=(!strip !debug) # Don't break ext of VSCode
_commit=dd066f332fcea7382764400fde902f61920648d5
_commit=f09fca384ceca23f7bf21f9c23655b162641d747
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)
sha512sums=('SKIP'
'937299c6cb6be2f8d25f7dbc95cf77423875c5f8353b8bd6cd7cc8e5603cbf8405b14dbf8bd615db2e3b36ed680fc8e1909410815f7f8587b7267a699e00ab37'
'793f9ff6306e3992ac89802d98110cba288ea1181a901467333293b7d76182ef9792c2a39ff49d9347a18a174b1f42bc58862091dff583f4146c2704eea28033'
'e79fe7659f59d1ae02fc68816399bfd31587315df6cdb6ccf1d0ca76f7cdc692c2a42b30591c0091147bd97ef14b1c7745dc26bd7cb3ea6bba45698e5044fa2a')
sha512sums[0]=3c952035fa1809caded7059b7addbb01a9143cd248ecff27cdb3cb3af47d4bc824ed60dfd9757e105a342e53280e075d81f775790295a3272818bab5a994b305
sha512sums=('8329138d207309d16410f1cb58da18eea1a034a35f2bdd022ef9b373bb8f1b3d80e489e65256b7283c40e10da77962d158bfa3a64e742337a942633810d80dbe' '937299c6cb6be2f8d25f7dbc95cf77423875c5f8353b8bd6cd7cc8e5603cbf8405b14dbf8bd615db2e3b36ed680fc8e1909410815f7f8587b7267a699e00ab37' '793f9ff6306e3992ac89802d98110cba288ea1181a901467333293b7d76182ef9792c2a39ff49d9347a18a174b1f42bc58862091dff583f4146c2704eea28033' 'e79fe7659f59d1ae02fc68816399bfd31587315df6cdb6ccf1d0ca76f7cdc692c2a42b30591c0091147bd97ef14b1c7745dc26bd7cb3ea6bba45698e5044fa2a')
noextract=(cursor_${pkgver}_amd64.deb) # avoid double tarball
_app=usr/share/cursor/resources/app
package() {
+13 -2
View File
@@ -1,5 +1,16 @@
{
"source": "aur",
"source": "local",
"release_ring": "fast",
"upstream_commit": "954e5556aa88f2309b86992b231c8b76f273cfb0"
"upstream": {
"watch": {
"regex": "https://cursor.com/install",
"pattern": "https://downloads\\.cursor\\.com/lab/(?P<version>[0-9]{4}\\.[0-9]{2}\\.[0-9]{2})-(?P<hash>[a-f0-9]+)/",
"version": "{version}.1.{hash}",
"sequence": true
}
},
"origin": {
"aur": "cursor-cli",
"commit": "954e5556aa88f2309b86992b231c8b76f273cfb0"
}
}
+3 -3
View File
@@ -1,7 +1,7 @@
# Maintainer: Ismet Togay <ismet.togay at gmail dot com>
# Contributor: Christopher Cooper <christopher@cg505.com>
pkgname=cursor-cli
pkgver=2026.08.25.1.3e8eec8
pkgver=2026.09.10.1.fd3934a
# Upstream is YYYY.MM.DD-<hash>. pkgver cannot contain hyphens, and hashes are
# not monotonically ordered, so pkgver is YYYY.MM.DD.<n>.<hash>: n resets to 1
# on a new date and increments when the same date gets a new hash.
@@ -25,8 +25,8 @@ source_x86_64=("cursor-cli-${_upstream_ver}-x86_64.tar.gz::https://downloads.cur
source_aarch64=("cursor-cli-${_upstream_ver}-aarch64.tar.gz::https://downloads.cursor.com/lab/${_upstream_ver}/linux/arm64/agent-cli-package.tar.gz")
b2sums=('d241ee9895bdb1c17514438fde8528222a8f2326568bd7a033d7a1b11432ce6b4575ff1a50625764bfe6bc6f8a9dc060f7439c3be7e95f8fd02912cdd37a011d'
'1928e04c713e13911ea607f84c3e4a2fed1f76af9795503811078f43d2b53c753e28b2233e553fc17e766831800fb0dbc272aad2a80b387f95ba6071d7d4116a')
b2sums_x86_64=('cd5485f7524688e1a688daa2b64669c76bedcdd9ab87638bac78f9b42c2442bd5000559920a2f5171e00b5eb7fcf737f9111ef296f1eba40369cebf3279ee0a9')
b2sums_aarch64=('191ff1c538f294134d93d501e9ca68cc6d4f8101cb1cee449753dfefcd9039daecd7bf9f9f2baf9ee9262f40eea19aaf15f834c9abb56d967fb48a3a3f23b8ea')
b2sums_x86_64=('121c0128fd630565c7000b86ae53bf76e73fd72c1ab8ba2509fae7739b1c7f4bed0587a82137340303ac4704f3344cf63a10eab0e8bea262c8e48bbb21bcb742')
b2sums_aarch64=('bfc0f540190214396df3cbb93c411ab8055677bc1dd0b0e76d1b914d6c6d90af12519434227ba8b38a38249f1bfe0a8848f47e16dc048b4fa005d366815307be')
prepare() {
# Block cursor-agent auto-updates by making its versions directory
+11 -2
View File
@@ -1,4 +1,13 @@
{
"source": "aur",
"upstream_commit": "80626b9efefc215d55eede4330d56c017a522682"
"source": "local",
"upstream": {
"watch": {
"github": "dropbox/dbxcli",
"pattern": "v(?P<version>[0-9]+(?:\\.[0-9]+)*)"
}
},
"origin": {
"aur": "dbxcli-bin",
"commit": "80626b9efefc215d55eede4330d56c017a522682"
}
}
+4 -4
View File
@@ -2,7 +2,7 @@
_pkgname="dbxcli"
pkgname="${_pkgname}-bin"
pkgver=3.7.2
pkgver=3.7.3
pkgrel=1
pkgdesc="A command line client for Dropbox built using the Go SDK"
arch=(
@@ -33,9 +33,9 @@ source_armv7h=(
source_x86_64=(
"${url}/releases/download/v${pkgver}/${_pkgname}_${pkgver}_linux_amd64.tar.gz"
)
sha256sums_aarch64=('fc451469c87ad0e4f2d3201f6e36f2b57c1119e5c0adb5ebaec6182b3eb378ad')
sha256sums_armv7h=('22f21d8b40dd23b5777ffb0ec07696d135d2910daa84f46679231a573aeb9899')
sha256sums_x86_64=('1b1fa67fb3d3f6e2940566afdb84f072a21804ddfdb4f0dfade385ec0683ee63')
sha256sums_aarch64=('9d654da62a1ac10c9e32ee8f66fa6cc8d88ed29bc95555435a5ce4255eb4b96a')
sha256sums_armv7h=('8067cee274dc2f062a06ceda26200c44d9251336ec235f7d7a3826743c9a379e')
sha256sums_x86_64=('fee977ce4144174356cd7d1bae0b546aecad2570f14666bd44417e96af943484')
prepare() {
local source_array="source_${CARCH}[0]"
@@ -0,0 +1,19 @@
{
"source": "local",
"origin": {
"aur": "dotnet-core-bin",
"commit": "2c499d7ce634efb8e93eee4c4239490b02e98e09"
},
"upstream": {
"watch": {
"json": "https://builds.dotnet.microsoft.com/dotnet/release-metadata/10.0/releases.json",
"path": "latest-sdk",
"fields": {
"runtime": "latest-runtime"
},
"variables": {
"_runtimever": "{runtime}"
}
}
}
}
+131
View File
@@ -0,0 +1,131 @@
# Maintainer: Attila Greguss <floyd0122[at]gmail[dot]com>
# Co-Maintainer: Nate Plumm <nate[at]ceresta[dot]com>
pkgbase=dotnet-core-bin
pkgname=(
'dotnet-host-bin'
'aspnet-runtime-bin'
'dotnet-runtime-bin'
'dotnet-sdk-bin'
'dotnet-targeting-pack-bin'
'aspnet-targeting-pack-bin'
)
# Version the split family by SDK release; dependencies expose runtime versions.
pkgver=10.0.401
_runtimever=10.0.12
_sdkver=$pkgver
_short_ver=10.0
pkgrel=1
arch=('x86_64' 'armv7h' 'aarch64')
url='https://www.microsoft.com/net/core'
license=('MIT')
options=('staticlibs')
source=('dotnet.sh')
source_armv7h=("https://builds.dotnet.microsoft.com/dotnet/Sdk/${_sdkver}/dotnet-sdk-${_sdkver}-linux-arm.tar.gz")
source_aarch64=("https://builds.dotnet.microsoft.com/dotnet/Sdk/${_sdkver}/dotnet-sdk-${_sdkver}-linux-arm64.tar.gz")
source_x86_64=("https://builds.dotnet.microsoft.com/dotnet/Sdk/${_sdkver}/dotnet-sdk-${_sdkver}-linux-x64.tar.gz")
sha512sums=('768151c7179fb6a126b3de9cae01e363e8894f6fab384b1e2c5066c2adca4578638983b1b62aea10dd18045e6d6e8f8ea13280481134de94f004a118919b2c06')
sha512sums_armv7h=('94a8a52862ca9f0de1075a468d6e4e307a1d4463098a9e8266e66939709a812b0126e212cce7e8c6feae6b078d86c8b77e088814a0e32531edb0da0a1ce90c11')
sha512sums_aarch64=('58ace73ced6b4360754689a686bdfb8a317f4da6cb8bb416dbc7d0ba9f47e43e3c09f5eb1f1a1cfaacbd10df9558da4882bf2a5e195d6ab56a02c1f9f76102ed')
sha512sums_x86_64=('51c8b999af9e8dd9998c9edc5944e19a90788862068acd38694e098889054ce8c23d4f0c5cccfa16bf187d044562359e5ee69a9f8ad0bbe913ba90311fbce25b')
# Keep each split package's notices usable when installed independently.
_install_license() {
install -Dm644 LICENSE.txt "$pkgdir/usr/share/licenses/$pkgname/LICENSE.txt"
install -Dm644 ThirdPartyNotices.txt "$pkgdir/usr/share/licenses/$pkgname/ThirdPartyNotices.txt"
}
package_dotnet-host-bin() {
pkgdesc='A generic driver for the .NET Core Command Line Interface (binary)'
provides=("dotnet-host" "dotnet-host=${_runtimever}")
conflicts=('dotnet-host')
depends=(
'libgcc'
'libstdc++'
'glibc'
)
install -dm 755 "${pkgdir}"/usr/{bin,lib,share/{dotnet,dnx}}
cp -dr --no-preserve='ownership' dotnet host dnx "${pkgdir}"/usr/share/dotnet/
_install_license
ln -sf /usr/share/dotnet/dotnet "${pkgdir}"/usr/bin/dotnet
ln -sf /usr/share/dotnet/dnx "${pkgdir}"/usr/bin/dnx
ln -sf /usr/share/dotnet/host/fxr/"${_runtimever}"/libhostfxr.so "${pkgdir}"/usr/lib/libhostfxr.so
install -Dm 644 "${srcdir}"/dotnet.sh -t "${pkgdir}"/etc/profile.d/
}
package_dotnet-runtime-bin() {
pkgdesc='The .NET Core runtime (binary)'
depends=(
"dotnet-host>=${_runtimever}"
'libgcc'
'libstdc++'
'glibc'
'icu'
'libunwind'
'zlib'
'openssl'
)
optdepends=('lttng-ust2.12: CoreCLR tracing')
provides=("dotnet-runtime=${_runtimever}" "dotnet-runtime-${_short_ver}")
conflicts=("dotnet-runtime=${_runtimever}" "dotnet-runtime-${_short_ver}")
install -dm 755 "${pkgdir}"/usr/share/{dotnet/shared,licenses}
cp -dr --no-preserve='ownership' shared/Microsoft.NETCore.App "${pkgdir}"/usr/share/dotnet/shared/
_install_license
}
package_aspnet-runtime-bin() {
pkgdesc='The ASP.NET Core runtime (binary)'
depends=('dotnet-runtime-bin')
provides=("aspnet-runtime=${_runtimever}" "aspnet-runtime-${_short_ver}")
conflicts=("aspnet-runtime=${_runtimever}" "aspnet-runtime-${_short_ver}")
install -dm 755 "${pkgdir}"/usr/share/{dotnet/shared,licenses}
cp -dr --no-preserve='ownership' shared/Microsoft.AspNetCore.App "${pkgdir}"/usr/share/dotnet/shared/
_install_license
}
package_dotnet-sdk-bin() {
pkgdesc='The .NET Core SDK (binary)'
depends=(
'glibc'
'libgcc'
'libstdc++'
'dotnet-runtime-bin'
'dotnet-targeting-pack-bin'
'aspnet-runtime-bin'
'aspnet-targeting-pack-bin'
)
provides=("dotnet-sdk-bin" "dotnet-sdk=${pkgver}" "dotnet-sdk-${_short_ver}=${pkgver}")
conflicts=("dotnet-sdk-bin" "dotnet-sdk=${pkgver}" "dotnet-sdk-${_short_ver}")
install -dm 755 "${pkgdir}"/usr/share/{dotnet,licenses}
cp -dr --no-preserve='ownership' sdk sdk-manifests templates "${pkgdir}"/usr/share/dotnet/
_install_license
}
package_dotnet-targeting-pack-bin() {
pkgdesc='The .NET Core targeting pack (binary)'
provides=(dotnet-targeting-pack=${_runtimever} dotnet-targeting-pack-${_short_ver})
conflicts=(dotnet-targeting-pack=${_runtimever} dotnet-targeting-pack-${_short_ver})
if [ $CARCH = 'x86_64' ]; then msarch=x64;
elif [ $CARCH = 'armv7h' ]; then msarch=arm;
elif [ $CARCH = 'aarch64' ]; then msarch=arm64; fi
install -dm 755 "${pkgdir}"/usr/share/{dotnet,dotnet/packs,licenses}
cp -dr --no-preserve='ownership' packs/Microsoft.NETCore.App.{Host.linux-${msarch},Ref} "${pkgdir}"/usr/share/dotnet/packs/
_install_license
}
package_aspnet-targeting-pack-bin() {
pkgdesc='The ASP.NET Core targeting pack (binary)'
depends=(dotnet-targeting-pack-bin)
provides=(aspnet-targeting-pack=${_runtimever} aspnet-targeting-pack-${_short_ver})
conflicts=(aspnet-targeting-pack=${_runtimever} aspnet-targeting-pack-${_short_ver})
install -dm 755 "${pkgdir}"/usr/share/{dotnet,dotnet/packs,licenses}
cp -dr --no-preserve='ownership' packs/Microsoft.AspNetCore.App.Ref "${pkgdir}"/usr/share/dotnet/packs/
_install_license
}
+19
View File
@@ -0,0 +1,19 @@
# Set location for AppHost lookup
[ -z "$DOTNET_ROOT" ] && export DOTNET_ROOT=/usr/share/dotnet
# Add dotnet directory to PATH, according to docs it must be added, plus VSCode C# Dev Kit doesn't work without this.
# See https://learn.microsoft.com/en-us/dotnet/core/install/linux-scripted-manual#set-environment-variables-system-wide
case "$PATH" in
*"$DOTNET_ROOT"* ) true ;;
* ) PATH="$PATH:$DOTNET_ROOT" ;;
esac
# Add dotnet tools directory to PATH
[ -z "$DOTNET_TOOLS_PATH" ] && export DOTNET_TOOLS_PATH="$HOME/.dotnet/tools"
case "$PATH" in
*"$DOTNET_TOOLS_PATH"* ) true ;;
* ) PATH="$PATH:$DOTNET_TOOLS_PATH" ;;
esac
# Extract self-contained executables under HOME to avoid multi-user issues from using the default '/var/tmp'
[ -z "$DOTNET_BUNDLE_EXTRACT_BASE_DIR" ] && export DOTNET_BUNDLE_EXTRACT_BASE_DIR="${XDG_CACHE_HOME:-"$HOME"/.cache}/dotnet_bundle_extract"
+11 -2
View File
@@ -1,5 +1,14 @@
{
"source": "aur",
"source": "local",
"release_ring": "fast",
"upstream_commit": "b67e475d16f4e061a16af53dae212a46d9bc3ea9"
"upstream": {
"watch": {
"regex": "https://linux.dropbox.com/packages/",
"pattern": "nautilus-dropbox-(?P<version>[0-9]{4}\\.[0-9]{2}\\.[0-9]{2})\\.tar\\.bz2"
}
},
"origin": {
"aur": "dropbox-cli",
"commit": "b67e475d16f4e061a16af53dae212a46d9bc3ea9"
}
}
@@ -1,11 +0,0 @@
--- 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")
+3 -4
View File
@@ -6,8 +6,8 @@
# Contributor: carstene1ns <arch carsten-teibes de>
pkgname=dropbox-cli
pkgver=2024.04.17
pkgrel=2.1
pkgver=2026.05.06
pkgrel=1
pkgdesc="Command line interface for Dropbox"
arch=("x86_64")
url="https://www.dropbox.com"
@@ -18,8 +18,7 @@ optdepends=("gtk3: Dropbox update GUI"
"python-gpgme: verify binary signature")
source=("https://linux.dropbox.com/packages/nautilus-dropbox-${pkgver}.tar.bz2"
"dropboxd-fallback.patch")
sha256sums=('a6a098cf16aa4747f40816ac793d59e37e8ae3b7080d0b30611d6c2b8663f2c1'
'711ed63c6dfccfd05c6e9abaa291be9ac3c3909e84f788f568cb44dee2d48229')
sha256sums=('c9d7ef418ccb0f34adc7cdda1952110be4fbbc788a79cdeb2cfd63e070bb3961' '711ed63c6dfccfd05c6e9abaa291be9ac3c3909e84f788f568cb44dee2d48229')
prepare() {
cd "nautilus-dropbox-${pkgver}"
+11 -2
View File
@@ -1,5 +1,14 @@
{
"source": "aur",
"source": "local",
"release_ring": "fast",
"upstream_commit": "6379feda6f36bbbd496c97f040e4f71c5a1dcec7"
"upstream": {
"watch": {
"redirect": "https://www.dropbox.com/download?plat=lnx.x86_64",
"pattern": "dropbox-lnx\\.x86_64-(?P<version>[0-9]+\\.4\\.[0-9]+)\\.tar\\.gz"
}
},
"origin": {
"aur": "dropbox",
"commit": "6379feda6f36bbbd496c97f040e4f71c5a1dcec7"
}
}
+2 -7
View File
@@ -4,7 +4,7 @@
# Contributor: David Manouchehri <d@32t.ca>
pkgname=dropbox
pkgver=264.4.3421
pkgver=270.4.3312
pkgrel=1
pkgdesc="A free service that lets you bring your photos, docs, and videos anywhere and share them easily."
arch=("x86_64")
@@ -27,12 +27,7 @@ source=("DropboxGlyph_Blue.svg"
"dropbox@.service"
"https://edge.dropboxstatic.com/dbx-releng/client/dropbox-lnx.x86_64-$pkgver.tar.gz"{,.asc})
sha256sums=('9ba76205ec5838db85d822f23cfd7e2112fd2757e8031d8374709f102143c548'
'1610ff57e8b20ee7a37682c3cc505da4ddc9cec2bd7234c90c0f2073657521d2'
'6c67a9c8c95c08fafafd2f1d828074b13e3347b05d2e4f4bf4e62746115d7477'
'98581e65a91ae1f19ed42edcdaaa52e102298b5da0d71b50089393d364474d3d'
'4aa06821de43b5e1cf4f27f83cb5f0bca82d01107c758091d7895f0d723f5411'
'SKIP')
sha256sums=('9ba76205ec5838db85d822f23cfd7e2112fd2757e8031d8374709f102143c548' '1610ff57e8b20ee7a37682c3cc505da4ddc9cec2bd7234c90c0f2073657521d2' '6c67a9c8c95c08fafafd2f1d828074b13e3347b05d2e4f4bf4e62746115d7477' '98581e65a91ae1f19ed42edcdaaa52e102298b5da0d71b50089393d364474d3d' '35404957d2a15dcac998d53cbec692d5236e197493f6c009accd91ea9aa8f34c' 'SKIP')
# The PGP key fingerprint should match the one on https://www.dropbox.com/help/desktop-web/linux-commands
validpgpkeys=(
'1C61A2656FB57B7E4DE0F4C1FC918B335044912E' # Dropbox Automatic Signing Key <linux@dropbox.com>
+11 -2
View File
@@ -1,4 +1,13 @@
{
"source": "aur",
"upstream_commit": "5614a0a61616643e448fb7c68d58237715ce2739"
"source": "local",
"upstream": {
"watch": {
"github": "abenz1267/elephant",
"pattern": "v(?P<version>[0-9]+(?:\\.[0-9]+)*)"
}
},
"origin": {
"aur": "elephant-all",
"commit": "5614a0a61616643e448fb7c68d58237715ce2739"
}
}
@@ -1,4 +1,13 @@
{
"source": "aur",
"upstream_commit": "659b81f1aa74a13fd2ebec222e19da2046d8e977"
"source": "local",
"upstream": {
"watch": {
"github": "abenz1267/elephant",
"pattern": "v(?P<version>[0-9]+(?:\\.[0-9]+)*)"
}
},
"origin": {
"aur": "elephant-archlinuxpkgs",
"commit": "659b81f1aa74a13fd2ebec222e19da2046d8e977"
}
}
@@ -1,4 +1,13 @@
{
"source": "aur",
"upstream_commit": "42d9dd5424884c51b8fe6bf7692caf0a31007f05"
"source": "local",
"upstream": {
"watch": {
"github": "abenz1267/elephant",
"pattern": "v(?P<version>[0-9]+(?:\\.[0-9]+)*)"
}
},
"origin": {
"aur": "elephant-bluetooth",
"commit": "42d9dd5424884c51b8fe6bf7692caf0a31007f05"
}
}
+11 -2
View File
@@ -1,4 +1,13 @@
{
"source": "aur",
"upstream_commit": "84aba9e90bbd65af45f83168cd6c7bce6ec4322e"
"source": "local",
"upstream": {
"watch": {
"github": "abenz1267/elephant",
"pattern": "v(?P<version>[0-9]+(?:\\.[0-9]+)*)"
}
},
"origin": {
"aur": "elephant-calc",
"commit": "84aba9e90bbd65af45f83168cd6c7bce6ec4322e"
}
}
@@ -1,4 +1,13 @@
{
"source": "aur",
"upstream_commit": "3176f9de1445e7115009383f9cdac116729fc7be"
"source": "local",
"upstream": {
"watch": {
"github": "abenz1267/elephant",
"pattern": "v(?P<version>[0-9]+(?:\\.[0-9]+)*)"
}
},
"origin": {
"aur": "elephant-clipboard",
"commit": "3176f9de1445e7115009383f9cdac116729fc7be"
}
}
@@ -1,4 +1,13 @@
{
"source": "aur",
"upstream_commit": "c30e33db5fef912dec9aa157773b10ffab1e0307"
"source": "local",
"upstream": {
"watch": {
"github": "abenz1267/elephant",
"pattern": "v(?P<version>[0-9]+(?:\\.[0-9]+)*)"
}
},
"origin": {
"aur": "elephant-desktopapplications",
"commit": "c30e33db5fef912dec9aa157773b10ffab1e0307"
}
}
+11 -2
View File
@@ -1,4 +1,13 @@
{
"source": "aur",
"upstream_commit": "2d16502b7a905e7d7a03e0026c5519c3b3c4abf2"
"source": "local",
"upstream": {
"watch": {
"github": "abenz1267/elephant",
"pattern": "v(?P<version>[0-9]+(?:\\.[0-9]+)*)"
}
},
"origin": {
"aur": "elephant-files",
"commit": "2d16502b7a905e7d7a03e0026c5519c3b3c4abf2"
}
}
+11 -2
View File
@@ -1,4 +1,13 @@
{
"source": "aur",
"upstream_commit": "5ab583fee6ba3e387d49ffe4e409bb84bc60ea24"
"source": "local",
"upstream": {
"watch": {
"github": "abenz1267/elephant",
"pattern": "v(?P<version>[0-9]+(?:\\.[0-9]+)*)"
}
},
"origin": {
"aur": "elephant-menus",
"commit": "5ab583fee6ba3e387d49ffe4e409bb84bc60ea24"
}
}
@@ -1,4 +1,13 @@
{
"source": "aur",
"upstream_commit": "1d756a138e926a81b9d33265f0197b8661168845"
"source": "local",
"upstream": {
"watch": {
"github": "abenz1267/elephant",
"pattern": "v(?P<version>[0-9]+(?:\\.[0-9]+)*)"
}
},
"origin": {
"aur": "elephant-providerlist",
"commit": "1d756a138e926a81b9d33265f0197b8661168845"
}
}
@@ -1,4 +1,13 @@
{
"source": "aur",
"upstream_commit": "e47641530d912199372204cf8780ef37f99f0b37"
"source": "local",
"upstream": {
"watch": {
"github": "abenz1267/elephant",
"pattern": "v(?P<version>[0-9]+(?:\\.[0-9]+)*)"
}
},
"origin": {
"aur": "elephant-runner",
"commit": "e47641530d912199372204cf8780ef37f99f0b37"
}
}
@@ -1,4 +1,13 @@
{
"source": "aur",
"upstream_commit": "32263e8b71390ab38b8aa1fd82fc2595b41a5157"
"source": "local",
"upstream": {
"watch": {
"github": "abenz1267/elephant",
"pattern": "v(?P<version>[0-9]+(?:\\.[0-9]+)*)"
}
},
"origin": {
"aur": "elephant-symbols",
"commit": "32263e8b71390ab38b8aa1fd82fc2595b41a5157"
}
}
+11 -2
View File
@@ -1,4 +1,13 @@
{
"source": "aur",
"upstream_commit": "447978df5ea3afd1e10959d7973c0b35367724c3"
"source": "local",
"upstream": {
"watch": {
"github": "abenz1267/elephant",
"pattern": "v(?P<version>[0-9]+(?:\\.[0-9]+)*)"
}
},
"origin": {
"aur": "elephant-todo",
"commit": "447978df5ea3afd1e10959d7973c0b35367724c3"
}
}
@@ -1,4 +1,13 @@
{
"source": "aur",
"upstream_commit": "23222b0d304a234c74f630c5b9176d58c6c6e0b3"
"source": "local",
"upstream": {
"watch": {
"github": "abenz1267/elephant",
"pattern": "v(?P<version>[0-9]+(?:\\.[0-9]+)*)"
}
},
"origin": {
"aur": "elephant-unicode",
"commit": "23222b0d304a234c74f630c5b9176d58c6c6e0b3"
}
}
@@ -1,4 +1,13 @@
{
"source": "aur",
"upstream_commit": "6b5da831da33e3533593823826a8ffb1a008a299"
"source": "local",
"upstream": {
"watch": {
"github": "abenz1267/elephant",
"pattern": "v(?P<version>[0-9]+(?:\\.[0-9]+)*)"
}
},
"origin": {
"aur": "elephant-websearch",
"commit": "6b5da831da33e3533593823826a8ffb1a008a299"
}
}
+11 -2
View File
@@ -1,4 +1,13 @@
{
"source": "aur",
"upstream_commit": "6dd02e6987a7a91be6a33e9600147523efa7fa5a"
"source": "local",
"upstream": {
"watch": {
"github": "abenz1267/elephant",
"pattern": "v(?P<version>[0-9]+(?:\\.[0-9]+)*)"
}
},
"origin": {
"aur": "elephant",
"commit": "6dd02e6987a7a91be6a33e9600147523efa7fa5a"
}
}
+46 -4
View File
@@ -1,8 +1,8 @@
# Maintainer: GM <gianmarcomorales@icloud.com>
pkgname=flea
pkgver=0.1.5
pkgrel=1
pkgver=0.2.1
pkgrel=3
pkgdesc='Fast, keyboard-first file manager for Omarchy'
arch=('x86_64' 'aarch64')
url='https://github.com/thisisgm/flea'
@@ -19,6 +19,8 @@ depends=(
'gvfs-nfs'
'gvfs-smb'
'hicolor-icon-theme'
'kimageformats'
'libheif'
'omarchy'
'python'
'python-gobject'
@@ -46,7 +48,7 @@ options=('!debug')
source=(
"$url/releases/download/v$pkgver/$pkgname-v$pkgver.tar.gz"
)
sha256sums=('5f9b6591e2a912625055c25d44d20bd5a6ebb94f4cf5d3c03fddd4b114beb480')
sha256sums=('75f9ac0274a09a0d55cf7d9187943983c1b78a9e443465738b3ac8af8f2a77e9')
build() {
cd "$pkgname-$pkgver"
@@ -69,6 +71,8 @@ check() {
--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_silent_failure_names_the_operation_that_was_running
--skip tui::job::tests::preview_worker_reads_held_source_in_readonly_sandbox
--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
@@ -90,7 +94,45 @@ check() {
)
fi
cargo test --frozen --release -- "${test_args[@]}"
# Recovery-lock tests are unreliable when run concurrently with other suites.
# These fixtures require O_TMPFILE, which the builder's /tmp filesystem may
# not provide. Keep executable fixtures in the remaining suites on the normal
# temp root (/dev/shm is noexec). Note that /dev/shm does NOT buy finer
# timestamps -- see the skip below.
local -a filesystem_tests=(
backend::menu_actions::tests::
backend::menudelete::tests::
backend::redo::tests::
backend::trashbrowse::tests::
backend::trashdelete::
backend::trashmanifest::tests::
)
# redo_refuses_changed_sources_and_destination_collisions writes a file and
# then immediately asks redo to notice the edit. flea decides "changed" from
# ctime alone -- src/backend/undo.rs records (ctime, ctime_nsec) as the whole
# identity -- and this kernel stamps ctime from the coarse clock, so two
# writes microseconds apart share a timestamp and the guard sees no change.
# Measured on this builder: 196/200 back-to-back write pairs on /dev/shm and
# 192/200 on the root filesystem produced an identical ctime. The assertion
# therefore fails on any builder fast enough to stay inside one granule, and
# moving the suite to tmpfs does not help. Both halves are upstream bugs --
# the test assumes a resolution the kernel never promised, and the identity it
# exercises cannot see a same-granule edit -- so skip it rather than paper
# over it by disabling the whole package.
local -a racy_ctime_tests=(
--skip backend::redo::tests::redo_refuses_changed_sources_and_destination_collisions
)
local test_tmp test_status=0 suite
test_tmp=$(mktemp -d /dev/shm/flea-tests.XXXXXXXX) || return 1
TMPDIR="$test_tmp" cargo test --frozen --release -- \
--test-threads=1 "${filesystem_tests[@]}" "${racy_ctime_tests[@]}" || test_status=$?
rm -rf -- "$test_tmp"
(( test_status == 0 )) || return "$test_status"
for suite in "${filesystem_tests[@]}"; do
test_args+=(--skip "$suite")
done
cargo test --frozen --release -- --test-threads=1 "${test_args[@]}"
./tests/js.sh
./tests/keymap-gen.sh
}
+10
View File
@@ -0,0 +1,10 @@
{
"source": "local",
"release_ring": "fast",
"upstream": {
"watch": {
"pattern": "v(?P<version>[0-9]+(?:\\.[0-9]+)*)",
"git_tags": "https://github.com/ghostty-org/ghostty.git"
}
}
}
+123
View File
@@ -0,0 +1,123 @@
# Ghostty for x86_64 and aarch64, built from the upstream release source tarball.
#
# Ghostty 1.3.x requires Zig 0.15.2 exactly. Distribution toolchains can move
# ahead, so use the verified upstream toolchain for each architecture only at
# package-build time rather than publishing a second Zig package.
pkgbase=ghostty
pkgname=(ghostty ghostty-shell-integration ghostty-terminfo ghostty-nautilus)
pkgver=1.3.1
pkgrel=3
pkgdesc='Fast, native, feature-rich terminal emulator pushing modern features'
arch=(x86_64 aarch64)
url='https://github.com/ghostty-org/ghostty'
license=(MIT)
depends=(
bzip2
fontconfig
freetype2
glib2
glibc
gtk4
gtk4-layer-shell
harfbuzz
libadwaita
libpng
oniguruma
pixman
wayland
zlib
)
makedepends=(
blueprint-compiler
curl
gettext
pkgconf
)
_zigver=0.15.2
_archive="$pkgbase-$pkgver"
source=(
"https://release.files.ghostty.org/$pkgver/$_archive.tar.gz"
'build-data-llvm.patch'
)
sha256sums=(
'3349d25600ffbda281197a18314f7d18791969cffe9474f0ff16a45a9ebfccdb'
'd9f5781b748651fa1ff7b919f4a79cd8570118faefe2848f64f02ea4220257ba'
)
source_x86_64=("https://ziglang.org/download/$_zigver/zig-x86_64-linux-$_zigver.tar.xz")
sha256sums_x86_64=('02aa270f183da276e5b5920b1dac44a63f1a49e55050ebde3aecc9eb82f93239')
source_aarch64=("https://ziglang.org/download/$_zigver/zig-aarch64-linux-$_zigver.tar.xz")
sha256sums_aarch64=('958ed7d1e00d0ea76590d27666efbf7a932281b3d7ba0c6b01b0ff26498f667f')
prepare() {
cd "$_archive"
# Zig's native x86 linker cannot read .sframe relocations in Arch's crt1.o.
# Use bundled LLVM for the build-data helper, as the main executable does.
if [[ "$CARCH" == x86_64 ]]; then
patch -Np1 -i "$srcdir/build-data-llvm.patch"
fi
PATH="$srcdir/zig-$CARCH-linux-$_zigver:$PATH" \
ZIG_GLOBAL_CACHE_DIR="$srcdir/zig-global-cache" \
./nix/build-support/fetch-zig-cache.sh
}
build() {
cd "$_archive"
# A '-' suffix is a SemVer prerelease and selects Ghostty's tip channel.
# Keep the package revision as build metadata on the stable release.
PATH="$srcdir/zig-$CARCH-linux-$_zigver:$PATH" \
DESTDIR=build \
zig build \
--prefix /usr \
--system "$srcdir/zig-global-cache/p" \
-Doptimize=ReleaseFast \
-Dgtk-x11=true \
-Dcpu=baseline \
-Dpie=true \
-Demit-docs=false \
-Dversion-string="$pkgver+omarchy.$pkgrel" \
--build-id=sha1
}
package_ghostty() {
depends+=(ghostty-shell-integration ghostty-terminfo)
optdepends=('ghostty-nautilus: Open in Ghostty context menu in GNOME Files')
cd "$_archive"
cp -a build/* "$pkgdir/"
install -Dm0644 LICENSE "$pkgdir/usr/share/licenses/ghostty/LICENSE"
rm -r "$pkgdir/usr/share/terminfo" \
"$pkgdir/usr/share/ghostty/shell-integration" \
"$pkgdir/usr/share/nautilus-python"
}
package_ghostty-shell-integration() {
pkgdesc='Shell integration scripts for Ghostty'
depends=()
cd "$_archive"
install -d "$pkgdir/usr/share/ghostty/shell-integration"
cp -a build/usr/share/ghostty/shell-integration/. \
"$pkgdir/usr/share/ghostty/shell-integration/"
install -Dm0644 LICENSE "$pkgdir/usr/share/licenses/$pkgname/LICENSE"
}
package_ghostty-terminfo() {
pkgdesc='Terminfo for Ghostty'
depends=()
cd "$_archive"
install -d "$pkgdir/usr/share/terminfo"
cp -a build/usr/share/terminfo/x "$pkgdir/usr/share/terminfo/"
install -Dm0644 LICENSE "$pkgdir/usr/share/licenses/$pkgname/LICENSE"
}
package_ghostty-nautilus() {
pkgdesc='Open in Ghostty for GNOME Files'
depends=(ghostty nautilus-python)
license=(GPL-2.0-or-later)
cd "$_archive"
install -d "$pkgdir/usr/share/nautilus-python"
cp -a build/usr/share/nautilus-python/. "$pkgdir/usr/share/nautilus-python/"
}
+10
View File
@@ -0,0 +1,10 @@
--- a/src/build/GhosttyResources.zig
+++ b/src/build/GhosttyResources.zig
@@ -15,6 +15,7 @@
// This is the exe used to generate some build data.
const build_data_exe = b.addExecutable(.{
.name = "ghostty-build-data",
+ .use_llvm = true,
.root_module = b.createModule(.{
.root_source_file = b.path("src/main_build_data.zig"),
.target = b.graph.host,
+3 -3
View File
@@ -6,7 +6,7 @@ _npmmodule=@github/copilot
pkgname=github-copilot-cli
_pkgexec=copilot
pkgver=1.0.83
pkgver=1.0.85
pkgrel=1
pkgdesc="GitHub Copilot CLI brings the power of Copilot coding agent directly to your terminal."
@@ -31,8 +31,8 @@ source=("https://registry.npmjs.org/${_npmmodule}/-/copilot-${pkgver}.tgz"
noextract=("copilot-${pkgver}.tgz")
sha256sums=(
'135506fc2b13163ab55dbf76a06e2fbcbad04ecac76b4e9c6659f0ba309e6a86'
'0c0064a10effac8adf9ad97338bafaa0d7d7d5bf191cc1c0384e05ff4366d36c'
'd26e3c15310bdcdcc910ed223285bed8d68aaebce0d344f97224b5cfdaeeee36'
'1b1a8fbd5562df73684b6e94865837ceffd6609d61822c39e6c8fcbd32d8ac41'
)
# Document: https://wiki.archlinux.org/title/Node.js_package_guidelines
+5 -2
View File
@@ -1,5 +1,8 @@
{
"source": "aur",
"source": "local",
"sync": false,
"upstream_commit": "05eb78fca06b482affda28b26223cbf249d4bbcd"
"origin": {
"aur": "grok-bot",
"commit": "05eb78fca06b482affda28b26223cbf249d4bbcd"
}
}
+31 -25
View File
@@ -2,14 +2,15 @@
# Contributor: Omarchy
pkgname=grok-bot
pkgver=0.29.0
pkgver=0.47.0
pkgrel=1
_commit=f0e5bfcee649ea84c0c61369cf896cd146d72136
_commit=c1e7d7a46549956d25f53e9c0b9f59666e03aa3a
pkgdesc='Grok Bot desktop agent'
arch=('x86_64')
arch=('x86_64' 'aarch64')
url='https://x.ai/bot'
license=('custom')
depends=(
'alsa-lib'
'at-spi2-core'
'gtk3'
'hicolor-icon-theme'
@@ -25,32 +26,36 @@ optdepends=('libappindicator-gtk3: tray support')
provides=('sand')
conflicts=('sand')
options=('!strip' '!debug')
install=grok-bot.install
_deb_x86_64="grok-bot_${pkgver}_amd64.deb"
_deb_aarch64="grok-bot_${pkgver}_arm64.deb"
source=(
"${pkgname}_${pkgver}.deb::https://downloads.cursor.com/grokbot/stable/${_commit}/linux/x64/Grok_Bot_${pkgver}.deb"
'grok-bot.sh'
'grok-bot.desktop'
)
sha256sums=('d223b5830282aef11d5c46d8f4d1edd239bf992e336405cbd288d4476b9233d4'
'6dfa6c305941afa6cbaefbeaae06d05ab5a88f31630005d25a819a160c20c7a3'
'856056c9ca63dda5d01158ce8fb6a9a7cbb3f67c13a92b573cd196d3e50f26e7')
noextract=("${pkgname}_${pkgver}.deb")
source_x86_64=(
"${_deb_x86_64}::https://downloads.cursor.com/grokbot/stable/${_commit}/linux/x64/${_deb_x86_64}"
)
source_aarch64=(
"${_deb_aarch64}::https://downloads.cursor.com/grokbot/stable/${_commit}/linux/arm64/${_deb_aarch64}"
)
sha256sums=('6dfa6c305941afa6cbaefbeaae06d05ab5a88f31630005d25a819a160c20c7a3'
'3e2a2461ea58d17ac1777616be9ba660f7cb9ceefa9292016e36c55758bf78dd')
sha256sums_x86_64=('11ca0f51a535b97af51a352adf9c0f9ecd2e1b0430a69ae9451b688a7a065808')
sha256sums_aarch64=('836f8d19d3826c6573c31ac45c7a9b797abc73381ae0d2b1e7a8dae5410e7e46')
noextract=("${_deb_x86_64}" "${_deb_aarch64}")
package() {
bsdtar -xOf "${srcdir}/${pkgname}_${pkgver}.deb" data.tar.xz |
local deb_var="_deb_${CARCH}"
local deb="${!deb_var}"
bsdtar -xOf "${srcdir}/${deb}" data.tar.xz |
bsdtar -x -C "${pkgdir}" -f -
rm -rf "${pkgdir}/usr/share/doc" \
"${pkgdir}/usr/share/applications/sand.desktop"
local icon1024="${pkgdir}/usr/share/icons/hicolor/1024x1024/apps"
if [[ -f "${icon1024}/sand.png" && ! -f "${icon1024}/grok-bot.png" ]]; then
install -Dm644 "${icon1024}/sand.png" "${icon1024}/grok-bot.png"
fi
rm -f "${icon1024}/sand.png"
if [[ -f "${icon1024}/grok-bot.png" ]]; then
install -Dm644 "${icon1024}/grok-bot.png" \
"${pkgdir}/usr/share/icons/hicolor/512x512/apps/grok-bot.png"
fi
"${pkgdir}/usr/share/applications/sand.desktop" \
"${pkgdir}/usr/share/applications/grok-bot.desktop"
# Always install our Wayland wrapper; do not keep any /usr/bin from the .deb.
rm -f "${pkgdir}/usr/bin/grok-bot" "${pkgdir}/usr/bin/sand"
@@ -64,9 +69,10 @@ package() {
install -Dm644 "${pkgdir}/opt/Grok Bot/LICENSES.chromium.html" \
"${pkgdir}/usr/share/licenses/${pkgname}/LICENSES.chromium.html"
if ! { [[ -L /proc/self/ns/user ]] && unshare --user true; }; then
chmod 4755 "${pkgdir}/opt/Grok Bot/chrome-sandbox"
else
chmod 0755 "${pkgdir}/opt/Grok Bot/chrome-sandbox"
fi
# Ship chrome-sandbox without setuid. Upstream's build-time userns probe
# would measure the CI container, not the user's machine, and a setuid
# helper could not exec from "/opt/Grok Bot/" anyway (electron#44414).
# grok-bot.install tells users on kernels without unprivileged user
# namespaces how to run without the sandbox.
chmod 0755 "${pkgdir}/opt/Grok Bot/chrome-sandbox"
}
+1 -1
View File
@@ -8,6 +8,6 @@ Terminal=false
Type=Application
Categories=Development;
MimeType=x-scheme-handler/grokbot;x-scheme-handler/sand;
StartupWMClass=Grok Bot
StartupWMClass=grok-bot
StartupNotify=true
Keywords=Grok;AI;Agent;
+41
View File
@@ -0,0 +1,41 @@
# Electron's renderer sandbox needs unprivileged user namespaces, or else a
# setuid-root chrome-sandbox. Upstream omarchy-pkgs probes for user namespaces
# inside package() and sets 4755 when they are missing. That is wrong twice
# for this repo: the build runs in a CI container where the probe fails, so
# every user would get the setuid helper; and the helper lives under
# "/opt/Grok Bot/", and Electron cannot exec a setuid chrome-sandbox from a
# path with a space (electron/electron#44414), so 4755 would not even work.
#
# The package therefore always ships chrome-sandbox as 0755. This hook only
# tells the user what to do on a host that lacks unprivileged user namespaces.
# The probe drops to nobody first: pacman runs hooks as root, and root can
# unshare a user namespace even where unprivileged users cannot.
_userns_available() {
[[ -L /proc/self/ns/user ]] || return 1
if (( EUID == 0 )) && command -v setpriv >/dev/null; then
setpriv --reuid=65534 --regid=65534 --clear-groups -- unshare --user true 2>/dev/null
else
# Already unprivileged (or no setpriv): the direct probe is the real answer.
unshare --user true 2>/dev/null
fi
}
_advise() {
_userns_available && return 0
cat <<'MSG'
==> Unprivileged user namespaces are unavailable on this kernel, so Grok Bot's
renderer sandbox cannot start. A setuid chrome-sandbox is not an option
here: Electron cannot exec it from "/opt/Grok Bot/" (electron#44414).
To run without the sandbox, add this line to ~/.config/grok-bot-flags.conf:
--no-sandbox
MSG
}
post_install() {
_advise
}
post_upgrade() {
_advise
}
+2 -2
View File
@@ -1,7 +1,7 @@
# Maintainer: David Heinemeier Hansson <david@hey.com>
pkgname=herdr
pkgver=0.8.2
pkgver=0.9.0
pkgrel=1
pkgdesc="Herdr terminal workspace manager for AI coding agents"
arch=('x86_64' 'aarch64')
@@ -17,7 +17,7 @@ _zigver=0.15.2
source=("herdr-$pkgver.tar.gz::$url/archive/refs/tags/v$pkgver.tar.gz")
source_x86_64=("zig-x86_64-linux-$_zigver.tar.xz::https://ziglang.org/download/$_zigver/zig-x86_64-linux-$_zigver.tar.xz")
source_aarch64=("zig-aarch64-linux-$_zigver.tar.xz::https://ziglang.org/download/$_zigver/zig-aarch64-linux-$_zigver.tar.xz")
sha256sums=('60453051025ee44ebf055d26cdaf665a0accd99a992cddd22c166a26c49cd161')
sha256sums=('1e83bff4b05834ed8281e16f1680e8f3e58375a94b2e3f2b3d021e28e293ef9a')
sha256sums_x86_64=('02aa270f183da276e5b5920b1dac44a63f1a49e55050ebde3aecc9eb82f93239')
sha256sums_aarch64=('958ed7d1e00d0ea76590d27666efbf7a932281b3d7ba0c6b01b0ff26498f667f')
@@ -1,4 +1,3 @@
{
"source": "local",
"release_ring": "fast"
"source": "local"
}
+8 -8
View File
@@ -4,8 +4,8 @@
# so the upstream updater can rebuild and relaunch it in place.
pkgname=hermes-desktop
pkgver=2026.8.31
pkgrel=3
pkgver=2026.9.7
pkgrel=1
pkgdesc='Native desktop shell for Hermes Agent'
arch=('x86_64')
url='https://github.com/NousResearch/hermes-agent'
@@ -65,7 +65,7 @@ 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=29112bef099274229cadff79cdff7bf7b99c4b77
_commit=2237be355906fbe6065ce1815711eee52b2d646e
_srcdir="hermes-agent-${pkgver}"
source=("${pkgname}-${pkgver}.tar.gz::${url}/archive/refs/tags/v${pkgver}.tar.gz"
@@ -74,12 +74,12 @@ source=("${pkgname}-${pkgver}.tar.gz::${url}/archive/refs/tags/v${pkgver}.tar.gz
'hermes-desktop.png'
'runtime.patch'
'runtime-test.py')
sha256sums=('78fb3ff707ec1d17044b875ecac8bef28aa39d44242824f6871ca40afe7bf217'
sha256sums=('907c2a72db1c5dd637ea8eeae97f4cb5b32cef615c17258f6b190924ec5bf688'
'094d5f3191109a80eea9f23053b78a2e00dbecf90d62d1ca04c8e48866251469'
'3ef685bfcf366776b025d26c37d32854d8d4aa2023b2bd07c8e08b001ef1e8c4'
'd60d164e24fdcf6532133b8ea43c77a201e4b9e9dbc396187b58d51d8590ef52'
'03b67e26c234c797a6b1d4c9f34a57502dba37f462540e47ce6c88f6ea79302a'
'461e1120e7e6779f531c114d9926479e47fab79113d1b4646efea36bc771b3c5')
'9d5015d1be762a901f8f64319981ae862e9852fa5cb9a22a2ba1e691f90430a2'
'7337a12c71e8091ad5fc2e879e922c9cb1706c65f81b59d6dd70b12123dc7c00')
build() {
cd "${srcdir}/${_srcdir}"
@@ -109,10 +109,10 @@ package() {
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.
# Omarchy's installer still requires this patch. The release includes the
# fix, so the installer recognizes it through its reverse-apply check.
install -Dm644 "${srcdir}/runtime.patch" "${pkgdir}/usr/share/${pkgname}/runtime.patch"
install -Dm644 "${srcdir}/hermes-desktop.desktop" \
"${pkgdir}/usr/share/applications/${pkgname}.desktop"
+9 -6
View File
@@ -14,7 +14,9 @@ with tempfile.TemporaryDirectory(prefix="hermes-runtime-check-") as 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)
# Omarchy's installer requires the patch and accepts an upstreamed fix
# through its reverse check. Verify that path without changing the release.
subprocess.run(["git", "apply", "--reverse", "--check", str(patch_file.resolve())], cwd=root, check=True)
home = root / "home with spaces"
runtime = home / ".hermes/hermes-agent"
@@ -42,13 +44,14 @@ Path(os.environ["TEST_OUTPUT"]).write_text(json.dumps({
module = runtime / "hermes_cli"
module.mkdir()
(module / "__init__.py").touch()
upstream = ast.parse((source / "hermes_cli/main.py").read_text())
upstream = ast.parse((source / "hermes_cli/main_desktop.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"
constants = [node for node in upstream.body if isinstance(node, ast.Assign)
and any(isinstance(target, ast.Name)
and target.id in ("_LINUX_PASSWORD_STORES", "_GPU_FLAG_WORDS")
for target in node.targets)]
helper = "import os, shlex\n" + "\n".join(map(ast.unparse, constants)) + "\n" + ast.unparse(option_parser) + "\n"
(module / "main.py").write_text(helper)
(module / "config.py").write_text('''import json, os
from pathlib import Path
+6 -4
View File
@@ -1,11 +1,13 @@
--- a/scripts/desktop-update/posix.sh
+++ b/scripts/desktop-update/posix.sh
@@ -317,6 +317,8 @@
@@ -327,6 +327,10 @@
sb="$unpacked/chrome-sandbox"
if [ ! -e "$sb" ]; then GATE=relaunch; return; fi
if [ -u "$sb" ] && [ "$(stat -c %u "$sb" 2>/dev/null)" = "0" ]; then GATE=relaunch; return; fi
+ # Namespace sandbox usable => Electron never consults the setuid helper,
+ # so a non-root chrome-sandbox does not block relaunch (mirrors the
+ # _desktop_linux_userns_sandbox_available() probe in hermes_cli/main.py).
+ 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
@@ -1,4 +1,13 @@
{
"source": "aur",
"upstream_commit": "44b4e963c69541700088a5719855d34f5cf16c85"
"source": "local",
"upstream": {
"watch": {
"github": "Heroic-Games-Launcher/HeroicGamesLauncher",
"pattern": "v(?P<version>[0-9]+(?:\\.[0-9]+)*)"
}
},
"origin": {
"aur": "heroic-games-launcher-bin",
"commit": "44b4e963c69541700088a5719855d34f5cf16c85"
}
}
+5 -6
View File
@@ -2,16 +2,16 @@
# Maintainer: CommandMC <kate@commandmc.de>
pkgname=heroic-games-launcher-bin
pkgver=2.22.1
pkgver=2.22.2
pkgrel=1
pkgdesc="An Open source Launcher for Epic, Amazon and GOG Games"
arch=('x86_64')
url="https://heroicgameslauncher.com/"
license=('GPL-3.0-only')
_filename=Heroic-2.22.1-linux-x64.pacman
source=("https://github.com/Heroic-Games-Launcher/HeroicGamesLauncher/releases/download/v2.22.1/Heroic-2.22.1-linux-x64.pacman")
noextract=("Heroic-2.22.1-linux-x64.pacman")
sha256sums=(66ed041a93ac2817b744d3d0985194adfa408c8d35f64d9a8967fa5e2a58f2c1)
_filename=Heroic-${pkgver}-linux-x64.pacman
source=("https://github.com/Heroic-Games-Launcher/HeroicGamesLauncher/releases/download/v${pkgver}/${_filename}")
noextract=("${_filename}")
sha256sums=('4e4033ac70b8c407eaf70ce072e4e4d017200f07a3e88f7cfd4d3a597f3c09b8')
options=(!strip)
depends=(
which
@@ -27,4 +27,3 @@ package() {
}
# vim:set ts=2 sw=2 et: syntax=sh
+10 -2
View File
@@ -1,5 +1,13 @@
{
"source": "aur",
"source": "local",
"release_ring": "fast",
"upstream_commit": "89c710019789541e4cf972ee847f266534d6a96d"
"upstream": {
"watch": {
"pypi": "hyprshade"
}
},
"origin": {
"aur": "hyprshade",
"commit": "89c710019789541e4cf972ee847f266534d6a96d"
}
}
+3 -3
View File
@@ -1,8 +1,8 @@
# Maintainer: Caleb Maclennan <caleb@alerque.com>
pkgname=hyprtoolkit
pkgver=0.5.4
pkgrel=5.1
pkgver=0.6.0
pkgrel=1
pkgdesc='A modern C++ Wayland-native GUI toolkit'
arch=(aarch64)
url="https://github.com/hyprwm/$pkgname"
@@ -31,7 +31,7 @@ makedepends=(cmake
provides=(libhyprtoolkit.so)
_archive="$pkgname-$pkgver"
source=("$url/archive/v$pkgver/$_archive.tar.gz")
sha256sums=('2fb59789f231c1c4e9154ceffc1e7524c0cae154807c0d57e6166806255b570f')
sha256sums=('53c41be72af97d9ede274a63c9c1034c58726d862905763ed6e5a564ae42ba6b')
build() {
cd "$_archive"
@@ -0,0 +1,77 @@
From f167239b3ecf242ae081767892cb0a4c54bad496 Mon Sep 17 00:00:00 2001
From: Arun T <arun.t@intel.com>
Date: Thu, 2 Jul 2026 06:19:35 +0530
Subject: [PATCH] Enable ov08x40 CVS for support upstream cvs driver
Linux 7.2 wires the Intel CVS (Computer Vision Sensing) controller into
the IPU media graph as a bridge entity named "Intel CVS" that sits between
the sensor and the IPU CSI2 receiver (ipu-bridge commit c6b1b34b5090,
in-tree driver drivers/media/i2c/cvs). Teach MediaControl to route the
sensor -> CSI2 link through that entity when it is present, and to find
the sensor's I2C bus through it. Kernels without the entity are unaffected.
Upstream: https://github.com/intel/ipu7-camera-hal/commit/f167239b3ecf242ae081767892cb0a4c54bad496
(only the src/ part; the ipu8 config changes are not needed for ipu75xa)
---
src/v4l2/MediaControl.cpp | 31 +++++++++++++++++++++++++++++++
1 file changed, 31 insertions(+)
diff --git a/src/v4l2/MediaControl.cpp b/src/v4l2/MediaControl.cpp
index 0b2cc33..807d4f4 100644
--- a/src/v4l2/MediaControl.cpp
+++ b/src/v4l2/MediaControl.cpp
@@ -63,6 +63,7 @@ struct MediaEntity {
char devname[32];
};
+static const string icvsName = "Intel CVS";
MediaControl* MediaControl::sInstance = nullptr;
Mutex MediaControl::sLock;
@@ -983,6 +984,33 @@ int MediaControl::mediaCtlSetup(int cameraId, MediaCtlConf* mc, int width, int h
}
}
+ MediaEntity* icvs = getEntityByName(icvsName.c_str());
+ if (icvs) {
+ for (uint32_t i = 0; i < icvs->numLinks; ++i) {
+ if (icvs->links[i].sink->entity == icvs) {
+ MediaEntity* sensor = icvs->links[i].source->entity;
+ int sensor_entity_id = sensor->info.id;
+ LOG1("@%s, found %s -> %s", __func__, sensor->info.name, icvsName.c_str());
+ for (McLink& link : mc->links) {
+ if (link.srcEntity == sensor_entity_id && link.sinkEntity != static_cast<int>(icvs->info.id)) {
+ LOG1("@%s, skip %s, link %s -> %s", __func__, link.srcEntityName.c_str(),
+ icvsName.c_str(), link.sinkEntityName.c_str());
+ link.srcEntity = icvs->info.id;
+ link.srcEntityName = icvsName;
+ for (uint32_t j = 0; j < icvs->info.pads; ++j) {
+ if (icvs->pads[j].flags & MEDIA_PAD_FL_SOURCE) {
+ link.srcPad = j;
+ break;
+ }
+ }
+ break;
+ }
+ }
+ break;
+ }
+ }
+ }
+
/* Set link in format Configuration */
ret = setMediaMcLink(mc->links);
CheckAndLogError(ret != OK, ret, "set MediaCtlConf McLink failed: ret = %d", ret);
@@ -1127,6 +1155,9 @@ int MediaControl::getI2CBusAddress(const string& sensorEntityName, const string&
for (int i = 0; i < linksCount; i++) {
if (strcmp(links[i].sink->entity->info.name, sinkEntityName.c_str()) == 0) {
entityName = entity.info.name;
+ if (strcmp(entityName, icvsName.c_str()) == 0) {
+ return getI2CBusAddress(sensorEntityName, icvsName, i2cBus);
+ }
break;
}
}
--
2.55.0
@@ -0,0 +1,32 @@
From: Spencer Bull <spencer@omarchy.org>
Subject: [PATCH] ipu75xa: set Intel CVS pad formats for ov08x40
On Linux 7.2 the "Intel CVS" bridge entity sits between ov08x40 and the
IPU7 CSI2 receiver. Its sink pad format must be set to the sensor format
(the source pad mirrors the sink) or link validation fails at stream-on.
Mirrors the upstream ipu8 change in ipu7-camera-hal commit f167239b3ecf.
Only formats are added; links stay sensor -> CSI2 in the config because
MediaControl rewrites them through the bridge at runtime, so this config
also keeps working on kernels without the CVS entity (a format entry for
a missing entity is logged and skipped).
---
diff --git i/config/linux/ipu75xa/sensors/ov08x40-uf.json w/config/linux/ipu75xa/sensors/ov08x40-uf.json
index ff31df5..483bf1d 100644
--- i/config/linux/ipu75xa/sensors/ov08x40-uf.json
+++ w/config/linux/ipu75xa/sensors/ov08x40-uf.json
@@ -31,6 +31,14 @@
"name": "ov08x40 $I2CBUS", "pad": 0, "width": 3856, "height": 2176,
"format": "V4L2_MBUS_FMT_SGRBG10_1X10"
},
+ {
+ "name": "Intel CVS", "pad": 0, "width": 3856, "height": 2176,
+ "format": "V4L2_MBUS_FMT_SGRBG10_1X10"
+ },
+ {
+ "name": "Intel CVS", "pad": 1, "width": 3856, "height": 2176,
+ "format": "V4L2_MBUS_FMT_SGRBG10_1X10"
+ },
{
"name": "Intel IPU7 CSI2 $CSI_PORT", "pad": 0, "width": 3856, "height": 2176,
"format": "V4L2_MBUS_FMT_SGRBG10_1X10"
@@ -0,0 +1,83 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Junjie Cao <junjie.cao@intel.com>
Date: Tue, 08 Sep 2026 18:57:17 +0800
Subject: [PATCH] media: i2c: cvs: Get the wake IRQ without claiming the GPIO
Message-ID: <20260908105717.496232-1-junjie.cao@intel.com>
Link: https://patchwork.linuxtv.org/project/linux-media/patch/20260908105717.496232-1-junjie.cao@intel.com/
The wake line is only used as an IRQ source, yet the driver requests
it with devm_gpiod_get() before the I2C handshake. Where ipu-bridge
does not expose the CSI endpoints, CSI init returns -EPROBE_DEFER and
every retry claims the line again for the length of the handshake.
On the Dell XPS 14 DA14260 (Panther Lake) the four CS35L57 amplifiers
read their speaker ID from one GpioIo (DSDT decoded in the second
link):
GpioIo (Shared, PullNone, 0, 0, IoRestrictionInputOnly,
"\_SB.GPI1", 0, ResourceConsumer,,) {20}
A request that lands while another consumer holds the line fails, and
cs35l56 does not retry:
cs35l56 sdw:0:2:01fa:3557:01:2: error -EBUSY: Failed to get spk-id-gpios
All four fail on Fedora 7.1.13, the first Fedora 7.1 kernel with the
driver enabled; the same board on 7.1.12 without it creates the card.
The second link shows the same failure on openSUSE 7.2.2, whose
config also enables the driver.
The INTC10E1 _CRS of this machine has not been decoded. The vendor
driver in intel/vision-drivers requests req, resp and rst the same
way but maps wake to an IRQ with acpi_dev_gpio_irq_get_by() without
requesting it, and on another DA14260 (board 0VRKYR, BIOS 1.8.2) a
build of it is bound while the amplifiers probe. The wake entry is
the line that differs.
Take the IRQ from the GpioInt entry the same way, as the I2C core
does for client->irq; this also applies the trigger type from _CRS.
The driver binds as a platform device too, hence the explicit lookup.
Fixes: 8e2b43d2c10b ("media: i2c: cvs: Add driver of Intel Computer Vision Sensing Controller(CVS)")
Cc: stable@vger.kernel.org
Link: https://bugzilla.redhat.com/show_bug.cgi?id=2529031
Link: https://github.com/thesofproject/sof/issues/11152
Signed-off-by: Junjie Cao <junjie.cao@intel.com>
---
drivers/media/i2c/cvs/core.c | 16 ++++++----------
1 file changed, 6 insertions(+), 10 deletions(-)
diff --git a/drivers/media/i2c/cvs/core.c b/drivers/media/i2c/cvs/core.c
index d4a3b9c3bab1e..8d857bbd8ab51 100644
--- a/drivers/media/i2c/cvs/core.c
+++ b/drivers/media/i2c/cvs/core.c
@@ -725,8 +725,6 @@ static int cvs_core_probe(struct device *dev, struct i2c_client *i2c)
}
if (ctx->res == ICVS_FULLCAP) {
- struct gpio_desc *wake;
-
ctx->rst = devm_gpiod_get(dev, "rst", GPIOD_OUT_HIGH);
if (IS_ERR(ctx->rst)) {
ret = dev_err_probe(dev, PTR_ERR(ctx->rst),
@@ -734,14 +732,12 @@ static int cvs_core_probe(struct device *dev, struct i2c_client *i2c)
goto err_put_ipu;
}
- wake = devm_gpiod_get(dev, "wake", GPIOD_IN);
- if (IS_ERR(wake)) {
- ret = dev_err_probe(dev, PTR_ERR(wake),
- "failed to get wake GPIO\n");
- goto err_put_ipu;
- }
-
- ctx->irq = gpiod_to_irq(wake);
+ /*
+ * Do not request the line: another device's _CRS may list
+ * the same pin, and its driver would then fail with -EBUSY.
+ */
+ ctx->irq = acpi_dev_gpio_irq_get_by(ACPI_COMPANION(dev),
+ "wake", 0);
if (ctx->irq < 0) {
ret = dev_err_probe(dev, ctx->irq,
"failed to get wake IRQ\n");
+58 -6
View File
@@ -1,7 +1,7 @@
# Maintainer: Omarchy <packages@omarchy.org>
pkgname=intel-ipu7-camera
pkgver=1.0.5
pkgver=1.0.6
pkgrel=2
pkgdesc="Intel IPU7 MIPI camera stack for Hurrican/Performance (OV08X40 + hardware ISP)"
arch=('x86_64')
@@ -9,7 +9,6 @@ url="https://github.com/TsaiGaggery/hurrican_omarchy_enabling"
license=('GPL-2.0-or-later')
depends=(
'dkms'
'linux-headers'
'v4l2loopback-dkms'
'v4l2-relayd'
'gstreamer'
@@ -37,16 +36,42 @@ _ipu7_camera_bins_commit="403c67db6b279dd02752f11db6a34552f31a3ac5"
_ipu7_camera_hal_commit="b1f6ebef12111fb5da0133b144d69dd9b001836c"
_icamerasrc_commit="4fb31db76b618aae72184c59314b839dedb42689"
# Linux 7.2 moved the Intel CVS (Computer Vision Sensing) controller into the
# IPU media graph: ipu-bridge now places it between the sensor and the IPU
# CSI2 receiver as an "Intel CVS" V4L2 bridge sub-device, provided by the
# in-tree drivers/media/i2c/cvs driver. The legacy vision-drivers misc driver
# has no such sub-device, so on 7.2 the sensor never joins the graph and the
# HAL finds no camera. We ship the in-tree driver as a DKMS module
# for 7.2+ kernels (Arch-derived kernel configs cannot enable it: the option
# is hidden behind MEDIA_HIDE_ANCILLARY_SUBDRV) and keep vision-drivers for
# older kernels. Both dkms.conf files carry matching BUILD_EXCLUSIVE_KERNEL.
#
# 0007 is the upstream fix for the audio regression on the Dell XPS 14
# DA14260: the driver requested its "wake" GPIO, which shares a pin with the
# CS35L57 amplifiers' speaker-ID GpioIo, so every cs35l56 probe failed with
# -EBUSY and the sound card never appeared. Drop it once the stable tag
# fetched below contains the fix.
_intel_cvs_kernel_tag="v7.2.5"
_intel_cvs_url="https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/plain/drivers/media/i2c/cvs"
source=(
"ipu7-drivers::git+https://github.com/intel/ipu7-drivers.git#commit=${_ipu7_drivers_commit}"
"vision-drivers::git+https://github.com/intel/vision-drivers.git#commit=${_vision_drivers_commit}"
"ipu7-camera-bins::git+https://github.com/intel/ipu7-camera-bins.git#commit=${_ipu7_camera_bins_commit}"
"ipu7-camera-hal::git+https://github.com/intel/ipu7-camera-hal.git#commit=${_ipu7_camera_hal_commit}"
"icamerasrc::git+https://github.com/intel/icamerasrc.git#commit=${_icamerasrc_commit}"
"intel-cvs-core.c::${_intel_cvs_url}/core.c?h=${_intel_cvs_kernel_tag}"
"intel-cvs-v4l2.c::${_intel_cvs_url}/v4l2.c?h=${_intel_cvs_kernel_tag}"
"intel-cvs-icvs.h::${_intel_cvs_url}/icvs.h?h=${_intel_cvs_kernel_tag}"
"0004-ipu7-psys-register-device-bus.patch"
"0003-icvs-set-rgbcamera_pwrup_host-0-for-Panther-Lake.patch"
"0005-camhal-MediaControl-route-through-Intel-CVS-bridge.patch"
"0006-camhal-ipu75xa-ov08x40-Intel-CVS-formats.patch"
"0007-media-i2c-cvs-get-the-wake-irq-without-claiming-the-gpio.patch"
"dkms-ipu7-drivers.conf"
"dkms-vision-drivers.conf"
"dkms-intel-cvs.conf"
"intel-cvs-Makefile"
"camera-deps.conf"
"v4l2loopback-modprobe.conf"
"camera-init.service"
@@ -62,10 +87,14 @@ source=(
"v4l2-relayd-ipu7-override.conf"
)
sha256sums=(
'SKIP' 'SKIP' 'SKIP' 'SKIP' 'SKIP'
'b4a90ad1815c02e4cadc1b5ad6c576ae0b150cb365c68d2c934cf964a995d91f'
'e988014f54b1b5183e9ef43b602d73bbc2c991f19a8d503560a8cdba112b76c6'
'6dcd5201fb78766a440038c8eba2cc8c3892064ba829065e2269c30dc1905983'
'SKIP' 'SKIP' 'SKIP' 'SKIP' 'SKIP' 'SKIP' 'SKIP'
'SKIP' 'SKIP' 'SKIP' 'SKIP' 'SKIP' 'SKIP'
'SKIP' 'SKIP' 'SKIP' 'SKIP' 'SKIP' 'SKIP'
'SKIP' 'SKIP' 'SKIP'
'SKIP' 'SKIP' 'SKIP' 'SKIP' 'SKIP' 'SKIP' 'SKIP'
'SKIP' 'SKIP' 'SKIP' 'SKIP' 'SKIP' 'SKIP' 'SKIP'
'SKIP'
)
prepare() {
@@ -79,6 +108,20 @@ prepare() {
git am "${srcdir}/0003-icvs-set-rgbcamera_pwrup_host-0-for-Panther-Lake.patch"
fi
# Route the media graph through the Linux 7.2 "Intel CVS" bridge entity
cd "${srcdir}/ipu7-camera-hal"
patch -Np1 -i "${srcdir}/0005-camhal-MediaControl-route-through-Intel-CVS-bridge.patch"
patch -Np1 -i "${srcdir}/0006-camhal-ipu75xa-ov08x40-Intel-CVS-formats.patch"
# Intel CVS: take the wake IRQ without claiming the GPIO (shared with the
# speaker-ID line on the XPS 14). makepkg symlinks plain downloads into
# srcdir and patch refuses to touch a symlink, so patch a real copy. The
# patch is against a/drivers/media/i2c/cvs/core.c, hence -p5.
cp --remove-destination "$(readlink -f "${srcdir}/intel-cvs-core.c")" \
"${srcdir}/intel-cvs-core.c"
patch -p5 -F0 --no-backup-if-mismatch "${srcdir}/intel-cvs-core.c" \
< "${srcdir}/0007-media-i2c-cvs-get-the-wake-irq-without-claiming-the-gpio.patch"
# Stage proprietary libs/headers for build-time use
local staging="${srcdir}/staging"
rm -rf "${staging}"
@@ -145,13 +188,22 @@ package() {
install -Dm644 "${srcdir}/dkms-ipu7-drivers.conf" "${ipu7_dkms_dir}/dkms.conf"
sed -i "s/@PKGVER@/${pkgver}/" "${ipu7_dkms_dir}/dkms.conf"
# DKMS: Vision drivers (CVS)
# DKMS: legacy Vision drivers (CVS misc driver), kernels before 7.2
local cvs_dkms_dir="${pkgdir}/usr/src/vision-drivers-${pkgver}"
install -dm755 "${cvs_dkms_dir}"
cp -r "${srcdir}/vision-drivers/"* "${cvs_dkms_dir}/"
install -Dm644 "${srcdir}/dkms-vision-drivers.conf" "${cvs_dkms_dir}/dkms.conf"
sed -i "s/@PKGVER@/${pkgver}/" "${cvs_dkms_dir}/dkms.conf"
# DKMS: in-tree Linux 7.2 Intel CVS V4L2 bridge driver, kernels 7.2+
local icvs_dkms_dir="${pkgdir}/usr/src/intel-cvs-${pkgver}"
install -Dm644 "${srcdir}/intel-cvs-core.c" "${icvs_dkms_dir}/core.c"
install -Dm644 "${srcdir}/intel-cvs-v4l2.c" "${icvs_dkms_dir}/v4l2.c"
install -Dm644 "${srcdir}/intel-cvs-icvs.h" "${icvs_dkms_dir}/icvs.h"
install -Dm644 "${srcdir}/intel-cvs-Makefile" "${icvs_dkms_dir}/Makefile"
install -Dm644 "${srcdir}/dkms-intel-cvs.conf" "${icvs_dkms_dir}/dkms.conf"
sed -i "s/@PKGVER@/${pkgver}/" "${icvs_dkms_dir}/dkms.conf"
# Firmware
install -dm755 "${pkgdir}/usr/lib/firmware/intel/ipu"
install -Dm644 "${srcdir}/ipu7-camera-bins/lib/firmware/intel/ipu/"*.bin \
@@ -0,0 +1,26 @@
# Intel CVS (Computer Vision Sensing) CSI-2 bridge driver.
#
# Copy of the in-tree Linux 7.2 driver (drivers/media/i2c/cvs) plus the
# upstream wake-IRQ fix carried by the PKGBUILD, built out of tree so it
# exists on every 7.2+ kernel regardless of the kernel's Kconfig.
# Arch-derived configs cannot enable VIDEO_INTEL_CVS at all: it lives under
# the "Miscellaneous helper chips" menu, which is hidden by
# MEDIA_HIDE_ANCILLARY_SUBDRV unless CONFIG_EXPERT is set.
#
# 7.2's ipu-bridge inserts the CVS device between the sensor and the IPU
# CSI2 receiver, so the IPU only sees the sensor once a driver registers
# this V4L2 bridge sub-device. Kernels before 7.2 keep using the legacy
# misc driver from vision-drivers (see dkms-vision-drivers.conf).
PACKAGE_NAME="intel-cvs"
PACKAGE_VERSION="@PKGVER@"
AUTOINSTALL="yes"
# 7.2 and later only (regex, so the pacman dkms hook also honours it)
BUILD_EXCLUSIVE_KERNEL="^(7\.([2-9]|[1-9][0-9]+)\.|([8-9]|[1-9][0-9]+)\.)"
BUILT_MODULE_NAME[0]="intel_cvs"
BUILT_MODULE_LOCATION[0]=""
DEST_MODULE_LOCATION[0]="/updates"
MAKE[0]="make KERNEL_SRC=${kernel_source_dir}"
CLEAN="make KERNEL_SRC=${kernel_source_dir} clean"
Loaded 100 of 571 files, more files were not shown because too many files have changed in this diff. Show more