Skip to content

Commit 8d7261f

Browse files
committed
Merge branch 'main' of https://github.com/coder/coder into spike/9165-provisioner-filesystem
2 parents d1a9b2a + 113894c commit 8d7261f

File tree

31 files changed

+1122
-226
lines changed

31 files changed

+1122
-226
lines changed

.github/workflows/ci.yaml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ jobs:
137137
138138
# Check for any typos
139139
- name: Check for typos
140-
uses: crate-ci/typos@v1.16.4
140+
uses: crate-ci/typos@v1.16.6
141141
with:
142142
config: .github/workflows/typos.toml
143143

@@ -568,6 +568,7 @@ jobs:
568568
# https://www.chromatic.com/docs/github-actions#forked-repositories
569569
projectToken: 695c25b6cb65
570570
workingDir: "./site"
571+
storybookBaseDir: "./site"
571572
# Prevent excessive build runs on minor version changes
572573
skip: "@(renovate/**|dependabot/**)"
573574
# Run TurboSnap to trace file dependencies to related stories
@@ -593,6 +594,7 @@ jobs:
593594
buildScriptName: "storybook:build"
594595
projectToken: 695c25b6cb65
595596
workingDir: "./site"
597+
storybookBaseDir: "./site"
596598
# Run TurboSnap to trace file dependencies to related stories
597599
# and tell chromatic to only take snapshots of relevent stories
598600
onlyChanged: true

.github/workflows/contrib.yaml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,8 @@ jobs:
4646
path-to-document: "https://github.com/coder/cla/blob/main/README.md"
4747
# branch should not be protected
4848
branch: "main"
49-
allowlist: dependabot*
49+
# Some users have signed a corporate CLA with Coder so are exempt from signing our community one.
50+
allowlist: "coryb,aaronlehmann,dependabot*"
5051

5152
release-labels:
5253
runs-on: ubuntu-latest

cli/parameterresolver.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,10 @@ next:
141141
continue // immutables should not be passed to consecutive builds
142142
}
143143

144+
if len(tvp.Options) > 0 && !isValidTemplateParameterOption(buildParameter, tvp.Options) {
145+
continue // do not propagate invalid options
146+
}
147+
144148
for i, r := range resolved {
145149
if r.Name == buildParameter.Name {
146150
resolved[i].Value = buildParameter.Value
@@ -180,10 +184,12 @@ func (pr *ParameterResolver) resolveWithInput(resolved []codersdk.WorkspaceBuild
180184
// Parameter has not been resolved yet, so CLI needs to determine if user should input it.
181185

182186
firstTimeUse := pr.isFirstTimeUse(tvp.Name)
187+
promptParameterOption := pr.isLastBuildParameterInvalidOption(tvp)
183188

184189
if (tvp.Ephemeral && pr.promptBuildOptions) ||
185190
(action == WorkspaceCreate && tvp.Required) ||
186191
(action == WorkspaceCreate && !tvp.Ephemeral) ||
192+
(action == WorkspaceUpdate && promptParameterOption) ||
187193
(action == WorkspaceUpdate && tvp.Mutable && tvp.Required) ||
188194
(action == WorkspaceUpdate && !tvp.Mutable && firstTimeUse) ||
189195
(action == WorkspaceUpdate && tvp.Mutable && !tvp.Ephemeral && pr.promptRichParameters) {
@@ -207,6 +213,19 @@ func (pr *ParameterResolver) isFirstTimeUse(parameterName string) bool {
207213
return findWorkspaceBuildParameter(parameterName, pr.lastBuildParameters) == nil
208214
}
209215

216+
func (pr *ParameterResolver) isLastBuildParameterInvalidOption(templateVersionParameter codersdk.TemplateVersionParameter) bool {
217+
if len(templateVersionParameter.Options) == 0 {
218+
return false
219+
}
220+
221+
for _, buildParameter := range pr.lastBuildParameters {
222+
if buildParameter.Name == templateVersionParameter.Name {
223+
return !isValidTemplateParameterOption(buildParameter, templateVersionParameter.Options)
224+
}
225+
}
226+
return false
227+
}
228+
210229
func findTemplateVersionParameter(workspaceBuildParameter codersdk.WorkspaceBuildParameter, templateVersionParameters []codersdk.TemplateVersionParameter) *codersdk.TemplateVersionParameter {
211230
for _, tvp := range templateVersionParameters {
212231
if tvp.Name == workspaceBuildParameter.Name {
@@ -224,3 +243,12 @@ func findWorkspaceBuildParameter(parameterName string, params []codersdk.Workspa
224243
}
225244
return nil
226245
}
246+
247+
func isValidTemplateParameterOption(buildParameter codersdk.WorkspaceBuildParameter, options []codersdk.TemplateVersionParameterOption) bool {
248+
for _, opt := range options {
249+
if opt.Value == buildParameter.Value {
250+
return true
251+
}
252+
}
253+
return false
254+
}

cli/root.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -981,6 +981,8 @@ func (p *prettyErrorFormatter) format(err error) {
981981
msg = sdkError.Message
982982
if sdkError.Helper != "" {
983983
msg = msg + "\n" + sdkError.Helper
984+
} else if sdkError.Detail != "" {
985+
msg = msg + "\n" + sdkError.Detail
984986
}
985987
// The SDK error is usually good enough, and we don't want to overwhelm
986988
// the user with output.

cli/update_test.go

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -556,12 +556,138 @@ func TestUpdateValidateRichParameters(t *testing.T) {
556556
assert.NoError(t, err)
557557
}()
558558

559+
pty.ExpectMatch("Planning workspace...")
560+
<-doneChan
561+
})
562+
563+
t.Run("ParameterOptionChanged", func(t *testing.T) {
564+
t.Parallel()
565+
566+
// Create template and workspace
567+
client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true})
568+
user := coderdtest.CreateFirstUser(t, client)
569+
570+
templateParameters := []*proto.RichParameter{
571+
{Name: stringParameterName, Type: "string", Mutable: true, Required: true, Options: []*proto.RichParameterOption{
572+
{Name: "First option", Description: "This is first option", Value: "1st"},
573+
{Name: "Second option", Description: "This is second option", Value: "2nd"},
574+
{Name: "Third option", Description: "This is third option", Value: "3rd"},
575+
}},
576+
}
577+
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, prepareEchoResponses(templateParameters))
578+
coderdtest.AwaitTemplateVersionJob(t, client, version.ID)
579+
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
580+
581+
inv, root := clitest.New(t, "create", "my-workspace", "--yes", "--template", template.Name, "--parameter", fmt.Sprintf("%s=%s", stringParameterName, "2nd"))
582+
clitest.SetupConfig(t, client, root)
583+
err := inv.Run()
584+
require.NoError(t, err)
585+
586+
// Update template
587+
updatedTemplateParameters := []*proto.RichParameter{
588+
{Name: stringParameterName, Type: "string", Mutable: true, Required: true, Options: []*proto.RichParameterOption{
589+
{Name: "first_option", Description: "This is first option", Value: "1"},
590+
{Name: "second_option", Description: "This is second option", Value: "2"},
591+
{Name: "third_option", Description: "This is third option", Value: "3"},
592+
}},
593+
}
594+
595+
updatedVersion := coderdtest.UpdateTemplateVersion(t, client, user.OrganizationID, prepareEchoResponses(updatedTemplateParameters), template.ID)
596+
coderdtest.AwaitTemplateVersionJob(t, client, updatedVersion.ID)
597+
err = client.UpdateActiveTemplateVersion(context.Background(), template.ID, codersdk.UpdateActiveTemplateVersion{
598+
ID: updatedVersion.ID,
599+
})
600+
require.NoError(t, err)
601+
602+
// Update the workspace
603+
inv, root = clitest.New(t, "update", "my-workspace")
604+
clitest.SetupConfig(t, client, root)
605+
doneChan := make(chan struct{})
606+
pty := ptytest.New(t).Attach(inv)
607+
go func() {
608+
defer close(doneChan)
609+
err := inv.Run()
610+
assert.NoError(t, err)
611+
}()
612+
559613
matches := []string{
614+
stringParameterName, "second_option",
560615
"Planning workspace...", "",
561616
}
562617
for i := 0; i < len(matches); i += 2 {
563618
match := matches[i]
619+
value := matches[i+1]
564620
pty.ExpectMatch(match)
621+
622+
if value != "" {
623+
pty.WriteLine(value)
624+
}
625+
}
626+
<-doneChan
627+
})
628+
629+
t.Run("ParameterOptionDisappeared", func(t *testing.T) {
630+
t.Parallel()
631+
632+
// Create template and workspace
633+
client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true})
634+
user := coderdtest.CreateFirstUser(t, client)
635+
636+
templateParameters := []*proto.RichParameter{
637+
{Name: stringParameterName, Type: "string", Mutable: true, Required: true, Options: []*proto.RichParameterOption{
638+
{Name: "First option", Description: "This is first option", Value: "1st"},
639+
{Name: "Second option", Description: "This is second option", Value: "2nd"},
640+
{Name: "Third option", Description: "This is third option", Value: "3rd"},
641+
}},
642+
}
643+
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, prepareEchoResponses(templateParameters))
644+
coderdtest.AwaitTemplateVersionJob(t, client, version.ID)
645+
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
646+
647+
inv, root := clitest.New(t, "create", "my-workspace", "--yes", "--template", template.Name, "--parameter", fmt.Sprintf("%s=%s", stringParameterName, "2nd"))
648+
clitest.SetupConfig(t, client, root)
649+
err := inv.Run()
650+
require.NoError(t, err)
651+
652+
// Update template - 2nd option disappeared, 4th option added
653+
updatedTemplateParameters := []*proto.RichParameter{
654+
{Name: stringParameterName, Type: "string", Mutable: true, Required: true, Options: []*proto.RichParameterOption{
655+
{Name: "First option", Description: "This is first option", Value: "1st"},
656+
{Name: "Third option", Description: "This is third option", Value: "3rd"},
657+
{Name: "Fourth option", Description: "This is fourth option", Value: "4th"},
658+
}},
659+
}
660+
661+
updatedVersion := coderdtest.UpdateTemplateVersion(t, client, user.OrganizationID, prepareEchoResponses(updatedTemplateParameters), template.ID)
662+
coderdtest.AwaitTemplateVersionJob(t, client, updatedVersion.ID)
663+
err = client.UpdateActiveTemplateVersion(context.Background(), template.ID, codersdk.UpdateActiveTemplateVersion{
664+
ID: updatedVersion.ID,
665+
})
666+
require.NoError(t, err)
667+
668+
// Update the workspace
669+
inv, root = clitest.New(t, "update", "my-workspace")
670+
clitest.SetupConfig(t, client, root)
671+
doneChan := make(chan struct{})
672+
pty := ptytest.New(t).Attach(inv)
673+
go func() {
674+
defer close(doneChan)
675+
err := inv.Run()
676+
assert.NoError(t, err)
677+
}()
678+
679+
matches := []string{
680+
stringParameterName, "Third option",
681+
"Planning workspace...", "",
682+
}
683+
for i := 0; i < len(matches); i += 2 {
684+
match := matches[i]
685+
value := matches[i+1]
686+
pty.ExpectMatch(match)
687+
688+
if value != "" {
689+
pty.WriteLine(value)
690+
}
565691
}
566692
<-doneChan
567693
})

coderd/apidoc/docs.go

Lines changed: 0 additions & 3 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: 3 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

coderd/database/dbfake/dbfake.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -626,7 +626,7 @@ func uniqueSortedUUIDs(uuids []uuid.UUID) []uuid.UUID {
626626
return unique
627627
}
628628

629-
func (q *FakeQuerier) getOrganizationMember(orgID uuid.UUID) []database.OrganizationMember {
629+
func (q *FakeQuerier) getOrganizationMemberNoLock(orgID uuid.UUID) []database.OrganizationMember {
630630
var members []database.OrganizationMember
631631
for _, member := range q.organizationMembers {
632632
if member.OrganizationID == orgID {
@@ -637,14 +637,14 @@ func (q *FakeQuerier) getOrganizationMember(orgID uuid.UUID) []database.Organiza
637637
return members
638638
}
639639

640-
// getEveryoneGroupMembers fetches all the users in an organization.
641-
func (q *FakeQuerier) getEveryoneGroupMembers(orgID uuid.UUID) []database.User {
640+
// getEveryoneGroupMembersNoLock fetches all the users in an organization.
641+
func (q *FakeQuerier) getEveryoneGroupMembersNoLock(orgID uuid.UUID) []database.User {
642642
var (
643643
everyone []database.User
644-
orgMembers = q.getOrganizationMember(orgID)
644+
orgMembers = q.getOrganizationMemberNoLock(orgID)
645645
)
646646
for _, member := range orgMembers {
647-
user, err := q.GetUserByID(context.TODO(), member.UserID)
647+
user, err := q.getUserByIDNoLock(member.UserID)
648648
if err != nil {
649649
return nil
650650
}
@@ -1434,7 +1434,7 @@ func (q *FakeQuerier) GetGroupMembers(_ context.Context, id uuid.UUID) ([]databa
14341434
defer q.mutex.RUnlock()
14351435

14361436
if q.isEveryoneGroup(id) {
1437-
return q.getEveryoneGroupMembers(id), nil
1437+
return q.getEveryoneGroupMembersNoLock(id), nil
14381438
}
14391439

14401440
var members []database.GroupMember

coderd/httpmw/apikey.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -241,8 +241,9 @@ func ExtractAPIKey(rw http.ResponseWriter, r *http.Request, cfg ExtractAPIKeyCon
241241
changed = false
242242
)
243243
if key.LoginType == database.LoginTypeGithub || key.LoginType == database.LoginTypeOIDC {
244+
var err error
244245
//nolint:gocritic // System needs to fetch UserLink to check if it's valid.
245-
link, err := cfg.DB.GetUserLinkByUserIDLoginType(dbauthz.AsSystemRestricted(ctx), database.GetUserLinkByUserIDLoginTypeParams{
246+
link, err = cfg.DB.GetUserLinkByUserIDLoginType(dbauthz.AsSystemRestricted(ctx), database.GetUserLinkByUserIDLoginTypeParams{
246247
UserID: key.UserID,
247248
LoginType: key.LoginType,
248249
})

0 commit comments

Comments
 (0)