Skip to content

Commit a634de2

Browse files
committed
Adjust scripts/tests to generate Go code coverage data
With Go 1.20+, we can generate coverage information from binaries (not just tests), so our "canonical output" tests can *also* generate appropriate coverage information so we can see more clearly how we're doing and where we can improve. I decided *not* to hook this up to Codecov for now, because I have not been happy with their service, but I did leave a `TODO` comment in the appropriate place for where/how we could implement similar functionality (like their "change in coverage" comments).
1 parent 96ed6a9 commit a634de2

File tree

10 files changed

+146
-25
lines changed

10 files changed

+146
-25
lines changed

.github/workflows/ci.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,4 +31,10 @@ jobs:
3131
.bin/bashbrew --version
3232
echo "$PWD/.bin" >> "$GITHUB_PATH"
3333
- run: .test/test.sh
34+
- uses: actions/upload-artifact@v4
35+
with:
36+
name: coverage
37+
path: .test/coverage**
38+
if-no-files-found: error
3439
- run: git diff --exit-code
40+
# TODO download latest coverage artifacts from HEAD / PR target to emulate Codecov but without another flaky third-party service that's begging for write-access to all our repositories via a GitHub App? 👀

.gitignore

Lines changed: 0 additions & 1 deletion
This file was deleted.

.go-env.sh

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ user="$(id -u):$(id -g)"
88
args=(
99
--interactive --rm --init
1010
--user "$user"
11-
--mount "type=bind,src=$dir,dst=/app"
12-
--workdir /app
11+
--mount "type=bind,src=$dir,dst=$dir"
12+
--workdir "$dir"
1313
--tmpfs /tmp,exec
1414
--env HOME=/tmp
1515

@@ -20,6 +20,7 @@ args=(
2020

2121
--env "CGO_ENABLED=${CGO_ENABLED-0}"
2222
--env "GOTOOLCHAIN=${GOTOOLCHAIN-local}"
23+
--env GOCOVERDIR # https://go.dev/doc/build-cover
2324
--env GODEBUG
2425
--env GOFLAGS
2526
--env GOOS --env GOARCH

.test/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
coverage**

.test/lookup-test.json

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
[
2+
{
3+
"schemaVersion": 2,
4+
"mediaType": "application/vnd.docker.distribution.manifest.list.v2+json",
5+
"manifests": [
6+
{
7+
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
8+
"digest": "sha256:2f19ce27632e6baf4ebb1b582960d68948e52902c8cfac10133da0058f1dab23",
9+
"size": 946,
10+
"annotations": {
11+
"com.docker.official-images.bashbrew.arch": "windows-amd64",
12+
"org.opencontainers.image.ref.name": "docker.io/tianon/test@sha256:2f19ce27632e6baf4ebb1b582960d68948e52902c8cfac10133da0058f1dab23"
13+
},
14+
"platform": {
15+
"architecture": "amd64",
16+
"os": "windows",
17+
"os.version": "10.0.20348.2340"
18+
}
19+
}
20+
],
21+
"annotations": {
22+
"org.opencontainers.image.ref.name": "docker.io/tianon/test@sha256:2f19ce27632e6baf4ebb1b582960d68948e52902c8cfac10133da0058f1dab23"
23+
}
24+
},
25+
{
26+
"schemaVersion": 2,
27+
"mediaType": "application/vnd.oci.image.index.v1+json",
28+
"manifests": [
29+
{
30+
"mediaType": "application/vnd.oci.image.manifest.v1+json",
31+
"digest": "sha256:e2fc4e5012d16e7fe466f5291c476431beaa1f9b90a5c2125b493ed28e2aba57",
32+
"size": 861,
33+
"annotations": {
34+
"com.docker.official-images.bashbrew.arch": "amd64",
35+
"org.opencontainers.image.ref.name": "docker.io/tianon/test@sha256:e2fc4e5012d16e7fe466f5291c476431beaa1f9b90a5c2125b493ed28e2aba57",
36+
"org.opencontainers.image.revision": "3fb6ebca4163bf5b9cc496ac3e8f11cb1e754aee",
37+
"org.opencontainers.image.source": "https://github.com/docker-library/hello-world.git#3fb6ebca4163bf5b9cc496ac3e8f11cb1e754aee:amd64/hello-world",
38+
"org.opencontainers.image.url": "https://hub.docker.com/_/hello-world",
39+
"org.opencontainers.image.version": "linux"
40+
},
41+
"platform": {
42+
"architecture": "amd64",
43+
"os": "linux"
44+
}
45+
},
46+
{
47+
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
48+
"digest": "sha256:2f19ce27632e6baf4ebb1b582960d68948e52902c8cfac10133da0058f1dab23",
49+
"size": 946,
50+
"annotations": {
51+
"com.docker.official-images.bashbrew.arch": "windows-amd64",
52+
"org.opencontainers.image.ref.name": "docker.io/tianon/test@sha256:2f19ce27632e6baf4ebb1b582960d68948e52902c8cfac10133da0058f1dab23"
53+
},
54+
"platform": {
55+
"architecture": "amd64",
56+
"os": "windows",
57+
"os.version": "10.0.20348.2340"
58+
}
59+
},
60+
{
61+
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
62+
"digest": "sha256:3a0bd0fb5ad6dd6528dc78726b3df78e980b39b379e99c5a508904ec17cfafe5",
63+
"size": 946,
64+
"annotations": {
65+
"com.docker.official-images.bashbrew.arch": "windows-amd64",
66+
"org.opencontainers.image.ref.name": "docker.io/tianon/test@sha256:3a0bd0fb5ad6dd6528dc78726b3df78e980b39b379e99c5a508904ec17cfafe5"
67+
},
68+
"platform": {
69+
"architecture": "amd64",
70+
"os": "windows",
71+
"os.version": "10.0.17763.5576"
72+
}
73+
}
74+
],
75+
"annotations": {
76+
"org.opencontainers.image.ref.name": "docker.io/tianon/test@sha256:347290ddd775c1b85a3e381b09edde95242478eb65153e9b17225356f4c072ac"
77+
}
78+
}
79+
]

.test/test.sh

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,40 @@ time bashbrew fetch "$@"
2828

2929
time "$dir/../sources.sh" "$@" > "$dir/sources.json"
3030

31+
rm -rf "$dir/coverage"
32+
mkdir -p "$dir/coverage"
33+
export GOCOVERDIR="${GOCOVERDIR:-"$dir/coverage"}"
34+
35+
rm -f "$dir/../bin/builds" # make sure we build with -cover for sure
3136
time "$dir/../builds.sh" --cache "$dir/cache-builds.json" "$dir/sources.json" > "$dir/builds.json"
3237

38+
# test again, but with "--cache=..." instead of "--cache ..." (which also lets us delete the cache and get slightly better coverage reports at the expense of speed / Hub requests)
39+
time "$dir/../builds.sh" --cache="$dir/cache-builds.json" "$dir/sources.json" > "$dir/builds.json"
40+
41+
# test "lookup" code for more edge cases
42+
"$dir/../.go-env.sh" go build -cover -trimpath -o "$dir/../bin/lookup" ./cmd/lookup
43+
lookup=(
44+
# force a config blob lookup for platform object creation (and top-level Docker media type!)
45+
'tianon/test@sha256:2f19ce27632e6baf4ebb1b582960d68948e52902c8cfac10133da0058f1dab23'
46+
# (this is the first Windows manifest of "tianon/test:index-no-platform-smaller" referenced below)
47+
48+
# tianon/test:index-no-platform-smaller - a "broken" index with *zero* platform objects in it (so every manifest requires a platform lookup)
49+
'tianon/test@sha256:347290ddd775c1b85a3e381b09edde95242478eb65153e9b17225356f4c072ac'
50+
# (doing these in the same run means the manifest from above should be cached and exercise more codepaths for better coverage)
51+
)
52+
"$dir/../bin/lookup" "${lookup[@]}" | jq -s > "$dir/lookup-test.json"
53+
54+
# don't leave around the "-cover" versions of these binaries
55+
rm -f "$dir/../bin/builds" "$dir/../bin/lookup"
56+
57+
# Go tests
58+
"$dir/../.go-env.sh" go test -cover ./... -args -test.gocoverdir="$GOCOVERDIR"
59+
60+
# combine the coverage data into the "legacy" coverage format (understood by "go tool cover") and pre-generate HTML for easier digestion of the data
61+
"$dir/../.go-env.sh" go tool covdata textfmt -i "$GOCOVERDIR" -o "$dir/coverage.txt"
62+
"$dir/../.go-env.sh" go tool cover -html "$dir/coverage.txt" -o "$dir/coverage.html"
63+
"$dir/../.go-env.sh" go tool cover -func "$dir/coverage.txt"
64+
3365
# generate an "example commands" file so that changes to generated commands are easier to review
3466
SOURCE_DATE_EPOCH=0 jq -r -L "$dir/.." '
3567
include "meta";

bin/.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
**
2+
!.gitignore

builds.sh

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,14 @@ export BASHBREW_STAGING_TEMPLATE
77

88
dir="$(dirname "$BASH_SOURCE")"
99
dir="$(readlink -ve "$dir")"
10-
if ( cd "$dir" && ./.any-go-nt.sh builds ); then
10+
bin="$dir/bin/builds"
11+
if ( cd "$dir" && ./.any-go-nt.sh "$bin" ); then
1112
{
12-
echo "building '$dir/builds' from 'builds.go'"
13-
"$dir/.go-env.sh" go build -v -o builds builds.go
14-
ls -l "$dir/builds"
13+
echo "building '$bin'"
14+
"$dir/.go-env.sh" go build ${GOCOVERDIR:+-cover} -v -trimpath -o "$bin" ./cmd/builds
15+
ls -l "$bin"
1516
} >&2
1617
fi
17-
[ -x "$dir/builds" ]
18+
[ -x "$bin" ]
1819

19-
"$dir/builds" "$@" | jq .
20+
"$bin" "$@" | jq .
File renamed without changes.

lookup.go renamed to cmd/lookup/main.go

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -15,21 +15,21 @@ func main() {
1515
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt)
1616
defer stop()
1717

18-
img := os.Args[1]
19-
20-
ref, err := registry.ParseRefNormalized(img)
21-
if err != nil {
22-
panic(err)
23-
}
24-
25-
index, err := registry.SynthesizeIndex(ctx, ref)
26-
if err != nil {
27-
panic(err)
28-
}
29-
30-
e := json.NewEncoder(os.Stdout)
31-
e.SetIndent("", "\t")
32-
if err := e.Encode(index); err != nil {
33-
panic(err)
18+
for _, img := range os.Args[1:] {
19+
ref, err := registry.ParseRefNormalized(img)
20+
if err != nil {
21+
panic(err)
22+
}
23+
24+
index, err := registry.SynthesizeIndex(ctx, ref)
25+
if err != nil {
26+
panic(err)
27+
}
28+
29+
e := json.NewEncoder(os.Stdout)
30+
e.SetIndent("", "\t")
31+
if err := e.Encode(index); err != nil {
32+
panic(err)
33+
}
3434
}
3535
}

0 commit comments

Comments
 (0)