Some checks failed
Release / Test backend (push) Successful in 55s
Release / Check ui (push) Successful in 1m0s
Release / Docker (push) Failing after 2m52s
Release / Deploy to prod (push) Has been skipped
Release / Deploy to homelab (push) Has been skipped
Release / Gitea Release (push) Has been skipped
Homelab is on private network (192.168.0.109), so we can safely disable strict host key checking. This avoids the complexity of managing known_hosts entries in Gitea secrets. Changes: - Remove HOMELAB_SSH_KNOWN_HOSTS requirement - Add -o StrictHostKeyChecking=no to scp/ssh commands - Add -o UserKnownHostsFile=/dev/null to avoid host key persistence
207 lines
7.5 KiB
YAML
207 lines
7.5 KiB
YAML
name: Release
|
|
|
|
on:
|
|
push:
|
|
tags:
|
|
- "v*" # e.g. v1.0.0, v1.2.3
|
|
|
|
concurrency:
|
|
group: ${{ gitea.workflow }}-${{ gitea.ref }}
|
|
cancel-in-progress: true
|
|
|
|
jobs:
|
|
# ── backend: vet & test ───────────────────────────────────────────────────────
|
|
test-backend:
|
|
name: Test backend
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
|
|
- uses: actions/setup-go@v5
|
|
with:
|
|
go-version-file: backend/go.mod
|
|
cache-dependency-path: backend/go.sum
|
|
|
|
- name: go vet
|
|
working-directory: backend
|
|
run: go vet ./...
|
|
|
|
- name: Run tests
|
|
working-directory: backend
|
|
run: go test -short -race -count=1 -timeout=60s ./...
|
|
|
|
# ── ui: type-check & build ────────────────────────────────────────────────────
|
|
check-ui:
|
|
name: Check ui
|
|
runs-on: ubuntu-latest
|
|
defaults:
|
|
run:
|
|
working-directory: ui
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
|
|
- uses: actions/setup-node@v4
|
|
with:
|
|
node-version: "22"
|
|
cache: npm
|
|
cache-dependency-path: ui/package-lock.json
|
|
|
|
- name: Install dependencies
|
|
run: npm ci
|
|
|
|
- name: Type check
|
|
run: npm run check
|
|
|
|
- name: Build
|
|
run: npm run build
|
|
|
|
# ── docker: build + push all images via docker bake ──────────────────────────
|
|
docker:
|
|
name: Docker
|
|
runs-on: ubuntu-latest
|
|
needs: [test-backend, check-ui]
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
|
|
- uses: docker/setup-buildx-action@v3
|
|
|
|
- name: Log in to Docker Hub
|
|
uses: docker/login-action@v3
|
|
with:
|
|
username: ${{ secrets.DOCKER_USER }}
|
|
password: ${{ secrets.DOCKER_TOKEN }}
|
|
|
|
- name: Compute version tags
|
|
id: ver
|
|
run: |
|
|
V="${{ gitea.ref_name }}"
|
|
VER="${V#v}"
|
|
echo "version=$VER" >> "$GITHUB_OUTPUT"
|
|
echo "major_minor=$(echo "$VER" | cut -d. -f1-2)" >> "$GITHUB_OUTPUT"
|
|
|
|
- name: Build and push all images
|
|
uses: docker/bake-action@v6
|
|
with:
|
|
files: docker-bake.hcl
|
|
set: |
|
|
*.output=type=image,push=true
|
|
env:
|
|
VERSION: ${{ steps.ver.outputs.version }}
|
|
MAJOR_MINOR: ${{ steps.ver.outputs.major_minor }}
|
|
COMMIT: ${{ gitea.sha }}
|
|
BUILD_TIME: ${{ gitea.event.head_commit.timestamp }}
|
|
|
|
# ── deploy: sync docker-compose.yml + restart prod ───────────────────────────
|
|
# Runs after all images are pushed to Docker Hub.
|
|
# Copies the compose file from the tagged commit to the server, pulls the new
|
|
# images, and restarts only the services whose image or config changed.
|
|
# --remove-orphans cleans up containers no longer defined in the compose file
|
|
# (e.g. the now-removed pb-init container).
|
|
#
|
|
# Required Gitea secrets:
|
|
# PROD_HOST — prod server IP or hostname
|
|
# PROD_USER — SSH login user (typically root)
|
|
# PROD_SSH_KEY — private key whose public half is in authorized_keys
|
|
# PROD_SSH_KNOWN_HOSTS — output of: ssh-keyscan -H <PROD_HOST>
|
|
deploy-prod:
|
|
name: Deploy to prod
|
|
runs-on: ubuntu-latest
|
|
needs: [docker]
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
|
|
- name: Install SSH key
|
|
run: |
|
|
mkdir -p ~/.ssh
|
|
printf '%s\n' "${{ secrets.PROD_SSH_KEY }}" > ~/.ssh/deploy_key
|
|
chmod 600 ~/.ssh/deploy_key
|
|
printf '%s\n' "${{ secrets.PROD_SSH_KNOWN_HOSTS }}" >> ~/.ssh/known_hosts
|
|
|
|
- name: Copy docker-compose.yml to prod
|
|
run: |
|
|
scp -i ~/.ssh/deploy_key \
|
|
docker-compose.yml \
|
|
"${{ secrets.PROD_USER }}@${{ secrets.PROD_HOST }}:/opt/libnovel/docker-compose.yml"
|
|
|
|
- name: Pull new images and restart changed services
|
|
run: |
|
|
ssh -i ~/.ssh/deploy_key \
|
|
"${{ secrets.PROD_USER }}@${{ secrets.PROD_HOST }}" \
|
|
'set -euo pipefail
|
|
cd /opt/libnovel
|
|
doppler run -- docker compose pull backend runner ui caddy pocketbase
|
|
doppler run -- docker compose up -d --remove-orphans'
|
|
|
|
# ── deploy homelab runner ─────────────────────────────────────────────────────
|
|
# Syncs the homelab runner compose file and restarts the runner service.
|
|
#
|
|
# Required Gitea secrets:
|
|
# HOMELAB_HOST — homelab server IP (192.168.0.109)
|
|
# HOMELAB_USER — SSH login user (typically root)
|
|
# HOMELAB_SSH_KEY — private key whose public half is in authorized_keys
|
|
# HOMELAB_SSH_KNOWN_HOSTS — output of: ssh-keyscan -H <HOMELAB_HOST>
|
|
deploy-homelab:
|
|
name: Deploy to homelab
|
|
runs-on: ubuntu-latest
|
|
needs: [docker]
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
|
|
- name: Install SSH key
|
|
run: |
|
|
mkdir -p ~/.ssh
|
|
printf '%s\n' "${{ secrets.HOMELAB_SSH_KEY }}" > ~/.ssh/homelab_key
|
|
chmod 600 ~/.ssh/homelab_key
|
|
|
|
- name: Copy docker-compose.yml to homelab
|
|
run: |
|
|
scp -i ~/.ssh/homelab_key \
|
|
-o StrictHostKeyChecking=no \
|
|
-o UserKnownHostsFile=/dev/null \
|
|
homelab/docker-compose.yml \
|
|
"${{ secrets.HOMELAB_USER }}@${{ secrets.HOMELAB_HOST }}:/opt/libnovel-runner/docker-compose.yml"
|
|
|
|
- name: Pull new runner image and restart
|
|
run: |
|
|
ssh -i ~/.ssh/homelab_key \
|
|
-o StrictHostKeyChecking=no \
|
|
-o UserKnownHostsFile=/dev/null \
|
|
"${{ secrets.HOMELAB_USER }}@${{ secrets.HOMELAB_HOST }}" \
|
|
'set -euo pipefail
|
|
cd /opt/libnovel-runner
|
|
doppler run --project libnovel --config prd_homelab -- docker compose pull runner
|
|
doppler run --project libnovel --config prd_homelab -- docker compose up -d runner'
|
|
|
|
# ── Gitea release ─────────────────────────────────────────────────────────────
|
|
release:
|
|
name: Gitea Release
|
|
runs-on: ubuntu-latest
|
|
needs: [docker]
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
with:
|
|
fetch-depth: 0
|
|
|
|
- name: Extract release notes from tag commit
|
|
id: notes
|
|
run: |
|
|
set -euo pipefail
|
|
# Subject line (first line of commit message) → release title
|
|
SUBJECT=$(git log -1 --format="%s" "${{ gitea.sha }}")
|
|
# Body (everything after the blank line) → release body
|
|
BODY=$(git log -1 --format="%b" "${{ gitea.sha }}" | sed '/^Co-Authored-By:/d' | sed '/^[[:space:]]*$/{ N; /^\n$/d }' | sed 's/^[[:space:]]*$//' | awk 'NF || !p; {p = !NF}')
|
|
echo "title=${SUBJECT}" >> "$GITHUB_OUTPUT"
|
|
# Use a heredoc delimiter to safely handle multi-line body
|
|
{
|
|
echo "body<<RELEASE_BODY_EOF"
|
|
echo "${BODY}"
|
|
echo "RELEASE_BODY_EOF"
|
|
} >> "$GITHUB_OUTPUT"
|
|
|
|
- name: Create release
|
|
uses: https://gitea.com/actions/gitea-release-action@v1
|
|
with:
|
|
token: ${{ secrets.GITEA_TOKEN }}
|
|
title: ${{ steps.notes.outputs.title }}
|
|
body: ${{ steps.notes.outputs.body }}
|