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

Commitc3620e2

Browse files
chore: add tests and fix bug
1 parentf528eb3 commitc3620e2

File tree

4 files changed

+268
-3
lines changed

4 files changed

+268
-3
lines changed

‎agent/agentcontainers/api.go

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -444,12 +444,25 @@ func (api *API) discoverDevcontainerProjects() error {
444444
}
445445

446446
func (api*API)discoverDevcontainersInProject(projectPathstring)error {
447+
devcontainerConfigPaths:= []string{
448+
"/.devcontainer/devcontainer.json",
449+
"/.devcontainer.json",
450+
}
451+
447452
returnafero.Walk(api.fs,projectPath,func(pathstring,info fs.FileInfo,errerror)error {
448-
ifstrings.HasSuffix(path,".devcontainer/devcontainer.json") {
449-
workspaceFolder:=strings.TrimSuffix(path,".devcontainer/devcontainer.json")
453+
for_,relativeConfigPath:=rangedevcontainerConfigPaths {
454+
if!strings.HasSuffix(path,relativeConfigPath) {
455+
continue
456+
}
457+
458+
workspaceFolder:=strings.TrimSuffix(path,relativeConfigPath)
459+
460+
api.logger.Debug(api.ctx,"discovered dev container project",slog.F("workspace_folder",workspaceFolder))
450461

451462
api.mu.Lock()
452463
if_,found:=api.knownDevcontainers[workspaceFolder];!found {
464+
api.logger.Debug(api.ctx,"adding dev container project",slog.F("workspace_folder",workspaceFolder))
465+
453466
dc:= codersdk.WorkspaceAgentDevcontainer{
454467
ID:uuid.New(),
455468
Name:"",// Updated later based on container state.

‎agent/agentcontainers/api_test.go

Lines changed: 232 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
"github.com/go-chi/chi/v5"
2121
"github.com/google/uuid"
2222
"github.com/lib/pq"
23+
"github.com/spf13/afero"
2324
"github.com/stretchr/testify/assert"
2425
"github.com/stretchr/testify/require"
2526
"go.uber.org/mock/gomock"
@@ -3189,3 +3190,234 @@ func TestWithDevcontainersNameGeneration(t *testing.T) {
31893190
assert.Equal(t,"bar-project",response.Devcontainers[0].Name,"second devcontainer should has a collision and uses the folder name with a prefix")
31903191
assert.Equal(t,"baz-project",response.Devcontainers[1].Name,"third devcontainer should use the folder name with a prefix since it collides with the first two")
31913192
}
3193+
3194+
funcTestDevcontainerDiscovery(t*testing.T) {
3195+
t.Parallel()
3196+
3197+
// We discover dev container projects by searching
3198+
// for git repositories at the agent's directory,
3199+
// and then recursively walking through these git
3200+
// repositories to find any `.devcontainer/devcontainer.json`
3201+
// files. These tests are to validate that behavior.
3202+
3203+
tests:= []struct {
3204+
namestring
3205+
agentDirstring
3206+
fsmap[string]string
3207+
expected []codersdk.WorkspaceAgentDevcontainer
3208+
}{
3209+
{
3210+
name:"GitProjectInRootDir/SingleProject",
3211+
agentDir:"/home/coder",
3212+
fs:map[string]string{
3213+
"/home/coder/.git/HEAD":"",
3214+
"/home/coder/.devcontainer/devcontainer.json":"",
3215+
},
3216+
expected: []codersdk.WorkspaceAgentDevcontainer{
3217+
{
3218+
WorkspaceFolder:"/home/coder",
3219+
ConfigPath:"/home/coder/.devcontainer/devcontainer.json",
3220+
Status:codersdk.WorkspaceAgentDevcontainerStatusStopped,
3221+
},
3222+
},
3223+
},
3224+
{
3225+
name:"GitProjectInRootDir/MultipleProjects",
3226+
agentDir:"/home/coder",
3227+
fs:map[string]string{
3228+
"/home/coder/.git/HEAD":"",
3229+
"/home/coder/.devcontainer/devcontainer.json":"",
3230+
"/home/coder/site/.devcontainer/devcontainer.json":"",
3231+
},
3232+
expected: []codersdk.WorkspaceAgentDevcontainer{
3233+
{
3234+
WorkspaceFolder:"/home/coder",
3235+
ConfigPath:"/home/coder/.devcontainer/devcontainer.json",
3236+
Status:codersdk.WorkspaceAgentDevcontainerStatusStopped,
3237+
},
3238+
{
3239+
WorkspaceFolder:"/home/coder/site",
3240+
ConfigPath:"/home/coder/site/.devcontainer/devcontainer.json",
3241+
Status:codersdk.WorkspaceAgentDevcontainerStatusStopped,
3242+
},
3243+
},
3244+
},
3245+
{
3246+
name:"GitProjectInChildDir/SingleProject",
3247+
agentDir:"/home/coder",
3248+
fs:map[string]string{
3249+
"/home/coder/coder/.git/HEAD":"",
3250+
"/home/coder/coder/.devcontainer/devcontainer.json":"",
3251+
},
3252+
expected: []codersdk.WorkspaceAgentDevcontainer{
3253+
{
3254+
WorkspaceFolder:"/home/coder/coder",
3255+
ConfigPath:"/home/coder/coder/.devcontainer/devcontainer.json",
3256+
Status:codersdk.WorkspaceAgentDevcontainerStatusStopped,
3257+
},
3258+
},
3259+
},
3260+
{
3261+
name:"GitProjectInChildDir/MultipleProjects",
3262+
agentDir:"/home/coder",
3263+
fs:map[string]string{
3264+
"/home/coder/coder/.git/HEAD":"",
3265+
"/home/coder/coder/.devcontainer/devcontainer.json":"",
3266+
"/home/coder/coder/site/.devcontainer/devcontainer.json":"",
3267+
},
3268+
expected: []codersdk.WorkspaceAgentDevcontainer{
3269+
{
3270+
WorkspaceFolder:"/home/coder/coder",
3271+
ConfigPath:"/home/coder/coder/.devcontainer/devcontainer.json",
3272+
Status:codersdk.WorkspaceAgentDevcontainerStatusStopped,
3273+
},
3274+
{
3275+
WorkspaceFolder:"/home/coder/coder/site",
3276+
ConfigPath:"/home/coder/coder/site/.devcontainer/devcontainer.json",
3277+
Status:codersdk.WorkspaceAgentDevcontainerStatusStopped,
3278+
},
3279+
},
3280+
},
3281+
{
3282+
name:"GitProjectInMultipleChildDirs/SingleProjectEach",
3283+
agentDir:"/home/coder",
3284+
fs:map[string]string{
3285+
"/home/coder/coder/.git/HEAD":"",
3286+
"/home/coder/coder/.devcontainer/devcontainer.json":"",
3287+
"/home/coder/envbuilder/.git/HEAD":"",
3288+
"/home/coder/envbuilder/.devcontainer/devcontainer.json":"",
3289+
},
3290+
expected: []codersdk.WorkspaceAgentDevcontainer{
3291+
{
3292+
WorkspaceFolder:"/home/coder/coder",
3293+
ConfigPath:"/home/coder/coder/.devcontainer/devcontainer.json",
3294+
Status:codersdk.WorkspaceAgentDevcontainerStatusStopped,
3295+
},
3296+
{
3297+
WorkspaceFolder:"/home/coder/envbuilder",
3298+
ConfigPath:"/home/coder/envbuilder/.devcontainer/devcontainer.json",
3299+
Status:codersdk.WorkspaceAgentDevcontainerStatusStopped,
3300+
},
3301+
},
3302+
},
3303+
{
3304+
name:"GitProjectInMultipleChildDirs/MultipleProjectEach",
3305+
agentDir:"/home/coder",
3306+
fs:map[string]string{
3307+
"/home/coder/coder/.git/HEAD":"",
3308+
"/home/coder/coder/.devcontainer/devcontainer.json":"",
3309+
"/home/coder/coder/site/.devcontainer/devcontainer.json":"",
3310+
"/home/coder/envbuilder/.git/HEAD":"",
3311+
"/home/coder/envbuilder/.devcontainer/devcontainer.json":"",
3312+
"/home/coder/envbuilder/x/.devcontainer/devcontainer.json":"",
3313+
},
3314+
expected: []codersdk.WorkspaceAgentDevcontainer{
3315+
{
3316+
WorkspaceFolder:"/home/coder/coder",
3317+
ConfigPath:"/home/coder/coder/.devcontainer/devcontainer.json",
3318+
Status:codersdk.WorkspaceAgentDevcontainerStatusStopped,
3319+
},
3320+
{
3321+
WorkspaceFolder:"/home/coder/coder/site",
3322+
ConfigPath:"/home/coder/coder/site/.devcontainer/devcontainer.json",
3323+
Status:codersdk.WorkspaceAgentDevcontainerStatusStopped,
3324+
},
3325+
{
3326+
WorkspaceFolder:"/home/coder/envbuilder",
3327+
ConfigPath:"/home/coder/envbuilder/.devcontainer/devcontainer.json",
3328+
Status:codersdk.WorkspaceAgentDevcontainerStatusStopped,
3329+
},
3330+
{
3331+
WorkspaceFolder:"/home/coder/envbuilder/x",
3332+
ConfigPath:"/home/coder/envbuilder/x/.devcontainer/devcontainer.json",
3333+
Status:codersdk.WorkspaceAgentDevcontainerStatusStopped,
3334+
},
3335+
},
3336+
},
3337+
}
3338+
3339+
initFS:=func(t*testing.T,filesmap[string]string) afero.Fs {
3340+
t.Helper()
3341+
3342+
fs:=afero.NewMemMapFs()
3343+
forname,content:=rangefiles {
3344+
err:=afero.WriteFile(fs,name, []byte(content+"\n"),0o600)
3345+
require.NoError(t,err)
3346+
}
3347+
returnfs
3348+
}
3349+
3350+
for_,tt:=rangetests {
3351+
t.Run(tt.name,func(t*testing.T) {
3352+
t.Parallel()
3353+
3354+
var (
3355+
ctx=testutil.Context(t,testutil.WaitShort)
3356+
logger=testutil.Logger(t)
3357+
mClock=quartz.NewMock(t)
3358+
tickerTrap=mClock.Trap().TickerFunc("updaterLoop")
3359+
3360+
r=chi.NewRouter()
3361+
)
3362+
3363+
api:=agentcontainers.NewAPI(logger,
3364+
agentcontainers.WithClock(mClock),
3365+
agentcontainers.WithWatcher(watcher.NewNoop()),
3366+
agentcontainers.WithFileSystem(initFS(t,tt.fs)),
3367+
agentcontainers.WithManifestInfo("owner","workspace","parent-agent",tt.agentDir),
3368+
agentcontainers.WithContainerCLI(&fakeContainerCLI{}),
3369+
agentcontainers.WithDevcontainerCLI(&fakeDevcontainerCLI{}),
3370+
)
3371+
api.Start()
3372+
deferapi.Close()
3373+
r.Mount("/",api.Routes())
3374+
3375+
tickerTrap.MustWait(ctx).MustRelease(ctx)
3376+
tickerTrap.Close()
3377+
3378+
// Wait until all projects have been discovered
3379+
require.Eventuallyf(t,func()bool {
3380+
req:=httptest.NewRequest(http.MethodGet,"/",nil).WithContext(ctx)
3381+
rec:=httptest.NewRecorder()
3382+
r.ServeHTTP(rec,req)
3383+
3384+
got:= codersdk.WorkspaceAgentListContainersResponse{}
3385+
err:=json.NewDecoder(rec.Body).Decode(&got)
3386+
require.NoError(t,err)
3387+
3388+
returnlen(got.Devcontainers)==len(tt.expected)
3389+
},testutil.WaitShort,testutil.IntervalFast,"dev containers never found")
3390+
3391+
// Now projects have been discovered, we'll allow the updater loop
3392+
// to set the appropriate status for these containers.
3393+
_,aw:=mClock.AdvanceNext()
3394+
aw.MustWait(ctx)
3395+
3396+
// Now we'll fetch the list of dev containers
3397+
req:=httptest.NewRequest(http.MethodGet,"/",nil).WithContext(ctx)
3398+
rec:=httptest.NewRecorder()
3399+
r.ServeHTTP(rec,req)
3400+
3401+
got:= codersdk.WorkspaceAgentListContainersResponse{}
3402+
err:=json.NewDecoder(rec.Body).Decode(&got)
3403+
require.NoError(t,err)
3404+
3405+
// We will set the IDs of each dev container to uuid.Nil to simplify
3406+
// this check.
3407+
foridx:=rangegot.Devcontainers {
3408+
got.Devcontainers[idx].ID=uuid.Nil
3409+
}
3410+
3411+
// Sort the expected dev containers and got dev containers by their workspace folder.
3412+
// This helps ensure a deterministic test.
3413+
slices.SortFunc(tt.expected,func(a,b codersdk.WorkspaceAgentDevcontainer)int {
3414+
returnstrings.Compare(a.WorkspaceFolder,b.WorkspaceFolder)
3415+
})
3416+
slices.SortFunc(got.Devcontainers,func(a,b codersdk.WorkspaceAgentDevcontainer)int {
3417+
returnstrings.Compare(a.WorkspaceFolder,b.WorkspaceFolder)
3418+
})
3419+
3420+
require.Equal(t,tt.expected,got.Devcontainers)
3421+
})
3422+
}
3423+
}

‎site/src/modules/resources/AgentDevcontainerCard.stories.tsx

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,17 @@ export const Recreating: Story = {
9191
},
9292
};
9393

94+
exportconstNoContainerOrSubAgent:Story={
95+
args:{
96+
devcontainer:{
97+
...MockWorkspaceAgentDevcontainer,
98+
container:undefined,
99+
agent:undefined,
100+
},
101+
subAgents:[],
102+
},
103+
};
104+
94105
exportconstNoSubAgent:Story={
95106
args:{
96107
devcontainer:{

‎site/src/modules/resources/AgentRow.tsx

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,16 @@ export const AgentRow: FC<AgentRowProps> = ({
137137
const[showParentApps,setShowParentApps]=useState(false);
138138

139139
letshouldDisplayAppsSection=shouldDisplayAgentApps;
140-
if(devcontainers&&devcontainers.length>0&&!showParentApps){
140+
if(
141+
devcontainers&&
142+
devcontainers.find(
143+
// We only want to hide the parent apps by default when there are dev
144+
// containers that are either starting or running. If they are all in
145+
// the stopped state, it doesn't make sense to hide the parent apps.
146+
(dc)=>dc.status==="running"||dc.status==="starting",
147+
)!==undefined&&
148+
!showParentApps
149+
){
141150
shouldDisplayAppsSection=false;
142151
}
143152

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp