Skip to content

Commit

Permalink
Add user configuration support
Browse files Browse the repository at this point in the history
  • Loading branch information
eandre committed Jan 22, 2025
1 parent 999c539 commit 136727f
Show file tree
Hide file tree
Showing 18 changed files with 1,070 additions and 18 deletions.
127 changes: 127 additions & 0 deletions cli/cmd/encore/config/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
package config

import (
"fmt"
"os"
"strings"

"encr.dev/cli/cmd/encore/cmdutil"
"encr.dev/cli/cmd/encore/root"
"encr.dev/internal/userconfig"
"github.com/spf13/cobra"
)

var (
forceApp, forceGlobal bool
viewAllSettings bool
)

var autoCompleteConfigKeys = cmdutil.AutoCompleteFromStaticList(userconfig.Keys()...)

var longDocs = `Gets or sets configuration values for customizing the behavior of the Encore CLI.
Configuration options can be set both for individual Encore applications,
as well as globally for the local user.
Configuration options can be set using ` + bt("encore config <key> <value>") + `,
and options can similarly be read using ` + bt("encore config <key>") + `.
When running ` + bt("encore config") + ` within an Encore application,
it automatically sets and gets configuration for that application.
To set or get global configuration, use the ` + bt("--global") + ` flag.
Available configuration settings are:
` + userconfig.CLIDocs()

var configCmd = &cobra.Command{
Use: "config <key> [<value>]",
Short: "Get or set a configuration value",
Long: longDocs,
Args: cobra.RangeArgs(0, 2),

Run: func(cmd *cobra.Command, args []string) {
appRoot, _, _ := cmdutil.MaybeAppRoot()

appScope := appRoot != ""
if forceApp {
appScope = true
} else if forceGlobal {
appScope = false
}

if appScope && appRoot == "" {
// If the user specified --app, error if there is no app.
cmdutil.Fatal(cmdutil.ErrNoEncoreApp)
}

if len(args) == 2 {
var err error
if appScope {
err = userconfig.SetForApp(appRoot, args[0], args[1])
} else {
err = userconfig.SetGlobal(args[0], args[1])
}
if err != nil {
cmdutil.Fatal(err)
}
} else {
var (
cfg *userconfig.Config
err error
)
if appScope {
appRoot, _ := cmdutil.AppRoot()
cfg, err = userconfig.ForApp(appRoot).Get()
} else {
cfg, err = userconfig.Global().Get()
}
if err != nil {
cmdutil.Fatal(err)
}

if viewAllSettings {
if len(args) > 0 {
cmdutil.Fatalf("cannot specify a settings key when using --all")
}
s := strings.TrimSuffix(cfg.Render(), "\n")
fmt.Println(s)
return
}

if len(args) == 0 {
// No args are only allowed when --all is specified.
_ = cmd.Usage()
os.Exit(1)
}

val, ok := cfg.GetByKey(args[0])
if !ok {
cmdutil.Fatalf("unknown key %q", args[0])
}
fmt.Printf("%v\n", val)
}
},
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
if len(args) == 0 {
// Completing the first argument, the config key
return autoCompleteConfigKeys(cmd, args, toComplete)
}
return nil, cobra.ShellCompDirectiveNoFileComp
},
}

func init() {
configCmd.Flags().BoolVar(&viewAllSettings, "all", false, "view all settings")
configCmd.Flags().BoolVar(&forceApp, "app", false, "set the value for the current app")
configCmd.Flags().BoolVar(&forceGlobal, "global", false, "set the value at the global level")
configCmd.MarkFlagsMutuallyExclusive("app", "global")

root.Cmd.AddCommand(configCmd)
}

// bt renders a backtick-enclosed string.
func bt(val string) string {
return fmt.Sprintf("`%s`", val)
}
2 changes: 2 additions & 0 deletions cli/cmd/encore/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,10 @@ import (

"encr.dev/cli/cmd/encore/cmdutil"
"encr.dev/cli/cmd/encore/root"

// Register commands
_ "encr.dev/cli/cmd/encore/app"
_ "encr.dev/cli/cmd/encore/config"
_ "encr.dev/cli/cmd/encore/k8s"
_ "encr.dev/cli/cmd/encore/namespace"
_ "encr.dev/cli/cmd/encore/secrets"
Expand Down
15 changes: 14 additions & 1 deletion cli/daemon/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (

"encr.dev/cli/daemon/run"
"encr.dev/internal/optracker"
"encr.dev/internal/userconfig"
"encr.dev/internal/version"
"encr.dev/pkg/fns"
daemonpb "encr.dev/proto/encore/daemon"
Expand All @@ -30,6 +31,13 @@ func (s *Server) Run(req *daemonpb.RunRequest, stream daemonpb.Daemon_RunServer)
})
}

userConfig, err := userconfig.ForApp(req.AppRoot).Get()
if err != nil {
_, _ = fmt.Fprintln(stderr, aurora.Sprintf(aurora.Red("failed to load config: %v"), err))
sendExit(1)
return nil
}

ctx, tracer, err := s.beginTracing(ctx, req.AppRoot, req.WorkingDir, req.TraceFile)
if err != nil {
_, _ = fmt.Fprintln(stderr, aurora.Sprintf(aurora.Red("failed to begin tracing: %v"), err))
Expand Down Expand Up @@ -120,6 +128,11 @@ func (s *Server) Run(req *daemonpb.RunRequest, stream daemonpb.Daemon_RunServer)
displayListenAddr = "localhost" + req.ListenAddr
}

browser := run.BrowserModeFromProto(req.Browser)
if browser == run.BrowserModeAuto {
browser = run.BrowserModeFromConfig(userConfig)
}

runInstance, err := s.mgr.Start(ctx, run.StartParams{
App: app,
NS: ns,
Expand All @@ -129,7 +142,7 @@ func (s *Server) Run(req *daemonpb.RunRequest, stream daemonpb.Daemon_RunServer)
Watch: req.Watch,
Environ: req.Environ,
OpsTracker: ops,
Browser: run.BrowserModeFromProto(req.Browser),
Browser: browser,
Debug: run.DebugModeFromProto(req.DebugMode),
})
if err != nil {
Expand Down
12 changes: 12 additions & 0 deletions cli/daemon/run/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import (
"encr.dev/cli/daemon/run/infra"
"encr.dev/cli/daemon/secret"
"encr.dev/internal/optracker"
"encr.dev/internal/userconfig"
"encr.dev/internal/version"
"encr.dev/pkg/builder"
"encr.dev/pkg/builder/builderimpl"
Expand Down Expand Up @@ -108,6 +109,17 @@ const (
BrowserModeAlways // always open
)

func BrowserModeFromConfig(cfg *userconfig.Config) BrowserMode {
switch cfg.RunBrowser {
case "never":
return BrowserModeNever
case "always":
return BrowserModeAlways
default:
return BrowserModeAuto
}
}

func BrowserModeFromProto(b daemonpb.RunRequest_BrowserMode) BrowserMode {
switch b {
case daemonpb.RunRequest_BROWSER_AUTO:
Expand Down
2 changes: 1 addition & 1 deletion docs/go/cli/cli-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,7 @@ Note that this strips trailing newlines from the secret value.

Lists secrets, optionally for a specific key

```shell
```shell
$ encore secret list [keys...]
```

Expand Down
50 changes: 50 additions & 0 deletions docs/go/cli/config-reference.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
---
seotitle: Encore CLI Configuration Options
seodesc: Configuration options to customize the behavior of the Encore CLI.
title: Configuration Reference
subtitle: Configuration options to customize the behavior of the Encore CLI.
lang: go
---


The Encore CLI has a number of configuration options to customize its behavior.

Configuration options can be set both for individual Encore applications, as well as
globally for the local user.

Configuration options can be set using `encore config <key> <value>`,
and options can similarly be read using `encore config <key>`.

When running `encore config` within an Encore application, it automatically
sets and gets configuration for that application.

To set or get global configuration, use the `--global` flag.

## Configuration files

The configuration is stored in one ore more TOML files on the filesystem.

The configuration is read from the following files, in order:

### Global configuration
* `$XDG_CONFIG_HOME/encore/config`
* `$HOME/.config/encore/config`
* `$HOME/.encoreconfig`

### Application-specific configuration
* `$APP_ROOT/.encore/config`

Where `$APP_ROOT` is the directory containing the `encore.app` file.

The files are read and merged, in the order defined above, with latter files taking precedence over earlier files.

## Configuration options

#### run.browser
Type: string<br/>
Default: auto<br/>
Must be one of: always, never, or auto

Whether to open the Local Development Dashboard in the browser on `encore run`.
If set to "auto", the browser will be opened if the dashboard is not already open.

10 changes: 10 additions & 0 deletions docs/menu.cue
Original file line number Diff line number Diff line change
Expand Up @@ -400,6 +400,11 @@
text: "Infra Namespaces"
path: "/go/cli/infra-namespaces"
file: "go/cli/infra-namespaces"
}, {
kind: "basic"
text: "CLI Configuration"
path: "/go/cli/config-reference"
file: "go/cli/config-reference"
}, {
kind: "basic"
text: "Telemetry"
Expand Down Expand Up @@ -823,6 +828,11 @@
text: "Infra Namespaces"
path: "/ts/cli/infra-namespaces"
file: "ts/cli/infra-namespaces"
}, {
kind: "basic"
text: "CLI Configuration"
path: "/ts/cli/config-reference"
file: "ts/cli/config-reference"
}, {
kind: "basic"
text: "Telemetry"
Expand Down
50 changes: 50 additions & 0 deletions docs/ts/cli/config-reference.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
---
seotitle: Encore CLI Configuration Options
seodesc: Configuration options to customize the behavior of the Encore CLI.
title: Configuration Reference
subtitle: Configuration options to customize the behavior of the Encore CLI.
lang: ts
---


The Encore CLI has a number of configuration options to customize its behavior.

Configuration options can be set both for individual Encore applications, as well as
globally for the local user.

Configuration options can be set using `encore config <key> <value>`,
and options can similarly be read using `encore config <key>`.

When running `encore config` within an Encore application, it automatically
sets and gets configuration for that application.

To set or get global configuration, use the `--global` flag.

## Configuration files

The configuration is stored in one ore more TOML files on the filesystem.

The configuration is read from the following files, in order:

### Global configuration
* `$XDG_CONFIG_HOME/encore/config`
* `$HOME/.config/encore/config`
* `$HOME/.encoreconfig`

### Application-specific configuration
* `$APP_ROOT/.encore/config`

Where `$APP_ROOT` is the directory containing the `encore.app` file.

The files are read and merged, in the order defined above, with latter files taking precedence over earlier files.

## Configuration options

#### run.browser
Type: string<br/>
Default: auto<br/>
Must be one of: always, never, or auto

Whether to open the Local Development Dashboard in the browser on `encore run`.
If set to "auto", the browser will be opened if the dashboard is not already open.

Loading

0 comments on commit 136727f

Please sign in to comment.