Skip to content

Commit 729ef2f

Browse files
authored
chore: Add tool and make targets to update go version (#5535)
### Brief description of Pull Request I built a small tool with two different commands `pr-1` and `pr-2`, these should be triggered from our make targets `make update-go-version-pr-1 VERSION=1.25.7` and `make update-go-version-pr-2 VERSION=1.25.7`. There is also a diff for some modules that was missed in previous pr:s to update version ### Pull Request Details <!-- Add a more detailed descripion of the Pull Request here, if needed. --> ### Issue(s) fixed by this Pull Request <!-- Uncomment the following line and fill in an issue number if you want a GitHub issue to be closed automatically when this PR gets merged. --> <!-- Fixes #issue_id --> ### Notes to the Reviewer <!-- Add any relevant notes for the reviewers and testers of this PR. --> ### PR Checklist <!-- Remove items that do not apply. For completed items, change [ ] to [x]. --> - [ ] Documentation added - [ ] Tests updated - [ ] Config converters updated
1 parent 2f3190c commit 729ef2f

6 files changed

Lines changed: 296 additions & 4 deletions

File tree

Makefile

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,8 @@
5858
## clean Clean caches and built binaries
5959
## help Displays this message
6060
## info Print Makefile-specific environment variables
61+
## update-go-version-pr-1 Update Go version in build images (use VERSION=1.25.8)
62+
## update-go-version-pr-2 Update Go version in go.mod and Dockerfiles (use VERSION=1.25.8)
6163
##
6264
## Environment variables:
6365
##
@@ -364,6 +366,16 @@ endif
364366
# build-container-cache and clean-build-container-cache are defined in
365367
# Makefile.build-container.
366368

369+
.PHONY: update-go-version-pr-1
370+
update-go-version-pr-1:
371+
@if [ -z "$(VERSION)" ]; then echo "VERSION is required (e.g. make update-build-image VERSION=1.25.8)"; exit 1; fi
372+
cd ./tools && go run ./go-version pr-1 $(VERSION)
373+
374+
.PHONY: update-go-version-pr-2
375+
update-go-version-pr-2:
376+
@if [ -z "$(VERSION)" ]; then echo "VERSION is required (e.g. make update-go-mod VERSION=1.25.8)"; exit 1; fi
377+
cd ./tools && go run ./go-version pr-2 $(VERSION)
378+
367379
.PHONY: clean
368380
clean: clean-dist clean-build-container-cache
369381
rm -rf ./build/*
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
FROM golang:1.24-alpine
1+
FROM golang:1.25.7-alpine
22

33
RUN go install github.com/grafana/grizzly/cmd/grr@v0.7.1
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
FROM golang:1.24-alpine
1+
FROM golang:1.25.7-alpine
22

33
RUN go install github.com/jsonnet-bundler/jsonnet-bundler/cmd/jb@v0.5.1

tools/generate-module-dependencies/go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
module github.com/grafana/replace-generator
22

3-
go 1.25.1
3+
go 1.25.7
44

55
require gopkg.in/yaml.v3 v3.0.1
66

tools/go-version/main.go

Lines changed: 280 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,280 @@
1+
package main
2+
3+
import (
4+
"bytes"
5+
"encoding/json"
6+
"fmt"
7+
"log"
8+
"net/http"
9+
"os"
10+
"os/exec"
11+
"path/filepath"
12+
"regexp"
13+
"strings"
14+
)
15+
16+
func main() {
17+
if len(os.Args) < 3 {
18+
printUsage()
19+
os.Exit(1)
20+
}
21+
22+
command, version := os.Args[1], os.Args[2]
23+
24+
root, err := gitRoot()
25+
if err != nil {
26+
log.Fatalf("failed to get root directory: %s", err)
27+
}
28+
29+
switch command {
30+
case "pr-1":
31+
if err := updateBuildImage(root, version); err != nil {
32+
log.Fatalf("failed to update build image version: %s", err)
33+
}
34+
case "pr-2":
35+
if err := updateGoModFiles(root, version); err != nil {
36+
log.Fatalf("failed to update go.mod files: %s", err)
37+
}
38+
if err := updateDockerFiles(root, version); err != nil {
39+
log.Fatalf("failed to update Dockerfiles: %s", err)
40+
}
41+
if err := bumpBuildImage(root); err != nil {
42+
log.Fatalf("failed to bump build image: %s", err)
43+
}
44+
45+
default:
46+
printUsage()
47+
os.Exit(1)
48+
}
49+
}
50+
51+
func printUsage() {
52+
fmt.Fprintf(os.Stderr, "usage: <pr-1|pr-2> <version>\n")
53+
}
54+
55+
func gitRoot() (string, error) {
56+
cmd := exec.Command("git", "rev-parse", "--show-toplevel")
57+
var out bytes.Buffer
58+
cmd.Stdout = &out
59+
cmd.Stderr = &out
60+
61+
err := cmd.Run()
62+
if err != nil {
63+
return "", fmt.Errorf("not a git repository: %v", err)
64+
}
65+
66+
return strings.TrimSpace(out.String()), nil
67+
}
68+
69+
func updateBuildImage(root string, version string) error {
70+
paths := []string{
71+
".github/workflows/create_build_image.yml",
72+
".github/workflows/check-linux-build-image.yml",
73+
"tools/build-image/windows/Dockerfile",
74+
}
75+
76+
for _, path := range paths {
77+
path = filepath.Join(root, path)
78+
content, err := os.ReadFile(path)
79+
if err != nil {
80+
return fmt.Errorf("failed to read file: %w", err)
81+
}
82+
83+
if err := os.WriteFile(path, replaceDockerGoVersion(content, version), 0644); err != nil {
84+
return fmt.Errorf("failed to update file: %w", err)
85+
}
86+
87+
}
88+
89+
return nil
90+
}
91+
92+
func updateGoModFiles(root, version string) error {
93+
paths, err := getPaths(root, "go.mod", "tools/generate-module-dependencies/testdata")
94+
if err != nil {
95+
return err
96+
}
97+
98+
re := regexp.MustCompile(`(?m)^go 1\.\d+(\.\d+)?\s*$`)
99+
for _, path := range paths {
100+
content, err := os.ReadFile(path)
101+
if err != nil {
102+
return fmt.Errorf("read %s: %w", path, err)
103+
}
104+
105+
newContent := re.ReplaceAllLiteral(content, []byte("go "+version+"\n"))
106+
107+
if err := os.WriteFile(path, newContent, 0644); err != nil {
108+
return fmt.Errorf("write %s: %w", path, err)
109+
}
110+
}
111+
112+
return nil
113+
}
114+
115+
func updateDockerFiles(root, version string) error {
116+
paths, err := getPaths(root, "Dockerfile", "Dockerfile.windows", "tools/build-image")
117+
if err != nil {
118+
return err
119+
}
120+
121+
for _, path := range paths {
122+
content, err := os.ReadFile(path)
123+
if err != nil {
124+
return fmt.Errorf("read %s: %w", path, err)
125+
}
126+
if err := os.WriteFile(path, replaceDockerGoVersion(content, version), 0644); err != nil {
127+
return fmt.Errorf("write %s: %w", path, err)
128+
}
129+
}
130+
131+
return nil
132+
}
133+
134+
func bumpBuildImage(root string) error {
135+
data, err := fetchBuildImageTags()
136+
if err != nil {
137+
return err
138+
}
139+
refs, err := buildImageRefsFromTags(data)
140+
if err != nil {
141+
return err
142+
}
143+
144+
var paths = []string{
145+
"Dockerfile",
146+
".github/workflows/build.yml",
147+
".github/workflows/release-publish-alloy-artifacts.yml",
148+
".github/workflows/test_full.yml",
149+
".github/workflows/publish-alloy-linux.yml",
150+
".github/workflows/test_linux_system_packages.yml",
151+
}
152+
153+
for _, relPath := range paths {
154+
path := filepath.Join(root, relPath)
155+
content, err := os.ReadFile(path)
156+
if err != nil {
157+
if os.IsNotExist(err) {
158+
continue
159+
}
160+
return fmt.Errorf("read %s: %w", relPath, err)
161+
}
162+
newContent := replaceBuildImageRefs(content, refs)
163+
if err := os.WriteFile(path, newContent, 0644); err != nil {
164+
return fmt.Errorf("write %s: %w", relPath, err)
165+
}
166+
}
167+
return nil
168+
}
169+
170+
const dockerHubTagsURL = "https://hub.docker.com/v2/repositories/grafana/alloy-build-image/tags?page_size=2"
171+
172+
type dockerTagsResponse struct {
173+
Results []dockerTag `json:"results"`
174+
}
175+
176+
type dockerTag struct {
177+
Name string `json:"name"`
178+
Digest string `json:"digest"`
179+
}
180+
181+
type buildImageRefs struct {
182+
Default string
183+
Boring string
184+
DefaultTag string
185+
}
186+
187+
func fetchBuildImageTags() (*dockerTagsResponse, error) {
188+
resp, err := http.Get(dockerHubTagsURL)
189+
if err != nil {
190+
return nil, fmt.Errorf("fetch tags: %w", err)
191+
}
192+
defer resp.Body.Close()
193+
if resp.StatusCode != http.StatusOK {
194+
return nil, fmt.Errorf("fetch tags: %s", resp.Status)
195+
}
196+
var data dockerTagsResponse
197+
if err := json.NewDecoder(resp.Body).Decode(&data); err != nil {
198+
return nil, fmt.Errorf("decode tags: %w", err)
199+
}
200+
return &data, nil
201+
}
202+
203+
// buildImageRefsFromTags assumes 2 tags: one default, one boringcrypto.
204+
func buildImageRefsFromTags(data *dockerTagsResponse) (*buildImageRefs, error) {
205+
if len(data.Results) != 2 {
206+
return nil, fmt.Errorf("expected 2 tags, got %d", len(data.Results))
207+
}
208+
var defaultTag, boringTag *dockerTag
209+
for i := range data.Results {
210+
t := &data.Results[i]
211+
if strings.HasSuffix(t.Name, "-boringcrypto") {
212+
boringTag = t
213+
} else {
214+
defaultTag = t
215+
}
216+
}
217+
if defaultTag == nil || boringTag == nil {
218+
return nil, fmt.Errorf("expected one default and one boringcrypto tag")
219+
}
220+
return &buildImageRefs{
221+
Default: "grafana/alloy-build-image:" + defaultTag.Name + "@" + defaultTag.Digest,
222+
Boring: "grafana/alloy-build-image:" + boringTag.Name + "@" + boringTag.Digest,
223+
DefaultTag: "grafana/alloy-build-image:" + defaultTag.Name,
224+
}, nil
225+
}
226+
227+
var (
228+
buildImageWithDigestRE = regexp.MustCompile(`grafana/alloy-build-image:v\d+\.\d+\.\d+@sha256:[a-f0-9]+`)
229+
buildImageBoringDigestRE = regexp.MustCompile(`grafana/alloy-build-image:v\d+\.\d+\.\d+-boringcrypto@sha256:[a-f0-9]+`)
230+
buildImageTagOnlyRE = regexp.MustCompile(`grafana/alloy-build-image:v\d+\.\d+\.\d+(\s|$)`)
231+
)
232+
233+
func replaceBuildImageRefs(content []byte, refs *buildImageRefs) []byte {
234+
out := buildImageBoringDigestRE.ReplaceAllLiteral(content, []byte(refs.Boring))
235+
out = buildImageWithDigestRE.ReplaceAllLiteral(out, []byte(refs.Default))
236+
out = buildImageTagOnlyRE.ReplaceAll(out, []byte(refs.DefaultTag+"$1"))
237+
return out
238+
}
239+
240+
func getPaths(root, pattern string, exclude ...string) ([]string, error) {
241+
var paths []string
242+
err := filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
243+
if err != nil {
244+
return err
245+
}
246+
if info.IsDir() {
247+
if info.Name() == "vendor" {
248+
return filepath.SkipDir
249+
}
250+
return nil
251+
}
252+
253+
if !strings.HasPrefix(info.Name(), pattern) {
254+
return nil
255+
}
256+
257+
rel, err := filepath.Rel(root, path)
258+
if err != nil {
259+
return err
260+
}
261+
262+
for _, ex := range exclude {
263+
if rel == ex || strings.HasPrefix(rel, ex+string(filepath.Separator)) {
264+
return nil
265+
}
266+
}
267+
268+
paths = append(paths, path)
269+
return nil
270+
})
271+
272+
return paths, err
273+
}
274+
275+
var dockerGoVersionRE = regexp.MustCompile(`golang:1\.\d+(\.\d+)?`)
276+
277+
func replaceDockerGoVersion(content []byte, version string) []byte {
278+
out := dockerGoVersionRE.ReplaceAllLiteral(content, []byte("golang:"+version))
279+
return out
280+
}

tools/go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
module github.com/grafana/alloy/tools
22

3-
go 1.25.1
3+
go 1.25.7
44

55
require (
66
github.com/google/go-github/v57 v57.0.0

0 commit comments

Comments
 (0)