internal/task: replace reviewers parameter with release coordinators

- Add a check for release coordinators parameter making sure the
  all coordinators have gerrit user name and github user name.
- The first user of the release coordinators will be assigned to the
  release tracking issues.
- All the release coordinators will be added as reviewers for the
  auto-generated CLs.

A local relui screenshot is at https://github.com/golang/vscode-go/issues/3500#issuecomment-2377704665

For golang/vscode-go#3500
For golang/go#57643

Change-Id: If528ab3c5bbc2d589c7198cffa0211f4e5db478f
Reviewed-on: https://go-review.googlesource.com/c/build/+/616195
Reviewed-by: Robert Findley <rfindley@google.com>
Reviewed-by: Hyang-Ah Hana Kim <hyangah@gmail.com>
Reviewed-by: Dmitri Shuralyov <dmitshur@golang.org>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
This commit is contained in:
Hongxiang Jiang 2024-09-26 18:52:46 +00:00
Родитель 959e7b51fa
Коммит 9fd11d6a45
8 изменённых файлов: 63 добавлений и 36 удалений

Просмотреть файл

@ -2680,7 +2680,7 @@ func init() {
addPerson("Taro Aoki", "aizu.s1230022@gmail.com", "@ktr0731")
addPerson("Tarrant", "tarrant@keyneston.com", "@tarrant")
addPerson("Taru Karttunen", "taruti@taruti.net", "@taruti")
addPerson("Tatiana Bradley", "tatiana@golang.org")
addPerson("Tatiana Bradley", "tatiana@golang.org", "@tatianab")
addPerson("Tatsuhiro Tsujikawa", "tatsuhiro.t@gmail.com", "@tatsuhiro-t")
addPerson("Taufiq Rahman", "taufiqrx8@gmail.com", "@Inconnu08")
addPerson("Ted Hahn", "teh@uber.com")

Просмотреть файл

@ -288,7 +288,7 @@ func mapCoordinators(users []string, f func(*gophers.Person) string) ([]string,
}
// CheckCoordinators checks that all users are known
// and have required information (name, Gerrit email).
// and have required information (name, Gerrit email, GitHub user name).
func CheckCoordinators(users []string) error {
var report strings.Builder
for _, user := range users {
@ -309,9 +309,13 @@ func lookupCoordinator(user string) (*gophers.Person, error) {
}
if person == nil {
return nil, fmt.Errorf("unknown username %q: no @golang or @google account", user)
} else if person.Name == "" {
}
if person.Name == "" {
return nil, fmt.Errorf("release coordinator %q is missing a name", person.Gerrit)
}
if person.GitHub == "" {
return nil, fmt.Errorf("release coordinator %q is missing github user name", person.Gerrit)
}
return person, nil
}

Просмотреть файл

@ -76,9 +76,9 @@ func TestPrivXPatch(t *testing.T) {
echo "Resolving deltas: 100% (5/5)"
echo "Waiting for private key checker: 1/1 objects left"
echo "Processing changes: refs: 1, new: 1, done"
echo
echo
echo "SUCCESS"
echo
echo
echo " https://go-review.googlesource.com/c/net/+/558675 net/mail: remove obsolete comment [NEW]"
echo`), 0777); err != nil {
t.Fatalf("failed to write git pre-receive hook: %s", err)
@ -111,7 +111,7 @@ echo`), 0777); err != nil {
wd := p.NewDefinition(&TagXReposTasks{Gerrit: &privxClient{}})
w, err := workflow.Start(wd, map[string]any{
"go-internal CL number": "1234",
"Reviewer usernames (optional)": []string{},
reviewersParam.Name: []string{},
"Repository name": filepath.Base(pubRepo.dir.dir),
"Skip post submit result (optional)": true,
"CVE": "CVE-2024-1234",

Просмотреть файл

@ -19,6 +19,16 @@ import (
"golang.org/x/mod/semver"
)
var releaseCoordinatorsParam = wf.ParamDef[[]string]{
Name: "Release Coordinator Usernames",
ParamType: wf.SliceShort,
Doc: `Release Coordinator Usernames is a required list of the coordinators of the release.
The first user in the list will be assigned to the release tracking issue.
All users will be added as reviewers for automatically generated CLs.`,
Check: CheckCoordinators,
}
// ReleaseGoplsTasks provides workflow definitions and tasks for releasing gopls.
type ReleaseGoplsTasks struct {
Github GitHubClientInterface
@ -41,19 +51,19 @@ func (r *ReleaseGoplsTasks) NewPrereleaseDefinition() *wf.Definition {
// bump strategy.
// Use with caution.
inputVersion := wf.Param(wd, wf.ParamDef[string]{Name: "explicit version (optional)"})
reviewers := wf.Param(wd, reviewersParam)
coordinators := wf.Param(wd, releaseCoordinatorsParam)
release := wf.Task2(wd, "determine the release version", r.determineReleaseVersion, inputVersion, versionBumpStrategy)
prerelease := wf.Task1(wd, "find the next pre-release version", r.nextPrereleaseVersion, release)
approved := wf.Action2(wd, "wait for release coordinator approval", r.approvePrerelease, release, prerelease)
issue := wf.Task2(wd, "create release git issue", r.findOrCreateGitHubIssue, release, wf.Const(true), wf.After(approved))
issue := wf.Task3(wd, "create release git issue", r.findOrCreateGitHubIssue, release, coordinators, wf.Const(true), wf.After(approved))
branchCreated := wf.Action1(wd, "create new branch if minor release", r.createBranchIfMinor, release, wf.After(issue))
configChangeID := wf.Task3(wd, "update branch's codereview.cfg", r.updateCodeReviewConfig, release, reviewers, issue, wf.After(branchCreated))
configChangeID := wf.Task3(wd, "update branch's codereview.cfg", r.updateCodeReviewConfig, release, coordinators, issue, wf.After(branchCreated))
configCommit := wf.Task1(wd, "await config CL submission", clAwaiter{r.Gerrit}.awaitSubmission, configChangeID)
dependencyChangeID := wf.Task4(wd, "update gopls' x/tools dependency", r.updateXToolsDependency, release, prerelease, reviewers, issue, wf.After(configCommit))
dependencyChangeID := wf.Task4(wd, "update gopls' x/tools dependency", r.updateXToolsDependency, release, prerelease, coordinators, issue, wf.After(configCommit))
dependencyCommit := wf.Task1(wd, "await gopls' x/tools dependency CL submission", clAwaiter{r.Gerrit}.awaitSubmission, dependencyChangeID)
verified := wf.Action1(wd, "verify installing latest gopls using release branch dependency commit", r.verifyGoplsInstallation, dependencyCommit)
@ -61,7 +71,7 @@ func (r *ReleaseGoplsTasks) NewPrereleaseDefinition() *wf.Definition {
prereleaseVerified := wf.Action1(wd, "verify installing latest gopls using release branch pre-release version", r.verifyGoplsInstallation, prereleaseVersion)
wf.Action4(wd, "mail announcement", r.mailPrereleaseAnnouncement, release, prereleaseVersion, dependencyCommit, issue, wf.After(prereleaseVerified))
vscodeGoChanges := wf.Task4(wd, "update gopls version in vscode-go", r.updateVSCodeGoGoplsVersion, reviewers, issue, release, prerelease, wf.After(prereleaseVerified))
vscodeGoChanges := wf.Task4(wd, "update gopls version in vscode-go", r.updateVSCodeGoGoplsVersion, coordinators, issue, release, prerelease, wf.After(prereleaseVerified))
_ = wf.Task1(wd, "await gopls version update CLs submission in vscode-go", clAwaiter{r.Gerrit}.awaitSubmissions, vscodeGoChanges)
wf.Output(wd, "version", prereleaseVersion)
@ -144,7 +154,7 @@ func (r *ReleaseGoplsTasks) approveRelease(ctx *wf.TaskContext, release releaseV
// If the release issue exists, return the issue ID.
// If 'create' is true and no issue exists, a new one is created.
// If 'create' is false and no issue exists, an error is returned.
func (r *ReleaseGoplsTasks) findOrCreateGitHubIssue(ctx *wf.TaskContext, release releaseVersion, create bool) (int64, error) {
func (r *ReleaseGoplsTasks) findOrCreateGitHubIssue(ctx *wf.TaskContext, release releaseVersion, coordinators []string, create bool) (int64, error) {
versionString := release.String()
milestoneName := fmt.Sprintf("gopls/%s", versionString)
// All milestones and issues resides under go repo.
@ -186,14 +196,20 @@ func (r *ReleaseGoplsTasks) findOrCreateGitHubIssue(ctx *wf.TaskContext, release
- [ ] tag gopls/%s
- [ ] (if vX.Y.0 release): update dependencies in master for the next release
`, versionString, goplsReleaseBranchName(release), versionString, versionString)
// TODO(hxjiang): accept a new parameter release coordinator.
assignee := "h9jiang"
if len(coordinators) == 0 {
return 0, fmt.Errorf("the input coordinators slice is empty")
}
assignee, err := lookupCoordinator(coordinators[0])
if err != nil {
return 0, fmt.Errorf("failed to find the coordinator %q", coordinators[0])
}
issue, _, err := r.Github.CreateIssue(ctx, "golang", "go", &github.IssueRequest{
Title: &title,
Body: &content,
Title: github.String(title),
Body: github.String(content),
Labels: &[]string{"gopls", "Tools"},
Assignee: &assignee,
Milestone: &milestoneID,
Assignee: github.String(assignee.GitHub),
Milestone: github.Int(milestoneID),
})
if err != nil {
return 0, fmt.Errorf("failed to create release tracking issue for %q: %w", versionString, err)
@ -620,7 +636,7 @@ func (r *ReleaseGoplsTasks) NewReleaseDefinition() *wf.Definition {
// bump strategy.
// Use with caution.
inputVersion := wf.Param(wd, wf.ParamDef[string]{Name: "explicit version (optional)"})
reviewers := wf.Param(wd, reviewersParam)
coordinators := wf.Param(wd, releaseCoordinatorsParam)
release := wf.Task2(wd, "determine the release version", r.determineReleaseVersion, inputVersion, versionBumpStrategy)
prerelease := wf.Task1(wd, "find the latest pre-release version", r.latestPrerelease, release)
@ -628,13 +644,13 @@ func (r *ReleaseGoplsTasks) NewReleaseDefinition() *wf.Definition {
tagged := wf.Action2(wd, "tag the release", r.tagRelease, release, prerelease, wf.After(approved))
issue := wf.Task2(wd, "find release git issue", r.findOrCreateGitHubIssue, release, wf.Const(false))
issue := wf.Task3(wd, "find release git issue", r.findOrCreateGitHubIssue, release, wf.Const([]string{}), wf.Const(false))
_ = wf.Action1(wd, "mail announcement", r.mailReleaseAnnouncement, release, wf.After(tagged))
changeID := wf.Task3(wd, "updating x/tools dependency in master branch in gopls sub dir", r.updateDependencyIfMinor, reviewers, release, issue, wf.After(tagged))
changeID := wf.Task3(wd, "updating x/tools dependency in master branch in gopls sub dir", r.updateDependencyIfMinor, coordinators, release, issue, wf.After(tagged))
_ = wf.Task1(wd, "await x/tools gopls dependency CL submission in gopls sub dir", clAwaiter{r.Gerrit}.awaitSubmission, changeID)
vscodeGoChanges := wf.Task4(wd, "update gopls version in vscode-go", r.updateVSCodeGoGoplsVersion, reviewers, issue, release, wf.Const(""), wf.After(tagged))
vscodeGoChanges := wf.Task4(wd, "update gopls version in vscode-go", r.updateVSCodeGoGoplsVersion, coordinators, issue, release, wf.Const(""), wf.After(tagged))
_ = wf.Task1(wd, "await gopls version update CLs submission in vscode-go", clAwaiter{r.Gerrit}.awaitSubmissions, vscodeGoChanges)
return wd

Просмотреть файл

@ -511,7 +511,7 @@ func TestFindOrCreateReleaseIssue(t *testing.T) {
if !ok {
t.Fatalf("parseVersion(%q) failed", tc.version)
}
gotIssue, err := tasks.findOrCreateGitHubIssue(&workflow.TaskContext{Context: ctx, Logger: &testLogger{t, ""}}, release, tc.create)
gotIssue, err := tasks.findOrCreateGitHubIssue(&workflow.TaskContext{Context: ctx, Logger: &testLogger{t, ""}}, release, []string{"gobot"}, tc.create)
if tc.wantErr && err == nil {
t.Errorf("createReleaseIssue(%s) should return error but return nil", tc.version)
@ -825,7 +825,7 @@ esac`, tc.wantVersion)
t.Run("manual input version: "+tc.name, func(t *testing.T) {
runTestWithInput(map[string]any{
reviewersParam.Name: []string(nil),
releaseCoordinatorsParam.Name: []string{"gobot"},
"explicit version (optional)": tc.release.String(),
"next version": "use explicit version",
})
@ -836,7 +836,7 @@ esac`, tc.wantVersion)
}
t.Run("interpret version "+versionBump+" : "+tc.name, func(t *testing.T) {
runTestWithInput(map[string]any{
reviewersParam.Name: []string(nil),
releaseCoordinatorsParam.Name: []string{"gobot"},
"explicit version (optional)": "",
"next version": versionBump,
})
@ -1317,7 +1317,7 @@ esac
}
t.Run("manual input version: "+tc.name, func(t *testing.T) {
runTestWithInput(map[string]any{
reviewersParam.Name: []string(nil),
releaseCoordinatorsParam.Name: []string{"gobot"},
"explicit version (optional)": tc.release.String(),
"next version": "use explicit version",
})
@ -1328,7 +1328,7 @@ esac
}
t.Run("interpret version "+versionBump+": "+tc.name, func(t *testing.T) {
runTestWithInput(map[string]any{
reviewersParam.Name: []string(nil),
releaseCoordinatorsParam.Name: []string{"gobot"},
"explicit version (optional)": "",
"next version": versionBump,
})

Просмотреть файл

@ -142,6 +142,7 @@ func (r *ReleaseVSCodeGoTasks) NewPrereleaseDefinition() *wf.Definition {
wd := wf.New(wf.ACL{Groups: []string{groups.ToolsTeam}})
versionBumpStrategy := wf.Param(wd, nextVersionParam)
coordinators := wf.Param(wd, releaseCoordinatorsParam)
release := wf.Task1(wd, "determine the release version", r.determineReleaseVersion, versionBumpStrategy)
prerelease := wf.Task1(wd, "find the next pre-release version", r.nextPrereleaseVersion, release)
@ -150,7 +151,7 @@ func (r *ReleaseVSCodeGoTasks) NewPrereleaseDefinition() *wf.Definition {
verified := wf.Action1(wd, "verify the release candidate", r.verifyTestResults, revision, wf.After(approved))
issue := wf.Task1(wd, "create release milestone and issue", r.createReleaseMilestoneAndIssue, release, wf.After(verified))
issue := wf.Task2(wd, "create release milestone and issue", r.createReleaseMilestoneAndIssue, release, coordinators, wf.After(verified))
branched := wf.Action2(wd, "create release branch", r.createReleaseBranch, release, prerelease, wf.After(verified))
build := wf.Task3(wd, "generate package extension (.vsix) for release candidate", r.generatePackageExtension, release, prerelease, revision, wf.After(verified))
@ -221,7 +222,7 @@ chown -R 1000:1000 .
return nil
}
func (r *ReleaseVSCodeGoTasks) createReleaseMilestoneAndIssue(ctx *wf.TaskContext, semv releaseVersion) (int, error) {
func (r *ReleaseVSCodeGoTasks) createReleaseMilestoneAndIssue(ctx *wf.TaskContext, semv releaseVersion, coordinators []string) (int, error) {
version := fmt.Sprintf("v%v.%v.%v", semv.Major, semv.Minor, semv.Patch)
// The vscode-go release milestone name matches the release version.
@ -246,12 +247,18 @@ func (r *ReleaseVSCodeGoTasks) createReleaseMilestoneAndIssue(ctx *wf.TaskContex
}
}
content := fmt.Sprintf(vscodeGoReleaseIssueTmplStr, version)
if len(coordinators) == 0 {
return 0, fmt.Errorf("the input coordinators slice is empty")
}
assignee, err := lookupCoordinator(coordinators[0])
if err != nil {
return 0, fmt.Errorf("failed to find the coordinator %q", coordinators[0])
}
issue, _, err := r.GitHub.CreateIssue(ctx, "golang", "vscode-go", &github.IssueRequest{
Title: &title,
Body: &content,
Assignee: github.String("h9jiang"),
Milestone: &milestoneID,
Title: github.String(title),
Body: github.String(fmt.Sprintf(vscodeGoReleaseIssueTmplStr, version)),
Assignee: github.String(assignee.GitHub),
Milestone: github.Int(milestoneID),
})
if err != nil {
return 0, fmt.Errorf("failed to create release tracking issue for %q: %w", version, err)

Просмотреть файл

@ -136,7 +136,7 @@ func TestCreateReleaseMilestoneAndIssue(t *testing.T) {
if !ok {
t.Fatalf("parseVersion(%q) failed", tc.version)
}
issueNumber, err := tasks.createReleaseMilestoneAndIssue(&workflow.TaskContext{Context: context.Background(), Logger: &testLogger{t, ""}}, release)
issueNumber, err := tasks.createReleaseMilestoneAndIssue(&workflow.TaskContext{Context: context.Background(), Logger: &testLogger{t, ""}}, release, []string{"gobot"})
if err != nil {
t.Fatal(err)
}

Просмотреть файл

@ -57,7 +57,7 @@ func (x *TagXReposTasks) NewSingleDefinition() *wf.Definition {
}
var reviewersParam = wf.ParamDef[[]string]{
Name: "Reviewer usernames (optional)",
Name: "Reviewer Gerrit Usernames (optional)",
ParamType: wf.SliceShort,
Doc: `Send code reviews to these users.`,
Example: "heschi",