name: CI # The release workflow gates a tag, which is late: a broken commit sits green until # somebody decides to publish. This runs the same checks on the way in. # # push is scoped to main rather than all branches so that a branch pushed as part of a # pull request is not checked twice. # # No actions/checkout, deliberately -- same as the letsvisit and charts workflows. The # runner image is ubuntu:22.04 whose `nodejs` package is Node 12, and actions/checkout@v4 # is built with ES2022 static initialiser blocks, so it dies with # `SyntaxError: Unexpected token '{'` before running. Cloning with git directly avoids JS # actions entirely. This repo is public, so the clone needs no credential at all. # # `${{ }}` values are passed through `env:` and referenced as quoted shell variables: a # ref name is attacker-influenced by anyone who can push a branch or open a PR, and # expanding one straight into `run:` is a shell-injection vector. on: push: branches: [main] pull_request: # A rapid series of pushes only needs the last one checked. concurrency: group: ci-${{ github.ref }} cancel-in-progress: true env: REPO_URL: https://git.ryuvia.com/niklas/terdut-server.git jobs: test: runs-on: ubuntu-latest container: # Runs inside the toolchain image rather than installing Go per job. Note this puts # the job on the dind bridge, which cannot reach github.com or get.helm.sh -- # proxy.golang.org and git.ryuvia.com are reachable, which is all this job needs. image: golang:1.26.6-bookworm # act_runner destroys a job's own volumes when it finishes, so without these every # run re-downloads the whole module graph. The names must appear in the runner's # container.valid_volumes allowlist (charts/act-runner in the k8s repo); unlisted # volumes are dropped silently, so a workflow that looks correct can still be # running uncached. volumes: - go-mod-cache:/go/pkg/mod - go-build-cache:/root/.cache/go-build - gobin-cache:/go/bin steps: - name: Checkout env: REF_NAME: ${{ github.ref_name }} HEAD_SHA: ${{ github.event.pull_request.head.sha }} run: | if [ -n "$HEAD_SHA" ]; then # A pull_request ref_name is "/merge", which is not a fetchable branch. git clone "$REPO_URL" . git checkout -q "$HEAD_SHA" else git clone --depth=1 --branch "$REF_NAME" "$REPO_URL" . fi # This exists because `go vet` does not look at import order: the move to # git.ryuvia.com rewrote every import path without re-sorting, the new path sorts # before github.com/..., and both repos sat unformatted through a green CI run and # a release before anyone noticed. # # Both of gofmt's failure modes need handling, and they are not alike. A file that # is merely misformatted is listed on stdout with exit 0 -- so the failure has to # be raised by hand. A file that does not parse is the opposite: nothing on stdout # and exit 2, which a naive `[ -n "$unformatted" ]` reads as success. The first # draft of this step had exactly that hole. - name: Format run: | if ! unformatted=$(gofmt -l .); then echo "::error::gofmt could not parse the tree" gofmt -l . # re-run unredirected so the parse errors reach the log exit 1 fi if [ -n "$unformatted" ]; then echo "::error::not gofmt'd:" echo "$unformatted" gofmt -d . exit 1 fi - name: Vet run: go vet ./... - name: Test run: go test ./...