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
3 changes: 3 additions & 0 deletions docs/docs/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ title: Changelog
## [Unreleased](https://github.com/lets-cli/lets/releases/tag/v0.0.X)

* `[Docs]` Convert design specs into ADRs.
* `[Refactoring]` Rename the internal download progress package to progressbar.
* `[Added]` Show interactive download progress for `lets self upgrade`. Issue [#367](https://github.com/lets-cli/lets/issues/367)
* `[Changed]` Unify terminal detection across the CLI; Cygwin/MSYS terminals are no longer treated as interactive (drops the direct `mattn/go-isatty` dependency).
* `[Added]` Add `lets self config path` and `lets self config edit` for user settings. Issue [#370](https://github.com/lets-cli/lets/issues/370)
* `[Added]` Show interactive download progress for remote configs and remote mixins. Issue [#360](https://github.com/lets-cli/lets/issues/360)
* `[Fixed]` Make `--no-cache` re-download remote mixins for local and remote configs. Issue [#365](https://github.com/lets-cli/lets/issues/365)
Expand Down
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ toolchain go1.26.0

require (
charm.land/bubbles/v2 v2.1.0
charm.land/bubbletea/v2 v2.0.2
charm.land/lipgloss/v2 v2.0.2
github.com/charmbracelet/colorprofile v0.4.2
github.com/charmbracelet/x/ansi v0.11.6
Expand All @@ -17,7 +18,6 @@ require (
github.com/fatih/color v1.16.0
github.com/kindermax/docopt.go v0.8.0
github.com/lets-cli/fang v0.1.0
github.com/mattn/go-isatty v0.0.20
github.com/odvcencio/gotreesitter v0.12.1
github.com/pkg/errors v0.9.1
github.com/sirupsen/logrus v1.9.3
Expand All @@ -28,7 +28,6 @@ require (
)

require (
charm.land/bubbletea/v2 v2.0.2 // indirect
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
github.com/aymanbagabas/go-udiff v0.4.1 // indirect
github.com/charmbracelet/harmonica v0.2.0 // indirect
Expand All @@ -41,6 +40,7 @@ require (
github.com/iancoleman/strcase v0.3.0 // indirect
github.com/lucasb-eyer/go-colorful v1.3.0 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-runewidth v0.0.21 // indirect
github.com/muesli/cancelreader v0.2.2 // indirect
github.com/muesli/mango v0.1.0 // indirect
Expand Down
15 changes: 7 additions & 8 deletions internal/cli/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,16 @@ import (
"github.com/lets-cli/lets/internal/cmd"
loader "github.com/lets-cli/lets/internal/config"
"github.com/lets-cli/lets/internal/config/config"
"github.com/lets-cli/lets/internal/downloadprogress"
"github.com/lets-cli/lets/internal/env"
"github.com/lets-cli/lets/internal/logging"
"github.com/lets-cli/lets/internal/progressbar"
"github.com/lets-cli/lets/internal/set"
"github.com/lets-cli/lets/internal/settings"
"github.com/lets-cli/lets/internal/theme"
"github.com/lets-cli/lets/internal/upgrade"
"github.com/lets-cli/lets/internal/upgrade/registry"
"github.com/lets-cli/lets/internal/util"
"github.com/lets-cli/lets/internal/workdir"
"github.com/mattn/go-isatty"
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
)
Expand Down Expand Up @@ -54,7 +54,7 @@ func Main(version string, buildDate string) int {
rootCmd.InitDefaultHelpFlag()
rootCmd.InitDefaultVersionFlag()
reinitCompletionCmd := cmd.InitCompletionCmd(rootCmd, nil)
cmd.InitSelfCmd(rootCmd, version)
cmd.InitSelfCmd(rootCmd, version, appSettings)
rootCmd.InitDefaultHelpCmd()

command, args, err := rootCmd.Traverse(os.Args[1:])
Expand Down Expand Up @@ -83,10 +83,10 @@ func Main(version string, buildDate string) int {

loadOptions := []loader.LoadOption{}
if isInteractiveStderr() {
loadOptions = append(loadOptions, loader.WithProgress(downloadprogress.New(
loadOptions = append(loadOptions, loader.WithProgress(progressbar.New(
os.Stderr,
downloadprogress.WithNoColor(appSettings.NoColor),
downloadprogress.WithTheme(appSettings.Theme),
progressbar.WithNoColor(appSettings.NoColor),
progressbar.WithTheme(appSettings.Theme),
)))
}

Expand Down Expand Up @@ -292,8 +292,7 @@ func shouldCheckForUpdate(command *cobra.Command, interactive bool, appSettings
}

func isInteractiveStderr() bool {
fd := os.Stderr.Fd()
return isatty.IsTerminal(fd) || isatty.IsCygwinTerminal(fd)
return util.IsTerminalWriter(os.Stderr)
}

func isRemoteURL(s string) bool {
Expand Down
4 changes: 2 additions & 2 deletions internal/cli/cli_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ func TestAllowsMissingConfig(t *testing.T) {

t.Run("self subcommand", func(t *testing.T) {
root := cmdpkg.CreateRootCommand("v0.0.0-test", "")
cmdpkg.InitSelfCmd(root, "v0.0.0-test")
cmdpkg.InitSelfCmd(root, "v0.0.0-test", settings.Default())

command, _, err := root.Find([]string{"self", "doc"})
if err != nil {
Expand Down Expand Up @@ -111,7 +111,7 @@ func TestShouldCheckForUpdate(t *testing.T) {

t.Run("should skip self subcommands", func(t *testing.T) {
root := cmdpkg.CreateRootCommand("v0.0.0-test", "")
cmdpkg.InitSelfCmd(root, "v0.0.0-test")
cmdpkg.InitSelfCmd(root, "v0.0.0-test", settings.Default())

for _, args := range [][]string{{"self"}, {"self", "doc"}, {"self", "upgrade"}} {
command, _, err := root.Find(args)
Expand Down
3 changes: 2 additions & 1 deletion internal/cmd/help_golden_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"github.com/lets-cli/fang"
"github.com/lets-cli/lets/internal/config"
"github.com/lets-cli/lets/internal/executor"
"github.com/lets-cli/lets/internal/settings"
"github.com/lets-cli/lets/internal/theme"
"github.com/spf13/cobra"
)
Expand All @@ -21,7 +22,7 @@ func newGoldenRoot(t *testing.T) *cobra.Command {
root := CreateRootCommand("0.0.0-dev", "")
root.InitDefaultHelpFlag()
root.InitDefaultVersionFlag()
InitSelfCmd(root, "0.0.0-dev")
InitSelfCmd(root, "0.0.0-dev", settings.Default())
InitCompletionCmd(root, nil)
root.InitDefaultHelpCmd()
return root
Expand Down
19 changes: 10 additions & 9 deletions internal/cmd/root_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"testing"

"github.com/lets-cli/lets/internal/config/config"
"github.com/lets-cli/lets/internal/settings"
"github.com/lets-cli/lets/internal/upgrade"
"github.com/spf13/cobra"
)
Expand Down Expand Up @@ -220,7 +221,7 @@ func TestSelfCmd(t *testing.T) {
t.Run("should use help func when run without args", func(t *testing.T) {
rootCmd := CreateRootCommand("v0.0.0-test", "")
rootCmd.SetArgs([]string{"self"})
initSelfCmd(rootCmd, "v0.0.0-test", func(string) error { return nil }, func(string) error { return nil })
initSelfCmd(rootCmd, "v0.0.0-test", settings.Default(), func(string) error { return nil }, func(string) error { return nil })

called := false
rootCmd.SetHelpFunc(func(c *cobra.Command, args []string) {
Expand All @@ -247,7 +248,7 @@ func TestSelfCmd(t *testing.T) {
rootCmd.SetArgs([]string{"self", "config", "path"})
rootCmd.SetOut(bufOut)
rootCmd.SetErr(bufOut)
initSelfCmd(rootCmd, "v0.0.0-test", func(string) error { return nil }, func(string) error { return nil })
initSelfCmd(rootCmd, "v0.0.0-test", settings.Default(), func(string) error { return nil }, func(string) error { return nil })

if err := rootCmd.Execute(); err != nil {
t.Fatalf("unexpected error: %v", err)
Expand All @@ -270,7 +271,7 @@ func TestSelfCmd(t *testing.T) {
rootCmd.SetArgs([]string{"self", "config", "edit"})
rootCmd.SetOut(bufOut)
rootCmd.SetErr(bufOut)
initSelfCmd(rootCmd, "v0.0.0-test", func(string) error { return nil }, func(path string) error {
initSelfCmd(rootCmd, "v0.0.0-test", settings.Default(), func(string) error { return nil }, func(path string) error {
called = true
gotPath = path

Expand Down Expand Up @@ -316,7 +317,7 @@ func TestSelfCmd(t *testing.T) {
rootCmd.SetArgs([]string{"self", "doc"})
rootCmd.SetOut(bufOut)
rootCmd.SetErr(bufOut)
initSelfCmd(rootCmd, "v0.0.0-test", openURL, func(string) error { return nil })
initSelfCmd(rootCmd, "v0.0.0-test", settings.Default(), openURL, func(string) error { return nil })

err := rootCmd.Execute()
if err != nil {
Expand All @@ -343,7 +344,7 @@ func TestSelfCmd(t *testing.T) {
rootCmd.SetArgs([]string{"self", "doc"})
rootCmd.SetOut(bufOut)
rootCmd.SetErr(bufOut)
initSelfCmd(rootCmd, "v0.0.0-test", openURL, func(string) error { return nil })
initSelfCmd(rootCmd, "v0.0.0-test", settings.Default(), openURL, func(string) error { return nil })

err := rootCmd.Execute()
if err == nil {
Expand All @@ -362,7 +363,7 @@ func TestSelfCmd(t *testing.T) {
rootCmd.SetArgs([]string{"self", "ls"})
rootCmd.SetOut(bufOut)
rootCmd.SetErr(bufOut)
InitSelfCmd(rootCmd, "v0.0.0-test")
InitSelfCmd(rootCmd, "v0.0.0-test", settings.Default())

err := rootCmd.Execute()
if err == nil {
Expand Down Expand Up @@ -395,7 +396,7 @@ func TestSelfCmd(t *testing.T) {
rootCmd.SetArgs([]string{"self", "zzzznotacommand"})
rootCmd.SetOut(bufOut)
rootCmd.SetErr(bufOut)
InitSelfCmd(rootCmd, "v0.0.0-test")
InitSelfCmd(rootCmd, "v0.0.0-test", settings.Default())

err := rootCmd.Execute()
if err == nil {
Expand Down Expand Up @@ -431,7 +432,7 @@ func TestSelfCmd(t *testing.T) {
}
rootCmd.AddCommand(selfCmd)

selfCmd.AddCommand(initUpgradeCommandWith(func() (upgrade.Upgrader, error) {
selfCmd.AddCommand(initUpgradeCommandWith(func(_ *cobra.Command) (upgrade.Upgrader, error) {
return mockUpgraderFunc(func(ctx context.Context) error {
called = true

Expand Down Expand Up @@ -462,7 +463,7 @@ func TestSelfCmd(t *testing.T) {
}
rootCmd.AddCommand(selfCmd)

selfCmd.AddCommand(initUpgradeCommandWith(func() (upgrade.Upgrader, error) {
selfCmd.AddCommand(initUpgradeCommandWith(func(_ *cobra.Command) (upgrade.Upgrader, error) {
return mockUpgraderFunc(func(ctx context.Context) error {
return errors.New("upgrade failed")
}), nil
Expand Down
13 changes: 9 additions & 4 deletions internal/cmd/self.go
Original file line number Diff line number Diff line change
@@ -1,17 +1,22 @@
package cmd

import (
"github.com/lets-cli/lets/internal/settings"
"github.com/lets-cli/lets/internal/util"
"github.com/spf13/cobra"
)

// InitSelfCmd intializes root 'self' subcommand.
func InitSelfCmd(rootCmd *cobra.Command, version string) {
initSelfCmd(rootCmd, version, util.OpenURL, util.OpenEditor)
func InitSelfCmd(rootCmd *cobra.Command, version string, appSettings settings.Settings) {
initSelfCmd(rootCmd, version, appSettings, util.OpenURL, util.OpenEditor)
}

func initSelfCmd(
rootCmd *cobra.Command, version string, openURL func(string) error, openEditor func(string) error,
rootCmd *cobra.Command,
version string,
appSettings settings.Settings,
openURL func(string) error,
openEditor func(string) error,
) {
selfCmd := &cobra.Command{
Use: "self",
Expand All @@ -30,5 +35,5 @@ func initSelfCmd(
selfCmd.AddCommand(initDocCommand(openURL))
selfCmd.AddCommand(initLspCommand(version))
selfCmd.AddCommand(initSkillsCommand())
selfCmd.AddCommand(initUpgradeCommand(version))
selfCmd.AddCommand(initUpgradeCommand(version, appSettings))
}
13 changes: 7 additions & 6 deletions internal/cmd/self_skills_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"strings"
"testing"

"github.com/lets-cli/lets/internal/settings"
skillpkg "github.com/lets-cli/lets/internal/skills"
)

Expand All @@ -17,7 +18,7 @@ func TestSelfSkillsCmd(t *testing.T) {
rootCmd.SetArgs([]string{"self", "skills", "show"})
rootCmd.SetOut(bufOut)
rootCmd.SetErr(new(bytes.Buffer))
InitSelfCmd(rootCmd, "v0.0.0-test")
InitSelfCmd(rootCmd, "v0.0.0-test", settings.Default())

if err := rootCmd.Execute(); err != nil {
t.Fatalf("unexpected error: %v", err)
Expand All @@ -37,7 +38,7 @@ func TestSelfSkillsCmd(t *testing.T) {
rootCmd.SetIn(strings.NewReader("local\n"))
rootCmd.SetOut(bufOut)
rootCmd.SetErr(bufErr)
InitSelfCmd(rootCmd, "v0.0.0-test")
InitSelfCmd(rootCmd, "v0.0.0-test", settings.Default())

if err := rootCmd.Execute(); err != nil {
t.Fatalf("unexpected error: %v", err)
Expand Down Expand Up @@ -65,7 +66,7 @@ func TestSelfSkillsCmd(t *testing.T) {
rootCmd.SetIn(strings.NewReader("global\n"))
rootCmd.SetOut(bufOut)
rootCmd.SetErr(new(bytes.Buffer))
InitSelfCmd(rootCmd, "v0.0.0-test")
InitSelfCmd(rootCmd, "v0.0.0-test", settings.Default())

if err := rootCmd.Execute(); err != nil {
t.Fatalf("unexpected error: %v", err)
Expand All @@ -83,7 +84,7 @@ func TestSelfSkillsCmd(t *testing.T) {
rootCmd.SetIn(strings.NewReader("2\n"))
rootCmd.SetOut(new(bytes.Buffer))
rootCmd.SetErr(new(bytes.Buffer))
InitSelfCmd(rootCmd, "v0.0.0-test")
InitSelfCmd(rootCmd, "v0.0.0-test", settings.Default())

if err := rootCmd.Execute(); err != nil {
t.Fatalf("unexpected error: %v", err)
Expand All @@ -108,7 +109,7 @@ func TestSelfSkillsCmd(t *testing.T) {
rootCmd.SetArgs([]string{"self", "skills", "install", "--local"})
rootCmd.SetOut(bufOut)
rootCmd.SetErr(new(bytes.Buffer))
InitSelfCmd(rootCmd, "v0.0.0-test")
InitSelfCmd(rootCmd, "v0.0.0-test", settings.Default())

if err := rootCmd.Execute(); err != nil {
t.Fatalf("unexpected error: %v", err)
Expand Down Expand Up @@ -142,7 +143,7 @@ func TestSelfSkillsCmd(t *testing.T) {
rootCmd.SetArgs([]string{"self", "skills", "update"})
rootCmd.SetOut(bufOut)
rootCmd.SetErr(new(bytes.Buffer))
InitSelfCmd(rootCmd, "v0.0.0-test")
InitSelfCmd(rootCmd, "v0.0.0-test", settings.Default())

if err := rootCmd.Execute(); err != nil {
t.Fatalf("unexpected error: %v", err)
Expand Down
31 changes: 26 additions & 5 deletions internal/cmd/upgrade.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,48 @@ package cmd

import (
"fmt"
"io"

"github.com/lets-cli/lets/internal/fetch"
"github.com/lets-cli/lets/internal/progressbar"
"github.com/lets-cli/lets/internal/settings"
"github.com/lets-cli/lets/internal/upgrade"
"github.com/lets-cli/lets/internal/upgrade/registry"
"github.com/lets-cli/lets/internal/util"
"github.com/spf13/cobra"
)

type upgraderFactory func() (upgrade.Upgrader, error)
type upgraderFactory func(cmd *cobra.Command) (upgrade.Upgrader, error)

func initUpgradeCommand(version string) *cobra.Command {
return initUpgradeCommandWith(func() (upgrade.Upgrader, error) {
return upgrade.NewBinaryUpgrader(registry.NewGithubRegistry(), version)
func initUpgradeCommand(version string, appSettings settings.Settings) *cobra.Command {
return initUpgradeCommandWith(func(cmd *cobra.Command) (upgrade.Upgrader, error) {
progress := upgradeProgress(cmd.ErrOrStderr(), appSettings)

return upgrade.NewBinaryUpgrader(registry.NewGithubRegistry(), version, upgrade.WithProgress(progress))
})
}

// upgradeProgress builds a progress observer for the upgrade download, or a
// no-op observer when the stream is not an interactive terminal.
func upgradeProgress(stderr io.Writer, appSettings settings.Settings) fetch.ProgressObserver { //nolint:ireturn // Returns NopObserver or *progressbar.Observer.
if !util.IsTerminalWriter(stderr) {
return fetch.NopObserver{}
}

return progressbar.New(
stderr,
progressbar.WithNoColor(appSettings.NoColor),
progressbar.WithTheme(appSettings.Theme),
)
}

func initUpgradeCommandWith(createUpgrader upgraderFactory) *cobra.Command {
upgradeCmd := &cobra.Command{
Use: "upgrade",
Short: "Upgrade lets to latest version",
Args: cobra.NoArgs,
RunE: func(cmd *cobra.Command, args []string) error {
upgrader, err := createUpgrader()
upgrader, err := createUpgrader(cmd)
if err != nil {
return fmt.Errorf("can not self-upgrade binary: %w", err)
}
Expand Down
Loading
Loading