Refactor from script to cli tool #9
259
main.go
259
main.go
@ -1,268 +1,13 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"code.obermui.de/6543/GitLab_MergeDevel2Default/utils"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"github.com/gobwas/glob"
|
||||
"github.com/xanzy/go-gitlab"
|
||||
"code.obermui.de/6543/GitLab_MergeDevel2Default/merge"
|
||||
)
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
saveCache(values)
|
||||
|
||||
// Compile glob regex
|
||||
excludeRule, err := glob.Compile(values.repoExclude)
|
||||
if err != nil {
|
||||
utils.Error(2, "not able to compile regex '%s'", values.repoExclude)
|
||||
}
|
||||
baseBranchRule, err := glob.Compile(values.pullBaseBranch)
|
||||
if err != nil {
|
||||
utils.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 {
|
||||
utils.Error(4, "Could not create Client: %v\n", err)
|
||||
}
|
||||
|
||||
// discover GroupID by name
|
||||
org, _, err := client.Groups.GetGroup(values.orgName)
|
||||
if err != nil {
|
||||
utils.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: utils.OptBool(false),
|
||||
IncludeSubgroups: utils.OptBool(false),
|
||||
})
|
||||
if err != nil {
|
||||
utils.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 {
|
||||
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 {
|
||||
utils.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: utils.OptString("opened"),
|
||||
})
|
||||
if err != nil {
|
||||
utils.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: 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
|
||||
}
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
merge.RunMerge()
|
||||
}
|
||||
|
267
merge/merge.go
267
merge/merge.go
@ -1,2 +1,269 @@
|
||||
package merge
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"code.obermui.de/6543/GitLab_MergeDevel2Default/utils"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"github.com/gobwas/glob"
|
||||
"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)
|
||||
}
|
||||
}
|
||||
|
||||
// RunMerge run script for merge subcommand
|
||||
func RunMerge() {
|
||||
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)
|
||||
}
|
||||
|
||||
saveCache(values)
|
||||
|
||||
// Compile glob regex
|
||||
excludeRule, err := glob.Compile(values.repoExclude)
|
||||
if err != nil {
|
||||
utils.Error(2, "not able to compile regex '%s'", values.repoExclude)
|
||||
}
|
||||
baseBranchRule, err := glob.Compile(values.pullBaseBranch)
|
||||
if err != nil {
|
||||
utils.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 {
|
||||
utils.Error(4, "Could not create Client: %v\n", err)
|
||||
}
|
||||
|
||||
// discover GroupID by name
|
||||
org, _, err := client.Groups.GetGroup(values.orgName)
|
||||
if err != nil {
|
||||
utils.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: utils.OptBool(false),
|
||||
IncludeSubgroups: utils.OptBool(false),
|
||||
})
|
||||
if err != nil {
|
||||
utils.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 {
|
||||
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 {
|
||||
utils.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: utils.OptString("opened"),
|
||||
})
|
||||
if err != nil {
|
||||
utils.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: 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
|
||||
}
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user