Enhance CLI functionality and improve update process
- Add CLI version command - Improve logging and error handling in deploy and push commands - Refactor update command with user prompts and permission checks - Optimize version checking logic - Less verbose timestamps to log outputs - Display image size in MB for better readability - Implement root user detection for update process - Add configuration field check before update
- commit
- cc09a5a15577774ce61390f464bd31404a0f6525
- author
- bamen · · 2024-09-18 08:14 UTC
- committer
- bamen · · 2024-09-18 08:21 UTC
- parent
- f6c3de2
- signature
- unsigned
7 files · +108 -34
M cmd/cli/main.go +6 -2
| @@ -8,6 +8,7 @@ import ( | |||
| 8 | 8 | "github.com/bnema/gordon/internal/cli/cmd" | |
| 9 | 9 | "github.com/bnema/gordon/internal/common" | |
| 10 | 10 | "github.com/bnema/gordon/internal/server" | |
| 11 | + | "github.com/charmbracelet/log" | |
| 11 | 12 | "github.com/spf13/cobra" | |
| 12 | 13 | ||
| 13 | 14 | // env auto load | |
| @@ -29,6 +30,7 @@ func InitializeCommands(client *cli.App, server *server.App) { | |||
| 29 | 30 | rootCmd.AddCommand(cmd.NewDeployCommand(client)) | |
| 30 | 31 | rootCmd.AddCommand(cmd.NewUpdateCommand(client)) | |
| 31 | 32 | rootCmd.AddCommand(cmd.NewPushCommand(client)) | |
| 33 | + | rootCmd.AddCommand(cmd.NewVersionCommand(client)) | |
| 32 | 34 | } | |
| 33 | 35 | ||
| 34 | 36 | func Execute(client *cli.App, server *server.App) { | |
| @@ -59,8 +61,10 @@ func main() { | |||
| 59 | 61 | // Check for new version | |
| 60 | 62 | go func() { | |
| 61 | 63 | msg, err := common.CheckVersionPeriodically(&s.Config) | |
| 62 | - | if err != nil || msg != "" { | |
| 63 | - | // log.Warnf("Error checking for new version: %s", err) | |
| 64 | + | if err != nil { | |
| 65 | + | log.Warnf("Error checking for new version: %s", err) | |
| 66 | + | } else if msg != "" { | |
| 67 | + | log.Info(msg) | |
| 64 | 68 | } | |
| 65 | 69 | }() | |
| 66 | 70 | ||
| @@ -8,6 +8,7 @@ import ( | |||||
| 8 | "github.com/bnema/gordon/internal/cli/cmd" | 8 | "github.com/bnema/gordon/internal/cli/cmd" | ||
| 9 | "github.com/bnema/gordon/internal/common" | 9 | "github.com/bnema/gordon/internal/common" | ||
| 10 | "github.com/bnema/gordon/internal/server" | 10 | "github.com/bnema/gordon/internal/server" | ||
| 11 | + | "github.com/charmbracelet/log" | |||
| 11 | "github.com/spf13/cobra" | 12 | "github.com/spf13/cobra" | ||
| 12 | 13 | ||||
| 13 | // env auto load | 14 | // env auto load | ||
| @@ -29,6 +30,7 @@ func InitializeCommands(client *cli.App, server *server.App) { | |||||
| 29 | rootCmd.AddCommand(cmd.NewDeployCommand(client)) | 30 | rootCmd.AddCommand(cmd.NewDeployCommand(client)) | ||
| 30 | rootCmd.AddCommand(cmd.NewUpdateCommand(client)) | 31 | rootCmd.AddCommand(cmd.NewUpdateCommand(client)) | ||
| 31 | rootCmd.AddCommand(cmd.NewPushCommand(client)) | 32 | rootCmd.AddCommand(cmd.NewPushCommand(client)) | ||
| 33 | + | rootCmd.AddCommand(cmd.NewVersionCommand(client)) | |||
| 32 | } | 34 | } | ||
| 33 | 35 | ||||
| 34 | func Execute(client *cli.App, server *server.App) { | 36 | func Execute(client *cli.App, server *server.App) { | ||
| @@ -59,8 +61,10 @@ func main() { | |||||
| 59 | // Check for new version | 61 | // Check for new version | ||
| 60 | go func() { | 62 | go func() { | ||
| 61 | msg, err := common.CheckVersionPeriodically(&s.Config) | 63 | msg, err := common.CheckVersionPeriodically(&s.Config) | ||
| 62 | - | if err != nil || msg != "" { | 64 | + | if err != nil { |
| 63 | - | // log.Warnf("Error checking for new version: %s", err) | 65 | + | log.Warnf("Error checking for new version: %s", err) |
| 66 | + | } else if msg != "" { | |||
| 67 | + | log.Info(msg) | |||
| 64 | } | 68 | } | ||
| 65 | }() | 69 | }() | ||
| 66 | 70 | ||||
M internal/cli/cmd/deploy.go +8 -1
| @@ -19,6 +19,11 @@ import ( | |||
| 19 | 19 | "github.com/spf13/cobra" | |
| 20 | 20 | ) | |
| 21 | 21 | ||
| 22 | + | func init() { | |
| 23 | + | log.SetReportTimestamp(true) | |
| 24 | + | log.SetTimeFormat("15:04") | |
| 25 | + | } | |
| 26 | + | ||
| 22 | 27 | func NewDeployCommand(a *cli.App) *cobra.Command { | |
| 23 | 28 | var port string | |
| 24 | 29 | var targetDomain string | |
| @@ -49,7 +54,9 @@ func NewDeployCommand(a *cli.App) *cobra.Command { | |||
| 49 | 54 | } | |
| 50 | 55 | defer reader.Close() | |
| 51 | 56 | ||
| 52 | - | log.Info("Image exported successfully", "image", imageName, "size", actualSize) | |
| 57 | + | sizeInMB := float64(actualSize) / 1024 / 1024 | |
| 58 | + | ||
| 59 | + | log.Info("Image exported successfully", "image", imageName, "size", fmt.Sprintf("%.2fMB", sizeInMB)) | |
| 53 | 60 | ||
| 54 | 61 | if err := deployImage(a, reader, imageName, port, targetDomain); err != nil { | |
| 55 | 62 | if deployErr, ok := err.(*common.DeploymentError); ok { | |
| @@ -19,6 +19,11 @@ import ( | |||||
| 19 | "github.com/spf13/cobra" | 19 | "github.com/spf13/cobra" | ||
| 20 | ) | 20 | ) | ||
| 21 | 21 | ||||
| 22 | + | func init() { | |||
| 23 | + | log.SetReportTimestamp(true) | |||
| 24 | + | log.SetTimeFormat("15:04") | |||
| 25 | + | } | |||
| 26 | + | ||||
| 22 | func NewDeployCommand(a *cli.App) *cobra.Command { | 27 | func NewDeployCommand(a *cli.App) *cobra.Command { | ||
| 23 | var port string | 28 | var port string | ||
| 24 | var targetDomain string | 29 | var targetDomain string | ||
| @@ -49,7 +54,9 @@ func NewDeployCommand(a *cli.App) *cobra.Command { | |||||
| 49 | } | 54 | } | ||
| 50 | defer reader.Close() | 55 | defer reader.Close() | ||
| 51 | 56 | ||||
| 52 | - | log.Info("Image exported successfully", "image", imageName, "size", actualSize) | 57 | + | sizeInMB := float64(actualSize) / 1024 / 1024 |
| 58 | + | ||||
| 59 | + | log.Info("Image exported successfully", "image", imageName, "size", fmt.Sprintf("%.2fMB", sizeInMB)) | |||
| 53 | 60 | ||||
| 54 | if err := deployImage(a, reader, imageName, port, targetDomain); err != nil { | 61 | if err := deployImage(a, reader, imageName, port, targetDomain); err != nil { | ||
| 55 | if deployErr, ok := err.(*common.DeploymentError); ok { | 62 | if deployErr, ok := err.(*common.DeploymentError); ok { | ||
M internal/cli/cmd/push.go +8 -1
| @@ -12,6 +12,11 @@ import ( | |||
| 12 | 12 | "github.com/spf13/cobra" | |
| 13 | 13 | ) | |
| 14 | 14 | ||
| 15 | + | func init() { | |
| 16 | + | log.SetReportTimestamp(true) | |
| 17 | + | log.SetTimeFormat("15:04") | |
| 18 | + | } | |
| 19 | + | ||
| 15 | 20 | func NewPushCommand(a *cli.App) *cobra.Command { | |
| 16 | 21 | var port string | |
| 17 | 22 | ||
| @@ -54,7 +59,9 @@ func pushImage(a *cli.App, imageName string) error { | |||
| 54 | 59 | } | |
| 55 | 60 | defer reader.Close() | |
| 56 | 61 | ||
| 57 | - | log.Info("Image exported successfully", "image", imageName, "size", actualSize) | |
| 62 | + | sizeInMB := float64(actualSize) / 1024 / 1024 | |
| 63 | + | ||
| 64 | + | log.Info("Image exported successfully", "image", imageName, "size", fmt.Sprintf("%.2fMB", sizeInMB)) | |
| 58 | 65 | ||
| 59 | 66 | reqPayload := common.RequestPayload{ | |
| 60 | 67 | Type: "push", | |
| @@ -12,6 +12,11 @@ import ( | |||||
| 12 | "github.com/spf13/cobra" | 12 | "github.com/spf13/cobra" | ||
| 13 | ) | 13 | ) | ||
| 14 | 14 | ||||
| 15 | + | func init() { | |||
| 16 | + | log.SetReportTimestamp(true) | |||
| 17 | + | log.SetTimeFormat("15:04") | |||
| 18 | + | } | |||
| 19 | + | ||||
| 15 | func NewPushCommand(a *cli.App) *cobra.Command { | 20 | func NewPushCommand(a *cli.App) *cobra.Command { | ||
| 16 | var port string | 21 | var port string | ||
| 17 | 22 | ||||
| @@ -54,7 +59,9 @@ func pushImage(a *cli.App, imageName string) error { | |||||
| 54 | } | 59 | } | ||
| 55 | defer reader.Close() | 60 | defer reader.Close() | ||
| 56 | 61 | ||||
| 57 | - | log.Info("Image exported successfully", "image", imageName, "size", actualSize) | 62 | + | sizeInMB := float64(actualSize) / 1024 / 1024 |
| 63 | + | ||||
| 64 | + | log.Info("Image exported successfully", "image", imageName, "size", fmt.Sprintf("%.2fMB", sizeInMB)) | |||
| 58 | 65 | ||||
| 59 | reqPayload := common.RequestPayload{ | 66 | reqPayload := common.RequestPayload{ | ||
| 60 | Type: "push", | 67 | Type: "push", | ||
M internal/cli/cmd/update.go +53 -4
| @@ -19,6 +19,7 @@ import ( | |||
| 19 | 19 | ||
| 20 | 20 | "github.com/AlecAivazis/survey/v2" | |
| 21 | 21 | "github.com/bnema/gordon/internal/cli" | |
| 22 | + | "github.com/bnema/gordon/internal/cli/handler" | |
| 22 | 23 | "github.com/bnema/gordon/pkg/docker" | |
| 23 | 24 | "github.com/fatih/color" | |
| 24 | 25 | "github.com/pocketbase/pocketbase/tools/archive" | |
| @@ -76,7 +77,7 @@ func NewUpdateCommand(a *cli.App) *cobra.Command { | |||
| 76 | 77 | var needConfirm bool | |
| 77 | 78 | if docker.IsRunningInContainer() { | |
| 78 | 79 | needConfirm = true | |
| 79 | - | color.Yellow("NB! It seems that you are in a Docker container.") | |
| 80 | + | color.Yellow("It seems that you are in a Docker container.") | |
| 80 | 81 | color.Yellow("The update command may not work as expected in this context because usually the version of the app is managed by the container image itself.") | |
| 81 | 82 | } | |
| 82 | 83 | ||
| @@ -92,6 +93,17 @@ func NewUpdateCommand(a *cli.App) *cobra.Command { | |||
| 92 | 93 | } | |
| 93 | 94 | } | |
| 94 | 95 | ||
| 96 | + | // Vérifier si l'utilisateur est root | |
| 97 | + | if isRoot() { | |
| 98 | + | color.Yellow("Running update as root. Skipping configuration check.") | |
| 99 | + | return pluginInstance.update() | |
| 100 | + | } | |
| 101 | + | ||
| 102 | + | // Si non root, effectuer la vérification des champs | |
| 103 | + | if err := handler.FieldCheck(a); err != nil { | |
| 104 | + | return fmt.Errorf("field check failed: %w", err) | |
| 105 | + | } | |
| 106 | + | ||
| 95 | 107 | return pluginInstance.update() | |
| 96 | 108 | }, | |
| 97 | 109 | } | |
| @@ -116,6 +128,17 @@ func (p *plugin) update() error { | |||
| 116 | 128 | return nil | |
| 117 | 129 | } | |
| 118 | 130 | ||
| 131 | + | // Prompt user for update | |
| 132 | + | confirm := false | |
| 133 | + | prompt := &survey.Confirm{ | |
| 134 | + | Message: fmt.Sprintf("A new version (%s) is available. Do you want to update?", latest.Tag), | |
| 135 | + | } | |
| 136 | + | survey.AskOne(prompt, &confirm) | |
| 137 | + | if !confirm { | |
| 138 | + | fmt.Println("Update cancelled by user.") | |
| 139 | + | return nil | |
| 140 | + | } | |
| 141 | + | ||
| 119 | 142 | suffix := archiveSuffix(runtime.GOOS, runtime.GOARCH) | |
| 120 | 143 | if suffix == "" { | |
| 121 | 144 | return errors.New("unsupported platform") | |
| @@ -156,18 +179,26 @@ func (p *plugin) update() error { | |||
| 156 | 179 | renamedOldExec := oldExec + ".old" | |
| 157 | 180 | defer os.Remove(renamedOldExec) | |
| 158 | 181 | ||
| 182 | + | // Check if the user has write permissions | |
| 183 | + | if err := checkWritePermission(oldExec); err != nil { | |
| 184 | + | color.Yellow("Administrator rights are required to perform the update.") | |
| 185 | + | color.Yellow("Please run the following command with sudo:") | |
| 186 | + | color.HiBlack("sudo gordon update") | |
| 187 | + | return nil | |
| 188 | + | } | |
| 189 | + | ||
| 159 | 190 | newExec := filepath.Join(extractDir, p.config.ArchiveExecutable) | |
| 160 | 191 | if _, err := os.Stat(newExec); err != nil { | |
| 161 | 192 | // try again with an .exe extension | |
| 162 | 193 | newExec = newExec + ".exe" | |
| 163 | 194 | if _, fallbackErr := os.Stat(newExec); fallbackErr != nil { | |
| 164 | - | return fmt.Errorf("The executable in the extracted path is missing or it is inaccessible: %v, %v", err, fallbackErr) | |
| 195 | + | return fmt.Errorf("the executable in the extracted path is missing or it is inaccessible: %v, %v", err, fallbackErr) | |
| 165 | 196 | } | |
| 166 | 197 | } | |
| 167 | 198 | ||
| 168 | 199 | // rename the current executable | |
| 169 | 200 | if err := os.Rename(oldExec, renamedOldExec); err != nil { | |
| 170 | - | return fmt.Errorf("Failed to rename the current executable: %w", err) | |
| 201 | + | return fmt.Errorf("failed to rename the current executable: %w", err) | |
| 171 | 202 | } | |
| 172 | 203 | ||
| 173 | 204 | tryToRevertExecChanges := func() { | |
| @@ -179,7 +210,7 @@ func (p *plugin) update() error { | |||
| 179 | 210 | // replace with the extracted binary | |
| 180 | 211 | if err := os.Rename(newExec, oldExec); err != nil { | |
| 181 | 212 | tryToRevertExecChanges() | |
| 182 | - | return fmt.Errorf("Failed replacing the executable: %w", err) | |
| 213 | + | return fmt.Errorf("failed replacing the executable: %w", err) | |
| 183 | 214 | } | |
| 184 | 215 | ||
| 185 | 216 | color.HiBlack("---") | |
| @@ -355,3 +386,21 @@ func compareVersions(a, b string) int { | |||
| 355 | 386 | ||
| 356 | 387 | return 0 // equal | |
| 357 | 388 | } | |
| 389 | + | ||
| 390 | + | // New function to check write permissions | |
| 391 | + | func checkWritePermission(path string) error { | |
| 392 | + | // Try to create a temporary file in the same directory | |
| 393 | + | dir := filepath.Dir(path) | |
| 394 | + | tmpFile := filepath.Join(dir, ".gordon_update_test") | |
| 395 | + | f, err := os.Create(tmpFile) | |
| 396 | + | if err != nil { | |
| 397 | + | return err | |
| 398 | + | } | |
| 399 | + | f.Close() | |
| 400 | + | os.Remove(tmpFile) | |
| 401 | + | return nil | |
| 402 | + | } | |
| 403 | + | ||
| 404 | + | func isRoot() bool { | |
| 405 | + | return os.Geteuid() == 0 | |
| 406 | + | } | |
| @@ -19,6 +19,7 @@ import ( | |||||
| 19 | 19 | ||||
| 20 | "github.com/AlecAivazis/survey/v2" | 20 | "github.com/AlecAivazis/survey/v2" | ||
| 21 | "github.com/bnema/gordon/internal/cli" | 21 | "github.com/bnema/gordon/internal/cli" | ||
| 22 | + | "github.com/bnema/gordon/internal/cli/handler" | |||
| 22 | "github.com/bnema/gordon/pkg/docker" | 23 | "github.com/bnema/gordon/pkg/docker" | ||
| 23 | "github.com/fatih/color" | 24 | "github.com/fatih/color" | ||
| 24 | "github.com/pocketbase/pocketbase/tools/archive" | 25 | "github.com/pocketbase/pocketbase/tools/archive" | ||
| @@ -76,7 +77,7 @@ func NewUpdateCommand(a *cli.App) *cobra.Command { | |||||
| 76 | var needConfirm bool | 77 | var needConfirm bool | ||
| 77 | if docker.IsRunningInContainer() { | 78 | if docker.IsRunningInContainer() { | ||
| 78 | needConfirm = true | 79 | needConfirm = true | ||
| 79 | - | color.Yellow("NB! It seems that you are in a Docker container.") | 80 | + | color.Yellow("It seems that you are in a Docker container.") |
| 80 | color.Yellow("The update command may not work as expected in this context because usually the version of the app is managed by the container image itself.") | 81 | color.Yellow("The update command may not work as expected in this context because usually the version of the app is managed by the container image itself.") | ||
| 81 | } | 82 | } | ||
| 82 | 83 | ||||
| @@ -92,6 +93,17 @@ func NewUpdateCommand(a *cli.App) *cobra.Command { | |||||
| 92 | } | 93 | } | ||
| 93 | } | 94 | } | ||
| 94 | 95 | ||||
| 96 | + | // Vérifier si l'utilisateur est root | |||
| 97 | + | if isRoot() { | |||
| 98 | + | color.Yellow("Running update as root. Skipping configuration check.") | |||
| 99 | + | return pluginInstance.update() | |||
| 100 | + | } | |||
| 101 | + | ||||
| 102 | + | // Si non root, effectuer la vérification des champs | |||
| 103 | + | if err := handler.FieldCheck(a); err != nil { | |||
| 104 | + | return fmt.Errorf("field check failed: %w", err) | |||
| 105 | + | } | |||
| 106 | + | ||||
| 95 | return pluginInstance.update() | 107 | return pluginInstance.update() | ||
| 96 | }, | 108 | }, | ||
| 97 | } | 109 | } | ||
| @@ -116,6 +128,17 @@ func (p *plugin) update() error { | |||||
| 116 | return nil | 128 | return nil | ||
| 117 | } | 129 | } | ||
| 118 | 130 | ||||
| 131 | + | // Prompt user for update | |||
| 132 | + | confirm := false | |||
| 133 | + | prompt := &survey.Confirm{ | |||
| 134 | + | Message: fmt.Sprintf("A new version (%s) is available. Do you want to update?", latest.Tag), | |||
| 135 | + | } | |||
| 136 | + | survey.AskOne(prompt, &confirm) | |||
| 137 | + | if !confirm { | |||
| 138 | + | fmt.Println("Update cancelled by user.") | |||
| 139 | + | return nil | |||
| 140 | + | } | |||
| 141 | + | ||||
| 119 | suffix := archiveSuffix(runtime.GOOS, runtime.GOARCH) | 142 | suffix := archiveSuffix(runtime.GOOS, runtime.GOARCH) | ||
| 120 | if suffix == "" { | 143 | if suffix == "" { | ||
| 121 | return errors.New("unsupported platform") | 144 | return errors.New("unsupported platform") | ||
| @@ -156,18 +179,26 @@ func (p *plugin) update() error { | |||||
| 156 | renamedOldExec := oldExec + ".old" | 179 | renamedOldExec := oldExec + ".old" | ||
| 157 | defer os.Remove(renamedOldExec) | 180 | defer os.Remove(renamedOldExec) | ||
| 158 | 181 | ||||
| 182 | + | // Check if the user has write permissions | |||
| 183 | + | if err := checkWritePermission(oldExec); err != nil { | |||
| 184 | + | color.Yellow("Administrator rights are required to perform the update.") | |||
| 185 | + | color.Yellow("Please run the following command with sudo:") | |||
| 186 | + | color.HiBlack("sudo gordon update") | |||
| 187 | + | return nil | |||
| 188 | + | } | |||
| 189 | + | ||||
| 159 | newExec := filepath.Join(extractDir, p.config.ArchiveExecutable) | 190 | newExec := filepath.Join(extractDir, p.config.ArchiveExecutable) | ||
| 160 | if _, err := os.Stat(newExec); err != nil { | 191 | if _, err := os.Stat(newExec); err != nil { | ||
| 161 | // try again with an .exe extension | 192 | // try again with an .exe extension | ||
| 162 | newExec = newExec + ".exe" | 193 | newExec = newExec + ".exe" | ||
| 163 | if _, fallbackErr := os.Stat(newExec); fallbackErr != nil { | 194 | if _, fallbackErr := os.Stat(newExec); fallbackErr != nil { | ||
| 164 | - | return fmt.Errorf("The executable in the extracted path is missing or it is inaccessible: %v, %v", err, fallbackErr) | 195 | + | return fmt.Errorf("the executable in the extracted path is missing or it is inaccessible: %v, %v", err, fallbackErr) |
| 165 | } | 196 | } | ||
| 166 | } | 197 | } | ||
| 167 | 198 | ||||
| 168 | // rename the current executable | 199 | // rename the current executable | ||
| 169 | if err := os.Rename(oldExec, renamedOldExec); err != nil { | 200 | if err := os.Rename(oldExec, renamedOldExec); err != nil { | ||
| 170 | - | return fmt.Errorf("Failed to rename the current executable: %w", err) | 201 | + | return fmt.Errorf("failed to rename the current executable: %w", err) |
| 171 | } | 202 | } | ||
| 172 | 203 | ||||
| 173 | tryToRevertExecChanges := func() { | 204 | tryToRevertExecChanges := func() { | ||
| @@ -179,7 +210,7 @@ func (p *plugin) update() error { | |||||
| 179 | // replace with the extracted binary | 210 | // replace with the extracted binary | ||
| 180 | if err := os.Rename(newExec, oldExec); err != nil { | 211 | if err := os.Rename(newExec, oldExec); err != nil { | ||
| 181 | tryToRevertExecChanges() | 212 | tryToRevertExecChanges() | ||
| 182 | - | return fmt.Errorf("Failed replacing the executable: %w", err) | 213 | + | return fmt.Errorf("failed replacing the executable: %w", err) |
| 183 | } | 214 | } | ||
| 184 | 215 | ||||
| 185 | color.HiBlack("---") | 216 | color.HiBlack("---") | ||
| @@ -355,3 +386,21 @@ func compareVersions(a, b string) int { | |||||
| 355 | 386 | ||||
| 356 | return 0 // equal | 387 | return 0 // equal | ||
| 357 | } | 388 | } | ||
| 389 | + | ||||
| 390 | + | // New function to check write permissions | |||
| 391 | + | func checkWritePermission(path string) error { | |||
| 392 | + | // Try to create a temporary file in the same directory | |||
| 393 | + | dir := filepath.Dir(path) | |||
| 394 | + | tmpFile := filepath.Join(dir, ".gordon_update_test") | |||
| 395 | + | f, err := os.Create(tmpFile) | |||
| 396 | + | if err != nil { | |||
| 397 | + | return err | |||
| 398 | + | } | |||
| 399 | + | f.Close() | |||
| 400 | + | os.Remove(tmpFile) | |||
| 401 | + | return nil | |||
| 402 | + | } | |||
| 403 | + | ||||
| 404 | + | func isRoot() bool { | |||
| 405 | + | return os.Geteuid() == 0 | |||
| 406 | + | } | |||
A internal/cli/cmd/version.go +23 -0
| @@ -0,0 +1,23 @@ | |||
| 1 | + | package cmd | |
| 2 | + | ||
| 3 | + | import ( | |
| 4 | + | "fmt" | |
| 5 | + | ||
| 6 | + | "github.com/bnema/gordon/internal/cli" | |
| 7 | + | "github.com/spf13/cobra" | |
| 8 | + | ) | |
| 9 | + | ||
| 10 | + | func NewVersionCommand(a *cli.App) *cobra.Command { | |
| 11 | + | return &cobra.Command{ | |
| 12 | + | Use: "version", | |
| 13 | + | Short: "Affiche la version de Gordon", | |
| 14 | + | Run: func(cmd *cobra.Command, args []string) { | |
| 15 | + | version := a.Config.GetVersion() | |
| 16 | + | if version == "" { | |
| 17 | + | fmt.Println("Version : devel") | |
| 18 | + | } else { | |
| 19 | + | fmt.Printf("Version : %s\n", version) | |
| 20 | + | } | |
| 21 | + | }, | |
| 22 | + | } | |
| 23 | + | } | |
| @@ -0,0 +1,23 @@ | |||||
| 1 | + | package cmd | |||
| 2 | + | ||||
| 3 | + | import ( | |||
| 4 | + | "fmt" | |||
| 5 | + | ||||
| 6 | + | "github.com/bnema/gordon/internal/cli" | |||
| 7 | + | "github.com/spf13/cobra" | |||
| 8 | + | ) | |||
| 9 | + | ||||
| 10 | + | func NewVersionCommand(a *cli.App) *cobra.Command { | |||
| 11 | + | return &cobra.Command{ | |||
| 12 | + | Use: "version", | |||
| 13 | + | Short: "Affiche la version de Gordon", | |||
| 14 | + | Run: func(cmd *cobra.Command, args []string) { | |||
| 15 | + | version := a.Config.GetVersion() | |||
| 16 | + | if version == "" { | |||
| 17 | + | fmt.Println("Version : devel") | |||
| 18 | + | } else { | |||
| 19 | + | fmt.Printf("Version : %s\n", version) | |||
| 20 | + | } | |||
| 21 | + | }, | |||
| 22 | + | } | |||
| 23 | + | } | |||
M internal/cli/newclient.go +1 -0
| @@ -16,6 +16,7 @@ func NewClientApp(buildConfig *common.BuildConfig) (*App, error) { | |||
| 16 | 16 | config := common.Config{ | |
| 17 | 17 | Build: *buildConfig, | |
| 18 | 18 | } | |
| 19 | + | ||
| 19 | 20 | _, err := config.LoadConfig() | |
| 20 | 21 | if err != nil { | |
| 21 | 22 | return nil, fmt.Errorf("failed to load configuration: %w", err) | |
| @@ -16,6 +16,7 @@ func NewClientApp(buildConfig *common.BuildConfig) (*App, error) { | |||||
| 16 | config := common.Config{ | 16 | config := common.Config{ | ||
| 17 | Build: *buildConfig, | 17 | Build: *buildConfig, | ||
| 18 | } | 18 | } | ||
| 19 | + | ||||
| 19 | _, err := config.LoadConfig() | 20 | _, err := config.LoadConfig() | ||
| 20 | if err != nil { | 21 | if err != nil { | ||
| 21 | return nil, fmt.Errorf("failed to load configuration: %w", err) | 22 | return nil, fmt.Errorf("failed to load configuration: %w", err) | ||
M internal/common/version.go +9 -26
| @@ -7,11 +7,9 @@ import ( | |||
| 7 | 7 | "log" | |
| 8 | 8 | "net/http" | |
| 9 | 9 | "runtime" | |
| 10 | - | "strings" | |
| 11 | 10 | "time" | |
| 12 | 11 | ||
| 13 | 12 | "github.com/bnema/gordon/pkg/docker" | |
| 14 | - | "github.com/fatih/color" | |
| 15 | 13 | ) | |
| 16 | 14 | ||
| 17 | 15 | type CurrentVersion struct { | |
| @@ -28,9 +26,8 @@ type VersionInfo struct { | |||
| 28 | 26 | } | |
| 29 | 27 | ||
| 30 | 28 | type VersionsResponse struct { | |
| 31 | - | Name string `json:"name"` | |
| 32 | - | AMD64 VersionInfo `json:"amd64"` | |
| 33 | - | ARM64 VersionInfo `json:"arm64"` | |
| 29 | + | AMD64 string `json:"amd64"` | |
| 30 | + | ARM64 string `json:"arm64"` | |
| 34 | 31 | } | |
| 35 | 32 | ||
| 36 | 33 | func GetCurrentBuildVersionInfo(c *Config) CurrentVersion { | |
| @@ -61,35 +58,22 @@ func checkAndUpdateVersion(c *Config) (string, error) { | |||
| 61 | 58 | var remoteVersion string | |
| 62 | 59 | switch getArch() { | |
| 63 | 60 | case "amd64": | |
| 64 | - | remoteVersion = remoteVersions.AMD64.Name | |
| 61 | + | remoteVersion = remoteVersions.AMD64 | |
| 65 | 62 | case "arm64": | |
| 66 | - | remoteVersion = remoteVersions.ARM64.Name | |
| 63 | + | remoteVersion = remoteVersions.ARM64 | |
| 67 | 64 | default: | |
| 68 | - | remoteVersion = remoteVersions.Name | |
| 65 | + | return "", fmt.Errorf("unsupported architecture: %s", getArch()) | |
| 69 | 66 | } | |
| 70 | 67 | ||
| 71 | - | // Check if remoteVersion is empty or doesn't contain the architecture suffix | |
| 72 | - | if remoteVersion == "" || !strings.Contains(remoteVersion, getArch()) { | |
| 68 | + | // Check if remoteVersion is empty | |
| 69 | + | if remoteVersion == "" { | |
| 73 | 70 | return "", fmt.Errorf("remote version not available for architecture %s", getArch()) | |
| 74 | 71 | } | |
| 75 | 72 | ||
| 76 | - | remoteVersion = remoteVersion[:len(remoteVersion)-len(getArch())-1] | |
| 77 | 73 | message := CheckForNewVersion(localVersion.Version, remoteVersion) | |
| 78 | 74 | ||
| 79 | - | // if local version is empty that means dev mode is on so we don't need to check for updates | |
| 80 | - | if localVersion.Version == "" { | |
| 81 | - | return "", nil | |
| 82 | - | } | |
| 83 | - | ||
| 84 | - | if message != "" { | |
| 85 | - | // Create a new color attribute | |
| 86 | - | green := color.New(color.FgGreen).SprintFunc() | |
| 87 | - | // Use the color attribute to format the string with ANSI codes | |
| 88 | - | coloredMessage := green(fmt.Sprintf("New version %s is available", remoteVersion)) | |
| 89 | - | return coloredMessage, nil | |
| 90 | - | } | |
| 91 | - | ||
| 92 | - | return "", nil | |
| 75 | + | // Return the message without an error, even if it's empty | |
| 76 | + | return message, nil | |
| 93 | 77 | } | |
| 94 | 78 | ||
| 95 | 79 | func CheckVersionPeriodically(c *Config) (string, error) { | |
| @@ -151,5 +135,4 @@ func getRemoteVersion(c *Config) (VersionsResponse, error) { | |||
| 151 | 135 | } | |
| 152 | 136 | ||
| 153 | 137 | return remoteVersionInfo, nil | |
| 154 | - | ||
| 155 | 138 | } | |
| @@ -7,11 +7,9 @@ import ( | |||||
| 7 | "log" | 7 | "log" | ||
| 8 | "net/http" | 8 | "net/http" | ||
| 9 | "runtime" | 9 | "runtime" | ||
| 10 | - | "strings" | |||
| 11 | "time" | 10 | "time" | ||
| 12 | 11 | ||||
| 13 | "github.com/bnema/gordon/pkg/docker" | 12 | "github.com/bnema/gordon/pkg/docker" | ||
| 14 | - | "github.com/fatih/color" | |||
| 15 | ) | 13 | ) | ||
| 16 | 14 | ||||
| 17 | type CurrentVersion struct { | 15 | type CurrentVersion struct { | ||
| @@ -28,9 +26,8 @@ type VersionInfo struct { | |||||
| 28 | } | 26 | } | ||
| 29 | 27 | ||||
| 30 | type VersionsResponse struct { | 28 | type VersionsResponse struct { | ||
| 31 | - | Name string `json:"name"` | 29 | + | AMD64 string `json:"amd64"` |
| 32 | - | AMD64 VersionInfo `json:"amd64"` | 30 | + | ARM64 string `json:"arm64"` |
| 33 | - | ARM64 VersionInfo `json:"arm64"` | |||
| 34 | } | 31 | } | ||
| 35 | 32 | ||||
| 36 | func GetCurrentBuildVersionInfo(c *Config) CurrentVersion { | 33 | func GetCurrentBuildVersionInfo(c *Config) CurrentVersion { | ||
| @@ -61,35 +58,22 @@ func checkAndUpdateVersion(c *Config) (string, error) { | |||||
| 61 | var remoteVersion string | 58 | var remoteVersion string | ||
| 62 | switch getArch() { | 59 | switch getArch() { | ||
| 63 | case "amd64": | 60 | case "amd64": | ||
| 64 | - | remoteVersion = remoteVersions.AMD64.Name | 61 | + | remoteVersion = remoteVersions.AMD64 |
| 65 | case "arm64": | 62 | case "arm64": | ||
| 66 | - | remoteVersion = remoteVersions.ARM64.Name | 63 | + | remoteVersion = remoteVersions.ARM64 |
| 67 | default: | 64 | default: | ||
| 68 | - | remoteVersion = remoteVersions.Name | 65 | + | return "", fmt.Errorf("unsupported architecture: %s", getArch()) |
| 69 | } | 66 | } | ||
| 70 | 67 | ||||
| 71 | - | // Check if remoteVersion is empty or doesn't contain the architecture suffix | 68 | + | // Check if remoteVersion is empty |
| 72 | - | if remoteVersion == "" || !strings.Contains(remoteVersion, getArch()) { | 69 | + | if remoteVersion == "" { |
| 73 | return "", fmt.Errorf("remote version not available for architecture %s", getArch()) | 70 | return "", fmt.Errorf("remote version not available for architecture %s", getArch()) | ||
| 74 | } | 71 | } | ||
| 75 | 72 | ||||
| 76 | - | remoteVersion = remoteVersion[:len(remoteVersion)-len(getArch())-1] | |||
| 77 | message := CheckForNewVersion(localVersion.Version, remoteVersion) | 73 | message := CheckForNewVersion(localVersion.Version, remoteVersion) | ||
| 78 | 74 | ||||
| 79 | - | // if local version is empty that means dev mode is on so we don't need to check for updates | 75 | + | // Return the message without an error, even if it's empty |
| 80 | - | if localVersion.Version == "" { | 76 | + | return message, nil |
| 81 | - | return "", nil | |||
| 82 | - | } | |||
| 83 | - | ||||
| 84 | - | if message != "" { | |||
| 85 | - | // Create a new color attribute | |||
| 86 | - | green := color.New(color.FgGreen).SprintFunc() | |||
| 87 | - | // Use the color attribute to format the string with ANSI codes | |||
| 88 | - | coloredMessage := green(fmt.Sprintf("New version %s is available", remoteVersion)) | |||
| 89 | - | return coloredMessage, nil | |||
| 90 | - | } | |||
| 91 | - | ||||
| 92 | - | return "", nil | |||
| 93 | } | 77 | } | ||
| 94 | 78 | ||||
| 95 | func CheckVersionPeriodically(c *Config) (string, error) { | 79 | func CheckVersionPeriodically(c *Config) (string, error) { | ||
| @@ -151,5 +135,4 @@ func getRemoteVersion(c *Config) (VersionsResponse, error) { | |||||
| 151 | } | 135 | } | ||
| 152 | 136 | ||||
| 153 | return remoteVersionInfo, nil | 137 | return remoteVersionInfo, nil | ||
| 154 | - | ||||
| 155 | } | 138 | } | ||