authorgravatar for maria@kuuro.netmaria <maria@kuuro.net> 2026-08-14 18:32:12-04:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-08-15 00:32:12+02:00
loge25021af767b10c560862fcec714cf67fb22cfae
treeca0dc823d7279abf452d3786626f15240122f36a
parentf0719072a1c6435b5a91243afc57bc8bf1f3e2b6
signature Signed by PGP key B5690EEEBB952194

feat(packaging): maintain AUR packages in-repo (#4128)


9 files changed, 422 insertions(+), 0 deletions(-)

.github/workflows/publish-aur.yml created+65
......@@ -0,0 +1,65 @@
1name: Publish AUR package
2
3# See packaging/aur/README.md.
4
5on:
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
30permissions:
31 contents: read
32
33concurrency:
34 group: publish-aur
35 cancel-in-progress: false
36
37jobs:
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+10
......@@ -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+10
......@@ -51,10 +51,20 @@ brew install --cask t3-code
5151
5252#### Arch Linux (AUR)
5353
54Stable:
55
5456```bash
5557yay -S t3code-bin
5658```
5759
60Nightly:
61
62```bash
63yay -S t3code-nightly-bin
64```
65
66The AUR packaging is maintained in this repository under [`packaging/aur`](./packaging/aur).
67
5868## Some notes
5969
6070We are very very early in this project. Expect bugs.
docs/user/install.md+8
......@@ -37,10 +37,18 @@ brew install --cask t3-code
3737
3838Arch Linux:
3939
40Stable:
41
4042```bash
4143yay -S t3code-bin
4244```
4345
46Nightly:
47
48```bash
49yay -S t3code-nightly-bin
50```
51
4452## Providers
4553
4654T3 Code drives provider CLIs; it does not ship them. Install the CLI for each provider you want
packaging/aur/.gitignore created+9
......@@ -0,0 +1,9 @@
1src/
2pkg/
3*.AppImage
4*.pkg.tar.zst
5.SRCINFO
6t3code-bin-*.png
7t3code-bin-*-LICENSE
8t3code-nightly-bin-*.png
9t3code-nightly-bin-*-LICENSE
packaging/aur/README.md created+20
......@@ -0,0 +1,20 @@
1# AUR packaging
2
3This 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
5repackage the official x86_64 AppImage from GitHub Releases.
6
7## Publishing
8
9The release workflow calls `.github/workflows/publish-aur.yml` after publishing a GitHub release;
10the workflow can also be run manually for a specific tag. It selects the stable or nightly
11package, then updates its version and checksums, builds it, regenerates `.SRCINFO`, and pushes it
12to the AUR.
13
14To validate a release on Arch Linux:
15
16```bash
17sudo pacman -Syu --needed base-devel github-cli jq namcap
18GH_TOKEN=$(gh auth token) RELEASE_TAG=v0.0.33 \
19 packaging/aur/scripts/release.sh
20```
packaging/aur/scripts/release.sh created+97
......@@ -0,0 +1,97 @@
1#!/usr/bin/env bash
2set -euo pipefail
3
4repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/../../.." && pwd)"
5repo='pingdotgg/t3code'
6tag="${RELEASE_TAG:?RELEASE_TAG is required}"
7pkgrel="${PKGREL:-1}"
8
9if [[ "$tag" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
10 pkgname='t3code-bin'
11 icon_path='assets/prod/black-universal-1024.png'
12elif [[ "$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'
15else
16 echo "Release $tag does not publish an AUR package."
17 exit 0
18fi
19
20version="${tag#v}"
21pkgver="${version//-/_}"
22asset_name="T3-Code-${version}-x86_64.AppImage"
23release_json="$(gh api "repos/$repo/releases/tags/$tag")"
24asset_digest="$(jq -r --arg name "$asset_name" \
25 '.assets[] | select(.name == $name) | .digest' <<<"$release_json")"
26appimage_sha256="${asset_digest#sha256:}"
27
28if [[ ! "$appimage_sha256" =~ ^[0-9a-f]{64}$ ]]; then
29 echo "Release $tag is missing $asset_name or its SHA-256 digest." >&2
30 exit 1
31fi
32
33work_dir="$(mktemp -d)"
34trap 'rm -rf -- "$work_dir"' EXIT
35gh api -H 'Accept: application/vnd.github.raw' \
36 "repos/$repo/contents/$icon_path?ref=$tag" > "$work_dir/icon.png"
37gh api -H 'Accept: application/vnd.github.raw' \
38 "repos/$repo/contents/LICENSE?ref=$tag" > "$work_dir/LICENSE"
39icon_sha256="$(sha256sum "$work_dir/icon.png" | awk '{print $1}')"
40license_sha256="$(sha256sum "$work_dir/LICENSE" | awk '{print $1}')"
41
42package_dir="$repo_root/packaging/aur/$pkgname"
43cd "$package_dir"
44sed -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
52run_as_builder() {
53 if [[ "$(id -u)" == 0 ]]; then
54 runuser -u builder -- "$@"
55 else
56 "$@"
57 fi
58}
59
60if [[ "$(id -u)" == 0 ]]; then
61 chown -R builder:builder "$package_dir"
62fi
63run_as_builder namcap PKGBUILD
64run_as_builder makepkg --printsrcinfo > .SRCINFO
65run_as_builder makepkg --syncdeps --cleanbuild --clean --noconfirm
66run_as_builder namcap "$(run_as_builder makepkg --packagelist)"
67
68if [[ -z "${AUR_SSH_PRIVATE_KEY:-}" ]]; then
69 echo 'AUR_SSH_PRIVATE_KEY is not set; build complete, skipping publish.'
70 exit 0
71fi
72
73key_file="$work_dir/id_ed25519"
74known_hosts_file="$work_dir/known_hosts"
75aur_dir="$work_dir/$pkgname"
76printf '%s\n' "$AUR_SSH_PRIVATE_KEY" > "$key_file"
77chmod 600 "$key_file"
78printf '%s\n' \
79 'aur.archlinux.org ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEuBKrPzbawxA/k2g6NcyV5jmqwJ2s+zpgZGZ7tpLIcN' \
80 > "$known_hosts_file"
81export GIT_SSH_COMMAND="ssh -i $key_file -o IdentitiesOnly=yes -o UserKnownHostsFile=$known_hosts_file -o StrictHostKeyChecking=yes"
82
83git clone "ssh://aur@aur.archlinux.org/$pkgname.git" "$aur_dir"
84cp PKGBUILD .SRCINFO "$aur_dir/"
85cd "$aur_dir"
86git rm --ignore-unmatch LICENSE .upstream-commit t3code-icon.png
87git config user.name 't3code-ci'
88git config user.email 't3code-ci@users.noreply.github.com'
89git add -A
90
91if git diff --cached --quiet; then
92 echo 'AUR package is already up to date.'
93 exit 0
94fi
95
96git commit -m "$pkgname: update to $pkgver-$pkgrel"
97git push origin HEAD:master
packaging/aur/t3code-bin/PKGBUILD created+101
......@@ -0,0 +1,101 @@
1# Maintainer: maria-rcks <maria at kuuro dot net>
2
3pkgname=t3code-bin
4pkgver=0.0.33
5pkgrel=1
6pkgdesc='Desktop control surface for local coding agents'
7arch=('x86_64')
8url='https://github.com/pingdotgg/t3code'
9license=('MIT')
10depends=(
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)
41optdepends=('openai-codex: use the system-installed Codex CLI')
42provides=("t3code=$pkgver")
43conflicts=('t3code')
44options=('!debug' '!strip')
45
46_appimage="T3-Code-${pkgver}-x86_64.AppImage"
47source=(
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)
52sha256sums=(
53 '415c8648f43c3d22d572f27f2c50fdc8c310ea7fcde9537b903e1e2f1c8775a1' # AppImage
54 '403e874556ffbecee8d1b2b5d612a874303fac791212a261bb3bd1b71d83e78d' # icon
55 '935d8f2af0c703f9c39517ee57cc4930b19d02d533be930b63f0e82f93614b43' # upstream license
56)
57
58prepare() {
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
70package() {
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
78exec /opt/t3code-bin/AppRun "$@"
79EOF
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]
87Name=T3 Code
88Comment=Desktop control surface for local coding agents
89Exec=t3code %U
90TryExec=t3code
91Terminal=false
92Type=Application
93Icon=t3code
94StartupWMClass=t3code
95Categories=Development;
96MimeType=x-scheme-handler/t3code;
97EOF
98
99 install -Dm644 "$srcdir/${pkgname}-${pkgver}-LICENSE" \
100 "$pkgdir/usr/share/licenses/$pkgname/LICENSE"
101}
packaging/aur/t3code-nightly-bin/PKGBUILD created+102
......@@ -0,0 +1,102 @@
1# Maintainer: maria-rcks <maria at kuuro dot net>
2
3pkgname=t3code-nightly-bin
4pkgver=0.0.34_nightly.20260814.1095
5pkgrel=1
6pkgdesc='Nightly desktop control surface for local coding agents'
7arch=('x86_64')
8url='https://github.com/pingdotgg/t3code'
9license=('MIT')
10depends=(
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)
41optdepends=('openai-codex: use the system-installed Codex CLI')
42provides=("t3code-nightly=$pkgver")
43conflicts=('t3code-nightly' 't3code')
44options=('!debug' '!strip')
45
46_upstream_version="${pkgver/_nightly./-nightly.}"
47_appimage="T3-Code-${_upstream_version}-x86_64.AppImage"
48source=(
49 "$_appimage::https://github.com/pingdotgg/t3code/releases/download/v${_upstream_version}/$_appimage"
50 "${pkgname}-${pkgver}.png::https://raw.githubusercontent.com/pingdotgg/t3code/v${_upstream_version}/assets/nightly/nightly-universal-1024.png"
51 "${pkgname}-${pkgver}-LICENSE::https://raw.githubusercontent.com/pingdotgg/t3code/v${_upstream_version}/LICENSE"
52)
53sha256sums=(
54 'c4dea5bba9ed0b51b2f60f2d4a4867e61d62b57c50ea66f2792a73112e054566' # AppImage
55 '7e59b6394016ef83ed1e946847769e01bf36d4062c5c5af2577fd3e228285fd9' # icon
56 '935d8f2af0c703f9c39517ee57cc4930b19d02d533be930b63f0e82f93614b43' # upstream license
57)
58
59prepare() {
60 chmod +x "$srcdir/$_appimage"
61 rm -rf "$srcdir/squashfs-root"
62 "$srcdir/$_appimage" --appimage-extract >/dev/null
63
64 if [[ ! -x "$srcdir/squashfs-root/AppRun" ||
65 ! -f "$srcdir/squashfs-root/chrome-sandbox" ]]; then
66 echo 'The AppImage payload is missing its launcher or Chromium sandbox.' >&2
67 return 1
68 fi
69}
70
71package() {
72 install -d "$pkgdir/opt/$pkgname"
73 cp -a --no-preserve=ownership "$srcdir/squashfs-root/." "$pkgdir/opt/$pkgname/"
74 chmod -R u=rwX,go=rX "$pkgdir/opt/$pkgname"
75 chmod 4755 "$pkgdir/opt/$pkgname/chrome-sandbox"
76
77 install -Dm755 /dev/stdin "$pkgdir/usr/bin/t3code-nightly" <<'EOF'
78#!/bin/sh
79exec /opt/t3code-nightly-bin/AppRun "$@"
80EOF
81 ln -s t3code-nightly "$pkgdir/usr/bin/t3-code-nightly-desktop"
82
83 install -Dm644 "$srcdir/${pkgname}-${pkgver}.png" \
84 "$pkgdir/usr/share/icons/hicolor/1024x1024/apps/t3code-nightly.png"
85
86 install -Dm644 /dev/stdin "$pkgdir/usr/share/applications/t3code.desktop" <<'EOF'
87[Desktop Entry]
88Name=T3 Code Nightly
89Comment=Nightly desktop control surface for local coding agents
90Exec=t3code-nightly %U
91TryExec=t3code-nightly
92Terminal=false
93Type=Application
94Icon=t3code-nightly
95StartupWMClass=t3code
96Categories=Development;
97MimeType=x-scheme-handler/t3code;
98EOF
99
100 install -Dm644 "$srcdir/${pkgname}-${pkgver}-LICENSE" \
101 "$pkgdir/usr/share/licenses/$pkgname/LICENSE"
102}