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

Commit5f50dcc

Browse files
authored
feat(cli): improve devcontainer support forcoder show (#18793)
Fixescoder/internal#747
1 parent2f50b3b commit5f50dcc

14 files changed

+600
-32
lines changed

‎cli/cliui/resources.go

Lines changed: 121 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
"golang.org/x/mod/semver"
1313

1414
"github.com/coder/coder/v2/coderd/database/dbtime"
15+
"github.com/coder/coder/v2/coderd/util/slice"
1516
"github.com/coder/coder/v2/codersdk"
1617
"github.com/coder/pretty"
1718
)
@@ -29,6 +30,7 @@ type WorkspaceResourcesOptions struct {
2930
ServerVersionstring
3031
ListeningPortsmap[uuid.UUID]codersdk.WorkspaceAgentListeningPortsResponse
3132
Devcontainersmap[uuid.UUID]codersdk.WorkspaceAgentListContainersResponse
33+
ShowDetailsbool
3234
}
3335

3436
// WorkspaceResources displays the connection status and tree-view of provided resources.
@@ -69,7 +71,11 @@ func WorkspaceResources(writer io.Writer, resources []codersdk.WorkspaceResource
6971

7072
totalAgents:=0
7173
for_,resource:=rangeresources {
72-
totalAgents+=len(resource.Agents)
74+
for_,agent:=rangeresource.Agents {
75+
if!agent.ParentID.Valid {
76+
totalAgents++
77+
}
78+
}
7379
}
7480

7581
for_,resource:=rangeresources {
@@ -94,12 +100,15 @@ func WorkspaceResources(writer io.Writer, resources []codersdk.WorkspaceResource
94100
"",
95101
})
96102
// Display all agents associated with the resource.
97-
forindex,agent:=rangeresource.Agents {
103+
agents:=slice.Filter(resource.Agents,func(agent codersdk.WorkspaceAgent)bool {
104+
return!agent.ParentID.Valid
105+
})
106+
forindex,agent:=rangeagents {
98107
tableWriter.AppendRow(renderAgentRow(agent,index,totalAgents,options))
99108
for_,row:=rangerenderListeningPorts(options,agent.ID,index,totalAgents) {
100109
tableWriter.AppendRow(row)
101110
}
102-
for_,row:=rangerenderDevcontainers(options,agent.ID,index,totalAgents) {
111+
for_,row:=rangerenderDevcontainers(resources,options,agent.ID,index,totalAgents) {
103112
tableWriter.AppendRow(row)
104113
}
105114
}
@@ -125,7 +134,7 @@ func renderAgentRow(agent codersdk.WorkspaceAgent, index, totalAgents int, optio
125134
}
126135
if!options.HideAccess {
127136
sshCommand:="coder ssh "+options.WorkspaceName
128-
iftotalAgents>1 {
137+
iftotalAgents>1||len(options.Devcontainers)>0{
129138
sshCommand+="."+agent.Name
130139
}
131140
sshCommand=pretty.Sprint(DefaultStyles.Code,sshCommand)
@@ -164,45 +173,129 @@ func renderPortRow(port codersdk.WorkspaceAgentListeningPort, idx, total int) ta
164173
return table.Row{sb.String()}
165174
}
166175

167-
funcrenderDevcontainers(wroWorkspaceResourcesOptions,agentID uuid.UUID,index,totalAgentsint) []table.Row {
176+
funcrenderDevcontainers(resources []codersdk.WorkspaceResource,wroWorkspaceResourcesOptions,agentID uuid.UUID,index,totalAgentsint) []table.Row {
168177
varrows []table.Row
169178
ifwro.Devcontainers==nil {
170179
return []table.Row{}
171180
}
172181
dc,ok:=wro.Devcontainers[agentID]
173-
if!ok||len(dc.Containers)==0 {
182+
if!ok||len(dc.Devcontainers)==0 {
174183
return []table.Row{}
175184
}
176185
rows=append(rows, table.Row{
177186
fmt.Sprintf(" %s─ %s",renderPipe(index,totalAgents),"Devcontainers"),
178187
})
179-
foridx,container:=rangedc.Containers {
180-
rows=append(rows,renderDevcontainerRow(container,idx,len(dc.Containers)))
188+
foridx,devcontainer:=rangedc.Devcontainers {
189+
rows=append(rows,renderDevcontainerRow(resources,devcontainer,idx,len(dc.Devcontainers),wro)...)
181190
}
182191
returnrows
183192
}
184193

185-
funcrenderDevcontainerRow(container codersdk.WorkspaceAgentContainer,index,totalint) table.Row {
186-
varrow table.Row
187-
varsb strings.Builder
188-
_,_=sb.WriteString(" ")
189-
_,_=sb.WriteString(renderPipe(index,total))
190-
_,_=sb.WriteString("─ ")
191-
_,_=sb.WriteString(pretty.Sprintf(DefaultStyles.Code,"%s",container.FriendlyName))
192-
row=append(row,sb.String())
193-
sb.Reset()
194-
ifcontainer.Running {
195-
_,_=sb.WriteString(pretty.Sprintf(DefaultStyles.Keyword,"(%s)",container.Status))
196-
}else {
197-
_,_=sb.WriteString(pretty.Sprintf(DefaultStyles.Error,"(%s)",container.Status))
194+
funcrenderDevcontainerRow(resources []codersdk.WorkspaceResource,devcontainer codersdk.WorkspaceAgentDevcontainer,index,totalint,wroWorkspaceResourcesOptions) []table.Row {
195+
varrows []table.Row
196+
197+
// If the devcontainer is running and has an associated agent, we want to
198+
// display the agent's details. Otherwise, we just display the devcontainer
199+
// name and status.
200+
varsubAgent*codersdk.WorkspaceAgent
201+
displayName:=devcontainer.Name
202+
ifdevcontainer.Agent!=nil&&devcontainer.Status==codersdk.WorkspaceAgentDevcontainerStatusRunning {
203+
for_,resource:=rangeresources {
204+
ifagent,found:=slice.Find(resource.Agents,func(agent codersdk.WorkspaceAgent)bool {
205+
returnagent.ID==devcontainer.Agent.ID
206+
});found {
207+
subAgent=&agent
208+
break
209+
}
210+
}
211+
ifsubAgent!=nil {
212+
displayName=subAgent.Name
213+
displayName+=fmt.Sprintf(" (%s, %s)",subAgent.OperatingSystem,subAgent.Architecture)
214+
}
215+
}
216+
217+
ifdevcontainer.Container!=nil {
218+
displayName+=" "+pretty.Sprint(DefaultStyles.Keyword,"["+devcontainer.Container.FriendlyName+"]")
219+
}
220+
221+
// Build the main row.
222+
row:= table.Row{
223+
fmt.Sprintf(" %s─ %s",renderPipe(index,total),displayName),
224+
}
225+
226+
// Add status, health, and version columns.
227+
if!wro.HideAgentState {
228+
ifsubAgent!=nil {
229+
row=append(row,renderAgentStatus(*subAgent))
230+
row=append(row,renderAgentHealth(*subAgent))
231+
row=append(row,renderAgentVersion(subAgent.Version,wro.ServerVersion))
232+
}else {
233+
row=append(row,renderDevcontainerStatus(devcontainer.Status))
234+
row=append(row,"")// No health for devcontainer without agent.
235+
row=append(row,"")// No version for devcontainer without agent.
236+
}
237+
}
238+
239+
// Add access column.
240+
if!wro.HideAccess {
241+
ifsubAgent!=nil {
242+
accessString:=fmt.Sprintf("coder ssh %s.%s",wro.WorkspaceName,subAgent.Name)
243+
row=append(row,pretty.Sprint(DefaultStyles.Code,accessString))
244+
}else {
245+
row=append(row,"")// No access for devcontainers without agent.
246+
}
247+
}
248+
249+
rows=append(rows,row)
250+
251+
// Add error message if present.
252+
iferrorMessage:=devcontainer.Error;errorMessage!="" {
253+
// Cap error message length for display.
254+
if!wro.ShowDetails&&len(errorMessage)>80 {
255+
errorMessage=errorMessage[:79]+"…"
256+
}
257+
errorRow:= table.Row{
258+
" × "+pretty.Sprint(DefaultStyles.Error,errorMessage),
259+
"",
260+
"",
261+
"",
262+
}
263+
if!wro.HideAccess {
264+
errorRow=append(errorRow,"")
265+
}
266+
rows=append(rows,errorRow)
267+
}
268+
269+
// Add listening ports for the devcontainer agent.
270+
ifsubAgent!=nil {
271+
portRows:=renderListeningPorts(wro,subAgent.ID,index,total)
272+
for_,portRow:=rangeportRows {
273+
// Adjust indentation for ports under devcontainer agent.
274+
iflen(portRow)>0 {
275+
ifstr,ok:=portRow[0].(string);ok {
276+
portRow[0]=" "+str// Add extra indentation.
277+
}
278+
}
279+
rows=append(rows,portRow)
280+
}
281+
}
282+
283+
returnrows
284+
}
285+
286+
funcrenderDevcontainerStatus(status codersdk.WorkspaceAgentDevcontainerStatus)string {
287+
switchstatus {
288+
casecodersdk.WorkspaceAgentDevcontainerStatusRunning:
289+
returnpretty.Sprint(DefaultStyles.Keyword,"▶ running")
290+
casecodersdk.WorkspaceAgentDevcontainerStatusStopped:
291+
returnpretty.Sprint(DefaultStyles.Placeholder,"⏹ stopped")
292+
casecodersdk.WorkspaceAgentDevcontainerStatusStarting:
293+
returnpretty.Sprint(DefaultStyles.Warn,"⧗ starting")
294+
casecodersdk.WorkspaceAgentDevcontainerStatusError:
295+
returnpretty.Sprint(DefaultStyles.Error,"✘ error")
296+
default:
297+
returnpretty.Sprint(DefaultStyles.Placeholder,"○ "+string(status))
198298
}
199-
row=append(row,sb.String())
200-
sb.Reset()
201-
// "health" is not applicable here.
202-
row=append(row,sb.String())
203-
_,_=sb.WriteString(container.Image)
204-
row=append(row,sb.String())
205-
returnrow
206299
}
207300

208301
funcrenderAgentStatus(agent codersdk.WorkspaceAgent)string {

‎cli/show.go

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,26 @@ import (
88

99
"github.com/google/uuid"
1010

11+
"github.com/coder/coder/v2/agent/agentcontainers"
1112
"github.com/coder/coder/v2/cli/cliui"
1213
"github.com/coder/coder/v2/codersdk"
1314
"github.com/coder/serpent"
1415
)
1516

1617
func (r*RootCmd)show()*serpent.Command {
1718
client:=new(codersdk.Client)
19+
vardetailsbool
1820
return&serpent.Command{
1921
Use:"show <workspace>",
2022
Short:"Display details of a workspace's resources and agents",
23+
Options: serpent.OptionSet{
24+
{
25+
Flag:"details",
26+
Description:"Show full error messages and additional details.",
27+
Default:"false",
28+
Value:serpent.BoolOf(&details),
29+
},
30+
},
2131
Middleware:serpent.Chain(
2232
serpent.RequireNArgs(1),
2333
r.InitClient(client),
@@ -35,13 +45,15 @@ func (r *RootCmd) show() *serpent.Command {
3545
options:= cliui.WorkspaceResourcesOptions{
3646
WorkspaceName:workspace.Name,
3747
ServerVersion:buildInfo.Version,
48+
ShowDetails:details,
3849
}
3950
ifworkspace.LatestBuild.Status==codersdk.WorkspaceStatusRunning {
4051
// Get listening ports for each agent.
4152
ports,devcontainers:=fetchRuntimeResources(inv,client,workspace.LatestBuild.Resources...)
4253
options.ListeningPorts=ports
4354
options.Devcontainers=devcontainers
4455
}
56+
4557
returncliui.WorkspaceResources(inv.Stdout,workspace.LatestBuild.Resources,options)
4658
},
4759
}
@@ -68,13 +80,17 @@ func fetchRuntimeResources(inv *serpent.Invocation, client *codersdk.Client, res
6880
ports[agent.ID]=lp
6981
mu.Unlock()
7082
}()
83+
84+
ifagent.ParentID.Valid {
85+
continue
86+
}
7187
wg.Add(1)
7288
gofunc() {
7389
deferwg.Done()
7490
dc,err:=client.WorkspaceAgentListContainers(inv.Context(),agent.ID,map[string]string{
7591
// Labels set by VSCode Remote Containers and @devcontainers/cli.
76-
"devcontainer.config_file":"",
77-
"devcontainer.local_folder":"",
92+
agentcontainers.DevcontainerConfigFileLabel:"",
93+
agentcontainers.DevcontainerLocalFolderLabel:"",
7894
})
7995
iferr!=nil {
8096
cliui.Warnf(inv.Stderr,"Failed to get devcontainers for agent %s: %v",agent.Name,err)

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp