diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 419807130e6..ea564bca646 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -15,6 +15,12 @@ env: # explicitly test our code for these versions so keeping this at prior # versions does not add value. DEFAULT_GO_VERSION: "~1.24.0" + + # Minimum Go version to use for the project. This should be the minimum + # version of Go that the project supports. This is used to ensure that + # the code is compatible with the minimum version of Go. + # see: https://github.com/open-telemetry/opentelemetry-go-contrib/blob/main/tools/verifygoversion + MINIMUM_GO_VERSION: "go 1.23.0" jobs: lint: runs-on: ubuntu-latest @@ -47,7 +53,7 @@ jobs: - name: Generate run: make generate - name: Run linters - run: make toolchain-check license-check lint vanity-import-check + run: make toolchain-check license-check lint vanity-import-check verify-goversion - name: Build run: make build - name: Check clean repository diff --git a/Makefile b/Makefile index 5a0d611e0fd..9b9c15599f5 100644 --- a/Makefile +++ b/Makefile @@ -22,8 +22,8 @@ DEPENDENCIES_DOCKERFILE=./dependencies.Dockerfile .DEFAULT_GOAL := precommit .PHONY: precommit ci -precommit: generate toolchain-check license-check misspell go-mod-tidy golangci-lint-fix test-default -ci: generate toolchain-check license-check lint vanity-import-check build test-default check-clean-work-tree test-coverage +precommit: generate toolchain-check license-check misspell go-mod-tidy golangci-lint-fix verify-goversion test-default +ci: generate toolchain-check license-check lint vanity-import-check verify-goversion build test-default check-clean-work-tree test-coverage # Tools @@ -73,7 +73,10 @@ $(GOJSONSCHEMA): PACKAGE=github.com/atombender/go-jsonschema GOVULNCHECK = $(TOOLS)/govulncheck $(GOVULNCHECK): PACKAGE=golang.org/x/vuln/cmd/govulncheck -tools: $(GOLANGCI_LINT) $(MISSPELL) $(GOCOVMERGE) $(STRINGER) $(PORTO) $(GOJQ) $(MULTIMOD) $(CROSSLINK) $(GOTMPL) $(GORELEASE) $(GOJSONSCHEMA) $(GOVULNCHECK) +VERIFYGOVERSION = $(TOOLS)/verifygoversion +$(VERIFYGOVERSION): PACKAGE=go.opentelemetry.io/contrib/$(TOOLS_MOD_DIR)/verifygoversion + +tools: $(GOLANGCI_LINT) $(MISSPELL) $(GOCOVMERGE) $(STRINGER) $(PORTO) $(GOJQ) $(MULTIMOD) $(CROSSLINK) $(GOTMPL) $(GORELEASE) $(GOJSONSCHEMA) $(GOVULNCHECK) $(VERIFYGOVERSION) # Virtualized python tools via docker @@ -356,4 +359,8 @@ codespell: $(CODESPELL) MARKDOWNIMAGE := $(shell awk '$$4=="markdown" {print $$2}' $(DEPENDENCIES_DOCKERFILE)) .PHONY: lint-markdown lint-markdown: - docker run --rm -u $(DOCKER_USER) -v "$(CURDIR):$(WORKDIR)" $(MARKDOWNIMAGE) -c $(WORKDIR)/.markdownlint.yaml $(WORKDIR)/**/*.md \ No newline at end of file + docker run --rm -u $(DOCKER_USER) -v "$(CURDIR):$(WORKDIR)" $(MARKDOWNIMAGE) -c $(WORKDIR)/.markdownlint.yaml $(WORKDIR)/**/*.md + +.PHONY: verifygoversion +verify-goversion: $(VERIFYGOVERSION) + $(VERIFYGOVERSION) \ No newline at end of file diff --git a/instrumentation/net/http/httptrace/otelhttptrace/internal/semconv/test/go.mod b/instrumentation/net/http/httptrace/otelhttptrace/internal/semconv/test/go.mod index 06ac658cbd5..c992780520d 100644 --- a/instrumentation/net/http/httptrace/otelhttptrace/internal/semconv/test/go.mod +++ b/instrumentation/net/http/httptrace/otelhttptrace/internal/semconv/test/go.mod @@ -1,6 +1,6 @@ module go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace/internal/semconv/test -go 1.23.6 +go 1.23.0 replace go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace => ../../../ diff --git a/tools/verifygoversion/main.go b/tools/verifygoversion/main.go new file mode 100644 index 00000000000..fa130c32378 --- /dev/null +++ b/tools/verifygoversion/main.go @@ -0,0 +1,92 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 +package main // import "go.opentelemetry.io/contrib/tools/verifygoversion" + +import ( + "errors" + "fmt" + "log" + "os" + "path/filepath" + "strings" +) + +var expectedGoVersion string + +func main() { + expectedGoVersion = os.Getenv("MINIMUM_GO_VERSION") + if expectedGoVersion == "" { + log.Fatal("MINIMUM_GO_VERSION environment variable is not set") + } + + root, err := os.Getwd() + if err != nil { + log.Fatal(err) + } + + log.Println("Current working directory:", root) + + if err := verifyGoVersion(root); err != nil { + os.Exit(1) + } +} + +func verifyGoVersion(root string) error { + var modFiles []string + + err := filepath.Walk(root, func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + + if !info.IsDir() && filepath.Base(path) == "go.mod" { + modFiles = append(modFiles, path) + } + + return nil + }) + if err != nil { + return fmt.Errorf("error walking the path %q: %v", root, err) + } + if len(modFiles) == 0 { + return fmt.Errorf("there is no go.mod file in the current directory") + } + + for _, file := range modFiles { + bytes, err := os.ReadFile(file) + if err != nil { + err = errors.Join(err, fmt.Errorf("error reading %s: %v", file, err)) + } + + content := string(bytes) + if strings.Contains(content, "toolchain") { + err = errors.Join(err, fmt.Errorf("toolchain is not supported in %s", file)) + } + + contents := strings.Split(content, "\n") + goVersionFound := false + + for _, line := range contents { + line = strings.TrimSpace(line) + if strings.HasPrefix(line, "go ") { + goVersionFound = true + if line != expectedGoVersion { + err = errors.Join(err, fmt.Errorf("expected %s in %s, but found %s", expectedGoVersion, file, line)) + } + break + } + } + + if !goVersionFound { + err = errors.Join(err, fmt.Errorf("expected %s in %s, but not found", expectedGoVersion, file)) + } + + if err != nil { + log.Println("Verification failed:", err) + } else { + log.Println("Verification succeeded:", file) + } + } + + return err +}