Files
randy 4c95927231
CI / check (push) Successful in 38s
CI / deploy (push) Successful in 12s
Pin check/renovate back to brick-build now that its image has Node
The runner's label-to-image mapping got repointed at Gitea's own
official runner image (docker.gitea.com/runner-images:ubuntu-latest,
has Node out of the box) — the earlier opensuse/leap:16.0 mapping for
brick-build specifically was what broke check/renovate on 2026-09-12,
not brick-build the label itself. Back to pinning both to this
machine specifically instead of whichever runner answers to the
shared ubuntu-latest label.
2026-09-13 12:32:14 +02:00

150 lines
7.0 KiB
YAML

name: CI
on:
push:
pull_request:
jobs:
check:
# brick-build's job container now points at Gitea's own official
# runner image (docker.gitea.com/runner-images:ubuntu-latest, has
# Node out of the box) — the earlier opensuse/leap:16.0 image that
# failed here on 2026-09-12 (no Node, every step below is a JS-based
# Action) got repointed. Pinned to brick specifically again, rather
# than the shared ubuntu-latest label "Gitea Runner on Cube" also
# answers to.
runs-on: brick-build
steps:
- uses: actions/checkout@v4
# No ruby-version input: the action auto-detects it from
# .ruby-version when the input is omitted (not "file", which isn't
# a recognized value and fails with "Unknown engine file").
- uses: ruby/setup-ruby@v1
with:
bundler-cache: true
- name: Ruby syntax check
run: find app.rb config lib bin -name "*.rb" -print0 | xargs -0 -n1 ruby -c
- name: App loads without errors
run: bundle exec ruby -e 'require_relative "app"'
- name: Ruby tests
run: bundle exec rake test
- uses: actions/setup-node@v4
with:
node-version: "22"
- name: JS syntax check
run: find public/js -name "*.js" -print0 | xargs -0 -n1 node --check
# Handled by a separate, native (non-Docker) runner registered with the
# single "brick-host" label (see docs/deployment.adoc) — it runs directly
# on the server, so it has podman/git/etc. from the host, no socket/bind
# mount tricks needed. The Docker-based runner keeps handling `check`.
deploy:
needs: check
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
runs-on: brick-host
steps:
# Not actions/checkout@v4: keeping this plain-git checkout from when
# the job ran in a Node-less container — no reason to add a Node
# dependency back in for something this already does.
- name: Checkout
run: |
rm -rf .git * .[!.]* 2>/dev/null
git init -q
git remote add origin "${{ gitea.server_url }}/${{ gitea.repository }}.git"
git -c http.extraHeader="Authorization: token ${{ secrets.GITHUB_TOKEN }}" \
fetch --depth 1 origin "${{ gitea.sha }}"
git checkout -q FETCH_HEAD
# Read by app.rb at boot and shown (as a hover tooltip on the topbar
# brand) so it's possible to tell which commit is actually live
# without digging through `tea actions runs list` — written here,
# before .git gets dropped below, since that's otherwise the only
# place the commit is recorded.
- name: Write REVISION
run: git rev-parse --short HEAD > REVISION
# The checkout lives in build/, wiped and fully replaced every
# deploy — data/ and .env live one level up, in /srv/podman/maptool/
# itself, so they're never inside the directory this nukes and never
# need special-casing here (an earlier version of this step had to
# skip data/ by name for exactly that reason). docker-compose.yaml
# itself is symlinked up to /srv/podman/maptool/ (relative target, so
# it survives /srv/podman itself ever being renamed) purely for
# discoverability — a human poking around expects to find it at the
# top, not a level down — and because that's what makes `./data` in
# it resolve to the sibling directory: compose resolves a relative
# volume path against the *symlink's* location, not build/ (verified
# against plain `docker-compose config`), so podman-compose@.service's
# own WorkingDirectory=/srv/podman/maptool needs no change either.
- name: Sync to /srv/podman/maptool/build
run: |
rm -rf /srv/podman/maptool/build
mkdir -p /srv/podman/maptool/build
cp -a . /srv/podman/maptool/build/
rm -rf /srv/podman/maptool/build/.git
ln -sf build/docker-compose.yaml /srv/podman/maptool/docker-compose.yaml
# Regenerated on every deploy from repo secrets (Settings -> Actions
# -> Secrets) rather than created once by hand on the server — see
# docs/deployment.adoc. Values are masked in the job log by Gitea
# automatically since they come from `secrets`. Secret names here
# can't start with "GITEA_"/"GITHUB_" — Gitea reserves those prefixes
# — hence OAUTH_CLIENT_* rather than GITEA_OAUTH_CLIENT_*; the .env
# var names on the right of `=` are unaffected, still what app.rb
# actually reads.
- name: Write .env
run: |
cat > /srv/podman/maptool/.env <<EOF
SESSION_SECRET=${{ secrets.SESSION_SECRET }}
GITEA_OAUTH_CLIENT_ID=${{ secrets.OAUTH_CLIENT_ID }}
GITEA_OAUTH_CLIENT_SECRET=${{ secrets.OAUTH_CLIENT_SECRET }}
MAPTOOL_ALLOWED_GITEA_USER=${{ secrets.MAPTOOL_ALLOWED_GITEA_USER }}
LIVETRACK_WEBHOOK_SECRET=${{ secrets.LIVETRACK_WEBHOOK_SECRET }}
P4N_USER=${{ secrets.P4N_USER }}
P4N_PASSWORD=${{ secrets.P4N_PASSWORD }}
P4N_SESSION_COOKIE=${{ secrets.P4N_SESSION_COOKIE }}
EOF
chmod 600 /srv/podman/maptool/.env
# podman-compose@maptool.service (/etc/systemd/system/podman-compose@.service)
# owns the container lifecycle — its ExecStartPre already tears down
# the previous containers/networks/volumes on every start, and its
# ExecStart runs `podman compose up --build` (mirroring
# docker-compose@.service's own `--build`), so a plain restart always
# picks up the code just synced above while still reusing unchanged
# layers (apt-get/bundle install) as cache — unlike explicitly
# removing the image first, which would delete that cache along with
# it. sudo is needed since the unit is root's, not gitea-runner's own
# (gitea-runner has passwordless sudo for this on brick-host).
- name: Deploy
run: sudo systemctl restart podman-compose@maptool.service
# systemctl restart above only confirms the process forked
# (Type=simple, no readiness protocol) — not that `podman compose up
# --build` actually finished and Puma is answering. Polls the health
# endpoint (brick-host itself, same machine the container's port is
# published on) instead of trusting that — checking for this
# deploy's own revision, not just any 200, so a stuck old container
# that never got torn down can't pass as a successful deploy of the
# new one. 5 minutes covers even a from-scratch rebuild (Gemfile
# change, ~3.5 minutes measured) with room to spare; a cached
# rebuild is normally up within a minute.
- name: Wait for it to come up
run: |
expected=$(git rev-parse --short HEAD)
for i in $(seq 1 30); do
if curl -sf http://localhost:4567/api/health | grep -q "\"revision\":\"$expected\""; then
echo "Healthy at $expected after $(( (i - 1) * 10 ))s"
exit 0
fi
sleep 10
done
echo "::error::maptool did not report revision $expected within 5 minutes"
exit 1