Merge pull request #149 from omacom-io/codex-desktop-upstream-feed
Track ChatGPT desktop from OpenAI's feed instead of the AUR
This commit is contained in:
@@ -0,0 +1,95 @@
|
|||||||
|
name: Sync Upstream Releases
|
||||||
|
|
||||||
|
on:
|
||||||
|
schedule:
|
||||||
|
# Every 6 hours, off the hour to dodge the scheduling backlog at :00
|
||||||
|
- cron: '20 */6 * * *'
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
packages:
|
||||||
|
description: 'Specific packages to update (space-separated, leave empty for all)'
|
||||||
|
required: false
|
||||||
|
default: ''
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
sync:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
pull-requests: write
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
|
|
||||||
|
# Runs in an Arch container for vercmp: whether a release is an upgrade has
|
||||||
|
# to be decided by the same comparator pacman will use on users' machines.
|
||||||
|
- name: Update packages from upstream release feeds
|
||||||
|
run: |
|
||||||
|
docker run --rm \
|
||||||
|
-e PACKAGES="$PACKAGES" \
|
||||||
|
-e HOST_UID="$(id -u)" \
|
||||||
|
-e HOST_GID="$(id -g)" \
|
||||||
|
-v "$PWD/bin:/workspace/bin:ro" \
|
||||||
|
-v "$PWD/helpers:/workspace/helpers:ro" \
|
||||||
|
-v "$PWD/pkgbuilds:/workspace/pkgbuilds" \
|
||||||
|
-w /workspace \
|
||||||
|
archlinux:base-devel bash -lc '
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
pacman -Syu --noconfirm jq
|
||||||
|
|
||||||
|
groupadd -g "$HOST_GID" runner
|
||||||
|
useradd -m -u "$HOST_UID" -g "$HOST_GID" runner
|
||||||
|
chown -R runner:runner /workspace/pkgbuilds
|
||||||
|
|
||||||
|
if [[ -n "${PACKAGES:-}" ]]; then
|
||||||
|
read -r -a package_args <<< "$PACKAGES"
|
||||||
|
runuser -u runner -- ./bin/sync-upstream "${package_args[@]}"
|
||||||
|
else
|
||||||
|
runuser -u runner -- ./bin/sync-upstream
|
||||||
|
fi
|
||||||
|
'
|
||||||
|
env:
|
||||||
|
PACKAGES: ${{ github.event.inputs.packages }}
|
||||||
|
|
||||||
|
- name: Check for changes
|
||||||
|
id: changes
|
||||||
|
run: |
|
||||||
|
if [ -z "$(git status --porcelain)" ]; then
|
||||||
|
echo "has_changes=false" >> "$GITHUB_OUTPUT"
|
||||||
|
else
|
||||||
|
echo "has_changes=true" >> "$GITHUB_OUTPUT"
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Create Pull Request
|
||||||
|
if: steps.changes.outputs.has_changes == 'true'
|
||||||
|
uses: peter-evans/create-pull-request@v7
|
||||||
|
with:
|
||||||
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
commit-message: 'chore: sync upstream releases'
|
||||||
|
title: 'chore: sync upstream releases'
|
||||||
|
body: |
|
||||||
|
Automated update of packages that track an upstream vendor release
|
||||||
|
feed rather than the AUR.
|
||||||
|
|
||||||
|
Each package reports its newest release through
|
||||||
|
`.omarchy/upstream.sh`.
|
||||||
|
branch: auto/sync-upstream
|
||||||
|
delete-branch: true
|
||||||
|
labels: automated
|
||||||
|
reviewers: ryanrhughes
|
||||||
|
|
||||||
|
- name: Notify Basecamp on failure
|
||||||
|
if: failure() && env.BASECAMP_CHATBOT_URL != ''
|
||||||
|
env:
|
||||||
|
BASECAMP_CHATBOT_URL: ${{ secrets.BASECAMP_CHATBOT_URL }}
|
||||||
|
run: |
|
||||||
|
curl -s -o /dev/null \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d "$(jq -n --arg content \
|
||||||
|
"🔴 <strong>Upstream sync failed</strong><br><a href=\"${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}\">View run</a>" \
|
||||||
|
'{content: $content}')" \
|
||||||
|
"$BASECAMP_CHATBOT_URL"
|
||||||
@@ -15,6 +15,7 @@ The filesystem no longer encodes release policy. Instead:
|
|||||||
- all other packages reach `stable` by promoting tested edge artifacts with `bin/repo migrate`
|
- all other packages reach `stable` by promoting tested edge artifacts with `bin/repo migrate`
|
||||||
- AUR sync behavior is controlled by `source`, `sync`, `aur`, patches, and hooks in `.omarchy/`
|
- AUR sync behavior is controlled by `source`, `sync`, `aur`, patches, and hooks in `.omarchy/`
|
||||||
- packages can opt out of unscoped builds with `skip_build`; explicit `--package` builds remain available
|
- packages can opt out of unscoped builds with `skip_build`; explicit `--package` builds remain available
|
||||||
|
- packages that follow a vendor release feed instead of the AUR carry an `.omarchy/upstream.sh` hook
|
||||||
|
|
||||||
## Prerequisites
|
## Prerequisites
|
||||||
### aarch64 Builds (Optional)
|
### aarch64 Builds (Optional)
|
||||||
@@ -248,6 +249,40 @@ bin/sync-aur yay v4l2-relayd # Sync specific packages
|
|||||||
|
|
||||||
AUR sync is metadata-driven. It preserves `.omarchy/`, replaces the package root with AUR contents, applies `.omarchy/patches/*.patch`, runs `.omarchy/post-sync.sh` when present, applies pkgrel metadata, removes AUR-only `.SRCINFO` and `.gitignore` files, and records `upstream_commit`.
|
AUR sync is metadata-driven. It preserves `.omarchy/`, replaces the package root with AUR contents, applies `.omarchy/patches/*.patch`, runs `.omarchy/post-sync.sh` when present, applies pkgrel metadata, removes AUR-only `.SRCINFO` and `.gitignore` files, and records `upstream_commit`.
|
||||||
|
|
||||||
|
### Sync Upstream Releases
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bin/sync-upstream # Update every package with an upstream hook
|
||||||
|
bin/sync-upstream openai-codex-desktop # Update specific packages
|
||||||
|
```
|
||||||
|
|
||||||
|
Some vendors publish a release feed of their own that is faster and more precise
|
||||||
|
than the AUR packaging of it. Those packages are `source: local` — Omarchy owns
|
||||||
|
the PKGBUILD — and provide `.omarchy/upstream.sh`, a hook that reports the newest
|
||||||
|
upstream release as JSON on stdout:
|
||||||
|
|
||||||
|
```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.
|
||||||
|
|
||||||
### Other
|
### Other
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@@ -260,6 +295,7 @@ bin/repo push # Upload local builds to the host and publi
|
|||||||
bin/add-package <package> # Add an AUR/local package with metadata
|
bin/add-package <package> # Add an AUR/local package with metadata
|
||||||
bin/package-worktree <package> # Create upstream/patched/current scratch workspace
|
bin/package-worktree <package> # Create upstream/patched/current scratch workspace
|
||||||
bin/repo remove <package> # Remove package
|
bin/repo remove <package> # Remove package
|
||||||
|
bin/sync-upstream # Update packages that track a vendor release feed
|
||||||
bin/clean-docker # Clear Docker images/cache (forces fresh rebuild)
|
bin/clean-docker # Clear Docker images/cache (forces fresh rebuild)
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -345,7 +381,8 @@ omarchy-pkgs/
|
|||||||
│ └── .omarchy/
|
│ └── .omarchy/
|
||||||
│ ├── package.json # Source/sync/release metadata
|
│ ├── package.json # Source/sync/release metadata
|
||||||
│ ├── patches/ # Omarchy patches reapplied after AUR sync
|
│ ├── patches/ # Omarchy patches reapplied after AUR sync
|
||||||
│ └── post-sync.sh # Optional dynamic post-sync customization hook
|
│ ├── post-sync.sh # Optional dynamic post-sync customization hook
|
||||||
|
│ └── upstream.sh # Optional vendor release feed hook (non-AUR packages)
|
||||||
├── build/
|
├── build/
|
||||||
├── build-output/ # Unsigned packages (temporary)
|
├── build-output/ # Unsigned packages (temporary)
|
||||||
│ ├── edge/
|
│ ├── edge/
|
||||||
@@ -396,7 +433,7 @@ Minimal examples:
|
|||||||
|
|
||||||
Fields:
|
Fields:
|
||||||
|
|
||||||
- `source`: `aur` or `local`
|
- `source`: `aur` or `local`. A `local` package can still follow an upstream release with an `.omarchy/upstream.sh` hook.
|
||||||
- `sync`: optional for AUR packages; defaults to `true`. Set `false` for AUR-origin packages that Omarchy maintains manually.
|
- `sync`: optional for AUR packages; defaults to `true`. Set `false` for AUR-origin packages that Omarchy maintains manually.
|
||||||
- `aur`: optional AUR package name when it differs from the local package directory, usually for split packages.
|
- `aur`: optional AUR package name when it differs from the local package directory, usually for split packages.
|
||||||
- `release_ring`: optional. `fast` means the package is built directly for stable as well as edge. Packages without a ring build in edge and reach stable through tested artifact promotion (`bin/repo migrate`).
|
- `release_ring`: optional. `fast` means the package is built directly for stable as well as edge. Packages without a ring build in edge and reach stable through tested artifact promotion (`bin/repo migrate`).
|
||||||
@@ -539,6 +576,7 @@ The repository includes GitHub workflows and systemd services for automated rele
|
|||||||
#### GitHub Workflows
|
#### GitHub Workflows
|
||||||
|
|
||||||
1. **sync-aur.yml** (Every 6 hours): Syncs AUR packages according to `.omarchy/package.json` and opens a PR when changes are found.
|
1. **sync-aur.yml** (Every 6 hours): Syncs AUR packages according to `.omarchy/package.json` and opens a PR when changes are found.
|
||||||
|
2. **sync-upstream.yml** (Every 6 hours): Runs `.omarchy/upstream.sh` for packages that track a vendor release feed and opens a PR when a newer version is out.
|
||||||
|
|
||||||
#### Systemd Services
|
#### Systemd Services
|
||||||
|
|
||||||
|
|||||||
Executable
+390
@@ -0,0 +1,390 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
BUILD_ROOT=$(realpath "${BASH_SOURCE[0]%/*}/..")
|
||||||
|
source "$BUILD_ROOT/helpers/message-helpers.sh"
|
||||||
|
source "$BUILD_ROOT/helpers/paths.sh"
|
||||||
|
source "$BUILD_ROOT/helpers/package-metadata.sh"
|
||||||
|
|
||||||
|
TEMP_DIR=$(mktemp -d)
|
||||||
|
trap 'rm -rf "$TEMP_DIR"' EXIT
|
||||||
|
|
||||||
|
SPECIFIC_PACKAGES=()
|
||||||
|
|
||||||
|
usage() {
|
||||||
|
cat <<EOF
|
||||||
|
Usage: $0 [PACKAGE...]
|
||||||
|
|
||||||
|
Update packages that track an upstream vendor release feed instead of the AUR.
|
||||||
|
|
||||||
|
A package opts in by providing pkgbuilds/<package>/.omarchy/upstream.sh, a hook
|
||||||
|
that reports the newest upstream release as JSON on stdout:
|
||||||
|
|
||||||
|
{
|
||||||
|
"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. An empty object ({}) reports no update.
|
||||||
|
|
||||||
|
When the reported version is newer than the checked-in one, pkgver and the
|
||||||
|
listed checksum arrays are rewritten and pkgrel is reset to 1.
|
||||||
|
|
||||||
|
Arguments:
|
||||||
|
PACKAGE One or more package names to update (optional)
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
$0 # Update every package with an upstream hook
|
||||||
|
$0 openai-codex-desktop # Update specific packages
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
while [[ $# -gt 0 ]]; do
|
||||||
|
case "$1" in
|
||||||
|
-h|--help)
|
||||||
|
usage
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
--*)
|
||||||
|
print_error "Unknown option: $1"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
SPECIFIC_PACKAGES+=("$1")
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
if ! command -v vercmp >/dev/null 2>&1; then
|
||||||
|
print_error "vercmp not found: this needs pacman to decide whether a release is an upgrade"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
print_header "Upstream Package Sync"
|
||||||
|
|
||||||
|
UPDATED=0
|
||||||
|
SKIPPED=0
|
||||||
|
FAILED=0
|
||||||
|
SPECIFIC_MODE=false
|
||||||
|
|
||||||
|
get_pkgver() {
|
||||||
|
local package_dir="$1"
|
||||||
|
|
||||||
|
grep -m1 '^pkgver=' "$package_dir/PKGBUILD" | cut -d= -f2- | tr -d "\"'"
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_single_assignment() {
|
||||||
|
local pkgbuild="$1"
|
||||||
|
local pattern="$2"
|
||||||
|
local label="$3"
|
||||||
|
|
||||||
|
if [[ $(grep -c "$pattern" "$pkgbuild") -ne 1 ]]; then
|
||||||
|
print_error "Expected exactly one $label assignment in $pkgbuild"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
set_pkgbuild_scalar() {
|
||||||
|
local pkgbuild="$1"
|
||||||
|
local field="$2"
|
||||||
|
local value="$3"
|
||||||
|
|
||||||
|
assert_single_assignment "$pkgbuild" "^${field}=" "$field" || return 1
|
||||||
|
sed -i "s/^${field}=.*/${field}=${value}/" "$pkgbuild"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Replace an array assignment, however many lines the original spans.
|
||||||
|
set_pkgbuild_array() {
|
||||||
|
local pkgbuild="$1"
|
||||||
|
local name="$2"
|
||||||
|
shift 2
|
||||||
|
local values=("$@")
|
||||||
|
|
||||||
|
assert_single_assignment "$pkgbuild" "^${name}=(" "$name" || return 1
|
||||||
|
|
||||||
|
if [[ ${#values[@]} -eq 0 ]]; then
|
||||||
|
print_error "No values to write for ${name}"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
local block="$TEMP_DIR/array-block"
|
||||||
|
if [[ ${#values[@]} -eq 1 ]]; then
|
||||||
|
printf "%s=('%s')\n" "$name" "${values[0]}" > "$block" || return 1
|
||||||
|
else
|
||||||
|
{
|
||||||
|
printf '%s=(\n' "$name"
|
||||||
|
printf " '%s'\n" "${values[@]}"
|
||||||
|
printf ')\n'
|
||||||
|
} > "$block" || return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
local rewritten="$TEMP_DIR/pkgbuild-rewritten"
|
||||||
|
if ! awk -v prefix="${name}=(" -v block="$block" '
|
||||||
|
!replaced && index($0, prefix) == 1 {
|
||||||
|
while ((getline line < block) > 0) print line
|
||||||
|
close(block)
|
||||||
|
replaced = 1
|
||||||
|
# A ")" anywhere past the opening closes the array; testing for one at end
|
||||||
|
# of line instead would treat a trailing comment as a continuation and eat
|
||||||
|
# every line up to the next ")".
|
||||||
|
if (index(substr($0, length(prefix) + 1), ")") == 0) skipping = 1
|
||||||
|
next
|
||||||
|
}
|
||||||
|
skipping { if ($0 ~ /\)/) skipping = 0; next }
|
||||||
|
{ print }
|
||||||
|
' "$pkgbuild" > "$rewritten"; then
|
||||||
|
print_error "Failed to rewrite ${name} in $pkgbuild"
|
||||||
|
rm -f "$rewritten"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
mv "$rewritten" "$pkgbuild"
|
||||||
|
}
|
||||||
|
|
||||||
|
# pacman's own comparator, because nothing else agrees with it at the corners:
|
||||||
|
# sort -V calls 1.0a newer than 1.0, vercmp calls it older, and pacman is what
|
||||||
|
# decides whether a published package is an upgrade.
|
||||||
|
version_is_newer() {
|
||||||
|
local candidate="$1"
|
||||||
|
local current="$2"
|
||||||
|
|
||||||
|
[[ "$candidate" != "$current" ]] || return 1
|
||||||
|
[[ "$(vercmp "$candidate" "$current")" -gt 0 ]]
|
||||||
|
}
|
||||||
|
|
||||||
|
validate_release() {
|
||||||
|
local release="$1"
|
||||||
|
|
||||||
|
# pkgver is written into the PKGBUILD, which makepkg sources as shell, so it
|
||||||
|
# is held to pacman's own character set rather than merely being non-empty.
|
||||||
|
# The anchors are \A and \z, not ^ and $: jq's $ also matches before a
|
||||||
|
# trailing newline, which would let "1.0\n" through and break the rewrite.
|
||||||
|
jq -e '
|
||||||
|
(.pkgver | type == "string" and test("\\A[A-Za-z0-9._+]+\\z"))
|
||||||
|
and (.sha256sums | type == "object" and length > 0)
|
||||||
|
and (.sha256sums | to_entries | all(
|
||||||
|
.key | test("\\A[a-z0-9_]+\\z")
|
||||||
|
))
|
||||||
|
and (.sha256sums | to_entries | all(
|
||||||
|
.value | type == "array" and length > 0 and all(test("\\A[0-9a-f]{64}\\z"))
|
||||||
|
))
|
||||||
|
' <<<"$release" >/dev/null
|
||||||
|
}
|
||||||
|
|
||||||
|
# Confirm the rewritten PKGBUILD parses and actually holds what we meant to put
|
||||||
|
# in it. Editing shell with awk and sed can go wrong in ways no amount of
|
||||||
|
# pattern-matching anticipates -- an array element carrying a ")" in a comment,
|
||||||
|
# say -- so the result is checked rather than trusted.
|
||||||
|
verify_pkgbuild() {
|
||||||
|
local pkgbuild="$1"
|
||||||
|
local release="$2"
|
||||||
|
local pkgver="$3"
|
||||||
|
shift 3
|
||||||
|
local arrays=("$@")
|
||||||
|
|
||||||
|
if ! bash -n "$pkgbuild" 2>/dev/null; then
|
||||||
|
print_error "Rewritten PKGBUILD is not valid shell"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
local dump
|
||||||
|
if ! dump=$(CARCH=x86_64 bash -c '
|
||||||
|
source "$1" >/dev/null 2>&1 || exit 1
|
||||||
|
printf "pkgver\t%s\n" "$pkgver"
|
||||||
|
printf "pkgrel\t%s\n" "$pkgrel"
|
||||||
|
for name in "${@:2}"; do
|
||||||
|
declare -n array="$name"
|
||||||
|
printf "%s\t%s\n" "$name" "${array[*]}"
|
||||||
|
done
|
||||||
|
' _ "$pkgbuild" "${arrays[@]}" 2>/dev/null); then
|
||||||
|
print_error "Rewritten PKGBUILD could not be read back"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
local expected
|
||||||
|
expected=$(
|
||||||
|
printf 'pkgver\t%s\n' "$pkgver"
|
||||||
|
printf 'pkgrel\t1\n'
|
||||||
|
local array arch
|
||||||
|
for array in "${arrays[@]}"; do
|
||||||
|
arch="${array#sha256sums}"
|
||||||
|
arch="${arch#_}"
|
||||||
|
[[ -n "$arch" ]] || arch="any"
|
||||||
|
printf '%s\t%s\n' "$array" \
|
||||||
|
"$(jq -r --arg arch "$arch" '.sha256sums[$arch] | join(" ")' <<<"$release")"
|
||||||
|
done
|
||||||
|
)
|
||||||
|
|
||||||
|
if [[ "$dump" != "$expected" ]]; then
|
||||||
|
print_error "Rewritten PKGBUILD does not hold the reported release"
|
||||||
|
diff <(echo "$expected") <(echo "$dump") | sed 's/^/ /' >&2 || true
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
apply_release() {
|
||||||
|
local package_dir="$1"
|
||||||
|
local release="$2"
|
||||||
|
local pkgver="$3"
|
||||||
|
local pkgbuild="$package_dir/PKGBUILD"
|
||||||
|
|
||||||
|
local arch array values
|
||||||
|
local arrays=()
|
||||||
|
|
||||||
|
while IFS= read -r arch; do
|
||||||
|
if [[ "$arch" == "any" ]]; then
|
||||||
|
array="sha256sums"
|
||||||
|
else
|
||||||
|
array="sha256sums_$arch"
|
||||||
|
fi
|
||||||
|
arrays+=("$array")
|
||||||
|
done < <(jq -r '.sha256sums | keys[]' <<<"$release")
|
||||||
|
|
||||||
|
# validate_release guarantees at least one entry, so an empty list here means
|
||||||
|
# jq died inside the process substitution rather than that there is nothing
|
||||||
|
# to do.
|
||||||
|
if [[ ${#arrays[@]} -eq 0 ]]; then
|
||||||
|
print_error "Could not read the checksum architectures from the reported release"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Every edit lands on a scratch copy that replaces the PKGBUILD in one rename
|
||||||
|
# at the end, so a failure part way through leaves the original untouched
|
||||||
|
# rather than half updated.
|
||||||
|
local scratch="$pkgbuild.sync-upstream"
|
||||||
|
cp "$pkgbuild" "$scratch" || return 1
|
||||||
|
|
||||||
|
if ! (
|
||||||
|
assert_single_assignment "$scratch" '^pkgver=' pkgver || exit 1
|
||||||
|
assert_single_assignment "$scratch" '^pkgrel=' pkgrel || exit 1
|
||||||
|
|
||||||
|
for array in "${arrays[@]}"; do
|
||||||
|
arch="${array#sha256sums}"
|
||||||
|
arch="${arch#_}"
|
||||||
|
[[ -n "$arch" ]] || arch="any"
|
||||||
|
|
||||||
|
mapfile -t values < <(jq -r --arg arch "$arch" '.sha256sums[$arch][]' <<<"$release")
|
||||||
|
set_pkgbuild_array "$scratch" "$array" "${values[@]}" || exit 1
|
||||||
|
done
|
||||||
|
|
||||||
|
set_pkgbuild_scalar "$scratch" pkgver "$pkgver" || exit 1
|
||||||
|
set_pkgbuild_scalar "$scratch" pkgrel 1 || exit 1
|
||||||
|
|
||||||
|
verify_pkgbuild "$scratch" "$release" "$pkgver" "${arrays[@]}" || exit 1
|
||||||
|
); then
|
||||||
|
rm -f "$scratch"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
chmod --reference="$pkgbuild" "$scratch"
|
||||||
|
mv "$scratch" "$pkgbuild"
|
||||||
|
}
|
||||||
|
|
||||||
|
sync_package() {
|
||||||
|
local package="$1"
|
||||||
|
local package_dir="$PKGBUILDS_DIR/$package"
|
||||||
|
local hook="$package_dir/.omarchy/upstream.sh"
|
||||||
|
|
||||||
|
if [[ ! -f "$package_dir/PKGBUILD" ]]; then
|
||||||
|
print_error "Package $package has no PKGBUILD"
|
||||||
|
((++FAILED))
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ ! -f "$hook" ]]; then
|
||||||
|
if [[ "$SPECIFIC_MODE" == true ]]; then
|
||||||
|
print_error "Package $package is missing .omarchy/upstream.sh"
|
||||||
|
((++FAILED))
|
||||||
|
else
|
||||||
|
print_info "Skipping $package: no upstream hook"
|
||||||
|
((++SKIPPED))
|
||||||
|
fi
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
print_info "Checking $package for upstream releases..."
|
||||||
|
|
||||||
|
local release
|
||||||
|
if ! release=$(cd "$package_dir" && PACKAGE_NAME="$package" bash .omarchy/upstream.sh); then
|
||||||
|
print_error "Upstream hook failed for $package"
|
||||||
|
((++FAILED))
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! jq -e . >/dev/null 2>&1 <<<"$release"; then
|
||||||
|
print_error "Upstream hook for $package did not report valid JSON"
|
||||||
|
((++FAILED))
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$(jq -r 'has("pkgver")' <<<"$release")" == "false" ]]; then
|
||||||
|
print_info " No upstream update reported"
|
||||||
|
((++SKIPPED))
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! validate_release "$release"; then
|
||||||
|
print_error "Upstream hook for $package reported a malformed release"
|
||||||
|
((++FAILED))
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
local pkgver current_pkgver
|
||||||
|
pkgver=$(jq -r '.pkgver' <<<"$release")
|
||||||
|
current_pkgver=$(get_pkgver "$package_dir")
|
||||||
|
|
||||||
|
if [[ -z "$current_pkgver" ]]; then
|
||||||
|
print_error "Could not read pkgver from $package_dir/PKGBUILD"
|
||||||
|
((++FAILED))
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$pkgver" == "$current_pkgver" ]]; then
|
||||||
|
print_info " Already at $current_pkgver"
|
||||||
|
((++SKIPPED))
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! version_is_newer "$pkgver" "$current_pkgver"; then
|
||||||
|
print_warning " Upstream reports $pkgver, older than the checked-in $current_pkgver; leaving it alone"
|
||||||
|
((++SKIPPED))
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! apply_release "$package_dir" "$release" "$pkgver"; then
|
||||||
|
print_error "Failed to update $package"
|
||||||
|
((++FAILED))
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
print_success " $current_pkgver -> $pkgver"
|
||||||
|
((++UPDATED))
|
||||||
|
}
|
||||||
|
|
||||||
|
if [[ ${#SPECIFIC_PACKAGES[@]} -gt 0 ]]; then
|
||||||
|
SPECIFIC_MODE=true
|
||||||
|
for package in "${SPECIFIC_PACKAGES[@]}"; do
|
||||||
|
sync_package "$package"
|
||||||
|
done
|
||||||
|
else
|
||||||
|
while IFS= read -r package; do
|
||||||
|
sync_package "$package"
|
||||||
|
done < <(packages_for_upstream_sync)
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
if [[ $FAILED -gt 0 ]]; then
|
||||||
|
print_error "Upstream sync completed with failures"
|
||||||
|
else
|
||||||
|
print_success "Upstream sync complete!"
|
||||||
|
fi
|
||||||
|
echo " Target: $PKGBUILDS_DIR"
|
||||||
|
echo " Updated: $UPDATED"
|
||||||
|
echo " Skipped: $SKIPPED"
|
||||||
|
echo " Failed: $FAILED"
|
||||||
|
|
||||||
|
if [[ $FAILED -gt 0 ]]; then
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
@@ -135,6 +135,19 @@ packages_for_aur_sync() {
|
|||||||
done
|
done
|
||||||
}
|
}
|
||||||
|
|
||||||
|
package_has_upstream_hook() {
|
||||||
|
local pkgdir="$1"
|
||||||
|
[[ -f "$pkgdir/.omarchy/upstream.sh" ]]
|
||||||
|
}
|
||||||
|
|
||||||
|
packages_for_upstream_sync() {
|
||||||
|
package_dirs | while IFS= read -r pkgdir; do
|
||||||
|
if package_has_upstream_hook "$pkgdir"; then
|
||||||
|
basename "$pkgdir"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
packages_for_mirror() {
|
packages_for_mirror() {
|
||||||
local mirror="$1"
|
local mirror="$1"
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
{
|
{
|
||||||
"source": "aur",
|
"source": "local",
|
||||||
"release_ring": "fast",
|
"release_ring": "fast"
|
||||||
"upstream_commit": "05f12e3c51ce07efb996698d4fae94327f45f03b"
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,15 +0,0 @@
|
|||||||
--- a/PKGBUILD
|
|
||||||
+++ b/PKGBUILD
|
|
||||||
@@ -55,6 +55,12 @@
|
|
||||||
conflicts=('chatgpt')
|
|
||||||
options=('!debug' '!strip')
|
|
||||||
|
|
||||||
+# Omarchy: the build image compresses with zstd at its default level, which
|
|
||||||
+# leaves this 1.3 GB Electron tree at 447 MB. Maximum zstd takes it to 322 MB
|
|
||||||
+# for ~4 extra minutes of build time -- worth it for a package this large that
|
|
||||||
+# every user re-downloads on each of OpenAI's frequent releases.
|
|
||||||
+COMPRESSZST=(zstd -c -z -q --ultra -22 --threads=0 -)
|
|
||||||
+
|
|
||||||
_deb_x86_64="chatgpt_${pkgver}_amd64.deb"
|
|
||||||
_deb_aarch64="chatgpt_${pkgver}_arm64.deb"
|
|
||||||
source=('chatgpt-launcher.sh')
|
|
||||||
+66
@@ -0,0 +1,66 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# OpenAI ships the ChatGPT desktop app from its own Debian repository. The
|
||||||
|
# per-architecture package index carries both the version and the SHA256 of
|
||||||
|
# every deb, so an update costs two small HTTP requests instead of a 750 MB
|
||||||
|
# download, and the pool keeps old versions, so the URLs pinned in the PKGBUILD
|
||||||
|
# stay resolvable after the next release.
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
BASE_URL="https://persistent.oaistatic.com/codex-app-prod/linux/deb"
|
||||||
|
declare -A DEB_ARCHES=([x86_64]=amd64 [aarch64]=arm64)
|
||||||
|
|
||||||
|
# Print "<version> <sha256>" for the newest stanza in a Packages index. Newest
|
||||||
|
# is vercmp's opinion, which is the one bin/sync-upstream and pacman both use;
|
||||||
|
# sort -V disagrees with it over versions like 1.0a.
|
||||||
|
newest_release() {
|
||||||
|
local index="$1"
|
||||||
|
local version sha256 best_version="" best_sha256=""
|
||||||
|
|
||||||
|
while read -r version sha256; do
|
||||||
|
if [[ -z "$best_version" ]] || [[ "$(vercmp "$version" "$best_version")" -gt 0 ]]; then
|
||||||
|
best_version="$version"
|
||||||
|
best_sha256="$sha256"
|
||||||
|
fi
|
||||||
|
done < <(awk '
|
||||||
|
{ sub(/\r$/, "") }
|
||||||
|
/^Version:/ { version = $2 }
|
||||||
|
/^SHA256:/ { sha256 = $2 }
|
||||||
|
/^$/ { if (version && sha256) print version, sha256; version = sha256 = "" }
|
||||||
|
END { if (version && sha256) print version, sha256 }
|
||||||
|
' <<<"$index")
|
||||||
|
|
||||||
|
[[ -n "$best_version" ]] || return 1
|
||||||
|
echo "$best_version $best_sha256"
|
||||||
|
}
|
||||||
|
|
||||||
|
versions=()
|
||||||
|
declare -A checksums=()
|
||||||
|
|
||||||
|
for arch in "${!DEB_ARCHES[@]}"; do
|
||||||
|
index=$(curl -fsSL "$BASE_URL/dists/stable/main/binary-${DEB_ARCHES[$arch]}/Packages")
|
||||||
|
|
||||||
|
read -r version sha256 <<<"$(newest_release "$index")"
|
||||||
|
if [[ -z "${version:-}" || -z "${sha256:-}" ]]; then
|
||||||
|
echo "No usable release found for $arch in the upstream package index" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
versions+=("$version")
|
||||||
|
checksums[$arch]="$sha256"
|
||||||
|
done
|
||||||
|
|
||||||
|
# A release lands one architecture at a time, and a single pkgver has to cover
|
||||||
|
# both. Report no update until they agree; the next run picks it up.
|
||||||
|
for version in "${versions[@]}"; do
|
||||||
|
if [[ "$version" != "${versions[0]}" ]]; then
|
||||||
|
echo "Upstream architectures are mid-release (${versions[*]}); skipping" >&2
|
||||||
|
echo '{}'
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
jq -n \
|
||||||
|
--arg pkgver "${versions[0]}" \
|
||||||
|
--arg x86_64 "${checksums[x86_64]}" \
|
||||||
|
--arg aarch64 "${checksums[aarch64]}" \
|
||||||
|
'{pkgver: $pkgver, sha256sums: {x86_64: [$x86_64], aarch64: [$aarch64]}}'
|
||||||
@@ -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 +0,0 @@
|
|||||||
../LICENSE
|
|
||||||
@@ -1,10 +1,12 @@
|
|||||||
# Maintainer: Parsiad Azimzadeh <parsiad.azimzadeh at gmail dot com>
|
# Maintainer: David Heinemeier Hansson <david@hey.com>
|
||||||
# Maintainer: mothran
|
|
||||||
# Official downloads: https://chatgpt.com/codex/
|
# Omarchy tracks OpenAI's own Debian repository rather than the AUR, which lags
|
||||||
|
# behind releases that ship several times a week. .omarchy/upstream.sh rewrites
|
||||||
|
# the version and checksums below from that repository's package index.
|
||||||
|
|
||||||
pkgname=openai-codex-desktop
|
pkgname=openai-codex-desktop
|
||||||
pkgver=26.803.81509
|
pkgver=26.810.52044
|
||||||
pkgrel=8.1
|
pkgrel=1
|
||||||
pkgdesc="Official ChatGPT desktop app with Codex"
|
pkgdesc="Official ChatGPT desktop app with Codex"
|
||||||
arch=('x86_64' 'aarch64')
|
arch=('x86_64' 'aarch64')
|
||||||
url="https://chatgpt.com/codex/"
|
url="https://chatgpt.com/codex/"
|
||||||
@@ -64,16 +66,13 @@ COMPRESSZST=(zstd -c -z -q --ultra -22 --threads=0 -)
|
|||||||
_deb_x86_64="chatgpt_${pkgver}_amd64.deb"
|
_deb_x86_64="chatgpt_${pkgver}_amd64.deb"
|
||||||
_deb_aarch64="chatgpt_${pkgver}_arm64.deb"
|
_deb_aarch64="chatgpt_${pkgver}_arm64.deb"
|
||||||
source=('chatgpt-launcher.sh')
|
source=('chatgpt-launcher.sh')
|
||||||
source_x86_64=(
|
_pool="https://persistent.oaistatic.com/codex-app-prod/linux/deb/pool/main/c/chatgpt"
|
||||||
"${_deb_x86_64}::https://persistent.oaistatic.com/codex-app-prod/linux/deb/latest/chatgpt_amd64.deb"
|
source_x86_64=("${_deb_x86_64}::${_pool}/${_deb_x86_64}")
|
||||||
)
|
source_aarch64=("${_deb_aarch64}::${_pool}/${_deb_aarch64}")
|
||||||
source_aarch64=(
|
|
||||||
"${_deb_aarch64}::https://persistent.oaistatic.com/codex-app-prod/linux/deb/latest/chatgpt_arm64.deb"
|
|
||||||
)
|
|
||||||
noextract=("${_deb_x86_64}" "${_deb_aarch64}")
|
noextract=("${_deb_x86_64}" "${_deb_aarch64}")
|
||||||
sha256sums=('4e3ca9302600bed268f8fd3ba2c9ac2f1ceb99da139ed71c50db0289b118d06f')
|
sha256sums=('b3a4503b5931f102444bc7015c3cf4e40266cf034e0d682bd2a407dc5b3ee58c')
|
||||||
sha256sums_x86_64=('a9bf91a368f9f7c4eea38082a9fb8fb46b8d005b719a6d7715d2e5a1982c38eb')
|
sha256sums_x86_64=('708a15a1bb76e2bb7f0e376e5145391fa277ad3a64057c1d32537bdc2a1b4e6e')
|
||||||
sha256sums_aarch64=('f38fcc194eca9ab0327dc10c92340681eae77c5d75164df700384ce2adaccbc1')
|
sha256sums_aarch64=('6ebea681b1e494d218a199f638b4bc886e94e1458dd61079b1e390a6fb98fdd2')
|
||||||
|
|
||||||
package() {
|
package() {
|
||||||
cd "${srcdir}"
|
cd "${srcdir}"
|
||||||
|
|||||||
@@ -1,25 +0,0 @@
|
|||||||
version = 1
|
|
||||||
|
|
||||||
[[annotations]]
|
|
||||||
path = [
|
|
||||||
"PKGBUILD",
|
|
||||||
"README.md",
|
|
||||||
"keys/**",
|
|
||||||
".SRCINFO",
|
|
||||||
".gitignore",
|
|
||||||
".nvchecker.toml",
|
|
||||||
"*.install",
|
|
||||||
"*.sysusers",
|
|
||||||
"*sysusers.conf",
|
|
||||||
"*.tmpfiles",
|
|
||||||
"*tmpfiles.conf",
|
|
||||||
"*.logrotate",
|
|
||||||
"*.pam",
|
|
||||||
"*.service",
|
|
||||||
"*.socket",
|
|
||||||
"*.timer",
|
|
||||||
"*.desktop",
|
|
||||||
"*.hook",
|
|
||||||
]
|
|
||||||
SPDX-FileCopyrightText = "Arch Linux contributors"
|
|
||||||
SPDX-License-Identifier = "0BSD"
|
|
||||||
Regular → Executable
+23
-28
@@ -1,37 +1,32 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
# SPDX-FileCopyrightText: 2026 Arch Linux Contributors
|
|
||||||
# SPDX-License-Identifier: 0BSD
|
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
user_flags=()
|
user_flags=()
|
||||||
ozone_flags=()
|
|
||||||
|
|
||||||
config_home="${XDG_CONFIG_HOME:-}"
|
config_home="${XDG_CONFIG_HOME:-}"
|
||||||
if [[ -z "${config_home}" && -n "${HOME:-}" ]]; then
|
[[ -n "$config_home" || -z "${HOME:-}" ]] || config_home="$HOME/.config"
|
||||||
config_home="${HOME}/.config"
|
flags_file="${config_home:+$config_home/codex-flags.conf}"
|
||||||
|
|
||||||
|
if [[ -n "$flags_file" && -f "$flags_file" && -r "$flags_file" ]]; then
|
||||||
|
while IFS= read -r line || [[ -n "$line" ]]; do
|
||||||
|
line="${line%%#*}"
|
||||||
|
[[ -n "${line//[[:space:]]/}" ]] || continue
|
||||||
|
read -r -a flags <<<"$line"
|
||||||
|
user_flags+=("${flags[@]}")
|
||||||
|
done <"$flags_file"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ -n "${config_home}" && -f "${config_home}/codex-flags.conf" ]]; then
|
# Chromium's own Ozone detection falls back to XWayland often enough to matter,
|
||||||
while IFS= read -r flag_line || [[ -n "${flag_line}" ]]; do
|
# and the result is a blurry window on every scaled display. Ask for Wayland
|
||||||
flag_line="${flag_line%%#*}"
|
# directly, unless the user has already picked a platform themselves.
|
||||||
read -r -a flag_parts <<<"${flag_line}"
|
platform_flags=()
|
||||||
user_flags+=("${flag_parts[@]}")
|
if [[ -n "${WAYLAND_DISPLAY:-}" || "${XDG_SESSION_TYPE:-}" == wayland ]]; then
|
||||||
done <"${config_home}/codex-flags.conf"
|
platform_flags=(--ozone-platform=wayland)
|
||||||
|
|
||||||
|
for flag in "${user_flags[@]}" "$@"; do
|
||||||
|
case "$flag" in
|
||||||
|
--ozone-platform=* | --ozone-platform-hint=*) platform_flags=() ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Use native Wayland rendering in Wayland sessions. Chromium's automatic Ozone
|
exec /usr/lib/chatgpt/ChatGPT "${platform_flags[@]}" "${user_flags[@]}" "$@"
|
||||||
# selection can fall back to XWayland, which produces a blurry UI on scaled
|
|
||||||
# displays. An explicit platform argument from the user always takes precedence.
|
|
||||||
if [[ "${XDG_SESSION_TYPE:-}" == wayland || -n "${WAYLAND_DISPLAY:-}" ]]; then
|
|
||||||
ozone_flags=(--ozone-platform=wayland)
|
|
||||||
fi
|
|
||||||
|
|
||||||
# A platform selected in the flags file or on the command line overrides the
|
|
||||||
# native Wayland default.
|
|
||||||
for flag in "${user_flags[@]}" "$@"; do
|
|
||||||
case "${flag}" in
|
|
||||||
--ozone-platform=*|--ozone-platform-hint=*) ozone_flags=() ;;
|
|
||||||
esac
|
|
||||||
done
|
|
||||||
|
|
||||||
exec /usr/lib/chatgpt/ChatGPT "${ozone_flags[@]}" "${user_flags[@]}" "$@"
|
|
||||||
|
|||||||
Reference in New Issue
Block a user