diff --git a/.gitignore b/.gitignore index 3675f62..767faf6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ /vendor .idea/ .cache.json +GitLab_MergeDevel2Default diff --git a/go.mod b/go.mod index 14780fc..394e83b 100644 --- a/go.mod +++ b/go.mod @@ -5,5 +5,6 @@ go 1.16 require ( github.com/AlecAivazis/survey/v2 v2.2.12 github.com/gobwas/glob v0.2.3 + github.com/urfave/cli/v2 v2.3.0 github.com/xanzy/go-gitlab v0.48.0 ) diff --git a/go.sum b/go.sum index a10e862..522e31d 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,10 @@ github.com/AlecAivazis/survey/v2 v2.2.12 h1:5a07y93zA6SZ09gOa9wLVLznF5zTJMQ+pJ3cZK4IuO8= github.com/AlecAivazis/survey/v2 v2.2.12/go.mod h1:6d4saEvBsfSHXeN1a5OA5m2+HJ2LuVokllnC77pAIKI= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/Netflix/go-expect v0.0.0-20180615182759-c93bf25de8e8 h1:xzYJEypr/85nBpB11F9br+3HUrpgb+fcm5iADzXXYEw= github.com/Netflix/go-expect v0.0.0-20180615182759-c93bf25de8e8/go.mod h1:oX5x61PbNXchhh0oikYAH+4Pcfw5LKv21+Jnpr6r6Pc= +github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY= +github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -31,11 +34,17 @@ github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b h1:j7+1HpAFS1zy5+Q4qx1f github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q= +github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo= +github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/urfave/cli/v2 v2.3.0 h1:qph92Y649prgesehzOrQjdWyxFOp/QVM+6imKHad91M= +github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI= github.com/xanzy/go-gitlab v0.48.0 h1:RP9r4pMDIwE2fbtc+QYiC1euDsPGHcAjPkhje4X3QPU= github.com/xanzy/go-gitlab v0.48.0/go.mod h1:UW8JJbyBbqtOyBYNHRo261IRdHUFJr2m0y0z1xUiu+E= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= @@ -65,3 +74,4 @@ google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/main.go b/main.go index a8484a4..6d50416 100644 --- a/main.go +++ b/main.go @@ -1,281 +1,32 @@ package main import ( - "encoding/json" - "fmt" - "io/ioutil" "os" - "time" - "github.com/AlecAivazis/survey/v2" - "github.com/gobwas/glob" - "github.com/xanzy/go-gitlab" + "code.obermui.de/6543/GitLab_MergeDevel2Default/merge" + "code.obermui.de/6543/GitLab_MergeDevel2Default/utils" + + "github.com/urfave/cli/v2" ) var ( Version = "dev" ) -type Cache struct { - token string `json:"token"` - baseURL string `json:"baseURL"` - orgName string `json:"orgName"` - repoExclude string `json:"repoExclude"` - pullBaseBranch string `json:"pullBaseBranch"` -} - -type pullCache struct { - repoID int - iid int - webURL string -} - -const ( - perPage = 10 - waitSecForMerge = 3 -) - -func loadCache() (cache *Cache) { - // default values - cache = &Cache{ - token: "XXXXXXXXXXXXXXXXXX", - baseURL: "https://gitlab.com", - orgName: "organisation", - repoExclude: "exclude*", - pullBaseBranch: "devel*", - } - - data, err := ioutil.ReadFile(".cache.json") - if err != nil { - return - } - var c Cache - if err := json.Unmarshal(data, &c); err != nil { - return - } - return &c -} - -func saveCache(cache *Cache) { - data, err := json.Marshal(cache) - if err != nil { - fmt.Printf("ERROR: marshal cache: %v\n", err) - } - err = ioutil.WriteFile(".cache.json", data, 0600) - if err != nil { - fmt.Printf("ERROR: save cache: %v\n", err) - } -} - func main() { - values := loadCache() - // ask for infos - if err := survey.AskOne(&survey.Input{Message: "GitLab Base URL:", Default: values.baseURL}, &values.baseURL); err != nil { - os.Exit(1) - } - if err := survey.AskOne(&survey.Input{Message: "GitLab Token:", Default: values.token}, &values.token); err != nil { - os.Exit(1) - } - if err := survey.AskOne(&survey.Input{Message: "Group Name:", Default: values.orgName}, &values.orgName); err != nil { - os.Exit(1) - } - if err := survey.AskOne(&survey.Input{Message: "Ignore Repo with patter:", Default: values.repoExclude}, &values.repoExclude); err != nil { - os.Exit(1) - } - if err := survey.AskOne(&survey.Input{Message: "Merge Branches with patter:", Default: values.pullBaseBranch}, &values.pullBaseBranch); err != nil { - os.Exit(1) + app := cli.NewApp() + app.Name = "vv-tool" + app.Usage = "VV command line tool to interact with Gitlab" + app.Description = "cli tool for VV to manage things" + app.Version = Version + app.Commands = []*cli.Command{ + &merge.CmdMerge, } - saveCache(values) - - // Compile glob regex - excludeRule, err := glob.Compile(values.repoExclude) + app.EnableBashCompletion = true + err := app.Run(os.Args) if err != nil { - error(2, "not able to compile regex '%s'", values.repoExclude) - } - baseBranchRule, err := glob.Compile(values.pullBaseBranch) - if err != nil { - error(3, "not able to compile regex '%s'", values.pullBaseBranch) - } - - // connect to gitlab - client, err := gitlab.NewClient(values.token, gitlab.WithBaseURL(values.baseURL)) - if err != nil { - error(4, "Could not create Client: %v\n", err) - } - - // discover GroupID by name - org, _, err := client.Groups.GetGroup(values.orgName) - if err != nil { - error(5, "Error cant get information for Organisation \"%s\": %v", values.orgName, err) - } - fmt.Printf("Found \"%s\"\n", org.WebURL) - - var repoList []*gitlab.Project - var page = 1 - fmt.Printf("Retrieving repository list...\n") - for { - fmt.Printf("%d", page) - repos, _, err := client.Groups.ListGroupProjects(org.ID, &gitlab.ListGroupProjectsOptions{ - ListOptions: gitlab.ListOptions{PerPage: 10, Page: page}, - Archived: optBool(false), - IncludeSubgroups: optBool(false), - }) - if err != nil { - error(5, "Could not obtain repo list: %v\n", err) - } - - repoList = append(repoList, repos...) - if len(repos) < 10 { - break - } - page++ - } - fmt.Println() - - var pullsToMergeCache []*pullCache - for i := range repoList { - if !excludeRule.Match(repoList[i].Name) { - // for each NOT excluded repo ... - pullsToMergeCache = append(pullsToMergeCache, - threatRepo(baseBranchRule, client, repoList[i]), - ) - } - } - - fmt.Printf("\n\nwait 3sec for gitlab before merge created pulls\n") - time.Sleep(waitSecForMerge * time.Second) - mergePulls(client, pullsToMergeCache) -} - -// threatRepo create (if needed) & merge "develop[ment]" branch in DefaultBranch -// and return pulls to merge -func threatRepo(baseBranchRule glob.Glob, client *gitlab.Client, repo *gitlab.Project) *pullCache { - fmt.Printf("Handle Repository '%s'\n", repo.Name) - - var branchList []*gitlab.Branch - var page = 1 - for { - bL, _, err := client.Branches.ListBranches(repo.ID, &gitlab.ListBranchesOptions{ - ListOptions: gitlab.ListOptions{ - Page: page, - PerPage: perPage, - }, - }) - if err != nil { - error(10, "Could not obtain branch list from '%s': %v\n", repo.Name, err) - } - - branchList = append(branchList, bL...) - if len(bL) < 10 { - break - } - page++ - } - - fmt.Printf(" found %d branches:", len(branchList)) - fmt.Printf(" ") - for _, b := range branchList { - fmt.Printf("'%s' ", b.Name) - } - fmt.Println() - - for _, baseBranch := range branchList { - if baseBranchRule.Match(baseBranch.Name) && !baseBranch.Default { - fmt.Printf(" found branch '%s'\n", baseBranch.Name) - - // check if source is ahead target branch - diff, _, err := client.Repositories.Compare(repo.ID, &gitlab.CompareOptions{ - From: &repo.DefaultBranch, - To: &baseBranch.Name, - }) - if err != nil { - error(10, "Could not compare '%s'...'%s' at '%s': %v\n", repo.DefaultBranch, baseBranch.Name, repo.Name, err) - } - - if len(diff.Commits) == 0 { - fmt.Printf(" branch '%s' is not ahead of '%s', skiping\n", baseBranch.Name, repo.DefaultBranch) - return nil - } - - // check if pull already exist - pullList, _, err := client.MergeRequests.ListProjectMergeRequests(repo.ID, &gitlab.ListProjectMergeRequestsOptions{ - SourceBranch: &baseBranch.Name, - TargetBranch: &repo.DefaultBranch, - State: optString("opened"), - }) - if err != nil { - error(11, "Could not obtain merge request list from '%s': %v\n", repo.Name, err) - } - - // if not create one - if len(pullList) == 0 { - fmt.Printf(" no existing pull for %s, creating one ...\n", baseBranch.Name) - pull, _, err := client.MergeRequests.CreateMergeRequest(repo.ID, &gitlab.CreateMergeRequestOptions{ - Title: optString(fmt.Sprintf("%s <- %s", repo.DefaultBranch, baseBranch.Name)), - Description: optString("Auto created by https://code.obermui.de/6543/GitLab_MergeDevel2Default"), - SourceBranch: &baseBranch.Name, - TargetBranch: &repo.DefaultBranch, - AllowCollaboration: optBool(true), - }) - if err != nil { - fmt.Printf(" could not create merge request '%s <- %s', skiping '%s'\n", repo.DefaultBranch, baseBranch.Name, repo.Name) - return nil - } - pullList = []*gitlab.MergeRequest{pull} - } else { - // check if changes exist for pull - changes, _, err := client.MergeRequests.GetMergeRequestChanges(repo.ID, pullList[0].IID, nil) - if err != nil { - fmt.Printf(" could not obtain changes of merge request '%s', skiping '%s'\n", pullList[0].WebURL, repo.Name) - return nil - } - - if len(changes.Changes) == 0 { - fmt.Printf(" pull '%s' does not conatin changes, skiping\n", changes.WebURL) - return nil - } - } - - // since we had a match go to next repo ... and return pull - return &pullCache{ - repoID: repo.ID, - iid: pullList[0].IID, - webURL: pullList[0].WebURL, - } - } - } - return nil -} - -func mergePulls(c *gitlab.Client, pulls []*pullCache) { - for i := range pulls { - if pull, _, err := c.MergeRequests.AcceptMergeRequest(pulls[i].repoID, pulls[i].iid, nil); err != nil { - fmt.Printf(" pull '%s' can not be merged: %v\n", pulls[i].webURL, err) - } else { - fmt.Printf(" pull '%s' got merged successfully\n", pull.WebURL) - - if err, _ := c.Branches.DeleteBranch(pulls[i].repoID, pull.SourceBranch); err != nil { - fmt.Printf(" branch '%s' can not be deleted: %v\n", pull.SourceBranch, err) - } else { - fmt.Printf(" branch '%s' successfully deleted\n", pull.SourceBranch) - } - } + utils.Error(1, "Error: %v\n", err) } } - -// optBool return ref of bool - dont ask it is go -func optBool(val bool) *bool { - return &val -} - -// optString return ref of string - dont ask it is go -func optString(val string) *string { - return &val -} - -func error(id int, format string, a ...interface{}) { - fmt.Printf(format, a...) - os.Exit(id) -} diff --git a/merge/merge.go b/merge/merge.go new file mode 100644 index 0000000..0d4b5c1 --- /dev/null +++ b/merge/merge.go @@ -0,0 +1,280 @@ +package merge + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "time" + + "code.obermui.de/6543/GitLab_MergeDevel2Default/utils" + + "github.com/AlecAivazis/survey/v2" + "github.com/gobwas/glob" + "github.com/urfave/cli/v2" + "github.com/xanzy/go-gitlab" +) + +var ( + Version = "dev" +) + +type Cache struct { + token string `json:"token"` + baseURL string `json:"baseURL"` + orgName string `json:"orgName"` + repoExclude string `json:"repoExclude"` + pullBaseBranch string `json:"pullBaseBranch"` +} + +type pullCache struct { + repoID int + iid int + webURL string +} + +const ( + perPage = 10 + waitSecForMerge = 3 +) + +func loadCache() (cache *Cache) { + // default values + cache = &Cache{ + token: "XXXXXXXXXXXXXXXXXX", + baseURL: "https://gitlab.com", + orgName: "organisation", + repoExclude: "exclude*", + pullBaseBranch: "devel*", + } + + data, err := ioutil.ReadFile(".cache.json") + if err != nil { + return + } + var c Cache + if err := json.Unmarshal(data, &c); err != nil { + return + } + return &c +} + +func saveCache(cache *Cache) { + data, err := json.Marshal(cache) + if err != nil { + fmt.Printf("ERROR: marshal cache: %v\n", err) + } + err = ioutil.WriteFile(".cache.json", data, 0600) + if err != nil { + fmt.Printf("ERROR: save cache: %v\n", err) + } +} + +var CmdMerge = cli.Command{ + Name: "merge", + Usage: "run script for merge subcommand", + Description: `run script for merge subcommand`, + Action: runMerge, +} + +// runMerge run script for merge subcommand +func runMerge(ctx *cli.Context) error { + values := loadCache() + + // ask for infos + if err := survey.AskOne(&survey.Input{Message: "GitLab Base URL:", Default: values.baseURL}, &values.baseURL); err != nil { + return err + } + if err := survey.AskOne(&survey.Input{Message: "GitLab Token:", Default: values.token}, &values.token); err != nil { + return err + } + if err := survey.AskOne(&survey.Input{Message: "Group Name:", Default: values.orgName}, &values.orgName); err != nil { + return err + } + if err := survey.AskOne(&survey.Input{Message: "Ignore Repo with patter:", Default: values.repoExclude}, &values.repoExclude); err != nil { + return err + } + if err := survey.AskOne(&survey.Input{Message: "Merge Branches with patter:", Default: values.pullBaseBranch}, &values.pullBaseBranch); err != nil { + return err + } + + saveCache(values) + + // Compile glob regex + excludeRule, err := glob.Compile(values.repoExclude) + if err != nil { + return fmt.Errorf("not able to compile regex '%s'", values.repoExclude) + } + baseBranchRule, err := glob.Compile(values.pullBaseBranch) + if err != nil { + return fmt.Errorf("not able to compile regex '%s'", values.pullBaseBranch) + } + + // connect to gitlab + client, err := gitlab.NewClient(values.token, gitlab.WithBaseURL(values.baseURL)) + if err != nil { + return fmt.Errorf("Could not create Client: %v\n", err) + } + + // discover GroupID by name + org, _, err := client.Groups.GetGroup(values.orgName) + if err != nil { + return fmt.Errorf("Error cant get information for Organisation \"%s\": %v", values.orgName, err) + } + fmt.Printf("Found \"%s\"\n", org.WebURL) + + var repoList []*gitlab.Project + var page = 1 + fmt.Printf("Retrieving repository list...\n") + for { + fmt.Printf("%d", page) + repos, _, err := client.Groups.ListGroupProjects(org.ID, &gitlab.ListGroupProjectsOptions{ + ListOptions: gitlab.ListOptions{PerPage: 10, Page: page}, + Archived: utils.OptBool(false), + IncludeSubgroups: utils.OptBool(false), + }) + if err != nil { + return fmt.Errorf("Could not obtain repo list: %v\n", err) + } + + repoList = append(repoList, repos...) + if len(repos) < 10 { + break + } + page++ + } + fmt.Println() + + var pullsToMergeCache []*pullCache + for i := range repoList { + if !excludeRule.Match(repoList[i].Name) { + // for each NOT excluded repo ... + pull, err := threatRepo(baseBranchRule, client, repoList[i]) + if err != nil { + return err + } + pullsToMergeCache = append(pullsToMergeCache, pull) + } + } + + fmt.Printf("\n\nwait 3sec for gitlab before merge created pulls\n") + time.Sleep(waitSecForMerge * time.Second) + mergePulls(client, pullsToMergeCache) + + return nil +} + +// threatRepo create (if needed) & merge "develop[ment]" branch in DefaultBranch +// and return pulls to merge +func threatRepo(baseBranchRule glob.Glob, client *gitlab.Client, repo *gitlab.Project) (*pullCache, error) { + fmt.Printf("Handle Repository '%s'\n", repo.Name) + + var branchList []*gitlab.Branch + var page = 1 + for { + bL, _, err := client.Branches.ListBranches(repo.ID, &gitlab.ListBranchesOptions{ + ListOptions: gitlab.ListOptions{ + Page: page, + PerPage: perPage, + }, + }) + if err != nil { + utils.Error(10, "Could not obtain branch list from '%s': %v\n", repo.Name, err) + } + + branchList = append(branchList, bL...) + if len(bL) < 10 { + break + } + page++ + } + + fmt.Printf(" found %d branches:", len(branchList)) + fmt.Printf(" ") + for _, b := range branchList { + fmt.Printf("'%s' ", b.Name) + } + fmt.Println() + + for _, baseBranch := range branchList { + if baseBranchRule.Match(baseBranch.Name) && !baseBranch.Default { + fmt.Printf(" found branch '%s'\n", baseBranch.Name) + + // check if source is ahead target branch + diff, _, err := client.Repositories.Compare(repo.ID, &gitlab.CompareOptions{ + From: &repo.DefaultBranch, + To: &baseBranch.Name, + }) + if err != nil { + return nil, fmt.Errorf("could not compare '%s'...'%s' at '%s': %v", repo.DefaultBranch, baseBranch.Name, repo.Name, err) + } + + if len(diff.Commits) == 0 { + fmt.Printf(" branch '%s' is not ahead of '%s', skiping\n", baseBranch.Name, repo.DefaultBranch) + return nil, nil + } + + // check if pull already exist + pullList, _, err := client.MergeRequests.ListProjectMergeRequests(repo.ID, &gitlab.ListProjectMergeRequestsOptions{ + SourceBranch: &baseBranch.Name, + TargetBranch: &repo.DefaultBranch, + State: utils.OptString("opened"), + }) + if err != nil { + return nil, fmt.Errorf("could not obtain merge request list from '%s': %v", repo.Name, err) + } + + // if not create one + if len(pullList) == 0 { + fmt.Printf(" no existing pull for %s, creating one ...\n", baseBranch.Name) + pull, _, err := client.MergeRequests.CreateMergeRequest(repo.ID, &gitlab.CreateMergeRequestOptions{ + Title: utils.OptString(fmt.Sprintf("%s <- %s", repo.DefaultBranch, baseBranch.Name)), + Description: utils.OptString("Auto created by https://code.obermui.de/6543/GitLab_MergeDevel2Default"), + SourceBranch: &baseBranch.Name, + TargetBranch: &repo.DefaultBranch, + AllowCollaboration: utils.OptBool(true), + }) + if err != nil { + fmt.Printf(" could not create merge request '%s <- %s', skiping '%s'\n", repo.DefaultBranch, baseBranch.Name, repo.Name) + return nil, nil + } + pullList = []*gitlab.MergeRequest{pull} + } else { + // check if changes exist for pull + changes, _, err := client.MergeRequests.GetMergeRequestChanges(repo.ID, pullList[0].IID, nil) + if err != nil { + fmt.Printf(" could not obtain changes of merge request '%s', skiping '%s'\n", pullList[0].WebURL, repo.Name) + return nil, nil + } + + if len(changes.Changes) == 0 { + fmt.Printf(" pull '%s' does not conatin changes, skiping\n", changes.WebURL) + return nil, nil + } + } + + // since we had a match go to next repo ... and return pull + return &pullCache{ + repoID: repo.ID, + iid: pullList[0].IID, + webURL: pullList[0].WebURL, + }, nil + } + } + return nil, nil +} + +func mergePulls(c *gitlab.Client, pulls []*pullCache) { + for i := range pulls { + if pull, _, err := c.MergeRequests.AcceptMergeRequest(pulls[i].repoID, pulls[i].iid, nil); err != nil { + fmt.Printf(" pull '%s' can not be merged: %v\n", pulls[i].webURL, err) + } else { + fmt.Printf(" pull '%s' got merged successfully\n", pull.WebURL) + + if err, _ := c.Branches.DeleteBranch(pulls[i].repoID, pull.SourceBranch); err != nil { + fmt.Printf(" branch '%s' can not be deleted: %v\n", pull.SourceBranch, err) + } else { + fmt.Printf(" branch '%s' successfully deleted\n", pull.SourceBranch) + } + } + } +} diff --git a/utils/utils.go b/utils/utils.go new file mode 100644 index 0000000..536ae4e --- /dev/null +++ b/utils/utils.go @@ -0,0 +1,22 @@ +package utils + +import ( + "fmt" + "os" +) + +// OptBool return ref of bool - dont ask it is go +func OptBool(val bool) *bool { + return &val +} + +// OptString return ref of string - dont ask it is go +func OptString(val string) *string { + return &val +} + +// Error format the error and exec the program with given status +func Error(status int, format string, a ...interface{}) { + fmt.Printf(format, a...) + os.Exit(status) +}