mirror of
https://github.com/jhd3197/ServerKit.git
synced 2026-10-25 12:37:52 +09:00
scripts/install.sh and scripts/install.ps1 are now VENDORED copies. The agent repo owns them; this repo carries a copy only because the panel serves them off its own disk at GET /api/v1/servers/install.sh|.ps1 and cannot link to another repository. Two copies of one file is what caused the second half of #101. When the agent moved out of this monorepo it took its installer with it, and from then on there were two -- actually four: the agent repo also carried a third, older pair under its own scripts/ that nothing referenced. Each looked fine in isolation, nothing ever compared them, and the panel went on serving one that downloaded from `agent-v*` tags which exist in no repository. Unit tests are structurally blind to this: they exercise whichever copy lives in their own repo and have no idea another exists. So: scripts/sync-agent-installers.sh re-vendors from the agent repo (a sibling checkout if present, else the published branch), normalising to LF so this repo's .gitattributes can apply its own EOL rule scripts/test/check-installer-drift.sh fails, with the diff and the fix, if a vendored copy differs from canonical (EOL-insensitive: *.ps1 is checked out CRLF here and LF there, which is not drift) The drift check runs nightly alongside the release-chain verification, since both need to reach GitHub. Both scripts run locally against a sibling checkout, so this works offline and before an agent change is published. Both installers carry a provenance header naming the canonical location, so anyone who opens the vendored copy to patch it is told where to patch instead. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
212 lines
9.1 KiB
YAML
212 lines
9.1 KiB
YAML
name: Scripts Nightly E2E
|
|
|
|
# Nightly, non-blocking end-to-end coverage for the artifacts users actually
|
|
# receive — deeper (and slower) than the PR-time Scripts CI:
|
|
#
|
|
# tarball-install builds the release tarball with scripts/build-release.sh
|
|
# and INSTALLS from it through the installer's offline
|
|
# path. The PR-time release smoke only proves the tarball
|
|
# *builds*; this proves the shipped tree lands and the
|
|
# pre-built, relocated venv actually runs at
|
|
# /opt/serverkit/venv (the shipped-venv portability class
|
|
# of breakage).
|
|
#
|
|
# update-from-release installs the latest PUBLISHED release, then runs THIS
|
|
# checkout's updater over it — a genuine cross-version
|
|
# update, which catches carry-forward/migration
|
|
# regressions that a same-version forced update cannot.
|
|
# The published baseline may itself be old or broken;
|
|
# that is tolerated loudly — only a failure in this
|
|
# checkout's updater fails the job.
|
|
#
|
|
# Both jobs mutate their runner (systemd units, nginx, /opt/serverkit), so each
|
|
# runs alone on a fresh VM. There is deliberately no pull_request trigger:
|
|
# nightly results must never block a PR.
|
|
|
|
on:
|
|
schedule:
|
|
# 05:17 UTC — off the top-of-hour spike, after the day's merges settle.
|
|
- cron: '17 5 * * *'
|
|
workflow_dispatch:
|
|
|
|
jobs:
|
|
# Asks GitHub whether the URLs the agent installer builds resolve to real
|
|
# assets. Every unit test stubs curl, so they prove the script is internally
|
|
# consistent and nothing more -- which is how the installer went on pointing
|
|
# at jhd3197/ServerKit and `agent-v*` tags for the entire period after the Go
|
|
# agent moved to its own repo (issue #101). This job is also the tripwire for
|
|
# the reverse case: a change to the agent's release naming or tag scheme
|
|
# breaking the panel's installer from the other side of the repo split.
|
|
agent-release-chain:
|
|
name: Agent download URLs resolve on GitHub
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 10
|
|
steps:
|
|
- name: Checkout code
|
|
uses: actions/checkout@v4
|
|
|
|
- name: Verify the agent release chain
|
|
run: bash scripts/test/verify-agent-release-chain.sh
|
|
|
|
# scripts/install.sh is a VENDORED copy of the agent repo's canonical one
|
|
# (the panel has to serve it off its own disk). Two copies of one file is
|
|
# what caused the second half of #101: one was fixed, the other was not,
|
|
# and nothing compared them. Unit tests cannot see this -- they only ever
|
|
# exercise the copy in their own repo -- so the comparison has to be a
|
|
# cross-repo job. Re-vendor with scripts/sync-agent-installers.sh.
|
|
- name: Vendored installers match the agent repo
|
|
run: bash scripts/test/check-installer-drift.sh
|
|
|
|
tarball-install:
|
|
name: Install from a freshly built release tarball
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 45
|
|
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 the release tarball
|
|
run: bash scripts/build-release.sh
|
|
|
|
- name: Install from the local tarball (offline release path)
|
|
run: |
|
|
# SERVERKIT_OFFLINE_TARBALL is the installer's documented local-tarball
|
|
# mechanism: fetch_release uses it verbatim instead of downloading.
|
|
# `sudo env`, not `... | sudo bash`, so the env contract survives
|
|
# sudo's environment reset; PATH is forwarded so the runner's
|
|
# toolchain stays visible to root.
|
|
TARBALL="$GITHUB_WORKSPACE/serverkit-v$(tr -d '\n\r ' < VERSION)-linux-amd64.tar.gz"
|
|
ls -lh "$TARBALL"
|
|
sudo env PATH="$PATH" \
|
|
INSTALL_FROM_RELEASE=1 \
|
|
SERVERKIT_OFFLINE_TARBALL="$TARBALL" \
|
|
SERVERKIT_SKIP_SSL=1 \
|
|
bash install.sh
|
|
|
|
- name: Assert the tree landed and the shipped venv works
|
|
run: |
|
|
readlink -f /opt/serverkit
|
|
test -d /opt/serverkit/backend/app
|
|
test -d /opt/serverkit/frontend/dist
|
|
test -x /opt/serverkit/serverkit
|
|
[ "$(tr -d '\n\r ' < VERSION)" = "$(tr -d '\n\r ' < /opt/serverkit/VERSION)" ]
|
|
test -x /opt/serverkit/venv/bin/python
|
|
# The venv was built elsewhere (the build step) and relocated by
|
|
# build-release.sh — importing the app's core deps from its installed
|
|
# path is the actual portability proof.
|
|
sudo /opt/serverkit/venv/bin/python -c 'import flask, sqlalchemy, gunicorn; print("shipped venv imports OK")'
|
|
|
|
- name: Panel healthy from the tarball install
|
|
run: |
|
|
for i in $(seq 1 30); do
|
|
curl -fsS http://127.0.0.1:5000/api/v1/system/health >/dev/null 2>&1 && {
|
|
echo "backend healthy (probe $i)"; exit 0; }
|
|
sleep 2
|
|
done
|
|
echo "backend never became healthy after the tarball install" >&2
|
|
exit 1
|
|
|
|
- name: Dump install state
|
|
if: failure()
|
|
run: |
|
|
set +e
|
|
echo '===== /var/log/serverkit ====='
|
|
sudo sh -c 'for f in /var/log/serverkit/*.log; do
|
|
[ -f "$f" ] || continue
|
|
echo "----- $f (last 300 lines) -----"
|
|
tail -n 300 "$f"
|
|
done'
|
|
echo '===== systemd ====='
|
|
sudo systemctl status serverkit --no-pager -l
|
|
sudo journalctl -u serverkit --no-pager -n 200
|
|
echo '===== nginx ====='
|
|
sudo systemctl status nginx --no-pager -l
|
|
sudo nginx -t
|
|
sudo tail -n 100 /var/log/nginx/error.log
|
|
echo '===== layout ====='
|
|
ls -la /opt/ /opt/serverkit/
|
|
true
|
|
|
|
update-from-release:
|
|
name: Update from the latest published release
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 60
|
|
steps:
|
|
- name: Checkout code
|
|
uses: actions/checkout@v4
|
|
|
|
- name: Install the latest published release (baseline)
|
|
id: baseline
|
|
run: |
|
|
# INSTALL_FROM_RELEASE=1 with no version pin resolves and installs the
|
|
# newest published tarball — the state a real, older box is in. The
|
|
# baseline is not this checkout's product: if it fails to install or
|
|
# come up healthy, warn loudly and skip the update leg instead of
|
|
# failing — this job only gates regressions in THIS checkout's updater.
|
|
ok=true
|
|
sudo env PATH="$PATH" INSTALL_FROM_RELEASE=1 SERVERKIT_SKIP_SSL=1 \
|
|
bash install.sh || ok=false
|
|
if [ "$ok" = "true" ]; then
|
|
ok=false
|
|
for i in $(seq 1 30); do
|
|
curl -fsS http://127.0.0.1:5000/api/v1/system/health >/dev/null 2>&1 && { ok=true; break; }
|
|
sleep 2
|
|
done
|
|
fi
|
|
echo "baseline version: $(tr -d '\n\r ' < /opt/serverkit/VERSION 2>/dev/null || echo none)"
|
|
echo "ok=$ok" >> "$GITHUB_OUTPUT"
|
|
[ "$ok" = "true" ] \
|
|
|| echo "::warning::baseline install of the published release failed or never got healthy — skipping the update leg (not a regression in this checkout)"
|
|
|
|
- name: Run THIS checkout's updater over the published install
|
|
if: steps.baseline.outputs.ok == 'true'
|
|
run: |
|
|
# SERVERKIT_NO_SELF_UPDATE=1: do not re-exec into the published
|
|
# updater — the object under test is scripts/update.sh from this
|
|
# checkout. --force: never let the version gate skip the run.
|
|
sudo env PATH="$PATH" SERVERKIT_NO_SELF_UPDATE=1 bash scripts/update.sh --force
|
|
|
|
- name: Panel healthy after the cross-version update
|
|
if: steps.baseline.outputs.ok == 'true'
|
|
run: |
|
|
for i in $(seq 1 45); do
|
|
curl -fsS http://127.0.0.1:5000/api/v1/system/health >/dev/null 2>&1 && {
|
|
echo "backend healthy after update to version $(tr -d '\n\r ' < /opt/serverkit/VERSION 2>/dev/null || echo unknown) (probe $i)"
|
|
exit 0
|
|
}
|
|
sleep 2
|
|
done
|
|
echo "backend never became healthy after the cross-version update" >&2
|
|
exit 1
|
|
|
|
- name: Dump install/update state
|
|
if: failure()
|
|
run: |
|
|
set +e
|
|
echo '===== /var/log/serverkit (updater logs) ====='
|
|
sudo sh -c 'for f in /var/log/serverkit/*.log; do
|
|
[ -f "$f" ] || continue
|
|
echo "----- $f (last 300 lines) -----"
|
|
tail -n 300 "$f"
|
|
done'
|
|
echo '===== systemd ====='
|
|
sudo systemctl status serverkit --no-pager -l
|
|
sudo journalctl -u serverkit --no-pager -n 200
|
|
echo '===== nginx ====='
|
|
sudo systemctl status nginx --no-pager -l
|
|
sudo nginx -t
|
|
sudo tail -n 100 /var/log/nginx/error.log
|
|
echo '===== layout ====='
|
|
ls -la /opt/ /opt/serverkit/
|
|
true
|