ci: Add go 1.26 to the supported version list
Adds Go 1.26 to go_supported so that Gitaly is built and tested against it. The 1.26 images are already published by gitlab-build-images, but our matrix only exercised 1.25.
Doing so surfaced three problems, fixed here.
Go 1.26 test failures
Two failures are caused by changes in Go 1.26 itself. Both fixes are also correct on Go 1.25, so nothing is version-gated.
-
testhelper.timeoutContext.Err()resolved the cancellation cause withcontext.Cause(c), passing the wrapper itself. Go 1.26 reorderedcontext.Cause()so that it now starts by callingc.Err(), which turned that into anErr()->Cause()->Err()cycle ending infatal error: stack overflow. Every test usingContextWithSimulatedTimeout()crashed, ininternal/limiterandinternal/gitaly/service/hook. The cause is now resolved via the embedded context, which is the one created bycontext.WithCancelCause(). -
TestGetDurationcompared errors withrequire.Equal(), which compares the wrapped error values structurally. Go 1.26 changedtime.ParseDuration()to return a*time.parseDurationErrorinstead of an*errors.errorString, so the comparison failed even though both errors render the same message. The test now compares error messages.
FIPS jobs running out of tmpfs
test:fips: [test-with-praefect, YesPlease, 1.26] failed consistently with No space left on device, which surfaced indirectly as a flood of praefect does not exist and gitaly-hooks does not exist failures.
test:fips overrides TEST_OPTIONS and thereby drops the -p=4 that the Makefile sets, so go test falls back to GOMAXPROCS for -p, which is 32 on the FIPS runners. Each concurrently running package builds its own set of Gitaly binaries into ${TEST_TMP_DIR} via testcfg.BuildBinary(). Those are hard-linked within a test process but not across packages, so every package holds roughly 350MB of a 10GB tmpfs.
The full suite was measured on the FIPS runner with both Go versions, sampling tmpfs usage and the number of concurrent test directories:
| Go | Cache | Peak tmpfs | Pkgs |
|---|---|---|---|
| 1.25.12 | cold | 4503 MB | 13 |
| 1.25.12 | warm | 6359 MB | 29 |
| 1.26.5 | cold | 8453 MB | 32 |
| 1.26.5 | warm | 6772 MB | 29 |
Go 1.26 does not use more space per package. Binaries are within 1% of the 1.25 ones, and with a warm cache both versions behave identically. It builds the cold suite faster and overlaps building with running more, so it reaches the fan-out that was always configured, whereas the slower cold build in Go 1.25 held concurrency at about 13 by accident. The FIPS jobs always run with a cold cache, which is why this failed on every run rather than intermittently.
Note that 1.25 was not safe here either, only slower: its warm-cache run already reached 6359MB against the same 10GB limit.
Candidate -p values were then measured with this job's real configuration, including --rerun-fails, and sampled twice a second, because two-second sampling understates the transient peaks during linking by roughly a factor of two:
| -p | Run | Peak tmpfs | Min MemAvailable |
|---|---|---|---|
| 16 | 1 | 8635 MB | 3152 MB |
| 16 | 2 | 8618 MB | 8838 MB |
| 16 | 3 | 8497 MB | 9446 MB |
| 8 | 1 | 7230 MB | 13356 MB |
| 8 | 2 | 7262 MB | 13635 MB |
-p=8 is used here. It leaves 2.8GB of the tmpfs unused where 16 leaves 1.4GB, and it keeps a comfortable memory margin: the runners have 31.8GB and no swap, and the tmpfs is RAM backed, so one of the 16 runs dropped to 3.1GB available. It also matches what the non-FIPS runners do implicitly, as they have 8 cores. The cost is about 35 seconds.
Relates to #7356 (closed)