diff --git a/go/cmd/pre_release/milestone.go b/go/cmd/pre_release/milestone.go new file mode 100644 index 0000000..b0a954a --- /dev/null +++ b/go/cmd/pre_release/milestone.go @@ -0,0 +1,34 @@ +/* +Copyright 2023 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreedto in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package pre_release + +import ( + "fmt" + + "github.com/spf13/cobra" + "vitess.io/vitess-releaser/go/releaser/pre_release" +) + +var createMilestone = &cobra.Command{ + Use: "create-milestone", + Short: "Create the next milestone for the future release.", + Run: func(cmd *cobra.Command, args []string) { + _, newMilestone := pre_release.NewMilestone() + out := newMilestone() + fmt.Println("New milestone created or found on GitHub:", out) + }, +} \ No newline at end of file diff --git a/go/interactive/create_github_milestones.go b/go/interactive/create_github_milestones.go new file mode 100644 index 0000000..3a127ee --- /dev/null +++ b/go/interactive/create_github_milestones.go @@ -0,0 +1,50 @@ +/* +Copyright 2023 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreedto in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package interactive + +import ( + tea "github.com/charmbracelet/bubbletea" + "vitess.io/vitess-releaser/go/releaser/pre_release" +) + +type createMilestone string + +func createMilestoneMenuItem() menuItem { + return menuItem{ + name: "Create a new GitHub Milestone", + act: createMilestoneAct, + update: createMilestoneUpdate, + } +} + +func createMilestoneUpdate(mi menuItem, msg tea.Msg) (menuItem, tea.Cmd) { + milestoneLink, ok := msg.(createMilestone) + if !ok { + return mi, nil + } + + mi.state = string(milestoneLink) + return mi, nil +} + +func createMilestoneAct(mi menuItem) (menuItem, tea.Cmd) { + mi.state = "running..." + pl, create := pre_release.NewMilestone() + return mi, tea.Batch(func() tea.Msg { + return createMilestone(create()) + }, push(newProgressDialog("Creating new GitHub Milestone", pl))) +} diff --git a/go/interactive/main_menu.go b/go/interactive/main_menu.go index d2c8e79..0f7d13f 100644 --- a/go/interactive/main_menu.go +++ b/go/interactive/main_menu.go @@ -34,6 +34,7 @@ func MainScreen() { prerelease := newMenu( "Pre Release", codeFreezeMenuItem(), + createMilestoneMenuItem(), ) postRelease := newMenu( diff --git a/go/releaser/github/milestone.go b/go/releaser/github/milestone.go new file mode 100644 index 0000000..c16a480 --- /dev/null +++ b/go/releaser/github/milestone.go @@ -0,0 +1,66 @@ +/* +Copyright 2023 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreedto in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package github + +import ( + "encoding/json" + "fmt" + "log" + "strings" + + "github.com/cli/go-gh" + "vitess.io/vitess-releaser/go/releaser/state" +) + +type Milestone struct { + URL string `json:"url"` +} + +func GetMilestonesByName(name string) []Milestone { + stdOut, _, err := gh.Exec( + "milestone", "list", + "--query", name, + "--repo", state.VitessRepo, + "--json", "url", + "--state", "all", + ) + if err != nil { + log.Fatal(err.Error()) + } + str := stdOut.String() + str = str[strings.Index(str, "]")+1:] + var ms []Milestone + err = json.Unmarshal([]byte(str), &ms) + if err != nil { + log.Fatal(err.Error()) + } + return ms +} + +func CreateNewMilestone(name string) string { + stdOut, _, err := gh.Exec( + "milestone", "create", + "--repo", state.VitessRepo, + "--title", name, + ) + if err != nil { + log.Fatal(err.Error()) + } + out := strings.ReplaceAll(stdOut.String(), "\n", "") + idx := strings.LastIndex(out, fmt.Sprintf("https://github.com/%s/milestone/", state.VitessRepo)) + return out[idx:] +} \ No newline at end of file diff --git a/go/releaser/logging/logging.go b/go/releaser/logging/logging.go index 84f7404..893aa2c 100644 --- a/go/releaser/logging/logging.go +++ b/go/releaser/logging/logging.go @@ -54,3 +54,10 @@ func (pl *ProgressLogging) GetStepInProgress() []string { return slices.Clone(pl.StepsDone) } + +func (pl *ProgressLogging) SetTotalStep(v int) { + pl.mu.Lock() + defer pl.mu.Unlock() + + pl.TotalSteps = v +} \ No newline at end of file diff --git a/go/releaser/pre_release/milestone.go b/go/releaser/pre_release/milestone.go new file mode 100644 index 0000000..966e262 --- /dev/null +++ b/go/releaser/pre_release/milestone.go @@ -0,0 +1,50 @@ +/* +Copyright 2023 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreedto in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package pre_release + +import ( + "fmt" + + "vitess.io/vitess-releaser/go/releaser/github" + "vitess.io/vitess-releaser/go/releaser/logging" + "vitess.io/vitess-releaser/go/releaser/vitess" +) + +func NewMilestone() (*logging.ProgressLogging, func() string) { + pl := &logging.ProgressLogging{ + TotalSteps: 3, + } + + return pl, func() string { + pl.NewStepf("Finding the next Milestone") + nextNextRelease := vitess.FindVersionAfterNextRelease() + newMilestone := fmt.Sprintf("v%s", nextNextRelease) + + ms := github.GetMilestonesByName(newMilestone) + if len(ms) > 0 { + pl.SetTotalStep(2) + pl.NewStepf("Found an existing Milestone: %s", ms[0].URL) + return ms[0].URL + } + + pl.NewStepf("Creating Milestone %s on GitHub", newMilestone) + link := github.CreateNewMilestone(newMilestone) + + pl.NewStepf("New Milestone %s created: %s", newMilestone, link) + return link + } +} \ No newline at end of file diff --git a/go/releaser/vitess/milestone.go b/go/releaser/vitess/milestone.go new file mode 100644 index 0000000..5dde047 --- /dev/null +++ b/go/releaser/vitess/milestone.go @@ -0,0 +1,56 @@ +/* +Copyright 2023 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreedto in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package vitess + +import ( + "fmt" + "log" + "strconv" + "strings" + + "vitess.io/vitess-releaser/go/releaser/state" +) + +func FindVersionAfterNextRelease() string { + CorrectCleanRepo() + nextRelease, _ := FindNextRelease(state.MajorRelease) + + if strings.Contains(nextRelease, "rc") { + panic("RC releases not supported for now") + } + + segments := strings.Split(nextRelease, ".") + if len(segments) != 3 { + return "" + } + + segmentInts := make([]int, 0, len(segments)) + for _, segment := range segments { + v, err := strconv.Atoi(segment) + if err != nil { + log.Fatal(err.Error()) + } + segmentInts = append(segmentInts, v) + } + + // if it is a major release + if segmentInts[1] == 0 && segmentInts[2] == 0 { + return fmt.Sprintf("%d.0.0", segmentInts[0]+1) + } + // if a patch release + return fmt.Sprintf("%d.%d.%d", segmentInts[0], segmentInts[1], segmentInts[2]+1) +} \ No newline at end of file