name: CI # A change that only touches these files skips the whole run: none of them # affects the build, the tests or the generated code. on: push: branches: [main] paths-ignore: &docs-only - '**.md' - '**.png' - 'docs/**' - 'LICENSE' pull_request: paths-ignore: *docs-only env: # Test the module alone, with the versions in go.mod (never a go.work). GOWORK: "off" jobs: lint: name: Lint runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 - uses: actions/setup-go@v7 with: go-version-file: go.mod cache: true - uses: golangci/golangci-lint-action@v9 with: version: v2.14.0 test: name: Test # ermite needs git >= 2.45; the ubuntu-latest image (Debian bookworm) has 2.39. # The job runs in archlinux:latest as root, with a current git. runs-on: ubuntu-latest container: archlinux:latest steps: # openssh provides ssh-keygen, which the tests use to check that the squash # commits verify (git verify-commit, ADR 0022). The tests fail without it when CI is set. # nodejs runs the tests of the file finder (internal/adapters/web/jstest). - name: System packages run: pacman -Syu --noconfirm --needed git gcc openssh nodejs - uses: actions/checkout@v6 # The workspace belongs to the runner user, not root: without this, git does not # treat it as a repository and `git diff a b` compares the two files instead. - name: Trust the workspace run: git config --global --add safe.directory "$GITHUB_WORKSPACE" - uses: actions/setup-go@v7 with: go-version-file: go.mod cache: true - name: Git is at least 2.45 run: | git --version v=$(git --version | sed -E 's/^git version ([0-9]+)\.([0-9]+).*/\1 \2/') set -- $v if [ "$1" -lt 2 ] || { [ "$1" -eq 2 ] && [ "$2" -lt 45 ]; }; then echo "git 2.45 or newer is required" >&2 exit 1 fi - name: Verify tidy run: | go mod tidy git diff --exit-code -- go.mod go.sum - name: Generated templ code is up to date run: scripts/templ-drift.sh - run: go vet ./... # The end-to-end tests (internal/app, a real server per test) run before a # PR is opened (README, Build) and here on main only: they are most of the # time of a PR run. - name: Tests run: go test -race $(go list ./... | grep -vx 'bnema.dev/ermite/internal/app') - name: End-to-end tests if: github.event_name == 'push' run: go test -race ./internal/app/ - name: Architecture run: go tool hexcheck -fail-on warn ./... # The core of the file finder (static/js/files.js) runs in Node. - name: JavaScript tests run: node --test 'internal/adapters/web/jstest/*.test.mjs' proto: name: Protobuf and generated code runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 with: # buf breaking compares with the base branch. fetch-depth: 0 - uses: actions/setup-go@v7 with: go-version-file: go.mod cache: true - run: go tool buf lint - name: Breaking changes if: github.event_name == 'pull_request' # Skipped by the script while the base branch has no proto/ directory. run: scripts/buf-breaking.sh "origin/${{ github.base_ref }}" - name: Generated protobuf code is up to date run: | go tool buf generate git diff --exit-code -- gen/ test -z "$(git status --porcelain -- gen/)" - name: Generated mocks are up to date run: | go generate ./internal/ports git diff --exit-code -- internal/ports/mocks/ test -z "$(git status --porcelain -- internal/ports/mocks/)" image: name: Image # Build only, nothing is pushed. A plain ubuntu-latest job (not a container): # the runner image provides the docker CLI and daemon. runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 with: # git describe needs the tags for the image version. fetch-depth: 0 - name: Build the image run: | docker build \ --build-arg VERSION="$(git describe --tags --always --dirty)" \ -t ermite:ci . - name: Smoke test run: | docker run --rm ermite:ci version | tee /dev/stderr | grep -qF "ermite $(git describe --tags --always --dirty)" docker run --rm --entrypoint git ermite:ci --version