# The gates. # # This repo makes two claims about itself — that it is Apache-2.0 clean, and that # a stranger can self-host it with no account, no key and no network — and until # this file existed nothing checked either one. Both had been verified per-lens, # in a unit test or by hand on a box that already had everything, and never once # end to end on an empty environment. A design critic put it exactly right: # until one of these jobs exists, self-hostable is a design intention, not a # property. # # Three jobs, one for each claim that can actually be measured: # # clean-clone a fresh checkout installs, builds and passes its tests # zero-config-boot the API answers health when handed nothing at all # no-binary-art src/** carries no committed binary assets # # They are deliberately independent and run in parallel: a broken build should # not hide a licensing regression. # # This runs on Gitea Actions, not GitHub. The syntax is GitHub-compatible and the # `actions/*` steps resolve through the instance's configured action registry. name: gates on: push: branches: [master] pull_request: workflow_dispatch: # A superseded push has nothing to tell us, and these jobs build a Docker image # and bind a port between them. concurrency: group: gates-${{ github.ref }} cancel-in-progress: true jobs: # ---- Job 1: a stranger clones this and it works ---- # # The whole job is the dev-kit promise from CONTRACT.md §0. Note what is absent # and is meant to stay absent: no `env:` block, no `secrets.*` anywhere, and no # dependency cache. The cache is the interesting omission — a warm cache would # make this job pass on a lockfile that no longer resolves from a clean state, # which is precisely the failure a stranger would hit first. clean-clone: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: # The server runs TypeScript directly by stripping types at load, so # the floor is real: server/package.json says >=22.18. 24 is what the # image in server/Dockerfile uses. node-version: "24" - name: npm ci run: npm ci - name: npm run build # tsc --noEmit, then vite build. A dynamic import of an uninstalled # package fails here, which is the reason the dependency policy is an # allowlist rather than a count — see CONTRACT.md §6. run: npm run build - name: server tests run: npm test --workspace @lumbridge/tera-api # ---- Job 2: the box boots on nothing ---- # # Two independent server designs defaulted the weather source to a provider # that needs a contact string and then failed hard without one, which breaks # the only acceptance test this repo has. This job exists specifically to catch # that class of bug, and it passes today. # # The gate starts the real entry point under a genuinely empty environment # rather than `docker compose up`, which is the wording in CONTRACT.md §0. The # property asserted is identical — the compose file adds only TERA_HOST and # container hardening, and every other variable in it carries a `:-` default so # an empty environment resolves it to the empty string — while a job that needs # docker-in-docker present on the runner would be measuring the runner instead # of the repo. `node scripts/check-zero-config-boot.mjs --compose` runs the # literal compose form for anyone who has a Docker. zero-config-boot: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: "24" - name: npm ci run: npm ci - name: health on an empty environment run: node scripts/check-zero-config-boot.mjs # ---- Job 3: no committed binary art ---- # # Scoped strictly to tracked files under src/**, because a self-hoster is told # to put their own legally-clean art in public/props/ and friends and an # earlier design would have failed their build for doing exactly that. The # script enumerates with `git ls-files` and has no filesystem-walk fallback. # See CONTRACT.md §7. # # No install step: the check is dependency-free on purpose, so it stays # runnable by hand and cannot be broken by a bad lockfile. no-binary-art: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: "24" - name: no binary art under src/ run: node scripts/check-no-binaries.mjs