Skip to content
This repository has been archived by the owner on Nov 22, 2022. It is now read-only.

Commit

Permalink
Feat: live pipeline gui view with keybinded controls
Browse files Browse the repository at this point in the history
  • Loading branch information
profclems committed Aug 14, 2020
1 parent 57f138d commit 2eb0f0c
Show file tree
Hide file tree
Showing 10 changed files with 1,138 additions and 129 deletions.
270 changes: 253 additions & 17 deletions commands/pipeline.go
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
package commands

import (
"errors"
"fmt"
"github.com/gookit/color"
"github.com/spf13/cobra"
"glab/internal/git"
"glab/internal/manip"
"io"
"math"
"os"
"text/tabwriter"

"github.com/gookit/color"
"github.com/spf13/cobra"
"github.com/xanzy/go-gitlab"
"glab/internal/git"
"glab/internal/manip"
)

func displayMultiplePipelines(m []*gitlab.PipelineInfo) {
Expand Down Expand Up @@ -42,65 +43,300 @@ func displayMultiplePipelines(m []*gitlab.PipelineInfo) {
}
}

func retryPipelineJob(pid int) *gitlab.Pipeline {
func retryPipeline(pid int, rep string) (*gitlab.Pipeline, error) {
gitlabClient, repo := git.InitGitlabClient()
if rep != "" {
repo = rep
}
pipe, _, err := gitlabClient.Pipelines.RetryPipelineBuild(repo, pid)
if err != nil {
er(err)
return nil, err
}
return pipe, nil
}
func playPipelineJob(pid int, rep string) (*gitlab.Job, error) {
gitlabClient, repo := git.InitGitlabClient()
if rep != "" {
repo = rep
}
pipe, _, err := gitlabClient.Jobs.PlayJob(repo, pid)
if err != nil {
return nil, err
}
return pipe, nil
}

func retryPipelineJob(pid int, rep string) (*gitlab.Job, error) {
gitlabClient, repo := git.InitGitlabClient()
if rep != "" {
repo = rep
}
pipe, _, err := gitlabClient.Jobs.RetryJob(repo, pid)
if err != nil {
return nil, err
}
return pipe, nil
}

func cancelPipelineJob(rep string, jobID int) (*gitlab.Job, error) {
gitlabClient, repo := git.InitGitlabClient()
if rep != "" {
repo = rep
}
pipe, _, err := gitlabClient.Jobs.CancelJob(repo, jobID)
if err != nil {
return nil, err
}
return pipe, nil
}

func playOrRetryJobs(repo string, jobID int, status string) (*gitlab.Job, error) {
switch status {
case "pending", "running":
return nil, nil
case "manual":
j, err := playPipelineJob(jobID, repo)
if err != nil {
return nil, err
}
return j, nil
default:

j, err := retryPipelineJob(jobID, repo)
if err != nil {
return nil, err
}

return j, nil
}
}

func erasePipelineJob(pid int, rep string) (*gitlab.Job, error) {
gitlabClient, repo := git.InitGitlabClient()
if rep != "" {
repo = rep
}
pipe, _, err := gitlabClient.Jobs.EraseJob(repo, pid)
if err != nil {
return nil, err
}
return pipe
return pipe, nil
}

func getPipelineJob(jid int) (*gitlab.Job, error) {
func getPipelineJob(jid int, rep string) (*gitlab.Job, error) {
gitlabClient, repo := git.InitGitlabClient()
if rep != "" {
repo = rep
}
job, _, err := gitlabClient.Jobs.GetJob(repo, jid)
return job, err
}

func getJobs(rep string, opts *gitlab.ListJobsOptions) ([]gitlab.Job, error) {
gitlabClient, repo := git.InitGitlabClient()
if rep != "" {
repo = rep
}

if opts == nil {
opts = &gitlab.ListJobsOptions{}
}
jobs, _, err := gitlabClient.Jobs.ListProjectJobs(repo, opts)
if err != nil {
return nil, err
}
return jobs, nil
}

func fmtDuration(duration float64) string {
s := math.Mod(duration, 60)
m := (duration - s) / 60
s = math.Round(s)
return fmt.Sprintf("%02vm %02vs", m, s)
}

func getPipelines(l *gitlab.ListProjectPipelinesOptions) ([]*gitlab.PipelineInfo, error) {
func getPipelines(l *gitlab.ListProjectPipelinesOptions, rep string) ([]*gitlab.PipelineInfo, error) {
gitlabClient, repo := git.InitGitlabClient()
if rep != "" {
repo = rep
}
pipes, _, err := gitlabClient.Pipelines.ListProjectPipelines(repo, l)
if err != nil {
return nil, err
}
return pipes, nil
}

func getPipelineJobs(pid int) []*gitlab.Job {
func getPipelineJobs(pid int, rep string) ([]*gitlab.Job, error) {
gitlabClient, repo := git.InitGitlabClient()
if rep != "" {
repo = rep
}
l := &gitlab.ListJobsOptions{}
pipeJobs, _, err := gitlabClient.Jobs.ListPipelineJobs(repo, pid, l)
if err != nil {
er(err)
return nil, err
}
return pipeJobs
return pipeJobs, nil
}

func getPipelineJobLog(jobID int) io.Reader {
func getPipelineJobLog(jobID int, rep string) (io.Reader, error) {
gitlabClient, repo := git.InitGitlabClient()
pipeJobs, _, err := gitlabClient.Jobs.GetTraceFile(repo, jobID)
if rep != "" {
repo = rep
}
pipeJoblog, _, err := gitlabClient.Jobs.GetTraceFile(repo, jobID)
if err != nil {
er(err)
return nil, err
}
return pipeJobs
return pipeJoblog, nil
}

func getSinglePipeline(pid int) (*gitlab.Pipeline, error) {
func getSinglePipeline(pid int, rep string) (*gitlab.Pipeline, error) {
gitlabClient, repo := git.InitGitlabClient()

if rep != "" {
repo = rep
}
pipes, _, err := gitlabClient.Pipelines.GetPipeline(repo, pid)
if err != nil {
return nil, err
}
return pipes, nil
}

func getCommit(repository string, ref string) (*gitlab.Commit, error) {
gitlabClient, repo := git.InitGitlabClient()

if repository != "" {
repo = repository
}
c, _, err := gitlabClient.Commits.GetCommit(repo, ref)
if err != nil {
return nil, err
}
return c, nil
}

func getPipelineFromBranch(ref, repo string) ([]*gitlab.Job, error) {
var err error
if ref == "" {
ref, err = git.CurrentBranch()
if err != nil {
return nil, err
}
}
l := &gitlab.ListProjectPipelinesOptions{
Ref: gitlab.String(ref),
OrderBy: gitlab.String("updated_at"),
Sort: gitlab.String("desc"),
}
l.Page = 1
l.PerPage = 1
if repo == "" {
repo = git.GetRepo()
}
pipes, err := getPipelines(l, repo)
if err != nil {
return nil, err
}
if len(pipes) == 0 {
err = errors.New("No pipelines running or available on " + ref + "branch")
return nil, err
}
pipeline := pipes[0]
jobs, err := getPipelineJobs(pipeline.ID, repo)
return jobs, nil
}

func pipelineJobTraceWithSha(pid interface{}, sha, name string) (io.Reader, *gitlab.Job, error) {
gitlabClient, _ := git.InitGitlabClient()
jobs, err := pipelineJobsWithSha(pid, sha)
if len(jobs) == 0 || err != nil {
return nil, nil, err
}
var (
job *gitlab.Job
lastRunning *gitlab.Job
firstPending *gitlab.Job
)

for _, j := range jobs {
if j.Status == "running" {
lastRunning = j
}
if j.Status == "pending" && firstPending == nil {
firstPending = j
}
if j.Name == name {
job = j
// don't break because there may be a newer version of the job
}
}
if job == nil {
job = lastRunning
}
if job == nil {
job = firstPending
}
if job == nil {
job = jobs[len(jobs)-1]
}
r, _, err := gitlabClient.Jobs.GetTraceFile(pid, job.ID)
if err != nil {
return nil, job, err
}

return r, job, err
}

// CIJobs returns a list of jobs in a pipeline for a given sha. The jobs are
// returned sorted by their CreatedAt time
func pipelineJobsWithSha(pid interface{}, sha string) ([]*gitlab.Job, error) {
gitlabClient, _ := git.InitGitlabClient()
pipelines, _, err := gitlabClient.Pipelines.ListProjectPipelines(pid, &gitlab.ListProjectPipelinesOptions{
SHA: gitlab.String(sha),
})
if len(pipelines) == 0 || err != nil {
return nil, err
}
target := pipelines[0].ID
opts := &gitlab.ListJobsOptions{
ListOptions: gitlab.ListOptions{
PerPage: 500,
},
}
list, resp, err := gitlabClient.Jobs.ListPipelineJobs(pid, target, opts)
if err != nil {
return nil, err
}
if resp.CurrentPage == resp.TotalPages {
return list, nil
}
opts.Page = resp.NextPage
for {
jobs, resp, err := gitlabClient.Jobs.ListPipelineJobs(pid, target, opts)
if err != nil {
return nil, err
}
opts.Page = resp.NextPage
list = append(list, jobs...)
if resp.CurrentPage == resp.TotalPages {
break
}
}
return list, nil
}

func pipelineCILint(content string) (*gitlab.LintResult, error) {
gitlabClient, _ := git.InitGitlabClient()
c, _, err := gitlabClient.Validate.Lint(content)
if err != nil {
return nil, err
}
return c, nil
}

// pipelineCmd is merge request command
var pipelineCmd = &cobra.Command{
Use: "pipeline <command> [flags]",
Expand Down
56 changes: 56 additions & 0 deletions commands/pipeline_ci_lint.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package commands

import (
"fmt"
"github.com/MakeNowJust/heredoc"
"github.com/gookit/color"
"io/ioutil"
"log"
"os"

"github.com/spf13/cobra"
)

// ciLintCmd represents the lint command
var pipelineCILintCmd = &cobra.Command{
Use: "lint",
Short: "Checks if your .gitlab-ci.yml file is valid.",
Long: ``,
Example: heredoc.Doc(`
$ glab pipeline ci lint # Uses .gitlab-ci.yml in the current directory
$ glab pipeline ci lint .gitlab-ci.yml
$ glab pipeline ci lint path/to/.gitlab-ci.yml
`),
Run: func(cmd *cobra.Command, args []string) {
path := ".gitlab-ci.yml"
if len(args) == 1 {
path = args[0]
}
fmt.Println("Getting contents in", path)

content, err := ioutil.ReadFile(path)
if !os.IsNotExist(err) && err != nil {
log.Fatal(err)
}
fmt.Println("Validating...")
lint, err := pipelineCILint(string(content))
if err != nil {
er(err)
return
}
if lint.Status == "invalid" {
color.Red.Println(path, "is invalid")
for i, err := range lint.Errors {
i++
fmt.Println(i, err)
return
}
}
color.Green.Println("CI yml is Valid!")
return
},
}

func init() {
pipelineCICmd.AddCommand(pipelineCILintCmd)
}
Loading

0 comments on commit 2eb0f0c

Please sign in to comment.