From 794a1980f7c321512091a3005ca32e9624359bed Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Wed, 28 Apr 2021 20:10:55 +0200 Subject: [PATCH] DRAFT1 --- go.mod | 1 + go.sum | 2 + main.go | 174 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 177 insertions(+) create mode 100644 main.go diff --git a/go.mod b/go.mod index c5f3fa1..14780fc 100644 --- a/go.mod +++ b/go.mod @@ -4,5 +4,6 @@ go 1.16 require ( github.com/AlecAivazis/survey/v2 v2.2.12 + github.com/gobwas/glob v0.2.3 github.com/xanzy/go-gitlab v0.48.0 ) diff --git a/go.sum b/go.sum index 21bfec1..a10e862 100644 --- a/go.sum +++ b/go.sum @@ -5,6 +5,8 @@ github.com/Netflix/go-expect v0.0.0-20180615182759-c93bf25de8e8/go.mod h1:oX5x61 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= +github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= +github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk= diff --git a/main.go b/main.go new file mode 100644 index 0000000..2f5306e --- /dev/null +++ b/main.go @@ -0,0 +1,174 @@ +package main + +import ( + "fmt" + "os" + + "github.com/AlecAivazis/survey/v2" + "github.com/gobwas/glob" + "github.com/xanzy/go-gitlab" +) + +var ( + Version = "dev" +) + +func main() { + // default values + var token string = "XXXXXXXXXXXXXXXXXX" + var baseURL string = "https://gitlab.com" + var orgName string = "scr-test" + var repoExclude string = "exclude*" + var pullBaseBranch string = "devel*" + + // ask for infos + if err := survey.AskOne(&survey.Input{Message: "GitLab Base URL:", Default: baseURL}, &baseURL); err != nil { + os.Exit(1) + } + if err := survey.AskOne(&survey.Input{Message: "GitLab Token:", Default: token}, &token); err != nil { + os.Exit(1) + } + if err := survey.AskOne(&survey.Input{Message: "Group Name:", Default: orgName}, &orgName); err != nil { + os.Exit(1) + } + if err := survey.AskOne(&survey.Input{Message: "Ignore Repo with patter:", Default: repoExclude}, &repoExclude); err != nil { + os.Exit(1) + } + if err := survey.AskOne(&survey.Input{Message: "Merge Branches with patter:", Default: pullBaseBranch}, &pullBaseBranch); err != nil { + os.Exit(1) + } + + // Compile glob regex + excludeRule, err := glob.Compile(repoExclude) + if err != nil { + error(2, "not able to compile regex '%s'", repoExclude) + } + baseBranchRule, err := glob.Compile(pullBaseBranch) + if err != nil { + error(3, "not able to compile regex '%s'", pullBaseBranch) + } + + // connect to gitlab + client, err := gitlab.NewClient(token, gitlab.WithBaseURL(baseURL)) + if err != nil { + error(4, "Could not create Client: %v\n", err) + } + + // discover GroupID by name + org, _, err := client.Groups.GetGroup(orgName) + if err != nil { + error(5, "Error cant get information for Organisation \"%s\": %v", orgName, err) + } + fmt.Printf("Found \"%s\"\n", org.WebURL) + + repoList, _, err := client.Groups.ListGroupProjects(org.ID, &gitlab.ListGroupProjectsOptions{ + ListOptions: gitlab.ListOptions{}, + Archived: optBool(false), + IncludeSubgroups: optBool(false), + }) + if err != nil { + error(5, "Could not obtain repo list: %v\n", err) + } + + for i := range repoList { + if !excludeRule.Match(repoList[i].Name) { + // for each NOT excluded repo ... + threatRepo(baseBranchRule, client, repoList[i]) + } + } +} + +// threatRepo create (if needed) & merge "develop[ment]" branch in DefaultBranch +func threatRepo(baseBranchRule glob.Glob, client *gitlab.Client, repo *gitlab.Project) { + fmt.Printf("Handle Repository '%s'\n", repo.Name) + + // TODO: handle pagination + branchList, _, err := client.Branches.ListBranches(repo.ID, nil) + if err != nil { + error(10, "Could not obtain branch list from '%s': %v\n", repo.Name, err) + } + + 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 + } + + // 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)), // TODO: better title (required) + 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 + } + 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 + } + + if len(changes.Changes) == 0 { + fmt.Printf(" pull '%s' does not conatin changes, skiping\n", changes.WebURL) + return + } + } + + // TODO: check and wait for pull to get ready + + // merge pull + if pull, _, err := client.MergeRequests.AcceptMergeRequest(repo.ID, pullList[0].IID, nil); err != nil { + fmt.Printf(" pull '%s' can not be merged: %v\n", pullList[0].WebURL, err) + } else { + fmt.Printf(" pull '%s' got merged successfully\n", pull.WebURL) + } + + // since we had a match go to next repo ... + return + } + } +} + +func optBool(val bool) *bool { + return &val +} + +func optString(val string) *string { + return &val +} + +func error(id int, format string, a ...interface{}) { + fmt.Printf(format, a...) + os.Exit(id) +}