-
Notifications
You must be signed in to change notification settings - Fork 903
feat: refactor deployment config #6347
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Changes from 85 commits
Commits
Show all changes
89 commits
Select commit
Hold shift + click to select a range
6c29207
Initial scaffold
ammario 12dcc45
Add Duration
ammario d1e2e15
Begin converting AccessURL
ammario 8db4626
fixup! Begin converting AccessURL
ammario dc81f47
Start migrating Server
ammario d511001
Convert a couple more fields
ammario 8cc406e
replace DeploymentConfig[T] with bigcli.[T]
ammario 18b2552
Remove .Value calls
ammario bad7aca
Finish destroying newConfig
ammario f075ad5
delete deployment/
ammario 69600d7
rewrite server.go
ammario 63159a6
ugh
ammario 03b08fa
Server boots
ammario e0d6c1e
Move critical options code into `codersdk`
ammario 5bebd0f
DefaultCacheDir
ammario 2c7b39d
Merge remote-tracking branch 'origin/main' into bigcli
ammario 05b3a10
Fix config dir bug
ammario dcbd0d8
update golden --help
ammario fc06f64
Support NoOptDefValue
ammario d31fa37
Don't hide pflag.FlagSet
ammario 0d7d557
Help template scaffold
ammario f3d45d3
Show env in help output
ammario d322114
Merge remote-tracking branch 'origin/main' into bigcli
ammario 8f9d44b
Write more descriptions
ammario acac92d
Organize telemetry
ammario 1c8359b
Finish grouping up flags
ammario 8d0020f
Render subcommands
ammario 882a80f
Convert Groups to first class bigcli feature
ammario ae858b4
deepMapNode?
ammario b34d481
Add YAML tags everywhere.......
ammario e0e113b
Minor changes
ammario 02c8f6c
Group Enterprise flags
ammario 000466f
Massive refactor to improve help formatting
ammario c6133f2
Improve help formatting again
ammario ab21e8f
Send group descriptions to YAML
ammario b816b1a
Fix YAML tests
ammario 559b046
make gen
ammario 3b24149
Pass linter
ammario e422c1d
Fix slim bugs
ammario ea6c3b7
Start frontend... about to go to sleep 🥱
ammario d4aa8d3
Merge remote-tracking branch 'origin/main' into bigcli
ammario 58b6324
Fix remaining go compilation errors
ammario cae2eab
Do some type generation
ammario d4bd380
Fix most broken frontend code...
ammario 8a493ef
Add envparse library
ammario 84b59be
Add legacy git auth env support
ammario ac3507f
Fix Git Provider rendering in frontend
ammario 95db142
Remove unused lint rules
ammario 790f45a
Update golden files
ammario 360b600
Fix all stories
ammario 2344013
Fix server tests
ammario 394fbfa
Merge remote-tracking branch 'origin/main' into bigcli
ammario ac909b5
Disable clidocsgen.. for now
ammario 8a2637d
Server tests pass?
ammario 3ed919e
Start figuring out CLI array bug
ammario d397839
bigcli: fix slice parsing
ammario 957500b
fix remaining calls to deprecated "experimental"
ammario 0890005
Fix more tests
ammario 622df7f
make gen
ammario 362618e
Fix another server test bug
ammario 2fa8b78
Fix logger!
ammario 28e4b07
fix access url test
ammario 410ad26
make gen
ammario 5c3f648
fix frontend!
ammario f29889f
Merge remote-tracking branch 'origin/main' into bigcli
ammario 6ced9cf
Debug log gitAuthConfigs
ammario d9d1592
No more implicit options
ammario 74c6da2
Re-enable cli docs gen
ammario 9e53c96
De-pointer DeploymentValues
ammario 06f82b5
Decompose DeploymentConfig
ammario 2f5a522
Some comments
ammario 53e92fc
Move envparse into bigcli
ammario e9e2908
Minor changes
ammario c3a11fd
Add context to clitest.Start
ammario a090cd4
Remove dead function
ammario d9a8da6
Merge remote-tracking branch 'origin/main' into bigcli
ammario e53367a
Fix FE
ammario 6252316
make lint passes
ammario bf1a099
Pass highlyConfigurable
ammario 274d5c5
Context bug
ammario a213d25
Rename to clibase
ammario 2938980
fix slim build
ammario 140438e
Merge remote-tracking branch 'origin/main' into bigcli
ammario 46936f5
Fix merge issues
ammario 06277c4
Set max token lifetime correctly
ammario fc8e6ca
Command -> Cmd
ammario 2a74445
Typo
ammario 3cfb4bf
Show disabled badge in zero option table
ammario 4d8bc75
Merge remote-tracking branch 'origin/main' into bigcli
ammario File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
// Package clibase offers an all-in-one solution for a highly configurable CLI | ||
// application. Within Coder, we use it for our `server` subcommand, which | ||
// demands more functionality than cobra/viper can offer. | ||
// | ||
// We will extend its usage to the rest of our application, completely replacing | ||
// cobra/viper. It's also a candidate to be broken out into its own open-source | ||
// library, so we avoid deep coupling with Coder concepts. | ||
package clibase | ||
|
||
import ( | ||
"strings" | ||
|
||
"golang.org/x/exp/maps" | ||
) | ||
|
||
// Group describes a hierarchy of groups that an option or command belongs to. | ||
type Group struct { | ||
Parent *Group `json:"parent,omitempty"` | ||
Name string `json:"name,omitempty"` | ||
Children []Group `json:"children,omitempty"` | ||
Description string `json:"description,omitempty"` | ||
} | ||
|
||
func (g *Group) AddChild(child Group) { | ||
child.Parent = g | ||
g.Children = append(g.Children, child) | ||
} | ||
|
||
// Ancestry returns the group and all of its parents, in order. | ||
func (g *Group) Ancestry() []Group { | ||
if g == nil { | ||
return nil | ||
} | ||
|
||
groups := []Group{*g} | ||
for p := g.Parent; p != nil; p = p.Parent { | ||
// Prepend to the slice so that the order is correct. | ||
groups = append([]Group{*p}, groups...) | ||
} | ||
return groups | ||
} | ||
|
||
func (g *Group) FullName() string { | ||
var names []string | ||
for _, g := range g.Ancestry() { | ||
names = append(names, g.Name) | ||
} | ||
return strings.Join(names, " / ") | ||
} | ||
|
||
// Annotations is an arbitrary key-mapping used to extend the Option and Command types. | ||
// Its methods won't panic if the map is nil. | ||
type Annotations map[string]string | ||
|
||
// Mark sets a value on the annotations map, creating one | ||
// if it doesn't exist. Mark does not mutate the original and | ||
// returns a copy. It is suitable for chaining. | ||
func (a Annotations) Mark(key string, value string) Annotations { | ||
var aa Annotations | ||
if a != nil { | ||
aa = maps.Clone(a) | ||
} else { | ||
aa = make(Annotations) | ||
} | ||
aa[key] = value | ||
return aa | ||
} | ||
|
||
// IsSet returns true if the key is set in the annotations map. | ||
func (a Annotations) IsSet(key string) bool { | ||
if a == nil { | ||
return false | ||
} | ||
_, ok := a[key] | ||
return ok | ||
} | ||
|
||
// Get retrieves a key from the map, returning false if the key is not found | ||
// or the map is nil. | ||
func (a Annotations) Get(key string) (string, bool) { | ||
if a == nil { | ||
return "", false | ||
} | ||
v, ok := a[key] | ||
return v, ok | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
package clibase | ||
|
||
import "strings" | ||
|
||
// Command describes an executable command. | ||
type Command struct { | ||
// Parent is the direct parent of the command. | ||
Parent *Command | ||
// Children is a list of direct descendants. | ||
Children []*Command | ||
// Use is provided in form "command [flags] [args...]". | ||
Use string | ||
// Short is a one-line description of the command. | ||
Short string | ||
// Long is a detailed description of the command, | ||
// presented on its help page. It may contain examples. | ||
Long string | ||
Options OptionSet | ||
Annotations Annotations | ||
} | ||
|
||
// Name returns the first word in the Use string. | ||
func (c *Command) Name() string { | ||
return strings.Split(c.Use, " ")[0] | ||
} | ||
|
||
// FullName returns the full invocation name of the command, | ||
// as seen on the command line. | ||
func (c *Command) FullName() string { | ||
var names []string | ||
|
||
if c.Parent != nil { | ||
names = append(names, c.Parent.FullName()) | ||
} | ||
names = append(names, c.Name()) | ||
return strings.Join(names, " ") | ||
} | ||
|
||
// FullName returns usage of the command, preceded | ||
// by the usage of its parents. | ||
func (c *Command) FullUsage() string { | ||
var uses []string | ||
if c.Parent != nil { | ||
uses = append(uses, c.Parent.FullUsage()) | ||
} | ||
uses = append(uses, c.Use) | ||
return strings.Join(uses, " ") | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
package clibase | ||
ammario marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
import "strings" | ||
|
||
// name returns the name of the environment variable. | ||
func envName(line string) string { | ||
return strings.ToUpper( | ||
strings.SplitN(line, "=", 2)[0], | ||
) | ||
} | ||
|
||
// value returns the value of the environment variable. | ||
func envValue(line string) string { | ||
tokens := strings.SplitN(line, "=", 2) | ||
if len(tokens) < 2 { | ||
return "" | ||
} | ||
return tokens[1] | ||
} | ||
|
||
// Var represents a single environment variable of form | ||
// NAME=VALUE. | ||
type EnvVar struct { | ||
Name string | ||
Value string | ||
} | ||
|
||
// EnvsWithPrefix returns all environment variables starting with | ||
// prefix without said prefix. | ||
func EnvsWithPrefix(environ []string, prefix string) []EnvVar { | ||
var filtered []EnvVar | ||
for _, line := range environ { | ||
name := envName(line) | ||
if strings.HasPrefix(name, prefix) { | ||
filtered = append(filtered, EnvVar{ | ||
Name: strings.TrimPrefix(name, prefix), | ||
Value: envValue(line), | ||
}) | ||
} | ||
} | ||
return filtered | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
package clibase_test | ||
|
||
import ( | ||
"reflect" | ||
"testing" | ||
|
||
"github.com/coder/coder/cli/clibase" | ||
) | ||
|
||
func TestFilterNamePrefix(t *testing.T) { | ||
t.Parallel() | ||
type args struct { | ||
environ []string | ||
prefix string | ||
} | ||
tests := []struct { | ||
name string | ||
args args | ||
want []clibase.EnvVar | ||
}{ | ||
{"empty", args{[]string{}, "SHIRE"}, nil}, | ||
{ | ||
"ONE", | ||
args{ | ||
[]string{ | ||
"SHIRE_BRANDYBUCK=hmm", | ||
}, | ||
"SHIRE_", | ||
}, | ||
[]clibase.EnvVar{ | ||
{Name: "BRANDYBUCK", Value: "hmm"}, | ||
}, | ||
}, | ||
} | ||
for _, tt := range tests { | ||
tt := tt | ||
t.Run(tt.name, func(t *testing.T) { | ||
t.Parallel() | ||
if got := clibase.EnvsWithPrefix(tt.args.environ, tt.args.prefix); !reflect.DeepEqual(got, tt.want) { | ||
t.Errorf("EnvsWithPrefix() = %v, want %v", got, tt.want) | ||
} | ||
}) | ||
} | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,149 @@ | ||
package clibase | ||
|
||
import ( | ||
"os" | ||
|
||
"github.com/hashicorp/go-multierror" | ||
"github.com/spf13/pflag" | ||
"golang.org/x/xerrors" | ||
) | ||
|
||
// Option is a configuration option for a CLI application. | ||
type Option struct { | ||
Name string `json:"name,omitempty"` | ||
Description string `json:"description,omitempty"` | ||
|
||
// Flag is the long name of the flag used to configure this option. If unset, | ||
// flag configuring is disabled. | ||
Flag string `json:"flag,omitempty"` | ||
// FlagShorthand is the one-character shorthand for the flag. If unset, no | ||
// shorthand is used. | ||
FlagShorthand string `json:"flag_shorthand,omitempty"` | ||
|
||
// Env is the environment variable used to configure this option. If unset, | ||
// environment configuring is disabled. | ||
Env string `json:"env,omitempty"` | ||
|
||
// YAML is the YAML key used to configure this option. If unset, YAML | ||
// configuring is disabled. | ||
YAML string `json:"yaml,omitempty"` | ||
|
||
// Default is parsed into Value if set. | ||
Default string `json:"default,omitempty"` | ||
// Value includes the types listed in values.go. | ||
Value pflag.Value `json:"value,omitempty"` | ||
|
||
// Annotations enable extensions to clibase higher up in the stack. It's useful for | ||
// help formatting and documentation generation. | ||
Annotations Annotations `json:"annotations,omitempty"` | ||
|
||
// Group is a group hierarchy that helps organize this option in help, configs | ||
// and other documentation. | ||
Group *Group `json:"group,omitempty"` | ||
|
||
// UseInstead is a list of options that should be used instead of this one. | ||
// The field is used to generate a deprecation warning. | ||
UseInstead []Option `json:"use_instead,omitempty"` | ||
|
||
Hidden bool `json:"hidden,omitempty"` | ||
} | ||
|
||
// OptionSet is a group of options that can be applied to a command. | ||
type OptionSet []Option | ||
|
||
// Add adds the given Options to the OptionSet. | ||
func (s *OptionSet) Add(opts ...Option) { | ||
*s = append(*s, opts...) | ||
} | ||
|
||
// FlagSet returns a pflag.FlagSet for the OptionSet. | ||
func (s *OptionSet) FlagSet() *pflag.FlagSet { | ||
fs := pflag.NewFlagSet("", pflag.ContinueOnError) | ||
for _, opt := range *s { | ||
if opt.Flag == "" { | ||
continue | ||
} | ||
var noOptDefValue string | ||
{ | ||
no, ok := opt.Value.(NoOptDefValuer) | ||
if ok { | ||
noOptDefValue = no.NoOptDefValue() | ||
} | ||
} | ||
|
||
fs.AddFlag(&pflag.Flag{ | ||
Name: opt.Flag, | ||
Shorthand: opt.FlagShorthand, | ||
Usage: opt.Description, | ||
Value: opt.Value, | ||
DefValue: "", | ||
Changed: false, | ||
Deprecated: "", | ||
NoOptDefVal: noOptDefValue, | ||
Hidden: opt.Hidden, | ||
}) | ||
} | ||
fs.Usage = func() { | ||
_, _ = os.Stderr.WriteString("Override (*FlagSet).Usage() to print help text.\n") | ||
} | ||
return fs | ||
} | ||
|
||
// ParseEnv parses the given environment variables into the OptionSet. | ||
func (s *OptionSet) ParseEnv(globalPrefix string, environ []string) error { | ||
var merr *multierror.Error | ||
|
||
// We parse environment variables first instead of using a nested loop to | ||
// avoid N*M complexity when there are a lot of options and environment | ||
// variables. | ||
envs := make(map[string]string) | ||
for _, v := range EnvsWithPrefix(environ, globalPrefix) { | ||
envs[v.Name] = v.Value | ||
} | ||
|
||
for _, opt := range *s { | ||
if opt.Env == "" { | ||
continue | ||
} | ||
|
||
envVal, ok := envs[opt.Env] | ||
if !ok { | ||
continue | ||
} | ||
|
||
if err := opt.Value.Set(envVal); err != nil { | ||
merr = multierror.Append( | ||
merr, xerrors.Errorf("parse %q: %w", opt.Name, err), | ||
) | ||
} | ||
} | ||
|
||
return merr.ErrorOrNil() | ||
} | ||
|
||
// SetDefaults sets the default values for each Option. | ||
// It should be called before all parsing (e.g. ParseFlags, ParseEnv). | ||
func (s *OptionSet) SetDefaults() error { | ||
var merr *multierror.Error | ||
for _, opt := range *s { | ||
if opt.Default == "" { | ||
continue | ||
} | ||
if opt.Value == nil { | ||
merr = multierror.Append( | ||
merr, | ||
xerrors.Errorf( | ||
"parse %q: no Value field set\nFull opt: %+v", | ||
opt.Name, opt, | ||
), | ||
) | ||
continue | ||
} | ||
if err := opt.Value.Set(opt.Default); err != nil { | ||
merr = multierror.Append( | ||
merr, xerrors.Errorf("parse %q: %w", opt.Name, err), | ||
) | ||
} | ||
} | ||
return merr.ErrorOrNil() | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.