171 Commits
Author SHA1 Message Date
Ryan Hughes d47199f2f2 Merge pull request #984 from omacom/auto/track-branches-omarchy-dev-omarchy-settings-dev
Track upstream branches: omarchy-dev 4.0.0.r6911.g9480d1b-1, omarchy-settings-dev 4.0.0.r6911.g9480d1b-1
2026-10-11 19:59:42 -04:00
Ryan Hughes 66ef55ded6 Merge pull request #985 from omacom/auto/track-branches-openclaw
Track upstream branches: openclaw 2026.10.1-1
2026-10-11 19:59:06 -04:00
ryanrhughes 8aa51cdc5b Track upstream branches: openclaw 2026.10.1-1 2026-10-11 23:51:45 +00:00
ryanrhughes f5f516d56d Track upstream branches: omarchy-dev 4.0.0.r6911.g9480d1b-1, omarchy-settings-dev 4.0.0.r6911.g9480d1b-1 2026-10-11 23:51:41 +00:00
Michael C HurleyandRyan Hughes f3e477be9f Add omadesign-bin 0.6.3 for ARM64 and x86_64 (#343)
* Add omadesign-bin alpha for ARM64 and x86_64

* Update omadesign-bin to 0.0.4alpha

* Update omadesign-bin to 0.6.3 with complete runtime and release watch

---------

Co-authored-by: Ryan Hughes <ryan@heyoodle.com>
2026-10-11 23:20:08 +00:00
Ryan Hughes c17afe1706 Merge pull request #983 from omacom/auto/sync-rebuilds-quickshell-git
Rebuild against updated dependencies: quickshell-git 0.3.2.r0.g4f508be-3
2026-10-11 18:33:14 -04:00
Ryan Hughes c82acec7a7 Merge pull request #982 from omacom/auto/sync-rebuilds-quickshell
Rebuild against updated dependencies: quickshell 0.3.2-4
2026-10-11 18:32:47 -04:00
dhh 9c26c9316a Rebuild against updated dependencies: quickshell-git 0.3.2.r0.g4f508be-3 2026-10-11 22:25:23 +00:00
dhh e470fa493d Rebuild against updated dependencies: quickshell 0.3.2-4 2026-10-11 22:25:21 +00:00
Ryan Hughes c01894c8b8 Merge pull request #981 from omacom/auto/sync-upstream-omacharts
Sync upstream releases: omacharts 0.1.13-1
2026-10-11 18:19:22 -04:00
Ryan Hughes e52f779dfe Merge pull request #980 from omacom/auto/sync-upstream-t3code-nightly-bin
Sync upstream releases: t3code-nightly-bin 0.0.46_nightly.20261011.2994-1
2026-10-11 18:16:47 -04:00
dhh 67569bb16f Sync upstream releases: omacharts 0.1.13-1 2026-10-11 22:09:32 +00:00
dhh b2e05dfe57 Sync upstream releases: t3code-nightly-bin 0.0.46_nightly.20261011.2994-1 2026-10-11 22:09:30 +00:00
Ryan Hughes e5852b6d4a Merge pull request #969 from omacom/auto/track-branches-omarchy-dev-omarchy-settings-dev
Track upstream branches: omarchy-dev 4.0.0.r6908.ge71a816-1, omarchy-settings-dev 4.0.0.r6908.ge71a816-1
2026-10-11 12:04:08 -04:00
ryanrhughes a3db46e792 Track upstream branches: omarchy-dev 4.0.0.r6908.ge71a816-1, omarchy-settings-dev 4.0.0.r6908.ge71a816-1 2026-10-11 15:54:13 +00:00
40ea1555f2 Ship the complete passwordless-sudo lifecycle in settings-dev (#645)
* Move the dev sudo grant lifecycle into omarchy-settings-dev

The hardened omarchy-sudo-passwordless publishes a grant only when its revocation hook is installed and no package transaction has left /run/omarchy-sudo-passwordless-package-removing behind. The hook sets that marker before every settings upgrade, and the settings scriptlets are what clear it, so shipping the hook alone let the first grant work and then refused every grant after the next update until a reboot.

omarchy-settings-dev now ships the command, omarchy-security-functions, the boot cleanup rule and the hook together, with the scriptlets from #257 that revoke before the package changes and clear the marker once no grant remains. omarchy-dev stops shipping the two helpers and requires the settings release that does: without it omarchy-update has no library to source.

Co-authored-by: Afonso Oliveira <afonso.oliveira707@gmail.com>

* Check that settings-dev registers the scriptlet the lifecycle test exercises

The lifecycle test reads the .install file directly, so it kept passing with the recipe's install= line gone, which is the one change that would strand the hook's marker again.

Co-Authored-By: Codex XHigh <noreply@openai.com>

* Keep omarchy-dev's settings dependency unversioned

The dev pair builds each package on its own against the published edge repository, in the PR build and in publish alike, so omarchy-dev cannot require a settings-dev release that only this change produces: the floor failed both omarchy-dev builds on this PR, and an exact pkgver match would fail the same way on every upstream pin bump. Nothing needs it. The upstream watch moves both pins together, publish stops the run before anything is published when one package fails, and a new settings-dev over an old runtime is refused on the files they would both own.

* Refuse settings-dev releases that predate the grant helper move

With the dependency unversioned, a new omarchy-dev could still install over a settings-dev that is held back or not yet published, removing omarchy-security-functions from both, and omarchy-update cannot start without it. A versioned conflict refuses that pairing at install time without constraining the per-package builds, which resolve only depends.

Co-Authored-By: Codex XHigh <noreply@openai.com>

* Require the security library as a capability instead of a version floor

The conflict named the last settings-dev release without the helpers, a version that moves with every upstream pin bump and settings-only pkgrel while this change waits to merge, and a rebase that kept a stale floor still passed the tests while the published release slipped past it. omarchy-settings-dev now provides omarchy-security-functions and omarchy-dev's package() requires it. makepkg checks only the top-level depends before building, so the per-package builds still resolve against the published repository, while pacman refuses a runtime without a settings release that ships the library, and pulls that release in when only the runtime is asked for.

Co-Authored-By: Codex XHigh <noreply@openai.com>

---------

Co-authored-by: Spencer Bull <spencer@omarchy.org>
Co-authored-by: Afonso Oliveira <afonso.oliveira707@gmail.com>
Co-authored-by: Codex XHigh <noreply@openai.com>
2026-10-11 09:56:47 -05:00
David Heinemeier HanssonandClaude Opus 5.5 60551aaddc Let Neovim's status line show the terminal background (#967)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 13:24:23 +00:00
Ryan Hughes d5406b5cb0 Merge pull request #966 from omacom/auto/sync-upstream-t3code-nightly-bin
Sync upstream releases: t3code-nightly-bin 0.0.46_nightly.20261011.2967-1
2026-10-11 09:14:14 -04:00
dhh 68a2c1fb4d Sync upstream releases: t3code-nightly-bin 0.0.46_nightly.20261011.2967-1 2026-10-11 13:07:38 +00:00
David Heinemeier HanssonandClaude Opus 5.5 9c81c8a705 Update omarchy-hyprland-titlebars to 0.2.1 (#964)
Titlebars stay off until the config enables them, so a plugin left loaded
after a workspace stops floating no longer puts bare bars on every window.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 13:03:38 +00:00
David Heinemeier HanssonandClaude Opus 5.5 d0815d6339 Let Neovim's current line show the terminal background (#963)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 12:57:49 +00:00
Ryan Hughes 175c605260 Merge pull request #961 from omacom/auto/track-branches-omarchy-dev-omarchy-settings-dev
Track upstream branches: omarchy-dev 4.0.0.r6897.g4df5459-1, omarchy-settings-dev 4.0.0.r6897.g4df5459-1
2026-10-11 05:20:34 -04:00
ryanrhughes be8f68c212 Track upstream branches: omarchy-dev 4.0.0.r6897.g4df5459-1, omarchy-settings-dev 4.0.0.r6897.g4df5459-1 2026-10-11 09:12:08 +00:00
David Heinemeier HanssonandClaude Opus 5.5 24a7358c17 Add Signed PDF signing package (#960)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 17:55:47 +09:00
David Heinemeier Hansson 556ab26156 Merge pull request #957 from omacom/titlebars-from-repo
Build omarchy-hyprland-titlebars from its own repository, and move pins on rebuilds
2026-10-11 08:17:49 +02:00
Ryan Hughes 93271007bb Merge pull request #958 from omacom/auto/sync-upstream-omakade
Sync upstream releases: omakade 1.16.1-1
2026-10-11 02:10:10 -04:00
Ryan Hughes 90636164c1 Merge pull request #959 from omacom/auto/sync-upstream-t3code-nightly-bin
Sync upstream releases: t3code-nightly-bin 0.0.46_nightly.20261011.2955-1
2026-10-11 02:09:01 -04:00
dhh efa2f210bc Sync upstream releases: t3code-nightly-bin 0.0.46_nightly.20261011.2955-1 2026-10-11 06:02:13 +00:00
dhh c61e5e472f Sync upstream releases: omakade 1.16.1-1 2026-10-11 06:02:12 +00:00
David Heinemeier HanssonandClaude Opus 5.5 f854e042a6 Move exact trigger pins when sync-rebuilds bumps a package
A package built against a trigger's ABI pins it exactly, such as a
Hyprland plugin on hyprland=0.56.2. Rebuilding it against a newer trigger
left the pin behind, so the rebuilt package demanded the version it no
longer loads into. sync-rebuilds now moves pins in the per-architecture
depends arrays to the version each architecture rebuilds against.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 14:46:10 +09:00
David Heinemeier HanssonandClaude Opus 5.5 41c7c2d948 Build omarchy-hyprland-titlebars from its own repository
The plugin source moved to omacom/omarchy-hyprland-titlebars, with its
history. The package now builds from that repository's release tags, like
Monologue and Hype, and keeps rebuilding with every Hyprland. v0.2.0
drops edge snapping, titles windows with their app's name, and highlights
titlebar buttons on hover.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 14:46:10 +09:00
Ryan Hughes 99dd4771b5 Merge pull request #953 from omacom/auto/track-branches-omarchy-dev-omarchy-settings-dev
Track upstream branches: omarchy-dev 4.0.0.r6871.g04b374a-1, omarchy-settings-dev 4.0.0.r6871.g04b374a-1
2026-10-10 22:14:03 -04:00
Ryan Hughes c132fcde07 Merge pull request #954 from omacom/auto/track-branches-mise-bin
Track upstream branches: mise-bin 2026.10.7-1
2026-10-10 22:12:00 -04:00
ryanrhughes 6645e01922 Track upstream branches: mise-bin 2026.10.7-1 2026-10-11 02:05:14 +00:00
ryanrhughes 9fe85272cb Track upstream branches: omarchy-dev 4.0.0.r6871.g04b374a-1, omarchy-settings-dev 4.0.0.r6871.g04b374a-1 2026-10-11 02:05:13 +00:00
Ryan Hughes 81d8b8c525 Merge pull request #951 from omacom/auto/track-branches-omarchy-dev-omarchy-settings-dev
Track upstream branches: omarchy-dev 4.0.0.r6868.gf2a39c4-1, omarchy-settings-dev 4.0.0.r6868.gf2a39c4-1
2026-10-10 18:59:08 -04:00
Ryan Hughes 06d43b0f6f Merge pull request #952 from omacom/auto/track-branches-rawmakase-bin
Track upstream branches: rawmakase-bin 0.2.4-1
2026-10-10 18:57:15 -04:00
ryanrhughes 1b75e24451 Track upstream branches: omarchy-dev 4.0.0.r6868.gf2a39c4-1, omarchy-settings-dev 4.0.0.r6868.gf2a39c4-1 2026-10-10 22:50:51 +00:00
ryanrhughes ce44914763 Track upstream branches: rawmakase-bin 0.2.4-1 2026-10-10 22:50:51 +00:00
Ryan Hughes d58cb276a2 Merge pull request #940 from birkskyum/drop-iwd-from-arm-runtime
Leave iwd to omarchy-mac on aarch64
2026-10-10 18:10:41 -04:00
Ryan Hughes 9e28d35c86 Merge pull request #949 from omacom/auto/sync-upstream-omacharts
Sync upstream releases: omacharts 0.1.11-1
2026-10-10 18:03:31 -04:00
Ryan Hughes d0c84b5919 Merge pull request #947 from omacom/auto/sync-upstream-omakade
Sync upstream releases: omakade 1.16.0-1
2026-10-10 18:01:41 -04:00
Ryan Hughes b066f816bd Merge pull request #948 from omacom/auto/sync-upstream-omarchy-meeting-recorder-bin
Sync upstream releases: omarchy-meeting-recorder-bin 1.7.0-1
2026-10-10 18:00:47 -04:00
Ryan Hughes f02f02b3a8 Merge pull request #946 from omacom/auto/sync-upstream-t3code-nightly-bin
Sync upstream releases: t3code-nightly-bin 0.0.46_nightly.20261010.2935-1
2026-10-10 18:00:36 -04:00
Marcelo Alcantara 5566be3e01 Pin the Mac packages to omarchy-mac-pkgs d8100ec9 (#950) 2026-10-10 21:58:21 +00:00
dhh 77c2c9807b Sync upstream releases: omacharts 0.1.11-1 2026-10-10 21:53:33 +00:00
dhh 356f7c657e Sync upstream releases: omakade 1.16.0-1 2026-10-10 21:53:33 +00:00
dhh 81670c4a4e Sync upstream releases: omarchy-meeting-recorder-bin 1.7.0-1 2026-10-10 21:53:33 +00:00
dhh c1581a8cbb Sync upstream releases: t3code-nightly-bin 0.0.46_nightly.20261010.2935-1 2026-10-10 21:53:32 +00:00
Birk Skyum 4997d4beb4 Merge remote-tracking branch 'origin/master' into drop-iwd-from-arm-runtime
# Conflicts:
#	pkgbuilds/omarchy-dev/PKGBUILD
2026-10-10 23:16:39 +02:00
Ryan Hughes effdae0b6a Merge pull request #941 from omacom/auto/track-branches-omarchy-dev-omarchy-settings-dev
Track upstream branches: omarchy-dev 4.0.0.r6866.g77bfaca-1, omarchy-settings-dev 4.0.0.r6866.g77bfaca-1
2026-10-10 15:37:42 -04:00
Ryan Hughes bc25acb404 Merge pull request #943 from omacom/auto/track-branches-cursor-bin
Track upstream branches: cursor-bin 3.24.12-1
2026-10-10 15:37:38 -04:00
Ryan Hughes 1161db0816 Merge pull request #942 from omacom/auto/track-branches-rawmakase-bin
Track upstream branches: rawmakase-bin 0.2.3-1
2026-10-10 15:34:45 -04:00
ryanrhughes d331905a0c Track upstream branches: cursor-bin 3.24.12-1 2026-10-10 19:28:50 +00:00
ryanrhughes ef38c10e58 Track upstream branches: rawmakase-bin 0.2.3-1 2026-10-10 19:28:49 +00:00
ryanrhughes 51c492e708 Track upstream branches: omarchy-dev 4.0.0.r6866.g77bfaca-1, omarchy-settings-dev 4.0.0.r6866.g77bfaca-1 2026-10-10 19:28:48 +00:00
Birk Skyum 2b04aa5bf6 Leave iwd to omarchy-mac on aarch64
omarchy and omarchy-dev pulled iwd in on every aarch64 machine. Only
Apple Silicon uses it, and omarchy-mac depends on it already. On other
ARM laptops its 80-iwd.link renames the Wi-Fi interface to wlan0 at the
next boot, so a saved NetworkManager connection tied to the old name
stops connecting.
2026-10-10 17:47:25 +02:00
Ryan Hughes 7aa734313d Merge pull request #938 from omacom/auto/track-branches-mise-bin
Track upstream branches: mise-bin 2026.10.6-1
2026-10-10 11:40:23 -04:00
Ryan Hughes f14c5d206d Merge pull request #939 from omacom/auto/track-branches-gliff
Track upstream branches: gliff 0.3.1-1
2026-10-10 11:39:36 -04:00
ryanrhughes 59b1dd0a08 Track upstream branches: gliff 0.3.1-1 2026-10-10 15:32:44 +00:00
ryanrhughes db4f3d9d2c Track upstream branches: mise-bin 2026.10.6-1 2026-10-10 15:32:43 +00:00
Ryan Hughes 0a906801f1 Merge pull request #936 from omacom/auto/sync-upstream-t3code-nightly-bin
Sync upstream releases: t3code-nightly-bin 0.0.46_nightly.20261010.2922-1
2026-10-10 08:53:06 -04:00
dhh 040315b98f Sync upstream releases: t3code-nightly-bin 0.0.46_nightly.20261010.2922-1 2026-10-10 12:45:03 +00:00
outfoxxed 5fcf959788 Patch Quickshell to temporarily work around Qt 6.12 breakage (#935)
Namespacing conflict from new Qt 6.12 types, patching until
its clear if this is a Qt bug or not.
2026-10-10 10:14:07 +00:00
Ryan Hughes b1d7399222 Merge pull request #934 from omacom/auto/track-branches-bun-bin
Track upstream branches: bun-bin 1.4.3-1
2026-10-10 04:56:24 -04:00
ryanrhughes 2d0e3b510a Track upstream branches: bun-bin 1.4.3-1 2026-10-10 08:49:39 +00:00
Ryan Hughes 8787c23f03 Merge pull request #933 from omacom/auto/track-branches-omarchy-dev-omarchy-settings-dev
Track upstream branches: omarchy-dev 4.0.0.r6863.ga15636b-1, omarchy-settings-dev 4.0.0.r6863.ga15636b-1
2026-10-10 02:34:40 -04:00
emirb c0ff0692f0 Track upstream branches: omarchy-dev 4.0.0.r6863.ga15636b-1, omarchy-settings-dev 4.0.0.r6863.ga15636b-1 2026-10-10 06:26:14 +00:00
Ryan Hughes 306ac722f5 Merge pull request #932 from omacom/auto/sync-upstream-omacharts
Sync upstream releases: omacharts 0.1.10-1
2026-10-10 02:14:35 -04:00
Ryan Hughes e79a9fbda8 Merge pull request #931 from omacom/auto/sync-upstream-t3code-nightly-bin
Sync upstream releases: t3code-nightly-bin 0.0.46_nightly.20261010.2908-1
2026-10-10 02:11:17 -04:00
dhh cec136fe20 Sync upstream releases: omacharts 0.1.10-1 2026-10-10 06:04:28 +00:00
dhh 703e9d5fbc Sync upstream releases: t3code-nightly-bin 0.0.46_nightly.20261010.2908-1 2026-10-10 06:04:27 +00:00
Ryan Hughes 3c27563492 Build gliff for aarch64 (#929)
gliff 0.3.0 compiles natively on aarch64 and its workspace tests pass;
its x86 SIMD paths have scalar fallbacks. openh264 only invokes nasm for
x86 targets, so nasm becomes an x86_64-only makedepend.
2026-10-10 02:49:23 +00:00
Jorge Manrubia c2166632e9 Add omacharts (#802)
A fast, keyboard-driven charting app for Omarchy, built on GTK4 and
libadwaita. It follows the active Omarchy theme, generates an indicator
palette for whichever theme is in force, and ships a plugin for the bar.

Source build from the project's own tagged release, tracked by a GitHub
watch so version bumps come from the release feed. Not in the AUR.
2026-10-10 02:23:06 +00:00
Marcelo Alcantara 859c6ff154 Update omarchy-mac to 0.1.2 (#927)
Pins omarchy-mac-pkgs 5b2c815: follows omacom/omarchy#14431's aarch64-apple
platform name and skips the Electron GL wrapping on a runtime without its
helpers.
2026-10-10 02:03:32 +00:00
Ryan Hughes 0a0c01a205 Merge pull request #926 from omacom/auto/track-branches-omarchy-dev-omarchy-settings-dev
Track upstream branches: omarchy-dev 4.0.0.r6860.g7cc6a55-1, omarchy-settings-dev 4.0.0.r6860.g7cc6a55-1
2026-10-09 22:01:23 -04:00
ryanrhughes f54d045df3 Track upstream branches: omarchy-dev 4.0.0.r6860.g7cc6a55-1, omarchy-settings-dev 4.0.0.r6860.g7cc6a55-1 2026-10-10 01:53:04 +00:00
Ryan Hughes 39ed2398ab Merge pull request #923 from omacom/auto/track-branches-omarchy-dev-omarchy-settings-dev
Track upstream branches: omarchy-dev 4.0.0.r6857.ge1b0e5e-1, omarchy-settings-dev 4.0.0.r6857.ge1b0e5e-1
2026-10-09 21:02:13 -04:00
emirb cfd1b9a554 Track upstream branches: omarchy-dev 4.0.0.r6857.ge1b0e5e-1, omarchy-settings-dev 4.0.0.r6857.ge1b0e5e-1 2026-10-10 00:54:20 +00:00
Emir Beganović 011a14a0d6 Ship the USB and Thunderbolt approval watchers as user units (#922)
Omarchy enables omarchy-usb-authorization.service and
omarchy-thunderbolt-authorization.service from first-run, and the
Thunderbolt one from its setup command and a migration. The settings
packages left both only in /usr/share/omarchy/default/systemd/user, where
systemd does not look.

On edge the Thunderbolt migration (1790344599) therefore fails with "Unit
omarchy-thunderbolt-authorization.service does not exist" and every later
migration stays pending. First-run fails as well and enables none of its
units.

Install both in /usr/lib/systemd/user, wherever the source has them: the
4.0.4 release does not, so its package is unchanged.
2026-10-10 00:18:43 +00:00
Ryan Hughes 4936892061 Merge pull request #914 from omacom/auto/sync-upstream-python-mediapipe
Sync upstream releases: python-mediapipe 1.1.0-1
2026-10-09 19:18:58 -04:00
Ryan Hughes 8a9b0f70ff Add buzz 0.5.28: Block's desktop workspace for humans and agents (#919)
Buzz (https://github.com/block/buzz, Apache-2.0) is a Tauri desktop app
on a Nostr relay. Upstream ships Linux builds for x86_64 only, so this
builds the desktop-v0.5.28 tag from source for x86_64 and aarch64,
following upstream's release-linux job: the six sidecar binaries from
the root workspace, then the app with --features mesh-llm. The updater
stays compiled out (its key and endpoint are unset), so pacman owns
updates. On the fast ring; the watch follows desktop-v* releases.

On aarch64, -march gains +crypto so sha2-asm can assemble its SHA
instructions; sha2 still detects them at runtime.

Built natively for aarch64 with bin/build and launched with isolated
HOME/XDG dirs: the window opens and renders the bundled frontend.
2026-10-09 23:11:59 +00:00
Ryan Hughes eb597ed851 Merge pull request #921 from omacom/auto/track-branches-omarchy-dev-omarchy-settings-dev
Track upstream branches: omarchy-dev 4.0.0.r6856.g6126321-1, omarchy-settings-dev 4.0.0.r6856.g6126321-1
2026-10-09 19:08:04 -04:00
Ryan Hughes 7dde80c9f7 python-mediapipe: leave the semantic retriever out of libmediapipe.so
MediaPipe 1.1.0 links its SQLite memory and vector stores from prebuilt
archives built with Google's internal toolchain. They have no archive symbol
table, and once indexed they still need Google's libc++ (std::__u) and
internal symbols (__alloc_token_*__Znwm, sqlite_helpers, sqlite3_vector_init),
so the link of libmediapipe.so fails. Drop the semantic retriever's C API from
the library; mediapipe and its other tasks import and work as before.
2026-10-09 19:03:02 -04:00
Ryan Hughes 6bbdcb8285 Merge pull request #920 from omacom/auto/track-branches-openai-codex-bin
Track upstream branches: openai-codex-bin 0.162.1-1
2026-10-09 18:55:19 -04:00
emirb 7f010b273e Track upstream branches: omarchy-dev 4.0.0.r6856.g6126321-1, omarchy-settings-dev 4.0.0.r6856.g6126321-1 2026-10-09 22:51:26 +00:00
emirb 0aa396dce2 Track upstream branches: openai-codex-bin 0.162.1-1 2026-10-09 22:47:42 +00:00
Ryan Hughes a268d610a3 Merge pull request #915 from omacom/auto/sync-upstream-t3code-nightly-bin
Sync upstream releases: t3code-nightly-bin 0.0.46_nightly.20261009.2886-1
2026-10-09 18:32:38 -04:00
Ryan Hughes 80ebf43ed9 Merge pull request #916 from omacom/auto/sync-upstream-walker
Sync upstream releases: walker 2.17.2-1
2026-10-09 18:28:42 -04:00
Ryan Hughes e6ca59be3a Merge pull request #913 from omacom/auto/sync-upstream-symfony-cli
Sync upstream releases: symfony-cli 5.22.0-1
2026-10-09 18:27:47 -04:00
Ryan Hughes 1aa8c1a886 Merge pull request #912 from omacom/auto/sync-upstream-1password-beta
Sync upstream releases: 1password-beta 8.12.42_32.BETA-1
2026-10-09 18:27:34 -04:00
Ryan Hughes 031fdcd972 Merge pull request #911 from omacom/auto/sync-upstream-once-bin
Sync upstream releases: once-bin 0.3.4-1
2026-10-09 18:27:32 -04:00
Ryan Hughes 16d6e44ad9 Merge pull request #910 from omacom/auto/sync-upstream-flea
Sync upstream releases: flea 0.3.8-1
2026-10-09 18:27:22 -04:00
Ryan Hughes 42aae5ee2c Merge pull request #909 from omacom/auto/sync-upstream-crush-bin
Sync upstream releases: crush-bin 0.98.1-1
2026-10-09 18:27:01 -04:00
ryanrhughes d764b4f7f4 Sync upstream releases: walker 2.17.2-1 2026-10-09 22:25:31 +00:00
ryanrhughes 29836c6da9 Sync upstream releases: t3code-nightly-bin 0.0.46_nightly.20261009.2886-1 2026-10-09 22:25:27 +00:00
ryanrhughes 39f0096f35 Sync upstream releases: python-mediapipe 1.1.0-1 2026-10-09 22:25:18 +00:00
ryanrhughes 473a6f12ea Sync upstream releases: symfony-cli 5.22.0-1 2026-10-09 22:25:16 +00:00
ryanrhughes d2cf8ee83e Sync upstream releases: 1password-beta 8.12.42_32.BETA-1 2026-10-09 22:25:05 +00:00
ryanrhughes 7fc218f0dd Sync upstream releases: once-bin 0.3.4-1 2026-10-09 22:25:05 +00:00
ryanrhughes 934eed2d7e Sync upstream releases: flea 0.3.8-1 2026-10-09 22:25:03 +00:00
ryanrhughes fc1d4b44dc Sync upstream releases: crush-bin 0.98.1-1 2026-10-09 22:25:03 +00:00
Ryan Hughes 9743e7654c Merge pull request #908 from omacom/fix/branch-tracker
Open one auto-merging PR per package from every sync workflow
2026-10-09 18:23:24 -04:00
Ryan Hughes ba7561a34c Open one auto-merging PR per package from every sync workflow
sync-upstream.yml, sync-rebuilds.yml and track-branches.yml each opened one
batch PR, so one package with a failing build held back every other update
in it (#788, #837). All three now group their changes with
package-pr-groups.sh and hand them to package-prs.yml, which opens a PR per
package and arms auto-merge. Packages pinned from the same upstream branch
still share one. Green packages land on their own and only the failures stay
open; a red PR conflicting with master, and each lane's old batch PR, get
closed.

sync-upstream.yml loses its human-merged lane: it now pushes with the PAT
like the other two, so its builds start without an approval hold and its PRs
auto-merge when green. min_release_age still quarantines fresh releases. Its
approve job, approve-sync-push.cjs, and auto-merge-pr.cjs's exception for
auto/sync-upstream go with it.
2026-10-09 18:20:57 -04:00
Ryan Hughes a48d48cc04 Merge pull request #907 from omacom/auto/track-branches
Track upstream branches: claude-code 2.1.296, claude-desktop 2.31226.1, github-copilot-cli 1.0.95, mise-bin 2026.10.5, omarchy-dev 4.0.0.r6848.g2519fd9, omarchy-settings-dev 4.0.0.r6848.g2519fd9, perplexity 26.10.1+build108257, zen-browser-bin 1.23.2b
2026-10-09 18:19:54 -04:00
Ryan Hughes a7f730d1e6 Give each tracked package its own PR, and let a fresh branch tip fall back
The branch tracker stalled omarchy-dev and omarchy-settings-dev twice in a
week:

- All ~50 auto_merge packages rode one PR, so omasnap-git's failing smoke
  suite kept the quattro pins unmerged from Oct 4 to Oct 8 (#788). Each
  package now gets its own PR on auto/track-branches-<package>; packages
  pinned from the same branch share one, since they move in lockstep. A run
  closes tracker PRs that master has moved past, and the old single PR.
- A branch tip younger than min_release_age held the whole branch, so a
  run that landed shortly after a quattro commit pinned nothing. The watch
  now falls back along first-parent history to the newest commit that has
  cleared the window, measured from clone time so siblings agree.
- The every-2-hours schedule fired every 6 to 9 hours in practice; run it
  hourly.
2026-10-09 18:09:40 -04:00
ryanrhughes 76c1de2185 Track upstream branches: claude-code 2.1.296, claude-desktop 2.31226.1, github-copilot-cli 1.0.95, mise-bin 2026.10.5, omarchy-dev 4.0.0.r6848.g2519fd9, omarchy-settings-dev 4.0.0.r6848.g2519fd9, perplexity 26.10.1+build108257, zen-browser-bin 1.23.2b 2026-10-09 22:05:49 +00:00
Emir Beganović 1cef67f336 Merge pull request #906 from omacom/ci/arch-image-from-ghcr
Pull the Arch image for tests and trackers from GHCR
2026-10-09 23:30:42 +02:00
Emir Beganovic e60fd71366 Pull the Arch image for tests and trackers from GHCR
The self-tests, the build-isolation fixture and the three tracker
workflows pulled archlinux:base-devel from Docker Hub without logging
in. On 2026-10-09 that failed two jobs on one pull request: one with
'toomanyrequests', one with a 504 from Docker Hub's token endpoint.

Arch publishes the same image as ghcr.io/archlinux/archlinux, which
GitHub's runners pull without that limit.
2026-10-09 23:27:47 +02:00
Ryan Hughes c1257946a3 Merge pull request #901 from omacom/auto/track-branches
Track upstream branches: basecamp-cli 0.13.0, brave-bin 1.97.56, brave-origin-bin 1.97.56, claude-code 2.1.295, claude-desktop 2.31226.0, cursor-bin 3.24.9, github-copilot-cli 1.0.94, lmstudio-bin 0.4.26, microsoft-edge-stable-bin 155.0.4283.45, mise-bin 2026.10.4, monologue 0.3.1, openai-codex-bin 0.162.0, openai-codex-desktop 26.1007.21434, openclaw 2026.9.9, slack-desktop 4.52.178, visual-studio-code-bin 1.141.0, zed 1.23.2, zen-browser-bin 1.23.1b
2026-10-09 12:27:57 -04:00
Ryan HughesandClaude Opus 5.5 d32f520cb9 Queue tracker PR builds instead of cancelling them (#902)
The tracker opens its PR and then labels it, so a new tracker PR starts
two build runs at once. The second cancelled the first, whose always-run
`result` job then failed and left a red required check on the PR (#901)
until the surviving run finished. The tracker also force-pushes every two
hours, which would cancel long builds the same way the sync branches did.

Treat auto/track-branches like auto/sync-*: the newer run waits and
reuses the finished build's artifacts.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 12:01:06 -04:00
ryanrhughes 0cda546059 Track upstream branches: basecamp-cli 0.13.0, brave-bin 1.97.56, brave-origin-bin 1.97.56, claude-code 2.1.295, claude-desktop 2.31226.0, cursor-bin 3.24.9, github-copilot-cli 1.0.94, lmstudio-bin 0.4.26, microsoft-edge-stable-bin 155.0.4283.45, mise-bin 2026.10.4, monologue 0.3.1, openai-codex-bin 0.162.0, openai-codex-desktop 26.1007.21434, openclaw 2026.9.9, slack-desktop 4.52.178, visual-studio-code-bin 1.141.0, zed 1.23.2, zen-browser-bin 1.23.1b 2026-10-09 15:31:54 +00:00
David Heinemeier HanssonandClaude Opus 5.5 88947c367b Auto-merge upstream updates for trusted packages (#892)
* Auto-merge upstream updates for trusted packages

The unattended lane already runs every upstream provider, not only branch
watches, so trusted release feeds can ship without waiting on the reviewed
sync PR. Opt in the browsers, large vendors' apps, Omacom's own projects,
and the vendor binaries that hold fresh releases for 24h: 39 packages,
including rawmakase-bin and gliff.

Drivers, firmware, the bootloader, core desktop components, beta and
nightly channels, and community projects stay on the reviewed lane.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Auto-merge grok-bot, perplexity, disktree-bin, cliamp, openclaw, voxtype-bin and ttfx

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 16:42:23 +02:00
David Heinemeier Hansson c104b20285 Install passwordless revoke hook in omarchy-dev (#896) 2026-10-09 14:42:00 +00:00
David Heinemeier HanssonandClaude Opus 5.5 cbded97503 Add omarchy-power-profiles-daemon 0.30, with a CPPC driver for ARM laptops (#897)
power-profiles-daemon only drives intel_pstate, amd_pstate and ACPI
platform_profile, so ARM laptops on cppc_cpufreq (NVIDIA N1X, Snapdragon X)
get placeholder profiles that change nothing and no performance profile.

This packages upstream 0.30 with a CPPC driver patch: balanced (EPP 64) and
power-saver (EPP 192) use CPPC autonomous frequency selection, performance
runs the performance governor. The driver only claims policies with the
schedutil and performance governors and writable autonomous selection knobs,
restores probe-time settings if the first activation fails, and reapplies the
profile on resume. The package provides and conflicts with
power-profiles-daemon, and fixes the powerprofilesctl shebang at package time
so Omarchy no longer needs to sed it.

Tested on an NVIDIA N1X laptop: median keystroke-to-frame latency in Chromium
went from ~130ms (schedutil) to ~56ms on balanced at the same idle power, and
~32ms on performance, which Omarchy selects on AC. All 137 upstream tests
pass, including five new CPPC tests.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 14:40:56 +00:00
ferdousandClaude Opus 5.5 8b8ede46ed Add Ghost: runtime with optional Omarchy HUD (#390)
* Add ghost: an owner-local AI persona with an Omarchy-native HUD

Ghost (https://github.com/ferdousbhai/ghost, Apache-2.0) is a local AI
persona for Omarchy: a daemon on 127.0.0.1, a Quickshell HUD summoned
with Super+Ctrl+G, a `ghost` CLI, an opt-in Chromium relay, and a
computer-use helper. The package builds from the v0.1.0 GitHub release's
sanitized source archive plus its prebuilt Bun runtime archive; the
`.omarchy/package.json` upstream block follows later releases the same
way. check() runs the project's own test suite against the payload.

Built locally with `makepkg --cleanbuild` from these exact inputs on
Omarchy 4.0.0.alpha: ghost-0.1.0-1-x86_64.pkg.tar.zst.

* ghost 0.1.1: arch=('any'), one runtime archive for every architecture

The payload is Bun-target JavaScript with no native modules; verified
on aarch64 under emulation (ferdousbhai/ghost#52). The runtime archive
is now named linux-any and listed with the source tarball under
sources.any.

* ghost: 0.1.2

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017TQbyws7icrhj7H7V7yi6v

* ghost: 0.2.0

* ghost: 0.2.1

* ghost: 0.2.2

* ghost: 0.2.3

* ghost: 0.3.0

* ghost: 0.4.0 with independently installable runtime

* ghost: 0.4.1

* ghost: 0.4.2

* ghost: 0.4.3

* ghost: 0.5.0

* ghost: 0.5.1

* ghost: 0.5.2

* ghost: 0.6.0

* ghost: 0.6.0-2, declare ripgrep for package checks

check() in the 0.6.0 source runs packaging tests that need rg, and the
clean build container does not have it, so every build since 0.5.0 stopped
with "ripgrep is required by package checks but is not installed".
Same version, so pkgrel goes to 2 (ghost e97eb09 carries the same
checkdepend for the next release's render).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-09 14:39:53 +00:00
Birk Skyum 1aecf94f22 Initialize pacman keys in build containers (#376)
Recreate pacman's local signing key before upgrading packages so keyring install scripts can sign trusted keys. Keep the key out of the builder image and exercise keyring population with a keyless test image.
2026-10-09 10:28:21 -04:00
bf0af463ef Add linux-aarch64 kernel compatibility shim (#222)
* Add linux-aarch64-pkgbase-shim

Arch Linux ARM's kernel packages install the kernel as /boot/Image and ship
no usr/lib/modules/<ver>/{pkgbase,vmlinuz}, which is how mkinitcpio and
limine-entry-tool find kernels. One pacman hook, ordered 85- so it runs
before 90-mkinitcpio-install, writes both files into every package-owned
modules directory that lacks pkgbase; the usual hook then builds the UKI and
writes the Limine entry. It also covers kernels already present when the
shim itself is installed, cleans up the leftover directories of removed
kernels, and never touches a directory that has pkgbase, so it retires by
removal once archlinuxarm/PKGBUILDs#2215 (or its successor) lands.

Added to the aarch64 workflow's ISO package set.

* linux-aarch64-pkgbase-shim: leave the rebuild to limine-update when the 90 hook is masked

omarchy-iso's installer points 90-mkinitcpio-install.hook at /dev/null in
the target during the chroot phase and runs limine-update itself afterwards.
When the shim is installed in that phase it writes pkgbase and vmlinuz and
would otherwise start a UKI build that finalize_limine_boot throws away.

* linux-aarch64-pkgbase-shim: pkgrel 2

The aarch64 repo build only rebuilds packages whose version moved, so the
previous script fix never reached the repository.

* Trim aarch64 kernel shim comments

* Refresh shim kernel images safely on reinstall

* Run package regression suites in existing CI

* Cover shim ownership and failed-copy handling

* Handle shared kernel-directory ownership in the ARM boot shim

* Limit the CI change to the package test scripts

* Warn when a kernel directory's pkgbase names another package

---------

Co-authored-by: Jimmy Van Veen <jimmy.van.veen@gmail.com>
Co-authored-by: Jim Martin <jimmartin@gmail.com>
2026-10-09 10:21:04 -04:00
9ea749835f Add safe Windows firmware extraction for Snapdragon laptops (#221)
* Add qcom-firmware-extract

Copy the vendor-signed Qualcomm firmware named by a Snapdragon laptop device tree from its Windows driver store into /usr/lib/firmware/updates.

* Select matching Qualcomm firmware variants

* Trim firmware extractor comments

* Keep firmware selection and initramfs updates safe on partial installs

* Run package regression suites in existing CI

* Match kernel firmware compression and search order

* Limit the CI change to the package test scripts

* Match firmware names exactly when updating the manifest

* Count compressed firmware only when the kernel can load it

* Scope the README's firmware claim to this package

* Find DSP device-tree images under their Windows names

The Surface Pro 11 device tree asks for adsp_dtb.mbn and cdsp_dtb.mbn,
but Windows ships them as adsp_dtbs.elf and cdsp_dtbs.elf, so
extraction left both DSP device-tree images missing. Look for the
Windows name when the device-tree name is not found. Exact names still
take precedence, and the existing companion matching still selects
between variants.

Validated with test.sh and on a Surface Pro 11 against its own Windows
driver store: 5 of 5 firmware files staged, up from 3, and the ADSP and
CDSP boot with the device-tree images taken from the .elf files.

* Let later firmware sources fill in what a stage lacks

Any stage manifest, even an empty one, made --stage exit as already
staged and made --install use only the stage, so Windows was never read
again. A --stage rerun now keeps the staged files and looks for the
rest, and --install scans Windows only for what the stage lacks. -d
still replaces both. A rerun without root, which cannot read Windows,
keeps the stage and succeeds as before. Each run reads the stage
manifest once.

Mount points honour QCOM_FW_ROOT so the tests can stub the partition
scan. Both root checks now take the user ID from QCOM_FW_TEST_EUID when
the tests set it, and the install-mode check no longer skips when
QCOM_FW_ROOT is set.

* Let board packages list firmware that must not come from Windows

Some device trees name firmware that should not be copied from Windows:
files a board package produces or holds back until they are tested, and
files the machine's boot firmware loads before Linux, which Windows
drivers do not carry.
On the Surface Laptop 8 these are its USB4 router image and SOCCP pair.

A board package can now list such names in
/usr/share/qcom-firmware-extract/provided.d/<compatible>.list. On a
machine with that compatible they are never searched for, installed or
reported by --list-missing, and the log names the list for each one. An
unreadable list or an entry containing a space is skipped with a
warning.

A copy an earlier --install made of a newly listed name stays in place:
the same bytes would not show that the file is still this tool's copy,
since a board's own tool may keep the same file there. The next
--install stops recording it and prints where it is, for the board
package or the user to remove.

Since --list-missing omits listed names, a board must not list firmware
that a hardware check relies on unless its package installs it before
the check runs.

* Find staged firmware by file name when its device-tree path changed

Since the stage became the first source, --install takes a staged file
only by its exact device-tree path. After a full-disk install the stage
is the only copy, so a device tree that files an image under another
path could no longer be served from it. After Windows, fall back to a
staged file with the same name that the stage manifest lists, but not
when Windows refused differing variants of it as ambiguous.

* Test that listed firmware never sends the extractor to the disks

With every name installed or listed, --list-missing prints nothing,
--stage and --install neither list nor mount partitions, and listed
names are not installed from Windows or the stage. A control run
without the list does scan. Listing the names again leaves the copies
it installed in place but stops recording them, and another missing
file still sends the extractor to Windows for that file alone.

* Read only internal disks from an installed system

--install mounts the Windows partitions whenever the stage lacks a
file, which after a full-disk install can be every later run, and it
mounted every NTFS partition, USB disks included. It now reads only
partitions on disks that lsblk reports as neither removable, hotplug
nor USB. --stage in the live session still reads all of them, internal
disks first, and -d takes a driver store from anywhere.

---------

Co-authored-by: Jimmy Van Veen <jimmy.van.veen@gmail.com>
Co-authored-by: Brandon Williams <turbine.bmw@gmail.com>
Co-authored-by: Jim Martin <jimmartin@gmail.com>
2026-10-09 10:13:34 -04:00
Ryan Hughes 69c684305e Merge pull request #895 from omacom/ci/builder-account-ssh-key
Attach operator keys to builders so none gets an emailed root password
2026-10-09 10:03:30 -04:00
Ryan Hughes 7e86af28b3 Attach operator keys to builders so none gets an emailed root password
The controller created every builder without a DigitalOcean account key, so
DigitalOcean set a root password, emailed it to the token's owner, and turned
password logins on: one email and one password-reachable box per builder,
146 of them on Oct 8.

DO_SSH_KEYS lists account key IDs attached at creation (Ryan, DHH and Emir
in the template). It needs ssh_key:read on the controller's token; without it
every create is refused with 403, so it stays empty until the token has it.
Builders also turn password SSH off in cloud-init, which holds even while no
account key is attached, and Emir joins the operators whose GitHub keys reach
root.
2026-10-09 10:01:16 -04:00
Emir Beganović e34944e9fb Merge pull request #879 from omacom/publish/queue-every-merge
Queue waiting publishes instead of cancelling all but one
2026-10-09 14:54:36 +02:00
Emir Beganović d1f4696163 Merge pull request #875 from omacom/docs/current-pipeline
Rewrite the README around the pipeline as it runs today
2026-10-09 14:50:46 +02:00
github-actions[bot]anddhh e3730916ff chore: sync upstream releases (#887)
Co-authored-by: dhh <2741+dhh@users.noreply.github.com>
2026-10-09 14:47:23 +02:00
David Heinemeier HanssonandClaude Opus 5.5 7fa74a3369 Add rawmakase-bin 0.2.2, following upstream GitHub releases (#890)
RAWmakase is a Lightroom-compatible RAW photo editor. Upstream publishes
self-contained Linux tarballs for x86_64 and aarch64 (LibRaw, lcms2,
libjpeg and ONNX Runtime bundled under /usr/lib/rawmakase) with a
SHA256SUMS manifest, so the package re-wraps that usr/ tree and
sync-upstream follows releases declaratively with a 24h quarantine.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 12:47:04 +00:00
David Heinemeier Hansson f3e71f8a13 Restart active OWE user services after upgrades (#888) 2026-10-09 11:43:56 +02:00
3e4cb2308e Update superwhisper-bin to 1.0.0 and follow its stable releases (#882)
Superwhisper 1.0.0 is its first stable release. The watch matched only beta
tags, so sync-upstream would never offer it; it now follows stable releases
and ignores prereleases.

Co-authored-by: superduck00 <294879536+superduck00@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 10:13:12 +02:00
Ryan Hughes a090ebeb4b Merge pull request #886 from omacom/auto/sync-rebuilds
chore: rebuild against updated dependencies
2026-10-09 02:45:46 -04:00
Ryan Hughes 26839faa98 Merge pull request #880 from omacom/auto/track-branches
Track upstream branches: omarchy-dev 4.0.0.r6818.gc352b62, omarchy-settings-dev 4.0.0.r6818.gc352b62
2026-10-09 02:41:30 -04:00
Ryan HughesandJim Martin 2684cc04c7 Build LM Studio for aarch64 (#885)
Co-authored-by: Jim Martin <jimmartin@gmail.com>
2026-10-09 02:41:18 -04:00
dhh 90066fc19b chore: rebuild against updated dependencies 2026-10-09 06:38:38 +00:00
ryanrhughes 83778ddcb0 Track upstream branches: omarchy-dev 4.0.0.r6818.gc352b62, omarchy-settings-dev 4.0.0.r6818.gc352b62 2026-10-09 06:37:15 +00:00
Spencer BullandGPT 6.1 Sol High ae5eac6532 Align OpenVINO GenAI with the edge runtime and hold syncing (#883)
Pin GenAI 2026.4.1.0 to match Arch OpenVINO 2026.4.1. Restrict the package to edge while rc and stable retain 2026.3.1, derive the exact core dependency from the GenAI version, and keep updates manual. Set standard PCRE2 install paths for the current CMake build.

Co-authored-by: GPT 6.1 Sol High <noreply@openai.com>
2026-10-08 23:44:39 -05:00
Spencer Bull 5c6444e604 Update cua-driver-bin to 0.33.4, which captures multi-monitor Hyprland desktops again (#807)
Driver 0.28.3 through 0.33.3 refused desktop capture on Hyprland with more than one output or one output away from the origin (trycua/cua#4161), which is why the package was held at 0.28.2; 0.33.4 fixes it (trycua/cua#4305). The plugin sources are unchanged from 0.32.0 through 0.33.4 and speak the same input protocol, so the plugin package stays and only its README names the new pairing.
2026-10-08 22:08:08 -05:00
Gabriel AramburuandClaude 17ae27fb21 Add OmaKeyboard: keyboard remapping and shortcuts (#564)
A Qt Quick application for remapping keys, building Fn-style layers and
managing shortcuts, per keyboard. It generates real XKB keymaps with
libxkbcommon and loads them through Hyprland's per-device kb_file; no
input daemon or third-party remapping engine is involved.

Builds from the published v0.1.0 tag. check() runs the project's tests,
which need no display.

Co-authored-by: Claude <noreply@anthropic.com>
2026-10-09 01:10:24 +00:00
Emir Beganovic 086d1e1336 Queue waiting publishes instead of cancelling all but one
The publish job runs in the concurrency group "publish" with
cancel-in-progress: false. By default such a group holds one running job
and one waiting job: when a third arrives, GitHub cancels the one that was
waiting and puts the new one in its place. That merge's publish is dropped,
with no publish record, PR comment or log line, and nothing reruns it.

17 of the last 181 publish runs ended cancelled, 12 of them from one burst
of merges on 2026-09-28. openvino-genai 2026.4.0.0-1 (#620) was one of
those and is still in no channel.

queue: max lets up to 100 jobs wait in the group and run in the order they
reach it. Nothing else changes: one publish at a time, and a running one is
not interrupted. unpublish.yml shares the group, so it gets the same
setting.

actionlint 1.7.12, the latest release, predates the key and reports it as
unexpected in both files (rhysd/actionlint#654). It reports nothing else.
2026-10-09 02:20:39 +02:00
Emir Beganovic 9b7daaf564 Rewrite the README around the pipeline as it runs today
The README described the repository host as the publisher: bin/repo
release as the main workflow, five-minute systemd timers, a signing key
that lives on the host and nowhere else, aarch64 under emulation. Since
September 18 packages are built by build-pr.yml and published by
publish.yml, and neither workflow was mentioned. ci/README.md still opened
with "Status: spike. Nothing here publishes."

The README now covers, in order: contributing a package, how a package
reaches users, where it is published, the maintainer tasks (republish,
build, retire, check that a version is live, read the publish log), what
to do when a publish fails, who can build and merge, the three scheduled
update workflows, package metadata, releases, and the rules that keep two
publishers from colliding.

Text about the old host pipeline moved to docs/repository-host.md, marked
abandoned. The rest moved to docs/ largely unchanged:
- docs/releases.md: the release train and versioning rules
- docs/rebuild-triggers.md: rebuild_on and bin/sync-rebuilds
- docs/builds.md: dependency order, isolation, builder images
- docs/upstream-sources.md: gains the provider and hook reference

ci/README.md is rewritten as the operating guide for the x86_64 builder
pool. Statements the move would have carried over wrong are corrected,
among them the signing key's location, AUR sync, and which architecture
list CI reads. bin/sync-upstream's help now points at the docs page that
holds the provider schemas.
2026-10-09 02:08:30 +02:00
Ryan Hughes 5abe08fbab Merge pull request #697 from omacom/auto/sync-rebuilds
chore: rebuild against updated dependencies
2026-10-08 19:39:59 -04:00
dhh 3a0d01876f chore: rebuild against updated dependencies 2026-10-08 23:32:58 +00:00
Emir Beganović 0aa4c721c1 Merge pull request #851 from omacom/t3code/appimage-lib-names
T3 Code: accept the AppImage's compatibility libraries under their new names
2026-10-09 00:36:48 +02:00
Marcelo Alcantara c71cfabed6 Re-pin omarchy-mac-boot to omarchy-mac-pkgs 162f3599e (#873)
main now has omarchy-mac-pkgs#20: a factory reset's reboot unlocks the disk
with a key from the Boot partition, and systemd could mount that partition
again just before switching to the real root without unmounting it
(systemd/systemd#28021), leaving /boot read-only so owner setup's re-key
failed. An initramfs drop-in makes the unmount finish before the switch. It
also has #18 (first-boot encryption progress on the splash) and test-only
changes (#11, #19). New UTC commit date, so 20261008-1. omarchy-mac stays at
2a3ed89.
2026-10-08 22:06:39 +00:00
Ryan Hughes 7d18e16925 Merge pull request #860 from omacom/auto/track-branches
Track upstream branches: omarchy-dev 4.0.0.r6815.g50d687a, omarchy-settings-dev 4.0.0.r6815.g50d687a
2026-10-08 16:23:28 -04:00
Ryan Hughes 2cce621d95 Retire the elephant packages and omarchy-walker (#872)
Omarchy 4 no longer uses Elephant or the omarchy-walker meta package, and
omarchy-upgrade-to-quattro removes them; every channel serves Omarchy 4.0.4.
Nothing else here depends on them. walker itself stays.

Deleting a recipe stops it building but leaves it in the channel databases,
and the only removal tool worked on the old repository host's local tree.
Add bin/unpublish-packages, publish-artifact's counterpart: pull the channel
database, repo-remove every entry built from the named pkgbases, upload it.
Package files stay in the bucket. unpublish.yml runs it per channel and
architecture under the publish lock, for packages with no recipe on master.
2026-10-08 16:18:30 -04:00
ryanrhughes 2ac2319b55 Track upstream branches: omarchy-dev 4.0.0.r6815.g50d687a, omarchy-settings-dev 4.0.0.r6815.g50d687a 2026-10-08 20:02:51 +00:00
Timothy Wright cb3909f052 Automate Grok Bot releases (#583)
* Automate Grok Bot releases

* Refresh Grok Bot to apt 0.66.0 and harden upstream.sh

Bump both architectures to Cursor apt 0.66.0 with matching SHA256s,
introduce a versioned _pool URL helper, and filter Packages stanzas by
Package: grok-bot so sync stays correct if the index grows.

* grok-bot: bump to 0.68.1, drop manual hold, validate pool filename

Bump both architectures to Cursor apt 0.68.1 with the SHA256s from the
amd64/arm64 Packages indexes (verified against the downloaded debs).

Drop grok-bot from the manual holds list in docs/upstream-sources.md and
have upstream.sh check that the newest stanza's Filename is the versioned
pool path the PKGBUILD downloads from. Both additions come from #848.
2026-10-08 15:50:44 -04:00
Ryan Hughes 678d7400ed Don't ask for a builder to rebuild what is already published (#870)
The split in #869 sent every tree without an artifact to the rebuild job,
so a dispatch whose x86 halves were already published waited eight minutes
for eleven droplets that each found nothing to build in five seconds. Make
the publish job's plan check in the changes job, on its hosted runner,
before a builder is requested.
2026-10-08 15:16:31 -04:00
Ryan Hughes ebd2d6a766 Publish in a minute: build outside the lock, sign on a hosted runner (#869)
A one-package merge took 9 to 15 minutes to publish for about 15 seconds of
signing and upload. The run-wide concurrency group made each merge wait for
every earlier run, builds included (#854 waited 13 minutes behind an aarch64
batch), and the publish job waited about 3 minutes for a builder droplet
even when every package had a PR artifact and nothing needed building.

Build x86_64 trees that have no artifact in the rebuild job, one droplet per
entry, as aarch64 already builds there on arm64 runners: in parallel, with
no secrets, and outside any lock. The publish job now only collects
artifacts, signs and uploads, on ubuntu-latest with the GHCR builder image
(#850), and only it holds the publish group.
2026-10-08 15:03:28 -04:00
Ryan Hughes 93b1655ca8 Auto-merge package PRs that bring their own test (#868)
#867 auto-merged only PRs changing nothing outside pkgbuilds/, so it would
have left #854 open too: like voxtype-bin and the IPU7 camera before it,
Superwhisper added tests/superwhisper-bin-install.sh and one test.yml line
running it.

Count those as package changes: a new file under tests/, and a test.yml
change whose every line adds a ./tests/*.sh call. test.yml runs on PRs only.
Editing an existing test still needs a maintainer, since builder-images.yml
runs tests/build-isolation.sh on master with packages: write.
2026-10-08 14:59:25 -04:00
Emir BeganovićandRyan Hughes 5d9783b85e Publish pulls the tested builder image instead of building it on every run (#850)
A builder droplet starts with no images, so publish.yml built
omarchy-pkg-builder from the Dockerfile each time: about 100 s of
pacstrap, keyring setup and toolchain install, to run gpg, repo-add,
bsdtar and rclone for about 14 s.

builder-images.yml already publishes the tested image for the current
build inputs to ghcr.io/omacom/omarchy-pkg-builder under bin/builder-image
key. Pull that, check its org.omarchy.builder.key label, and tag it as the
local name the rest of the step uses.

Build as before when no image carries the key, which is what a merge
that changes build/ sees until the refresh it triggered has finished.

Co-authored-by: Ryan Hughes <ryan@heyoodle.com>
2026-10-08 14:57:21 -04:00
Ryan Hughes 48d6217ff7 Auto-merge package PRs that are trusted to build (#867)
A package PR approved to build, by its author being trusted or by the
build-approved label, sat open after going green until someone merged it by
hand, so nothing it built was published. Enable GitHub's auto-merge on it
with PKGS_BOT_TOKEN, so the merge lands once the required checks pass and
starts publish.yml.

The trust rule is build-pr.yml's. Only PRs changing nothing outside
pkgbuilds/ qualify: a PR's own tooling never runs in its build, and after
merge it runs with the publish secrets. The upstream sync, which labels its
own PRs, stays on the reviewed lane. Removing build-approved withdraws the
auto-merge.
2026-10-08 14:33:54 -04:00
David Heinemeier Hansson 7d2c7c50af Package Superwhisper with upstream beta tracking (#854)
* Package Superwhisper with upstream beta tracking

* Seed fresh Superwhisper shortcuts before daemon startup

* Refresh checksum for Superwhisper user setup

* Fix Superwhisper panel setup and packaging review findings

* Read shell replies when retrying Superwhisper panel setup

* Exclude Superwhisper portable menu integration
2026-10-08 20:29:07 +02:00
Ryan Hughes e285432795 omatrack: fetch the pinned source from a copy, upstream is gone (#865)
tobi/omatrack no longer exists on GitHub, so the pinned commit's archive is
404 and 1.8.6 cannot be rebuilt; the old repository host's sync deleted the
published 1.8.6-1 from R2, leaving edge at 1.2.0. A public copy serves the
same commit with an archive matching the pinned sha256 byte for byte.

pkgrel 2 gives the rebuild a new filename, so no cache can serve the
deleted 1.8.6-1 bytes for it.
2026-10-08 13:38:28 -04:00
Ryan Hughes 5961a3ff5d Replace builder droplets stuck provisioning (#864)
A droplet DigitalOcean still reports as "new" never registers a runner, but
the controller counted it as one booting and created no replacement until
MAX_AGE_MINUTES. A publish job sat queued for minutes behind one in mkc1.

Delete droplets still provisioning after MAX_BOOT_MINUTES (10) and leave them
out of the live count, so the same tick creates a replacement.
2026-10-08 11:59:48 -04:00
Ryan Hughes 259a3fa8b8 quickshell-git: build v0.3.2, which compiles against Qt 6.12 (#863)
The pinned 0.3.0.r20 commit fails Qt 6.12's "Meta Types must be fully
defined" assertion in the PipeWire service, which kept the rebuild PR red.
2026-10-08 11:40:40 -04:00
37288aada4 Auto-merge rebuild PRs once their builds pass (#862)
sync-rebuilds now runs on the unattended lane like track-branches: it
opens the pkgrel bump PR with PKGS_BOT_TOKEN and enables auto-merge, so a
Qt (or any rebuild_on) update reaches users without a maintainer merge.
Branch protection still requires result, self-tests and build-isolation
to pass; a failed rebuild stays an open red PR.

The PAT is required because a GITHUB_TOKEN merge does not start
publish.yml. PAT pushes are not held for approval, so the approve job,
the build-approved label and the review request go away.

Co-authored-by: David Heinemeier Hansson <david@hey.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 11:08:41 -04:00
Ryan Hughes 128c5647ba Keep builders coming when DigitalOcean sells out a size or region (#861)
* Keep builders coming when DigitalOcean sells out a droplet size

ric1 sold out of g5-32vcpu-64gb-50gb, and every create came back 422. curl -f
dropped the reason and set -e ended the tick, so builders only appeared when
capacity happened to free up, and the journal showed nothing but "curl: (22)".

Try each of SIZES in turn, logging DigitalOcean's refusal message, and fail
the tick only when every size is refused. Builders now power off however
start.sh exits, so a failed registration is reaped instead of counting as a
booting runner until MAX_AGE_MINUTES. The controller unit pulls the checkout
before each tick, so merged controller fixes reach the box.

* Create builders in any region that has the size in stock

ric1 sold out of g5-32vcpu-128gb-50gb within minutes of the box switching to
it. Builders need nothing from a particular region, so read DigitalOcean's
size catalog once per tick and try each of SIZES in every region it lists in
stock, REGIONS first if set. A refused pair is dropped for the rest of the
tick.
2026-10-08 10:50:06 -04:00
David Heinemeier Hansson 024943141d Count the full builder queue across workflow states (#859) 2026-10-08 15:38:31 +02:00
David Heinemeier Hansson a1ad25bbae Bump releases for packages with mismatched cached archives (#858) 2026-10-08 15:01:06 +02:00
David Heinemeier HanssonandClaude Opus 5.5 77d62dd156 Add quickshell 0.3.2 built from upstream releases (#855)
Tracks tagged releases from the quickshell-mirror GitHub repository
through an upstream watch, so new versions arrive via sync-upstream.
The recipe follows Arch's extra/quickshell, builds for x86_64 and
aarch64, ships the Qt compatibility check hook, and rebuilds on Qt
updates like quickshell-git. It conflicts with quickshell-git.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 11:51:47 +02:00
David Heinemeier Hansson 7d70768c3f Retire redundant edge package overrides (#853)
* Prepare OPR overrides and rebuild Quickshell for Qt 6.12

* Retire redundant edge package overrides

* Drop Quickshell rebuild from retirement scope

* Remove retirement notes from upstream source documentation
2026-10-08 10:45:07 +02:00
Ryan Hughes efc09808c6 Merge pull request #852 from omacom/omarchy-hyprland-titlebars
Add omarchy-hyprland-titlebars
2026-10-08 02:35:38 -04:00
Ryan Hughes f004f5fa20 Merge pull request #788 from omacom/auto/track-branches
Track upstream branches: omarchy-dev 4.0.0.r6807.g902fd8a, omarchy-settings-dev 4.0.0.r6807.g902fd8a
2026-10-08 02:33:16 -04:00
ryanrhughes 963cbf1230 Track upstream branches: omarchy-dev 4.0.0.r6807.g902fd8a, omarchy-settings-dev 4.0.0.r6807.g902fd8a 2026-10-08 06:31:32 +00:00
Ryan HughesandClaude Opus 5.5 a57d0fdb7f Add omarchy-hyprland-titlebars
The native Hyprland plugin behind Omarchy's floating workspaces: themed
titlebars and edge snapping, built from a hyprbars fork. It is rebuilt
with every Hyprland change, since a plugin only loads into the exact
build it was compiled against.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-08 02:23:16 -04:00
Emir Beganovic 00a98d2aa7 T3 Code: accept the AppImage's compatibility libraries under their new names
The 2026-10-07 nightly ships the same six compatibility libraries with
their full versioned file names beside the short ones (libXss.so.1.0.0),
and two under their GTK 3 names (libappindicator3, libindicator3). The
guard in package() listed the short names only, so it stopped the build
on both architectures and with it the bundled upstream sync (#837).

Accept an optional 3 and trailing version numbers. Anything else in
usr/ still stops the build. t3code-bin carries the same guard and gets
the same pattern before the change reaches a stable release.
2026-10-08 03:06:40 +02:00
Emir Beganović d37fcd09d1 Merge pull request #849 from omacom/omasnap-git/smoke-output-dir
omasnap-git: give the smoke suite its output directory as --output-dir
2026-10-08 02:45:49 +02:00
Emir Beganovic c19b48c445 omasnap-git: give the smoke suite its output directory as --output-dir
Upstream's 6771b19 ("keep smoke artifacts in the build directory") made
omasnap-smoke reject positional paths; the directory is now passed with
--output-dir. check() still passed it positionally, so every build of a
pin at or past that commit failed with "Unexpected positional argument;
use --output-dir <directory>."

Pass it as --output-dir and move the pin to 6771b19, the tip the branch
tracker has been trying to merge since October 4. The two have to land
together: the old pin does not know --output-dir.
2026-10-08 00:49:37 +02:00
Krzysztof Wilczyński 0e90a7652b Merge pull request #846 from kwilczynski/feature/add-muqss-version
Add MuQSS kernel based on v7.2.8-5 with latest MuQSS from CK tree
2026-10-07 23:52:36 +02:00
Krzysztof Wilczyński 4b6cc8ba0f Add MuQSS kernel based on v7.2.8-5 with latest MuQSS from CK tree
Signed-off-by: Krzysztof Wilczyński <kwilczynski@omarchy.org>
2026-10-08 05:43:59 +09:00
David Heinemeier Hansson 2d56129a55 Keep OWE lock feed in its separate package (#844) 2026-10-07 21:34:00 +02:00
David Heinemeier Hansson fe4b80b0a6 Update OWE to 0.2.10 and package its lock feed (#843)
* Update OWE to 0.2.10 and package its lock feed

* Print OWE regression failures during package checks

* Make OWE package regression independent of timestamp precision
2026-10-07 21:11:46 +02:00
582 changed files with 149504 additions and 2946 deletions

No files matched your search

-34
View File
@@ -1,34 +0,0 @@
const approvePrWorkflows = require('./approve-pr-workflows.cjs');
const BOT = 'github-actions[bot]';
// A sync workflow pushes its branch with GITHUB_TOKEN. GitHub holds the
// resulting pull_request runs for approval and, unlike a person's push,
// creates no pull_request_target run, so approve-pr.yml never sees it. The
// sync workflow therefore releases the runs for the commit it just pushed,
// under the same rule approve-pr.yml applies: only while build-approved is
// on the PR. The sync applies that label itself, so its pushes build without
// a maintainer. It acts only on its own bot-authored, same-repository PR for
// the branch and commit it pushed.
module.exports = async function approveSyncPush({ github, context, core,
number, branch, headSha, since, approve = approvePrWorkflows, ...options }) {
if (!Number.isInteger(number) || !branch || !headSha || !since) {
throw new Error('Missing sync PR number, branch, head SHA or push time.');
}
const { data: pr } = await github.rest.pulls.get({ ...context.repo, pull_number: number });
const repository = `${context.repo.owner}/${context.repo.repo}`;
if (pr.user?.login !== BOT || pr.head.repo?.full_name !== repository ||
pr.base.repo?.full_name !== repository || pr.head.ref !== branch) {
throw new Error(`PR #${number} is not ${BOT}'s ${branch} PR in ${repository}; refusing to approve.`);
}
if (pr.state !== 'open' || pr.head.sha !== headSha) {
core.info(`PR #${number} is closed or has moved past ${headSha}; nothing to approve.`);
return;
}
if (!pr.labels.some(label => label.name === 'build-approved')) {
core.info(`PR #${number} has no build-approved label; its runs stay held.`);
return;
}
await approve({ github, context, core, vouchStatus: 'bot', pullRequest: pr,
action: 'synchronize', since, ...options });
};
+76
View File
@@ -0,0 +1,76 @@
// Whether a PR rides to master on its own once the required checks pass.
//
// The rule is the build gate's, from build-pr.yml: a PR trusted to build is
// trusted to ship. Collaborators, vouched authors and bots are trusted; an
// unknown author is trusted while the PR carries build-approved; a
// denouncement is absolute. Two limits on top of it:
//
// - Only package changes. A PR's workflows, scripts and build tooling never
// run in its own build (build-pr.yml overlays only its package directories
// onto base tooling), so green checks say nothing about them, and after
// merge they run with the publish secrets. Allowed: anything under
// pkgbuilds/, plus the test a package PR brings with it, which is a new
// file under tests/ and lines in test.yml that only run new tests/*.sh.
// test.yml runs on PRs only; an existing test may also run on master
// (builder-images.yml runs tests/build-isolation.sh with packages: write),
// so editing one still needs a maintainer.
const PACKAGES = 'pkgbuilds/';
const TESTS = 'tests/';
const TEST_WORKFLOW = '.github/workflows/test.yml';
const TEST_LINE = /^\+\s*\.\/tests\/[A-Za-z0-9._-]+\.sh\s*$/;
// Every changed line adds a ./tests/<name>.sh call; nothing removed. A diff
// too large for the API has no patch and does not qualify.
function onlyRunsTests(patch) {
if (!patch) return false;
const changed = patch.split('\n').filter(line =>
/^[+-]/.test(line) && !line.startsWith('+++') && !line.startsWith('---'));
return changed.length > 0 && changed.every(line => TEST_LINE.test(line));
}
function packageChange(file) {
if (file.previous_filename && !file.previous_filename.startsWith(PACKAGES)) return false;
if (file.filename.startsWith(PACKAGES)) return true;
if (file.filename.startsWith(TESTS)) return file.status === 'added';
if (file.filename === TEST_WORKFLOW) return file.status === 'modified' && onlyRunsTests(file.patch);
return false;
}
function decide({ pr, files, vouchStatus, repository }) {
if (pr.state !== 'open') return { enable: false, reason: 'PR is not open' };
if (pr.draft) return { enable: false, reason: 'PR is a draft' };
const labelled = pr.labels.some(label => label.name === 'build-approved');
let trusted;
switch (vouchStatus) {
case 'bot': case 'collaborator': case 'vouched': trusted = true; break;
case 'unknown': trusted = labelled; break;
default: trusted = false; // denounced, or a failed lookup
}
if (!trusted) {
return { enable: false, reason: `author not trusted to build (${vouchStatus || 'missing'}${labelled ? ', labelled' : ''})` };
}
if (!files.length) return { enable: false, reason: 'PR changes no files' };
const outside = files.filter(file => !packageChange(file));
if (outside.length) {
const names = outside.slice(0, 3).map(file => file.filename).join(', ');
return { enable: false, reason: `changes more than packages and their new tests: ${names}${outside.length > 3 ? ', ...' : ''}` };
}
return { enable: true, reason: `${vouchStatus === 'unknown' ? 'build-approved' : vouchStatus} author, package changes only` };
}
module.exports = async function autoMerge({ github, context, core, number, vouchStatus }) {
const { data: pr } = await github.rest.pulls.get({ ...context.repo, pull_number: number });
const files = await github.paginate(github.rest.pulls.listFiles, {
...context.repo, pull_number: number, per_page: 100,
});
const decision = decide({
pr, files, vouchStatus, repository: `${context.repo.owner}/${context.repo.repo}`,
});
core.info(`#${number}: ${decision.enable ? 'auto-merge' : 'leave for a maintainer'} (${decision.reason})`);
core.setOutput('enable', String(decision.enable));
core.setOutput('head_sha', pr.head.sha);
return decision;
};
module.exports.decide = decide;
+36
View File
@@ -0,0 +1,36 @@
#!/bin/bash
# Usage: package-pr-groups.sh BRANCH_PREFIX PATCH_OUT
#
# Run from the repository root after a sync has rewritten recipes in the
# working tree. Writes those changes to PATCH_OUT and prints, as one JSON
# line, the PRs they split into: [{"branch": ..., "packages": "a b"}, ...].
#
# One PR per package, so a package whose build fails holds back only itself.
# Packages pinned from the same upstream branch (omarchy-dev and
# omarchy-settings-dev) share a PR: sync-upstream moves them in lockstep, and
# merging one without the other would ship the pair from different commits.
# Branch names come from sync-pr-branch.sh, so a scheduled run and a dispatch
# naming the same packages update the same PR.
set -euo pipefail
prefix=${1:?branch prefix required}
patch=${2:?patch path required}
scripts=$(dirname "$0")
git diff --binary -- pkgbuilds > "$patch"
declare -A members=()
while IFS= read -r package; do
key=$(jq -r '(.upstream.watch? | objects | select(has("git_branch")) | "\(.git_branch)#\(.branch)") // empty' \
"pkgbuilds/$package/.omarchy/package.json" 2>/dev/null || true)
members["${key:-$package}"]+="$package "
done < <(git diff --name-only -- pkgbuilds | cut -d/ -f2 | sort -u)
groups='[]'
for key in "${!members[@]}"; do
read -r -a packages <<<"${members[$key]}"
branch=$("$scripts/sync-pr-branch.sh" "$prefix" "${packages[@]}" | sed -n 's/^branch=//p')
groups=$(jq -c --arg branch "$branch" --arg packages "${packages[*]}" \
'. + [{branch: $branch, packages: $packages}]' <<<"$groups")
done
jq -c 'sort_by(.branch)' <<<"$groups"
+108
View File
@@ -0,0 +1,108 @@
name: Auto-merge approved package PRs
# A package PR trusted to build is trusted to ship: once its builds are
# green it should merge and publish without a maintainer pressing the
# button. This enables GitHub's auto-merge on such PRs; branch protection
# still holds the merge until `result`, `self-tests` and `build-isolation`
# pass, and a red build stays an open PR. .github/scripts/auto-merge-pr.cjs
# has the rule.
#
# Auto-merge is enabled with the PAT in PKGS_BOT_TOKEN: a merge made with the
# built-in GITHUB_TOKEN does not start publish.yml.
#
# pull_request_target runs this default-branch code with secrets; the PR's
# code is never checked out here.
on:
pull_request_target:
types: [opened, reopened, synchronize, ready_for_review, labeled, unlabeled]
workflow_dispatch:
inputs:
pr:
description: 'PR number to evaluate'
required: true
permissions:
contents: read
pull-requests: read
concurrency:
group: auto-merge-pr-${{ github.event.pull_request.number || github.event.inputs.pr }}
cancel-in-progress: true
jobs:
auto-merge:
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- uses: actions/checkout@v4
with:
ref: ${{ github.event.repository.default_branch }}
persist-credentials: false
- name: Find the PR's author
id: pr
uses: actions/github-script@v7
env:
NUMBER: ${{ github.event.pull_request.number || github.event.inputs.pr }}
with:
script: |
const { data: pr } = await github.rest.pulls.get({
...context.repo, pull_number: Number(process.env.NUMBER),
});
core.setOutput('number', String(pr.number));
core.setOutput('author', pr.user.login);
- id: vouch
uses: mitchellh/vouch/action/check-user@f23dbb5e745334f97414ec70463ce7301071a661 # v1
with:
user: ${{ steps.pr.outputs.author }}
allow-fail: true
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Decide
id: decide
uses: actions/github-script@v7
env:
NUMBER: ${{ steps.pr.outputs.number }}
VOUCH_STATUS: ${{ steps.vouch.outputs.status }}
with:
script: |
const autoMerge = require('./.github/scripts/auto-merge-pr.cjs');
await autoMerge({ github, context, core,
number: Number(process.env.NUMBER), vouchStatus: process.env.VOUCH_STATUS });
- name: Enable auto-merge
if: steps.decide.outputs.enable == 'true'
env:
GH_TOKEN: ${{ secrets.PKGS_BOT_TOKEN }}
PR: ${{ steps.pr.outputs.number }}
HEAD_SHA: ${{ steps.decide.outputs.head_sha }}
run: |
if [[ -z "$GH_TOKEN" ]]; then
echo "::error::Set PKGS_BOT_TOKEN; a GITHUB_TOKEN merge would not publish."
exit 1
fi
# Idempotent: enabling twice errors. Bot lanes enable their own.
if [[ "$(gh pr view "$PR" -R "$GITHUB_REPOSITORY" --json autoMergeRequest --jq '.autoMergeRequest != null')" == true ]]; then
echo "auto-merge already enabled on #$PR"
exit 0
fi
# --match-head-commit: never arm a merge for a commit newer than
# the one just judged.
gh pr merge --auto --squash --match-head-commit "$HEAD_SHA" "$PR" -R "$GITHUB_REPOSITORY"
# Removing build-approved withdraws the approval, so withdraw the
# auto-merge it armed too. Only on that event: auto-merge a maintainer
# enabled by hand on any other PR is theirs to keep.
- name: Withdraw auto-merge
if: >-
steps.decide.outputs.enable == 'false' &&
github.event.action == 'unlabeled' && github.event.label.name == 'build-approved'
env:
GH_TOKEN: ${{ secrets.PKGS_BOT_TOKEN }}
PR: ${{ steps.pr.outputs.number }}
run: |
if [[ "$(gh pr view "$PR" -R "$GITHUB_REPOSITORY" --json autoMergeRequest --jq '.autoMergeRequest != null')" == true ]]; then
gh pr merge --disable-auto "$PR" -R "$GITHUB_REPOSITORY"
fi
+10 -8
View File
@@ -20,16 +20,18 @@ on:
description: "Space-separated package directories to build" description: "Space-separated package directories to build"
required: true required: true
# A new push normally cancels the PR's in-flight build. The sync bots' # A new push normally cancels the PR's in-flight build. The bots' branches
# branches (auto/sync-*) are the exception: they are force-pushed with fresh # (auto/sync-* and auto/track-branches-*) are the exception: they are
# upstream releases several times a day, which kept cancelling multi-hour # force-pushed with fresh upstream releases several times a day, which kept
# aarch64 builds before they could finish. There the newest run waits # cancelling multi-hour aarch64 builds before they could finish. There the
# instead (GitHub keeps at most one pending run per group, replacing older # newest run waits instead (GitHub keeps at most one pending run per group,
# pending ones), and when it starts it reuses every artifact the finished # replacing older pending ones), and when it starts it reuses every artifact
# build uploaded, so only packages whose tree changed are built again. # the finished build uploaded, so only packages whose tree changed are built
# again. The tracker labels the PR it opens, so a new tracker PR starts two
# runs at once; cancelling the first left a red `result` on the PR (#901).
concurrency: concurrency:
group: build-${{ github.event.pull_request.number || github.run_id }} group: build-${{ github.event.pull_request.number || github.run_id }}
cancel-in-progress: ${{ !(github.event.pull_request.head.repo.full_name == github.repository && startsWith(github.head_ref, 'auto/sync-')) }} cancel-in-progress: ${{ !(github.event.pull_request.head.repo.full_name == github.repository && (startsWith(github.head_ref, 'auto/sync-') || startsWith(github.head_ref, 'auto/track-branches'))) }}
jobs: jobs:
# Builds cost real machines, so they run only for trusted authors: # Builds cost real machines, so they run only for trusted authors:
+134
View File
@@ -0,0 +1,134 @@
name: Open package PRs
# Shared by the three bot lanes (sync-upstream.yml, sync-rebuilds.yml and
# track-branches.yml). The caller's sync job rewrites recipes, groups the
# changes with .github/scripts/package-pr-groups.sh and uploads them as the
# `package-pins` artifact. This opens or updates one PR per group and arms
# auto-merge, so every green package lands on its own and only the failures
# stay open. A single batch PR let one red package hold back every other one
# (#788, #837).
#
# Everything goes through the PAT in PKGS_BOT_TOKEN: its pushes build without
# an approval hold, and a merge made with it starts publish.yml. A merge made
# with the built-in GITHUB_TOKEN would not publish.
on:
workflow_call:
inputs:
groups:
description: 'JSON from package-pr-groups.sh: [{"branch", "packages"}]'
required: true
type: string
prefix:
description: 'Branch prefix the caller owns, e.g. auto/sync-upstream'
required: true
type: string
title:
description: 'PR title; ": <package> <version>, ..." is appended'
required: true
type: string
body:
description: 'PR body'
required: true
type: string
secrets:
PKGS_BOT_TOKEN:
required: true
permissions:
contents: read
jobs:
pr:
if: ${{ inputs.groups != '[]' }}
runs-on: ubuntu-latest
strategy:
fail-fast: false
max-parallel: 4
matrix:
group: ${{ fromJSON(inputs.groups) }}
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
persist-credentials: false
- name: Fetch the pins
uses: actions/download-artifact@v4
with:
name: package-pins
path: ${{ runner.temp }}
# The PR title names what moved, so the merged history reads like a
# changelog: "Sync upstream releases: zed 1.23.2-1".
- name: Apply this PR's packages
id: apply
env:
PACKAGES: ${{ matrix.group.packages }}
TITLE: ${{ inputs.title }}
run: |
read -r -a packages <<<"$PACKAGES"
git apply $(printf -- '--include=pkgbuilds/%s/* ' "${packages[@]}") "$RUNNER_TEMP/pins.patch"
git status --porcelain
field() { sed -n "s/^$2=//p" "pkgbuilds/$1/PKGBUILD" | head -1 | tr -d "\"'"; }
versions=$(for package in "${packages[@]}"; do
printf '%s %s-%s, ' "$package" "$(field "$package" pkgver)" "$(field "$package" pkgrel)"
done)
title="$TITLE: ${versions%, }"
echo "title=$title" >> "$GITHUB_OUTPUT"
echo "- $title" >> "$GITHUB_STEP_SUMMARY"
- name: Open or update the PR
id: pr
uses: peter-evans/create-pull-request@5f6978faf089d4d20b00c7766989d076bb2fc7f1 # v8.1.1
with:
token: ${{ secrets.PKGS_BOT_TOKEN }}
commit-message: ${{ steps.apply.outputs.title }}
title: ${{ steps.apply.outputs.title }}
body: ${{ inputs.body }}
branch: ${{ matrix.group.branch }}
delete-branch: true
labels: automated
# Auto-merge, not a direct merge: branch protection still has to see
# `result`, `self-tests` and `build-isolation` green. auto-merge-pr.yml
# may arm the same PR at the same moment; either one arming it is the
# outcome wanted.
- name: Enable auto-merge
if: ${{ steps.pr.outputs.pull-request-number != '' }}
env:
GH_TOKEN: ${{ secrets.PKGS_BOT_TOKEN }}
PR: ${{ steps.pr.outputs.pull-request-number }}
run: |
armed() { [[ "$(gh pr view "$PR" -R "$GITHUB_REPOSITORY" --json autoMergeRequest --jq '.autoMergeRequest != null')" == true ]]; }
if armed; then
echo "auto-merge already enabled on #$PR"
exit 0
fi
gh pr merge --auto --merge "$PR" -R "$GITHUB_REPOSITORY" || armed
# A PR on this lane's prefix that the run did not refresh, and that
# conflicts with master, is stale: master moved its packages on without it.
# Close it rather than leave a red PR behind. Not being refreshed is not
# enough on its own, since a feed that failed this run (one of dozens timing
# out is routine) leaves a still-valid PR unrefreshed too. The lane's old
# batch PR, on the bare prefix, is retired outright.
close-superseded:
runs-on: ubuntu-latest
steps:
- name: Close superseded PRs
env:
GH_TOKEN: ${{ secrets.PKGS_BOT_TOKEN }}
GROUPS_JSON: ${{ inputs.groups }}
PREFIX: ${{ inputs.prefix }}
run: |
gh pr list -R "$GITHUB_REPOSITORY" --state open --limit 500 --json number,headRefName,mergeable \
--jq '.[] | select(.headRefName == env.PREFIX or (.headRefName | startswith(env.PREFIX + "-"))) | "\(.number) \(.headRefName) \(.mergeable)"' |
while read -r number branch mergeable; do
jq -e --arg branch "$branch" 'any(.[]; .branch == $branch)' <<<"$GROUPS_JSON" >/dev/null && continue
[[ $branch == "$PREFIX" || $mergeable == CONFLICTING ]] || continue
echo "closing #$number ($branch): superseded"
gh pr close "$number" -R "$GITHUB_REPOSITORY" --delete-branch \
--comment "Superseded: master has moved these packages on, or per-package PRs carry them now."
done
+74 -47
View File
@@ -24,11 +24,10 @@ on:
description: "Space-separated package directories to publish from master" description: "Space-separated package directories to publish from master"
required: true required: true
# Merges serialize. Two publishes into one channel at once would race on # Only the publish job serializes (see its concurrency group): two publishes
# the database; queued is fine, cancelled is not. # into one channel at once would race on the database. Builds run outside the
concurrency: # lock, so a merge waits behind another merge's signing and upload, seconds,
group: publish # never behind its kernel build.
cancel-in-progress: false
jobs: jobs:
changes: changes:
@@ -58,11 +57,10 @@ jobs:
echo "matrix=$matrix" >> "$GITHUB_OUTPUT" echo "matrix=$matrix" >> "$GITHUB_OUTPUT"
echo "count=$(jq '.include | length' <<<"$matrix")" >> "$GITHUB_OUTPUT" echo "count=$(jq '.include | length' <<<"$matrix")" >> "$GITHUB_OUTPUT"
jq -r '.include[] | "\(.package) \(.arch) -> \(.channels)"' <<<"$matrix" jq -r '.include[] | "\(.package) \(.arch) -> \(.channels)"' <<<"$matrix"
# Reuse or rebuild, decided per entry and said out loud. An aarch64 # Reuse or rebuild, decided per entry and said out loud. A tree with
# tree with no build artifact (PR artifacts last 7 days; a dispatch # no build artifact (PR artifacts last 7 days; a dispatch may name
# may name any package) goes to the rebuild job, which builds it # any package) goes to the rebuild job: aarch64 natively on GitHub's
# natively on GitHub's arm64 runner. x86_64 builds inside the # arm64 runner, x86_64 on a builder droplet, one runner per entry.
# publish job on the droplet, as before.
rebuild=() rebuild=()
echo "| package | arch | tree | decision |" >> "$GITHUB_STEP_SUMMARY" echo "| package | arch | tree | decision |" >> "$GITHUB_STEP_SUMMARY"
echo "| --- | --- | --- | --- |" >> "$GITHUB_STEP_SUMMARY" echo "| --- | --- | --- | --- |" >> "$GITHUB_STEP_SUMMARY"
@@ -73,13 +71,23 @@ jobs:
found=$(curl -fsS -H "Authorization: Bearer $GH_TOKEN" -H "Accept: application/vnd.github+json" \ found=$(curl -fsS -H "Authorization: Bearer $GH_TOKEN" -H "Accept: application/vnd.github+json" \
"https://api.github.com/repos/${{ github.repository }}/actions/artifacts?name=$label&per_page=5" \ "https://api.github.com/repos/${{ github.repository }}/actions/artifacts?name=$label&per_page=5" \
| jq -r '[.artifacts[] | select(.expired|not)] | sort_by(.created_at) | last | select(. != null) | "run \(.workflow_run.id), expires \(.expires_at)"') | jq -r '[.artifacts[] | select(.expired|not)] | sort_by(.created_at) | last | select(. != null) | "run \(.workflow_run.id), expires \(.expires_at)"')
# The plan the publish job makes, made here before a runner is
# asked for: a tree the channel already holds at master's version
# (a re-run, or a dispatch naming a package that is fine) needs
# no build, and an x86 rebuild would wait minutes for a droplet
# to find that out in seconds.
plan=""
[[ -n "$found" ]] || plan=$(bin/build --dry-run --mirror edge --arch "$arch" --package "$package" 2>&1 | grep -E '^==> Plan complete' || true)
if [[ -n "$found" ]]; then if [[ -n "$found" ]]; then
decision="reuse the build artifact ($found)" decision="reuse the build artifact ($found)"
elif [[ -n "$plan" && -z "$(sed -E 's/.*would build: *//' <<<"$plan" | tr -d '[:space:]')" ]]; then
decision="already published at master's version, nothing to build"
elif [[ $arch == aarch64 ]]; then elif [[ $arch == aarch64 ]]; then
decision="no build artifact: rebuild natively on ubuntu-24.04-arm" decision="no build artifact: rebuild natively on ubuntu-24.04-arm"
rebuild+=("$entry") rebuild+=("$(jq -c '. + {runner: "[\"ubuntu-24.04-arm\"]"}' <<<"$entry")")
else else
decision="no build artifact: build in the publish job on the self-hosted builder" decision="no build artifact: rebuild on a builder droplet"
rebuild+=("$(jq -c '. + {runner: "[\"self-hosted\",\"omarchy-builder\"]"}' <<<"$entry")")
fi fi
echo "==> $label: $decision" echo "==> $label: $decision"
echo "| $package | $arch | ${hash:0:12} | $decision |" >> "$GITHUB_STEP_SUMMARY" echo "| $package | $arch | ${hash:0:12} | $decision |" >> "$GITHUB_STEP_SUMMARY"
@@ -87,16 +95,19 @@ jobs:
echo "rebuild=$(printf '%s\n' "${rebuild[@]}" | jq -sc '{include: .}')" >> "$GITHUB_OUTPUT" echo "rebuild=$(printf '%s\n' "${rebuild[@]}" | jq -sc '{include: .}')" >> "$GITHUB_OUTPUT"
echo "rebuild_count=${#rebuild[@]}" >> "$GITHUB_OUTPUT" echo "rebuild_count=${#rebuild[@]}" >> "$GITHUB_OUTPUT"
# The aarch64 half of "build it now when there is none". It builds exactly # "Build it now when there is none". Each entry builds exactly as
# as build-pr.yml's aarch64 path does (same runner, same builder image, # build-pr.yml builds it (same runner kind, same builder image, same
# same bin/build call) and uploads under the same label, so the publish # bin/build call) and uploads under the same label, so the publish job
# job collects this run's artifact the way it collects a PR's. No secret # collects this run's artifact the way it collects a PR's. No secret
# reaches this runner; signing and upload stay on the self-hosted builder. # reaches these runners, and they hold no lock: entries build in parallel,
# and other merges publish while they do.
rebuild: rebuild:
needs: changes needs: changes
if: needs.changes.outputs.rebuild_count != '0' if: needs.changes.outputs.rebuild_count != '0'
runs-on: ubuntu-24.04-arm runs-on: ${{ fromJSON(matrix.runner) }}
timeout-minutes: 180 # The droplets are x86, so aarch64 never builds there. omarchy-mac-boot
# under QEMU took 2h47m; native arm64 and x86 kernels fit easily.
timeout-minutes: 240
permissions: permissions:
contents: read contents: read
strategy: strategy:
@@ -109,7 +120,7 @@ jobs:
# The same check the publish job makes before building: a re-run for a # The same check the publish job makes before building: a re-run for a
# package the channel already holds at master's version builds # package the channel already holds at master's version builds
# nothing, and uploads nothing that could shadow the published file. # nothing, and uploads nothing that could shadow the published file.
- name: Build ${{ matrix.package }} (${{ matrix.arch }}, native) - name: Build ${{ matrix.package }} (${{ matrix.arch }})
id: build id: build
env: env:
CONTAINER_ENGINE: docker CONTAINER_ENGINE: docker
@@ -140,23 +151,30 @@ jobs:
if-no-files-found: error if-no-files-found: error
retention-days: 7 retention-days: 7
# One job for the whole merge. It collects every PR artifact for the # One job for the whole merge. It collects every artifact for the merged
# merged tree (building only what has none; aarch64 comes from the # tree (PR builds, or the rebuild job above), then walks each channel and
# rebuild job above), then walks each channel and
# architecture slot exactly once: pull that database, add every package # architecture slot exactly once: pull that database, add every package
# that belongs in it, upload. Six slots, six round trips, however many # that belongs in it, upload. Six slots, six round trips, however many
# packages the merge carried. One process is the only writer, so there # packages the merge carried. One process is the only writer, so there
# is no race between packages; the run-level concurrency group above # is no race between packages; the concurrency group keeps one merge's
# keeps one merge from overlapping the next. # publish from overlapping the next. It builds nothing, so it runs on a
# hosted runner in about a minute instead of waiting for a droplet.
# It waits for the rebuild job and runs whatever that job's result: a # It waits for the rebuild job and runs whatever that job's result: a
# failed rebuild leaves its package without an artifact, and the collect # failed rebuild leaves its package without an artifact, and the collect
# step below records that and stops before any publish. # step below records that and stops before any publish.
publish: publish:
needs: [changes, rebuild] needs: [changes, rebuild]
if: ${{ !cancelled() && needs.changes.result == 'success' && needs.changes.outputs.count != '0' }} if: ${{ !cancelled() && needs.changes.result == 'success' && needs.changes.outputs.count != '0' }}
runs-on: [self-hosted, omarchy-builder] runs-on: ubuntu-latest
environment: publish environment: publish
timeout-minutes: 240 timeout-minutes: 30
# queue: max, because by default a group holds one waiting job: a third
# merge arriving cancels the second one's publish, and nothing reruns it.
# With it up to 100 wait, in the order they reach this job.
concurrency:
group: publish
cancel-in-progress: false
queue: max
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
with: with:
@@ -172,8 +190,8 @@ jobs:
EOF_MATRIX EOF_MATRIX
cat plan.txt cat plan.txt
# Fetch each package's PR artifact into build-output/edge/<arch>/, or # Fetch each package's artifact into build-output/edge/<arch>/: the PR's,
# build it when no artifact exists for exactly this tree. An artifact # or the rebuild job's when the PR's had expired. An artifact
# carries its package files inside packages.tar (see build-pr.yml and # carries its package files inside packages.tar (see build-pr.yml and
# helpers/artifact-helpers.sh: the upload action rejects the colon in # helpers/artifact-helpers.sh: the upload action rejects the colon in
# an epoch filename). # an epoch filename).
@@ -199,8 +217,8 @@ jobs:
mkdir -p "build-output/edge/$arch" mkdir -p "build-output/edge/$arch"
if [[ -n "$found" ]]; then if [[ -n "$found" ]]; then
if [[ $from_run == "${{ github.run_id }}" ]]; then if [[ $from_run == "${{ github.run_id }}" ]]; then
kind=native-rebuild kind=rebuild
echo "==> $label: artifact from this run's native $arch rebuild" echo "==> $label: artifact from this run's $arch rebuild"
else else
kind=pr-artifact kind=pr-artifact
echo "==> $label: reusing the build artifact from run $from_run" echo "==> $label: reusing the build artifact from run $from_run"
@@ -225,21 +243,12 @@ jobs:
jq -nc --arg p "$package" --arg a "$arch" '{package:$p, arch:$a, source:"already-published"}' >> sources.jsonl jq -nc --arg p "$package" --arg a "$arch" '{package:$p, arch:$a, source:"already-published"}' >> sources.jsonl
continue continue
fi fi
# aarch64 never builds here: this droplet is x86 and would # Nothing builds here. No artifact means the rebuild failed (see
# emulate it. No artifact means the native rebuild failed (see # the rebuild job), or an artifact expired between planning and
# the rebuild job), or an artifact expired between planning # now (re-run all jobs).
# and now (re-run all jobs). echo "::error::$label: no artifact from the rebuild job"
if [[ $arch == aarch64 ]]; then
echo "::error::$label: no artifact from the native rebuild; not building aarch64 under emulation"
jq -nc --arg p "$package" --arg a "$arch" '{package:$p, arch:$a, source:"native-build-failed"}' >> sources.jsonl; failed=1; break
fi
echo "==> $label: no artifact for this tree, building"
if OMARCHY_KEEP_BUILD_WORKSPACE=1 bin/build --mirror edge --arch "$arch" --package "$package"; then
jq -nc --arg p "$package" --arg a "$arch" '{package:$p, arch:$a, source:"built"}' >> sources.jsonl
else
jq -nc --arg p "$package" --arg a "$arch" '{package:$p, arch:$a, source:"build-failed"}' >> sources.jsonl; failed=1; break jq -nc --arg p "$package" --arg a "$arch" '{package:$p, arch:$a, source:"build-failed"}' >> sources.jsonl; failed=1; break
fi fi
fi
done < plan.txt done < plan.txt
ls -1 build-output/edge/*/*.pkg.tar.zst 2>/dev/null || true ls -1 build-output/edge/*/*.pkg.tar.zst 2>/dev/null || true
if (( failed )); then if (( failed )); then
@@ -280,8 +289,26 @@ jobs:
cat publish-record.json cat publish-record.json
exit 0 exit 0
fi fi
docker image inspect omarchy-pkg-builder:latest-x86_64-edge >/dev/null 2>&1 \ # A fresh runner has no images, and building this one here cost
|| docker buildx build --load -t omarchy-pkg-builder:latest-x86_64-edge --build-arg MIRROR=edge build # every publish about 100 s (and 20 s more to start a container
# from it) for the 14 s of signing and upload it is needed for.
# builder-images.yml already publishes the tested image for exactly
# these build inputs under their key; pull that. Build only when no
# image carries the key: a merge that changed build/ publishes
# before the refresh it triggered has finished.
builder=omarchy-pkg-builder:latest-x86_64-edge
if ! docker image inspect "$builder" >/dev/null 2>&1; then
key=$(bin/builder-image key --arch x86_64 --mirror edge)
published="ghcr.io/omacom/omarchy-pkg-builder:$key"
if docker pull --quiet "$published" &&
[[ $(docker image inspect "$published" --format '{{index .Config.Labels "org.omarchy.builder.key"}}') == "$key" ]]; then
docker tag "$published" "$builder"
echo "==> Builder image: pulled $published"
else
echo "==> Builder image: none published for $key, building it"
docker buildx build --load -t "$builder" --build-arg MIRROR=edge build
fi
fi
# Group the merge's files by the (channel, architecture) slot each # Group the merge's files by the (channel, architecture) slot each
# belongs to. A package's files live under build-output/edge/<built # belongs to. A package's files live under build-output/edge/<built
@@ -374,7 +401,7 @@ jobs:
run: | run: |
jq -r --arg outcome "${{ needs.publish.result }}" ' jq -r --arg outcome "${{ needs.publish.result }}" '
def pkgs: [.packages[] | select(test("-debug-")|not)] | join(", "); def pkgs: [.packages[] | select(test("-debug-")|not)] | join(", ");
def src: if .source=="pr-artifact" then "PR artifact" elif .source=="native-rebuild" then "rebuilt natively on arm64" elif .source=="built" then "built here" elif .source=="already-published" then "already published, skipped" else "**" + .source + "**" end; def src: if .source=="pr-artifact" then "PR artifact" elif .source=="rebuild" or .source=="native-rebuild" then "rebuilt at merge" elif .source=="built" then "built here" elif .source=="already-published" then "already published, skipped" else "**" + .source + "**" end;
"### Publish " + (if $outcome=="success" then "succeeded" else "FAILED" end) + "### Publish " + (if $outcome=="success" then "succeeded" else "FAILED" end) +
" → **" + .target + "**" + (if .target!="live" then " (proof prefix, not live)" else "" end), " → **" + .target + "**" + (if .target!="live" then " (proof prefix, not live)" else "" end),
"", "",
+64 -83
View File
@@ -1,5 +1,16 @@
name: Sync Rebuild Triggers name: Sync Rebuild Triggers
# Rebuilds ride the unattended lane, like track-branches.yml: each package's
# pkgrel bump gets its own PR on auto/sync-rebuilds-<package>, opened by
# package-prs.yml. It builds on the droplets, auto-merge lands it once
# `result`, `self-tests` and `build-isolation` are green, and the merge
# publishes. A rebuild that fails stays an unmerged red PR for a maintainer
# without holding back any other package.
#
# The PR and auto-merge use the PAT in PKGS_BOT_TOKEN, because a merge made
# with the built-in GITHUB_TOKEN does not start publish.yml, and its pushes
# are held for approval instead of building.
on: on:
schedule: schedule:
# Every 6 hours, off the hour to dodge the scheduling backlog at :00 # Every 6 hours, off the hour to dodge the scheduling backlog at :00
@@ -11,42 +22,42 @@ on:
required: false required: false
default: '' default: ''
# One sync at a time: two runs racing on one auto/sync-rebuilds-* branch
# would each force-push their own bump over the other's.
concurrency:
group: sync-rebuilds
cancel-in-progress: false
jobs: jobs:
sync: sync:
runs-on: ubuntu-latest runs-on: ubuntu-latest
permissions: permissions:
contents: write contents: read
pull-requests: write
outputs: outputs:
branch: ${{ steps.branch.outputs.branch }} groups: ${{ steps.groups.outputs.groups }}
pushed_at: ${{ steps.pushed.outputs.at }}
number: ${{ steps.cpr.outputs.pull-request-number }}
operation: ${{ steps.cpr.outputs.pull-request-operation }}
head_sha: ${{ steps.cpr.outputs.pull-request-head-sha }}
steps: steps:
- name: Require the bot token
env:
PKGS_BOT_TOKEN: ${{ secrets.PKGS_BOT_TOKEN }}
run: |
if [[ -z "$PKGS_BOT_TOKEN" ]]; then
echo "::error::Set PKGS_BOT_TOKEN to a PAT with Contents: write and Pull requests: write on this repository, owned by an account trusted to trigger builds."
exit 1
fi
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v4 uses: actions/checkout@v4
with: with:
persist-credentials: false persist-credentials: false
# A scoped dispatch regenerates only the named packages. Pushed to the
# shared branch, that would replace every other pending update in its
# PR, so it gets a branch and PR of its own.
- name: Choose the PR branch
id: branch
env:
PACKAGES: ${{ github.event.inputs.packages }}
run: |
read -r -a package_args <<< "${PACKAGES:-}"
.github/scripts/sync-pr-branch.sh auto/sync-rebuilds "${package_args[@]}" | tee -a "$GITHUB_OUTPUT"
# Runs in an Arch container against the mirror the x86_64 builder itself # Runs in an Arch container against the mirror the x86_64 builder itself
# uses, because the question being asked is what that builder will link # uses, because the question being asked is what that builder will link
# against and a different mirror can be hours ahead of it. Recording a # against and a different mirror can be hours ahead of it. Recording a
# version the build never saw is the one failure this command must not # version the build never saw is the one failure this command must not
# have: nothing re-fires once the record matches. # have: nothing re-fires once the record matches.
- name: Bump pkgrel for packages whose dependencies moved - name: Bump pkgrel for packages whose dependencies moved
id: sync
run: | run: |
docker run --rm \ docker run --rm \
-e PACKAGES="$PACKAGES" \ -e PACKAGES="$PACKAGES" \
@@ -56,7 +67,7 @@ jobs:
-v "$PWD/helpers:/workspace/helpers:ro" \ -v "$PWD/helpers:/workspace/helpers:ro" \
-v "$PWD/pkgbuilds:/workspace/pkgbuilds" \ -v "$PWD/pkgbuilds:/workspace/pkgbuilds" \
-w /workspace \ -w /workspace \
archlinux:base-devel bash -lc ' ghcr.io/archlinux/archlinux:base-devel bash -lc '
set -euo pipefail set -euo pipefail
printf "Server = https://mirror.omarchy.org/\$repo/os/\$arch\n" > /etc/pacman.d/mirrorlist printf "Server = https://mirror.omarchy.org/\$repo/os/\$arch\n" > /etc/pacman.d/mirrorlist
@@ -76,32 +87,33 @@ jobs:
env: env:
PACKAGES: ${{ github.event.inputs.packages }} PACKAGES: ${{ github.event.inputs.packages }}
- name: Check for changes - name: Group the bumps into PRs
id: changes if: ${{ !cancelled() && steps.sync.outcome != 'skipped' }}
id: groups
run: | run: |
if [ -z "$(git status --porcelain)" ]; then groups=$(.github/scripts/package-pr-groups.sh auto/sync-rebuilds "$RUNNER_TEMP/pins.patch")
echo "has_changes=false" >> "$GITHUB_OUTPUT" echo "groups=$groups" >> "$GITHUB_OUTPUT"
else
echo "has_changes=true" >> "$GITHUB_OUTPUT"
fi
# Runs created by this push are newer than this; the approve job - name: Hand the bumps to the PR jobs
# waits for them. A minute's slack absorbs runner clock skew. if: ${{ !cancelled() && steps.groups.outputs.groups != '' && steps.groups.outputs.groups != '[]' }}
- name: Record push time uses: actions/upload-artifact@v4
if: steps.changes.outputs.has_changes == 'true'
id: pushed
run: echo "at=$(date -u -d '-1 minute' +%Y-%m-%dT%H:%M:%SZ)" >> "$GITHUB_OUTPUT"
- name: Create Pull Request
if: steps.changes.outputs.has_changes == 'true'
id: cpr
uses: peter-evans/create-pull-request@v7
with: with:
token: ${{ secrets.GITHUB_TOKEN }} name: package-pins
commit-message: 'chore: rebuild against updated dependencies' path: ${{ runner.temp }}/pins.patch
title: "chore: rebuild against updated dependencies${{ steps.branch.outputs.scope && format(' ({0})', steps.branch.outputs.scope) || '' }}" retention-days: 1
prs:
needs: sync
if: ${{ !cancelled() && needs.sync.outputs.groups != '' }}
permissions:
contents: read
uses: ./.github/workflows/package-prs.yml
with:
groups: ${{ needs.sync.outputs.groups }}
prefix: auto/sync-rebuilds
title: Rebuild against updated dependencies
body: | body: |
Automated pkgrel bump for packages that link against a dependency Automated pkgrel bump for a package that links against a dependency
which has moved in the official repositories. which has moved in the official repositories.
Each package names those dependencies in `rebuild_on` and carries the Each package names those dependencies in `rebuild_on` and carries the
@@ -109,17 +121,19 @@ jobs:
bump is what makes the rebuilt package an upgrade pacman will offer; bump is what makes the rebuilt package an upgrade pacman will offer;
without it the build produces the version already published and no without it the build produces the version already published and no
one receives it. one receives it.
branch: ${{ steps.branch.outputs.branch }}
delete-branch: true
# The bot is trusted; build-approved lets the approve job below
# release GitHub's hold on its pushes without a maintainer.
labels: |
automated
build-approved
reviewers: ryanrhughes
This PR auto-merges once the build checks pass. A failing rebuild
leaves it open for a maintainer without holding back any other package.
secrets:
PKGS_BOT_TOKEN: ${{ secrets.PKGS_BOT_TOKEN }}
notify:
needs: [sync, prs]
if: ${{ failure() }}
runs-on: ubuntu-latest
steps:
- name: Notify Basecamp on failure - name: Notify Basecamp on failure
if: failure() && env.BASECAMP_CHATBOT_URL != '' if: ${{ env.BASECAMP_CHATBOT_URL != '' }}
env: env:
BASECAMP_CHATBOT_URL: ${{ secrets.BASECAMP_CHATBOT_URL }} BASECAMP_CHATBOT_URL: ${{ secrets.BASECAMP_CHATBOT_URL }}
run: | run: |
@@ -129,36 +143,3 @@ jobs:
"🔴 <strong>Rebuild trigger sync failed</strong><br><a href=\"${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}\">View run</a>" \ "🔴 <strong>Rebuild trigger sync failed</strong><br><a href=\"${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}\">View run</a>" \
'{content: $content}')" \ '{content: $content}')" \
"$BASECAMP_CHATBOT_URL" "$BASECAMP_CHATBOT_URL"
# GitHub holds pull_request runs from a GITHUB_TOKEN push for approval and
# creates no pull_request_target run for it, so approve-pr.yml never sees
# the sync's own pushes. The sync labels its PR build-approved, so release
# the held runs for the commit just pushed, whether it opened the PR or
# updated it. A separate job, so the sync container's token never holds
# actions: write.
approve:
needs: sync
if: ${{ !cancelled() && (needs.sync.outputs.operation == 'created' || needs.sync.outputs.operation == 'updated') }}
runs-on: ubuntu-latest
timeout-minutes: 5
permissions:
contents: read
pull-requests: read
actions: write
steps:
- uses: actions/checkout@v4
with:
persist-credentials: false
- name: Release held build and test runs
uses: actions/github-script@v7
env:
NUMBER: ${{ needs.sync.outputs.number }}
BRANCH: ${{ needs.sync.outputs.branch }}
HEAD_SHA: ${{ needs.sync.outputs.head_sha }}
SINCE: ${{ needs.sync.outputs.pushed_at }}
with:
script: |
const approve = require('./.github/scripts/approve-sync-push.cjs');
const { NUMBER, BRANCH, HEAD_SHA, SINCE } = process.env;
await approve({ github, context, core, number: Number(NUMBER),
branch: BRANCH, headSha: HEAD_SHA, since: SINCE });
+66 -88
View File
@@ -1,5 +1,17 @@
name: Sync Upstream Releases name: Sync Upstream Releases
# Bumps every package whose upstream release feed has moved, except the
# "auto_merge": true packages that track-branches.yml checks hourly. Each
# package gets its own PR on auto/sync-upstream-<package>, opened by
# package-prs.yml: the PR builds on the droplets, auto-merge lands it when
# `result` is green, and the merge publishes. A package that fails to build
# stays an open red PR for a maintainer without holding back any other; the
# next run updates it if upstream moves again. `min_release_age` still
# quarantines fresh releases before they are proposed.
#
# The PRs and auto-merge use the PAT in PKGS_BOT_TOKEN, so their pushes
# build without an approval hold and their merges start publish.yml.
on: on:
schedule: schedule:
# Every 6 hours, off the hour to dodge the scheduling backlog at :00 # Every 6 hours, off the hour to dodge the scheduling backlog at :00
@@ -11,36 +23,35 @@ on:
required: false required: false
default: '' default: ''
# One sync at a time: two runs racing on one auto/sync-upstream-* branch
# would each force-push their own update over the other's.
concurrency:
group: sync-upstream
cancel-in-progress: false
jobs: jobs:
sync: sync:
runs-on: ubuntu-latest runs-on: ubuntu-latest
permissions: permissions:
contents: write contents: read
pull-requests: write
outputs: outputs:
branch: ${{ steps.branch.outputs.branch }} groups: ${{ steps.groups.outputs.groups }}
pushed_at: ${{ steps.pushed.outputs.at }}
number: ${{ steps.cpr.outputs.pull-request-number }}
operation: ${{ steps.cpr.outputs.pull-request-operation }}
head_sha: ${{ steps.cpr.outputs.pull-request-head-sha }}
steps: steps:
- name: Require the bot token
env:
PKGS_BOT_TOKEN: ${{ secrets.PKGS_BOT_TOKEN }}
run: |
if [[ -z "$PKGS_BOT_TOKEN" ]]; then
echo "::error::Set PKGS_BOT_TOKEN to a PAT with Contents: write and Pull requests: write on this repository, owned by an account trusted to trigger builds."
exit 1
fi
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v4 uses: actions/checkout@v4
with: with:
persist-credentials: false persist-credentials: false
# A scoped dispatch regenerates only the named packages. Pushed to the
# shared branch, that would replace every other pending update in its
# PR, so it gets a branch and PR of its own.
- name: Choose the PR branch
id: branch
env:
PACKAGES: ${{ github.event.inputs.packages }}
run: |
read -r -a package_args <<< "${PACKAGES:-}"
.github/scripts/sync-pr-branch.sh auto/sync-upstream "${package_args[@]}" | tee -a "$GITHUB_OUTPUT"
# Runs in an Arch container for vercmp: whether a release is an upgrade has # 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. # to be decided by the same comparator pacman will use on users' machines.
- name: Update packages from upstream release feeds - name: Update packages from upstream release feeds
@@ -55,7 +66,7 @@ jobs:
-v "$PWD/helpers:/workspace/helpers:ro" \ -v "$PWD/helpers:/workspace/helpers:ro" \
-v "$PWD/pkgbuilds:/workspace/pkgbuilds" \ -v "$PWD/pkgbuilds:/workspace/pkgbuilds" \
-w /workspace \ -w /workspace \
archlinux:base-devel bash -lc ' ghcr.io/archlinux/archlinux:base-devel bash -lc '
set -euo pipefail set -euo pipefail
pacman -Syu --noconfirm git jq python libarchive pacman -Syu --noconfirm git jq python libarchive
@@ -64,8 +75,8 @@ jobs:
useradd -m -u "$HOST_UID" -g "$HOST_GID" runner useradd -m -u "$HOST_UID" -g "$HOST_GID" runner
chown -R runner:runner /workspace/pkgbuilds chown -R runner:runner /workspace/pkgbuilds
# The reviewed lane only: packages marked auto_merge ride # Packages marked auto_merge ride track-branches.yml instead,
# track-branches.yml, which merges without a human. # which checks them hourly.
if [[ -n "${PACKAGES:-}" ]]; then if [[ -n "${PACKAGES:-}" ]]; then
read -r -a package_args <<< "$PACKAGES" read -r -a package_args <<< "$PACKAGES"
runuser -u runner -- ./bin/sync-upstream --lane reviewed "${package_args[@]}" runuser -u runner -- ./bin/sync-upstream --lane reviewed "${package_args[@]}"
@@ -78,50 +89,50 @@ jobs:
UPSTREAM_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} UPSTREAM_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# Failed feeds leave their recipes untouched; completed updates still # Failed feeds leave their recipes untouched; completed updates still
# reach review. The failed sync step keeps the workflow red and notifies. # get their PRs. The failed sync step keeps the workflow red and notifies.
- name: Check for changes - name: Group the updates into PRs
if: ${{ !cancelled() && steps.sync.outcome != 'skipped' }} if: ${{ !cancelled() && steps.sync.outcome != 'skipped' }}
id: changes id: groups
run: | run: |
if [ -z "$(git status --porcelain)" ]; then groups=$(.github/scripts/package-pr-groups.sh auto/sync-upstream "$RUNNER_TEMP/pins.patch")
echo "has_changes=false" >> "$GITHUB_OUTPUT" echo "groups=$groups" >> "$GITHUB_OUTPUT"
else
echo "has_changes=true" >> "$GITHUB_OUTPUT"
fi
# Runs created by this push are newer than this; the approve job - name: Hand the updates to the PR jobs
# waits for them. A minute's slack absorbs runner clock skew. if: ${{ !cancelled() && steps.groups.outputs.groups != '' && steps.groups.outputs.groups != '[]' }}
- name: Record push time uses: actions/upload-artifact@v4
if: ${{ !cancelled() && steps.changes.outputs.has_changes == 'true' }}
id: pushed
run: echo "at=$(date -u -d '-1 minute' +%Y-%m-%dT%H:%M:%SZ)" >> "$GITHUB_OUTPUT"
- name: Create Pull Request
if: ${{ !cancelled() && steps.changes.outputs.has_changes == 'true' }}
id: cpr
uses: peter-evans/create-pull-request@v7
with: with:
token: ${{ secrets.GITHUB_TOKEN }} name: package-pins
commit-message: 'chore: sync upstream releases' path: ${{ runner.temp }}/pins.patch
title: "chore: sync upstream releases${{ steps.branch.outputs.scope && format(' ({0})', steps.branch.outputs.scope) || '' }}" retention-days: 1
prs:
needs: sync
if: ${{ !cancelled() && needs.sync.outputs.groups != '' }}
permissions:
contents: read
uses: ./.github/workflows/package-prs.yml
with:
groups: ${{ needs.sync.outputs.groups }}
prefix: auto/sync-upstream
title: Sync upstream releases
body: | body: |
Automated update of packages that track an upstream vendor release Automated update of a package that tracks an upstream vendor release
feed rather than the AUR. feed rather than the AUR.
Release watches and providers are declared in `.omarchy/package.json`; Release watches and providers are declared in `.omarchy/package.json`;
exceptional feeds use `.omarchy/upstream.sh`. Failed package updates exceptional feeds use `.omarchy/upstream.sh`. This PR auto-merges once
are left untouched; check the workflow result for outstanding failures. the build checks pass. A failing build leaves it open for a maintainer
branch: ${{ steps.branch.outputs.branch }} without holding back any other package.
delete-branch: true secrets:
# The bot is trusted; build-approved lets the approve job below PKGS_BOT_TOKEN: ${{ secrets.PKGS_BOT_TOKEN }}
# release GitHub's hold on its pushes without a maintainer.
labels: |
automated
build-approved
reviewers: ryanrhughes
notify:
needs: [sync, prs]
if: ${{ failure() }}
runs-on: ubuntu-latest
steps:
- name: Notify Basecamp on failure - name: Notify Basecamp on failure
if: failure() && env.BASECAMP_CHATBOT_URL != '' if: ${{ env.BASECAMP_CHATBOT_URL != '' }}
env: env:
BASECAMP_CHATBOT_URL: ${{ secrets.BASECAMP_CHATBOT_URL }} BASECAMP_CHATBOT_URL: ${{ secrets.BASECAMP_CHATBOT_URL }}
run: | run: |
@@ -131,36 +142,3 @@ jobs:
"🔴 <strong>Upstream sync failed</strong><br><a href=\"${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}\">View run</a>" \ "🔴 <strong>Upstream sync failed</strong><br><a href=\"${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}\">View run</a>" \
'{content: $content}')" \ '{content: $content}')" \
"$BASECAMP_CHATBOT_URL" "$BASECAMP_CHATBOT_URL"
# GitHub holds pull_request runs from a GITHUB_TOKEN push for approval and
# creates no pull_request_target run for it, so approve-pr.yml never sees
# the sync's own pushes. The sync labels its PR build-approved, so release
# the held runs for the commit just pushed, whether it opened the PR or
# updated it. A separate job, so the sync container's token never holds
# actions: write.
approve:
needs: sync
if: ${{ !cancelled() && (needs.sync.outputs.operation == 'created' || needs.sync.outputs.operation == 'updated') }}
runs-on: ubuntu-latest
timeout-minutes: 5
permissions:
contents: read
pull-requests: read
actions: write
steps:
- uses: actions/checkout@v4
with:
persist-credentials: false
- name: Release held build and test runs
uses: actions/github-script@v7
env:
NUMBER: ${{ needs.sync.outputs.number }}
BRANCH: ${{ needs.sync.outputs.branch }}
HEAD_SHA: ${{ needs.sync.outputs.head_sha }}
SINCE: ${{ needs.sync.outputs.pushed_at }}
with:
script: |
const approve = require('./.github/scripts/approve-sync-push.cjs');
const { NUMBER, BRANCH, HEAD_SHA, SINCE } = process.env;
await approve({ github, context, core, number: Number(NUMBER),
branch: BRANCH, headSha: HEAD_SHA, since: SINCE });
+9 -1
View File
@@ -45,10 +45,12 @@ jobs:
docker run --rm \ docker run --rm \
-v "$PWD:/workspace:ro" \ -v "$PWD:/workspace:ro" \
-w /workspace \ -w /workspace \
archlinux:base-devel bash -lc ' ghcr.io/archlinux/archlinux:base-devel bash -lc '
set -euo pipefail set -euo pipefail
pacman -Syu --noconfirm git jq python libarchive neovim tmux pacman -Syu --noconfirm git jq python libarchive neovim tmux
python tests/owe-package-restart.py
python tests/oma-service-removal.py python tests/oma-service-removal.py
python tests/passwordless-package-lifecycle.py
python tests/upstream-watch.py python tests/upstream-watch.py
bash tests/ipu7-install.sh bash tests/ipu7-install.sh
bash tests/ipu7-headers.sh bash tests/ipu7-headers.sh
@@ -62,11 +64,17 @@ jobs:
./tests/published-build-plan.sh ./tests/published-build-plan.sh
./tests/settings-boot-config.sh ./tests/settings-boot-config.sh
./tests/settings-runtime-profile.sh ./tests/settings-runtime-profile.sh
./tests/passwordless-package-files.sh
./tests/pinned-sources.sh ./tests/pinned-sources.sh
./tests/controller.sh ./tests/controller.sh
./tests/artifact-helpers.sh ./tests/artifact-helpers.sh
./tests/limine-mkinitcpio-hook.sh ./tests/limine-mkinitcpio-hook.sh
./tests/voxtype-bin-install.sh ./tests/voxtype-bin-install.sh
./tests/superwhisper-bin-install.sh
pacman -S --noconfirm --quiet rclone >/dev/null pacman -S --noconfirm --quiet rclone >/dev/null
./tests/publish-artifact.sh ./tests/publish-artifact.sh
shopt -s nullglob
for test in pkgbuilds/*/test.sh; do
bash "$test"
done
' '
+62 -70
View File
@@ -1,13 +1,21 @@
name: Track upstream branches name: Track upstream branches
# The unattended lane. Packages marked "auto_merge": true follow a moving # The hourly lane. Packages marked "auto_merge": true update here instead of
# upstream branch (omarchy-dev and omarchy-settings-dev on quattro, omasnap-git # in sync-upstream.yml's 6-hourly run: some follow a moving upstream branch
# on main) rather than tagged releases, so nothing in this repository changes # (omarchy-dev and omarchy-settings-dev on quattro, omasnap-git on main)
# when their source does. This workflow makes each new branch tip a commit pin # rather than tagged releases, so nothing in this repository changes when
# in the recipe, which publish.yml then treats like any other version bump: # their source does; the rest are release feeds worth shipping within the
# the PR builds on the droplets, auto-merge lands it when `result` is green, # hour (browsers, large vendors, Omacom's own projects). This workflow makes
# and the merge publishes the artifacts. A tip that fails to build stays an # each new branch tip a commit pin in the recipe, which publish.yml then
# unmerged red PR that the next tick supersedes. # treats like any other version bump: the PR builds on the droplets,
# auto-merge lands it when `result` is green, and the merge publishes the
# artifacts. A tip that fails to build stays an unmerged red PR that the next
# tick supersedes.
#
# One PR per package, on auto/track-branches-<package>, opened by
# package-prs.yml. Packages pinned from the same upstream branch
# (omarchy-dev and omarchy-settings-dev) share one, because sync-upstream
# moves them in lockstep.
# #
# The PR and auto-merge use the PAT in PKGS_BOT_TOKEN so they trigger the # The PR and auto-merge use the PAT in PKGS_BOT_TOKEN so they trigger the
# build and publish workflows. The built-in GITHUB_TOKEN cannot drive this # build and publish workflows. The built-in GITHUB_TOKEN cannot drive this
@@ -16,8 +24,10 @@ name: Track upstream branches
on: on:
schedule: schedule:
# Every 2 hours, off the hour to dodge the scheduling backlog at :00 # Hourly, off the hour to dodge the scheduling backlog at :00. GitHub
- cron: '35 */2 * * *' # drops scheduled runs under load: the old every-2-hours entry fired
# every 6 to 9 hours in practice.
- cron: '35 * * * *'
workflow_dispatch: workflow_dispatch:
inputs: inputs:
packages: packages:
@@ -25,8 +35,8 @@ on:
required: false required: false
default: '' default: ''
# One tracker at a time: two runs racing on auto/track-branches would each # One tracker at a time: two runs racing on one auto/track-branches-* branch
# force-push their own pin over the other's. # would each force-push their own pin over the other's.
concurrency: concurrency:
group: track-branches group: track-branches
cancel-in-progress: false cancel-in-progress: false
@@ -36,6 +46,8 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
permissions: permissions:
contents: read contents: read
outputs:
groups: ${{ steps.groups.outputs.groups }}
steps: steps:
- name: Require the tracking token - name: Require the tracking token
@@ -52,7 +64,7 @@ jobs:
with: with:
persist-credentials: false persist-credentials: false
# Same container as the reviewed sync: vercmp decides whether a pin is # Same container as sync-upstream.yml: vercmp decides whether a pin is
# an upgrade with the comparator pacman uses on users' machines. # an upgrade with the comparator pacman uses on users' machines.
- name: Pin tracked branches to their current tips - name: Pin tracked branches to their current tips
id: sync id: sync
@@ -66,7 +78,7 @@ jobs:
-v "$PWD/helpers:/workspace/helpers:ro" \ -v "$PWD/helpers:/workspace/helpers:ro" \
-v "$PWD/pkgbuilds:/workspace/pkgbuilds" \ -v "$PWD/pkgbuilds:/workspace/pkgbuilds" \
-w /workspace \ -w /workspace \
archlinux:base-devel bash -lc ' ghcr.io/archlinux/archlinux:base-devel bash -lc '
set -euo pipefail set -euo pipefail
pacman -Syu --noconfirm git jq python libarchive pacman -Syu --noconfirm git jq python libarchive
@@ -86,70 +98,50 @@ jobs:
PACKAGES: ${{ github.event.inputs.packages }} PACKAGES: ${{ github.event.inputs.packages }}
UPSTREAM_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} UPSTREAM_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Check for changes - name: Group the pins into PRs
if: ${{ !cancelled() && steps.sync.outcome != 'skipped' }} if: ${{ !cancelled() && steps.sync.outcome != 'skipped' }}
id: changes id: groups
run: | run: |
if [ -z "$(git status --porcelain)" ]; then groups=$(.github/scripts/package-pr-groups.sh auto/track-branches "$RUNNER_TEMP/pins.patch")
echo "has_changes=false" >> "$GITHUB_OUTPUT" echo "groups=$groups" >> "$GITHUB_OUTPUT"
else
echo "has_changes=true" >> "$GITHUB_OUTPUT"
git status --porcelain
{
echo "### Pinned"
git diff --unified=0 -- 'pkgbuilds/*/PKGBUILD' | grep -E '^\+(pkgver|_commit)=' | sed 's/^+/- /'
} >> "$GITHUB_STEP_SUMMARY"
fi
# The PR title names what moved, so the merged history reads like a - name: Hand the pins to the PR jobs
# changelog: "Track upstream branches: omarchy-dev 4.0.0.r6520.g1a2b3c4, ...". if: ${{ !cancelled() && steps.groups.outputs.groups != '' && steps.groups.outputs.groups != '[]' }}
- name: Describe the pins uses: actions/upload-artifact@v4
if: ${{ !cancelled() && steps.changes.outputs.has_changes == 'true' }}
id: describe
run: |
title=$(git diff --unified=0 -- 'pkgbuilds/*/PKGBUILD' \
| awk '/^\+\+\+ b\/pkgbuilds\//{split($2,p,"/"); pkg=p[3]} /^\+pkgver=/{sub(/^\+pkgver=/,""); printf "%s %s, ", pkg, $0}' \
| sed 's/, $//')
echo "title=Track upstream branches: ${title}" >> "$GITHUB_OUTPUT"
- name: Open or update the tracking PR
if: ${{ !cancelled() && steps.changes.outputs.has_changes == 'true' }}
id: pr
uses: peter-evans/create-pull-request@5f6978faf089d4d20b00c7766989d076bb2fc7f1 # v8.1.1
with: with:
token: ${{ secrets.PKGS_BOT_TOKEN }} name: package-pins
commit-message: ${{ steps.describe.outputs.title }} path: ${{ runner.temp }}/pins.patch
title: ${{ steps.describe.outputs.title }} retention-days: 1
prs:
needs: track
if: ${{ !cancelled() && needs.track.outputs.groups != '' }}
permissions:
contents: read
uses: ./.github/workflows/package-prs.yml
with:
groups: ${{ needs.track.outputs.groups }}
prefix: auto/track-branches
title: Track upstream branches
body: | body: |
Automated pin of packages that follow a moving upstream branch Automated pin of a package that follows a moving upstream branch or a
(`"auto_merge": true` in `.omarchy/package.json`). Each package's trusted release feed (`"auto_merge": true` in `.omarchy/package.json`).
`_commit` now points at the branch tip. Fresh tips wait until Its recipe now points at the newest upstream release, or the newest
their commit timestamp is at least `min_release_age` old. branch tip whose commit timestamp is at least `min_release_age` old.
This PR auto-merges once the build checks pass. A failing build This PR auto-merges once the build checks pass. A failing build leaves
leaves it open; the next tracker run replaces it with the newer tip. it open without holding back any other package; the next tracker run
branch: auto/track-branches replaces it with the newer tip.
delete-branch: true secrets:
labels: automated PKGS_BOT_TOKEN: ${{ secrets.PKGS_BOT_TOKEN }}
# Auto-merge, not a direct merge: branch protection still has to see
# `result`, `self-tests` and `build-isolation` green, and this lane
# inherits every rule the reviewed lane has except the human.
- name: Enable auto-merge
if: ${{ !cancelled() && steps.pr.outputs.pull-request-number != '' }}
env:
GH_TOKEN: ${{ secrets.PKGS_BOT_TOKEN }}
PR: ${{ steps.pr.outputs.pull-request-number }}
run: |
# Idempotent across re-runs of an updated PR: enabling twice errors.
if [[ "$(gh pr view "$PR" -R "${{ github.repository }}" --json autoMergeRequest --jq '.autoMergeRequest != null')" == true ]]; then
echo "auto-merge already enabled on #$PR"
exit 0
fi
gh pr merge --auto --merge "$PR" -R "${{ github.repository }}"
notify:
needs: [track, prs]
if: ${{ failure() }}
runs-on: ubuntu-latest
steps:
- name: Notify Basecamp on failure - name: Notify Basecamp on failure
if: failure() && env.BASECAMP_CHATBOT_URL != '' if: ${{ env.BASECAMP_CHATBOT_URL != '' }}
env: env:
BASECAMP_CHATBOT_URL: ${{ secrets.BASECAMP_CHATBOT_URL }} BASECAMP_CHATBOT_URL: ${{ secrets.BASECAMP_CHATBOT_URL }}
run: | run: |
+81
View File
@@ -0,0 +1,81 @@
name: Unpublish retired packages
# Takes packages out of the channel databases after their recipes are gone
# from master. Deleting a recipe stops it building; this stops pacman
# offering it. Files stay in the bucket (see bin/unpublish-packages).
#
# Only packages with no recipe on master: one that still has a recipe would
# come back on its next publish, and refusing it keeps a typo from pulling a
# live package out of every channel.
on:
workflow_dispatch:
inputs:
packages:
description: "Space-separated pkgbases whose recipes were removed from master"
required: true
channels:
description: "Channels to remove them from"
required: true
default: "edge rc stable"
jobs:
unpublish:
runs-on: ubuntu-latest
environment: publish
timeout-minutes: 15
# The publish job's group: one writer per channel database at a time.
# queue: max, as there: a waiting job is queued, not replaced.
concurrency:
group: publish
cancel-in-progress: false
queue: max
steps:
- uses: actions/checkout@v4
with:
persist-credentials: false
- name: Refuse packages that still have a recipe
env:
PACKAGES: ${{ github.event.inputs.packages }}
CHANNELS: ${{ github.event.inputs.channels }}
run: |
set -euo pipefail
for c in $CHANNELS; do
[[ $c == edge || $c == rc || $c == stable ]] || { echo "::error::Unknown channel: $c"; exit 1; }
done
for p in $PACKAGES; do
[[ $p =~ ^[a-z0-9@._+-]+$ ]] || { echo "::error::Not a package name: $p"; exit 1; }
if [[ -e pkgbuilds/$p ]]; then
echo "::error::pkgbuilds/$p still exists on master; remove the recipe first"
exit 1
fi
done
- name: Unpublish
env:
PACKAGES: ${{ github.event.inputs.packages }}
CHANNELS: ${{ github.event.inputs.channels }}
RCLONE_CONFIG_R2_TYPE: s3
RCLONE_CONFIG_R2_PROVIDER: Cloudflare
RCLONE_CONFIG_R2_NO_CHECK_BUCKET: "true"
RCLONE_CONFIG_R2_ACCESS_KEY_ID: ${{ secrets.R2_ACCESS_KEY_ID }}
RCLONE_CONFIG_R2_SECRET_ACCESS_KEY: ${{ secrets.R2_SECRET_ACCESS_KEY }}
RCLONE_CONFIG_R2_ENDPOINT: ${{ secrets.R2_ENDPOINT }}
OMARCHY_PUBLISH_PREFIX: ${{ vars.OMARCHY_PUBLISH_PREFIX }}
run: |
set -euo pipefail
# repo-remove and bsdtar are Arch tools: run in the tested builder
# image, as the publish job does.
builder=ghcr.io/omacom/omarchy-pkg-builder:$(bin/builder-image key --arch x86_64 --mirror edge)
docker pull --quiet "$builder"
for mirror in $CHANNELS; do
for arch in x86_64 aarch64; do
docker run --rm \
-e OMARCHY_PUBLISH_PREFIX \
-e RCLONE_CONFIG_R2_TYPE -e RCLONE_CONFIG_R2_PROVIDER -e RCLONE_CONFIG_R2_ENDPOINT -e RCLONE_CONFIG_R2_NO_CHECK_BUCKET \
-e RCLONE_CONFIG_R2_ACCESS_KEY_ID -e RCLONE_CONFIG_R2_SECRET_ACCESS_KEY \
-v "$PWD:/w:ro" -w /w "$builder" \
bin/unpublish-packages --remote R2:omarchy-pkgs --mirror "$mirror" --arch "$arch" $PACKAGES
done
done
+441 -1011
View File
File diff suppressed because it is too large. Load diff
+1 -1
View File
@@ -86,7 +86,7 @@ while [[ $# -gt 0 ]]; do
echo " $0 --arch aarch64" echo " $0 --arch aarch64"
echo " $0 --mirror stable" echo " $0 --mirror stable"
echo " $0 --package yay" echo " $0 --package yay"
echo " $0 --package yay elephant cursor-bin" echo " $0 --package yay walker cursor-bin"
echo "" echo ""
echo "Environment (for CI and resumed builds; defaults keep today's behaviour):" echo "Environment (for CI and resumed builds; defaults keep today's behaviour):"
echo " OMARCHY_KEEP_BUILD_WORKSPACE=1 keep build-output and reuse packages already there" echo " OMARCHY_KEEP_BUILD_WORKSPACE=1 keep build-output and reuse packages already there"
+54
View File
@@ -329,6 +329,43 @@ write_pkgrel() {
mv "$scratch" "$pkgbuild" mv "$scratch" "$pkgbuild"
} }
# A package built against a trigger's ABI pins it exactly, once per architecture
# because Arch Linux ARM can trail x86_64: depends_x86_64=('hyprland=0.56.2').
# Rebuilding against a newer trigger has to move that pin with it, or the
# rebuilt package demands the version it no longer loads into. Unpinned
# dependencies, and pins outside the per-architecture arrays, are left alone.
write_pins() {
local package_dir="$1"
local current="$2"
local pkgbuild="$package_dir/PKGBUILD"
local scratch="$pkgbuild.sync-rebuilds"
local arch trigger version pattern
cp "$pkgbuild" "$scratch" || return 1
while IFS=$'\t' read -r arch trigger version; do
pattern=$(printf '%s' "$trigger" | sed 's/[][\.*^$+?(){}|/]/\\&/g')
if ! awk -v array="depends_$arch" -v pattern="$pattern" -v pin="$trigger=$(version_pkgver "$version")" '
$0 ~ "^" array "=\\(" { inside = 1 }
inside { gsub("[\"\047]" pattern "=[^\"\047]*[\"\047]", "\047" pin "\047") }
inside && /\)/ { inside = 0 }
{ print }
' "$scratch" > "$scratch.new"; then
rm -f "$scratch" "$scratch.new"
return 1
fi
mv "$scratch.new" "$scratch"
done < <(jq -r 'to_entries[] | .key as $arch | .value | to_entries[] | [$arch, .key, .value] | @tsv' <<<"$current")
if ! bash -n "$scratch" 2>/dev/null; then
print_error "PKGBUILD with moved pins is not valid shell"
rm -f "$scratch"
return 1
fi
chmod --reference="$pkgbuild" "$scratch"
mv "$scratch" "$pkgbuild"
}
write_metadata() { write_metadata() {
local package_dir="$1" local package_dir="$1"
local filter="$2" local filter="$2"
@@ -551,6 +588,12 @@ sync_package() {
return 0 return 0
fi fi
if ! write_pins "$package_dir" "$current"; then
print_error " Failed to move pinned trigger versions for $package"
((++FAILED))
return 0
fi
if [[ -n "$suffix" ]] && ! write_metadata "$package_dir" '.pkgrel.suffix = ($suffix | tonumber)' --arg suffix "$suffix"; then if [[ -n "$suffix" ]] && ! write_metadata "$package_dir" '.pkgrel.suffix = ($suffix | tonumber)' --arg suffix "$suffix"; then
print_error " Failed to record pkgrel suffix for $package" print_error " Failed to record pkgrel suffix for $package"
((++FAILED)) ((++FAILED))
@@ -792,6 +835,17 @@ cmd_self_test() {
check "run succeeds" 0 "$(run_case "$root")" check "run succeeds" 0 "$(run_case "$root")"
check "pkgrel untouched" 1 "$(pkgrel_of "$root/pkgbuilds/t-current")" check "pkgrel untouched" 1 "$(pkgrel_of "$root/pkgbuilds/t-current")"
echo "A rebuild moves the trigger's exact pin for the architecture it ran for:"
root=$(selftest_root pins)
selftest_package "$root" t-pins 1 '{"source":"local","rebuild_on":["dep-a"],"rebuilt_against":{"dep-a":"1.0-1"}}'
printf "depends_x86_64=('dep-a=1.0' 'dep-b')\ndepends_aarch64=('dep-a=1.0')\n" >> "$root/pkgbuilds/t-pins/PKGBUILD"
selftest_pacman "$root" dep-a=2.0-1
selftest_published "$root"
check "run succeeds" 0 "$(run_case "$root")"
check "pkgrel bumped" 2 "$(pkgrel_of "$root/pkgbuilds/t-pins")"
check "x86_64 pin moved" "depends_x86_64=('dep-a=2.0' 'dep-b')" "$(grep '^depends_x86_64=' "$root/pkgbuilds/t-pins/PKGBUILD")"
check "aarch64 pin left for its own run" "depends_aarch64=('dep-a=1.0')" "$(grep '^depends_aarch64=' "$root/pkgbuilds/t-pins/PKGBUILD")"
echo "Metadata that does not parse fails the run instead of vanishing from it:" echo "Metadata that does not parse fails the run instead of vanishing from it:"
root=$(selftest_root unreadable) root=$(selftest_root unreadable)
selftest_package "$root" t-broken 1 '{"source":"local","rebuild_on":["dep-a"' selftest_package "$root" t-broken 1 '{"source":"local","rebuild_on":["dep-a"'
+1 -1
View File
@@ -23,7 +23,7 @@ Update packages that track an upstream vendor release feed instead of the AUR.
Packages opt in declaratively through "upstream" in .omarchy/package.json. Packages opt in declaratively through "upstream" in .omarchy/package.json.
Providers cover GitHub Releases with checksum manifests or API asset digests, Providers cover GitHub Releases with checksum manifests or API asset digests,
semver-shaped git tags, npm dist-tags, and plain Debian Packages indexes. See semver-shaped git tags, npm dist-tags, and plain Debian Packages indexes. See
README.md for the schemas. Anything outside those conventions may provide docs/upstream-sources.md for the schemas. Anything outside those conventions may provide
pkgbuilds/<package>/.omarchy/upstream.sh, a hook that reports JSON on stdout: pkgbuilds/<package>/.omarchy/upstream.sh, a hook that reports JSON on stdout:
{ {
+80
View File
@@ -0,0 +1,80 @@
#!/bin/bash
# Retire packages from one channel of the remote repository.
#
# unpublish-packages --mirror <edge|rc|stable> --arch <arch> <pkgbase...>
#
# The counterpart of publish-artifact, with the same steps:
# 1. pull the channel's current database from the remote
# 2. find every entry built from the named pkgbases (so a package's split
# outputs and -debug go with it)
# 3. repo-remove them
# 4. upload the database
#
# Package files stay in the bucket. Published filenames are immutable and the
# R2 cache cannot recover from a rewrite; an unreferenced file costs nothing
# and pacman never sees it. Naming a package the channel does not hold is not
# an error, so a re-run is harmless.
#
# The remote is an rclone remote (REMOTE, default the production one);
# OMARCHY_PUBLISH_PREFIX can point a proof run at a scratch prefix.
set -euo pipefail
BUILD_ROOT=$(realpath "${BASH_SOURCE[0]%/*}/..")
source "$BUILD_ROOT/helpers/message-helpers.sh"
source "$BUILD_ROOT/helpers/paths.sh"
REMOTE=${REMOTE:-pkgs.omarchy.org:omarchy-pkgs}
PREFIX=${OMARCHY_PUBLISH_PREFIX:-}
BASES=()
while [[ $# -gt 0 ]]; do
case $1 in
--mirror) MIRROR=$2; require_valid_mirror "$MIRROR"; shift 2 ;;
--arch) ARCH=$2; require_valid_arch "$ARCH"; shift 2 ;;
--remote) REMOTE=$2; shift 2 ;;
-h|--help) sed -n '2,19p' "$0"; exit 0 ;;
-*) print_error "Unknown option: $1"; exit 1 ;;
*) BASES+=("$1"); shift ;;
esac
done
(( ${#BASES[@]} )) || { print_error "No packages given"; exit 1; }
DEST="$REMOTE/${PREFIX:+$PREFIX/}$MIRROR/$ARCH"
WORK=$(mktemp -d)
trap 'rm -rf "$WORK"' EXIT
print_header "Unpublish from $DEST"
# --- 1. pull the current database -----------------------------------------
mkdir -p "$WORK/repo"
listing=$(rclone lsf "$DEST/" --s3-no-head 2>/dev/null || true)
if ! grep -q '^omarchy.db.tar.zst$' <<<"$listing"; then
print_info "No database at $DEST; nothing to remove"
exit 0
fi
rclone copy "$DEST/omarchy.db.tar.zst" "$WORK/repo" --s3-no-head
rclone copy "$DEST/omarchy.files.tar.zst" "$WORK/repo" --s3-no-head 2>/dev/null || true
before=$(tar -tf "$WORK/repo/omarchy.db.tar.zst" | grep -c '/$')
print_info "Pulled current database ($before entries)"
# --- 2. entries built from the named pkgbases -----------------------------
names=$(bsdtar -xOf "$WORK/repo/omarchy.db.tar.zst" --include '*/desc' |
awk -v bases=" ${BASES[*]} " '
/^%NAME%$/ { getline name }
/^%BASE%$/ { getline base; if (index(bases, " " base " ")) print name }')
if [[ -z "$names" ]]; then
print_info "None of ${BASES[*]} is in $MIRROR/$ARCH; nothing to remove"
exit 0
fi
mapfile -t NAMES <<<"$names"
for n in "${NAMES[@]}"; do print_step "removing $n"; done
# --- 3. repo-remove ---------------------------------------------------------
( cd "$WORK/repo" && repo-remove --quiet omarchy.db.tar.zst "${NAMES[@]}" )
ln -sf omarchy.db.tar.zst "$WORK/repo/omarchy.db"
ln -sf omarchy.files.tar.zst "$WORK/repo/omarchy.files"
after=$(tar -tf "$WORK/repo/omarchy.db.tar.zst" | grep -c '/$')
(( before - after == ${#NAMES[@]} )) || {
print_error "Expected to remove ${#NAMES[@]} entries, removed $((before - after))"; exit 1; }
print_info "Database now has $after entries"
# --- 4. upload the database -------------------------------------------------
rclone copy "$WORK/repo" "$DEST" --s3-no-head --checksum --copy-links --filter '+ omarchy.db*' --filter '+ omarchy.files*' --filter '- *'
print_success "Removed ${#NAMES[@]} package(s) from $DEST"
+4
View File
@@ -88,6 +88,10 @@ if [[ "$DRY_RUN" != true ]]; then
mkdir -p "$BUILD_OUTPUT_DIR" "$FINAL_OUTPUT_DIR" mkdir -p "$BUILD_OUTPUT_DIR" "$FINAL_OUTPUT_DIR"
# The image drops pacman's private signing key. Keyring upgrades need a
# container-local key to sign newly trusted keys.
sudo pacman-key --init || exit 1
# Bring the container up to date before any makedepends are installed. The # Bring the container up to date before any makedepends are installed. The
# image is layer-cached, so its glibc drifts behind the mirror while makepkg # image is layer-cached, so its glibc drifts behind the mirror while makepkg
# -s pulls makedepends from the freshly synced database -- a partial upgrade # -s pulls makedepends from the freshly synced database -- a partial upgrade
+104 -69
View File
@@ -1,83 +1,118 @@
# CI spike: build PRs on ephemeral DigitalOcean droplets # The x86_64 builder pool
Status: spike. Nothing here publishes. The repository host keeps building and x86_64 package builds run on DigitalOcean droplets that exist for one job
signing on merge exactly as before. each. aarch64 builds run on GitHub's `ubuntu-24.04-arm` runners and need
nothing here.
## Pieces ## How it works
- `.github/workflows/build-pr.yml` — on a PR touching `pkgbuilds/**`, one job - **Jobs ask for a runner labelled `omarchy-builder`.** `build-pr.yml` builds
per changed package on runners labelled `omarchy-builder`. aarch64 jobs x86_64 packages there, and so does the `rebuild` job in `publish.yml`.
run on GitHub's native `ubuntu-24.04-arm` runners instead. Uploads the unsigned - **A controller creates one droplet per queued job.** `controller.sh` runs
`.pkg.tar.zst` as a workflow artifact (7 days). from a systemd timer every minute on a small always-on droplet (tag
- `runner-cloud-init.yaml` — Ubuntu 24.04 user-data: docker + buildx, the `omarchy-controller`). It polls GitHub for queued jobs with the label and
GitHub runner registered `--ephemeral`, runs one job, powers off. creates droplets up to `MAX_DROPLETS`. It has no inbound endpoint.
- `controller.sh` — systemd timer every minute on a small always-on droplet. - **A droplet runs one job and powers off.** `runner-cloud-init.yaml` installs
Polls for queued jobs with our label, creates one g5-32vcpu-64gb-50gb droplet (ric1) per job up Docker and the GitHub runner, registers it `--ephemeral` with a
to `MAX_DROPLETS`, deletes droplets that are powered off or older than registration token that expires in an hour, runs the job, and powers off, also when the runner download or
`MAX_AGE_MINUTES`. No inbound endpoint. Plain curl against both APIs, no registration fails. The controller removes any other failed droplet at
doctl and no gh: a token in the environment cannot pick the wrong account `MAX_AGE_MINUTES`.
the way a saved doctl context can. Needs curl and jq. - **The controller deletes droplets** that are powered off, older than
`tests/controller.sh` exercises every decision against canned responses. `MAX_AGE_MINUTES`, or still provisioning after `MAX_BOOT_MINUTES`, and
- `controller-box/` — the always-on droplet: unit, timer, env template, replaces a stuck one in the same tick.
cloud-init, and `create.sh` to stand it up with one API call. - **Sizes and regions fall back.** Each tick tries every size in `SIZES`, in
every region DigitalOcean lists it in stock. `REGIONS` only sets which
regions go first. A refused create is logged with DigitalOcean's message.
## Standing up the controller box No secret reaches a builder. Signing and upload happen in the `publish` job on
a GitHub-hosted runner.
DIGITALOCEAN_TOKEN=<omarchy account> GITHUB_TOKEN=<fine-grained PAT> \ ## Operate it
REPO=omacom/omarchy-pkgs ci/controller-box/create.sh <branch>
The GitHub PAT is fine-grained, scoped to the one repo: Actions read, SSH to the controller droplet as root. It is the droplet tagged
Administration read+write (registration tokens). The DO token is baked into `omarchy-controller` in the DigitalOcean account that pays for the builders;
the box's env file, so it is the account that pays for builder droplets. the operators' keys were installed when it was created.
Watch it with `journalctl -u omarchy-controller -f` on the box.
## What the spike proved (2026-09-17, fork ryanrhughes/omarchy-pkgs) ```bash
journalctl -u omarchy-controller -n 50 --no-pager # What it is doing
journalctl -u omarchy-controller -f # Follow it
systemctl list-timers omarchy-controller.timer # Is it ticking
```
- `bin/build` works from a bare clone: with no local published tree it | Log line | Meaning |
plans against and resolves from `https://pkgs.omarchy.org/<mirror>/<arch>`. |---|---|
- Droplet create → runner registered: ~70 s. omarchy-fish PR job: 2 min | `creating <name> (<size> in <region>)` | A queued job is getting a droplet |
including the builder image build. Droplet powers off after the job. | `<size> in <region> refused: <message>` | DigitalOcean refused; the next region or size is tried |
- linux-omarchy on a c-32 droplet: 30 min wall clock for the build job | `no size in '<sizes>' can be created in any region` | Every create was refused. Read the `refused:` lines above it for why: out of stock (add a size to `SIZES`), or the account's droplet limit |
(23:39 → 00:09), 254 MB artifact. Cold start ~90 s before the job began. | `at cap (<live>/<max>, <busy> busy) with <queued> queued` | `MAX_DROPLETS` is reached. Raise it if the queue is long |
- A PR whose PKGBUILD fails to build turns the required check red and GitHub | `deleting droplet <id> (status=<status> age=<n>m)` | Reaping a finished, old or stuck droplet |
refuses the merge (`mergeStateStatus=BLOCKED`, `gh pr merge` refuses
without `--admin`).
- Controller: one queued job + one busy droplet ⇒ creates exactly one more;
reaps powered-off droplets on the next tick.
## Not done (required before this touches the real repo) Check the pool from anywhere:
- Tooling from base: check out master's `bin/ helpers/ build/` and overlay ```bash
only the PR's `pkgbuilds/<name>`; today a PR can edit the build script gh api repos/omacom/omarchy-pkgs/actions/runners --jq \
and it runs on the droplet. The vouch gate limits who can do that, not '"online \([.runners[]|select(.status=="online")]|length), busy \([.runners[]|select(.busy)]|length)"'
what they can do. ```
- DigitalOcean cloud firewall on the `omarchy-builder` tag: no inbound, no
egress to private ranges or the metadata address.
- A fine-grained GitHub token for the real repository (the one on the
controller box is scoped to the fork), and the publish environment's
secrets set there.
- Disable the host's auto-release timers for any channel CI publishes to,
so two writers never touch one database.
## Done since the spike README was first written Jobs queued with no runner online for several minutes means the controller is
not creating droplets: read its journal. See what is waiting:
- Controller as a systemd timer on its own droplet, plain curl, self-test. ```bash
- Build once against edge; one artifact per package per architecture, gh run list -R omacom/omarchy-pkgs --workflow build-pr.yml --status queued
published into every channel it belongs to (fast ring: all three at ```
once). arch=any builds once for every architecture database.
- Publish is incremental and immutable: pull the channel db, refuse
different bytes under an existing name, accept identical bytes, upload
packages then signatures then the db.
- aarch64 under QEMU with credential-preserving binfmt. PR builds now run
aarch64 natively on `ubuntu-24.04-arm` (QEMU was up to ~15x slower). When a
merged aarch64 tree has no artifact, publish.yml rebuilds it there too, in its
own job, and signs and uploads it on the droplet like a PR artifact.
- Vouch gate: collaborators, `.github/VOUCHED.td`, or the `build-approved`
label; denounced authors cannot be overridden by the label.
- Tests run on PRs only; `result`, `self-tests`, `build-isolation` are the
required checks with strict up-to-date branches.
## Cleanup Only x86_64 jobs use this pool. A queued aarch64 job is waiting on GitHub's
own runners.
doctl compute droplet list --tag-name omarchy-builder ### Change settings
doctl compute droplet delete -f <id>
Edit `/etc/omarchy-controller.env` on the box. The next tick reads it.
| Setting | Default in `controller-box/controller.env.example` | |
|---|---|---|
| `MAX_DROPLETS` | 6 | Builders alive at once. The DigitalOcean account's droplet limit also applies |
| `SIZES` | `g5-32vcpu-64gb-50gb g5-32vcpu-128gb-50gb` | Tried in order |
| `REGIONS` | empty | Regions to try first; empty means any |
| `MAX_AGE_MINUTES` | 200 | Delete a droplet older than this |
| `MAX_BOOT_MINUTES` | 10 | Delete a droplet still provisioning after this |
| `DO_SSH_KEYS` | Ryan, DHH and Emir's account key IDs | Attached to every builder. Without one, DigitalOcean emails a root password per builder |
### Change the controller
Merge the change to `master`. The unit pulls `/opt/omarchy-pkgs` before every
tick, so it is live within a minute. The unit and timer files are copies made
when the box was created; after changing them, on the box:
```bash
cp /opt/omarchy-pkgs/ci/controller-box/omarchy-controller.{service,timer} /etc/systemd/system/
systemctl daemon-reload
```
`tests/controller.sh` exercises every controller decision against canned API
responses. Run it before merging a change.
### Reach a builder
Operators can SSH to a builder as root while it lives. It powers off after its
one job.
### Clean up by hand
The controller reaps on its own. To do it manually, delete droplets tagged
`omarchy-builder` in the DigitalOcean account.
## Stand up a controller
```bash
DIGITALOCEAN_TOKEN=<account that pays for builders> GITHUB_TOKEN=<fine-grained PAT> \
REPO=omacom/omarchy-pkgs ci/controller-box/create.sh
```
- The GitHub PAT is fine-grained and scoped to this repository: Actions read,
Administration read and write (for runner registration tokens).
- The DigitalOcean token is written into the box's env file, so it is the
account that pays. It needs ssh_key:read to attach `DO_SSH_KEYS`; without
it every create is refused with 403.
- `ADMIN_GITHUB_USERS` names whose GitHub SSH keys get root on the box and
the builders. Set it; the default is a fixed list in `create.sh`.
- The script refuses to create a second controller.
+11 -2
View File
@@ -5,11 +5,20 @@ GITHUB_TOKEN=github_pat_...
REPO=omacom/omarchy-pkgs REPO=omacom/omarchy-pkgs
LABEL=omarchy-builder LABEL=omarchy-builder
TAG=omarchy-builder TAG=omarchy-builder
REGION=ric1 # Builder sizes, tried in order in any region that has them in stock.
SIZE=g5-32vcpu-64gb-50gb SIZES="g5-32vcpu-64gb-50gb g5-32vcpu-128gb-50gb"
# Optional: regions to try first, e.g. "ric1". Empty means any.
REGIONS=
MAX_DROPLETS=6 MAX_DROPLETS=6
MAX_AGE_MINUTES=200 MAX_AGE_MINUTES=200
# Delete a droplet DigitalOcean still reports as provisioning after this long.
MAX_BOOT_MINUTES=10
LOCK=/run/omarchy-controller/lock LOCK=/run/omarchy-controller/lock
# Operator public keys for root on every builder droplet (JSON array). # Operator public keys for root on every builder droplet (JSON array).
# create.sh fills this from the operators' GitHub keys. # create.sh fills this from the operators' GitHub keys.
SSH_KEYS_JSON=[] SSH_KEYS_JSON=[]
# DigitalOcean account SSH key IDs attached to every builder (JSON array), so
# DigitalOcean does not email a root password for each one. Needs ssh_key:read
# on DIGITALOCEAN_TOKEN. List them with: doctl compute ssh-key list
# Ryan R. Hughes, DHH, Emir.
DO_SSH_KEYS=[59143572,59143588,59779105]
+1 -1
View File
@@ -18,7 +18,7 @@ SSH_KEYS=${SSH_KEYS:-[]}
# Public keys authorized for root: the operators' GitHub keys, fetched at # Public keys authorized for root: the operators' GitHub keys, fetched at
# creation so the box never depends on an ssh_key API scope. Override with # creation so the box never depends on an ssh_key API scope. Override with
# ADMIN_GITHUB_USERS. # ADMIN_GITHUB_USERS.
ADMIN_GITHUB_USERS=${ADMIN_GITHUB_USERS:-ryanrhughes dhh} ADMIN_GITHUB_USERS=${ADMIN_GITHUB_USERS:-ryanrhughes dhh emirb}
ssh_keys_json=$(for u in $ADMIN_GITHUB_USERS; do curl -fsS "https://github.com/$u.keys"; done | jq -R . | jq -sc .) ssh_keys_json=$(for u in $ADMIN_GITHUB_USERS; do curl -fsS "https://github.com/$u.keys"; done | jq -R . | jq -sc .)
[[ $(jq length <<<"$ssh_keys_json") -gt 0 ]] || { echo "no ssh keys fetched for $ADMIN_GITHUB_USERS" >&2; exit 1; } [[ $(jq length <<<"$ssh_keys_json") -gt 0 ]] || { echo "no ssh keys fetched for $ADMIN_GITHUB_USERS" >&2; exit 1; }
@@ -7,6 +7,9 @@ Wants=network-online.target
Type=oneshot Type=oneshot
User=controller User=controller
EnvironmentFile=/etc/omarchy-controller.env EnvironmentFile=/etc/omarchy-controller.env
# Run the branch's current controller, not the one cloned when the box was
# built. As root (the checkout's owner); a failed pull keeps the last one.
ExecStartPre=-+/usr/bin/git -C /opt/omarchy-pkgs pull --ff-only --quiet
ExecStart=/opt/omarchy-pkgs/ci/controller.sh ExecStart=/opt/omarchy-pkgs/ci/controller.sh
# The reaper's safety net is time, not state; a hung tick must not hold the lock. # The reaper's safety net is time, not state; a hung tick must not hold the lock.
TimeoutStartSec=240 TimeoutStartSec=240
+94 -18
View File
@@ -4,7 +4,8 @@
# Run from a systemd timer every minute on a small always-on droplet. No # Run from a systemd timer every minute on a small always-on droplet. No
# inbound endpoint: it polls GitHub for queued jobs wanting our label, creates # inbound endpoint: it polls GitHub for queued jobs wanting our label, creates
# one ephemeral droplet per job (up to MAX_DROPLETS), and deletes droplets # one ephemeral droplet per job (up to MAX_DROPLETS), and deletes droplets
# that have powered off or exceeded MAX_AGE_MINUTES. The reaper does not # that have powered off, exceeded MAX_AGE_MINUTES, or are still provisioning
# after MAX_BOOT_MINUTES. The reaper does not
# trust its own bookkeeping: it lists by tag and acts on what DigitalOcean # trust its own bookkeeping: it lists by tag and acts on what DigitalOcean
# reports. # reports.
# #
@@ -21,16 +22,30 @@ REPO=${REPO:?owner/name}
: "${DIGITALOCEAN_TOKEN:?}" "${GITHUB_TOKEN:?}" : "${DIGITALOCEAN_TOKEN:?}" "${GITHUB_TOKEN:?}"
LABEL=${LABEL:-omarchy-builder} LABEL=${LABEL:-omarchy-builder}
TAG=${TAG:-omarchy-builder} TAG=${TAG:-omarchy-builder}
REGION=${REGION:-ric1} # Sizes to try, in order, in any region DigitalOcean lists them in stock. A
SIZE=${SIZE:-g5-32vcpu-64gb-50gb} # size can sell out in a region for hours; the create is then refused with
# 422 and the next region, then the next size, is tried. REGIONS only orders
# the regions tried first. SIZE and REGION, if set, are one-item lists.
SIZES=${SIZES:-${SIZE:-g5-32vcpu-64gb-50gb g5-32vcpu-128gb-50gb}}
REGIONS=${REGIONS:-${REGION:-}}
IMAGE=${IMAGE:-ubuntu-24-04-x64} IMAGE=${IMAGE:-ubuntu-24-04-x64}
MAX_DROPLETS=${MAX_DROPLETS:-4} MAX_DROPLETS=${MAX_DROPLETS:-4}
MAX_AGE_MINUTES=${MAX_AGE_MINUTES:-200} MAX_AGE_MINUTES=${MAX_AGE_MINUTES:-200}
# A droplet DigitalOcean still reports as "new" this long after creation is
# stuck provisioning. Left alone it counts as a runner booting, and holds a
# queued job until MAX_AGE_MINUTES.
MAX_BOOT_MINUTES=${MAX_BOOT_MINUTES:-10}
RUNNER_VERSION=${RUNNER_VERSION:-2.337.0} RUNNER_VERSION=${RUNNER_VERSION:-2.337.0}
CLOUD_INIT=${CLOUD_INIT:-$(dirname "$0")/runner-cloud-init.yaml} CLOUD_INIT=${CLOUD_INIT:-$(dirname "$0")/runner-cloud-init.yaml}
# Operator public keys authorized on every builder (JSON array of strings). # Operator public keys authorized on every builder (JSON array of strings).
# The box's env file carries them; empty means no root login. # The box's env file carries them; empty means no root login.
SSH_KEYS_JSON=${SSH_KEYS_JSON:-[]} SSH_KEYS_JSON=${SSH_KEYS_JSON:-[]}
# DigitalOcean account SSH key IDs or fingerprints attached at creation (JSON
# array). A droplet created with none gets a root password that DigitalOcean
# emails to the token's owner, once per builder. Attaching one needs the
# ssh_key:read scope on DIGITALOCEAN_TOKEN; without it every create is
# refused with 403.
DO_SSH_KEYS=${DO_SSH_KEYS:-[]}
LOCK=${LOCK:-/tmp/omarchy-controller.lock} LOCK=${LOCK:-/tmp/omarchy-controller.lock}
log() { echo "$(date '+%F %T') $*"; } log() { echo "$(date '+%F %T') $*"; }
@@ -39,7 +54,8 @@ log() { echo "$(date '+%F %T') $*"; }
# both, so every decision below is exercised against canned responses. # both, so every decision below is exercised against canned responses.
do_api() { # do_api <path> [curl args...] do_api() { # do_api <path> [curl args...]
local path=$1; shift local path=$1; shift
curl -fsS -H "Authorization: Bearer $DIGITALOCEAN_TOKEN" \ # --fail-with-body: a refused create still prints why.
curl -sS --fail-with-body -H "Authorization: Bearer $DIGITALOCEAN_TOKEN" \
-H "Content-Type: application/json" "https://api.digitalocean.com/v2/$path" "$@" -H "Content-Type: application/json" "https://api.digitalocean.com/v2/$path" "$@"
} }
gh_api() { # gh_api <path> [curl args...] gh_api() { # gh_api <path> [curl args...]
@@ -55,7 +71,8 @@ reap() {
while read -r id status created; do while read -r id status created; do
[[ -n "$id" ]] || continue [[ -n "$id" ]] || continue
age=$(( (now - $(date -d "$created" +%s)) / 60 )) age=$(( (now - $(date -d "$created" +%s)) / 60 ))
if [[ $status == off ]] || (( age > MAX_AGE_MINUTES )); then if [[ $status == off ]] || (( age > MAX_AGE_MINUTES )) ||
{ [[ $status == new ]] && (( age > MAX_BOOT_MINUTES )); }; then
log "deleting droplet $id (status=$status age=${age}m)" log "deleting droplet $id (status=$status age=${age}m)"
do_api "droplets/$id" -X DELETE do_api "droplets/$id" -X DELETE
fi fi
@@ -64,18 +81,40 @@ reap() {
} }
# --- demand ---------------------------------------------------------------- # --- demand ----------------------------------------------------------------
# Emit every item, including later pages of large build matrices. Keep API
# failures fatal so a failed query cannot look like an empty queue.
gh_items() {
local path=$1 key=$2 page=1 response count separator="?"
[[ $path != *"?"* ]] || separator="&"
while :; do
response=$(gh_api "${path}${separator}per_page=100&page=$page") || return 1
count=$(jq -er --arg key "$key" '.[$key] | arrays | length' <<< "$response") || return 1
jq -c --arg key "$key" '.[$key][]' <<< "$response" || return 1
(( count == 100 )) || break
((page += 1))
done
}
queued_jobs() { queued_jobs() {
local run local status runs run
gh_api "repos/$REPO/actions/runs?status=queued&per_page=50" --get \ # A workflow can be in progress while most of its matrix is still queued.
| jq -r '.workflow_runs[].id' | runs=$(
for status in queued in_progress; do
gh_items "repos/$REPO/actions/runs?status=$status" workflow_runs || exit 1
done
) || return 1
jq -r '.id' <<< "$runs" | sort -u |
while read -r run; do while read -r run; do
gh_api "repos/$REPO/actions/runs/$run/jobs" \ gh_items "repos/$REPO/actions/runs/$run/jobs" jobs |
| jq -r --arg l "$LABEL" '.jobs[] | select(.status=="queued") | select(.labels | index($l)) | .id' jq -r --arg l "$LABEL" 'select(.status=="queued") | select(.labels | index($l)) | .id' || return 1
done | wc -l done | sort -u | wc -l
} }
live_droplets() { live_droplets() {
do_api "droplets?tag_name=$TAG&per_page=200" | jq '[.droplets[] | select(.status != "off")] | length' # Not the ones reap() just deleted: DigitalOcean can list them for a while.
do_api "droplets?tag_name=$TAG&per_page=200" | jq --argjson boot "$MAX_BOOT_MINUTES" '
[.droplets[] | select(.status != "off")
| select(.status != "new" or (now - (.created_at | fromdateiso8601)) / 60 <= $boot)] | length'
} }
busy_runners() { busy_runners() {
@@ -83,22 +122,59 @@ busy_runners() {
| jq --arg l "$LABEL" '[.runners[] | select(.busy) | select(any(.labels[]; .name == $l))] | length' | jq --arg l "$LABEL" '[.runners[] | select(.busy) | select(any(.labels[]; .name == $l))] | length'
} }
# --- capacity --------------------------------------------------------------
# "size region" lines to try, best first: SIZES order, then REGIONS order,
# then every other region where DigitalOcean lists the size in stock.
candidates() {
local page=1 response count catalog=""
while :; do
response=$(do_api "sizes?per_page=200&page=$page") || return 1
count=$(jq -er '.sizes | arrays | length' <<< "$response") || return 1
catalog+=$(jq -c '.sizes[]' <<< "$response")$'\n'
(( count == 200 )) || break
((page += 1))
done
jq -rs --arg sizes "$SIZES" --arg regions "$REGIONS" '
($regions | split(" ") | map(select(length > 0))) as $pref
| INDEX(.slug) as $by
| $sizes | split(" ") | map(select(length > 0)) | .[]
| . as $size | $by[$size] // {} | select(.available == true)
| .regions as $in
| (($pref | map(select(. as $r | $in | index($r)))) + ($in - $pref))[]
| "\($size) \(.)"' <<< "$catalog"
}
# --- create ---------------------------------------------------------------- # --- create ----------------------------------------------------------------
# Built once per tick by the first create; a refused pair is dropped from it.
CANDIDATES=""
create_droplet() { create_droplet() {
local token userdata name body local token userdata name size region body response
[[ -n $CANDIDATES ]] || CANDIDATES=$(candidates) || return 1
token=$(gh_api "repos/$REPO/actions/runners/registration-token" -X POST | jq -r .token) token=$(gh_api "repos/$REPO/actions/runners/registration-token" -X POST | jq -r .token)
userdata=$(sed -e "s|__REPO__|$REPO|g" -e "s|__RUNNER_TOKEN__|$token|g" \ userdata=$(sed -e "s|__REPO__|$REPO|g" -e "s|__RUNNER_TOKEN__|$token|g" \
-e "s|__RUNNER_LABELS__|$LABEL|g" -e "s|__RUNNER_VERSION__|$RUNNER_VERSION|g" \ -e "s|__RUNNER_LABELS__|$LABEL|g" -e "s|__RUNNER_VERSION__|$RUNNER_VERSION|g" \
-e "s|__SSH_KEYS_JSON__|$SSH_KEYS_JSON|" "$CLOUD_INIT") -e "s|__SSH_KEYS_JSON__|$SSH_KEYS_JSON|" "$CLOUD_INIT")
name="$TAG-$(date +%s)-$RANDOM" name="$TAG-$(date +%s)-$RANDOM"
body=$(jq -n --arg name "$name" --arg region "$REGION" --arg size "$SIZE" --arg image "$IMAGE" \ while read -r size region; do
--arg tag "$TAG" --arg ud "$userdata" \ [[ -n $size ]] || continue
'{name:$name, region:$region, size:$size, image:$image, tags:[$tag], user_data:$ud, monitoring:false}') body=$(jq -n --arg name "$name" --arg region "$region" --arg size "$size" --arg image "$IMAGE" \
log "creating $name ($SIZE)" --arg tag "$TAG" --arg ud "$userdata" --argjson keys "$DO_SSH_KEYS" \
do_api droplets -X POST -d "$body" | jq -r '"created droplet \(.droplet.id)"' '{name:$name, region:$region, size:$size, image:$image, tags:[$tag], user_data:$ud, ssh_keys:$keys, monitoring:false}')
log "creating $name ($size in $region)"
if response=$(do_api droplets -X POST -d "$body"); then
jq -r '"created droplet \(.droplet.id)"' <<< "$response"
return 0
fi
log "$size in $region refused: $(jq -r .message <<< "$response" 2>/dev/null || echo "$response")"
CANDIDATES=$(grep -Fvx "$size $region" <<< "$CANDIDATES" || true)
done <<< "$CANDIDATES"
# Every size refused everywhere: the rest of this tick's creates would be too.
log "no size in '$SIZES' can be created in any region"
return 1
} }
controller_tick() { controller_tick() {
CANDIDATES=""
reap reap
local queued live busy available need room local queued live busy available need room
queued=$(queued_jobs) queued=$(queued_jobs)
+7 -2
View File
@@ -14,7 +14,10 @@
# Operators can reach a builder by key while it lives; it powers off after # Operators can reach a builder by key while it lives; it powers off after
# one job anyway. Keys are substituted by the controller (__SSH_KEYS_JSON__). # one job anyway. Keys are substituted by the controller (__SSH_KEYS_JSON__).
# Never by password: DigitalOcean sets and emails one when no account key is
# attached, and its vendor data turns password logins on to match.
disable_root: false disable_root: false
ssh_pwauth: false
chpasswd: chpasswd:
expire: false expire: false
ssh_authorized_keys: __SSH_KEYS_JSON__ ssh_authorized_keys: __SSH_KEYS_JSON__
@@ -45,6 +48,10 @@ write_files:
content: | content: |
#!/bin/bash #!/bin/bash
set -euo pipefail set -euo pipefail
# Power off after the one job, and also when the download or the
# registration fails: the controller deletes powered-off droplets, but
# counts a running one as a runner still booting until MAX_AGE_MINUTES.
trap 'sudo poweroff' EXIT
cd /home/runner cd /home/runner
mkdir -p actions-runner && cd actions-runner mkdir -p actions-runner && cd actions-runner
arch=$(uname -m); [[ $arch == x86_64 ]] && arch=x64 arch=$(uname -m); [[ $arch == x86_64 ]] && arch=x64
@@ -58,8 +65,6 @@ write_files:
--labels "__RUNNER_LABELS__" \ --labels "__RUNNER_LABELS__" \
--replace --replace
./run.sh ./run.sh
# One job done. Power off; the controller deletes powered-off droplets.
sudo poweroff
runcmd: runcmd:
# With no account ssh key attached, DO expires root's password, and sshd # With no account ssh key attached, DO expires root's password, and sshd
+80
View File
@@ -0,0 +1,80 @@
# How a package builds
`bin/build` is the one builder: PR builds, rebuilds at merge and local builds
all call it.
## Dependency resolution
Within one `bin/build` run, the build system handles dependencies between the
packages it was asked to build. CI builds each package in its own job, so a
PR's packages do not see each other's fresh builds; they resolve from `edge`.
1. Plans dependency order once, including `depends`, `makedepends`,
`checkdepends`, and their architecture-specific arrays.
2. Builds each package in a fresh container. Installed packages and changes
to the container's system files cannot carry over to the next build.
3. Shares successful artifacts through the temporary `[omarchy-build]` repo,
installing newly built prerequisites in each consumer's container.
4. Blocks consumers of a failed prerequisite while continuing independent
builds.
Example: If `aether` depends on `hyprshade`, `hyprshade` is built first.
Isolation also lets the release and dev Omarchy pairs build in the same run:
Flea can install `omarchy` without preventing `omarchy-dev` from installing
its conflicting settings package in a different container.
Pacman downloads are cached under `cache/pacman/<channel>/<arch>/` across
containers and runs. The installed package database is never shared. Each
container updates its base system before resolving build dependencies, so a
cached builder image cannot cause a partial system upgrade. The existing
`OMARCHY_KEEP_BUILD_WORKSPACE`, `OMARCHY_SKIP_BUILDER_IMAGE`, and
`OMARCHY_DEFER_RUNTIME_DEPS` flags retain their behavior.
`tests/build-isolation.sh` exercises conflicting package pairs, failed
prerequisites, resumed builds, cache replacement, and deferred dependencies
using real containers and pacman transactions. It uses the prepared builder
image, or an image named by `TEST_BUILDER_IMAGE`; CI builds the small fixture
image in `tests/build-isolation.Dockerfile`.
## Daily builder images
`Refresh builder images` builds fresh `edge` environments daily at 04:23 UTC,
when their inputs change on `master`, and on manual dispatch. x86_64 and
aarch64 build on native GitHub-hosted runners, without occupying the DO
package-builder pool. Each candidate must pass `tests/build-isolation.sh`,
including real package builds, before publication to
`ghcr.io/omacom/omarchy-pkg-builder`. Only `master` in this repository can
publish; PR workflows cannot replace the shared images.
PRs that change image inputs also build and test both candidates on native
runners, with a read-only token and no registry publication.
The compatibility tag contains the architecture, mirror, and a hash of the
entire `build/` context, including executable bits and symlink targets but
excluding checkout timestamps and ownership. This deliberately invalidates
images when mounted build scripts change too. `v1` identifies the image build
contract; change it if the invocation or compatibility rules change. Each
successful refresh also gets a run-specific tag for diagnosis and rollback.
A failed build, isolation test, or push leaves the previous compatible image
selected. Scheduled builds use `--pull --no-cache` so unchanged Dockerfiles
still pick up fresh Arch packages.
To build and test a candidate locally:
```bash
bin/builder-image key --arch x86_64 --mirror edge
bin/builder-image build --arch x86_64 --mirror edge --tag builder-candidate:test --fresh
CONTAINER_ENGINE=docker TEST_BUILDER_IMAGE=builder-candidate:test tests/build-isolation.sh
```
The workflow uses its repository `GITHUB_TOKEN` with `packages: write`; no
registry PAT is needed. **One-time setup when the image package is first
created:** GHCR
creates the package private. In the `omacom/omarchy-pkg-builder` package
settings, change visibility to **Public**, then rerun the failed refresh job.
The workflow checks anonymous registry access before advancing the compatible
tag, so fork PRs will not be directed to an image they cannot pull. Subsequent
refreshes preserve that package visibility.
`publish.yml` and `unpublish.yml` pull the compatible image to sign and update
databases. Package build jobs still build their own image on the runner.
+44
View File
@@ -0,0 +1,44 @@
# Rebuild triggers
```bash
bin/sync-rebuilds # Bump every package whose dependencies moved
bin/sync-rebuilds quickshell-git # Update specific packages
bin/sync-rebuilds --self-test # Run the regression tests
```
Some packages have to be rebuilt when something they link against changes, even though nothing in their own source moved. A Qt private-API consumer is the usual case: `Qt_6_PRIVATE_API` symbols are not covered by the soname, so a qt6-base point release can leave an installed binary unable to resolve a symbol at startup, and pacman upgrades Qt out from under it because the dependency is unversioned. The package still builds from the same git commit, so its version alone would not trigger a rebuild.
A package names those dependencies in `.omarchy/package.json`:
```json
{ "source": "local", "rebuild_on": ["qt6-base", "qt6-declarative", "qt6-wayland"] }
```
`bin/sync-rebuilds` reads each named package's version from the official
repositories for every architecture a merge builds (`CI_ARCHES` in
`helpers/paths.sh`, both by default) that the package supports and compares
it to `rebuilt_against`. Records are kept per architecture because Arch and
Arch Linux ARM can carry different dependency versions. pkgrel is bumped once
when any recorded version moves; that one source revision is then built for
each architecture by the PR that carries the bump.
The bump is the point of the command, and it has to land in git rather than in the builder. A rebuild that reuses the published version string produces a package pacman will never offer anyone, so rebuilding without a version change would ship nothing. Bumping pkgrel needs no other change: the PR that carries the bump builds the package, and the merge publishes it.
The bumped version is checked against the published one as well as the checked-in one, and refused when pacman would not order it higher. The checked-in version is not the floor; what a user already has is, and a checkout that has fallen behind the repository can otherwise be bumped to something that loses to the package it means to replace. That check is skipped with a warning when the published database cannot be read.
x86_64 versions are read from the local pacman database, so the workflow runs
in an Arch container pointed at `mirror.omarchy.org`, the same mirror as the
x86_64 builder. aarch64 versions are read directly from the live Arch Linux ARM
repository database, which is also what the ARM builder uses. Testing and
staging repositories do not count. A legacy flat `rebuilt_against` record is
read as x86_64 and is migrated naturally the next time a rebuild is needed.
A dependency this repository carries for an architecture (a recipe here that builds for edge on it, such as aquamarine on aarch64) shadows the distribution's, because the builder lists `[omarchy]` first. Its version is the recipe's, and it counts only once edge publishes that version: until then the builder still links against the previous one, so dependents are left alone for that run.
## The workflow
`sync-rebuilds.yml` runs `bin/sync-rebuilds` every 6 hours and opens one PR per
package on `auto/sync-rebuilds-<package>` with auto-merge enabled. Each PR lands
once `result`, `self-tests` and `build-isolation` pass, and the merge publishes.
A rebuild that fails stays an open red PR for a maintainer without holding back
the others.
+119
View File
@@ -0,0 +1,119 @@
# Cutting an Omarchy release
The release train runs from `bin/omarchy-release`. Nothing in the GitHub
workflows promotes a channel: its channel moves and RC builds were written to
execute on the old repository host, which is abandoned (see
[repository-host.md](repository-host.md)), against a local copy of the
published tree.
> `rc` and `ship` build the release pair with `bin/repo release` and move
> channels with `bin/repo advance`, on the machine that holds the published
> tree. No workflow replaces them yet. That machine's tree has to match the
> bucket first: `advance` reads the source channel from local disk.
`bin/omarchy-release` is the front door. A release train has three human
moments, each one command — and bare `omarchy-release` observes reality
(branches, pins, published channels, tags) and walks you to the next one:
```bash
bin/omarchy-release # shepherd: status + guided next step
bin/omarchy-release start 4.0.2 # open the train: branch v4-0-2 + staging PR
bin/omarchy-release pick # choose merged PRs to cherry-pick (multi-select)
bin/omarchy-release rc # publish the next 4.0.2rcN to the rc channel
bin/omarchy-release ship # tag, final pins, promote rc → stable,
# draft GitHub release, ISO, website — one swoop
bin/omarchy-release doctor # verify credentials/connections up front
```
Versions are inferred from branch names (`v4-0-2` ⇒ `4.0.2rcN` ⇒ tag
`v4.0.2`); `start` is the only place a version is typed. Every command is
idempotent — re-runs skip whatever is already done. `ship` refuses to promote
a commit no RC was cut from. One exception: `ship` carries on past a failed
ISO or website step, and a later `ship` exits once stable holds the version
and the GitHub release exists. Finish a failed ISO or website step by hand.
## Promoting a channel
Ship the tested rc channel to stable (copies packages + signatures, then
cleans, rebuilds the database, and syncs):
```
bin/repo advance --from rc --to stable --arch all
```
Open a release train that ships new edge packages by carrying edge into rc
first:
```
bin/repo advance --from edge --to rc --arch all
```
`advance` moves x86_64 alone unless given `--arch`. `--arch all` covers the
architectures in `PUBLISHED_ARCHES` (`helpers/paths.sh`), x86_64 only by
default; set `OMARCHY_ARCHES="x86_64 aarch64"` on the machine that runs it to
promote both. The variable is not carried over when `bin/repo` forwards a
command over ssh, and `omarchy-release ship` promotes through the same path. It refuses to
carry a package into a channel its `channels` metadata excludes.
## The pin engine
The release pair is marked `"pinned": true`. It is built for `rc` only from
the `rc` branch worktree, with `OMARCHY_RC_PINS=1` set (`omarchy-release rc`
sets it), and never built for `stable`: stable gets it by promotion. A merge to
`master` therefore cannot overwrite an RC in flight.
The `omarchy` and `omarchy-settings` packages are released as a pair, always
built from the same upstream commit of basecamp/omarchy.
**Use `bin/omarchy-release`** — it drives the whole train across all four repositories and
calls the pin engine below for you.
`bin/omarchy-pkgs` is that engine, available directly for one-off pins and
debugging. It rewrites both PKGBUILDs in lockstep (same `_tag`/`_commit`/
`pkgver`/`sha256sums`), validates ordering with `vercmp`, commits, and pushes
the current branch. Driven by `omarchy-release` it pins to the release branch
on the `rc` branch and orders against the rc channel; invoked directly it
targets the current branch and edge.
```bash
bin/omarchy-pkgs release v4.0.0 # Final release from the upstream v4.0.0 tag
bin/omarchy-pkgs release rc v4.0.0 # Newest upstream v4.0.0-rcN tag -> 4.0.0rcN
bin/omarchy-pkgs release beta v4.0.0 # Same for beta (alpha also supported)
bin/omarchy-pkgs release latest # Newest upstream tag, rc/beta included (prompts)
bin/omarchy-pkgs release rc # Untagged RC from the quattro tip, auto-numbered
bin/omarchy-pkgs release --commit abc123 --base 4.1.0 # Untagged RC from a commit
bin/omarchy-pkgs release ... --dry-run # Show the plan; write nothing
bin/omarchy-pkgs release ... --no-push # Full flow, local commit only (testing)
bin/omarchy-pkgs self-test # Version normalization + ordering tests
```
## Versioning rules
- Finals are `X.Y.Z`; pre-releases are `X.Y.ZalphaN` / `X.Y.ZbetaN` /
`X.Y.ZrcN` in the **attached** form only. pacman's vercmp orders
`4.0.0alpha1 < 4.0.0beta1 < 4.0.0rc1 < 4.0.0`, but separator forms
(`4.0.0.rc1`, `4.0.0_rc1`) sort **after** `4.0.0` and would strand users on
the pre-release — the tooling normalizes upstream tags (`v4.0.0-rc1`,
`v4.0.0-rc.1`, ...) to the attached form and refuses anything it cannot
normalize. Upstream tags are cut on the quattro branch.
- `pkgrel` resets to 1 on every version change. Bump `pkgrel` by hand only to
repackage the same source.
- `epoch` is never set by tooling. It is sticky forever; adding one is a
human decision of last resort.
## Where releases land
- **RCs build for the rc channel.** RC testers and RC ISO installs run the real
`omarchy` package against the package set stable users will actually get.
Stable never sees an rc version; rc testers upgrade rc1 → rc2 → final
naturally.
- **Finals build into rc, then the whole channel promotes.** After testing, the
ship step promotes the exact tested artifacts (packages and signatures)
forward:
```bash
bin/repo advance --from rc --to stable --arch all
```
Neither package is on the `fast` ring, and `bin/omarchy-pkgs` never touches
stable — promotion is always this explicit step.
+586
View File
@@ -0,0 +1,586 @@
# The repository host (abandoned)
**This pipeline is abandoned.** Packages are built and published by GitHub
workflows: see the [README](../README.md). Don't follow this page to publish
anything.
The package pipeline used to run on one machine that built, signed and
published every channel on a timer. This page is kept as the reference for
the tooling that was written for it and is still in the tree: `bin/repo`, the
units in `systemd/`, and the host side of the release train
([releases.md](releases.md)).
**That tooling writes to the same bucket as CI.** If you run any of it
(`release`, `sync`, `promote`, `push`, `deploy`, `advance`, `remove`), these
rules apply:
- Do not run `bin/repo release`, `sync`, `push` or `deploy` for a package CI
publishes. Merge a PR, or dispatch `publish.yml`, instead.
- Keep the `omarchy-auto-release-*` and `omarchy-check-versions` timers
disabled while CI publishes the same channels. Two writers on one channel
database overwrite each other.
- Never pass `--prune` to `bin/repo sync`. The host's tree does not hold what
CI published, and `--prune` deletes remote files the local tree lacks.
- Never rebuild a version that is already published. Bump `pkgrel`.
## Setting up a host
> Abandoned. Kept as reference for the tooling; don't follow it to publish.
### aarch64 under emulation
The repository host builds every architecture it publishes on the same
machine. A foreign architecture runs under QEMU user emulation, which
`bin/build` checks by actually running a container for the target platform.
Rootful Docker registers QEMU on first use. Rootless Podman uses the host's
registration and prints the one-time Arch setup commands when it is missing or
lacks the credential flag required by `sudo` inside the builder:
```bash
# Verify
podman run --rm --platform linux/arm64 docker.io/library/alpine:latest uname -m
# Should output: aarch64
```
**Note**: emulated builds are much slower than native ones.
## Published architectures
> Abandoned. Kept as reference for the tooling; don't follow it to publish.
`helpers/paths.sh` names the architectures the host's scheduled pipeline and
release train work on. CI does not read it:
```bash
PUBLISHED_ARCHES="${OMARCHY_ARCHES:-x86_64}"
```
That list drives the whole scheduled pipeline. `check-versions` compares
PKGBUILDs against each architecture's channel databases and writes one queue
file per channel and architecture (`.sync-needed-<channel>-<arch>`);
`auto-release <channel>` works through the queues one architecture at a
time, each with its own backoff (`.build-failed-<channel>-<arch>`), so a
failing build on one architecture never holds up the other; and the release
train advances channels with `--arch all`: it takes one host-wide lock and
verifies every architecture's source database before moving any of them. The
first entry is the reference architecture the release train observes channels
through. Rerunning the same advance after a failed remote sync completes it
safely.
Adding an architecture to the scheduled pipeline is therefore one checked-in
change to that list: the next `check-versions` tick queues everything the new
architecture lacks, and the next `auto-release` tick starts building it. CI
does not read this list: it builds the architectures in `CI_ARCHES`, both by
default. For a one-off run, override it directly:
```bash
OMARCHY_ARCHES=x86_64 bin/check-versions
OMARCHY_ARCHES=aarch64 bin/check-versions
OMARCHY_ARCHES="x86_64 aarch64" bin/check-versions
```
The builder image bootstraps
`omarchy-keyring` from the x86_64 tree for every architecture, so the first
build of a new architecture does not depend on a repository that only it can
create.
## Release in one command
> Abandoned. Kept as reference for the tooling; don't follow it to publish.
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
# Stable Mirror
bin/repo release --mirror stable
# ARM64
bin/repo release --arch aarch64
# Build one package (still skipped when its version is already published)
bin/repo release --package omarchy-nvim
# Show what would build without signing/promoting/syncing
bin/repo release --dry-run
```
### Step-by-Step
```bash
bin/repo build # Build (unsigned)
bin/repo sign # Sign packages
bin/repo promote # Copy to production
bin/repo clean # Remove old versions
bin/repo update # Update database
bin/repo sync # Sync to remote
```
## Building Heavy Packages Locally
> Abandoned. Kept as reference for the tooling; don't follow it to publish.
Large packages build faster on a local machine than on the server. Build them
here, then hand the artifacts to the repository host, which signs and publishes
them:
```bash
bin/repo deploy --package nvidia-580xx-utils # Build here, publish from the host
```
`deploy` is `build` followed by `push`. The two steps are also available
separately when a build needs inspecting before it ships:
```bash
bin/build --package nvidia-580xx-utils # Build on the fast machine
bin/repo push --package nvidia-580xx-utils # Upload + publish on the host
```
`push` uploads to the host's `build-output/`, verifies checksums, and runs
`bin/upload-prebuilt` there. Do not publish from a local checkout instead: a
local machine holds neither the complete repository nor a signing key.
**Name the package.** `build` plans against the local repository database, or
against the public channel's when there is none. `deploy` refuses to run
unscoped when the local database is missing. Unscoped builds belong on
the repository host, where `bin/repo release` does the same job against a real
database.
Every other `bin/repo` command — `sign`, `promote`, `update`, `clean`,
`advance`, `remove`, `sync`, `release` — works on the published tree. Run them
from anywhere: with a repository host configured `bin/repo` forwards them over
ssh to run there (see [Build trigger and the build host](#build-trigger-and-the-build-host)),
and `--local` forces execution on the current machine. `bin/repo build`
forwards too; call `bin/build` directly, or pass `--local`, to build on this
machine. `push` and `deploy` always run locally.
## Commands
> Abandoned. Kept as reference for the tooling; don't follow it to publish.
### Global Flags
These flags can be used with all commands:
- `--mirror <edge|rc|stable>`: Selects the channel (default: `edge`).
- `--local`: Run on this machine instead of forwarding to the repository host.
- `--arch <x86_64|aarch64>`: Selects the target architecture (default: `x86_64`).
### Build
```bash
bin/repo build # All packages (x86_64, edge)
bin/repo build --arch aarch64 # ARM64
bin/repo build --mirror rc # Release-candidate channel
bin/repo build --mirror stable # Stable channel
bin/repo build --package yay cursor-bin # Specific packages
bin/repo build --dry-run # Show what would build
```
**Output**: Unsigned `.pkg.tar.zst` in `build-output/`. Only builds packages whose version differs from what the repository holds.
Use `--dry-run` to show the build plan without running `makepkg`.
### Sign
```bash
bin/repo sign
```
Reads the GPG key and passphrase from the environment and signs all packages in `build-output/`. Supports `--arch` and `--mirror`.
### Promote
```bash
bin/repo promote # Copy to production
bin/repo promote --arch aarch64 # ARM64
bin/repo promote --dry-run # Preview
```
Copies signed packages from `build-output/` → `pkgs.omarchy.org/`.
### Clean
```bash
bin/repo clean # Keep 2 versions
bin/repo clean --keep 3 # Keep 3 versions
bin/repo clean --dry-run # Preview
```
Removes old package versions from the file system. **Does not update the database.**
### Update
```bash
bin/repo update # Update database
```
Updates the repository database (adding the newest version of each package). Run this after `promote` or `clean`.
### Sync Repository
```bash
bin/repo sync # Sync current arch/mirror
bin/repo sync --mirror stable # Sync stable
bin/repo sync --arch aarch64 # Sync ARM64
bin/repo sync --skip-prod-check # No confirmation
```
Syncs package repositories to the remote server using rclone based on the configured mirror and architecture.
**Uploads are additive.** A local tree is not authoritative about what belongs on
the remote — `pkgs.omarchy.org/` is gitignored, and packages built on another
machine exist only there — so sync never deletes by default. To take a package
out of the channels, use the `unpublish.yml` workflow.
For the same reason sync refuses to publish a repository database built from a
tree holding fewer packages than the remote database already lists. The database
is what pacman resolves against, so a partial one hides every package it does not
know about even though the files are still on the mirror. Use `bin/repo push` to
publish packages built on another machine.
### Deploy
```bash
bin/repo deploy --package nvidia-580xx-utils # Build locally, publish from the host
bin/repo deploy --host root@example.com # Point at a specific repo host
bin/repo deploy --dry-run # Show the plan, change nothing
```
Runs `build` then `push` in one command. The repository host is resolved before
the build starts, so a missing `--host` fails immediately rather than after a long
compile.
### Push to the Repository Host
```bash
bin/repo push # Push everything in build-output
bin/repo push --package nvidia-580xx-utils # Push one package
bin/repo push --mirror stable --arch aarch64 # Pick mirror and architecture
bin/repo push --host root@example.com # Override the repo host
bin/repo push --dry-run # Show the plan, transfer nothing
```
Uploads packages from `build-output/` to the repository host and publishes them there
with `bin/upload-prebuilt` (sign → promote → update → sync). Use it when a package
is quicker to build on a local machine than on the server.
Publishing happens on the host rather than locally for two reasons: the host
has a GPG signing key (CI has its own copy in the `publish` environment), and
it holds the repository tree that its database and sync are built from. Local
machines therefore need no secrets.
The host comes from `--host`, `$OMARCHY_REPO_HOST`, then `.repo-host`. The
setting is named for the repository rather than for building, which happens
wherever you like. The same setting tells `bin/omarchy-pkgs release` which host
to poke after a release push.
`--package` means the same thing as it does to `build`: a pkgbase, whose every
output ships together. Pushing `nvidia-580xx-utils` carries `nvidia-580xx-dkms`
and `opencl-nvidia-580xx` with it, because that is what the build produced. An
output's own name still selects just that one, for publishing a single package
on purpose. Omit `--package` to push everything built.
Publishing signs and promotes everything staged on the host, not just what this
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.
### Other host commands
```bash
bin/repo advance --from rc --to stable # Ship the tested rc channel to stable
bin/repo advance --from edge --to rc # Open a train: carry edge forward into rc
bin/repo advance --from edge --to rc --package x # Deliberate single-package advance
bin/repo advance --from rc --to stable --dry-run # Preview without changing files
bin/repo bootstrap-rc # One-time initial rc seed from stable
bin/repo timers # Timer schedule, last runs, queues, backoff, lock
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/repo remove <package> # Remove package (host workflow; CI uses unpublish.yml)
bin/clean-docker # Clear Docker images/cache (forces fresh rebuild)
```
## Build trigger and the build host
> Abandoned. Kept as reference for the tooling; don't follow it to publish.
Release commands run from anywhere. `bin/repo` is a **remote control**: with a
repository host configured, every command that operates on the published tree
(`release`, `build`, `sign`, `promote`, `update`, `clean`, `advance`,
`bootstrap-rc`, `remove`, `sync`, `migrate`) executes ON the host over ssh —
the exact same code, run where the tree lives, so ssh'ing in and running the
same commands by hand behaves identically. Pass `--local` to force execution
on the current machine. `list`, `push`, `deploy`, and `setup` never forward
(`push`/`deploy` exist precisely to move local builds *to* the host).
Without a configured host, commands run locally — which on the build host
itself (no `.repo-host` there) is exactly right, and elsewhere the exact
commands to run are printed by the orchestrator, with the 5-minute
auto-release timer as the backstop.
The host setting is any destination `ssh` accepts, resolved in this order:
1. `--host <dest>`, for `push`, `deploy` and `bin/omarchy-release`
2. `OMARCHY_REPO_HOST` environment variable
3. the git-ignored `.repo-host` file (one line; `#` comments allowed)
The server layout lives under `/root`, so the value is `root@<ip>`,
`root@<hostname>`, or — nicest — a `Host` alias from `~/.ssh/config` that
carries the user, key, and port:
```
# .repo-host
root@pkgs.example.com
```
All connections are plain ssh; nothing else is used to reach the host.
**How "this machine is the build host" is detected — and its one caveat.**
The marker is the published database living in this checkout
(`pkgs.omarchy.org/<channel>/<arch>/omarchy.db*`), the same convention the
rest of `bin/` uses. A workstation that once ran a full local
`bin/repo release` carries that marker too and would infer it is the host.
An explicitly configured destination always outranks the inference — `--host`,
`OMARCHY_REPO_HOST`, and `.repo-host` are checked first, and only when none is
set does local detection apply. So if a machine ever misidentifies itself,
writing the real host into `.repo-host` is the fix.
## Directory Structure
> Abandoned. Kept as reference for the tooling; don't follow it to publish.
```
omarchy-pkgs/
├── pkgbuilds/ # Source PKGBUILDs
│ └── package-name/
│ ├── PKGBUILD
│ └── .omarchy/
│ ├── package.json # Source/sync/release metadata
│ └── upstream.sh # Optional custom vendor release feed hook
├── build/
├── build-output/ # Unsigned packages (temporary)
│ ├── edge/ # (rc/ and stable/ alongside, each x86_64 + aarch64)
│ ├── rc/
│ └── stable/
├── pkgs.omarchy.org/ # Signed packages (production)
│ ├── .release.lock # Host-wide lock: one channel mutation at a time
│ ├── edge/ # Each channel: x86_64/ and aarch64/
│ ├── rc/
│ └── stable/
└── bin/ # CLI tools (on host)
```
## Architecture-Specific Notes
> Abandoned. Kept as reference for the tooling; don't follow it to publish.
### x86_64
- Native builds (fast)
- Mirrors: mirror.omarchy.org, rackspace, pkgbuild.com
### aarch64
- Built on the repository host like x86_64; under QEMU when the host is x86_64
- On an ARM host, package builds and the signing/database utility containers
run natively; only an explicitly requested x86_64 package build is emulated
- Uses Arch Linux ARM repositories through the same HTTPS mirror for every
channel (Arch Linux ARM publishes no dated snapshots to pin a channel's base)
- Additional repos: `[alarm]`, `[aur]`
- Same workflow, just add `--arch aarch64`; the scheduled pipeline runs it
automatically once `aarch64` is in `PUBLISHED_ARCHES`
- Packages whose `arch=()` lacks `aarch64` are skipped, not failed
### Building for Both Architectures
```bash
# Build x86_64
bin/repo release --package myapp
# Build aarch64
bin/repo release --arch aarch64 --package myapp
# Sync both
bin/repo sync
bin/repo sync --arch aarch64
```
## Version Management
> Abandoned. Kept as reference for the tooling; don't follow it to publish.
Packages are only rebuilt if:
- PKGBUILD version differs from the repository version
- Package doesn't exist in production
A package that has to be rebuilt because something underneath it changed is handled by turning that into a version change: `bin/sync-rebuilds` bumps pkgrel when a dependency named in `rebuild_on` moves.
## Scheduled releases on the host
> Abandoned. Kept as reference for the tooling; don't follow it to publish.
Four systemd units in `systemd/` drive unattended releases from the host.
All four units run **every 5 minutes**, staggered by a minute each, so a push
reaches the mirror in minutes rather than hours:
1. **check-versions** (`*:0/5`): Pulls latest from git, compares PKGBUILD versions to published versions for every published architecture, creates one state file per channel and architecture if builds are needed
2. **auto-release-edge** (`*:1/5`): For each published architecture with a state file, builds all edge packages that need updates
3. **auto-release-rc** (`*:2/5`): Builds fast-ring packages for rc, from the main checkout like the other two — natively in the rc image, not copied from another channel. The pinned release pair is built separately by `omarchy-release rc` in the `rc` branch worktree
4. **auto-release-stable** (`*:3/5`): If a state file exists, builds `release_ring=fast` packages for stable
That cadence is only safe because of three guards:
- **No overlap.** Every channel-mutating run takes a host-wide lock
(`pkgs.omarchy.org/.release.lock`). Scheduled runs take it
**non-blocking**: if a build is already going, the tick exits immediately
instead of queuing. Waiting would stack one stalled process per tick behind
a long build and stampede when it finished. Manual commands still wait, as
an operator expects. `check-versions` takes it too — its `git pull` would
otherwise swap PKGBUILDs out from under a running build.
- **Backoff on failure.** A failed release records the attempt in
`.build-failed-<channel>-<arch>` and backs off exponentially — 10m, 20m, 40m, up to
a 6h ceiling — instead of rebuilding the same broken tree every 5 minutes.
**Any new commit clears the backoff immediately**, since a push is the most
likely fix. Clear it by hand with
`rm /root/.state/.build-failed-<channel>-<arch>`.
- **Quiet when idle.** With nothing queued a tick exits without output, so the
journal shows the runs that mattered rather than 288 no-ops a day.
`bin/repo timers` reports all of this: schedules, last results, what is
queued, what is failing and when it will retry, and whether the lock is held.
## Build reports
> Abandoned. Kept as reference for the tooling; don't follow it to publish.
Every release run reports to Basecamp Campfire — not just failures, so a push
can be followed all the way to the mirror without watching the host:
| Event | Message |
|---|---|
| 🔨 Started | channel, arch, host, the commit being built, and which packages are queued |
| ✅ Published | the packages and versions that went out, duration, and the channel URL |
| 📦 Promoted | what `advance` moved between channels (including the rc bootstrap) |
| 📦 Nothing to publish | a queued run built nothing — see below |
| 🔴 Failed | which step failed, the commit, and the last 25 log lines |
Reports are tied to release *runs*, not timer ticks: a tick with nothing
queued exits silently, so a quiet day is a quiet chat. That is what makes the
"nothing to publish" report meaningful rather than routine — a release only
runs when the version check queued work, so building nothing means the check
and the builder disagree about what is out of date. It names the packages that
were queued but not built, which is where to start looking.
By default everything posts to `BASECAMP_CHATBOT_URL`, the same chat the
sync workflows use — one variable, nothing extra to configure.
If release traffic starts drowning that chat, give it its own: create a second
Basecamp chat, add a chatbot integration to it, and export its lines URL
alongside the existing one in `/root/.omarchy/build-credentials`:
```bash
export OMARCHY_RELEASE_CHATBOT_URL="https://3.basecamp.com/<account>/integrations/<key>/buckets/<project>/chats/<chat>/lines"
```
Release reports then go there while the sync workflows keep posting to
`BASECAMP_CHATBOT_URL`. With neither set, reports are silently skipped.
`bin/setup` reports which of the three cases applies.
Check on all of it with `bin/repo timers` — schedule, each unit's last run and
whether it succeeded, what is queued, whether a release is running right now,
and any failed units. Like the other host commands it forwards over ssh, so
the build box's state is one command away from any machine:
```bash
bin/repo timers # runs on the repository host when one is configured
bin/repo timers --local # inspect this machine instead
```
State files are stored in `/root/.state/`:
- `.sync-needed-<channel>-<arch>` — the packages queued for that channel and
architecture, one per line; the release run reads them to name what it is
building
- `.build-failed-<channel>-<arch>` — consecutive failure count, timestamp, and
the commit it failed on (drives the backoff; removing it forces a retry)
Legacy files without the architecture suffix are consumed once as x86_64
state, so upgrading the host does not lose an in-flight build.
## Schedule (America/New_York)
> Abandoned. Kept as reference for the tooling; don't follow it to publish.
| Minute of every hour | Action |
|------|--------|
| :00, :05, :10, … | check-versions (git pull + creates state files) |
| :01, :06, :11, … | auto-release-edge |
| :02, :07, :12, … | auto-release-rc |
| :03, :08, :13, … | auto-release-stable |
Each release unit is a no-op when its channel has nothing queued, another run
holds the lock, or the channel is in failure backoff.
## Installation
> Abandoned. Kept as reference for the tooling; don't follow it to publish.
```bash
ssh root@<host> 'cd /root/omarchy-pkgs && bin/setup --skip-timers'
```
`bin/setup` installs the dependencies, ensures Docker is running and creates
the state directory. Without `--skip-timers` it also installs and enables the
release timers, which must stay off while CI publishes. It works on
Debian/Ubuntu and on Arch, and is idempotent, so run it again whenever a
dependency is added.
The host does not need to be Arch: makepkg, repo-add and package signing all
run inside containers, so it needs only Docker, rclone, bsdtar, jq, git and
rsync. Docker is left alone when it already works, rather than replacing a
working installation from Docker's own repository with the distribution's.
```bash
bin/repo setup --check # Report what is missing, change nothing
bin/repo setup --skip-timers # Prepare the host without the release timers
```
Signing credentials (`/root/.omarchy/build-credentials`) and the rclone remote
hold secrets, so setup reports on them rather than creating them.
## Management
> Abandoned. Kept as reference for the tooling; don't follow it to publish.
```bash
# Everything at a glance: schedules, last runs, queues, backoff, lock
bin/repo timers # forwards to the host when one is configured
# Manual trigger (edge, rc, stable)
systemctl start omarchy-check-versions.service
systemctl start omarchy-auto-release-edge.service
# View logs
journalctl -u omarchy-auto-release-edge.service -n 50
# Clear a channel stuck in failure backoff (a new commit also clears it)
rm /root/.state/.build-failed-edge-x86_64
# Release lock left behind by a killed run (bin/repo timers shows if it is live)
rm /root/omarchy-pkgs/pkgs.omarchy.org/.release.lock
```
With the timers enabled, `check-versions` pulls the main checkout on its
5-minute tick. With them disabled, nothing does: `bin/repo` pulls before a
command it forwards, and a command run on the host itself needs a `git pull`
first. The checkout must be on `master`.
+194 -25
View File
@@ -73,13 +73,21 @@ because its published history counted every commit and the number must never
go down. go down.
`min_release_age` holds a branch tip until its commit timestamp is old enough. `min_release_age` holds a branch tip until its commit timestamp is old enough.
A fresh tip leaves the existing pin alone; the watch never walks backward to A fresh tip falls back to the newest commit on the branch's first-parent
an older commit. This uses Git's committer date, not the time a commit was history that has cleared the window, so a busy branch still advances; it never
pushed. `BYPASS_MIN_RELEASE_AGE=1` bypasses the hold. selects a commit from a merged side branch. This uses Git's committer date, not
the time a commit was pushed, measured from when the run cloned the branch so
packages pinned from it agree. `BYPASS_MIN_RELEASE_AGE=1` bypasses the hold.
Packages marked `"auto_merge": true` ride the unattended lane Packages marked `"auto_merge": true` ride the unattended lane
(`track-branches.yml`) instead of the reviewed sync PR: their bump PR is opened (`track-branches.yml`), checked hourly instead of every 6 hours. Every lane
and auto-merged as soon as the build checks pass. `bin/sync-upstream --lane opens its PRs through `package-prs.yml` and auto-merges them as soon as the
build checks pass. Each package gets its own
PR on `auto/track-branches-<package>`, and packages that pin the same branch
share one, so a package whose build fails holds back only itself. The lane works with every
provider, not only branch watches, so it also carries release feeds trusted to
ship without review: the browsers, large vendors' apps, Omacom's own projects,
and vendor binaries that hold fresh releases with `min_release_age`. `bin/sync-upstream --lane
reviewed|auto-merge|all` selects a lane; the scheduled workflows each pass their reviewed|auto-merge|all` selects a lane; the scheduled workflows each pass their
own. Packages that pin the same branch move in lockstep: if one of them fails own. Packages that pin the same branch move in lockstep: if one of them fails
to update, the run restores the others and reports the group as failed. A to update, the run restores the others and reports the group as failed. A
@@ -151,7 +159,6 @@ in `origin` and has no effect on release selection.
| `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-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) | | `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) | | `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) | | `basecamp-cli` | github | [basecamp/basecamp-cli](https://github.com/basecamp/basecamp-cli) |
| `bun-bin` | github | [oven-sh/bun](https://github.com/oven-sh/bun) | | `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) | | `claude-code` | regex | [https://downloads.claude.ai/claude-code-releases/latest](https://downloads.claude.ai/claude-code-releases/latest) |
@@ -162,21 +169,6 @@ in `origin` and has no effect on release selection.
| `dbxcli-bin` | github | [dropbox/dbxcli](https://github.com/dropbox/dbxcli) | | `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` | 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/) | | `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) | | `heroic-games-launcher-bin` | github | [Heroic-Games-Launcher/HeroicGamesLauncher](https://github.com/Heroic-Games-Launcher/HeroicGamesLauncher) |
| `hyprshade` | pypi | [hyprshade](https://pypi.org/project/hyprshade/) | | `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/) | | `lib32-nvidia-580xx-utils` | regex | [https://download.nvidia.com/XFree86/Linux-x86_64/](https://download.nvidia.com/XFree86/Linux-x86_64/) |
@@ -213,7 +205,6 @@ in `origin` and has no effect on release selection.
| `vi` | regex | [https://sources.archlinux.org/other/vi/](https://sources.archlinux.org/other/vi/) | | `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) | | `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) | | `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) | | `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) | | `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) | | `yay` | github | [Jguer/yay](https://github.com/Jguer/yay) |
@@ -221,12 +212,10 @@ in `origin` and has no effect on release selection.
## Existing manual holds ## 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`. `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. These packages were already excluded from automatic AUR updates. The migration preserves that policy.
`linux-firmware-cirrus` is a deliberate hold: a self-retiring shim that ships Arch's linux-firmware-cirrus 20260910-2 payload to stable while stable's Arch snapshot is on 20260810-2 (Dell XPS 13 DX13260 / 1028:0e54 speaker firmware). It is versioned 20260810-3 so the genuine Arch package supersedes it as soon as the snapshot advances; bumping it to the Arch version would defeat that. Delete the recipe once stable's snapshot carries linux-firmware >= 20260910.
`m1n1-aurora` and `uboot-asahi` are deliberate holds: Apple Silicon boot code, pinned by hand like `linux-aurora`, and bumped only after a cold boot on the qualification Macs. `m1n1-aurora` pins an aurora-silicon/m1n1 commit plus a local patch. `uboot-asahi` follows asahi-alarm's recipe and patch set (asahi-alarm/PKGBUILDs), which a tag watch on AsahiLinux/u-boot cannot carry. `m1n1-aurora` and `uboot-asahi` are deliberate holds: Apple Silicon boot code, pinned by hand like `linux-aurora`, and bumped only after a cold boot on the qualification Macs. `m1n1-aurora` pins an aurora-silicon/m1n1 commit plus a local patch. `uboot-asahi` follows asahi-alarm's recipe and patch set (asahi-alarm/PKGBUILDs), which a tag watch on AsahiLinux/u-boot cannot carry.
`cua-driver-bin` is a deliberate hold: Omarchy bumps it by hand, so a Cua release ships only when a maintainer has verified it. It keeps its `.omarchy/upstream.sh` hook and `min_release_age`, so lifting the hold means removing `"sync": false`. `cua-hyprland-plugin` declares no upstream source, so no automation updates it either. `cua-driver-bin` is a deliberate hold: Omarchy bumps it by hand, so a Cua release ships only when a maintainer has verified it. It keeps its `.omarchy/upstream.sh` hook and `min_release_age`, so lifting the hold means removing `"sync": false`. `cua-hyprland-plugin` declares no upstream source, so no automation updates it either.
@@ -239,3 +228,183 @@ These packages were already excluded from automatic AUR updates. The migration p
- RustDesk reads hbb_common from the release gitlink; its existing build-time dependency/toolchain checks remain in force. - 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. - 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. - Source and build compatibility still need review when upstream code changes. Direct watches remove AUR recipe churn, not the need to maintain packaging.
## How the sync PRs build
`sync-upstream.yml`, `sync-rebuilds.yml` and `track-branches.yml` each run their
sync once, split the changed recipes into one PR per package with
`.github/scripts/package-pr-groups.sh`, and hand them to `package-prs.yml`. That
pushes each PR with the PAT in `PKGS_BOT_TOKEN`, so its builds start without an
approval hold, and arms auto-merge. A push to an `auto/sync-*` or
`auto/track-branches-*` branch does not cancel the PR's build in flight: the new
build waits, then reuses every artifact the finished one uploaded.
A red PR stays open for a maintainer and holds back only its own package. A
later run updates it if upstream moves again, and closes it once it conflicts
with `master`. A run started by hand with `packages` updates the same
per-package PRs a scheduled run would.
## Declarative providers and hooks
The older provider form, still in use beside `upstream.watch`. Give a new
package an `upstream.watch`; `helpers/upstream-watch.py check` validates that
form only.
```bash
bin/sync-upstream # Update every package with an upstream hook
bin/sync-upstream openai-codex-desktop # Update specific packages
```
A package declares where its releases come from either as data or, for an
unusual feed, a small hook.
A vendor shipping tagged GitHub releases is pure data, declared as `upstream`
in `.omarchy/package.json` with no code at all:
```json
"upstream": {
"github": "jdx/mise",
"checksums": "SHASUMS256.txt",
"assets": {
"x86_64": "mise-{tag}-linux-x64.tar.xz",
"aarch64": "mise-{tag}-linux-arm64.tar.xz"
}
}
```
`checksums` names the manifest asset the vendor publishes. A vendor publishing
none sets `"digests": true` instead, and the checksums come from the SHA-256
digest GitHub's release API reports for every asset — see
`pkgbuilds/schist-bin/.omarchy/package.json`. Either way the artifacts
themselves are never downloaded.
An architecture may map to an ordered array when its PKGBUILD downloads more
than one release asset. Small versioned files outside the release assets can be
listed under `sources` and are downloaded and hashed when a new version appears:
```json
"upstream": {
"github": "owner/project",
"digests": true,
"assets": {
"x86_64": ["tool-{pkgver}-x86_64", "tool-{pkgver}-x86_64.asc"],
"aarch64": ["tool-{pkgver}-aarch64", "tool-{pkgver}-aarch64.asc"]
},
"sources": {
"any": ["https://raw.githubusercontent.com/owner/project/{tag}/LICENSE"]
}
}
```
Asset and source keys must be disjoint because each key maps to one PKGBUILD
checksum array (`any` means the unsuffixed `sha256sums`).
Repositories whose historical releases use incompatible tag schemes may set
`"latest_only": true`. The provider then considers only the newest stable
GitHub release, while retaining all validation for that release. A quarantine
will wait for that release to age instead of falling back to an older one.
`{tag}` and `{pkgver}` interpolate into asset names; a leading `v` on the tag is
stripped for `pkgver`; drafts and prereleases are ignored. Only the 100 most
recent releases are considered. The provider fails closed on anything it cannot
read — an unusable tag, timestamp, or checksum stops the sync rather than being
skipped.
Projects that publish version tags but no checksum manifest can declare the
tag repository, the exact tag shape, and every source that should be hashed:
```json
"upstream": {
"git_tags": "https://github.com/owner/project.git",
"tag_pattern": "v{pkgver}",
"sources": {
"any": ["https://github.com/owner/project/archive/refs/tags/{tag}.tar.gz"]
}
}
```
The newest matching tag is selected with pacman's `vercmp`; unrelated tags are
ignored. `tag_pattern` must contain exactly one `{pkgver}`. Source templates may
use `{tag}` and `{pkgver}`. Each expanded URL must be HTTPS and is downloaded
only when the discovered version is newer. A checked-in patch or other local
source can be included as `file:patch-name.patch`; it is hashed from the package
directory. Keys such as `any`, `x86_64`, and `aarch64` select the corresponding
`sha256sums` array.
npm packages use the same source mapping, with `{npm_tarball}` available for
the tarball named by the selected dist-tag:
```json
"upstream": {
"npm": "@scope/package",
"dist_tag": "latest",
"sources": {
"any": ["{npm_tarball}", "https://example.com/v{pkgver}/CHANGELOG.md"]
}
}
```
`dist_tag` defaults to `latest`. The registry's publication timestamp is
carried into the provider result, so `min_release_age` works for npm packages.
A vendor with a plain-text Debian `Packages` index can use it to discover the
newest exact package version, then hash immutable source URLs:
```json
"upstream": {
"debian": "https://example.com/debian/dists/stable/main/binary-amd64/Packages",
"package": "example-app",
"sources": {
"x86_64": ["https://example.com/tool-{pkgver}-x64.tar.gz"],
"aarch64": ["https://example.com/tool-{pkgver}-arm64.tar.gz"]
}
}
```
This deliberately accepts only Debian versions that are already valid Arch
`pkgver` values. Feeds needing epoch, revision, or filename translation retain
a hook. Exactly one of `github`, `git_tags`, `npm`, or `debian` may appear in a
declaration.
A timestamped provider may also declare `"min_release_age": "24h"`
(`s`/`m`/`h`/`d` suffix or bare seconds) to quarantine fresh releases until
maintainers have had time to pull a bad or compromised one. GitHub Releases and
npm provide publication times; raw git tags and Debian Packages indexes do not,
so combining either with this policy fails closed. The newest release that has
cleared the window ships, so a fast release cadence cannot starve updates. The
window is enforced
centrally: whatever reports the release must prove its age via `published_at`,
or the sync fails. A maintainer deliberately shipping inside the window runs
`BYPASS_MIN_RELEASE_AGE=1 bin/sync-upstream <package>` locally and merges the
result through a normal PR; scheduled automation never sets the bypass.
A vendor whose feed fits no convention (a Debian package index, a bare
version.txt) instead provides `.omarchy/upstream.sh`, a hook that reports the
newest upstream release as JSON on stdout — declaring both an `upstream` block
and a hook is an error:
```json
{
"pkgver": "1.2.3",
"sha256sums": { "x86_64": ["<sha256>"], "aarch64": ["<sha256>"] }
}
```
Architecture keys become `sha256sums_<arch>` in the PKGBUILD; the key `any` means
the unsuffixed `sha256sums` array, and only the arrays a hook names are touched.
An empty object (`{}`) reports no update, which is how a hook waits out a release
that has landed for one architecture but not yet the other.
When the reported version is newer than the checked-in one, `bin/sync-upstream`
rewrites `pkgver` and those checksum arrays and resets `pkgrel` to 1. A version
that is equal or older leaves the package alone, so a vendor rolling a release
back cannot walk the repository backwards.
Hooks should read checksums from whatever manifest the vendor publishes rather
than downloading the artifacts — see `pkgbuilds/openai-codex-desktop/.omarchy/upstream.sh`,
which reads OpenAI's Debian package index and never fetches the 750 MB of debs
it describes. Hooks honoring `min_release_age` receive the window as
`MIN_RELEASE_AGE_SECONDS` and report `published_at` alongside `pkgver`.
`bin/sync-upstream self-test` runs offline fixture tests over the release
selection, quarantine backstop, duration parsing, and manifest validation.
+2 -1
View File
@@ -333,7 +333,8 @@ packages_for_upstream_sync() {
# "auto_merge": true rides the unattended lane instead: its bump PR is opened # "auto_merge": true rides the unattended lane instead: its bump PR is opened
# and auto-merged by the branch tracker as soon as CI is green, which is how a # and auto-merged by the branch tracker as soon as CI is green, which is how a
# package that follows a moving branch (omarchy-dev, omasnap-git) gets rebuilt # package that follows a moving branch (omarchy-dev, omasnap-git) gets rebuilt
# without anyone clicking. The lanes are disjoint so a branch tip can never # without anyone clicking, and how trusted release feeds (browsers, large
# vendors, Omacom's own projects) ship without waiting for review. The lanes are disjoint so a branch tip can never
# hold up a reviewed vendor release, or the other way round. # hold up a reviewed vendor release, or the other way round.
package_auto_merge() { package_auto_merge() {
local pkgdir="$1" metadata local pkgdir="$1" metadata
+21 -8
View File
@@ -186,9 +186,9 @@ def matches(watch, text, extra=None, full=False):
yield candidate(watch, {**(extra or {}), **match.groupdict()}) yield candidate(watch, {**(extra or {}), **match.groupdict()})
def git_branch_tip(url, branch, tag_pattern, cache): def git_branch_tip(url, branch, tag_pattern, cache, min_age=0):
"""Describe the current tip of an upstream branch: """Describe the newest commit on an upstream branch that has cleared
commit, total count, date, and with a tag_pattern min_age: commit, total count, date, and with a tag_pattern
the newest release tag reachable from it plus the distance from that tag, the newest release tag reachable from it plus the distance from that tag,
so a branch build can be versioned <tag>.r<n>.g<sha>, above the release it so a branch build can be versioned <tag>.r<n>.g<sha>, above the release it
follows and below the next one, the way a pkgver() function would. follows and below the next one, the way a pkgver() function would.
@@ -196,20 +196,33 @@ def git_branch_tip(url, branch, tag_pattern, cache):
One blobless single-branch clone per (url, branch) per run, shared by One blobless single-branch clone per (url, branch) per run, shared by
every package that tracks it, so two recipes pinned from one clone always every package that tracks it, so two recipes pinned from one clone always
see the same commit. The clone is read with git only; nothing in it runs. see the same commit. The clone is read with git only; nothing in it runs.
select_release applies the age hold to this tip, without walking back A tip younger than min_age falls back along first-parent history, the
into history (which could select a commit from a merged side branch). commits that were themselves the branch tip, never into a merged side
branch. Holding the whole branch instead stalled a busy branch for as
long as it kept getting commits. Age is measured from when the clone was
made, so siblings synced seconds apart cannot straddle the window.
""" """
https(url) https(url)
key = hashlib.sha256(f"{url}#{branch}".encode()).hexdigest() key = hashlib.sha256(f"{url}#{branch}".encode()).hexdigest()
work = Path(cache) / f"{key}.branch.git" work = Path(cache) / f"{key}.branch.git"
cloned_at = work.with_name(f"{key}.branch.time")
if not work.exists(): if not work.exists():
scratch = work.with_name(f"{work.name}.{os.getpid()}.tmp") scratch = work.with_name(f"{work.name}.{os.getpid()}.tmp")
started = dt.datetime.now(dt.timezone.utc).isoformat()
subprocess.run(["git", "clone", "--quiet", "--bare", "--filter=blob:none", "--single-branch", "--branch", branch, url, str(scratch)], check=True) subprocess.run(["git", "clone", "--quiet", "--bare", "--filter=blob:none", "--single-branch", "--branch", branch, url, str(scratch)], check=True)
cloned_at.write_text(started)
scratch.replace(work) scratch.replace(work)
git = ["git", "-C", str(work)] git = ["git", "-C", str(work)]
commit = run([*git, "rev-parse", "HEAD"], text=True).strip() commit = run([*git, "rev-parse", "HEAD"], text=True).strip()
if not re.fullmatch(r"[0-9a-f]{40}", commit): if not re.fullmatch(r"[0-9a-f]{40}", commit):
raise ValueError("branch tip is not a commit") raise ValueError("branch tip is not a commit")
if min_age:
now = dt.datetime.fromisoformat(cloned_at.read_text().strip())
for line in run([*git, "log", "--first-parent", "--format=%H %cI", commit], text=True).splitlines():
sha, committed = line.split()
if (now - dt.datetime.fromisoformat(committed)).total_seconds() >= min_age:
commit = sha
break
count = run([*git, "rev-list", "--count", commit], text=True).strip() count = run([*git, "rev-list", "--count", commit], text=True).strip()
date = run([*git, "show", "-s", "--format=%cs", commit], text=True).strip().replace("-", "") date = run([*git, "show", "-s", "--format=%cs", commit], text=True).strip().replace("-", "")
timestamp = run([*git, "show", "-s", "--format=%cI", commit], text=True).strip() timestamp = run([*git, "show", "-s", "--format=%cI", commit], text=True).strip()
@@ -233,7 +246,7 @@ def git_branch_tip(url, branch, tag_pattern, cache):
return values return values
def discover(watch, fetch): def discover(watch, fetch, min_age=0):
provider = validate(watch) provider = validate(watch)
feed = watch[provider] feed = watch[provider]
results = [] results = []
@@ -260,7 +273,7 @@ def discover(watch, fetch):
for tag, commit in tags.items(): for tag, commit in tags.items():
results.extend(matches(watch, tag, {"tag": tag, "commit": commit}, full=True)) results.extend(matches(watch, tag, {"tag": tag, "commit": commit}, full=True))
elif provider == "git_branch": elif provider == "git_branch":
tip = git_branch_tip(feed, watch["branch"], watch.get("tag_pattern"), fetch.cache) tip = git_branch_tip(feed, watch["branch"], watch.get("tag_pattern"), fetch.cache, min_age)
results.append(candidate(watch, tip)) results.append(candidate(watch, tip))
elif provider == "npm": elif provider == "npm":
data = fetch.json("https://registry.npmjs.org/" + quote(feed, safe="")) data = fetch.json("https://registry.npmjs.org/" + quote(feed, safe=""))
@@ -544,7 +557,7 @@ def sync(package, fetch, min_age=0, check=False):
original = path.read_text() original = path.read_text()
before = read_recipe(path) before = read_recipe(path)
bypass = os.environ.get("BYPASS_MIN_RELEASE_AGE") == "1" bypass = os.environ.get("BYPASS_MIN_RELEASE_AGE") == "1"
release = select_release(discover(watch, fetch), min_age, bypass=bypass) release = select_release(discover(watch, fetch, 0 if bypass else min_age), min_age, bypass=bypass)
if release is None: if release is None:
return {"status": "skipped", "reason": "minimum release age"} return {"status": "skipped", "reason": "minimum release age"}
current = scalar(before, "pkgver") current = scalar(before, "pkgver")
+4 -4
View File
@@ -1,6 +1,6 @@
pkgname=1password-beta pkgname=1password-beta
_tarver=8.12.40-27.BETA _tarver=8.12.42-32.BETA
case "${CARCH}" in case "${CARCH}" in
x86_64) x86_64)
_archdir="x64" _archdir="x64"
@@ -9,7 +9,7 @@ case "${CARCH}" in
_archdir="arm64" _archdir="arm64"
;; ;;
esac esac
pkgver=8.12.40_27.BETA pkgver=8.12.42_32.BETA
pkgrel=1 pkgrel=1
conflicts=('1password' '1password-beta-bin') conflicts=('1password' '1password-beta-bin')
pkgdesc="Password manager and secure wallet" pkgdesc="Password manager and secure wallet"
@@ -22,8 +22,8 @@ source=()
sha256sums=() sha256sums=()
source_x86_64=(https://downloads.1password.com/linux/tar/beta/x86_64/1password-${_tarver}.x64.tar.gz{,.sig}) 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}) source_aarch64=(https://downloads.1password.com/linux/tar/beta/aarch64/1password-${_tarver}.arm64.tar.gz{,.sig})
sha256sums_x86_64=('1327d102255b5c347e6c5e19b1a6581986446e06848015b93b5d7b10b9bc3f52' '2a19fba2b81ade500d9707a20829930d4fca972254fdc653ffa27cee57638098') sha256sums_x86_64=('55fe8dbc80e65337e657c926948dff356b09c3d0860f3b381f1df5af2b99741a' '62494cae0f90d553cade1eeda82cec4d5b26e4719f2233085ce47a70cc80da63')
sha256sums_aarch64=('54b14cae2a676480f3f2df7b85e42bf389d1f84207b0a4ad1e32382071625146' '3a55dc9cdee5729e4bdf6830e04d9813c11546b328003800a837df83f79e33f9') sha256sums_aarch64=('53a63a857e9219da410839915216c4f9cc017cb17bbff9095caf01365e28c5d8' 'd910aedf72c4ff31745b0bbfc12d03c03b177df85f73a381a35227439320ad5c')
validpgpkeys=('3FEF9748469ADBE15DA7CA80AC2D62742012EA22') validpgpkeys=('3FEF9748469ADBE15DA7CA80AC2D62742012EA22')
package() { package() {
@@ -1,5 +1,6 @@
{ {
"source": "local", "source": "local",
"auto_merge": true,
"release_ring": "fast", "release_ring": "fast",
"upstream": { "upstream": {
"watch": { "watch": {
+1 -1
View File
@@ -3,7 +3,7 @@
pkgname=1password-cli pkgname=1password-cli
pkgver=2.40.0 pkgver=2.40.0
pkgrel=1 pkgrel=2
pkgdesc="1Password command line tool" pkgdesc="1Password command line tool"
arch=('x86_64' 'i686' 'arm' 'armv6h' 'aarch64') arch=('x86_64' 'i686' 'arm' 'armv6h' 'aarch64')
url="https://app-updates.agilebits.com/product_history/CLI2" url="https://app-updates.agilebits.com/product_history/CLI2"
@@ -1,5 +1,6 @@
{ {
"source": "local", "source": "local",
"auto_merge": true,
"release_ring": "fast", "release_ring": "fast",
"upstream": { "upstream": {
"debian": "https://downloads.1password.com/linux/debian/amd64/dists/stable/main/binary-amd64/Packages", "debian": "https://downloads.1password.com/linux/debian/amd64/dists/stable/main/binary-amd64/Packages",
+1 -1
View File
@@ -1,6 +1,6 @@
pkgname=1password pkgname=1password
pkgver=8.12.40 pkgver=8.12.40
pkgrel=1 pkgrel=2
conflicts=('1password-beta' '1password-beta-bin') conflicts=('1password-beta' '1password-beta-bin')
pkgdesc="Password manager and secure wallet" pkgdesc="Password manager and secure wallet"
arch=('x86_64' 'aarch64') arch=('x86_64' 'aarch64')
+1
View File
@@ -1,5 +1,6 @@
{ {
"source": "local", "source": "local",
"auto_merge": true,
"release_ring": "fast", "release_ring": "fast",
"upstream": { "upstream": {
"watch": { "watch": {
+1 -1
View File
@@ -1,7 +1,7 @@
# Maintainer: Bjarne Øverli <bjarne@oever.li> # Maintainer: Bjarne Øverli <bjarne@oever.li>
pkgname=aether pkgname=aether
pkgver=4.32.0 pkgver=4.32.0
pkgrel=1 pkgrel=2
pkgdesc='Desktop theming application - extract colors from wallpapers and apply cohesive themes' pkgdesc='Desktop theming application - extract colors from wallpapers and apply cohesive themes'
arch=('x86_64' 'aarch64') arch=('x86_64' 'aarch64')
url='https://github.com/omacom/aether' url='https://github.com/omacom/aether'
-3
View File
@@ -1,3 +0,0 @@
[asusctl]
source = "git"
git = "https://github.com/OpenGamingCollective/asusctl.git"
-13
View File
@@ -1,13 +0,0 @@
{
"source": "local",
"upstream": {
"watch": {
"git_tags": "https://github.com/OpenGamingCollective/asusctl.git",
"pattern": "(?P<version>[0-9]+(?:\\.[0-9]+)*)"
}
},
"origin": {
"aur": "asusctl",
"commit": "b0ec6ca495eb331a684db91b0fe12085a868b632"
}
}
-12
View File
@@ -1,12 +0,0 @@
Copyright Arch Linux Contributors
Permission to use, copy, modify, and/or distribute this software for
any purpose with or without fee is hereby granted.
THE SOFTWARE IS PROVIDED “AS IS” AND THE AUTHOR DISCLAIMS ALL
WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE
FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-1
View File
@@ -1 +0,0 @@
../LICENSE
-59
View File
@@ -1,59 +0,0 @@
# Maintainer: Mahdi Sarikhani <mahdisarikhani@outlook.com>
# Contributor: Fabian Bornschein <fabiscafe@archlinux.org>
# Contributor: Static_Rocket
pkgbase=asusctl
pkgname=(asusctl rog-control-center)
pkgver=6.5.0
pkgrel=1
pkgdesc="Daemon and tools to control your ASUS ROG laptop"
arch=('x86_64')
url="https://asus-linux.org"
license=('MPL-2.0')
makedepends=('cargo' 'fontconfig')
source=("${pkgbase}-${pkgver}.tar.gz::https://github.com/OpenGamingCollective/asusctl/archive/${pkgver}.tar.gz")
b2sums=('4179e08a60f9480b62e41d84faade1f46407140a213ca4d60c8699837a2486bda8e66b4ca43fa06149667d02e3d060c3efd792348f9a93a675f762d6ea2d05f8')
prepare() {
cd "${pkgbase}-${pkgver}"
export RUSTUP_TOOLCHAIN=stable
cargo fetch --locked --target host-tuple
}
build() {
cd "${pkgbase}-${pkgver}"
export RUSTUP_TOOLCHAIN=stable
export CARGO_TARGET_DIR=target
make build
}
package_asusctl() {
pkgdesc="An utility for Linux to control many aspects of various ASUS laptops"
depends=('glibc' 'libgcc' 'libusb' 'systemd-libs')
optdepends=(
'acpi_call: fan control'
'asusctltray: tray profile switcher'
'rog-control-center: graphical user interface for asusctl'
'supergfxctl: hybrid GPU control'
)
install=asusctl.install
cd "${pkgbase}-${pkgver}"
make DESTDIR="${pkgdir}" \
install-asusctl \
install-asusd \
install-asusd_user \
install-asus-shutdown \
install-data-asusd \
install-data-asusd_user
}
package_rog-control-center() {
pkgdesc="Graphical user interface for asusctl"
depends=('asusctl' 'fontconfig' 'glibc' 'hicolor-icon-theme' 'libgcc' 'systemd-libs')
cd "${pkgbase}-${pkgver}"
make DESTDIR="${pkgdir}" \
install-rog_gui \
install-data-rog_gui
}
-22
View File
@@ -1,22 +0,0 @@
version = 1
[[annotations]]
path = [
"PKGBUILD",
"README.md",
"keys/**",
".SRCINFO",
".nvchecker.toml",
"*.install",
"*.sysusers",
"*.tmpfiles",
"*.logrotate",
"*.pam",
"*.service",
"*.socket",
"*.timer",
"*.desktop",
"*.hook",
]
SPDX-FileCopyrightText = "Arch Linux contributors"
SPDX-License-Identifier = "0BSD"
-18
View File
@@ -1,18 +0,0 @@
post_install() {
printf ":: asusd provides a service that is activated by an udev rule on\n"
printf ":: startup. Please reboot the system or run\n"
printf ":: # systemctl start asusd.service\n"
printf ":: to make it work.\n"
printf ":: See https://github.com/OpenGamingCollective/asusctl#kernel-requirements\n"
printf ":: for latest required kernel patches/versions\n"
}
post_upgrade() {
if systemctl is-active asusd.service --quiet; then
printf ":: asusd service will be restarted…\n"
systemctl daemon-reload
systemctl restart asusd.service
fi
printf ":: See https://github.com/OpenGamingCollective/asusctl#kernel-requirements\n"
printf ":: for latest required kernel patches/versions\n"
}
@@ -1,5 +1,6 @@
{ {
"source": "local", "source": "local",
"auto_merge": true,
"release_ring": "fast", "release_ring": "fast",
"upstream": { "upstream": {
"watch": { "watch": {
+3 -3
View File
@@ -1,6 +1,6 @@
# Maintainer: Basecamp <support@basecamp.com> # Maintainer: Basecamp <support@basecamp.com>
pkgname=basecamp-cli pkgname=basecamp-cli
pkgver=0.12.0 pkgver=0.13.0
pkgrel=1 pkgrel=1
pkgdesc="CLI for Basecamp project management" pkgdesc="CLI for Basecamp project management"
arch=('x86_64' 'aarch64') arch=('x86_64' 'aarch64')
@@ -15,8 +15,8 @@ optdepends=(
) )
source_x86_64=("https://github.com/basecamp/basecamp-cli/releases/download/v${pkgver}/basecamp_${pkgver}_linux_amd64.tar.gz") 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") source_aarch64=("https://github.com/basecamp/basecamp-cli/releases/download/v${pkgver}/basecamp_${pkgver}_linux_arm64.tar.gz")
sha256sums_x86_64=('25d61c38de5660e97855661a2bf7329264912fcd46e2b3fb893fbc70bb467b5c') sha256sums_x86_64=('8b33a93018c165f82ac7050d83cad8cde509f9cb7cfbccbc48fb9d98c0649931')
sha256sums_aarch64=('1f692d2a8cc733ef95232eeb107414e32f1dd3228a0dde6c9039538661e0f2ca') sha256sums_aarch64=('bef69d622d66685899b4f955b255951bb588cf1aac54f990b6bd818ae551104c')
package() { package() {
install -Dm755 "basecamp" "${pkgdir}/usr/bin/basecamp" install -Dm755 "basecamp" "${pkgdir}/usr/bin/basecamp"
@@ -1,5 +1,6 @@
{ {
"source": "local", "source": "local",
"auto_merge": true,
"release_ring": "fast", "release_ring": "fast",
"upstream": { "upstream": {
"watch": { "watch": {
+3 -3
View File
@@ -10,7 +10,7 @@
# Contributor: Giusy Digital <kurmikon at libero dot it> # Contributor: Giusy Digital <kurmikon at libero dot it>
pkgname=brave-bin pkgname=brave-bin
pkgver=1.96.61 pkgver=1.97.56
pkgrel=1 pkgrel=1
epoch=1 epoch=1
pkgdesc='Web browser that blocks ads and trackers by default (binary release)' pkgdesc='Web browser that blocks ads and trackers by default (binary release)'
@@ -35,8 +35,8 @@ source_aarch64=(${pkgname}-${pkgver}-aarch64.zip::https://github.com/brave/brave
noextract=(${pkgname}-${pkgver}-x86_64.zip ${pkgname}-${pkgver}-aarch64.zip) noextract=(${pkgname}-${pkgver}-x86_64.zip ${pkgname}-${pkgver}-aarch64.zip)
sha256sums=('75a87dd17b42fcc6f27adfd16c82bed1c08e9251b07d2012f8d49f7412fa1d00' sha256sums=('75a87dd17b42fcc6f27adfd16c82bed1c08e9251b07d2012f8d49f7412fa1d00'
'c07276b69c7304981525ecb022f92daf7ae125a4fb05ac3442157b50826e257a') 'c07276b69c7304981525ecb022f92daf7ae125a4fb05ac3442157b50826e257a')
sha256sums_x86_64=('c68cf179603470e8001a949294fe98b593f0749ca95f42687d855081b1c394a4') sha256sums_x86_64=('8acac76eae35942e30cc63e05adc772f632ad87be8a5ffb3d8a6e280380584ec')
sha256sums_aarch64=('33a1513379505642e4df0dda36a1dd78f735ee351631cffd4a4ac90b1cec0cd5') sha256sums_aarch64=('f18823bfe8bf626a7412a2cc142ba851f991665ca31af8d750f80dbb8371f984')
prepare() { prepare() {
mkdir -p brave mkdir -p brave
@@ -1,5 +1,6 @@
{ {
"source": "local", "source": "local",
"auto_merge": true,
"release_ring": "fast", "release_ring": "fast",
"upstream": { "upstream": {
"watch": { "watch": {
+3 -3
View File
@@ -1,7 +1,7 @@
# Maintainer: brave <aur-release@brave.com> # Maintainer: brave <aur-release@brave.com>
pkgname=brave-origin-bin pkgname=brave-origin-bin
pkgver=1.96.61 pkgver=1.97.56
pkgrel=1 pkgrel=1
epoch=1 epoch=1
pkgdesc='The minimalist browser from the makers of Brave (binary release).' pkgdesc='The minimalist browser from the makers of Brave (binary release).'
@@ -22,8 +22,8 @@ source_aarch64=("${pkgname}-${pkgver}-aarch64.zip::https://github.com/brave/brav
noextract=("${pkgname}-${pkgver}-x86_64.zip" "${pkgname}-${pkgver}-aarch64.zip") noextract=("${pkgname}-${pkgver}-x86_64.zip" "${pkgname}-${pkgver}-aarch64.zip")
sha256sums=('5ff70ee473f35c2fc7642c422c8abe20aaac0d7cc30a3292744eb9fbeafba1bd' sha256sums=('5ff70ee473f35c2fc7642c422c8abe20aaac0d7cc30a3292744eb9fbeafba1bd'
'c70bc71c696b6764247070375ae111bd76c8bad9c7bda4d46e03975b95571a8a') 'c70bc71c696b6764247070375ae111bd76c8bad9c7bda4d46e03975b95571a8a')
sha256sums_x86_64=('06e4c48b71c65a33bdb94b6909aab505237b5939adf8985b332cb14557792816') sha256sums_x86_64=('4710187525d4153d47c1d0366a69829f351b445f568ed091bf62215de25ddf34')
sha256sums_aarch64=('df07224b25284b67b5c7d0e8859d16f3d2533abcf588c81437df59e9fd4f4c62') sha256sums_aarch64=('66789d7cd7d423f761d713edc9959ebff86b1493b1718c15346f627b02f6ef74')
prepare() { prepare() {
mkdir -p brave mkdir -p brave
+1
View File
@@ -1,5 +1,6 @@
{ {
"source": "local", "source": "local",
"auto_merge": true,
"release_ring": "fast", "release_ring": "fast",
"upstream": { "upstream": {
"watch": { "watch": {
+3 -3
View File
@@ -3,7 +3,7 @@
# Contributor: 37h4n (aarch64 support added by Ethan Reece <aur at ethanreece dot com>) # 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. # Contributor: sh!zeeg (shizeeque@gmail.com) support for non-avx2 CPUs, shell completions.
pkgname=bun-bin pkgname=bun-bin
pkgver=1.4.2 pkgver=1.4.3
pkgrel=1 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" 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') arch=('x86_64' 'aarch64')
@@ -12,8 +12,8 @@ license=('MIT')
provides=('bun') provides=('bun')
conflicts=('bun') conflicts=('bun')
options=('!debug') options=('!debug')
sha256sums_x86_64=('36368faef7527875d5ffa52e53cd48021741f2a83eb6208a8dd64068d422a913' 'c678040f14fe0440eb839d37cbd0ce4c051a32da72806ac97de6a6aab6bf728f' '9b296bcc20090b5ea079dc1bca15913a32246121169bcf54cbdb7384d6f5b32b') sha256sums_x86_64=('3e730528d89775f03f87f05c06787c4df4099d0a37f3f252e7b829cb23816e18' '1fc2edac843102909e3a1be1d8d9802cc6071cf074e67e88231f4ffe0f8b397b' '9b296bcc20090b5ea079dc1bca15913a32246121169bcf54cbdb7384d6f5b32b')
sha256sums_aarch64=('54328bbc2d9c8e0c9f892c544d66c57a83b84139e34909e5ee81758f1ac8fda7' '9b296bcc20090b5ea079dc1bca15913a32246121169bcf54cbdb7384d6f5b32b') sha256sums_aarch64=('efa9813da5ed72423bf847f916e8d2c47c0d776add972354026a75e10da9aa21' '9b296bcc20090b5ea079dc1bca15913a32246121169bcf54cbdb7384d6f5b32b')
source_x86_64=( source_x86_64=(
"bun-x64.zip::https://github.com/oven-sh/bun/releases/download/bun-v${pkgver}/bun-linux-x64.zip" "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" "bun-x64-baseline.zip::https://github.com/oven-sh/bun/releases/download/bun-v${pkgver}/bun-linux-x64-baseline.zip"
+10
View File
@@ -0,0 +1,10 @@
{
"source": "local",
"release_ring": "fast",
"upstream": {
"watch": {
"github": "block/buzz",
"pattern": "desktop-v(?P<version>[0-9]+(?:\\.[0-9]+)*)"
}
}
}
+92
View File
@@ -0,0 +1,92 @@
pkgname=buzz
pkgver=0.5.28
pkgrel=1
pkgdesc='Workspace where humans and AI agents build together, on a Nostr relay you own'
arch=('x86_64' 'aarch64')
url='https://github.com/block/buzz'
license=('Apache-2.0')
depends=(
'alsa-lib'
'cairo'
'gcc-libs'
'gdk-pixbuf2'
'glib2'
'glibc'
'gst-plugins-good'
'gtk3'
'hicolor-icon-theme'
'libayatana-appindicator'
'libsoup3'
'opus'
'webkit2gtk-4.1'
)
makedepends=('cargo' 'cmake' 'nodejs' 'pnpm' 'pkgconf')
conflicts=('buzz-appimage' 'buzz-bin' 'buzz-desktop-git')
# Arch's GCC LTO objects cannot be consumed when crates compile bundled C
# libraries (secp256k1-sys among them).
options=('!lto')
_srcdir="$pkgname-desktop-v$pkgver"
source=("$pkgname-$pkgver.tar.gz::$url/archive/refs/tags/desktop-v$pkgver.tar.gz"
'buzz.desktop')
sha256sums=('4ba8c250d6ebb075ba60e6c4d217b77ea605b183161715b77629bd8660b81a43'
'3c152420e67c64f5046cc0e10a9d948e1c7d3efa512582ad279f073b0900526a')
# The sidecars the desktop app spawns from its own directory, from upstream's
# scripts/bundle-sidecars.sh.
_sidecars=(buzz-acp buzz-agent buzz-backend-kubernetes buzz-dev-mcp git-credential-nostr buzz)
prepare() {
cd "$_srcdir"
export npm_config_store_dir="$srcdir/pnpm-store"
pnpm install --frozen-lockfile
# The sidecars build in the root workspace; the app has its own lockfile.
cargo fetch --locked --target "$CARCH-unknown-linux-gnu"
cargo fetch --locked --target "$CARCH-unknown-linux-gnu" \
--manifest-path desktop/src-tauri/Cargo.toml
}
build() {
cd "$_srcdir"
export npm_config_store_dir="$srcdir/pnpm-store"
# audiopus_sys's vendored opus fallback declares an old CMake minimum.
export CMAKE_POLICY_VERSION_MINIMUM=3.5
# sha2-asm assembles ARMv8 SHA instructions, which Arch Linux ARM's
# -march=armv8-a leaves out. sha2 still checks for them at runtime.
[[ $CARCH == aarch64 ]] && CFLAGS=${CFLAGS/-march=armv8-a/-march=armv8-a+crypto}
cargo build --frozen --release -p buzz-acp -p buzz-agent -p buzz-backend-kubernetes \
-p buzz-dev-mcp -p git-credential-nostr -p buzz-cli
./scripts/bundle-sidecars.sh
# Upstream's Linux release build, without its bundles. Leaving
# BUZZ_UPDATER_PUBLIC_KEY and BUZZ_UPDATER_ENDPOINT unset compiles the
# self-updater out: pacman owns updates.
cd desktop
pnpm tauri build --ci --no-bundle --features mesh-llm -- --frozen
}
package() {
cd "$_srcdir"
install -Dm755 desktop/src-tauri/target/release/buzz-desktop "$pkgdir/usr/bin/buzz-desktop"
local bin
for bin in "${_sidecars[@]}"; do
install -Dm755 "target/release/$bin" "$pkgdir/usr/bin/$bin"
done
install -Dm644 "$srcdir/buzz.desktop" "$pkgdir/usr/share/applications/buzz-desktop.desktop"
local icons=desktop/src-tauri/icons size
for size in 32 64 128; do
install -Dm644 "$icons/${size}x$size.png" \
"$pkgdir/usr/share/icons/hicolor/${size}x$size/apps/buzz-desktop.png"
done
install -Dm644 "$icons/128x128@2x.png" "$pkgdir/usr/share/icons/hicolor/256x256/apps/buzz-desktop.png"
install -Dm644 "$icons/icon.png" "$pkgdir/usr/share/icons/hicolor/512x512/apps/buzz-desktop.png"
install -Dm644 LICENSE "$pkgdir/usr/share/licenses/$pkgname/LICENSE"
}
+10
View File
@@ -0,0 +1,10 @@
[Desktop Entry]
Type=Application
Name=Buzz
Comment=Workspace where humans and AI agents build together
Exec=buzz-desktop %u
Icon=buzz-desktop
Terminal=false
StartupWMClass=buzz-desktop
Categories=Network;InstantMessaging;Chat;
MimeType=x-scheme-handler/buzz;
@@ -1,5 +1,6 @@
{ {
"source": "local", "source": "local",
"auto_merge": true,
"release_ring": "fast", "release_ring": "fast",
"upstream": { "upstream": {
"watch": { "watch": {
+3 -3
View File
@@ -4,7 +4,7 @@
# Automation repository: https://github.com/fabifont/claude-code-aur # Automation repository: https://github.com/fabifont/claude-code-aur
pkgname=claude-code pkgname=claude-code
pkgver=2.1.291 pkgver=2.1.296
pkgrel=1 pkgrel=1
pkgdesc="An agentic coding tool that lives in your terminal" pkgdesc="An agentic coding tool that lives in your terminal"
arch=('x86_64' 'aarch64') 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") source_aarch64=("claude-${pkgver}-aarch64::https://downloads.claude.ai/claude-code-releases/${pkgver}/linux-arm64/claude")
sha256sums=('SKIP') sha256sums=('SKIP')
sha256sums_x86_64=('078fad28d0297c9a25d306b635b2d8816c6839347520f29eb54ffea5d56142fb') sha256sums_x86_64=('24972e3bc859fab2b46ed4c1e51f7d6130f06d3bd550811a114640de3370d0de')
sha256sums_aarch64=('c18473a04cc4f077435d5d9081f09ebea46e699eb2825cea64741c4bccb87647') sha256sums_aarch64=('f1f6e96e0d8342b9dbf41d7e88255397a6a52ce3d8736ad6a4c6b59c9b62fefa')
package() { package() {
install -Dm755 "${srcdir}/claude-${pkgver}-${CARCH}" "${pkgdir}/opt/claude-code/bin/claude" install -Dm755 "${srcdir}/claude-${pkgver}-${CARCH}" "${pkgdir}/opt/claude-code/bin/claude"
@@ -1,5 +1,6 @@
{ {
"source": "local", "source": "local",
"auto_merge": true,
"release_ring": "fast", "release_ring": "fast",
"upstream": { "upstream": {
"debian": "https://downloads.claude.ai/claude-desktop/apt/stable/dists/stable/main/binary-amd64/Packages", "debian": "https://downloads.claude.ai/claude-desktop/apt/stable/dists/stable/main/binary-amd64/Packages",
+3 -3
View File
@@ -6,7 +6,7 @@
# repository's package index. # repository's package index.
pkgname=claude-desktop pkgname=claude-desktop
pkgver=2.19675.1 pkgver=2.31226.1
pkgrel=1 pkgrel=1
pkgdesc="Official Claude desktop app with Claude Code" pkgdesc="Official Claude desktop app with Claude Code"
arch=('x86_64' 'aarch64') arch=('x86_64' 'aarch64')
@@ -63,8 +63,8 @@ source_x86_64=("${_deb_x86_64}::${_pool}/${_deb_x86_64}")
source_aarch64=("${_deb_aarch64}::${_pool}/${_deb_aarch64}") source_aarch64=("${_deb_aarch64}::${_pool}/${_deb_aarch64}")
noextract=("${_deb_x86_64}" "${_deb_aarch64}") noextract=("${_deb_x86_64}" "${_deb_aarch64}")
sha256sums=('edfdbc63b65891ef7c481b07086c7e630fc102c042b6ed65331a52fcaf72b14a') sha256sums=('edfdbc63b65891ef7c481b07086c7e630fc102c042b6ed65331a52fcaf72b14a')
sha256sums_x86_64=('9ba127eeccf270f6e60d35f5c5333654053bf0540c88fc82a009d01711b106fc') sha256sums_x86_64=('5a9bebdfcb1df6ce38767b373f3a7fabf77156a2ddd8a3f5d04961da5bde305e')
sha256sums_aarch64=('681d122ae97d0eb302f0e6d92c7f232847064bb01746458dc50a2c095a40ed12') sha256sums_aarch64=('d2f57a38ffc73cfda660f8772a5f8bada05e6d74399948a268c6f5d53593ee8f')
package() { package() {
cd "${srcdir}" cd "${srcdir}"
+1
View File
@@ -1,5 +1,6 @@
{ {
"source": "local", "source": "local",
"auto_merge": true,
"upstream": { "upstream": {
"watch": { "watch": {
"github": "bjarneo/cliamp", "github": "bjarneo/cliamp",
+1 -1
View File
@@ -1,7 +1,7 @@
# Maintainer: bjarneo <https://github.com/bjarneo> # Maintainer: bjarneo <https://github.com/bjarneo>
pkgname=cliamp pkgname=cliamp
pkgver=2.3.0 pkgver=2.3.0
pkgrel=1 pkgrel=2
pkgdesc='A retro terminal music player inspired by Winamp 2.x' pkgdesc='A retro terminal music player inspired by Winamp 2.x'
arch=('x86_64' 'aarch64') arch=('x86_64' 'aarch64')
url='https://github.com/bjarneo/cliamp' url='https://github.com/bjarneo/cliamp'
+5 -5
View File
@@ -3,7 +3,7 @@
# Maintainer: caarlos0 <carlos@charm.sh> # Maintainer: caarlos0 <carlos@charm.sh>
pkgname='crush-bin' pkgname='crush-bin'
pkgver=0.97.1 pkgver=0.98.1
pkgrel=1 pkgrel=1
pkgdesc='A powerful terminal-based AI assistant for developers, providing intelligent coding assistance directly in your terminal.' pkgdesc='A powerful terminal-based AI assistant for developers, providing intelligent coding assistance directly in your terminal.'
url='https://charm.sh/crush' url='https://charm.sh/crush'
@@ -13,16 +13,16 @@ provides=('crush')
conflicts=('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") source_aarch64=("${pkgname}_${pkgver}_aarch64.tar.gz::https://github.com/charmbracelet/crush/releases/download/v${pkgver}/crush_${pkgver}_Linux_arm64.tar.gz")
sha256sums_aarch64=('16c9477a178d69e02a51bf4babe6174e62051314b2cd7f36b1ec5fea47e25a88') sha256sums_aarch64=('098e875c1d9501822d08ef14ab094f24cfc87396385af5402994bf741a673b99')
source_armv7h=("${pkgname}_${pkgver}_armv7h.tar.gz::https://github.com/charmbracelet/crush/releases/download/v${pkgver}/crush_${pkgver}_Linux_armv7.tar.gz") source_armv7h=("${pkgname}_${pkgver}_armv7h.tar.gz::https://github.com/charmbracelet/crush/releases/download/v${pkgver}/crush_${pkgver}_Linux_armv7.tar.gz")
sha256sums_armv7h=('1999746feb3778aa65554df726a04161e98f38f00dc7d565617d88ba0ebc7e33') sha256sums_armv7h=('ba4361b5b2bfe28d7c0063cb25a570c4472d4fe0c10b125af12813b3ab27e25b')
source_i686=("${pkgname}_${pkgver}_i686.tar.gz::https://github.com/charmbracelet/crush/releases/download/v${pkgver}/crush_${pkgver}_Linux_i386.tar.gz") source_i686=("${pkgname}_${pkgver}_i686.tar.gz::https://github.com/charmbracelet/crush/releases/download/v${pkgver}/crush_${pkgver}_Linux_i386.tar.gz")
sha256sums_i686=('a46de190551e8c45f27f36c26247e5a8ff01d082e66e61cb39a8bce77b9fbad0') sha256sums_i686=('6d972d44e177130c27a76c4316a8a40ac6a4357dcd2f79b7904a0b31801a0fce')
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") 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=('1b7cbe0600a3797538a74dc00dd8c4bac54ac4b8f4455ba5ff2312b29c7bd598') sha256sums_x86_64=('d85d913effef350f0152f594a70bd9749ad455bf15af77bb0d2b731036079885')
package() { package() {
case "$CARCH" in case "$CARCH" in
+7 -8
View File
@@ -19,10 +19,8 @@
# binary to point at pm.sh, a stand-in that declines and names pacman instead. # binary to point at pm.sh, a stand-in that declines and names pacman instead.
pkgname=cua-driver-bin pkgname=cua-driver-bin
# Held at 0.28.2: 0.28.3 and newer break screenshots. Bump by hand once a pkgver=0.33.4
# fixed release is verified. pkgrel=1
pkgver=0.28.2
pkgrel=3
pkgdesc="Computer-use driver for native GUI apps: accessibility-tree snapshots and input injection" pkgdesc="Computer-use driver for native GUI apps: accessibility-tree snapshots and input injection"
arch=('x86_64' 'aarch64') arch=('x86_64' 'aarch64')
url="https://github.com/trycua/cua" url="https://github.com/trycua/cua"
@@ -49,8 +47,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") source_aarch64=("https://github.com/trycua/cua/releases/download/cua-driver-rs-v${pkgver}/cua-driver-rs-${pkgver}-linux-arm64.tar.gz")
sha256sums=('c0779290c1d4783169aa3dbfb55feb505e563ef8a004bbf55298ceffcfbda8d9' sha256sums=('c0779290c1d4783169aa3dbfb55feb505e563ef8a004bbf55298ceffcfbda8d9'
'c76e251c3ed424200eac52bec35ba534336307fabd83a175ab0b47e2084ab0d8') 'c76e251c3ed424200eac52bec35ba534336307fabd83a175ab0b47e2084ab0d8')
sha256sums_x86_64=('8f3e5b669e2bcd98d0eecc64f40640aac77f358b6332a06abc6ee79991620f7d') sha256sums_x86_64=('a4759cc41c00691a345f08abfc28ece79aef41447d27a463ddd8662939fa3378')
sha256sums_aarch64=('cadd7e6b757c3ce50f2b5f6e273c154ea48450fb5fcaff744209b382915eddf5') sha256sums_aarch64=('63a1e858a1a407c44ceb407ad133909b8cddedd713aea3b56d7ccb6db784eb48')
case "${CARCH}" in case "${CARCH}" in
x86_64) _platform="linux-x86_64" ;; x86_64) _platform="linux-x86_64" ;;
@@ -72,8 +70,8 @@ prepare() {
return 1 return 1
fi fi
# In 0.28.1 the URL appears in the updater, the printed reinstall command, # Through 0.33.4 the URL appears in the updater, the printed reinstall
# and two embedded copies of Skills/cua-driver/README.md. Rewrite all four # 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 # so the embedded instructions also defer to pacman. Any other count means
# the release layout changed and needs review before packaging. # the release layout changed and needs review before packaging.
local expected=4 found local expected=4 found
@@ -113,4 +111,5 @@ package() {
ln -s ../lib/cua-driver/cua-driver "${pkgdir}/usr/bin/cua-driver" ln -s ../lib/cua-driver/cua-driver "${pkgdir}/usr/bin/cua-driver"
install -Dm644 "${srcdir}/LICENSE" "${pkgdir}/usr/share/licenses/${pkgname}/LICENSE" install -Dm644 "${srcdir}/LICENSE" "${pkgdir}/usr/share/licenses/${pkgname}/LICENSE"
install -Dm644 THIRD_PARTY_NOTICES.md "${pkgdir}/usr/share/licenses/${pkgname}/THIRD_PARTY_NOTICES.md"
} }
+3 -3
View File
@@ -1,6 +1,6 @@
# Optional Cua Hyprland plugin # Optional Cua Hyprland plugin
This package targets **Omarchy x86_64**, with Inkscape `1.4.4-6` and two independent background-input lanes. Release `0.32.0-3` is an edge candidate built from the plugin source published with Driver `0.32.0`, paired with `cua-driver-bin` `0.28.2`. That source carries the independent agent keymaps and compatible Num Lock handling that releases `0.26.1-5` through `0.28.2-2` applied as an Omarchy patch; this release applies a smaller one, `downstream.patch`, so foreground typing keeps working with modifier and Compose remaps (see *Keyboard behavior*) and so that, with its guard active, restarting fcitx5 does not crash Hyprland (see *Input-method popups*). The upstream native qualification below covers older, unpatched source. 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. This package targets **Omarchy x86_64**, with Inkscape `1.4.4-6` and two independent background-input lanes. Release `0.32.0-3` is an edge candidate built from the plugin source published with Driver `0.32.0`, paired with `cua-driver-bin` `0.33.4`; the plugin sources are unchanged from 0.32.0 through 0.33.4. That source carries the independent agent keymaps and compatible Num Lock handling that releases `0.26.1-5` through `0.28.2-2` applied as an Omarchy patch; this release applies a smaller one, `downstream.patch`, so foreground typing keeps working with modifier and Compose remaps (see *Keyboard behavior*) and so that, with its guard active, restarting fcitx5 does not crash Hyprland (see *Input-method popups*). The upstream native qualification below covers older, unpatched source. 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. The upstream qualification covers the original stable profile; the updated Aquamarine profile needs its own Omabot validation before promotion. 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. The upstream qualification covers the original stable profile; the updated Aquamarine profile needs its own Omabot validation before promotion.
@@ -8,7 +8,7 @@ The plugin is optional. Cua Driver works independently, and installation does no
The package uses the [Driver 0.32.0 plugin source](https://github.com/trycua/cua/releases/tag/cua-driver-rs-v0.32.0). Against the Driver 0.26.1 source the profile kit was qualified with, it adds the keyboard behaviour below ([Cua #3970](https://github.com/trycua/cua/pull/3970), adapted from [#473](https://github.com/omacom/omarchy-pkgs/pull/473)) and gives agent keyboards the user seat's key repeat rate instead of zero ([Cua #4358](https://github.com/trycua/cua/pull/4358)), which crashed single-seat clients such as imv that bound an agent seat ([Cua #4257](https://github.com/trycua/cua/issues/4257)). The input protocol header is unchanged. The package uses the [Driver 0.32.0 plugin source](https://github.com/trycua/cua/releases/tag/cua-driver-rs-v0.32.0). Against the Driver 0.26.1 source the profile kit was qualified with, it adds the keyboard behaviour below ([Cua #3970](https://github.com/trycua/cua/pull/3970), adapted from [#473](https://github.com/omacom/omarchy-pkgs/pull/473)) and gives agent keyboards the user seat's key repeat rate instead of zero ([Cua #4358](https://github.com/trycua/cua/pull/4358)), which crashed single-seat clients such as imv that bound an agent seat ([Cua #4257](https://github.com/trycua/cua/issues/4257)). The input protocol header is unchanged.
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. This package pairs with `cua-driver-bin 0.28.2`, which speaks the same input protocol v3 to this source; Driver finds the plugin only through its versioned input socket, not through the plugin's provenance. Discovery protocol v2 is separate. `cua-driver-bin` stays at 0.28.2 because Driver 0.28.3 through 0.32.0 refuse desktop capture on Hyprland with more than one output or with one output away from the origin ([Cua #4161](https://github.com/trycua/cua/issues/4161)), where 0.28.2 captures. This pairing is a changed pairing and requires affected replay before promotion. 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. This package pairs with `cua-driver-bin 0.33.4`, which speaks the same input protocol v3 to this source; Driver finds the plugin only through its versioned input socket, not through the plugin's provenance. Discovery protocol v2 is separate. Driver 0.28.3 through 0.33.3 refuse desktop capture on Hyprland with more than one output or with one output away from the origin ([Cua #4161](https://github.com/trycua/cua/issues/4161)); 0.33.4 captures there again ([Cua #4305](https://github.com/trycua/cua/pull/4305)). This pairing is a changed pairing and requires affected replay before promotion.
Profile `omarchy-hyprland-0562r4-remaps`, kit tooling `1.1.0`, and package release `3` pin: Profile `omarchy-hyprland-0562r4-remaps`, kit tooling `1.1.0`, and package release `3` pin:
@@ -19,7 +19,7 @@ Profile `omarchy-hyprland-0562r4-remaps`, kit tooling `1.1.0`, and package relea
This profile derives from Cua's `omarchy-stable-20260910` profile. Arch's Hyprland `0.56.2-4` is a rebuild of the same 0.56.2 release against vulkan-sdk 1.4.363 and glslang, built with the same GCC `16.2.1 20260810` and linked to the same `libstdc++`. Its executable (SHA-256 `55da553be71222566ee73b973d2f56dbb9939044d8f22f81aa54b66c83f2f6d1`) and two of its 497 headers differ from `-3`: `src/version.h` now names the Aquamarine `0.15.1` and hyprutils `0.14.2` it was built against, and `protocols/hyprland-input-capture-v1.hpp` gains a destroy handler. The header inventory is re-measured the way `profile_verify.py` measures it, giving `1fdefe6ac027a159d04a5dfee4928ec7ebd15544a9a25b5f66d2f5a46fcf364a`; that method reproduces the kit's `-2`/`-3` values exactly. This profile derives from Cua's `omarchy-stable-20260910` profile. Arch's Hyprland `0.56.2-4` is a rebuild of the same 0.56.2 release against vulkan-sdk 1.4.363 and glslang, built with the same GCC `16.2.1 20260810` and linked to the same `libstdc++`. Its executable (SHA-256 `55da553be71222566ee73b973d2f56dbb9939044d8f22f81aa54b66c83f2f6d1`) and two of its 497 headers differ from `-3`: `src/version.h` now names the Aquamarine `0.15.1` and hyprutils `0.14.2` it was built against, and `protocols/hyprland-input-capture-v1.hpp` gains a destroy handler. The header inventory is re-measured the way `profile_verify.py` measures it, giving `1fdefe6ac027a159d04a5dfee4928ec7ebd15544a9a25b5f66d2f5a46fcf364a`; that method reproduces the kit's `-2`/`-3` values exactly.
The checked-in `PROFILE.json` changes only the profile name, package release, source release, exact Hyprland, Aquamarine and glibc package versions, and the re-measured compositor executable and header hashes. Compiler and libstdc++ runtime identities remain Cua's; the separately recorded patch changes the build source. Cua publishes the profile tooling only in the 0.26.1 kit, and the Driver 0.32.0 source manifest has exactly the fields that kit's `profile_verify.py` checks. The download wrapper verifies the original kit and the Driver 0.32.0 source archive before substituting that archive and its manifest into the kit, deriving the updated profile and provenance, and rendering the recipe from the kit's own `PROFILE-PKGBUILD.in`. It then verifies every derived member against its recorded digest, so the result is the kit Cua's `profile_verify.py` accepts as complete for this profile. The checked-in `PROFILE.json` changes only the profile name, package release, source release, exact Hyprland, Aquamarine and glibc package versions, and the re-measured compositor executable and header hashes. Compiler and libstdc++ runtime identities remain Cua's; the separately recorded patch changes the build source. Cua publishes the profile tooling only in the 0.26.1 kit, and the Driver 0.32.0 source manifest has exactly the fields that kit's `profile_verify.py` checks. The download wrapper verifies the original kit and the Driver 0.32.0 source archive before substituting that archive and its manifest into the kit, deriving the updated profile and provenance, and rendering the recipe from the kit's own `PROFILE-PKGBUILD.in`. It then verifies every derived member against its recorded digest, so the result is the kit Cua's `profile_verify.py` accepts as complete for this profile.
The native qualification below was recorded with package `0.26.1-2` and Hyprland `-2`. The 0.32.0 source and its Omarchy patch, the Aquamarine and glibc updates, and the Driver 0.28.2 pairing need their own application and Driver replay before promotion. Hyprland `0.56.2-4`, Aquamarine `0.15.1-1` and glibc `2.44+r50+g1848099f063e-1` must all reach a destination channel before this artifact can be installed there; publication still follows edge → RC → stable. The native qualification below was recorded with package `0.26.1-2` and Hyprland `-2`. The 0.32.0 source and its Omarchy patch, the Aquamarine and glibc updates, and the Driver 0.33.4 pairing need their own application and Driver replay before promotion. Hyprland `0.56.2-4`, Aquamarine `0.15.1-1` and glibc `2.44+r50+g1848099f063e-1` must all 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, The generated `PKGBUILD` identifies the immutable kit download, outer checksum,
and member checksums. The kit records the full source and tooling revisions, and member checksums. The kit records the full source and tooling revisions,
@@ -1,5 +1,6 @@
{ {
"source": "local", "source": "local",
"auto_merge": true,
"release_ring": "fast", "release_ring": "fast",
"upstream": { "upstream": {
"watch": { "watch": {
+4 -4
View File
@@ -1,7 +1,7 @@
# Maintainer: Gunther Schulz <dev@guntherschulz.de> # Maintainer: Gunther Schulz <dev@guntherschulz.de>
pkgname=cursor-bin pkgname=cursor-bin
pkgver=3.23.23 pkgver=3.24.12
pkgrel=1 pkgrel=1
pkgdesc='AI-first coding environment' pkgdesc='AI-first coding environment'
arch=('x86_64' 'aarch64') arch=('x86_64' 'aarch64')
@@ -18,13 +18,13 @@ depends_aarch64=(
libxkbcommon libxrandr mesa nspr nss pango systemd-libs which libxkbcommon libxrandr mesa nspr nss pango systemd-libs which
) )
options=(!strip !debug) # Don't break ext of VSCode options=(!strip !debug) # Don't break ext of VSCode
_commit=2dac2428994fe34f12658d9ecad1541b98db2c04 _commit=37b865941ccb31e2fc90c685157534efca87f964
source_x86_64=("https://downloads.cursor.com/production/${_commit}/linux/x64/deb/amd64/deb/cursor_${pkgver}_amd64.deb" source_x86_64=("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} "https://gitlab.archlinux.org/archlinux/packaging/packages/code/-/raw/main/code."{sh,mjs}
rg.sh) rg.sh)
sha512sums_x86_64=('acdf6f2677cd817c41fe91181075f8fa2cbf6fe21211c937a750769f1b036911a05c2e48c3f5186cc5e3d71a1b56aac8e6fbfead84a3c86902c39b77f8776328' '937299c6cb6be2f8d25f7dbc95cf77423875c5f8353b8bd6cd7cc8e5603cbf8405b14dbf8bd615db2e3b36ed680fc8e1909410815f7f8587b7267a699e00ab37' '793f9ff6306e3992ac89802d98110cba288ea1181a901467333293b7d76182ef9792c2a39ff49d9347a18a174b1f42bc58862091dff583f4146c2704eea28033' 'e79fe7659f59d1ae02fc68816399bfd31587315df6cdb6ccf1d0ca76f7cdc692c2a42b30591c0091147bd97ef14b1c7745dc26bd7cb3ea6bba45698e5044fa2a') sha512sums_x86_64=('336dc2b642cbb6d16a65bbd79cd4241e53c0d1fd3d9189850d7a922d1afcc9c37908d8a1e24d8c06429a12b375e7629a06e5f168c51e7be12e0b57430f6f1b86' '937299c6cb6be2f8d25f7dbc95cf77423875c5f8353b8bd6cd7cc8e5603cbf8405b14dbf8bd615db2e3b36ed680fc8e1909410815f7f8587b7267a699e00ab37' '793f9ff6306e3992ac89802d98110cba288ea1181a901467333293b7d76182ef9792c2a39ff49d9347a18a174b1f42bc58862091dff583f4146c2704eea28033' 'e79fe7659f59d1ae02fc68816399bfd31587315df6cdb6ccf1d0ca76f7cdc692c2a42b30591c0091147bd97ef14b1c7745dc26bd7cb3ea6bba45698e5044fa2a')
source_aarch64=("https://downloads.cursor.com/production/${_commit}/linux/arm64/deb/arm64/deb/cursor_${pkgver}_arm64.deb") source_aarch64=("https://downloads.cursor.com/production/${_commit}/linux/arm64/deb/arm64/deb/cursor_${pkgver}_arm64.deb")
sha512sums_aarch64=('28ae4e335d48a8c9aa189645e75e26a2384462c8d2c910a7a1340442259eda55beb065210f049f895eeb809c4cdc331823e0c6b539a79bab209a94594777d5c0') sha512sums_aarch64=('fd2fc67f4cbec2e35a43046019ecddf14c025ae06398a1652369839a58fbd0edd565cf433525734c8a114b0bb4b5a3a6c9cfce0cc71739f4f803cbf984275876')
noextract=(cursor_${pkgver}_amd64.deb cursor_${pkgver}_arm64.deb) # avoid double tarball noextract=(cursor_${pkgver}_amd64.deb cursor_${pkgver}_arm64.deb) # avoid double tarball
_app=usr/share/cursor/resources/app _app=usr/share/cursor/resources/app
package() { package() {
@@ -1,5 +1,6 @@
{ {
"source": "local", "source": "local",
"auto_merge": true,
"release_ring": "fast", "release_ring": "fast",
"upstream": { "upstream": {
"watch": { "watch": {
+1 -1
View File
@@ -8,7 +8,7 @@ pkgver=2026.10.01.1.e373342
# Derive the upstream version (YYYY.MM.DD-<hash>) from that pkgver. # Derive the upstream version (YYYY.MM.DD-<hash>) from that pkgver.
_upstream_ver="${pkgver%.*}" _upstream_ver="${pkgver%.*}"
_upstream_ver="${_upstream_ver%.*}-${pkgver##*.}" _upstream_ver="${_upstream_ver%.*}-${pkgver##*.}"
pkgrel=1 pkgrel=2
# epoch=1: bumped when switching from the original `20250808.0.<sha>` scheme # epoch=1: bumped when switching from the original `20250808.0.<sha>` scheme
# to the current `YYYY.MM.DD.<n>.<hash>` scheme (2025-08-09). Never decrease. # to the current `YYYY.MM.DD.<n>.<hash>` scheme (2025-08-09). Never decrease.
epoch=1 epoch=1
@@ -1,5 +1,6 @@
{ {
"source": "local", "source": "local",
"auto_merge": true,
"min_release_age": "24h", "min_release_age": "24h",
"upstream": { "upstream": {
"github": "tobi/disktree", "github": "tobi/disktree",
+1 -1
View File
@@ -3,7 +3,7 @@
pkgname=disktree-bin pkgname=disktree-bin
_name=disktree _name=disktree
pkgver=0.11.0 pkgver=0.11.0
pkgrel=1 pkgrel=2
pkgdesc="Disk space treemap for Omarchy: see what fills a disk by kind, mark what should go, and remove it" pkgdesc="Disk space treemap for Omarchy: see what fills a disk by kind, mark what should go, and remove it"
arch=('x86_64' 'aarch64') arch=('x86_64' 'aarch64')
url="https://github.com/tobi/disktree" url="https://github.com/tobi/disktree"
+1 -1
View File
@@ -7,7 +7,7 @@
pkgname=dropbox-cli pkgname=dropbox-cli
pkgver=2026.09.28 pkgver=2026.09.28
pkgrel=2 pkgrel=3
pkgdesc="Command line interface for Dropbox" pkgdesc="Command line interface for Dropbox"
arch=("any") arch=("any")
url="https://www.dropbox.com" url="https://www.dropbox.com"
+1
View File
@@ -1,5 +1,6 @@
{ {
"source": "local", "source": "local",
"auto_merge": true,
"release_ring": "fast", "release_ring": "fast",
"upstream": { "upstream": {
"watch": { "watch": {
@@ -1,13 +0,0 @@
{
"source": "local",
"upstream": {
"watch": {
"github": "abenz1267/elephant",
"pattern": "v(?P<version>[0-9]+(?:\\.[0-9]+)*)"
}
},
"origin": {
"aur": "elephant-all",
"commit": "5614a0a61616643e448fb7c68d58237715ce2739"
}
}
-47
View File
@@ -1,47 +0,0 @@
# Maintainer: Andrej Benz <hello[at]benz[dot]dev>
pkgname=elephant-all
pkgver=2.22.1
pkgrel=1
pkgdesc='elephant + all official elephant providers'
url='https://github.com/abenz1267/elephant'
arch=('x86_64' 'aarch64')
license=('GPL')
depends=('libqalculate' 'wl-clipboard' 'imagemagick' 'fd' 'wtype' 'jq' 'sqlite3')
makedepends=('go')
conflicts=('elephant' 'elephant-playerctl' 'elephant-wireplumber' 'elephant-bitwarden' 'elephant-dnfpackages' 'elephant-1password' 'elephant-bookmarks' 'elephant-nirisessions' 'elephant-niriactions' 'elephant-archlinuxpkgs' 'elephant-bluetooth' 'elephant-calc' 'elephant-clipboard' 'elephant-desktopapplications' 'elephant-files' 'elephant-menus' 'elephant-providerlist' 'elephant-runner' 'elephant-snippets' 'elephant-symbols' 'elephant-todo' 'elephant-unicode' 'elephant-websearch' 'elephant-windows')
provides=('elephant' 'elephant-playerctl' 'elephant-wireplumber' 'elephant-nirisessions' 'elephant-niriactions' 'elephant-archlinuxpkgs' 'elephant-bluetooth' 'elephant-calc' 'elephant-clipboard' 'elephant-desktopapplications' 'elephant-files' 'elephant-menus' 'elephant-providerlist' 'elephant-runner' 'elephant-snippets' 'elephant-symbols' 'elephant-todo' 'elephant-unicode' 'elephant-websearch' 'elephant-windows')
source=("${url}/archive/refs/tags/v${pkgver}.tar.gz")
sha256sums=('3d1d0d4c55ae531fa3f06406b96504b5165a0d7b53523d1f8351d9d93e457f44')
build() {
# Build main elephant binary
cd elephant-${pkgver}/cmd/elephant
go build -ldflags="-s -w" -buildvcs=false -x -o elephant -trimpath
# Build all provider plugins
cd ../../internal/providers
# Build each provider
for provider in playerctl wireplumber archlinuxpkgs bitwarden dnfpackages 1password bookmarks bluetooth nirisessions niriactions calc clipboard desktopapplications files menus providerlist runner snippets symbols todo unicode websearch windows; do
cd $provider
go build -ldflags="-s -w" -buildvcs=false -buildmode=plugin -trimpath
cd ..
done
}
package() {
# Install main elephant binary
cd elephant-${pkgver}/cmd/elephant
install -Dm 755 elephant -t "${pkgdir}/usr/bin"
# Install all provider plugins
cd ../../internal/providers
for provider in playerctl wireplumber archlinuxpkgs bitwarden dnfpackages bookmarks 1password nirisessions niriactions bluetooth calc clipboard desktopapplications files menus providerlist runner snippets symbols todo unicode websearch windows; do
install -Dm 755 $provider/$provider.so -t "${pkgdir}/usr/lib/elephant"
done
# Install license
cd ../../
install -Dm 644 LICENSE -t "${pkgdir}/usr/share/licenses/${pkgname}"
}
@@ -1,13 +0,0 @@
{
"source": "local",
"upstream": {
"watch": {
"github": "abenz1267/elephant",
"pattern": "v(?P<version>[0-9]+(?:\\.[0-9]+)*)"
}
},
"origin": {
"aur": "elephant-archlinuxpkgs",
"commit": "659b81f1aa74a13fd2ebec222e19da2046d8e977"
}
}
-27
View File
@@ -1,27 +0,0 @@
# Maintainer: Andrej Benz <hello[at]benz[dot]dev>
pkgname=elephant-archlinuxpkgs
pkgver=2.22.1
pkgrel=1
pkgdesc='archlinuxpkgs provider for elephant'
url='https://github.com/abenz1267/elephant'
arch=('x86_64' 'aarch64')
license=('GPL')
makedepends=('go')
conflicts=('elephant-archlinuxpkgs')
provides=('elephant-archlinuxpkgs')
source=("${url}/archive/refs/tags/v${pkgver}.tar.gz")
sha256sums=('3d1d0d4c55ae531fa3f06406b96504b5165a0d7b53523d1f8351d9d93e457f44')
build() {
cd elephant-${pkgver}/internal/providers/archlinuxpkgs
go build -ldflags="-s -w" -buildvcs=false -buildmode=plugin -trimpath
}
package() {
cd elephant-${pkgver}/internal/providers/archlinuxpkgs
install -Dm 755 archlinuxpkgs.so -t "${pkgdir}/usr/lib/elephant"
cd ../../../
install -Dm 644 LICENSE -t "${pkgdir}/usr/share/licenses/${pkgname}"
}
@@ -1,13 +0,0 @@
{
"source": "local",
"upstream": {
"watch": {
"github": "abenz1267/elephant",
"pattern": "v(?P<version>[0-9]+(?:\\.[0-9]+)*)"
}
},
"origin": {
"aur": "elephant-bluetooth",
"commit": "42d9dd5424884c51b8fe6bf7692caf0a31007f05"
}
}
-27
View File
@@ -1,27 +0,0 @@
# Maintainer: Andrej Benz <hello[at]benz[dot]dev>
pkgname=elephant-bluetooth
pkgver=2.22.1
pkgrel=1
pkgdesc='bluetooth provider for elephant'
url='https://github.com/abenz1267/elephant'
arch=('x86_64' 'aarch64')
license=('GPL')
makedepends=('go')
conflicts=('elephant-bluetooth')
provides=('elephant-bluetooth')
source=("${url}/archive/refs/tags/v${pkgver}.tar.gz")
sha256sums=('3d1d0d4c55ae531fa3f06406b96504b5165a0d7b53523d1f8351d9d93e457f44')
build() {
cd elephant-${pkgver}/internal/providers/bluetooth
go build -ldflags="-s -w" -buildvcs=false -buildmode=plugin -trimpath
}
package() {
cd elephant-${pkgver}/internal/providers/bluetooth
install -Dm 755 bluetooth.so -t "${pkgdir}/usr/lib/elephant"
cd ../../../
install -Dm 644 LICENSE -t "${pkgdir}/usr/share/licenses/${pkgname}"
}
@@ -1,13 +0,0 @@
{
"source": "local",
"upstream": {
"watch": {
"github": "abenz1267/elephant",
"pattern": "v(?P<version>[0-9]+(?:\\.[0-9]+)*)"
}
},
"origin": {
"aur": "elephant-calc",
"commit": "84aba9e90bbd65af45f83168cd6c7bce6ec4322e"
}
}
-28
View File
@@ -1,28 +0,0 @@
# Maintainer: Andrej Benz <hello[at]benz[dot]dev>
pkgname=elephant-calc
pkgver=2.22.1
pkgrel=1
pkgdesc='calc provider for elephant'
url='https://github.com/abenz1267/elephant'
arch=('x86_64' 'aarch64')
license=('GPL')
depends=('libqalculate')
makedepends=('go')
conflicts=('elephant-calc')
provides=('elephant-calc')
source=("${url}/archive/refs/tags/v${pkgver}.tar.gz")
sha256sums=('3d1d0d4c55ae531fa3f06406b96504b5165a0d7b53523d1f8351d9d93e457f44')
build() {
cd elephant-${pkgver}/internal/providers/calc
go build -ldflags="-s -w" -buildvcs=false -buildmode=plugin -trimpath
}
package() {
cd elephant-${pkgver}/internal/providers/calc
install -Dm 755 calc.so -t "${pkgdir}/usr/lib/elephant"
cd ../../../
install -Dm 644 LICENSE -t "${pkgdir}/usr/share/licenses/${pkgname}"
}
@@ -1,13 +0,0 @@
{
"source": "local",
"upstream": {
"watch": {
"github": "abenz1267/elephant",
"pattern": "v(?P<version>[0-9]+(?:\\.[0-9]+)*)"
}
},
"origin": {
"aur": "elephant-clipboard",
"commit": "3176f9de1445e7115009383f9cdac116729fc7be"
}
}
-28
View File
@@ -1,28 +0,0 @@
# Maintainer: Andrej Benz <hello[at]benz[dot]dev>
pkgname=elephant-clipboard
pkgver=2.22.1
pkgrel=1
pkgdesc='clipboard provider for elephant'
url='https://github.com/abenz1267/elephant'
arch=('x86_64' 'aarch64')
license=('GPL')
depends=('wl-clipboard' 'imagemagick')
makedepends=('go')
conflicts=('elephant-clipboard')
provides=('elephant-clipboard')
source=("${url}/archive/refs/tags/v${pkgver}.tar.gz")
sha256sums=('3d1d0d4c55ae531fa3f06406b96504b5165a0d7b53523d1f8351d9d93e457f44')
build() {
cd elephant-${pkgver}/internal/providers/clipboard
go build -ldflags="-s -w" -buildvcs=false -buildmode=plugin -trimpath
}
package() {
cd elephant-${pkgver}/internal/providers/clipboard
install -Dm 755 clipboard.so -t "${pkgdir}/usr/lib/elephant"
cd ../../../
install -Dm 644 LICENSE -t "${pkgdir}/usr/share/licenses/${pkgname}"
}
@@ -1,13 +0,0 @@
{
"source": "local",
"upstream": {
"watch": {
"github": "abenz1267/elephant",
"pattern": "v(?P<version>[0-9]+(?:\\.[0-9]+)*)"
}
},
"origin": {
"aur": "elephant-desktopapplications",
"commit": "c30e33db5fef912dec9aa157773b10ffab1e0307"
}
}
@@ -1,27 +0,0 @@
# Maintainer: Andrej Benz <hello[at]benz[dot]dev>
pkgname=elephant-desktopapplications
pkgver=2.22.1
pkgrel=1
pkgdesc='desktopapplications provider for elephant'
url='https://github.com/abenz1267/elephant'
arch=('x86_64' 'aarch64')
license=('GPL')
makedepends=('go')
conflicts=('elephant-desktopapplications')
provides=('elephant-desktopapplications')
source=("${url}/archive/refs/tags/v${pkgver}.tar.gz")
sha256sums=('3d1d0d4c55ae531fa3f06406b96504b5165a0d7b53523d1f8351d9d93e457f44')
build() {
cd elephant-${pkgver}/internal/providers/desktopapplications
go build -ldflags="-s -w" -buildvcs=false -buildmode=plugin -trimpath
}
package() {
cd elephant-${pkgver}/internal/providers/desktopapplications
install -Dm 755 desktopapplications.so -t "${pkgdir}/usr/lib/elephant"
cd ../../../
install -Dm 644 LICENSE -t "${pkgdir}/usr/share/licenses/${pkgname}"
}
@@ -1,13 +0,0 @@
{
"source": "local",
"upstream": {
"watch": {
"github": "abenz1267/elephant",
"pattern": "v(?P<version>[0-9]+(?:\\.[0-9]+)*)"
}
},
"origin": {
"aur": "elephant-files",
"commit": "2d16502b7a905e7d7a03e0026c5519c3b3c4abf2"
}
}
-28
View File
@@ -1,28 +0,0 @@
# Maintainer: Andrej Benz <hello[at]benz[dot]dev>
pkgname=elephant-files
pkgver=2.22.1
pkgrel=1
pkgdesc='files provider for elephant'
url='https://github.com/abenz1267/elephant'
arch=('x86_64' 'aarch64')
license=('GPL')
depends=('fd')
makedepends=('go')
conflicts=('elephant-files')
provides=('elephant-files')
source=("${url}/archive/refs/tags/v${pkgver}.tar.gz")
sha256sums=('3d1d0d4c55ae531fa3f06406b96504b5165a0d7b53523d1f8351d9d93e457f44')
build() {
cd elephant-${pkgver}/internal/providers/files
go build -ldflags="-s -w" -buildvcs=false -buildmode=plugin -trimpath
}
package() {
cd elephant-${pkgver}/internal/providers/files
install -Dm 755 files.so -t "${pkgdir}/usr/lib/elephant"
cd ../../../
install -Dm 644 LICENSE -t "${pkgdir}/usr/share/licenses/${pkgname}"
}
@@ -1,13 +0,0 @@
{
"source": "local",
"upstream": {
"watch": {
"github": "abenz1267/elephant",
"pattern": "v(?P<version>[0-9]+(?:\\.[0-9]+)*)"
}
},
"origin": {
"aur": "elephant-menus",
"commit": "5ab583fee6ba3e387d49ffe4e409bb84bc60ea24"
}
}
-27
View File
@@ -1,27 +0,0 @@
# Maintainer: Andrej Benz <hello[at]benz[dot]dev>
pkgname=elephant-menus
pkgver=2.22.1
pkgrel=1
pkgdesc='menus provider for elephant'
url='https://github.com/abenz1267/elephant'
arch=('x86_64' 'aarch64')
license=('GPL')
makedepends=('go')
conflicts=('elephant-menus')
provides=('elephant-menus')
source=("${url}/archive/refs/tags/v${pkgver}.tar.gz")
sha256sums=('3d1d0d4c55ae531fa3f06406b96504b5165a0d7b53523d1f8351d9d93e457f44')
build() {
cd elephant-${pkgver}/internal/providers/menus
go build -ldflags="-s -w" -buildvcs=false -buildmode=plugin -trimpath
}
package() {
cd elephant-${pkgver}/internal/providers/menus
install -Dm 755 menus.so -t "${pkgdir}/usr/lib/elephant"
cd ../../../
install -Dm 644 LICENSE -t "${pkgdir}/usr/share/licenses/${pkgname}"
}
@@ -1,13 +0,0 @@
{
"source": "local",
"upstream": {
"watch": {
"github": "abenz1267/elephant",
"pattern": "v(?P<version>[0-9]+(?:\\.[0-9]+)*)"
}
},
"origin": {
"aur": "elephant-providerlist",
"commit": "1d756a138e926a81b9d33265f0197b8661168845"
}
}
-27
View File
@@ -1,27 +0,0 @@
# Maintainer: Andrej Benz <hello[at]benz[dot]dev>
pkgname=elephant-providerlist
pkgver=2.22.1
pkgrel=1
pkgdesc='providerlist provider for elephant'
url='https://github.com/abenz1267/elephant'
arch=('x86_64' 'aarch64')
license=('GPL')
makedepends=('go')
conflicts=('elephant-providerlist')
provides=('elephant-providerlist')
source=("${url}/archive/refs/tags/v${pkgver}.tar.gz")
sha256sums=('3d1d0d4c55ae531fa3f06406b96504b5165a0d7b53523d1f8351d9d93e457f44')
build() {
cd elephant-${pkgver}/internal/providers/providerlist
go build -ldflags="-s -w" -buildvcs=false -buildmode=plugin -trimpath
}
package() {
cd elephant-${pkgver}/internal/providers/providerlist
install -Dm 755 providerlist.so -t "${pkgdir}/usr/lib/elephant"
cd ../../../
install -Dm 644 LICENSE -t "${pkgdir}/usr/share/licenses/${pkgname}"
}
@@ -1,13 +0,0 @@
{
"source": "local",
"upstream": {
"watch": {
"github": "abenz1267/elephant",
"pattern": "v(?P<version>[0-9]+(?:\\.[0-9]+)*)"
}
},
"origin": {
"aur": "elephant-runner",
"commit": "e47641530d912199372204cf8780ef37f99f0b37"
}
}
-27
View File
@@ -1,27 +0,0 @@
# Maintainer: Andrej Benz <hello[at]benz[dot]dev>
pkgname=elephant-runner
pkgver=2.22.1
pkgrel=1
pkgdesc='runner provider for elephant'
url='https://github.com/abenz1267/elephant'
arch=('x86_64' 'aarch64')
license=('GPL')
makedepends=('go')
conflicts=('elephant-runner')
provides=('elephant-runner')
source=("${url}/archive/refs/tags/v${pkgver}.tar.gz")
sha256sums=('3d1d0d4c55ae531fa3f06406b96504b5165a0d7b53523d1f8351d9d93e457f44')
build() {
cd elephant-${pkgver}/internal/providers/runner
go build -ldflags="-s -w" -buildvcs=false -buildmode=plugin -trimpath
}
package() {
cd elephant-${pkgver}/internal/providers/runner
install -Dm 755 runner.so -t "${pkgdir}/usr/lib/elephant"
cd ../../../
install -Dm 644 LICENSE -t "${pkgdir}/usr/share/licenses/${pkgname}"
}
@@ -1,13 +0,0 @@
{
"source": "local",
"upstream": {
"watch": {
"github": "abenz1267/elephant",
"pattern": "v(?P<version>[0-9]+(?:\\.[0-9]+)*)"
}
},
"origin": {
"aur": "elephant-symbols",
"commit": "32263e8b71390ab38b8aa1fd82fc2595b41a5157"
}
}
-27
View File
@@ -1,27 +0,0 @@
# Maintainer: Andrej Benz <hello[at]benz[dot]dev>
pkgname=elephant-symbols
pkgver=2.22.1
pkgrel=1
pkgdesc='symbols provider for elephant'
url='https://github.com/abenz1267/elephant'
arch=('x86_64' 'aarch64')
license=('GPL')
makedepends=('go')
conflicts=('elephant-symbols')
provides=('elephant-symbols')
source=("${url}/archive/refs/tags/v${pkgver}.tar.gz")
sha256sums=('3d1d0d4c55ae531fa3f06406b96504b5165a0d7b53523d1f8351d9d93e457f44')
build() {
cd elephant-${pkgver}/internal/providers/symbols
go build -ldflags="-s -w" -buildvcs=false -buildmode=plugin -trimpath
}
package() {
cd elephant-${pkgver}/internal/providers/symbols
install -Dm 755 symbols.so -t "${pkgdir}/usr/lib/elephant"
cd ../../../
install -Dm 644 LICENSE -t "${pkgdir}/usr/share/licenses/${pkgname}"
}
@@ -1,13 +0,0 @@
{
"source": "local",
"upstream": {
"watch": {
"github": "abenz1267/elephant",
"pattern": "v(?P<version>[0-9]+(?:\\.[0-9]+)*)"
}
},
"origin": {
"aur": "elephant-todo",
"commit": "447978df5ea3afd1e10959d7973c0b35367724c3"
}
}
-27
View File
@@ -1,27 +0,0 @@
# Maintainer: Andrej Benz <hello[at]benz[dot]dev>
pkgname=elephant-todo
pkgver=2.22.1
pkgrel=1
pkgdesc='todo provider for elephant'
url='https://github.com/abenz1267/elephant'
arch=('x86_64' 'aarch64')
license=('GPL')
makedepends=('go')
conflicts=('elephant-todo')
provides=('elephant-todo')
source=("${url}/archive/refs/tags/v${pkgver}.tar.gz")
sha256sums=('3d1d0d4c55ae531fa3f06406b96504b5165a0d7b53523d1f8351d9d93e457f44')
build() {
cd elephant-${pkgver}/internal/providers/todo
go build -ldflags="-s -w" -buildvcs=false -buildmode=plugin -trimpath
}
package() {
cd elephant-${pkgver}/internal/providers/todo
install -Dm 755 todo.so -t "${pkgdir}/usr/lib/elephant"
cd ../../../
install -Dm 644 LICENSE -t "${pkgdir}/usr/share/licenses/${pkgname}"
}
@@ -1,13 +0,0 @@
{
"source": "local",
"upstream": {
"watch": {
"github": "abenz1267/elephant",
"pattern": "v(?P<version>[0-9]+(?:\\.[0-9]+)*)"
}
},
"origin": {
"aur": "elephant-unicode",
"commit": "23222b0d304a234c74f630c5b9176d58c6c6e0b3"
}
}
-27
View File
@@ -1,27 +0,0 @@
# Maintainer: Andrej Benz <hello[at]benz[dot]dev>
pkgname=elephant-unicode
pkgver=2.22.1
pkgrel=1
pkgdesc='unicode provider for elephant'
url='https://github.com/abenz1267/elephant'
arch=('x86_64' 'aarch64')
license=('GPL')
makedepends=('go')
conflicts=('elephant-unicode')
provides=('elephant-unicode')
source=("${url}/archive/refs/tags/v${pkgver}.tar.gz")
sha256sums=('3d1d0d4c55ae531fa3f06406b96504b5165a0d7b53523d1f8351d9d93e457f44')
build() {
cd elephant-${pkgver}/internal/providers/unicode
go build -ldflags="-s -w" -buildvcs=false -buildmode=plugin -trimpath
}
package() {
cd elephant-${pkgver}/internal/providers/unicode
install -Dm 755 unicode.so -t "${pkgdir}/usr/lib/elephant"
cd ../../../
install -Dm 644 LICENSE -t "${pkgdir}/usr/share/licenses/${pkgname}"
}
@@ -1,13 +0,0 @@
{
"source": "local",
"upstream": {
"watch": {
"github": "abenz1267/elephant",
"pattern": "v(?P<version>[0-9]+(?:\\.[0-9]+)*)"
}
},
"origin": {
"aur": "elephant-websearch",
"commit": "6b5da831da33e3533593823826a8ffb1a008a299"
}
}
Loaded 100 of 582 files, more files were not shown because too many files have changed in this diff. Show more