Files
devdesk/.gitea/workflows/build-deb.yml
T
jaredandClaude Opus 5.5 74927a7640
build-deb / Lint, test, package (push) Successful in 3m53s
build-deb / Deploy to CT 126 (push) Successful in 10s
build-deb / Notify on failure (push) Skipped
fix: v0.9.1 robustness cleanups
- run_resume falls back to a new conversation only when the transcript file
  is gone; other failures end at the menu with the recorded id intact.
- The SessionStart hook resolves the session name under the registry lock;
  rename holds it across tmux rename-session and the registry move
  (dd_registry_locked, nestable).
- dd_load_env parses KEY=value like systemd instead of sourcing the files.
- devdesk-deploy: DPkg::Lock::Timeout, explicit FAILED lines on stdout and in
  the log; the hook answers synchronously and CI checks the HTTP status.
  logrotate for the deploy log.
- postinst drops duplicate terminal-features entries; hooks sweep stale
  temp files.

Closes #12

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 01:01:51 -04:00

196 lines
7.9 KiB
YAML

name: build-deb
on:
push:
branches: ["**"]
tags: ["v*"]
pull_request:
concurrency:
group: build-deb-${{ github.ref }}
cancel-in-progress: true
defaults:
run:
shell: bash # the runner's default is sh; steps below use bash syntax
env:
GITEA_URL: https://code.lotusguild.org
OWNER: LotusGuild
REPO: LotusGuild/devdesk
jobs:
build:
name: Lint, test, package
runs-on: ubuntu-latest
# Bookworm (tmux 3.3, debhelper 13); the node image keeps actions/checkout working.
container:
image: node:22-bookworm
steps:
- uses: actions/checkout@v3
- name: Install build tools
run: |
apt-get update -qq
apt-get install -y -qq --no-install-recommends \
build-essential debhelper devscripts lintian fakeroot bats tmux fzf jq curl ca-certificates xz-utils >/dev/null
VER=v0.10.0
curl -fsSL --retry 5 --retry-delay 5 --retry-all-errors -o shellcheck.tar.xz \
"https://github.com/koalaman/shellcheck/releases/download/${VER}/shellcheck-${VER}.linux.x86_64.tar.xz"
tar -xJf shellcheck.tar.xz
install "shellcheck-${VER}/shellcheck" /usr/local/bin/shellcheck
rm -rf shellcheck.tar.xz "shellcheck-${VER}"
shellcheck --version | head -2
- name: npm ci
run: |
for i in 1 2 3; do
npm ci --no-audit --no-fund && break
echo "npm ci failed (attempt $i), retrying"; sleep 10
done
git diff --exit-code package-lock.json
- name: Lint
run: make lint
- name: Test
run: make test
- name: Set package version
id: version
env:
DEBEMAIL: ci@lotusguild.org
DEBFULLNAME: Lotus CI
run: |
base=$(dpkg-parsechangelog -S Version)
case "$GITHUB_REF" in
refs/tags/v*) version=${GITHUB_REF#refs/tags/v} ;;
*) version="${base}+ci${GITHUB_RUN_NUMBER}.g${GITHUB_SHA::7}" ;;
esac
if [ "$version" != "$base" ]; then
dch --newversion "$version" --distribution bookworm --force-distribution "CI build of ${GITHUB_SHA::7}"
fi
echo "version=$version" >> "$GITHUB_OUTPUT"
echo "Building devdesk $version"
- name: Build package
run: |
DEB_BUILD_OPTIONS=nocheck dpkg-buildpackage -us -uc -b
mkdir -p out && mv ../devdesk_*.deb out/
ls -l out/
- name: Lintian
run: lintian --fail-on error --info out/*.deb
- name: Smoke test
run: |
set -x
apt-get install -y -qq ./out/devdesk_*.deb >/dev/null
/usr/lib/devdesk/bin/ttyd --version
test -s /etc/devdesk/secret.env
. /etc/devdesk/secret.env
mkdir -p /run/devdesk
tmux -u -D -S /run/devdesk/tmux.sock -f /usr/share/devdesk/tmux.conf &
/usr/lib/devdesk/devdesk-tmux-ready
DEVDESK_CREDENTIAL="$DEVDESK_CREDENTIAL" /usr/lib/devdesk/devdesk-serve &
for _ in $(seq 1 30); do curl -s -o /dev/null localhost:7681/ && break; sleep 0.2; done
code=$(curl -s -o /dev/null -w '%{http_code}' localhost:7681/)
[ "$code" = 401 ] || { echo "expected 401 without credential, got $code"; exit 1; }
# Download first: `curl | grep -q` fails under pipefail (grep exits early, curl gets SIGPIPE).
curl -sf -u "$DEVDESK_CREDENTIAL" -o page.html localhost:7681/
grep -q 'name="generator" content="devdesk"' page.html
curl -sf -u "$DEVDESK_CREDENTIAL" localhost:7681/token | jq -e '.token | length > 0'
DEVDESK_NO_SYSTEMD=1 devdesk attach --list | jq -e '.default == "main"'
echo "smoke test passed"
- name: Upload artifact
uses: actions/upload-artifact@v3
with:
name: devdesk_${{ steps.version.outputs.version }}_amd64
path: out/*.deb
- name: Publish to Gitea apt registry
if: startsWith(github.ref, 'refs/tags/v')
env:
TOKEN: ${{ secrets.PACKAGE_TOKEN || secrets.NPM_PUBLISH_TOKEN }}
VERSION: ${{ steps.version.outputs.version }}
run: |
deb=$(ls out/devdesk_*.deb)
pool="$GITEA_URL/api/packages/$OWNER/debian/pool/bookworm/main"
upload() { curl -s -o resp.txt -w '%{http_code}' -H "Authorization: token $TOKEN" --upload-file "$deb" "$pool/upload"; }
code=$(upload)
if [ "$code" = 409 ]; then
echo "devdesk $VERSION already published; replacing it"
curl -sf -X DELETE -H "Authorization: token $TOKEN" "$pool/devdesk/$VERSION/amd64"
code=$(upload)
fi
[ "$code" = 201 ] || { echo "registry upload failed: HTTP $code"; cat resp.txt; exit 1; }
echo "published devdesk $VERSION to $GITEA_URL/$OWNER/-/packages/debian/devdesk"
- name: Create Gitea release
if: startsWith(github.ref, 'refs/tags/v')
env:
TOKEN: ${{ secrets.RELEASE_TOKEN }}
TAG: ${{ github.ref_name }}
run: |
api="$GITEA_URL/api/v1/repos/$REPO"
auth=(-H "Authorization: token $TOKEN")
id=$(curl -sf "${auth[@]}" "$api/releases/tags/$TAG" | jq -r '.id // empty' || true)
if [ -z "$id" ]; then
body=$(jq -n --arg tag "$TAG" '{tag_name: $tag, name: ("devdesk " + $tag), draft: false, prerelease: false,
body: "Install: `apt update && apt install devdesk` (Gitea apt registry), or download the .deb below."}')
id=$(curl -sf "${auth[@]}" -H 'Content-Type: application/json' -d "$body" "$api/releases" | jq -r '.id')
fi
deb=$(ls out/devdesk_*.deb)
name=$(basename "$deb")
existing=$(curl -sf "${auth[@]}" "$api/releases/$id/assets" | jq -r --arg n "$name" '.[] | select(.name == $n) | .id')
[ -n "$existing" ] && curl -sf -X DELETE "${auth[@]}" "$api/releases/$id/assets/$existing"
curl -sf "${auth[@]}" -H 'Content-Type: application/octet-stream' --data-binary "@$deb" \
"$api/releases/$id/assets?name=$name" >/dev/null
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.
# The hook answers when the install is done (HTTP 500 if it failed), so
# this job's result is the deploy's result.
- 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}')
code=$(curl -sS --connect-timeout 10 --max-time 900 -o /tmp/deploy.out -w '%{http_code}' \
-X POST \
-H "Content-Type: application/json" \
-H "X-Gitea-Signature: ${SIG}" \
-d "$PAYLOAD" \
"http://10.10.10.58:9000/hooks/devdesk-deploy") || code=000
cat /tmp/deploy.out 2>/dev/null || true
echo "webhook: HTTP $code"
[ "$code" = 200 ]
notify-failure:
name: Notify on failure
runs-on: ubuntu-latest
needs: [build, deploy]
if: failure() && github.event_name == 'push'
steps:
- name: Send Matrix alert
env:
MATRIX_WEBHOOK_URL: ${{ secrets.MATRIX_WEBHOOK_URL }}
REPO: ${{ github.repository }}
BRANCH: ${{ github.ref_name }}
RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
run: |
if [ -z "$MATRIX_WEBHOOK_URL" ] || [ "$MATRIX_WEBHOOK_URL" = "CONFIGURE_ME" ]; then exit 0; fi
curl -sf -X POST "$MATRIX_WEBHOOK_URL" \
-H "Content-Type: application/json" \
-d "{\"text\":\"CI FAILED: ${REPO} @ ${BRANCH} — ${RUN_URL}\"}"