diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml deleted file mode 100644 index 506b772..0000000 --- a/.github/workflows/ci.yml +++ /dev/null @@ -1,19 +0,0 @@ -name: ci -on: - push: - pull_request: -jobs: - goreleaser: - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@master - - name: Setup Go - uses: actions/setup-go@v1 - with: - go-version: 1.13 - - name: GoReleaser - uses: goreleaser/goreleaser-action@v1 - with: - version: latest - args: release --snapshot --rm-dist diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml deleted file mode 100644 index 6cd71cf..0000000 --- a/.github/workflows/release.yml +++ /dev/null @@ -1,25 +0,0 @@ -name: release -on: - push: - tags: - - 'v*.*.*' -jobs: - goreleaser: - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@master - - name: Setup Go - uses: actions/setup-go@v1 - with: - go-version: 1.13 - - name: GoReleaser - uses: goreleaser/goreleaser-action@v1 - with: - version: latest - args: release --rm-dist - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - name: Update new version in krew-index - uses: rajatjindal/krew-release-bot@v0.0.25 - diff --git a/.goreleaser.yml b/.goreleaser.yml deleted file mode 100644 index b9180de..0000000 --- a/.goreleaser.yml +++ /dev/null @@ -1,24 +0,0 @@ -before: - hooks: - - go mod download -builds: -- id: kubectl-select - main: ./ - binary: kubectl-select - env: - - CGO_ENABLED=0 - goos: - - linux - - darwin - - windows - goarch: - - amd64 - -archives: -- builds: - - kubectl-select - name_template: "{{ .ProjectName }}_{{ .Tag }}_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}" - wrap_in_directory: false - format: tar.gz - files: - - LICENSE diff --git a/.krew.yaml b/.krew.yaml deleted file mode 100644 index f4b64fa..0000000 --- a/.krew.yaml +++ /dev/null @@ -1,40 +0,0 @@ -apiVersion: krew.googlecontainertools.github.com/v1alpha2 -kind: Plugin -metadata: - name: select -spec: - version: {{ .TagName }} - homepage: https://github.com/n3wscott/kubectl-select - shortDescription: Select the active Kubernetes context using a tui. - description: | - This plugin allows you see all configured Kubernetes contexts as a human friendly list and use your arrow keys to select the active context. - caveats: | - * Contexts must already be setup in the local context file. - platforms: - - selector: - matchLabels: - os: darwin - arch: amd64 - {{addURIAndSha "https://github.com/n3wscott/kubectl-select/releases/download/{{ .TagName }}/kubectl-select_{{ .TagName }}_darwin_amd64.tar.gz" .TagName }} - files: - - from: "*" - to: "." - bin: kubectl-select - - selector: - matchLabels: - os: linux - arch: amd64 - {{addURIAndSha "https://github.com/n3wscott/kubectl-select/releases/download/{{ .TagName }}/kubectl-select_{{ .TagName }}_linux_amd64.tar.gz" .TagName }} - files: - - from: "*" - to: "." - bin: kubectl-select - - selector: - matchLabels: - os: windows - arch: amd64 - {{addURIAndSha "https://github.com/n3wscott/kubectl-select/releases/download/{{ .TagName }}/kubectl-select_{{ .TagName }}_windows_amd64.tar.gz" .TagName }} - files: - - from: "*" - to: "." - bin: kubectl-select.exe diff --git a/README.md b/README.md index dc5f109..c1864ba 100644 --- a/README.md +++ b/README.md @@ -2,19 +2,12 @@ A `kubectl` extension to select from local config via a TUI. - ## Installation `kubectl-select` can be installed via: ```shell -go get github.com/n3wscott/kubectl-select -``` - -To update your installation: - -```shell -go get -u github.com/n3wscott/kubectl-select +go install github.com/n3wscott/kubectl-select@latest ``` ## Usage @@ -25,7 +18,7 @@ Use as a kubernetes extension, kubectl select ``` -This will show a menu driven off the currently configured Kubernetes clients. +This will show a menu-driven selector from the locally configured Kubernetes clients. Select one by pressing `ENTER`. To cancel, `ESC` or `q`. diff --git a/go.mod b/go.mod index 9a8d785..5463725 100644 --- a/go.mod +++ b/go.mod @@ -1,5 +1,18 @@ module github.com/n3wscott/kubectl-select -go 1.13 +go 1.17 -require github.com/marcusolsson/tui-go v0.4.0 // indirect +require ( + github.com/gdamore/tcell/v2 v2.4.1-0.20210905002822-f057f0a857a1 + github.com/rivo/tview v0.0.0-20211029142923-a4acb08f513e +) + +require ( + github.com/gdamore/encoding v1.0.0 // indirect + github.com/lucasb-eyer/go-colorful v1.2.0 // indirect + github.com/mattn/go-runewidth v0.0.13 // indirect + github.com/rivo/uniseg v0.2.0 // indirect + golang.org/x/sys v0.0.0-20210309074719-68d13333faf2 // indirect + golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d // indirect + golang.org/x/text v0.3.6 // indirect +) diff --git a/go.sum b/go.sum index fcaa18f..0d891d9 100644 --- a/go.sum +++ b/go.sum @@ -1,20 +1,22 @@ -github.com/gdamore/encoding v0.0.0-20151215212835-b23993cbb635 h1:hheUEMzaOie/wKeIc1WPa7CDVuIO5hqQxjS+dwTQEnI= -github.com/gdamore/encoding v0.0.0-20151215212835-b23993cbb635/go.mod h1:yrQYJKKDTrHmbYxI7CYi+/hbdiDT2m4Hj+t0ikCjsrQ= -github.com/gdamore/tcell v1.1.0 h1:RbQgl7jukmdqROeNcKps7R2YfDCQbWkOd1BwdXrxfr4= -github.com/gdamore/tcell v1.1.0/go.mod h1:tqyG50u7+Ctv1w5VX67kLzKcj9YXR/JSBZQq/+mLl1A= -github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= -github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= -github.com/jtolds/gls v4.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= -github.com/lucasb-eyer/go-colorful v0.0.0-20180709185858-c7842319cf3a h1:B2QfFRl5yGVGGcyEVFzfdXlC1BBvszsIAsCeef2oD0k= -github.com/lucasb-eyer/go-colorful v0.0.0-20180709185858-c7842319cf3a/go.mod h1:NXg0ArsFk0Y01623LgUqoqcouGDB+PwCCQlrwrG6xJ4= -github.com/marcusolsson/tui-go v0.4.0 h1:PZD0lIS+2OUKxs71qsc5U/P+eVU39FeBRgdsh5iQZ28= -github.com/marcusolsson/tui-go v0.4.0/go.mod h1:vp1U15jwzYTPWex1hV+CZ7MeQQH7Wr73fz9hc/0I9YI= -github.com/mattn/go-runewidth v0.0.3 h1:a+kO+98RDGEfo6asOGMmpodZq4FNtnGP54yps8BzLR4= -github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= -github.com/mitchellh/go-wordwrap v1.0.0 h1:6GlHJ/LTGMrIJbwgdqdl2eEH8o+Exx/0m8ir9Gns0u4= -github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= -github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= -github.com/smartystreets/goconvey v0.0.0-20181108003508-044398e4856c/go.mod h1:XDJAKZRPZ1CvBcN2aX5YOUTYGHki24fSF0Iv48Ibg0s= -golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= +github.com/gdamore/encoding v1.0.0 h1:+7OoQ1Bc6eTm5niUzBa0Ctsh6JbMW6Ra+YNuAtDBdko= +github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg= +github.com/gdamore/tcell/v2 v2.4.1-0.20210905002822-f057f0a857a1 h1:QqwPZCwh/k1uYqq6uXSb9TRDhTkfQbO80v8zhnIe5zM= +github.com/gdamore/tcell/v2 v2.4.1-0.20210905002822-f057f0a857a1/go.mod h1:Az6Jt+M5idSED2YPGtwnfJV0kXohgdCBPmHGSYc1r04= +github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= +github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= +github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU= +github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/rivo/tview v0.0.0-20211029142923-a4acb08f513e h1:dVBzRaVTERZmv0MRjt8/a+afStgA+4tXk3PnrqT6mlo= +github.com/rivo/tview v0.0.0-20211029142923-a4acb08f513e/go.mod h1:WIfMkQNY+oq/mWwtsjOYHIZBuwthioY2srOmljJkTnk= +github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= +github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210309074719-68d13333faf2 h1:46ULzRKLh1CwgRq2dC5SlBzEqqNCi8rreOZnNrbqcIY= +golang.org/x/sys v0.0.0-20210309074719-68d13333faf2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d h1:SZxvLBoTP5yHO3Frd4z4vrF+DBX9vMVanchswa69toE= +golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -gopkg.in/DATA-DOG/go-sqlmock.v1 v1.3.0/go.mod h1:OdE7CF6DbADk7lN8LIKRzRJTTZXIjtWgA5THM5lhBAw= +golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= diff --git a/kubectl-select.go b/kubectl-select.go index 7214179..94aa686 100644 --- a/kubectl-select.go +++ b/kubectl-select.go @@ -19,8 +19,8 @@ package main import ( "encoding/json" "fmt" - "github.com/marcusolsson/tui-go" - "log" + "github.com/gdamore/tcell/v2" + "github.com/rivo/tview" "os/exec" "strings" ) @@ -61,69 +61,50 @@ func getConfig() *K8sConfig { } func main() { - cfg := getConfig() - table := tui.NewTable(0, 0) - table.SetColumnStretch(0, 1) - table.SetColumnStretch(1, 4) - table.SetColumnStretch(2, 4) - table.SetColumnStretch(3, 4) - table.SetFocused(true) + app := tview.NewApplication() + list := tview.NewList() + list.SetBorder(true).SetTitle("Select a Context") - table.AppendRow( - tui.NewLabel("SELECTED"), - tui.NewLabel("NAME"), - tui.NewLabel("CLUSTER"), - tui.NewLabel("USER"), - ) + doSelect := func(i int) { + _, err := cmd(fmt.Sprintf("kubectl config use-context %s", cfg.Contexts[i].Name)) + if err != nil { + panic(err) + } + app.Stop() + fmt.Printf("selected %s\n", cfg.Contexts[i].Name) + } + shortcut := 'a' for i, c := range cfg.Contexts { selected := "" if c.Name == cfg.CurrentContext { - selected = "*" - table.Select(i + 1) + selected = "[current]" + // TODO: can we select this ui element? } - table.AppendRow( - tui.NewLabel(selected), - tui.NewLabel(c.Name), - tui.NewLabel(c.Context.Cluster), - tui.NewLabel(c.Context.User), - ) + // TODO: if someone runs shortcut into q, not sure which shortcut item wins. + list.AddItem(fmt.Sprintf("%s %s", selected, c.Name), fmt.Sprintf("%s@%s", c.Context.User, c.Context.Cluster), shortcut, func() { + i := i + doSelect(i) + }) + shortcut++ } - status := tui.NewStatusBar("") - status.SetPermanentText(`ESC or 'q' to QUIT`) - - root := tui.NewVBox( - table, - tui.NewSpacer(), - status, - ) - - ui, err := tui.New(root) - if err != nil { - log.Fatal(err) - } + list.AddItem("Quit", "Press `q` or `ESC` to exit", 'q', func() { + app.Stop() + }) - table.OnItemActivated(func(t *tui.Table) { - if t.Selected() == 0 { - ui.Quit() + list.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey { + if event.Key() == tcell.KeyEscape { fmt.Printf("no selection; context unchanged\n") - return + app.Stop() + return nil } - _, err := cmd(fmt.Sprintf("kubectl config use-context %s", cfg.Contexts[t.Selected()-1].Name)) - if err != nil { - panic(err) - } - ui.Quit() - fmt.Printf("selected %s\n", cfg.Contexts[t.Selected()-1].Name) + return event }) - ui.SetKeybinding("Esc", func() { ui.Quit() }) - ui.SetKeybinding("q", func() { ui.Quit() }) - - if err := ui.Run(); err != nil { - log.Fatal(err) + if err := app.SetRoot(list, true).SetFocus(list).Run(); err != nil { + panic(err) } }