diff --git a/.gitignore b/.gitignore index 5cbcb54724e..3c7844bc18b 100644 --- a/.gitignore +++ b/.gitignore @@ -173,3 +173,6 @@ test-reports/ # go workspace files shouldn't be committed go.work + +# binary +/sg diff --git a/dev/sg/checks.go b/dev/sg/checks.go index 9112e29ccbf..309ebaac0d8 100644 --- a/dev/sg/checks.go +++ b/dev/sg/checks.go @@ -14,6 +14,7 @@ import ( "github.com/jackc/pgx/v4" "github.com/sourcegraph/sourcegraph/dev/sg/internal/check" + "github.com/sourcegraph/sourcegraph/dev/sg/internal/sgconf" "github.com/sourcegraph/sourcegraph/dev/sg/internal/stdout" "github.com/sourcegraph/sourcegraph/dev/sg/internal/usershell" "github.com/sourcegraph/sourcegraph/dev/sg/root" @@ -229,8 +230,8 @@ func checkSourcegraphDatabase(ctx context.Context) error { // This check runs only in the `sourcegraph/sourcegraph` repository, so // we try to parse the globalConf and use its `Env` to configure the // Postgres connection. - ok, _ := parseConf(configFlag, overwriteConfigFlag) - if !ok { + config, _ := sgconf.Get(configFile, configOverwriteFile) + if config == nil { return errors.New("failed to read sg.config.yaml. This step of `sg setup` needs to be run in the `sourcegraph` repository") } @@ -242,7 +243,7 @@ func checkSourcegraphDatabase(ctx context.Context) error { return val } // Otherwise check in globalConf.Env - return globalConf.Env[key] + return config.Env[key] } dsn := postgresdsn.New("", "", getEnv) diff --git a/dev/sg/config.go b/dev/sg/internal/sgconf/config.go similarity index 96% rename from dev/sg/config.go rename to dev/sg/internal/sgconf/config.go index 0f91663dfcd..56d750f6e26 100644 --- a/dev/sg/config.go +++ b/dev/sg/internal/sgconf/config.go @@ -1,4 +1,4 @@ -package main +package sgconf import ( "io" @@ -10,7 +10,7 @@ import ( "github.com/sourcegraph/sourcegraph/lib/errors" ) -func ParseConfigFile(name string) (*Config, error) { +func parseConfigFile(name string) (*Config, error) { file, err := os.Open(name) if err != nil { return nil, errors.Wrapf(err, "cannot open file %q", name) @@ -22,10 +22,10 @@ func ParseConfigFile(name string) (*Config, error) { return nil, errors.Wrap(err, "reading configuration file") } - return ParseConfig(data) + return parseConfig(data) } -func ParseConfig(data []byte) (*Config, error) { +func parseConfig(data []byte) (*Config, error) { var conf Config if err := yaml.Unmarshal(data, &conf); err != nil { return nil, err diff --git a/dev/sg/config_test.go b/dev/sg/internal/sgconf/config_test.go similarity index 95% rename from dev/sg/config_test.go rename to dev/sg/internal/sgconf/config_test.go index c879edca824..3e24cc6aaa6 100644 --- a/dev/sg/config_test.go +++ b/dev/sg/internal/sgconf/config_test.go @@ -1,4 +1,4 @@ -package main +package sgconf import ( "testing" @@ -40,7 +40,7 @@ commandsets: - gitserver ` - have, err := ParseConfig([]byte(input)) + have, err := parseConfig([]byte(input)) if err != nil { t.Errorf("unexpected error: %s", err) } @@ -92,7 +92,7 @@ commands: - enterprise/internal - enterprise/cmd/frontend ` - config, err := ParseConfig([]byte(a)) + config, err := parseConfig([]byte(a)) if err != nil { t.Errorf("unexpected error: %s", err) } @@ -104,7 +104,7 @@ commands: EXTSVC_CONFIG_FILE: '' ` - overwrite, err := ParseConfig([]byte(b)) + overwrite, err := parseConfig([]byte(b)) if err != nil { t.Errorf("unexpected error: %s", err) } diff --git a/dev/sg/internal/sgconf/global.go b/dev/sg/internal/sgconf/global.go new file mode 100644 index 00000000000..aebe959a725 --- /dev/null +++ b/dev/sg/internal/sgconf/global.go @@ -0,0 +1,82 @@ +package sgconf + +import ( + "os" + "path/filepath" + "sync" + + "github.com/sourcegraph/sourcegraph/dev/sg/root" + "github.com/sourcegraph/sourcegraph/lib/errors" +) + +const ( + DefaultFile = "sg.config.yaml" + DefaultOverwriteFile = "sg.config.overwrite.yaml" +) + +var ( + globalConfOnce sync.Once + globalConf *Config + globalConfErr error +) + +// Get retrieves the global config files and merges them into a single sg config. +// +// It must not be called before flag initalization, i.e. when confFile or overwriteFile is +// not set, or it will panic. This means that it can only be used in (*cli).Action, +// (*cli).Before/(*cli).After, and postInitHooks +func Get(confFile, overwriteFile string) (*Config, error) { + // If unset, Get was called in an illegal context, since sg.Before validates that the + // flags are non-empty. + if confFile == "" || overwriteFile == "" { + panic("sgconf.Get called before flag initialization") + } + + globalConfOnce.Do(func() { + globalConf, globalConfErr = parseConf(confFile, overwriteFile) + }) + return globalConf, globalConfErr +} + +func parseConf(confFile, overwriteFile string) (*Config, error) { + // Try to determine root of repository, so we can look for config there + repoRoot, err := root.RepositoryRoot() + if err != nil { + return nil, errors.Wrap(err, "Failed to determine repository root location") + } + + // If the configFlag/overwriteConfigFlag flags have their default value, we + // take the value as relative to the root of the repository. + if confFile == DefaultFile { + confFile = filepath.Join(repoRoot, confFile) + } + if overwriteFile == DefaultOverwriteFile { + overwriteFile = filepath.Join(repoRoot, overwriteFile) + } + + conf, err := parseConfigFile(confFile) + if err != nil { + return nil, errors.Wrapf(err, "Failed to parse %q as configuration file", confFile) + } + + if ok, _ := fileExists(overwriteFile); ok { + overwriteConf, err := parseConfigFile(overwriteFile) + if err != nil { + return nil, errors.Wrapf(err, "Failed to parse %q as configuration overwrite file", confFile) + } + conf.Merge(overwriteConf) + } + + return conf, nil +} + +func fileExists(path string) (bool, error) { + _, err := os.Stat(path) + if err != nil { + if os.IsNotExist(err) { + return false, nil + } + return false, err + } + return true, nil +} diff --git a/dev/sg/main.go b/dev/sg/main.go index d75479b4d24..b70afefde83 100644 --- a/dev/sg/main.go +++ b/dev/sg/main.go @@ -10,9 +10,10 @@ import ( "github.com/urfave/cli/v2" "github.com/sourcegraph/sourcegraph/dev/sg/internal/secrets" + "github.com/sourcegraph/sourcegraph/dev/sg/internal/sgconf" "github.com/sourcegraph/sourcegraph/dev/sg/internal/stdout" "github.com/sourcegraph/sourcegraph/dev/sg/root" - "github.com/sourcegraph/sourcegraph/lib/output" + "github.com/sourcegraph/sourcegraph/lib/errors" ) func main() { @@ -27,24 +28,21 @@ func main() { } const ( - defaultConfigFile = "sg.config.yaml" - defaultConfigOverwriteFile = "sg.config.overwrite.yaml" - defaultSecretsFile = "sg.secrets.json" + defaultSecretsFile = "sg.secrets.json" ) var ( BuildCommit = "dev" - // globalConf is the global config. If a command needs to access it, it *must* call - // `parseConf` before. - globalConf *Config - // secretsStore is instantiated when sg gets run. secretsStore *secrets.Store - // Note that these values are only available after the main sg CLI app has been run. - configFlag string - overwriteConfigFlag string + // configFile is the path to use with sgconf.Get - it must not be used before flag + // initialization. + configFile string + // configOverwriteFile is the path to use with sgconf.Get - it must not be used before + // flag initialization. + configOverwriteFile string // Global verbose mode verbose bool @@ -81,19 +79,21 @@ var sg = &cli.App{ }, &cli.StringFlag{ Name: "config", + Aliases: []string{"c"}, Usage: "load sg configuration from `file`", EnvVars: []string{"SG_CONFIG"}, TakesFile: true, - Value: defaultConfigFile, - Destination: &configFlag, + Value: sgconf.DefaultFile, + Destination: &configFile, }, &cli.StringFlag{ Name: "overwrite", + Aliases: []string{"o"}, Usage: "load sg configuration from `file` that is gitignored and can be used to, for example, add credentials", EnvVars: []string{"SG_OVERWRITE"}, TakesFile: true, - Value: defaultConfigOverwriteFile, - Destination: &overwriteConfigFlag, + Value: sgconf.DefaultOverwriteFile, + Destination: &configOverwriteFile, }, &cli.BoolFlag{ Name: "skip-auto-update", @@ -103,16 +103,24 @@ var sg = &cli.App{ }, }, Before: func(cmd *cli.Context) error { - if verbose { - stdout.Out.SetVerbose() - } - if batchCompletionMode { // All other setup pertains to running commands - to keep completions fast, // we skip all other setup. return nil } + if verbose { + stdout.Out.SetVerbose() + } + + // Validate configuration flags, which is required for sgconf.Get to work everywhere else. + if configFile == "" { + return errors.Newf("--config must not be empty") + } + if configOverwriteFile == "" { + return errors.Newf("--overwrite must not be empty") + } + // Set up access to secrets if err := loadSecrets(); err != nil { writeWarningLinef("failed to open secrets: %s", err) @@ -188,42 +196,3 @@ func loadSecrets() error { secretsStore, err = secrets.LoadFile(fp) return err } - -// parseConf parses the config file and the optional overwrite file. -// Iear the conf has already been parsed it's a noop. -func parseConf(confFile, overwriteFile string) (bool, output.FancyLine) { - if globalConf != nil { - return true, output.FancyLine{} - } - - // Try to determine root of repository, so we can look for config there - repoRoot, err := root.RepositoryRoot() - if err != nil { - return false, output.Linef("", output.StyleWarning, "Failed to determine repository root location: %s", err) - } - - // If the configFlag/overwriteConfigFlag flags have their default value, we - // take the value as relative to the root of the repository. - if confFile == defaultConfigFile { - confFile = filepath.Join(repoRoot, confFile) - } - - if overwriteFile == defaultConfigOverwriteFile { - overwriteFile = filepath.Join(repoRoot, overwriteFile) - } - - globalConf, err = ParseConfigFile(confFile) - if err != nil { - return false, output.Linef("", output.StyleWarning, "Failed to parse %s%s%s%s as configuration file:%s\n%s", output.StyleBold, confFile, output.StyleReset, output.StyleWarning, output.StyleReset, err) - } - - if ok, _ := fileExists(overwriteFile); ok { - overwriteConf, err := ParseConfigFile(overwriteFile) - if err != nil { - return false, output.Linef("", output.StyleWarning, "Failed to parse %s%s%s%s as overwrites configuration file:%s\n%s", output.StyleBold, overwriteFile, output.StyleReset, output.StyleWarning, output.StyleReset, err) - } - globalConf.Merge(overwriteConf) - } - - return true, output.FancyLine{} -} diff --git a/dev/sg/main_test.go b/dev/sg/main_test.go index acd6058e7c5..5e8c11e0d07 100644 --- a/dev/sg/main_test.go +++ b/dev/sg/main_test.go @@ -9,17 +9,31 @@ import ( "github.com/urfave/cli/v2" ) +// testSG creates a copy of the sg app for testing. +func testSG() *cli.App { + tsg := *sg + return &tsg +} + func TestAppRun(t *testing.T) { - // Check app starts up correctly + sg := testSG() + + // Capture output var out, err bytes.Buffer sg.Writer = &out sg.ErrWriter = &err + // Check app starts up correctly assert.NoError(t, sg.Run([]string{"help"})) assert.Contains(t, out.String(), "The Sourcegraph developer tool!") + // We do not want errors anywhere + assert.NotContains(t, out.String(), "error") + assert.NotContains(t, out.String(), "panic") assert.Empty(t, err.String()) } func TestCommandFormatting(t *testing.T) { + sg := testSG() + sg.Setup() for _, cmd := range sg.Commands { testCommandFormatting(t, cmd) diff --git a/dev/sg/os.go b/dev/sg/os.go index 114d5b462c1..67fb9e53a0f 100644 --- a/dev/sg/os.go +++ b/dev/sg/os.go @@ -1,7 +1,6 @@ package main import ( - "os" "syscall" "github.com/sourcegraph/sourcegraph/lib/errors" @@ -29,14 +28,3 @@ func setMaxOpenFiles() error { return nil } - -func fileExists(path string) (bool, error) { - _, err := os.Stat(path) - if err != nil { - if os.IsNotExist(err) { - return false, nil - } - return false, err - } - return true, nil -} diff --git a/dev/sg/printing.go b/dev/sg/printing.go index 7ebde3fb96f..12e193f836c 100644 --- a/dev/sg/printing.go +++ b/dev/sg/printing.go @@ -19,8 +19,12 @@ func writeFailureLinef(fmtStr string, args ...interface{}) { stdout.Out.WriteLine(output.Linef(output.EmojiFailure, output.StyleWarning, fmtStr, args...)) } +func newWarningLinef(fmtStr string, args ...interface{}) output.FancyLine { + return output.Linef(output.EmojiWarningSign, output.StyleYellow, fmtStr, args...) +} + func writeWarningLinef(fmtStr string, args ...interface{}) { - stdout.Out.WriteLine(output.Linef(output.EmojiWarningSign, output.StyleYellow, fmtStr, args...)) + stdout.Out.WriteLine(newWarningLinef(fmtStr, args...)) } func writeSkippedLinef(fmtStr string, args ...interface{}) { diff --git a/dev/sg/sg_db.go b/dev/sg/sg_db.go index bf56ba633a2..bb78aba8ada 100644 --- a/dev/sg/sg_db.go +++ b/dev/sg/sg_db.go @@ -12,6 +12,7 @@ import ( "github.com/urfave/cli/v2" "github.com/sourcegraph/sourcegraph/dev/sg/internal/db" + "github.com/sourcegraph/sourcegraph/dev/sg/internal/sgconf" "github.com/sourcegraph/sourcegraph/internal/database" connections "github.com/sourcegraph/sourcegraph/internal/database/connections/live" "github.com/sourcegraph/sourcegraph/internal/database/migration/runner" @@ -78,13 +79,13 @@ func dbAddUserAction(cmd *cli.Context) error { ctx := cmd.Context // Read the configuration. - ok, _ := parseConf(configFlag, overwriteConfigFlag) - if !ok { + conf, _ := sgconf.Get(configFile, configOverwriteFile) + if conf == nil { return errors.New("failed to read sg.config.yaml. This command needs to be run in the `sourcegraph` repository") } // Connect to the database. - conn, err := connections.EnsureNewFrontendDB(postgresdsn.New("", "", globalConf.GetEnv), "frontend", &observation.TestContext) + conn, err := connections.EnsureNewFrontendDB(postgresdsn.New("", "", conf.GetEnv), "frontend", &observation.TestContext) if err != nil { return err } @@ -131,13 +132,13 @@ func dbAddUserAction(cmd *cli.Context) error { func dbResetRedisExec(ctx context.Context, args []string) error { // Read the configuration. - ok, _ := parseConf(configFlag, overwriteConfigFlag) - if !ok { + config, _ := sgconf.Get(configFile, configOverwriteFile) + if config == nil { return errors.New("failed to read sg.config.yaml. This command needs to be run in the `sourcegraph` repository") } // Connect to the redis database. - endpoint := globalConf.GetEnv("REDIS_ENDPOINT") + endpoint := config.GetEnv("REDIS_ENDPOINT") conn, err := redis.Dial("tcp", endpoint, redis.DialConnectTimeout(5*time.Second)) if err != nil { return errors.Wrapf(err, "failed to connect to Redis at %s", endpoint) @@ -154,8 +155,8 @@ func dbResetRedisExec(ctx context.Context, args []string) error { func dbResetPGExec(ctx context.Context, args []string) error { // Read the configuration. - ok, _ := parseConf(configFlag, overwriteConfigFlag) - if !ok { + config, _ := sgconf.Get(configFile, configOverwriteFile) + if config == nil { return errors.New("failed to read sg.config.yaml. This command needs to be run in the `sourcegraph` repository") } @@ -172,9 +173,9 @@ func dbResetPGExec(ctx context.Context, args []string) error { for _, name := range schemaNames { if name == "frontend" { - dsnMap[name] = postgresdsn.New("", "", globalConf.GetEnv) + dsnMap[name] = postgresdsn.New("", "", config.GetEnv) } else { - dsnMap[name] = postgresdsn.New(strings.ToUpper(name), "", globalConf.GetEnv) + dsnMap[name] = postgresdsn.New(strings.ToUpper(name), "", config.GetEnv) } } diff --git a/dev/sg/sg_live.go b/dev/sg/sg_live.go index a9f1dbe4d51..21821434b82 100644 --- a/dev/sg/sg_live.go +++ b/dev/sg/sg_live.go @@ -31,7 +31,7 @@ func constructLiveCmdLongHelp() string { fmt.Fprintf(&out, "Prints the Sourcegraph version deployed to the given environment.") fmt.Fprintf(&out, "\n") fmt.Fprintf(&out, "\n") - fmt.Fprintf(&out, "AVAILABLE PRESET ENVIRONMENTS\n") + fmt.Fprintf(&out, "AVAILABLE PRESET ENVIRONMENTS:\n") for _, name := range environmentNames() { fmt.Fprintf(&out, " %s\n", name) diff --git a/dev/sg/sg_migration.go b/dev/sg/sg_migration.go index ebd9f029fa8..0370d8219b1 100644 --- a/dev/sg/sg_migration.go +++ b/dev/sg/sg_migration.go @@ -13,6 +13,7 @@ import ( "github.com/sourcegraph/sourcegraph/dev/sg/internal/db" "github.com/sourcegraph/sourcegraph/dev/sg/internal/migration" + "github.com/sourcegraph/sourcegraph/dev/sg/internal/sgconf" "github.com/sourcegraph/sourcegraph/dev/sg/internal/stdout" "github.com/sourcegraph/sourcegraph/dev/sg/root" connections "github.com/sourcegraph/sourcegraph/internal/database/connections/live" @@ -101,9 +102,9 @@ func makeRunner(ctx context.Context, schemaNames []string) (cliutil.Runner, erro // Try to read the `sg` configuration so we can read ENV vars from the // configuration and use process env as fallback. var getEnv func(string) string - ok, _ := parseConf(configFlag, overwriteConfigFlag) - if ok { - getEnv = globalConf.GetEnv + config, _ := sgconf.Get(configFile, configOverwriteFile) + if config != nil { + getEnv = config.GetEnv } else { getEnv = os.Getenv } diff --git a/dev/sg/sg_run.go b/dev/sg/sg_run.go index d55fc005486..27926d04bb2 100644 --- a/dev/sg/sg_run.go +++ b/dev/sg/sg_run.go @@ -11,6 +11,7 @@ import ( "github.com/urfave/cli/v2" "github.com/sourcegraph/sourcegraph/dev/sg/internal/run" + "github.com/sourcegraph/sourcegraph/dev/sg/internal/sgconf" "github.com/sourcegraph/sourcegraph/dev/sg/internal/stdout" "github.com/sourcegraph/sourcegraph/lib/output" ) @@ -23,17 +24,20 @@ func init() { } var runCommand = &cli.Command{ - Name: "run", - Usage: "Run the given commands", - ArgsUsage: "[command]", - Description: constructRunCmdLongHelp(), - Category: CategoryDev, + Name: "run", + Usage: "Run the given commands", + ArgsUsage: "[command]", + Category: CategoryDev, Flags: []cli.Flag{ addToMacOSFirewallFlag, }, Action: execAdapter(runExec), BashComplete: completeOptions(func() (options []string) { - for name := range globalConf.Commands { + config, _ := sgconf.Get(configFile, configOverwriteFile) + if config == nil { + return + } + for name := range config.Commands { options = append(options, name) } return @@ -41,9 +45,9 @@ var runCommand = &cli.Command{ } func runExec(ctx context.Context, args []string) error { - ok, errLine := parseConf(configFlag, overwriteConfigFlag) - if !ok { - stdout.Out.WriteLine(errLine) + config, err := sgconf.Get(configFile, configOverwriteFile) + if err != nil { + writeWarningLinef(err.Error()) os.Exit(1) } @@ -54,7 +58,7 @@ func runExec(ctx context.Context, args []string) error { var cmds []run.Command for _, arg := range args { - cmd, ok := globalConf.Commands[arg] + cmd, ok := config.Commands[arg] if !ok { stdout.Out.WriteLine(output.Linef("", output.StyleWarning, "ERROR: command %q not found :(", arg)) return flag.ErrHelp @@ -62,28 +66,30 @@ func runExec(ctx context.Context, args []string) error { cmds = append(cmds, cmd) } - return run.Commands(ctx, globalConf.Env, addToMacOSFirewall, verbose, cmds...) + return run.Commands(ctx, config.Env, addToMacOSFirewall, verbose, cmds...) } + func constructRunCmdLongHelp() string { var out strings.Builder fmt.Fprintf(&out, " Runs the given command. If given a whitespace-separated list of commands it runs the set of commands.\n") - ok, warning := parseConf(configFlag, overwriteConfigFlag) - if ok { - fmt.Fprintf(&out, "\n") - fmt.Fprintf(&out, "AVAILABLE COMMANDS IN %s%s%s\n", output.StyleBold, configFlag, output.StyleReset) - - var names []string - for name := range globalConf.Commands { - names = append(names, name) - } - sort.Strings(names) - fmt.Fprint(&out, strings.Join(names, "\n")) - } else { + config, err := sgconf.Get(configFile, configOverwriteFile) + if err != nil { out.Write([]byte("\n")) - output.NewOutput(&out, output.OutputOpts{}).WriteLine(warning) + output.NewOutput(&out, output.OutputOpts{}).WriteLine(newWarningLinef(err.Error())) + return out.String() } + fmt.Fprintf(&out, "\n") + fmt.Fprintf(&out, "AVAILABLE COMMANDS IN %s%s%s:\n", output.StyleBold, configFile, output.StyleReset) + + var names []string + for name := range config.Commands { + names = append(names, name) + } + sort.Strings(names) + fmt.Fprint(&out, strings.Join(names, "\n")) + return out.String() } diff --git a/dev/sg/sg_start.go b/dev/sg/sg_start.go index 6cf17f3a5c2..3df34876c39 100644 --- a/dev/sg/sg_start.go +++ b/dev/sg/sg_start.go @@ -12,6 +12,7 @@ import ( "github.com/urfave/cli/v2" "github.com/sourcegraph/sourcegraph/dev/sg/internal/run" + "github.com/sourcegraph/sourcegraph/dev/sg/internal/sgconf" "github.com/sourcegraph/sourcegraph/dev/sg/internal/stdout" "github.com/sourcegraph/sourcegraph/dev/sg/root" "github.com/sourcegraph/sourcegraph/lib/errors" @@ -72,7 +73,11 @@ var ( addToMacOSFirewallFlag, }, BashComplete: completeOptions(func() (options []string) { - for name := range globalConf.Commandsets { + config, _ := sgconf.Get(configFile, configOverwriteFile) + if config == nil { + return + } + for name := range config.Commandsets { options = append(options, name) } return @@ -91,36 +96,37 @@ If no commandset is specified, it starts the commandset with the name 'default'. Use this to start your Sourcegraph environment! `) - ok, warning := parseConf(configFlag, overwriteConfigFlag) - if ok { - fmt.Fprintf(&out, "\n") - fmt.Fprintf(&out, "AVAILABLE COMMANDSETS IN %s%s%s\n", output.StyleBold, configFlag, output.StyleReset) - - var names []string - for name := range globalConf.Commandsets { - switch name { - case "enterprise-codeintel": - names = append(names, fmt.Sprintf(" %s 🧠", name)) - case "batches": - names = append(names, fmt.Sprintf(" %s 🦡", name)) - default: - names = append(names, fmt.Sprintf(" %s", name)) - } - } - sort.Strings(names) - fmt.Fprint(&out, strings.Join(names, "\n")) - } else { + config, err := sgconf.Get(configFile, configOverwriteFile) + if err != nil { out.Write([]byte("\n")) - output.NewOutput(&out, output.OutputOpts{}).WriteLine(warning) + output.NewOutput(&out, output.OutputOpts{}).WriteLine(newWarningLinef(err.Error())) + return out.String() } + fmt.Fprintf(&out, "\n") + fmt.Fprintf(&out, "AVAILABLE COMMANDSETS IN %s%s%s:\n", output.StyleBold, configFile, output.StyleReset) + + var names []string + for name := range config.Commandsets { + switch name { + case "enterprise-codeintel": + names = append(names, fmt.Sprintf(" %s 🧠", name)) + case "batches": + names = append(names, fmt.Sprintf(" %s 🦡", name)) + default: + names = append(names, fmt.Sprintf(" %s", name)) + } + } + sort.Strings(names) + fmt.Fprint(&out, strings.Join(names, "\n")) + return out.String() } func startExec(ctx context.Context, args []string) error { - ok, errLine := parseConf(configFlag, overwriteConfigFlag) - if !ok { - stdout.Out.WriteLine(errLine) + config, err := sgconf.Get(configFile, configOverwriteFile) + if err != nil { + writeWarningLinef(err.Error()) os.Exit(1) } @@ -130,15 +136,15 @@ func startExec(ctx context.Context, args []string) error { } if len(args) != 1 { - if globalConf.DefaultCommandset != "" { - args = append(args, globalConf.DefaultCommandset) + if config.DefaultCommandset != "" { + args = append(args, config.DefaultCommandset) } else { stdout.Out.WriteLine(output.Linef("", output.StyleWarning, "ERROR: No commandset specified and no 'defaultCommandset' specified in sg.config.yaml\n")) return flag.ErrHelp } } - set, ok := globalConf.Commandsets[args[0]] + set, ok := config.Commandsets[args[0]] if !ok { stdout.Out.WriteLine(output.Linef("", output.StyleWarning, "ERROR: commandset %q not found :(", args[0])) return flag.ErrHelp @@ -179,10 +185,10 @@ func startExec(ctx context.Context, args []string) error { } } - return startCommandSet(ctx, set, globalConf, addToMacOSFirewall) + return startCommandSet(ctx, set, config, addToMacOSFirewall) } -func startCommandSet(ctx context.Context, set *Commandset, conf *Config, addToMacOSFirewall bool) error { +func startCommandSet(ctx context.Context, set *sgconf.Commandset, conf *sgconf.Config, addToMacOSFirewall bool) error { if err := runChecksWithName(ctx, set.Checks); err != nil { return err } diff --git a/dev/sg/sg_start_test.go b/dev/sg/sg_start_test.go index cc8be8f908c..312f9c3f4f9 100644 --- a/dev/sg/sg_start_test.go +++ b/dev/sg/sg_start_test.go @@ -9,6 +9,7 @@ import ( "github.com/google/go-cmp/cmp" "github.com/sourcegraph/sourcegraph/dev/sg/internal/run" + "github.com/sourcegraph/sourcegraph/dev/sg/internal/sgconf" "github.com/sourcegraph/sourcegraph/dev/sg/internal/stdout" "github.com/sourcegraph/sourcegraph/lib/output" "github.com/sourcegraph/sourcegraph/lib/output/outputtest" @@ -20,16 +21,16 @@ func TestStartCommandSet(t *testing.T) { buf := useOutputBuffer(t) - commandSet := &Commandset{Name: "test-set", Commands: []string{"test-cmd-1"}} + commandSet := &sgconf.Commandset{Name: "test-set", Commands: []string{"test-cmd-1"}} command := run.Command{ Name: "test-cmd-1", Install: "echo 'booting up horsegraph'", Cmd: "echo 'horsegraph booted up. mount your horse.' && echo 'quitting. not horsing around anymore.'", } - testConf := &Config{ + testConf := &sgconf.Config{ Commands: map[string]run.Command{"test-cmd-1": command}, - Commandsets: map[string]*Commandset{"test-set": commandSet}, + Commandsets: map[string]*sgconf.Commandset{"test-set": commandSet}, } if err := startCommandSet(ctx, commandSet, testConf, false); err != nil { @@ -60,16 +61,16 @@ func TestStartCommandSet_InstallError(t *testing.T) { buf := useOutputBuffer(t) - commandSet := &Commandset{Name: "test-set", Commands: []string{"test-cmd-1"}} + commandSet := &sgconf.Commandset{Name: "test-set", Commands: []string{"test-cmd-1"}} command := run.Command{ Name: "test-cmd-1", Install: "echo 'booting up horsegraph' && exit 1", Cmd: "echo 'never appears'", } - testConf := &Config{ + testConf := &sgconf.Config{ Commands: map[string]run.Command{"test-cmd-1": command}, - Commandsets: map[string]*Commandset{"test-set": commandSet}, + Commandsets: map[string]*sgconf.Commandset{"test-set": commandSet}, } err := startCommandSet(ctx, commandSet, testConf, withPostInstallCallback) diff --git a/dev/sg/sg_tests.go b/dev/sg/sg_tests.go index d85105e7096..97abae04310 100644 --- a/dev/sg/sg_tests.go +++ b/dev/sg/sg_tests.go @@ -11,6 +11,7 @@ import ( "github.com/urfave/cli/v2" "github.com/sourcegraph/sourcegraph/dev/sg/internal/run" + "github.com/sourcegraph/sourcegraph/dev/sg/internal/sgconf" "github.com/sourcegraph/sourcegraph/dev/sg/internal/stdout" "github.com/sourcegraph/sourcegraph/lib/output" ) @@ -28,7 +29,11 @@ var testCommand = &cli.Command{ Usage: "Run the given test suite", Category: CategoryDev, BashComplete: completeOptions(func() (options []string) { - for name := range globalConf.Tests { + config, _ := sgconf.Get(configFile, configOverwriteFile) + if config == nil { + return + } + for name := range config.Tests { options = append(options, name) } return @@ -37,9 +42,9 @@ var testCommand = &cli.Command{ } func testExec(ctx context.Context, args []string) error { - ok, errLine := parseConf(configFlag, overwriteConfigFlag) - if !ok { - stdout.Out.WriteLine(errLine) + config, err := sgconf.Get(configFile, configOverwriteFile) + if err != nil { + writeWarningLinef(err.Error()) os.Exit(1) } @@ -48,13 +53,13 @@ func testExec(ctx context.Context, args []string) error { return flag.ErrHelp } - cmd, ok := globalConf.Tests[args[0]] + cmd, ok := config.Tests[args[0]] if !ok { stdout.Out.WriteLine(output.Linef("", output.StyleWarning, "ERROR: test suite %q not found :(", args[0])) return flag.ErrHelp } - return run.Test(ctx, cmd, args[1:], globalConf.Env) + return run.Test(ctx, cmd, args[1:], config.Env) } func constructTestCmdLongHelp() string { @@ -64,22 +69,23 @@ func constructTestCmdLongHelp() string { // Attempt to parse config to list available testsuites, but don't fail on // error, because we should never error when the user wants --help output. - ok, warning := parseConf(configFlag, overwriteConfigFlag) - if ok { - fmt.Fprintf(&out, "\n\n") - fmt.Fprintf(&out, "AVAILABLE TESTSUITES IN %s%s%s:\n", output.StyleBold, configFlag, output.StyleReset) - fmt.Fprintf(&out, "\n") - - var names []string - for name := range globalConf.Tests { - names = append(names, name) - } - sort.Strings(names) - fmt.Fprint(&out, strings.Join(names, "\n")) - } else { + config, err := sgconf.Get(configFile, configOverwriteFile) + if err != nil { out.Write([]byte("\n")) - output.NewOutput(&out, output.OutputOpts{}).WriteLine(warning) + output.NewOutput(&out, output.OutputOpts{}).WriteLine(newWarningLinef(err.Error())) + return out.String() } + fmt.Fprintf(&out, "\n\n") + fmt.Fprintf(&out, "AVAILABLE TESTSUITES IN %s%s%s:\n", output.StyleBold, configFile, output.StyleReset) + fmt.Fprintf(&out, "\n") + + var names []string + for name := range config.Tests { + names = append(names, name) + } + sort.Strings(names) + fmt.Fprint(&out, strings.Join(names, "\n")) + return out.String() }