mirror of
https://github.com/skoelle/kctl-tui.git
synced 2026-09-17 20:10:24 +00:00
fix: tmux exec type mismatch, add --version flag, add config check command
This commit is contained in:
@@ -60,7 +60,7 @@ jobs:
|
|||||||
ext=""
|
ext=""
|
||||||
if [ "${{ matrix.goos }}" = "windows" ]; then ext=".exe"; fi
|
if [ "${{ matrix.goos }}" = "windows" ]; then ext=".exe"; fi
|
||||||
out="dist/kctl-tui-${{ matrix.goos }}-${{ matrix.goarch }}${ext}"
|
out="dist/kctl-tui-${{ matrix.goos }}-${{ matrix.goarch }}${ext}"
|
||||||
go build -o "$out" -ldflags "-s -w" ./cmd/kctl-tui
|
go build -o "$out" -ldflags "-s -w -X main.version=${GITHUB_REF_NAME}" ./cmd/kctl-tui
|
||||||
echo "Built $out"
|
echo "Built $out"
|
||||||
|
|
||||||
- name: Upload artifact
|
- name: Upload artifact
|
||||||
|
|||||||
@@ -76,11 +76,11 @@ is still open. For the full requirements, see [SPEC.md](SPEC.md).
|
|||||||
|
|
||||||
## Phase 4 — Nice-to-haves (open, not committed)
|
## Phase 4 — Nice-to-haves (open, not committed)
|
||||||
|
|
||||||
|
- [x] `--version` flag — prints version, set via `-ldflags` at build time.
|
||||||
|
- [x] Config validation command (`kctl-tui config check`) — validates
|
||||||
|
required fields and shows a resolved context example.
|
||||||
- [ ] Optional direct use of `client-go` instead of shelling out to
|
- [ ] Optional direct use of `client-go` instead of shelling out to
|
||||||
`kubectl`, for faster context/namespace/label queries.
|
`kubectl`, for faster context/namespace/label queries.
|
||||||
- [ ] Config validation command (`kctl-tui config check`) that reports
|
|
||||||
unknown label keys or context names not present in the current
|
|
||||||
kubeconfig.
|
|
||||||
- [ ] Homebrew tap / `scoop` manifest as additional install options
|
- [ ] Homebrew tap / `scoop` manifest as additional install options
|
||||||
alongside `install.sh`.
|
alongside `install.sh`.
|
||||||
|
|
||||||
|
|||||||
@@ -178,6 +178,16 @@ mkdir -p ~/.kube
|
|||||||
ln -s /mnt/c/Users/<your-windows-username>/.kube/config ~/.kube/config
|
ln -s /mnt/c/Users/<your-windows-username>/.kube/config ~/.kube/config
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```bash
|
||||||
|
kctl-tui # start the TUI (full navigation mode)
|
||||||
|
kctl-tui --version # print version
|
||||||
|
kctl-tui --verbose # enable debug logging to stderr
|
||||||
|
kctl-tui config check # validate ~/.kctl-tui/config.yaml
|
||||||
|
kctl-tui panel --context=... --ns=... --team=... # internal (called by tmux)
|
||||||
|
```
|
||||||
|
|
||||||
## Development
|
## Development
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|||||||
@@ -264,11 +264,7 @@ func (m *fullModel) startTmuxSession() tea.Cmd {
|
|||||||
if err := kubeexec.CheckTool("k9s"); err != nil {
|
if err := kubeexec.CheckTool("k9s"); err != nil {
|
||||||
return errMsg{err}
|
return errMsg{err}
|
||||||
}
|
}
|
||||||
return m.buildAndRunTmux()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *fullModel) buildAndRunTmux() tea.Msg {
|
|
||||||
selfPath := "kctl-tui" // resolved via PATH; see README for install instructions
|
selfPath := "kctl-tui" // resolved via PATH; see README for install instructions
|
||||||
panelCmd := fmt.Sprintf("%s panel --context=%s --ns=%s --team=%s",
|
panelCmd := fmt.Sprintf("%s panel --context=%s --ns=%s --team=%s",
|
||||||
selfPath, m.selectedContext, m.selectedNamespace, m.selectedTeam)
|
selfPath, m.selectedContext, m.selectedNamespace, m.selectedTeam)
|
||||||
@@ -303,6 +299,7 @@ func (m *fullModel) buildAndRunTmux() tea.Msg {
|
|||||||
return tmuxDoneMsg{err: err}
|
return tmuxDoneMsg{err: err}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (m *fullModel) View() string {
|
func (m *fullModel) View() string {
|
||||||
view := m.list.View()
|
view := m.list.View()
|
||||||
|
|||||||
+73
-4
@@ -6,19 +6,27 @@ import (
|
|||||||
|
|
||||||
tea "github.com/charmbracelet/bubbletea"
|
tea "github.com/charmbracelet/bubbletea"
|
||||||
|
|
||||||
|
"github.com/skoelle/kctl-tui/internal/config"
|
||||||
"github.com/skoelle/kctl-tui/internal/kubeexec"
|
"github.com/skoelle/kctl-tui/internal/kubeexec"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// version is set via -ldflags at build time.
|
||||||
|
var version = "dev"
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
args := os.Args[1:]
|
args := os.Args[1:]
|
||||||
|
|
||||||
// Extract --verbose before delegating to panel or full mode.
|
// Extract global flags before delegating to sub-commands.
|
||||||
verbose := false
|
verbose := false
|
||||||
filtered := make([]string, 0, len(args))
|
filtered := make([]string, 0, len(args))
|
||||||
for _, a := range args {
|
for _, a := range args {
|
||||||
if a == "--verbose" {
|
switch a {
|
||||||
|
case "--verbose":
|
||||||
verbose = true
|
verbose = true
|
||||||
} else {
|
case "--version", "-v":
|
||||||
|
fmt.Printf("kctl-tui %s\n", version)
|
||||||
|
return
|
||||||
|
default:
|
||||||
filtered = append(filtered, a)
|
filtered = append(filtered, a)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -26,12 +34,21 @@ func main() {
|
|||||||
kubeexec.SetVerbose(true, os.Stderr)
|
kubeexec.SetVerbose(true, os.Stderr)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(filtered) > 0 && filtered[0] == "panel" {
|
if len(filtered) > 0 {
|
||||||
|
switch filtered[0] {
|
||||||
|
case "panel":
|
||||||
if err := runPanel(filtered[1:]); err != nil {
|
if err := runPanel(filtered[1:]); err != nil {
|
||||||
fmt.Fprintln(os.Stderr, "kctl-tui panel error:", err)
|
fmt.Fprintln(os.Stderr, "kctl-tui panel error:", err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
|
case "config":
|
||||||
|
if err := runConfig(filtered[1:]); err != nil {
|
||||||
|
fmt.Fprintln(os.Stderr, "kctl-tui config error:", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
m := newFullModel()
|
m := newFullModel()
|
||||||
@@ -41,3 +58,55 @@ func main() {
|
|||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func runConfig(args []string) error {
|
||||||
|
if len(args) == 0 || args[0] != "check" {
|
||||||
|
return fmt.Errorf("usage: kctl-tui config check")
|
||||||
|
}
|
||||||
|
cfgPath, err := config.DefaultPath()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("cannot determine config path: %w", err)
|
||||||
|
}
|
||||||
|
cfg, err := config.Load(cfgPath)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to load %s: %w", cfgPath, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ok := true
|
||||||
|
if len(cfg.Contexts) == 0 {
|
||||||
|
fmt.Fprintln(os.Stderr, "ERROR: no 'contexts' configured")
|
||||||
|
ok = false
|
||||||
|
}
|
||||||
|
if len(cfg.Envs) == 0 {
|
||||||
|
fmt.Fprintln(os.Stderr, "ERROR: no 'envs' configured")
|
||||||
|
ok = false
|
||||||
|
}
|
||||||
|
if cfg.ContextTemplate == "" {
|
||||||
|
fmt.Fprintln(os.Stderr, "ERROR: 'context_template' is empty")
|
||||||
|
ok = false
|
||||||
|
}
|
||||||
|
if cfg.SecretNameTemplate == "" {
|
||||||
|
fmt.Fprintln(os.Stderr, "ERROR: 'secret_name_template' is empty")
|
||||||
|
ok = false
|
||||||
|
}
|
||||||
|
if cfg.TeamLabelKey == "" {
|
||||||
|
fmt.Fprintln(os.Stderr, "WARNING: 'team_label_key' is empty — team selection will have no groups")
|
||||||
|
}
|
||||||
|
if cfg.AWSRegion == "" {
|
||||||
|
fmt.Fprintln(os.Stderr, "WARNING: 'aws_region' is empty — secrets workflow will fail")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try resolving one context to verify the template works.
|
||||||
|
if len(cfg.Contexts) > 0 && len(cfg.Envs) > 0 && cfg.ContextTemplate != "" {
|
||||||
|
ctx := cfg.ResolveContext(cfg.Envs[0], cfg.Contexts[0])
|
||||||
|
fmt.Printf("Resolved context example: %s\n", ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
if ok {
|
||||||
|
fmt.Println("Config OK")
|
||||||
|
} else {
|
||||||
|
fmt.Fprintln(os.Stderr, "Config has errors — see above")
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user