diff --git a/api/pipeline.go b/api/pipeline.go index 42e32e686..48541ad19 100644 --- a/api/pipeline.go +++ b/api/pipeline.go @@ -387,3 +387,19 @@ var DownloadArtifactJob = func(client *gitlab.Client, repo string, ref string, o } return jobs, nil } + +var ListPipelineTriggers = func(client *gitlab.Client, projectID interface{}, opts *gitlab.ListPipelineTriggersOptions) ([]*gitlab.PipelineTrigger, error) { + if client == nil { + client = apiClient.Lab() + } + triggers, _, err := client.PipelineTriggers.ListPipelineTriggers(projectID, opts) + return triggers, err +} + +var RunPipelineTrigger = func(client *gitlab.Client, projectID interface{}, opts *gitlab.RunPipelineTriggerOptions) (*gitlab.Pipeline, error) { + if client == nil { + client = apiClient.Lab() + } + pipe, _, err := client.PipelineTriggers.RunPipelineTrigger(projectID, opts) + return pipe, err +} diff --git a/commands/ci/ci.go b/commands/ci/ci.go index af7ef5766..70a989fe0 100644 --- a/commands/ci/ci.go +++ b/commands/ci/ci.go @@ -6,10 +6,12 @@ import ( legacyCICmd "github.com/profclems/glab/commands/ci/legacyci" ciLintCmd "github.com/profclems/glab/commands/ci/lint" pipeListCmd "github.com/profclems/glab/commands/ci/list" + listPipeTriggerCmd "github.com/profclems/glab/commands/ci/list_triggers" pipeRetryCmd "github.com/profclems/glab/commands/ci/retry" pipeRunCmd "github.com/profclems/glab/commands/ci/run" pipeStatusCmd "github.com/profclems/glab/commands/ci/status" ciTraceCmd "github.com/profclems/glab/commands/ci/trace" + triggerPipeCmd "github.com/profclems/glab/commands/ci/trigger" ciViewCmd "github.com/profclems/glab/commands/ci/view" "github.com/profclems/glab/commands/cmdutils" @@ -36,5 +38,7 @@ func NewCmdCI(f *cmdutils.Factory) *cobra.Command { ciCmd.AddCommand(pipeRetryCmd.NewCmdRetry(f)) ciCmd.AddCommand(pipeRunCmd.NewCmdRun(f)) ciCmd.AddCommand(jobArtifactCmd.NewCmdRun(f)) + ciCmd.AddCommand(listPipeTriggerCmd.NewCmdRun(f)) + ciCmd.AddCommand(triggerPipeCmd.NewCmdRun(f)) return ciCmd } diff --git a/commands/ci/ciutils/utils.go b/commands/ci/ciutils/utils.go index e41eab4b7..1771db27e 100644 --- a/commands/ci/ciutils/utils.go +++ b/commands/ci/ciutils/utils.go @@ -59,6 +59,35 @@ func DisplayMultiplePipelines(s *iostreams.IOStreams, p []*gitlab.PipelineInfo, return "No Pipelines available on " + projectID } +func DisplayMultipleTriggers(s *iostreams.IOStreams, triggers []*gitlab.PipelineTrigger) string { + c := s.Color() + + table := tableprinter.NewTablePrinter() + + if len(triggers) > 0 { + + for _, trigger := range triggers { + duration := "" + name := "unknown owner" + if trigger.CreatedAt != nil { + duration = c.Magenta("(" + utils.TimeToPrettyTimeAgo(*trigger.CreatedAt) + ")") + } + + if trigger.Owner != nil { + name = trigger.Owner.Name + } + + line := c.Green(fmt.Sprintf("[%s] • #%s", name, trigger.ID)) + + table.AddRow(line, trigger.ID, duration) + } + + return table.Render() + } + + return "" // empty message is provided by the title +} + func RunTraceSha(ctx context.Context, apiClient *gitlab.Client, w io.Writer, pid interface{}, sha, name string) error { job, err := api.PipelineJobWithSha(apiClient, pid, sha, name) if err != nil || job == nil { diff --git a/commands/ci/list/list.go b/commands/ci/list/list.go index eaa20dd5b..371cb466a 100644 --- a/commands/ci/list/list.go +++ b/commands/ci/list/list.go @@ -21,8 +21,9 @@ func NewCmdList(f *cmdutils.Factory) *cobra.Command { $ glab ci list $ glab ci list --status=failed `), - Long: ``, - Args: cobra.ExactArgs(0), + Long: ``, + Aliases: []string{"ls"}, + Args: cobra.ExactArgs(0), RunE: func(cmd *cobra.Command, args []string) error { var err error var titleQualifier string diff --git a/commands/ci/list_triggers/list_triggers.go b/commands/ci/list_triggers/list_triggers.go new file mode 100644 index 000000000..78083d577 --- /dev/null +++ b/commands/ci/list_triggers/list_triggers.go @@ -0,0 +1,71 @@ +package trigger + +import ( + "fmt" + "github.com/MakeNowJust/heredoc" + "github.com/profclems/glab/api" + "github.com/profclems/glab/commands/ci/ciutils" + "github.com/profclems/glab/commands/cmdutils" + "github.com/profclems/glab/pkg/utils" + "github.com/spf13/cobra" + "github.com/xanzy/go-gitlab" +) + +func NewCmdRun(f *cmdutils.Factory) *cobra.Command { + var pipelineRunCmd = &cobra.Command{ + Use: "list-triggers [flags]", + Short: `List triggers of a pipeline in the CI`, + Aliases: []string{"lst"}, + Example: heredoc.Doc(` + $ glab ci list-triggers + $ glab ci lst + $ glab ci list-triggers --page 0 + `), + Long: ``, + Args: cobra.ExactArgs(0), + RunE: func(cmd *cobra.Command, args []string) error { + var err error + + apiClient, err := f.HttpClient() + if err != nil { + return err + } + + repo, err := f.BaseRepo() + if err != nil { + return err + } + + c := &gitlab.ListPipelineTriggersOptions{ + PerPage: 30, + Page: 0, + } + + if p, _ := cmd.Flags().GetInt("per-page"); p != 0 { + c.PerPage = p + } + + if p, err := cmd.Flags().GetInt("page"); err != nil { + c.Page = p + } + + triggers, err := api.ListPipelineTriggers(apiClient, repo.FullName(), c) + if err != nil { + return err + } + + title := utils.NewListTitle(fmt.Sprintf("[%s] Pipeline trigger", repo.FullName())) + title.RepoName = repo.FullName() + title.Page = c.Page + title.CurrentPageTotal = len(triggers) + + fmt.Fprintf(f.IO.StdOut, "%s\n%s\n", title.Describe(), ciutils.DisplayMultipleTriggers(f.IO, triggers)) + + return nil + }, + } + pipelineRunCmd.Flags().IntP("page", "p", 0, "Which page of the pipeline triggers to return (default 0)") + pipelineRunCmd.Flags().IntP("per-page", "P", 30, "Number of items to list per page. (default 30)") + + return pipelineRunCmd +} diff --git a/commands/ci/trigger/trigger.go b/commands/ci/trigger/trigger.go new file mode 100644 index 000000000..c45101943 --- /dev/null +++ b/commands/ci/trigger/trigger.go @@ -0,0 +1,105 @@ +package trigger + +import ( + "fmt" + "regexp" + "strings" + + "github.com/profclems/glab/api" + "github.com/profclems/glab/commands/cmdutils" + "github.com/profclems/glab/pkg/git" + + "github.com/MakeNowJust/heredoc" + "github.com/spf13/cobra" + "github.com/xanzy/go-gitlab" +) + +const keyValuePair = ".+:.+" + +var re = regexp.MustCompile(keyValuePair) + +func getDefaultBranch(f *cmdutils.Factory) string { + repo, err := f.BaseRepo() + if err != nil { + return "master" + } + + remotes, err := f.Remotes() + if err != nil { + return "master" + } + + repoRemote, err := remotes.FindByRepo(repo.RepoOwner(), repo.RepoName()) + if err != nil { + return "master" + } + + branch, _ := git.GetDefaultBranch(repoRemote.Name) + + return branch +} + +func NewCmdRun(f *cmdutils.Factory) *cobra.Command { + var pipelineRunCmd = &cobra.Command{ + Use: "trigger [flags]", + Short: `Trigger a pipeline in the CI`, + Aliases: []string{"t"}, + Example: heredoc.Doc(` + $ glab ci trigger + $ glab ci trigger -b trunk + $ glab ci run -b trunk --variables MYKEY:some_value --variables KEY2:another_value + `), + Long: ``, + Args: cobra.ExactArgs(0), + RunE: func(cmd *cobra.Command, args []string) error { + var err error + + apiClient, err := f.HttpClient() + if err != nil { + return err + } + + repo, err := f.BaseRepo() + if err != nil { + return err + } + + pipelineVars := map[string]string{ + // Do this, so the + "CI_PIPELINE_SOURCE" : "trigger", + } + + if customRunPipelineVars, _ := cmd.Flags().GetStringSlice("variables"); len(customRunPipelineVars) > 0 { + for _, v := range customRunPipelineVars { + if !re.MatchString(v) { + return fmt.Errorf("Bad pipeline variable : \"%s\" should be of format KEY:VALUE", v) + } + s := strings.SplitN(v, ":", 2) + pipelineVars[s[0]] = s[1] + } + } + + c := &gitlab.RunPipelineTriggerOptions{ + Variables: pipelineVars, + } + + if m, _ := cmd.Flags().GetString("branch"); m != "" { + c.Ref = gitlab.String(m) + } else { + c.Ref = gitlab.String(getDefaultBranch(f)) + } + + pipe, err := api.RunPipelineTrigger(apiClient, repo.FullName(), c) + if err != nil { + return err + } + + fmt.Fprintln(f.IO.StdOut, "Ran pipeline (id:", pipe.ID, "), status:", pipe.Status, ", ref:", pipe.Ref, ", weburl: ", pipe.WebURL, ")") + return nil + }, + } + pipelineRunCmd.Flags().StringP("branch", "b", "", "Run pipeline on branch/ref ") + pipelineRunCmd.Flags().StringSliceP("variables", "", []string{}, "Pass variables to pipeline run") + + return pipelineRunCmd +}