Skip to content

Commit 7f51c3a

Browse files
erock2112SkCQ
authored andcommitted
[autoroll Add README.chromium update capability
Bug: b/462200742 Change-Id: I5bac2c5c61db2a917c1547e2126c75345aee90f5 Reviewed-on: https://skia-review.googlesource.com/c/buildbot/+/1152996 Reviewed-by: Kaylee Lubick <[email protected]> Commit-Queue: Eric Boren <[email protected]>
1 parent dbdda0c commit 7f51c3a

6 files changed

Lines changed: 685 additions & 0 deletions

File tree

autoroll/go/repo_manager/common/version_file_common/BUILD.bazel

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,10 @@ go_library(
1212
"//autoroll/go/revision",
1313
"//go/bazel",
1414
"//go/depot_tools/deps_parser",
15+
"//go/readme_chromium",
1516
"//go/skerr",
1617
"//go/sklog",
18+
"//go/util",
1719
],
1820
)
1921

autoroll/go/repo_manager/common/version_file_common/version_file_common.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package version_file_common
22

33
import (
44
"context"
5+
"path"
56
"regexp"
67
"strings"
78

@@ -10,8 +11,10 @@ import (
1011
"go.skia.org/infra/autoroll/go/revision"
1112
"go.skia.org/infra/go/bazel"
1213
"go.skia.org/infra/go/depot_tools/deps_parser"
14+
"go.skia.org/infra/go/readme_chromium"
1315
"go.skia.org/infra/go/skerr"
1416
"go.skia.org/infra/go/sklog"
17+
"go.skia.org/infra/go/util"
1518
)
1619

1720
func getUsingRegex(dep *config.VersionFileConfig_File, contents string) ([]string, []string, error) {
@@ -54,6 +57,12 @@ func getPinnedRevInFile(id string, file *config.VersionFileConfig_File, contents
5457
return "", skerr.Wrap(err)
5558
}
5659
return depsEntry.Version, nil
60+
} else if path.Base(file.Path) == readme_chromium.FileName {
61+
rcf, err := readme_chromium.Parse([]byte(contents))
62+
if err != nil {
63+
return "", skerr.Wrap(err)
64+
}
65+
return rcf.Revision, nil
5766
} else if strings.HasSuffix(file.Path, ".pyl") {
5867
return pyl.Get(contents, id)
5968
} else if bazel.IsBazelFile(file.Path) {
@@ -120,6 +129,26 @@ func setPinnedRevInFile(id string, dep *config.VersionFileConfig_File, newRev *r
120129
} else if dep.Path == deps_parser.DepsFileName {
121130
newContents, err := deps_parser.SetDep(oldContents, id, newRev.Id)
122131
return newContents, skerr.Wrap(err)
132+
} else if path.Base(dep.Path) == readme_chromium.FileName {
133+
rcf, err := readme_chromium.Parse([]byte(oldContents))
134+
if err != nil {
135+
return "", skerr.Wrap(err)
136+
}
137+
rcf.Revision = newRev.Id
138+
rcf.Version = "N/A"
139+
if newRev.Release != "" {
140+
rcf.Version = newRev.Release
141+
}
142+
rcf.Date = ""
143+
if !util.TimeIsZero(newRev.Timestamp) {
144+
rcf.Date = newRev.Timestamp.Format("2006-01-02")
145+
}
146+
rcf.UpdateMechanism = readme_chromium.UpdateMechanism_Autoroll
147+
newContents, err := rcf.NewContent()
148+
if err != nil {
149+
return "", skerr.Wrap(err)
150+
}
151+
return string(newContents), nil
123152
} else if strings.HasSuffix(dep.Path, ".pyl") {
124153
return pyl.Set(oldContents, id, newRev.Id)
125154
} else if bazel.IsBazelFile(dep.Path) {

autoroll/go/repo_manager/common/version_file_common/version_file_common_test.go

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"context"
55
"fmt"
66
"testing"
7+
"time"
78

89
"github.com/stretchr/testify/require"
910
"go.skia.org/infra/autoroll/go/config"
@@ -35,6 +36,28 @@ func TestGetPinnedRev(t *testing.T) {
3536
}`,
3637
"my-rev",
3738
)
39+
test("README.chromium",
40+
&config.VersionFileConfig{
41+
Id: "my-dep",
42+
File: []*config.VersionFileConfig_File{
43+
{
44+
Path: "path/to/README.chromium",
45+
},
46+
},
47+
},
48+
`Name: Abseil
49+
Short Name: absl
50+
URL: https://github.com/abseil/abseil-cpp
51+
License: Apache-2.0
52+
License File: LICENSE
53+
Version: N/A
54+
Revision: 0437a6d16a02455a07bb59da6f08ef01c6a20682
55+
Update Mechanism: Manual
56+
Security Critical: yes
57+
Shipped: yes
58+
`,
59+
"0437a6d16a02455a07bb59da6f08ef01c6a20682",
60+
)
3861
test("PlainVersionFile",
3962
&config.VersionFileConfig{
4063
Id: "my-dep",
@@ -135,6 +158,86 @@ func TestSetPinnedRev(t *testing.T) {
135158
"my-dep-path": "my-dep@new-rev",
136159
}`,
137160
)
161+
test("README.chromium",
162+
&config.VersionFileConfig{
163+
Id: "my-dep",
164+
File: []*config.VersionFileConfig_File{
165+
{
166+
Path: "path/to/README.chromium",
167+
},
168+
},
169+
},
170+
`Name: Abseil
171+
Short Name: absl
172+
URL: https://github.com/abseil/abseil-cpp
173+
License: Apache-2.0
174+
License File: LICENSE
175+
Version: N/A
176+
Revision: 0437a6d16a02455a07bb59da6f08ef01c6a20682
177+
Date: 2025-09-23
178+
Update Mechanism: Manual
179+
Security Critical: yes
180+
Shipped: yes
181+
`,
182+
&revision.Revision{
183+
Id: "new-rev",
184+
Release: "v1.2.3",
185+
Timestamp: time.Date(2026, time.January, 25, 0, 0, 0, 0, time.UTC),
186+
},
187+
`Name: Abseil
188+
Short Name: absl
189+
URL: https://github.com/abseil/abseil-cpp
190+
License: Apache-2.0
191+
License File: LICENSE
192+
Version: v1.2.3
193+
Revision: new-rev
194+
Date: 2026-01-25
195+
Update Mechanism: Autoroll
196+
Security Critical: yes
197+
Shipped: yes
198+
`,
199+
)
200+
201+
test("README.chromium_NoRelease",
202+
&config.VersionFileConfig{
203+
Id: "my-dep",
204+
File: []*config.VersionFileConfig_File{
205+
{
206+
Path: "path/to/README.chromium",
207+
},
208+
},
209+
},
210+
`Name: Abseil
211+
Short Name: absl
212+
URL: https://github.com/abseil/abseil-cpp
213+
License: Apache-2.0
214+
License File: LICENSE
215+
Version: N/A
216+
Revision: 0437a6d16a02455a07bb59da6f08ef01c6a20682
217+
Date: 2025-09-23
218+
Update Mechanism: Manual
219+
Security Critical: yes
220+
Shipped: yes
221+
`,
222+
&revision.Revision{
223+
Id: "new-rev",
224+
Release: "",
225+
Timestamp: time.Date(2026, time.January, 25, 0, 0, 0, 0, time.UTC),
226+
},
227+
`Name: Abseil
228+
Short Name: absl
229+
URL: https://github.com/abseil/abseil-cpp
230+
License: Apache-2.0
231+
License File: LICENSE
232+
Version: N/A
233+
Revision: new-rev
234+
Date: 2026-01-25
235+
Update Mechanism: Autoroll
236+
Security Critical: yes
237+
Shipped: yes
238+
`,
239+
)
240+
138241
test("PlainVersionFile",
139242
&config.VersionFileConfig{
140243
Id: "my-dep",

go/readme_chromium/BUILD.bazel

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
load("@rules_go//go:def.bzl", "go_library")
2+
load("//bazel/go:go_test.bzl", "go_test")
3+
4+
go_library(
5+
name = "readme_chromium",
6+
srcs = ["readme_chromium.go"],
7+
importpath = "go.skia.org/infra/go/readme_chromium",
8+
visibility = ["//visibility:public"],
9+
deps = ["//go/skerr"],
10+
)
11+
12+
go_test(
13+
name = "readme_chromium_test",
14+
srcs = ["readme_chromium_test.go"],
15+
embed = [":readme_chromium"],
16+
deps = ["@com_github_stretchr_testify//require"],
17+
)
Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
package readme_chromium
2+
3+
/*
4+
This package contains utilities for working with README.chromium files.
5+
*/
6+
7+
import (
8+
"bytes"
9+
"fmt"
10+
"reflect"
11+
"regexp"
12+
"strings"
13+
14+
"go.skia.org/infra/go/skerr"
15+
)
16+
17+
const FileName = "README.chromium"
18+
19+
type UpdateMechanism string
20+
21+
const (
22+
UpdateMechanism_Autoroll UpdateMechanism = "Autoroll"
23+
UpdateMechanism_Manual UpdateMechanism = "Manual"
24+
UpdateMechanism_Static UpdateMechanism = "Static"
25+
UpdateMechanism_StaticHardFork UpdateMechanism = "Static.HardFork"
26+
)
27+
28+
// ReadmeChromiumFile represents the contents of a README.chromium file.
29+
type ReadmeChromiumFile struct {
30+
originalContentLines [][]byte
31+
fields []*field
32+
33+
Name string
34+
ShortName string
35+
URL string
36+
Version string
37+
Date string
38+
Revision string
39+
UpdateMechanism UpdateMechanism
40+
License string
41+
LicenseFile string
42+
Shipped bool
43+
SecurityCritical bool
44+
LicenseAndroidCompatible bool
45+
CPEPrefix string
46+
47+
// Note: according to the template[1] we've skipped Description and
48+
// LocalModifications. These are multiline sections which may or may not be
49+
// present, which makes it difficult to parse them. We'll revisit if
50+
// necessary.
51+
// [1] https://chromium.googlesource.com/chromium/src.git/+/main/third_party/README.chromium.template
52+
}
53+
54+
// Parse and return a ReadmeChromiumFile file with the given contents.
55+
func Parse(content []byte) (*ReadmeChromiumFile, error) {
56+
rv := &ReadmeChromiumFile{
57+
originalContentLines: bytes.Split(content, []byte("\n")),
58+
fields: makeFields(),
59+
}
60+
val := reflect.ValueOf(rv).Elem()
61+
for _, f := range rv.fields {
62+
regex := regexForField(f)
63+
for lineNo, line := range rv.originalContentLines {
64+
matches := regex.FindSubmatchIndex(line)
65+
// [startOfOverallMatch, endOfOverallMatch, startOfSubMatch, endOfSubMatch]
66+
if len(matches) == 4 {
67+
f.LineNo = lineNo
68+
f.StartIndex = matches[2]
69+
f.EndIndex = matches[3]
70+
break
71+
}
72+
}
73+
74+
if f.Required && f.LineNo == 0 && f.StartIndex == 0 && f.EndIndex == 0 {
75+
return nil, skerr.Fmt("failed to find field %q using regex %q", f.Name, regex.String())
76+
}
77+
78+
// Assign the field to the return value.
79+
strVal := string(rv.originalContentLines[f.LineNo][f.StartIndex:f.EndIndex])
80+
field := val.FieldByName(strings.ReplaceAll(f.Name, " ", ""))
81+
if !field.IsValid() {
82+
return nil, skerr.Fmt("field %q is invalid", f.Name)
83+
}
84+
if !field.CanSet() {
85+
return nil, skerr.Fmt("field %q cannot be set", f.Name)
86+
}
87+
if field.Kind() == reflect.String {
88+
field.SetString(strVal)
89+
} else if field.Kind() == reflect.Bool {
90+
field.SetBool(strVal == "yes")
91+
}
92+
}
93+
return rv, nil
94+
}
95+
96+
// NewContent returns the updated README.chromium file content, incorporating
97+
// any changes to the fields of the ReadmeChromiumFile.
98+
func (file *ReadmeChromiumFile) NewContent() ([]byte, error) {
99+
val := reflect.ValueOf(file).Elem()
100+
newContentLines := make([][]byte, 0, len(file.originalContentLines))
101+
for _, line := range file.originalContentLines {
102+
newContentLines = append(newContentLines, line)
103+
}
104+
105+
for _, f := range file.fields {
106+
if f.LineNo == 0 && f.StartIndex == 0 && f.EndIndex == 0 {
107+
// This field isn't set, so we can't update it.
108+
continue
109+
}
110+
111+
oldValue := newContentLines[f.LineNo][f.StartIndex:f.EndIndex]
112+
113+
field := val.FieldByName(strings.ReplaceAll(f.Name, " ", ""))
114+
if !field.IsValid() {
115+
return nil, skerr.Fmt("field %q is invalid", f.Name)
116+
}
117+
newValue := field.String()
118+
if field.Kind() == reflect.Bool {
119+
if field.Bool() {
120+
newValue = "yes"
121+
} else {
122+
newValue = "no"
123+
}
124+
}
125+
126+
newContentLines[f.LineNo] = bytes.Replace(newContentLines[f.LineNo], oldValue, []byte(newValue), 1)
127+
}
128+
return bytes.Join(newContentLines, []byte("\n")), nil
129+
}
130+
131+
type field struct {
132+
Name string
133+
LineNo int
134+
StartIndex int
135+
EndIndex int
136+
Required bool
137+
}
138+
139+
func makeFields() []*field {
140+
return []*field{
141+
{Name: "Name", Required: true},
142+
{Name: "Short Name"},
143+
{Name: "URL", Required: true},
144+
{Name: "Version", Required: true},
145+
{Name: "Date"},
146+
{Name: "Revision"},
147+
{Name: "Update Mechanism", Required: true},
148+
{Name: "License", Required: true},
149+
{Name: "License File"},
150+
{Name: "Shipped"},
151+
{Name: "Security Critical"},
152+
{Name: "License Android Compatible"},
153+
{Name: "CPEPrefix"},
154+
}
155+
}
156+
157+
func regexForField(f *field) *regexp.Regexp {
158+
return regexp.MustCompile(fmt.Sprintf(`^%s:\s+(.+)$`, f.Name))
159+
}

0 commit comments

Comments
 (0)