diff --git a/context/config.go b/context/config.go new file mode 100644 index 0000000..9114320 --- /dev/null +++ b/context/config.go @@ -0,0 +1,47 @@ +package context + +import ( + "encoding/json" + "fmt" + "io/ioutil" +) + +type Cache struct { + Token string `json:"Token"` + BaseURL string `json:"BaseURL"` + OrgName string `json:"OrgName"` + RepoExclude string `json:"RepoExclude"` + PullBaseBranch string `json:"PullBaseBranch"` +} + +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) + } +} diff --git a/main.go b/main.go index 6d50416..79a6ae7 100644 --- a/main.go +++ b/main.go @@ -4,6 +4,7 @@ import ( "os" "code.obermui.de/6543/GitLab_MergeDevel2Default/merge" + "code.obermui.de/6543/GitLab_MergeDevel2Default/tags" "code.obermui.de/6543/GitLab_MergeDevel2Default/utils" "github.com/urfave/cli/v2" @@ -22,6 +23,7 @@ func main() { app.Version = Version app.Commands = []*cli.Command{ &merge.CmdMerge, + &tags.CmdCreateTag, } app.EnableBashCompletion = true diff --git a/merge/merge.go b/merge/merge.go index 85f045b..f3d8a49 100644 --- a/merge/merge.go +++ b/merge/merge.go @@ -1,11 +1,10 @@ package merge import ( - "encoding/json" "fmt" - "io/ioutil" "time" + "code.obermui.de/6543/GitLab_MergeDevel2Default/context" "code.obermui.de/6543/GitLab_MergeDevel2Default/utils" "github.com/AlecAivazis/survey/v2" @@ -18,14 +17,6 @@ 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 @@ -37,41 +28,9 @@ const ( 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", + Usage: "Merge develop branch into default branch", Description: `run script for merge subcommand`, Action: runMerge, Flags: []cli.Flag{ @@ -117,23 +76,23 @@ var CmdMerge = cli.Command{ // runMerge run script for merge subcommand func runMerge(ctx *cli.Context) error { - values := loadCache() + values := context.LoadCache() interactive := false if ctx.IsSet("token") { - values.token = ctx.String("token") + values.Token = ctx.String("token") } else { interactive = true - if err := survey.AskOne(&survey.Input{Message: "GitLab Token:", Default: values.token}, &values.token); err != nil { + if err := survey.AskOne(&survey.Input{Message: "GitLab Token:", Default: values.Token}, &values.Token); err != nil { return err } } if ctx.IsSet("url") { - values.baseURL = ctx.String("url") + values.BaseURL = ctx.String("url") } else { interactive = true - if err := survey.AskOne(&survey.Input{Message: "GitLab Base URL:", Default: values.baseURL}, &values.baseURL); err != nil { + if err := survey.AskOne(&survey.Input{Message: "GitLab Base URL:", Default: values.BaseURL}, &values.BaseURL); err != nil { return err } } @@ -141,58 +100,58 @@ func runMerge(ctx *cli.Context) error { orgID := ctx.Int("group-id") if orgID == 0 { if ctx.IsSet("group-name") { - values.orgName = ctx.String("group-name") + values.OrgName = ctx.String("group-name") } else { interactive = true - if err := survey.AskOne(&survey.Input{Message: "Group Name:", Default: values.orgName}, &values.orgName); err != nil { + if err := survey.AskOne(&survey.Input{Message: "Group Name:", Default: values.OrgName}, &values.OrgName); err != nil { return err } } } if ctx.IsSet("repo-ignore-pattern") { - values.repoExclude = ctx.String("repo-ignore-pattern") + values.RepoExclude = ctx.String("repo-ignore-pattern") } else { interactive = true - if err := survey.AskOne(&survey.Input{Message: "Ignore Repo with patter:", Default: values.repoExclude}, &values.repoExclude); err != nil { + if err := survey.AskOne(&survey.Input{Message: "Ignore Repo with patter:", Default: values.RepoExclude}, &values.RepoExclude); err != nil { return err } } if ctx.IsSet("branch-pattern") { - values.pullBaseBranch = ctx.String("branch-pattern") + values.PullBaseBranch = ctx.String("branch-pattern") } else { interactive = true - if err := survey.AskOne(&survey.Input{Message: "Merge Branches with patter:", Default: values.pullBaseBranch}, &values.pullBaseBranch); err != nil { + if err := survey.AskOne(&survey.Input{Message: "Merge Branches with patter:", Default: values.PullBaseBranch}, &values.PullBaseBranch); err != nil { return err } } if interactive { - saveCache(values) + context.SaveCache(values) } // Compile glob regex - excludeRule, err := glob.Compile(values.repoExclude) + excludeRule, err := glob.Compile(values.RepoExclude) if err != nil { - return fmt.Errorf("not able to compile regex '%s'", values.repoExclude) + return fmt.Errorf("not able to compile regex '%s'", values.RepoExclude) } - baseBranchRule, err := glob.Compile(values.pullBaseBranch) + baseBranchRule, err := glob.Compile(values.PullBaseBranch) if err != nil { - return fmt.Errorf("not able to compile regex '%s'", values.pullBaseBranch) + return fmt.Errorf("not able to compile regex '%s'", values.PullBaseBranch) } // connect to gitlab - client, err := gitlab.NewClient(values.token, gitlab.WithBaseURL(values.baseURL)) + client, err := gitlab.NewClient(values.Token, gitlab.WithBaseURL(values.BaseURL)) if err != nil { return fmt.Errorf("Could not create Client: %v\n", err) } if orgID == 0 { // discover GroupID by name - org, _, err := client.Groups.GetGroup(values.orgName) + org, _, err := client.Groups.GetGroup(values.OrgName) if err != nil { - return fmt.Errorf("Error cant get information for Organisation \"%s\": %v", values.orgName, err) + return fmt.Errorf("Error cant get information for Organisation \"%s\": %v", values.OrgName, err) } fmt.Printf("Found \"%s\"\n", org.WebURL) @@ -205,7 +164,7 @@ func runMerge(ctx *cli.Context) error { for { fmt.Printf("%d", page) repos, _, err := client.Groups.ListGroupProjects(orgID, &gitlab.ListGroupProjectsOptions{ - ListOptions: gitlab.ListOptions{PerPage: 10, Page: page}, + ListOptions: gitlab.ListOptions{PerPage: perPage, Page: page}, Archived: utils.OptBool(false), IncludeSubgroups: utils.OptBool(false), }) @@ -214,7 +173,7 @@ func runMerge(ctx *cli.Context) error { } repoList = append(repoList, repos...) - if len(repos) < 10 { + if len(repos) < perPage { break } page++ @@ -259,7 +218,7 @@ func threatRepo(baseBranchRule glob.Glob, client *gitlab.Client, repo *gitlab.Pr } branchList = append(branchList, bL...) - if len(bL) < 10 { + if len(bL) < perPage { break } page++ diff --git a/tags/taging.go b/tags/taging.go new file mode 100644 index 0000000..7cbbffe --- /dev/null +++ b/tags/taging.go @@ -0,0 +1,188 @@ +package tags + +import ( + "fmt" + + "code.obermui.de/6543/GitLab_MergeDevel2Default/context" + "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 ( + perPage = 10 +) + +var CmdCreateTag = cli.Command{ + Name: "create-tag", + Usage: "Create a tag on commit of latest default branch", + Description: `run script for merge subcommand`, + Action: createTag, + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "token", + Usage: "GitLab Access Token", + EnvVars: []string{"TOKEN"}, + Required: false, + }, + &cli.StringFlag{ + Name: "url", + Usage: "GitLab Base URL", + EnvVars: []string{"BASE_URL"}, + Required: false, + }, + &cli.IntFlag{ + Name: "group-id", + Usage: "Group ID where repositories are stored", + EnvVars: []string{"GROUP_ID"}, + Required: false, + }, + &cli.StringFlag{ + Name: "group-name", + Usage: "Group Name where repositories are stored, it's used if group-id is not set", + EnvVars: []string{"GROUP_NAME"}, + Required: false, + }, + &cli.StringFlag{ + Name: "repo-ignore-pattern", + Usage: "Repo Ignore Pattern", + EnvVars: []string{"REPO_IGNORE_PATTERN"}, + Required: false, + }, + + &cli.StringFlag{ + Name: "tag", + Usage: "name of tag to create", + EnvVars: []string{"TAG"}, + Required: false, + }, + }, +} + +func createTag(ctx *cli.Context) error { + values := context.LoadCache() + interactive := false + + if ctx.IsSet("token") { + values.Token = ctx.String("token") + } else { + interactive = true + if err := survey.AskOne(&survey.Input{Message: "GitLab Token:", Default: values.Token}, &values.Token); err != nil { + return err + } + } + + if ctx.IsSet("url") { + values.BaseURL = ctx.String("url") + } else { + interactive = true + if err := survey.AskOne(&survey.Input{Message: "GitLab Base URL:", Default: values.BaseURL}, &values.BaseURL); err != nil { + return err + } + } + + orgID := ctx.Int("group-id") + if orgID == 0 { + if ctx.IsSet("group-name") { + values.OrgName = ctx.String("group-name") + } else { + interactive = true + if err := survey.AskOne(&survey.Input{Message: "Group Name:", Default: values.OrgName}, &values.OrgName); err != nil { + return err + } + } + } + + if ctx.IsSet("repo-ignore-pattern") { + values.RepoExclude = ctx.String("repo-ignore-pattern") + } else { + interactive = true + if err := survey.AskOne(&survey.Input{Message: "Ignore Repo with patter:", Default: values.RepoExclude}, &values.RepoExclude); err != nil { + return err + } + } + + tag := ctx.String("tag") + if len(tag) == 0 { + interactive = true + if err := survey.AskOne(&survey.Input{Message: "Tag Name:", Default: tag}, &tag); err != nil { + return err + } + } + + if interactive { + context.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) + } + + // 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) + } + + if orgID == 0 { + // 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) + + orgID = org.ID + } + + var repoList []*gitlab.Project + var page = 1 + fmt.Printf("Retrieving repository list... ") + for { + fmt.Printf("%d ", page) + repos, _, err := client.Groups.ListGroupProjects(orgID, &gitlab.ListGroupProjectsOptions{ + ListOptions: gitlab.ListOptions{PerPage: perPage, 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) < perPage { + break + } + page++ + } + fmt.Printf("... DONE\nFound %d Repos\n", len(repoList)) + + for i := range repoList { + if !excludeRule.Match(repoList[i].Name) { + // for each NOT excluded repo ... + if err := threatRepo(client, tag, repoList[i]); err != nil { + return err + } + } + } + + return nil +} + +func threatRepo(client *gitlab.Client, tagName string, repo *gitlab.Project) error { + tag, _, err := client.Tags.CreateTag(repo.ID, &gitlab.CreateTagOptions{ + TagName: &tagName, + Ref: &repo.DefaultBranch, + }) + if err != nil { + return err + } + + fmt.Printf(" - Created tag '%s' in '%s'\n", tag.Name, repo.WebURL) + return nil +}