Skip to content

Commit

Permalink
Add CLI arg resolver for builds (#253)
Browse files Browse the repository at this point in the history
* Add CLI arg resolver for builds

* Add tests for CLI resolver
  • Loading branch information
jradtilbrook authored May 15, 2024
1 parent 5bace3f commit ed5208e
Show file tree
Hide file tree
Showing 2 changed files with 155 additions and 0 deletions.
71 changes: 71 additions & 0 deletions internal/build/resolver/cli.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package resolver

import (
"context"
"fmt"
"strconv"
"strings"

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

func ResolveFromPositionalArgument(args []string, index int, pipeline pipelineResolver.PipelineResolverFn, conf *config.Config) BuildResolverFn {
return func(ctx context.Context) (*build.Build, error) {
// if args does not have values, skip this resolver
if len(args) < 1 {
return nil, nil
}
// if the index is out of bounds
if (len(args) - 1) < index {
return nil, nil
}

build := parseBuildArg(ctx, args[index], pipeline)
// if we get here, we should be able to parse the value and return an error if not
// this is because a user has explicitly given an input value for us to use - we shouldnt ignore it on error
if build == nil {
return nil, fmt.Errorf("unable to parse the input build argument: \"%s\"", args[index])
}

return build, nil
}
}

func parseBuildArg(ctx context.Context, arg string, pipeline pipelineResolver.PipelineResolverFn) *build.Build {
buildIsURL := strings.Contains(arg, ":")
buildIsSlug := !buildIsURL && strings.Contains(arg, "/")

if buildIsURL {
return splitBuildURL(arg)
} else if buildIsSlug {
part := strings.Split(arg, "/")
if len(part) < 3 {
return nil
}
num, err := strconv.Atoi(part[2])
if err != nil {
return nil
}
return &build.Build{
Organization: part[0],
Pipeline: part[1],
BuildNumber: num,
}
}

num, err := strconv.Atoi(arg)
if err != nil {
return nil
}
p, err := pipeline(ctx)
if err != nil || p == nil {
return nil
}
return &build.Build{
Organization: p.Org,
Pipeline: p.Name,
BuildNumber: num,
}
}
84 changes: 84 additions & 0 deletions internal/build/resolver/cli_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
package resolver_test

import (
"context"
"testing"

"github.com/buildkite/cli/v3/internal/build/resolver"
"github.com/buildkite/cli/v3/internal/config"
"github.com/buildkite/cli/v3/internal/pipeline"
"github.com/spf13/afero"
)

func TestParseBuildArg(t *testing.T) {
t.Parallel()

testcases := map[string]struct {
url, org, pipeline string
num int
}{
"org_pipeline_slug": {
url: "buildkite/cli/34",
org: "buildkite",
pipeline: "cli",
num: 34,
},
"pipeline_slug": {
url: "42",
org: "testing",
pipeline: "abcd",
num: 42,
},
"url": {
url: "https://buildkite.com/buildkite/buildkite-cli/builds/99",
org: "buildkite",
pipeline: "buildkite-cli",
num: 99,
},
}

for name, testcase := range testcases {
testcase := testcase
t.Run(name, func(t *testing.T) {
t.Parallel()

conf := config.New(afero.NewMemMapFs(), nil)
conf.SelectOrganization("testing")
res := func(context.Context) (*pipeline.Pipeline, error) {
return &pipeline.Pipeline{
Name: testcase.pipeline,
Org: testcase.org,
}, nil
}
f := resolver.ResolveFromPositionalArgument([]string{testcase.url}, 0, res, conf)
build, err := f(context.Background())
if err != nil {
t.Error(err)
}
if build.Organization != testcase.org {
t.Error("parsed organization slug did not match expected")
}
if build.Pipeline != testcase.pipeline {
t.Error("parsed pipeline name did not match expected")
}
if build.BuildNumber != testcase.num {
t.Error("parsed build number did not match expected")
}
})
}

t.Run("Returns error if failed parsing", func(t *testing.T) {
t.Parallel()

conf := config.New(afero.NewMemMapFs(), nil)
conf.SelectOrganization("testing")
f := resolver.ResolveFromPositionalArgument([]string{"https://buildkite.com/"}, 0, nil, conf)
build, err := f(context.Background())
if err == nil {
t.Error("should have failed parsing build")
}
if build != nil {
t.Error("no build should be returned")
}
})
}

0 comments on commit ed5208e

Please sign in to comment.