Skip to content

Commit 7c348b6

Browse files
authored
Merge branch 'main' into mafredri/fix-httpmw-apikey-missing-oauthconfig-for-db-token
2 parents 41c9b83 + 700ec96 commit 7c348b6

File tree

19 files changed

+226
-140
lines changed

19 files changed

+226
-140
lines changed

.devcontainer/Dockerfile

Lines changed: 0 additions & 83 deletions
This file was deleted.

.devcontainer/devcontainer.json

Lines changed: 9 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,12 @@
1-
// For format details, see https://aka.ms/devcontainer.json
21
{
32
"name": "Development environments on your infrastructure",
4-
5-
// Sets the run context to one level up instead of the .devcontainer folder.
6-
"context": ".",
7-
8-
// Update the 'dockerFile' property if you aren't using the standard 'Dockerfile' filename.
9-
"dockerFile": "Dockerfile",
10-
11-
// Use 'forwardPorts' to make a list of ports inside the container available locally.
12-
// "forwardPorts": [],
13-
14-
"postStartCommand": "dockerd",
15-
16-
// privileged is required by GitHub codespaces - https://github.com/microsoft/vscode-dev-containers/issues/727
17-
"runArgs": [
18-
"--cap-add=SYS_PTRACE",
19-
"--security-opt",
20-
"seccomp=unconfined",
21-
"--privileged",
22-
"--init"
23-
]
3+
"image": "codercom/oss-dogfood:latest",
4+
5+
"features": {
6+
// See all possible options here https://github.com/devcontainers/features/tree/main/src/docker-in-docker
7+
"ghcr.io/devcontainers/features/docker-in-docker:2": {}
8+
},
9+
// SYS_PTRACE to enable go debugging
10+
// without --priviliged the Github Codespace build fails (not required otherwise)
11+
"runArgs": ["--cap-add=SYS_PTRACE", "--privileged"]
2412
}

.golangci.yaml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22
# Over time we should try tightening some of these.
33

44
linters-settings:
5+
exhaustruct:
6+
include:
7+
# Gradually extend to cover more of the codebase.
8+
- 'httpmw\.\w+'
59
gocognit:
610
min-complexity: 46 # Min code complexity (def 30).
711

@@ -195,6 +199,10 @@ issues:
195199
# We use assertions rather than explicitly checking errors in tests
196200
- errcheck
197201
- forcetypeassert
202+
- exhaustruct # This is unhelpful in tests.
203+
- path: scripts/*
204+
linters:
205+
- exhaustruct
198206

199207
fix: true
200208
max-issues-per-linter: 0
@@ -219,6 +227,7 @@ linters:
219227
- errcheck
220228
- errname
221229
- errorlint
230+
- exhaustruct
222231
- exportloopref
223232
- forcetypeassert
224233
- gocritic

cli/clibase/cmd.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -333,6 +333,18 @@ func (inv *Invocation) run(state *runState) error {
333333
)
334334
}
335335

336+
// All options should be set. Check all required options have sources,
337+
// meaning they were set by the user in some way (env, flag, etc).
338+
var missing []string
339+
for _, opt := range inv.Command.Options {
340+
if opt.Required && opt.ValueSource == ValueSourceNone {
341+
missing = append(missing, opt.Flag)
342+
}
343+
}
344+
if len(missing) > 0 {
345+
return xerrors.Errorf("Missing values for the required flags: %s", strings.Join(missing, ", "))
346+
}
347+
336348
if inv.Command.RawArgs {
337349
// If we're at the root command, then the name is omitted
338350
// from the arguments, so we can just use the entire slice.

cli/clibase/cmd_test.go

Lines changed: 87 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ func TestCommand(t *testing.T) {
3838
verbose bool
3939
lower bool
4040
prefix string
41+
reqBool bool
42+
reqStr string
4143
)
4244
return &clibase.Cmd{
4345
Use: "root [subcommand]",
@@ -54,6 +56,34 @@ func TestCommand(t *testing.T) {
5456
},
5557
},
5658
Children: []*clibase.Cmd{
59+
{
60+
Use: "required-flag --req-bool=true --req-string=foo",
61+
Short: "Example with required flags",
62+
Options: clibase.OptionSet{
63+
clibase.Option{
64+
Name: "req-bool",
65+
Flag: "req-bool",
66+
Value: clibase.BoolOf(&reqBool),
67+
Required: true,
68+
},
69+
clibase.Option{
70+
Name: "req-string",
71+
Flag: "req-string",
72+
Value: clibase.Validate(clibase.StringOf(&reqStr), func(value *clibase.String) error {
73+
ok := strings.Contains(value.String(), " ")
74+
if !ok {
75+
return xerrors.Errorf("string must contain a space")
76+
}
77+
return nil
78+
}),
79+
Required: true,
80+
},
81+
},
82+
Handler: func(i *clibase.Invocation) error {
83+
_, _ = i.Stdout.Write([]byte(fmt.Sprintf("%s-%t", reqStr, reqBool)))
84+
return nil
85+
},
86+
},
5787
{
5888
Use: "toupper [word]",
5989
Short: "Converts a word to upper case",
@@ -68,8 +98,8 @@ func TestCommand(t *testing.T) {
6898
Value: clibase.BoolOf(&lower),
6999
},
70100
},
71-
Handler: (func(i *clibase.Invocation) error {
72-
i.Stdout.Write([]byte(prefix))
101+
Handler: func(i *clibase.Invocation) error {
102+
_, _ = i.Stdout.Write([]byte(prefix))
73103
w := i.Args[0]
74104
if lower {
75105
w = strings.ToLower(w)
@@ -85,7 +115,7 @@ func TestCommand(t *testing.T) {
85115
i.Stdout.Write([]byte("!!!"))
86116
}
87117
return nil
88-
}),
118+
},
89119
},
90120
},
91121
}
@@ -213,6 +243,60 @@ func TestCommand(t *testing.T) {
213243
fio := fakeIO(i)
214244
require.Error(t, i.Run(), fio.Stdout.String())
215245
})
246+
247+
t.Run("RequiredFlagsMissing", func(t *testing.T) {
248+
t.Parallel()
249+
i := cmd().Invoke(
250+
"required-flag",
251+
)
252+
fio := fakeIO(i)
253+
err := i.Run()
254+
require.Error(t, err, fio.Stdout.String())
255+
require.ErrorContains(t, err, "Missing values")
256+
})
257+
258+
t.Run("RequiredFlagsMissingBool", func(t *testing.T) {
259+
t.Parallel()
260+
i := cmd().Invoke(
261+
"required-flag", "--req-string", "foo bar",
262+
)
263+
fio := fakeIO(i)
264+
err := i.Run()
265+
require.Error(t, err, fio.Stdout.String())
266+
require.ErrorContains(t, err, "Missing values for the required flags: req-bool")
267+
})
268+
269+
t.Run("RequiredFlagsMissingString", func(t *testing.T) {
270+
t.Parallel()
271+
i := cmd().Invoke(
272+
"required-flag", "--req-bool", "true",
273+
)
274+
fio := fakeIO(i)
275+
err := i.Run()
276+
require.Error(t, err, fio.Stdout.String())
277+
require.ErrorContains(t, err, "Missing values for the required flags: req-string")
278+
})
279+
280+
t.Run("RequiredFlagsInvalid", func(t *testing.T) {
281+
t.Parallel()
282+
i := cmd().Invoke(
283+
"required-flag", "--req-string", "nospace",
284+
)
285+
fio := fakeIO(i)
286+
err := i.Run()
287+
require.Error(t, err, fio.Stdout.String())
288+
require.ErrorContains(t, err, "string must contain a space")
289+
})
290+
291+
t.Run("RequiredFlagsOK", func(t *testing.T) {
292+
t.Parallel()
293+
i := cmd().Invoke(
294+
"required-flag", "--req-bool", "true", "--req-string", "foo bar",
295+
)
296+
fio := fakeIO(i)
297+
err := i.Run()
298+
require.NoError(t, err, fio.Stdout.String())
299+
})
216300
}
217301

218302
func TestCommand_DeepNest(t *testing.T) {

cli/clibase/option.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,10 @@ const (
2323
type Option struct {
2424
Name string `json:"name,omitempty"`
2525
Description string `json:"description,omitempty"`
26+
// Required means this value must be set by some means. It requires
27+
// `ValueSource != ValueSourceNone`
28+
// If `Default` is set, then `Required` is ignored.
29+
Required bool `json:"required,omitempty"`
2630

2731
// Flag is the long name of the flag used to configure this option. If unset,
2832
// flag configuring is disabled.

cli/clibase/values.go

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,40 @@ type NoOptDefValuer interface {
2424
NoOptDefValue() string
2525
}
2626

27+
// Validator is a wrapper around a pflag.Value that allows for validation
28+
// of the value after or before it has been set.
29+
type Validator[T pflag.Value] struct {
30+
Value T
31+
// validate is called after the value is set.
32+
validate func(T) error
33+
}
34+
35+
func Validate[T pflag.Value](opt T, validate func(value T) error) *Validator[T] {
36+
return &Validator[T]{Value: opt, validate: validate}
37+
}
38+
39+
func (i *Validator[T]) String() string {
40+
return i.Value.String()
41+
}
42+
43+
func (i *Validator[T]) Set(input string) error {
44+
err := i.Value.Set(input)
45+
if err != nil {
46+
return err
47+
}
48+
if i.validate != nil {
49+
err = i.validate(i.Value)
50+
if err != nil {
51+
return err
52+
}
53+
}
54+
return nil
55+
}
56+
57+
func (i *Validator[T]) Type() string {
58+
return i.Value.Type()
59+
}
60+
2761
// values.go contains a standard set of value types that can be used as
2862
// Option Values.
2963

@@ -329,10 +363,12 @@ type Struct[T any] struct {
329363
Value T
330364
}
331365

366+
//nolint:revive
332367
func (s *Struct[T]) Set(v string) error {
333368
return yaml.Unmarshal([]byte(v), &s.Value)
334369
}
335370

371+
//nolint:revive
336372
func (s *Struct[T]) String() string {
337373
byt, err := yaml.Marshal(s.Value)
338374
if err != nil {
@@ -361,6 +397,7 @@ func (s *Struct[T]) UnmarshalYAML(n *yaml.Node) error {
361397
return n.Decode(&s.Value)
362398
}
363399

400+
//nolint:revive
364401
func (s *Struct[T]) Type() string {
365402
return fmt.Sprintf("struct[%T]", s.Value)
366403
}

coderd/apidoc/docs.go

Lines changed: 4 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

coderd/apidoc/swagger.json

Lines changed: 4 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)