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
6 changes: 3 additions & 3 deletions CONTEXT.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,11 @@ _Avoid_: app, stack, group
by integer id; only the per-project detail endpoint reveals which Project owns it.

**Resource**: An Application, Service, or Database. The three come from three endpoints with
overlapping shapes and are modelled as one type carrying a Kind.
overlapping shapes and are modeled as one type carrying a Kind.
_Avoid_: container, workload

**Deployment**: One deploy of an Application. Has a Status (`queued`, `in_progress`, `finished`,
`failed`, `cancelled-by-user`) and inlines its whole build log.
`failed`, `cancelled-by-user`) and inlines its whole build log. <!-- spelling: ok (Coolify API status value) -->
_Avoid_: build, release, run

**Container status**: Coolify's `"<state>:<health>"` string on a Resource, e.g. `running:healthy`,
Expand Down Expand Up @@ -57,7 +57,7 @@ so a running app whose last deploy failed still reads red.
| 🟢 Green | Healthy | `running:healthy`, `running:unknown`, deployment `finished` |
| 🔴 Red | Broken | `running:unhealthy`, `exited`, `stopped`, `degraded`, deployment `failed` |
| 🟡 Yellow | In flight | deployment `queued` / `in_progress`, `restarting`, `running:starting` |
| ⚪ Grey | No signal | empty or unrecognised status, deployment `cancelled-by-user` |
| ⚪ Gray | No signal | empty or unrecognized status, deployment `cancelled-by-user` <!-- spelling: ok (Coolify API status value) --> |

`running:unknown` is green, not a warning: it is what Coolify reports for every resource with no
health check configured, which is most of them.
Expand Down
2 changes: 1 addition & 1 deletion docs/adr/0001-discovery-over-configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

## Context

coolify-green is modelled on aws-green, where the config file names every pipeline, stack, and ECS
coolify-green is modeled on aws-green, where the config file names every pipeline, stack, and ECS
service to watch. That works for AWS because there is no single call that answers "what does this
account deploy?" — you have to know what you are looking for.

Expand Down
16 changes: 8 additions & 8 deletions internal/aggregator/aggregator.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import "strings"
type Stoplight int

const (
StoplightGrey Stoplight = iota // unknown, or nothing deployed yet
StoplightGray Stoplight = iota // unknown, or nothing deployed yet
StoplightGreen // running, last deployment finished
StoplightYellow // deploying, starting, restarting
StoplightRed // down, unhealthy, or last deployment failed
Expand All @@ -34,7 +34,7 @@ const (
DeployInProgress DeployStatus = "in_progress"
DeployFinished DeployStatus = "finished"
DeployFailed DeployStatus = "failed"
DeployCancelled DeployStatus = "cancelled-by-user"
DeployCanceled DeployStatus = "cancelled-by-user" // spelling: ok (Coolify API status value)
)

// InFlight reports whether a deployment is still running or waiting to run.
Expand All @@ -51,10 +51,10 @@ func DeployStoplight(status string) Stoplight {
return StoplightYellow
case DeployFailed:
return StoplightRed
case DeployCancelled:
return StoplightGrey
case DeployCanceled:
return StoplightGray
default:
return StoplightGrey
return StoplightGray
}
}

Expand Down Expand Up @@ -92,14 +92,14 @@ func ContainerStoplight(status string) Stoplight {
case "exited", "stopped", "dead", "paused", "removing", "degraded":
return StoplightRed
default:
return StoplightGrey
return StoplightGray
}
}

// Aggregate returns the worst-case Stoplight across the provided lights.
// Red > Yellow > Green > Grey.
// Red > Yellow > Green > Gray.
func Aggregate(lights ...Stoplight) Stoplight {
worst := StoplightGrey
worst := StoplightGray
for _, l := range lights {
if l > worst {
worst = l
Expand Down
14 changes: 7 additions & 7 deletions internal/aggregator/aggregator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ func TestContainerStoplight(t *testing.T) {
{"exited", StoplightRed},
{"stopped", StoplightRed},
{"degraded", StoplightRed},
{"", StoplightGrey},
{"who-knows", StoplightGrey},
{"", StoplightGray},
{"who-knows", StoplightGray},
{" Running:Healthy ", StoplightGreen},
}
for _, tc := range tests {
Expand All @@ -37,8 +37,8 @@ func TestDeployStoplight(t *testing.T) {
{"in_progress", StoplightYellow},
{"queued", StoplightYellow},
{"failed", StoplightRed},
{"cancelled-by-user", StoplightGrey},
{"", StoplightGrey},
{"cancelled-by-user", StoplightGray}, // spelling: ok (Coolify API status value)
{"", StoplightGray},
}
for _, tc := range tests {
if got := DeployStoplight(tc.status); got != tc.want {
Expand All @@ -57,8 +57,8 @@ func TestInFlight(t *testing.T) {
}

func TestAggregate(t *testing.T) {
if got := Aggregate(); got != StoplightGrey {
t.Errorf("empty Aggregate = %v, want grey", got)
if got := Aggregate(); got != StoplightGray {
t.Errorf("empty Aggregate = %v, want gray", got)
}
if got := Aggregate(StoplightGreen, StoplightRed, StoplightYellow); got != StoplightRed {
t.Errorf("Aggregate = %v, want red", got)
Expand All @@ -69,7 +69,7 @@ func TestAggregate(t *testing.T) {
}

func TestSortPriorityPutsInProgressFirst(t *testing.T) {
order := []Stoplight{StoplightYellow, StoplightRed, StoplightGreen, StoplightGrey}
order := []Stoplight{StoplightYellow, StoplightRed, StoplightGreen, StoplightGray}
for i := 1; i < len(order); i++ {
if SortPriority(order[i-1]) >= SortPriority(order[i]) {
t.Fatalf("priority order broken at %d: %v vs %v", i, order[i-1], order[i])
Expand Down
2 changes: 1 addition & 1 deletion internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ const (
// not given.
var DefaultWatch = []string{"applications", "services", "databases"}

// Settings holds instance-wide polling behaviour.
// Settings holds instance-wide polling behavior.
type Settings struct {
PollInterval int `toml:"poll_interval_seconds"`
// StuckThresholdMinutes is how long a resource stays broken or a deploy
Expand Down
2 changes: 1 addition & 1 deletion internal/config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ func TestLoadRejectsBadInput(t *testing.T) {
}
}

func TestWatchesKindHonoursSetting(t *testing.T) {
func TestWatchesKindHonorsSetting(t *testing.T) {
cfg, err := Load(writeConfig(t, `
[settings]
watch = ["apps"]
Expand Down
2 changes: 1 addition & 1 deletion internal/state/state.go
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ func (p ProjectState) Stoplight() aggregator.Stoplight {

// KindSummary returns the worst stoplight and count for one resource kind.
func (p ProjectState) KindSummary(kind coolify.Kind) (aggregator.Stoplight, int) {
worst := aggregator.StoplightGrey
worst := aggregator.StoplightGray
count := 0
for _, r := range p.Resources {
if r.Kind != kind {
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 @@ -34,8 +34,8 @@ func TestProjectStoplightIsWorstResource(t *testing.T) {
if got := p.Stoplight(); got != aggregator.StoplightRed {
t.Errorf("got %v", got)
}
if got := (ProjectState{}).Stoplight(); got != aggregator.StoplightGrey {
t.Errorf("empty project should be grey, got %v", got)
if got := (ProjectState{}).Stoplight(); got != aggregator.StoplightGray {
t.Errorf("empty project should be gray, got %v", got)
}
}

Expand All @@ -50,7 +50,7 @@ func TestKindSummary(t *testing.T) {
t.Errorf("apps: %v %d", light, count)
}
light, count = p.KindSummary(coolify.KindDatabase)
if light != aggregator.StoplightGrey || count != 0 {
if light != aggregator.StoplightGray || count != 0 {
t.Errorf("databases: %v %d", light, count)
}
}
Expand All @@ -69,12 +69,12 @@ func TestKeyIsInstanceQualified(t *testing.T) {
func TestSortedProjectsPutsDeployingFirst(t *testing.T) {
projects := []ProjectState{
{Name: "green", Resources: []ResourceState{{ContainerLight: aggregator.StoplightGreen}}},
{Name: "grey"},
{Name: "gray"},
{Name: "red", Resources: []ResourceState{{ContainerLight: aggregator.StoplightRed}}},
{Name: "yellow", Resources: []ResourceState{{ContainerLight: aggregator.StoplightYellow}}},
}
order := SortedProjects(projects)
want := []string{"yellow", "red", "green", "grey"}
want := []string{"yellow", "red", "green", "gray"}
for i, idx := range order {
if projects[idx].Name != want[i] {
t.Fatalf("position %d = %q, want %q", i, projects[idx].Name, want[i])
Expand Down
2 changes: 1 addition & 1 deletion internal/ui/autoexpand_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ func project(uuid, name string, light aggregator.Stoplight) state.ProjectState {
aggregator.StoplightGreen: "running:healthy",
aggregator.StoplightYellow: "starting:unknown",
aggregator.StoplightRed: "exited:unhealthy",
aggregator.StoplightGrey: "",
aggregator.StoplightGray: "",
}[light]
return state.ProjectState{
Instance: "studio", InstanceURL: "https://coolify.test",
Expand Down
2 changes: 1 addition & 1 deletion internal/ui/dashboard.go
Original file line number Diff line number Diff line change
Expand Up @@ -520,7 +520,7 @@ func (d Dashboard) bodyLines() ([]string, int) {
return lines, cursorLine
}

// window trims the rendered rows to what fits on screen, centred on the
// window trims the rendered rows to what fits on screen, centered on the
// cursor. The title, the blank separator, and the hint line live outside the
// body, so the budget is the terminal height less three.
func (d Dashboard) window(lines []string, cursorLine int) []string {
Expand Down
6 changes: 3 additions & 3 deletions internal/ui/dashboard_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -248,9 +248,9 @@ func TestFixConfirmFlow(t *testing.T) {
}

// esc cancels without acting.
cancelled, _ := d.Update(tea.KeyMsg{Type: tea.KeyEsc})
if cancelled.fixStatus != fixIdle || executed {
t.Errorf("esc should cancel the fix (status=%v executed=%v)", cancelled.fixStatus, executed)
canceled, _ := d.Update(tea.KeyMsg{Type: tea.KeyEsc})
if canceled.fixStatus != fixIdle || executed {
t.Errorf("esc should cancel the fix (status=%v executed=%v)", canceled.fixStatus, executed)
}

// enter runs it.
Expand Down
4 changes: 2 additions & 2 deletions internal/ui/icons.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,8 @@ func deployStatusLabel(status string) (string, lipgloss.Style) {
return "↻ queued", confirmStyle
case aggregator.DeployFailed:
return "✗ deploy failed", errorStyle
case aggregator.DeployCancelled:
return "⊘ deploy cancelled", staleStyle
case aggregator.DeployCanceled:
return "⊘ deploy canceled", staleStyle
case "":
return "", staleStyle
default:
Expand Down
2 changes: 1 addition & 1 deletion main.go
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,7 @@ Config: ` + configPath() + `

// 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