Skip to content

Commit e25021a

Browse files
authored
feat(packaging): maintain AUR packages in-repo (#4128)
1 parent f071907 commit e25021a

9 files changed

Lines changed: 422 additions & 0 deletions

File tree

.github/workflows/publish-aur.yml

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
name: Publish AUR package
2+
3+
# See packaging/aur/README.md.
4+
5+
on:
6+
workflow_call:
7+
inputs:
8+
release_tag:
9+
required: true
10+
type: string
11+
pkgrel:
12+
required: false
13+
default: "1"
14+
type: string
15+
secrets:
16+
AUR_SSH_PRIVATE_KEY:
17+
required: true
18+
workflow_dispatch:
19+
inputs:
20+
release_tag:
21+
description: "Release tag to publish"
22+
required: true
23+
type: string
24+
pkgrel:
25+
description: "Arch package release override"
26+
required: false
27+
default: "1"
28+
type: string
29+
30+
permissions:
31+
contents: read
32+
33+
concurrency:
34+
group: publish-aur
35+
cancel-in-progress: false
36+
37+
jobs:
38+
publish:
39+
name: Validate and publish
40+
runs-on: blacksmith-8vcpu-ubuntu-2404
41+
timeout-minutes: 30
42+
container:
43+
image: archlinux:base-devel
44+
45+
steps:
46+
- name: Install Arch packaging tools
47+
run: pacman -Syu --noconfirm --needed git github-cli jq namcap openssh sudo
48+
49+
- name: Checkout packaging sources
50+
uses: actions/checkout@v6
51+
52+
- name: Create unprivileged build user
53+
run: |
54+
useradd --create-home builder
55+
install -Dm0440 /dev/stdin /etc/sudoers.d/builder <<'EOF'
56+
builder ALL=(root) NOPASSWD: /usr/bin/pacman
57+
EOF
58+
59+
- name: Validate and publish package sources
60+
env:
61+
GH_TOKEN: ${{ github.token }}
62+
RELEASE_TAG: ${{ inputs.release_tag }}
63+
PKGREL: ${{ inputs.pkgrel || '1' }}
64+
AUR_SSH_PRIVATE_KEY: ${{ secrets.AUR_SSH_PRIVATE_KEY }}
65+
run: packaging/aur/scripts/release.sh

.github/workflows/release.yml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -856,6 +856,16 @@ jobs:
856856
fail_on_unmatched_files: true
857857
token: ${{ github.token }}
858858

859+
publish_aur:
860+
name: Publish AUR package
861+
needs: [preflight, release]
862+
if: ${{ !failure() && !cancelled() && needs.preflight.result == 'success' && needs.release.result == 'success' }}
863+
uses: ./.github/workflows/publish-aur.yml
864+
with:
865+
release_tag: ${{ needs.preflight.outputs.tag }}
866+
secrets:
867+
AUR_SSH_PRIVATE_KEY: ${{ secrets.AUR_SSH_PRIVATE_KEY }}
868+
859869
deploy_web:
860870
name: Deploy hosted web app
861871
needs: [preflight, relay_public_config, release]

README.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,10 +51,20 @@ brew install --cask t3-code
5151

5252
#### Arch Linux (AUR)
5353

54+
Stable:
55+
5456
```bash
5557
yay -S t3code-bin
5658
```
5759

60+
Nightly:
61+
62+
```bash
63+
yay -S t3code-nightly-bin
64+
```
65+
66+
The AUR packaging is maintained in this repository under [`packaging/aur`](./packaging/aur).
67+
5868
## Some notes
5969

6070
We are very very early in this project. Expect bugs.

docs/user/install.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,18 @@ brew install --cask t3-code
3737

3838
Arch Linux:
3939

40+
Stable:
41+
4042
```bash
4143
yay -S t3code-bin
4244
```
4345

46+
Nightly:
47+
48+
```bash
49+
yay -S t3code-nightly-bin
50+
```
51+
4452
## Providers
4553

4654
T3 Code drives provider CLIs; it does not ship them. Install the CLI for each provider you want

packaging/aur/.gitignore

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
src/
2+
pkg/
3+
*.AppImage
4+
*.pkg.tar.zst
5+
.SRCINFO
6+
t3code-bin-*.png
7+
t3code-bin-*-LICENSE
8+
t3code-nightly-bin-*.png
9+
t3code-nightly-bin-*-LICENSE

packaging/aur/README.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# AUR packaging
2+
3+
This directory maintains the [`t3code-bin`](https://aur.archlinux.org/packages/t3code-bin) and
4+
[`t3code-nightly-bin`](https://aur.archlinux.org/packages/t3code-nightly-bin) packages. Both
5+
repackage the official x86_64 AppImage from GitHub Releases.
6+
7+
## Publishing
8+
9+
The release workflow calls `.github/workflows/publish-aur.yml` after publishing a GitHub release;
10+
the workflow can also be run manually for a specific tag. It selects the stable or nightly
11+
package, then updates its version and checksums, builds it, regenerates `.SRCINFO`, and pushes it
12+
to the AUR.
13+
14+
To validate a release on Arch Linux:
15+
16+
```bash
17+
sudo pacman -Syu --needed base-devel github-cli jq namcap
18+
GH_TOKEN=$(gh auth token) RELEASE_TAG=v0.0.33 \
19+
packaging/aur/scripts/release.sh
20+
```

packaging/aur/scripts/release.sh

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
#!/usr/bin/env bash
2+
set -euo pipefail
3+
4+
repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/../../.." && pwd)"
5+
repo='pingdotgg/t3code'
6+
tag="${RELEASE_TAG:?RELEASE_TAG is required}"
7+
pkgrel="${PKGREL:-1}"
8+
9+
if [[ "$tag" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
10+
pkgname='t3code-bin'
11+
icon_path='assets/prod/black-universal-1024.png'
12+
elif [[ "$tag" =~ ^v[0-9]+\.[0-9]+\.[0-9]+-nightly\.[0-9]{8}\.[0-9]+$ ]]; then
13+
pkgname='t3code-nightly-bin'
14+
icon_path='assets/nightly/nightly-universal-1024.png'
15+
else
16+
echo "Release $tag does not publish an AUR package."
17+
exit 0
18+
fi
19+
20+
version="${tag#v}"
21+
pkgver="${version//-/_}"
22+
asset_name="T3-Code-${version}-x86_64.AppImage"
23+
release_json="$(gh api "repos/$repo/releases/tags/$tag")"
24+
asset_digest="$(jq -r --arg name "$asset_name" \
25+
'.assets[] | select(.name == $name) | .digest' <<<"$release_json")"
26+
appimage_sha256="${asset_digest#sha256:}"
27+
28+
if [[ ! "$appimage_sha256" =~ ^[0-9a-f]{64}$ ]]; then
29+
echo "Release $tag is missing $asset_name or its SHA-256 digest." >&2
30+
exit 1
31+
fi
32+
33+
work_dir="$(mktemp -d)"
34+
trap 'rm -rf -- "$work_dir"' EXIT
35+
gh api -H 'Accept: application/vnd.github.raw' \
36+
"repos/$repo/contents/$icon_path?ref=$tag" > "$work_dir/icon.png"
37+
gh api -H 'Accept: application/vnd.github.raw' \
38+
"repos/$repo/contents/LICENSE?ref=$tag" > "$work_dir/LICENSE"
39+
icon_sha256="$(sha256sum "$work_dir/icon.png" | awk '{print $1}')"
40+
license_sha256="$(sha256sum "$work_dir/LICENSE" | awk '{print $1}')"
41+
42+
package_dir="$repo_root/packaging/aur/$pkgname"
43+
cd "$package_dir"
44+
sed -Ei \
45+
-e "s/^pkgver=.*/pkgver=$pkgver/" \
46+
-e "s/^pkgrel=.*/pkgrel=$pkgrel/" \
47+
-e "/# AppImage$/s/'[0-9a-f]{64}'/'$appimage_sha256'/" \
48+
-e "/# icon$/s/'[0-9a-f]{64}'/'$icon_sha256'/" \
49+
-e "/# upstream license$/s/'[0-9a-f]{64}'/'$license_sha256'/" \
50+
PKGBUILD
51+
52+
run_as_builder() {
53+
if [[ "$(id -u)" == 0 ]]; then
54+
runuser -u builder -- "$@"
55+
else
56+
"$@"
57+
fi
58+
}
59+
60+
if [[ "$(id -u)" == 0 ]]; then
61+
chown -R builder:builder "$package_dir"
62+
fi
63+
run_as_builder namcap PKGBUILD
64+
run_as_builder makepkg --printsrcinfo > .SRCINFO
65+
run_as_builder makepkg --syncdeps --cleanbuild --clean --noconfirm
66+
run_as_builder namcap "$(run_as_builder makepkg --packagelist)"
67+
68+
if [[ -z "${AUR_SSH_PRIVATE_KEY:-}" ]]; then
69+
echo 'AUR_SSH_PRIVATE_KEY is not set; build complete, skipping publish.'
70+
exit 0
71+
fi
72+
73+
key_file="$work_dir/id_ed25519"
74+
known_hosts_file="$work_dir/known_hosts"
75+
aur_dir="$work_dir/$pkgname"
76+
printf '%s\n' "$AUR_SSH_PRIVATE_KEY" > "$key_file"
77+
chmod 600 "$key_file"
78+
printf '%s\n' \
79+
'aur.archlinux.org ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEuBKrPzbawxA/k2g6NcyV5jmqwJ2s+zpgZGZ7tpLIcN' \
80+
> "$known_hosts_file"
81+
export GIT_SSH_COMMAND="ssh -i $key_file -o IdentitiesOnly=yes -o UserKnownHostsFile=$known_hosts_file -o StrictHostKeyChecking=yes"
82+
83+
git clone "ssh://aur@aur.archlinux.org/$pkgname.git" "$aur_dir"
84+
cp PKGBUILD .SRCINFO "$aur_dir/"
85+
cd "$aur_dir"
86+
git rm --ignore-unmatch LICENSE .upstream-commit t3code-icon.png
87+
git config user.name 't3code-ci'
88+
git config user.email 't3code-ci@users.noreply.github.com'
89+
git add -A
90+
91+
if git diff --cached --quiet; then
92+
echo 'AUR package is already up to date.'
93+
exit 0
94+
fi
95+
96+
git commit -m "$pkgname: update to $pkgver-$pkgrel"
97+
git push origin HEAD:master

packaging/aur/t3code-bin/PKGBUILD

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
# Maintainer: maria-rcks <maria at kuuro dot net>
2+
3+
pkgname=t3code-bin
4+
pkgver=0.0.33
5+
pkgrel=1
6+
pkgdesc='Desktop control surface for local coding agents'
7+
arch=('x86_64')
8+
url='https://github.com/pingdotgg/t3code'
9+
license=('MIT')
10+
depends=(
11+
'alsa-lib'
12+
'at-spi2-core'
13+
'cairo'
14+
'dbus'
15+
'expat'
16+
'gdk-pixbuf2'
17+
'glib2'
18+
'glibc'
19+
'gtk3'
20+
'hicolor-icon-theme'
21+
'libcups'
22+
'libdrm'
23+
'libgcc'
24+
'libstdc++'
25+
'libx11'
26+
'libxcb'
27+
'libxcomposite'
28+
'libxdamage'
29+
'libxext'
30+
'libxfixes'
31+
'libxkbcommon'
32+
'libxrandr'
33+
'mesa'
34+
'nspr'
35+
'nss'
36+
'pango'
37+
'systemd-libs'
38+
'xdg-utils'
39+
'zlib'
40+
)
41+
optdepends=('openai-codex: use the system-installed Codex CLI')
42+
provides=("t3code=$pkgver")
43+
conflicts=('t3code')
44+
options=('!debug' '!strip')
45+
46+
_appimage="T3-Code-${pkgver}-x86_64.AppImage"
47+
source=(
48+
"$_appimage::https://github.com/pingdotgg/t3code/releases/download/v${pkgver}/$_appimage"
49+
"${pkgname}-${pkgver}.png::https://raw.githubusercontent.com/pingdotgg/t3code/v${pkgver}/assets/prod/black-universal-1024.png"
50+
"${pkgname}-${pkgver}-LICENSE::https://raw.githubusercontent.com/pingdotgg/t3code/v${pkgver}/LICENSE"
51+
)
52+
sha256sums=(
53+
'415c8648f43c3d22d572f27f2c50fdc8c310ea7fcde9537b903e1e2f1c8775a1' # AppImage
54+
'403e874556ffbecee8d1b2b5d612a874303fac791212a261bb3bd1b71d83e78d' # icon
55+
'935d8f2af0c703f9c39517ee57cc4930b19d02d533be930b63f0e82f93614b43' # upstream license
56+
)
57+
58+
prepare() {
59+
chmod +x "$srcdir/$_appimage"
60+
rm -rf "$srcdir/squashfs-root"
61+
"$srcdir/$_appimage" --appimage-extract >/dev/null
62+
63+
if [[ ! -x "$srcdir/squashfs-root/AppRun" ||
64+
! -f "$srcdir/squashfs-root/chrome-sandbox" ]]; then
65+
echo 'The AppImage payload is missing its launcher or Chromium sandbox.' >&2
66+
return 1
67+
fi
68+
}
69+
70+
package() {
71+
install -d "$pkgdir/opt/$pkgname"
72+
cp -a --no-preserve=ownership "$srcdir/squashfs-root/." "$pkgdir/opt/$pkgname/"
73+
chmod -R u=rwX,go=rX "$pkgdir/opt/$pkgname"
74+
chmod 4755 "$pkgdir/opt/$pkgname/chrome-sandbox"
75+
76+
install -Dm755 /dev/stdin "$pkgdir/usr/bin/t3code" <<'EOF'
77+
#!/bin/sh
78+
exec /opt/t3code-bin/AppRun "$@"
79+
EOF
80+
ln -s t3code "$pkgdir/usr/bin/t3-code-desktop"
81+
82+
install -Dm644 "$srcdir/${pkgname}-${pkgver}.png" \
83+
"$pkgdir/usr/share/icons/hicolor/1024x1024/apps/t3code.png"
84+
85+
install -Dm644 /dev/stdin "$pkgdir/usr/share/applications/t3code.desktop" <<'EOF'
86+
[Desktop Entry]
87+
Name=T3 Code
88+
Comment=Desktop control surface for local coding agents
89+
Exec=t3code %U
90+
TryExec=t3code
91+
Terminal=false
92+
Type=Application
93+
Icon=t3code
94+
StartupWMClass=t3code
95+
Categories=Development;
96+
MimeType=x-scheme-handler/t3code;
97+
EOF
98+
99+
install -Dm644 "$srcdir/${pkgname}-${pkgver}-LICENSE" \
100+
"$pkgdir/usr/share/licenses/$pkgname/LICENSE"
101+
}

0 commit comments

Comments
 (0)