feat: auto-deploy tagged releases to CT 126 via signed webhook (0.3.1)
CI's new deploy job (tags only) sends the house HMAC webhook to CT 126; the listener runs /usr/lib/devdesk/devdesk-deploy, which waits for the version in the apt registry and upgrades devdesk with --force-confold. Running sessions are untouched. Hook setup is documented in docs/DEPLOY.md. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -149,10 +149,32 @@ jobs:
|
|||||||
"$api/releases/$id/assets?name=$name" >/dev/null
|
"$api/releases/$id/assets?name=$name" >/dev/null
|
||||||
echo "release $TAG: $GITEA_URL/$REPO/releases/tag/$TAG"
|
echo "release $TAG: $GITEA_URL/$REPO/releases/tag/$TAG"
|
||||||
|
|
||||||
|
deploy:
|
||||||
|
name: Deploy to CT 126
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: [build]
|
||||||
|
if: startsWith(github.ref, 'refs/tags/v')
|
||||||
|
steps:
|
||||||
|
# CT 126's webhook listener runs /usr/lib/devdesk/devdesk-deploy, which
|
||||||
|
# waits for this version in the apt registry and upgrades the package.
|
||||||
|
- name: Trigger devdesk-deploy webhook
|
||||||
|
env:
|
||||||
|
WEBHOOK_SECRET: ${{ secrets.WEBHOOK_SECRET }}
|
||||||
|
GIT_REF: ${{ github.ref }}
|
||||||
|
run: |
|
||||||
|
PAYLOAD="{\"ref\":\"${GIT_REF}\"}"
|
||||||
|
SIG=$(echo -n "$PAYLOAD" | openssl dgst -sha256 -hmac "$WEBHOOK_SECRET" | awk '{print $2}')
|
||||||
|
curl -sf --connect-timeout 10 \
|
||||||
|
-X POST \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-H "X-Gitea-Signature: ${SIG}" \
|
||||||
|
-d "$PAYLOAD" \
|
||||||
|
"http://10.10.10.58:9000/hooks/devdesk-deploy"
|
||||||
|
|
||||||
notify-failure:
|
notify-failure:
|
||||||
name: Notify on failure
|
name: Notify on failure
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs: [build]
|
needs: [build, deploy]
|
||||||
if: failure() && github.event_name == 'push'
|
if: failure() && github.event_name == 'push'
|
||||||
steps:
|
steps:
|
||||||
- name: Send Matrix alert
|
- name: Send Matrix alert
|
||||||
|
|||||||
@@ -39,7 +39,8 @@ install: all
|
|||||||
install -m 0755 bin/devdesk $(DESTDIR)$(PREFIX)/bin/devdesk
|
install -m 0755 bin/devdesk $(DESTDIR)$(PREFIX)/bin/devdesk
|
||||||
install -m 0644 libexec/devdesk-lib.sh $(DESTDIR)$(LIBDIR)/devdesk-lib.sh
|
install -m 0644 libexec/devdesk-lib.sh $(DESTDIR)$(LIBDIR)/devdesk-lib.sh
|
||||||
install -m 0755 libexec/devdesk-picker libexec/devdesk-claude-run libexec/devdesk-session-hook \
|
install -m 0755 libexec/devdesk-picker libexec/devdesk-claude-run libexec/devdesk-session-hook \
|
||||||
libexec/devdesk-tmux-ready libexec/devdesk-serve libexec/devdesk-statusline $(DESTDIR)$(LIBDIR)/
|
libexec/devdesk-tmux-ready libexec/devdesk-serve libexec/devdesk-statusline \
|
||||||
|
libexec/devdesk-deploy $(DESTDIR)$(LIBDIR)/
|
||||||
install -m 0755 build/ttyd $(DESTDIR)$(LIBDIR)/bin/ttyd
|
install -m 0755 build/ttyd $(DESTDIR)$(LIBDIR)/bin/ttyd
|
||||||
install -m 0644 dist/index.html share/tmux.conf share/claude-settings.json $(DESTDIR)$(SHAREDIR)/
|
install -m 0644 dist/index.html share/tmux.conf share/claude-settings.json $(DESTDIR)$(SHAREDIR)/
|
||||||
install -m 0644 etc/env $(DESTDIR)$(SYSCONFDIR)/devdesk/env
|
install -m 0644 etc/env $(DESTDIR)$(SYSCONFDIR)/devdesk/env
|
||||||
|
|||||||
@@ -71,8 +71,8 @@ apt install ../devdesk_*_amd64.deb
|
|||||||
|
|
||||||
CI (`.gitea/workflows/build-deb.yml`):
|
CI (`.gitea/workflows/build-deb.yml`):
|
||||||
- Every push lints, tests, builds and smoke-tests the package and uploads it as an artifact.
|
- Every push lints, tests, builds and smoke-tests the package and uploads it as an artifact.
|
||||||
- A `v*` tag also publishes to `https://code.lotusguild.org/api/packages/LotusGuild/debian` and creates a release.
|
- A `v*` tag also publishes to `https://code.lotusguild.org/api/packages/LotusGuild/debian`, creates a release, and auto-deploys to CT 126. A signed webhook runs `devdesk-deploy`, which upgrades the package; see [docs/DEPLOY.md](docs/DEPLOY.md).
|
||||||
|
|
||||||
## Roadmap
|
## Roadmap
|
||||||
- **v1.1:** `devdesk-api` sidecar for the Repo/CI panel, Matrix notifications when you're away, and auto-deploy.
|
- **Next:** Matrix notifications when you're away (needs the Ci-Alert hookshot URL).
|
||||||
- **v2:** Ops Alerts (Grafana) with "Ask Claude" prompts, and file drop.
|
- **v2:** Ops Alerts (Grafana) with "Ask Claude" prompts, and file drop.
|
||||||
|
|||||||
Vendored
+8
@@ -1,3 +1,11 @@
|
|||||||
|
devdesk (0.3.1) bookworm; urgency=medium
|
||||||
|
|
||||||
|
* Auto-deploy: tagged releases trigger CT 126's webhook listener (house HMAC
|
||||||
|
pattern), which runs /usr/lib/devdesk/devdesk-deploy to upgrade the
|
||||||
|
package from the apt registry. Running sessions are untouched.
|
||||||
|
|
||||||
|
-- Jared Vititoe <jjvititoe1@gmail.com> Wed, 07 Oct 2026 13:20:58 -0400
|
||||||
|
|
||||||
devdesk (0.3.0) bookworm; urgency=medium
|
devdesk (0.3.0) bookworm; urgency=medium
|
||||||
|
|
||||||
* Repo / CI panel is live: branch, ahead/behind, uncommitted changes, the
|
* Repo / CI panel is live: branch, ahead/behind, uncommitted changes, the
|
||||||
|
|||||||
+24
-1
@@ -36,7 +36,30 @@ Then run `authelia validate-config --config /etc/authelia/configuration.yml && s
|
|||||||
- **Cloudflare:** add a CNAME `dev` → apex, DNS-only (grey cloud).
|
- **Cloudflare:** add a CNAME `dev` → apex, DNS-only (grey cloud).
|
||||||
- **Pi-hole:** add `"10.10.10.27 dev.lotusguild.org"` to `dns.hosts` in `pve-infra/containers/163-pihole/etc/pihole/pihole.toml`. A push auto-deploys it, and LAN clients then reach NPM directly instead of hairpinning.
|
- **Pi-hole:** add `"10.10.10.27 dev.lotusguild.org"` to `dns.hosts` in `pve-infra/containers/163-pihole/etc/pihole/pihole.toml`. A push auto-deploys it, and LAN clients then reach NPM directly instead of hairpinning.
|
||||||
|
|
||||||
## 5. Optional hardening
|
## 5. Auto-deploy on release
|
||||||
|
A `v*` tag makes CI publish the package, then send a signed webhook (the house HMAC pattern) to CT 126's existing `webhook` listener on :9000. The listener runs `/usr/lib/devdesk/devdesk-deploy <ref>`, which waits for that version in the apt registry and upgrades only `devdesk` with `--force-confold`. Running Claude sessions are untouched. The log is `/var/log/devdesk-deploy.log`.
|
||||||
|
|
||||||
|
**Setup, done once:**
|
||||||
|
1. Generate a secret.
|
||||||
|
2. Add this hook to `/etc/webhook/hooks.json`, next to `126-config-deploy`. The file is local and untracked because it holds secrets.
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"id": "devdesk-deploy",
|
||||||
|
"execute-command": "/usr/lib/devdesk/devdesk-deploy",
|
||||||
|
"command-working-directory": "/",
|
||||||
|
"pass-arguments-to-command": [{"source": "payload", "name": "ref"}],
|
||||||
|
"trigger-rule": {"and": [
|
||||||
|
{"match": {"type": "payload-hash-sha256", "secret": "<WEBHOOK_SECRET>",
|
||||||
|
"parameter": {"source": "header", "name": "X-Gitea-Signature"}}},
|
||||||
|
{"match": {"type": "regex", "regex": "^refs/tags/v[0-9]+\\.[0-9]+\\.[0-9]+$",
|
||||||
|
"parameter": {"source": "payload", "name": "ref"}}}
|
||||||
|
]}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
3. Run `systemctl restart webhook`.
|
||||||
|
4. Store the same secret as the devdesk repo's Actions secret `WEBHOOK_SECRET`.
|
||||||
|
|
||||||
|
## 6. Optional hardening
|
||||||
Enable the Proxmox firewall on CT 126 (`firewall=1` on net0) and allow tcp/7681 only from 10.10.10.27. The same rule suits code-server on :8680.
|
Enable the Proxmox firewall on CT 126 (`firewall=1` on net0) and allow tcp/7681 only from 10.10.10.27. The same rule suits code-server on :8680.
|
||||||
|
|
||||||
## Operations
|
## Operations
|
||||||
|
|||||||
Executable
+54
@@ -0,0 +1,54 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Upgrade DevDesk from the LotusGuild apt registry. Run by the host's webhook
|
||||||
|
# listener (hook "devdesk-deploy") when CI publishes a v* tag; the tag's ref is
|
||||||
|
# passed as $1. Safe for running sessions: package upgrades restart only
|
||||||
|
# devdesk.service (ttyd), never devdesk-tmux.service where Claude lives.
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
LOG=${DEVDESK_DEPLOY_LOG:-/var/log/devdesk-deploy.log}
|
||||||
|
SOURCES=${DEVDESK_APT_SOURCES:-sources.list.d/lotusguild.list}
|
||||||
|
|
||||||
|
log() {
|
||||||
|
printf '%s %s\n' "$(date '+%F %T')" "$*" >>"$LOG"
|
||||||
|
}
|
||||||
|
|
||||||
|
exec 9>/run/devdesk-deploy.lock
|
||||||
|
flock -w 900 9 || {
|
||||||
|
log "another deploy is still running; giving up"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
want=${1:-}
|
||||||
|
want=${want#refs/tags/}
|
||||||
|
want=${want#v}
|
||||||
|
before=$(dpkg-query -W -f='${Version}' devdesk 2>/dev/null || echo none)
|
||||||
|
log "=== deploy: installed $before, release ${want:-latest}"
|
||||||
|
|
||||||
|
refresh() {
|
||||||
|
apt-get update -q -o Dir::Etc::sourcelist="$SOURCES" -o Dir::Etc::sourceparts=- \
|
||||||
|
-o APT::Get::List-Cleanup=0 >>"$LOG" 2>&1
|
||||||
|
}
|
||||||
|
|
||||||
|
# The registry can lag the release by a few seconds: wait for the tagged version.
|
||||||
|
candidate=""
|
||||||
|
for _ in $(seq 1 18); do
|
||||||
|
refresh || true
|
||||||
|
candidate=$(apt-cache policy devdesk | awk '/Candidate:/ {print $2}')
|
||||||
|
if [ -z "$want" ] || [ "$candidate" = "$want" ]; then
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
sleep 10
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ -n "$want" ] && [ "$candidate" != "$want" ]; then
|
||||||
|
log "registry still offers $candidate, not $want; giving up"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if [ "$candidate" = "$before" ]; then
|
||||||
|
log "already at $before; nothing to do"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
DEBIAN_FRONTEND=noninteractive apt-get install -y -q --only-upgrade \
|
||||||
|
-o Dpkg::Options::=--force-confold devdesk </dev/null >>"$LOG" 2>&1
|
||||||
|
log "devdesk $before -> $(dpkg-query -W -f='${Version}' devdesk)"
|
||||||
Reference in New Issue
Block a user