Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SUP-1903 Add path based pipeline resolver #225

Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package pipeline
package resolver

import (
"fmt"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package pipeline
package resolver

import (
"os"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,31 @@
package pipeline
package resolver

import (
"strings"

"github.com/buildkite/cli/v3/internal/pipeline"
"github.com/buildkite/go-buildkite/v3/buildkite"
"github.com/go-git/go-git/v5"
)

func ResolveFromPath(path string, org string, client *buildkite.Client) ([]string, error) {
func ResolveFromPath(path string, org string, client *buildkite.Client) PipelineResolverFn {
return func() (*pipeline.Pipeline, error) {
pipelines, err := resolveFromPath(path, org, client)
if err != nil {
return nil, err
}
if len(pipelines) == 0 {
return nil, nil
}

return &pipeline.Pipeline{
Name: pipelines[0],
Org: org,
}, nil
}
}

func resolveFromPath(path string, org string, client *buildkite.Client) ([]string, error) {
repos, err := getRepoURLs(path)
if err != nil {
return nil, err
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package pipeline
package resolver

import (
"net/http"
Expand All @@ -23,7 +23,7 @@ func TestResolvePipelinesFromPath(t *testing.T) {
gock.InterceptClient(client)

bkClient := buildkite.NewClient(client)
pipelines, err := ResolveFromPath("../..", "testOrg", bkClient)
pipelines, err := resolveFromPath("../..", "testOrg", bkClient)
if err != nil {
t.Errorf("Error: %s", err)
}
Expand All @@ -46,7 +46,7 @@ func TestResolvePipelinesFromPath(t *testing.T) {
gock.InterceptClient(client)

bkClient := buildkite.NewClient(client)
pipelines, err := ResolveFromPath(".", "testOrg", bkClient)
pipelines, err := resolveFromPath(".", "testOrg", bkClient)
if err != nil {
t.Errorf("Error: %s", err)
}
Expand All @@ -69,7 +69,7 @@ func TestResolvePipelinesFromPath(t *testing.T) {
gock.InterceptClient(client)

bkClient := buildkite.NewClient(client)
pipelines, err := ResolveFromPath(".", "testOrg", bkClient)
pipelines, err := resolveFromPath(".", "testOrg", bkClient)
if err != nil {
t.Errorf("Error: %s", err)
}
Expand All @@ -92,7 +92,7 @@ func TestResolvePipelinesFromPath(t *testing.T) {
gock.InterceptClient(client)

bkClient := buildkite.NewClient(client)
pipelines, err := ResolveFromPath(".", "testOrg", bkClient)
pipelines, err := resolveFromPath(".", "testOrg", bkClient)
if err != nil {
t.Errorf("Error: %s", err)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,22 @@
package pipeline
package resolver

import "errors"
import (
"errors"

"github.com/buildkite/cli/v3/internal/pipeline"
)

// PipelineResolverFn is a function for the purpose of finding a pipeline. It returns an error if an irrecoverable
// scenario happens and should halt execution. Otherwise if the resolver does not find a pipeline, it should return
// (nil, nil) to indicate this. ie. no error occurred, but no pipeline was found either.
type PipelineResolverFn func() (*Pipeline, error)
type PipelineResolverFn func() (*pipeline.Pipeline, error)

type AggregateResolver []PipelineResolverFn

// Resolve is a PipelineResolverFn that wraps up a list of resolvers to loop through to try find a pipeline. The first
// pipeline that is found will be returned, if none are found if won't return an error to match the expectation of a
// PipelineResolveFn
func (pr AggregateResolver) Resolve() (*Pipeline, error) {
func (pr AggregateResolver) Resolve() (*pipeline.Pipeline, error) {
for _, resolve := range pr {
p, err := resolve()
if err != nil {
Expand All @@ -33,6 +37,6 @@ func NewAggregateResolver(resolvers ...PipelineResolverFn) AggregateResolver {
return append(resolvers, errorResolver)
}

func errorResolver() (*Pipeline, error) {
func errorResolver() (*pipeline.Pipeline, error) {
return nil, errors.New("Failed to resolve a pipeline.")
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
package pipeline_test
package resolver_test

import (
"testing"

"github.com/buildkite/cli/v3/internal/pipeline"
"github.com/buildkite/cli/v3/internal/pipeline/resolver"
)

func TestAggregateResolver(t *testing.T) {
Expand All @@ -12,7 +13,7 @@ func TestAggregateResolver(t *testing.T) {
t.Run("it loops over resolvers until one returns", func(t *testing.T) {
t.Parallel()

agg := pipeline.AggregateResolver{
agg := resolver.AggregateResolver{
func() (*pipeline.Pipeline, error) { return nil, nil },
func() (*pipeline.Pipeline, error) { return &pipeline.Pipeline{Name: "test"}, nil },
}
Expand All @@ -30,7 +31,7 @@ func TestAggregateResolver(t *testing.T) {
t.Run("returns nil if nothing resolves", func(t *testing.T) {
t.Parallel()

agg := pipeline.AggregateResolver{}
agg := resolver.AggregateResolver{}

p, err := agg.Resolve()

Expand Down
22 changes: 19 additions & 3 deletions pkg/cmd/build/cancel.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"github.com/MakeNowJust/heredoc"
"github.com/buildkite/cli/v3/internal/io"
"github.com/buildkite/cli/v3/internal/pipeline"
"github.com/buildkite/cli/v3/internal/pipeline/resolver"
"github.com/buildkite/cli/v3/pkg/cmd/factory"
tea "github.com/charmbracelet/bubbletea"
"github.com/spf13/cobra"
Expand All @@ -24,19 +25,34 @@ func NewCmdBuildCancel(f *factory.Factory) *cobra.Command {

It accepts a build number and a pipeline slug as an argument.
The pipeline can be a {pipeline_slug} or in the format {org_slug}/{pipeline_slug}.
If the pipeline argument is omitted, it will be resolved using the current directory.
`),
RunE: func(cmd *cobra.Command, args []string) error {
buildId := args[0]
resolvers := pipeline.NewAggregateResolver(pipelineResolverPositionArg(args[1:], f.Config))
pipeline, err := resolvers.Resolve()
resolvers := resolver.NewAggregateResolver(
pipelineResolverPositionArg(args[1:], f.Config),
resolver.ResolveFromPath("", f.Config.Organization, f.RestAPIClient),
)
var pipeline pipeline.Pipeline
r := io.NewPendingCommand(func() tea.Msg {
p, err := resolvers.Resolve()
if err != nil {
return err
}
pipeline = *p

return io.PendingOutput(fmt.Sprintf("Resolved pipeline to: %s", pipeline.Name))
}, "Resolving pipeline")
p := tea.NewProgram(r)
_, err := p.Run()
if err != nil {
return err
}
return cancelBuild(pipeline.Org, pipeline.Name, buildId, web, f)
},
}

cmd.Flags().BoolVarP(&web, "web", "w", false, "Open the build in a web browser after it has been created.")
cmd.Flags().BoolVarP(&web, "web", "w", false, "Open the build in a web browser after it has been cancelled.")
cmd.Flags().SortFlags = false
return &cmd
}
Expand Down
24 changes: 20 additions & 4 deletions pkg/cmd/build/new.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"github.com/buildkite/cli/v3/internal/config"
"github.com/buildkite/cli/v3/internal/io"
"github.com/buildkite/cli/v3/internal/pipeline"
"github.com/buildkite/cli/v3/internal/pipeline/resolver"
"github.com/buildkite/cli/v3/pkg/cmd/factory"
"github.com/buildkite/go-buildkite/v3/buildkite"
tea "github.com/charmbracelet/bubbletea"
Expand All @@ -28,11 +29,26 @@ func NewCmdBuildNew(f *factory.Factory) *cobra.Command {
Long: heredoc.Doc(`
Creates a new build for the specified pipeline and output the URL to the build.

It accepts {pipeline_slug}, {org_slug}/{pipeline_slug} or a full URL to the pipeline as an argument.
The pipeline can be a {pipeline_slug} or in the format {org_slug}/{pipeline_slug}.
If the pipeline argument is omitted, it will be resolved using the current directory.
`),
RunE: func(cmd *cobra.Command, args []string) error {
resolvers := pipeline.NewAggregateResolver(pipelineResolverPositionArg(args, f.Config))
pipeline, err := resolvers.Resolve()
resolvers := resolver.NewAggregateResolver(
pipelineResolverPositionArg(args, f.Config),
resolver.ResolveFromPath("", f.Config.Organization, f.RestAPIClient),
)
var pipeline pipeline.Pipeline
r := io.NewPendingCommand(func() tea.Msg {
p, err := resolvers.Resolve()
if err != nil {
return err
}
pipeline = *p

return io.PendingOutput(fmt.Sprintf("Resolved pipeline to: %s", pipeline.Name))
}, "Resolving pipeline")
p := tea.NewProgram(r)
_, err := p.Run()
if err != nil {
return err
}
Expand All @@ -48,7 +64,7 @@ func NewCmdBuildNew(f *factory.Factory) *cobra.Command {
return &cmd
}

func pipelineResolverPositionArg(args []string, conf *config.Config) pipeline.PipelineResolverFn {
func pipelineResolverPositionArg(args []string, conf *config.Config) resolver.PipelineResolverFn {
return func() (*pipeline.Pipeline, error) {
// if args does not have values, skip this resolver
if len(args) < 1 {
Expand Down
20 changes: 18 additions & 2 deletions pkg/cmd/build/rebuild.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"github.com/MakeNowJust/heredoc"
"github.com/buildkite/cli/v3/internal/io"
"github.com/buildkite/cli/v3/internal/pipeline"
"github.com/buildkite/cli/v3/internal/pipeline/resolver"
"github.com/buildkite/cli/v3/pkg/cmd/factory"
tea "github.com/charmbracelet/bubbletea"
"github.com/spf13/cobra"
Expand All @@ -24,11 +25,26 @@ func NewCmdBuildRebuild(f *factory.Factory) *cobra.Command {

It accepts a build number and a pipeline slug as an argument.
The pipeline can be a {pipeline_slug} or in the format {org_slug}/{pipeline_slug}.
If the pipeline argument is omitted, it will be resolved using the current directory.
`),
RunE: func(cmd *cobra.Command, args []string) error {
buildId := args[0]
resolvers := pipeline.NewAggregateResolver(pipelineResolverPositionArg(args[1:], f.Config))
pipeline, err := resolvers.Resolve()
resolvers := resolver.NewAggregateResolver(
pipelineResolverPositionArg(args[1:], f.Config),
resolver.ResolveFromPath("", f.Config.Organization, f.RestAPIClient),
)
var pipeline pipeline.Pipeline
r := io.NewPendingCommand(func() tea.Msg {
p, err := resolvers.Resolve()
if err != nil {
return err
}
pipeline = *p

return io.PendingOutput(fmt.Sprintf("Resolved pipeline to: %s", pipeline.Name))
}, "Resolving pipeline")
p := tea.NewProgram(r)
_, err := p.Run()
if err != nil {
return err
}
Expand Down
26 changes: 22 additions & 4 deletions pkg/cmd/build/view.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"github.com/buildkite/cli/v3/internal/build"
"github.com/buildkite/cli/v3/internal/io"
"github.com/buildkite/cli/v3/internal/pipeline"
"github.com/buildkite/cli/v3/internal/pipeline/resolver"
"github.com/buildkite/cli/v3/pkg/cmd/factory"
"github.com/buildkite/go-buildkite/v3/buildkite"
tea "github.com/charmbracelet/bubbletea"
Expand All @@ -28,14 +29,31 @@ func NewCmdBuildView(f *factory.Factory) *cobra.Command {
Long: heredoc.Doc(`
View a build's information.

It accepts a build number and a pipeline slug as an argument.
It accepts a build number and a pipeline slug as an argument.
The pipeline can be a {pipeline_slug} or in the format {org_slug}/{pipeline_slug}.
If the pipeline argument is omitted, it will be resolved using the current directory.
`),
RunE: func(cmd *cobra.Command, args []string) error {
var buildArtifacts = make([]buildkite.Artifact, 0)
buildId := args[0]
resolvers := pipeline.NewAggregateResolver(pipelineResolverPositionArg(args[1:], f.Config))
pipeline, err := resolvers.Resolve()
resolvers := resolver.NewAggregateResolver(
pipelineResolverPositionArg(args[1:], f.Config),
resolver.ResolveFromPath("", f.Config.Organization, f.RestAPIClient),
)

var pipeline pipeline.Pipeline

r := io.NewPendingCommand(func() tea.Msg {
p, err := resolvers.Resolve()
if err != nil {
return err
}
pipeline = *p

return io.PendingOutput(fmt.Sprintf("Resolved pipeline to: %s", pipeline.Name))
}, "Resolving pipeline")
p := tea.NewProgram(r)
_, err := p.Run()
if err != nil {
return err
}
Expand Down Expand Up @@ -74,7 +92,7 @@ func NewCmdBuildView(f *factory.Factory) *cobra.Command {
return io.PendingOutput(summary)
}, "Loading build information")

p := tea.NewProgram(l)
p = tea.NewProgram(l)
_, err = p.Run()

return err
Expand Down