Skip to content

Commit 73acd0f

Browse files
committed
Merge remote-tracking branch 'origin/main' into kacpersaw/feat-coder-attach
# Conflicts: # coderd/database/dump.sql # coderd/database/modelqueries.go # coderd/database/queries.sql.go # coderd/database/queries/templates.sql # coderd/searchquery/search.go # coderd/searchquery/search_test.go
2 parents 3c1d694 + b200fc8 commit 73acd0f

File tree

102 files changed

+4073
-937
lines changed

Some content is hidden

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

102 files changed

+4073
-937
lines changed

.github/actions/setup-go/action.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ description: |
44
inputs:
55
version:
66
description: "The Go version to use."
7-
default: "1.24.4"
7+
default: "1.24.6"
88
use-preinstalled-go:
99
description: "Whether to use preinstalled Go."
1010
default: "false"

.github/workflows/release.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -635,7 +635,7 @@ jobs:
635635
run: ls -lh build
636636

637637
- name: Publish Coder CLI binaries and detached signatures to GCS
638-
if: ${{ !inputs.dry_run && github.ref == 'refs/heads/main' && github.repository_owner == 'coder'}}
638+
if: ${{ !inputs.dry_run }}
639639
run: |
640640
set -euxo pipefail
641641

CODEOWNERS

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,3 +29,8 @@ site/src/api/countriesGenerated.ts
2929
site/src/api/rbacresourcesGenerated.ts
3030
site/src/api/typesGenerated.ts
3131
site/CLAUDE.md
32+
33+
# The blood and guts of the autostop algorithm, which is quite complex and
34+
# requires elite ball knowledge of most of the scheduling code to make changes
35+
# without inadvertently affecting other parts of the codebase.
36+
coderd/schedule/autostop.go @deansheather @DanielleMaywood

agent/agentssh/x11_test.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ func TestServer_X11_EvictionLRU(t *testing.T) {
135135
t.Skip("X11 forwarding is only supported on Linux")
136136
}
137137

138-
ctx := testutil.Context(t, testutil.WaitLong)
138+
ctx := testutil.Context(t, testutil.WaitSuperLong)
139139
logger := testutil.Logger(t)
140140
fs := afero.NewMemMapFs()
141141

@@ -238,7 +238,9 @@ func TestServer_X11_EvictionLRU(t *testing.T) {
238238
payload := "hello world"
239239
go func() {
240240
conn, err := inproc.Dial(ctx, testutil.NewAddr("tcp", fmt.Sprintf("localhost:%d", agentssh.X11StartPort+agentssh.X11DefaultDisplayOffset)))
241-
assert.NoError(t, err)
241+
if !assert.NoError(t, err) {
242+
return
243+
}
242244
_, err = conn.Write([]byte(payload))
243245
assert.NoError(t, err)
244246
_ = conn.Close()

cli/cliui/parameter.go

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -38,15 +38,16 @@ func RichParameter(inv *serpent.Invocation, templateVersionParameter codersdk.Te
3838
// Move the cursor up a single line for nicer display!
3939
_, _ = fmt.Fprint(inv.Stdout, "\033[1A")
4040

41-
var options []string
42-
err = json.Unmarshal([]byte(templateVersionParameter.DefaultValue), &options)
41+
var defaults []string
42+
err = json.Unmarshal([]byte(templateVersionParameter.DefaultValue), &defaults)
4343
if err != nil {
4444
return "", err
4545
}
4646

47-
values, err := MultiSelect(inv, MultiSelectOptions{
48-
Options: options,
49-
Defaults: options,
47+
values, err := RichMultiSelect(inv, RichMultiSelectOptions{
48+
Options: templateVersionParameter.Options,
49+
Defaults: defaults,
50+
EnableCustomInput: templateVersionParameter.FormType == "tag-select",
5051
})
5152
if err == nil {
5253
v, err := json.Marshal(&values)

cli/cliui/select.go

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"fmt"
66
"os"
77
"os/signal"
8+
"slices"
89
"strings"
910
"syscall"
1011

@@ -299,6 +300,77 @@ func (m selectModel) filteredOptions() []string {
299300
return options
300301
}
301302

303+
type RichMultiSelectOptions struct {
304+
Message string
305+
Options []codersdk.TemplateVersionParameterOption
306+
Defaults []string
307+
EnableCustomInput bool
308+
}
309+
310+
func RichMultiSelect(inv *serpent.Invocation, richOptions RichMultiSelectOptions) ([]string, error) {
311+
var opts []string
312+
var defaultOpts []string
313+
314+
asLine := func(option codersdk.TemplateVersionParameterOption) string {
315+
line := option.Name
316+
if len(option.Description) > 0 {
317+
line += ": " + option.Description
318+
}
319+
return line
320+
}
321+
322+
var predefinedOpts []string
323+
for i, option := range richOptions.Options {
324+
opts = append(opts, asLine(option)) // Some options may have description defined.
325+
326+
// Check if option is selected by default
327+
if slices.Contains(richOptions.Defaults, option.Value) {
328+
defaultOpts = append(defaultOpts, opts[i])
329+
predefinedOpts = append(predefinedOpts, option.Value)
330+
}
331+
}
332+
333+
// Check if "defaults" contains extra/custom options, user could select them.
334+
for _, def := range richOptions.Defaults {
335+
if !slices.Contains(predefinedOpts, def) {
336+
opts = append(opts, def)
337+
defaultOpts = append(defaultOpts, def)
338+
}
339+
}
340+
341+
selected, err := MultiSelect(inv, MultiSelectOptions{
342+
Message: richOptions.Message,
343+
Options: opts,
344+
Defaults: defaultOpts,
345+
EnableCustomInput: richOptions.EnableCustomInput,
346+
})
347+
if err != nil {
348+
return nil, err
349+
}
350+
351+
// Check selected option, convert descriptions (line) to values
352+
//
353+
// The function must return an initialized empty array, since it is later marshaled
354+
// into JSON. Otherwise, `var results []string` would be marshaled to "null".
355+
// See: https://github.com/golang/go/issues/27589
356+
results := []string{}
357+
for _, sel := range selected {
358+
custom := true
359+
for i, option := range richOptions.Options {
360+
if asLine(option) == sel {
361+
results = append(results, richOptions.Options[i].Value)
362+
custom = false
363+
break
364+
}
365+
}
366+
367+
if custom {
368+
results = append(results, sel)
369+
}
370+
}
371+
return results, nil
372+
}
373+
302374
type MultiSelectOptions struct {
303375
Message string
304376
Options []string

cli/cliui/select_test.go

Lines changed: 114 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -52,15 +52,8 @@ func TestRichSelect(t *testing.T) {
5252
go func() {
5353
resp, err := newRichSelect(ptty, cliui.RichSelectOptions{
5454
Options: []codersdk.TemplateVersionParameterOption{
55-
{
56-
Name: "A-Name",
57-
Value: "A-Value",
58-
Description: "A-Description.",
59-
}, {
60-
Name: "B-Name",
61-
Value: "B-Value",
62-
Description: "B-Description.",
63-
},
55+
{Name: "A-Name", Value: "A-Value", Description: "A-Description."},
56+
{Name: "B-Name", Value: "B-Value", Description: "B-Description."},
6457
},
6558
})
6659
assert.NoError(t, err)
@@ -86,63 +79,130 @@ func newRichSelect(ptty *ptytest.PTY, opts cliui.RichSelectOptions) (string, err
8679
return value, inv.Run()
8780
}
8881

89-
func TestMultiSelect(t *testing.T) {
82+
func TestRichMultiSelect(t *testing.T) {
9083
t.Parallel()
91-
t.Run("MultiSelect", func(t *testing.T) {
92-
items := []string{"aaa", "bbb", "ccc"}
9384

94-
t.Parallel()
95-
ptty := ptytest.New(t)
96-
msgChan := make(chan []string)
97-
go func() {
98-
resp, err := newMultiSelect(ptty, items)
99-
assert.NoError(t, err)
100-
msgChan <- resp
101-
}()
102-
require.Equal(t, items, <-msgChan)
103-
})
85+
tests := []struct {
86+
name string
87+
options []codersdk.TemplateVersionParameterOption
88+
defaults []string
89+
allowCustom bool
90+
want []string
91+
}{
92+
{
93+
name: "Predefined",
94+
options: []codersdk.TemplateVersionParameterOption{
95+
{Name: "AAA", Description: "This is AAA", Value: "aaa"},
96+
{Name: "BBB", Description: "This is BBB", Value: "bbb"},
97+
{Name: "CCC", Description: "This is CCC", Value: "ccc"},
98+
},
99+
defaults: []string{"bbb", "ccc"},
100+
allowCustom: false,
101+
want: []string{"bbb", "ccc"},
102+
},
103+
{
104+
name: "Custom",
105+
options: []codersdk.TemplateVersionParameterOption{
106+
{Name: "AAA", Description: "This is AAA", Value: "aaa"},
107+
{Name: "BBB", Description: "This is BBB", Value: "bbb"},
108+
{Name: "CCC", Description: "This is CCC", Value: "ccc"},
109+
},
110+
defaults: []string{"aaa", "bbb"},
111+
allowCustom: true,
112+
want: []string{"aaa", "bbb"},
113+
},
114+
{
115+
name: "NoOptionSelected",
116+
options: []codersdk.TemplateVersionParameterOption{
117+
{Name: "AAA", Description: "This is AAA", Value: "aaa"},
118+
{Name: "BBB", Description: "This is BBB", Value: "bbb"},
119+
{Name: "CCC", Description: "This is CCC", Value: "ccc"},
120+
},
121+
defaults: []string{},
122+
allowCustom: false,
123+
want: []string{},
124+
},
125+
}
104126

105-
t.Run("MultiSelectWithCustomInput", func(t *testing.T) {
106-
t.Parallel()
107-
items := []string{"Code", "Chairs", "Whale", "Diamond", "Carrot"}
108-
ptty := ptytest.New(t)
109-
msgChan := make(chan []string)
110-
go func() {
111-
resp, err := newMultiSelectWithCustomInput(ptty, items)
112-
assert.NoError(t, err)
113-
msgChan <- resp
114-
}()
115-
require.Equal(t, items, <-msgChan)
116-
})
117-
}
127+
for _, tt := range tests {
128+
t.Run(tt.name, func(t *testing.T) {
129+
t.Parallel()
118130

119-
func newMultiSelectWithCustomInput(ptty *ptytest.PTY, items []string) ([]string, error) {
120-
var values []string
121-
cmd := &serpent.Command{
122-
Handler: func(inv *serpent.Invocation) error {
123-
selectedItems, err := cliui.MultiSelect(inv, cliui.MultiSelectOptions{
124-
Options: items,
125-
Defaults: items,
126-
EnableCustomInput: true,
127-
})
128-
if err == nil {
129-
values = selectedItems
131+
var selectedItems []string
132+
var err error
133+
cmd := &serpent.Command{
134+
Handler: func(inv *serpent.Invocation) error {
135+
selectedItems, err = cliui.RichMultiSelect(inv, cliui.RichMultiSelectOptions{
136+
Options: tt.options,
137+
Defaults: tt.defaults,
138+
EnableCustomInput: tt.allowCustom,
139+
})
140+
return err
141+
},
130142
}
131-
return err
143+
144+
doneChan := make(chan struct{})
145+
go func() {
146+
defer close(doneChan)
147+
err := cmd.Invoke().Run()
148+
assert.NoError(t, err)
149+
}()
150+
<-doneChan
151+
152+
require.Equal(t, tt.want, selectedItems)
153+
})
154+
}
155+
}
156+
157+
func TestMultiSelect(t *testing.T) {
158+
t.Parallel()
159+
160+
tests := []struct {
161+
name string
162+
items []string
163+
allowCustom bool
164+
want []string
165+
}{
166+
{
167+
name: "MultiSelect",
168+
items: []string{"aaa", "bbb", "ccc"},
169+
allowCustom: false,
170+
want: []string{"aaa", "bbb", "ccc"},
171+
},
172+
{
173+
name: "MultiSelectWithCustomInput",
174+
items: []string{"Code", "Chairs", "Whale", "Diamond", "Carrot"},
175+
allowCustom: true,
176+
want: []string{"Code", "Chairs", "Whale", "Diamond", "Carrot"},
132177
},
133178
}
134-
inv := cmd.Invoke()
135-
ptty.Attach(inv)
136-
return values, inv.Run()
179+
180+
for _, tt := range tests {
181+
t.Run(tt.name, func(t *testing.T) {
182+
t.Parallel()
183+
184+
ptty := ptytest.New(t)
185+
msgChan := make(chan []string)
186+
187+
go func() {
188+
resp, err := newMultiSelect(ptty, tt.items, tt.allowCustom)
189+
assert.NoError(t, err)
190+
msgChan <- resp
191+
}()
192+
193+
require.Equal(t, tt.want, <-msgChan)
194+
})
195+
}
137196
}
138197

139-
func newMultiSelect(ptty *ptytest.PTY, items []string) ([]string, error) {
198+
func newMultiSelect(pty *ptytest.PTY, items []string, custom bool) ([]string, error) {
140199
var values []string
141200
cmd := &serpent.Command{
142201
Handler: func(inv *serpent.Invocation) error {
143202
selectedItems, err := cliui.MultiSelect(inv, cliui.MultiSelectOptions{
144-
Options: items,
145-
Defaults: items,
203+
Options: items,
204+
Defaults: items,
205+
EnableCustomInput: custom,
146206
})
147207
if err == nil {
148208
values = selectedItems
@@ -151,6 +211,6 @@ func newMultiSelect(ptty *ptytest.PTY, items []string) ([]string, error) {
151211
},
152212
}
153213
inv := cmd.Invoke()
154-
ptty.Attach(inv)
214+
pty.Attach(inv)
155215
return values, inv.Run()
156216
}

cli/exp_prompts.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,20 @@ func (RootCmd) promptExample() *serpent.Command {
174174
_, _ = fmt.Fprintf(inv.Stdout, "%q are nice choices.\n", strings.Join(multiSelectValues, ", "))
175175
return multiSelectError
176176
}, useThingsOption, enableCustomInputOption),
177+
promptCmd("rich-multi-select", func(inv *serpent.Invocation) error {
178+
if len(multiSelectValues) == 0 {
179+
multiSelectValues, multiSelectError = cliui.MultiSelect(inv, cliui.MultiSelectOptions{
180+
Message: "Select some things:",
181+
Options: []string{
182+
"Apples", "Plums", "Grapes", "Oranges", "Bananas",
183+
},
184+
Defaults: []string{"Grapes", "Plums"},
185+
EnableCustomInput: enableCustomInput,
186+
})
187+
}
188+
_, _ = fmt.Fprintf(inv.Stdout, "%q are nice choices.\n", strings.Join(multiSelectValues, ", "))
189+
return multiSelectError
190+
}, useThingsOption, enableCustomInputOption),
177191
promptCmd("rich-parameter", func(inv *serpent.Invocation) error {
178192
value, err := cliui.RichSelect(inv, cliui.RichSelectOptions{
179193
Options: []codersdk.TemplateVersionParameterOption{

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy