Skip to content

Commit b041a95

Browse files
heschigopherbot
authored andcommitted
internal/task: add support for tagging a single repository
To make one-off tagging less error-prone, run a miniature version of the overall workflow to tag a specific repository. Also includes a tiny bug fix for expansions with parameters. For golang/go#48523. Change-Id: Ic9089966aa28d20c716169b41121e2da21faf54a Reviewed-on: https://go-review.googlesource.com/c/build/+/445955 Auto-Submit: Heschi Kreinick <[email protected]> Reviewed-by: Dmitri Shuralyov <[email protected]> TryBot-Result: Gopher Robot <[email protected]> Run-TryBot: Heschi Kreinick <[email protected]> Reviewed-by: Dmitri Shuralyov <[email protected]>
1 parent 193064a commit b041a95

File tree

4 files changed

+146
-44
lines changed

4 files changed

+146
-44
lines changed

Diff for: cmd/relui/main.go

+1
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,7 @@ func main() {
215215
ApproveAction: relui.ApproveActionDep(dbPool),
216216
}
217217
dh.RegisterDefinition("Tag x/ repos", tagTasks.NewDefinition())
218+
dh.RegisterDefinition("Tag a single x/ repo", tagTasks.NewSingleDefinition())
218219

219220
var base *url.URL
220221
if *baseURL != "" {

Diff for: internal/task/tagx.go

+42-9
Original file line numberDiff line numberDiff line change
@@ -45,13 +45,22 @@ func (x *TagXReposTasks) NewDefinition() *wf.Definition {
4545
return wd
4646
}
4747

48+
func (x *TagXReposTasks) NewSingleDefinition() *wf.Definition {
49+
wd := wf.New()
50+
repos := wf.Task0(wd, "Load all repositories", x.SelectRepos)
51+
name := wf.Param(wd, wf.ParamDef[string]{Name: "Repository name", Example: "tools"})
52+
wf.Expand2(wd, "Create single-repo plan", x.BuildSingleRepoPlan, repos, name)
53+
return wd
54+
}
55+
4856
// TagRepo contains information about a repo that can be tagged.
4957
type TagRepo struct {
50-
Name string // Gerrit project name, e.g. "tools".
51-
ModPath string // Module path, e.g. "golang.org/x/tools".
52-
Deps []string // Dependency module paths.
53-
Compat string // The version to pass to go mod tidy -compat for this repository.
54-
Version string // After a tagging decision has been made, the version dependencies should upgrade to.
58+
Name string // Gerrit project name, e.g. "tools".
59+
ModPath string // Module path, e.g. "golang.org/x/tools".
60+
Deps []string // Dependency module paths.
61+
Compat string // The Go version to pass to go mod tidy -compat for this repository.
62+
StartVersion string // The version of the module when the workflow started.
63+
Version string // After a tagging decision has been made, the version dependencies should upgrade to.
5564
}
5665

5766
func (x *TagXReposTasks) SelectRepos(ctx *wf.TaskContext) ([]TagRepo, error) {
@@ -127,8 +136,9 @@ func (x *TagXReposTasks) readRepo(ctx *wf.TaskContext, project string) (*TagRepo
127136
}
128137

129138
result := &TagRepo{
130-
Name: project,
131-
ModPath: mf.Module.Mod.Path,
139+
Name: project,
140+
ModPath: mf.Module.Mod.Path,
141+
StartVersion: tag,
132142
}
133143

134144
compatRe := regexp.MustCompile(`tagx:compat\s+([\d.]+)`)
@@ -251,8 +261,31 @@ func (x *TagXReposTasks) BuildPlan(wd *wf.Definition, repos []TagRepo) error {
251261
return nil
252262
}
253263

254-
// planRepo returns a Value containing the tagged repository's information, or
255-
// nil, false if its dependencies haven't been planned yet.
264+
func (x *TagXReposTasks) BuildSingleRepoPlan(wd *wf.Definition, repoSlice []TagRepo, name string) error {
265+
repos := map[string]TagRepo{}
266+
updatedRepos := map[string]wf.Value[TagRepo]{}
267+
for _, r := range repoSlice {
268+
repos[r.Name] = r
269+
270+
// Pretend that we've just tagged version that was live when we started.
271+
r.Version = r.StartVersion
272+
updatedRepos[r.ModPath] = wf.Const(r)
273+
}
274+
repo, ok := repos[name]
275+
if !ok {
276+
return fmt.Errorf("no repository %q", name)
277+
}
278+
tagged, ok := x.planRepo(wd, repo, updatedRepos)
279+
if !ok {
280+
return fmt.Errorf("%q doesn't have all of its dependencies (%q)", repo.Name, repo.Deps)
281+
}
282+
wf.Output(wd, "tagged repository", tagged)
283+
return nil
284+
}
285+
286+
// planRepo adds tasks to wf to update and tag repo. It returns a Value
287+
// containing the tagged repository's information, or nil, false if its
288+
// dependencies haven't been planned yet.
256289
func (x *TagXReposTasks) planRepo(wd *wf.Definition, repo TagRepo, updated map[string]wf.Value[TagRepo]) (_ wf.Value[TagRepo], ready bool) {
257290
var deps []wf.Value[TagRepo]
258291
for _, repoDeps := range repo.Deps {

Diff for: internal/task/tagx_test.go

+102-35
Original file line numberDiff line numberDiff line change
@@ -334,48 +334,35 @@ case "$1" in
334334
esac
335335
`
336336

337-
func TestTagXRepos(t *testing.T) {
337+
type tagXTestDeps struct {
338+
ctx context.Context
339+
gerrit *FakeGerrit
340+
tagXTasks *TagXReposTasks
341+
}
342+
343+
func newTagXTestDeps(t *testing.T, repos ...*FakeRepo) *tagXTestDeps {
338344
if runtime.GOOS != "linux" && runtime.GOOS != "darwin" {
339345
t.Skip("Requires bash shell scripting support.")
340346
}
341347

348+
ctx, cancel := context.WithCancel(context.Background())
349+
t.Cleanup(cancel)
350+
342351
goServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
343352
ServeTarball("dl/go1.19.linux-amd64.tar.gz", map[string]string{
344353
"go/bin/go": fakeGo,
345354
}, w, r)
346355
}))
347356
t.Cleanup(goServer.Close)
348357

349-
fakeBuildlets := NewFakeBuildlets(t, "")
350-
351358
goRepo := NewFakeRepo(t, "go")
352359
go1 := goRepo.Commit(map[string]string{
353360
"main.go": "I'm the go command or something",
354361
})
355-
sys := NewFakeRepo(t, "sys")
356-
sys1 := sys.Commit(map[string]string{
357-
"go.mod": "module golang.org/x/sys\n",
358-
"go.sum": "\n",
359-
})
360-
sys.Tag("v0.1.0", sys1)
361-
sys2 := sys.Commit(map[string]string{
362-
"main.go": "package main",
363-
})
364-
mod := NewFakeRepo(t, "mod")
365-
mod1 := mod.Commit(map[string]string{
366-
"go.mod": "module golang.org/x/mod\n",
367-
"go.sum": "\n",
368-
})
369-
mod.Tag("v1.0.0", mod1)
370-
tools := NewFakeRepo(t, "tools")
371-
tools1 := tools.Commit(map[string]string{
372-
"go.mod": "module golang.org/x/tools\nrequire golang.org/x/mod v1.0.0\ngo 1.18 // tagx:compat 1.16\nrequire golang.org/x/sys v0.1.0\n",
373-
"go.sum": "\n",
374-
"gopls/go.mod": "module golang.org/x/tools/gopls\nrequire golang.org/x/mod v1.0.0\n",
375-
"gopls/go.sum": "\n",
376-
})
377-
tools.Tag("v1.1.5", tools1)
378-
fakeGerrit := NewFakeGerrit(t, goRepo, sys, mod, tools)
362+
repos = append(repos, goRepo)
363+
364+
fakeBuildlets := NewFakeBuildlets(t, "")
365+
fakeGerrit := NewFakeGerrit(t, repos...)
379366
var builders, allOK []string
380367
for _, b := range dashboard.Builders {
381368
builders = append(builders, b.Name)
@@ -391,7 +378,7 @@ func TestTagXRepos(t *testing.T) {
391378
},
392379
}
393380
}
394-
for _, r := range []*FakeRepo{sys, mod, tools} {
381+
for _, r := range repos {
395382
if repo != "golang.org/x/"+r.name {
396383
continue
397384
}
@@ -427,39 +414,73 @@ func TestTagXRepos(t *testing.T) {
427414
return nil
428415
},
429416
}
430-
wd := tasks.NewDefinition()
417+
return &tagXTestDeps{
418+
ctx: ctx,
419+
gerrit: fakeGerrit,
420+
tagXTasks: tasks,
421+
}
422+
}
423+
424+
func TestTagXRepos(t *testing.T) {
425+
sys := NewFakeRepo(t, "sys")
426+
sys1 := sys.Commit(map[string]string{
427+
"go.mod": "module golang.org/x/sys\n",
428+
"go.sum": "\n",
429+
})
430+
sys.Tag("v0.1.0", sys1)
431+
sys2 := sys.Commit(map[string]string{
432+
"main.go": "package main",
433+
})
434+
mod := NewFakeRepo(t, "mod")
435+
mod1 := mod.Commit(map[string]string{
436+
"go.mod": "module golang.org/x/mod\n",
437+
"go.sum": "\n",
438+
})
439+
mod.Tag("v1.0.0", mod1)
440+
tools := NewFakeRepo(t, "tools")
441+
tools1 := tools.Commit(map[string]string{
442+
"go.mod": "module golang.org/x/tools\nrequire golang.org/x/mod v1.0.0\ngo 1.18 // tagx:compat 1.16\nrequire golang.org/x/sys v0.1.0\n",
443+
"go.sum": "\n",
444+
"gopls/go.mod": "module golang.org/x/tools/gopls\nrequire golang.org/x/mod v1.0.0\n",
445+
"gopls/go.sum": "\n",
446+
})
447+
tools.Tag("v1.1.5", tools1)
448+
449+
deps := newTagXTestDeps(t, sys, mod, tools)
450+
451+
wd := deps.tagXTasks.NewDefinition()
431452
w, err := workflow.Start(wd, nil)
432453
if err != nil {
433454
t.Fatal(err)
434455
}
435-
ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
456+
ctx, cancel := context.WithTimeout(deps.ctx, time.Minute)
436457
defer cancel()
437458
_, err = w.Run(ctx, &verboseListener{t: t})
438459
if err != nil {
439460
t.Fatal(err)
440461
}
441462

442-
tag, err := fakeGerrit.GetTag(ctx, "sys", "v0.2.0")
463+
tag, err := deps.gerrit.GetTag(ctx, "sys", "v0.2.0")
443464
if err != nil {
444465
t.Fatalf("sys should have been tagged with v0.2.0: %v", err)
445466
}
446467
if tag.Revision != sys2 {
447468
t.Errorf("sys v0.2.0 = %v, want %v", tag.Revision, sys2)
448469
}
449470

450-
tags, err := fakeGerrit.ListTags(ctx, "mod")
471+
tags, err := deps.gerrit.ListTags(ctx, "mod")
451472
if err != nil {
452473
t.Fatal(err)
453474
}
454475
if !reflect.DeepEqual(tags, []string{"v1.0.0"}) {
455476
t.Errorf("mod has tags %v, wanted only v1.0.0", tags)
456477
}
457478

458-
tag, err = fakeGerrit.GetTag(ctx, "tools", "v1.2.0")
479+
tag, err = deps.gerrit.GetTag(ctx, "tools", "v1.2.0")
459480
if err != nil {
460481
t.Fatalf("tools should have been tagged with v1.2.0: %v", err)
461482
}
462-
goMod, err := fakeGerrit.ReadFile(ctx, "tools", tag.Revision, "go.mod")
483+
goMod, err := deps.gerrit.ReadFile(ctx, "tools", tag.Revision, "go.mod")
463484
if err != nil {
464485
t.Fatal(err)
465486
}
@@ -469,7 +490,7 @@ func TestTagXRepos(t *testing.T) {
469490
if !strings.Contains(string(goMod), "tidied!") {
470491
t.Error("tools go.mod should be tidied")
471492
}
472-
goplsMod, err := fakeGerrit.ReadFile(ctx, "tools", tag.Revision, "gopls/go.mod")
493+
goplsMod, err := deps.gerrit.ReadFile(ctx, "tools", tag.Revision, "gopls/go.mod")
473494
if err != nil {
474495
t.Fatal(err)
475496
}
@@ -478,6 +499,52 @@ func TestTagXRepos(t *testing.T) {
478499
}
479500
}
480501

502+
func TestTagSingleRepo(t *testing.T) {
503+
mod := NewFakeRepo(t, "mod")
504+
mod1 := mod.Commit(map[string]string{
505+
"go.mod": "module golang.org/x/mod\n",
506+
"go.sum": "\n",
507+
})
508+
mod.Tag("v1.1.0", mod1)
509+
foo := NewFakeRepo(t, "foo")
510+
foo1 := foo.Commit(map[string]string{
511+
"go.mod": "module golang.org/x/foo\nrequire golang.org/x/mod v1.0.0\n",
512+
"go.sum": "\n",
513+
})
514+
foo.Tag("v1.1.5", foo1)
515+
foo.Commit(map[string]string{
516+
"main.go": "package main",
517+
})
518+
519+
deps := newTagXTestDeps(t, mod, foo)
520+
521+
wd := deps.tagXTasks.NewSingleDefinition()
522+
ctx, cancel := context.WithTimeout(deps.ctx, time.Minute)
523+
w, err := workflow.Start(wd, map[string]interface{}{
524+
"Repository name": "foo",
525+
})
526+
if err != nil {
527+
t.Fatal(err)
528+
}
529+
defer cancel()
530+
_, err = w.Run(ctx, &verboseListener{t: t})
531+
if err != nil {
532+
t.Fatal(err)
533+
}
534+
535+
tag, err := deps.gerrit.GetTag(ctx, "foo", "v1.2.0")
536+
if err != nil {
537+
t.Fatalf("foo should have been tagged with v1.2.0: %v", err)
538+
}
539+
goMod, err := deps.gerrit.ReadFile(ctx, "foo", tag.Revision, "go.mod")
540+
if err != nil {
541+
t.Fatal(err)
542+
}
543+
if !strings.Contains(string(goMod), "[email protected]") {
544+
t.Errorf("foo should use mod v1.1.0. go.mod: %v", string(goMod))
545+
}
546+
}
547+
481548
type verboseListener struct {
482549
t *testing.T
483550
outputListener func(string, interface{})

Diff for: internal/workflow/workflow.go

+1
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ func (d *Definition) name(name string) string {
8383

8484
func (d *Definition) shallowClone() *Definition {
8585
clone := New()
86+
clone.parameters = append([]MetaParameter(nil), d.parameters...)
8687
for k, v := range d.tasks {
8788
clone.tasks[k] = v
8889
}

0 commit comments

Comments
 (0)