Add release pipeline: AUR packaging, Gitea workflow, MIT license
Release / build (x86_64, ubuntu-latest) (push) Successful in 31m46s
Release / build (aarch64, aarch64) (push) Successful in 41s
Release / update-aur (push) Successful in 12s
Release / docker (push) Successful in 25m53s

Mirrors the gpupaper release convention: v* tags build x86_64/aarch64
tarballs (binary + leptos site + systemd unit), upload them as Gitea
release assets, push bendik/cnats to Docker Hub, and update the AUR
package with checksums from the release assets.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-09 14:38:40 +02:00
parent ec3771f2e7
commit 8d0c87ad2e
6 changed files with 266 additions and 0 deletions
+149
View File
@@ -0,0 +1,149 @@
name: Release
on:
push:
tags:
- 'v*'
env:
CARGO_TERM_COLOR: always
jobs:
build:
strategy:
matrix:
include:
- arch: x86_64
runs-on: ubuntu-latest
- arch: aarch64
runs-on: aarch64
runs-on: ${{ matrix.runs-on }}
steps:
- uses: actions/checkout@v4
- name: Install Rust stable
uses: dtolnay/rust-toolchain@stable
with:
targets: wasm32-unknown-unknown
- name: Cache cargo registry and tools
uses: actions/cache@v3
with:
path: |
~/.cargo/registry
~/.cargo/bin
key: ${{ runner.os }}-${{ matrix.arch }}-cargo-leptos-${{ hashFiles('Cargo.lock') }}
- name: Install cargo-leptos
run: command -v cargo-leptos || cargo install cargo-leptos --locked
- name: Build
run: cargo leptos build --release
- name: Package
run: |
TAG=${{ gitea.ref_name }}
TARBALL="cnats-${TAG}-${{ matrix.arch }}.tar.gz"
mkdir pkg
cp target/release/cnats pkg/
cp -r target/site pkg/site
cp packaging/cnats.service packaging/cnats.env pkg/
cp LICENSE README.md pkg/
tar -czf "${TARBALL}" -C pkg .
sha256sum "${TARBALL}" > "${TARBALL}.sha256"
echo "TARBALL=${TARBALL}" >> $GITHUB_ENV
- name: Create release
run: |
curl -sX POST \
-H "Authorization: token ${{ secrets.GITEA_TOKEN }}" \
-H "Content-Type: application/json" \
"${{ gitea.server_url }}/api/v1/repos/${{ gitea.repository }}/releases" \
-d "{\"tag_name\":\"${{ gitea.ref_name }}\",\"name\":\"${{ gitea.ref_name }}\"}" \
--fail-with-body || true
- name: Upload assets
run: |
RELEASE_ID=$(curl -s \
-H "Authorization: token ${{ secrets.GITEA_TOKEN }}" \
"${{ gitea.server_url }}/api/v1/repos/${{ gitea.repository }}/releases/tags/${{ gitea.ref_name }}" \
| jq -r '.id')
for FILE in "${{ env.TARBALL }}" "${{ env.TARBALL }}.sha256"; do
# Remove any existing asset with the same name so re-runs stay clean
EXISTING=$(curl -s \
-H "Authorization: token ${{ secrets.GITEA_TOKEN }}" \
"${{ gitea.server_url }}/api/v1/repos/${{ gitea.repository }}/releases/${RELEASE_ID}/assets" \
| jq -r ".[] | select(.name == \"${FILE}\") | .id")
for AID in $EXISTING; do
curl -sX DELETE \
-H "Authorization: token ${{ secrets.GITEA_TOKEN }}" \
"${{ gitea.server_url }}/api/v1/repos/${{ gitea.repository }}/releases/${RELEASE_ID}/assets/${AID}"
done
curl -sX POST \
-H "Authorization: token ${{ secrets.GITEA_TOKEN }}" \
-H "Content-Type: application/octet-stream" \
"${{ gitea.server_url }}/api/v1/repos/${{ gitea.repository }}/releases/${RELEASE_ID}/assets?name=${FILE}" \
--data-binary "@${FILE}" --fail-with-body
done
docker:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Log in to Docker Hub
run: echo "${{ secrets.DOCKERHUB_TOKEN }}" | docker login -u bendik --password-stdin
- name: Build and push
run: |
TAG=${{ gitea.ref_name }}
docker build -t "bendik/cnats:${TAG#v}" -t bendik/cnats:latest .
docker push "bendik/cnats:${TAG#v}"
docker push bendik/cnats:latest
update-aur:
needs: build
runs-on: aarch64
steps:
- uses: actions/checkout@v4
- name: Compute checksums and update PKGBUILD
run: |
TAG=${{ gitea.ref_name }}
BASE="${{ gitea.server_url }}/${{ gitea.repository }}/releases/download/${TAG}"
SUM_X86=$(curl -sL "${BASE}/cnats-${TAG}-x86_64.tar.gz" | sha256sum | cut -d' ' -f1)
SUM_AARCH=$(curl -sL "${BASE}/cnats-${TAG}-aarch64.tar.gz" | sha256sum | cut -d' ' -f1)
sed -i "s/^pkgver=.*/pkgver=${TAG#v}/" aur/PKGBUILD
sed -i "s/sha256sums_x86_64=('.*')/sha256sums_x86_64=('${SUM_X86}')/" aur/PKGBUILD
sed -i "s/sha256sums_aarch64=('.*')/sha256sums_aarch64=('${SUM_AARCH}')/" aur/PKGBUILD
- name: Push to AUR
env:
AUR_SSH_KEY: ${{ secrets.AUR_SSH_KEY }}
run: |
install -dm700 ~/.ssh
echo "$AUR_SSH_KEY" > ~/.ssh/aur
chmod 600 ~/.ssh/aur
echo "Host aur.archlinux.org" >> ~/.ssh/config
echo " IdentityFile ~/.ssh/aur" >> ~/.ssh/config
echo " User aur" >> ~/.ssh/config
ssh-keyscan aur.archlinux.org >> ~/.ssh/known_hosts 2>/dev/null
rm -rf /tmp/aur-cnats
git clone ssh://aur@aur.archlinux.org/cnats.git /tmp/aur-cnats
cp aur/PKGBUILD /tmp/aur-cnats/
cd /tmp/aur-cnats
makepkg --printsrcinfo > .SRCINFO
git config user.name "Bendik Aagaard Lynghaug"
git config user.email "bendik.lynghaug@gmail.com"
git add PKGBUILD .SRCINFO
git commit -m "Update to ${{ gitea.ref_name }}"
git push origin master
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2026 Bendik Aagaard Lynghaug
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+11
View File
@@ -17,6 +17,17 @@ signed-in session (sender identity comes from the session, never the client)
and fans messages out to browsers over Server-Sent Events. Any other NATS and fans messages out to browsers over Server-Sent Events. Any other NATS
client on the bus can publish/subscribe to `chat.room.*` and participate. client on the bus can publish/subscribe to `chat.room.*` and participate.
## Install
- **Arch (AUR):** `yay -S cnats` — installs the binary, `cnats.service`, and
config at `/etc/cnats/env`; then `systemctl enable --now cnats`.
- **Docker:** `docker run --env-file .env -p 3000:3000 bendik/cnats` (or the
compose file below).
Releases are cut by the Gitea workflow in `.gitea/workflows/release.yml`: a
`v*` tag builds x86_64 + aarch64 tarballs, publishes them as release assets,
pushes the image to Docker Hub, and updates the AUR package.
## Prerequisites ## Prerequisites
- Rust (stable) with the `wasm32-unknown-unknown` target - Rust (stable) with the `wasm32-unknown-unknown` target
+34
View File
@@ -0,0 +1,34 @@
# Maintainer: Bendik Aagaard Lynghaug <bendik.lynghaug@gmail.com>
pkgname=cnats
pkgver=0.1.0
pkgrel=1
pkgdesc="Web chat over NATS subjects with Kanidm SSO (Leptos SSR)"
arch=('x86_64' 'aarch64')
url="https://prosjekt.klingenbergbygg.no/bl/cnats"
license=('MIT')
depends=('glibc' 'gcc-libs')
optdepends=(
'nats-server: local message bus'
'postgresql: local message history archive'
)
provides=('cnats')
conflicts=('cnats-git' 'cnats-bin')
backup=('etc/cnats/env')
source_x86_64=("cnats-v${pkgver}-x86_64.tar.gz::https://prosjekt.klingenbergbygg.no/bl/cnats/releases/download/v${pkgver}/cnats-v${pkgver}-x86_64.tar.gz")
source_aarch64=("cnats-v${pkgver}-aarch64.tar.gz::https://prosjekt.klingenbergbygg.no/bl/cnats/releases/download/v${pkgver}/cnats-v${pkgver}-aarch64.tar.gz")
sha256sums_x86_64=('SKIP')
sha256sums_aarch64=('SKIP')
package() {
install -Dm755 cnats "$pkgdir/usr/bin/cnats"
# Hydration wasm/js + css served by the binary (LEPTOS_SITE_ROOT).
install -dm755 "$pkgdir/usr/share/cnats"
cp -r site "$pkgdir/usr/share/cnats/site"
install -Dm644 cnats.service "$pkgdir/usr/lib/systemd/system/cnats.service"
install -Dm600 cnats.env "$pkgdir/etc/cnats/env"
install -Dm644 LICENSE "$pkgdir/usr/share/licenses/$pkgname/LICENSE"
install -Dm644 README.md "$pkgdir/usr/share/doc/$pkgname/README.md"
}
+19
View File
@@ -0,0 +1,19 @@
# cnats configuration — read by cnats.service (EnvironmentFile).
# See https://prosjekt.klingenbergbygg.no/bl/cnats for details.
LEPTOS_SITE_ADDR=127.0.0.1:3000
NATS_URL=nats://127.0.0.1:4222
DATABASE_URL=postgres://cnats:cnats@127.0.0.1:5432/cnats
KANIDM_URL=https://idm.example.com
OAUTH2_CLIENT_ID=cnats
OAUTH2_CLIENT_SECRET=change-me
# Must match a redirect URL registered on the Kanidm oauth2 client:
# ${PUBLIC_URL}/auth/callback
PUBLIC_URL=http://localhost:3000
# Set true when served over HTTPS (production).
COOKIE_SECURE=false
RUST_LOG=info
+32
View File
@@ -0,0 +1,32 @@
[Unit]
Description=cnats - web chat over NATS subjects with Kanidm SSO
Documentation=https://prosjekt.klingenbergbygg.no/bl/cnats
After=network-online.target nats-server.service postgresql.service
Wants=network-online.target
[Service]
DynamicUser=yes
EnvironmentFile=/etc/cnats/env
Environment=LEPTOS_SITE_ROOT=/usr/share/cnats/site
ExecStart=/usr/bin/cnats
Restart=on-failure
RestartSec=5
# Hardening: the app only needs outbound sockets and its static site dir.
NoNewPrivileges=yes
ProtectSystem=strict
ProtectHome=yes
PrivateTmp=yes
PrivateDevices=yes
ProtectKernelTunables=yes
ProtectKernelModules=yes
ProtectControlGroups=yes
RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX
RestrictNamespaces=yes
LockPersonality=yes
MemoryDenyWriteExecute=yes
SystemCallArchitectures=native
CapabilityBoundingSet=
[Install]
WantedBy=multi-user.target