From 92696bba5bd0a3f487fdbea604c3760fd5d53533 Mon Sep 17 00:00:00 2001 From: zulkhair Date: Wed, 9 Oct 2024 14:46:15 +0700 Subject: [PATCH] fix sentry version and message --- .goreleaser.yaml | 2 ++ cmd/world/cardinal/cardinal.go | 17 ++++++++++- cmd/world/cardinal/dev.go | 2 +- cmd/world/cardinal/purge.go | 2 +- cmd/world/cardinal/restart.go | 10 ++++-- cmd/world/cardinal/start.go | 16 +++------- cmd/world/cardinal/stop.go | 2 +- cmd/world/evm/start.go | 12 ++++---- cmd/world/evm/stop.go | 2 +- cmd/world/main.go | 16 +++++++--- cmd/world/root/doctor.go | 1 - cmd/world/root/root.go | 8 +++-- cmd/world/root/root_test.go | 17 +++++------ cmd/world/root/version.go | 6 +++- common/config/config.go | 36 +++++++++++----------- common/config/config_test.go | 54 ++++++++++++++++++++------------- common/dependency/dependency.go | 11 ++----- common/docker/client_image.go | 5 ++- common/docker/client_test.go | 1 + common/editor/editor.go | 11 +++---- common/port.go | 4 ++- common/teacmd/docker.go | 9 +++--- telemetry/sentry.go | 33 +++----------------- 23 files changed, 142 insertions(+), 135 deletions(-) diff --git a/.goreleaser.yaml b/.goreleaser.yaml index a171694..348126c 100644 --- a/.goreleaser.yaml +++ b/.goreleaser.yaml @@ -17,11 +17,13 @@ builds: - CGO_ENABLED=0 - SENTRY_DSN={{ if index .Env "SENTRY_DSN" }}{{ .Env.SENTRY_DSN }}{{ else }}default_null{{ end }} - POSTHOG_API_KEY={{ if index .Env "POSTHOG_API_KEY" }}{{ .Env.POSTHOG_API_KEY }}{{ else }}default_null{{ end }} + - BUILD_ENV={{ if index .Env "BUILD_ENV" }}{{ .Env.BUILD_ENV }}{{ else }}DEV{{ end }} ldflags: - -s -w - -X main.AppVersion={{.Version}} - -X main.SentryDsn={{.Env.SENTRY_DSN}} - -X main.PosthogAPIKey={{.Env.POSTHOG_API_KEY}} + - -X main.Env={{.Env.BUILD_ENV}} goos: - linux - windows diff --git a/cmd/world/cardinal/cardinal.go b/cmd/world/cardinal/cardinal.go index fbeeb7f..a12390a 100644 --- a/cmd/world/cardinal/cardinal.go +++ b/cmd/world/cardinal/cardinal.go @@ -3,7 +3,9 @@ package cardinal import ( "github.com/spf13/cobra" + "pkg.world.dev/world-cli/common/config" "pkg.world.dev/world-cli/common/dependency" + "pkg.world.dev/world-cli/common/docker/service" "pkg.world.dev/world-cli/common/logger" "pkg.world.dev/world-cli/tea/style" ) @@ -23,10 +25,12 @@ var BaseCmd = &cobra.Command{ dependency.DockerDaemon, ) }, - Run: func(cmd *cobra.Command, _ []string) { + RunE: func(cmd *cobra.Command, _ []string) error { if err := cmd.Help(); err != nil { logger.Fatalf("Failed to execute cardinal command : %s", err.Error()) + return err } + return nil }, } @@ -34,3 +38,14 @@ func init() { // Register subcommands - `world cardinal [subcommand]` BaseCmd.AddCommand(startCmd, devCmd, restartCmd, purgeCmd, stopCmd) } + +func getServices(cfg *config.Config) []service.Builder { + services := []service.Builder{service.NakamaDB, service.Redis, service.Cardinal, service.Nakama} + if cfg.Telemetry && cfg.DockerEnv["NAKAMA_TRACE_ENABLED"] == "true" { + services = append(services, service.Jaeger) + } + if cfg.Telemetry && cfg.DockerEnv["NAKAMA_METRICS_ENABLED"] == "true" { + services = append(services, service.Prometheus) + } + return services +} diff --git a/cmd/world/cardinal/dev.go b/cmd/world/cardinal/dev.go index 0583626..4adbf3b 100644 --- a/cmd/world/cardinal/dev.go +++ b/cmd/world/cardinal/dev.go @@ -53,7 +53,7 @@ var devCmd = &cobra.Command{ return err } - cfg, err := config.GetConfig(cmd) + cfg, err := config.GetConfig() if err != nil { return err } diff --git a/cmd/world/cardinal/purge.go b/cmd/world/cardinal/purge.go index 52785ca..3b36e84 100644 --- a/cmd/world/cardinal/purge.go +++ b/cmd/world/cardinal/purge.go @@ -22,7 +22,7 @@ var purgeCmd = &cobra.Command{ Long: `Stop and reset the state of your Cardinal game shard. This command stop all Docker services and remove all Docker volumes.`, RunE: func(cmd *cobra.Command, _ []string) error { - cfg, err := config.GetConfig(cmd) + cfg, err := config.GetConfig() if err != nil { return err } diff --git a/cmd/world/cardinal/restart.go b/cmd/world/cardinal/restart.go index 8222f0b..f9c866b 100644 --- a/cmd/world/cardinal/restart.go +++ b/cmd/world/cardinal/restart.go @@ -5,7 +5,6 @@ import ( "pkg.world.dev/world-cli/common/config" "pkg.world.dev/world-cli/common/docker" - "pkg.world.dev/world-cli/common/docker/service" ) // restartCmd restarts your Cardinal game shard stack @@ -19,7 +18,7 @@ This will restart the following Docker services: - Cardinal (Core game logic) - Nakama (Relay)`, RunE: func(cmd *cobra.Command, _ []string) error { - cfg, err := config.GetConfig(cmd) + cfg, err := config.GetConfig() if err != nil { return err } @@ -39,7 +38,7 @@ This will restart the following Docker services: } defer dockerClient.Close() - err = dockerClient.Restart(cmd.Context(), service.Cardinal, service.Nakama) + err = dockerClient.Restart(cmd.Context(), getServices(cfg)...) if err != nil { return err } @@ -53,5 +52,10 @@ This will restart the following Docker services: ///////////////// func init() { + registerEditorFlag(restartCmd, true) + restartCmd.Flags().Bool(flagBuild, true, "Rebuild Docker images before starting") restartCmd.Flags().Bool(flagDetach, false, "Run in detached mode") + restartCmd.Flags().String(flagLogLevel, "", "Set the log level") + restartCmd.Flags().Bool(flagDebug, false, "Enable delve debugging") + restartCmd.Flags().Bool(flagTelemetry, false, "Enable tracing, metrics, and profiling") } diff --git a/cmd/world/cardinal/start.go b/cmd/world/cardinal/start.go index 779a37a..2e855e4 100644 --- a/cmd/world/cardinal/start.go +++ b/cmd/world/cardinal/start.go @@ -12,7 +12,6 @@ import ( "pkg.world.dev/world-cli/common" "pkg.world.dev/world-cli/common/config" "pkg.world.dev/world-cli/common/docker" - "pkg.world.dev/world-cli/common/docker/service" ) ///////////////// @@ -59,7 +58,7 @@ This will start the following Docker services and its dependencies: - Nakama (Relay) - Redis (Cardinal dependency)`, RunE: func(cmd *cobra.Command, _ []string) error { - cfg, err := config.GetConfig(cmd) + cfg, err := config.GetConfig() if err != nil { return err } @@ -90,7 +89,7 @@ This will start the following Docker services and its dependencies: if logLevel != "" { zeroLogLevel, err := zerolog.ParseLevel(logLevel) if err != nil { - return fmt.Errorf("invalid value for flag %s: must be one of (%v)", flagLogLevel, validLogLevels) + return eris.Errorf("invalid value for flag %s: must be one of (%v)", flagLogLevel, validLogLevels) } cfg.DockerEnv[DockerCardinalEnvLogLevel] = zeroLogLevel.String() } @@ -101,7 +100,7 @@ This will start the following Docker services and its dependencies: } else if _, err := zerolog.ParseLevel(cfg.DockerEnv[DockerCardinalEnvLogLevel]); err != nil { // make sure the log level is valid when the flag is not set and using env var from config // Error when CARDINAL_LOG_LEVEL is not a valid log level - return fmt.Errorf("invalid value for %s env variable in the config file: must be one of (%v)", + return eris.Errorf("invalid value for %s env variable in the config file: must be one of (%v)", DockerCardinalEnvLogLevel, validLogLevels) } @@ -125,14 +124,7 @@ This will start the following Docker services and its dependencies: // Start the World Engine stack group.Go(func() error { - services := []service.Builder{service.NakamaDB, service.Redis, service.Cardinal, service.Nakama} - if cfg.Telemetry && cfg.DockerEnv["NAKAMA_TRACE_ENABLED"] == "true" { - services = append(services, service.Jaeger) - } - if cfg.Telemetry && cfg.DockerEnv["NAKAMA_METRICS_ENABLED"] == "true" { - services = append(services, service.Prometheus) - } - if err := dockerClient.Start(ctx, services...); err != nil { + if err := dockerClient.Start(ctx, getServices(cfg)...); err != nil { return eris.Wrap(err, "Encountered an error with Docker") } return eris.Wrap(ErrGracefulExit, "Stack terminated") diff --git a/cmd/world/cardinal/stop.go b/cmd/world/cardinal/stop.go index 5fb7533..d727fdb 100644 --- a/cmd/world/cardinal/stop.go +++ b/cmd/world/cardinal/stop.go @@ -26,7 +26,7 @@ This will stop the following Docker services: - Nakama (Relay) + DB - Redis (Cardinal dependency)`, RunE: func(cmd *cobra.Command, _ []string) error { - cfg, err := config.GetConfig(cmd) + cfg, err := config.GetConfig() if err != nil { return err } diff --git a/cmd/world/evm/start.go b/cmd/world/evm/start.go index d6f9c23..3064511 100644 --- a/cmd/world/evm/start.go +++ b/cmd/world/evm/start.go @@ -21,7 +21,7 @@ var startCmd = &cobra.Command{ Short: "Start the EVM base shard. Use --da-auth-token to pass in an auth token directly.", Long: "Start the EVM base shard. Requires connection to celestia DA.", RunE: func(cmd *cobra.Command, _ []string) error { - cfg, err := config.GetConfig(cmd) + cfg, err := config.GetConfig() if err != nil { return err } @@ -52,7 +52,7 @@ var startCmd = &cobra.Command{ err = dockerClient.Start(cmd.Context(), service.EVM) if err != nil { - return fmt.Errorf("error starting %s docker container: %w", teacmd.DockerServiceEVM, err) + return eris.Wrapf(err, "error starting %s docker container", teacmd.DockerServiceEVM) } // Stop the DA service if it was started in dev mode @@ -82,7 +82,7 @@ func validateDevDALayer(ctx context.Context, cfg *config.Config, dockerClient *d cfg.Timeout = -1 logger.Println("starting DA docker service for dev mode...") if err := dockerClient.Start(ctx, service.CelestiaDevNet); err != nil { - return fmt.Errorf("error starting %s docker container: %w", daService, err) + return eris.Wrapf(err, "error starting %s docker container", daService) } logger.Println("started DA service...") @@ -115,12 +115,12 @@ func validateProdDALayer(cfg *config.Config) error { if len(cfg.DockerEnv[env]) > 0 { continue } - errs = append(errs, fmt.Errorf("missing %q", env)) + errs = append(errs, eris.Errorf("missing %q", env)) } if len(errs) > 0 { // Prepend an error describing the overall problem errs = append([]error{ - errors.New("the [evm] section of your config is missing some required variables"), + eris.New("the [evm] section of your config is missing some required variables"), }, errs...) return errors.Join(errs...) } @@ -169,7 +169,7 @@ func getDAToken(ctx context.Context, cfg *config.Config, dockerClient *docker.Cl } if token == "" { - return "", errors.New("got empty DA token") + return "", eris.New("got empty DA token") } return token, nil } diff --git a/cmd/world/evm/stop.go b/cmd/world/evm/stop.go index ba43c93..1995f4b 100644 --- a/cmd/world/evm/stop.go +++ b/cmd/world/evm/stop.go @@ -15,7 +15,7 @@ var stopCmd = &cobra.Command{ Short: "Stop the EVM base shard and DA layer client.", Long: "Stop the EVM base shard and data availability layer client if they are running.", RunE: func(cmd *cobra.Command, _ []string) error { - cfg, err := config.GetConfig(cmd) + cfg, err := config.GetConfig() if err != nil { return err } diff --git a/cmd/world/main.go b/cmd/world/main.go index 14b0b12..e5d541c 100644 --- a/cmd/world/main.go +++ b/cmd/world/main.go @@ -14,11 +14,15 @@ import ( // This variable will be overridden by ldflags during build // Example: -// go build -ldflags "-X main.AppVersion=1.0.0 -X main.PosthogAPIKey= -X main.SentryDsn=" +/* + go build -ldflags "-X main.AppVersion=1.0.0 -X main.PosthogAPIKey= + -X main.SentryDsn= -X main.Env=" +*/ var ( AppVersion string PosthogAPIKey string SentryDsn string + Env string ) func init() { @@ -27,16 +31,18 @@ func init() { AppVersion = "v0.0.1-dev" } root.AppVersion = AppVersion + + if Env == "" { + Env = "DEV" + } + root.Env = Env } func main() { // Sentry initialization - telemetry.SentryInit(SentryDsn) + telemetry.SentryInit(SentryDsn, Env, AppVersion) defer telemetry.SentryFlush() - // Set logger sentry hook - log.Logger = log.Logger.Hook(telemetry.SentryHook{}) - // Set up config directory "~/.worldcli/" err := globalconfig.SetupConfigDir() if err != nil { diff --git a/cmd/world/root/doctor.go b/cmd/world/root/doctor.go index 807e03c..956e12c 100644 --- a/cmd/world/root/doctor.go +++ b/cmd/world/root/doctor.go @@ -15,7 +15,6 @@ var DoctorDeps = []dependency.Dependency{ dependency.Git, dependency.Go, dependency.Docker, - dependency.DockerCompose, dependency.DockerDaemon, } diff --git a/cmd/world/root/root.go b/cmd/world/root/root.go index 3b41968..22c0f89 100644 --- a/cmd/world/root/root.go +++ b/cmd/world/root/root.go @@ -3,7 +3,6 @@ package root import ( "context" "encoding/json" - "errors" "fmt" "io" "net/http" @@ -13,6 +12,7 @@ import ( "time" "github.com/charmbracelet/lipgloss" + "github.com/getsentry/sentry-go" "github.com/hashicorp/go-version" "github.com/rotisserie/eris" "github.com/spf13/cobra" @@ -72,6 +72,9 @@ func init() { rootCmd.AddCommand(cardinal.BaseCmd) rootCmd.AddCommand(evm.BaseCmd) + // Remove completion subcommand + rootCmd.CompletionOptions.DisableDefaultCmd = true + config.AddConfigFlag(rootCmd) logger.AddVerboseFlag(rootCmd) } @@ -95,7 +98,7 @@ func checkLatestVersion() error { defer resp.Body.Close() if resp.StatusCode != http.StatusOK { - logger.Debug(eris.Wrap(errors.New("status is not 200"), "error fetching the latest release")) + logger.Debug(eris.Wrap(eris.New("status is not 200"), "error fetching the latest release")) return nil } @@ -139,6 +142,7 @@ func checkLatestVersion() error { // This is called by main.main(). It only needs to happen once to the rootCmd. func Execute() { if err := rootCmd.Execute(); err != nil { + sentry.CaptureException(err) logger.Errors(err) } // print log stack diff --git a/cmd/world/root/root_test.go b/cmd/world/root/root_test.go index 49e49d3..62f91e2 100644 --- a/cmd/world/root/root_test.go +++ b/cmd/world/root/root_test.go @@ -3,14 +3,13 @@ package root import ( "bytes" "context" - "errors" - "fmt" "net" "os" "strings" "testing" "time" + "github.com/rotisserie/eris" "github.com/spf13/cobra" "gotest.tools/v3/assert" ) @@ -34,12 +33,12 @@ func outputFromCmd(cobra *cobra.Command, cmd string) ([]string, error) { }() if err := cobra.Execute(); err != nil { - return nil, fmt.Errorf("root command failed with: %w", err) + return nil, eris.Errorf("root command failed with: %v", err) } lines := strings.Split(stdOut.String(), "\n") errorStr := stdErr.String() if len(errorStr) > 0 { - return lines, errors.New(errorStr) + return lines, eris.New(errorStr) } return lines, nil @@ -49,11 +48,10 @@ func TestSubcommandsHaveHelpText(t *testing.T) { lines, err := outputFromCmd(rootCmd, "help") assert.NilError(t, err) seenSubcommands := map[string]int{ - "cardinal": 0, - "completion": 0, - "doctor": 0, - "help": 0, - "version": 0, + "cardinal": 0, + "doctor": 0, + "help": 0, + "version": 0, } for _, line := range lines { @@ -78,7 +76,6 @@ func TestExecuteDoctorCommand(t *testing.T) { "Git": 0, "Go": 0, "Docker": 0, - "Docker Compose": 0, "Docker daemon is running": 0, } diff --git a/cmd/world/root/version.go b/cmd/world/root/version.go index 79784dd..f89375a 100644 --- a/cmd/world/root/version.go +++ b/cmd/world/root/version.go @@ -7,6 +7,7 @@ import ( ) var AppVersion string +var Env string // versionCmd print the version number of World CLI // Usage: `world version` @@ -15,6 +16,9 @@ var versionCmd = &cobra.Command{ Short: "Print the version number of World CLI", Long: `Print the version number of World CLI`, Run: func(_ *cobra.Command, _ []string) { - fmt.Printf("World CLI %s\n", AppVersion) + if Env == "PROD" { + Env = "" + } + fmt.Printf("World CLI %s %s\n", AppVersion, Env) }, } diff --git a/common/config/config.go b/common/config/config.go index 58c6694..cd84f61 100644 --- a/common/config/config.go +++ b/common/config/config.go @@ -1,13 +1,13 @@ package config import ( - "errors" "fmt" "os" "path" "path/filepath" "github.com/pelletier/go-toml" + "github.com/rotisserie/eris" "github.com/spf13/cobra" "pkg.world.dev/world-cli/common/logger" @@ -20,10 +20,14 @@ const ( flagForConfigFile = "config" ) -// Items under these toml headers will be included in the environment variables when -// running docker. An error will be generated if a duplicate key is found across -// these sections. -var dockerEnvHeaders = []string{"cardinal", "evm", "nakama", "common"} +var ( + // Items under these toml headers will be included in the environment variables when + // running docker. An error will be generated if a duplicate key is found across + // these sections. + dockerEnvHeaders = []string{"cardinal", "evm", "nakama", "common"} + + configFile string // Config file flag value +) type Config struct { RootDir string @@ -38,11 +42,11 @@ type Config struct { } func AddConfigFlag(cmd *cobra.Command) { - cmd.PersistentFlags().String(flagForConfigFile, "", "a toml encoded config file") + cmd.PersistentFlags().StringVarP(&configFile, flagForConfigFile, "c", "", "a toml encoded config file") } -func GetConfig(cmd *cobra.Command) (*Config, error) { - cfg, err := findAndLoadConfigFile(cmd) +func GetConfig() (*Config, error) { + cfg, err := findAndLoadConfigFile() if err != nil { return nil, err } @@ -57,13 +61,9 @@ func GetConfig(cmd *cobra.Command) (*Config, error) { // 2. A config file set via an environment variable // 3. A config file named "world.toml" in the current directory // 4. A config file found in a parent directory. -func findAndLoadConfigFile(cmd *cobra.Command) (*Config, error) { +func findAndLoadConfigFile() (*Config, error) { // First look for the config file in the config file flag. - if cmd.PersistentFlags().Changed(flagForConfigFile) { - configFile, err := cmd.PersistentFlags().GetString(flagForConfigFile) - if err != nil { - return nil, err - } + if configFile != "" { return loadConfigFromFile(configFile) } @@ -93,7 +93,7 @@ func findAndLoadConfigFile(cmd *cobra.Command) (*Config, error) { } } - return nil, errors.New("no config file found") + return nil, eris.New("No config file found") } func loadConfigFromFile(filename string) (*Config, error) { @@ -113,7 +113,7 @@ func loadConfigFromFile(filename string) (*Config, error) { if rootDir, ok := data["root_dir"]; ok { cfg.RootDir, ok = rootDir.(string) if !ok { - return nil, errors.New("root_dir must be a string") + return nil, eris.New("root_dir must be a string") } } else { cfg.RootDir, _ = filepath.Split(filename) @@ -123,7 +123,7 @@ func loadConfigFromFile(filename string) (*Config, error) { if gameDir, ok := data["game_dir"]; ok { cfg.GameDir, ok = gameDir.(string) if !ok { - return nil, errors.New("game_dir must be a string") + return nil, eris.New("game_dir must be a string") } } else { cfg.GameDir = "cardinal" @@ -136,7 +136,7 @@ func loadConfigFromFile(filename string) (*Config, error) { } for key, val := range m.(map[string]any) { if _, ok := cfg.DockerEnv[key]; ok { - return nil, fmt.Errorf("duplicate env variable %q", key) + return nil, eris.Errorf("duplicate env variable %q", key) } cfg.DockerEnv[key] = fmt.Sprintf("%v", val) } diff --git a/common/config/config_test.go b/common/config/config_test.go index 131f0e3..c40af5c 100644 --- a/common/config/config_test.go +++ b/common/config/config_test.go @@ -18,14 +18,13 @@ func cmdZero() *cobra.Command { } // cmdWithConfig creates a command that has the --config flag set to the given filename -func cmdWithConfig(filename string) *cobra.Command { +func cmdWithConfig(t *testing.T, filename string) { cmd := cmdZero() AddConfigFlag(cmd) - err := cmd.PersistentFlags().Set(flagForConfigFile, filename) - if err != nil { - panic(err) - } - return cmd + assert.NilError(t, cmd.PersistentFlags().Set(flagForConfigFile, filename)) + t.Cleanup(func() { + assert.NilError(t, cmd.PersistentFlags().Set(flagForConfigFile, "")) + }) } func getNamespace(t *testing.T, cfg *Config) string { @@ -63,7 +62,9 @@ func makeConfigAtFile(t *testing.T, file *os.File, namespace string) { func TestCanSetNamespaceWithFilename(t *testing.T) { file := makeConfigAtTemp(t, "alpha") - cfg, err := GetConfig(cmdWithConfig(file)) + cmdWithConfig(t, file) + + cfg, err := GetConfig() assert.NilError(t, err) assert.Equal(t, "alpha", getNamespace(t, cfg)) } @@ -79,7 +80,7 @@ func replaceEnvVarForTest(t *testing.T, env, value string) { func TestCanSetNamespaceWithEnvVariable(t *testing.T) { file := makeConfigAtTemp(t, "alpha") replaceEnvVarForTest(t, WorldCLIConfigFileEnvVariable, file) - cfg, err := GetConfig(cmdZero()) + cfg, err := GetConfig() assert.NilError(t, err) assert.Equal(t, "alpha", getNamespace(t, cfg)) } @@ -88,7 +89,9 @@ func TestConfigPreference(t *testing.T) { fileConfig := makeConfigAtTemp(t, "alpha") envConfig := makeConfigAtTemp(t, "beta") replaceEnvVarForTest(t, WorldCLIConfigFileEnvVariable, envConfig) - cfg, err := GetConfig(cmdWithConfig(fileConfig)) + + cmdWithConfig(t, fileConfig) + cfg, err := GetConfig() assert.NilError(t, err) assert.Equal(t, "alpha", getNamespace(t, cfg)) } @@ -117,7 +120,7 @@ func TestConfigFromLocalFile(t *testing.T) { configFile := path.Join(tempdir, WorldCLIConfigFilename) makeConfigAtPath(t, configFile, "alpha") - cfg, err := GetConfig(cmdZero()) + cfg, err := GetConfig() assert.NilError(t, err) assert.Equal(t, "alpha", getNamespace(t, cfg)) } @@ -136,7 +139,7 @@ func TestLoadConfigLooksInParentDirectories(t *testing.T) { // The eventual call to LoadConfig should find this config file makeConfigAtPath(t, configFile, "alpha") - cfg, err := GetConfig(cmdZero()) + cfg, err := GetConfig() assert.NilError(t, err) assert.Equal(t, "alpha", getNamespace(t, cfg)) } @@ -160,7 +163,8 @@ CARDINAL_NAMESPACE="alpha" ` filename := makeTempConfigWithContent(t, content) - cfg, err := GetConfig(cmdWithConfig(filename)) + cmdWithConfig(t, filename) + cfg, err := GetConfig() assert.NilError(t, err) assert.Equal(t, "alpha", getNamespace(t, cfg)) } @@ -175,7 +179,8 @@ ENV_BETA="beta" ` filename := makeTempConfigWithContent(t, content) - cfg, err := GetConfig(cmdWithConfig(filename)) + cmdWithConfig(t, filename) + cfg, err := GetConfig() assert.NilError(t, err) assert.Equal(t, cfg.DockerEnv["ENV_ALPHA"], "alpha") assert.Equal(t, cfg.DockerEnv["ENV_BETA"], "beta") @@ -189,7 +194,8 @@ FOO = "bar" filename := makeTempConfigWithContent(t, content) // by default, the root path should match the location of the toml file. wantRootDir, _ := path.Split(filename) - cfg, err := GetConfig(cmdWithConfig(filename)) + cmdWithConfig(t, filename) + cfg, err := GetConfig() assert.NilError(t, err) assert.Equal(t, wantRootDir, cfg.RootDir) assert.Equal(t, cfg.DockerEnv["FOO"], "bar") @@ -202,14 +208,15 @@ FOO = "bar" ` wantRootDir = "/some/crazy/path" filename = makeTempConfigWithContent(t, content) - cfg, err = GetConfig(cmdWithConfig(filename)) + cmdWithConfig(t, filename) + cfg, err = GetConfig() assert.NilError(t, err) assert.Equal(t, wantRootDir, cfg.RootDir) assert.Equal(t, "bar", cfg.DockerEnv["FOO"]) } func TestErrorWhenNoConfigFileExists(t *testing.T) { - _, err := GetConfig(cmdZero()) + _, err := GetConfig() assert.Check(t, err != nil) } @@ -220,7 +227,8 @@ SOME_INT = 100 SOME_FLOAT = 99.9 ` filename := makeTempConfigWithContent(t, content) - cfg, err := GetConfig(cmdWithConfig(filename)) + cmdWithConfig(t, filename) + cfg, err := GetConfig() assert.NilError(t, err) assert.Equal(t, "100", cfg.DockerEnv["SOME_INT"]) assert.Equal(t, "99.9", cfg.DockerEnv["SOME_FLOAT"]) @@ -234,7 +242,8 @@ SOME_FLOAT = 99.9 =1000 ` filename := makeTempConfigWithContent(t, invalidContent) - _, err := GetConfig(cmdWithConfig(filename)) + cmdWithConfig(t, filename) + _, err := GetConfig() assert.Check(t, err != nil) } @@ -273,14 +282,16 @@ DUPLICATE = 200 for _, tc := range testCases { filename := makeTempConfigWithContent(t, tc.toml) - _, err := GetConfig(cmdWithConfig(filename)) + cmdWithConfig(t, filename) + _, err := GetConfig() assert.Check(t, err != nil, "in %q", tc.name) } } func TestCanParseExampleConfig(t *testing.T) { exampleConfig := "../../example-world.toml" - cfg, err := GetConfig(cmdWithConfig(exampleConfig)) + cmdWithConfig(t, exampleConfig) + cfg, err := GetConfig() assert.NilError(t, err) assert.Equal(t, "my-world-1", cfg.DockerEnv["CARDINAL_NAMESPACE"]) assert.Equal(t, "world-engine", cfg.DockerEnv["CHAIN_ID"]) @@ -288,6 +299,7 @@ func TestCanParseExampleConfig(t *testing.T) { func TestConfigFlagCannotBeEmpty(t *testing.T) { // If you set the config file, it cannot be empty - _, err := GetConfig(cmdWithConfig("")) + cmdWithConfig(t, "") + _, err := GetConfig() assert.Check(t, err != nil) } diff --git a/common/dependency/dependency.go b/common/dependency/dependency.go index 16793d1..9ef03e2 100644 --- a/common/dependency/dependency.go +++ b/common/dependency/dependency.go @@ -2,8 +2,9 @@ package dependency import ( "errors" - "fmt" "os/exec" + + "github.com/rotisserie/eris" ) var ( @@ -23,12 +24,6 @@ Learn how to install Go: https://go.dev/doc/install`, Name: "Docker", Cmd: exec.Command("docker", "--version"), Help: `Docker is required to build and run World Engine game shards. -Learn how to install Docker: https://docs.docker.com/engine/install/`, - } - DockerCompose = Dependency{ - Name: "Docker Compose", - Cmd: exec.Command("docker", "compose", "version"), - Help: `Docker Compose is required to build and run World Engine game shards. Learn how to install Docker: https://docs.docker.com/engine/install/`, } DockerDaemon = Dependency{ @@ -52,7 +47,7 @@ type Dependency struct { func (d Dependency) Check() error { if err := d.Cmd.Run(); err != nil { - return fmt.Errorf("dependency check %q failed with: %w", d.Name, err) + return eris.Wrapf(err, "dependency check for %q failed", d.Name) } return nil } diff --git a/common/docker/client_image.go b/common/docker/client_image.go index a946975..d01ff75 100644 --- a/common/docker/client_image.go +++ b/common/docker/client_image.go @@ -39,7 +39,6 @@ func (c *Client) buildImages(ctx context.Context, dockerServices ...service.Serv imagesName = append(imagesName, dockerService.Image) } } - if len(serviceToBuild) == 0 { return nil } @@ -125,7 +124,7 @@ func (c *Client) buildImage(ctx context.Context, dockerService service.Service) } // Add source code to the tar archive - if err := c.addFileToTarWriter(".", tw); err != nil { + if err := c.addFileToTarWriter(c.cfg.RootDir, tw); err != nil { return nil, eris.Wrap(err, "Failed to add source code to tar writer") } @@ -394,7 +393,7 @@ func (c *Client) pullImages(ctx context.Context, services ...service.Service) er if err != nil { // Handle the error: log it and send it to the error channel fmt.Printf("Error pulling image %s: %v\n", imageName, err) - errChan <- fmt.Errorf("error pulling image %s: %w", imageName, err) + errChan <- eris.Wrapf(err, "error pulling image %s", imageName) // Stop the progress bar without clearing bar.Abort(false) diff --git a/common/docker/client_test.go b/common/docker/client_test.go index c053cfc..2ad21e2 100644 --- a/common/docker/client_test.go +++ b/common/docker/client_test.go @@ -190,6 +190,7 @@ func TestBuild(t *testing.T) { DockerEnv: map[string]string{ "CARDINAL_NAMESPACE": cardinalNamespace, }, + RootDir: dir, } cardinalService := service.Cardinal(cfg) ctx := context.Background() diff --git a/common/editor/editor.go b/common/editor/editor.go index 5e518a4..72e3997 100644 --- a/common/editor/editor.go +++ b/common/editor/editor.go @@ -4,7 +4,6 @@ import ( "archive/zip" "context" "encoding/json" - "errors" "fmt" "io" "net/http" @@ -169,7 +168,7 @@ func downloadAndUnzip(url string, targetDir string) error { } resp, err := client.Do(req) if err != nil { - return errors.New(url) + return eris.New("Failed to download Cardinal Editor from " + url) } defer resp.Body.Close() @@ -248,13 +247,13 @@ func sanitizeExtractPath(dst string, filePath string) (string, error) { if strings.HasPrefix(dstPath, filepath.Clean(dst)) { return dstPath, nil } - return "", fmt.Errorf("%s: illegal file path", filePath) + return "", eris.Errorf("%s: illegal file path", filePath) } func copyDir(src string, dst string) error { srcDir, err := os.ReadDir(src) if err != nil { - return errors.New(src) + return eris.New("Failed to read directory " + src) } if err := os.MkdirAll(dst, 0755); err != nil { @@ -365,7 +364,7 @@ func getModuleVersion(gomodPath, modulePath string) (string, error) { } // Return an error if the module is not found - return "", fmt.Errorf("module %s not found", modulePath) + return "", eris.Errorf("module %s not found", modulePath) } // fileExists checks if a file exists and is not a directory before we @@ -400,7 +399,7 @@ func getVersionMap(url string) (map[string]string, error) { // Check for HTTP error if resp.StatusCode != http.StatusOK { - return nil, fmt.Errorf("HTTP error: %d - %s", resp.StatusCode, resp.Status) + return nil, eris.Errorf("HTTP error: %d - %s", resp.StatusCode, resp.Status) } // Read the response body diff --git a/common/port.go b/common/port.go index 40e97b7..07021c1 100644 --- a/common/port.go +++ b/common/port.go @@ -3,6 +3,8 @@ package common import ( "fmt" "net" + + "github.com/rotisserie/eris" ) // FindUnusedPort finds an unused port in the range [start, end] for Cardinal Editor @@ -17,5 +19,5 @@ func FindUnusedPort(start int, end int) (int, error) { return port, nil } } - return 0, fmt.Errorf("no available port in the range %d-%d", start, end) + return 0, eris.Errorf("no available port in the range %d-%d", start, end) } diff --git a/common/teacmd/docker.go b/common/teacmd/docker.go index 73b4c9b..c155996 100644 --- a/common/teacmd/docker.go +++ b/common/teacmd/docker.go @@ -10,6 +10,7 @@ import ( "strings" "github.com/magefile/mage/sh" + "github.com/rotisserie/eris" "pkg.world.dev/world-cli/common/config" ) @@ -71,7 +72,7 @@ func dockerComposeWithCfg(cfg *config.Config, args ...string) error { // Runs with the debug docker compose, if `debug` is true func DockerStart(cfg *config.Config, services []DockerService) error { if services == nil { - return errors.New("no service names provided") + return eris.New("no service names provided") } if err := prepareDirs(path.Join(cfg.RootDir, "cardinal")); err != nil { return err @@ -115,7 +116,7 @@ func DockerStartAll(cfg *config.Config) error { // DockerRestart restarts a given docker container by name, rebuilds the image if `build` is true func DockerRestart(cfg *config.Config, services []DockerService) error { if services == nil { - return errors.New("no service names provided") + return eris.New("no service names provided") } if cfg.Build { if err := DockerStop(services); err != nil { @@ -136,7 +137,7 @@ func DockerRestart(cfg *config.Config, services []DockerService) error { // If you want to reset all the services state, use DockerPurge func DockerStop(services []DockerService) error { if services == nil { - return errors.New("no service names provided") + return eris.New("no service names provided") } if err := dockerCompose(dockerArgs("stop", services)...); err != nil { return err @@ -185,7 +186,7 @@ func dockerArgs(args string, services []DockerService, flags ...string) []string func prepareDirs(dirs ...string) error { for _, dir := range dirs { if err := prepareDir(dir); err != nil { - return fmt.Errorf("failed to prepare dir %s: %w", dir, err) + return eris.Wrapf(err, "failed to prepare dir %s", dir) } } return nil diff --git a/telemetry/sentry.go b/telemetry/sentry.go index 7968d2d..78743c1 100644 --- a/telemetry/sentry.go +++ b/telemetry/sentry.go @@ -1,12 +1,10 @@ package telemetry import ( - "errors" - "slices" + "fmt" "time" "github.com/getsentry/sentry-go" - "github.com/rs/zerolog" "github.com/rs/zerolog/log" ) @@ -14,33 +12,8 @@ var ( sentryInitialized bool ) -// SentryHook is a custom hook that implements zerolog.Hook interface -type SentryHook struct{} - -// Run is called for every log event and implements the zerolog.Hook interface -func (h SentryHook) Run(_ *zerolog.Event, level zerolog.Level, msg string) { - shouldBeLogged := slices.Contains(h.Levels(), level) - if sentryInitialized && shouldBeLogged { - // Capture error message - if level == zerolog.ErrorLevel || level == zerolog.FatalLevel || level == zerolog.PanicLevel { - sentry.CaptureException(errors.New(msg)) - } - - // Capture warning message - if level == zerolog.WarnLevel || level == zerolog.DebugLevel { - sentry.CaptureMessage(msg) - } - } -} - -// Levels returns the log levels that this hook should be triggered for -func (h SentryHook) Levels() []zerolog.Level { - return []zerolog.Level{zerolog.ErrorLevel, zerolog.FatalLevel, zerolog.DebugLevel, - zerolog.PanicLevel, zerolog.WarnLevel} -} - // SentryInit initialize sentry -func SentryInit(sentryDsn string) { +func SentryInit(sentryDsn string, env string, appVersion string) { if sentryDsn != "" { err := sentry.Init(sentry.ClientOptions{ Dsn: sentryDsn, @@ -48,6 +21,8 @@ func SentryInit(sentryDsn string) { TracesSampleRate: 1.0, ProfilesSampleRate: 1.0, AttachStacktrace: true, + Environment: env, + Release: fmt.Sprintf("world-cli@%s", appVersion), }) if err != nil { log.Err(err).Msg("Cannot initialize sentry")