The image job failed on PR #252 and passed on main with no code difference between them that touches CI. The runner is the difference: run 434 built on mac2 and passed, run 441 built on mac1 and failed at "staging postgres never became ready", on the same two commits. mac1 has the smaller OrbStack allocation, and the gate starts three sibling containers on the host daemon. The job now names mac2 rather than taking whichever runner is free. The gate also prints the container's state and log when Postgres does not come up. "Never became ready" covered both a Postgres still starting and a container that never ran, which need different fixes, and mac1 said nothing further, so 90 seconds of waiting read as a relay bug. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
162 lines
7.4 KiB
YAML
162 lines
7.4 KiB
YAML
# flamenet-relay CI/CD on Gitea Actions (mac1/mac2, amd64).
|
|
#
|
|
# Replaces .gitlab-ci.yml (runner ovh-flamenetmessenger on the production
|
|
# box) and the build half of Scripts/staging-gate.sh, which compiled the relay
|
|
# on the box from a copied src/ tree. Now:
|
|
#
|
|
# test secret sweep, swift build + test (swift:6.1-noble, matching the
|
|
# Dockerfile), section 8 conformance at the pinned e2e commit,
|
|
# generated-page hashes, configuration doc, package completeness.
|
|
# conformance-head and doc-pages-head stay advisory.
|
|
# image docker build with FN_RELEASE=<sha>, then the staging gate
|
|
# (Scripts/ci-staging-gate.sh) against that exact image, then push
|
|
# :<short sha> and :latest to git.extant2000.com/extant2000/flamenet-relay.
|
|
# deploy main only: deploy/gitea-deploy.sh on the box pulls the gated
|
|
# image, tags it flamenet-relay:prod, checks the edge, rolls back
|
|
# to :previous on failure, then recreates the test fleet.
|
|
#
|
|
# Secrets never reach the image or CI: /srv/prod/messenger/.env on the box is
|
|
# the source of truth and nothing here writes it.
|
|
name: ci
|
|
|
|
on:
|
|
push:
|
|
branches: [main]
|
|
pull_request:
|
|
|
|
env:
|
|
REGISTRY: git.extant2000.com
|
|
IMAGE: git.extant2000.com/extant2000/flamenet-relay
|
|
|
|
jobs:
|
|
checks:
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
with: { fetch-depth: 20 }
|
|
- name: secret sweep
|
|
run: bash Scripts/secret-sweep.sh
|
|
- name: package complete
|
|
run: |
|
|
for f in Package.swift Dockerfile LICENSE .env.example; do test -f "$f" || { echo "missing $f"; exit 1; }; done
|
|
test -d Sources && test -d Tests
|
|
- name: configuration doc
|
|
run: python3 Scripts/check-configuration-doc.py
|
|
- name: e2e at the pin
|
|
run: |
|
|
bash Scripts/ci-fetch-e2e.sh /tmp/e2e
|
|
- name: doc pages
|
|
run: python3 Scripts/check-doc-pages.py --engine /tmp/e2e
|
|
- name: doc pages at e2e HEAD (advisory)
|
|
continue-on-error: true
|
|
run: |
|
|
bash Scripts/ci-fetch-e2e.sh /tmp/e2e-head HEAD
|
|
echo "pinned $(cat .e2e-ref)"
|
|
python3 Scripts/check-doc-pages.py --engine /tmp/e2e-head
|
|
|
|
swift:
|
|
runs-on: ubuntu-latest
|
|
container: swift:6.1-noble
|
|
steps:
|
|
- name: checkout
|
|
run: |
|
|
git clone -q "${{ gitea.server_url }}/${{ gitea.repository }}.git" src
|
|
cd src && git fetch -q origin "${{ gitea.sha }}" && git checkout -q "${{ gitea.sha }}"
|
|
- name: build
|
|
run: cd src && swift build
|
|
# swift-testing results print separately from XCTest's "Executed 0
|
|
# tests"; an empty run must not pass.
|
|
- name: test
|
|
run: |
|
|
cd src
|
|
swift test 2>&1 | tee /tmp/test.log
|
|
grep -qE 'Test run with [0-9]+ tests? .*passed' /tmp/test.log || { echo "no passing swift-testing run found"; exit 1; }
|
|
# Node 22 from nodejs.org (Noble ships 18, which lacks globalThis.crypto
|
|
# for ML-DSA). The swift image has no curl or xz, so those come from apt.
|
|
- name: node
|
|
run: |
|
|
apt-get update -qq && apt-get install -y -qq --no-install-recommends curl ca-certificates xz-utils >/dev/null
|
|
curl -fsSL https://nodejs.org/dist/v22.20.0/node-v22.20.0-linux-x64.tar.xz | tar xJ -C /opt
|
|
ln -sf /opt/node-v22.20.0-linux-x64/bin/node /usr/local/bin/node
|
|
node --version
|
|
- name: conformance (pinned e2e)
|
|
run: |
|
|
R="$PWD/src"
|
|
bash "$R/Scripts/ci-fetch-e2e.sh" /tmp/e2e
|
|
cd /tmp/e2e && FN_RELAY_DIR="$R" node test/relay.mjs
|
|
- name: conformance at e2e HEAD (advisory)
|
|
continue-on-error: true
|
|
run: |
|
|
R="$PWD/src"
|
|
bash "$R/Scripts/ci-fetch-e2e.sh" /tmp/e2e-head HEAD
|
|
cd /tmp/e2e-head && FN_RELAY_DIR="$R" node test/relay.mjs
|
|
|
|
image:
|
|
needs: [checks, swift]
|
|
# mac2, by name. The staging gate starts a throwaway Postgres, a Redis and
|
|
# the candidate relay as sibling containers on the host daemon. On mac1 the
|
|
# Postgres never reached `pg_isready` inside the gate's 90 s window (runs
|
|
# 441 and 434 differ in nothing but the runner: mac2 passed, mac1 failed on
|
|
# the same two commits), and mac1 has the smaller OrbStack allocation.
|
|
# Named here rather than left to whichever runner is free, so the result
|
|
# does not depend on which machine picks the job up.
|
|
runs-on: [ubuntu-latest, mac2]
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
# The Dockerfile uses RUN --mount (BuildKit). The static docker tarball
|
|
# has no buildx plugin, so `docker build` fell back to the legacy
|
|
# builder and refused --mount. Same apt install as the suite workflows.
|
|
- name: docker cli
|
|
run: |
|
|
install -m 0755 -d /etc/apt/keyrings
|
|
curl -fsSL https://download.docker.com/linux/debian/gpg -o /etc/apt/keyrings/docker.asc
|
|
echo "deb [arch=amd64 signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/debian bookworm stable" > /etc/apt/sources.list.d/docker.list
|
|
apt-get update -qq
|
|
apt-get install -y -qq --no-install-recommends docker-ce-cli docker-buildx-plugin >/dev/null
|
|
docker version --format 'client {{.Client.Version}} server {{.Server.Version}}'
|
|
docker buildx version
|
|
- name: build (amd64, FN_RELEASE=${{ gitea.sha }})
|
|
run: docker build --build-arg FN_RELEASE="$GITHUB_SHA" -t "$IMAGE:${GITHUB_SHA::8}" .
|
|
- name: staging gate
|
|
run: bash Scripts/ci-staging-gate.sh "$IMAGE:${GITHUB_SHA::8}"
|
|
- name: push
|
|
if: gitea.event_name == 'push' && gitea.ref == 'refs/heads/main'
|
|
run: |
|
|
printf '%s' "${{ secrets.REGISTRY_TOKEN }}" | docker login "$REGISTRY" -u extant2000git --password-stdin
|
|
docker tag "$IMAGE:${GITHUB_SHA::8}" "$IMAGE:latest"
|
|
for i in 1 2 3; do
|
|
docker push "$IMAGE:${GITHUB_SHA::8}" && docker push "$IMAGE:latest" && break
|
|
[ $i = 3 ] && exit 1; sleep 10
|
|
done
|
|
docker logout "$REGISTRY"
|
|
|
|
deploy:
|
|
if: gitea.event_name == 'push' && gitea.ref == 'refs/heads/main'
|
|
needs: [image]
|
|
runs-on: ubuntu-latest
|
|
concurrency: deploy-relay
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
- name: ssh
|
|
run: |
|
|
mkdir -p ~/.ssh && chmod 700 ~/.ssh
|
|
printf '%s' "${{ secrets.OVH_DEPLOY_KEY_B64 }}" | base64 -d > ~/.ssh/id_ed25519 && chmod 600 ~/.ssh/id_ed25519
|
|
printf '%s\n' "${{ secrets.OVH_KNOWN_HOSTS }}" > ~/.ssh/known_hosts
|
|
ssh -o StrictHostKeyChecking=yes -o BatchMode=yes "${{ secrets.OVH_USER }}@${{ secrets.OVH_HOST }}" true
|
|
- name: deploy
|
|
run: |
|
|
H="${{ secrets.OVH_USER }}@${{ secrets.OVH_HOST }}"
|
|
STAGE="/tmp/relay-deploy-$GITHUB_SHA"
|
|
ssh -o BatchMode=yes "$H" "mkdir -p $STAGE"
|
|
scp -o BatchMode=yes deploy/gitea-deploy.sh "$H:$STAGE/"
|
|
printf '%s\n%s\n' extant2000git "${{ secrets.REGISTRY_TOKEN }}" | ssh -o BatchMode=yes "$H" "bash $STAGE/gitea-deploy.sh $GITHUB_SHA $STAGE"
|
|
- name: smoke test (public edge)
|
|
run: |
|
|
set -eu
|
|
for u in https://flamenetmessenger.com/health https://messenger.flamenet.io/health; do
|
|
b=$(curl -fsS -m 10 "$u"); echo "$u -> $b"; [ "$b" = ok ] || exit 1
|
|
done
|
|
for p in / /about /privacy /metadata /releases /es/; do
|
|
c=$(curl -s -o /dev/null -m 10 -w '%{http_code}' "https://flamenetmessenger.com$p"); echo "$p $c"; [ "$c" = 200 ] || exit 1
|
|
done
|