Skip to content

Commit 1266cda

Browse files
GODRIVER-3493 Compile check all support Go versions (#1992)
1 parent 927052b commit 1266cda

File tree

7 files changed

+454
-43
lines changed

7 files changed

+454
-43
lines changed

.evergreen/config.yml

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1421,16 +1421,15 @@ tasks:
14211421
vars:
14221422
MONGO_GO_DRIVER_COMPRESSOR: "snappy"
14231423

1424-
# Build the compilecheck submodule with the oldest supported version of Go.
1425-
- name: go1.18-build
1424+
# Build the compilecheck submodule with all supported versions of Go >=
1425+
# the minimum supported version.
1426+
- name: go-build
14261427
tags: ["compile-check"]
14271428
commands:
14281429
- command: subprocess.exec
14291430
params:
14301431
binary: bash
1431-
env:
1432-
GO_VERSION: "1.18"
1433-
args: [*task-runner, build-compile-check]
1432+
args: [*task-runner, build-compile-check-all]
14341433

14351434
# Build with the same Go version that we're using for tests.
14361435
- name: build
@@ -2122,7 +2121,7 @@ buildvariants:
21222121
tags: ["pullrequest"]
21232122
display_name: "Compile Only Checks"
21242123
run_on:
2125-
- rhel8.7-small
2124+
- ubuntu2204-small
21262125
expansions:
21272126
GO_DIST: "/opt/golang/go1.23"
21282127
tasks:

Taskfile.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ tasks:
3030

3131
build-compile-check: bash etc/compile_check.sh
3232

33+
build-compile-check-all: bash etc/run-compile-check-test.sh
34+
3335
build-aws-ecs-test: go build ${BUILD_TAGS} ./internal/cmd/testaws/main.go
3436

3537
cross-compile:

etc/compile_check.sh

Lines changed: 28 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -2,56 +2,47 @@
22
set -e # exit when any command fails
33
set -x # show all commands being run
44

5-
# Default to Go 1.18 if GO_VERSION is not set.
6-
#
7-
# Use the "=" operator (instead of the more common ":-" operator) so that it
8-
# allows setting GO_VERSION="" to use the Go installation in the PATH, and it
9-
# sets the GO_VERSION variable if the default is used.
10-
GC=go${GO_VERSION="1.18"}
5+
: ${GC:=go${GO_VERSION="1.18"}}
6+
117
COMPILE_CHECK_DIR="internal/cmd/compilecheck"
8+
ARCHITECTURES=("386" "arm" "arm64" "ppc64le" "s390x")
9+
BUILD_CMD="${GC} build -buildvcs=false"
1210

1311
# compile_check will attempt to build the internal/test/compilecheck project
1412
# using the provided Go version. This is to simulate an end-to-end use case.
1513
function compile_check {
16-
# Change the directory to the compilecheck test directory.
17-
pushd ${COMPILE_CHECK_DIR}
18-
19-
# If a custom Go version is set using the GO_VERSION env var (e.g. "1.18"),
20-
# add the GOPATH bin directory to PATH and then install that Go version.
21-
if [ ! -z "$GO_VERSION" ]; then
22-
PATH=$(go env GOPATH)/bin:$PATH
23-
export PATH
14+
# Change the directory to the compilecheck test directory.
15+
pushd "${COMPILE_CHECK_DIR}" >/dev/null
2416

25-
go install golang.org/dl/go$GO_VERSION@latest
26-
${GC} download
27-
fi
17+
# If a custom Go version is set using the GO_VERSION env var (e.g. "1.18"),
18+
# add the GOPATH bin directory to PATH and then install that Go version.
19+
if [ ! -z "$GO_VERSION" ]; then
20+
PATH=$(go env GOPATH)/bin:$PATH
21+
export PATH
2822

29-
${GC} version
30-
${GC} mod tidy
23+
go install golang.org/dl/go$GO_VERSION@latest
24+
${GC} download
25+
fi
3126

32-
# Check simple build.
33-
${GC} build ./...
27+
${GC} version
28+
${GC} mod tidy
3429

35-
# Check build with dynamic linking.
36-
${GC} build -buildmode=plugin
30+
# Standard build
31+
$BUILD_CMD ./...
3732

38-
# Check build with tags.
39-
${GC} build $BUILD_TAGS ./...
33+
# Dynamic linking
34+
$BUILD_CMD -buildmode=plugin
4035

41-
# Check build with various architectures.
42-
GOOS=linux GOARCH=386 ${GC} build ./...
43-
GOOS=linux GOARCH=arm ${GC} build ./...
44-
GOOS=linux GOARCH=arm64 ${GC} build ./...
45-
GOOS=linux GOARCH=amd64 ${GC} build ./...
46-
GOOS=linux GOARCH=ppc64le ${GC} build ./...
47-
GOOS=linux GOARCH=s390x ${GC} build ./...
36+
# Check build with tags.
37+
[[ -n "$BUILD_TAGS" ]] && $BUILD_CMD $BUILD_TAGS ./...
4838

49-
# Remove the binaries.
50-
rm compilecheck
51-
rm compilecheck.so
39+
# Check build with various architectures.
40+
for ARCH in "${ARCHITECTURES[@]}"; do
41+
GOOS=linux GOARCH=$ARCH $BUILD_CMD ./...
42+
done
5243

53-
# Change the directory back to the working directory.
54-
popd
44+
# Change the directory back to the working directory.
45+
popd >/dev/null
5546
}
5647

5748
compile_check

etc/run-compile-check-test.sh

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
#!/usr/bin/env bash
2+
# run-compile-check-test
3+
# Run compile check tests.
4+
set -eu
5+
set +x
6+
7+
echo "Running internal/test/compilecheck"
8+
pushd internal/test/compilecheck
9+
GOWORK=off go test -timeout 30m -v ./... >>../../../test.suite
10+
popd
Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
// Copyright (C) MongoDB, Inc. 2025-present.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License"); you may
4+
// not use this file except in compliance with the License. You may obtain
5+
// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
6+
7+
package main
8+
9+
import (
10+
"context"
11+
"encoding/json"
12+
"fmt"
13+
"io"
14+
"net/http"
15+
"os"
16+
"path/filepath"
17+
"strings"
18+
"testing"
19+
20+
"github.com/stretchr/testify/assert"
21+
"github.com/stretchr/testify/require"
22+
"github.com/testcontainers/testcontainers-go"
23+
"golang.org/x/mod/semver"
24+
)
25+
26+
// TODO(GODRIVER-3515): This module cannot be included in the workspace since it
27+
// requires a version of klauspost/compress that is not compatible with the Go
28+
// Driver. Must use GOWORK=off to run this test.
29+
30+
const minSupportedVersion = "1.18"
31+
32+
func TestCompileCheck(t *testing.T) {
33+
cwd, err := os.Getwd()
34+
require.NoError(t, err)
35+
36+
rootDir := filepath.Dir(filepath.Dir(filepath.Dir(cwd)))
37+
38+
versions, err := getDockerGolangImages()
39+
require.NoError(t, err)
40+
41+
for _, version := range versions {
42+
version := version // Capture range variable.
43+
44+
image := fmt.Sprintf("golang:%s", version)
45+
t.Run(image, func(t *testing.T) {
46+
t.Parallel()
47+
48+
req := testcontainers.ContainerRequest{
49+
Image: image,
50+
Cmd: []string{"tail", "-f", "/dev/null"},
51+
Mounts: []testcontainers.ContainerMount{
52+
testcontainers.BindMount(rootDir, "/workspace"),
53+
},
54+
WorkingDir: "/workspace",
55+
Env: map[string]string{
56+
"GC": "go",
57+
// Compilation modules are not part of the workspace as testcontainers requires
58+
// a version of klauspost/compress not supported by the Go Driver / other modules
59+
// in the workspace.
60+
"GOWORK": "off",
61+
},
62+
}
63+
64+
genReq := testcontainers.GenericContainerRequest{
65+
ContainerRequest: req,
66+
Started: true,
67+
}
68+
69+
container, err := testcontainers.GenericContainer(context.Background(), genReq)
70+
require.NoError(t, err)
71+
72+
defer func() {
73+
err := container.Terminate(context.Background())
74+
require.NoError(t, err)
75+
}()
76+
77+
exitCode, outputReader, err := container.Exec(context.Background(), []string{"bash", "etc/compile_check.sh"})
78+
require.NoError(t, err)
79+
80+
output, err := io.ReadAll(outputReader)
81+
require.NoError(t, err)
82+
83+
t.Logf("output: %s", output)
84+
assert.Equal(t, 0, exitCode)
85+
})
86+
}
87+
}
88+
89+
// getDockerGolangImages retrieves the available Golang Docker image tags from
90+
// Docker Hub that are considered valid and meet the specified version
91+
// condition. It returns a slice of version strings in the MajorMinor format and
92+
// an error, if any.
93+
func getDockerGolangImages() ([]string, error) {
94+
// URL to fetch the Golang tags from Docker Hub with a page size of 100
95+
// records.
96+
var url = "https://hub.docker.com/v2/repositories/library/golang/tags?page_size=100"
97+
98+
versionSet := map[string]bool{}
99+
versions := []string{}
100+
101+
// Iteratively fetch and process tags from Docker Hub as long as there is a
102+
// valid next page URL.
103+
for url != "" {
104+
resp, err := http.Get(url)
105+
if err != nil {
106+
return nil, fmt.Errorf("failed to get response from Docker Hub: %w", err)
107+
}
108+
109+
var data struct {
110+
Results []struct {
111+
Name string `json:"name"`
112+
} `json:"results"`
113+
Next string `json:"next"` // URL of the next page for pagination.
114+
}
115+
116+
if err := json.NewDecoder(resp.Body).Decode(&data); err != nil {
117+
resp.Body.Close()
118+
119+
return nil, fmt.Errorf("failed to decode response Body from Docker Hub: %w", err)
120+
}
121+
122+
resp.Body.Close()
123+
124+
for _, tag := range data.Results {
125+
// Skip tags that don't start with a digit (typically version numbers).
126+
if len(tag.Name) == 0 || tag.Name[0] < '0' || tag.Name[0] > '9' {
127+
continue
128+
}
129+
130+
// Split the tag name and extract the base version part.
131+
// This handles tags like `1.18.1-alpine` by extracting `1.18.1`.
132+
base := strings.Split(tag.Name, "-")[0]
133+
134+
// Reduce the base version to MajorMinor format (e.g., `v1.18`).
135+
baseMajMin := semver.MajorMinor("v" + base)
136+
if !semver.IsValid(baseMajMin) || versionSet[baseMajMin] {
137+
continue
138+
}
139+
140+
if semver.Compare(baseMajMin, "v"+minSupportedVersion) >= 0 {
141+
versionSet[baseMajMin] = true
142+
versions = append(versions, baseMajMin[1:])
143+
}
144+
}
145+
146+
// Move to the next page of results, set by the `Next` field.
147+
url = data.Next
148+
}
149+
150+
return versions, nil
151+
}

internal/test/compilecheck/go.mod

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
module go.mongodb.go/mongo-driver/internal/test/compilecheck
2+
3+
go 1.23.0
4+
5+
toolchain go1.23.1
6+
7+
require (
8+
github.com/stretchr/testify v1.10.0
9+
github.com/testcontainers/testcontainers-go v0.35.0
10+
golang.org/x/mod v0.24.0
11+
)
12+
13+
require (
14+
dario.cat/mergo v1.0.0 // indirect
15+
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect
16+
github.com/Microsoft/go-winio v0.6.2 // indirect
17+
github.com/cenkalti/backoff/v4 v4.2.1 // indirect
18+
github.com/containerd/containerd v1.7.18 // indirect
19+
github.com/containerd/log v0.1.0 // indirect
20+
github.com/containerd/platforms v0.2.1 // indirect
21+
github.com/cpuguy83/dockercfg v0.3.2 // indirect
22+
github.com/davecgh/go-spew v1.1.1 // indirect
23+
github.com/distribution/reference v0.6.0 // indirect
24+
github.com/docker/docker v27.1.1+incompatible // indirect
25+
github.com/docker/go-connections v0.5.0 // indirect
26+
github.com/docker/go-units v0.5.0 // indirect
27+
github.com/felixge/httpsnoop v1.0.4 // indirect
28+
github.com/go-logr/logr v1.4.1 // indirect
29+
github.com/go-logr/stdr v1.2.2 // indirect
30+
github.com/go-ole/go-ole v1.2.6 // indirect
31+
github.com/gogo/protobuf v1.3.2 // indirect
32+
github.com/google/uuid v1.6.0 // indirect
33+
github.com/klauspost/compress v1.17.4 // indirect
34+
github.com/kr/text v0.2.0 // indirect
35+
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
36+
github.com/magiconair/properties v1.8.7 // indirect
37+
github.com/moby/docker-image-spec v1.3.1 // indirect
38+
github.com/moby/patternmatcher v0.6.0 // indirect
39+
github.com/moby/sys/sequential v0.5.0 // indirect
40+
github.com/moby/sys/user v0.1.0 // indirect
41+
github.com/moby/term v0.5.0 // indirect
42+
github.com/morikuni/aec v1.0.0 // indirect
43+
github.com/opencontainers/go-digest v1.0.0 // indirect
44+
github.com/opencontainers/image-spec v1.1.0 // indirect
45+
github.com/pkg/errors v0.9.1 // indirect
46+
github.com/pmezard/go-difflib v1.0.0 // indirect
47+
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
48+
github.com/shirou/gopsutil/v3 v3.23.12 // indirect
49+
github.com/shoenig/go-m1cpu v0.1.6 // indirect
50+
github.com/sirupsen/logrus v1.9.3 // indirect
51+
github.com/tklauser/go-sysconf v0.3.12 // indirect
52+
github.com/tklauser/numcpus v0.6.1 // indirect
53+
github.com/yusufpapurcu/wmi v1.2.3 // indirect
54+
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 // indirect
55+
go.opentelemetry.io/otel v1.24.0 // indirect
56+
go.opentelemetry.io/otel/metric v1.24.0 // indirect
57+
go.opentelemetry.io/otel/trace v1.24.0 // indirect
58+
golang.org/x/crypto v0.31.0 // indirect
59+
golang.org/x/net v0.26.0 // indirect
60+
golang.org/x/sys v0.28.0 // indirect
61+
google.golang.org/genproto/googleapis/api v0.0.0-20240318140521-94a12d6c2237 // indirect
62+
google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237 // indirect
63+
gopkg.in/yaml.v3 v3.0.1 // indirect
64+
)

0 commit comments

Comments
 (0)