Files
ServerKit/.github/workflows/release.yml
T
Juan DenisandClaude Opus 5 4f12f8164d ci: boot the built image, and check the agent download URLs resolve
Issue #101 shipped because release.yml went straight from `build` to `push`
with nothing in between. The image was missing scripts/, so every containerised
panel 404'd its own enrollment URL, while the entire test suite stayed green --
pytest runs against the source tree, where the file always exists. Only booting
the artifact catches that class of bug.

scripts/test/smoke-docker-image.sh builds (or takes) an image, asserts the files
the panel reads at request time are present, boots it, and requests every route
that serves a file off disk. release.yml now builds amd64, runs it, and only
then pushes multi-arch (layers are cached, so the push reuses the build).
release-smoke.yml gets the same job on dev and PRs, and its path filters now
include Dockerfile, .dockerignore and scripts/ -- none of which used to trigger
it.

scripts/test/verify-agent-release-chain.sh covers the other half, which unit
tests structurally cannot: they stub curl, so they only ever prove the installer
is self-consistent, never that the URL it builds resolves to a real file. This
one reads the coordinates back out of the script, asks GitHub, downloads the
archive, verifies the checksum and confirms the tarball entry name the installer
moves. It runs nightly, and is also the tripwire for the reverse case -- the
agent repo changing its release naming and breaking the panel from the other
side of the repo split.

Both are plain scripts so the same check runs locally:
  bash scripts/test/smoke-docker-image.sh
  bash scripts/test/verify-agent-release-chain.sh

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-16 05:27:38 -04:00

277 lines
8.7 KiB
YAML

name: Release
on:
push:
branches:
- main
tags:
- 'v*'
workflow_dispatch:
inputs:
version:
description: 'Version to release (e.g., 1.6.24)'
required: true
type: string
prerelease:
description: 'Mark as pre-release'
type: boolean
default: false
concurrency:
group: release-${{ github.ref }}
cancel-in-progress: false
permissions:
contents: write
env:
DOCKER_IMAGE: jhd3197/serverkit
jobs:
ci-gate:
name: CI Gate
uses: ./.github/workflows/backend-ci.yml
version:
name: Resolve Version
runs-on: ubuntu-latest
needs: ci-gate
outputs:
version: ${{ steps.version.outputs.version }}
tag: ${{ steps.version.outputs.tag }}
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Get version
id: version
run: |
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
VERSION="${{ github.event.inputs.version }}"
elif [ "${{ github.event_name }}" = "push" ] && [ "${{ github.ref_type }}" = "tag" ]; then
VERSION="${GITHUB_REF#refs/tags/v}"
else
VERSION=$(cat VERSION | tr -d '[:space:]')
fi
TAG="v$VERSION"
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
echo "tag=$TAG" >> "$GITHUB_OUTPUT"
echo "Releasing ServerKit version: $VERSION"
- name: Create and push tag
if: github.event_name == 'push' && github.ref_type != 'tag'
run: |
TAG="${{ steps.version.outputs.tag }}"
if git rev-parse "$TAG" >/dev/null 2>&1; then
echo "Tag $TAG already exists, skipping"
else
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git tag -a "$TAG" -m "Release $TAG"
git push origin "$TAG"
fi
build-release:
name: Build Release Tarball
runs-on: ${{ matrix.runner }}
needs: version
strategy:
fail-fast: true
matrix:
include:
- runner: ubuntu-latest
arch: amd64
- runner: ubuntu-24.04-arm
arch: arm64
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: '22'
- name: Install build dependencies
run: |
sudo apt-get update
sudo apt-get install -y python3-venv python3-pip rsync
- name: Build release tarball
env:
BUILD_ARCH: ${{ matrix.arch }}
run: bash scripts/build-release.sh
- name: Upload release artifacts
uses: actions/upload-artifact@v4
with:
name: serverkit-release-${{ matrix.arch }}
path: |
serverkit-*.tar.gz
checksums.txt
if-no-files-found: error
build-docker:
name: Build Docker Image
runs-on: ubuntu-latest
needs: version
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
continue-on-error: true
# Build amd64 first and boot it before anything is published. Issue #101
# shipped because this job went straight from `build` to `push`: the image
# was missing scripts/, so /api/v1/servers/install.sh 404'd in every
# container while the whole test suite stayed green (pytest runs against
# the source tree, where the file always exists). Only booting the artifact
# catches that. The layers are cached, so the multi-arch push below reuses
# this build rather than repeating it.
- name: Build amd64 image for smoke test
uses: docker/build-push-action@v5
with:
context: .
platforms: linux/amd64
push: false
load: true
tags: serverkit:smoke
cache-from: type=gha
cache-to: type=gha,mode=max
build-args: |
VERSION=${{ needs.version.outputs.version }}
BUILD_TIME=${{ github.event.repository.updated_at }}
GIT_COMMIT=${{ github.sha }}
- name: Smoke test the built image
run: SKIP_BUILD=1 bash scripts/test/smoke-docker-image.sh serverkit:smoke
- name: Build and push Docker image
uses: docker/build-push-action@v5
with:
context: .
platforms: linux/amd64,linux/arm64
push: ${{ secrets.DOCKERHUB_USERNAME != '' }}
cache-from: type=gha
tags: |
${{ env.DOCKER_IMAGE }}:latest
${{ env.DOCKER_IMAGE }}:${{ needs.version.outputs.version }}
build-args: |
VERSION=${{ needs.version.outputs.version }}
BUILD_TIME=${{ github.event.repository.updated_at }}
GIT_COMMIT=${{ github.sha }}
release:
name: Create Release
runs-on: ubuntu-latest
needs: [version, build-docker, build-release]
permissions:
contents: write
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Download release artifacts
uses: actions/download-artifact@v4
with:
path: release-artifacts
pattern: serverkit-release-*
merge-multiple: true
- name: Assemble release files
run: |
mkdir -p release
cp VERSION release/VERSION
cp docker-compose.yml release/docker-compose.yml
cp Dockerfile release/Dockerfile
mv release-artifacts/serverkit-*.tar.gz release/ 2>/dev/null || true
mv release-artifacts/checksums.txt release/checksums.txt 2>/dev/null || true
cd release
sha256sum serverkit-*.tar.gz >> checksums.txt
sort -u checksums.txt -o checksums.txt
cat checksums.txt
- name: Create GitHub Release
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ needs.version.outputs.tag }}
name: ServerKit ${{ needs.version.outputs.tag }}
draft: false
prerelease: ${{ github.event.inputs.prerelease == 'true' }}
files: |
release/*
body: |
## ServerKit ${{ needs.version.outputs.tag }}
ServerKit is a server control panel for managing web applications, databases, Docker containers, and security on VPS/dedicated servers.
### Docker
```bash
docker pull ${{ env.DOCKER_IMAGE }}:${{ needs.version.outputs.version }}
docker pull ${{ env.DOCKER_IMAGE }}:latest
```
### One-line installer
```bash
curl -fsSL https://serverkit.ai/install.sh | bash
```
### Quick Start
```bash
git clone https://github.com/${{ github.repository }}.git
cd ServerKit
docker compose up -d
```
Default credentials: `admin` / `admin`
### What's New
See [CHANGELOG](https://github.com/${{ github.repository }}/blob/main/CHANGELOG.md) for details.
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
summary:
name: Release Summary
runs-on: ubuntu-latest
needs: [version, release]
steps:
- name: Create summary
run: |
echo "## ServerKit Release Summary" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "**Version:** ${{ needs.version.outputs.tag }}" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "### Docker" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "| Tag | Image |" >> $GITHUB_STEP_SUMMARY
echo "|-----|-------|" >> $GITHUB_STEP_SUMMARY
echo "| latest | ${{ env.DOCKER_IMAGE }}:latest |" >> $GITHUB_STEP_SUMMARY
echo "| ${{ needs.version.outputs.version }} | ${{ env.DOCKER_IMAGE }}:${{ needs.version.outputs.version }} |" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "### Release" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "Tarballs: serverkit-${{ needs.version.outputs.tag }}-linux-amd64.tar.gz, serverkit-${{ needs.version.outputs.tag }}-linux-arm64.tar.gz" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "Release created at: https://github.com/${{ github.repository }}/releases/tag/${{ needs.version.outputs.tag }}" >> $GITHUB_STEP_SUMMARY