# Test, Go. Push and pull request to development. Never on main. # # The gates are the ones the justfile's `gates` recipe runs, minus race: the shared # runner box cannot afford the race detector on every push. Race has its own # pipeline, dispatched by hand, and the local `just gates` runs it once per # task. The box is one core and 2 GB beside Gitea, so # parallelism is bounded on purpose and everything runs in one job. Extra jobs would # duplicate the checkout, the Go setup and the dependency download three times without # buying any parallelism. # # Every step is one command, so the step that fails is the gate that failed, and no shell # option has to be trusted for the run to stop. The scripted steps are Perl, not shell and # not Python: Perl behaves the same on both runner images, there is no bashism to trip over # on ash, and it is one language instead of two. The Perl uses builtins only, because # Fedora packages the Perl modules separately and nothing beyond `perl` itself may be # assumed present. # # After the gates runs the toml-test compliance suite, which is this library's conformance # record rather than a gate. It is fixed cases, not exploration, and it fits the box, so it # is none of the three burdens the push pipeline refuses: no fuzz, no race, no heavy sweep. name: Test on: push: branches: [development] pull_request: branches: [development] env: # One core: parallelism buys no speed here and costs memory the box does not have. GOFLAGS: -p=1 GOMAXPROCS: "2" # A superseded run of the same ref is cancelled instead of queueing behind one # that no longer matters. Verified on this Gitea on 2026-09-17: a queued run # whose ref moved on is cancelled before it ever reaches the runner, while a # run already dispatched there runs to completion. concurrency: group: ${{ gitea.workflow }}-${{ gitea.ref }} cancel-in-progress: true jobs: test: runs-on: fedora timeout-minutes: 10 steps: - uses: actions/checkout@v7 - uses: actions/setup-go@v6 with: # The module is the source of truth for the version, so it cannot drift. go-version-file: go.mod cache: true - name: Install Perl # The runner images are minimal and Perl is not guaranteed. The install is a # no-op where it is already present; drop this step once verified on the box. run: dnf install -y perl - name: Format run: | perl -e ' open(my $g, q{-|}, q{gofmt}, q{-l}, q{.}) or die qq{gofmt: $!}; my @bad = <$g>; close($g); print @bad; exit(@bad ? 1 : 0); ' - name: Vet run: go vet ./... - name: Modernise # Exits non-zero when it has something to rewrite, so it needs no output capture. run: go fix -diff ./... - name: Build run: go build ./... - name: Tests # Scope the pattern to the packages that hold the logic when a thin cmd/ drags the # total under the floor, and keep it equal to `packages` in the project's justfile. run: go test -count=1 -timeout 10m -coverprofile=coverage.out ./... - name: Coverage floor run: | perl -e ' open(my $c, q{-|}, q{go}, q{tool}, q{cover}, q{-func=coverage.out}) or die qq{cover: $!}; my $total; while (my $l = <$c>) { $total = $1 if $l =~ m{^total:\s+\S+\s+([0-9.]+)%} } close($c); die qq{no total line in coverage.out\n} unless defined $total; printf qq{Total coverage: %s%%\n}, $total; exit($total < 80 ? 1 : 0); ' - name: Install toml-test # A dev and CI tool only; it is not a module dependency of interpres. GOBIN pins # the destination, so the later step invokes the tool by path and no command # output has to be captured into a variable. env: GOBIN: ${{ gitea.workspace }}/bin run: go install github.com/toml-lang/toml-test/v2/cmd/toml-test@v2.2.0 - name: Build the decoder run: go build -o bin/interpres-decode ./cmd/interpres-decode - name: Compliance suite # interpres implements TOML 1.1, and the suite runs both directions: the decoder # on the valid and invalid corpora, the encoder on the tagged JSON of the valid # one. The mode is pinned so an upstream default change cannot silently move the # corpus. run: bin/toml-test test -decoder=bin/interpres-decode -encoder='bin/interpres-decode -encode' -toml=1.1