Files
homelable/.github/workflows/install-smoke.yml
T
Pouzor 97472b65ba fix(ci): run the install smoke test under bash, not dash
The runner hands container steps `sh -e {0}` unless told otherwise, so
every step died on `set: Illegal option -o pipefail` before the installer
ran. Pin the job's shell to bash; `[[ … ]]` in the assertions needs it too.

ha-relevant: no
2026-09-02 02:15:10 +02:00

132 lines
5.0 KiB
YAML

name: Install smoke test
# Runs scripts/install-baremetal.sh for real, in a clean debian:12 container.
# ShellCheck reads the script but never executes it, so it cannot catch an
# unintended `set -euo pipefail` abort — the failure class this job exists for.
# debian:12 reproduces the two conditions that broke the installer once already:
# the image carries no Node at all (so the version probe and the nodesource
# install both run) and the job has no TTY (so the prompt fallbacks run).
#
# What it does not cover: systemd. A container has no PID 1 systemd, so
# `systemctl` is stubbed out and the unit is written but never started. The
# backend is booted by hand afterwards to prove the generated .env parses.
on:
pull_request:
branches: [main]
paths:
- 'scripts/install-baremetal.sh'
- 'backend/requirements.txt'
- 'frontend/package.json'
- 'frontend/package-lock.json'
- '.github/workflows/install-smoke.yml'
push:
branches: [main]
paths:
- 'scripts/install-baremetal.sh'
- 'backend/requirements.txt'
- 'frontend/package.json'
- 'frontend/package-lock.json'
- '.github/workflows/install-smoke.yml'
permissions:
contents: read
jobs:
baremetal:
runs-on: ubuntu-latest
container: debian:12
timeout-minutes: 25
# Without this the runner hands container steps `sh -e {0}` — dash, where
# `set -o pipefail` and `[[ … ]]` are syntax errors.
defaults:
run:
shell: bash
steps:
- name: Install checkout prerequisites
run: |
apt-get update -qq
DEBIAN_FRONTEND=noninteractive apt-get install -y -qq git ca-certificates >/dev/null
- uses: actions/checkout@v4
- name: Confirm the container reproduces a fresh host
run: |
set -euo pipefail
node --version || echo "no node — the nodesource path will run"
[ -t 0 ] && echo "TTY present (unexpected)" || echo "no TTY — prompt fallbacks will run"
- name: Stub systemctl
# No PID 1 systemd in a container. The stub logs the calls so the run
# shows which unit actions the script attempted.
run: |
printf '#!/bin/sh\necho "[systemctl stub] $*"\n' > /usr/local/bin/systemctl
chmod +x /usr/local/bin/systemctl
- name: Run the installer
# INSTALL_DIR is the checkout, so the clone is skipped and the script
# installs the tree under test rather than main. SKIP_NGINX because the
# front end is not what this job is asserting.
run: |
INSTALL_DIR="$GITHUB_WORKSPACE" \
SKIP_NGINX=1 \
ADMIN_PASSWORD=ci-smoke-password \
SCANNER_RANGES='["10.0.0.0/24"]' \
bash scripts/install-baremetal.sh
- name: The generated .env is well formed
run: |
set -euo pipefail
test -f backend/.env
grep -qE '^SECRET_KEY=[0-9a-f]{64}$' backend/.env
grep -qE '^AUTH_PASSWORD_HASH=.\$2b\$' backend/.env
# JSON values must stay single-quoted: systemd's EnvironmentFile
# parser strips bare double quotes, which would break the JSON.
grep -qE "^CORS_ORIGINS='\\[" backend/.env
grep -qE "^SCANNER_RANGES='\\[" backend/.env
grep -q "^SQLITE_PATH=$GITHUB_WORKSPACE/data/homelab.db$" backend/.env
test "$(stat -c '%a' backend/.env)" = "600"
- name: The build artefacts and service user exist
run: |
set -euo pipefail
test -x backend/.venv/bin/uvicorn
test -f frontend/dist/index.html
id homelable
- name: The systemd unit was written
run: |
set -euo pipefail
test -f /etc/systemd/system/homelable.service
grep -q 'ExecStart=.*/uvicorn app.main:app --host 127.0.0.1 --port 8000' \
/etc/systemd/system/homelable.service
grep -q "EnvironmentFile=$GITHUB_WORKSPACE/backend/.env" \
/etc/systemd/system/homelable.service
- name: The backend boots on the generated config
# The unit's own ExecStart, run by hand. Proves the .env pydantic reads
# is valid — the assertion that catches config drift.
run: |
set -euo pipefail
cd backend
.venv/bin/uvicorn app.main:app --host 127.0.0.1 --port 8000 &
for _ in $(seq 1 30); do
if curl -fsS http://127.0.0.1:8000/api/v1/health >/dev/null 2>&1; then
echo "backend healthy"
exit 0
fi
sleep 1
done
echo "backend did not answer /api/v1/health within 30s" >&2
exit 1
- name: Re-running is idempotent and keeps .env
run: |
set -euo pipefail
before="$(sha256sum backend/.env)"
INSTALL_DIR="$GITHUB_WORKSPACE" SKIP_NGINX=1 \
bash scripts/install-baremetal.sh | tee /tmp/run2.log
after="$(sha256sum backend/.env)"
test "$before" = "$after" || { echo "second run rewrote backend/.env" >&2; exit 1; }
grep -q 'keeping existing values' /tmp/run2.log