Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit3d70b2a

Browse files
committed
Merge branch 'main' into exportstats
2 parents0037a64 +d67552f commit3d70b2a

File tree

137 files changed

+6909
-7508
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

137 files changed

+6909
-7508
lines changed

‎.golangci.yaml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,6 @@ linters:
215215
-asciicheck
216216
-bidichk
217217
-bodyclose
218-
-deadcode
219218
-dogsled
220219
-errcheck
221220
-errname
@@ -259,4 +258,3 @@ linters:
259258
-typecheck
260259
-unconvert
261260
-unused
262-
-varcheck

‎Makefile

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -501,7 +501,8 @@ docs/admin/prometheus.md: scripts/metricsdocgen/main.go scripts/metricsdocgen/me
501501
yarn run format:write:only ../docs/admin/prometheus.md
502502

503503
docs/cli.md: scripts/clidocgen/main.go$(GO_SRC_FILES) docs/manifest.json
504-
rm -rf ./docs/cli/*.md
504+
# TODO(@ammario): re-enable server.md once we finish clibase migration.
505+
ls ./docs/cli/*.md| grep -vP"\/coder_server"| xargs rm
505506
BASE_PATH="." go run ./scripts/clidocgen
506507
cd site
507508
yarn run format:write:only ../docs/cli.md ../docs/cli/*.md ../docs/manifest.json

‎cli/clibase/clibase.go

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
// Package clibase offers an all-in-one solution for a highly configurable CLI
2+
// application. Within Coder, we use it for our `server` subcommand, which
3+
// demands more functionality than cobra/viper can offer.
4+
//
5+
// We will extend its usage to the rest of our application, completely replacing
6+
// cobra/viper. It's also a candidate to be broken out into its own open-source
7+
// library, so we avoid deep coupling with Coder concepts.
8+
package clibase
9+
10+
import (
11+
"strings"
12+
13+
"golang.org/x/exp/maps"
14+
)
15+
16+
// Group describes a hierarchy of groups that an option or command belongs to.
17+
typeGroupstruct {
18+
Parent*Group`json:"parent,omitempty"`
19+
Namestring`json:"name,omitempty"`
20+
Children []Group`json:"children,omitempty"`
21+
Descriptionstring`json:"description,omitempty"`
22+
}
23+
24+
func (g*Group)AddChild(childGroup) {
25+
child.Parent=g
26+
g.Children=append(g.Children,child)
27+
}
28+
29+
// Ancestry returns the group and all of its parents, in order.
30+
func (g*Group)Ancestry() []Group {
31+
ifg==nil {
32+
returnnil
33+
}
34+
35+
groups:= []Group{*g}
36+
forp:=g.Parent;p!=nil;p=p.Parent {
37+
// Prepend to the slice so that the order is correct.
38+
groups=append([]Group{*p},groups...)
39+
}
40+
returngroups
41+
}
42+
43+
func (g*Group)FullName()string {
44+
varnames []string
45+
for_,g:=rangeg.Ancestry() {
46+
names=append(names,g.Name)
47+
}
48+
returnstrings.Join(names," / ")
49+
}
50+
51+
// Annotations is an arbitrary key-mapping used to extend the Option and Command types.
52+
// Its methods won't panic if the map is nil.
53+
typeAnnotationsmap[string]string
54+
55+
// Mark sets a value on the annotations map, creating one
56+
// if it doesn't exist. Mark does not mutate the original and
57+
// returns a copy. It is suitable for chaining.
58+
func (aAnnotations)Mark(keystring,valuestring)Annotations {
59+
varaaAnnotations
60+
ifa!=nil {
61+
aa=maps.Clone(a)
62+
}else {
63+
aa=make(Annotations)
64+
}
65+
aa[key]=value
66+
returnaa
67+
}
68+
69+
// IsSet returns true if the key is set in the annotations map.
70+
func (aAnnotations)IsSet(keystring)bool {
71+
ifa==nil {
72+
returnfalse
73+
}
74+
_,ok:=a[key]
75+
returnok
76+
}
77+
78+
// Get retrieves a key from the map, returning false if the key is not found
79+
// or the map is nil.
80+
func (aAnnotations)Get(keystring) (string,bool) {
81+
ifa==nil {
82+
return"",false
83+
}
84+
v,ok:=a[key]
85+
returnv,ok
86+
}

‎cli/clibase/cmd.go

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package clibase
2+
3+
import"strings"
4+
5+
// Cmd describes an executable command.
6+
typeCmdstruct {
7+
// Parent is the direct parent of the command.
8+
Parent*Cmd
9+
// Children is a list of direct descendants.
10+
Children []*Cmd
11+
// Use is provided in form "command [flags] [args...]".
12+
Usestring
13+
// Short is a one-line description of the command.
14+
Shortstring
15+
// Long is a detailed description of the command,
16+
// presented on its help page. It may contain examples.
17+
Longstring
18+
OptionsOptionSet
19+
AnnotationsAnnotations
20+
}
21+
22+
// Name returns the first word in the Use string.
23+
func (c*Cmd)Name()string {
24+
returnstrings.Split(c.Use," ")[0]
25+
}
26+
27+
// FullName returns the full invocation name of the command,
28+
// as seen on the command line.
29+
func (c*Cmd)FullName()string {
30+
varnames []string
31+
32+
ifc.Parent!=nil {
33+
names=append(names,c.Parent.FullName())
34+
}
35+
names=append(names,c.Name())
36+
returnstrings.Join(names," ")
37+
}
38+
39+
// FullName returns usage of the command, preceded
40+
// by the usage of its parents.
41+
func (c*Cmd)FullUsage()string {
42+
varuses []string
43+
ifc.Parent!=nil {
44+
uses=append(uses,c.Parent.FullUsage())
45+
}
46+
uses=append(uses,c.Use)
47+
returnstrings.Join(uses," ")
48+
}

‎cli/clibase/env.go

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package clibase
2+
3+
import"strings"
4+
5+
// name returns the name of the environment variable.
6+
funcenvName(linestring)string {
7+
returnstrings.ToUpper(
8+
strings.SplitN(line,"=",2)[0],
9+
)
10+
}
11+
12+
// value returns the value of the environment variable.
13+
funcenvValue(linestring)string {
14+
tokens:=strings.SplitN(line,"=",2)
15+
iflen(tokens)<2 {
16+
return""
17+
}
18+
returntokens[1]
19+
}
20+
21+
// Var represents a single environment variable of form
22+
// NAME=VALUE.
23+
typeEnvVarstruct {
24+
Namestring
25+
Valuestring
26+
}
27+
28+
// EnvsWithPrefix returns all environment variables starting with
29+
// prefix without said prefix.
30+
funcEnvsWithPrefix(environ []string,prefixstring) []EnvVar {
31+
varfiltered []EnvVar
32+
for_,line:=rangeenviron {
33+
name:=envName(line)
34+
ifstrings.HasPrefix(name,prefix) {
35+
filtered=append(filtered,EnvVar{
36+
Name:strings.TrimPrefix(name,prefix),
37+
Value:envValue(line),
38+
})
39+
}
40+
}
41+
returnfiltered
42+
}

‎cli/clibase/env_test.go

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package clibase_test
2+
3+
import (
4+
"reflect"
5+
"testing"
6+
7+
"github.com/coder/coder/cli/clibase"
8+
)
9+
10+
funcTestFilterNamePrefix(t*testing.T) {
11+
t.Parallel()
12+
typeargsstruct {
13+
environ []string
14+
prefixstring
15+
}
16+
tests:= []struct {
17+
namestring
18+
argsargs
19+
want []clibase.EnvVar
20+
}{
21+
{"empty",args{[]string{},"SHIRE"},nil},
22+
{
23+
"ONE",
24+
args{
25+
[]string{
26+
"SHIRE_BRANDYBUCK=hmm",
27+
},
28+
"SHIRE_",
29+
},
30+
[]clibase.EnvVar{
31+
{Name:"BRANDYBUCK",Value:"hmm"},
32+
},
33+
},
34+
}
35+
for_,tt:=rangetests {
36+
tt:=tt
37+
t.Run(tt.name,func(t*testing.T) {
38+
t.Parallel()
39+
ifgot:=clibase.EnvsWithPrefix(tt.args.environ,tt.args.prefix);!reflect.DeepEqual(got,tt.want) {
40+
t.Errorf("EnvsWithPrefix() = %v, want %v",got,tt.want)
41+
}
42+
})
43+
}
44+
}

‎cli/clibase/option.go

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
package clibase
2+
3+
import (
4+
"os"
5+
6+
"github.com/hashicorp/go-multierror"
7+
"github.com/spf13/pflag"
8+
"golang.org/x/xerrors"
9+
)
10+
11+
// Option is a configuration option for a CLI application.
12+
typeOptionstruct {
13+
Namestring`json:"name,omitempty"`
14+
Descriptionstring`json:"description,omitempty"`
15+
16+
// Flag is the long name of the flag used to configure this option. If unset,
17+
// flag configuring is disabled.
18+
Flagstring`json:"flag,omitempty"`
19+
// FlagShorthand is the one-character shorthand for the flag. If unset, no
20+
// shorthand is used.
21+
FlagShorthandstring`json:"flag_shorthand,omitempty"`
22+
23+
// Env is the environment variable used to configure this option. If unset,
24+
// environment configuring is disabled.
25+
Envstring`json:"env,omitempty"`
26+
27+
// YAML is the YAML key used to configure this option. If unset, YAML
28+
// configuring is disabled.
29+
YAMLstring`json:"yaml,omitempty"`
30+
31+
// Default is parsed into Value if set.
32+
Defaultstring`json:"default,omitempty"`
33+
// Value includes the types listed in values.go.
34+
Value pflag.Value`json:"value,omitempty"`
35+
36+
// Annotations enable extensions to clibase higher up in the stack. It's useful for
37+
// help formatting and documentation generation.
38+
AnnotationsAnnotations`json:"annotations,omitempty"`
39+
40+
// Group is a group hierarchy that helps organize this option in help, configs
41+
// and other documentation.
42+
Group*Group`json:"group,omitempty"`
43+
44+
// UseInstead is a list of options that should be used instead of this one.
45+
// The field is used to generate a deprecation warning.
46+
UseInstead []Option`json:"use_instead,omitempty"`
47+
48+
Hiddenbool`json:"hidden,omitempty"`
49+
}
50+
51+
// OptionSet is a group of options that can be applied to a command.
52+
typeOptionSet []Option
53+
54+
// Add adds the given Options to the OptionSet.
55+
func (s*OptionSet)Add(opts...Option) {
56+
*s=append(*s,opts...)
57+
}
58+
59+
// FlagSet returns a pflag.FlagSet for the OptionSet.
60+
func (s*OptionSet)FlagSet()*pflag.FlagSet {
61+
fs:=pflag.NewFlagSet("",pflag.ContinueOnError)
62+
for_,opt:=range*s {
63+
ifopt.Flag=="" {
64+
continue
65+
}
66+
varnoOptDefValuestring
67+
{
68+
no,ok:=opt.Value.(NoOptDefValuer)
69+
ifok {
70+
noOptDefValue=no.NoOptDefValue()
71+
}
72+
}
73+
74+
fs.AddFlag(&pflag.Flag{
75+
Name:opt.Flag,
76+
Shorthand:opt.FlagShorthand,
77+
Usage:opt.Description,
78+
Value:opt.Value,
79+
DefValue:"",
80+
Changed:false,
81+
Deprecated:"",
82+
NoOptDefVal:noOptDefValue,
83+
Hidden:opt.Hidden,
84+
})
85+
}
86+
fs.Usage=func() {
87+
_,_=os.Stderr.WriteString("Override (*FlagSet).Usage() to print help text.\n")
88+
}
89+
returnfs
90+
}
91+
92+
// ParseEnv parses the given environment variables into the OptionSet.
93+
func (s*OptionSet)ParseEnv(globalPrefixstring,environ []string)error {
94+
varmerr*multierror.Error
95+
96+
// We parse environment variables first instead of using a nested loop to
97+
// avoid N*M complexity when there are a lot of options and environment
98+
// variables.
99+
envs:=make(map[string]string)
100+
for_,v:=rangeEnvsWithPrefix(environ,globalPrefix) {
101+
envs[v.Name]=v.Value
102+
}
103+
104+
for_,opt:=range*s {
105+
ifopt.Env=="" {
106+
continue
107+
}
108+
109+
envVal,ok:=envs[opt.Env]
110+
if!ok {
111+
continue
112+
}
113+
114+
iferr:=opt.Value.Set(envVal);err!=nil {
115+
merr=multierror.Append(
116+
merr,xerrors.Errorf("parse %q: %w",opt.Name,err),
117+
)
118+
}
119+
}
120+
121+
returnmerr.ErrorOrNil()
122+
}
123+
124+
// SetDefaults sets the default values for each Option.
125+
// It should be called before all parsing (e.g. ParseFlags, ParseEnv).
126+
func (s*OptionSet)SetDefaults()error {
127+
varmerr*multierror.Error
128+
for_,opt:=range*s {
129+
ifopt.Default=="" {
130+
continue
131+
}
132+
ifopt.Value==nil {
133+
merr=multierror.Append(
134+
merr,
135+
xerrors.Errorf(
136+
"parse %q: no Value field set\nFull opt: %+v",
137+
opt.Name,opt,
138+
),
139+
)
140+
continue
141+
}
142+
iferr:=opt.Value.Set(opt.Default);err!=nil {
143+
merr=multierror.Append(
144+
merr,xerrors.Errorf("parse %q: %w",opt.Name,err),
145+
)
146+
}
147+
}
148+
returnmerr.ErrorOrNil()
149+
}

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp