Skip to content

Commit eaac6c1

Browse files
committed
Merge branch 'main' of https://github.com/coder/coder into bq/add-registered-users-endpoint
2 parents c011a05 + b6d0b77 commit eaac6c1

File tree

90 files changed

+2664
-857
lines changed

Some content is hidden

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

90 files changed

+2664
-857
lines changed

.github/.linkspector.yml

+16-3
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,22 @@
11
dirs:
22
- docs
3+
excludedDirs:
4+
# Downstream bug in linkspector means large markdown files fail to parse
5+
# but these are autogenerated and shouldn't need checking
6+
- docs/reference
7+
# Older changelogs may contain broken links
8+
- docs/changelogs
39
ignorePatterns:
4-
- pattern: "://localhost"
5-
- pattern: '://.*.?example\\.com'
6-
- pattern: "github.com/<your_github_handle>"
10+
- pattern: "localhost"
11+
- pattern: "example.com"
712
- pattern: "mailto:"
13+
- pattern: "127.0.0.1"
14+
- pattern: "0.0.0.0"
15+
- pattern: "JFROG_URL"
16+
- pattern: "coder.company.org"
17+
# These real sites were blocking the linkspector action / GitHub runner IPs(?)
18+
- pattern: "i.imgur.com"
19+
- pattern: "code.visualstudio.com"
20+
- pattern: "www.emacswiki.org"
821
aliveStatusCodes:
922
- 200

.github/workflows/ci.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ jobs:
8080
- "cmd/**"
8181
- "coderd/**"
8282
- "enterprise/**"
83-
- "examples/*"
83+
- "examples/**"
8484
- "helm/**"
8585
- "provisioner/**"
8686
- "provisionerd/**"

.github/workflows/weekly-docs.yaml

+8-5
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,20 @@ on:
44
schedule:
55
- cron: "0 9 * * 1"
66
workflow_dispatch: # allows to run manually for testing
7-
# pull_request:
8-
# branches:
9-
# - main
10-
# paths:
11-
# - "docs/**"
7+
pull_request:
8+
branches:
9+
- main
10+
paths:
11+
- "docs/**"
1212

1313
permissions:
1414
contents: read
1515

1616
jobs:
1717
check-docs:
1818
runs-on: ubuntu-latest
19+
permissions:
20+
pull-requests: write # required to post PR review comments by the action
1921
steps:
2022
- name: Harden Runner
2123
uses: step-security/harden-runner@91182cccc01eb5e619899d80e4e971d6181294a7 # v2.10.1
@@ -33,6 +35,7 @@ jobs:
3335
reporter: github-pr-review
3436
config_file: ".github/.linkspector.yml"
3537
fail_on_error: "true"
38+
filter_mode: "nofilter"
3639

3740
- name: Send Slack notification
3841
if: failure() && github.event_name == 'schedule'

cli/clilog/clilog.go

+8-7
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,11 @@ import (
44
"context"
55
"fmt"
66
"io"
7-
"os"
87
"regexp"
98
"strings"
109

1110
"golang.org/x/xerrors"
11+
"gopkg.in/natefinch/lumberjack.v2"
1212

1313
"cdr.dev/slog"
1414
"cdr.dev/slog/sloggers/sloghuman"
@@ -104,20 +104,21 @@ func (b *Builder) Build(inv *serpent.Invocation) (log slog.Logger, closeLog func
104104
addSinkIfProvided := func(sinkFn func(io.Writer) slog.Sink, loc string) error {
105105
switch loc {
106106
case "":
107-
108107
case "/dev/stdout":
109108
sinks = append(sinks, sinkFn(inv.Stdout))
110109

111110
case "/dev/stderr":
112111
sinks = append(sinks, sinkFn(inv.Stderr))
113112

114113
default:
115-
fi, err := os.OpenFile(loc, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0o644)
116-
if err != nil {
117-
return xerrors.Errorf("open log file %q: %w", loc, err)
114+
logWriter := &lumberjack.Logger{
115+
Filename: loc,
116+
MaxSize: 5, // MB
117+
// Without this, rotated logs will never be deleted.
118+
MaxBackups: 1,
118119
}
119-
closers = append(closers, fi.Close)
120-
sinks = append(sinks, sinkFn(fi))
120+
closers = append(closers, logWriter.Close)
121+
sinks = append(sinks, sinkFn(logWriter))
121122
}
122123
return nil
123124
}

cli/clilog/clilog_test.go

-25
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ package clilog_test
22

33
import (
44
"encoding/json"
5-
"io/fs"
65
"os"
76
"path/filepath"
87
"strings"
@@ -145,30 +144,6 @@ func TestBuilder(t *testing.T) {
145144
assertLogsJSON(t, tempJSON, info, infoLog, warn, warnLog)
146145
})
147146
})
148-
149-
t.Run("NotFound", func(t *testing.T) {
150-
t.Parallel()
151-
152-
tempFile := filepath.Join(t.TempDir(), "doesnotexist", "test.log")
153-
cmd := &serpent.Command{
154-
Use: "test",
155-
Handler: func(inv *serpent.Invocation) error {
156-
logger, closeLog, err := clilog.New(
157-
clilog.WithFilter("foo", "baz"),
158-
clilog.WithHuman(tempFile),
159-
clilog.WithVerbose(),
160-
).Build(inv)
161-
if err != nil {
162-
return err
163-
}
164-
defer closeLog()
165-
logger.Error(inv.Context(), "you will never see this")
166-
return nil
167-
},
168-
}
169-
err := cmd.Invoke().Run()
170-
require.ErrorIs(t, err, fs.ErrNotExist)
171-
})
172147
}
173148

174149
var (

cli/create_test.go

+27-17
Original file line numberDiff line numberDiff line change
@@ -864,24 +864,34 @@ func TestCreateValidateRichParameters(t *testing.T) {
864864
coderdtest.AwaitTemplateVersionJobCompleted(t, client, version.ID)
865865
template := coderdtest.CreateTemplate(t, client, owner.OrganizationID, version.ID)
866866

867-
inv, root := clitest.New(t, "create", "my-workspace", "--template", template.Name)
868-
clitest.SetupConfig(t, member, root)
869-
pty := ptytest.New(t).Attach(inv)
870-
clitest.Start(t, inv)
867+
t.Run("Prompt", func(t *testing.T) {
868+
inv, root := clitest.New(t, "create", "my-workspace-1", "--template", template.Name)
869+
clitest.SetupConfig(t, member, root)
870+
pty := ptytest.New(t).Attach(inv)
871+
clitest.Start(t, inv)
872+
873+
pty.ExpectMatch(listOfStringsParameterName)
874+
pty.ExpectMatch("aaa, bbb, ccc")
875+
pty.ExpectMatch("Confirm create?")
876+
pty.WriteLine("yes")
877+
})
871878

872-
matches := []string{
873-
listOfStringsParameterName, "",
874-
"aaa, bbb, ccc", "",
875-
"Confirm create?", "yes",
876-
}
877-
for i := 0; i < len(matches); i += 2 {
878-
match := matches[i]
879-
value := matches[i+1]
880-
pty.ExpectMatch(match)
881-
if value != "" {
882-
pty.WriteLine(value)
883-
}
884-
}
879+
t.Run("Default", func(t *testing.T) {
880+
t.Parallel()
881+
inv, root := clitest.New(t, "create", "my-workspace-2", "--template", template.Name, "--yes")
882+
clitest.SetupConfig(t, member, root)
883+
clitest.Run(t, inv)
884+
})
885+
886+
t.Run("CLIOverride/DoubleQuote", func(t *testing.T) {
887+
t.Parallel()
888+
889+
// Note: see https://go.dev/play/p/vhTUTZsVrEb for how to escape this properly
890+
parameterArg := fmt.Sprintf(`"%s=[""ddd=foo"",""eee=bar"",""fff=baz""]"`, listOfStringsParameterName)
891+
inv, root := clitest.New(t, "create", "my-workspace-3", "--template", template.Name, "--parameter", parameterArg, "--yes")
892+
clitest.SetupConfig(t, member, root)
893+
clitest.Run(t, inv)
894+
})
885895
})
886896

887897
t.Run("ValidateListOfStrings_YAMLFile", func(t *testing.T) {

cli/organizationsettings.go

+40-8
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,23 @@ func (r *RootCmd) organizationSettings(orgContext *OrganizationContext) *serpent
4848
return cli.RoleIDPSyncSettings(ctx, org.String())
4949
},
5050
},
51+
{
52+
Name: "organization-sync",
53+
Aliases: []string{"organizationsync", "org-sync", "orgsync"},
54+
Short: "Organization sync settings to sync organization memberships from an IdP.",
55+
DisableOrgContext: true,
56+
Patch: func(ctx context.Context, cli *codersdk.Client, _ uuid.UUID, input json.RawMessage) (any, error) {
57+
var req codersdk.OrganizationSyncSettings
58+
err := json.Unmarshal(input, &req)
59+
if err != nil {
60+
return nil, xerrors.Errorf("unmarshalling organization sync settings: %w", err)
61+
}
62+
return cli.PatchOrganizationIDPSyncSettings(ctx, req)
63+
},
64+
Fetch: func(ctx context.Context, cli *codersdk.Client, _ uuid.UUID) (any, error) {
65+
return cli.OrganizationIDPSyncSettings(ctx)
66+
},
67+
},
5168
}
5269
cmd := &serpent.Command{
5370
Use: "settings",
@@ -68,8 +85,13 @@ type organizationSetting struct {
6885
Name string
6986
Aliases []string
7087
Short string
71-
Patch func(ctx context.Context, cli *codersdk.Client, org uuid.UUID, input json.RawMessage) (any, error)
72-
Fetch func(ctx context.Context, cli *codersdk.Client, org uuid.UUID) (any, error)
88+
// DisableOrgContext is kinda a kludge. It tells the command constructor
89+
// to not require an organization context. This is used for the organization
90+
// sync settings which are not tied to a specific organization.
91+
// It feels excessive to build a more elaborate solution for this one-off.
92+
DisableOrgContext bool
93+
Patch func(ctx context.Context, cli *codersdk.Client, org uuid.UUID, input json.RawMessage) (any, error)
94+
Fetch func(ctx context.Context, cli *codersdk.Client, org uuid.UUID) (any, error)
7395
}
7496

7597
func (r *RootCmd) setOrganizationSettings(orgContext *OrganizationContext, settings []organizationSetting) *serpent.Command {
@@ -107,9 +129,14 @@ func (r *RootCmd) setOrganizationSettings(orgContext *OrganizationContext, setti
107129
),
108130
Handler: func(inv *serpent.Invocation) error {
109131
ctx := inv.Context()
110-
org, err := orgContext.Selected(inv, client)
111-
if err != nil {
112-
return err
132+
var org codersdk.Organization
133+
var err error
134+
135+
if !set.DisableOrgContext {
136+
org, err = orgContext.Selected(inv, client)
137+
if err != nil {
138+
return err
139+
}
113140
}
114141

115142
// Read in the json
@@ -178,9 +205,14 @@ func (r *RootCmd) printOrganizationSetting(orgContext *OrganizationContext, sett
178205
),
179206
Handler: func(inv *serpent.Invocation) error {
180207
ctx := inv.Context()
181-
org, err := orgContext.Selected(inv, client)
182-
if err != nil {
183-
return err
208+
var org codersdk.Organization
209+
var err error
210+
211+
if !set.DisableOrgContext {
212+
org, err = orgContext.Selected(inv, client)
213+
if err != nil {
214+
return err
215+
}
184216
}
185217

186218
output, err := fetch(ctx, client, org.ID)

cli/prompts.go

+32-18
Original file line numberDiff line numberDiff line change
@@ -22,14 +22,26 @@ func (RootCmd) promptExample() *serpent.Command {
2222
}
2323
}
2424

25-
var useSearch bool
26-
useSearchOption := serpent.Option{
27-
Name: "search",
28-
Description: "Show the search.",
29-
Required: false,
30-
Flag: "search",
31-
Value: serpent.BoolOf(&useSearch),
32-
}
25+
var (
26+
useSearch bool
27+
useSearchOption = serpent.Option{
28+
Name: "search",
29+
Description: "Show the search.",
30+
Required: false,
31+
Flag: "search",
32+
Value: serpent.BoolOf(&useSearch),
33+
}
34+
35+
multiSelectValues []string
36+
multiSelectError error
37+
useThingsOption = serpent.Option{
38+
Name: "things",
39+
Description: "Tell me what things you want.",
40+
Flag: "things",
41+
Default: "",
42+
Value: serpent.StringArrayOf(&multiSelectValues),
43+
}
44+
)
3345
cmd := &serpent.Command{
3446
Use: "prompt-example",
3547
Short: "Example of various prompt types used within coder cli.",
@@ -140,16 +152,18 @@ func (RootCmd) promptExample() *serpent.Command {
140152
return err
141153
}),
142154
promptCmd("multi-select", func(inv *serpent.Invocation) error {
143-
values, err := cliui.MultiSelect(inv, cliui.MultiSelectOptions{
144-
Message: "Select some things:",
145-
Options: []string{
146-
"Code", "Chair", "Whale", "Diamond", "Carrot",
147-
},
148-
Defaults: []string{"Code"},
149-
})
150-
_, _ = fmt.Fprintf(inv.Stdout, "%q are nice choices.\n", strings.Join(values, ", "))
151-
return err
152-
}),
155+
if len(multiSelectValues) == 0 {
156+
multiSelectValues, multiSelectError = cliui.MultiSelect(inv, cliui.MultiSelectOptions{
157+
Message: "Select some things:",
158+
Options: []string{
159+
"Code", "Chair", "Whale", "Diamond", "Carrot",
160+
},
161+
Defaults: []string{"Code"},
162+
})
163+
}
164+
_, _ = fmt.Fprintf(inv.Stdout, "%q are nice choices.\n", strings.Join(multiSelectValues, ", "))
165+
return multiSelectError
166+
}, useThingsOption),
153167
promptCmd("rich-parameter", func(inv *serpent.Invocation) error {
154168
value, err := cliui.RichSelect(inv, cliui.RichSelectOptions{
155169
Options: []codersdk.TemplateVersionParameterOption{

cli/server_test.go

+33
Original file line numberDiff line numberDiff line change
@@ -1620,6 +1620,39 @@ func TestServer_Production(t *testing.T) {
16201620
require.NoError(t, err)
16211621
}
16221622

1623+
//nolint:tparallel,paralleltest // This test sets environment variables.
1624+
func TestServer_TelemetryDisable(t *testing.T) {
1625+
// Set the default telemetry to true (normally disabled in tests).
1626+
t.Setenv("CODER_TEST_TELEMETRY_DEFAULT_ENABLE", "true")
1627+
1628+
//nolint:paralleltest // No need to reinitialise the variable tt (Go version).
1629+
for _, tt := range []struct {
1630+
key string
1631+
val string
1632+
want bool
1633+
}{
1634+
{"", "", true},
1635+
{"CODER_TELEMETRY_ENABLE", "true", true},
1636+
{"CODER_TELEMETRY_ENABLE", "false", false},
1637+
{"CODER_TELEMETRY", "true", true},
1638+
{"CODER_TELEMETRY", "false", false},
1639+
} {
1640+
t.Run(fmt.Sprintf("%s=%s", tt.key, tt.val), func(t *testing.T) {
1641+
t.Parallel()
1642+
var b bytes.Buffer
1643+
inv, _ := clitest.New(t, "server", "--write-config")
1644+
inv.Stdout = &b
1645+
inv.Environ.Set(tt.key, tt.val)
1646+
clitest.Run(t, inv)
1647+
1648+
var dv codersdk.DeploymentValues
1649+
err := yaml.Unmarshal(b.Bytes(), &dv)
1650+
require.NoError(t, err)
1651+
assert.Equal(t, tt.want, dv.Telemetry.Enable.Value())
1652+
})
1653+
}
1654+
}
1655+
16231656
//nolint:tparallel,paralleltest // This test cannot be run in parallel due to signal handling.
16241657
func TestServer_InterruptShutdown(t *testing.T) {
16251658
t.Skip("This test issues an interrupt signal which will propagate to the test runner.")

cli/testdata/coder_organizations_settings_set_--help.golden

+5-2
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,11 @@ USAGE:
1010
$ coder organization settings set groupsync < input.json
1111

1212
SUBCOMMANDS:
13-
group-sync Group sync settings to sync groups from an IdP.
14-
role-sync Role sync settings to sync organization roles from an IdP.
13+
group-sync Group sync settings to sync groups from an IdP.
14+
organization-sync Organization sync settings to sync organization
15+
memberships from an IdP.
16+
role-sync Role sync settings to sync organization roles from an
17+
IdP.
1518

1619
———
1720
Run `coder --help` for a list of global options.

0 commit comments

Comments
 (0)