From b4c2853ab73e039a4de49c97f17374a04e4d9ad1 Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Wed, 26 Jun 2024 08:26:52 -0500 Subject: [PATCH 01/14] chore: remove org context switcher in the cli --- cli/create.go | 4 +- cli/login.go | 7 -- cli/organization.go | 145 +++++++++---------------------------- cli/organizationmembers.go | 26 +++---- cli/organizationroles.go | 14 ++-- cli/root.go | 66 +++++++++-------- cli/templatecreate.go | 3 +- cli/usercreate.go | 5 +- 8 files changed, 98 insertions(+), 172 deletions(-) diff --git a/cli/create.go b/cli/create.go index 46d67c22663d2..df60fdd72d345 100644 --- a/cli/create.go +++ b/cli/create.go @@ -29,6 +29,7 @@ func (r *RootCmd) create() *serpent.Command { parameterFlags workspaceParameterFlags autoUpdates string copyParametersFrom string + orgContext = NewOrganizationContext() ) client := new(codersdk.Client) cmd := &serpent.Command{ @@ -43,7 +44,7 @@ func (r *RootCmd) create() *serpent.Command { ), Middleware: serpent.Chain(r.InitClient(client)), Handler: func(inv *serpent.Invocation) error { - organization, err := CurrentOrganization(r, inv, client) + organization, err := orgContext.Selected(inv, client) if err != nil { return err } @@ -269,6 +270,7 @@ func (r *RootCmd) create() *serpent.Command { ) cmd.Options = append(cmd.Options, parameterFlags.cliParameters()...) cmd.Options = append(cmd.Options, parameterFlags.cliParameterDefaults()...) + orgContext.AttachOptions(cmd) return cmd } diff --git a/cli/login.go b/cli/login.go index 7dde98b118c5d..834ba73ce38a0 100644 --- a/cli/login.go +++ b/cli/login.go @@ -358,13 +358,6 @@ func (r *RootCmd) login() *serpent.Command { return xerrors.Errorf("write server url: %w", err) } - // If the current organization cannot be fetched, then reset the organization context. - // Otherwise, organization cli commands will fail. - _, err = CurrentOrganization(r, inv, client) - if err != nil { - _ = config.Organization().Delete() - } - _, _ = fmt.Fprintf(inv.Stdout, Caret+"Welcome to Coder, %s! You're authenticated.\n", pretty.Sprint(cliui.DefaultStyles.Keyword, resp.Username)) return nil }, diff --git a/cli/organization.go b/cli/organization.go index 44f9c3308139e..0dcada1030f4e 100644 --- a/cli/organization.go +++ b/cli/organization.go @@ -1,9 +1,7 @@ package cli import ( - "errors" "fmt" - "os" "slices" "strings" @@ -17,6 +15,8 @@ import ( ) func (r *RootCmd) organizations() *serpent.Command { + orgContext := NewOrganizationContext() + cmd := &serpent.Command{ Use: "organizations [subcommand]", Short: "Organization related commands", @@ -26,114 +26,14 @@ func (r *RootCmd) organizations() *serpent.Command { return inv.Command.HelpHandler(inv) }, Children: []*serpent.Command{ - r.currentOrganization(), - r.switchOrganization(), + r.showOrganization(orgContext), r.createOrganization(), - r.organizationMembers(), - r.organizationRoles(), - }, - } - - cmd.Options = serpent.OptionSet{} - return cmd -} - -func (r *RootCmd) switchOrganization() *serpent.Command { - client := new(codersdk.Client) - - cmd := &serpent.Command{ - Use: "set ", - Short: "set the organization used by the CLI. Pass an empty string to reset to the default organization.", - Long: "set the organization used by the CLI. Pass an empty string to reset to the default organization.\n" + FormatExamples( - Example{ - Description: "Remove the current organization and defer to the default.", - Command: "coder organizations set ''", - }, - Example{ - Description: "Switch to a custom organization.", - Command: "coder organizations set my-org", - }, - ), - Middleware: serpent.Chain( - r.InitClient(client), - serpent.RequireRangeArgs(0, 1), - ), - Options: serpent.OptionSet{}, - Handler: func(inv *serpent.Invocation) error { - conf := r.createConfig() - orgs, err := client.OrganizationsByUser(inv.Context(), codersdk.Me) - if err != nil { - return xerrors.Errorf("failed to get organizations: %w", err) - } - // Keep the list of orgs sorted - slices.SortFunc(orgs, func(a, b codersdk.Organization) int { - return strings.Compare(a.Name, b.Name) - }) - - var switchToOrg string - if len(inv.Args) == 0 { - // Pull switchToOrg from a prompt selector, rather than command line - // args. - switchToOrg, err = promptUserSelectOrg(inv, conf, orgs) - if err != nil { - return err - } - } else { - switchToOrg = inv.Args[0] - } - - // If the user passes an empty string, we want to remove the organization - // from the config file. This will defer to default behavior. - if switchToOrg == "" { - err := conf.Organization().Delete() - if err != nil && !errors.Is(err, os.ErrNotExist) { - return xerrors.Errorf("failed to unset organization: %w", err) - } - _, _ = fmt.Fprintf(inv.Stdout, "Organization unset\n") - } else { - // Find the selected org in our list. - index := slices.IndexFunc(orgs, func(org codersdk.Organization) bool { - return org.Name == switchToOrg || org.ID.String() == switchToOrg - }) - if index < 0 { - // Using this error for better error message formatting - err := &codersdk.Error{ - Response: codersdk.Response{ - Message: fmt.Sprintf("Organization %q not found. Is the name correct, and are you a member of it?", switchToOrg), - Detail: "Ensure the organization argument is correct and you are a member of it.", - }, - Helper: fmt.Sprintf("Valid organizations you can switch to: %s", strings.Join(orgNames(orgs), ", ")), - } - return err - } - - // Always write the uuid to the config file. Names can change. - err := conf.Organization().Write(orgs[index].ID.String()) - if err != nil { - return xerrors.Errorf("failed to write organization to config file: %w", err) - } - } - - // Verify it worked. - current, err := CurrentOrganization(r, inv, client) - if err != nil { - // An SDK error could be a permission error. So offer the advice to unset the org - // and reset the context. - var sdkError *codersdk.Error - if errors.As(err, &sdkError) { - if sdkError.Helper == "" && sdkError.StatusCode() != 500 { - sdkError.Helper = `If this error persists, try unsetting your org with 'coder organizations set ""'` - } - return sdkError - } - return xerrors.Errorf("failed to get current organization: %w", err) - } - - _, _ = fmt.Fprintf(inv.Stdout, "Current organization context set to %s (%s)\n", current.Name, current.ID.String()) - return nil + r.organizationMembers(orgContext), + r.organizationRoles(orgContext), }, } + orgContext.AttachOptions(cmd) return cmd } @@ -207,7 +107,7 @@ func orgNames(orgs []codersdk.Organization) []string { return names } -func (r *RootCmd) currentOrganization() *serpent.Command { +func (r *RootCmd) showOrganization(orgContext *OrganizationContext) *serpent.Command { var ( stringFormat func(orgs []codersdk.Organization) (string, error) client = new(codersdk.Client) @@ -226,8 +126,29 @@ func (r *RootCmd) currentOrganization() *serpent.Command { onlyID = false ) cmd := &serpent.Command{ - Use: "show [current|me|uuid]", - Short: "Show the organization, if no argument is given, the organization currently in use will be shown.", + Use: "show [\"selected\"|\"me\"|uuid|org_name]", + Short: "Show the organization. " + + "Using \"selected\" will show the selected organization from the \"--org\" flag. " + + "Using \"me\" will show all organizations you are a member of.", + Long: FormatExamples( + Example{ + Description: "coder org show selected", + Command: "Shows the organizations selected with '--org='. " + + "This organization is the organization used by the cli.", + }, + Example{ + Description: "coder org show me", + Command: "List of all organizations you are a member of.", + }, + Example{ + Description: "coder org show developers", + Command: "Show organization with name 'developers'", + }, + Example{ + Description: "coder org show 90ee1875-3db5-43b3-828e-af3687522e43", + Command: "Show organization with the given ID.", + }, + ), Middleware: serpent.Chain( r.InitClient(client), serpent.RequireRangeArgs(0, 1), @@ -242,7 +163,7 @@ func (r *RootCmd) currentOrganization() *serpent.Command { }, }, Handler: func(inv *serpent.Invocation) error { - orgArg := "current" + orgArg := "selected" if len(inv.Args) >= 1 { orgArg = inv.Args[0] } @@ -250,14 +171,14 @@ func (r *RootCmd) currentOrganization() *serpent.Command { var orgs []codersdk.Organization var err error switch strings.ToLower(orgArg) { - case "current": + case "selected": stringFormat = func(orgs []codersdk.Organization) (string, error) { if len(orgs) != 1 { return "", xerrors.Errorf("expected 1 organization, got %d", len(orgs)) } return fmt.Sprintf("Current CLI Organization: %s (%s)\n", orgs[0].Name, orgs[0].ID.String()), nil } - org, err := CurrentOrganization(r, inv, client) + org, err := orgContext.Selected(inv, client) if err != nil { return err } diff --git a/cli/organizationmembers.go b/cli/organizationmembers.go index 521ec5bfb7d37..99b0700c4688d 100644 --- a/cli/organizationmembers.go +++ b/cli/organizationmembers.go @@ -11,16 +11,16 @@ import ( "github.com/coder/serpent" ) -func (r *RootCmd) organizationMembers() *serpent.Command { +func (r *RootCmd) organizationMembers(orgContext *OrganizationContext) *serpent.Command { cmd := &serpent.Command{ Use: "members", Aliases: []string{"member"}, Short: "Manage organization members", Children: []*serpent.Command{ - r.listOrganizationMembers(), - r.assignOrganizationRoles(), - r.addOrganizationMember(), - r.removeOrganizationMember(), + r.listOrganizationMembers(orgContext), + r.assignOrganizationRoles(orgContext), + r.addOrganizationMember(orgContext), + r.removeOrganizationMember(orgContext), }, Handler: func(inv *serpent.Invocation) error { return inv.Command.HelpHandler(inv) @@ -30,7 +30,7 @@ func (r *RootCmd) organizationMembers() *serpent.Command { return cmd } -func (r *RootCmd) removeOrganizationMember() *serpent.Command { +func (r *RootCmd) removeOrganizationMember(orgContext *OrganizationContext) *serpent.Command { client := new(codersdk.Client) cmd := &serpent.Command{ @@ -42,7 +42,7 @@ func (r *RootCmd) removeOrganizationMember() *serpent.Command { ), Handler: func(inv *serpent.Invocation) error { ctx := inv.Context() - organization, err := CurrentOrganization(r, inv, client) + organization, err := orgContext.Selected(inv, client) if err != nil { return err } @@ -61,7 +61,7 @@ func (r *RootCmd) removeOrganizationMember() *serpent.Command { return cmd } -func (r *RootCmd) addOrganizationMember() *serpent.Command { +func (r *RootCmd) addOrganizationMember(orgContext *OrganizationContext) *serpent.Command { client := new(codersdk.Client) cmd := &serpent.Command{ @@ -73,7 +73,7 @@ func (r *RootCmd) addOrganizationMember() *serpent.Command { ), Handler: func(inv *serpent.Invocation) error { ctx := inv.Context() - organization, err := CurrentOrganization(r, inv, client) + organization, err := orgContext.Selected(inv, client) if err != nil { return err } @@ -92,7 +92,7 @@ func (r *RootCmd) addOrganizationMember() *serpent.Command { return cmd } -func (r *RootCmd) assignOrganizationRoles() *serpent.Command { +func (r *RootCmd) assignOrganizationRoles(orgContext *OrganizationContext) *serpent.Command { client := new(codersdk.Client) cmd := &serpent.Command{ @@ -104,7 +104,7 @@ func (r *RootCmd) assignOrganizationRoles() *serpent.Command { ), Handler: func(inv *serpent.Invocation) error { ctx := inv.Context() - organization, err := CurrentOrganization(r, inv, client) + organization, err := orgContext.Selected(inv, client) if err != nil { return err } @@ -135,7 +135,7 @@ func (r *RootCmd) assignOrganizationRoles() *serpent.Command { return cmd } -func (r *RootCmd) listOrganizationMembers() *serpent.Command { +func (r *RootCmd) listOrganizationMembers(orgContext *OrganizationContext) *serpent.Command { formatter := cliui.NewOutputFormatter( cliui.TableFormat([]codersdk.OrganizationMemberWithName{}, []string{"username", "organization_roles"}), cliui.JSONFormat(), @@ -151,7 +151,7 @@ func (r *RootCmd) listOrganizationMembers() *serpent.Command { ), Handler: func(inv *serpent.Invocation) error { ctx := inv.Context() - organization, err := CurrentOrganization(r, inv, client) + organization, err := orgContext.Selected(inv, client) if err != nil { return err } diff --git a/cli/organizationroles.go b/cli/organizationroles.go index 75cf048198b30..70ff90ab5bee1 100644 --- a/cli/organizationroles.go +++ b/cli/organizationroles.go @@ -16,7 +16,7 @@ import ( "github.com/coder/serpent" ) -func (r *RootCmd) organizationRoles() *serpent.Command { +func (r *RootCmd) organizationRoles(orgContext *OrganizationContext) *serpent.Command { cmd := &serpent.Command{ Use: "roles", Short: "Manage organization roles.", @@ -26,14 +26,14 @@ func (r *RootCmd) organizationRoles() *serpent.Command { }, Hidden: true, Children: []*serpent.Command{ - r.showOrganizationRoles(), - r.editOrganizationRole(), + r.showOrganizationRoles(orgContext), + r.editOrganizationRole(orgContext), }, } return cmd } -func (r *RootCmd) showOrganizationRoles() *serpent.Command { +func (r *RootCmd) showOrganizationRoles(orgContext *OrganizationContext) *serpent.Command { formatter := cliui.NewOutputFormatter( cliui.ChangeFormatterData( cliui.TableFormat([]roleTableRow{}, []string{"name", "display_name", "site_permissions", "organization_permissions", "user_permissions"}), @@ -63,7 +63,7 @@ func (r *RootCmd) showOrganizationRoles() *serpent.Command { ), Handler: func(inv *serpent.Invocation) error { ctx := inv.Context() - org, err := CurrentOrganization(r, inv, client) + org, err := orgContext.Selected(inv, client) if err != nil { return err } @@ -100,7 +100,7 @@ func (r *RootCmd) showOrganizationRoles() *serpent.Command { return cmd } -func (r *RootCmd) editOrganizationRole() *serpent.Command { +func (r *RootCmd) editOrganizationRole(orgContext *OrganizationContext) *serpent.Command { formatter := cliui.NewOutputFormatter( cliui.ChangeFormatterData( cliui.TableFormat([]roleTableRow{}, []string{"name", "display_name", "site_permissions", "organization_permissions", "user_permissions"}), @@ -148,7 +148,7 @@ func (r *RootCmd) editOrganizationRole() *serpent.Command { ), Handler: func(inv *serpent.Invocation) error { ctx := inv.Context() - org, err := CurrentOrganization(r, inv, client) + org, err := orgContext.Selected(inv, client) if err != nil { return err } diff --git a/cli/root.go b/cli/root.go index 073486c640744..fa7f87f7d55ff 100644 --- a/cli/root.go +++ b/cli/root.go @@ -29,6 +29,7 @@ import ( "golang.org/x/mod/semver" "golang.org/x/xerrors" + "github.com/coder/coder/v2/coderd/database/db2sdk" "github.com/coder/pretty" "github.com/coder/coder/v2/buildinfo" @@ -632,52 +633,57 @@ func (r *RootCmd) createAgentClient() (*agentsdk.Client, error) { return client, nil } -// CurrentOrganization returns the currently active organization for the authenticated user. -func CurrentOrganization(r *RootCmd, inv *serpent.Invocation, client *codersdk.Client) (codersdk.Organization, error) { - conf := r.createConfig() - selected := r.organizationSelect - if selected == "" && conf.Organization().Exists() { - org, err := conf.Organization().Read() - if err != nil { - return codersdk.Organization{}, xerrors.Errorf("read selected organization from config file %q: %w", conf.Organization(), err) - } - selected = org - } +type OrganizationContext struct { + // FlagSelect is the value passed in via the --org flag + FlagSelect string +} + +func NewOrganizationContext() *OrganizationContext { + return &OrganizationContext{} +} - // Verify the org exists and the user is a member +func (o *OrganizationContext) AttachOptions(cmd *serpent.Command) { + cmd.Options = append(cmd.Options, serpent.Option{ + Name: "Organization", + Description: "Set the organization for the command to use.", + // Only required if the user is a part of more than 1 organization. + // Otherwise, we can assume a default value. + Required: false, + Flag: "org", + Env: "CODER_ORGANIZATION", + Value: serpent.StringOf(&o.FlagSelect), + }) +} + +func (o *OrganizationContext) Selected(inv *serpent.Invocation, client *codersdk.Client) (codersdk.Organization, error) { + // Fetch the set of organizations the user is a member of. orgs, err := client.OrganizationsByUser(inv.Context(), codersdk.Me) if err != nil { - return codersdk.Organization{}, err + return codersdk.Organization{}, xerrors.Errorf("get organizations: %w", err) } // User manually selected an organization - if selected != "" { + if o.FlagSelect != "" { index := slices.IndexFunc(orgs, func(org codersdk.Organization) bool { - return org.Name == selected || org.ID.String() == selected + return org.Name == o.FlagSelect || org.ID.String() == o.FlagSelect }) if index < 0 { - return codersdk.Organization{}, xerrors.Errorf("organization %q not found, are you sure you are a member of this organization? If unsure, run 'coder organizations set \"\" ' to reset your current context.", selected) + names := db2sdk.List(orgs, func(f codersdk.Organization) string { + return f.Name + }) + return codersdk.Organization{}, xerrors.Errorf("organization %q not found, are you sure you are a member of this organization? "+ + "Valid options for '--org=' are [%s].", o.FlagSelect, strings.Join(names, ", ")) } return orgs[index], nil } - // User did not select an organization, so use the default. - index := slices.IndexFunc(orgs, func(org codersdk.Organization) bool { - return org.IsDefault - }) - if index < 0 { - if len(orgs) == 1 { - // If there is no "isDefault", but only 1 org is present. We can just - // assume the single organization is correct. This is mainly a helper - // for cli hitting an old instance, or a user that belongs to a single - // org that is not the default. - return orgs[0], nil - } - return codersdk.Organization{}, xerrors.Errorf("unable to determine current organization. Use 'coder org set ' to select an organization to use") + if len(orgs) == 1 { + return orgs[0], nil } - return orgs[index], nil + // No org selected, and we are more than 1? Return an error. + return codersdk.Organization{}, xerrors.Errorf("Must select an organization with --org=.") } func splitNamedWorkspace(identifier string) (owner string, workspaceName string, err error) { diff --git a/cli/templatecreate.go b/cli/templatecreate.go index c570a0d60620d..e70a7bba03a8b 100644 --- a/cli/templatecreate.go +++ b/cli/templatecreate.go @@ -31,6 +31,7 @@ func (r *RootCmd) templateCreate() *serpent.Command { dormancyAutoDeletion time.Duration uploadFlags templateUploadFlags + orgContext = NewOrganizationContext() ) client := new(codersdk.Client) cmd := &serpent.Command{ @@ -68,7 +69,7 @@ func (r *RootCmd) templateCreate() *serpent.Command { } } - organization, err := CurrentOrganization(r, inv, client) + organization, err := orgContext.Selected(inv, client) if err != nil { return err } diff --git a/cli/usercreate.go b/cli/usercreate.go index 3c4a43b33bc2d..257bb1634f1d8 100644 --- a/cli/usercreate.go +++ b/cli/usercreate.go @@ -24,6 +24,7 @@ func (r *RootCmd) userCreate() *serpent.Command { password string disableLogin bool loginType string + orgContext = NewOrganizationContext() ) client := new(codersdk.Client) cmd := &serpent.Command{ @@ -33,7 +34,7 @@ func (r *RootCmd) userCreate() *serpent.Command { r.InitClient(client), ), Handler: func(inv *serpent.Invocation) error { - organization, err := CurrentOrganization(r, inv, client) + organization, err := orgContext.Selected(inv, client) if err != nil { return err } @@ -175,5 +176,7 @@ Create a workspace `+pretty.Sprint(cliui.DefaultStyles.Code, "coder create")+`! Value: serpent.StringOf(&loginType), }, } + + orgContext.AttachOptions(cmd) return cmd } From e9b236a56a8a006b585df98e4867939eb7d93e5c Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Wed, 26 Jun 2024 09:16:28 -0500 Subject: [PATCH 02/14] use new org context --- cli/templatedelete.go | 4 +++- cli/templateedit.go | 4 +++- cli/templatelist.go | 4 +++- cli/templatepull.go | 4 +++- cli/templatepush.go | 4 +++- cli/templateversionarchive.go | 8 ++++++-- cli/templateversions.go | 4 +++- 7 files changed, 24 insertions(+), 8 deletions(-) diff --git a/cli/templatedelete.go b/cli/templatedelete.go index 7ded11dd8f00a..120693b952eef 100644 --- a/cli/templatedelete.go +++ b/cli/templatedelete.go @@ -15,6 +15,7 @@ import ( ) func (r *RootCmd) templateDelete() *serpent.Command { + orgContext := NewOrganizationContext() client := new(codersdk.Client) cmd := &serpent.Command{ Use: "delete [name...]", @@ -32,7 +33,7 @@ func (r *RootCmd) templateDelete() *serpent.Command { templates = []codersdk.Template{} ) - organization, err := CurrentOrganization(r, inv, client) + organization, err := orgContext.Selected(inv, client) if err != nil { return err } @@ -81,6 +82,7 @@ func (r *RootCmd) templateDelete() *serpent.Command { return nil }, } + orgContext.AttachOptions(cmd) return cmd } diff --git a/cli/templateedit.go b/cli/templateedit.go index fbf740097b86f..4ac9c56f92534 100644 --- a/cli/templateedit.go +++ b/cli/templateedit.go @@ -36,6 +36,7 @@ func (r *RootCmd) templateEdit() *serpent.Command { requireActiveVersion bool deprecationMessage string disableEveryone bool + orgContext = NewOrganizationContext() ) client := new(codersdk.Client) @@ -77,7 +78,7 @@ func (r *RootCmd) templateEdit() *serpent.Command { } } - organization, err := CurrentOrganization(r, inv, client) + organization, err := orgContext.Selected(inv, client) if err != nil { return xerrors.Errorf("get current organization: %w", err) } @@ -324,6 +325,7 @@ func (r *RootCmd) templateEdit() *serpent.Command { }, cliui.SkipPromptOption(), } + orgContext.AttachOptions(cmd) return cmd } diff --git a/cli/templatelist.go b/cli/templatelist.go index ece2d2703b409..6a866ad3f83e5 100644 --- a/cli/templatelist.go +++ b/cli/templatelist.go @@ -11,6 +11,7 @@ import ( ) func (r *RootCmd) templateList() *serpent.Command { + orgContext := NewOrganizationContext() formatter := cliui.NewOutputFormatter( cliui.TableFormat([]templateTableRow{}, []string{"name", "last updated", "used by"}), cliui.JSONFormat(), @@ -25,7 +26,7 @@ func (r *RootCmd) templateList() *serpent.Command { r.InitClient(client), ), Handler: func(inv *serpent.Invocation) error { - organization, err := CurrentOrganization(r, inv, client) + organization, err := orgContext.Selected(inv, client) if err != nil { return err } @@ -52,5 +53,6 @@ func (r *RootCmd) templateList() *serpent.Command { } formatter.AttachOptions(&cmd.Options) + orgContext.AttachOptions(cmd) return cmd } diff --git a/cli/templatepull.go b/cli/templatepull.go index 7f9317be376f6..3170e3cd585ea 100644 --- a/cli/templatepull.go +++ b/cli/templatepull.go @@ -20,6 +20,7 @@ func (r *RootCmd) templatePull() *serpent.Command { tarMode bool zipMode bool versionName string + orgContext = NewOrganizationContext() ) client := new(codersdk.Client) @@ -45,7 +46,7 @@ func (r *RootCmd) templatePull() *serpent.Command { return xerrors.Errorf("either tar or zip can be selected") } - organization, err := CurrentOrganization(r, inv, client) + organization, err := orgContext.Selected(inv, client) if err != nil { return xerrors.Errorf("get current organization: %w", err) } @@ -187,6 +188,7 @@ func (r *RootCmd) templatePull() *serpent.Command { }, cliui.SkipPromptOption(), } + orgContext.AttachOptions(cmd) return cmd } diff --git a/cli/templatepush.go b/cli/templatepush.go index b4ff8e50eb5ed..de02af5c0e0db 100644 --- a/cli/templatepush.go +++ b/cli/templatepush.go @@ -34,6 +34,7 @@ func (r *RootCmd) templatePush() *serpent.Command { provisionerTags []string uploadFlags templateUploadFlags activate bool + orgContext = NewOrganizationContext() ) client := new(codersdk.Client) cmd := &serpent.Command{ @@ -46,7 +47,7 @@ func (r *RootCmd) templatePush() *serpent.Command { Handler: func(inv *serpent.Invocation) error { uploadFlags.setWorkdir(workdir) - organization, err := CurrentOrganization(r, inv, client) + organization, err := orgContext.Selected(inv, client) if err != nil { return err } @@ -226,6 +227,7 @@ func (r *RootCmd) templatePush() *serpent.Command { cliui.SkipPromptOption(), } cmd.Options = append(cmd.Options, uploadFlags.options()...) + orgContext.AttachOptions(cmd) return cmd } diff --git a/cli/templateversionarchive.go b/cli/templateversionarchive.go index f9ae87e330be0..10beda42b9afa 100644 --- a/cli/templateversionarchive.go +++ b/cli/templateversionarchive.go @@ -31,6 +31,7 @@ func (r *RootCmd) setArchiveTemplateVersion(archive bool) *serpent.Command { pastVerb = "unarchived" } + orgContext := NewOrganizationContext() client := new(codersdk.Client) cmd := &serpent.Command{ Use: presentVerb + " [template-version-names...] ", @@ -47,7 +48,7 @@ func (r *RootCmd) setArchiveTemplateVersion(archive bool) *serpent.Command { versions []codersdk.TemplateVersion ) - organization, err := CurrentOrganization(r, inv, client) + organization, err := orgContext.Selected(inv, client) if err != nil { return err } @@ -92,6 +93,7 @@ func (r *RootCmd) setArchiveTemplateVersion(archive bool) *serpent.Command { return nil }, } + orgContext.AttachOptions(cmd) return cmd } @@ -99,6 +101,7 @@ func (r *RootCmd) setArchiveTemplateVersion(archive bool) *serpent.Command { func (r *RootCmd) archiveTemplateVersions() *serpent.Command { var all serpent.Bool client := new(codersdk.Client) + orgContext := NewOrganizationContext() cmd := &serpent.Command{ Use: "archive [template-name...] ", Short: "Archive unused or failed template versions from a given template(s)", @@ -121,7 +124,7 @@ func (r *RootCmd) archiveTemplateVersions() *serpent.Command { templates = []codersdk.Template{} ) - organization, err := CurrentOrganization(r, inv, client) + organization, err := orgContext.Selected(inv, client) if err != nil { return err } @@ -179,6 +182,7 @@ func (r *RootCmd) archiveTemplateVersions() *serpent.Command { return nil }, } + orgContext.AttachOptions(cmd) return cmd } diff --git a/cli/templateversions.go b/cli/templateversions.go index 4460c3b5bfee5..9154e6724291d 100644 --- a/cli/templateversions.go +++ b/cli/templateversions.go @@ -51,6 +51,7 @@ func (r *RootCmd) templateVersionsList() *serpent.Command { cliui.JSONFormat(), ) client := new(codersdk.Client) + orgContext := NewOrganizationContext() var includeArchived serpent.Bool @@ -93,7 +94,7 @@ func (r *RootCmd) templateVersionsList() *serpent.Command { }, }, Handler: func(inv *serpent.Invocation) error { - organization, err := CurrentOrganization(r, inv, client) + organization, err := orgContext.Selected(inv, client) if err != nil { return xerrors.Errorf("get current organization: %w", err) } @@ -122,6 +123,7 @@ func (r *RootCmd) templateVersionsList() *serpent.Command { }, } + orgContext.AttachOptions(cmd) formatter.AttachOptions(&cmd.Options) return cmd } From 29174749d201c3849cedc1a323cc6d7d448e03f9 Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Wed, 26 Jun 2024 09:47:26 -0500 Subject: [PATCH 03/14] update golden files --- cli/testdata/coder_create_--help.golden | 3 +++ cli/testdata/coder_templates_archive_--help.golden | 3 +++ cli/testdata/coder_templates_delete_--help.golden | 3 +++ cli/testdata/coder_templates_edit_--help.golden | 3 +++ cli/testdata/coder_templates_list_--help.golden | 3 +++ cli/testdata/coder_templates_pull_--help.golden | 3 +++ cli/testdata/coder_templates_push_--help.golden | 3 +++ cli/testdata/coder_templates_versions_archive_--help.golden | 3 +++ cli/testdata/coder_templates_versions_list_--help.golden | 3 +++ cli/testdata/coder_templates_versions_unarchive_--help.golden | 3 +++ cli/testdata/coder_users_create_--help.golden | 3 +++ 11 files changed, 33 insertions(+) diff --git a/cli/testdata/coder_create_--help.golden b/cli/testdata/coder_create_--help.golden index 9edadd550012d..de48b6a38824b 100644 --- a/cli/testdata/coder_create_--help.golden +++ b/cli/testdata/coder_create_--help.golden @@ -10,6 +10,9 @@ USAGE: $ coder create / OPTIONS: + --org string, $CODER_ORGANIZATION + Set the organization for the command to use. + --automatic-updates string, $CODER_WORKSPACE_AUTOMATIC_UPDATES (default: never) Specify automatic updates setting for the workspace (accepts 'always' or 'never'). diff --git a/cli/testdata/coder_templates_archive_--help.golden b/cli/testdata/coder_templates_archive_--help.golden index ad9778ad9990c..8227b48753d7a 100644 --- a/cli/testdata/coder_templates_archive_--help.golden +++ b/cli/testdata/coder_templates_archive_--help.golden @@ -6,6 +6,9 @@ USAGE: Archive unused or failed template versions from a given template(s) OPTIONS: + --org string, $CODER_ORGANIZATION + Set the organization for the command to use. + --all bool Include all unused template versions. By default, only failed template versions are archived. diff --git a/cli/testdata/coder_templates_delete_--help.golden b/cli/testdata/coder_templates_delete_--help.golden index 2ba706b7d2aab..c0b6bce544878 100644 --- a/cli/testdata/coder_templates_delete_--help.golden +++ b/cli/testdata/coder_templates_delete_--help.golden @@ -8,6 +8,9 @@ USAGE: Aliases: rm OPTIONS: + --org string, $CODER_ORGANIZATION + Set the organization for the command to use. + -y, --yes bool Bypass prompts. diff --git a/cli/testdata/coder_templates_edit_--help.golden b/cli/testdata/coder_templates_edit_--help.golden index 29184b969bf44..dfbaa43e7431c 100644 --- a/cli/testdata/coder_templates_edit_--help.golden +++ b/cli/testdata/coder_templates_edit_--help.golden @@ -6,6 +6,9 @@ USAGE: Edit the metadata of a template by name. OPTIONS: + --org string, $CODER_ORGANIZATION + Set the organization for the command to use. + --activity-bump duration Edit the template activity bump - workspaces created from this template will have their shutdown time bumped by this value when diff --git a/cli/testdata/coder_templates_list_--help.golden b/cli/testdata/coder_templates_list_--help.golden index c76905cae27f4..939f782c4614a 100644 --- a/cli/testdata/coder_templates_list_--help.golden +++ b/cli/testdata/coder_templates_list_--help.golden @@ -8,6 +8,9 @@ USAGE: Aliases: ls OPTIONS: + --org string, $CODER_ORGANIZATION + Set the organization for the command to use. + -c, --column string-array (default: name,last updated,used by) Columns to display in table output. Available columns: name, created at, last updated, organization id, provisioner, active version id, diff --git a/cli/testdata/coder_templates_pull_--help.golden b/cli/testdata/coder_templates_pull_--help.golden index 2598e35a303ef..a7f3cc20d967b 100644 --- a/cli/testdata/coder_templates_pull_--help.golden +++ b/cli/testdata/coder_templates_pull_--help.golden @@ -6,6 +6,9 @@ USAGE: Download the active, latest, or specified version of a template to a path. OPTIONS: + --org string, $CODER_ORGANIZATION + Set the organization for the command to use. + --tar bool Output the template as a tar archive to stdout. diff --git a/cli/testdata/coder_templates_push_--help.golden b/cli/testdata/coder_templates_push_--help.golden index 092e16f897bee..bfcf7b2eabd8f 100644 --- a/cli/testdata/coder_templates_push_--help.golden +++ b/cli/testdata/coder_templates_push_--help.golden @@ -6,6 +6,9 @@ USAGE: Create or update a template from the current directory or as specified by flag OPTIONS: + --org string, $CODER_ORGANIZATION + Set the organization for the command to use. + --activate bool (default: true) Whether the new template will be marked active. diff --git a/cli/testdata/coder_templates_versions_archive_--help.golden b/cli/testdata/coder_templates_versions_archive_--help.golden index 463a83cf22a1d..b32cafe435bc3 100644 --- a/cli/testdata/coder_templates_versions_archive_--help.golden +++ b/cli/testdata/coder_templates_versions_archive_--help.golden @@ -7,6 +7,9 @@ USAGE: Archive a template version(s). OPTIONS: + --org string, $CODER_ORGANIZATION + Set the organization for the command to use. + -y, --yes bool Bypass prompts. diff --git a/cli/testdata/coder_templates_versions_list_--help.golden b/cli/testdata/coder_templates_versions_list_--help.golden index 3646c2dada80e..8b2eeccf36d6c 100644 --- a/cli/testdata/coder_templates_versions_list_--help.golden +++ b/cli/testdata/coder_templates_versions_list_--help.golden @@ -6,6 +6,9 @@ USAGE: List all the versions of the specified template OPTIONS: + --org string, $CODER_ORGANIZATION + Set the organization for the command to use. + -c, --column string-array (default: Name,Created At,Created By,Status,Active) Columns to display in table output. Available columns: name, created at, created by, status, active, archived. diff --git a/cli/testdata/coder_templates_versions_unarchive_--help.golden b/cli/testdata/coder_templates_versions_unarchive_--help.golden index e2241b14bc018..53fc8b719695f 100644 --- a/cli/testdata/coder_templates_versions_unarchive_--help.golden +++ b/cli/testdata/coder_templates_versions_unarchive_--help.golden @@ -7,6 +7,9 @@ USAGE: Unarchive a template version(s). OPTIONS: + --org string, $CODER_ORGANIZATION + Set the organization for the command to use. + -y, --yes bool Bypass prompts. diff --git a/cli/testdata/coder_users_create_--help.golden b/cli/testdata/coder_users_create_--help.golden index d55d522181c95..3f9b086d032ea 100644 --- a/cli/testdata/coder_users_create_--help.golden +++ b/cli/testdata/coder_users_create_--help.golden @@ -4,6 +4,9 @@ USAGE: coder users create [flags] OPTIONS: + --org string, $CODER_ORGANIZATION + Set the organization for the command to use. + -e, --email string Specifies an email address for the new user. From 268c187188d4532ed6dffe500c8c1cc374660479 Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Wed, 26 Jun 2024 14:16:55 -0500 Subject: [PATCH 04/14] fixup --- enterprise/cli/groupcreate.go | 4 +++- enterprise/cli/groupdelete.go | 4 +++- enterprise/cli/groupedit.go | 4 +++- enterprise/cli/grouplist.go | 4 +++- 4 files changed, 12 insertions(+), 4 deletions(-) diff --git a/enterprise/cli/groupcreate.go b/enterprise/cli/groupcreate.go index cff12bd418a82..21d5535583045 100644 --- a/enterprise/cli/groupcreate.go +++ b/enterprise/cli/groupcreate.go @@ -16,6 +16,7 @@ func (r *RootCmd) groupCreate() *serpent.Command { var ( avatarURL string displayName string + orgContext = agpl.NewOrganizationContext() ) client := new(codersdk.Client) @@ -29,7 +30,7 @@ func (r *RootCmd) groupCreate() *serpent.Command { Handler: func(inv *serpent.Invocation) error { ctx := inv.Context() - org, err := agpl.CurrentOrganization(&r.RootCmd, inv, client) + org, err := orgContext.Selected(inv, client) if err != nil { return xerrors.Errorf("current organization: %w", err) } @@ -63,6 +64,7 @@ func (r *RootCmd) groupCreate() *serpent.Command { Value: serpent.StringOf(&displayName), }, } + orgContext.AttachOptions(cmd) return cmd } diff --git a/enterprise/cli/groupdelete.go b/enterprise/cli/groupdelete.go index aeb753b5f803f..7729f961c2dca 100644 --- a/enterprise/cli/groupdelete.go +++ b/enterprise/cli/groupdelete.go @@ -13,6 +13,7 @@ import ( ) func (r *RootCmd) groupDelete() *serpent.Command { + orgContext := agpl.NewOrganizationContext() client := new(codersdk.Client) cmd := &serpent.Command{ Use: "delete ", @@ -27,7 +28,7 @@ func (r *RootCmd) groupDelete() *serpent.Command { groupName = inv.Args[0] ) - org, err := agpl.CurrentOrganization(&r.RootCmd, inv, client) + org, err := orgContext.Selected(inv, client) if err != nil { return xerrors.Errorf("current organization: %w", err) } @@ -46,6 +47,7 @@ func (r *RootCmd) groupDelete() *serpent.Command { return nil }, } + orgContext.AttachOptions(cmd) return cmd } diff --git a/enterprise/cli/groupedit.go b/enterprise/cli/groupedit.go index eb0776e2e5440..73dd3e13be11d 100644 --- a/enterprise/cli/groupedit.go +++ b/enterprise/cli/groupedit.go @@ -22,6 +22,7 @@ func (r *RootCmd) groupEdit() *serpent.Command { displayName string addUsers []string rmUsers []string + orgContext = agpl.NewOrganizationContext() ) client := new(codersdk.Client) cmd := &serpent.Command{ @@ -37,7 +38,7 @@ func (r *RootCmd) groupEdit() *serpent.Command { groupName = inv.Args[0] ) - org, err := agpl.CurrentOrganization(&r.RootCmd, inv, client) + org, err := orgContext.Selected(inv, client) if err != nil { return xerrors.Errorf("current organization: %w", err) } @@ -116,6 +117,7 @@ func (r *RootCmd) groupEdit() *serpent.Command { Value: serpent.StringArrayOf(&rmUsers), }, } + orgContext.AttachOptions(cmd) return cmd } diff --git a/enterprise/cli/grouplist.go b/enterprise/cli/grouplist.go index c0cff7b3bf69b..19040acbdade4 100644 --- a/enterprise/cli/grouplist.go +++ b/enterprise/cli/grouplist.go @@ -18,6 +18,7 @@ func (r *RootCmd) groupList() *serpent.Command { cliui.TableFormat([]groupTableRow{}, nil), cliui.JSONFormat(), ) + orgContext := agpl.NewOrganizationContext() client := new(codersdk.Client) cmd := &serpent.Command{ @@ -30,7 +31,7 @@ func (r *RootCmd) groupList() *serpent.Command { Handler: func(inv *serpent.Invocation) error { ctx := inv.Context() - org, err := agpl.CurrentOrganization(&r.RootCmd, inv, client) + org, err := orgContext.Selected(inv, client) if err != nil { return xerrors.Errorf("current organization: %w", err) } @@ -58,6 +59,7 @@ func (r *RootCmd) groupList() *serpent.Command { } formatter.AttachOptions(&cmd.Options) + orgContext.AttachOptions(cmd) return cmd } From 9ed5347f6c081d731223960d6a501d8b964b0766 Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Wed, 26 Jun 2024 14:35:36 -0500 Subject: [PATCH 05/14] update golden files --- docs/cli/create.md | 9 +++++++++ docs/cli/groups_create.md | 9 +++++++++ docs/cli/groups_delete.md | 13 ++++++++++++- docs/cli/groups_edit.md | 9 +++++++++ docs/cli/groups_list.md | 9 +++++++++ docs/cli/templates_archive.md | 9 +++++++++ docs/cli/templates_delete.md | 9 +++++++++ docs/cli/templates_edit.md | 9 +++++++++ docs/cli/templates_list.md | 9 +++++++++ docs/cli/templates_pull.md | 9 +++++++++ docs/cli/templates_push.md | 9 +++++++++ docs/cli/templates_versions_archive.md | 9 +++++++++ docs/cli/templates_versions_list.md | 9 +++++++++ docs/cli/templates_versions_unarchive.md | 9 +++++++++ docs/cli/users_create.md | 9 +++++++++ .../cli/testdata/coder_groups_create_--help.golden | 3 +++ .../cli/testdata/coder_groups_delete_--help.golden | 6 +++++- .../cli/testdata/coder_groups_edit_--help.golden | 3 +++ .../cli/testdata/coder_groups_list_--help.golden | 3 +++ 19 files changed, 152 insertions(+), 2 deletions(-) diff --git a/docs/cli/create.md b/docs/cli/create.md index 53f90751513d2..d3841ff186f5d 100644 --- a/docs/cli/create.md +++ b/docs/cli/create.md @@ -100,3 +100,12 @@ Specify a file path with values for rich parameters defined in the template. | Environment | $CODER_RICH_PARAMETER_DEFAULT | Rich parameter default values in the format "name=value". + +### --org + +| | | +| ----------- | -------------------------------- | +| Type | string | +| Environment | $CODER_ORGANIZATION | + +Set the organization for the command to use. diff --git a/docs/cli/groups_create.md b/docs/cli/groups_create.md index dd51ed7233a9a..80e4a40605da4 100644 --- a/docs/cli/groups_create.md +++ b/docs/cli/groups_create.md @@ -29,3 +29,12 @@ Set an avatar for a group. | Environment | $CODER_DISPLAY_NAME | Optional human friendly name for the group. + +### --org + +| | | +| ----------- | -------------------------------- | +| Type | string | +| Environment | $CODER_ORGANIZATION | + +Set the organization for the command to use. diff --git a/docs/cli/groups_delete.md b/docs/cli/groups_delete.md index f57faff0b9f59..67ca990ee1c6d 100644 --- a/docs/cli/groups_delete.md +++ b/docs/cli/groups_delete.md @@ -11,5 +11,16 @@ Aliases: ## Usage ```console -coder groups delete +coder groups delete [flags] ``` + +## Options + +### --org + +| | | +| ----------- | -------------------------------- | +| Type | string | +| Environment | $CODER_ORGANIZATION | + +Set the organization for the command to use. diff --git a/docs/cli/groups_edit.md b/docs/cli/groups_edit.md index 2006ba85abd4d..29184a50bdc6b 100644 --- a/docs/cli/groups_edit.md +++ b/docs/cli/groups_edit.md @@ -52,3 +52,12 @@ Add users to the group. Accepts emails or IDs. | Type | string-array | Remove users to the group. Accepts emails or IDs. + +### --org + +| | | +| ----------- | -------------------------------- | +| Type | string | +| Environment | $CODER_ORGANIZATION | + +Set the organization for the command to use. diff --git a/docs/cli/groups_list.md b/docs/cli/groups_list.md index 5f9e184f3995d..9ed5f6dc0f5c9 100644 --- a/docs/cli/groups_list.md +++ b/docs/cli/groups_list.md @@ -29,3 +29,12 @@ Columns to display in table output. Available columns: name, display name, organ | Default | table | Output format. Available formats: table, json. + +### --org + +| | | +| ----------- | -------------------------------- | +| Type | string | +| Environment | $CODER_ORGANIZATION | + +Set the organization for the command to use. diff --git a/docs/cli/templates_archive.md b/docs/cli/templates_archive.md index 04f6d65927a08..25aa72b3414e0 100644 --- a/docs/cli/templates_archive.md +++ b/docs/cli/templates_archive.md @@ -27,3 +27,12 @@ Bypass prompts. | Type | bool | Include all unused template versions. By default, only failed template versions are archived. + +### --org + +| | | +| ----------- | -------------------------------- | +| Type | string | +| Environment | $CODER_ORGANIZATION | + +Set the organization for the command to use. diff --git a/docs/cli/templates_delete.md b/docs/cli/templates_delete.md index aad8ac207f071..a2d1baa2b6442 100644 --- a/docs/cli/templates_delete.md +++ b/docs/cli/templates_delete.md @@ -23,3 +23,12 @@ coder templates delete [flags] [name...] | Type | bool | Bypass prompts. + +### --org + +| | | +| ----------- | -------------------------------- | +| Type | string | +| Environment | $CODER_ORGANIZATION | + +Set the organization for the command to use. diff --git a/docs/cli/templates_edit.md b/docs/cli/templates_edit.md index 45851225f129a..96784c4d59b2c 100644 --- a/docs/cli/templates_edit.md +++ b/docs/cli/templates_edit.md @@ -171,3 +171,12 @@ Disable the default behavior of granting template access to the 'everyone' group | Type | bool | Bypass prompts. + +### --org + +| | | +| ----------- | -------------------------------- | +| Type | string | +| Environment | $CODER_ORGANIZATION | + +Set the organization for the command to use. diff --git a/docs/cli/templates_list.md b/docs/cli/templates_list.md index 7e418e32c35c2..69e8c7d8cbba8 100644 --- a/docs/cli/templates_list.md +++ b/docs/cli/templates_list.md @@ -33,3 +33,12 @@ Columns to display in table output. Available columns: name, created at, last up | Default | table | Output format. Available formats: table, json. + +### --org + +| | | +| ----------- | -------------------------------- | +| Type | string | +| Environment | $CODER_ORGANIZATION | + +Set the organization for the command to use. diff --git a/docs/cli/templates_pull.md b/docs/cli/templates_pull.md index ab99df094ef30..f9f565c09187b 100644 --- a/docs/cli/templates_pull.md +++ b/docs/cli/templates_pull.md @@ -43,3 +43,12 @@ The name of the template version to pull. Use 'active' to pull the active versio | Type | bool | Bypass prompts. + +### --org + +| | | +| ----------- | -------------------------------- | +| Type | string | +| Environment | $CODER_ORGANIZATION | + +Set the organization for the command to use. diff --git a/docs/cli/templates_push.md b/docs/cli/templates_push.md index aea080a28d186..1dbdd61a64e69 100644 --- a/docs/cli/templates_push.md +++ b/docs/cli/templates_push.md @@ -102,3 +102,12 @@ Ignore warnings about not having a .terraform.lock.hcl file present in the templ | Type | string | Specify a message describing the changes in this version of the template. Messages longer than 72 characters will be displayed as truncated. + +### --org + +| | | +| ----------- | -------------------------------- | +| Type | string | +| Environment | $CODER_ORGANIZATION | + +Set the organization for the command to use. diff --git a/docs/cli/templates_versions_archive.md b/docs/cli/templates_versions_archive.md index 3921f6d0032b5..bb2d5a9b9c961 100644 --- a/docs/cli/templates_versions_archive.md +++ b/docs/cli/templates_versions_archive.md @@ -19,3 +19,12 @@ coder templates versions archive [flags] [template-version-names | Type | bool | Bypass prompts. + +### --org + +| | | +| ----------- | -------------------------------- | +| Type | string | +| Environment | $CODER_ORGANIZATION | + +Set the organization for the command to use. diff --git a/docs/cli/templates_versions_list.md b/docs/cli/templates_versions_list.md index 2c6544569dcba..9d7fefde37044 100644 --- a/docs/cli/templates_versions_list.md +++ b/docs/cli/templates_versions_list.md @@ -20,6 +20,15 @@ coder templates versions list [flags]