Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions cmd/job/context_flags.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package job

import (
"fmt"
"io"
"os"
)

func warnIgnoredJobContextFlags(w io.Writer, pipeline, buildNumber string) {
if pipeline == "" && buildNumber == "" {
return
}
if w == nil {
w = os.Stderr
}

fmt.Fprintln(w, "Warning: --pipeline and --build are deprecated and ignored because job UUIDs no longer require pipeline or build context")
}
59 changes: 59 additions & 0 deletions cmd/job/context_flags_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package job

import (
"bytes"
"strings"
"testing"
)

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

tests := []struct {
name string
pipeline string
buildNumber string
wantWarning bool
}{
{
name: "no flags",
wantWarning: false,
},
{
name: "pipeline",
pipeline: "cli",
wantWarning: true,
},
{
name: "build",
buildNumber: "123",
wantWarning: true,
},
{
name: "both flags",
pipeline: "cli",
buildNumber: "123",
wantWarning: true,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()

var stderr bytes.Buffer
warnIgnoredJobContextFlags(&stderr, tt.pipeline, tt.buildNumber)

got := stderr.String()
if tt.wantWarning {
if !strings.Contains(got, "Warning: --pipeline and --build are deprecated and ignored") {
t.Fatalf("warning = %q", got)
}
return
}
if got != "" {
t.Fatalf("warning = %q, want empty", got)
}
})
}
}
55 changes: 13 additions & 42 deletions cmd/job/log.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,33 +6,27 @@ import (
"regexp"

"github.com/alecthomas/kong"
buildResolver "github.com/buildkite/cli/v3/internal/build/resolver"
"github.com/buildkite/cli/v3/internal/build/resolver/options"
"github.com/buildkite/cli/v3/internal/cli"
bkIO "github.com/buildkite/cli/v3/internal/io"
pipelineResolver "github.com/buildkite/cli/v3/internal/pipeline/resolver"
"github.com/buildkite/cli/v3/pkg/cmd/factory"
"github.com/buildkite/cli/v3/pkg/cmd/validation"
)

type LogCmd struct {
JobID string `arg:"" help:"Job UUID to get logs for"`
Pipeline string `help:"The pipeline to use. This can be a {pipeline slug} or in the format {org slug}/{pipeline slug}" short:"p"`
BuildNumber string `help:"The build number" short:"b"`
Pipeline string `help:"Deprecated; ignored because job UUIDs no longer require pipeline or build context" short:"p"`
BuildNumber string `help:"Deprecated; ignored because job UUIDs no longer require pipeline or build context" short:"b"`
NoTimestamps bool `help:"Strip timestamp prefixes from log output" name:"no-timestamps"`
}

func (c *LogCmd) Help() string {
return `
Examples:
# Get a job's logs by UUID (requires --pipeline and --build)
$ bk job log 0190046e-e199-453b-a302-a21a4d649d31 -p my-pipeline -b 123

# If inside a git repository with a configured pipeline
$ bk job log 0190046e-e199-453b-a302-a21a4d649d31 -b 123
# Get a job's logs by UUID
$ bk job log 0190046e-e199-453b-a302-a21a4d649d31

# Strip timestamp prefixes from output
$ bk job log 0190046e-e199-453b-a302-a21a4d649d31 -p my-pipeline -b 123 --no-timestamps
$ bk job log 0190046e-e199-453b-a302-a21a4d649d31 --no-timestamps
`
}

Expand All @@ -47,46 +41,23 @@ func (c *LogCmd) Run(kongCtx *kong.Context, globals cli.GlobalFlags) error {
f.Quiet = globals.IsQuiet()
f.NoPager = f.NoPager || globals.DisablePager()

organization, err := configuredOrganization(f.Config.OrganizationSlug())
if err != nil {
return err
}
if err := validation.ValidateConfiguration(f.Config, kongCtx.Command()); err != nil {
return err
}
warnIgnoredJobContextFlags(kongCtx.Stderr, c.Pipeline, c.BuildNumber)

ctx := context.Background()

pipelineRes := pipelineResolver.NewAggregateResolver(
pipelineResolver.ResolveFromFlag(c.Pipeline, f.Config),
pipelineResolver.ResolveFromConfig(f.Config, pipelineResolver.PickOneWithFactory(f)),
pipelineResolver.ResolveFromRepository(f, pipelineResolver.CachedPicker(f.Config, pipelineResolver.PickOneWithFactory(f))),
)

optionsResolver := options.AggregateResolver{
options.ResolveBranchFromRepository(f.GitRepository),
}

args := []string{}
if c.BuildNumber != "" {
args = []string{c.BuildNumber}
}
buildRes := buildResolver.NewAggregateResolver(
buildResolver.ResolveFromPositionalArgument(args, 0, pipelineRes.Resolve, f.Config),
buildResolver.ResolveBuildWithOpts(f, pipelineRes.Resolve, optionsResolver...),
)

bld, err := buildRes.Resolve(ctx)
if err != nil {
return err
}
if bld == nil {
return fmt.Errorf("no build found")
}

var logContent string
if err = bkIO.SpinWhile(f, "Fetching job log", func() error {
jobLog, _, apiErr := f.RestAPIClient.Jobs.GetJobLog(
jobLog, apiErr := getJobLog(
ctx,
bld.Organization,
bld.Pipeline,
fmt.Sprint(bld.BuildNumber),
f.RestAPIClient,
organization,
c.JobID,
)
if apiErr != nil {
Expand Down
11 changes: 11 additions & 0 deletions cmd/job/organization.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package job

import "fmt"

func configuredOrganization(organization string) (string, error) {
if organization == "" {
return "", fmt.Errorf("no organization configured. Run bk auth login, or bk use, to set an organization")
}

return organization, nil
}
23 changes: 23 additions & 0 deletions cmd/job/organization_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package job

import "testing"

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

got, err := configuredOrganization("buildkite")
if err != nil {
t.Fatalf("configuredOrganization() error = %v", err)
}
if got != "buildkite" {
t.Fatalf("configuredOrganization() = %q", got)
}
}

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

if _, err := configuredOrganization(""); err == nil {
t.Fatal("configuredOrganization() error = nil, want error")
}
}
57 changes: 13 additions & 44 deletions cmd/job/reprioritize.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,8 @@ import (
"fmt"

"github.com/alecthomas/kong"
buildResolver "github.com/buildkite/cli/v3/internal/build/resolver"
"github.com/buildkite/cli/v3/internal/build/resolver/options"
"github.com/buildkite/cli/v3/internal/cli"
bkIO "github.com/buildkite/cli/v3/internal/io"
pipelineResolver "github.com/buildkite/cli/v3/internal/pipeline/resolver"
"github.com/buildkite/cli/v3/pkg/cmd/factory"
"github.com/buildkite/cli/v3/pkg/cmd/validation"
buildkite "github.com/buildkite/go-buildkite/v4"
Expand All @@ -18,18 +15,15 @@ import (
type ReprioritizeCmd struct {
JobID string `arg:"" help:"Job UUID to reprioritize"`
Priority int `arg:"" help:"New priority value for the job"`
Pipeline string `help:"The pipeline to use. This can be a {pipeline slug} or in the format {org slug}/{pipeline slug}" short:"p"`
BuildNumber string `help:"The build number" short:"b"`
Pipeline string `help:"Deprecated; ignored because job UUIDs no longer require pipeline or build context" short:"p"`
BuildNumber string `help:"Deprecated; ignored because job UUIDs no longer require pipeline or build context" short:"b"`
}

func (c *ReprioritizeCmd) Help() string {
return `
Examples:
# Reprioritize a job (requires --pipeline and --build)
$ bk job reprioritize 0190046e-e199-453b-a302-a21a4d649d31 1 -p my-pipeline -b 123

# If inside a git repository with a configured pipeline
$ bk job reprioritize 0190046e-e199-453b-a302-a21a4d649d31 1 -b 123
# Reprioritize a job by UUID
$ bk job reprioritize 0190046e-e199-453b-a302-a21a4d649d31 1
`
}

Expand All @@ -43,51 +37,26 @@ func (c *ReprioritizeCmd) Run(kongCtx *kong.Context, globals cli.GlobalFlags) er
f.NoInput = globals.DisableInput()
f.Quiet = globals.IsQuiet()

organization, err := configuredOrganization(f.Config.OrganizationSlug())
if err != nil {
return err
}
if err := validation.ValidateConfiguration(f.Config, kongCtx.Command()); err != nil {
return err
}
warnIgnoredJobContextFlags(kongCtx.Stderr, c.Pipeline, c.BuildNumber)

ctx := context.Background()

pipelineRes := pipelineResolver.NewAggregateResolver(
pipelineResolver.ResolveFromFlag(c.Pipeline, f.Config),
pipelineResolver.ResolveFromConfig(f.Config, pipelineResolver.PickOneWithFactory(f)),
pipelineResolver.ResolveFromRepository(f, pipelineResolver.CachedPicker(f.Config, pipelineResolver.PickOneWithFactory(f))),
)

optionsResolver := options.AggregateResolver{
options.ResolveBranchFromRepository(f.GitRepository),
}

args := []string{}
if c.BuildNumber != "" {
args = []string{c.BuildNumber}
}
buildRes := buildResolver.NewAggregateResolver(
buildResolver.ResolveFromPositionalArgument(args, 0, pipelineRes.Resolve, f.Config),
buildResolver.ResolveBuildWithOpts(f, pipelineRes.Resolve, optionsResolver...),
)

bld, err := buildRes.Resolve(ctx)
if err != nil {
return err
}
if bld == nil {
return fmt.Errorf("no build found")
}

var job buildkite.Job
if err = bkIO.SpinWhile(f, "Reprioritizing job", func() error {
var apiErr error
job, _, apiErr = f.RestAPIClient.Jobs.ReprioritizeJob(
job, apiErr = reprioritizeJob(
ctx,
bld.Organization,
bld.Pipeline,
fmt.Sprint(bld.BuildNumber),
f.RestAPIClient,
organization,
c.JobID,
&buildkite.JobReprioritizationOptions{
Priority: c.Priority,
},
c.Priority,
)
return apiErr
}); err != nil {
Expand Down
69 changes: 69 additions & 0 deletions cmd/job/rest.go
Comment thread
lox marked this conversation as resolved.
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package job

import (
"context"
"fmt"
"net/url"

buildkite "github.com/buildkite/go-buildkite/v4"
)

type unblockJobOptions struct {
Fields map[string]any `json:"fields,omitempty"`
}

func organizationJobPath(organization, jobID, action string) string {
return fmt.Sprintf(
"v2/organizations/%s/jobs/%s/%s",
url.PathEscape(organization),
url.PathEscape(jobID),
action,
)
}

func getJobLog(ctx context.Context, client *buildkite.Client, organization, jobID string) (buildkite.JobLog, error) {
req, err := client.NewRequest(ctx, "GET", organizationJobPath(organization, jobID, "log"), nil)
if err != nil {
return buildkite.JobLog{}, err
}
req.Header.Set("Accept", "application/json")

var jobLog buildkite.JobLog
if _, err := client.Do(req, &jobLog); err != nil {
return buildkite.JobLog{}, err
}

return jobLog, nil
}

func reprioritizeJob(ctx context.Context, client *buildkite.Client, organization, jobID string, priority int) (buildkite.Job, error) {
req, err := client.NewRequest(ctx, "PUT", organizationJobPath(organization, jobID, "reprioritize"), &buildkite.JobReprioritizationOptions{
Priority: priority,
})
if err != nil {
return buildkite.Job{}, err
}

var job buildkite.Job
if _, err := client.Do(req, &job); err != nil {
return buildkite.Job{}, err
}

return job, nil
}

func unblockJob(ctx context.Context, client *buildkite.Client, organization, jobID string, fields map[string]any) (buildkite.Job, error) {
req, err := client.NewRequest(ctx, "PUT", organizationJobPath(organization, jobID, "unblock"), &unblockJobOptions{
Fields: fields,
})
if err != nil {
return buildkite.Job{}, err
}

var job buildkite.Job
if _, err := client.Do(req, &job); err != nil {
return buildkite.Job{}, err
}

return job, nil
}
Loading