diff --git a/cmd/job/graphql/retry.graphql b/cmd/job/graphql/retry.graphql deleted file mode 100644 index 93d11016..00000000 --- a/cmd/job/graphql/retry.graphql +++ /dev/null @@ -1,9 +0,0 @@ -mutation RetryJob($id: ID!) { - jobTypeCommandRetry(input: {id: $id}) { - jobTypeCommand { - id - state - url - } - } -} diff --git a/cmd/job/rest.go b/cmd/job/rest.go index 842e928e..383d84d7 100644 --- a/cmd/job/rest.go +++ b/cmd/job/rest.go @@ -52,6 +52,20 @@ func reprioritizeJob(ctx context.Context, client *buildkite.Client, organization return job, nil } +func retryJob(ctx context.Context, client *buildkite.Client, organization, jobID string) (buildkite.Job, error) { + req, err := client.NewRequest(ctx, "PUT", organizationJobPath(organization, jobID, "retry"), nil) + 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, diff --git a/cmd/job/retry.go b/cmd/job/retry.go index a8287e7e..be985c84 100644 --- a/cmd/job/retry.go +++ b/cmd/job/retry.go @@ -6,15 +6,12 @@ import ( "github.com/alecthomas/kong" "github.com/buildkite/cli/v3/internal/cli" - bkGraphQL "github.com/buildkite/cli/v3/internal/graphql" bkIO "github.com/buildkite/cli/v3/internal/io" - "github.com/buildkite/cli/v3/internal/util" "github.com/buildkite/cli/v3/pkg/cmd/factory" "github.com/buildkite/cli/v3/pkg/cmd/validation" + buildkite "github.com/buildkite/go-buildkite/v4" ) -const jobCommandPrefix = "JobTypeCommand---" - type RetryCmd struct { JobID string `arg:"" help:"Job UUID to retry"` } @@ -38,28 +35,30 @@ func (c *RetryCmd) Run(kongCtx *kong.Context, globals cli.GlobalFlags) error { 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 } - // Given a job UUID argument, we need to generate the GraphQL ID matching - graphqlID := util.GenerateGraphQLID(jobCommandPrefix, c.JobID) - ctx := context.Background() - var j *bkGraphQL.RetryJobResponse + var job buildkite.Job if err = bkIO.SpinWhile(f, "Retrying job", func() error { - j, err = bkGraphQL.RetryJob(ctx, f.GraphQLClient, graphqlID) - return err + var apiErr error + job, apiErr = retryJob( + ctx, + f.RestAPIClient, + organization, + c.JobID, + ) + return apiErr }); err != nil { return err } - // Fixes segfault when error is returned, e.g. "Jobs from canceled builds cannot be retried" - if j == nil || j.JobTypeCommandRetry == nil { - return fmt.Errorf("failed to retry job") - } - - fmt.Println("Successfully retried job: " + j.JobTypeCommandRetry.JobTypeCommand.Url) + fmt.Println("Successfully retried job: " + job.WebURL) return nil } diff --git a/cmd/job/retry_test.go b/cmd/job/retry_test.go new file mode 100644 index 00000000..953e3822 --- /dev/null +++ b/cmd/job/retry_test.go @@ -0,0 +1,52 @@ +package job + +import ( + "context" + "io" + "net/http" + "net/http/httptest" + "testing" + + buildkite "github.com/buildkite/go-buildkite/v4" +) + +func TestRetryJobUsesOrganizationEndpoint(t *testing.T) { + t.Parallel() + + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.Method != http.MethodPut { + t.Fatalf("method = %s, want PUT", r.Method) + } + if r.URL.Path != "/v2/organizations/buildkite/jobs/job-1/retry" { + t.Fatalf("path = %s", r.URL.Path) + } + + body, err := io.ReadAll(r.Body) + if err != nil { + t.Fatalf("read body: %v", err) + } + if len(body) != 0 { + t.Fatalf("body = %q, want empty", body) + } + + w.Header().Set("Content-Type", "application/json") + _, _ = w.Write([]byte(`{"id":"job-2","state":"scheduled","retried_in_job_id":"job-2","web_url":"https://buildkite.com/buildkite/cli/builds/42#job-2"}`)) + })) + defer server.Close() + + client, err := buildkite.NewOpts( + buildkite.WithBaseURL(server.URL), + buildkite.WithTokenAuth("test-token"), + ) + if err != nil { + t.Fatalf("new client: %v", err) + } + + job, err := retryJob(context.Background(), client, "buildkite", "job-1") + if err != nil { + t.Fatalf("retryJob() error = %v", err) + } + if job.ID != "job-2" { + t.Fatalf("job = %#v", job) + } +} diff --git a/internal/graphql/generated.go b/internal/graphql/generated.go index f484957f..6a0a3ba7 100644 --- a/internal/graphql/generated.go +++ b/internal/graphql/generated.go @@ -3344,57 +3344,6 @@ func (v *PipelineCreateWebhookResponse) GetPipelineCreateWebhook() *PipelineCrea return v.PipelineCreateWebhook } -// RetryJobJobTypeCommandRetryJobTypeCommandRetryPayload includes the requested fields of the GraphQL type JobTypeCommandRetryPayload. -// The GraphQL type's documentation follows. -// -// Autogenerated return type of JobTypeCommandRetry. -type RetryJobJobTypeCommandRetryJobTypeCommandRetryPayload struct { - JobTypeCommand RetryJobJobTypeCommandRetryJobTypeCommandRetryPayloadJobTypeCommand `json:"jobTypeCommand"` -} - -// GetJobTypeCommand returns RetryJobJobTypeCommandRetryJobTypeCommandRetryPayload.JobTypeCommand, and is useful for accessing the field via an interface. -func (v *RetryJobJobTypeCommandRetryJobTypeCommandRetryPayload) GetJobTypeCommand() RetryJobJobTypeCommandRetryJobTypeCommandRetryPayloadJobTypeCommand { - return v.JobTypeCommand -} - -// RetryJobJobTypeCommandRetryJobTypeCommandRetryPayloadJobTypeCommand includes the requested fields of the GraphQL type JobTypeCommand. -// The GraphQL type's documentation follows. -// -// A type of job that runs a command on an agent -type RetryJobJobTypeCommandRetryJobTypeCommandRetryPayloadJobTypeCommand struct { - Id string `json:"id"` - // The state of the job - State JobStates `json:"state"` - // The URL for the job - Url string `json:"url"` -} - -// GetId returns RetryJobJobTypeCommandRetryJobTypeCommandRetryPayloadJobTypeCommand.Id, and is useful for accessing the field via an interface. -func (v *RetryJobJobTypeCommandRetryJobTypeCommandRetryPayloadJobTypeCommand) GetId() string { - return v.Id -} - -// GetState returns RetryJobJobTypeCommandRetryJobTypeCommandRetryPayloadJobTypeCommand.State, and is useful for accessing the field via an interface. -func (v *RetryJobJobTypeCommandRetryJobTypeCommandRetryPayloadJobTypeCommand) GetState() JobStates { - return v.State -} - -// GetUrl returns RetryJobJobTypeCommandRetryJobTypeCommandRetryPayloadJobTypeCommand.Url, and is useful for accessing the field via an interface. -func (v *RetryJobJobTypeCommandRetryJobTypeCommandRetryPayloadJobTypeCommand) GetUrl() string { - return v.Url -} - -// RetryJobResponse is returned by RetryJob on success. -type RetryJobResponse struct { - // Retry a job. - JobTypeCommandRetry *RetryJobJobTypeCommandRetryJobTypeCommandRetryPayload `json:"jobTypeCommandRetry"` -} - -// GetJobTypeCommandRetry returns RetryJobResponse.JobTypeCommandRetry, and is useful for accessing the field via an interface. -func (v *RetryJobResponse) GetJobTypeCommandRetry() *RetryJobJobTypeCommandRetryJobTypeCommandRetryPayload { - return v.JobTypeCommandRetry -} - // UnblockJobJobTypeBlockUnblockJobTypeBlockUnblockPayload includes the requested fields of the GraphQL type JobTypeBlockUnblockPayload. // The GraphQL type's documentation follows. // @@ -3623,14 +3572,6 @@ type __PipelineCreateWebhookInput struct { // GetId returns __PipelineCreateWebhookInput.Id, and is useful for accessing the field via an interface. func (v *__PipelineCreateWebhookInput) GetId() string { return v.Id } -// __RetryJobInput is used internally by genqlient -type __RetryJobInput struct { - Id string `json:"id"` -} - -// GetId returns __RetryJobInput.Id, and is useful for accessing the field via an interface. -func (v *__RetryJobInput) GetId() string { return v.Id } - // __UnblockJobInput is used internally by genqlient type __UnblockJobInput struct { Id string `json:"id"` @@ -4247,44 +4188,6 @@ func PipelineCreateWebhook( return data_, err_ } -// The mutation executed by RetryJob. -const RetryJob_Operation = ` -mutation RetryJob ($id: ID!) { - jobTypeCommandRetry(input: {id:$id}) { - jobTypeCommand { - id - state - url - } - } -} -` - -func RetryJob( - ctx_ context.Context, - client_ graphql.Client, - id string, -) (data_ *RetryJobResponse, err_ error) { - req_ := &graphql.Request{ - OpName: "RetryJob", - Query: RetryJob_Operation, - Variables: &__RetryJobInput{ - Id: id, - }, - } - - data_ = &RetryJobResponse{} - resp_ := &graphql.Response{Data: data_} - - err_ = client_.MakeRequest( - ctx_, - req_, - resp_, - ) - - return data_, err_ -} - // The mutation executed by UnblockJob. const UnblockJob_Operation = ` mutation UnblockJob ($id: ID!, $fields: JSON) {