Skip to content

Commit

Permalink
config: Use global user config if required (#1378)
Browse files Browse the repository at this point in the history
If a config file is not found in a parent of the workspace, then we
attempt to load the user's global default file instead.

Signed-off-by: Charlie Egan <charlie@styra.com>
  • Loading branch information
charlieegan3 authored Jan 30, 2025
1 parent 1f9e2e4 commit a295fef
Show file tree
Hide file tree
Showing 5 changed files with 52 additions and 11 deletions.
17 changes: 15 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -403,13 +403,26 @@ project:
Regal will automatically search for a configuration file (`.regal/config.yaml`
or `.regal.yaml`) in the current directory, and if not found, traverse the
parent directories either until either one is found, or the top of the directory
hierarchy is reached. If no configuration file is found, Regal will use the
default configuration.
hierarchy is reached. If no configuration file is found, and no file is found at
`~/.config/regal/config.yaml` either, Regal will use the default configuration.

A custom configuration may be also be provided using the `--config-file`/`-c`
option for `regal lint`, which when provided will be used to override the
default configuration.

### User-level Configuration

Generally, users will want to commit their Regal configuration file to the repo
containing their Rego source code. This allows configurations to be shared
among team members and makes the configuration options available to Regal when
running as a [CI linter](https://docs.styra.com/regal/cicd) too.

Sometimes however it can be handy to have some user defaults when a project
configuration file is not found, hasn't been created yet or is not applicable.

In such cases Regal will check for a configuration file at
`~/.config/regal/config.yaml` instead.

## Ignoring Rules

If one of Regal's rules doesn't align with your team's preferences, don't worry! Regal is not meant to be the law,
Expand Down
2 changes: 1 addition & 1 deletion cmd/lint.go
Original file line number Diff line number Diff line change
Expand Up @@ -362,7 +362,7 @@ func updateCheckAndWarn(params *lintCommandParams, regalRules *bundle.Bundle, us
CurrentVersion: version.Version,
CurrentTime: time.Now().UTC(),
Debug: params.debug,
StateDir: config.GlobalDir(),
StateDir: config.GlobalConfigDir(true),
}, os.Stderr)
}
}
Expand Down
15 changes: 15 additions & 0 deletions cmd/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
"fmt"
"os"
"path/filepath"
"time"

"github.com/styrainc/regal/pkg/config"
Expand All @@ -29,6 +30,20 @@ func readUserConfig(params configFileParams, searchPath string) (userConfig *os.
userConfig, err = config.FindConfig(searchPath)
}

// if there is no config found, attempt to load the user's global config if
// it exists
if err != nil {
globalConfigDir := config.GlobalConfigDir(false)
if globalConfigDir != "" {
globalConfigFile := filepath.Join(globalConfigDir, "config.yaml")

userConfig, err = os.Open(globalConfigFile)
if err != nil {
return nil, fmt.Errorf("failed to open global config file %w", err)
}
}
}

return userConfig, err //nolint:wrapcheck
}

Expand Down
16 changes: 12 additions & 4 deletions internal/lsp/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -660,7 +660,7 @@ func (l *LanguageServer) StartConfigWorker(ctx context.Context) {
CurrentVersion: version.Version,
CurrentTime: time.Now().UTC(),
Debug: false,
StateDir: config.GlobalDir(),
StateDir: config.GlobalConfigDir(true),
}, os.Stderr)
}
}()
Expand Down Expand Up @@ -2359,11 +2359,19 @@ func (l *LanguageServer) handleInitialize(ctx context.Context, params types.Init
})

configFile, err := config.FindConfig(workspaceRootPath)
if err == nil {

globalConfigDir := config.GlobalConfigDir(false)

switch {
case err == nil:
l.logf(log.LevelMessage, "using config file: %s", configFile.Name())
l.configWatcher.Watch(configFile.Name())
} else {
l.logf(log.LevelMessage, "no config file found in workspace: %s", err)
case globalConfigDir != "":
globalConfigFile := filepath.Join(globalConfigDir, "config.yaml")
l.logf(log.LevelMessage, "using global config file: %s", globalConfigFile)
l.configWatcher.Watch(globalConfigFile)
default:
l.logf(log.LevelMessage, "no config file found for workspace: %s", err)
}

if _, err = l.loadWorkspaceContents(ctx, false); err != nil {
Expand Down
13 changes: 9 additions & 4 deletions pkg/config/global.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,22 @@ import (
"path/filepath"
)

// GlobalDir is the config directory that will be used for user-wide configuration.
// This is different from the .regal directories that are searched for when
// linting.
func GlobalDir() string {
// GlobalConfigDir is the config directory that will be used for user-wide
// configuration. This is different from the .regal directories that are
// searched for when linting. If create is false, the function will return an
// empty string if the directory does not exist.
func GlobalConfigDir(create bool) string {
cfgDir, err := os.UserHomeDir()
if err != nil {
return ""
}

regalDir := filepath.Join(cfgDir, ".config", "regal")
if _, err := os.Stat(regalDir); os.IsNotExist(err) {
if !create {
return ""
}

if err := os.Mkdir(regalDir, os.ModePerm); err != nil {
return ""
}
Expand Down

0 comments on commit a295fef

Please # to comment.