Skip to content

Commit

Permalink
Merge pull request #12 from nexthink-oss/feature/branch-creation
Browse files Browse the repository at this point in the history
feat: automatic branch creation on content update
  • Loading branch information
isometry authored May 8, 2023
2 parents 527992b + 146c9a5 commit dc3b18b
Show file tree
Hide file tree
Showing 6 changed files with 232 additions and 59 deletions.
17 changes: 11 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ Flags:
--tag string tag name

Global Flags:
-b, --branch string branch name (default "[local-branch-or-main]")
-b, --branch string target branch name (default "[local-branch-or-main]")
-f, --force force action
-m, --message string message (default "Commit via API")
-o, --owner string repository owner (default "[owner-of-first-github-remote-or-required]")
Expand Down Expand Up @@ -83,13 +83,16 @@ Usage:
ghup content [flags] [<file-spec> ...]

Flags:
-d, --delete stringArray file-path to delete
-h, --help help for update
-s, --separator string separator (default ":")
-u, --update stringArray file-spec to update
--base-branch string base branch name (default: "[remote-default-branch]")
--create-branch create missing target branch (default true)
-d, --delete strings file-path to delete
-h, --help help for content
--pr-title string create pull request iff target branch is created and title is specified
-s, --separator string file-spec separator (default ":")
-u, --update strings file-spec to update

Global Flags:
-b, --branch string branch name (default "[local-branch-or-main]")
-b, --branch string target branch name (default "[local-branch-or-main]")
-f, --force force action
-m, --message string message (default "Commit via API")
-o, --owner string repository owner (default "[owner-of-first-github-remote-or-required]")
Expand All @@ -107,6 +110,8 @@ Each `file-path` provided to the `--delete` flag is a `<remote-target-path>`: th

Unless `--force` is used, content that already matches the remote repository state is ignored.

Note: Due to limitations in the GitHub V4 API, when the target branch does not exist, branch creation and content push will trigger two distinct "push" events.

#### Content Example

Update `.zshrc` in my `dotfiles` repo, adding if 's missing and updating if-and-only-if changed:
Expand Down
76 changes: 69 additions & 7 deletions cmd/content.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,18 @@ var contentCmd = &cobra.Command{
}

func init() {
contentCmd.PersistentFlags().Bool("create-branch", true, "create missing target branch")
viper.BindPFlag("create-branch", contentCmd.PersistentFlags().Lookup("create-branch"))
viper.BindEnv("create-branch", "GHUP_CREATE_BRANCH")

contentCmd.PersistentFlags().String("pr-title", "", "create pull request iff target branch is created and title is specified")
viper.BindPFlag("pr-title", contentCmd.PersistentFlags().Lookup("pr-title"))
viper.BindEnv("pr-title", "GHUP_PR_TITLE")

contentCmd.PersistentFlags().String("base-branch", "", `base branch name (default: "[remote-default-branch])"`)
viper.BindPFlag("base-branch", contentCmd.PersistentFlags().Lookup("base-branch"))
viper.BindEnv("base-branch", "GHUP_BASE_BRANCH")

contentCmd.Flags().StringP("separator", "s", ":", "file-spec separator")
viper.BindPFlag("separator", contentCmd.Flags().Lookup("separator"))

Expand All @@ -49,6 +61,46 @@ func runContentCmd(cmd *cobra.Command, args []string) (err error) {
return fmt.Errorf("invalid separator")
}

repoInfo, err := client.GetRepositoryInfo(owner, repo, branch)
if err != nil {
return err
}

if repoInfo.IsEmpty {
return fmt.Errorf("cannot push to empty repository")
}

targetOid := repoInfo.TargetBranch.Commit
baseBranch := viper.GetString("base-branch")
newBranch := false

if targetOid == "" {
if !viper.GetBool("create-branch") {
return fmt.Errorf("target branch %q does not exist", branch)
}
log.Debugf("creating target branch %q", branch)
if baseBranch == "" {
baseBranch = repoInfo.DefaultBranch.Name
targetOid = repoInfo.DefaultBranch.Commit
log.Debugf("defaulting base branch to %q", baseBranch)
} else {
targetOid, err = client.GetRefOidV4(owner, repo, baseBranch)
if err != nil {
return err
}
}

createRefInput := githubv4.CreateRefInput{
RepositoryID: repoInfo.NodeID,
Name: githubv4.String(fmt.Sprintf("refs/heads/%s", branch)),
Oid: targetOid,
}
if err := client.CreateRefV4(createRefInput); err != nil {
return err
}
newBranch = true
}

updateFiles := append(args, viper.GetStringSlice("update")...)
deleteFiles := viper.GetStringSlice("delete")

Expand Down Expand Up @@ -96,17 +148,12 @@ func runContentCmd(cmd *cobra.Command, args []string) (err error) {
Deletions: &deletions,
}

headOid, err := client.GetHeadOidV4(owner, repo, branch)
if err != nil {
return err
}

message = util.BuildCommitMessage()

input := githubv4.CreateCommitOnBranchInput{
Branch: remote.CommittableBranch(owner, repo, branch),
Message: remote.CommitMessage(message),
ExpectedHeadOid: headOid,
ExpectedHeadOid: targetOid,
FileChanges: &changes,
}

Expand All @@ -115,6 +162,21 @@ func runContentCmd(cmd *cobra.Command, args []string) (err error) {
return err
}

fmt.Println(commitUrl)
if title := viper.GetString("pr-title"); newBranch && title != "" {
log.Debugf("opening pull request from %q to %q", branch, baseBranch)
input := githubv4.CreatePullRequestInput{
RepositoryID: repoInfo.NodeID,
BaseRefName: githubv4.String(baseBranch),
HeadRefName: githubv4.String(branch),
Title: githubv4.String(title),
}
pullRequestUrl, err := client.CreatePullRequestV4(input)
if err != nil {
return err
}
fmt.Println(pullRequestUrl)
} else {
fmt.Println(commitUrl)
}
return
}
2 changes: 1 addition & 1 deletion cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ func init() {
rootCmd.PersistentFlags().StringP("repo", "r", defaultRepo, "repository name")
viper.BindPFlag("repo", rootCmd.PersistentFlags().Lookup("repo"))

rootCmd.PersistentFlags().StringP("branch", "b", defaultBranch, "branch name")
rootCmd.PersistentFlags().StringP("branch", "b", defaultBranch, "target branch name")
viper.BindPFlag("branch", rootCmd.PersistentFlags().Lookup("branch"))
viper.BindEnv("branch", "GHUP_BRANCH", "CHANGE_BRANCH", "BRANCH_NAME", "GIT_BRANCH")

Expand Down
28 changes: 14 additions & 14 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ require (
github.com/apex/log v1.9.0
github.com/go-git/go-git/v5 v5.6.1
github.com/google/go-github/v50 v50.2.0
github.com/shurcooL/githubv4 v0.0.0-20230215024106-420ad0987b9b
github.com/shurcooL/githubv4 v0.0.0-20230424031643-6cea62ecd5a9
github.com/spf13/cobra v1.7.0
github.com/spf13/viper v1.15.0
github.com/whilp/git-urls v1.0.0
Expand All @@ -15,46 +15,46 @@ require (
)

require (
github.com/Microsoft/go-winio v0.6.0 // indirect
github.com/ProtonMail/go-crypto v0.0.0-20230217124315-7d5c6f04bbb8 // indirect
github.com/Microsoft/go-winio v0.6.1 // indirect
github.com/ProtonMail/go-crypto v0.0.0-20230426101702-58e86b294756 // indirect
github.com/acomagu/bufpipe v1.0.4 // indirect
github.com/cloudflare/circl v1.3.2 // indirect
github.com/cloudflare/circl v1.3.3 // indirect
github.com/emirpasic/gods v1.18.1 // indirect
github.com/fatih/color v1.14.1 // indirect
github.com/fatih/color v1.15.0 // indirect
github.com/fsnotify/fsnotify v1.6.0 // indirect
github.com/go-git/gcfg v1.5.0 // indirect
github.com/go-git/go-billy/v5 v5.4.1 // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/google/go-querystring v1.1.0 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/imdario/mergo v0.3.13 // indirect
github.com/imdario/mergo v0.3.15 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
github.com/kevinburke/ssh_config v1.2.0 // indirect
github.com/magiconair/properties v1.8.7 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.17 // indirect
github.com/mattn/go-isatty v0.0.18 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/pelletier/go-toml/v2 v2.0.6 // indirect
github.com/pelletier/go-toml/v2 v2.0.7 // indirect
github.com/pjbgf/sha1cd v0.3.0 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/sergi/go-diff v1.3.1 // indirect
github.com/shurcooL/graphql v0.0.0-20220606043923-3cf50f8a0a29 // indirect
github.com/skeema/knownhosts v1.1.0 // indirect
github.com/spf13/afero v1.9.4 // indirect
github.com/spf13/afero v1.9.5 // indirect
github.com/spf13/cast v1.5.0 // indirect
github.com/spf13/jwalterweatherman v1.1.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/subosito/gotenv v1.4.2 // indirect
github.com/xanzy/ssh-agent v0.3.3 // indirect
golang.org/x/crypto v0.7.0 // indirect
golang.org/x/mod v0.8.0 // indirect
golang.org/x/crypto v0.8.0 // indirect
golang.org/x/mod v0.10.0 // indirect
golang.org/x/net v0.9.0 // indirect
golang.org/x/sys v0.7.0 // indirect
golang.org/x/text v0.9.0 // indirect
golang.org/x/tools v0.6.0 // indirect
golang.org/x/tools v0.8.0 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/protobuf v1.28.1 // indirect
google.golang.org/protobuf v1.30.0 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/warnings.v0 v0.1.2 // indirect
)
Loading

0 comments on commit dc3b18b

Please sign in to comment.