diff --git a/internal/api/fetch_test_plan.go b/internal/api/fetch_test_plan.go index 36fc5c45..0ef9c005 100644 --- a/internal/api/fetch_test_plan.go +++ b/internal/api/fetch_test_plan.go @@ -10,8 +10,8 @@ import ( // FetchTestPlan fetchs a test plan from the server. // ErrRetryTimeout is returned if the client failed to communicate with the server after exceeding the retry limit. -func (c Client) FetchTestPlan(ctx context.Context, suiteSlug string, identifier string) (*plan.TestPlan, error) { - url := fmt.Sprintf("%s/v2/analytics/organizations/%s/suites/%s/test_plan?identifier=%s", c.ServerBaseUrl, c.OrganizationSlug, suiteSlug, identifier) +func (c Client) FetchTestPlan(ctx context.Context, suiteSlug string, identifier string, jobRetryCount int) (*plan.TestPlan, error) { + url := fmt.Sprintf("%s/v2/analytics/organizations/%s/suites/%s/test_plan?identifier=%s&job_retry_count=%d", c.ServerBaseUrl, c.OrganizationSlug, suiteSlug, identifier, jobRetryCount) var testPlan plan.TestPlan diff --git a/internal/api/fetch_test_plan_test.go b/internal/api/fetch_test_plan_test.go index d5c61579..89a36750 100644 --- a/internal/api/fetch_test_plan_test.go +++ b/internal/api/fetch_test_plan_test.go @@ -32,6 +32,7 @@ func TestFetchTestPlan(t *testing.T) { WithRequest("GET", "/v2/analytics/organizations/buildkite/suites/rspec/test_plan", func(b *consumer.V2RequestBuilder) { b.Header("Authorization", matchers.Like("Bearer asdf1234")) b.Query("identifier", matchers.Like("abc123")) + b.Query("job_retry_count", matchers.Like("0")) }). WillRespondWith(200, func(b *consumer.V2ResponseBuilder) { b.Header("Content-Type", matchers.Like("application/json; charset=utf-8")) @@ -62,7 +63,7 @@ func TestFetchTestPlan(t *testing.T) { c := NewClient(cfg) - got, err := c.FetchTestPlan(context.Background(), "rspec", "abc123") + got, err := c.FetchTestPlan(context.Background(), "rspec", "abc123", 0) if err != nil { t.Errorf("FetchTestPlan() error = %v", err) @@ -113,7 +114,8 @@ func TestFetchTestPlan_NotFound(t *testing.T) { WithRequest("GET", "/v2/analytics/organizations/buildkite/suites/rspec/test_plan", func(b *consumer.V2RequestBuilder) { b. Header("Authorization", matchers.Like("Bearer asdf1234")). - Query("identifier", matchers.Like("abc123")) + Query("identifier", matchers.Like("abc123")). + Query("job_retry_count", matchers.Like("0")) }). WillRespondWith(404, func(b *consumer.V2ResponseBuilder) { b.Header("Content-Type", matchers.Like("application/json; charset=utf-8")) @@ -132,7 +134,7 @@ func TestFetchTestPlan_NotFound(t *testing.T) { c := NewClient(cfg) - got, err := c.FetchTestPlan(context.Background(), "rspec", "abc123") + got, err := c.FetchTestPlan(context.Background(), "rspec", "abc123", 0) if err != nil { t.Errorf("FetchTestPlan() error = %v", err) @@ -165,7 +167,7 @@ func TestFetchTestPlan_BadRequest(t *testing.T) { } c := NewClient(cfg) - got, err := c.FetchTestPlan(context.Background(), "my-suite", "xyz") + got, err := c.FetchTestPlan(context.Background(), "my-suite", "xyz", 0) if requestCount > 1 { t.Errorf("http request count = %v, want %d", requestCount, 1) @@ -199,7 +201,7 @@ func TestFetchTestPlan_InternalServerError(t *testing.T) { } c := NewClient(cfg) - got, err := c.FetchTestPlan(context.Background(), "my-suite", "xyz") + got, err := c.FetchTestPlan(context.Background(), "my-suite", "xyz", 0) if !errors.Is(err, ErrRetryTimeout) { t.Errorf("FetchTestPlan() error = %v, want %v", err, ErrRetryTimeout) diff --git a/internal/config/config.go b/internal/config/config.go index d5f4b29e..8c8753fd 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -34,6 +34,8 @@ type Config struct { TestRunner string // Branch is the string value of the git branch name, used by Buildkite only. Branch string + // JobRetryCount is the count of the number of times the job has been retried. + JobRetryCount int // errs is a map of environment variables name and the validation errors associated with them. errs InvalidConfigError } diff --git a/internal/config/config_test.go b/internal/config/config_test.go index a8e95563..03cc75aa 100644 --- a/internal/config/config_test.go +++ b/internal/config/config_test.go @@ -22,6 +22,7 @@ func setEnv(t *testing.T) { os.Setenv("BUILDKITE_STEP_ID", "456") os.Setenv("BUILDKITE_TEST_ENGINE_TEST_RUNNER", "rspec") os.Setenv("BUILDKITE_TEST_ENGINE_RESULT_PATH", "tmp/rspec.json") + os.Setenv("BUILDKITE_RETRY_COUNT", "0") } func TestNewConfig(t *testing.T) { @@ -44,6 +45,7 @@ func TestNewConfig(t *testing.T) { ResultPath: "tmp/rspec.json", SuiteSlug: "my_suite", TestRunner: "rspec", + JobRetryCount: 0, errs: InvalidConfigError{}, } @@ -84,6 +86,7 @@ func TestNewConfig_MissingConfigWithDefault(t *testing.T) { SuiteSlug: "my_suite", TestRunner: "rspec", ResultPath: "tmp/rspec.json", + JobRetryCount: 0, } if diff := cmp.Diff(c, want, cmpopts.IgnoreUnexported(Config{})); diff != "" { diff --git a/internal/config/env.go b/internal/config/env.go index 14a6b4a2..596c50ba 100644 --- a/internal/config/env.go +++ b/internal/config/env.go @@ -52,6 +52,7 @@ func (c Config) DumpEnv() map[string]string { "BUILDKITE_TEST_ENGINE_TEST_RUNNER", "BUILDKITE_STEP_ID", "BUILDKITE_BRANCH", + "BUILDKITE_RETRY_COUNT", } envs := make(map[string]string) diff --git a/internal/config/read.go b/internal/config/read.go index 025db14a..cfd10238 100644 --- a/internal/config/read.go +++ b/internal/config/read.go @@ -26,6 +26,7 @@ import ( // - BUILDKITE_TEST_ENGINE_TEST_FILE_PATTERN (TestFilePattern) // - BUILDKITE_TEST_ENGINE_TEST_FILE_EXCLUDE_PATTERN (TestFileExcludePattern) // - BUILDKITE_BRANCH (Branch) +// - BUILDKITE_RETRY_COUNT (JobRetryCount) // // If we are going to support other CI environment in the future, // we will need to change where we read the configuration from. @@ -59,6 +60,12 @@ func (c *Config) readFromEnv() error { // used by Buildkite only, for experimental plans c.Branch = os.Getenv("BUILDKITE_BRANCH") + JobRetryCount, err := getIntEnvWithDefault("BUILDKITE_RETRY_COUNT", 0) + c.JobRetryCount = JobRetryCount + if err != nil { + c.errs.appendFieldError("BUILDKITE_RETRY_COUNT", "was %q, must be a number", os.Getenv("BUILDKITE_RETRY_COUNT")) + } + MaxRetries, err := getIntEnvWithDefault("BUILDKITE_TEST_ENGINE_RETRY_COUNT", 0) c.MaxRetries = MaxRetries if err != nil { diff --git a/internal/config/read_test.go b/internal/config/read_test.go index 0b4b0ae0..4aa0c8e2 100644 --- a/internal/config/read_test.go +++ b/internal/config/read_test.go @@ -45,6 +45,7 @@ func TestConfigReadFromEnv(t *testing.T) { TestFileExcludePattern: "spec/feature/**/*_spec.rb", TestRunner: "rspec", ResultPath: "result.json", + JobRetryCount: 0, } if err != nil { diff --git a/main.go b/main.go index a79abf7c..82635081 100644 --- a/main.go +++ b/main.go @@ -300,7 +300,7 @@ func fetchOrCreateTestPlan(ctx context.Context, apiClient *api.Client, cfg confi debug.Println("Fetching test plan") // Fetch the plan from the server's cache. - cachedPlan, err := apiClient.FetchTestPlan(ctx, cfg.SuiteSlug, cfg.Identifier) + cachedPlan, err := apiClient.FetchTestPlan(ctx, cfg.SuiteSlug, cfg.Identifier, cfg.JobRetryCount) handleError := func(err error) (plan.TestPlan, error) { if errors.Is(err, api.ErrRetryTimeout) { diff --git a/main_test.go b/main_test.go index 030d04c7..a70b1b6b 100644 --- a/main_test.go +++ b/main_test.go @@ -798,6 +798,7 @@ func TestSendMetadata(t *testing.T) { "BUILDKITE_TEST_ENGINE_SUITE_SLUG": "rspec", "BUILDKITE_TEST_ENGINE_TEST_CMD": "bundle exec rspec", "BUILDKITE_TEST_ENGINE_TEST_RUNNER": "rspec", + "BUILDKITE_RETRY_COUNT": "0", } for k, v := range env { _ = os.Setenv(k, v) @@ -840,6 +841,7 @@ func TestSendMetadata(t *testing.T) { "BUILDKITE_TEST_ENGINE_TEST_FILE_PATTERN": "", "BUILDKITE_TEST_ENGINE_TEST_RUNNER": "rspec", "BUILDKITE_BRANCH": "", + "BUILDKITE_RETRY_COUNT": "0", }, Statistics: runner.RunStatistics{ Total: 3,