Artifact 5e58ee08e8e70a1b69969dda46fc6dfa67bf3a4773c04c70aff7f548dcf33bf1:
- File
.github/workflows/test.yml
— part of check-in
[0650171d55]
at
2026-10-23 21:25:20
on branch main
— feat(wasm): build the whole public API with TinyGo
go-libfossil compiled for js/wasm only with the standard toolchain, whose output is about 16 MB, too large to ship with a page or an edge function. Three standard-library features TinyGo lacks kept it from building there: http.ErrUseLastResponse and net.DNSError in the sync client, and os.SameFile in checkout-marker recovery.
Each now sits behind a tinygo build tag. Under TinyGo a sync redirect ends the request with an error instead of being followed, DNS failures are not classified as transient, and two names of one checkout marker are reported as ambiguous rather than paired; the standard build is unchanged.
examples/tinygo exercises the whole public API, sync and signing included. Built with TinyGo 0.43 and wasm-opt -Oz it is 3.83 MB raw and 1.40 MB gzipped, against 16.1 MB and 4.06 MB from the standard toolchain. A new CI job builds it with TinyGo on every change. (user: [email protected], size: 12643) [annotate] [blame] [check-ins using] [more...]
name: Tests on: push: branches: [main] pull_request: env: # The canonical fossil the real-fossil-gated tests compare against. Local # development validates against this exact version, so CI must too -- # otherwise the two are checking our output against different reference # implementations. Bump deliberately, here, in one place. # # The distro package is unusable for this: it is unpinned, currently lags # several releases behind, and tracks different upstream check-ins over time # under the same version string, so two runs at nominally the same version # can behave differently. The official upstream release tarball is a fixed # build, and FOSSIL_SHA256 pins it to that exact artifact -- a re-upload # upstream then fails the checksum loudly instead of silently changing what # we test against. FOSSIL_VERSION: "2.28" FOSSIL_SHA256: "c9d2bf9d150dff1076eae1b0b822ed3cde892e1273f78b040c4f8ace1b59dd40" jobs: test: name: Test (modernc) runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-go@v5 with: go-version-file: go.mod - name: Install pinned Fossil run: | curl -fsSL --retry 3 --retry-delay 2 --retry-all-errors \ -o /tmp/fossil.tar.gz \ "https://fossil-scm.org/home/uv/fossil-linux-x64-${FOSSIL_VERSION}.tar.gz" echo "${FOSSIL_SHA256} /tmp/fossil.tar.gz" | sha256sum -c - sudo tar -xzf /tmp/fossil.tar.gz -C /usr/local/bin fossil rm -f /tmp/fossil.tar.gz - name: Verify Fossil version # A silent upstream change has to surface as an explicit failure here, # not as mysterious behavioural drift in the real-fossil-gated tests. run: | reported=$(fossil version | sed -n 's/.*fossil version \([0-9][0-9.]*\).*/\1/p') if [ "$reported" != "${FOSSIL_VERSION}" ]; then echo "expected fossil ${FOSSIL_VERSION}, got: $(fossil version)" >&2 exit 1 fi fossil version - name: Warm Fossil global config # Fossil keeps a per-user global config database outside any repository # and creates it lazily on first use. `go test` runs packages # concurrently, several of them shell out to fossil, and nothing sets # HOME, so on a fresh runner two test processes can race to create it: # one wins and the others die with "table global_config already exists". # Creating it once here, single-threaded, means no test process is ever # the one to create it. `fossil version` does not open that database, so # the step above does not incidentally cover this; `fossil all list` # does, and prints nothing. # # The first call's exit status is ignored deliberately -- it exists to # create the database, not to assert anything, so an unrelated non-zero # must not red the job. The second call is what carries the weight: it # must succeed on its own, which proves the database is both present and # usable. Checking that a file exists would not -- a truncated or # partially-written database passes an existence check while concurrent # invocations still die on the missing schema, which is exactly the # "looks like protection while the race stays live" failure this guards # against. Re-running the command also keeps the check independent of # where fossil resolved the database to (~/.fossil, ~/.config/fossil.db, # $XDG_CONFIG_HOME, or $FOSSIL_HOME). run: | fossil all list >/dev/null 2>&1 || true fossil all list >/dev/null - name: Build all packages # Build every package, not just the cmd/libfossil dependency closure. # The `go test $(go list ./...)` step below silently drops any package # that fails to compile (go list omits it from stdout instead of # failing), so a broken import can vanish from the test run undetected. # Building `./...` first makes that a loud, fail-fast build error. run: GOWORK=off go build ./... - name: Unit tests # REQUIRE_FOSSIL_BIN turns "fossil is missing" into a failure rather # than an invisible skip. Verifying a real fossil reads what we write # is the one thing our own tests cannot substitute for. env: REQUIRE_FOSSIL_BIN: "1" run: GOWORK=off go test $(GOWORK=off go list ./... | grep -v '/dst') -count=1 -timeout=300s - name: DST run: GOWORK=off go test ./dst/... -count=1 -timeout=300s - name: Driver tests (modernc) run: cd db/driver/modernc && GOWORK=off go test ./... -count=1 - name: Vet run: GOWORK=off go vet ./... test-ncruces: name: Test (ncruces) runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-go@v5 with: go-version-file: go.mod - name: Install pinned Fossil run: | curl -fsSL --retry 3 --retry-delay 2 --retry-all-errors \ -o /tmp/fossil.tar.gz \ "https://fossil-scm.org/home/uv/fossil-linux-x64-${FOSSIL_VERSION}.tar.gz" echo "${FOSSIL_SHA256} /tmp/fossil.tar.gz" | sha256sum -c - sudo tar -xzf /tmp/fossil.tar.gz -C /usr/local/bin fossil rm -f /tmp/fossil.tar.gz - name: Verify Fossil version # A silent upstream change has to surface as an explicit failure here, # not as mysterious behavioural drift in the real-fossil-gated tests. run: | reported=$(fossil version | sed -n 's/.*fossil version \([0-9][0-9.]*\).*/\1/p') if [ "$reported" != "${FOSSIL_VERSION}" ]; then echo "expected fossil ${FOSSIL_VERSION}, got: $(fossil version)" >&2 exit 1 fi fossil version - name: Warm Fossil global config # Same single-threaded warm-up as the modernc leg -- see the comment # there for why `fossil version` does not cover this, and why the first # call's exit status is ignored but the second call's is not. run: | fossil all list >/dev/null 2>&1 || true fossil all list >/dev/null - name: Build all packages (ncruces) # Same fail-fast guard as the modernc leg, under the test_ncruces tag so # the ncruces driver's compile surface is covered too. run: GOWORK=off go build -tags test_ncruces ./... - name: Unit tests (ncruces) env: REQUIRE_FOSSIL_BIN: "1" run: GOWORK=off go test -tags test_ncruces $(GOWORK=off go list ./... | grep -v '/dst') -count=1 -timeout=300s - name: DST (ncruces) run: GOWORK=off go test -tags test_ncruces ./dst/... -count=1 -timeout=300s - name: Driver tests (ncruces) run: cd db/driver/ncruces && GOWORK=off go test ./... -count=1 build-wasm: name: Build (js/wasm) runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-go@v5 with: go-version-file: go.mod - name: Build main module for js/wasm # Browser support is the reason the ncruces driver is in this repo at # all, but until this job nothing was ever compiled for js/wasm. # # What a build gate catches is a dependency with no js/wasm build at # all -- most realistically the wrong SQLite driver reaching a shared # package, since modernc.org/libc is linux/darwin-only and would fail # here while both linux legs above stay green. It does not catch every # WASM problem: much of the stdlib (os/exec included) compiles for js # behind stubs and only fails when called, so runtime-behaviour gaps # need tests under a browser harness, which is a separate question. # # test_ncruces is the same tag the ncruces leg builds under. It switches # both internal/testdriver and cmd/libfossil from modernc to ncruces, # which is the driver that can actually target WASM. Without the tag, # both packages would pull modernc into this build. # # The package set comes from `go list`, not a hand-written list, so a # newly added package is inside the gate by default rather than # silently outside it. `-e` stops go list from aborting on the package # excluded below -- without it that unresolvable import would # discard the entire list. Packages that already declare `//go:build # !js` in source (internal/stash, internal/undo) are dropped by go list # itself under GOOS=js; that exclusion lives in the code, not here. # # The only exclusion is examples/embed-repo-api: its main package # imports the modernc driver directly, and modernc.org/libc ships no # js/wasm build. cmd/libfossil is deliberately built here with the # test_ncruces binding, so this gate compiles the tagged CLI for WASM. # examples/embed-repo-api stays fully covered by the linux legs above. env: GOWORK: "off" GOOS: js GOARCH: wasm run: | set -o pipefail pkgs=$(go list -e -tags test_ncruces ./... \ | grep -v -e '/examples/embed-repo-api$') echo "building $(echo "$pkgs" | wc -l) packages for js/wasm:" echo "$pkgs" echo "$pkgs" | xargs go build -tags test_ncruces - name: Build ncruces driver for js/wasm # A separate module, so the step above never reaches it -- and it is # the driver the browser build actually runs on. Its js-only source # (ncruces_js.go) is compiled by no other job in this workflow. env: GOWORK: "off" GOOS: js GOARCH: wasm run: cd db/driver/ncruces && go build ./... build-tinygo: name: Build (tinygo) runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-go@v5 with: go-version-file: go.mod - name: Install TinyGo # The release's own .deb, pinned: TinyGo releases change what compiles # and how large the output is, so the gate moves only on purpose. run: | curl -sSfL -o tinygo.deb https://github.com/tinygo-org/tinygo/releases/download/v0.43.0/tinygo_0.43.0_amd64.deb sudo dpkg -i tinygo.deb tinygo version - name: Build examples/tinygo for wasm # The whole public API, sync and signing included, must keep compiling # under TinyGo: applications ship it to browsers and edge runtimes that # way. A standard-library feature TinyGo lacks fails here, not in an # embedder's build. env: GOWORK: "off" run: | tinygo build -target=wasm -no-debug -opt=z -stack-size=1MB -o libfossil.wasm ./examples/tinygo/ echo "raw $(wc -c < libfossil.wasm) bytes, gzip -9 $(gzip -9c libfossil.wasm | wc -c) bytes" test-otel: name: Test (observer/otel) runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-go@v5 with: go-version-file: go.mod - name: OTel sub-module tests run: cd observer/otel && GOWORK=off go test ./... -count=1 test-docs: name: Test (docs/site) runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-go@v5 with: go-version-file: go.mod - name: Install Hugo (extended) # docs/site pulls the hextra theme via Hugo Modules, which need both the # Go toolchain (setup-go above) and the Hugo binary. Pin the version so a # future Hugo release can't silently change build behaviour under us. env: HUGO_VERSION: "0.162.1" run: | curl -fsSL "https://github.com/gohugoio/hugo/releases/download/v${HUGO_VERSION}/hugo_extended_${HUGO_VERSION}_linux-amd64.tar.gz" \ | sudo tar -xz -C /usr/local/bin hugo - name: Build docs site # docs/site is the one module with no Go source of its own: its go.mod # exists only to import the hextra theme as a Hugo Module, so `go build # ./...` there matches no packages and gates nothing. A real Hugo build # is the equivalent compile gate — it resolves the module import, # compiles templates, and renders content, failing loud on a broken # import or template the way the Go build steps do for the other modules. run: cd docs/site && GOWORK=off hugo --gc --minify