From c7e7dd439349c7bcc08638929550a47fc19fcb3f Mon Sep 17 00:00:00 2001 From: Derek Blank Date: Wed, 11 Oct 2023 14:04:36 +1000 Subject: [PATCH 01/31] Add base gbm prepare command --- cli/pkg/gbm/gbm.go | 137 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 137 insertions(+) create mode 100644 cli/pkg/gbm/gbm.go diff --git a/cli/pkg/gbm/gbm.go b/cli/pkg/gbm/gbm.go new file mode 100644 index 00000000..f785bb57 --- /dev/null +++ b/cli/pkg/gbm/gbm.go @@ -0,0 +1,137 @@ +package gbm + +import ( + "fmt" + "os" + "path/filepath" + + "github.com/wordpress-mobile/gbm-cli/pkg/console" + "github.com/wordpress-mobile/gbm-cli/pkg/exec" + "github.com/wordpress-mobile/gbm-cli/pkg/gh" + "github.com/wordpress-mobile/gbm-cli/pkg/git" + "github.com/wordpress-mobile/gbm-cli/pkg/utils" +) + + +func PrepareBranch(dir string, pr *gh.PullRequest, gbPr *gh.PullRequest, verbose bool) (gh.Repository, error) { + + gbmDir := filepath.Join(dir, "gutenberg-mobile") + npm := execNpm(gbmDir, verbose) + + version := pr.ReleaseVersion + var ( + gbmr *g.Repository + err error + ) + // If we already have a copy of GBM, initialize the repo + // Otherwise clone at pr.Base + if _, ok := os.Stat(gbmDir); ok != nil { + os.MkdirAll(gbmDir, os.ModePerm) + console.Info("Cloning Gutenberg Mobile") + gbmr, err = git.CloneGBM(dir, *pr, verbose) + if err != nil { + return nil, err + } + } else { + console.Info("Initializing Gutenberg Mobile Repo at %s", gbmDir) + gbmr, err = git.Open(gbmDir) + if err != nil { + return nil, fmt.Errorf("issue opening gutenberg-mobile (err %s)", err) + } + } + + if err := git.Switch(gbmDir, "gutenberg-mobile", pr.Head.Ref, verbose); err != nil { + return nil, err + } + // Set up GB + if err := setupGb(gbmDir, gbmr, gbPr, verbose); err != nil { + return nil, err + } + + console.Info("Set up Node") + if err := exec.SetupNode(gbmDir, verbose); err != nil { + return nil, fmt.Errorf("failed to update node (err %s)", err) + } + + console.Info("Installing npm packages") + if err := npm("ci"); err != nil { + return nil, fmt.Errorf("failed to update node packages (err %s)", err) + } + + console.Info("Update XCFramework builders project Podfile.lock") + xcfDir := filepath.Join(gbmDir, "ios-xcframework") + if err := exec.BundleInstall(xcfDir, verbose); err != nil { + return nil, err + } + if err := exec.PodInstall(xcfDir, verbose); err != nil { + return nil, err + } + + if err := git.CommitAll(gbmr, "Release script: Sync XCFramework `Podfile.lock`"); err != nil { + return nil, err + } + + // If there is a version we should update the package json + if version != "" { + + console.Info("Updating the version") + if err := npm("--no-git-tag-version", "version", version); err != nil { + return nil, err + } + + if err := git.CommitAll(gbmr, "Update Version", "package.json", "package-lock.json"); err != nil { + return nil, err + } + + } else { + // Otherwise just update the bundle + if err := npm("run", "bundle"); err != nil { + return nil, err + } + } + + console.Info("Updating the bundle") + if err := git.CommitAll(gbmr, "Release script: Update bundle for "+version); err != nil { + return nil, err + } + + return gbmr, nil +} + +func setupGb(gbmDir string, gbmr *g.Repository, gbPr *gh.PullRequest, verbose bool) error { + + console.Info("Checking Gutenberg") + + gb, err := git.GetSubmodule(gbmr, "gutenberg") + if err != nil { + return err + } + if curr, err := git.IsSubmoduleCurrent(gb, gbPr.Head.Sha); err != nil { + return fmt.Errorf("issue checking the submodule (err %s)", err) + } else if !curr { + if err := git.Switch(filepath.Join(gbmDir, "gutenberg"), "gutenberg", gbPr.Head.Ref, verbose); err != nil { + return fmt.Errorf("unable to switch to %s (err %s)", gbPr.Head.Ref, err) + } + } + + console.Info("Updating Gutenberg") + if clean, _ := git.IsPorcelain(gbmr); !clean { + if err = git.CommitSubmodule(gbmDir, "Release script: Updating gutenberg ref", "gutenberg", verbose); err != nil { + return fmt.Errorf("failed to update gutenberg: %s", err) + } + } + + return nil +} + +func CreatePr(gbmr *g.Repository, pr *gh.PullRequest, verbose bool) error { + + // TODO: make sure we are not on trunk before pushing + console.Info("Pushing the branch") + if err := git.Push(gbmr, verbose); err != nil { + return err + } + + console.Info("Creating the PR") + return gh.CreatePr("gutenberg-mobile", pr) +} \ No newline at end of file From db9339b5a5216d9be5576320afecb0af70dffd92 Mon Sep 17 00:00:00 2001 From: Derek Blank Date: Wed, 11 Oct 2023 14:04:57 +1000 Subject: [PATCH 02/31] Add CloneGBM function to git pkg --- cli/pkg/git/git.go | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/cli/pkg/git/git.go b/cli/pkg/git/git.go index dcd30c08..2540a27d 100644 --- a/cli/pkg/git/git.go +++ b/cli/pkg/git/git.go @@ -4,6 +4,8 @@ import ( "fmt" "github.com/wordpress-mobile/gbm-cli/pkg/exec" + "github.com/wordpress-mobile/gbm-cli/pkg/gh" + "github.com/wordpress-mobile/gbm-cli/pkg/repo" ) func Clone(repo, dir string, shallow bool) error { @@ -14,6 +16,30 @@ func Clone(repo, dir string, shallow bool) error { return cmd("clone", repo) } +func CloneGBM(dir string, pr gh.PullRequest, verbose bool) (*g.Repository, error) { + git := exec.ExecGit(dir, verbose) + + org, _ := repo.GetOrg("gutenberg-mobile") + url := fmt.Sprintf("git@github.com:%s/%s.git", org, "gutenberg-mobile") + + cmd := []string{"clone", "--recurse-submodules", "--depth", "1"} + + fmt.Println("Checking remote branch...") + // check to see if the remote branch exists + if err := git("ls-remote", "--exit-code", "--heads", url, pr.Head.Ref); err != nil { + cmd = append(cmd, url) + } else { + cmd = append(cmd, "--branch", pr.Head.Ref, url) + } + + if err := git(cmd...); err != nil { + return nil, fmt.Errorf("unable to clone gutenberg mobile %s", err) + } + // return Open(filepath.Join(dir, "gutenberg-mobile")) + + return false, nil +} + func Switch(dir, branch string, create bool) error { cmd := exec.ExecGit(dir, true) if create { From f3bbb6f75150a5b26f856e9e1f87e1d8e09b0907 Mon Sep 17 00:00:00 2001 From: Derek Blank Date: Wed, 11 Oct 2023 16:17:44 +1000 Subject: [PATCH 03/31] Add git submodule utility functions --- cli/pkg/git/git.go | 55 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/cli/pkg/git/git.go b/cli/pkg/git/git.go index 2540a27d..2998c041 100644 --- a/cli/pkg/git/git.go +++ b/cli/pkg/git/git.go @@ -55,3 +55,58 @@ func CommitAll(dir, format string, args ...interface{}) error { func Push(dir, branch string) error { return exec.ExecGit(dir, true)("push", "origin", branch) } + +func GetSubmodule(r gh.Repo, path string) (*g.Submodule, error) { + w, err := r.Worktree() + if err != nil { + return nil, err + } + + return w.Submodule(path) +} + +func CommitSubmodule(dir, message, submodule string, verbose bool) error { + git := exec.ExecGit(dir, verbose) + + if err := git("add", submodule); err != nil { + return fmt.Errorf("unable to add submodule %s in %s :%s", submodule, dir, err) + } + + if err := git("commit", "-m", message); err != nil { + return fmt.Errorf("unable to commit submodule update %s : %s", submodule, err) + } + return nil +} + +func IsSubmoduleCurrent(s gh.Submodule, expectedHash string) (bool, error) { + // Check if the submodule is porcelain + sr, err := s.Repository() + if clean, err := IsPorcelain(sr); err != nil { + return false, err + } else if !clean { + return false, &NotPorcelainError{fmt.Errorf("submodule %s is not clean", s.Config().Name)} + } + + if err != nil { + return false, err + } + stat, err := s.Status() + if err != nil { + return false, err + } + eh := plumbing.NewHash(expectedHash) + + return stat.Current == eh, nil +} + +func IsPorcelain(r gh.Repo) (bool, error) { + w, err := r.Worktree() + if err != nil { + return false, err + } + status, err := w.Status() + if err != nil { + return false, err + } + return status.IsClean(), nil +} From 606055cb2f015395865a9688b239eccc3b32e82f Mon Sep 17 00:00:00 2001 From: Derek Blank Date: Wed, 11 Oct 2023 16:20:06 +1000 Subject: [PATCH 04/31] Update gbm.go --- cli/pkg/gbm/gbm.go | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/cli/pkg/gbm/gbm.go b/cli/pkg/gbm/gbm.go index f785bb57..39679e56 100644 --- a/cli/pkg/gbm/gbm.go +++ b/cli/pkg/gbm/gbm.go @@ -9,11 +9,9 @@ import ( "github.com/wordpress-mobile/gbm-cli/pkg/exec" "github.com/wordpress-mobile/gbm-cli/pkg/gh" "github.com/wordpress-mobile/gbm-cli/pkg/git" - "github.com/wordpress-mobile/gbm-cli/pkg/utils" ) - -func PrepareBranch(dir string, pr *gh.PullRequest, gbPr *gh.PullRequest, verbose bool) (gh.Repository, error) { +func PrepareBranch(dir string, pr *gh.PullRequest, gbPr *gh.PullRequest, verbose bool) (gh.Repo, error) { gbmDir := filepath.Join(dir, "gutenberg-mobile") npm := execNpm(gbmDir, verbose) @@ -74,7 +72,7 @@ func PrepareBranch(dir string, pr *gh.PullRequest, gbPr *gh.PullRequest, verbose // If there is a version we should update the package json if version != "" { - console.Info("Updating the version") + console.Info("Updating the version") if err := npm("--no-git-tag-version", "version", version); err != nil { return nil, err } @@ -98,8 +96,7 @@ func PrepareBranch(dir string, pr *gh.PullRequest, gbPr *gh.PullRequest, verbose return gbmr, nil } -func setupGb(gbmDir string, gbmr *g.Repository, gbPr *gh.PullRequest, verbose bool) error { - +func setupGb(gbmDir string, gbmr gh.Repo, gbPr *gh.PullRequest, verbose bool) error { console.Info("Checking Gutenberg") gb, err := git.GetSubmodule(gbmr, "gutenberg") @@ -125,7 +122,6 @@ func setupGb(gbmDir string, gbmr *g.Repository, gbPr *gh.PullRequest, verbose bo } func CreatePr(gbmr *g.Repository, pr *gh.PullRequest, verbose bool) error { - // TODO: make sure we are not on trunk before pushing console.Info("Pushing the branch") if err := git.Push(gbmr, verbose); err != nil { @@ -134,4 +130,4 @@ func CreatePr(gbmr *g.Repository, pr *gh.PullRequest, verbose bool) error { console.Info("Creating the PR") return gh.CreatePr("gutenberg-mobile", pr) -} \ No newline at end of file +} From 485270c559929e87958ec71ba75888cfbfa864bc Mon Sep 17 00:00:00 2001 From: Derek Blank Date: Thu, 12 Oct 2023 11:00:18 +1000 Subject: [PATCH 05/31] Add Submodule to git client --- cli/pkg/git/git.go | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/cli/pkg/git/git.go b/cli/pkg/git/git.go index 1c9c3120..c0d72111 100644 --- a/cli/pkg/git/git.go +++ b/cli/pkg/git/git.go @@ -12,6 +12,7 @@ type Client interface { CommitAll(string, ...interface{}) error Push() error RemoteExists(string, string) bool + Submodule(...string) error } type client struct { @@ -53,4 +54,10 @@ func (c *client) RemoteExists(remote, branch string) bool { cmd := exec.Git(c.dir, c.verbose) err := cmd("ls-remote", "--exit-code", "--heads", remote, branch) return err == nil -} \ No newline at end of file +} + +func (c *client) Submodule(args ...string) error { + cmd := exec.Git(c.dir, c.verbose) + submodule := append([]string{"submodule"}, args...) + return cmd(submodule...) +} From d64997c511a52126c4aa2c52896872f6f9f47fdf Mon Sep 17 00:00:00 2001 From: Derek Blank Date: Thu, 12 Oct 2023 11:00:36 +1000 Subject: [PATCH 06/31] Update gbm.go steps --- cli/pkg/gbm/gbm.go | 133 ------------------------------------ cli/pkg/release/gbm.go | 148 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 148 insertions(+), 133 deletions(-) delete mode 100644 cli/pkg/gbm/gbm.go create mode 100644 cli/pkg/release/gbm.go diff --git a/cli/pkg/gbm/gbm.go b/cli/pkg/gbm/gbm.go deleted file mode 100644 index 39679e56..00000000 --- a/cli/pkg/gbm/gbm.go +++ /dev/null @@ -1,133 +0,0 @@ -package gbm - -import ( - "fmt" - "os" - "path/filepath" - - "github.com/wordpress-mobile/gbm-cli/pkg/console" - "github.com/wordpress-mobile/gbm-cli/pkg/exec" - "github.com/wordpress-mobile/gbm-cli/pkg/gh" - "github.com/wordpress-mobile/gbm-cli/pkg/git" -) - -func PrepareBranch(dir string, pr *gh.PullRequest, gbPr *gh.PullRequest, verbose bool) (gh.Repo, error) { - - gbmDir := filepath.Join(dir, "gutenberg-mobile") - npm := execNpm(gbmDir, verbose) - - version := pr.ReleaseVersion - var ( - gbmr *g.Repository - err error - ) - // If we already have a copy of GBM, initialize the repo - // Otherwise clone at pr.Base - if _, ok := os.Stat(gbmDir); ok != nil { - os.MkdirAll(gbmDir, os.ModePerm) - console.Info("Cloning Gutenberg Mobile") - gbmr, err = git.CloneGBM(dir, *pr, verbose) - if err != nil { - return nil, err - } - } else { - console.Info("Initializing Gutenberg Mobile Repo at %s", gbmDir) - gbmr, err = git.Open(gbmDir) - if err != nil { - return nil, fmt.Errorf("issue opening gutenberg-mobile (err %s)", err) - } - } - - if err := git.Switch(gbmDir, "gutenberg-mobile", pr.Head.Ref, verbose); err != nil { - return nil, err - } - // Set up GB - if err := setupGb(gbmDir, gbmr, gbPr, verbose); err != nil { - return nil, err - } - - console.Info("Set up Node") - if err := exec.SetupNode(gbmDir, verbose); err != nil { - return nil, fmt.Errorf("failed to update node (err %s)", err) - } - - console.Info("Installing npm packages") - if err := npm("ci"); err != nil { - return nil, fmt.Errorf("failed to update node packages (err %s)", err) - } - - console.Info("Update XCFramework builders project Podfile.lock") - xcfDir := filepath.Join(gbmDir, "ios-xcframework") - if err := exec.BundleInstall(xcfDir, verbose); err != nil { - return nil, err - } - if err := exec.PodInstall(xcfDir, verbose); err != nil { - return nil, err - } - - if err := git.CommitAll(gbmr, "Release script: Sync XCFramework `Podfile.lock`"); err != nil { - return nil, err - } - - // If there is a version we should update the package json - if version != "" { - - console.Info("Updating the version") - if err := npm("--no-git-tag-version", "version", version); err != nil { - return nil, err - } - - if err := git.CommitAll(gbmr, "Update Version", "package.json", "package-lock.json"); err != nil { - return nil, err - } - - } else { - // Otherwise just update the bundle - if err := npm("run", "bundle"); err != nil { - return nil, err - } - } - - console.Info("Updating the bundle") - if err := git.CommitAll(gbmr, "Release script: Update bundle for "+version); err != nil { - return nil, err - } - - return gbmr, nil -} - -func setupGb(gbmDir string, gbmr gh.Repo, gbPr *gh.PullRequest, verbose bool) error { - console.Info("Checking Gutenberg") - - gb, err := git.GetSubmodule(gbmr, "gutenberg") - if err != nil { - return err - } - if curr, err := git.IsSubmoduleCurrent(gb, gbPr.Head.Sha); err != nil { - return fmt.Errorf("issue checking the submodule (err %s)", err) - } else if !curr { - if err := git.Switch(filepath.Join(gbmDir, "gutenberg"), "gutenberg", gbPr.Head.Ref, verbose); err != nil { - return fmt.Errorf("unable to switch to %s (err %s)", gbPr.Head.Ref, err) - } - } - - console.Info("Updating Gutenberg") - if clean, _ := git.IsPorcelain(gbmr); !clean { - if err = git.CommitSubmodule(gbmDir, "Release script: Updating gutenberg ref", "gutenberg", verbose); err != nil { - return fmt.Errorf("failed to update gutenberg: %s", err) - } - } - - return nil -} - -func CreatePr(gbmr *g.Repository, pr *gh.PullRequest, verbose bool) error { - // TODO: make sure we are not on trunk before pushing - console.Info("Pushing the branch") - if err := git.Push(gbmr, verbose); err != nil { - return err - } - - console.Info("Creating the PR") - return gh.CreatePr("gutenberg-mobile", pr) -} diff --git a/cli/pkg/release/gbm.go b/cli/pkg/release/gbm.go new file mode 100644 index 00000000..d1160d90 --- /dev/null +++ b/cli/pkg/release/gbm.go @@ -0,0 +1,148 @@ +package release + +import ( + "fmt" + "path/filepath" + + "github.com/wordpress-mobile/gbm-cli/pkg/console" + "github.com/wordpress-mobile/gbm-cli/pkg/exec" + "github.com/wordpress-mobile/gbm-cli/pkg/gh" + g "github.com/wordpress-mobile/gbm-cli/pkg/git" + "github.com/wordpress-mobile/gbm-cli/pkg/render" + "github.com/wordpress-mobile/gbm-cli/pkg/repo" + "github.com/wordpress-mobile/gbm-cli/pkg/utils" +) + +func CreateGbmPR(version, dir string) (gh.PullRequest, error) { + var pr gh.PullRequest + + gbmDir := fmt.Sprintf("%s/gutenberg-mobile", dir) + git := g.NewClient(gbmDir, true) + + // org, err := repo.GetOrg("gutenberg-mobile") + // console.ExitIfError(err) + + branch := "release/" + version + + console.Info("Checking if branch %s exists", branch) + exists, _ := gh.SearchBranch("gutenberg-mobile", branch) + + if (exists != gh.Branch{}) { + console.Info("Branch %s already exists", branch) + return pr, nil + } else { + console.Info("Cloning Gutenberg Mobile to %s", dir) + + err := git.Clone(repo.GetRepoPath("gutenberg-mobile"), "--depth=1") + if err != nil { + return pr, err + } + + console.Info("Checking out branch %s", branch) + err = git.Switch(branch, "-c") + if err != nil { + return pr, err + } + } + + console.Info("Updating package versions") + pkgs := []string{"./package.json", "./package-lock.json"} + for _, pkg := range pkgs { + if err := utils.UpdatePackageVersion(version, pkg); err != nil { + return pr, err + } + } + + if err := git.CommitAll(gbmDir, "Release script: Update package.json version to %s", version); err != nil { + return pr, err + } + + if err := git.Submodule("update", "--init", "--recursive", "--depth=1", "--recommend-shallow"); err != nil { + return pr, err + } + + console.Info("Setting up Gutenberg Mobile node environment") + if err := exec.SetupNode(gbmDir, true); err != nil { + return pr, err + } + + gbGit := g.NewClient(filepath.Join(gbmDir, "gutenberg"), true) + if err := gbGit.Switch("rnmobile/release_" + version); err != nil { + return pr, err + } + + if err := git.CommitAll(gbmDir, "Release script: Update gutenberg submodule"); err != nil { + return pr, err + } + + if err := exec.NpmCi(gbmDir, true); err != nil { + return pr, err + } + + if err := exec.NpmRun(gbmDir, true, "bundle"); err != nil { + return pr, err + } + + if err := git.CommitAll(gbmDir, "Release script: Update bundle for %s", version); err != nil { + return pr, err + } + + console.Info("Update XCFramework builders project Podfile.lock") + xcframeworkDir := fmt.Sprintf("%s/ios-xcframework", dir) + + if err := exec.BundleInstall(xcframeworkDir, true); err != nil { + return pr, err + } + + if err := exec.Bundle(xcframeworkDir, true, "exec", "pod", "install"); err != nil { + return pr, err + } + + if err := git.CommitAll(xcframeworkDir, "Release script: Sync XCFramework `Podfile.lock` with %s", version); err != nil { + return pr, err + } + + console.Info("Update the release-notes in the mobile package") + chnPath := filepath.Join(gbmDir, "RELEASE-NOTES.txt") + if err := utils.UpdateReleaseNotes(version, chnPath); err != nil { + return pr, err + } + + if err := git.CommitAll(gbmDir, "Release script: Update release notes for version %s", version); err != nil { + return pr, err + } + + console.Info("Creating PR for %s", branch) + pr.Title = fmt.Sprint("Release ", version) + pr.Base.Ref = "trunk" + pr.Head.Ref = branch + + if err := renderGbmPrBody(version, &pr); err != nil { + console.Info("Unable to render the GB PR body (err %s)", err) + } + + pr.Labels = []gh.Label{{ + Name: "release-process", + }} + + return pr, nil +} + +func renderGbmPrBody(version string, pr *gh.PullRequest) error { + t := render.Template{ + Path: "templates/release/gbm_pr_body.md", + Data: struct { + Version string + GbmPrUrl string + }{ + Version: version, + }, + } + + body, err := render.Render(t) + if err != nil { + return err + } + pr.Body = body + return nil +} From e00416d01cb8e3601ea8c905af4b3977003abc59 Mon Sep 17 00:00:00 2001 From: Derek Blank Date: Thu, 12 Oct 2023 11:00:55 +1000 Subject: [PATCH 07/31] Add UpdateReleaseNotes util --- cli/pkg/utils/change_logs.go | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/cli/pkg/utils/change_logs.go b/cli/pkg/utils/change_logs.go index 24887250..b4fc3c64 100644 --- a/cli/pkg/utils/change_logs.go +++ b/cli/pkg/utils/change_logs.go @@ -7,6 +7,22 @@ import ( "regexp" ) +// Updates the release notes by replacing "Unreleased" with +// the new version and adding a new "Unreleased" section +func UpdateReleaseNotes(version, path string) error { + return readWriteNotes(version, path, releaseNotesUpdater) +} + +// See UpdateReleaseNotes +// This handles the string replacement +func releaseNotesUpdater(version string, notes []byte) []byte { + re := regexp.MustCompile(`(^Unreleased\s*\n)`) + + repl := fmt.Sprintf("$1---\n\n%s\n", version) + + return re.ReplaceAll(notes, []byte(repl)) +} + // Updates the change log by replacing "Unreleased" with // the new version and adding a new "Unreleased" section func UpdateChangeLog(version, path string) error { From 3e4d3cc8e07d26b306d97840c223a13ffc63ff57 Mon Sep 17 00:00:00 2001 From: Derek Blank Date: Thu, 12 Oct 2023 11:01:12 +1000 Subject: [PATCH 08/31] Add Bundle exec command --- cli/pkg/exec/exec.go | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/cli/pkg/exec/exec.go b/cli/pkg/exec/exec.go index dd7a9369..a5042a9c 100644 --- a/cli/pkg/exec/exec.go +++ b/cli/pkg/exec/exec.go @@ -42,8 +42,12 @@ func NpmRun(dir string, verbose bool, args ...string) error { return exc(verbose, dir, "npm", append([]string{"run"}, args...)...) } -func BundleInstall(dir string, verbose bool) error { - return exc(verbose, dir, "bundle", "install") +func Bundle(dir string, verbose bool, args ...string) error { + return exc(verbose, dir, "bundle", args...) +} + +func BundleInstall(dir string, verbose bool, args ...string) error { + return Bundle(dir, true, append([]string{"install"}, args...)...) } func exc(verbose bool, dir, cmd string, args ...string) error { From 33ef3caae72d1b5138f168896ebebb9f33f49f28 Mon Sep 17 00:00:00 2001 From: Derek Blank Date: Thu, 12 Oct 2023 11:01:31 +1000 Subject: [PATCH 09/31] Add GBM PR body template --- cli/templates/release/gbm_pr_body.md | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 cli/templates/release/gbm_pr_body.md diff --git a/cli/templates/release/gbm_pr_body.md b/cli/templates/release/gbm_pr_body.md new file mode 100644 index 00000000..10a5889f --- /dev/null +++ b/cli/templates/release/gbm_pr_body.md @@ -0,0 +1,27 @@ +Release for Gutenberg Mobile {{ .Version }} + + + + + +## Test plan + +Once the installable builds of the main apps are ready, perform a quick smoke test of the editor on both iOS and Android to verify it launches without crashing. We will perform additional testing after the main apps cut their releases. + +## Release Submission Checklist + +- [ ] Verify Items from test plan have been completed +- [ ] Check if `RELEASE-NOTES.txt` is updated with all the changes that made it to the release. Replace `Unreleased` section with the release version and create a new `Unreleased` section. +- [ ] Check if `gutenberg/packages/react-native-editor/CHANGELOG.md` is updated with all the changes that made it to the release. Replace `## Unreleased` with the release version and create a new `## Unreleased`. +- [ ] Bundle package of the release is updated. \ No newline at end of file From 368b70c8970fec139f51c1dd19c637a696cfdc44 Mon Sep 17 00:00:00 2001 From: Derek Blank Date: Thu, 12 Oct 2023 15:34:28 +1000 Subject: [PATCH 10/31] Add steps to preview and create Gutenberg Mobile PR --- cli/pkg/release/gbm.go | 29 +++++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/cli/pkg/release/gbm.go b/cli/pkg/release/gbm.go index d1160d90..383136a2 100644 --- a/cli/pkg/release/gbm.go +++ b/cli/pkg/release/gbm.go @@ -19,8 +19,8 @@ func CreateGbmPR(version, dir string) (gh.PullRequest, error) { gbmDir := fmt.Sprintf("%s/gutenberg-mobile", dir) git := g.NewClient(gbmDir, true) - // org, err := repo.GetOrg("gutenberg-mobile") - // console.ExitIfError(err) + org, err := repo.GetOrg("gutenberg-mobile") + console.ExitIfError(err) branch := "release/" + version @@ -112,6 +112,8 @@ func CreateGbmPR(version, dir string) (gh.PullRequest, error) { return pr, err } + console.Info("\n 🎉 Gutenberg Mobile preparations succeeded.") + console.Info("Creating PR for %s", branch) pr.Title = fmt.Sprint("Release ", version) pr.Base.Ref = "trunk" @@ -125,6 +127,29 @@ func CreateGbmPR(version, dir string) (gh.PullRequest, error) { Name: "release-process", }} + gh.PreviewPr("gutenberg-mobile", gbmDir, &pr) + + + prompt := fmt.Sprintf("\nReady to create the PR on %s/gutenberg?", org) + cont := console.Confirm(prompt) + + if !cont { + console.Info("Bye 👋") + return pr, fmt.Errorf("exiting before creating PR") + } + + if err := git.Push(); err != nil { + return pr, err + } + + if err := gh.CreatePr("gutenberg-mobile", &pr); err != nil { + return pr, err + } + + if pr.Number == 0 { + return pr, fmt.Errorf("failed to create the PR") + } + return pr, nil } From f90bd9c2d08f890ae8aa527635bb08d1f54ce2d3 Mon Sep 17 00:00:00 2001 From: Derek Blank Date: Thu, 12 Oct 2023 15:41:13 +1000 Subject: [PATCH 11/31] Add code comments to gbm prepare command --- cli/pkg/release/gbm.go | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/cli/pkg/release/gbm.go b/cli/pkg/release/gbm.go index 383136a2..13f81599 100644 --- a/cli/pkg/release/gbm.go +++ b/cli/pkg/release/gbm.go @@ -16,14 +16,20 @@ import ( func CreateGbmPR(version, dir string) (gh.PullRequest, error) { var pr gh.PullRequest + // Set Gutenberg Mobile directory gbmDir := fmt.Sprintf("%s/gutenberg-mobile", dir) git := g.NewClient(gbmDir, true) + // Set Gutenberg Mobile repository and org org, err := repo.GetOrg("gutenberg-mobile") console.ExitIfError(err) + // Set Gutenberg Mobile branch name e.g., (release/x.xx.x) branch := "release/" + version + // Check if branch already exists + // Return if it does + // Otherwise, clone the repo and checkout the branch console.Info("Checking if branch %s exists", branch) exists, _ := gh.SearchBranch("gutenberg-mobile", branch) @@ -45,6 +51,7 @@ func CreateGbmPR(version, dir string) (gh.PullRequest, error) { } } + // Update package versions for package.json and package-lock.json console.Info("Updating package versions") pkgs := []string{"./package.json", "./package-lock.json"} for _, pkg := range pkgs { @@ -53,28 +60,34 @@ func CreateGbmPR(version, dir string) (gh.PullRequest, error) { } } + // Commit package.json and package-lock.json if err := git.CommitAll(gbmDir, "Release script: Update package.json version to %s", version); err != nil { return pr, err } + // Update the release-notes in the mobile package if err := git.Submodule("update", "--init", "--recursive", "--depth=1", "--recommend-shallow"); err != nil { return pr, err } + // Set up Gutenberg Mobile node environment console.Info("Setting up Gutenberg Mobile node environment") if err := exec.SetupNode(gbmDir, true); err != nil { return pr, err } + // Create a git client for Gutenberg submodule so the Gutenberg ref can be updated to the correct branch gbGit := g.NewClient(filepath.Join(gbmDir, "gutenberg"), true) if err := gbGit.Switch("rnmobile/release_" + version); err != nil { return pr, err } + // Commit the updated Gutenberg submodule ref if err := git.CommitAll(gbmDir, "Release script: Update gutenberg submodule"); err != nil { return pr, err } + // Run npm ci and npm run bundle if err := exec.NpmCi(gbmDir, true); err != nil { return pr, err } @@ -83,25 +96,31 @@ func CreateGbmPR(version, dir string) (gh.PullRequest, error) { return pr, err } + // Commit the updated bundle output if err := git.CommitAll(gbmDir, "Release script: Update bundle for %s", version); err != nil { return pr, err } + // Update XCFramework builders project Podfile.lock console.Info("Update XCFramework builders project Podfile.lock") xcframeworkDir := fmt.Sprintf("%s/ios-xcframework", dir) + // Run `bundle install` if err := exec.BundleInstall(xcframeworkDir, true); err != nil { return pr, err } + // Run `bundle exec pod install`` if err := exec.Bundle(xcframeworkDir, true, "exec", "pod", "install"); err != nil { return pr, err } + // Commit output of bundle commands if err := git.CommitAll(xcframeworkDir, "Release script: Sync XCFramework `Podfile.lock` with %s", version); err != nil { return pr, err } + // Update the RELEASE-NOTES.txt and commit output console.Info("Update the release-notes in the mobile package") chnPath := filepath.Join(gbmDir, "RELEASE-NOTES.txt") if err := utils.UpdateReleaseNotes(version, chnPath); err != nil { @@ -114,6 +133,7 @@ func CreateGbmPR(version, dir string) (gh.PullRequest, error) { console.Info("\n 🎉 Gutenberg Mobile preparations succeeded.") + // Create Gutenberg Mobile PR console.Info("Creating PR for %s", branch) pr.Title = fmt.Sprint("Release ", version) pr.Base.Ref = "trunk" @@ -123,13 +143,15 @@ func CreateGbmPR(version, dir string) (gh.PullRequest, error) { console.Info("Unable to render the GB PR body (err %s)", err) } + // Add PR labels pr.Labels = []gh.Label{{ Name: "release-process", }} + // Display PR preview gh.PreviewPr("gutenberg-mobile", gbmDir, &pr) - + // Add prompt to confirm PR creation prompt := fmt.Sprintf("\nReady to create the PR on %s/gutenberg?", org) cont := console.Confirm(prompt) @@ -138,10 +160,12 @@ func CreateGbmPR(version, dir string) (gh.PullRequest, error) { return pr, fmt.Errorf("exiting before creating PR") } + // Push the branch if err := git.Push(); err != nil { return pr, err } + // Create the PR if err := gh.CreatePr("gutenberg-mobile", &pr); err != nil { return pr, err } From ba0a45730485be0e41052b139cbdcdd20b52f951 Mon Sep 17 00:00:00 2001 From: Derek Blank Date: Fri, 13 Oct 2023 14:32:47 +1000 Subject: [PATCH 12/31] Call CreateGbmPR from gbm release subcommand --- cli/cmd/release/prepare/gbm.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/cli/cmd/release/prepare/gbm.go b/cli/cmd/release/prepare/gbm.go index 0e8ca6b9..b95dfffb 100644 --- a/cli/cmd/release/prepare/gbm.go +++ b/cli/cmd/release/prepare/gbm.go @@ -4,6 +4,7 @@ import ( "github.com/spf13/cobra" "github.com/wordpress-mobile/gbm-cli/pkg/console" "github.com/wordpress-mobile/gbm-cli/pkg/gbm" + "github.com/wordpress-mobile/gbm-cli/pkg/release" "github.com/wordpress-mobile/gbm-cli/pkg/utils" ) @@ -29,6 +30,9 @@ var gbmCmd = &cobra.Command{ console.Info("Created temporary directory %s", tempDir) - // console.ExitError("not implemented") + pr, err := release.CreateGbmPR(version, tempDir) + console.Info("Created PR %s", pr.Url) + + console.ExitIfError(err) }, } From c7318b09f17b80dad818098b2eeb1454bd7511bb Mon Sep 17 00:00:00 2001 From: jhnstn Date: Fri, 13 Oct 2023 15:05:15 -0400 Subject: [PATCH 13/31] Add shell package for shell based commands inludes git, npm, and bundler --- cli/pkg/shell/bundler.go | 11 +++++++ cli/pkg/shell/cmds.go | 71 ++++++++++++++++++++++++++++++++++++++++ cli/pkg/shell/git.go | 63 +++++++++++++++++++++++++++++++++++ cli/pkg/shell/npm.go | 16 +++++++++ 4 files changed, 161 insertions(+) create mode 100644 cli/pkg/shell/bundler.go create mode 100644 cli/pkg/shell/cmds.go create mode 100644 cli/pkg/shell/git.go create mode 100644 cli/pkg/shell/npm.go diff --git a/cli/pkg/shell/bundler.go b/cli/pkg/shell/bundler.go new file mode 100644 index 00000000..39b5e4da --- /dev/null +++ b/cli/pkg/shell/bundler.go @@ -0,0 +1,11 @@ +package shell + +type BundlerCmds interface { + Install(...string) error + PodInstall(...string) error +} + +func (c *client) PodInstall(args ...string) error { + podInstall := append([]string{"exec", "pod", "install"}, args...) + return c.cmd(podInstall...) +} diff --git a/cli/pkg/shell/cmds.go b/cli/pkg/shell/cmds.go new file mode 100644 index 00000000..8d5474f6 --- /dev/null +++ b/cli/pkg/shell/cmds.go @@ -0,0 +1,71 @@ +package shell + +import ( + "os" + "os/exec" +) + +type CmdProps struct { + Dir string + Verbose bool +} + +type client struct { + cmd func(...string) error +} + +func NpmCmd(cp CmdProps) NpmCmds { + return &client{ + cmd: func(cmds ...string) error { + cmd := exec.Command("npm", cmds...) + cmd.Dir = cp.Dir + + if cp.Verbose { + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + } + + return cmd.Run() + }, + } +} + +func GitCmd(cp CmdProps) gitCmds { + return &client{ + cmd: func(cmds ...string) error { + cmd := exec.Command("git", cmds...) + cmd.Dir = cp.Dir + + if cp.Verbose { + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + } + + return cmd.Run() + }, + } +} + +func BundlerCmd(cp CmdProps) BundlerCmds { + return &client{ + cmd: func(cmds ...string) error { + cmd := exec.Command("bundle", cmds...) + cmd.Dir = cp.Dir + + if cp.Verbose { + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + } + + return cmd.Run() + }, + } +} + +// common commands + +// Install is used by npm and bundler +func (c *client) Install(args ...string) error { + install := append([]string{"install"}, args...) + return c.cmd(install...) +} diff --git a/cli/pkg/shell/git.go b/cli/pkg/shell/git.go new file mode 100644 index 00000000..30650eae --- /dev/null +++ b/cli/pkg/shell/git.go @@ -0,0 +1,63 @@ +package shell + +import ( + "fmt" +) + +type gitCmds interface { + Clone(...string) error + Switch(...string) error + CommitAll(string, ...interface{}) error + Push() error + RemoteExists(string, string) bool + Submodule(...string) error + Fetch(...string) error + SetRemoteBranches(...string) error + IsPorcelain() bool +} + +func (c *client) Clone(args ...string) error { + clone := append([]string{"clone"}, args...) + return c.cmd(clone...) +} + +func (c *client) Switch(args ...string) error { + swtch := append([]string{"switch"}, args...) + return c.cmd(swtch...) +} + +func (c *client) CommitAll(format string, args ...interface{}) error { + message := fmt.Sprintf(format, args...) + return c.cmd("commit", "-am", message) +} + +func (c *client) Push() error { + return c.cmd("push", "origin", "HEAD") +} + +func (c *client) RemoteExists(remote, branch string) bool { + err := c.cmd("ls-remote", "--exit-code", "--heads", remote, branch) + return err == nil +} + +func (c *client) Submodule(args ...string) error { + submodule := append([]string{"submodule"}, args...) + return c.cmd(submodule...) +} + +func (c *client) Fetch(args ...string) error { + // Let's make sure we can fetch the branch by setting the remote branches + c.cmd("remote", "set-branches", "origin", "*") + fetch := append([]string{"fetch", "origin"}, args...) + return c.cmd(fetch...) +} + +func (c *client) SetRemoteBranches(args ...string) error { + checkout := append([]string{"remote", "set-branches", "origin"}, args...) + return c.cmd(checkout...) +} + +func (c *client) IsPorcelain() bool { + err := c.cmd("diff", "--exit-code") + return err == nil +} diff --git a/cli/pkg/shell/npm.go b/cli/pkg/shell/npm.go new file mode 100644 index 00000000..1e64c95c --- /dev/null +++ b/cli/pkg/shell/npm.go @@ -0,0 +1,16 @@ +package shell + +type NpmCmds interface { + Install(...string) error + Ci() error + Run(...string) error +} + +func (c *client) Ci() error { + return c.cmd("ci") +} + +func (c *client) Run(args ...string) error { + run := append([]string{"run"}, args...) + return c.cmd(run...) +} From 4f4fc6b8e70bad7937a31680b9432cb5446e906e Mon Sep 17 00:00:00 2001 From: jhnstn Date: Fri, 13 Oct 2023 15:05:45 -0400 Subject: [PATCH 14/31] Update gb.go to use the new shell commands --- cli/pkg/exec/exec.go | 13 +++++---- cli/pkg/git/git.go | 63 ------------------------------------------- cli/pkg/release/gb.go | 5 ++-- 3 files changed, 11 insertions(+), 70 deletions(-) delete mode 100644 cli/pkg/git/git.go diff --git a/cli/pkg/exec/exec.go b/cli/pkg/exec/exec.go index a5042a9c..903cc651 100644 --- a/cli/pkg/exec/exec.go +++ b/cli/pkg/exec/exec.go @@ -5,6 +5,7 @@ import ( "os/exec" ) +// Deprecated: Use shell package instead func Git(dir string, verbose bool) func(...string) error { return func(cmds ...string) error { cmd := exec.Command("git", cmds...) @@ -19,33 +20,35 @@ func Git(dir string, verbose bool) func(...string) error { } } -// Deprecated: Use Git instead +// Deprecated: Use shell package instead func ExecGit(dir string, verbose bool) func(...string) error { return Git(dir, verbose) } func SetupNode(dir string, verbose bool) error { // Check for nvm - _, ok := os.LookupEnv("NVM_DIR") - if ok { - exc(verbose, dir, "nvm", "use") - } + + exc(verbose, dir, "nvm", "use") return nil } +// Deprecated: Use shell package instead func NpmCi(dir string, verbose bool) error { return exc(verbose, dir, "npm", "ci") } +// Deprecated: Use shell package instead func NpmRun(dir string, verbose bool, args ...string) error { return exc(verbose, dir, "npm", append([]string{"run"}, args...)...) } +// Deprecated: Use shell package instead func Bundle(dir string, verbose bool, args ...string) error { return exc(verbose, dir, "bundle", args...) } +// Deprecated: Use shell package instead func BundleInstall(dir string, verbose bool, args ...string) error { return Bundle(dir, true, append([]string{"install"}, args...)...) } diff --git a/cli/pkg/git/git.go b/cli/pkg/git/git.go deleted file mode 100644 index c0d72111..00000000 --- a/cli/pkg/git/git.go +++ /dev/null @@ -1,63 +0,0 @@ -package git - -import ( - "fmt" - - "github.com/wordpress-mobile/gbm-cli/pkg/exec" -) - -type Client interface { - Clone(...string) error - Switch(...string) error - CommitAll(string, ...interface{}) error - Push() error - RemoteExists(string, string) bool - Submodule(...string) error -} - -type client struct { - dir string - verbose bool -} - -func NewClient(dir string, verbose bool) Client { - return &client{ - dir: dir, - verbose: verbose, - } -} - -func (c *client) Clone(args ...string) error { - cmd := exec.Git(c.dir, c.verbose) - clone := append([]string{"clone"}, args...) - return cmd(clone...) -} - -func (c *client) Switch(args ...string) error { - cmd := exec.Git(c.dir, c.verbose) - swtch := append([]string{"switch"}, args...) - return cmd(swtch...) -} - -func (c *client) CommitAll(format string, args ...interface{}) error { - cmd := exec.Git(c.dir, c.verbose) - message := fmt.Sprintf(format, args...) - return cmd("commit", "-am", message) -} - -func (c *client) Push() error { - cmd := exec.Git(c.dir, c.verbose) - return cmd("push", "origin", "HEAD") -} - -func (c *client) RemoteExists(remote, branch string) bool { - cmd := exec.Git(c.dir, c.verbose) - err := cmd("ls-remote", "--exit-code", "--heads", remote, branch) - return err == nil -} - -func (c *client) Submodule(args ...string) error { - cmd := exec.Git(c.dir, c.verbose) - submodule := append([]string{"submodule"}, args...) - return cmd(submodule...) -} diff --git a/cli/pkg/release/gb.go b/cli/pkg/release/gb.go index 36c451d0..7f2fa1bc 100644 --- a/cli/pkg/release/gb.go +++ b/cli/pkg/release/gb.go @@ -7,16 +7,17 @@ import ( "github.com/wordpress-mobile/gbm-cli/pkg/console" "github.com/wordpress-mobile/gbm-cli/pkg/exec" "github.com/wordpress-mobile/gbm-cli/pkg/gh" - g "github.com/wordpress-mobile/gbm-cli/pkg/git" "github.com/wordpress-mobile/gbm-cli/pkg/render" "github.com/wordpress-mobile/gbm-cli/pkg/repo" + "github.com/wordpress-mobile/gbm-cli/pkg/shell" "github.com/wordpress-mobile/gbm-cli/pkg/utils" ) func CreateGbPR(version, dir string) (gh.PullRequest, error) { var pr gh.PullRequest - git := g.NewClient(dir, true) + shellProps := shell.CmdProps{Dir: dir, Verbose: true} + git := shell.GitCmd(shellProps) org, err := repo.GetOrg("gutenberg") console.ExitIfError(err) From 0a74d43a783d604b72c6d26978fb834d69bdb874 Mon Sep 17 00:00:00 2001 From: jhnstn Date: Fri, 13 Oct 2023 15:06:03 -0400 Subject: [PATCH 15/31] Update gbm release package --- cli/pkg/release/gbm.go | 90 ++++++++++++++++++++++++++---------------- 1 file changed, 55 insertions(+), 35 deletions(-) diff --git a/cli/pkg/release/gbm.go b/cli/pkg/release/gbm.go index 13f81599..6768031e 100644 --- a/cli/pkg/release/gbm.go +++ b/cli/pkg/release/gbm.go @@ -7,7 +7,8 @@ import ( "github.com/wordpress-mobile/gbm-cli/pkg/console" "github.com/wordpress-mobile/gbm-cli/pkg/exec" "github.com/wordpress-mobile/gbm-cli/pkg/gh" - g "github.com/wordpress-mobile/gbm-cli/pkg/git" + "github.com/wordpress-mobile/gbm-cli/pkg/shell" + "github.com/wordpress-mobile/gbm-cli/pkg/render" "github.com/wordpress-mobile/gbm-cli/pkg/repo" "github.com/wordpress-mobile/gbm-cli/pkg/utils" @@ -16,9 +17,8 @@ import ( func CreateGbmPR(version, dir string) (gh.PullRequest, error) { var pr gh.PullRequest - // Set Gutenberg Mobile directory - gbmDir := fmt.Sprintf("%s/gutenberg-mobile", dir) - git := g.NewClient(gbmDir, true) + sp := shell.CmdProps{Dir: dir, Verbose: true} + git := shell.GitCmd(sp) // Set Gutenberg Mobile repository and org org, err := repo.GetOrg("gutenberg-mobile") @@ -39,95 +39,115 @@ func CreateGbmPR(version, dir string) (gh.PullRequest, error) { } else { console.Info("Cloning Gutenberg Mobile to %s", dir) - err := git.Clone(repo.GetRepoPath("gutenberg-mobile"), "--depth=1") + err := git.Clone(repo.GetRepoPath("gutenberg-mobile"), "--depth=1", "--recursive", ".") if err != nil { return pr, err } console.Info("Checking out branch %s", branch) - err = git.Switch(branch, "-c") + err = git.Switch("-c", branch) if err != nil { return pr, err } } + // Set up Gutenberg Mobile node environment + console.Info("Setting up Node environment") + npm := shell.NpmCmd(sp) + if err := exec.SetupNode(dir, true); err != nil { + return pr, err + } + // Run npm ci and npm run bundle + if err := npm.Ci(); err != nil { + return pr, err + } + if err := npm.Run("bundle"); err != nil { + return pr, err + } // Update package versions for package.json and package-lock.json + console.Info("Updating package versions") pkgs := []string{"./package.json", "./package-lock.json"} for _, pkg := range pkgs { - if err := utils.UpdatePackageVersion(version, pkg); err != nil { + if err := utils.UpdatePackageVersion(version, filepath.Join(dir, pkg)); err != nil { return pr, err } } // Commit package.json and package-lock.json - if err := git.CommitAll(gbmDir, "Release script: Update package.json version to %s", version); err != nil { + if err := git.CommitAll(dir, "Release script: Update package.json version to %s", version); err != nil { return pr, err } // Update the release-notes in the mobile package - if err := git.Submodule("update", "--init", "--recursive", "--depth=1", "--recommend-shallow"); err != nil { - return pr, err - } - - // Set up Gutenberg Mobile node environment - console.Info("Setting up Gutenberg Mobile node environment") - if err := exec.SetupNode(gbmDir, true); err != nil { - return pr, err - } + // if err := git.Submodule("update", "--init", "--recursive", "--depth=1", "--recommend-shallow"); err != nil { + // return pr, err + // } // Create a git client for Gutenberg submodule so the Gutenberg ref can be updated to the correct branch - gbGit := g.NewClient(filepath.Join(gbmDir, "gutenberg"), true) - if err := gbGit.Switch("rnmobile/release_" + version); err != nil { - return pr, err + gbBranch := "rnmobile/release_" + version + if exists, _ := gh.SearchBranch("gutenberg", gbBranch); (exists == gh.Branch{}) { + if org != repo.WpMobileOrg { + console.Warn("You are not using the %s org. Check the .gitmodules file to make sure the gutenberg submodule is pointing to %s/gutenberg.", repo.WpMobileOrg, org) + } + return pr, fmt.Errorf("the Gutenberg branch %s does not exist on %s/gutenberg-mobile", gbBranch, org) } - // Commit the updated Gutenberg submodule ref - if err := git.CommitAll(gbmDir, "Release script: Update gutenberg submodule"); err != nil { + gbSp := sp + gbSp.Dir = filepath.Join(dir, "gutenberg") + gbGit := shell.GitCmd(gbSp) + + if err := gbGit.Fetch(gbBranch); err != nil { return pr, err } - // Run npm ci and npm run bundle - if err := exec.NpmCi(gbmDir, true); err != nil { + if err := gbGit.Switch(gbBranch); err != nil { return pr, err } - if err := exec.NpmRun(gbmDir, true, "bundle"); err != nil { - return pr, err + // Commit the updated Gutenberg submodule ref + if git.IsPorcelain() { + console.Info("Nothing to commit after bundling") + } else { + if err := git.CommitAll("Release script: Update gutenberg submodule"); err != nil { + return pr, err + } } // Commit the updated bundle output - if err := git.CommitAll(gbmDir, "Release script: Update bundle for %s", version); err != nil { + if err := git.CommitAll("Release script: Update bundle for %s", version); err != nil { return pr, err } // Update XCFramework builders project Podfile.lock console.Info("Update XCFramework builders project Podfile.lock") - xcframeworkDir := fmt.Sprintf("%s/ios-xcframework", dir) + xcSp := sp + xcSp.Dir = fmt.Sprintf("%s/ios-xcframework", dir) + bundle := shell.BundlerCmd(xcSp) // Run `bundle install` - if err := exec.BundleInstall(xcframeworkDir, true); err != nil { + if err := bundle.Install(); err != nil { return pr, err } // Run `bundle exec pod install`` - if err := exec.Bundle(xcframeworkDir, true, "exec", "pod", "install"); err != nil { + if err := bundle.PodInstall(); err != nil { return pr, err } // Commit output of bundle commands - if err := git.CommitAll(xcframeworkDir, "Release script: Sync XCFramework `Podfile.lock` with %s", version); err != nil { + if err := git.CommitAll("Release script: Sync XCFramework `Podfile.lock` with %s", version); err != nil { return pr, err } // Update the RELEASE-NOTES.txt and commit output console.Info("Update the release-notes in the mobile package") - chnPath := filepath.Join(gbmDir, "RELEASE-NOTES.txt") + chnPath := filepath.Join(dir, "RELEASE-NOTES.txt") if err := utils.UpdateReleaseNotes(version, chnPath); err != nil { return pr, err } - if err := git.CommitAll(gbmDir, "Release script: Update release notes for version %s", version); err != nil { + if err := git.CommitAll(dir, "Release script: Update release notes for version %s", version); err != nil { return pr, err } @@ -149,7 +169,7 @@ func CreateGbmPR(version, dir string) (gh.PullRequest, error) { }} // Display PR preview - gh.PreviewPr("gutenberg-mobile", gbmDir, &pr) + gh.PreviewPr("gutenberg-mobile", dir, &pr) // Add prompt to confirm PR creation prompt := fmt.Sprintf("\nReady to create the PR on %s/gutenberg?", org) @@ -159,7 +179,7 @@ func CreateGbmPR(version, dir string) (gh.PullRequest, error) { console.Info("Bye 👋") return pr, fmt.Errorf("exiting before creating PR") } - + // Push the branch if err := git.Push(); err != nil { return pr, err From c5961fa92ee9e3ce03994d1eb2d4ab734e0405b9 Mon Sep 17 00:00:00 2001 From: Derek Blank Date: Tue, 17 Oct 2023 14:52:27 +1000 Subject: [PATCH 16/31] Add SetUpstreamTo and AddRemote git interfaces --- cli/pkg/shell/git.go | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/cli/pkg/shell/git.go b/cli/pkg/shell/git.go index 30650eae..d05e9b80 100644 --- a/cli/pkg/shell/git.go +++ b/cli/pkg/shell/git.go @@ -13,6 +13,8 @@ type gitCmds interface { Submodule(...string) error Fetch(...string) error SetRemoteBranches(...string) error + AddRemote(...string) error + SetUpstreamTo(string) bool IsPorcelain() bool } @@ -57,6 +59,16 @@ func (c *client) SetRemoteBranches(args ...string) error { return c.cmd(checkout...) } +func (c *client) AddRemote(args ...string) error { + clone := append([]string{"remote", "add"}, args...) + return c.cmd(clone...) +} + +func (c *client) SetUpstreamTo(args ...string) error { + branch := append([]string{"branch", "--set-upstream-to", "origin"}, args...) + return c.cmd(branch...) +} + func (c *client) IsPorcelain() bool { err := c.cmd("diff", "--exit-code") return err == nil From 9c0a1a1916227ebddf03230a88f9e5e17aad8d9b Mon Sep 17 00:00:00 2001 From: Derek Blank Date: Tue, 17 Oct 2023 14:53:01 +1000 Subject: [PATCH 17/31] Add remote and set upstream to GBM prepare command --- cli/pkg/release/gbm.go | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/cli/pkg/release/gbm.go b/cli/pkg/release/gbm.go index 6768031e..e6078d1c 100644 --- a/cli/pkg/release/gbm.go +++ b/cli/pkg/release/gbm.go @@ -38,18 +38,30 @@ func CreateGbmPR(version, dir string) (gh.PullRequest, error) { return pr, nil } else { console.Info("Cloning Gutenberg Mobile to %s", dir) - err := git.Clone(repo.GetRepoPath("gutenberg-mobile"), "--depth=1", "--recursive", ".") if err != nil { return pr, err } + console.Info("Add remote for %s", org) + err = git.AddRemote("upstream", repo.GetRepoPath("gutenberg-mobile")) + if err != nil { + return pr, err + } + + console.Info("Set upstream to trunk", org) + err = git.SetUpstreamTo("trunk") + if err != nil { + return pr, err + } + console.Info("Checking out branch %s", branch) err = git.Switch("-c", branch) if err != nil { return pr, err } } + // Set up Gutenberg Mobile node environment console.Info("Setting up Node environment") npm := shell.NpmCmd(sp) @@ -64,8 +76,8 @@ func CreateGbmPR(version, dir string) (gh.PullRequest, error) { if err := npm.Run("bundle"); err != nil { return pr, err } - // Update package versions for package.json and package-lock.json + // Update package versions for package.json and package-lock.json console.Info("Updating package versions") pkgs := []string{"./package.json", "./package-lock.json"} for _, pkg := range pkgs { From f5fa9f61653b7b1aed67505280a1a309166bbd5b Mon Sep 17 00:00:00 2001 From: jhnstn Date: Tue, 17 Oct 2023 14:39:20 -0400 Subject: [PATCH 18/31] Place the success message after verifying the PR was created --- cli/cmd/release/prepare/gbm.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cli/cmd/release/prepare/gbm.go b/cli/cmd/release/prepare/gbm.go index b95dfffb..b85a3201 100644 --- a/cli/cmd/release/prepare/gbm.go +++ b/cli/cmd/release/prepare/gbm.go @@ -31,8 +31,8 @@ var gbmCmd = &cobra.Command{ console.Info("Created temporary directory %s", tempDir) pr, err := release.CreateGbmPR(version, tempDir) - console.Info("Created PR %s", pr.Url) - console.ExitIfError(err) + + console.Info("Created PR %s", pr.Url) }, } From 32d354ddfc2ae1893024fcbfce59c1e4b050802b Mon Sep 17 00:00:00 2001 From: jhnstn Date: Tue, 17 Oct 2023 14:42:53 -0400 Subject: [PATCH 19/31] Add prepare cmd utils --- cli/pkg/release/utils.go | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 cli/pkg/release/utils.go diff --git a/cli/pkg/release/utils.go b/cli/pkg/release/utils.go new file mode 100644 index 00000000..a79ab06f --- /dev/null +++ b/cli/pkg/release/utils.go @@ -0,0 +1,24 @@ +package release + +import ( + "path/filepath" + + "github.com/wordpress-mobile/gbm-cli/pkg/shell" + "github.com/wordpress-mobile/gbm-cli/pkg/utils" +) + +func updatePackageJson(dir, version string, pkgs ...string) error { + sp := shell.CmdProps{Dir: dir, Verbose: true} + git := shell.GitCmd(sp) + + for _, pkg := range pkgs { + if err := utils.UpdatePackageVersion(version, filepath.Join(dir, pkg)); err != nil { + return err + } + } + if err := git.CommitAll("Release script: Update package.json versions to %s", version); err != nil { + return err + } + + return nil +} From 41a304e253937daaec8f4e9740dfae4645b9676f Mon Sep 17 00:00:00 2001 From: jhnstn Date: Tue, 17 Oct 2023 14:45:54 -0400 Subject: [PATCH 20/31] Update SetUpstreamTo signature --- cli/pkg/shell/git.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/pkg/shell/git.go b/cli/pkg/shell/git.go index d05e9b80..626eadfa 100644 --- a/cli/pkg/shell/git.go +++ b/cli/pkg/shell/git.go @@ -14,7 +14,7 @@ type gitCmds interface { Fetch(...string) error SetRemoteBranches(...string) error AddRemote(...string) error - SetUpstreamTo(string) bool + SetUpstreamTo(...string) error IsPorcelain() bool } From 0afefb110d99c5088d6efa88aa6782b701c32eff Mon Sep 17 00:00:00 2001 From: jhnstn Date: Tue, 17 Oct 2023 14:46:48 -0400 Subject: [PATCH 21/31] Reorder some events and clean up logging messages --- cli/pkg/release/gbm.go | 54 +++++++++++++++++------------------------- 1 file changed, 22 insertions(+), 32 deletions(-) diff --git a/cli/pkg/release/gbm.go b/cli/pkg/release/gbm.go index e6078d1c..1c28bad2 100644 --- a/cli/pkg/release/gbm.go +++ b/cli/pkg/release/gbm.go @@ -49,7 +49,7 @@ func CreateGbmPR(version, dir string) (gh.PullRequest, error) { return pr, err } - console.Info("Set upstream to trunk", org) + console.Info("Set upstream to trunk", org) err = git.SetUpstreamTo("trunk") if err != nil { return pr, err @@ -73,35 +73,18 @@ func CreateGbmPR(version, dir string) (gh.PullRequest, error) { return pr, err } - if err := npm.Run("bundle"); err != nil { - return pr, err - } - + // Commit package.json and package-lock.json // Update package versions for package.json and package-lock.json console.Info("Updating package versions") - pkgs := []string{"./package.json", "./package-lock.json"} - for _, pkg := range pkgs { - if err := utils.UpdatePackageVersion(version, filepath.Join(dir, pkg)); err != nil { - return pr, err - } - } - - // Commit package.json and package-lock.json - if err := git.CommitAll(dir, "Release script: Update package.json version to %s", version); err != nil { - return pr, err - } - - // Update the release-notes in the mobile package - // if err := git.Submodule("update", "--init", "--recursive", "--depth=1", "--recommend-shallow"); err != nil { - // return pr, err - // } + updatePackageJson(dir, version, "package.json", "package-lock.json") // Create a git client for Gutenberg submodule so the Gutenberg ref can be updated to the correct branch + console.Info("Updating Gutenberg submodule") gbBranch := "rnmobile/release_" + version + if org != repo.WpMobileOrg { + console.Warn("You are not using the %s org. Check the .gitmodules file to make sure the gutenberg submodule is pointing to %s/gutenberg.", repo.WpMobileOrg, org) + } if exists, _ := gh.SearchBranch("gutenberg", gbBranch); (exists == gh.Branch{}) { - if org != repo.WpMobileOrg { - console.Warn("You are not using the %s org. Check the .gitmodules file to make sure the gutenberg submodule is pointing to %s/gutenberg.", repo.WpMobileOrg, org) - } return pr, fmt.Errorf("the Gutenberg branch %s does not exist on %s/gutenberg-mobile", gbBranch, org) } @@ -117,22 +100,29 @@ func CreateGbmPR(version, dir string) (gh.PullRequest, error) { return pr, err } + if err := git.CommitAll("Release script: Update gutenberg submodule"); err != nil { + return pr, err + } + + console.Info("Bundling Gutenberg Mobile") + if err := npm.Run("bundle"); err != nil { + return pr, err + } + // Commit the updated Gutenberg submodule ref if git.IsPorcelain() { console.Info("Nothing to commit after bundling") } else { - if err := git.CommitAll("Release script: Update gutenberg submodule"); err != nil { + // Commit the updated bundle output + if err := git.CommitAll("Release script: Update bundle for %s", version); err != nil { return pr, err } } - // Commit the updated bundle output - if err := git.CommitAll("Release script: Update bundle for %s", version); err != nil { - return pr, err - } - // Update XCFramework builders project Podfile.lock console.Info("Update XCFramework builders project Podfile.lock") + + // set up a shell command for the ios-xcframework directory xcSp := sp xcSp.Dir = fmt.Sprintf("%s/ios-xcframework", dir) bundle := shell.BundlerCmd(xcSp) @@ -159,7 +149,7 @@ func CreateGbmPR(version, dir string) (gh.PullRequest, error) { return pr, err } - if err := git.CommitAll(dir, "Release script: Update release notes for version %s", version); err != nil { + if err := git.CommitAll("Release script: Update release notes for version %s", version); err != nil { return pr, err } @@ -184,7 +174,7 @@ func CreateGbmPR(version, dir string) (gh.PullRequest, error) { gh.PreviewPr("gutenberg-mobile", dir, &pr) // Add prompt to confirm PR creation - prompt := fmt.Sprintf("\nReady to create the PR on %s/gutenberg?", org) + prompt := fmt.Sprintf("\nReady to create the PR on %s/gutenberg-mobile?", org) cont := console.Confirm(prompt) if !cont { From 871037185f78efdb7b7470636c44cd34cdaadf5a Mon Sep 17 00:00:00 2001 From: jhnstn Date: Mon, 16 Oct 2023 19:14:00 -0400 Subject: [PATCH 22/31] Add a cmd utils package --- cli/cmd/utils/utils.go | 47 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 cli/cmd/utils/utils.go diff --git a/cli/cmd/utils/utils.go b/cli/cmd/utils/utils.go new file mode 100644 index 00000000..747cee2b --- /dev/null +++ b/cli/cmd/utils/utils.go @@ -0,0 +1,47 @@ +package utils + +import ( + "fmt" + "os" + + "github.com/wordpress-mobile/gbm-cli/pkg/console" + "github.com/wordpress-mobile/gbm-cli/pkg/utils" +) + +func GetVersionArg(args []string) (string, error) { + if len(args) == 0 { + return "", fmt.Errorf("missing version") + } + return utils.NormalizeVersion(args[0]) +} + +func SetTempDir() (string, error) { + tempDir, err := os.MkdirTemp("", "gbm-") + if err != nil { + return "", err + } + return tempDir, nil +} + +func CleanupTempDir(tempDir string) error { + console.Info("Cleaning up temporary directory %s", tempDir) + err := os.RemoveAll(tempDir) + if err != nil { + return err + } + return nil +} + +func ExitIfErrorHandler(deferred func()) func(error, int) { + return func(err error, code int) { + if err != nil { + console.Error(err) + + os.Exit(func() int { + defer deferred() + return code + }()) + + } + } +} From 254ae1199233f4c579d705d7ce2da7d1f25b27d6 Mon Sep 17 00:00:00 2001 From: jhnstn Date: Mon, 16 Oct 2023 19:14:44 -0400 Subject: [PATCH 23/31] Move temp directory helpers to the cmd utils --- cli/pkg/utils/utils.go | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/cli/pkg/utils/utils.go b/cli/pkg/utils/utils.go index 8b3c82a9..7ffe8626 100644 --- a/cli/pkg/utils/utils.go +++ b/cli/pkg/utils/utils.go @@ -7,8 +7,6 @@ import ( "os" "regexp" "time" - - "github.com/wordpress-mobile/gbm-cli/pkg/console" ) func ValidateVersion(version string) bool { @@ -43,23 +41,6 @@ func NormalizeVersion(version string) (string, error) { return v, nil } -func SetTempDir() (string, error) { - tempDir, err := os.MkdirTemp("", "gbm-") - if err != nil { - return "", err - } - return tempDir, nil -} - -func CleanupTempDir(tempDir string) error { - console.Info("Cleaning up temporary directory %s", tempDir) - err := os.RemoveAll(tempDir) - if err != nil { - return err - } - return nil -} - func UpdatePackageVersion(version, path string) error { f, err := os.Open(path) if err != nil { From 8b705b5edcc3b722a2189034c1984557104e3f9d Mon Sep 17 00:00:00 2001 From: jhnstn Date: Mon, 16 Oct 2023 19:17:19 -0400 Subject: [PATCH 24/31] Deprecate the console error functions --- cli/pkg/console/console.go | 6 ++++++ cli/pkg/release/gb.go | 5 +++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/cli/pkg/console/console.go b/cli/pkg/console/console.go index 37da88a0..741da23c 100644 --- a/cli/pkg/console/console.go +++ b/cli/pkg/console/console.go @@ -78,6 +78,12 @@ func Warn(format string, args ...interface{}) { color.Unset() } +func Error(err error) { + red := color.New(color.FgRed).SprintfFunc() + l.Printf(red("\n" + err.Error())) + color.Unset() +} + func Confirm(ask string) bool { reader := bufio.NewReader(os.Stdin) diff --git a/cli/pkg/release/gb.go b/cli/pkg/release/gb.go index 7f2fa1bc..7be30765 100644 --- a/cli/pkg/release/gb.go +++ b/cli/pkg/release/gb.go @@ -20,8 +20,9 @@ func CreateGbPR(version, dir string) (gh.PullRequest, error) { git := shell.GitCmd(shellProps) org, err := repo.GetOrg("gutenberg") - console.ExitIfError(err) - + if err != nil { + return pr, err + } branch := "rnmobile/release_" + version console.Info("Checking if branch %s exists", branch) From 53b59f24af90eaacf2fb948df3396ce76b890ab4 Mon Sep 17 00:00:00 2001 From: jhnstn Date: Mon, 16 Oct 2023 19:22:12 -0400 Subject: [PATCH 25/31] Use the cmd utils to exit on error --- cli/cmd/release/prepare/gb.go | 21 ++++++++++++--------- cli/cmd/release/prepare/gbm.go | 19 +++++++++++-------- cli/cmd/release/prepare/root.go | 26 +++++++++++++------------- cli/cmd/release/root.go | 7 ++++++- cli/cmd/render/aztec.go | 3 +-- cli/cmd/render/checklist.go | 4 ++-- cli/cmd/render/root.go | 3 +++ cli/pkg/console/console.go | 3 +++ 8 files changed, 51 insertions(+), 35 deletions(-) diff --git a/cli/cmd/release/prepare/gb.go b/cli/cmd/release/prepare/gb.go index 2b757be9..89815143 100644 --- a/cli/cmd/release/prepare/gb.go +++ b/cli/cmd/release/prepare/gb.go @@ -1,37 +1,40 @@ package prepare import ( + "errors" + "github.com/spf13/cobra" + "github.com/wordpress-mobile/gbm-cli/cmd/utils" "github.com/wordpress-mobile/gbm-cli/pkg/console" "github.com/wordpress-mobile/gbm-cli/pkg/gbm" "github.com/wordpress-mobile/gbm-cli/pkg/release" - "github.com/wordpress-mobile/gbm-cli/pkg/utils" ) var gbCmd = &cobra.Command{ Use: "gb", Short: "Prepare Gutenberg for a mobile release", Long: `Use this command to prepare a Gutenberg release PR`, - Run: func(cmd *cobra.Command, args []string) { - version, err := getVersionArg(args) - console.ExitIfError(err) + Run: func(cc *cobra.Command, args []string) { + + version, err := utils.GetVersionArg(args) + exitIfError(err, 1) // Validate Aztec version if valid := gbm.ValidateAztecVersions(); !valid { - console.ExitError("Aztec versions are not valid") + exitIfError(errors.New("invalid Aztec versions found"), 1) } console.Info("Preparing Gutenberg for release %s", version) - tempDir, err := utils.SetTempDir() - console.ExitIfError(err) + tempDir, err = utils.SetTempDir() + exitIfError(err, 1) - defer utils.CleanupTempDir(tempDir) + defer cleanup() console.Info("Created temporary directory %s", tempDir) pr, err := release.CreateGbPR(version, tempDir) - console.ExitIfError(err) + exitIfError(err, 1) console.Info("Created PR %s", pr.Url) }, diff --git a/cli/cmd/release/prepare/gbm.go b/cli/cmd/release/prepare/gbm.go index b85a3201..03e1cda8 100644 --- a/cli/cmd/release/prepare/gbm.go +++ b/cli/cmd/release/prepare/gbm.go @@ -1,11 +1,17 @@ package prepare import ( + "errors" + "github.com/spf13/cobra" + "github.com/wordpress-mobile/gbm-cli/cmd/utils" "github.com/wordpress-mobile/gbm-cli/pkg/console" "github.com/wordpress-mobile/gbm-cli/pkg/gbm" +<<<<<<< HEAD "github.com/wordpress-mobile/gbm-cli/pkg/release" "github.com/wordpress-mobile/gbm-cli/pkg/utils" +======= +>>>>>>> bf3c626 (Use the cmd utils to exit on error) ) var gbmCmd = &cobra.Command{ @@ -13,26 +19,23 @@ var gbmCmd = &cobra.Command{ Short: "Prepare Gutenberg Mobile release", Long: `Use this command to prepare a Gutenberg Mobile release PR`, Run: func(cmd *cobra.Command, args []string) { - version, err := getVersionArg(args) - console.ExitIfError(err) + version, err := utils.GetVersionArg(args) + exitIfError(err, 1) // Validate Aztec version if valid := gbm.ValidateAztecVersions(); !valid { - console.ExitError("Aztec versions are not valid") + exitIfError(errors.New("the Aztec versions are not valid"), 1) } console.Info("Preparing Gutenberg Mobile for release %s", version) tempDir, err := utils.SetTempDir() - console.ExitIfError(err) + exitIfError(err, 1) defer utils.CleanupTempDir(tempDir) console.Info("Created temporary directory %s", tempDir) - pr, err := release.CreateGbmPR(version, tempDir) - console.ExitIfError(err) - - console.Info("Created PR %s", pr.Url) + exitIfError(errors.New("not implemented"), 1) }, } diff --git a/cli/cmd/release/prepare/root.go b/cli/cmd/release/prepare/root.go index 22bff1cb..7734f003 100644 --- a/cli/cmd/release/prepare/root.go +++ b/cli/cmd/release/prepare/root.go @@ -1,13 +1,14 @@ package prepare import ( - "fmt" - "github.com/spf13/cobra" - "github.com/wordpress-mobile/gbm-cli/pkg/console" - "github.com/wordpress-mobile/gbm-cli/pkg/utils" + "github.com/wordpress-mobile/gbm-cli/cmd/utils" ) +var tempDir string +var cleanup func() +var exitIfError func(error, int) + var PrepareCmd = &cobra.Command{ Use: "prepare", Short: "Prepare for a release", @@ -15,18 +16,17 @@ var PrepareCmd = &cobra.Command{ func Execute() { err := PrepareCmd.Execute() - console.ExitIfError(err) + exitIfError(err, 1) } func init() { - PrepareCmd.AddCommand(gbmCmd) - PrepareCmd.AddCommand(gbCmd) -} - -func getVersionArg(args []string) (string, error) { - if len(args) == 0 { - return "", fmt.Errorf("missing version") + cleanup = func() { + if tempDir != "" { + utils.CleanupTempDir(tempDir) + } } + exitIfError = utils.ExitIfErrorHandler(cleanup) - return utils.NormalizeVersion(args[0]) + PrepareCmd.AddCommand(gbmCmd) + PrepareCmd.AddCommand(gbCmd) } diff --git a/cli/cmd/release/root.go b/cli/cmd/release/root.go index 257e3654..6cb98ac9 100644 --- a/cli/cmd/release/root.go +++ b/cli/cmd/release/root.go @@ -1,6 +1,8 @@ package release import ( + "os" + "github.com/spf13/cobra" "github.com/wordpress-mobile/gbm-cli/cmd/release/prepare" "github.com/wordpress-mobile/gbm-cli/pkg/console" @@ -13,7 +15,10 @@ var ReleaseCmd = &cobra.Command{ func Execute() { err := ReleaseCmd.Execute() - console.ExitIfError(err) + if err != nil { + console.Error(err) + os.Exit(1) + } } func init() { diff --git a/cli/cmd/render/aztec.go b/cli/cmd/render/aztec.go index d1bfa5ea..304eb952 100644 --- a/cli/cmd/render/aztec.go +++ b/cli/cmd/render/aztec.go @@ -10,8 +10,7 @@ var AztecCmd = &cobra.Command{ Short: "Render the steps for upgrading Aztec", Run: func(cmd *cobra.Command, args []string) { result, err := renderAztecSteps(false) - - console.ExitIfError(err) + exitIfError(err, 1) if writeToClipboard { console.Clipboard(result) diff --git a/cli/cmd/render/checklist.go b/cli/cmd/render/checklist.go index 6f4c07f8..f068689f 100644 --- a/cli/cmd/render/checklist.go +++ b/cli/cmd/render/checklist.go @@ -39,7 +39,7 @@ var ChecklistCmd = &cobra.Command{ vv := utils.ValidateVersion(version) if !vv { - console.ExitError("%v is not a valid version. Versions must have a `Major.Minor.Patch` form", version) + exitIfError(fmt.Errorf("%v is not a valid version. Versions must have a `Major.Minor.Patch` form", version), 1) } // For now let's assume we should include the Aztec steps unless explicitly checking if the versions are valid. @@ -76,7 +76,7 @@ var ChecklistCmd = &cobra.Command{ } result, err := render.RenderTasks(t) - console.ExitIfError(err) + exitIfError(err, 1) if writeToClipboard { console.Clipboard(result) diff --git a/cli/cmd/render/root.go b/cli/cmd/render/root.go index 05ec78a3..53a52899 100644 --- a/cli/cmd/render/root.go +++ b/cli/cmd/render/root.go @@ -4,9 +4,11 @@ import ( "os" "github.com/spf13/cobra" + "github.com/wordpress-mobile/gbm-cli/cmd/utils" ) var writeToClipboard bool +var exitIfError func(error, int) // rootCmd represents the render command var RenderCmd = &cobra.Command{ @@ -25,6 +27,7 @@ var RenderCmd = &cobra.Command{ } func init() { + exitIfError = utils.ExitIfErrorHandler(func() {}) RenderCmd.AddCommand(ChecklistCmd) RenderCmd.AddCommand(AztecCmd) RenderCmd.PersistentFlags().BoolVar(&writeToClipboard, "c", false, "Send output to clipboard") diff --git a/cli/pkg/console/console.go b/cli/pkg/console/console.go index 741da23c..27966b84 100644 --- a/cli/pkg/console/console.go +++ b/cli/pkg/console/console.go @@ -19,12 +19,14 @@ func init() { l = log.New(os.Stderr, "", 0) } +// Deprecated func ExitIfError(err error) { if err != nil { ExitError(err.Error() + "\n") } } +// Deprecated func ExitError(format string, args ...interface{}) { if len(args) == 0 { Exit(1, format) @@ -33,6 +35,7 @@ func ExitError(format string, args ...interface{}) { } } +// Deprecated func Exit(code int, format string, args ...interface{}) { red := color.New(color.FgRed).SprintfFunc() l.Printf(red("\n"+format, args...)) From 7da8dbbecf401b66b8d3b786dc8570ba92b5d9b8 Mon Sep 17 00:00:00 2001 From: jhnstn Date: Mon, 16 Oct 2023 19:37:13 -0400 Subject: [PATCH 26/31] Fix how the deferred function is handling cleanup --- cli/cmd/release/prepare/gb.go | 9 +++++++-- cli/cmd/release/prepare/gbm.go | 8 +++++++- cli/cmd/release/prepare/root.go | 9 +-------- cli/cmd/utils/utils.go | 13 ++++++++----- 4 files changed, 23 insertions(+), 16 deletions(-) diff --git a/cli/cmd/release/prepare/gb.go b/cli/cmd/release/prepare/gb.go index 89815143..140a2b5e 100644 --- a/cli/cmd/release/prepare/gb.go +++ b/cli/cmd/release/prepare/gb.go @@ -26,11 +26,16 @@ var gbCmd = &cobra.Command{ console.Info("Preparing Gutenberg for release %s", version) - tempDir, err = utils.SetTempDir() + tempDir, err := utils.SetTempDir() exitIfError(err, 1) - + cleanup := func() { + utils.CleanupTempDir(tempDir) + } defer cleanup() + // Reset the exitIfError to handle the cleanup + exitIfError = utils.ExitIfErrorHandler(cleanup) + console.Info("Created temporary directory %s", tempDir) pr, err := release.CreateGbPR(version, tempDir) diff --git a/cli/cmd/release/prepare/gbm.go b/cli/cmd/release/prepare/gbm.go index 03e1cda8..c4a3c9f2 100644 --- a/cli/cmd/release/prepare/gbm.go +++ b/cli/cmd/release/prepare/gbm.go @@ -32,7 +32,13 @@ var gbmCmd = &cobra.Command{ tempDir, err := utils.SetTempDir() exitIfError(err, 1) - defer utils.CleanupTempDir(tempDir) + cleanup := func() { + utils.CleanupTempDir(tempDir) + } + + // Reset the exitIfError to handle the cleanup + exitIfError = utils.ExitIfErrorHandler(cleanup) + defer cleanup() console.Info("Created temporary directory %s", tempDir) diff --git a/cli/cmd/release/prepare/root.go b/cli/cmd/release/prepare/root.go index 7734f003..a224b063 100644 --- a/cli/cmd/release/prepare/root.go +++ b/cli/cmd/release/prepare/root.go @@ -5,8 +5,6 @@ import ( "github.com/wordpress-mobile/gbm-cli/cmd/utils" ) -var tempDir string -var cleanup func() var exitIfError func(error, int) var PrepareCmd = &cobra.Command{ @@ -20,12 +18,7 @@ func Execute() { } func init() { - cleanup = func() { - if tempDir != "" { - utils.CleanupTempDir(tempDir) - } - } - exitIfError = utils.ExitIfErrorHandler(cleanup) + exitIfError = utils.ExitIfErrorHandler(func() {}) PrepareCmd.AddCommand(gbmCmd) PrepareCmd.AddCommand(gbCmd) diff --git a/cli/cmd/utils/utils.go b/cli/cmd/utils/utils.go index 747cee2b..79619a3c 100644 --- a/cli/cmd/utils/utils.go +++ b/cli/cmd/utils/utils.go @@ -37,11 +37,14 @@ func ExitIfErrorHandler(deferred func()) func(error, int) { if err != nil { console.Error(err) - os.Exit(func() int { - defer deferred() - return code - }()) - + Exit(deferred, code) } } } + +func Exit(deferred func(), code int) { + os.Exit(func() int { + defer deferred() + return code + }()) +} From 73c3499143bacf99b3373b20410d337c8d7d046a Mon Sep 17 00:00:00 2001 From: Derek Blank Date: Wed, 11 Oct 2023 16:17:44 +1000 Subject: [PATCH 27/31] Add git submodule utility functions --- cli/pkg/git/git.go | 111 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 111 insertions(+) create mode 100644 cli/pkg/git/git.go diff --git a/cli/pkg/git/git.go b/cli/pkg/git/git.go new file mode 100644 index 00000000..83207b35 --- /dev/null +++ b/cli/pkg/git/git.go @@ -0,0 +1,111 @@ +package git + +import ( + "fmt" + + "github.com/wordpress-mobile/gbm-cli/pkg/exec" +) + +type Client interface { + Clone(...string) error + Switch(...string) error + CommitAll(string, ...interface{}) error + Push() error + RemoteExists(string, string) bool +} + +type client struct { + dir string + verbose bool +} + +func NewClient(dir string, verbose bool) Client { + return &client{ + dir: dir, + verbose: verbose, + } +} + +func (c *client) Clone(args ...string) error { + cmd := exec.Git(c.dir, c.verbose) + clone := append([]string{"clone"}, args...) + return cmd(clone...) +} + +func (c *client) Switch(args ...string) error { + cmd := exec.Git(c.dir, c.verbose) + swtch := append([]string{"switch"}, args...) + return cmd(swtch...) +} + +func (c *client) CommitAll(format string, args ...interface{}) error { + cmd := exec.Git(c.dir, c.verbose) + message := fmt.Sprintf(format, args...) + return cmd("commit", "-am", message) +} + +func (c *client) Push() error { + cmd := exec.Git(c.dir, c.verbose) + return cmd("push", "origin", "HEAD") +} + +func (c *client) RemoteExists(remote, branch string) bool { + cmd := exec.Git(c.dir, c.verbose) + err := cmd("ls-remote", "--exit-code", "--heads", remote, branch) + return err == nil +} + +func GetSubmodule(r gh.Repo, path string) (*g.Submodule, error) { + w, err := r.Worktree() + if err != nil { + return nil, err + } + + return w.Submodule(path) +} + +func CommitSubmodule(dir, message, submodule string, verbose bool) error { + git := exec.ExecGit(dir, verbose) + + if err := git("add", submodule); err != nil { + return fmt.Errorf("unable to add submodule %s in %s :%s", submodule, dir, err) + } + + if err := git("commit", "-m", message); err != nil { + return fmt.Errorf("unable to commit submodule update %s : %s", submodule, err) + } + return nil +} + +func IsSubmoduleCurrent(s gh.Submodule, expectedHash string) (bool, error) { + // Check if the submodule is porcelain + sr, err := s.Repository() + if clean, err := IsPorcelain(sr); err != nil { + return false, err + } else if !clean { + return false, &NotPorcelainError{fmt.Errorf("submodule %s is not clean", s.Config().Name)} + } + + if err != nil { + return false, err + } + stat, err := s.Status() + if err != nil { + return false, err + } + eh := plumbing.NewHash(expectedHash) + + return stat.Current == eh, nil +} + +func IsPorcelain(r gh.Repo) (bool, error) { + w, err := r.Worktree() + if err != nil { + return false, err + } + status, err := w.Status() + if err != nil { + return false, err + } + return status.IsClean(), nil +} From 8a6485f71523fa8f359fdeaeb8f476b70824abdf Mon Sep 17 00:00:00 2001 From: Derek Blank Date: Thu, 12 Oct 2023 11:00:18 +1000 Subject: [PATCH 28/31] Add Submodule to git client --- cli/pkg/git/git.go | 58 ++++------------------------------------------ 1 file changed, 5 insertions(+), 53 deletions(-) diff --git a/cli/pkg/git/git.go b/cli/pkg/git/git.go index 83207b35..c0d72111 100644 --- a/cli/pkg/git/git.go +++ b/cli/pkg/git/git.go @@ -12,6 +12,7 @@ type Client interface { CommitAll(string, ...interface{}) error Push() error RemoteExists(string, string) bool + Submodule(...string) error } type client struct { @@ -55,57 +56,8 @@ func (c *client) RemoteExists(remote, branch string) bool { return err == nil } -func GetSubmodule(r gh.Repo, path string) (*g.Submodule, error) { - w, err := r.Worktree() - if err != nil { - return nil, err - } - - return w.Submodule(path) -} - -func CommitSubmodule(dir, message, submodule string, verbose bool) error { - git := exec.ExecGit(dir, verbose) - - if err := git("add", submodule); err != nil { - return fmt.Errorf("unable to add submodule %s in %s :%s", submodule, dir, err) - } - - if err := git("commit", "-m", message); err != nil { - return fmt.Errorf("unable to commit submodule update %s : %s", submodule, err) - } - return nil -} - -func IsSubmoduleCurrent(s gh.Submodule, expectedHash string) (bool, error) { - // Check if the submodule is porcelain - sr, err := s.Repository() - if clean, err := IsPorcelain(sr); err != nil { - return false, err - } else if !clean { - return false, &NotPorcelainError{fmt.Errorf("submodule %s is not clean", s.Config().Name)} - } - - if err != nil { - return false, err - } - stat, err := s.Status() - if err != nil { - return false, err - } - eh := plumbing.NewHash(expectedHash) - - return stat.Current == eh, nil -} - -func IsPorcelain(r gh.Repo) (bool, error) { - w, err := r.Worktree() - if err != nil { - return false, err - } - status, err := w.Status() - if err != nil { - return false, err - } - return status.IsClean(), nil +func (c *client) Submodule(args ...string) error { + cmd := exec.Git(c.dir, c.verbose) + submodule := append([]string{"submodule"}, args...) + return cmd(submodule...) } From b449907881f5657e98bc48e77cbd939d55580af5 Mon Sep 17 00:00:00 2001 From: Derek Blank Date: Fri, 13 Oct 2023 14:32:47 +1000 Subject: [PATCH 29/31] Call CreateGbmPR from gbm release subcommand --- cli/cmd/release/prepare/gbm.go | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/cli/cmd/release/prepare/gbm.go b/cli/cmd/release/prepare/gbm.go index c4a3c9f2..a2c8741c 100644 --- a/cli/cmd/release/prepare/gbm.go +++ b/cli/cmd/release/prepare/gbm.go @@ -7,11 +7,7 @@ import ( "github.com/wordpress-mobile/gbm-cli/cmd/utils" "github.com/wordpress-mobile/gbm-cli/pkg/console" "github.com/wordpress-mobile/gbm-cli/pkg/gbm" -<<<<<<< HEAD "github.com/wordpress-mobile/gbm-cli/pkg/release" - "github.com/wordpress-mobile/gbm-cli/pkg/utils" -======= ->>>>>>> bf3c626 (Use the cmd utils to exit on error) ) var gbmCmd = &cobra.Command{ @@ -42,6 +38,9 @@ var gbmCmd = &cobra.Command{ console.Info("Created temporary directory %s", tempDir) - exitIfError(errors.New("not implemented"), 1) + pr, err := release.CreateGbmPR(version, tempDir) + console.Info("Created PR %s", pr.Url) + + console.ExitIfError(err) }, } From a9c15300905608f8494d97b404cf44b1bb22cc2f Mon Sep 17 00:00:00 2001 From: jhnstn Date: Fri, 13 Oct 2023 15:05:45 -0400 Subject: [PATCH 30/31] Update gb.go to use the new shell commands --- cli/pkg/git/git.go | 63 ---------------------------------------------- 1 file changed, 63 deletions(-) delete mode 100644 cli/pkg/git/git.go diff --git a/cli/pkg/git/git.go b/cli/pkg/git/git.go deleted file mode 100644 index c0d72111..00000000 --- a/cli/pkg/git/git.go +++ /dev/null @@ -1,63 +0,0 @@ -package git - -import ( - "fmt" - - "github.com/wordpress-mobile/gbm-cli/pkg/exec" -) - -type Client interface { - Clone(...string) error - Switch(...string) error - CommitAll(string, ...interface{}) error - Push() error - RemoteExists(string, string) bool - Submodule(...string) error -} - -type client struct { - dir string - verbose bool -} - -func NewClient(dir string, verbose bool) Client { - return &client{ - dir: dir, - verbose: verbose, - } -} - -func (c *client) Clone(args ...string) error { - cmd := exec.Git(c.dir, c.verbose) - clone := append([]string{"clone"}, args...) - return cmd(clone...) -} - -func (c *client) Switch(args ...string) error { - cmd := exec.Git(c.dir, c.verbose) - swtch := append([]string{"switch"}, args...) - return cmd(swtch...) -} - -func (c *client) CommitAll(format string, args ...interface{}) error { - cmd := exec.Git(c.dir, c.verbose) - message := fmt.Sprintf(format, args...) - return cmd("commit", "-am", message) -} - -func (c *client) Push() error { - cmd := exec.Git(c.dir, c.verbose) - return cmd("push", "origin", "HEAD") -} - -func (c *client) RemoteExists(remote, branch string) bool { - cmd := exec.Git(c.dir, c.verbose) - err := cmd("ls-remote", "--exit-code", "--heads", remote, branch) - return err == nil -} - -func (c *client) Submodule(args ...string) error { - cmd := exec.Git(c.dir, c.verbose) - submodule := append([]string{"submodule"}, args...) - return cmd(submodule...) -} From 3764813dfeee5e24517180122029bff04645ff52 Mon Sep 17 00:00:00 2001 From: jhnstn Date: Tue, 17 Oct 2023 14:53:13 -0400 Subject: [PATCH 31/31] Update exit on prepare gbm command --- cli/cmd/release/prepare/gbm.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cli/cmd/release/prepare/gbm.go b/cli/cmd/release/prepare/gbm.go index a2c8741c..1c5f0357 100644 --- a/cli/cmd/release/prepare/gbm.go +++ b/cli/cmd/release/prepare/gbm.go @@ -39,8 +39,8 @@ var gbmCmd = &cobra.Command{ console.Info("Created temporary directory %s", tempDir) pr, err := release.CreateGbmPR(version, tempDir) - console.Info("Created PR %s", pr.Url) + exitIfError(err, 1) - console.ExitIfError(err) + console.Info("Created PR %s", pr.Url) }, }