Skip to content

Commit bc7e152

Browse files
authored
🎁 Buoy will float to branch for submodules other then root (#37)
* Collecting information about repository submodule * Avoid to float to tags for non-root submodules within repo * Add docs on using tags for Golang sub-modules
1 parent 703489d commit bc7e152

11 files changed

Lines changed: 212 additions & 41 deletions

File tree

buoy/README.md

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -121,15 +121,15 @@ knative.dev/eventing-github not ready for release because of the following depen
121121
If you need to see a more verbose output, use `--verbose`:
122122

123123
```
124-
$ bouy check $HOME/go/src/knative.dev/eventing-github/go.mod --domain knative.dev --release 0.18 --verbose
124+
$ buoy check $HOME/go/src/knative.dev/eventing-github/go.mod --domain knative.dev --release 0.18 --verbose
125125
knative.dev/eventing-github
126126
✔ knative.dev/eventing@v0.18.0
127127
✔ knative.dev/pkg@release-0.18
128128
✔ knative.dev/serving@v0.18.0
129129
✔ knative.dev/test-infra@release-0.18
130130
[exit status 0]
131131
132-
$ bouy check $HOME/go/src/knative.dev/eventing-github/go.mod --domain knative.dev --release 0.19 --verbose
132+
$ buoy check $HOME/go/src/knative.dev/eventing-github/go.mod --domain knative.dev --release 0.19 --verbose
133133
knative.dev/eventing-github
134134
✘ knative.dev/eventing@master
135135
✘ knative.dev/pkg@master
@@ -161,6 +161,12 @@ Rulesets,
161161
162162
For rulesets that that restrict the selection process, no ref is selected.
163163
164+
NOTE: For Go modules that don't live on the root of their repository, the tagged
165+
releases are skipped. Golang requires the tags for sub-modules to be in form of
166+
"sub/v1.99.0" to be recognized by "go get" command. It's cumbersome to have
167+
both "v1.99.0" and "sub/v1.99.0", so float will skip looking for tags in this
168+
case.
169+
164170
Usage:
165171
buoy float go.mod [flags]
166172

buoy/commands/float.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,12 @@ Rulesets,
5757
ReleaseOrBranch tagged releases, release branch
5858
5959
For rulesets that that restrict the selection process, no ref is selected.
60+
61+
NOTE: For Go modules that don't live on the root of their repository, the tagged
62+
releases are skipped. Golang requires the tags for sub-modules to be in form of
63+
"sub/v1.99.0" to be recognized by "go get" command. It's cumbersome to have
64+
both "v1.99.0" and "sub/v1.99.0", so float will skip looking for tags in this
65+
case.
6066
`,
6167
Args: cobra.ExactArgs(1),
6268
PreRunE: func(cmd *cobra.Command, args []string) error {

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
module knative.dev/toolbox
22

3-
go 1.21
3+
go 1.22.7
44

55
require (
66
cloud.google.com/go/storage v1.39.1

go.work

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
go 1.21
1+
go 1.22.7
22

33
use (
44
.

magetasks/go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
module knative.dev/toolbox/magetasks
22

3-
go 1.21
3+
go 1.22.7
44

55
require (
66
github.com/blang/semver/v4 v4.0.0

pkg/git/git.go

Lines changed: 34 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -27,18 +27,21 @@ import (
2727
)
2828

2929
// Repo is a simplified git remote, containing only the list of tags, default
30-
// branch and branches.
30+
// branch, branches, URL and submodule information.
3131
type Repo struct {
3232
Ref string
3333
DefaultBranch string
3434
Tags []string
3535
Branches []string
36+
Submodule string
37+
URL string
3638
}
3739

3840
// GetRepo will fetch a git repo and process it into a Repo object.
3941
func GetRepo(ref, url string) (*Repo, error) {
4042
repo := new(Repo)
4143
repo.Ref = ref
44+
repo.URL = url
4245

4346
rem := git.NewRemote(memory.NewStorage(), &config.RemoteConfig{
4447
Name: "origin",
@@ -91,28 +94,17 @@ func (rt RefType) String() string {
9194
}
9295

9396
// BestRefFor Returns module@ref, isRelease based on the provided ruleset for
94-
// a this release.
97+
// this release.
9598
func (r *Repo) BestRefFor(release, moduleRelease semver.Version, ruleset RulesetType) (string, RefType) {
96-
switch ruleset {
97-
case AnyRule, ReleaseOrReleaseBranchRule, ReleaseRule:
98-
var largest *semver.Version
99-
// Look for a release.
100-
for _, t := range r.Tags {
101-
if sv, ok := normalizeTagVersion(t); ok {
102-
v, _ := semver.Make(sv)
103-
// Go does not understand how to fetch semver tags with pre or build tags, skip those.
104-
if v.Pre != nil || v.Build != nil {
105-
continue
106-
}
107-
if v.Major == moduleRelease.Major && v.Minor == moduleRelease.Minor {
108-
if largest == nil || largest.LT(v) {
109-
largest = &v
110-
}
111-
}
99+
// don't look for tags if given module lives within repo as non-root member
100+
if r.Submodule == "" {
101+
switch ruleset {
102+
case AnyRule, ReleaseOrReleaseBranchRule, ReleaseRule:
103+
largest := r.lookForRelease(moduleRelease)
104+
if largest != nil {
105+
return fmt.Sprintf("%s@%s", r.Ref, ReleaseVersion(*largest)), ReleaseRef
112106
}
113-
}
114-
if largest != nil {
115-
return fmt.Sprintf("%s@%s", r.Ref, ReleaseVersion(*largest)), ReleaseRef
107+
default:
116108
}
117109
}
118110

@@ -134,18 +126,39 @@ func (r *Repo) BestRefFor(release, moduleRelease semver.Version, ruleset Ruleset
134126
if largest != nil {
135127
return fmt.Sprintf("%s@%s", r.Ref, ReleaseBranchVersion(*largest)), ReleaseBranchRef
136128
}
129+
default:
137130
}
138131

139132
switch ruleset {
140133
case AnyRule:
141134
// Look for a Return default branch.
142135
return fmt.Sprintf("%s@%s", r.Ref, r.DefaultBranch), DefaultBranchRef
136+
default:
143137
}
144138

145139
// No ref found with the provided rule
146140
return r.Ref, NoRef
147141
}
148142

143+
func (r *Repo) lookForRelease(moduleRelease semver.Version) *semver.Version {
144+
var largest *semver.Version
145+
for _, t := range r.Tags {
146+
if sv, ok := normalizeTagVersion(t); ok {
147+
v, _ := semver.Make(sv)
148+
// Go does not understand how to fetch semver tags with pre or build tags, skip those.
149+
if v.Pre != nil || v.Build != nil {
150+
continue
151+
}
152+
if v.Major == moduleRelease.Major && v.Minor == moduleRelease.Minor {
153+
if largest == nil || largest.LT(v) {
154+
largest = &v
155+
}
156+
}
157+
}
158+
}
159+
return largest
160+
}
161+
149162
func normalizeTagVersion(v string) (string, bool) {
150163
if strings.HasPrefix(v, "v") {
151164
// No need to account for unicode widths.

pkg/git/git_test.go

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222
"github.com/blang/semver/v4"
2323
fixtures "github.com/go-git/go-git-fixtures/v4"
2424
"github.com/google/go-cmp/cmp"
25+
"github.com/stretchr/testify/assert"
2526
)
2627

2728
func TestGetRepo_BasicOne(t *testing.T) {
@@ -54,15 +55,17 @@ func TestGetRepo_Error(t *testing.T) {
5455
}
5556

5657
func TestRepo_BestRefFor(t *testing.T) {
57-
repo := &Repo{
58+
repo := Repo{
5859
Ref: "ref",
5960
DefaultBranch: "main",
6061
Tags: []string{"v0.1.0", "bar", "v0.2.0", "baz", "v0.2.1", "v0.2.2-rc.1", "v0.2.2+build", "foo"},
6162
Branches: []string{"release-0.1", "bar", "release-0.2", "baz", "main", "release-0.3", "release-1.0"},
6263
}
64+
submoduleRepo := repo
65+
submoduleRepo.Submodule = "sub"
6366

6467
tests := map[string]struct {
65-
repo *Repo
68+
repo Repo
6669
version semver.Version
6770
moduleVersion semver.Version
6871
want string
@@ -83,6 +86,13 @@ func TestRepo_BestRefFor(t *testing.T) {
8386
release: ReleaseRef,
8487
rule: AnyRule,
8588
},
89+
"Any - v0.2 - with submodule": {
90+
repo: submoduleRepo,
91+
version: semver.MustParse("0.2.0"),
92+
want: "ref@release-0.2",
93+
release: ReleaseBranchRef,
94+
rule: AnyRule,
95+
},
8696
"Any - v0.3": {
8797
repo: repo,
8898
version: semver.MustParse("0.3.0"),
@@ -120,6 +130,13 @@ func TestRepo_BestRefFor(t *testing.T) {
120130
release: ReleaseRef,
121131
rule: ReleaseOrReleaseBranchRule,
122132
},
133+
"ReleaseOrReleaseBranch - v0.2 - Submodule": {
134+
repo: submoduleRepo,
135+
version: semver.MustParse("0.2.0"),
136+
want: "ref@release-0.2",
137+
release: ReleaseBranchRef,
138+
rule: ReleaseOrReleaseBranchRule,
139+
},
123140
"ReleaseOrReleaseBranch - v0.3": {
124141
repo: repo,
125142
version: semver.MustParse("0.3.0"),
@@ -165,6 +182,13 @@ func TestRepo_BestRefFor(t *testing.T) {
165182
release: ReleaseRef,
166183
rule: ReleaseRule,
167184
},
185+
"Release - v0.2 - Submodule": {
186+
repo: submoduleRepo,
187+
version: semver.MustParse("0.2.0"),
188+
want: "ref",
189+
release: NoRef,
190+
rule: ReleaseRule,
191+
},
168192
"Release - v0.3": {
169193
repo: repo,
170194
version: semver.MustParse("0.3.0"),
@@ -231,12 +255,8 @@ func TestRepo_BestRefFor(t *testing.T) {
231255
tt.moduleVersion = tt.version
232256
}
233257
got, release := tt.repo.BestRefFor(tt.version, tt.moduleVersion, tt.rule)
234-
if got != tt.want {
235-
t.Errorf("repo.BestRefFor() got ref = %v, want %v", got, tt.want)
236-
}
237-
if release != tt.release {
238-
t.Errorf("repo.BestRefFor() got isRelease = %v, want %v", got, tt.want)
239-
}
258+
assert.Equal(t, tt.want, got)
259+
assert.Equal(t, tt.release, release)
240260
})
241261
}
242262
}

pkg/golang/goimport.go

Lines changed: 36 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -71,10 +71,21 @@ func metaContent(doc *html.Node, name string) (string, error) {
7171
return "", fmt.Errorf("missing <meta name=%s> in the node tree", name)
7272
}
7373

74+
// Importer imports the metadata for given URL of a Go module.
75+
type Importer struct {
76+
*http.Client
77+
}
78+
79+
var defaultImporter = Importer{}
80+
7481
// GetMetaImport fetches and parses header tags named go-import into a
7582
// MetaImport object.
76-
func GetMetaImport(url string) (*MetaImport, error) {
77-
resp, err := http.Get(url)
83+
func (i Importer) GetMetaImport(url string) (*MetaImport, error) {
84+
cl := i.Client
85+
if cl == nil {
86+
cl = http.DefaultClient
87+
}
88+
resp, err := cl.Get(url)
7889
if err != nil {
7990
return nil, err
8091
}
@@ -98,7 +109,8 @@ func GetMetaImport(url string) (*MetaImport, error) {
98109
}
99110

100111
// ModuleToRepo resolves a go module name to a remote git repo.
101-
func ModuleToRepo(module string) (*git.Repo, error) {
112+
func (i Importer) ModuleToRepo(module string) (*git.Repo, error) {
113+
originalModule := module
102114
// If module has more than 1 domain + 2 paths (e.g. github.com/blang/semver/v4),
103115
// make it 1 domain + 2 paths. Otherwise, it fails to fetch go import on GetMetaImport().
104116
m := strings.Split(module, "/")
@@ -107,7 +119,7 @@ func ModuleToRepo(module string) (*git.Repo, error) {
107119
}
108120

109121
url := fmt.Sprintf("https://%s?go-get=1", module)
110-
meta, err := GetMetaImport(url)
122+
meta, err := i.GetMetaImport(url)
111123
if err != nil {
112124
return nil, fmt.Errorf("unable to fetch go import %s: %w", url, err)
113125
}
@@ -116,5 +128,24 @@ func ModuleToRepo(module string) (*git.Repo, error) {
116128
return nil, errors.New("unknown VCS: " + meta.VCS)
117129
}
118130

119-
return git.GetRepo(module, meta.RepoRoot)
131+
repo, err := git.GetRepo(module, meta.RepoRoot)
132+
if err != nil {
133+
return nil, err
134+
}
135+
if meta.Prefix != originalModule {
136+
repo.Submodule = strings.TrimPrefix(
137+
strings.TrimPrefix(originalModule, meta.Prefix), "/")
138+
}
139+
return repo, nil
140+
}
141+
142+
// GetMetaImport fetches and parses header tags named go-import into a
143+
// MetaImport object.
144+
func GetMetaImport(url string) (*MetaImport, error) {
145+
return defaultImporter.GetMetaImport(url)
146+
}
147+
148+
// ModuleToRepo resolves a go module name to a remote git repo.
149+
func ModuleToRepo(module string) (*git.Repo, error) {
150+
return defaultImporter.ModuleToRepo(module)
120151
}

0 commit comments

Comments
 (0)