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
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
2.0.46
2.0.51
28 changes: 11 additions & 17 deletions cmd/deepsource/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"log"
"net/http"
"os"
"path/filepath"
"strings"
"time"

Expand All @@ -16,6 +17,7 @@ import (
clierrors "github.com/deepsourcelabs/cli/internal/errors"
"github.com/deepsourcelabs/cli/internal/update"
"github.com/getsentry/sentry-go"
"github.com/pterm/pterm"
)

var (
Expand Down Expand Up @@ -71,28 +73,20 @@ func mainRun() (exitCode int) {
func run() int {
v.SetBuildInfo(version, Date, buildMode)

// Two-phase auto-update: apply pending update or check for new one
if update.ShouldAutoUpdate() {
// Check for available updates and notify (skip when running "update" itself)
isUpdateCmd := len(os.Args) >= 2 && os.Args[1] == "update"
if !isUpdateCmd && update.ShouldCheckForUpdate() {
client := &http.Client{Timeout: 3 * time.Second}
if err := update.CheckForUpdate(client); err != nil {
debug.Log("update: %v", err)
}

state, err := update.ReadUpdateState()
if err != nil {
debug.Log("update: %v", err)
}

if state != nil {
// Phase 2: a previous run found a newer version — apply it now
client := &http.Client{Timeout: 30 * time.Second}
newVer, err := update.ApplyUpdate(client)
if err != nil {
debug.Log("update: %v", err)
} else if newVer != "" {
fmt.Fprintf(os.Stderr, "%s\n", style.Yellow("Updated DeepSource CLI to v%s", newVer))
}
} else {
// Phase 1: check manifest and write state file for next run
client := &http.Client{Timeout: 3 * time.Second}
if err := update.CheckForUpdate(client); err != nil {
debug.Log("update: %v", err)
}
fmt.Fprintln(os.Stderr, pterm.Yellow(fmt.Sprintf("Update available: v%s, run '%s update' to install.", state.Version, filepath.Base(os.Args[0]))))
}
}

Expand Down
24 changes: 18 additions & 6 deletions command/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"github.com/deepsourcelabs/cli/buildinfo"
"github.com/deepsourcelabs/cli/command/auth"
completionCmd "github.com/deepsourcelabs/cli/command/completion"
updateCmd "github.com/deepsourcelabs/cli/command/update"
"github.com/deepsourcelabs/cli/command/issues"
"github.com/deepsourcelabs/cli/command/metrics"
"github.com/deepsourcelabs/cli/command/report"
Expand Down Expand Up @@ -89,7 +90,12 @@ func NewCmdRoot() *cobra.Command {
completionC.GroupID = "setup"
cmd.AddCommand(completionC)

updateC := updateCmd.NewCmdUpdate()
updateC.GroupID = "setup"
cmd.AddCommand(updateC)

cmd.PersistentFlags().Bool("skip-tls-verify", false, "Skip TLS certificate verification (for self-signed certs)")
cmd.Flags().BoolP("verbose", "v", false, "Show detailed output including examples")

cmd.InitDefaultHelpFlag()
cmd.InitDefaultVersionFlag()
Expand Down Expand Up @@ -167,13 +173,19 @@ func rootHelpFunc(cmd *cobra.Command, _ []string) {
}
}

// Examples
examples := buildExampleText()
if examples != "" {
fmt.Fprintf(out, "%s\n", style.BoldCyan("Examples:"))
for _, line := range strings.Split(examples, "\n") {
fmt.Fprintf(out, " %s\n", line)
// Examples (shown only with --verbose / -v)
verbose, _ := cmd.Flags().GetBool("verbose")
if verbose {
examples := buildExampleText()
if examples != "" {
fmt.Fprintf(out, "%s\n", style.BoldCyan("Examples:"))
for _, line := range strings.Split(examples, "\n") {
fmt.Fprintf(out, " %s\n", line)
}
fmt.Fprintln(out)
}
} else {
fmt.Fprintln(out, pterm.Gray("Use --help -v to see usage examples."))
fmt.Fprintln(out)
}

Expand Down
56 changes: 56 additions & 0 deletions command/update/update.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package update

import (
"fmt"
"net/http"
"time"

"github.com/deepsourcelabs/cli/buildinfo"
"github.com/deepsourcelabs/cli/internal/update"
"github.com/spf13/cobra"
)

func NewCmdUpdate() *cobra.Command {
return &cobra.Command{
Use: "update",
Short: "Update DeepSource CLI to the latest version",
RunE: func(cmd *cobra.Command, _ []string) error {
return runUpdate(cmd)
},
}
}

func runUpdate(cmd *cobra.Command) error {
w := cmd.ErrOrStderr()

// Check for the latest version
checkClient := &http.Client{Timeout: 10 * time.Second}
if err := update.CheckForUpdate(checkClient); err != nil {
return fmt.Errorf("checking for updates: %w", err)
}

state, err := update.ReadUpdateState()
if err != nil {
return fmt.Errorf("reading update state: %w", err)
}

if state == nil {
bi := buildinfo.GetBuildInfo()
fmt.Fprintf(w, "Already up to date (v%s)\n", bi.Version)
return nil
}

fmt.Fprintf(w, "Updating to v%s...\n", state.Version)

applyClient := &http.Client{Timeout: 30 * time.Second}
newVer, err := update.ApplyUpdate(applyClient)
if err != nil {
return fmt.Errorf("applying update: %w", err)
}

if newVer != "" {
fmt.Fprintf(w, "Updated to v%s\n", newVer)
}

return nil
}
3 changes: 1 addition & 2 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,7 @@ type CLIConfig struct {
User string `toml:"user"`
Token string `toml:"token"`
TokenExpiresIn time.Time `toml:"token_expires_in,omitempty"`
AutoUpdate *bool `toml:"auto_update,omitempty"`
SkipTLSVerify bool `toml:"skip_tls_verify,omitempty"`
SkipTLSVerify bool `toml:"skip_tls_verify,omitempty"`
TokenFromEnv bool `toml:"-"`
}

Expand Down
12 changes: 2 additions & 10 deletions internal/update/updater.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ import (
"time"

"github.com/deepsourcelabs/cli/buildinfo"
"github.com/deepsourcelabs/cli/config"
"github.com/deepsourcelabs/cli/internal/debug"
)

Expand Down Expand Up @@ -179,8 +178,8 @@ func ApplyUpdate(client *http.Client) (string, error) {
return state.Version, nil
}

// ShouldAutoUpdate reports whether the auto-updater should run.
func ShouldAutoUpdate() bool {
// ShouldCheckForUpdate reports whether the update check should run.
func ShouldCheckForUpdate() bool {
bi := buildinfo.GetBuildInfo()
if bi == nil {
return false
Expand All @@ -204,13 +203,6 @@ func ShouldAutoUpdate() bool {
}
}

// Check config
cfg, err := config.GetConfig()
if err == nil && cfg.AutoUpdate != nil && !*cfg.AutoUpdate {
debug.Log("update: skipping (disabled in config)")
return false
}

return true
}

Expand Down
16 changes: 8 additions & 8 deletions internal/update/updater_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ func TestReplaceBinary(t *testing.T) {
}
}

func TestShouldAutoUpdate_DevBuild(t *testing.T) {
func TestShouldCheckForUpdate_DevBuild(t *testing.T) {
buildinfo.SetBuildInfo("2.0.3", "", "dev")

// Clear CI vars so they don't interfere
Expand All @@ -109,27 +109,27 @@ func TestShouldAutoUpdate_DevBuild(t *testing.T) {
t.Setenv(v, "")
}

if !ShouldAutoUpdate() {
if !ShouldCheckForUpdate() {
t.Error("expected true for dev build with real version")
}
}

func TestShouldAutoUpdate_DevelopmentVersion(t *testing.T) {
func TestShouldCheckForUpdate_DevelopmentVersion(t *testing.T) {
buildinfo.SetBuildInfo("development", "", "")
if ShouldAutoUpdate() {
if ShouldCheckForUpdate() {
t.Error("expected false for development version")
}
}

func TestShouldAutoUpdate_CI(t *testing.T) {
func TestShouldCheckForUpdate_CI(t *testing.T) {
buildinfo.SetBuildInfo("2.0.3", "", "prod")
t.Setenv("CI", "true")
if ShouldAutoUpdate() {
if ShouldCheckForUpdate() {
t.Error("expected false in CI")
}
}

func TestShouldAutoUpdate_Prod(t *testing.T) {
func TestShouldCheckForUpdate_Prod(t *testing.T) {
buildinfo.SetBuildInfo("2.0.3", "", "prod")

// Clear CI vars
Expand All @@ -138,7 +138,7 @@ func TestShouldAutoUpdate_Prod(t *testing.T) {
t.Setenv(v, "")
}

if !ShouldAutoUpdate() {
if !ShouldCheckForUpdate() {
t.Error("expected true for prod build outside CI")
}
}
Expand Down
Loading