Skip to content
Merged
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
2 changes: 1 addition & 1 deletion CONTEXT.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ The visual health indicator for a Pipeline. Derived from the latest Execution st
| 🔴 Red | Broken or blocked | `Failed`, `Stopped` |
| 🟡 Yellow | In progress | `InProgress` |
| ⏸ Awaiting approval | Waiting on a person | `InProgress` at a manual approval action with an open approval token |
| ⚪ Grey | No signal | `Superseded`, no executions yet |
| ⚪ Gray | No signal | `Superseded`, no executions yet |

Awaiting approval ranks between Yellow and Red: it needs a person, not more time, but a failure still outranks it. It is raised in `state.NewPipeline` from the action's approval token, because execution statuses alone can't tell an approval gate from a running build.

Expand Down
4 changes: 2 additions & 2 deletions docs/screenshot.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion internal/cfn/cfn.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ func (c *Client) FetchStacks(ctx context.Context, names []string) ([]state.Stack
result = append(result, state.StackState{
Name: name,
Status: "NOT_FOUND",
Stoplight: health.StoplightGrey,
Stoplight: health.StoplightGray,
})
continue
}
Expand Down
8 changes: 4 additions & 4 deletions internal/cfn/fetch_internal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ func (f fakeDescribeStacks) DescribeStacks(_ context.Context, in *cloudformation
}

// A failed call has to reach the poller as an error, so it keeps the last
// known stacks on screen marked stale instead of turning them grey.
// known stacks on screen marked stale instead of turning them gray.
func TestFetchStacksReturnsCallErrors(t *testing.T) {
c := &Client{svc: fakeDescribeStacks{
stacks: map[string]cfntypes.StackStatus{"ok": cfntypes.StackStatusUpdateComplete},
Expand All @@ -42,7 +42,7 @@ func TestFetchStacksReturnsCallErrors(t *testing.T) {
}

// A stack that doesn't exist is an answer, not a failed call: AWS reports it
// as a ValidationError, and it shows grey without marking the fetch stale.
// as a ValidationError, and it shows gray without marking the fetch stale.
func TestFetchStacksReportsMissingStackAsNotFound(t *testing.T) {
c := &Client{svc: fakeDescribeStacks{
stacks: map[string]cfntypes.StackStatus{"ok": cfntypes.StackStatusUpdateComplete},
Expand All @@ -60,7 +60,7 @@ func TestFetchStacksReportsMissingStackAsNotFound(t *testing.T) {
if got[0].Stoplight != health.StoplightGreen {
t.Errorf("ok: Stoplight = %v, want green", got[0].Stoplight)
}
if got[1].Status != "NOT_FOUND" || got[1].Stoplight != health.StoplightGrey {
t.Errorf("gone: got %+v, want NOT_FOUND grey", got[1])
if got[1].Status != "NOT_FOUND" || got[1].Stoplight != health.StoplightGray {
t.Errorf("gone: got %+v, want NOT_FOUND gray", got[1])
}
}
4 changes: 2 additions & 2 deletions internal/ecs/ecs.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ func (c *Client) FetchServices(ctx context.Context, cluster string, services []s
result = append(result, sd)
}

// For any requested service not returned, add a grey entry.
// For any requested service not returned, add a gray entry.
found := make(map[string]bool, len(result))
for _, sd := range result {
found[sd.Name] = true
Expand All @@ -100,7 +100,7 @@ func (c *Client) FetchServices(ctx context.Context, cluster string, services []s
result = append(result, state.ECSServiceState{
Name: svcName,
Cluster: cluster,
Stoplight: health.StoplightGrey,
Stoplight: health.StoplightGray,
})
}
}
Expand Down
6 changes: 3 additions & 3 deletions internal/fix/fix_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,11 @@ func TestPlan_noActionWhenGreen(t *testing.T) {
}
}

func TestPlan_noActionWhenGrey(t *testing.T) {
p := proj(health.StoplightGrey, nil, nil)
func TestPlan_noActionWhenGray(t *testing.T) {
p := proj(health.StoplightGray, nil, nil)
plan := fix.Plan(p, 30*time.Minute)
if plan != nil {
t.Errorf("expected nil plan for grey project, got %v", plan)
t.Errorf("expected nil plan for gray project, got %v", plan)
}
}

Expand Down
8 changes: 4 additions & 4 deletions internal/health/health.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ package health
type Stoplight int

const (
StoplightGrey Stoplight = iota // no executions, or superseded
StoplightGray Stoplight = iota // no executions, or superseded
StoplightGreen // succeeded
StoplightYellow // in progress
// StoplightAwaitingApproval is a pipeline paused at a manual approval. It
Expand Down Expand Up @@ -50,14 +50,14 @@ func statusToStoplight(s ExecutionStatus) Stoplight {
case StatusInProgress:
return StoplightYellow
default:
return StoplightGrey
return StoplightGray
}
}

// Aggregate returns the worst-case Stoplight across all provided execution statuses.
// Red > Yellow > Green > Grey.
// Red > Yellow > Green > Gray.
func Aggregate(statuses []ExecutionStatus) Stoplight {
result := StoplightGrey
result := StoplightGray
for _, s := range statuses {
light := statusToStoplight(s)
if light > result {
Expand Down
6 changes: 3 additions & 3 deletions internal/health/health_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ import (

func TestAggregate_empty(t *testing.T) {
got := health.Aggregate(nil)
if got != health.StoplightGrey {
t.Errorf("expected grey, got %v", got)
if got != health.StoplightGray {
t.Errorf("expected gray, got %v", got)
}
}

Expand Down Expand Up @@ -49,7 +49,7 @@ func TestStoplight_String(t *testing.T) {
{health.StoplightGreen, "🟢"},
{health.StoplightRed, "🔴"},
{health.StoplightYellow, "🟡"},
{health.StoplightGrey, "⚪"},
{health.StoplightGray, "⚪"},
}
for _, tc := range cases {
if got := tc.light.String(); got != tc.want {
Expand Down
2 changes: 1 addition & 1 deletion internal/health/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ type Service struct {
func (s Service) Stoplight() Stoplight {
switch {
case s.Desired == 0 && s.Running == 0:
return StoplightGrey
return StoplightGray
case s.Running == 0 && s.Desired > 0:
return StoplightRed
case s.FailingTasks > 0:
Expand Down
2 changes: 1 addition & 1 deletion internal/health/service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ func TestServiceStoplight(t *testing.T) {
{"deploying", health.Service{Running: 2, Desired: 2, ActiveDeployment: true}, health.StoplightYellow},
{"scaling_up", health.Service{Running: 1, Desired: 2}, health.StoplightYellow},
{"pending_tasks", health.Service{Running: 2, Desired: 2, Pending: 1}, health.StoplightYellow},
{"zero_desired_zero_running", health.Service{}, health.StoplightGrey},
{"zero_desired_zero_running", health.Service{}, health.StoplightGray},
{"down", health.Service{Running: 0, Desired: 2}, health.StoplightRed},
{"failing_tasks", health.Service{Running: 2, Desired: 2, FailingTasks: 1}, health.StoplightRed},
{"failing_tasks_with_running", health.Service{Running: 1, Desired: 2, FailingTasks: 2}, health.StoplightRed},
Expand Down
4 changes: 2 additions & 2 deletions internal/health/stack.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,10 +65,10 @@ func (s StackStatus) Stoplight() Stoplight {
strings.HasPrefix(status, "DELETE_"):
return StoplightRed
case status == "REVIEW_IN_PROGRESS":
return StoplightGrey
return StoplightGray
case strings.HasSuffix(status, "_IN_PROGRESS"):
return StoplightYellow
default:
return StoplightGrey
return StoplightGray
}
}
6 changes: 3 additions & 3 deletions internal/health/stack_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ func TestStackStuckReason(t *testing.T) {
{"CREATE_FAILED", "stack_failed"},
{"UPDATE_ROLLBACK_FAILED", "stack_failed"},
{"DELETE_FAILED", "stack_failed"},
// A change set waiting on review shows grey: it's parked on a person,
// A change set waiting on review shows gray: it's parked on a person,
// not a wedged deploy.
{"REVIEW_IN_PROGRESS", ""},
}
Expand All @@ -46,8 +46,8 @@ func TestStackStoplight(t *testing.T) {
{"CREATE_IN_PROGRESS", health.StoplightYellow},
{"UPDATE_IN_PROGRESS", health.StoplightYellow},
{"UPDATE_ROLLBACK_IN_PROGRESS", health.StoplightYellow},
{"REVIEW_IN_PROGRESS", health.StoplightGrey},
{"", health.StoplightGrey},
{"REVIEW_IN_PROGRESS", health.StoplightGray},
{"", health.StoplightGray},
}

for _, tc := range cases {
Expand Down
2 changes: 1 addition & 1 deletion internal/poller/fetch.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ func fetchProject(ctx context.Context, proj config.Project, c accountClients, pr
out.Pipeline = prev.Pipeline
out.Pipeline.FetchStatus = stale(noClient("", proj.Account, c.pipelineErr))
case proj.Pipeline.Name == "":
out.Pipeline = state.PipelineState{Account: proj.Account, Stoplight: health.StoplightGrey}
out.Pipeline = state.PipelineState{Account: proj.Account, Stoplight: health.StoplightGray}
default:
data, err := c.pipeline.FetchPipeline(ctx, proj.Pipeline.Name)
if err != nil {
Expand Down
6 changes: 3 additions & 3 deletions internal/poller/fetch_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ func TestFetchProjectRecordsFailureWithNothingToCarry(t *testing.T) {
}
}

// A project with only stacks gets a grey pipeline placeholder and no
// A project with only stacks gets a gray pipeline placeholder and no
// pipeline call.
func TestFetchProjectWithoutPipeline(t *testing.T) {
proj := appProject()
Expand All @@ -192,7 +192,7 @@ func TestFetchProjectWithoutPipeline(t *testing.T) {
if len(pipes.fetched()) != 0 {
t.Errorf("expected no pipeline fetch, got %v", pipes.fetched())
}
if got.Pipeline.Stoplight != health.StoplightGrey || got.Pipeline.Account != "prod" {
t.Errorf("expected a grey placeholder for prod, got %+v", got.Pipeline)
if got.Pipeline.Stoplight != health.StoplightGray || got.Pipeline.Account != "prod" {
t.Errorf("expected a gray placeholder for prod, got %+v", got.Pipeline)
}
}
4 changes: 2 additions & 2 deletions internal/poller/poller.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ func New(cfg *config.Config, factory ClientFactory, cfnFactory CFNClientFactory,
Pipeline: state.PipelineState{
Account: p.Account,
Name: p.Pipeline.Name,
Stoplight: health.StoplightGrey,
Stoplight: health.StoplightGray,
},
}
}
Expand Down Expand Up @@ -213,7 +213,7 @@ func servicesForCluster(services []state.ECSServiceState, cluster string) []stat

// carryForward rebases a project's last known state onto its new config entry.
// Rebuilding every row from zero on a config edit would blank the health of
// every project until the next poll returns — the same "grey means both
// every project until the next poll returns — the same "gray means both
// 'unknown' and 'was fine a second ago'" ambiguity that carried-forward fetches
// exist to avoid. State the edit invalidated is dropped rather than carried, so
// the edit still shows up immediately.
Expand Down
22 changes: 11 additions & 11 deletions internal/poller/poller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -235,8 +235,8 @@ name = "beta-pipeline"
if snap.Projects[0].Name != "beta" {
t.Errorf("expected beta, got %q", snap.Projects[0].Name)
}
if snap.Projects[0].Pipeline.Stoplight != health.StoplightGrey {
t.Errorf("expected seeded state to be grey, got %v", snap.Projects[0].Pipeline.Stoplight)
if snap.Projects[0].Pipeline.Stoplight != health.StoplightGray {
t.Errorf("expected seeded state to be gray, got %v", snap.Projects[0].Pipeline.Stoplight)
}
}

Expand Down Expand Up @@ -340,7 +340,7 @@ func TestPrevPipelineMatchesByAccountAndName(t *testing.T) {
if got := prevPipeline(prev, "beta", "prod"); got.Name != "beta-pipeline" || got.Stoplight != health.StoplightRed {
t.Errorf("expected beta's pipeline, got %+v", got)
}
if got := prevPipeline(prev, "gamma", "prod"); got.Name != "" || got.Stoplight != health.StoplightGrey {
if got := prevPipeline(prev, "gamma", "prod"); got.Name != "" || got.Stoplight != health.StoplightGray {
t.Errorf("expected zero PipelineState for an unknown project, got %+v", got)
}
if got := prevPipeline(nil, "alpha", "prod"); got.Name != "" {
Expand Down Expand Up @@ -535,10 +535,10 @@ name = "delta-pipeline"
if got.Pipeline.Name != "gamma-pipeline" {
t.Errorf("expected gamma-pipeline, got %q", got.Pipeline.Name)
}
// gamma is new to the config, so there is nothing to carry and grey is
// the honest starting colour.
if got.Pipeline.Stoplight != health.StoplightGrey {
t.Errorf("expected grey for a project new to the config, got %v", got.Pipeline.Stoplight)
// gamma is new to the config, so there is nothing to carry and gray is
// the honest starting color.
if got.Pipeline.Stoplight != health.StoplightGray {
t.Errorf("expected gray for a project new to the config, got %v", got.Pipeline.Stoplight)
}
}

Expand Down Expand Up @@ -583,8 +583,8 @@ name = "gamma-pipeline"
if got := byName["beta"].Pipeline.Stoplight; got != health.StoplightRed {
t.Errorf("expected beta's red to survive the reload, got %v", got)
}
if got := byName["gamma"].Pipeline.Stoplight; got != health.StoplightGrey {
t.Errorf("expected the new project to start grey, got %v", got)
if got := byName["gamma"].Pipeline.Stoplight; got != health.StoplightGray {
t.Errorf("expected the new project to start gray, got %v", got)
}
}

Expand Down Expand Up @@ -656,8 +656,8 @@ services = ["keep-svc"]
if got.Pipeline.Name != "renamed-pipeline" || len(got.Pipeline.Stages) != 0 {
t.Errorf("expected the renamed pipeline to start over, got %+v", got.Pipeline)
}
if got.Pipeline.Stoplight != health.StoplightGrey {
t.Errorf("expected the renamed pipeline grey, got %v", got.Pipeline.Stoplight)
if got.Pipeline.Stoplight != health.StoplightGray {
t.Errorf("expected the renamed pipeline gray, got %v", got.Pipeline.Stoplight)
}
if len(got.Stacks) != 1 || got.Stacks[0].Name != "keep-stack" {
t.Errorf("expected only the still-configured stack, got %+v", got.Stacks)
Expand Down
8 changes: 4 additions & 4 deletions internal/state/state.go
Original file line number Diff line number Diff line change
Expand Up @@ -158,19 +158,19 @@ func (p ProjectState) Stoplight() health.Stoplight {
}

// StacksStoplight returns the worst-case stoplight across the project's
// stacks, or grey when it has none.
// stacks, or gray when it has none.
func (p ProjectState) StacksStoplight() health.Stoplight {
worst := health.StoplightGrey
worst := health.StoplightGray
for _, s := range p.Stacks {
worst = max(worst, s.Stoplight)
}
return worst
}

// ECSStoplight returns the worst-case stoplight across the project's ECS
// services, or grey when it has none.
// services, or gray when it has none.
func (p ProjectState) ECSStoplight() health.Stoplight {
worst := health.StoplightGrey
worst := health.StoplightGray
for _, s := range p.ECSServices {
worst = max(worst, s.Stoplight)
}
Expand Down
10 changes: 5 additions & 5 deletions internal/state/state_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,12 @@ func TestProjectState_stoplightFromPipeline(t *testing.T) {
}
}

func TestProjectState_stoplightGrey_noPipeline(t *testing.T) {
func TestProjectState_stoplightGray_noPipeline(t *testing.T) {
proj := state.ProjectState{
Name: "empty-project",
}
if proj.Stoplight() != health.StoplightGrey {
t.Errorf("expected grey, got %v", proj.Stoplight())
if proj.Stoplight() != health.StoplightGray {
t.Errorf("expected gray, got %v", proj.Stoplight())
}
}

Expand Down Expand Up @@ -141,7 +141,7 @@ func TestProjectState_perKindStoplights(t *testing.T) {
if got := proj.ECSStoplight(); got != health.StoplightYellow {
t.Errorf("ECSStoplight() = %v, want yellow", got)
}
if got := (state.ProjectState{}).StacksStoplight(); got != health.StoplightGrey {
t.Errorf("no stacks: StacksStoplight() = %v, want grey", got)
if got := (state.ProjectState{}).StacksStoplight(); got != health.StoplightGray {
t.Errorf("no stacks: StacksStoplight() = %v, want gray", got)
}
}
2 changes: 1 addition & 1 deletion internal/ui/autoexpand_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ func TestAutoExpandOnFirstSighting(t *testing.T) {
{health.StoplightYellow, true},
{health.StoplightAwaitingApproval, true},
{health.StoplightGreen, false},
{health.StoplightGrey, false},
{health.StoplightGray, false},
} {
d := NewDashboard(snapWith(tc.light), nil, context.Background())
if got := d.expanded[projKey]; got != tc.want {
Expand Down
2 changes: 1 addition & 1 deletion main.go
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,7 @@ Usage:

// runCommand handles the non-TUI invocations. It reports whether args were
// handled here, along with the exit code to use when they were. Anything it
// does not recognise falls through to launching the dashboard.
// does not recognize falls through to launching the dashboard.
func runCommand(args []string, out io.Writer) (int, bool) {
if len(args) == 0 {
return 0, false
Expand Down
Loading