rastrillo / fiesta Public

Resolve the site's /1/ links against the release the source builds until it is published

Getting started lists every file the source release ships, each
linked through /1/, and the site's link check resolved /1/ against the
newest published release only. So any file added to the source was a
404 from the day it was added until the release that carries it was
cut: copy.js turned the check red on every Getting started page, 36
lines, though the site goes out with that release and will serve it.

While the source is ahead of releases/ (no releases/<Version>/, or one
holding other files than build.Dist builds now), the check now serves
the source's build beside the published releases and points the /M/
rewrite at it, as the edge will once the release is cut. Once
releases/ holds what the source builds, /1/ resolves against the
newest published release as before. Today Version is still 1.0.0 and
releases/1.0.0/ exists, so "no releases/<Version>/" alone would not
have covered it; the files are compared too. The build sits under
<Version>/ when that is free and under unreleased/ when Version is
published already, so a pinned /<Version>/ link still reaches the
bytes that version shipped.

Rejected: listing unreleased files on Getting started without a link
(a design change to the page), and leaving the check red until the
release (every task in between would run red).

The check was red on today's tree first: 36 lines, each
"getting-started.html: a href https://fiesta.rastrillo.org/1/copy.js
resolves to /1/copy.js, which the edge answers with a 404". Green with
the change, and on 95bd8cc, before copy.js, the source equals
releases/1.0.0/ and /1/ resolves against it as before.

A new control, red by name under each mutation: a scratch page links
/1/fiesta.css (published), /1/copy.js (only in the source build) and
/1/missing.js (in neither). Against 1.0.0 the last two fail; served as
the release to come, under the same version or a new one, only
/1/missing.js does. unpublished is false for a release equal to the
source build (its stamp aside), true with one file changed and true
with no directory. Mutations: servingSource adding nothing, any /1/
link accepted, unpublished always false, unpublished always true;
each turns the control red.

The ship-time guard, refusing to ship unless releases/<Version>/
exists, is not here; it goes with shipping.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
claude agent, operated by paul@keymail.dev a7a112946cd49248016be1c41d27b1fb7aafec95 parent 22c1878
1 file changed, +160 −2
  • internal/www/links_test.go +160 −2

CI failed — run details

CI log
=== gofmt ===
=== no-rastrillo ===
go test -run '^(TestFiestaNeverImportsRastrillo|TestThePublicPackagesLinkNoBrowser)$' -count=1 .
ok  	amadan.net/rastrillo/fiesta	0.147s
=== size ===
go test ./internal/build -run '^TestTheOneLinkIsUnder25KiBGzipped$' -count=1 -v
=== RUN   TestTheOneLinkIsUnder25KiBGzipped
    build_test.go:138: fiesta-plain.css: 152856 bytes, 22303 bytes gzipped (21.8 KiB of the 25.0 KiB limit)
    build_test.go:138: fiesta-signal.css: 152951 bytes, 22414 bytes gzipped (21.9 KiB of the 25.0 KiB limit)
    build_test.go:138: fiesta.css: 155770 bytes, 23018 bytes gzipped (22.5 KiB of the 25.0 KiB limit)
--- PASS: TestTheOneLinkIsUnder25KiBGzipped (0.02s)
PASS
ok  	amadan.net/rastrillo/fiesta/internal/build	0.024s
=== history ===
history: 11 commits; release tags: v1.0.0
=== suite ===
--- shard 0 of 48 ---
==> shard 0 of 48: make root
go build ./...
go vet ./...
FIESTA_TEST_REQUIRE_NODE=1 FIESTA_TEST_REQUIRE_GIT=1 FIESTA_TEST_REQUIRE_TAGS=1 ./hack/gotest.sh root 'internal/gallery@3 cmd/fiesta@2 internal/www@2'
ok  	amadan.net/rastrillo/fiesta	2.886s
ok  	amadan.net/rastrillo/fiesta/check	5.327s
ok  	amadan.net/rastrillo/fiesta/example	0.097s
?   	amadan.net/rastrillo/fiesta/hack/seed/english	[no test files]
ok  	amadan.net/rastrillo/fiesta/internal/axe	0.122s
ok  	amadan.net/rastrillo/fiesta/internal/build	1.040s
ok  	amadan.net/rastrillo/fiesta/internal/codeview	0.004s
?   	amadan.net/rastrillo/fiesta/internal/harness	[no test files]
ok  	amadan.net/rastrillo/fiesta/internal/markup	0.009s
ok  	amadan.net/rastrillo/fiesta/internal/nodetest	0.355s
ok  	amadan.net/rastrillo/fiesta/internal/site	3.290s
ok  	amadan.net/rastrillo/fiesta/internal/tokens	0.011s
suite: root took 13s; CPU, user then system, of this shell and then of the role:
0m0.000000s 0m0.000000s
0m10.240000s 0m2.040000s
--- shard 1 of 48 ---
==> shard 1 of 48: make test.internal.gallery~1of3
FIESTA_TEST_REQUIRE_NODE=1 FIESTA_TEST_REQUIRE_GIT=1 FIESTA_TEST_REQUIRE_TAGS=1 ./hack/gotest.sh test 'internal.gallery~1of3'
gotest: slice 1 of 3 of ./internal/gallery/ runs 35 of 105 test tests
ok  	amadan.net/rastrillo/fiesta/internal/gallery	62.450s
suite: test.internal.gallery~1of3 took 68s; CPU, user then system, of this shell and then of the role:
0m0.000000s 0m0.000000s
0m48.480000s 0m1.210000s
--- shard 2 of 48 ---
==> shard 2 of 48: make test.internal.gallery~2of3
FIESTA_TEST_REQUIRE_NODE=1 FIESTA_TEST_REQUIRE_GIT=1 FIESTA_TEST_REQUIRE_TAGS=1 ./hack/gotest.sh test 'internal.gallery~2of3'
gotest: slice 2 of 3 of ./internal/gallery/ runs 35 of 105 test tests
ok  	amadan.net/rastrillo/fiesta/internal/gallery	61.284s
suite: test.internal.gallery~2of3 took 67s; CPU, user then system, of this shell and then of the role:
0m0.000000s 0m0.000000s
0m46.470000s 0m1.290000s
--- shard 3 of 48 ---
==> shard 3 of 48: make test.internal.gallery~3of3
FIESTA_TEST_REQUIRE_NODE=1 FIESTA_TEST_REQUIRE_GIT=1 FIESTA_TEST_REQUIRE_TAGS=1 ./hack/gotest.sh test 'internal.gallery~3of3'
gotest: slice 3 of 3 of ./internal/gallery/ runs 35 of 105 test tests
ok  	amadan.net/rastrillo/fiesta/internal/gallery	58.210s
suite: test.internal.gallery~3of3 took 64s; CPU, user then system, of this shell and then of the role:
0m0.000000s 0m0.000000s
0m44.080000s 0m1.270000s
--- shard 4 of 48 ---
==> shard 4 of 48: make test.cmd.fiesta~1of2
FIESTA_TEST_REQUIRE_NODE=1 FIESTA_TEST_REQUIRE_GIT=1 FIESTA_TEST_REQUIRE_TAGS=1 ./hack/gotest.sh test 'cmd.fiesta~1of2'
gotest: slice 1 of 2 of ./cmd/fiesta/ runs 7 of 12 test tests
ok  	amadan.net/rastrillo/fiesta/cmd/fiesta	48.368s
suite: test.cmd.fiesta~1of2 took 52s; CPU, user then system, of this shell and then of the role:
0m0.000000s 0m0.000000s
0m32.700000s 0m2.100000s
--- shard 5 of 48 ---
==> shard 5 of 48: make test.cmd.fiesta~2of2
FIESTA_TEST_REQUIRE_NODE=1 FIESTA_TEST_REQUIRE_GIT=1 FIESTA_TEST_REQUIRE_TAGS=1 ./hack/gotest.sh test 'cmd.fiesta~2of2'
gotest: slice 2 of 2 of ./cmd/fiesta/ runs 5 of 12 test tests
ok  	amadan.net/rastrillo/fiesta/cmd/fiesta	51.778s
suite: test.cmd.fiesta~2of2 took 56s; CPU, user then system, of this shell and then of the role:
0m0.000000s 0m0.000000s
0m35.060000s 0m2.340000s
--- shard 6 of 48 ---
==> shard 6 of 48: make test.internal.www~1of2
FIESTA_TEST_REQUIRE_NODE=1 FIESTA_TEST_REQUIRE_GIT=1 FIESTA_TEST_REQUIRE_TAGS=1 ./hack/gotest.sh test 'internal.www~1of2'
gotest: slice 1 of 2 of ./internal/www/ runs 11 of 25 test tests
ok  	amadan.net/rastrillo/fiesta/internal/www	63.201s
suite: test.internal.www~1of2 took 68s; CPU, user then system, of this shell and then of the role:
0m0.000000s 0m0.000000s
0m46.460000s 0m1.670000s
--- shard 7 of 48 ---
==> shard 7 of 48: make test.internal.www~2of2
FIESTA_TEST_REQUIRE_NODE=1 FIESTA_TEST_REQUIRE_GIT=1 FIESTA_TEST_REQUIRE_TAGS=1 ./hack/gotest.sh test 'internal.www~2of2'
gotest: slice 2 of 2 of ./internal/www/ runs 14 of 25 test tests
ok  	amadan.net/rastrillo/fiesta/internal/www	59.924s
suite: test.internal.www~2of2 took 65s; CPU, user then system, of this shell and then of the role:
0m0.000000s 0m0.000000s
0m44.570000s 0m1.690000s
--- shard 8 of 48 ---
==> shard 8 of 48: make staticcheck
go run honnef.co/go/tools/cmd/staticcheck@v0.8.2 -tags browser ./...
suite: staticcheck took 12s; CPU, user then system, of this shell and then of the role:
0m0.000000s 0m0.000000s
0m15.010000s 0m1.000000s
--- shard 9 of 48 ---
==> shard 9 of 48: make govulncheck
go run golang.org/x/vuln/cmd/govulncheck@v1.8.0 ./...
=== Symbol Results ===

No vulnerabilities found.

Your code is affected by 0 vulnerabilities.
This scan also found 2 vulnerabilities in packages you import and 5
vulnerabilities in modules you require, but your code doesn't appear to call
these vulnerabilities.
Use '-show verbose' for more details.
suite: govulncheck took 8s; CPU, user then system, of this shell and then of the role:
0m0.000000s 0m0.000000s
0m3.080000s 0m0.330000s
--- shard 10 of 48 ---
==> shard 10 of 48: make gitleaks
go run github.com/zricethezav/gitleaks/v8@v8.30.1 git --no-banner --redact --log-opts='--full-history --diff-filter=tuxdb HEAD' .
12:16AM INF Unknown SCM platform. Use --platform to include links in findings. host=amadan.net
12:16AM INF 11 commits scanned.
12:16AM INF scanned ~8173605 bytes (8.17 MB) in 6.62s
12:16AM INF no leaks found
suite: gitleaks took 7s; CPU, user then system, of this shell and then of the role:
0m0.000000s 0m0.000000s
0m6.860000s 0m0.160000s
--- shard 11 of 48 ---
==> shard 11 of 48: make browser.internal.harness
./hack/gotest.sh browser 'internal.harness'
gotest: on ip-172-31-19-71, driving the harness's own choice (/usr/local/bin/chromium)
gotest: ./internal/harness/ runs its 10 browser tests
ok  	amadan.net/rastrillo/fiesta/internal/harness	6.632s
suite: browser.internal.harness took 10s; CPU, user then system, of this shell and then of the role:
0m0.000000s 0m0.000000s
0m1.750000s 0m1.240000s
--- shard 12 of 48 ---
==> shard 12 of 48: make browser.check~1of13
./hack/gotest.sh browser 'check~1of13'
gotest: on ip-172-31-19-71, driving /opt/amadan-browser/chromium-1234/chrome-linux/chrome
gotest: slice 1 of 13 of ./check/ runs 14 of 204 browser tests
ok  	amadan.net/rastrillo/fiesta/check	80.255s
suite: browser.check~1of13 took 90s; CPU, user then system, of this shell and then of the role:
0m0.000000s 0m0.000000s
0m42.460000s 0m13.340000s
--- shard 13 of 48 ---
==> shard 13 of 48: make browser.check~2of13
./hack/gotest.sh browser 'check~2of13'
gotest: on ip-172-31-19-71, driving /opt/amadan-browser/chromium-1234/chrome-linux/chrome
gotest: slice 2 of 13 of ./check/ runs 15 of 204 browser tests
ok  	amadan.net/rastrillo/fiesta/check	80.738s
suite: browser.check~2of13 took 91s; CPU, user then system, of this shell and then of the role:
0m0.000000s 0m0.000000s
0m42.610000s 0m13.400000s
--- shard 14 of 48 ---
==> shard 14 of 48: make browser.check~3of13
./hack/gotest.sh browser 'check~3of13'
gotest: on ip-172-31-19-71, driving /opt/amadan-browser/chromium-1234/chrome-linux/chrome
gotest: slice 3 of 13 of ./check/ runs 15 of 204 browser tests
--- FAIL: TestEveryOneOhExampleRendersAsItDid2of4 (300.63s)
    oneoh_browser_test.go:318: context deadline exceeded
FAIL
FAIL	amadan.net/rastrillo/fiesta/check	325.966s
FAIL
make[1]: *** [Makefile:234: browser.check~3of13] Error 1
suite: browser.check~3of13 took 336s; CPU, user then system, of this shell and then of the role:
0m0.000000s 0m0.000000s
0m34.040000s 0m11.280000s
make: *** [Makefile:76: suite] Error 2

amadan: step suite shard 14 of 48 failed: exit status 2
--- shard 15 of 48 ---
==> shard 15 of 48: make browser.check~4of13
./hack/gotest.sh browser 'check~4of13'
gotest: on ip-172-31-19-71, driving /opt/amadan-browser/chromium-1234/chrome-linux/chrome
gotest: slice 4 of 13 of ./check/ runs 15 of 204 browser tests
ok  	amadan.net/rastrillo/fiesta/check	81.922s
suite: browser.check~4of13 took 92s; CPU, user then system, of this shell and then of the role:
0m0.000000s 0m0.000000s
0m45.510000s 0m15.440000s
--- shard 16 of 48 ---
==> shard 16 of 48: make browser.check~5of13
./hack/gotest.sh browser 'check~5of13'
gotest: on ip-172-31-19-71, driving /opt/amadan-browser/chromium-1234/chrome-linux/chrome
gotest: slice 5 of 13 of ./check/ runs 15 of 204 browser tests
ok  	amadan.net/rastrillo/fiesta/check	82.601s
suite: browser.check~5of13 took 92s; CPU, user then system, of this shell and then of the role:
0m0.000000s 0m0.000000s
0m44.230000s 0m12.000000s
--- shard 17 of 48 ---
==> shard 17 of 48: make browser.check~6of13
./hack/gotest.sh browser 'check~6of13'
gotest: on ip-172-31-19-71, driving /opt/amadan-browser/chromium-1234/chrome-linux/chrome
gotest: slice 6 of 13 of ./check/ runs 16 of 204 browser tests
ok  	amadan.net/rastrillo/fiesta/check	85.155s
suite: browser.check~6of13 took 95s; CPU, user then system, of this shell and then of the role:
0m0.000000s 0m0.000000s
0m41.690000s 0m13.320000s
--- shard 18 of 48 ---
==> shard 18 of 48: make browser.check~7of13
./hack/gotest.sh browser 'check~7of13'
gotest: on ip-172-31-19-71, driving /opt/amadan-browser/chromium-1234/chrome-linux/chrome
gotest: slice 7 of 13 of ./check/ runs 16 of 204 browser tests
ok  	amadan.net/rastrillo/fiesta/check	84.934s
suite: browser.check~7of13 took 95s; CPU, user then system, of this shell and then of the role:
0m0.000000s 0m0.000000s
0m47.680000s 0m12.160000s
--- shard 19 of 48 ---
==> shard 19 of 48: make browser.check~8of13
./hack/gotest.sh browser 'check~8of13'
gotest: on ip-172-31-19-71, driving /opt/amadan-browser/chromium-1234/chrome-linux/chrome
gotest: slice 8 of 13 of ./check/ runs 16 of 204 browser tests
ok  	amadan.net/rastrillo/fiesta/check	80.423s
suite: browser.check~8of13 took 91s; CPU, user then system, of this shell and then of the role:
0m0.000000s 0m0.000000s
0m36.310000s 0m13.190000s
--- shard 20 of 48 ---
==> shard 20 of 48: make browser.check~9of13
./hack/gotest.sh browser 'check~9of13'
gotest: on ip-172-31-19-71, driving /opt/amadan-browser/chromium-1234/chrome-linux/chrome
gotest: slice 9 of 13 of ./check/ runs 16 of 204 browser tests
ok  	amadan.net/rastrillo/fiesta/check	83.473s
suite: browser.check~9of13 took 93s; CPU, user then system, of this shell and then of the role:
0m0.000000s 0m0.000000s
0m40.110000s 0m13.350000s
--- shard 21 of 48 ---
==> shard 21 of 48: make browser.check~10of13
./hack/gotest.sh browser 'check~10of13'
gotest: on ip-172-31-19-71, driving /opt/amadan-browser/chromium-1234/chrome-linux/chrome
gotest: slice 10 of 13 of ./check/ runs 16 of 204 browser tests
ok  	amadan.net/rastrillo/fiesta/check	89.319s
suite: browser.check~10of13 took 99s; CPU, user then system, of this shell and then of the role:
0m0.000000s 0m0.000000s
0m51.820000s 0m11.710000s
--- shard 22 of 48 ---
==> shard 22 of 48: make browser.check~11of13
./hack/gotest.sh browser 'check~11of13'
gotest: on ip-172-31-19-71, driving /opt/amadan-browser/chromium-1234/chrome-linux/chrome
gotest: slice 11 of 13 of ./check/ runs 16 of 204 browser tests
ok  	amadan.net/rastrillo/fiesta/check	83.427s
suite: browser.check~11of13 took 94s; CPU, user then system, of this shell and then of the role:
0m0.000000s 0m0.000000s
0m46.560000s 0m17.230000s
--- shard 23 of 48 ---
==> shard 23 of 48: make browser.check~12of13
./hack/gotest.sh browser 'check~12of13'
gotest: on ip-172-31-19-71, driving /opt/amadan-browser/chromium-1234/chrome-linux/chrome
gotest: slice 12 of 13 of ./check/ runs 17 of 204 browser tests
ok  	amadan.net/rastrillo/fiesta/check	78.788s
suite: browser.check~12of13 took 89s; CPU, user then system, of this shell and then of the role:
0m0.000000s 0m0.000000s
0m31.970000s 0m10.210000s
--- shard 24 of 48 ---
==> shard 24 of 48: make browser.check~13of13
./hack/gotest.sh browser 'check~13of13'
gotest: on ip-172-31-19-71, driving /opt/amadan-browser/chromium-1234/chrome-linux/chrome
gotest: slice 13 of 13 of ./check/ runs 17 of 204 browser tests
ok  	amadan.net/rastrillo/fiesta/check	79.104s
suite: browser.check~13of13 took 89s; CPU, user then system, of this shell and then of the role:
0m0.000000s 0m0.000000s
0m29.100000s 0m9.800000s
--- shard 25 of 48 ---
==> shard 25 of 48: make browser.internal.gallery~1of18
./hack/gotest.sh browser 'internal.gallery~1of18'
gotest: on ip-172-31-19-71, driving the harness's own choice (/usr/local/bin/chromium)
gotest: slice 1 of 18 of ./internal/gallery/ runs 6 of 83 browser tests
ok  	amadan.net/rastrillo/fiesta/internal/gallery	32.438s
suite: browser.internal.gallery~1of18 took 42s; CPU, user then system, of this shell and then of the role:
0m0.000000s 0m0.000000s
0m14.410000s 0m2.950000s
--- shard 26 of 48 ---
==> shard 26 of 48: make browser.internal.gallery~2of18
./hack/gotest.sh browser 'internal.gallery~2of18'
gotest: on ip-172-31-19-71, driving the harness's own choice (/usr/local/bin/chromium)
gotest: slice 2 of 18 of ./internal/gallery/ runs 6 of 83 browser tests
ok  	amadan.net/rastrillo/fiesta/internal/gallery	49.740s
suite: browser.internal.gallery~2of18 took 58s; CPU, user then system, of this shell and then of the role:
0m0.000000s 0m0.000000s
0m15.630000s 0m3.740000s
--- shard 27 of 48 ---
==> shard 27 of 48: make browser.internal.gallery~3of18
./hack/gotest.sh browser 'internal.gallery~3of18'
gotest: on ip-172-31-19-71, driving the harness's own choice (/usr/local/bin/chromium)
gotest: slice 3 of 18 of ./internal/gallery/ runs 6 of 83 browser tests
ok  	amadan.net/rastrillo/fiesta/internal/gallery	59.176s
suite: browser.internal.gallery~3of18 took 68s; CPU, user then system, of this shell and then of the role:
0m0.000000s 0m0.000000s
0m15.970000s 0m4.200000s
--- shard 28 of 48 ---
==> shard 28 of 48: make browser.internal.gallery~4of18
./hack/gotest.sh browser 'internal.gallery~4of18'
gotest: on ip-172-31-19-71, driving the harness's own choice (/usr/local/bin/chromium)
gotest: slice 4 of 18 of ./internal/gallery/ runs 6 of 83 browser tests
ok  	amadan.net/rastrillo/fiesta/internal/gallery	58.921s
suite: browser.internal.gallery~4of18 took 68s; CPU, user then system, of this shell and then of the role:
0m0.000000s 0m0.000000s
0m18.920000s 0m5.590000s
--- shard 29 of 48 ---
==> shard 29 of 48: make browser.internal.gallery~5of18
./hack/gotest.sh browser 'internal.gallery~5of18'
gotest: on ip-172-31-19-71, driving the harness's own choice (/usr/local/bin/chromium)
gotest: slice 5 of 18 of ./internal/gallery/ runs 6 of 83 browser tests
ok  	amadan.net/rastrillo/fiesta/internal/gallery	60.772s
suite: browser.internal.gallery~5of18 took 70s; CPU, user then system, of this shell and then of the role:
0m0.000000s 0m0.000000s
0m16.800000s 0m4.610000s
--- shard 30 of 48 ---
==> shard 30 of 48: make browser.internal.gallery~6of18
./hack/gotest.sh browser 'internal.gallery~6of18'
gotest: on ip-172-31-19-71, driving the harness's own choice (/usr/local/bin/chromium)
gotest: slice 6 of 18 of ./internal/gallery/ runs 6 of 83 browser tests
ok  	amadan.net/rastrillo/fiesta/internal/gallery	55.050s
suite: browser.internal.gallery~6of18 took 64s; CPU, user then system, of this shell and then of the role:
0m0.000000s 0m0.000000s
0m15.390000s 0m3.840000s
--- shard 31 of 48 ---
==> shard 31 of 48: make browser.internal.gallery~7of18
./hack/gotest.sh browser 'internal.gallery~7of18'
gotest: on ip-172-31-19-71, driving the harness's own choice (/usr/local/bin/chromium)
gotest: slice 7 of 18 of ./internal/gallery/ runs 5 of 83 browser tests
ok  	amadan.net/rastrillo/fiesta/internal/gallery	55.344s
suite: browser.internal.gallery~7of18 took 64s; CPU, user then system, of this shell and then of the role:
0m0.000000s 0m0.000000s
0m13.830000s 0m2.830000s
--- shard 32 of 48 ---
==> shard 32 of 48: make browser.internal.gallery~8of18
./hack/gotest.sh browser 'internal.gallery~8of18'
gotest: on ip-172-31-19-71, driving the harness's own choice (/usr/local/bin/chromium)
gotest: slice 8 of 18 of ./internal/gallery/ runs 5 of 83 browser tests
ok  	amadan.net/rastrillo/fiesta/internal/gallery	57.765s
suite: browser.internal.gallery~8of18 took 67s; CPU, user then system, of this shell and then of the role:
0m0.000000s 0m0.000000s
0m18.210000s 0m5.240000s
--- shard 33 of 48 ---
==> shard 33 of 48: make browser.internal.gallery~9of18
./hack/gotest.sh browser 'internal.gallery~9of18'
gotest: on ip-172-31-19-71, driving the harness's own choice (/usr/local/bin/chromium)
gotest: slice 9 of 18 of ./internal/gallery/ runs 5 of 83 browser tests
ok  	amadan.net/rastrillo/fiesta/internal/gallery	64.422s
suite: browser.internal.gallery~9of18 took 73s; CPU, user then system, of this shell and then of the role:
0m0.000000s 0m0.000000s
0m22.240000s 0m7.010000s
--- shard 34 of 48 ---
==> shard 34 of 48: make browser.internal.gallery~10of18
./hack/gotest.sh browser 'internal.gallery~10of18'
gotest: on ip-172-31-19-71, driving the harness's own choice (/usr/local/bin/chromium)
gotest: slice 10 of 18 of ./internal/gallery/ runs 2 of 83 browser tests
ok  	amadan.net/rastrillo/fiesta/internal/gallery	61.289s
suite: browser.internal.gallery~10of18 took 70s; CPU, user then system, of this shell and then of the role:
0m0.000000s 0m0.000000s
0m14.000000s 0m2.700000s
--- shard 35 of 48 ---
==> shard 35 of 48: make browser.internal.gallery~11of18
./hack/gotest.sh browser 'internal.gallery~11of18'
gotest: on ip-172-31-19-71, driving the harness's own choice (/usr/local/bin/chromium)
gotest: slice 11 of 18 of ./internal/gallery/ runs 2 of 83 browser tests
ok  	amadan.net/rastrillo/fiesta/internal/gallery	62.708s
suite: browser.internal.gallery~11of18 took 72s; CPU, user then system, of this shell and then of the role:
0m0.000000s 0m0.000000s
0m13.930000s 0m2.660000s
--- shard 36 of 48 ---
==> shard 36 of 48: make browser.internal.gallery~12of18
./hack/gotest.sh browser 'internal.gallery~12of18'
gotest: on ip-172-31-19-71, driving the harness's own choice (/usr/local/bin/chromium)
gotest: slice 12 of 18 of ./internal/gallery/ runs 2 of 83 browser tests
ok  	amadan.net/rastrillo/fiesta/internal/gallery	63.006s
suite: browser.internal.gallery~12of18 took 72s; CPU, user then system, of this shell and then of the role:
0m0.000000s 0m0.000000s
0m18.320000s 0m4.800000s
--- shard 37 of 48 ---
==> shard 37 of 48: make browser.internal.gallery~13of18
./hack/gotest.sh browser 'internal.gallery~13of18'
gotest: on ip-172-31-19-71, driving the harness's own choice (/usr/local/bin/chromium)
gotest: slice 13 of 18 of ./internal/gallery/ runs 3 of 83 browser tests
ok  	amadan.net/rastrillo/fiesta/internal/gallery	64.557s
suite: browser.internal.gallery~13of18 took 74s; CPU, user then system, of this shell and then of the role:
0m0.000000s 0m0.000000s
0m19.200000s 0m5.200000s
--- shard 38 of 48 ---
==> shard 38 of 48: make browser.internal.gallery~14of18
./hack/gotest.sh browser 'internal.gallery~14of18'
gotest: on ip-172-31-19-71, driving the harness's own choice (/usr/local/bin/chromium)
gotest: slice 14 of 18 of ./internal/gallery/ runs 4 of 83 browser tests
ok  	amadan.net/rastrillo/fiesta/internal/gallery	66.627s
suite: browser.internal.gallery~14of18 took 76s; CPU, user then system, of this shell and then of the role:
0m0.000000s 0m0.000000s
0m19.300000s 0m5.330000s
--- shard 39 of 48 ---
==> shard 39 of 48: make browser.internal.gallery~15of18
./hack/gotest.sh browser 'internal.gallery~15of18'
gotest: on ip-172-31-19-71, driving the harness's own choice (/usr/local/bin/chromium)
gotest: slice 15 of 18 of ./internal/gallery/ runs 4 of 83 browser tests
ok  	amadan.net/rastrillo/fiesta/internal/gallery	65.001s
suite: browser.internal.gallery~15of18 took 74s; CPU, user then system, of this shell and then of the role:
0m0.000000s 0m0.000000s
0m19.440000s 0m5.570000s
--- shard 40 of 48 ---
==> shard 40 of 48: make browser.internal.gallery~16of18
./hack/gotest.sh browser 'internal.gallery~16of18'
gotest: on ip-172-31-19-71, driving the harness's own choice (/usr/local/bin/chromium)
gotest: slice 16 of 18 of ./internal/gallery/ runs 5 of 83 browser tests
ok  	amadan.net/rastrillo/fiesta/internal/gallery	63.045s
suite: browser.internal.gallery~16of18 took 72s; CPU, user then system, of this shell and then of the role:
0m0.000000s 0m0.000000s
0m16.230000s 0m4.090000s
--- shard 41 of 48 ---
==> shard 41 of 48: make browser.internal.gallery~17of18
./hack/gotest.sh browser 'internal.gallery~17of18'
gotest: on ip-172-31-19-71, driving the harness's own choice (/usr/local/bin/chromium)
gotest: slice 17 of 18 of ./internal/gallery/ runs 5 of 83 browser tests
ok  	amadan.net/rastrillo/fiesta/internal/gallery	66.697s
suite: browser.internal.gallery~17of18 took 76s; CPU, user then system, of this shell and then of the role:
0m0.000000s 0m0.000000s
0m19.880000s 0m5.880000s
--- shard 42 of 48 ---
==> shard 42 of 48: make browser.internal.gallery~18of18
./hack/gotest.sh browser 'internal.gallery~18of18'
gotest: on ip-172-31-19-71, driving the harness's own choice (/usr/local/bin/chromium)
gotest: slice 18 of 18 of ./internal/gallery/ runs 5 of 83 browser tests
ok  	amadan.net/rastrillo/fiesta/internal/gallery	67.051s
suite: browser.internal.gallery~18of18 took 76s; CPU, user then system, of this shell and then of the role:
0m0.000000s 0m0.000000s
0m19.160000s 0m5.450000s
--- shard 43 of 48 ---
==> shard 43 of 48: make browser.internal.gallery.history
./hack/gotest.sh browser 'internal.gallery.history'
gotest: on ip-172-31-19-71, driving /opt/amadan-browser/chromium-1234/chrome-linux/chrome
gotest: ./internal/gallery/history/ runs its 3 browser tests
ok  	amadan.net/rastrillo/fiesta/internal/gallery/history	32.414s
suite: browser.internal.gallery.history took 38s; CPU, user then system, of this shell and then of the role:
0m0.000000s 0m0.000000s
0m21.340000s 0m2.140000s
--- shard 44 of 48 ---
==> shard 44 of 48: make browser.internal.www~1of4
./hack/gotest.sh browser 'internal.www~1of4'
gotest: on ip-172-31-19-71, driving the harness's own choice (/usr/local/bin/chromium)
gotest: slice 1 of 4 of ./internal/www/ runs 4 of 22 browser tests
ok  	amadan.net/rastrillo/fiesta/internal/www	69.872s
suite: browser.internal.www~1of4 took 78s; CPU, user then system, of this shell and then of the role:
0m0.000000s 0m0.000000s
0m31.240000s 0m2.130000s
--- shard 45 of 48 ---
==> shard 45 of 48: make browser.internal.www~2of4
./hack/gotest.sh browser 'internal.www~2of4'
gotest: on ip-172-31-19-71, driving the harness's own choice (/usr/local/bin/chromium)
gotest: slice 2 of 4 of ./internal/www/ runs 6 of 22 browser tests
ok  	amadan.net/rastrillo/fiesta/internal/www	66.949s
suite: browser.internal.www~2of4 took 75s; CPU, user then system, of this shell and then of the role:
0m0.000000s 0m0.000000s
0m45.700000s 0m2.680000s
--- shard 46 of 48 ---
==> shard 46 of 48: make browser.internal.www~3of4
./hack/gotest.sh browser 'internal.www~3of4'
gotest: on ip-172-31-19-71, driving the harness's own choice (/usr/local/bin/chromium)
gotest: slice 3 of 4 of ./internal/www/ runs 6 of 22 browser tests
ok  	amadan.net/rastrillo/fiesta/internal/www	81.140s
suite: browser.internal.www~3of4 took 89s; CPU, user then system, of this shell and then of the role:
0m0.000000s 0m0.000000s
0m51.910000s 0m3.020000s
--- shard 47 of 48 ---
==> shard 47 of 48: make browser.internal.www~4of4
./hack/gotest.sh browser 'internal.www~4of4'
gotest: on ip-172-31-19-71, driving the harness's own choice (/usr/local/bin/chromium)
gotest: slice 4 of 4 of ./internal/www/ runs 6 of 22 browser tests
ok  	amadan.net/rastrillo/fiesta/internal/www	77.186s
suite: browser.internal.www~4of4 took 85s; CPU, user then system, of this shell and then of the role:
0m0.000000s 0m0.000000s
0m46.000000s 0m2.990000s

amadan: job failed at step suite shard 14 of 48 — its output is above, not at the tail of this log
diff --git a/internal/www/links_test.go b/internal/www/links_test.go
index 98466d8..e82d648 100644
--- a/internal/www/links_test.go
+++ b/internal/www/links_test.go
@@ -1,17 +1,26 @@
package www_test
import (
+ "bytes"
"fmt"
+ "io/fs"
+ "maps"
"net/http"
"net/http/httptest"
"net/url"
+ "os"
"regexp"
+ "slices"
"sort"
"strings"
"testing"
+ "testing/fstest"
xhtml "golang.org/x/net/html"
+ "amadan.net/rastrillo/fiesta"
+ "amadan.net/rastrillo/fiesta/internal/build"
+ "amadan.net/rastrillo/fiesta/internal/site"
"amadan.net/rastrillo/fiesta/internal/www"
)
@@ -198,6 +207,66 @@ func linkProblems(t *testing.T, tree map[string][]byte) (problems []string, page
return problems, pages
}
+// unpublished reports whether the source is ahead of releases/: there
+// is no releases/<version>/, or it holds other files than dist, the
+// release the source builds. That is the stretch between a change to
+// the source and the release that carries it, and the stretch the
+// design system's pages spend linking files of the release to come.
+func unpublished(releases fs.FS, version string, dist map[string][]byte) (bool, error) {
+ if _, err := fs.Stat(releases, version); err != nil {
+ return true, nil
+ }
+ dir, err := fs.Sub(releases, version)
+ if err != nil {
+ return false, err
+ }
+ published, err := site.Release(dir)
+ if err != nil {
+ return false, err
+ }
+ return !maps.EqualFunc(published, dist, bytes.Equal), nil
+}
+
+// servingSource is tree as the edge will serve it once dist is
+// released: dist sits beside the published releases, and the rewrite
+// for version's major sends /M/ to it rather than to the newest
+// published release. It sits under version/ when that is free, where
+// fiesta release will put it, and under unreleased/ when version is
+// published already and the source has moved past it, so a pinned
+// /version/ link still reaches the bytes that version shipped.
+func servingSource(t *testing.T, tree map[string][]byte, version string, dist map[string][]byte) map[string][]byte {
+ t.Helper()
+ out := maps.Clone(tree)
+ dir := version
+ for name := range tree {
+ if strings.HasPrefix(name, version+"/") {
+ dir = "unreleased"
+ break
+ }
+ }
+ for name, data := range dist {
+ out[dir+"/"+name] = data
+ }
+ major, _, _ := strings.Cut(version, ".")
+ prefix := "/" + major + "/* "
+ rule := prefix + "/" + dir + "/:splat 200"
+ var lines []string
+ found := false
+ for _, line := range strings.Split(strings.TrimSuffix(string(tree["_redirects"]), "\n"), "\n") {
+ if strings.HasPrefix(line, prefix) {
+ line, found = rule, true
+ }
+ lines = append(lines, line)
+ }
+ if !found {
+ // A new major's first release: its rule goes first, as
+ // site.Build writes the newest major's.
+ lines = append([]string{rule}, lines...)
+ }
+ out["_redirects"] = []byte(strings.Join(lines, "\n") + "\n")
+ return out
+}
+
// TestEveryLinkInTheSiteResolves: on every page of the built site,
// the landing page in each language, the design system's index, every
// example page and every frame, every href, src and srcset reaches a
@@ -208,10 +277,20 @@ func linkProblems(t *testing.T, tree map[string][]byte) (problems []string, page
// is held to the same rule as the hreflang links: the site's own,
// through the rewrite to the newest release.
//
+// While the source is unpublished (see unpublished), /1/ is resolved
+// against the release the source builds, not the newest published one:
+// the design system lists every file the source ships, linked through
+// /1/, and the site goes out with the release that carries them. Read
+// against 1.0.0 alone, every new file is a 404 from the day it is
+// added to the day the release is cut. Once releases/ holds what the
+// source builds, /1/ resolves against it as the edge will.
+//
// The control comes first: a scratch site whose one page links a
// missing page, a missing fragment, a wrong path under the site's own
// origin and an outside URL that is not on the list. The check has to
-// name all four, or a clean run of the real site proves nothing.
+// name all four, or a clean run of the real site proves nothing. A
+// second control holds the unpublished stretch: a file the source
+// builds resolves through /1/, and one it does not build still fails.
func TestEveryLinkInTheSiteResolves(t *testing.T) {
control := map[string][]byte{
"_redirects": []byte("/1/* /1.0.0/:splat 200\n"),
@@ -237,11 +316,25 @@ func TestEveryLinkInTheSiteResolves(t *testing.T) {
if len(problems) != 4 {
t.Errorf("the control has 4 broken links and %d were reported:\n %s", len(problems), strings.Join(problems, "\n "))
}
+ unpublishedControl(t)
if t.Failed() {
return
}
- problems, pages := linkProblems(t, builtSite(t))
+ tree := builtSite(t)
+ dist, err := build.Dist(fiesta.FS(), fiesta.Version)
+ if err != nil {
+ t.Fatalf("building the release the source would publish: %v", err)
+ }
+ ahead, err := unpublished(os.DirFS("../../releases"), fiesta.Version, dist)
+ if err != nil {
+ t.Fatalf("reading releases/%s: %v", fiesta.Version, err)
+ }
+ if ahead {
+ t.Logf("the source is ahead of releases/%s, so /1/ resolves against the release it builds", fiesta.Version)
+ tree = servingSource(t, tree, fiesta.Version, dist)
+ }
+ problems, pages := linkProblems(t, tree)
for _, p := range problems {
t.Error(p)
}
@@ -268,3 +361,68 @@ func TestEveryLinkInTheSiteResolves(t *testing.T) {
t.Errorf("%d example pages and %d frames were read; the design system has both", examples, frames)
}
}
+
+// unpublishedControl is the unpublished stretch on scratch trees. A
+// page links /1/fiesta.css, which 1.0.0 has, /1/copy.js, which only
+// the source builds, and /1/missing.js, which nothing does. Read
+// against 1.0.0 alone the last two fail; served as the release to come
+// only the last does. unpublished is held on both sides: a release
+// equal to the source build (its stamp aside) is published, one with a
+// file changed or none at all is not.
+func unpublishedControl(t *testing.T) {
+ t.Helper()
+ dist := map[string][]byte{"fiesta.css": []byte("body{}"), "copy.js": []byte("0")}
+ dist["SHA256SUMS"] = sumsOf(dist)
+ tree := map[string][]byte{
+ "_redirects": []byte("/1/* /1.0.0/:splat 200\n"),
+ "1.0.0/fiesta.css": []byte("body{}"),
+ "index.html": []byte(`<main id="main"><link rel="stylesheet" href="/1/fiesta.css">` +
+ `<script src="/1/copy.js"></script><script src="/1/missing.js"></script></main>`),
+ }
+ for _, c := range []struct {
+ name string
+ tree map[string][]byte
+ want []string
+ }{
+ {"against 1.0.0", tree, []string{"/1/copy.js", "/1/missing.js"}},
+ {"as the release to come", servingSource(t, tree, "1.0.0", dist), []string{"/1/missing.js"}},
+ {"as a new version's release", servingSource(t, tree, "1.1.0", dist), []string{"/1/missing.js"}},
+ } {
+ problems, _ := linkProblems(t, c.tree)
+ var got []string
+ for _, p := range problems {
+ for _, link := range []string{"/1/fiesta.css", "/1/copy.js", "/1/missing.js"} {
+ if strings.Contains(p, "resolves to "+link+",") {
+ got = append(got, link)
+ }
+ }
+ }
+ if len(got) != len(problems) || !slices.Equal(got, c.want) {
+ t.Errorf("the unpublished control, %s, reported %q; want only %q broken", c.name, problems, c.want)
+ }
+ }
+
+ stamp := &fstest.MapFile{Data: []byte("fiesta release\n")}
+ same := fstest.MapFS{"1.0.0/" + site.StampName: stamp}
+ changed := fstest.MapFS{"1.0.0/" + site.StampName: stamp}
+ for name, data := range dist {
+ same["1.0.0/"+name] = &fstest.MapFile{Data: data}
+ changed["1.0.0/"+name] = &fstest.MapFile{Data: data}
+ }
+ changed["1.0.0/copy.js"] = &fstest.MapFile{Data: []byte("1")}
+ changed["1.0.0/SHA256SUMS"] = &fstest.MapFile{Data: sumsOf(map[string][]byte{"fiesta.css": dist["fiesta.css"], "copy.js": []byte("1")})}
+ for _, c := range []struct {
+ name string
+ releases fs.FS
+ want bool
+ }{
+ {"the same files", same, false},
+ {"one file changed", changed, true},
+ {"no release directory", fstest.MapFS{}, true},
+ } {
+ got, err := unpublished(c.releases, "1.0.0", dist)
+ if err != nil || got != c.want {
+ t.Errorf("unpublished with %s = %v, %v; want %v", c.name, got, err, c.want)
+ }
+ }
+}