Skip to content

Commit

Permalink
Merge pull request #1069 from wakatime/develop
Browse files Browse the repository at this point in the history
Release v1.97.1
  • Loading branch information
alanhamlett authored Jul 23, 2024
2 parents b52f968 + f0a62d7 commit 5d46cab
Show file tree
Hide file tree
Showing 13 changed files with 354 additions and 521 deletions.
2 changes: 0 additions & 2 deletions cmd/heartbeat/heartbeat.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,8 +105,6 @@ func SendHeartbeats(v *viper.Viper, queueFilepath string) error {
}

handleOpts = append(handleOpts, offline.WithQueue(queueFilepath))
} else if params.Offline.OfflineOnly {
return errors.New("--offline-only can NOT be used with --disable-offline")
}

handleOpts = append(handleOpts, backoff.WithBackoff(backoff.Config{
Expand Down
4 changes: 2 additions & 2 deletions cmd/offline/offline.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ import (
)

// SaveHeartbeats saves heartbeats to the offline db without trying to send to the API.
// Used when we have more heartbeats than `offline.SendLimit`, when --offline-only enabled,
// when we couldn't send heartbeats to the API, or the API returned an auth error.
// Used when we have more heartbeats than `offline.SendLimit`, when we couldn't send
// heartbeats to the API, or the API returned an auth error.
func SaveHeartbeats(v *viper.Viper, heartbeats []heartbeat.Heartbeat, queueFilepath string) error {
params, err := loadParams(v)
if err != nil {
Expand Down
5 changes: 1 addition & 4 deletions cmd/params/params.go
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,6 @@ type (
// Offline contains offline related parameters.
Offline struct {
Disabled bool
OfflineOnly bool
PrintMax int
QueueFile string
QueueFileLegacy string
Expand Down Expand Up @@ -657,7 +656,6 @@ func LoadOfflineParams(v *viper.Viper) Offline {

return Offline{
Disabled: disabled,
OfflineOnly: v.GetBool("offline-only"),
PrintMax: v.GetInt("print-offline-heartbeats"),
QueueFile: vipertools.GetString(v, "offline-queue-file"),
QueueFileLegacy: vipertools.GetString(v, "offline-queue-file-legacy"),
Expand Down Expand Up @@ -1041,9 +1039,8 @@ func (p Heartbeat) String() string {
// String implements fmt.Stringer interface.
func (p Offline) String() string {
return fmt.Sprintf(
"disabled: %t, offline only: %t, print max: %d, queue file: '%s', queue file legacy: '%s', num sync max: %d",
"disabled: %t, print max: %d, queue file: '%s', queue file legacy: '%s', num sync max: %d",
p.Disabled,
p.OfflineOnly,
p.PrintMax,
p.QueueFile,
p.QueueFileLegacy,
Expand Down
2 changes: 1 addition & 1 deletion cmd/params/params_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2505,7 +2505,7 @@ func TestOffline_String(t *testing.T) {

assert.Equal(
t,
"disabled: true, offline only: false, print max: 6, queue file: '/path/to/queue.file',"+
"disabled: true, print max: 6, queue file: '/path/to/queue.file',"+
" queue file legacy: '/path/to/legacy.file', num sync max: 12",
offline.String(),
)
Expand Down
20 changes: 17 additions & 3 deletions cmd/root.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
package cmd

import (
"errors"
"fmt"
"os"

"github.com/wakatime/wakatime-cli/pkg/api"
"github.com/wakatime/wakatime-cli/pkg/exitcode"
"github.com/wakatime/wakatime-cli/pkg/offline"

log "github.com/sirupsen/logrus"
Expand All @@ -25,8 +28,20 @@ func NewRootCMD() *cobra.Command {
cmd := &cobra.Command{
Use: "wakatime-cli",
Short: "Command line interface used by all WakaTime text editor plugins.",
Run: func(cmd *cobra.Command, _ []string) {
Run(cmd, v)
RunE: func(cmd *cobra.Command, _ []string) error {
if err := RunE(cmd, v); err != nil {
var errexitcode exitcode.Err

if errors.As(err, &errexitcode) {
os.Exit(errexitcode.Code)
}

os.Exit(exitcode.ErrGeneric)
}

os.Exit(exitcode.Success)

return nil
},
}

Expand Down Expand Up @@ -230,7 +245,6 @@ func setFlags(cmd *cobra.Command, v *viper.Viper) {
" new heartbeats.", offline.SyncMaxDefault),
)
flags.Bool("offline-count", false, "Prints the number of heartbeats in the offline db, then exits.")
flags.Bool("offline-only", false, "Saves the heartbeat(s) to the offline db, then exits.")
flags.Int(
"timeout",
api.DefaultTimeoutSecs,
Expand Down
97 changes: 42 additions & 55 deletions cmd/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,8 @@ type diagnostics struct {
Stack string
}

// Run executes commands parsed from a command line.
func Run(cmd *cobra.Command, v *viper.Viper) {
// RunE executes commands parsed from a command line.
func RunE(cmd *cobra.Command, v *viper.Viper) error {
// force setup logging otherwise log goes to std out
_, err := SetupLogging(v)
if err != nil {
Expand All @@ -58,9 +58,9 @@ func Run(cmd *cobra.Command, v *viper.Viper) {
log.Errorf("failed to parse config files: %s", err)

if v.IsSet("entity") {
saveHeartbeats(v)
_ = saveHeartbeats(v)

os.Exit(exitcode.ErrConfigFileParse)
return exitcode.Err{Code: exitcode.ErrConfigFileParse}
}
}

Expand All @@ -75,90 +75,82 @@ func Run(cmd *cobra.Command, v *viper.Viper) {
log.Fatalf("failed to register custom lexers: %s", err)
}

shutdown := shutdownFn(func() {})

// start profiling if enabled
if logFileParams.Metrics {
shutdown, err = metrics.StartProfiling()
shutdown, err := metrics.StartProfiling()
if err != nil {
log.Errorf("failed to start profiling: %s", err)
}

defer shutdown()
}

if v.GetBool("user-agent") {
log.Debugln("command: user-agent")

fmt.Println(heartbeat.UserAgent(vipertools.GetString(v, "plugin")))

shutdown()

os.Exit(exitcode.Success)
return nil
}

if v.GetBool("version") {
log.Debugln("command: version")

RunCmd(v, logFileParams.Verbose, logFileParams.SendDiagsOnErrors, runVersion, shutdown)
return RunCmd(v, logFileParams.Verbose, logFileParams.SendDiagsOnErrors, runVersion)
}

if v.IsSet("config-read") {
log.Debugln("command: config-read")

RunCmd(v, logFileParams.Verbose, logFileParams.SendDiagsOnErrors, configread.Run, shutdown)
return RunCmd(v, logFileParams.Verbose, logFileParams.SendDiagsOnErrors, configread.Run)
}

if v.IsSet("config-write") {
log.Debugln("command: config-write")

RunCmd(v, logFileParams.Verbose, logFileParams.SendDiagsOnErrors, configwrite.Run, shutdown)
return RunCmd(v, logFileParams.Verbose, logFileParams.SendDiagsOnErrors, configwrite.Run)
}

if v.GetBool("today") {
log.Debugln("command: today")

RunCmd(v, logFileParams.Verbose, logFileParams.SendDiagsOnErrors, today.Run, shutdown)
return RunCmd(v, logFileParams.Verbose, logFileParams.SendDiagsOnErrors, today.Run)
}

if v.IsSet("today-goal") {
log.Debugln("command: today-goal")

RunCmd(v, logFileParams.Verbose, logFileParams.SendDiagsOnErrors, todaygoal.Run, shutdown)
return RunCmd(v, logFileParams.Verbose, logFileParams.SendDiagsOnErrors, todaygoal.Run)
}

if v.GetBool("file-experts") {
log.Debugln("command: file-experts")

RunCmd(v, logFileParams.Verbose, logFileParams.SendDiagsOnErrors, fileexperts.Run, shutdown)
return RunCmd(v, logFileParams.Verbose, logFileParams.SendDiagsOnErrors, fileexperts.Run)
}

if v.IsSet("entity") {
log.Debugln("command: heartbeat")

if v.GetBool("offline-only") {
saveHeartbeats(v)
shutdown()
os.Exit(exitcode.Success)
}

RunCmdWithOfflineSync(v, logFileParams.Verbose, logFileParams.SendDiagsOnErrors, cmdheartbeat.Run, shutdown)
return RunCmdWithOfflineSync(v, logFileParams.Verbose, logFileParams.SendDiagsOnErrors, cmdheartbeat.Run)
}

if v.IsSet("sync-offline-activity") {
log.Debugln("command: sync-offline-activity")

RunCmd(v, logFileParams.Verbose, logFileParams.SendDiagsOnErrors, offlinesync.Run, shutdown)
return RunCmd(v, logFileParams.Verbose, logFileParams.SendDiagsOnErrors, offlinesync.Run)
}

if v.GetBool("offline-count") {
log.Debugln("command: offline-count")

RunCmd(v, logFileParams.Verbose, logFileParams.SendDiagsOnErrors, offlinecount.Run, shutdown)
return RunCmd(v, logFileParams.Verbose, logFileParams.SendDiagsOnErrors, offlinecount.Run)
}

if v.IsSet("print-offline-heartbeats") {
log.Debugln("command: print-offline-heartbeats")

RunCmd(v, logFileParams.Verbose, logFileParams.SendDiagsOnErrors, offlineprint.Run, shutdown)
return RunCmd(v, logFileParams.Verbose, logFileParams.SendDiagsOnErrors, offlineprint.Run)
}

log.Warnf("one of the following parameters has to be provided: %s", strings.Join([]string{
Expand All @@ -177,7 +169,7 @@ func Run(cmd *cobra.Command, v *viper.Viper) {

_ = cmd.Help()

os.Exit(exitcode.ErrGeneric)
return exitcode.Err{Code: exitcode.ErrGeneric}
}

func parseConfigFiles(v *viper.Viper) error {
Expand Down Expand Up @@ -264,46 +256,31 @@ func SetupLogging(v *viper.Viper) (*logfile.Params, error) {
return &logfileParams, nil
}

type (
// cmdFn represents a command function.
cmdFn func(v *viper.Viper) (int, error)
// shutdownFn represents a shutdown function. It will be called before exiting.
shutdownFn func()
)
// cmdFn represents a command function.
type cmdFn func(v *viper.Viper) (int, error)

// RunCmd runs a command function and exits with the exit code returned by
// the command function. Will send diagnostic on any errors or panics.
func RunCmd(v *viper.Viper, verbose bool, sendDiagsOnErrors bool, cmd cmdFn, shutdown shutdownFn) {
exitCode := runCmd(v, verbose, sendDiagsOnErrors, cmd)

shutdown()

os.Exit(exitCode)
func RunCmd(v *viper.Viper, verbose bool, sendDiagsOnErrors bool, cmd cmdFn) error {
return runCmd(v, verbose, sendDiagsOnErrors, cmd)
}

// RunCmdWithOfflineSync runs a command function and exits with the exit code
// returned by the command function. If command run was successful, it will execute
// offline sync command afterwards. Will send diagnostic on any errors or panics.
func RunCmdWithOfflineSync(v *viper.Viper, verbose bool, sendDiagsOnErrors bool, cmd cmdFn, shutdown shutdownFn) {
exitCode := runCmd(v, verbose, sendDiagsOnErrors, cmd)
if exitCode != exitcode.Success {
shutdown()

os.Exit(exitCode)
func RunCmdWithOfflineSync(v *viper.Viper, verbose bool, sendDiagsOnErrors bool, cmd cmdFn) error {
if err := runCmd(v, verbose, sendDiagsOnErrors, cmd); err != nil {
return err
}

exitCode = runCmd(v, verbose, sendDiagsOnErrors, offlinesync.Run)

shutdown()

os.Exit(exitCode)
return runCmd(v, verbose, sendDiagsOnErrors, offlinesync.Run)
}

// runCmd contains the main logic of RunCmd.
// It will send diagnostic on any errors or panics.
// On panic, it will send diagnostic and exit with ErrGeneric exit code.
// On error, it will only send diagnostic if sendDiagsOnErrors and verbose is true.
func runCmd(v *viper.Viper, verbose bool, sendDiagsOnErrors bool, cmd cmdFn) (exitCode int) {
func runCmd(v *viper.Viper, verbose bool, sendDiagsOnErrors bool, cmd cmdFn) (errresponse error) {
logs := bytes.NewBuffer(nil)
resetLogs := captureLogs(logs)

Expand All @@ -328,14 +305,14 @@ func runCmd(v *viper.Viper, verbose bool, sendDiagsOnErrors bool, cmd cmdFn) (ex
log.Warnf("failed to send diagnostics: %s", err)
}

exitCode = exitcode.ErrGeneric
errresponse = exitcode.Err{Code: exitcode.ErrGeneric}
}
}()

var err error

// run command
exitCode, err = cmd(v)
exitCode, err := cmd(v)
// nolint:nestif
if err != nil {
if errwaka, ok := err.(wakaerror.Error); ok {
Expand All @@ -362,18 +339,28 @@ func runCmd(v *viper.Viper, verbose bool, sendDiagsOnErrors bool, cmd cmdFn) (ex
}
}

return exitCode
if exitCode != exitcode.Success {
log.Debugf("command failed with exit code %d", exitCode)

errresponse = exitcode.Err{Code: exitCode}
}

return errresponse
}

func saveHeartbeats(v *viper.Viper) {
func saveHeartbeats(v *viper.Viper) int {
queueFilepath, err := offline.QueueFilepath()
if err != nil {
log.Warnf("failed to load offline queue filepath: %s", err)
}

if err := cmdoffline.SaveHeartbeats(v, nil, queueFilepath); err != nil {
log.Errorf("failed to save heartbeats to offline queue: %s", err)

return exitcode.ErrGeneric
}

return exitcode.Success
}

func sendDiagnostics(v *viper.Viper, d diagnostics) error {
Expand Down
Loading

0 comments on commit 5d46cab

Please sign in to comment.