Help us shape the future of Go! Take the 2026 Go Developer Survey

config

package
v1.65.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 21, 2026 License: MIT Imports: 13 Imported by: 0

Documentation

Overview

Package config provides subcommands for managing the eng CLI configuration. It allows users to view and modify settings stored in the $HOME/.eng.yaml file.

Index

Constants

This section is empty.

Variables

View Source
var ConfigCmd = &cobra.Command{
	Use:   "config",
	Short: "Manage the cli's config file.",
	Long: `This command is used to facilitate the management of the config file specific to this cli.

It should be located at $HOME/.eng.yaml`,
	Run: func(cmd *cobra.Command, _args []string) {
		headerStyle := lipgloss.NewStyle().
			Bold(true).
			Foreground(theme.Primary).
			MarginBottom(1)
		if !ui.DisableProgress {
			fmt.Fprintln(log.Out, headerStyle.Render("⚙️ CLI Configuration Inspector"))
		}

		config.Email()
		config.DotfilesRepo()
		config.RepoURL()
		config.Branch()
		config.BareRepoPath()
		config.GitDevPath()
		config.GitEditor()
		config.Verbose()
	},
}

ConfigCmd represents the base command for all configuration related operations.

View Source
var DotfilesBareRepoPathCmd = &cobra.Command{
	Use:   "dotfiles-bare-repo-path",
	Short: "Update config dotfiles bare repo path",
	Long:  `Get or set the path where the bare dotfiles repository should be stored.`,
	Run: func(cmd *cobra.Command, args []string) {
		config.BareRepoPath()
	},
}

DotfilesBareRepoPathCmd represents the command to manage the bare repository path configuration.

View Source
var DotfilesBranchCmd = &cobra.Command{
	Use:   "dotfiles-branch",
	Short: "Update config dotfiles branch",
	Long:  `Get or set the dotfiles branch (main/work/server) in the configuration file.`,
	Run: func(cmd *cobra.Command, _args []string) {
		config.Branch()
	},
}

DotfilesBranchCmd represents the command to manage the dotfiles branch configuration.

View Source
var DotfilesRepoCmd = &cobra.Command{
	Use:   "dotfiles-repo",
	Short: "Update config dotfiles repo path",
	Long:  `This command sets the path to the local dotfiles repository in the config file.`,
	Run: func(cmd *cobra.Command, args []string) {
		log.Start("Checking for dotfile repo path in config file...")
		config.DotfilesRepo()
	},
}

DotfilesRepoCmd defines the command for setting the local dotfiles repository path.

View Source
var DotfilesRepoURLCmd = &cobra.Command{
	Use:   "dotfiles-repo-url",
	Short: "Update config dotfiles repo URL",
	Long:  `Get or set the dotfiles repository URL in the configuration file.`,
	Run: func(cmd *cobra.Command, args []string) {
		config.RepoURL()
	},
}

DotfilesRepoURLCmd represents the command to manage the dotfiles repository URL configuration.

View Source
var DotfilesTargetRepoPathCmd = &cobra.Command{
	Use:   "dotfiles-target-repo-path [path]",
	Short: "Show or set the dotfiles target repo path for copy-changes",
	Long: `Show or set the explicit destination git repository used by 'eng dotfiles copy-changes'.

With a path argument, validates that it exists and saves it, so future copies
no longer depend on dev-folder heuristics. Without arguments, prints the
currently effective path and where it came from.`,
	Example: `  eng config dotfiles-target-repo-path
  eng config dotfiles-target-repo-path ~/Development/dotfiles`,
	Args: cobra.MaximumNArgs(1),
	Run: func(cmd *cobra.Command, args []string) {
		if len(args) > 0 {
			if err := config.SetTargetRepoPath(args[0]); err != nil {
				log.Error("%s", err)
				return
			}
			log.Success("Target repository path set successfully")
			return
		}
		if explicit := config.GetDotfilesConfig().TargetRepoPath; explicit != "" {
			log.Info("Target repository path (explicit config): %s", os.ExpandEnv(explicit))
			return
		}
		log.Info(
			"No explicit target set (heuristic would resolve: %s). Pass a path to pin it.",
			config.TargetRepoPath(config.GetGitConfig().DevPath),
		)
	},
}

DotfilesTargetRepoPathCmd represents the command to manage the target repository path.

View Source
var EditCmd = &cobra.Command{
	Use:   "edit",
	Short: "Interactively edit the eng CLI configuration",
	Long: `Launch a beautiful TUI wizard to easily update your .eng.yaml configuration settings.

By default, this command launches the interactive editor. Use --interactive=false if you want to bypass the wizard in the future (though currently, the interactive editor is the primary function of this command).`,
	Example: `  eng config edit
  eng config edit --interactive`,
	RunE: func(cmd *cobra.Command, args []string) error {
		headerStyle := lipgloss.NewStyle().
			Bold(true).
			Foreground(theme.Primary).
			MarginBottom(1)
		if !ui.DisableProgress {
			fmt.Fprintln(log.Out, headerStyle.Render("📝 Interactive Configuration Editor"))
		}

		if !interactiveMode {

			return config.RunInteractiveEditor()
		}
		return config.RunInteractiveEditor()
	},
}

EditCmd represents the edit command.

View Source
var EmailCmd = &cobra.Command{
	Use:   "email",
	Short: "Update config email",
	Long:  `This command should validate and update the email config.`,
	Run: func(cmd *cobra.Command, args []string) {
		log.Start("Checking for email in config file...")
		config.Email()
	},
}

EmailCmd defines the command for setting the user's email in the configuration.

View Source
var GetCmd = &cobra.Command{
	Use:   "get <key>",
	Short: "Print a single configuration value and where it comes from",
	Long: `Prints one configuration value with its source (env, file, or default).

Example: eng config get git.dev_path`,
	Args: cobra.ExactArgs(1),
	RunE: func(_ *cobra.Command, args []string) error {
		key := args[0]
		if _, ok := settableKeys[key]; !ok {
			return fmt.Errorf("unknown config key %q (run `eng config list` for known settings)", key)
		}
		v := config.NewLoader(config.ResolveConfigPath(viper.ConfigFileUsed()))
		rc, err := config.LoadResolved(v)
		if err != nil {
			return err
		}
		log.Message("%s=%s (source: %s)", key, resolvedValue(rc, key), rc.Source[key])
		return nil
	},
}

GetCmd prints a single config value with its source.

View Source
var GitDevPathCmd = &cobra.Command{
	Use:   "git-dev-path",
	Short: "Update config git dev path",
	Long:  `This command sets the path to the development folder containing git repositories in the config file.`,
	Run: func(cmd *cobra.Command, _args []string) {
		log.Start("Checking for development folder path in config file...")
		config.GitDevPath()
	},
}

GitDevPathCmd defines the command for setting the development folder path.

View Source
var GitEditorCmd = &cobra.Command{
	Use:     "git-editor [editor]",
	Aliases: []string{"editor"},
	Short:   "Update config default editor",
	Long: `Select the default editor used by the dashboard (e) from installed editors detected on PATH.

You may also pass the editor command directly. The dashboard falls back to $VISUAL/$EDITOR,
then agy-ide, code, and nano when no default is configured.`,
	Example: `  eng config git-editor
  eng config git-editor agy-ide
  eng config editor code`,
	Run: func(cmd *cobra.Command, args []string) {
		log.Start("Checking for default editor in config file...")
		if len(args) > 0 {
			config.GitEditor(args[0])
		} else {
			config.GitEditor()
		}
	},
}

GitEditorCmd defines the command for setting the default editor.

View Source
var IdeURLCmd = &cobra.Command{
	Use:     "ide-url [url]",
	Aliases: []string{"antigravity-url", "antigravity-ide-url", "ide-download-url"},
	Short:   "Update Antigravity IDE download URL",
	Long:    `This command configures the direct download URL for Antigravity IDE updates in $HOME/.eng.yaml.`,
	Run: func(cmd *cobra.Command, args []string) {
		log.Start("Checking Antigravity IDE download URL in config file...")
		if len(args) > 0 {
			config.IdeURL(args[0])
		} else {
			config.IdeURL()
		}
	},
}

IdeURLCmd defines the command for setting the Antigravity IDE download URL.

View Source
var InitCmd = &cobra.Command{
	Use:   "init",
	Short: "Guided walkthrough to configure eng step by step",
	Long: `Walks through profile, git, dotfiles, proxy, and telemetry settings one
section at a time, showing a preview before writing anything.

Resume mid-walkthrough with --from, or preview without writing with --dry-run.

Example: eng config init --from dotfiles --dry-run`,
	RunE: func(cmd *cobra.Command, _ []string) error {
		if !ui.IsTerminal(os.Stdin) || !ui.IsTerminal(os.Stdout) {
			return fmt.Errorf("config init requires an interactive terminal (stdin and stdout must be TTYs)")
		}
		fromStr, _ := cmd.Flags().GetString("from")
		dryRun, _ := cmd.Flags().GetBool("dry-run")
		step, err := config.ParseWizardStep(fromStr)
		if err != nil {
			return err
		}
		v := config.NewLoader(config.ResolveConfigPath(viper.ConfigFileUsed()))
		rc, err := config.LoadResolved(v)
		if err != nil {
			return err
		}
		changes, err := config.RunWizard(config.DefaultsFromResolved(rc), step)
		if err != nil {
			return err
		}
		rows := make([][]string, 0, len(changes))
		for k, val := range changes {
			rows = append(rows, []string{k, fmt.Sprintf("%v", val)})
		}
		if !ui.DisableProgress {
			fmt.Fprintln(log.Out, theme.InfoBox.Render("Planned changes:\n"+ui.RenderTable(ui.TableOpts{
				Headers: []string{"KEY", "VALUE"},
				Rows:    rows,
			})))
		}
		if dryRun {
			log.Info("Dry run: nothing written")
			return nil
		}
		for k, val := range changes {
			viper.Set(k, val)
		}
		path := config.ResolveConfigPath(viper.ConfigFileUsed())
		if err := viper.WriteConfigAs(path); err != nil {
			return fmt.Errorf("write config: %w", err)
		}
		theme.SuccessMessage(fmt.Sprintf("Configuration saved to %s", path))
		return nil
	},
}

InitCmd runs the guided configuration walkthrough.

View Source
var ListCmd = &cobra.Command{
	Use:     "list",
	Aliases: []string{"show", "ls"},
	Short:   "Display a summary of all active configuration settings",
	Long:    `Renders a formatted overview of all current configuration keys, values, and file paths.`,
	Run: func(cmd *cobra.Command, _args []string) {
		PrintConfigSummary()
	},
}

ListCmd represents the config list/show command.

View Source
var MigrateCmd = &cobra.Command{
	Use:   "migrate",
	Short: "Migrate the config file to the current schema version",
	Long: `Migrates legacy keys to the current schema, deletes renamed keys,
stamps the version, and writes a timestamped backup before changing anything.

Use --check to preview without writing.

Example: eng config migrate --check`,
	RunE: func(cmd *cobra.Command, _ []string) error {
		check, _ := cmd.Flags().GetBool("check")
		path := config.ResolveConfigPath(viper.ConfigFileUsed())
		if check {
			data, err := os.ReadFile(path)
			if err != nil {
				return fmt.Errorf("read config: %w", err)
			}
			var decoded map[string]any
			if err := yaml.Unmarshal(data, &decoded); err != nil {
				return fmt.Errorf("parse config: %w", err)
			}
			_, changed, err := config.MigrateMap(decoded)
			if err != nil {
				return err
			}
			if unknown := config.FindUnknownKeys(path); len(unknown) > 0 {
				log.Warn("Unknown config keys (not read by any command): %s", strings.Join(unknown, ", "))
				log.Info("Drop them with `eng config unset <key>` after confirming nothing depends on them")
			}
			if changed {
				log.Info("Migration needed for %s (run without --check to apply)", path)
			} else {
				theme.SuccessMessage("Configuration is already at the latest schema")
			}
			return nil
		}
		backup, changed, err := config.MigrateFile(path)
		if err != nil {
			return err
		}
		if !changed {
			theme.SuccessMessage("Configuration is already at the latest schema")
			return nil
		}
		theme.SuccessMessage(fmt.Sprintf("Migrated to schema v%d", config.CurrentVersion))
		log.Info("Backup written to %s", backup)
		return nil
	},
}

MigrateCmd migrates the config file to the current schema version.

View Source
var SecretsCmd = &cobra.Command{
	Use:   "secrets",
	Short: "Manage secret storage (bitwarden, keychain, env)",
	Long: `Inspect and migrate secret storage. Tokens resolve with precedence:
env vars first, then Bitwarden items, then OS keychain accounts.
Plaintext config storage is deprecated.

Example: eng config secrets migrate --to keychain --item gitlab`,
}

SecretsCmd manages secret storage for tokens.

View Source
var SecretsMigrateCmd = &cobra.Command{
	Use:   "migrate",
	Short: "Move plaintext tokens from config into keychain or bitwarden",
	Long: `Moves gitlab.token from plaintext config into the chosen store,
clears the plaintext key, and records the reference.

Example: eng config secrets migrate --to keychain --item gitlab`,
	RunE: func(cmd *cobra.Command, _ []string) error {
		to, _ := cmd.Flags().GetString("to")
		item, _ := cmd.Flags().GetString("item")
		provider := config.SecretProvider(to)
		if provider != config.ProviderKeychain && provider != config.ProviderBitwarden {
			return fmt.Errorf("invalid --to %q (use keychain or bitwarden)", to)
		}
		if viper.GetString("gitlab.token") == "" {
			theme.SuccessMessage("No plaintext secrets to migrate")
			return nil
		}
		if err := config.MigratePlaintextToken(provider, item); err != nil {
			return err
		}
		theme.SuccessMessage(fmt.Sprintf("Migrated gitlab.token to %s", provider))
		log.Info("Plaintext key cleared from config")
		return nil
	},
}

SecretsMigrateCmd moves plaintext tokens into a secure store.

View Source
var SetCmd = &cobra.Command{
	Use:   "set <key> <value>",
	Short: "Set a single configuration value",
	Long: `Sets one configuration key and validates it before writing.

Keys: email, verbose, git.dev_path, git.editor, dotfiles.repo_url,
dotfiles.branch, dotfiles.bare_repo_path, dotfiles.worktree_path,
dotfiles.target_repo_path, containers.path, antigravity.ide_download_url.

Example: eng config set email you@example.com`,
	Args: cobra.ExactArgs(2),
	RunE: func(_ *cobra.Command, args []string) error {
		key, value := args[0], args[1]
		viperKey, ok := settableKeys[key]
		if !ok {
			return fmt.Errorf("unknown config key %q (run `eng config list` for known settings)", key)
		}

		if err := validateField(key, value); err != nil {
			return err
		}
		var stored any = value
		if viperKey == "verbose" {
			b, err := strconv.ParseBool(value)
			if err != nil {
				return fmt.Errorf("invalid verbose %q: use true or false", value)
			}
			stored = b
		}
		viper.Set(viperKey, stored)
		path := config.ResolveConfigPath(viper.ConfigFileUsed())
		if err := viper.WriteConfigAs(path); err != nil {
			return fmt.Errorf("write config: %w", err)
		}
		theme.SuccessMessage(fmt.Sprintf("Set %s", key))
		log.Info("Stored in %s", path)
		return nil
	},
}

SetCmd sets a single config value, validating the field before write.

View Source
var TelemetryCmd = &cobra.Command{
	Use:     "telemetry",
	Aliases: []string{"analytics", "metrics"},
	Short:   "Manage OpenPanel telemetry and analytics settings",
	Long: `Configure anonymous telemetry reporting to your OpenPanel instance.
Telemetry helps track command usage, execution performance, and reliability insights.

Privacy respects: The DO_NOT_TRACK=1 and ENG_TELEMETRY_DISABLED=1 environment variables
are honored globally.`,
	Run: func(cmd *cobra.Command, _args []string) {
		config.Telemetry()
	},
}

TelemetryCmd defines the command for managing telemetry and analytics preferences.

View Source
var UnsetCmd = &cobra.Command{
	Use:   "unset <key>",
	Short: "Remove a configuration key from the config file",
	Long: `Removes a top-level or dotted key from the config file entirely.
Useful for dropping orphan keys flagged by validate, e.g. project_filter.

Example: eng config unset project_filter`,
	Args: cobra.ExactArgs(1),
	RunE: func(_ *cobra.Command, args []string) error {
		key := args[0]
		path := config.ResolveConfigPath(viper.ConfigFileUsed())
		data, err := os.ReadFile(path)
		if err != nil {
			return fmt.Errorf("read config: %w", err)
		}
		var decoded map[string]any
		if err := yaml.Unmarshal(data, &decoded); err != nil {
			return fmt.Errorf("parse config: %w", err)
		}
		if !deleteKey(decoded, key) {
			return fmt.Errorf("key %q not present in %s", key, path)
		}
		out, err := yaml.Marshal(decoded)
		if err != nil {
			return fmt.Errorf("encode config: %w", err)
		}
		if err := os.WriteFile(path, out, 0o600); err != nil {
			return fmt.Errorf("write config: %w", err)
		}
		theme.SuccessMessage(fmt.Sprintf("Removed %s", key))
		return nil
	},
}

UnsetCmd removes a configuration key from the config file.

View Source
var ValidateCmd = &cobra.Command{
	Use:   "validate",
	Short: "Validate the resolved configuration",
	Long: `Loads configuration with ENG_ env overrides applied, runs central
validation, and reports one actionable error per invalid field.

Example: eng config validate`,
	RunE: func(_ *cobra.Command, _ []string) error {
		v := config.NewLoader(config.ResolveConfigPath(viper.ConfigFileUsed()))
		rc, err := config.LoadResolved(v)
		if err != nil {
			return err
		}
		errs := rc.Validate()
		if w := config.PlaintextSecretWarning(); w != nil {
			errs = append(errs, *w)
		}
		if len(errs) == 0 {
			theme.SuccessMessage("Configuration is valid")
			return nil
		}
		rows := make([][]string, 0, len(errs))
		for _, fe := range errs {
			rows = append(rows, []string{fe.Field, fe.Value, fe.Hint})
		}
		if !ui.DisableProgress {
			fmt.Fprintln(log.Out, ui.RenderTable(ui.TableOpts{
				Headers: []string{"FIELD", "VALUE", "FIX"},
				Rows:    rows,
			}))
		}
		theme.ErrorMessage("Configuration has validation errors")
		return nil
	},
}

ValidateCmd checks the resolved configuration and reports field errors.

View Source
var VerboseCmd = &cobra.Command{
	Use:   "verbose",
	Short: "Update config verbose setting",
	Long:  `This command should validate and update the verbose config setting.`,
	Run: func(cmd *cobra.Command, _args []string) {
		log.Start("Checking for verbose setting in config file...")
		config.Verbose()
	},
}

VerboseCmd defines the command for managing the verbose output setting.

Functions

func PrintConfigSummary added in v1.41.0

func PrintConfigSummary()

PrintConfigSummary renders a stylized card of all current settings.

func PrintTelemetryStatus added in v1.48.0

func PrintTelemetryStatus()

PrintTelemetryStatus prints a styled summary card of telemetry configuration.

Types

This section is empty.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL