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
14 changes: 14 additions & 0 deletions src/cmd/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,20 @@ func getVersionStatus(version, globalVersion, localVersion string) string {
return status
}

// getVersionStatusWithLifecycle returns a status string that combines the
// global/local indicators with a color-coded lifecycle label (e.g., "Active LTS").
func getVersionStatusWithLifecycle(version, globalVersion, localVersion, lifecycleStatus string) string {
base := getVersionStatus(version, globalVersion, localVersion)
colored := tui.RenderLifecycleStatus(lifecycleStatus)
if colored == "" {
return base
}
if base == "" {
return colored
}
return base + " · " + colored
}

// isVersionActive returns true if this version is the currently active one
func isVersionActive(version, globalVersion, localVersion string) bool {
isGlobal := version == globalVersion
Expand Down
68 changes: 68 additions & 0 deletions src/cmd/list_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package cmd

import (
"testing"

"github.com/CodingWithCalvin/dtvem.cli/src/internal/tui"
)

func TestGetVersionStatusWithLifecycle(t *testing.T) {
tests := []struct {
name string
version string
globalVersion string
localVersion string
lifecycleStatus string
want string
}{
{
name: "lifecycle only",
version: "22.14.0",
globalVersion: "",
localVersion: "",
lifecycleStatus: "Active LTS",
want: tui.RenderLifecycleStatus("Active LTS"),
},
{
name: "global only",
version: "22.14.0",
globalVersion: "22.14.0",
localVersion: "",
lifecycleStatus: "",
want: globalIndicator + " global",
},
{
name: "global with lifecycle",
version: "22.14.0",
globalVersion: "22.14.0",
localVersion: "",
lifecycleStatus: "Active LTS",
want: globalIndicator + " global" + " · " + tui.RenderLifecycleStatus("Active LTS"),
},
{
name: "local with lifecycle",
version: "22.14.0",
globalVersion: "",
localVersion: "22.14.0",
lifecycleStatus: "Maintenance LTS",
want: localIndicator + " local" + " · " + tui.RenderLifecycleStatus("Maintenance LTS"),
},
{
name: "no status at all",
version: "22.14.0",
globalVersion: "",
localVersion: "",
lifecycleStatus: "",
want: "",
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := getVersionStatusWithLifecycle(tt.version, tt.globalVersion, tt.localVersion, tt.lifecycleStatus)
if got != tt.want {
t.Errorf("getVersionStatusWithLifecycle() = %q, want %q", got, tt.want)
}
})
}
}
8 changes: 4 additions & 4 deletions src/cmd/listall.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ Examples:
}

// Create table for this page
table := tui.NewTable("", "Version", "Status", "Notes")
table := tui.NewTable("", "Version", "Status")
table.SetTitle(provider.DisplayName())

for i := 0; i < pageSize; i++ {
Expand All @@ -112,10 +112,10 @@ Examples:
marker = tui.CheckMark
}

// Get status (global/local indicators)
status := getVersionStatus(version, globalVersion, localVersion)
// Build status: combine global/local indicators with lifecycle
status := getVersionStatusWithLifecycle(version, globalVersion, localVersion, v.LifecycleStatus)

table.AddRow(marker, version, status, v.Notes)
table.AddRow(marker, version, status)
}

fmt.Println()
Expand Down
30 changes: 30 additions & 0 deletions src/internal/lifecycle/lifecycle.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// Package lifecycle provides types for runtime version lifecycle status.
// Providers can implement StatusProvider to surface lifecycle information
// (e.g., LTS, EOL) in version listings.
package lifecycle

// Status represents a version's position in its runtime's release lifecycle.
type Status string

const (
// Current indicates an actively developed release line (typically the latest).
Current Status = "Current"

// ActiveLTS indicates a release receiving active long-term support.
ActiveLTS Status = "Active LTS"

// MaintenanceLTS indicates a release receiving only critical fixes.
MaintenanceLTS Status = "Maintenance LTS"

// EOL indicates a release that has reached end of life.
EOL Status = "EOL"
)

// StatusProvider returns lifecycle status for a given version string.
// Providers that track release schedules (e.g., Node.js) can implement
// this interface so list-all displays lifecycle information.
type StatusProvider interface {
// VersionStatus returns the lifecycle status label for the given version,
// or an empty string if the status is unknown.
VersionStatus(version string) string
}
8 changes: 4 additions & 4 deletions src/internal/runtime/version.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,10 @@ func (iv InstalledVersion) String() string {
// AvailableVersion represents a version available for installation
type AvailableVersion struct {
Version
DownloadURL string
Size int64
Checksum string
Notes string // Optional notes (e.g., "LTS", "Latest", "Stable")
DownloadURL string
Size int64
Checksum string
LifecycleStatus string // Optional lifecycle label (e.g., "Active LTS", "EOL")
}

// DetectedVersion represents a runtime version found on the system
Expand Down
23 changes: 23 additions & 0 deletions src/internal/tui/styles.go
Original file line number Diff line number Diff line change
Expand Up @@ -201,3 +201,26 @@ func GetCrossMark() string {
initStyles()
return CrossMark
}

// RenderLifecycleStatus renders a lifecycle status label with color coding.
// Current → Cyan, Active LTS → Green, Maintenance LTS → Yellow, EOL → Red.
// Unknown labels are returned unstyled.
func RenderLifecycleStatus(status string) string {
if status == "" {
return ""
}
initStyles()

switch status {
case "Current":
return lipgloss.NewStyle().Foreground(colorPrimary).Render(status)
case "Active LTS":
return lipgloss.NewStyle().Foreground(colorSuccess).Bold(true).Render(status)
case "Maintenance LTS":
return lipgloss.NewStyle().Foreground(colorWarning).Render(status)
case "EOL":
return lipgloss.NewStyle().Foreground(colorError).Render(status)
default:
return status
}
}
153 changes: 153 additions & 0 deletions src/runtimes/node/data/schedule.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
{
"v0.8": {
"start": "2012-06-25",
"end": "2014-07-31"
},
"v0.10": {
"start": "2013-03-11",
"end": "2016-10-31"
},
"v0.12": {
"start": "2015-02-06",
"end": "2016-12-31"
},
"v4": {
"start": "2015-09-08",
"lts": "2015-10-12",
"maintenance": "2017-04-01",
"end": "2018-04-30",
"codename": "Argon"
},
"v5": {
"start": "2015-10-29",
"maintenance": "2016-04-30",
"end": "2016-06-30"
},
"v6": {
"start": "2016-04-26",
"lts": "2016-10-18",
"maintenance": "2018-04-30",
"end": "2019-04-30",
"codename": "Boron"
},
"v7": {
"start": "2016-10-25",
"maintenance": "2017-04-30",
"end": "2017-06-30"
},
"v8": {
"start": "2017-05-30",
"lts": "2017-10-31",
"maintenance": "2019-01-01",
"end": "2019-12-31",
"codename": "Carbon"
},
"v9": {
"start": "2017-10-01",
"maintenance": "2018-04-01",
"end": "2018-06-30"
},
"v10": {
"start": "2018-04-24",
"lts": "2018-10-30",
"maintenance": "2020-05-19",
"end": "2021-04-30",
"codename": "Dubnium"
},
"v11": {
"start": "2018-10-23",
"maintenance": "2019-04-22",
"end": "2019-06-01"
},
"v12": {
"start": "2019-04-23",
"lts": "2019-10-21",
"maintenance": "2020-11-30",
"end": "2022-04-30",
"codename": "Erbium"
},
"v13": {
"start": "2019-10-22",
"maintenance": "2020-04-01",
"end": "2020-06-01"
},
"v14": {
"start": "2020-04-21",
"lts": "2020-10-27",
"maintenance": "2021-10-19",
"end": "2023-04-30",
"codename": "Fermium"
},
"v15": {
"start": "2020-10-20",
"maintenance": "2021-04-01",
"end": "2021-06-01"
},
"v16": {
"start": "2021-04-20",
"lts": "2021-10-26",
"maintenance": "2022-10-18",
"end": "2023-09-11",
"codename": "Gallium"
},
"v17": {
"start": "2021-10-19",
"maintenance": "2022-04-01",
"end": "2022-06-01"
},
"v18": {
"start": "2022-04-19",
"lts": "2022-10-25",
"maintenance": "2023-10-18",
"end": "2025-04-30",
"codename": "Hydrogen"
},
"v19": {
"start": "2022-10-18",
"maintenance": "2023-04-01",
"end": "2023-06-01"
},
"v20": {
"start": "2023-04-18",
"lts": "2023-10-24",
"maintenance": "2024-10-22",
"end": "2026-04-30",
"codename": "Iron"
},
"v21": {
"start": "2023-10-17",
"maintenance": "2024-04-01",
"end": "2024-06-01"
},
"v22": {
"start": "2024-04-24",
"lts": "2024-10-29",
"maintenance": "2025-10-21",
"end": "2027-04-30",
"codename": "Jod"
},
"v23": {
"start": "2024-10-16",
"maintenance": "2025-04-01",
"end": "2025-06-01"
},
"v24": {
"start": "2025-05-06",
"lts": "2025-10-28",
"maintenance": "2026-10-20",
"end": "2028-04-30",
"codename": "Krypton"
},
"v25": {
"start": "2025-10-15",
"maintenance": "2026-04-01",
"end": "2026-06-01"
},
"v26": {
"start": "2026-04-22",
"lts": "2026-10-28",
"maintenance": "2027-10-20",
"end": "2029-04-30",
"codename": ""
}
}
Loading