Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add command to prepare GBM release #158

Merged
merged 35 commits into from
Oct 17, 2023
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
c7e7dd4
Add base gbm prepare command
derekblank Oct 11, 2023
db9339b
Add CloneGBM function to git pkg
derekblank Oct 11, 2023
f3bbb6f
Add git submodule utility functions
derekblank Oct 11, 2023
606055c
Update gbm.go
derekblank Oct 11, 2023
0973f88
Merge trunk
derekblank Oct 11, 2023
485270c
Add Submodule to git client
derekblank Oct 12, 2023
d64997c
Update gbm.go steps
derekblank Oct 12, 2023
e00416d
Add UpdateReleaseNotes util
derekblank Oct 12, 2023
3e4d3cc
Add Bundle exec command
derekblank Oct 12, 2023
33ef3ca
Add GBM PR body template
derekblank Oct 12, 2023
368b70c
Add steps to preview and create Gutenberg Mobile PR
derekblank Oct 12, 2023
f90bd9c
Add code comments to gbm prepare command
derekblank Oct 12, 2023
03fa629
Merge remote-tracking branch 'origin' into cli/gbm-prepare
derekblank Oct 13, 2023
e9ee191
Merge remote-tracking branch 'origin' into cli/gbm-prepare
derekblank Oct 13, 2023
ba0a457
Call CreateGbmPR from gbm release subcommand
derekblank Oct 13, 2023
c7318b0
Add shell package for shell based commands
jhnstn Oct 13, 2023
4f4fc6b
Update gb.go to use the new shell commands
jhnstn Oct 13, 2023
0a74d43
Update gbm release package
jhnstn Oct 13, 2023
c5961fa
Add SetUpstreamTo and AddRemote git interfaces
derekblank Oct 17, 2023
9c0a1a1
Add remote and set upstream to GBM prepare command
derekblank Oct 17, 2023
f5fa9f6
Place the success message after verifying the PR was created
jhnstn Oct 17, 2023
32d354d
Add prepare cmd utils
jhnstn Oct 17, 2023
41a304e
Update SetUpstreamTo signature
jhnstn Oct 17, 2023
0afefb1
Reorder some events and clean up logging messages
jhnstn Oct 17, 2023
8710371
Add a cmd utils package
jhnstn Oct 16, 2023
254ae11
Move temp directory helpers to the cmd utils
jhnstn Oct 16, 2023
8b705b5
Deprecate the console error functions
jhnstn Oct 16, 2023
53b59f2
Use the cmd utils to exit on error
jhnstn Oct 16, 2023
7da8dbb
Fix how the deferred function is handling cleanup
jhnstn Oct 16, 2023
73c3499
Add git submodule utility functions
derekblank Oct 11, 2023
8a6485f
Add Submodule to git client
derekblank Oct 12, 2023
b449907
Call CreateGbmPR from gbm release subcommand
derekblank Oct 13, 2023
a9c1530
Update gb.go to use the new shell commands
jhnstn Oct 13, 2023
3764813
Update exit on prepare gbm command
jhnstn Oct 17, 2023
ac2d874
Merge branch 'trunk' into cli/gbm-prepare
jhnstn Oct 17, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions cli/pkg/exec/exec.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
7 changes: 7 additions & 0 deletions cli/pkg/git/git.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ type Client interface {
CommitAll(string, ...interface{}) error
Push() error
RemoteExists(string, string) bool
Submodule(...string) error
}

type client struct {
Expand Down Expand Up @@ -54,3 +55,9 @@ func (c *client) RemoteExists(remote, branch string) bool {
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...)
}
197 changes: 197 additions & 0 deletions cli/pkg/release/gbm.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
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

// 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)

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")
jhnstn marked this conversation as resolved.
Show resolved Hide resolved
if err != nil {
return pr, err
}

console.Info("Checking out branch %s", branch)
err = git.Switch(branch, "-c")
if 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 {
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 {
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
}

if err := exec.NpmRun(gbmDir, true, "bundle"); err != nil {
jhnstn marked this conversation as resolved.
Show resolved Hide resolved
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 {
return pr, err
}

if err := git.CommitAll(gbmDir, "Release script: Update release notes for version %s", version); err != nil {
return pr, err
}

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"
pr.Head.Ref = branch

if err := renderGbmPrBody(version, &pr); err != nil {
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)

if !cont {
console.Info("Bye 👋")
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
}

if pr.Number == 0 {
return pr, fmt.Errorf("failed to create the PR")
}

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
}
16 changes: 16 additions & 0 deletions cli/pkg/utils/change_logs.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
27 changes: 27 additions & 0 deletions cli/templates/release/gbm_pr_body.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
Release for Gutenberg Mobile {{ .Version }}

<!-- ## Related PRs
{{ range .RelatedPRs }}
- {{ .Url }}{{ end }}

## Changes

{{ range .Changes }}
### {{ .Title }}
* PR {{ .PrUrl }}
{{ range $i, $issue := .Issues }}
* Issue {{ $i }}{{ $issue }}{{ end }}
{{ end }} -->


<!-- To determine the changes you can check the RELEASE-NOTES.txt and gutenberg/packages/react-native-editor/CHANGELOG.md files and cross check with the list of commits that are part of the PR -->
## 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.
Loading