mirror of
https://github.com/jhd3197/ServerKit.git
synced 2026-10-23 19:49:14 +09:00
The frontend's 141 node --test assertions were invocable but invoked by no workflow — written insurance never cashed in. frontend-ci now runs npm test after lint (verified green locally: 141/141 in ~2s). release.yml's ci-gate covered backend-ci only, so a release could ship with a red frontend or drifted builtin extensions. The gate now also requires frontend-ci and extensions-ci (both fast, both already workflow_call-able). scripts-ci stays ungated on purpose — its update-e2e leg takes ~45 min — with a comment showing how to add it. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
320 lines
10 KiB
YAML
320 lines
10 KiB
YAML
name: Release
|
|
|
|
on:
|
|
push:
|
|
branches:
|
|
- main
|
|
|
|
concurrency:
|
|
group: release-${{ github.ref }}
|
|
cancel-in-progress: false
|
|
|
|
permissions:
|
|
contents: write
|
|
pull-requests: read
|
|
|
|
env:
|
|
DOCKER_IMAGE: jhd3197/serverkit
|
|
|
|
jobs:
|
|
promotion-gate:
|
|
name: Verify dev promotion
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- name: Require a merged dev to main pull request
|
|
uses: actions/github-script@v9
|
|
with:
|
|
script: |
|
|
const { owner, repo } = context.repo;
|
|
const { data: pulls } = await github.rest.repos.listPullRequestsAssociatedWithCommit({
|
|
owner,
|
|
repo,
|
|
commit_sha: context.sha,
|
|
});
|
|
const promotion = pulls.find((pull) => (
|
|
pull.merged_at
|
|
&& pull.base.ref === 'main'
|
|
&& pull.head.ref === 'dev'
|
|
&& pull.head.repo?.full_name === `${owner}/${repo}`
|
|
));
|
|
|
|
if (!promotion) {
|
|
core.setFailed(
|
|
'Releases from main require a merged pull request from this repository\'s dev branch.'
|
|
);
|
|
return;
|
|
}
|
|
|
|
core.info(`Verified dev → main promotion via PR #${promotion.number}.`);
|
|
|
|
# A release must not ship with a red frontend or drifted builtin
|
|
# extensions, so those suites gate here alongside backend-ci.
|
|
# scripts-ci (installer/updater suites incl. the ~45-min update-e2e leg)
|
|
# supports workflow_call too but is deliberately NOT gated to keep
|
|
# releases fast — add `uses: ./.github/workflows/scripts-ci.yml` as a
|
|
# fourth gate job if that trade-off changes.
|
|
ci-gate:
|
|
name: CI Gate
|
|
needs: promotion-gate
|
|
uses: ./.github/workflows/backend-ci.yml
|
|
|
|
frontend-gate:
|
|
name: Frontend Gate
|
|
needs: promotion-gate
|
|
uses: ./.github/workflows/frontend-ci.yml
|
|
|
|
extensions-gate:
|
|
name: Extensions Gate
|
|
needs: promotion-gate
|
|
uses: ./.github/workflows/extensions-ci.yml
|
|
|
|
version:
|
|
name: Resolve Version
|
|
runs-on: ubuntu-latest
|
|
needs: [ci-gate, frontend-gate, extensions-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: |
|
|
VERSION=$(tr -d '[:space:]' < VERSION)
|
|
TAG="v$VERSION"
|
|
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
|
|
echo "tag=$TAG" >> "$GITHUB_OUTPUT"
|
|
echo "Releasing ServerKit version: $VERSION"
|
|
|
|
- name: Require an unused release version
|
|
env:
|
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
TAG: ${{ steps.version.outputs.tag }}
|
|
run: |
|
|
if git show-ref --verify --quiet "refs/tags/$TAG"; then
|
|
echo "::error::Refusing to overwrite existing tag $TAG. Bump VERSION before promoting dev to main."
|
|
exit 1
|
|
fi
|
|
if gh release view "$TAG" --repo "$GITHUB_REPOSITORY" >/dev/null 2>&1; then
|
|
echo "::error::Refusing to overwrite existing release $TAG. Bump VERSION before promoting dev to main."
|
|
exit 1
|
|
fi
|
|
|
|
- name: Create and push tag
|
|
run: |
|
|
TAG="${{ steps.version.outputs.tag }}"
|
|
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 "refs/tags/$TAG"
|
|
|
|
build-release:
|
|
name: Build Release Tarball
|
|
runs-on: ${{ matrix.runner }}
|
|
needs: version
|
|
# Past runs finish in ~13 min; without this cap a hung apt mirror once
|
|
# held the job for GitHub's 6-hour default.
|
|
timeout-minutes: 30
|
|
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@v3
|
|
with:
|
|
tag_name: ${{ needs.version.outputs.tag }}
|
|
name: ServerKit ${{ needs.version.outputs.tag }}
|
|
draft: false
|
|
prerelease: false
|
|
overwrite_files: false
|
|
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
|