Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commitd2998c6

Browse files
authored
feat: implement organization context in the cli (#12259)
* feat: implement organization context in the cli`coder org show current`
1 parentf44c89d commitd2998c6

24 files changed

+290
-52
lines changed

‎cli/config/file.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,14 @@ func (r Root) PostgresPort() File {
7070
// File provides convenience methods for interacting with *os.File.
7171
typeFilestring
7272

73+
func (fFile)Exists()bool {
74+
iff=="" {
75+
returnfalse
76+
}
77+
_,err:=os.Stat(string(f))
78+
returnerr==nil
79+
}
80+
7381
// Delete deletes the file.
7482
func (fFile)Delete()error {
7583
iff=="" {

‎cli/create.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ func (r *RootCmd) create() *clibase.Cmd {
4343
),
4444
Middleware:clibase.Chain(r.InitClient(client)),
4545
Handler:func(inv*clibase.Invocation)error {
46-
organization,err:=CurrentOrganization(inv,client)
46+
organization,err:=CurrentOrganization(r,inv,client)
4747
iferr!=nil {
4848
returnerr
4949
}

‎cli/organization.go

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
package cli
2+
3+
import (
4+
"fmt"
5+
"strings"
6+
7+
"github.com/coder/coder/v2/cli/clibase"
8+
"github.com/coder/coder/v2/cli/cliui"
9+
"github.com/coder/coder/v2/codersdk"
10+
)
11+
12+
func (r*RootCmd)organizations()*clibase.Cmd {
13+
cmd:=&clibase.Cmd{
14+
Annotations:workspaceCommand,
15+
Use:"organizations [subcommand]",
16+
Short:"Organization related commands",
17+
Aliases: []string{"organization","org","orgs"},
18+
Hidden:true,// Hidden until these commands are complete.
19+
Handler:func(inv*clibase.Invocation)error {
20+
returninv.Command.HelpHandler(inv)
21+
},
22+
Children: []*clibase.Cmd{
23+
r.currentOrganization(),
24+
},
25+
}
26+
27+
cmd.Options= clibase.OptionSet{}
28+
returncmd
29+
}
30+
31+
func (r*RootCmd)currentOrganization()*clibase.Cmd {
32+
var (
33+
stringFormatfunc(orgs []codersdk.Organization) (string,error)
34+
client=new(codersdk.Client)
35+
formatter=cliui.NewOutputFormatter(
36+
cliui.ChangeFormatterData(cliui.TextFormat(),func(dataany) (any,error) {
37+
typed,ok:=data.([]codersdk.Organization)
38+
if!ok {
39+
// This should never happen
40+
return"",fmt.Errorf("expected []Organization, got %T",data)
41+
}
42+
returnstringFormat(typed)
43+
}),
44+
cliui.TableFormat([]codersdk.Organization{}, []string{"id","name","default"}),
45+
cliui.JSONFormat(),
46+
)
47+
onlyID=false
48+
)
49+
cmd:=&clibase.Cmd{
50+
Use:"show [current|me|uuid]",
51+
Short:"Show the organization, if no argument is given, the organization currently in use will be shown.",
52+
Middleware:clibase.Chain(
53+
r.InitClient(client),
54+
clibase.RequireRangeArgs(0,1),
55+
),
56+
Options: clibase.OptionSet{
57+
{
58+
Name:"only-id",
59+
Description:"Only print the organization ID.",
60+
Required:false,
61+
Flag:"only-id",
62+
Value:clibase.BoolOf(&onlyID),
63+
},
64+
},
65+
Handler:func(inv*clibase.Invocation)error {
66+
orgArg:="current"
67+
iflen(inv.Args)>=1 {
68+
orgArg=inv.Args[0]
69+
}
70+
71+
varorgs []codersdk.Organization
72+
varerrerror
73+
switchstrings.ToLower(orgArg) {
74+
case"current":
75+
stringFormat=func(orgs []codersdk.Organization) (string,error) {
76+
iflen(orgs)!=1 {
77+
return"",fmt.Errorf("expected 1 organization, got %d",len(orgs))
78+
}
79+
returnfmt.Sprintf("Current CLI Organization: %s (%s)\n",orgs[0].Name,orgs[0].ID.String()),nil
80+
}
81+
org,err:=CurrentOrganization(r,inv,client)
82+
iferr!=nil {
83+
returnerr
84+
}
85+
orgs= []codersdk.Organization{org}
86+
case"me":
87+
stringFormat=func(orgs []codersdk.Organization) (string,error) {
88+
varstr strings.Builder
89+
_,_=fmt.Fprint(&str,"Organizations you are a member of:\n")
90+
for_,org:=rangeorgs {
91+
_,_=fmt.Fprintf(&str,"\t%s (%s)\n",org.Name,org.ID.String())
92+
}
93+
returnstr.String(),nil
94+
}
95+
orgs,err=client.OrganizationsByUser(inv.Context(),codersdk.Me)
96+
iferr!=nil {
97+
returnerr
98+
}
99+
default:
100+
stringFormat=func(orgs []codersdk.Organization) (string,error) {
101+
iflen(orgs)!=1 {
102+
return"",fmt.Errorf("expected 1 organization, got %d",len(orgs))
103+
}
104+
returnfmt.Sprintf("Organization: %s (%s)\n",orgs[0].Name,orgs[0].ID.String()),nil
105+
}
106+
// This works for a uuid or a name
107+
org,err:=client.OrganizationByName(inv.Context(),orgArg)
108+
iferr!=nil {
109+
returnerr
110+
}
111+
orgs= []codersdk.Organization{org}
112+
}
113+
114+
ifonlyID {
115+
for_,org:=rangeorgs {
116+
_,_=fmt.Fprintf(inv.Stdout,"%s\n",org.ID)
117+
}
118+
}else {
119+
out,err:=formatter.Format(inv.Context(),orgs)
120+
iferr!=nil {
121+
returnerr
122+
}
123+
_,_=fmt.Fprint(inv.Stdout,out)
124+
}
125+
returnnil
126+
},
127+
}
128+
formatter.AttachOptions(&cmd.Options)
129+
130+
returncmd
131+
}

‎cli/organization_test.go

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
package cli_test
2+
3+
import (
4+
"testing"
5+
6+
"github.com/stretchr/testify/require"
7+
8+
"github.com/coder/coder/v2/cli/clitest"
9+
"github.com/coder/coder/v2/coderd/coderdtest"
10+
"github.com/coder/coder/v2/coderd/rbac"
11+
"github.com/coder/coder/v2/codersdk"
12+
"github.com/coder/coder/v2/pty/ptytest"
13+
"github.com/coder/coder/v2/testutil"
14+
)
15+
16+
funcTestCurrentOrganization(t*testing.T) {
17+
t.Parallel()
18+
19+
t.Run("OnlyID",func(t*testing.T) {
20+
t.Parallel()
21+
ownerClient:=coderdtest.New(t,nil)
22+
first:=coderdtest.CreateFirstUser(t,ownerClient)
23+
// Owner is required to make orgs
24+
client,_:=coderdtest.CreateAnotherUser(t,ownerClient,first.OrganizationID,rbac.RoleOwner())
25+
26+
ctx:=testutil.Context(t,testutil.WaitMedium)
27+
orgs:= []string{"foo","bar"}
28+
for_,orgName:=rangeorgs {
29+
_,err:=client.CreateOrganization(ctx, codersdk.CreateOrganizationRequest{
30+
Name:orgName,
31+
})
32+
require.NoError(t,err)
33+
}
34+
35+
inv,root:=clitest.New(t,"organizations","show","--only-id")
36+
clitest.SetupConfig(t,client,root)
37+
pty:=ptytest.New(t).Attach(inv)
38+
errC:=make(chanerror)
39+
gofunc() {
40+
errC<-inv.Run()
41+
}()
42+
require.NoError(t,<-errC)
43+
pty.ExpectMatch(first.OrganizationID.String())
44+
})
45+
}

‎cli/root.go

Lines changed: 36 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ func (r *RootCmd) Core() []*clibase.Cmd {
9494
r.tokens(),
9595
r.users(),
9696
r.version(defaultVersionInfo),
97+
r.organizations(),
9798

9899
// Workspace Commands
99100
r.autoupdate(),
@@ -698,14 +699,44 @@ func (r *RootCmd) createAgentClient() (*agentsdk.Client, error) {
698699
}
699700

700701
// CurrentOrganization returns the currently active organization for the authenticated user.
701-
funcCurrentOrganization(inv*clibase.Invocation,client*codersdk.Client) (codersdk.Organization,error) {
702+
funcCurrentOrganization(r*RootCmd,inv*clibase.Invocation,client*codersdk.Client) (codersdk.Organization,error) {
703+
conf:=r.createConfig()
704+
selected:=""
705+
ifconf.Organization().Exists() {
706+
org,err:=conf.Organization().Read()
707+
iferr!=nil {
708+
return codersdk.Organization{},fmt.Errorf("read selected organization from config file %q: %w",conf.Organization(),err)
709+
}
710+
selected=org
711+
}
712+
713+
// Verify the org exists and the user is a member
702714
orgs,err:=client.OrganizationsByUser(inv.Context(),codersdk.Me)
703715
iferr!=nil {
704-
return codersdk.Organization{},nil
716+
return codersdk.Organization{},err
705717
}
706-
// For now, we won't use the config to set this.
707-
// Eventually, we will support changing using "coder switch <org>"
708-
returnorgs[0],nil
718+
719+
// User manually selected an organization
720+
ifselected!="" {
721+
index:=slices.IndexFunc(orgs,func(org codersdk.Organization)bool {
722+
returnorg.Name==selected||org.ID.String()==selected
723+
})
724+
725+
ifindex<0 {
726+
return codersdk.Organization{},xerrors.Errorf("organization %q not found, are you sure you are a member of this organization?",selected)
727+
}
728+
returnorgs[index],nil
729+
}
730+
731+
// User did not select an organization, so use the default.
732+
index:=slices.IndexFunc(orgs,func(org codersdk.Organization)bool {
733+
returnorg.IsDefault
734+
})
735+
ifindex<0 {
736+
return codersdk.Organization{},xerrors.Errorf("unable to determine current organization. Use 'coder organizations switch <org>' to select an organization to use")
737+
}
738+
739+
returnorgs[index],nil
709740
}
710741

711742
funcsplitNamedWorkspace(identifierstring) (ownerstring,workspaceNamestring,errerror) {

‎cli/templatecreate.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ func (r *RootCmd) templateCreate() *clibase.Cmd {
6969
}
7070
}
7171

72-
organization,err:=CurrentOrganization(inv,client)
72+
organization,err:=CurrentOrganization(r,inv,client)
7373
iferr!=nil {
7474
returnerr
7575
}

‎cli/templatecreate_test.go

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -243,19 +243,7 @@ func TestTemplateCreate(t *testing.T) {
243243
assert.Error(t,err)
244244
}()
245245

246-
matches:= []struct {
247-
matchstring
248-
writestring
249-
}{
250-
{match:"Upload",write:"yes"},
251-
}
252-
for_,m:=rangematches {
253-
pty.ExpectMatch(m.match)
254-
iflen(m.write)>0 {
255-
pty.WriteLine(m.write)
256-
}
257-
}
258-
246+
pty.ExpectMatch("context canceled")
259247
<-ctx.Done()
260248
})
261249

‎cli/templatedelete.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ func (r *RootCmd) templateDelete() *clibase.Cmd {
3232
templates= []codersdk.Template{}
3333
)
3434

35-
organization,err:=CurrentOrganization(inv,client)
35+
organization,err:=CurrentOrganization(r,inv,client)
3636
iferr!=nil {
3737
returnerr
3838
}

‎cli/templateedit.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ func (r *RootCmd) templateEdit() *clibase.Cmd {
7979
}
8080
}
8181

82-
organization,err:=CurrentOrganization(inv,client)
82+
organization,err:=CurrentOrganization(r,inv,client)
8383
iferr!=nil {
8484
returnxerrors.Errorf("get current organization: %w",err)
8585
}

‎cli/templatelist.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ func (r *RootCmd) templateList() *clibase.Cmd {
2525
r.InitClient(client),
2626
),
2727
Handler:func(inv*clibase.Invocation)error {
28-
organization,err:=CurrentOrganization(inv,client)
28+
organization,err:=CurrentOrganization(r,inv,client)
2929
iferr!=nil {
3030
returnerr
3131
}

‎cli/templatepull.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ func (r *RootCmd) templatePull() *clibase.Cmd {
4444
returnxerrors.Errorf("either tar or zip can be selected")
4545
}
4646

47-
organization,err:=CurrentOrganization(inv,client)
47+
organization,err:=CurrentOrganization(r,inv,client)
4848
iferr!=nil {
4949
returnxerrors.Errorf("get current organization: %w",err)
5050
}

‎cli/templatepush.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ func (r *RootCmd) templatePush() *clibase.Cmd {
4646
Handler:func(inv*clibase.Invocation)error {
4747
uploadFlags.setWorkdir(workdir)
4848

49-
organization,err:=CurrentOrganization(inv,client)
49+
organization,err:=CurrentOrganization(r,inv,client)
5050
iferr!=nil {
5151
returnerr
5252
}

‎cli/templateversionarchive.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ func (r *RootCmd) setArchiveTemplateVersion(archive bool) *clibase.Cmd {
4747
versions []codersdk.TemplateVersion
4848
)
4949

50-
organization,err:=CurrentOrganization(inv,client)
50+
organization,err:=CurrentOrganization(r,inv,client)
5151
iferr!=nil {
5252
returnerr
5353
}
@@ -121,7 +121,7 @@ func (r *RootCmd) archiveTemplateVersions() *clibase.Cmd {
121121
templates= []codersdk.Template{}
122122
)
123123

124-
organization,err:=CurrentOrganization(inv,client)
124+
organization,err:=CurrentOrganization(r,inv,client)
125125
iferr!=nil {
126126
returnerr
127127
}

‎cli/templateversions.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ func (r *RootCmd) templateVersionsList() *clibase.Cmd {
9393
},
9494
},
9595
Handler:func(inv*clibase.Invocation)error {
96-
organization,err:=CurrentOrganization(inv,client)
96+
organization,err:=CurrentOrganization(r,inv,client)
9797
iferr!=nil {
9898
returnxerrors.Errorf("get current organization: %w",err)
9999
}

‎cli/usercreate.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ func (r *RootCmd) userCreate() *clibase.Cmd {
3131
r.InitClient(client),
3232
),
3333
Handler:func(inv*clibase.Invocation)error {
34-
organization,err:=CurrentOrganization(inv,client)
34+
organization,err:=CurrentOrganization(r,inv,client)
3535
iferr!=nil {
3636
returnerr
3737
}

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp