diff --git a/agent/agent_test.go b/agent/agent_test.go index 6c0feca812e8b..3a2562237b603 100644 --- a/agent/agent_test.go +++ b/agent/agent_test.go @@ -2226,7 +2226,7 @@ func TestAgent_DevcontainerRecreate(t *testing.T) { // devcontainer, we do it in a goroutine so we can process logs // concurrently. go func(container codersdk.WorkspaceAgentContainer) { - err := conn.RecreateDevcontainer(ctx, container.ID) + _, err := conn.RecreateDevcontainer(ctx, container.ID) assert.NoError(t, err, "recreate devcontainer should succeed") }(container) diff --git a/agent/agentcontainers/api.go b/agent/agentcontainers/api.go index b28e39ad8c57b..349b85e3d269f 100644 --- a/agent/agentcontainers/api.go +++ b/agent/agentcontainers/api.go @@ -403,6 +403,7 @@ func (api *API) processUpdatedContainersLocked(ctx context.Context, updated code // Check if the container is running and update the known devcontainers. for i := range updated.Containers { container := &updated.Containers[i] // Grab a reference to the container to allow mutating it. + container.DevcontainerStatus = "" // Reset the status for the container (updated later). container.DevcontainerDirty = false // Reset dirty state for the container (updated later). workspaceFolder := container.Labels[DevcontainerLocalFolderLabel] @@ -465,9 +466,17 @@ func (api *API) processUpdatedContainersLocked(ctx context.Context, updated code for _, dc := range api.knownDevcontainers { switch { case dc.Status == codersdk.WorkspaceAgentDevcontainerStatusStarting: + if dc.Container != nil { + dc.Container.DevcontainerStatus = dc.Status + dc.Container.DevcontainerDirty = dc.Dirty + } continue // This state is handled by the recreation routine. case dc.Status == codersdk.WorkspaceAgentDevcontainerStatusError && (dc.Container == nil || dc.Container.CreatedAt.Before(api.recreateErrorTimes[dc.WorkspaceFolder])): + if dc.Container != nil { + dc.Container.DevcontainerStatus = dc.Status + dc.Container.DevcontainerDirty = dc.Dirty + } continue // The devcontainer needs to be recreated. case dc.Container != nil: @@ -475,6 +484,7 @@ func (api *API) processUpdatedContainersLocked(ctx context.Context, updated code if dc.Container.Running { dc.Status = codersdk.WorkspaceAgentDevcontainerStatusRunning } + dc.Container.DevcontainerStatus = dc.Status dc.Dirty = false if lastModified, hasModTime := api.configFileModifiedTimes[dc.ConfigPath]; hasModTime && dc.Container.CreatedAt.Before(lastModified) { @@ -608,6 +618,9 @@ func (api *API) handleDevcontainerRecreate(w http.ResponseWriter, r *http.Reques // Update the status so that we don't try to recreate the // devcontainer multiple times in parallel. dc.Status = codersdk.WorkspaceAgentDevcontainerStatusStarting + if dc.Container != nil { + dc.Container.DevcontainerStatus = dc.Status + } api.knownDevcontainers[dc.WorkspaceFolder] = dc api.recreateWg.Add(1) go api.recreateDevcontainer(dc, configPath) @@ -680,6 +693,9 @@ func (api *API) recreateDevcontainer(dc codersdk.WorkspaceAgentDevcontainer, con api.mu.Lock() dc = api.knownDevcontainers[dc.WorkspaceFolder] dc.Status = codersdk.WorkspaceAgentDevcontainerStatusError + if dc.Container != nil { + dc.Container.DevcontainerStatus = dc.Status + } api.knownDevcontainers[dc.WorkspaceFolder] = dc api.recreateErrorTimes[dc.WorkspaceFolder] = api.clock.Now("recreate", "errorTimes") api.mu.Unlock() @@ -695,10 +711,12 @@ func (api *API) recreateDevcontainer(dc codersdk.WorkspaceAgentDevcontainer, con // allows the update routine to update the devcontainer status, but // to minimize the time between API consistency, we guess the status // based on the container state. - if dc.Container != nil && dc.Container.Running { - dc.Status = codersdk.WorkspaceAgentDevcontainerStatusRunning - } else { - dc.Status = codersdk.WorkspaceAgentDevcontainerStatusStopped + dc.Status = codersdk.WorkspaceAgentDevcontainerStatusStopped + if dc.Container != nil { + if dc.Container.Running { + dc.Status = codersdk.WorkspaceAgentDevcontainerStatusRunning + } + dc.Container.DevcontainerStatus = dc.Status } dc.Dirty = false api.recreateSuccessTimes[dc.WorkspaceFolder] = api.clock.Now("recreate", "successTimes") diff --git a/agent/agentcontainers/api_test.go b/agent/agentcontainers/api_test.go index 37d613f0ac954..fb55825097190 100644 --- a/agent/agentcontainers/api_test.go +++ b/agent/agentcontainers/api_test.go @@ -477,6 +477,8 @@ func TestAPI(t *testing.T) { require.NoError(t, err, "unmarshal response failed") require.Len(t, resp.Devcontainers, 1, "expected one devcontainer in response") assert.Equal(t, codersdk.WorkspaceAgentDevcontainerStatusStarting, resp.Devcontainers[0].Status, "devcontainer is not starting") + require.NotNil(t, resp.Devcontainers[0].Container, "devcontainer should have container reference") + assert.Equal(t, codersdk.WorkspaceAgentDevcontainerStatusStarting, resp.Devcontainers[0].Container.DevcontainerStatus, "container dc status is not starting") // Allow the devcontainer CLI to continue the up process. close(tt.devcontainerCLI.continueUp) @@ -503,6 +505,8 @@ func TestAPI(t *testing.T) { require.NoError(t, err, "unmarshal response failed after error") require.Len(t, resp.Devcontainers, 1, "expected one devcontainer in response after error") assert.Equal(t, codersdk.WorkspaceAgentDevcontainerStatusError, resp.Devcontainers[0].Status, "devcontainer is not in an error state after up failure") + require.NotNil(t, resp.Devcontainers[0].Container, "devcontainer should have container reference after up failure") + assert.Equal(t, codersdk.WorkspaceAgentDevcontainerStatusError, resp.Devcontainers[0].Container.DevcontainerStatus, "container dc status is not error after up failure") return } @@ -525,7 +529,9 @@ func TestAPI(t *testing.T) { err = json.NewDecoder(rec.Body).Decode(&resp) require.NoError(t, err, "unmarshal response failed after recreation") require.Len(t, resp.Devcontainers, 1, "expected one devcontainer in response after recreation") - assert.Equal(t, codersdk.WorkspaceAgentDevcontainerStatusRunning, resp.Devcontainers[0].Status, "devcontainer is not stopped after recreation") + assert.Equal(t, codersdk.WorkspaceAgentDevcontainerStatusRunning, resp.Devcontainers[0].Status, "devcontainer is not running after recreation") + require.NotNil(t, resp.Devcontainers[0].Container, "devcontainer should have container reference after recreation") + assert.Equal(t, codersdk.WorkspaceAgentDevcontainerStatusRunning, resp.Devcontainers[0].Container.DevcontainerStatus, "container dc status is not running after recreation") }) } }) @@ -620,6 +626,7 @@ func TestAPI(t *testing.T) { assert.Equal(t, codersdk.WorkspaceAgentDevcontainerStatusRunning, dc.Status) require.NotNil(t, dc.Container) assert.Equal(t, "runtime-container-1", dc.Container.ID) + assert.Equal(t, codersdk.WorkspaceAgentDevcontainerStatusRunning, dc.Container.DevcontainerStatus) }, }, { @@ -660,12 +667,14 @@ func TestAPI(t *testing.T) { assert.Equal(t, codersdk.WorkspaceAgentDevcontainerStatusStopped, known2.Status) assert.Equal(t, codersdk.WorkspaceAgentDevcontainerStatusRunning, runtime1.Status) - require.NotNil(t, known1.Container) assert.Nil(t, known2.Container) - require.NotNil(t, runtime1.Container) + require.NotNil(t, known1.Container) assert.Equal(t, "known-container-1", known1.Container.ID) + assert.Equal(t, codersdk.WorkspaceAgentDevcontainerStatusRunning, known1.Container.DevcontainerStatus) + require.NotNil(t, runtime1.Container) assert.Equal(t, "runtime-container-1", runtime1.Container.ID) + assert.Equal(t, codersdk.WorkspaceAgentDevcontainerStatusRunning, runtime1.Container.DevcontainerStatus) }, }, { @@ -704,10 +713,12 @@ func TestAPI(t *testing.T) { assert.Equal(t, codersdk.WorkspaceAgentDevcontainerStatusStopped, nonRunning.Status) require.NotNil(t, running.Container, "running container should have container reference") - require.NotNil(t, nonRunning.Container, "non-running container should have container reference") - assert.Equal(t, "running-container", running.Container.ID) + assert.Equal(t, codersdk.WorkspaceAgentDevcontainerStatusRunning, running.Container.DevcontainerStatus) + + require.NotNil(t, nonRunning.Container, "non-running container should have container reference") assert.Equal(t, "non-running-container", nonRunning.Container.ID) + assert.Equal(t, codersdk.WorkspaceAgentDevcontainerStatusStopped, nonRunning.Container.DevcontainerStatus) }, }, { @@ -743,6 +754,7 @@ func TestAPI(t *testing.T) { assert.NotEmpty(t, dc2.ConfigPath) require.NotNil(t, dc2.Container) assert.Equal(t, "known-container-2", dc2.Container.ID) + assert.Equal(t, codersdk.WorkspaceAgentDevcontainerStatusRunning, dc2.Container.DevcontainerStatus) }, }, { @@ -811,9 +823,14 @@ func TestAPI(t *testing.T) { logger := slogtest.Make(t, &slogtest.Options{IgnoreErrors: true}).Leveled(slog.LevelDebug) + mClock := quartz.NewMock(t) + mClock.Set(time.Now()).MustWait(testutil.Context(t, testutil.WaitShort)) + tickerTrap := mClock.Trap().TickerFunc("updaterLoop") + // Setup router with the handler under test. r := chi.NewRouter() apiOptions := []agentcontainers.Option{ + agentcontainers.WithClock(mClock), agentcontainers.WithLister(tt.lister), agentcontainers.WithWatcher(watcher.NewNoop()), } @@ -838,6 +855,15 @@ func TestAPI(t *testing.T) { ctx := testutil.Context(t, testutil.WaitShort) + // Make sure the ticker function has been registered + // before advancing the clock. + tickerTrap.MustWait(ctx).MustRelease(ctx) + tickerTrap.Close() + + // Advance the clock to run the updater loop. + _, aw := mClock.AdvanceNext() + aw.MustWait(ctx) + req := httptest.NewRequest(http.MethodGet, "/devcontainers", nil). WithContext(ctx) rec := httptest.NewRecorder() diff --git a/cli/testdata/coder_list_--output_json.golden b/cli/testdata/coder_list_--output_json.golden index c37c89c4efe2a..d8e6a306cabcf 100644 --- a/cli/testdata/coder_list_--output_json.golden +++ b/cli/testdata/coder_list_--output_json.golden @@ -23,7 +23,7 @@ "workspace_id": "===========[workspace ID]===========", "workspace_name": "test-workspace", "workspace_owner_id": "==========[first user ID]===========", - "workspace_owner_name": "testuser", + "workspace_owner_username": "testuser", "template_version_id": "============[version ID]============", "template_version_name": "===========[version name]===========", "build_number": 1, diff --git a/coderd/apidoc/docs.go b/coderd/apidoc/docs.go index b6a00051bba77..e7eed3c03e9b3 100644 --- a/coderd/apidoc/docs.go +++ b/coderd/apidoc/docs.go @@ -12649,9 +12649,11 @@ const docTemplate = `{ "web-push", "dynamic-parameters", "workspace-prebuilds", - "agentic-chat" + "agentic-chat", + "ai-tasks" ], "x-enum-comments": { + "ExperimentAITasks": "Enables the new AI tasks feature.", "ExperimentAgenticChat": "Enables the new agentic AI chat feature.", "ExperimentAutoFillParameters": "This should not be taken out of experiments until we have redesigned the feature.", "ExperimentDynamicParameters": "Enables dynamic parameters when creating a workspace.", @@ -12669,7 +12671,8 @@ const docTemplate = `{ "ExperimentWebPush", "ExperimentDynamicParameters", "ExperimentWorkspacePrebuilds", - "ExperimentAgenticChat" + "ExperimentAgenticChat", + "ExperimentAITasks" ] }, "codersdk.ExternalAuth": { @@ -17198,6 +17201,14 @@ const docTemplate = `{ "description": "DevcontainerDirty is true if the devcontainer configuration has changed\nsince the container was created. This is used to determine if the\ncontainer needs to be rebuilt.", "type": "boolean" }, + "devcontainer_status": { + "description": "DevcontainerStatus is the status of the devcontainer, if this\ncontainer is a devcontainer. This is used to determine if the\ndevcontainer is running, stopped, starting, or in an error state.", + "allOf": [ + { + "$ref": "#/definitions/codersdk.WorkspaceAgentDevcontainerStatus" + } + ] + }, "id": { "description": "ID is the unique identifier of the container.", "type": "string" @@ -17262,6 +17273,21 @@ const docTemplate = `{ } } }, + "codersdk.WorkspaceAgentDevcontainerStatus": { + "type": "string", + "enum": [ + "running", + "stopped", + "starting", + "error" + ], + "x-enum-varnames": [ + "WorkspaceAgentDevcontainerStatusRunning", + "WorkspaceAgentDevcontainerStatusStopped", + "WorkspaceAgentDevcontainerStatusStarting", + "WorkspaceAgentDevcontainerStatusError" + ] + }, "codersdk.WorkspaceAgentHealth": { "type": "object", "properties": { @@ -17819,6 +17845,9 @@ const docTemplate = `{ }, "workspace_owner_name": { "type": "string" + }, + "workspace_owner_username": { + "type": "string" } } }, diff --git a/coderd/apidoc/swagger.json b/coderd/apidoc/swagger.json index e5fdca7025089..78da5e76c64d7 100644 --- a/coderd/apidoc/swagger.json +++ b/coderd/apidoc/swagger.json @@ -11351,9 +11351,11 @@ "web-push", "dynamic-parameters", "workspace-prebuilds", - "agentic-chat" + "agentic-chat", + "ai-tasks" ], "x-enum-comments": { + "ExperimentAITasks": "Enables the new AI tasks feature.", "ExperimentAgenticChat": "Enables the new agentic AI chat feature.", "ExperimentAutoFillParameters": "This should not be taken out of experiments until we have redesigned the feature.", "ExperimentDynamicParameters": "Enables dynamic parameters when creating a workspace.", @@ -11371,7 +11373,8 @@ "ExperimentWebPush", "ExperimentDynamicParameters", "ExperimentWorkspacePrebuilds", - "ExperimentAgenticChat" + "ExperimentAgenticChat", + "ExperimentAITasks" ] }, "codersdk.ExternalAuth": { @@ -15703,6 +15706,14 @@ "description": "DevcontainerDirty is true if the devcontainer configuration has changed\nsince the container was created. This is used to determine if the\ncontainer needs to be rebuilt.", "type": "boolean" }, + "devcontainer_status": { + "description": "DevcontainerStatus is the status of the devcontainer, if this\ncontainer is a devcontainer. This is used to determine if the\ndevcontainer is running, stopped, starting, or in an error state.", + "allOf": [ + { + "$ref": "#/definitions/codersdk.WorkspaceAgentDevcontainerStatus" + } + ] + }, "id": { "description": "ID is the unique identifier of the container.", "type": "string" @@ -15767,6 +15778,16 @@ } } }, + "codersdk.WorkspaceAgentDevcontainerStatus": { + "type": "string", + "enum": ["running", "stopped", "starting", "error"], + "x-enum-varnames": [ + "WorkspaceAgentDevcontainerStatusRunning", + "WorkspaceAgentDevcontainerStatusStopped", + "WorkspaceAgentDevcontainerStatusStarting", + "WorkspaceAgentDevcontainerStatusError" + ] + }, "codersdk.WorkspaceAgentHealth": { "type": "object", "properties": { @@ -16274,6 +16295,9 @@ }, "workspace_owner_name": { "type": "string" + }, + "workspace_owner_username": { + "type": "string" } } }, diff --git a/coderd/database/dbmem/dbmem.go b/coderd/database/dbmem/dbmem.go index 1a1455d83045b..a13dd33466bf6 100644 --- a/coderd/database/dbmem/dbmem.go +++ b/coderd/database/dbmem/dbmem.go @@ -531,6 +531,7 @@ func (q *FakeQuerier) convertToWorkspaceRowsNoLock(ctx context.Context, workspac OwnerAvatarUrl: extended.OwnerAvatarUrl, OwnerUsername: extended.OwnerUsername, + OwnerName: extended.OwnerName, OrganizationName: extended.OrganizationName, OrganizationDisplayName: extended.OrganizationDisplayName, @@ -628,6 +629,7 @@ func (q *FakeQuerier) extendWorkspace(w database.WorkspaceTable) database.Worksp return u.ID == w.OwnerID }) extended.OwnerUsername = owner.Username + extended.OwnerName = owner.Name extended.OwnerAvatarUrl = owner.AvatarURL return extended diff --git a/coderd/database/dump.sql b/coderd/database/dump.sql index ec196405df2d3..5e79149ff6f52 100644 --- a/coderd/database/dump.sql +++ b/coderd/database/dump.sql @@ -1507,6 +1507,7 @@ COMMENT ON COLUMN template_versions.message IS 'Message describing the changes i CREATE VIEW visible_users AS SELECT users.id, users.username, + users.name, users.avatar_url FROM users; @@ -1527,7 +1528,8 @@ CREATE VIEW template_version_with_user AS template_versions.archived, template_versions.source_example_id, COALESCE(visible_users.avatar_url, ''::text) AS created_by_avatar_url, - COALESCE(visible_users.username, ''::text) AS created_by_username + COALESCE(visible_users.username, ''::text) AS created_by_username, + COALESCE(visible_users.name, ''::text) AS created_by_name FROM (template_versions LEFT JOIN visible_users ON ((template_versions.created_by = visible_users.id))); @@ -1623,6 +1625,7 @@ CREATE VIEW template_with_names AS templates.use_classic_parameter_flow, COALESCE(visible_users.avatar_url, ''::text) AS created_by_avatar_url, COALESCE(visible_users.username, ''::text) AS created_by_username, + COALESCE(visible_users.name, ''::text) AS created_by_name, COALESCE(organizations.name, ''::text) AS organization_name, COALESCE(organizations.display_name, ''::text) AS organization_display_name, COALESCE(organizations.icon, ''::text) AS organization_icon @@ -2040,7 +2043,8 @@ CREATE VIEW workspace_build_with_user AS workspace_builds.max_deadline, workspace_builds.template_version_preset_id, COALESCE(visible_users.avatar_url, ''::text) AS initiator_by_avatar_url, - COALESCE(visible_users.username, ''::text) AS initiator_by_username + COALESCE(visible_users.username, ''::text) AS initiator_by_username, + COALESCE(visible_users.name, ''::text) AS initiator_by_name FROM (workspace_builds LEFT JOIN visible_users ON ((workspace_builds.initiator_id = visible_users.id))); @@ -2243,6 +2247,7 @@ CREATE VIEW workspaces_expanded AS workspaces.next_start_at, visible_users.avatar_url AS owner_avatar_url, visible_users.username AS owner_username, + visible_users.name AS owner_name, organizations.name AS organization_name, organizations.display_name AS organization_display_name, organizations.icon AS organization_icon, diff --git a/coderd/database/migrations/000330_workspace_with_correct_owner_names.down.sql b/coderd/database/migrations/000330_workspace_with_correct_owner_names.down.sql new file mode 100644 index 0000000000000..ec7bd37266c00 --- /dev/null +++ b/coderd/database/migrations/000330_workspace_with_correct_owner_names.down.sql @@ -0,0 +1,209 @@ +DROP VIEW template_version_with_user; + +DROP VIEW workspace_build_with_user; + +DROP VIEW template_with_names; + +DROP VIEW workspaces_expanded; + +DROP VIEW visible_users; + +-- Recreate `visible_users` as described in dump.sql + +CREATE VIEW visible_users AS +SELECT users.id, users.username, users.avatar_url +FROM users; + +COMMENT ON VIEW visible_users IS 'Visible fields of users are allowed to be joined with other tables for including context of other resources.'; + +-- Recreate `workspace_build_with_user` as described in dump.sql + +CREATE VIEW workspace_build_with_user AS +SELECT + workspace_builds.id, + workspace_builds.created_at, + workspace_builds.updated_at, + workspace_builds.workspace_id, + workspace_builds.template_version_id, + workspace_builds.build_number, + workspace_builds.transition, + workspace_builds.initiator_id, + workspace_builds.provisioner_state, + workspace_builds.job_id, + workspace_builds.deadline, + workspace_builds.reason, + workspace_builds.daily_cost, + workspace_builds.max_deadline, + workspace_builds.template_version_preset_id, + COALESCE( + visible_users.avatar_url, + ''::text + ) AS initiator_by_avatar_url, + COALESCE( + visible_users.username, + ''::text + ) AS initiator_by_username +FROM ( + workspace_builds + LEFT JOIN visible_users ON ( + ( + workspace_builds.initiator_id = visible_users.id + ) + ) + ); + +COMMENT ON VIEW workspace_build_with_user IS 'Joins in the username + avatar url of the initiated by user.'; + +-- Recreate `template_with_names` as described in dump.sql + +CREATE VIEW template_with_names AS +SELECT + templates.id, + templates.created_at, + templates.updated_at, + templates.organization_id, + templates.deleted, + templates.name, + templates.provisioner, + templates.active_version_id, + templates.description, + templates.default_ttl, + templates.created_by, + templates.icon, + templates.user_acl, + templates.group_acl, + templates.display_name, + templates.allow_user_cancel_workspace_jobs, + templates.allow_user_autostart, + templates.allow_user_autostop, + templates.failure_ttl, + templates.time_til_dormant, + templates.time_til_dormant_autodelete, + templates.autostop_requirement_days_of_week, + templates.autostop_requirement_weeks, + templates.autostart_block_days_of_week, + templates.require_active_version, + templates.deprecated, + templates.activity_bump, + templates.max_port_sharing_level, + templates.use_classic_parameter_flow, + COALESCE( + visible_users.avatar_url, + ''::text + ) AS created_by_avatar_url, + COALESCE( + visible_users.username, + ''::text + ) AS created_by_username, + COALESCE(organizations.name, ''::text) AS organization_name, + COALESCE( + organizations.display_name, + ''::text + ) AS organization_display_name, + COALESCE(organizations.icon, ''::text) AS organization_icon +FROM ( + ( + templates + LEFT JOIN visible_users ON ( + ( + templates.created_by = visible_users.id + ) + ) + ) + LEFT JOIN organizations ON ( + ( + templates.organization_id = organizations.id + ) + ) + ); + +COMMENT ON VIEW template_with_names IS 'Joins in the display name information such as username, avatar, and organization name.'; + +-- Recreate `template_version_with_user` as described in dump.sql + +CREATE VIEW template_version_with_user AS +SELECT + template_versions.id, + template_versions.template_id, + template_versions.organization_id, + template_versions.created_at, + template_versions.updated_at, + template_versions.name, + template_versions.readme, + template_versions.job_id, + template_versions.created_by, + template_versions.external_auth_providers, + template_versions.message, + template_versions.archived, + template_versions.source_example_id, + COALESCE( + visible_users.avatar_url, + ''::text + ) AS created_by_avatar_url, + COALESCE( + visible_users.username, + ''::text + ) AS created_by_username +FROM ( + template_versions + LEFT JOIN visible_users ON ( + template_versions.created_by = visible_users.id + ) + ); + +COMMENT ON VIEW template_version_with_user IS 'Joins in the username + avatar url of the created by user.'; + +-- Recreate `workspaces_expanded` as described in dump.sql + +CREATE VIEW workspaces_expanded AS +SELECT + workspaces.id, + workspaces.created_at, + workspaces.updated_at, + workspaces.owner_id, + workspaces.organization_id, + workspaces.template_id, + workspaces.deleted, + workspaces.name, + workspaces.autostart_schedule, + workspaces.ttl, + workspaces.last_used_at, + workspaces.dormant_at, + workspaces.deleting_at, + workspaces.automatic_updates, + workspaces.favorite, + workspaces.next_start_at, + visible_users.avatar_url AS owner_avatar_url, + visible_users.username AS owner_username, + organizations.name AS organization_name, + organizations.display_name AS organization_display_name, + organizations.icon AS organization_icon, + organizations.description AS organization_description, + templates.name AS template_name, + templates.display_name AS template_display_name, + templates.icon AS template_icon, + templates.description AS template_description +FROM ( + ( + ( + workspaces + JOIN visible_users ON ( + ( + workspaces.owner_id = visible_users.id + ) + ) + ) + JOIN organizations ON ( + ( + workspaces.organization_id = organizations.id + ) + ) + ) + JOIN templates ON ( + ( + workspaces.template_id = templates.id + ) + ) + ); + +COMMENT ON VIEW workspaces_expanded IS 'Joins in the display name information such as username, avatar, and organization name.'; diff --git a/coderd/database/migrations/000330_workspace_with_correct_owner_names.up.sql b/coderd/database/migrations/000330_workspace_with_correct_owner_names.up.sql new file mode 100644 index 0000000000000..0374ef335a138 --- /dev/null +++ b/coderd/database/migrations/000330_workspace_with_correct_owner_names.up.sql @@ -0,0 +1,209 @@ +DROP VIEW template_version_with_user; + +DROP VIEW workspace_build_with_user; + +DROP VIEW template_with_names; + +DROP VIEW workspaces_expanded; + +DROP VIEW visible_users; + +-- Adds users.name +CREATE VIEW visible_users AS +SELECT users.id, users.username, users.name, users.avatar_url +FROM users; + +COMMENT ON VIEW visible_users IS 'Visible fields of users are allowed to be joined with other tables for including context of other resources.'; + +-- Recreate `workspace_build_with_user` as described in dump.sql +CREATE VIEW workspace_build_with_user AS +SELECT + workspace_builds.id, + workspace_builds.created_at, + workspace_builds.updated_at, + workspace_builds.workspace_id, + workspace_builds.template_version_id, + workspace_builds.build_number, + workspace_builds.transition, + workspace_builds.initiator_id, + workspace_builds.provisioner_state, + workspace_builds.job_id, + workspace_builds.deadline, + workspace_builds.reason, + workspace_builds.daily_cost, + workspace_builds.max_deadline, + workspace_builds.template_version_preset_id, + COALESCE( + visible_users.avatar_url, + ''::text + ) AS initiator_by_avatar_url, + COALESCE( + visible_users.username, + ''::text + ) AS initiator_by_username, + COALESCE(visible_users.name, ''::text) AS initiator_by_name +FROM ( + workspace_builds + LEFT JOIN visible_users ON ( + ( + workspace_builds.initiator_id = visible_users.id + ) + ) + ); + +COMMENT ON VIEW workspace_build_with_user IS 'Joins in the username + avatar url of the initiated by user.'; + +-- Recreate `template_with_names` as described in dump.sql +CREATE VIEW template_with_names AS +SELECT + templates.id, + templates.created_at, + templates.updated_at, + templates.organization_id, + templates.deleted, + templates.name, + templates.provisioner, + templates.active_version_id, + templates.description, + templates.default_ttl, + templates.created_by, + templates.icon, + templates.user_acl, + templates.group_acl, + templates.display_name, + templates.allow_user_cancel_workspace_jobs, + templates.allow_user_autostart, + templates.allow_user_autostop, + templates.failure_ttl, + templates.time_til_dormant, + templates.time_til_dormant_autodelete, + templates.autostop_requirement_days_of_week, + templates.autostop_requirement_weeks, + templates.autostart_block_days_of_week, + templates.require_active_version, + templates.deprecated, + templates.activity_bump, + templates.max_port_sharing_level, + templates.use_classic_parameter_flow, + COALESCE( + visible_users.avatar_url, + ''::text + ) AS created_by_avatar_url, + COALESCE( + visible_users.username, + ''::text + ) AS created_by_username, + COALESCE(visible_users.name, ''::text) AS created_by_name, + COALESCE(organizations.name, ''::text) AS organization_name, + COALESCE( + organizations.display_name, + ''::text + ) AS organization_display_name, + COALESCE(organizations.icon, ''::text) AS organization_icon +FROM ( + ( + templates + LEFT JOIN visible_users ON ( + ( + templates.created_by = visible_users.id + ) + ) + ) + LEFT JOIN organizations ON ( + ( + templates.organization_id = organizations.id + ) + ) + ); + +COMMENT ON VIEW template_with_names IS 'Joins in the display name information such as username, avatar, and organization name.'; + +-- Recreate `template_version_with_user` as described in dump.sql +CREATE VIEW template_version_with_user AS +SELECT + template_versions.id, + template_versions.template_id, + template_versions.organization_id, + template_versions.created_at, + template_versions.updated_at, + template_versions.name, + template_versions.readme, + template_versions.job_id, + template_versions.created_by, + template_versions.external_auth_providers, + template_versions.message, + template_versions.archived, + template_versions.source_example_id, + COALESCE( + visible_users.avatar_url, + ''::text + ) AS created_by_avatar_url, + COALESCE( + visible_users.username, + ''::text + ) AS created_by_username, + COALESCE(visible_users.name, ''::text) AS created_by_name +FROM ( + template_versions + LEFT JOIN visible_users ON ( + template_versions.created_by = visible_users.id + ) + ); + +COMMENT ON VIEW template_version_with_user IS 'Joins in the username + avatar url of the created by user.'; + +-- Recreate `workspaces_expanded` as described in dump.sql + +CREATE VIEW workspaces_expanded AS +SELECT + workspaces.id, + workspaces.created_at, + workspaces.updated_at, + workspaces.owner_id, + workspaces.organization_id, + workspaces.template_id, + workspaces.deleted, + workspaces.name, + workspaces.autostart_schedule, + workspaces.ttl, + workspaces.last_used_at, + workspaces.dormant_at, + workspaces.deleting_at, + workspaces.automatic_updates, + workspaces.favorite, + workspaces.next_start_at, + visible_users.avatar_url AS owner_avatar_url, + visible_users.username AS owner_username, + visible_users.name AS owner_name, + organizations.name AS organization_name, + organizations.display_name AS organization_display_name, + organizations.icon AS organization_icon, + organizations.description AS organization_description, + templates.name AS template_name, + templates.display_name AS template_display_name, + templates.icon AS template_icon, + templates.description AS template_description +FROM ( + ( + ( + workspaces + JOIN visible_users ON ( + ( + workspaces.owner_id = visible_users.id + ) + ) + ) + JOIN organizations ON ( + ( + workspaces.organization_id = organizations.id + ) + ) + ) + JOIN templates ON ( + ( + workspaces.template_id = templates.id + ) + ) + ); + +COMMENT ON VIEW workspaces_expanded IS 'Joins in the display name information such as username, avatar, and organization name.'; diff --git a/coderd/database/modelqueries.go b/coderd/database/modelqueries.go index 4144c183de380..1e4d249d8a034 100644 --- a/coderd/database/modelqueries.go +++ b/coderd/database/modelqueries.go @@ -120,6 +120,7 @@ func (q *sqlQuerier) GetAuthorizedTemplates(ctx context.Context, arg GetTemplate &i.UseClassicParameterFlow, &i.CreatedByAvatarURL, &i.CreatedByUsername, + &i.CreatedByName, &i.OrganizationName, &i.OrganizationDisplayName, &i.OrganizationIcon, @@ -294,6 +295,7 @@ func (q *sqlQuerier) GetAuthorizedWorkspaces(ctx context.Context, arg GetWorkspa &i.NextStartAt, &i.OwnerAvatarUrl, &i.OwnerUsername, + &i.OwnerName, &i.OrganizationName, &i.OrganizationDisplayName, &i.OrganizationIcon, diff --git a/coderd/database/models.go b/coderd/database/models.go index d5047f6bbe65f..e9f12d3581936 100644 --- a/coderd/database/models.go +++ b/coderd/database/models.go @@ -3178,6 +3178,7 @@ type Template struct { UseClassicParameterFlow bool `db:"use_classic_parameter_flow" json:"use_classic_parameter_flow"` CreatedByAvatarURL string `db:"created_by_avatar_url" json:"created_by_avatar_url"` CreatedByUsername string `db:"created_by_username" json:"created_by_username"` + CreatedByName string `db:"created_by_name" json:"created_by_name"` OrganizationName string `db:"organization_name" json:"organization_name"` OrganizationDisplayName string `db:"organization_display_name" json:"organization_display_name"` OrganizationIcon string `db:"organization_icon" json:"organization_icon"` @@ -3270,6 +3271,7 @@ type TemplateVersion struct { SourceExampleID sql.NullString `db:"source_example_id" json:"source_example_id"` CreatedByAvatarURL string `db:"created_by_avatar_url" json:"created_by_avatar_url"` CreatedByUsername string `db:"created_by_username" json:"created_by_username"` + CreatedByName string `db:"created_by_name" json:"created_by_name"` } type TemplateVersionParameter struct { @@ -3443,6 +3445,7 @@ type UserStatusChange struct { type VisibleUser struct { ID uuid.UUID `db:"id" json:"id"` Username string `db:"username" json:"username"` + Name string `db:"name" json:"name"` AvatarURL string `db:"avatar_url" json:"avatar_url"` } @@ -3475,6 +3478,7 @@ type Workspace struct { NextStartAt sql.NullTime `db:"next_start_at" json:"next_start_at"` OwnerAvatarUrl string `db:"owner_avatar_url" json:"owner_avatar_url"` OwnerUsername string `db:"owner_username" json:"owner_username"` + OwnerName string `db:"owner_name" json:"owner_name"` OrganizationName string `db:"organization_name" json:"organization_name"` OrganizationDisplayName string `db:"organization_display_name" json:"organization_display_name"` OrganizationIcon string `db:"organization_icon" json:"organization_icon"` @@ -3754,6 +3758,7 @@ type WorkspaceBuild struct { TemplateVersionPresetID uuid.NullUUID `db:"template_version_preset_id" json:"template_version_preset_id"` InitiatorByAvatarUrl string `db:"initiator_by_avatar_url" json:"initiator_by_avatar_url"` InitiatorByUsername string `db:"initiator_by_username" json:"initiator_by_username"` + InitiatorByName string `db:"initiator_by_name" json:"initiator_by_name"` } type WorkspaceBuildParameter struct { diff --git a/coderd/database/queries.sql.go b/coderd/database/queries.sql.go index c3bc5ba7c4fd5..044185a56bf88 100644 --- a/coderd/database/queries.sql.go +++ b/coderd/database/queries.sql.go @@ -10623,7 +10623,7 @@ func (q *sqlQuerier) GetTemplateAverageBuildTime(ctx context.Context, arg GetTem const getTemplateByID = `-- name: GetTemplateByID :one SELECT - id, created_at, updated_at, organization_id, deleted, name, provisioner, active_version_id, description, default_ttl, created_by, icon, user_acl, group_acl, display_name, allow_user_cancel_workspace_jobs, allow_user_autostart, allow_user_autostop, failure_ttl, time_til_dormant, time_til_dormant_autodelete, autostop_requirement_days_of_week, autostop_requirement_weeks, autostart_block_days_of_week, require_active_version, deprecated, activity_bump, max_port_sharing_level, use_classic_parameter_flow, created_by_avatar_url, created_by_username, organization_name, organization_display_name, organization_icon + id, created_at, updated_at, organization_id, deleted, name, provisioner, active_version_id, description, default_ttl, created_by, icon, user_acl, group_acl, display_name, allow_user_cancel_workspace_jobs, allow_user_autostart, allow_user_autostop, failure_ttl, time_til_dormant, time_til_dormant_autodelete, autostop_requirement_days_of_week, autostop_requirement_weeks, autostart_block_days_of_week, require_active_version, deprecated, activity_bump, max_port_sharing_level, use_classic_parameter_flow, created_by_avatar_url, created_by_username, created_by_name, organization_name, organization_display_name, organization_icon FROM template_with_names WHERE @@ -10667,6 +10667,7 @@ func (q *sqlQuerier) GetTemplateByID(ctx context.Context, id uuid.UUID) (Templat &i.UseClassicParameterFlow, &i.CreatedByAvatarURL, &i.CreatedByUsername, + &i.CreatedByName, &i.OrganizationName, &i.OrganizationDisplayName, &i.OrganizationIcon, @@ -10676,7 +10677,7 @@ func (q *sqlQuerier) GetTemplateByID(ctx context.Context, id uuid.UUID) (Templat const getTemplateByOrganizationAndName = `-- name: GetTemplateByOrganizationAndName :one SELECT - id, created_at, updated_at, organization_id, deleted, name, provisioner, active_version_id, description, default_ttl, created_by, icon, user_acl, group_acl, display_name, allow_user_cancel_workspace_jobs, allow_user_autostart, allow_user_autostop, failure_ttl, time_til_dormant, time_til_dormant_autodelete, autostop_requirement_days_of_week, autostop_requirement_weeks, autostart_block_days_of_week, require_active_version, deprecated, activity_bump, max_port_sharing_level, use_classic_parameter_flow, created_by_avatar_url, created_by_username, organization_name, organization_display_name, organization_icon + id, created_at, updated_at, organization_id, deleted, name, provisioner, active_version_id, description, default_ttl, created_by, icon, user_acl, group_acl, display_name, allow_user_cancel_workspace_jobs, allow_user_autostart, allow_user_autostop, failure_ttl, time_til_dormant, time_til_dormant_autodelete, autostop_requirement_days_of_week, autostop_requirement_weeks, autostart_block_days_of_week, require_active_version, deprecated, activity_bump, max_port_sharing_level, use_classic_parameter_flow, created_by_avatar_url, created_by_username, created_by_name, organization_name, organization_display_name, organization_icon FROM template_with_names AS templates WHERE @@ -10728,6 +10729,7 @@ func (q *sqlQuerier) GetTemplateByOrganizationAndName(ctx context.Context, arg G &i.UseClassicParameterFlow, &i.CreatedByAvatarURL, &i.CreatedByUsername, + &i.CreatedByName, &i.OrganizationName, &i.OrganizationDisplayName, &i.OrganizationIcon, @@ -10736,7 +10738,7 @@ func (q *sqlQuerier) GetTemplateByOrganizationAndName(ctx context.Context, arg G } const getTemplates = `-- name: GetTemplates :many -SELECT id, created_at, updated_at, organization_id, deleted, name, provisioner, active_version_id, description, default_ttl, created_by, icon, user_acl, group_acl, display_name, allow_user_cancel_workspace_jobs, allow_user_autostart, allow_user_autostop, failure_ttl, time_til_dormant, time_til_dormant_autodelete, autostop_requirement_days_of_week, autostop_requirement_weeks, autostart_block_days_of_week, require_active_version, deprecated, activity_bump, max_port_sharing_level, use_classic_parameter_flow, created_by_avatar_url, created_by_username, organization_name, organization_display_name, organization_icon FROM template_with_names AS templates +SELECT id, created_at, updated_at, organization_id, deleted, name, provisioner, active_version_id, description, default_ttl, created_by, icon, user_acl, group_acl, display_name, allow_user_cancel_workspace_jobs, allow_user_autostart, allow_user_autostop, failure_ttl, time_til_dormant, time_til_dormant_autodelete, autostop_requirement_days_of_week, autostop_requirement_weeks, autostart_block_days_of_week, require_active_version, deprecated, activity_bump, max_port_sharing_level, use_classic_parameter_flow, created_by_avatar_url, created_by_username, created_by_name, organization_name, organization_display_name, organization_icon FROM template_with_names AS templates ORDER BY (name, id) ASC ` @@ -10781,6 +10783,7 @@ func (q *sqlQuerier) GetTemplates(ctx context.Context) ([]Template, error) { &i.UseClassicParameterFlow, &i.CreatedByAvatarURL, &i.CreatedByUsername, + &i.CreatedByName, &i.OrganizationName, &i.OrganizationDisplayName, &i.OrganizationIcon, @@ -10800,7 +10803,7 @@ func (q *sqlQuerier) GetTemplates(ctx context.Context) ([]Template, error) { const getTemplatesWithFilter = `-- name: GetTemplatesWithFilter :many SELECT - id, created_at, updated_at, organization_id, deleted, name, provisioner, active_version_id, description, default_ttl, created_by, icon, user_acl, group_acl, display_name, allow_user_cancel_workspace_jobs, allow_user_autostart, allow_user_autostop, failure_ttl, time_til_dormant, time_til_dormant_autodelete, autostop_requirement_days_of_week, autostop_requirement_weeks, autostart_block_days_of_week, require_active_version, deprecated, activity_bump, max_port_sharing_level, use_classic_parameter_flow, created_by_avatar_url, created_by_username, organization_name, organization_display_name, organization_icon + id, created_at, updated_at, organization_id, deleted, name, provisioner, active_version_id, description, default_ttl, created_by, icon, user_acl, group_acl, display_name, allow_user_cancel_workspace_jobs, allow_user_autostart, allow_user_autostop, failure_ttl, time_til_dormant, time_til_dormant_autodelete, autostop_requirement_days_of_week, autostop_requirement_weeks, autostart_block_days_of_week, require_active_version, deprecated, activity_bump, max_port_sharing_level, use_classic_parameter_flow, created_by_avatar_url, created_by_username, created_by_name, organization_name, organization_display_name, organization_icon FROM template_with_names AS templates WHERE @@ -10903,6 +10906,7 @@ func (q *sqlQuerier) GetTemplatesWithFilter(ctx context.Context, arg GetTemplate &i.UseClassicParameterFlow, &i.CreatedByAvatarURL, &i.CreatedByUsername, + &i.CreatedByName, &i.OrganizationName, &i.OrganizationDisplayName, &i.OrganizationIcon, @@ -11431,7 +11435,7 @@ func (q *sqlQuerier) ArchiveUnusedTemplateVersions(ctx context.Context, arg Arch const getPreviousTemplateVersion = `-- name: GetPreviousTemplateVersion :one SELECT - id, template_id, organization_id, created_at, updated_at, name, readme, job_id, created_by, external_auth_providers, message, archived, source_example_id, created_by_avatar_url, created_by_username + id, template_id, organization_id, created_at, updated_at, name, readme, job_id, created_by, external_auth_providers, message, archived, source_example_id, created_by_avatar_url, created_by_username, created_by_name FROM template_version_with_user AS template_versions WHERE @@ -11471,13 +11475,14 @@ func (q *sqlQuerier) GetPreviousTemplateVersion(ctx context.Context, arg GetPrev &i.SourceExampleID, &i.CreatedByAvatarURL, &i.CreatedByUsername, + &i.CreatedByName, ) return i, err } const getTemplateVersionByID = `-- name: GetTemplateVersionByID :one SELECT - id, template_id, organization_id, created_at, updated_at, name, readme, job_id, created_by, external_auth_providers, message, archived, source_example_id, created_by_avatar_url, created_by_username + id, template_id, organization_id, created_at, updated_at, name, readme, job_id, created_by, external_auth_providers, message, archived, source_example_id, created_by_avatar_url, created_by_username, created_by_name FROM template_version_with_user AS template_versions WHERE @@ -11503,13 +11508,14 @@ func (q *sqlQuerier) GetTemplateVersionByID(ctx context.Context, id uuid.UUID) ( &i.SourceExampleID, &i.CreatedByAvatarURL, &i.CreatedByUsername, + &i.CreatedByName, ) return i, err } const getTemplateVersionByJobID = `-- name: GetTemplateVersionByJobID :one SELECT - id, template_id, organization_id, created_at, updated_at, name, readme, job_id, created_by, external_auth_providers, message, archived, source_example_id, created_by_avatar_url, created_by_username + id, template_id, organization_id, created_at, updated_at, name, readme, job_id, created_by, external_auth_providers, message, archived, source_example_id, created_by_avatar_url, created_by_username, created_by_name FROM template_version_with_user AS template_versions WHERE @@ -11535,13 +11541,14 @@ func (q *sqlQuerier) GetTemplateVersionByJobID(ctx context.Context, jobID uuid.U &i.SourceExampleID, &i.CreatedByAvatarURL, &i.CreatedByUsername, + &i.CreatedByName, ) return i, err } const getTemplateVersionByTemplateIDAndName = `-- name: GetTemplateVersionByTemplateIDAndName :one SELECT - id, template_id, organization_id, created_at, updated_at, name, readme, job_id, created_by, external_auth_providers, message, archived, source_example_id, created_by_avatar_url, created_by_username + id, template_id, organization_id, created_at, updated_at, name, readme, job_id, created_by, external_auth_providers, message, archived, source_example_id, created_by_avatar_url, created_by_username, created_by_name FROM template_version_with_user AS template_versions WHERE @@ -11573,13 +11580,14 @@ func (q *sqlQuerier) GetTemplateVersionByTemplateIDAndName(ctx context.Context, &i.SourceExampleID, &i.CreatedByAvatarURL, &i.CreatedByUsername, + &i.CreatedByName, ) return i, err } const getTemplateVersionsByIDs = `-- name: GetTemplateVersionsByIDs :many SELECT - id, template_id, organization_id, created_at, updated_at, name, readme, job_id, created_by, external_auth_providers, message, archived, source_example_id, created_by_avatar_url, created_by_username + id, template_id, organization_id, created_at, updated_at, name, readme, job_id, created_by, external_auth_providers, message, archived, source_example_id, created_by_avatar_url, created_by_username, created_by_name FROM template_version_with_user AS template_versions WHERE @@ -11611,6 +11619,7 @@ func (q *sqlQuerier) GetTemplateVersionsByIDs(ctx context.Context, ids []uuid.UU &i.SourceExampleID, &i.CreatedByAvatarURL, &i.CreatedByUsername, + &i.CreatedByName, ); err != nil { return nil, err } @@ -11627,7 +11636,7 @@ func (q *sqlQuerier) GetTemplateVersionsByIDs(ctx context.Context, ids []uuid.UU const getTemplateVersionsByTemplateID = `-- name: GetTemplateVersionsByTemplateID :many SELECT - id, template_id, organization_id, created_at, updated_at, name, readme, job_id, created_by, external_auth_providers, message, archived, source_example_id, created_by_avatar_url, created_by_username + id, template_id, organization_id, created_at, updated_at, name, readme, job_id, created_by, external_auth_providers, message, archived, source_example_id, created_by_avatar_url, created_by_username, created_by_name FROM template_version_with_user AS template_versions WHERE @@ -11706,6 +11715,7 @@ func (q *sqlQuerier) GetTemplateVersionsByTemplateID(ctx context.Context, arg Ge &i.SourceExampleID, &i.CreatedByAvatarURL, &i.CreatedByUsername, + &i.CreatedByName, ); err != nil { return nil, err } @@ -11721,7 +11731,7 @@ func (q *sqlQuerier) GetTemplateVersionsByTemplateID(ctx context.Context, arg Ge } const getTemplateVersionsCreatedAfter = `-- name: GetTemplateVersionsCreatedAfter :many -SELECT id, template_id, organization_id, created_at, updated_at, name, readme, job_id, created_by, external_auth_providers, message, archived, source_example_id, created_by_avatar_url, created_by_username FROM template_version_with_user AS template_versions WHERE created_at > $1 +SELECT id, template_id, organization_id, created_at, updated_at, name, readme, job_id, created_by, external_auth_providers, message, archived, source_example_id, created_by_avatar_url, created_by_username, created_by_name FROM template_version_with_user AS template_versions WHERE created_at > $1 ` func (q *sqlQuerier) GetTemplateVersionsCreatedAfter(ctx context.Context, createdAt time.Time) ([]TemplateVersion, error) { @@ -11749,6 +11759,7 @@ func (q *sqlQuerier) GetTemplateVersionsCreatedAfter(ctx context.Context, create &i.SourceExampleID, &i.CreatedByAvatarURL, &i.CreatedByUsername, + &i.CreatedByName, ); err != nil { return nil, err } @@ -14143,7 +14154,7 @@ const getWorkspaceAgentAndLatestBuildByAuthToken = `-- name: GetWorkspaceAgentAn SELECT workspaces.id, workspaces.created_at, workspaces.updated_at, workspaces.owner_id, workspaces.organization_id, workspaces.template_id, workspaces.deleted, workspaces.name, workspaces.autostart_schedule, workspaces.ttl, workspaces.last_used_at, workspaces.dormant_at, workspaces.deleting_at, workspaces.automatic_updates, workspaces.favorite, workspaces.next_start_at, workspace_agents.id, workspace_agents.created_at, workspace_agents.updated_at, workspace_agents.name, workspace_agents.first_connected_at, workspace_agents.last_connected_at, workspace_agents.disconnected_at, workspace_agents.resource_id, workspace_agents.auth_token, workspace_agents.auth_instance_id, workspace_agents.architecture, workspace_agents.environment_variables, workspace_agents.operating_system, workspace_agents.instance_metadata, workspace_agents.resource_metadata, workspace_agents.directory, workspace_agents.version, workspace_agents.last_connected_replica_id, workspace_agents.connection_timeout_seconds, workspace_agents.troubleshooting_url, workspace_agents.motd_file, workspace_agents.lifecycle_state, workspace_agents.expanded_directory, workspace_agents.logs_length, workspace_agents.logs_overflowed, workspace_agents.started_at, workspace_agents.ready_at, workspace_agents.subsystems, workspace_agents.display_apps, workspace_agents.api_version, workspace_agents.display_order, workspace_agents.parent_id, workspace_agents.api_key_scope, - workspace_build_with_user.id, workspace_build_with_user.created_at, workspace_build_with_user.updated_at, workspace_build_with_user.workspace_id, workspace_build_with_user.template_version_id, workspace_build_with_user.build_number, workspace_build_with_user.transition, workspace_build_with_user.initiator_id, workspace_build_with_user.provisioner_state, workspace_build_with_user.job_id, workspace_build_with_user.deadline, workspace_build_with_user.reason, workspace_build_with_user.daily_cost, workspace_build_with_user.max_deadline, workspace_build_with_user.template_version_preset_id, workspace_build_with_user.initiator_by_avatar_url, workspace_build_with_user.initiator_by_username + workspace_build_with_user.id, workspace_build_with_user.created_at, workspace_build_with_user.updated_at, workspace_build_with_user.workspace_id, workspace_build_with_user.template_version_id, workspace_build_with_user.build_number, workspace_build_with_user.transition, workspace_build_with_user.initiator_id, workspace_build_with_user.provisioner_state, workspace_build_with_user.job_id, workspace_build_with_user.deadline, workspace_build_with_user.reason, workspace_build_with_user.daily_cost, workspace_build_with_user.max_deadline, workspace_build_with_user.template_version_preset_id, workspace_build_with_user.initiator_by_avatar_url, workspace_build_with_user.initiator_by_username, workspace_build_with_user.initiator_by_name FROM workspace_agents JOIN @@ -14251,6 +14262,7 @@ func (q *sqlQuerier) GetWorkspaceAgentAndLatestBuildByAuthToken(ctx context.Cont &i.WorkspaceBuild.TemplateVersionPresetID, &i.WorkspaceBuild.InitiatorByAvatarUrl, &i.WorkspaceBuild.InitiatorByUsername, + &i.WorkspaceBuild.InitiatorByName, ) return i, err } @@ -16748,7 +16760,7 @@ func (q *sqlQuerier) InsertWorkspaceBuildParameters(ctx context.Context, arg Ins } const getActiveWorkspaceBuildsByTemplateID = `-- name: GetActiveWorkspaceBuildsByTemplateID :many -SELECT wb.id, wb.created_at, wb.updated_at, wb.workspace_id, wb.template_version_id, wb.build_number, wb.transition, wb.initiator_id, wb.provisioner_state, wb.job_id, wb.deadline, wb.reason, wb.daily_cost, wb.max_deadline, wb.template_version_preset_id, wb.initiator_by_avatar_url, wb.initiator_by_username +SELECT wb.id, wb.created_at, wb.updated_at, wb.workspace_id, wb.template_version_id, wb.build_number, wb.transition, wb.initiator_id, wb.provisioner_state, wb.job_id, wb.deadline, wb.reason, wb.daily_cost, wb.max_deadline, wb.template_version_preset_id, wb.initiator_by_avatar_url, wb.initiator_by_username, wb.initiator_by_name FROM ( SELECT workspace_id, MAX(build_number) as max_build_number @@ -16805,6 +16817,7 @@ func (q *sqlQuerier) GetActiveWorkspaceBuildsByTemplateID(ctx context.Context, t &i.TemplateVersionPresetID, &i.InitiatorByAvatarUrl, &i.InitiatorByUsername, + &i.InitiatorByName, ); err != nil { return nil, err } @@ -16901,7 +16914,7 @@ func (q *sqlQuerier) GetFailedWorkspaceBuildsByTemplateID(ctx context.Context, a const getLatestWorkspaceBuildByWorkspaceID = `-- name: GetLatestWorkspaceBuildByWorkspaceID :one SELECT - id, created_at, updated_at, workspace_id, template_version_id, build_number, transition, initiator_id, provisioner_state, job_id, deadline, reason, daily_cost, max_deadline, template_version_preset_id, initiator_by_avatar_url, initiator_by_username + id, created_at, updated_at, workspace_id, template_version_id, build_number, transition, initiator_id, provisioner_state, job_id, deadline, reason, daily_cost, max_deadline, template_version_preset_id, initiator_by_avatar_url, initiator_by_username, initiator_by_name FROM workspace_build_with_user AS workspace_builds WHERE @@ -16933,12 +16946,13 @@ func (q *sqlQuerier) GetLatestWorkspaceBuildByWorkspaceID(ctx context.Context, w &i.TemplateVersionPresetID, &i.InitiatorByAvatarUrl, &i.InitiatorByUsername, + &i.InitiatorByName, ) return i, err } const getLatestWorkspaceBuilds = `-- name: GetLatestWorkspaceBuilds :many -SELECT wb.id, wb.created_at, wb.updated_at, wb.workspace_id, wb.template_version_id, wb.build_number, wb.transition, wb.initiator_id, wb.provisioner_state, wb.job_id, wb.deadline, wb.reason, wb.daily_cost, wb.max_deadline, wb.template_version_preset_id, wb.initiator_by_avatar_url, wb.initiator_by_username +SELECT wb.id, wb.created_at, wb.updated_at, wb.workspace_id, wb.template_version_id, wb.build_number, wb.transition, wb.initiator_id, wb.provisioner_state, wb.job_id, wb.deadline, wb.reason, wb.daily_cost, wb.max_deadline, wb.template_version_preset_id, wb.initiator_by_avatar_url, wb.initiator_by_username, wb.initiator_by_name FROM ( SELECT workspace_id, MAX(build_number) as max_build_number @@ -16979,6 +16993,7 @@ func (q *sqlQuerier) GetLatestWorkspaceBuilds(ctx context.Context) ([]WorkspaceB &i.TemplateVersionPresetID, &i.InitiatorByAvatarUrl, &i.InitiatorByUsername, + &i.InitiatorByName, ); err != nil { return nil, err } @@ -16994,7 +17009,7 @@ func (q *sqlQuerier) GetLatestWorkspaceBuilds(ctx context.Context) ([]WorkspaceB } const getLatestWorkspaceBuildsByWorkspaceIDs = `-- name: GetLatestWorkspaceBuildsByWorkspaceIDs :many -SELECT wb.id, wb.created_at, wb.updated_at, wb.workspace_id, wb.template_version_id, wb.build_number, wb.transition, wb.initiator_id, wb.provisioner_state, wb.job_id, wb.deadline, wb.reason, wb.daily_cost, wb.max_deadline, wb.template_version_preset_id, wb.initiator_by_avatar_url, wb.initiator_by_username +SELECT wb.id, wb.created_at, wb.updated_at, wb.workspace_id, wb.template_version_id, wb.build_number, wb.transition, wb.initiator_id, wb.provisioner_state, wb.job_id, wb.deadline, wb.reason, wb.daily_cost, wb.max_deadline, wb.template_version_preset_id, wb.initiator_by_avatar_url, wb.initiator_by_username, wb.initiator_by_name FROM ( SELECT workspace_id, MAX(build_number) as max_build_number @@ -17037,6 +17052,7 @@ func (q *sqlQuerier) GetLatestWorkspaceBuildsByWorkspaceIDs(ctx context.Context, &i.TemplateVersionPresetID, &i.InitiatorByAvatarUrl, &i.InitiatorByUsername, + &i.InitiatorByName, ); err != nil { return nil, err } @@ -17053,7 +17069,7 @@ func (q *sqlQuerier) GetLatestWorkspaceBuildsByWorkspaceIDs(ctx context.Context, const getWorkspaceBuildByID = `-- name: GetWorkspaceBuildByID :one SELECT - id, created_at, updated_at, workspace_id, template_version_id, build_number, transition, initiator_id, provisioner_state, job_id, deadline, reason, daily_cost, max_deadline, template_version_preset_id, initiator_by_avatar_url, initiator_by_username + id, created_at, updated_at, workspace_id, template_version_id, build_number, transition, initiator_id, provisioner_state, job_id, deadline, reason, daily_cost, max_deadline, template_version_preset_id, initiator_by_avatar_url, initiator_by_username, initiator_by_name FROM workspace_build_with_user AS workspace_builds WHERE @@ -17083,13 +17099,14 @@ func (q *sqlQuerier) GetWorkspaceBuildByID(ctx context.Context, id uuid.UUID) (W &i.TemplateVersionPresetID, &i.InitiatorByAvatarUrl, &i.InitiatorByUsername, + &i.InitiatorByName, ) return i, err } const getWorkspaceBuildByJobID = `-- name: GetWorkspaceBuildByJobID :one SELECT - id, created_at, updated_at, workspace_id, template_version_id, build_number, transition, initiator_id, provisioner_state, job_id, deadline, reason, daily_cost, max_deadline, template_version_preset_id, initiator_by_avatar_url, initiator_by_username + id, created_at, updated_at, workspace_id, template_version_id, build_number, transition, initiator_id, provisioner_state, job_id, deadline, reason, daily_cost, max_deadline, template_version_preset_id, initiator_by_avatar_url, initiator_by_username, initiator_by_name FROM workspace_build_with_user AS workspace_builds WHERE @@ -17119,13 +17136,14 @@ func (q *sqlQuerier) GetWorkspaceBuildByJobID(ctx context.Context, jobID uuid.UU &i.TemplateVersionPresetID, &i.InitiatorByAvatarUrl, &i.InitiatorByUsername, + &i.InitiatorByName, ) return i, err } const getWorkspaceBuildByWorkspaceIDAndBuildNumber = `-- name: GetWorkspaceBuildByWorkspaceIDAndBuildNumber :one SELECT - id, created_at, updated_at, workspace_id, template_version_id, build_number, transition, initiator_id, provisioner_state, job_id, deadline, reason, daily_cost, max_deadline, template_version_preset_id, initiator_by_avatar_url, initiator_by_username + id, created_at, updated_at, workspace_id, template_version_id, build_number, transition, initiator_id, provisioner_state, job_id, deadline, reason, daily_cost, max_deadline, template_version_preset_id, initiator_by_avatar_url, initiator_by_username, initiator_by_name FROM workspace_build_with_user AS workspace_builds WHERE @@ -17159,6 +17177,7 @@ func (q *sqlQuerier) GetWorkspaceBuildByWorkspaceIDAndBuildNumber(ctx context.Co &i.TemplateVersionPresetID, &i.InitiatorByAvatarUrl, &i.InitiatorByUsername, + &i.InitiatorByName, ) return i, err } @@ -17232,7 +17251,7 @@ func (q *sqlQuerier) GetWorkspaceBuildStatsByTemplates(ctx context.Context, sinc const getWorkspaceBuildsByWorkspaceID = `-- name: GetWorkspaceBuildsByWorkspaceID :many SELECT - id, created_at, updated_at, workspace_id, template_version_id, build_number, transition, initiator_id, provisioner_state, job_id, deadline, reason, daily_cost, max_deadline, template_version_preset_id, initiator_by_avatar_url, initiator_by_username + id, created_at, updated_at, workspace_id, template_version_id, build_number, transition, initiator_id, provisioner_state, job_id, deadline, reason, daily_cost, max_deadline, template_version_preset_id, initiator_by_avatar_url, initiator_by_username, initiator_by_name FROM workspace_build_with_user AS workspace_builds WHERE @@ -17305,6 +17324,7 @@ func (q *sqlQuerier) GetWorkspaceBuildsByWorkspaceID(ctx context.Context, arg Ge &i.TemplateVersionPresetID, &i.InitiatorByAvatarUrl, &i.InitiatorByUsername, + &i.InitiatorByName, ); err != nil { return nil, err } @@ -17320,7 +17340,7 @@ func (q *sqlQuerier) GetWorkspaceBuildsByWorkspaceID(ctx context.Context, arg Ge } const getWorkspaceBuildsCreatedAfter = `-- name: GetWorkspaceBuildsCreatedAfter :many -SELECT id, created_at, updated_at, workspace_id, template_version_id, build_number, transition, initiator_id, provisioner_state, job_id, deadline, reason, daily_cost, max_deadline, template_version_preset_id, initiator_by_avatar_url, initiator_by_username FROM workspace_build_with_user WHERE created_at > $1 +SELECT id, created_at, updated_at, workspace_id, template_version_id, build_number, transition, initiator_id, provisioner_state, job_id, deadline, reason, daily_cost, max_deadline, template_version_preset_id, initiator_by_avatar_url, initiator_by_username, initiator_by_name FROM workspace_build_with_user WHERE created_at > $1 ` func (q *sqlQuerier) GetWorkspaceBuildsCreatedAfter(ctx context.Context, createdAt time.Time) ([]WorkspaceBuild, error) { @@ -17350,6 +17370,7 @@ func (q *sqlQuerier) GetWorkspaceBuildsCreatedAfter(ctx context.Context, created &i.TemplateVersionPresetID, &i.InitiatorByAvatarUrl, &i.InitiatorByUsername, + &i.InitiatorByName, ); err != nil { return nil, err } @@ -18079,7 +18100,7 @@ func (q *sqlQuerier) GetDeploymentWorkspaceStats(ctx context.Context) (GetDeploy const getWorkspaceByAgentID = `-- name: GetWorkspaceByAgentID :one SELECT - id, created_at, updated_at, owner_id, organization_id, template_id, deleted, name, autostart_schedule, ttl, last_used_at, dormant_at, deleting_at, automatic_updates, favorite, next_start_at, owner_avatar_url, owner_username, organization_name, organization_display_name, organization_icon, organization_description, template_name, template_display_name, template_icon, template_description + id, created_at, updated_at, owner_id, organization_id, template_id, deleted, name, autostart_schedule, ttl, last_used_at, dormant_at, deleting_at, automatic_updates, favorite, next_start_at, owner_avatar_url, owner_username, owner_name, organization_name, organization_display_name, organization_icon, organization_description, template_name, template_display_name, template_icon, template_description FROM workspaces_expanded as workspaces WHERE @@ -18129,6 +18150,7 @@ func (q *sqlQuerier) GetWorkspaceByAgentID(ctx context.Context, agentID uuid.UUI &i.NextStartAt, &i.OwnerAvatarUrl, &i.OwnerUsername, + &i.OwnerName, &i.OrganizationName, &i.OrganizationDisplayName, &i.OrganizationIcon, @@ -18143,7 +18165,7 @@ func (q *sqlQuerier) GetWorkspaceByAgentID(ctx context.Context, agentID uuid.UUI const getWorkspaceByID = `-- name: GetWorkspaceByID :one SELECT - id, created_at, updated_at, owner_id, organization_id, template_id, deleted, name, autostart_schedule, ttl, last_used_at, dormant_at, deleting_at, automatic_updates, favorite, next_start_at, owner_avatar_url, owner_username, organization_name, organization_display_name, organization_icon, organization_description, template_name, template_display_name, template_icon, template_description + id, created_at, updated_at, owner_id, organization_id, template_id, deleted, name, autostart_schedule, ttl, last_used_at, dormant_at, deleting_at, automatic_updates, favorite, next_start_at, owner_avatar_url, owner_username, owner_name, organization_name, organization_display_name, organization_icon, organization_description, template_name, template_display_name, template_icon, template_description FROM workspaces_expanded WHERE @@ -18174,6 +18196,7 @@ func (q *sqlQuerier) GetWorkspaceByID(ctx context.Context, id uuid.UUID) (Worksp &i.NextStartAt, &i.OwnerAvatarUrl, &i.OwnerUsername, + &i.OwnerName, &i.OrganizationName, &i.OrganizationDisplayName, &i.OrganizationIcon, @@ -18188,7 +18211,7 @@ func (q *sqlQuerier) GetWorkspaceByID(ctx context.Context, id uuid.UUID) (Worksp const getWorkspaceByOwnerIDAndName = `-- name: GetWorkspaceByOwnerIDAndName :one SELECT - id, created_at, updated_at, owner_id, organization_id, template_id, deleted, name, autostart_schedule, ttl, last_used_at, dormant_at, deleting_at, automatic_updates, favorite, next_start_at, owner_avatar_url, owner_username, organization_name, organization_display_name, organization_icon, organization_description, template_name, template_display_name, template_icon, template_description + id, created_at, updated_at, owner_id, organization_id, template_id, deleted, name, autostart_schedule, ttl, last_used_at, dormant_at, deleting_at, automatic_updates, favorite, next_start_at, owner_avatar_url, owner_username, owner_name, organization_name, organization_display_name, organization_icon, organization_description, template_name, template_display_name, template_icon, template_description FROM workspaces_expanded as workspaces WHERE @@ -18226,6 +18249,7 @@ func (q *sqlQuerier) GetWorkspaceByOwnerIDAndName(ctx context.Context, arg GetWo &i.NextStartAt, &i.OwnerAvatarUrl, &i.OwnerUsername, + &i.OwnerName, &i.OrganizationName, &i.OrganizationDisplayName, &i.OrganizationIcon, @@ -18240,7 +18264,7 @@ func (q *sqlQuerier) GetWorkspaceByOwnerIDAndName(ctx context.Context, arg GetWo const getWorkspaceByResourceID = `-- name: GetWorkspaceByResourceID :one SELECT - id, created_at, updated_at, owner_id, organization_id, template_id, deleted, name, autostart_schedule, ttl, last_used_at, dormant_at, deleting_at, automatic_updates, favorite, next_start_at, owner_avatar_url, owner_username, organization_name, organization_display_name, organization_icon, organization_description, template_name, template_display_name, template_icon, template_description + id, created_at, updated_at, owner_id, organization_id, template_id, deleted, name, autostart_schedule, ttl, last_used_at, dormant_at, deleting_at, automatic_updates, favorite, next_start_at, owner_avatar_url, owner_username, owner_name, organization_name, organization_display_name, organization_icon, organization_description, template_name, template_display_name, template_icon, template_description FROM workspaces_expanded as workspaces WHERE @@ -18285,6 +18309,7 @@ func (q *sqlQuerier) GetWorkspaceByResourceID(ctx context.Context, resourceID uu &i.NextStartAt, &i.OwnerAvatarUrl, &i.OwnerUsername, + &i.OwnerName, &i.OrganizationName, &i.OrganizationDisplayName, &i.OrganizationIcon, @@ -18299,7 +18324,7 @@ func (q *sqlQuerier) GetWorkspaceByResourceID(ctx context.Context, resourceID uu const getWorkspaceByWorkspaceAppID = `-- name: GetWorkspaceByWorkspaceAppID :one SELECT - id, created_at, updated_at, owner_id, organization_id, template_id, deleted, name, autostart_schedule, ttl, last_used_at, dormant_at, deleting_at, automatic_updates, favorite, next_start_at, owner_avatar_url, owner_username, organization_name, organization_display_name, organization_icon, organization_description, template_name, template_display_name, template_icon, template_description + id, created_at, updated_at, owner_id, organization_id, template_id, deleted, name, autostart_schedule, ttl, last_used_at, dormant_at, deleting_at, automatic_updates, favorite, next_start_at, owner_avatar_url, owner_username, owner_name, organization_name, organization_display_name, organization_icon, organization_description, template_name, template_display_name, template_icon, template_description FROM workspaces_expanded as workspaces WHERE @@ -18356,6 +18381,7 @@ func (q *sqlQuerier) GetWorkspaceByWorkspaceAppID(ctx context.Context, workspace &i.NextStartAt, &i.OwnerAvatarUrl, &i.OwnerUsername, + &i.OwnerName, &i.OrganizationName, &i.OrganizationDisplayName, &i.OrganizationIcon, @@ -18413,7 +18439,7 @@ SELECT ), filtered_workspaces AS ( SELECT - workspaces.id, workspaces.created_at, workspaces.updated_at, workspaces.owner_id, workspaces.organization_id, workspaces.template_id, workspaces.deleted, workspaces.name, workspaces.autostart_schedule, workspaces.ttl, workspaces.last_used_at, workspaces.dormant_at, workspaces.deleting_at, workspaces.automatic_updates, workspaces.favorite, workspaces.next_start_at, workspaces.owner_avatar_url, workspaces.owner_username, workspaces.organization_name, workspaces.organization_display_name, workspaces.organization_icon, workspaces.organization_description, workspaces.template_name, workspaces.template_display_name, workspaces.template_icon, workspaces.template_description, + workspaces.id, workspaces.created_at, workspaces.updated_at, workspaces.owner_id, workspaces.organization_id, workspaces.template_id, workspaces.deleted, workspaces.name, workspaces.autostart_schedule, workspaces.ttl, workspaces.last_used_at, workspaces.dormant_at, workspaces.deleting_at, workspaces.automatic_updates, workspaces.favorite, workspaces.next_start_at, workspaces.owner_avatar_url, workspaces.owner_username, workspaces.owner_name, workspaces.organization_name, workspaces.organization_display_name, workspaces.organization_icon, workspaces.organization_description, workspaces.template_name, workspaces.template_display_name, workspaces.template_icon, workspaces.template_description, latest_build.template_version_id, latest_build.template_version_name, latest_build.completed_at as latest_build_completed_at, @@ -18653,7 +18679,7 @@ WHERE -- @authorize_filter ), filtered_workspaces_order AS ( SELECT - fw.id, fw.created_at, fw.updated_at, fw.owner_id, fw.organization_id, fw.template_id, fw.deleted, fw.name, fw.autostart_schedule, fw.ttl, fw.last_used_at, fw.dormant_at, fw.deleting_at, fw.automatic_updates, fw.favorite, fw.next_start_at, fw.owner_avatar_url, fw.owner_username, fw.organization_name, fw.organization_display_name, fw.organization_icon, fw.organization_description, fw.template_name, fw.template_display_name, fw.template_icon, fw.template_description, fw.template_version_id, fw.template_version_name, fw.latest_build_completed_at, fw.latest_build_canceled_at, fw.latest_build_error, fw.latest_build_transition, fw.latest_build_status + fw.id, fw.created_at, fw.updated_at, fw.owner_id, fw.organization_id, fw.template_id, fw.deleted, fw.name, fw.autostart_schedule, fw.ttl, fw.last_used_at, fw.dormant_at, fw.deleting_at, fw.automatic_updates, fw.favorite, fw.next_start_at, fw.owner_avatar_url, fw.owner_username, fw.owner_name, fw.organization_name, fw.organization_display_name, fw.organization_icon, fw.organization_description, fw.template_name, fw.template_display_name, fw.template_icon, fw.template_description, fw.template_version_id, fw.template_version_name, fw.latest_build_completed_at, fw.latest_build_canceled_at, fw.latest_build_error, fw.latest_build_transition, fw.latest_build_status FROM filtered_workspaces fw ORDER BY @@ -18674,7 +18700,7 @@ WHERE $20 ), filtered_workspaces_order_with_summary AS ( SELECT - fwo.id, fwo.created_at, fwo.updated_at, fwo.owner_id, fwo.organization_id, fwo.template_id, fwo.deleted, fwo.name, fwo.autostart_schedule, fwo.ttl, fwo.last_used_at, fwo.dormant_at, fwo.deleting_at, fwo.automatic_updates, fwo.favorite, fwo.next_start_at, fwo.owner_avatar_url, fwo.owner_username, fwo.organization_name, fwo.organization_display_name, fwo.organization_icon, fwo.organization_description, fwo.template_name, fwo.template_display_name, fwo.template_icon, fwo.template_description, fwo.template_version_id, fwo.template_version_name, fwo.latest_build_completed_at, fwo.latest_build_canceled_at, fwo.latest_build_error, fwo.latest_build_transition, fwo.latest_build_status + fwo.id, fwo.created_at, fwo.updated_at, fwo.owner_id, fwo.organization_id, fwo.template_id, fwo.deleted, fwo.name, fwo.autostart_schedule, fwo.ttl, fwo.last_used_at, fwo.dormant_at, fwo.deleting_at, fwo.automatic_updates, fwo.favorite, fwo.next_start_at, fwo.owner_avatar_url, fwo.owner_username, fwo.owner_name, fwo.organization_name, fwo.organization_display_name, fwo.organization_icon, fwo.organization_description, fwo.template_name, fwo.template_display_name, fwo.template_icon, fwo.template_description, fwo.template_version_id, fwo.template_version_name, fwo.latest_build_completed_at, fwo.latest_build_canceled_at, fwo.latest_build_error, fwo.latest_build_transition, fwo.latest_build_status FROM filtered_workspaces_order fwo -- Return a technical summary row with total count of workspaces. @@ -18699,6 +18725,7 @@ WHERE '0001-01-01 00:00:00+00'::timestamptz, -- next_start_at '', -- owner_avatar_url '', -- owner_username + '', -- owner_name '', -- organization_name '', -- organization_display_name '', -- organization_icon @@ -18724,7 +18751,7 @@ WHERE filtered_workspaces ) SELECT - fwos.id, fwos.created_at, fwos.updated_at, fwos.owner_id, fwos.organization_id, fwos.template_id, fwos.deleted, fwos.name, fwos.autostart_schedule, fwos.ttl, fwos.last_used_at, fwos.dormant_at, fwos.deleting_at, fwos.automatic_updates, fwos.favorite, fwos.next_start_at, fwos.owner_avatar_url, fwos.owner_username, fwos.organization_name, fwos.organization_display_name, fwos.organization_icon, fwos.organization_description, fwos.template_name, fwos.template_display_name, fwos.template_icon, fwos.template_description, fwos.template_version_id, fwos.template_version_name, fwos.latest_build_completed_at, fwos.latest_build_canceled_at, fwos.latest_build_error, fwos.latest_build_transition, fwos.latest_build_status, + fwos.id, fwos.created_at, fwos.updated_at, fwos.owner_id, fwos.organization_id, fwos.template_id, fwos.deleted, fwos.name, fwos.autostart_schedule, fwos.ttl, fwos.last_used_at, fwos.dormant_at, fwos.deleting_at, fwos.automatic_updates, fwos.favorite, fwos.next_start_at, fwos.owner_avatar_url, fwos.owner_username, fwos.owner_name, fwos.organization_name, fwos.organization_display_name, fwos.organization_icon, fwos.organization_description, fwos.template_name, fwos.template_display_name, fwos.template_icon, fwos.template_description, fwos.template_version_id, fwos.template_version_name, fwos.latest_build_completed_at, fwos.latest_build_canceled_at, fwos.latest_build_error, fwos.latest_build_transition, fwos.latest_build_status, tc.count FROM filtered_workspaces_order_with_summary fwos @@ -18776,6 +18803,7 @@ type GetWorkspacesRow struct { NextStartAt sql.NullTime `db:"next_start_at" json:"next_start_at"` OwnerAvatarUrl string `db:"owner_avatar_url" json:"owner_avatar_url"` OwnerUsername string `db:"owner_username" json:"owner_username"` + OwnerName string `db:"owner_name" json:"owner_name"` OrganizationName string `db:"organization_name" json:"organization_name"` OrganizationDisplayName string `db:"organization_display_name" json:"organization_display_name"` OrganizationIcon string `db:"organization_icon" json:"organization_icon"` @@ -18848,6 +18876,7 @@ func (q *sqlQuerier) GetWorkspaces(ctx context.Context, arg GetWorkspacesParams) &i.NextStartAt, &i.OwnerAvatarUrl, &i.OwnerUsername, + &i.OwnerName, &i.OrganizationName, &i.OrganizationDisplayName, &i.OrganizationIcon, diff --git a/coderd/database/queries/workspaces.sql b/coderd/database/queries/workspaces.sql index 44b7dcbf0387d..d439ae2aa9944 100644 --- a/coderd/database/queries/workspaces.sql +++ b/coderd/database/queries/workspaces.sql @@ -395,6 +395,7 @@ WHERE '0001-01-01 00:00:00+00'::timestamptz, -- next_start_at '', -- owner_avatar_url '', -- owner_username + '', -- owner_name '', -- organization_name '', -- organization_display_name '', -- organization_icon diff --git a/coderd/workspaceagents.go b/coderd/workspaceagents.go index 5a8adab6132c5..6b25fcbcfeaf6 100644 --- a/coderd/workspaceagents.go +++ b/coderd/workspaceagents.go @@ -956,7 +956,7 @@ func (api *API) workspaceAgentRecreateDevcontainer(rw http.ResponseWriter, r *ht } defer release() - err = agentConn.RecreateDevcontainer(ctx, container) + m, err := agentConn.RecreateDevcontainer(ctx, container) if err != nil { if errors.Is(err, context.Canceled) { httpapi.Write(ctx, rw, http.StatusRequestTimeout, codersdk.Response{ @@ -977,7 +977,7 @@ func (api *API) workspaceAgentRecreateDevcontainer(rw http.ResponseWriter, r *ht return } - httpapi.Write(ctx, rw, http.StatusNoContent, nil) + httpapi.Write(ctx, rw, http.StatusAccepted, m) } // @Summary Get connection info for workspace agent diff --git a/coderd/workspaceagents_test.go b/coderd/workspaceagents_test.go index 1d17560c38816..5635296d1a47b 100644 --- a/coderd/workspaceagents_test.go +++ b/coderd/workspaceagents_test.go @@ -1483,7 +1483,7 @@ func TestWorkspaceAgentRecreateDevcontainer(t *testing.T) { ctx := testutil.Context(t, testutil.WaitLong) - err := client.WorkspaceAgentRecreateDevcontainer(ctx, agentID, devContainer.ID) + _, err := client.WorkspaceAgentRecreateDevcontainer(ctx, agentID, devContainer.ID) if wantStatus > 0 { cerr, ok := codersdk.AsError(err) require.True(t, ok, "expected error to be a coder error") diff --git a/coderd/workspacebuilds.go b/coderd/workspacebuilds.go index 08b90b834ccca..1fd0c95ff3a77 100644 --- a/coderd/workspacebuilds.go +++ b/coderd/workspacebuilds.go @@ -1095,7 +1095,8 @@ func (api *API) convertWorkspaceBuild( CreatedAt: build.CreatedAt, UpdatedAt: build.UpdatedAt, WorkspaceOwnerID: workspace.OwnerID, - WorkspaceOwnerName: workspace.OwnerUsername, + WorkspaceOwnerName: workspace.OwnerName, + WorkspaceOwnerUsername: workspace.OwnerUsername, WorkspaceOwnerAvatarURL: workspace.OwnerAvatarUrl, WorkspaceID: build.WorkspaceID, WorkspaceName: workspace.Name, diff --git a/coderd/workspacebuilds_test.go b/coderd/workspacebuilds_test.go index 08a8f3f26e0fa..d9a6baee7b68a 100644 --- a/coderd/workspacebuilds_test.go +++ b/coderd/workspacebuilds_test.go @@ -78,7 +78,8 @@ func TestWorkspaceBuild(t *testing.T) { }, testutil.WaitShort, testutil.IntervalFast) wb, err := client.WorkspaceBuild(testutil.Context(t, testutil.WaitShort), workspace.LatestBuild.ID) require.NoError(t, err) - require.Equal(t, up.Username, wb.WorkspaceOwnerName) + require.Equal(t, up.Username, wb.WorkspaceOwnerUsername) + require.Equal(t, up.Name, wb.WorkspaceOwnerName) require.Equal(t, up.AvatarURL, wb.WorkspaceOwnerAvatarURL) } diff --git a/codersdk/deployment.go b/codersdk/deployment.go index 89834f163affd..696e6bda52682 100644 --- a/codersdk/deployment.go +++ b/codersdk/deployment.go @@ -3346,6 +3346,7 @@ const ( ExperimentDynamicParameters Experiment = "dynamic-parameters" // Enables dynamic parameters when creating a workspace. ExperimentWorkspacePrebuilds Experiment = "workspace-prebuilds" // Enables the new workspace prebuilds feature. ExperimentAgenticChat Experiment = "agentic-chat" // Enables the new agentic AI chat feature. + ExperimentAITasks Experiment = "ai-tasks" // Enables the new AI tasks feature. ) // ExperimentsSafe should include all experiments that are safe for diff --git a/codersdk/users.go b/codersdk/users.go index 3207e3fbabaa1..f65223a666a62 100644 --- a/codersdk/users.go +++ b/codersdk/users.go @@ -663,7 +663,14 @@ func (c *Client) ChangePasswordWithOneTimePasscode(ctx context.Context, req Chan // based authentication to oauth based. The response has the oauth state code // to use in the oauth flow. func (c *Client) ConvertLoginType(ctx context.Context, req ConvertLoginRequest) (OAuthConversionResponse, error) { - res, err := c.Request(ctx, http.MethodPost, "/api/v2/users/me/convert-login", req) + return c.ConvertUserLoginType(ctx, Me, req) +} + +// ConvertUserLoginType will send a request to convert the user from password +// based authentication to oauth based. The response has the oauth state code +// to use in the oauth flow. +func (c *Client) ConvertUserLoginType(ctx context.Context, user string, req ConvertLoginRequest) (OAuthConversionResponse, error) { + res, err := c.Request(ctx, http.MethodPost, fmt.Sprintf("/api/v2/users/%s/convert-login", user), req) if err != nil { return OAuthConversionResponse{}, err } diff --git a/codersdk/workspaceagents.go b/codersdk/workspaceagents.go index 0c5aaddf913da..6a4380fed47ac 100644 --- a/codersdk/workspaceagents.go +++ b/codersdk/workspaceagents.go @@ -450,6 +450,10 @@ type WorkspaceAgentContainer struct { // Volumes is a map of "things" mounted into the container. Again, this // is somewhat implementation-dependent. Volumes map[string]string `json:"volumes"` + // DevcontainerStatus is the status of the devcontainer, if this + // container is a devcontainer. This is used to determine if the + // devcontainer is running, stopped, starting, or in an error state. + DevcontainerStatus WorkspaceAgentDevcontainerStatus `json:"devcontainer_status,omitempty"` // DevcontainerDirty is true if the devcontainer configuration has changed // since the container was created. This is used to determine if the // container needs to be rebuilt. @@ -518,16 +522,20 @@ func (c *Client) WorkspaceAgentListContainers(ctx context.Context, agentID uuid. } // WorkspaceAgentRecreateDevcontainer recreates the devcontainer with the given ID. -func (c *Client) WorkspaceAgentRecreateDevcontainer(ctx context.Context, agentID uuid.UUID, containerIDOrName string) error { +func (c *Client) WorkspaceAgentRecreateDevcontainer(ctx context.Context, agentID uuid.UUID, containerIDOrName string) (Response, error) { res, err := c.Request(ctx, http.MethodPost, fmt.Sprintf("/api/v2/workspaceagents/%s/containers/devcontainers/container/%s/recreate", agentID, containerIDOrName), nil) if err != nil { - return err + return Response{}, err } defer res.Body.Close() - if res.StatusCode != http.StatusNoContent { - return ReadBodyAsError(res) + if res.StatusCode != http.StatusAccepted { + return Response{}, ReadBodyAsError(res) } - return nil + var m Response + if err := json.NewDecoder(res.Body).Decode(&m); err != nil { + return Response{}, xerrors.Errorf("decode response body: %w", err) + } + return m, nil } //nolint:revive // Follow is a control flag on the server as well. diff --git a/codersdk/workspacebuilds.go b/codersdk/workspacebuilds.go index ee31876f44fab..dd7af027ae701 100644 --- a/codersdk/workspacebuilds.go +++ b/codersdk/workspacebuilds.go @@ -57,7 +57,8 @@ type WorkspaceBuild struct { WorkspaceID uuid.UUID `json:"workspace_id" format:"uuid"` WorkspaceName string `json:"workspace_name"` WorkspaceOwnerID uuid.UUID `json:"workspace_owner_id" format:"uuid"` - WorkspaceOwnerName string `json:"workspace_owner_name"` + WorkspaceOwnerName string `json:"workspace_owner_name,omitempty"` + WorkspaceOwnerUsername string `json:"workspace_owner_username"` WorkspaceOwnerAvatarURL string `json:"workspace_owner_avatar_url,omitempty"` TemplateVersionID uuid.UUID `json:"template_version_id" format:"uuid"` TemplateVersionName string `json:"template_version_name"` diff --git a/codersdk/workspacesdk/agentconn.go b/codersdk/workspacesdk/agentconn.go index c9e9824e2950f..3477ec98328ac 100644 --- a/codersdk/workspacesdk/agentconn.go +++ b/codersdk/workspacesdk/agentconn.go @@ -389,18 +389,22 @@ func (c *AgentConn) ListContainers(ctx context.Context) (codersdk.WorkspaceAgent // RecreateDevcontainer recreates a devcontainer with the given container. // This is a blocking call and will wait for the container to be recreated. -func (c *AgentConn) RecreateDevcontainer(ctx context.Context, containerIDOrName string) error { +func (c *AgentConn) RecreateDevcontainer(ctx context.Context, containerIDOrName string) (codersdk.Response, error) { ctx, span := tracing.StartSpan(ctx) defer span.End() res, err := c.apiRequest(ctx, http.MethodPost, "/api/v0/containers/devcontainers/container/"+containerIDOrName+"/recreate", nil) if err != nil { - return xerrors.Errorf("do request: %w", err) + return codersdk.Response{}, xerrors.Errorf("do request: %w", err) } defer res.Body.Close() if res.StatusCode != http.StatusAccepted { - return codersdk.ReadBodyAsError(res) + return codersdk.Response{}, codersdk.ReadBodyAsError(res) } - return nil + var m codersdk.Response + if err := json.NewDecoder(res.Body).Decode(&m); err != nil { + return codersdk.Response{}, xerrors.Errorf("decode response body: %w", err) + } + return m, nil } // apiRequest makes a request to the workspace agent's HTTP API server. diff --git a/docs/admin/security/audit-logs.md b/docs/admin/security/audit-logs.md index d0b2a46a9d002..c754c532cb841 100644 --- a/docs/admin/security/audit-logs.md +++ b/docs/admin/security/audit-logs.md @@ -8,32 +8,32 @@ We track the following resources: -| Resource | | | -|----------------------------------------------------------|----------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| APIKey
login, logout, register, create, delete | |
FieldTracked
created_attrue
expires_attrue
hashed_secretfalse
idfalse
ip_addressfalse
last_usedtrue
lifetime_secondsfalse
login_typefalse
scopefalse
token_namefalse
updated_atfalse
user_idtrue
| -| AuditOAuthConvertState
| |
FieldTracked
created_attrue
expires_attrue
from_login_typetrue
to_login_typetrue
user_idtrue
| -| Group
create, write, delete | |
FieldTracked
avatar_urltrue
display_nametrue
idtrue
memberstrue
nametrue
organization_idfalse
quota_allowancetrue
sourcefalse
| -| AuditableOrganizationMember
| |
FieldTracked
created_attrue
organization_idfalse
rolestrue
updated_attrue
user_idtrue
usernametrue
| -| CustomRole
| |
FieldTracked
created_atfalse
display_nametrue
idfalse
nametrue
org_permissionstrue
organization_idfalse
site_permissionstrue
updated_atfalse
user_permissionstrue
| -| GitSSHKey
create | |
FieldTracked
created_atfalse
private_keytrue
public_keytrue
updated_atfalse
user_idtrue
| -| GroupSyncSettings
| |
FieldTracked
auto_create_missing_groupstrue
fieldtrue
legacy_group_name_mappingfalse
mappingtrue
regex_filtertrue
| -| HealthSettings
| |
FieldTracked
dismissed_healthcheckstrue
idfalse
| -| License
create, delete | |
FieldTracked
exptrue
idfalse
jwtfalse
uploaded_attrue
uuidtrue
| -| NotificationTemplate
| |
FieldTracked
actionstrue
body_templatetrue
enabled_by_defaulttrue
grouptrue
idfalse
kindtrue
methodtrue
nametrue
title_templatetrue
| -| NotificationsSettings
| |
FieldTracked
idfalse
notifier_pausedtrue
| -| OAuth2ProviderApp
| |
FieldTracked
callback_urltrue
created_atfalse
icontrue
idfalse
nametrue
updated_atfalse
| -| OAuth2ProviderAppSecret
| |
FieldTracked
app_idfalse
created_atfalse
display_secretfalse
hashed_secretfalse
idfalse
last_used_atfalse
secret_prefixfalse
| -| Organization
| |
FieldTracked
created_atfalse
deletedtrue
descriptiontrue
display_nametrue
icontrue
idfalse
is_defaulttrue
nametrue
updated_attrue
| -| OrganizationSyncSettings
| |
FieldTracked
assign_defaulttrue
fieldtrue
mappingtrue
| -| RoleSyncSettings
| |
FieldTracked
fieldtrue
mappingtrue
| -| Template
write, delete | |
FieldTracked
active_version_idtrue
activity_bumptrue
allow_user_autostarttrue
allow_user_autostoptrue
allow_user_cancel_workspace_jobstrue
autostart_block_days_of_weektrue
autostop_requirement_days_of_weektrue
autostop_requirement_weekstrue
created_atfalse
created_bytrue
created_by_avatar_urlfalse
created_by_usernamefalse
default_ttltrue
deletedfalse
deprecatedtrue
descriptiontrue
display_nametrue
failure_ttltrue
group_acltrue
icontrue
idtrue
max_port_sharing_leveltrue
nametrue
organization_display_namefalse
organization_iconfalse
organization_idfalse
organization_namefalse
provisionertrue
require_active_versiontrue
time_til_dormanttrue
time_til_dormant_autodeletetrue
updated_atfalse
use_classic_parameter_flowtrue
user_acltrue
| -| TemplateVersion
create, write | |
FieldTracked
archivedtrue
created_atfalse
created_bytrue
created_by_avatar_urlfalse
created_by_usernamefalse
external_auth_providersfalse
idtrue
job_idfalse
messagefalse
nametrue
organization_idfalse
readmetrue
source_example_idfalse
template_idtrue
updated_atfalse
| -| User
create, write, delete | |
FieldTracked
avatar_urlfalse
created_atfalse
deletedtrue
emailtrue
github_com_user_idfalse
hashed_one_time_passcodefalse
hashed_passwordtrue
idtrue
is_systemtrue
last_seen_atfalse
login_typetrue
nametrue
one_time_passcode_expires_attrue
quiet_hours_scheduletrue
rbac_rolestrue
statustrue
updated_atfalse
usernametrue
| -| WorkspaceAgent
connect, disconnect | |
FieldTracked
api_key_scopefalse
api_versionfalse
architecturefalse
auth_instance_idfalse
auth_tokenfalse
connection_timeout_secondsfalse
created_atfalse
directoryfalse
disconnected_atfalse
display_appsfalse
display_orderfalse
environment_variablesfalse
expanded_directoryfalse
first_connected_atfalse
idfalse
instance_metadatafalse
last_connected_atfalse
last_connected_replica_idfalse
lifecycle_statefalse
logs_lengthfalse
logs_overflowedfalse
motd_filefalse
namefalse
operating_systemfalse
parent_idfalse
ready_atfalse
resource_idfalse
resource_metadatafalse
started_atfalse
subsystemsfalse
troubleshooting_urlfalse
updated_atfalse
versionfalse
| -| WorkspaceApp
open, close | |
FieldTracked
agent_idfalse
commandfalse
created_atfalse
display_namefalse
display_orderfalse
externalfalse
healthfalse
healthcheck_intervalfalse
healthcheck_thresholdfalse
healthcheck_urlfalse
hiddenfalse
iconfalse
idfalse
open_infalse
sharing_levelfalse
slugfalse
subdomainfalse
urlfalse
| -| WorkspaceBuild
start, stop | |
FieldTracked
build_numberfalse
created_atfalse
daily_costfalse
deadlinefalse
idfalse
initiator_by_avatar_urlfalse
initiator_by_usernamefalse
initiator_idfalse
job_idfalse
max_deadlinefalse
provisioner_statefalse
reasonfalse
template_version_idtrue
template_version_preset_idfalse
transitionfalse
updated_atfalse
workspace_idfalse
| -| WorkspaceProxy
| |
FieldTracked
created_attrue
deletedfalse
derp_enabledtrue
derp_onlytrue
display_nametrue
icontrue
idtrue
nametrue
region_idtrue
token_hashed_secrettrue
updated_atfalse
urltrue
versiontrue
wildcard_hostnametrue
| -| WorkspaceTable
| |
FieldTracked
automatic_updatestrue
autostart_scheduletrue
created_atfalse
deletedfalse
deleting_attrue
dormant_attrue
favoritetrue
idtrue
last_used_atfalse
nametrue
next_start_attrue
organization_idfalse
owner_idtrue
template_idtrue
ttltrue
updated_atfalse
| +| Resource | | | +|----------------------------------------------------------|----------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| APIKey
login, logout, register, create, delete | |
FieldTracked
created_attrue
expires_attrue
hashed_secretfalse
idfalse
ip_addressfalse
last_usedtrue
lifetime_secondsfalse
login_typefalse
scopefalse
token_namefalse
updated_atfalse
user_idtrue
| +| AuditOAuthConvertState
| |
FieldTracked
created_attrue
expires_attrue
from_login_typetrue
to_login_typetrue
user_idtrue
| +| Group
create, write, delete | |
FieldTracked
avatar_urltrue
display_nametrue
idtrue
memberstrue
nametrue
organization_idfalse
quota_allowancetrue
sourcefalse
| +| AuditableOrganizationMember
| |
FieldTracked
created_attrue
organization_idfalse
rolestrue
updated_attrue
user_idtrue
usernametrue
| +| CustomRole
| |
FieldTracked
created_atfalse
display_nametrue
idfalse
nametrue
org_permissionstrue
organization_idfalse
site_permissionstrue
updated_atfalse
user_permissionstrue
| +| GitSSHKey
create | |
FieldTracked
created_atfalse
private_keytrue
public_keytrue
updated_atfalse
user_idtrue
| +| GroupSyncSettings
| |
FieldTracked
auto_create_missing_groupstrue
fieldtrue
legacy_group_name_mappingfalse
mappingtrue
regex_filtertrue
| +| HealthSettings
| |
FieldTracked
dismissed_healthcheckstrue
idfalse
| +| License
create, delete | |
FieldTracked
exptrue
idfalse
jwtfalse
uploaded_attrue
uuidtrue
| +| NotificationTemplate
| |
FieldTracked
actionstrue
body_templatetrue
enabled_by_defaulttrue
grouptrue
idfalse
kindtrue
methodtrue
nametrue
title_templatetrue
| +| NotificationsSettings
| |
FieldTracked
idfalse
notifier_pausedtrue
| +| OAuth2ProviderApp
| |
FieldTracked
callback_urltrue
created_atfalse
icontrue
idfalse
nametrue
updated_atfalse
| +| OAuth2ProviderAppSecret
| |
FieldTracked
app_idfalse
created_atfalse
display_secretfalse
hashed_secretfalse
idfalse
last_used_atfalse
secret_prefixfalse
| +| Organization
| |
FieldTracked
created_atfalse
deletedtrue
descriptiontrue
display_nametrue
icontrue
idfalse
is_defaulttrue
nametrue
updated_attrue
| +| OrganizationSyncSettings
| |
FieldTracked
assign_defaulttrue
fieldtrue
mappingtrue
| +| RoleSyncSettings
| |
FieldTracked
fieldtrue
mappingtrue
| +| Template
write, delete | |
FieldTracked
active_version_idtrue
activity_bumptrue
allow_user_autostarttrue
allow_user_autostoptrue
allow_user_cancel_workspace_jobstrue
autostart_block_days_of_weektrue
autostop_requirement_days_of_weektrue
autostop_requirement_weekstrue
created_atfalse
created_bytrue
created_by_avatar_urlfalse
created_by_namefalse
created_by_usernamefalse
default_ttltrue
deletedfalse
deprecatedtrue
descriptiontrue
display_nametrue
failure_ttltrue
group_acltrue
icontrue
idtrue
max_port_sharing_leveltrue
nametrue
organization_display_namefalse
organization_iconfalse
organization_idfalse
organization_namefalse
provisionertrue
require_active_versiontrue
time_til_dormanttrue
time_til_dormant_autodeletetrue
updated_atfalse
use_classic_parameter_flowtrue
user_acltrue
| +| TemplateVersion
create, write | |
FieldTracked
archivedtrue
created_atfalse
created_bytrue
created_by_avatar_urlfalse
created_by_namefalse
created_by_usernamefalse
external_auth_providersfalse
idtrue
job_idfalse
messagefalse
nametrue
organization_idfalse
readmetrue
source_example_idfalse
template_idtrue
updated_atfalse
| +| User
create, write, delete | |
FieldTracked
avatar_urlfalse
created_atfalse
deletedtrue
emailtrue
github_com_user_idfalse
hashed_one_time_passcodefalse
hashed_passwordtrue
idtrue
is_systemtrue
last_seen_atfalse
login_typetrue
nametrue
one_time_passcode_expires_attrue
quiet_hours_scheduletrue
rbac_rolestrue
statustrue
updated_atfalse
usernametrue
| +| WorkspaceAgent
connect, disconnect | |
FieldTracked
api_key_scopefalse
api_versionfalse
architecturefalse
auth_instance_idfalse
auth_tokenfalse
connection_timeout_secondsfalse
created_atfalse
directoryfalse
disconnected_atfalse
display_appsfalse
display_orderfalse
environment_variablesfalse
expanded_directoryfalse
first_connected_atfalse
idfalse
instance_metadatafalse
last_connected_atfalse
last_connected_replica_idfalse
lifecycle_statefalse
logs_lengthfalse
logs_overflowedfalse
motd_filefalse
namefalse
operating_systemfalse
parent_idfalse
ready_atfalse
resource_idfalse
resource_metadatafalse
started_atfalse
subsystemsfalse
troubleshooting_urlfalse
updated_atfalse
versionfalse
| +| WorkspaceApp
open, close | |
FieldTracked
agent_idfalse
commandfalse
created_atfalse
display_namefalse
display_orderfalse
externalfalse
healthfalse
healthcheck_intervalfalse
healthcheck_thresholdfalse
healthcheck_urlfalse
hiddenfalse
iconfalse
idfalse
open_infalse
sharing_levelfalse
slugfalse
subdomainfalse
urlfalse
| +| WorkspaceBuild
start, stop | |
FieldTracked
build_numberfalse
created_atfalse
daily_costfalse
deadlinefalse
idfalse
initiator_by_avatar_urlfalse
initiator_by_namefalse
initiator_by_usernamefalse
initiator_idfalse
job_idfalse
max_deadlinefalse
provisioner_statefalse
reasonfalse
template_version_idtrue
template_version_preset_idfalse
transitionfalse
updated_atfalse
workspace_idfalse
| +| WorkspaceProxy
| |
FieldTracked
created_attrue
deletedfalse
derp_enabledtrue
derp_onlytrue
display_nametrue
icontrue
idtrue
nametrue
region_idtrue
token_hashed_secrettrue
updated_atfalse
urltrue
versiontrue
wildcard_hostnametrue
| +| WorkspaceTable
| |
FieldTracked
automatic_updatestrue
autostart_scheduletrue
created_atfalse
deletedfalse
deleting_attrue
dormant_attrue
favoritetrue
idtrue
last_used_atfalse
nametrue
next_start_attrue
organization_idfalse
owner_idtrue
template_idtrue
ttltrue
updated_atfalse
| diff --git a/docs/admin/templates/extending-templates/parameters.md b/docs/admin/templates/extending-templates/parameters.md index 9c1235d51a915..f3640007634e0 100644 --- a/docs/admin/templates/extending-templates/parameters.md +++ b/docs/admin/templates/extending-templates/parameters.md @@ -392,13 +392,13 @@ parameters in one of two ways: Or set the [environment variable](../../setup/index.md), `CODER_EXPERIMENTS=auto-fill-parameters` -## Dynamic Parameters +## Dynamic Parameters (Early Access) Dynamic Parameters enhances Coder's existing parameter system with real-time validation, conditional parameter behavior, and richer input types. This feature allows template authors to create more interactive and responsive workspace creation experiences. -### Enable Dynamic Parameters (Early Access) +### Enable Dynamic Parameters To use Dynamic Parameters, enable the experiment flag or set the environment variable. @@ -488,7 +488,7 @@ The "Options" column in the table below indicates whether the form type requires ### Form Type Examples -
`checkbox`: A single checkbox for boolean values +
checkbox: A single checkbox for boolean values ```tf data "coder_parameter" "enable_gpu" { @@ -502,7 +502,7 @@ data "coder_parameter" "enable_gpu" {
-
`dropdown`: A searchable select menu for choosing a single option from a list +
dropdown: A searchable select menu for choosing a single option from a list ```tf data "coder_parameter" "region" { @@ -525,7 +525,7 @@ data "coder_parameter" "region" {
-
`input`: A standard text input field +
input: A standard text input field ```tf data "coder_parameter" "custom_domain" { @@ -539,7 +539,7 @@ data "coder_parameter" "custom_domain" {
-
`key-value`: Input for entering key-value pairs +
key-value: Input for entering key-value pairs ```tf data "coder_parameter" "environment_vars" { @@ -553,7 +553,7 @@ data "coder_parameter" "environment_vars" {
-
`multi-select`: Checkboxes for selecting multiple options from a list +
multi-select: Checkboxes for selecting multiple options from a list ```tf data "coder_parameter" "tools" { @@ -580,7 +580,7 @@ data "coder_parameter" "tools" {
-
`password`: A text input that masks sensitive information +
password: A text input that masks sensitive information ```tf data "coder_parameter" "api_key" { @@ -594,7 +594,7 @@ data "coder_parameter" "api_key" {
-
`radio`: Radio buttons for selecting a single option with high visibility +
radio: Radio buttons for selecting a single option with high visibility ```tf data "coder_parameter" "environment" { @@ -617,7 +617,7 @@ data "coder_parameter" "environment" {
-
`slider`: A slider for selecting numeric values within a range +
slider: A slider for selecting numeric values within a range ```tf data "coder_parameter" "cpu_cores" { @@ -635,7 +635,7 @@ data "coder_parameter" "cpu_cores" {
-
`switch`: A toggle switch for boolean values +
switch: A toggle switch for boolean values ```tf data "coder_parameter" "advanced_mode" { @@ -649,7 +649,7 @@ data "coder_parameter" "advanced_mode" {
-
`textarea`: A multi-line text input field for longer content +
textarea: A multi-line text input field for longer content ```tf data "coder_parameter" "init_script" { diff --git a/docs/reference/api/agents.md b/docs/reference/api/agents.md index fd0cd38d355e0..d0169416239d7 100644 --- a/docs/reference/api/agents.md +++ b/docs/reference/api/agents.md @@ -777,6 +777,7 @@ curl -X GET http://coder-server:8080/api/v2/workspaceagents/{workspaceagent}/con { "created_at": "2019-08-24T14:15:22Z", "devcontainer_dirty": true, + "devcontainer_status": "running", "id": "string", "image": "string", "labels": { diff --git a/docs/reference/api/builds.md b/docs/reference/api/builds.md index 3cfd25f2a6e0f..a22256d340f1a 100644 --- a/docs/reference/api/builds.md +++ b/docs/reference/api/builds.md @@ -224,7 +224,8 @@ curl -X GET http://coder-server:8080/api/v2/users/{user}/workspace/{workspacenam "workspace_name": "string", "workspace_owner_avatar_url": "string", "workspace_owner_id": "e7078695-5279-4c86-8774-3ac2367a2fc7", - "workspace_owner_name": "string" + "workspace_owner_name": "string", + "workspace_owner_username": "string" } ``` @@ -458,7 +459,8 @@ curl -X GET http://coder-server:8080/api/v2/workspacebuilds/{workspacebuild} \ "workspace_name": "string", "workspace_owner_avatar_url": "string", "workspace_owner_id": "e7078695-5279-4c86-8774-3ac2367a2fc7", - "workspace_owner_name": "string" + "workspace_owner_name": "string", + "workspace_owner_username": "string" } ``` @@ -1169,7 +1171,8 @@ curl -X GET http://coder-server:8080/api/v2/workspacebuilds/{workspacebuild}/sta "workspace_name": "string", "workspace_owner_avatar_url": "string", "workspace_owner_id": "e7078695-5279-4c86-8774-3ac2367a2fc7", - "workspace_owner_name": "string" + "workspace_owner_name": "string", + "workspace_owner_username": "string" } ``` @@ -1476,7 +1479,8 @@ curl -X GET http://coder-server:8080/api/v2/workspaces/{workspace}/builds \ "workspace_name": "string", "workspace_owner_avatar_url": "string", "workspace_owner_id": "e7078695-5279-4c86-8774-3ac2367a2fc7", - "workspace_owner_name": "string" + "workspace_owner_name": "string", + "workspace_owner_username": "string" } ] ``` @@ -1648,6 +1652,7 @@ Status Code **200** | `» workspace_owner_avatar_url` | string | false | | | | `» workspace_owner_id` | string(uuid) | false | | | | `» workspace_owner_name` | string | false | | | +| `» workspace_owner_username` | string | false | | | #### Enumerated Values @@ -1959,7 +1964,8 @@ curl -X POST http://coder-server:8080/api/v2/workspaces/{workspace}/builds \ "workspace_name": "string", "workspace_owner_avatar_url": "string", "workspace_owner_id": "e7078695-5279-4c86-8774-3ac2367a2fc7", - "workspace_owner_name": "string" + "workspace_owner_name": "string", + "workspace_owner_username": "string" } ``` diff --git a/docs/reference/api/schemas.md b/docs/reference/api/schemas.md index 2374c6af8800f..7aaf9193acaf9 100644 --- a/docs/reference/api/schemas.md +++ b/docs/reference/api/schemas.md @@ -3379,6 +3379,7 @@ CreateWorkspaceRequest provides options for creating a new workspace. Only one o | `dynamic-parameters` | | `workspace-prebuilds` | | `agentic-chat` | +| `ai-tasks` | ## codersdk.ExternalAuth @@ -8407,7 +8408,8 @@ If the schedule is empty, the user will be updated to use the default schedule.| "workspace_name": "string", "workspace_owner_avatar_url": "string", "workspace_owner_id": "e7078695-5279-4c86-8774-3ac2367a2fc7", - "workspace_owner_name": "string" + "workspace_owner_name": "string", + "workspace_owner_username": "string" }, "name": "string", "next_start_at": "2019-08-24T14:15:22Z", @@ -8636,6 +8638,7 @@ If the schedule is empty, the user will be updated to use the default schedule.| { "created_at": "2019-08-24T14:15:22Z", "devcontainer_dirty": true, + "devcontainer_status": "running", "id": "string", "image": "string", "labels": { @@ -8662,20 +8665,21 @@ If the schedule is empty, the user will be updated to use the default schedule.| ### Properties -| Name | Type | Required | Restrictions | Description | -|----------------------|---------------------------------------------------------------------------------------|----------|--------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `created_at` | string | false | | Created at is the time the container was created. | -| `devcontainer_dirty` | boolean | false | | Devcontainer dirty is true if the devcontainer configuration has changed since the container was created. This is used to determine if the container needs to be rebuilt. | -| `id` | string | false | | ID is the unique identifier of the container. | -| `image` | string | false | | Image is the name of the container image. | -| `labels` | object | false | | Labels is a map of key-value pairs of container labels. | -| » `[any property]` | string | false | | | -| `name` | string | false | | Name is the human-readable name of the container. | -| `ports` | array of [codersdk.WorkspaceAgentContainerPort](#codersdkworkspaceagentcontainerport) | false | | Ports includes ports exposed by the container. | -| `running` | boolean | false | | Running is true if the container is currently running. | -| `status` | string | false | | Status is the current status of the container. This is somewhat implementation-dependent, but should generally be a human-readable string. | -| `volumes` | object | false | | Volumes is a map of "things" mounted into the container. Again, this is somewhat implementation-dependent. | -| » `[any property]` | string | false | | | +| Name | Type | Required | Restrictions | Description | +|-----------------------|----------------------------------------------------------------------------------------|----------|--------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `created_at` | string | false | | Created at is the time the container was created. | +| `devcontainer_dirty` | boolean | false | | Devcontainer dirty is true if the devcontainer configuration has changed since the container was created. This is used to determine if the container needs to be rebuilt. | +| `devcontainer_status` | [codersdk.WorkspaceAgentDevcontainerStatus](#codersdkworkspaceagentdevcontainerstatus) | false | | Devcontainer status is the status of the devcontainer, if this container is a devcontainer. This is used to determine if the devcontainer is running, stopped, starting, or in an error state. | +| `id` | string | false | | ID is the unique identifier of the container. | +| `image` | string | false | | Image is the name of the container image. | +| `labels` | object | false | | Labels is a map of key-value pairs of container labels. | +| » `[any property]` | string | false | | | +| `name` | string | false | | Name is the human-readable name of the container. | +| `ports` | array of [codersdk.WorkspaceAgentContainerPort](#codersdkworkspaceagentcontainerport) | false | | Ports includes ports exposed by the container. | +| `running` | boolean | false | | Running is true if the container is currently running. | +| `status` | string | false | | Status is the current status of the container. This is somewhat implementation-dependent, but should generally be a human-readable string. | +| `volumes` | object | false | | Volumes is a map of "things" mounted into the container. Again, this is somewhat implementation-dependent. | +| » `[any property]` | string | false | | | ## codersdk.WorkspaceAgentContainerPort @@ -8697,6 +8701,23 @@ If the schedule is empty, the user will be updated to use the default schedule.| | `network` | string | false | | Network is the network protocol used by the port (tcp, udp, etc). | | `port` | integer | false | | Port is the port number *inside* the container. | +## codersdk.WorkspaceAgentDevcontainerStatus + +```json +"running" +``` + +### Properties + +#### Enumerated Values + +| Value | +|------------| +| `running` | +| `stopped` | +| `starting` | +| `error` | + ## codersdk.WorkspaceAgentHealth ```json @@ -8743,6 +8764,7 @@ If the schedule is empty, the user will be updated to use the default schedule.| { "created_at": "2019-08-24T14:15:22Z", "devcontainer_dirty": true, + "devcontainer_status": "running", "id": "string", "image": "string", "labels": { @@ -9374,7 +9396,8 @@ If the schedule is empty, the user will be updated to use the default schedule.| "workspace_name": "string", "workspace_owner_avatar_url": "string", "workspace_owner_id": "e7078695-5279-4c86-8774-3ac2367a2fc7", - "workspace_owner_name": "string" + "workspace_owner_name": "string", + "workspace_owner_username": "string" } ``` @@ -9405,6 +9428,7 @@ If the schedule is empty, the user will be updated to use the default schedule.| | `workspace_owner_avatar_url` | string | false | | | | `workspace_owner_id` | string | false | | | | `workspace_owner_name` | string | false | | | +| `workspace_owner_username` | string | false | | | #### Enumerated Values @@ -10081,7 +10105,8 @@ If the schedule is empty, the user will be updated to use the default schedule.| "workspace_name": "string", "workspace_owner_avatar_url": "string", "workspace_owner_id": "e7078695-5279-4c86-8774-3ac2367a2fc7", - "workspace_owner_name": "string" + "workspace_owner_name": "string", + "workspace_owner_username": "string" }, "name": "string", "next_start_at": "2019-08-24T14:15:22Z", diff --git a/docs/reference/api/workspaces.md b/docs/reference/api/workspaces.md index 241d80ac05f7d..0a0176ee39501 100644 --- a/docs/reference/api/workspaces.md +++ b/docs/reference/api/workspaces.md @@ -279,7 +279,8 @@ of the template will be used. "workspace_name": "string", "workspace_owner_avatar_url": "string", "workspace_owner_id": "e7078695-5279-4c86-8774-3ac2367a2fc7", - "workspace_owner_name": "string" + "workspace_owner_name": "string", + "workspace_owner_username": "string" }, "name": "string", "next_start_at": "2019-08-24T14:15:22Z", @@ -562,7 +563,8 @@ curl -X GET http://coder-server:8080/api/v2/users/{user}/workspace/{workspacenam "workspace_name": "string", "workspace_owner_avatar_url": "string", "workspace_owner_id": "e7078695-5279-4c86-8774-3ac2367a2fc7", - "workspace_owner_name": "string" + "workspace_owner_name": "string", + "workspace_owner_username": "string" }, "name": "string", "next_start_at": "2019-08-24T14:15:22Z", @@ -871,7 +873,8 @@ of the template will be used. "workspace_name": "string", "workspace_owner_avatar_url": "string", "workspace_owner_id": "e7078695-5279-4c86-8774-3ac2367a2fc7", - "workspace_owner_name": "string" + "workspace_owner_name": "string", + "workspace_owner_username": "string" }, "name": "string", "next_start_at": "2019-08-24T14:15:22Z", @@ -1140,7 +1143,8 @@ curl -X GET http://coder-server:8080/api/v2/workspaces \ "workspace_name": "string", "workspace_owner_avatar_url": "string", "workspace_owner_id": "e7078695-5279-4c86-8774-3ac2367a2fc7", - "workspace_owner_name": "string" + "workspace_owner_name": "string", + "workspace_owner_username": "string" }, "name": "string", "next_start_at": "2019-08-24T14:15:22Z", @@ -1424,7 +1428,8 @@ curl -X GET http://coder-server:8080/api/v2/workspaces/{workspace} \ "workspace_name": "string", "workspace_owner_avatar_url": "string", "workspace_owner_id": "e7078695-5279-4c86-8774-3ac2367a2fc7", - "workspace_owner_name": "string" + "workspace_owner_name": "string", + "workspace_owner_username": "string" }, "name": "string", "next_start_at": "2019-08-24T14:15:22Z", @@ -1823,7 +1828,8 @@ curl -X PUT http://coder-server:8080/api/v2/workspaces/{workspace}/dormant \ "workspace_name": "string", "workspace_owner_avatar_url": "string", "workspace_owner_id": "e7078695-5279-4c86-8774-3ac2367a2fc7", - "workspace_owner_name": "string" + "workspace_owner_name": "string", + "workspace_owner_username": "string" }, "name": "string", "next_start_at": "2019-08-24T14:15:22Z", diff --git a/enterprise/audit/table.go b/enterprise/audit/table.go index 3c836c9442043..af81a68791c11 100644 --- a/enterprise/audit/table.go +++ b/enterprise/audit/table.go @@ -102,6 +102,7 @@ var auditableResourcesTypes = map[any]map[string]Action{ "autostop_requirement_weeks": ActionTrack, "created_by": ActionTrack, "created_by_username": ActionIgnore, + "created_by_name": ActionIgnore, "created_by_avatar_url": ActionIgnore, "group_acl": ActionTrack, "user_acl": ActionTrack, @@ -131,6 +132,7 @@ var auditableResourcesTypes = map[any]map[string]Action{ "external_auth_providers": ActionIgnore, // Not helpful because this can only change when new versions are added. "created_by_avatar_url": ActionIgnore, "created_by_username": ActionIgnore, + "created_by_name": ActionIgnore, "archived": ActionTrack, "source_example_id": ActionIgnore, // Never changes. }, @@ -189,6 +191,7 @@ var auditableResourcesTypes = map[any]map[string]Action{ "max_deadline": ActionIgnore, "initiator_by_avatar_url": ActionIgnore, "initiator_by_username": ActionIgnore, + "initiator_by_name": ActionIgnore, "template_version_preset_id": ActionIgnore, // Never changes. }, &database.AuditableGroup{}: { diff --git a/enterprise/coderd/prebuilds/reconcile.go b/enterprise/coderd/prebuilds/reconcile.go index c07c15de2aeb8..90c97afa26d69 100644 --- a/enterprise/coderd/prebuilds/reconcile.go +++ b/enterprise/coderd/prebuilds/reconcile.go @@ -361,15 +361,23 @@ func (c *StoreReconciler) ReconcilePreset(ctx context.Context, ps prebuilds.Pres slog.F("preset_name", ps.Preset.Name), ) - // Report a preset as hard-limited only if all the following conditions are met: - // - The preset is marked as hard-limited - // - The preset is using the active version of its template, and the template has not been deleted + // Report a metric only if the preset uses the latest version of the template and the template is not deleted. + // This avoids conflicts between metrics from old and new template versions. // - // The second condition is important because a hard-limited preset that has become outdated is no longer relevant. - // Its associated prebuilt workspaces were likely deleted, and it's not meaningful to continue reporting it - // as hard-limited to the admin. - reportAsHardLimited := ps.IsHardLimited && ps.Preset.UsingActiveVersion && !ps.Preset.Deleted - c.metrics.trackHardLimitedStatus(ps.Preset.OrganizationName, ps.Preset.TemplateName, ps.Preset.Name, reportAsHardLimited) + // NOTE: Multiple versions of a preset can exist with the same orgName, templateName, and presetName, + // because templates can have multiple versions — or deleted templates can share the same name. + // + // The safest approach is to report the metric only for the latest version of the preset. + // When a new template version is released, the metric for the new preset should overwrite + // the old value in Prometheus. + // + // However, there’s one edge case: if an admin creates a template, it becomes hard-limited, + // then deletes the template and never creates another with the same name, + // the old preset will continue to be reported as hard-limited — + // even though it’s deleted. This will persist until `coderd` is restarted. + if ps.Preset.UsingActiveVersion && !ps.Preset.Deleted { + c.metrics.trackHardLimitedStatus(ps.Preset.OrganizationName, ps.Preset.TemplateName, ps.Preset.Name, ps.IsHardLimited) + } // If the preset reached the hard failure limit for the first time during this iteration: // - Mark it as hard-limited in the database diff --git a/enterprise/coderd/prebuilds/reconcile_test.go b/enterprise/coderd/prebuilds/reconcile_test.go index a0e1f9726d7d5..7de22db64c8be 100644 --- a/enterprise/coderd/prebuilds/reconcile_test.go +++ b/enterprise/coderd/prebuilds/reconcile_test.go @@ -1034,7 +1034,8 @@ func TestHardLimitedPresetShouldNotBlockDeletion(t *testing.T) { require.Equal(t, database.WorkspaceTransitionDelete, workspaceBuilds[0].Transition) require.Equal(t, database.WorkspaceTransitionStart, workspaceBuilds[1].Transition) - // Metric is deleted after preset became outdated. + // The metric is still set to 1, even though the preset has become outdated. + // This happens because the old value hasn't been overwritten by a newer preset yet. mf, err = registry.Gather() require.NoError(t, err) metric = findMetric(mf, prebuilds.MetricPresetHardLimitedGauge, map[string]string{ @@ -1042,7 +1043,9 @@ func TestHardLimitedPresetShouldNotBlockDeletion(t *testing.T) { "preset_name": preset.Name, "org_name": org.Name, }) - require.Nil(t, metric) + require.NotNil(t, metric) + require.NotNil(t, metric.GetGauge()) + require.EqualValues(t, 1, metric.GetGauge().GetValue()) }) } } diff --git a/go.mod b/go.mod index fc4c924d4cb2b..1bc98d5f01b26 100644 --- a/go.mod +++ b/go.mod @@ -66,7 +66,7 @@ replace github.com/charmbracelet/bubbletea => github.com/coder/bubbletea v1.2.2- // Trivy has some issues that we're floating patches for, and will hopefully // be upstreamed eventually. -replace github.com/aquasecurity/trivy => github.com/coder/trivy v0.0.0-20250409153844-e6b004bc465a +replace github.com/aquasecurity/trivy => github.com/coder/trivy v0.0.0-20250527170238-9416a59d7019 // afero/tarfs has a bug that breaks our usage. A PR has been submitted upstream. // https://github.com/spf13/afero/pull/487 @@ -101,7 +101,7 @@ require ( github.com/coder/quartz v0.2.1-0.20250527113331-b71761ce32df github.com/coder/retry v1.5.1 github.com/coder/serpent v0.10.0 - github.com/coder/terraform-provider-coder/v2 v2.5.0 + github.com/coder/terraform-provider-coder/v2 v2.5.2 github.com/coder/websocket v1.8.13 github.com/coder/wgtunnel v0.1.13-0.20240522110300-ade90dfb2da0 github.com/coreos/go-oidc/v3 v3.14.1 @@ -196,7 +196,7 @@ require ( go.uber.org/mock v0.5.0 go4.org/netipx v0.0.0-20230728180743-ad4cb58a6516 golang.org/x/crypto v0.38.0 - golang.org/x/exp v0.0.0-20250210185358-939b2ce775ac + golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0 golang.org/x/mod v0.24.0 golang.org/x/net v0.40.0 golang.org/x/oauth2 v0.29.0 @@ -256,8 +256,8 @@ require ( github.com/armon/go-radix v1.0.1-0.20221118154546-54df44f2176c // indirect github.com/atotto/clipboard v0.1.4 // indirect github.com/aws/aws-sdk-go-v2 v1.36.3 - github.com/aws/aws-sdk-go-v2/config v1.29.13 - github.com/aws/aws-sdk-go-v2/credentials v1.17.66 // indirect + github.com/aws/aws-sdk-go-v2/config v1.29.14 + github.com/aws/aws-sdk-go-v2/credentials v1.17.67 // indirect github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.30 // indirect github.com/aws/aws-sdk-go-v2/feature/rds/auth v1.5.1 github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.34 // indirect @@ -268,7 +268,7 @@ require ( github.com/aws/aws-sdk-go-v2/service/ssm v1.52.4 // indirect github.com/aws/aws-sdk-go-v2/service/sso v1.25.3 // indirect github.com/aws/aws-sdk-go-v2/service/ssooidc v1.30.1 // indirect - github.com/aws/aws-sdk-go-v2/service/sts v1.33.18 // indirect + github.com/aws/aws-sdk-go-v2/service/sts v1.33.19 // indirect github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect github.com/aymerick/douceur v0.2.0 // indirect github.com/beorn7/perks v1.0.1 // indirect @@ -280,12 +280,12 @@ require ( github.com/chromedp/sysutil v1.1.0 // indirect github.com/cihub/seelog v0.0.0-20170130134532-f561c5e57575 // indirect github.com/clbanning/mxj/v2 v2.7.0 // indirect - github.com/cloudflare/circl v1.6.0 // indirect + github.com/cloudflare/circl v1.6.1 // indirect github.com/containerd/continuity v0.4.5 // indirect github.com/coreos/go-iptables v0.6.0 // indirect github.com/dlclark/regexp2 v1.11.5 // indirect - github.com/docker/cli v28.0.4+incompatible // indirect - github.com/docker/docker v28.0.4+incompatible // indirect + github.com/docker/cli v28.1.1+incompatible // indirect + github.com/docker/docker v28.1.1+incompatible // indirect github.com/docker/go-connections v0.5.0 // indirect github.com/docker/go-units v0.5.0 // indirect github.com/dop251/goja v0.0.0-20241024094426-79f3a7efcdbd // indirect @@ -304,7 +304,7 @@ require ( github.com/go-openapi/jsonpointer v0.21.0 // indirect github.com/go-openapi/jsonreference v0.21.0 // indirect github.com/go-openapi/spec v0.21.0 // indirect - github.com/go-openapi/swag v0.23.0 // indirect + github.com/go-openapi/swag v0.23.1 // indirect github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect github.com/go-sourcemap/sourcemap v2.1.3+incompatible // indirect @@ -320,7 +320,7 @@ require ( github.com/gohugoio/hashstructure v0.5.0 // indirect github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect github.com/golang/protobuf v1.5.4 // indirect - github.com/google/btree v1.1.2 // indirect + github.com/google/btree v1.1.3 // indirect github.com/google/go-querystring v1.1.0 // indirect github.com/google/nftables v0.2.0 // indirect github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db // indirect @@ -361,7 +361,7 @@ require ( github.com/leodido/go-urn v1.4.0 // indirect github.com/lucasb-eyer/go-colorful v1.2.0 // indirect github.com/lufia/plan9stats v0.0.0-20240226150601-1dcf7310316a // indirect - github.com/mailru/easyjson v0.7.7 // indirect + github.com/mailru/easyjson v0.9.0 // indirect github.com/mattn/go-colorable v0.1.14 // indirect github.com/mattn/go-localereader v0.0.1 // indirect github.com/mattn/go-runewidth v0.0.16 // indirect @@ -409,7 +409,7 @@ require ( github.com/secure-systems-lab/go-securesystemslib v0.9.0 // indirect github.com/sirupsen/logrus v1.9.3 // indirect github.com/spaolacci/murmur3 v1.1.0 // indirect - github.com/spf13/cast v1.7.1 // indirect + github.com/spf13/cast v1.8.0 // indirect github.com/swaggo/files/v2 v2.0.0 // indirect github.com/tadvi/systray v0.0.0-20190226123456-11a2b8fa57af // indirect github.com/tailscale/certstore v0.1.1-0.20220316223106-78d6e1c49d8d // indirect @@ -442,7 +442,7 @@ require ( github.com/yuin/goldmark v1.7.10 // indirect github.com/yuin/goldmark-emoji v1.0.6 // indirect github.com/yusufpapurcu/wmi v1.2.4 // indirect - github.com/zclconf/go-cty v1.16.2 + github.com/zclconf/go-cty v1.16.3 github.com/zeebo/errs v1.4.0 // indirect go.opentelemetry.io/auto/sdk v1.1.0 // indirect go.opentelemetry.io/collector/component v0.120.0 // indirect @@ -485,7 +485,7 @@ require ( require ( github.com/anthropics/anthropic-sdk-go v0.2.0-beta.3 - github.com/coder/preview v0.0.2-0.20250521212114-e6a60ffa74f2 + github.com/coder/preview v0.0.2-0.20250527172548-ab173d35040c github.com/fsnotify/fsnotify v1.9.0 github.com/kylecarbs/aisdk-go v0.0.8 github.com/mark3labs/mcp-go v0.30.0 @@ -496,7 +496,7 @@ require ( require ( cel.dev/expr v0.20.0 // indirect cloud.google.com/go v0.120.0 // indirect - cloud.google.com/go/iam v1.4.0 // indirect + cloud.google.com/go/iam v1.4.1 // indirect cloud.google.com/go/monitoring v1.24.0 // indirect cloud.google.com/go/storage v1.50.0 // indirect github.com/DataDog/datadog-agent/comp/core/tagger/origindetection v0.64.0-rc.1 // indirect @@ -506,21 +506,21 @@ require ( github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.50.0 // indirect github.com/aquasecurity/go-version v0.0.1 // indirect github.com/aquasecurity/trivy v0.58.2 // indirect - github.com/aws/aws-sdk-go v1.55.6 // indirect + github.com/aws/aws-sdk-go v1.55.7 // indirect github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d // indirect github.com/charmbracelet/x/exp/slice v0.0.0-20250327172914-2fdc97757edf // indirect github.com/cncf/xds/go v0.0.0-20250121191232-2f005788dc42 // indirect github.com/dgryski/go-farm v0.0.0-20240924180020-3414d57e47da // indirect github.com/envoyproxy/go-control-plane/envoy v1.32.4 // indirect github.com/envoyproxy/protoc-gen-validate v1.2.1 // indirect - github.com/gorilla/websocket v1.5.3 // indirect + github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 // indirect github.com/hashicorp/go-getter v1.7.8 // indirect github.com/hashicorp/go-safetemp v1.0.0 // indirect github.com/klauspost/cpuid/v2 v2.2.10 // indirect - github.com/moby/sys/user v0.3.0 // indirect + github.com/moby/sys/user v0.4.0 // indirect github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect github.com/puzpuzpuz/xsync/v3 v3.5.1 // indirect - github.com/samber/lo v1.49.1 // indirect + github.com/samber/lo v1.50.0 // indirect github.com/spiffe/go-spiffe/v2 v2.5.0 // indirect github.com/tidwall/sjson v1.2.5 // indirect github.com/ulikunitz/xz v0.5.12 // indirect diff --git a/go.sum b/go.sum index d8521ea5c7bb1..ff82f4db0ec17 100644 --- a/go.sum +++ b/go.sum @@ -319,8 +319,8 @@ cloud.google.com/go/iam v0.8.0/go.mod h1:lga0/y3iH6CX7sYqypWJ33hf7kkfXJag67naqGE cloud.google.com/go/iam v0.11.0/go.mod h1:9PiLDanza5D+oWFZiH1uG+RnRCfEGKoyl6yo4cgWZGY= cloud.google.com/go/iam v0.12.0/go.mod h1:knyHGviacl11zrtZUoDuYpDgLjvr28sLQaG0YB2GYAY= cloud.google.com/go/iam v0.13.0/go.mod h1:ljOg+rcNfzZ5d6f1nAUJ8ZIxOaZUVoS14bKCtaLZ/D0= -cloud.google.com/go/iam v1.4.0 h1:ZNfy/TYfn2uh/ukvhp783WhnbVluqf/tzOaqVUPlIPA= -cloud.google.com/go/iam v1.4.0/go.mod h1:gMBgqPaERlriaOV0CUl//XUzDhSfXevn4OEUbg6VRs4= +cloud.google.com/go/iam v1.4.1 h1:cFC25Nv+u5BkTR/BT1tXdoF2daiVbZ1RLx2eqfQ9RMM= +cloud.google.com/go/iam v1.4.1/go.mod h1:2vUEJpUG3Q9p2UdsyksaKpDzlwOrnMzS30isdReIcLM= cloud.google.com/go/iap v1.4.0/go.mod h1:RGFwRJdihTINIe4wZ2iCP0zF/qu18ZwyKxrhMhygBEc= cloud.google.com/go/iap v1.5.0/go.mod h1:UH/CGgKd4KyohZL5Pt0jSKE4m3FR51qg6FKQ/z/Ix9A= cloud.google.com/go/iap v1.6.0/go.mod h1:NSuvI9C/j7UdjGjIde7t7HBz+QTwBcapPE07+sSRcLk= @@ -750,14 +750,14 @@ github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn github.com/awalterschulze/gographviz v2.0.3+incompatible h1:9sVEXJBJLwGX7EQVhLm2elIKCm7P2YHFC8v6096G09E= github.com/awalterschulze/gographviz v2.0.3+incompatible/go.mod h1:GEV5wmg4YquNw7v1kkyoX9etIk8yVmXj+AkDHuuETHs= github.com/aws/aws-sdk-go v1.44.122/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo= -github.com/aws/aws-sdk-go v1.55.6 h1:cSg4pvZ3m8dgYcgqB97MrcdjUmZ1BeMYKUxMMB89IPk= -github.com/aws/aws-sdk-go v1.55.6/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU= +github.com/aws/aws-sdk-go v1.55.7 h1:UJrkFq7es5CShfBwlWAC8DA077vp8PyVbQd3lqLiztE= +github.com/aws/aws-sdk-go v1.55.7/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU= github.com/aws/aws-sdk-go-v2 v1.36.3 h1:mJoei2CxPutQVxaATCzDUjcZEjVRdpsiiXi2o38yqWM= github.com/aws/aws-sdk-go-v2 v1.36.3/go.mod h1:LLXuLpgzEbD766Z5ECcRmi8AzSwfZItDtmABVkRLGzg= -github.com/aws/aws-sdk-go-v2/config v1.29.13 h1:RgdPqWoE8nPpIekpVpDJsBckbqT4Liiaq9f35pbTh1Y= -github.com/aws/aws-sdk-go-v2/config v1.29.13/go.mod h1:NI28qs/IOUIRhsR7GQ/JdexoqRN9tDxkIrYZq0SOF44= -github.com/aws/aws-sdk-go-v2/credentials v1.17.66 h1:aKpEKaTy6n4CEJeYI1MNj97oSDLi4xro3UzQfwf5RWE= -github.com/aws/aws-sdk-go-v2/credentials v1.17.66/go.mod h1:xQ5SusDmHb/fy55wU0QqTy0yNfLqxzec59YcsRZB+rI= +github.com/aws/aws-sdk-go-v2/config v1.29.14 h1:f+eEi/2cKCg9pqKBoAIwRGzVb70MRKqWX4dg1BDcSJM= +github.com/aws/aws-sdk-go-v2/config v1.29.14/go.mod h1:wVPHWcIFv3WO89w0rE10gzf17ZYy+UVS1Geq8Iei34g= +github.com/aws/aws-sdk-go-v2/credentials v1.17.67 h1:9KxtdcIA/5xPNQyZRgUSpYOE6j9Bc4+D7nZua0KGYOM= +github.com/aws/aws-sdk-go-v2/credentials v1.17.67/go.mod h1:p3C44m+cfnbv763s52gCqrjaqyPikj9Sg47kUVaNZQQ= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.30 h1:x793wxmUWVDhshP8WW2mlnXuFrO4cOd3HLBroh1paFw= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.30/go.mod h1:Jpne2tDnYiFascUEs2AWHJL9Yp7A5ZVy3TNyxaAjD6M= github.com/aws/aws-sdk-go-v2/feature/rds/auth v1.5.1 h1:yg6nrV33ljY6CppoRnnsKLqIZ5ExNdQOGRBGNfc56Yw= @@ -778,8 +778,8 @@ github.com/aws/aws-sdk-go-v2/service/sso v1.25.3 h1:1Gw+9ajCV1jogloEv1RRnvfRFia2 github.com/aws/aws-sdk-go-v2/service/sso v1.25.3/go.mod h1:qs4a9T5EMLl/Cajiw2TcbNt2UNo/Hqlyp+GiuG4CFDI= github.com/aws/aws-sdk-go-v2/service/ssooidc v1.30.1 h1:hXmVKytPfTy5axZ+fYbR5d0cFmC3JvwLm5kM83luako= github.com/aws/aws-sdk-go-v2/service/ssooidc v1.30.1/go.mod h1:MlYRNmYu/fGPoxBQVvBYr9nyr948aY/WLUvwBMBJubs= -github.com/aws/aws-sdk-go-v2/service/sts v1.33.18 h1:xz7WvTMfSStb9Y8NpCT82FXLNC3QasqBfuAFHY4Pk5g= -github.com/aws/aws-sdk-go-v2/service/sts v1.33.18/go.mod h1:cQnB8CUnxbMU82JvlqjKR2HBOm3fe9pWorWBza6MBJ4= +github.com/aws/aws-sdk-go-v2/service/sts v1.33.19 h1:1XuUZ8mYJw9B6lzAkXhqHlJd/XvaX32evhproijJEZY= +github.com/aws/aws-sdk-go-v2/service/sts v1.33.19/go.mod h1:cQnB8CUnxbMU82JvlqjKR2HBOm3fe9pWorWBza6MBJ4= github.com/aws/smithy-go v1.22.3 h1:Z//5NuZCSW6R4PhQ93hShNbyBbn8BWCmCVCt+Q8Io5k= github.com/aws/smithy-go v1.22.3/go.mod h1:t1ufH5HMublsJYulve2RKmHDC15xu1f26kHCp/HgceI= github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k= @@ -877,8 +877,8 @@ github.com/clbanning/mxj/v2 v2.7.0/go.mod h1:hNiWqW14h+kc+MdF9C6/YoRfjEJoR3ou6tn github.com/cli/safeexec v1.0.1 h1:e/C79PbXF4yYTN/wauC4tviMxEV13BwljGj0N9j+N00= github.com/cli/safeexec v1.0.1/go.mod h1:Z/D4tTN8Vs5gXYHDCbaM1S/anmEDnJb1iW0+EJ5zx3Q= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cloudflare/circl v1.6.0 h1:cr5JKic4HI+LkINy2lg3W2jF8sHCVTBncJr5gIIq7qk= -github.com/cloudflare/circl v1.6.0/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs= +github.com/cloudflare/circl v1.6.1 h1:zqIqSPIndyBh1bjLVVDHMPpVKqp8Su/V+6MeDzzQBQ0= +github.com/cloudflare/circl v1.6.1/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= @@ -911,8 +911,8 @@ github.com/coder/pq v1.10.5-0.20240813183442-0c420cb5a048 h1:3jzYUlGH7ZELIH4XggX github.com/coder/pq v1.10.5-0.20240813183442-0c420cb5a048/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/coder/pretty v0.0.0-20230908205945-e89ba86370e0 h1:3A0ES21Ke+FxEM8CXx9n47SZOKOpgSE1bbJzlE4qPVs= github.com/coder/pretty v0.0.0-20230908205945-e89ba86370e0/go.mod h1:5UuS2Ts+nTToAMeOjNlnHFkPahrtDkmpydBen/3wgZc= -github.com/coder/preview v0.0.2-0.20250521212114-e6a60ffa74f2 h1:D52yPPupcbNWppZzWAjZJG5L34TGpNyKj7vG1VT13FU= -github.com/coder/preview v0.0.2-0.20250521212114-e6a60ffa74f2/go.mod h1:9bwyhQSVDjcxAWuFFaG6/qBqhaiW5oqF5PEQMhevKLs= +github.com/coder/preview v0.0.2-0.20250527172548-ab173d35040c h1:lPIImqcf46QcK3hYlr20xt2SG66IAAK/kfZdEhM6OJc= +github.com/coder/preview v0.0.2-0.20250527172548-ab173d35040c/go.mod h1:Ltd83BrbNN7Nj+hToa6My7xTTj8FVa2xNk5589b1DJc= github.com/coder/quartz v0.2.1-0.20250527113331-b71761ce32df h1:o6lpiSrGKVopPv6Fnh3S+FrI5ntRE0eSUo6Bqpzpkgo= github.com/coder/quartz v0.2.1-0.20250527113331-b71761ce32df/go.mod h1:vsiCc+AHViMKH2CQpGIpFgdHIEQsxwm8yCscqKmzbRA= github.com/coder/retry v1.5.1 h1:iWu8YnD8YqHs3XwqrqsjoBTAVqT9ml6z9ViJ2wlMiqc= @@ -925,10 +925,10 @@ github.com/coder/tailscale v1.1.1-0.20250422090654-5090e715905e h1:nope/SZfoLB9M github.com/coder/tailscale v1.1.1-0.20250422090654-5090e715905e/go.mod h1:1ggFFdHTRjPRu9Yc1yA7nVHBYB50w9Ce7VIXNqcW6Ko= github.com/coder/terraform-config-inspect v0.0.0-20250107175719-6d06d90c630e h1:JNLPDi2P73laR1oAclY6jWzAbucf70ASAvf5mh2cME0= github.com/coder/terraform-config-inspect v0.0.0-20250107175719-6d06d90c630e/go.mod h1:Gz/z9Hbn+4KSp8A2FBtNszfLSdT2Tn/uAKGuVqqWmDI= -github.com/coder/terraform-provider-coder/v2 v2.5.0 h1:RLB+wONx4vErT4naWAk0EP2GqMObpN9PePTDblH+rjw= -github.com/coder/terraform-provider-coder/v2 v2.5.0/go.mod h1:kqP2MW/OF5u3QBRPDt84vn1izKjncICFfv26nSb781I= -github.com/coder/trivy v0.0.0-20250409153844-e6b004bc465a h1:yryP7e+IQUAArlycH4hQrjXQ64eRNbxsV5/wuVXHgME= -github.com/coder/trivy v0.0.0-20250409153844-e6b004bc465a/go.mod h1:dDvq9axp3kZsT63gY2Znd1iwzfqDq3kXbQnccIrjRYY= +github.com/coder/terraform-provider-coder/v2 v2.5.2 h1:hE1IGbsGtWrJjq+But2psGdZy5k8fRnOqysmlpoHNPg= +github.com/coder/terraform-provider-coder/v2 v2.5.2/go.mod h1:kqP2MW/OF5u3QBRPDt84vn1izKjncICFfv26nSb781I= +github.com/coder/trivy v0.0.0-20250527170238-9416a59d7019 h1:MHkv/W7l9eRAN9gOG0qZ1TLRGWIIfNi92273vPAQ8Fs= +github.com/coder/trivy v0.0.0-20250527170238-9416a59d7019/go.mod h1:eqk+w9RLBmbd/cB5XfPZFuVn77cf/A6fB7qmEVeSmXk= github.com/coder/websocket v1.8.13 h1:f3QZdXy7uGVz+4uCJy2nTZyM0yTBj8yANEHhqlXZ9FE= github.com/coder/websocket v1.8.13/go.mod h1:LNVeNrXQZfe5qhS9ALED3uA+l5pPqvwXg3CKoDBB2gs= github.com/coder/wgtunnel v0.1.13-0.20240522110300-ade90dfb2da0 h1:C2/eCr+r0a5Auuw3YOiSyLNHkdMtyCZHPFBx7syN4rk= @@ -981,10 +981,10 @@ github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5Qvfr github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= github.com/dlclark/regexp2 v1.11.5 h1:Q/sSnsKerHeCkc/jSTNq1oCm7KiVgUMZRDUoRu0JQZQ= github.com/dlclark/regexp2 v1.11.5/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= -github.com/docker/cli v28.0.4+incompatible h1:pBJSJeNd9QeIWPjRcV91RVJihd/TXB77q1ef64XEu4A= -github.com/docker/cli v28.0.4+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= -github.com/docker/docker v28.0.4+incompatible h1:JNNkBctYKurkw6FrHfKqY0nKIDf5nrbxjVBtS+cdcok= -github.com/docker/docker v28.0.4+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/cli v28.1.1+incompatible h1:eyUemzeI45DY7eDPuwUcmDyDj1pM98oD5MdSpiItp8k= +github.com/docker/cli v28.1.1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= +github.com/docker/docker v28.1.1+incompatible h1:49M11BFLsVO1gxY9UX9p/zwkE/rswggs8AdFmXQw51I= +github.com/docker/docker v28.1.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c= github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc= github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= @@ -1093,8 +1093,8 @@ github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66D github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic= github.com/go-git/go-billy/v5 v5.6.2 h1:6Q86EsPXMa7c3YZ3aLAQsMA0VlWmy43r6FHqa/UNbRM= github.com/go-git/go-billy/v5 v5.6.2/go.mod h1:rcFC2rAsp/erv7CMz9GczHcuD0D32fWzH+MJAU+jaUU= -github.com/go-git/go-git/v5 v5.14.0 h1:/MD3lCrGjCen5WfEAzKg00MJJffKhC8gzS80ycmCi60= -github.com/go-git/go-git/v5 v5.14.0/go.mod h1:Z5Xhoia5PcWA3NF8vRLURn9E5FRhSl7dGj9ItW3Wk5k= +github.com/go-git/go-git/v5 v5.16.0 h1:k3kuOEpkc0DeY7xlL6NaaNg39xdgQbtH5mwCafHO9AQ= +github.com/go-git/go-git/v5 v5.16.0/go.mod h1:4Ge4alE/5gPs30F2H1esi2gPd69R0C39lolkucHBOp8= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= @@ -1123,8 +1123,8 @@ github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4= github.com/go-openapi/spec v0.21.0 h1:LTVzPc3p/RzRnkQqLRndbAzjY0d0BCL72A6j3CdL9ZY= github.com/go-openapi/spec v0.21.0/go.mod h1:78u6VdPw81XU44qEWGhtr982gJ5BWg2c0I5XwVMotYk= -github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE= -github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ= +github.com/go-openapi/swag v0.23.1 h1:lpsStH0n2ittzTnbaSloVZLuB5+fvSY/+hnagBjSNZU= +github.com/go-openapi/swag v0.23.1/go.mod h1:STZs8TbRvEQQKUA+JZNAm3EWlgaOBGpyFDqQnDHMef0= github.com/go-pdf/fpdf v0.5.0/go.mod h1:HzcnA+A23uwogo0tp9yU+l3V+KXhiESpt1PMayhOh5M= github.com/go-pdf/fpdf v0.6.0/go.mod h1:HzcnA+A23uwogo0tp9yU+l3V+KXhiESpt1PMayhOh5M= github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= @@ -1231,8 +1231,8 @@ github.com/gomarkdown/markdown v0.0.0-20240930133441-72d49d9543d8 h1:4txT5G2kqVA github.com/gomarkdown/markdown v0.0.0-20240930133441-72d49d9543d8/go.mod h1:JDGcbDT52eL4fju3sZ4TeHGsQwhG9nbDV21aMyhwPoA= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU= -github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= +github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg= +github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= github.com/google/flatbuffers v2.0.8+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= github.com/google/flatbuffers v25.2.10+incompatible h1:F3vclr7C3HpB1k9mxCGRMXq6FdUalZ6H/pNX4FP1v0Q= github.com/google/flatbuffers v25.2.10+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= @@ -1324,8 +1324,8 @@ github.com/gorilla/css v1.0.1 h1:ntNaBIghp6JmvWnxbZKANoLyuXTPZ4cAMlo6RyhlbO8= github.com/gorilla/css v1.0.1/go.mod h1:BvnYkspnSzMmwRK+b8/xgNPLiIuNZr6vbZBTPQ2A3b0= github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ= -github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= -github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 h1:JeSE6pjso5THxAzdVpqr6/geYxZytqFMBCOtn/ujyeo= +github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674/go.mod h1:r4w70xmWCQKmi1ONH4KIaBptdivuRPyosB9RmPlGEwA= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0/go.mod h1:hgWBS7lorOAVIJEQMi4ZsPv9hVvWI6+ch50m39Pf2Ks= github.com/grpc-ecosystem/grpc-gateway/v2 v2.11.3/go.mod h1:o//XUCC/F+yRGJoPO/VU0GSB0f8Nhgmxx0VIRUvaC0w= @@ -1498,10 +1498,10 @@ github.com/lufia/plan9stats v0.0.0-20240226150601-1dcf7310316a/go.mod h1:ilwx/Dt github.com/lyft/protoc-gen-star v0.6.0/go.mod h1:TGAoBVkt8w7MPG72TrKIu85MIdXwDuzJYeZuUPFPNwA= github.com/lyft/protoc-gen-star v0.6.1/go.mod h1:TGAoBVkt8w7MPG72TrKIu85MIdXwDuzJYeZuUPFPNwA= github.com/lyft/protoc-gen-star/v2 v2.0.1/go.mod h1:RcCdONR2ScXaYnQC5tUzxzlpA3WVYF7/opLeUgcQs/o= -github.com/magiconair/properties v1.8.9 h1:nWcCbLq1N2v/cpNsy5WvQ37Fb+YElfq20WJ/a8RkpQM= -github.com/magiconair/properties v1.8.9/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= -github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= -github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/magiconair/properties v1.8.10 h1:s31yESBquKXCV9a/ScB3ESkOjUYYv+X0rg8SYxI99mE= +github.com/magiconair/properties v1.8.10/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= +github.com/mailru/easyjson v0.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4= +github.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU= github.com/makeworld-the-better-one/dither/v2 v2.4.0 h1:Az/dYXiTcwcRSe59Hzw4RI1rSnAZns+1msaCXetrMFE= github.com/makeworld-the-better-one/dither/v2 v2.4.0/go.mod h1:VBtN8DXO7SNtyGmLiGA7IsFeKrBkQPze1/iAeM95arc= github.com/marekm4/color-extractor v1.2.1 h1:3Zb2tQsn6bITZ8MBVhc33Qn1k5/SEuZ18mrXGUqIwn0= @@ -1563,14 +1563,16 @@ github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zx github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0= github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo= +github.com/moby/go-archive v0.1.0 h1:Kk/5rdW/g+H8NHdJW2gsXyZ7UnzvJNOy6VKJqueWdcQ= +github.com/moby/go-archive v0.1.0/go.mod h1:G9B+YoujNohJmrIYFBpSd54GTUB4lt9S+xVQvsJyFuo= github.com/moby/moby v28.1.1+incompatible h1:lyEaGTiUhIdXRUv/vPamckAbPt5LcPQkeHmwAHN98eQ= github.com/moby/moby v28.1.1+incompatible/go.mod h1:fDXVQ6+S340veQPv35CzDahGBmHsiclFwfEygB/TWMc= github.com/moby/patternmatcher v0.6.0 h1:GmP9lR19aU5GqSSFko+5pRqHi+Ohk1O69aFiKkVGiPk= github.com/moby/patternmatcher v0.6.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc= github.com/moby/sys/sequential v0.6.0 h1:qrx7XFUd/5DxtqcoH1h438hF5TmOvzC/lspjy7zgvCU= github.com/moby/sys/sequential v0.6.0/go.mod h1:uyv8EUTrca5PnDsdMGXhZe6CCe8U/UiTWd+lL+7b/Ko= -github.com/moby/sys/user v0.3.0 h1:9ni5DlcW5an3SvRSx4MouotOygvzaXbaSrc/wGDFWPo= -github.com/moby/sys/user v0.3.0/go.mod h1:bG+tYYYJgaMtRKgEmuueC0hJEAZWwtIbZTB+85uoHjs= +github.com/moby/sys/user v0.4.0 h1:jhcMKit7SA80hivmFJcbB1vqmw//wU61Zdui2eQXuMs= +github.com/moby/sys/user v0.4.0/go.mod h1:bG+tYYYJgaMtRKgEmuueC0hJEAZWwtIbZTB+85uoHjs= github.com/moby/sys/userns v0.1.0 h1:tVLXkFOxVu9A64/yh59slHVv9ahO9UIev4JZusOLG/g= github.com/moby/sys/userns v0.1.0/go.mod h1:IHUYgu/kao6N8YZlp9Cf444ySSvCmDlmzUcYfDHOl28= github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0= @@ -1722,8 +1724,8 @@ github.com/ruudk/golang-pdf417 v0.0.0-20201230142125-a7e3863a1245/go.mod h1:pQAZ github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkBk= github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc= -github.com/samber/lo v1.49.1 h1:4BIFyVfuQSEpluc7Fua+j1NolZHiEHEpaSEKdsH0tew= -github.com/samber/lo v1.49.1/go.mod h1:dO6KHFzUKXgP8LDhU0oI8d2hekjXnGOu0DB8Jecxd6o= +github.com/samber/lo v1.50.0 h1:XrG0xOeHs+4FQ8gJR97zDz5uOFMW7OwFWiFVzqopKgY= +github.com/samber/lo v1.50.0/go.mod h1:RjZyNk6WSnUFRKK6EyOhsRJMqft3G+pg7dCWHQCWvsc= github.com/satori/go.uuid v1.2.1-0.20181028125025-b2ce2384e17b h1:gQZ0qzfKHQIybLANtM3mBXNUtOfsCFXeTsnBqCsx1KM= github.com/satori/go.uuid v1.2.1-0.20181028125025-b2ce2384e17b/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= github.com/secure-systems-lab/go-securesystemslib v0.9.0 h1:rf1HIbL64nUpEIZnjLZ3mcNEL9NBPB0iuVjyxvq3LZc= @@ -1744,8 +1746,8 @@ github.com/sosedoff/gitkit v0.4.0/go.mod h1:V3EpGZ0nvCBhXerPsbDeqtyReNb48cwP9Ktk github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= -github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y= -github.com/spf13/cast v1.7.1/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= +github.com/spf13/cast v1.8.0 h1:gEN9K4b8Xws4EX0+a0reLmhq8moKn7ntRlQYgjPeCDk= +github.com/spf13/cast v1.8.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o= github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spiffe/go-spiffe/v2 v2.5.0 h1:N2I01KCUkv1FAjZXJMwh95KK1ZIQLYbPfhaxw8WS0hE= @@ -1803,10 +1805,10 @@ github.com/tdewolff/parse/v2 v2.7.15/go.mod h1:3FbJWZp3XT9OWVN3Hmfp0p/a08v4h8J9W github.com/tdewolff/test v1.0.11-0.20231101010635-f1265d231d52/go.mod h1:6DAvZliBAAnD7rhVgwaM7DE5/d9NMOAJ09SqYqeK4QE= github.com/tdewolff/test v1.0.11-0.20240106005702-7de5f7df4739 h1:IkjBCtQOOjIn03u/dMQK9g+Iw9ewps4mCl1nB8Sscbo= github.com/tdewolff/test v1.0.11-0.20240106005702-7de5f7df4739/go.mod h1:XPuWBzvdUzhCuxWO1ojpXsyzsA5bFoS3tO/Q3kFuTG8= -github.com/testcontainers/testcontainers-go v0.36.0 h1:YpffyLuHtdp5EUsI5mT4sRw8GZhO/5ozyDT1xWGXt00= -github.com/testcontainers/testcontainers-go v0.36.0/go.mod h1:yk73GVJ0KUZIHUtFna6MO7QS144qYpoY8lEEtU9Hed0= -github.com/testcontainers/testcontainers-go/modules/localstack v0.36.0 h1:zVwbe46NYg2vtC26aF0ndClK5S9J7TgAliQbTLyHm+0= -github.com/testcontainers/testcontainers-go/modules/localstack v0.36.0/go.mod h1:rxyzj5nX/OUn7QK5PVxKYHJg1eeNtNzWMX2hSbNNJk0= +github.com/testcontainers/testcontainers-go v0.37.0 h1:L2Qc0vkTw2EHWQ08djon0D2uw7Z/PtHS/QzZZ5Ra/hg= +github.com/testcontainers/testcontainers-go v0.37.0/go.mod h1:QPzbxZhQ6Bclip9igjLFj6z0hs01bU8lrl2dHQmgFGM= +github.com/testcontainers/testcontainers-go/modules/localstack v0.37.0 h1:nPuxUYseqS0eYJg7KDJd95PhoMhdpTnSNtkDLwWFngo= +github.com/testcontainers/testcontainers-go/modules/localstack v0.37.0/go.mod h1:Mw+N4qqJ5iWbg45yWsdLzICfeCEwvYNudfAHHFqCU8Q= github.com/tetratelabs/wazero v1.9.0 h1:IcZ56OuxrtaEz8UYNRHBrUa9bYeX9oVY93KspZZBf/I= github.com/tetratelabs/wazero v1.9.0/go.mod h1:TSbcXCfFP0L2FGkRPxHphadXPjo1T6W+CseNNY7EkjM= github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= @@ -1897,8 +1899,8 @@ github.com/yuin/goldmark-emoji v1.0.6 h1:QWfF2FYaXwL74tfGOW5izeiZepUDroDJfWubQI9 github.com/yuin/goldmark-emoji v1.0.6/go.mod h1:ukxJDKFpdFb5x0a5HqbdlcKtebh086iJpI31LTKmWuA= github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0= github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= -github.com/zclconf/go-cty v1.16.2 h1:LAJSwc3v81IRBZyUVQDUdZ7hs3SYs9jv0eZJDWHD/70= -github.com/zclconf/go-cty v1.16.2/go.mod h1:VvMs5i0vgZdhYawQNq5kePSpLAoz8u1xvZgrPIxfnZE= +github.com/zclconf/go-cty v1.16.3 h1:osr++gw2T61A8KVYHoQiFbFd1Lh3JOCXc/jFLJXKTxk= +github.com/zclconf/go-cty v1.16.3/go.mod h1:VvMs5i0vgZdhYawQNq5kePSpLAoz8u1xvZgrPIxfnZE= github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940 h1:4r45xpDWB6ZMSMNJFMOjqrGHynW3DIBuR2H9j0ug+Mo= github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940/go.mod h1:CmBdvvj3nqzfzJ6nTCIwDTPZ56aVGvDrmztiO5g3qrM= github.com/zclconf/go-cty-yaml v1.1.0 h1:nP+jp0qPHv2IhUVqmQSzjvqAWcObN0KBkUl2rWBdig0= @@ -2027,8 +2029,8 @@ golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= golang.org/x/exp v0.0.0-20220827204233-334a2380cb91/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE= -golang.org/x/exp v0.0.0-20250210185358-939b2ce775ac h1:l5+whBCLH3iH2ZNHYLbAe58bo7yrN4mVcnkHDYz5vvs= -golang.org/x/exp v0.0.0-20250210185358-939b2ce775ac/go.mod h1:hH+7mtFmImwwcMvScyxUhjuVHR3HGaDPMn9rMSUUbxo= +golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0 h1:R84qjqJb5nVJMxqWYb3np9L5ZsaDtB+a39EqjV0JSUM= +golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0/go.mod h1:S9Xr4PYopiDyqSyp5NjCrhFrqg6A5zA2E/iPHPhqnS8= golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= @@ -2746,8 +2748,8 @@ honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9 honnef.co/go/tools v0.1.3/go.mod h1:NgwopIslSNH47DimFoV78dnkksY2EFtX0ajyb3K/las= howett.net/plist v1.0.0 h1:7CrbWYbPPO/PyNy38b2EB/+gYbjCe2DXBxgtOOZbSQM= howett.net/plist v1.0.0/go.mod h1:lqaXoTrLY4hg8tnEzNru53gicrbv7rrk+2xJA/7hw9g= -k8s.io/apimachinery v0.32.3 h1:JmDuDarhDmA/Li7j3aPrwhpNBA94Nvk5zLeOge9HH1U= -k8s.io/apimachinery v0.32.3/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE= +k8s.io/apimachinery v0.33.1 h1:mzqXWV8tW9Rw4VeW9rEkqvnxj59k1ezDUl20tFK/oM4= +k8s.io/apimachinery v0.33.1/go.mod h1:BHW0YOu7n22fFv/JkYOEfkUYNRN0fj0BlvMFWA7b+SM= k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 h1:M3sRQVHv7vB20Xc2ybTt7ODCeFj6JSWYFzOFnYeS6Ro= k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= kernel.org/pub/linux/libs/security/libcap/cap v1.2.73 h1:Th2b8jljYqkyZKS3aD3N9VpYsQpHuXLgea+SZUIfODA= @@ -2774,8 +2776,8 @@ modernc.org/libc v1.16.17/go.mod h1:hYIV5VZczAmGZAnG15Vdngn5HSF5cSkbvfz2B7GRuVU= modernc.org/libc v1.16.19/go.mod h1:p7Mg4+koNjc8jkqwcoFBJx7tXkpj00G77X7A72jXPXA= modernc.org/libc v1.17.0/go.mod h1:XsgLldpP4aWlPlsjqKRdHPqCxCjISdHfM/yeWC5GyW0= modernc.org/libc v1.17.1/go.mod h1:FZ23b+8LjxZs7XtFMbSzL/EhPxNbfZbErxEHc7cbD9s= -modernc.org/libc v1.61.13 h1:3LRd6ZO1ezsFiX1y+bHd1ipyEHIJKvuprv0sLTBwLW8= -modernc.org/libc v1.61.13/go.mod h1:8F/uJWL/3nNil0Lgt1Dpz+GgkApWh04N3el3hxJcA6E= +modernc.org/libc v1.62.1 h1:s0+fv5E3FymN8eJVmnk0llBe6rOxCu/DEU+XygRbS8s= +modernc.org/libc v1.62.1/go.mod h1:iXhATfJQLjG3NWy56a6WVU73lWOcdYVxsvwCgoPljuo= modernc.org/mathutil v1.2.2/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= modernc.org/mathutil v1.4.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= modernc.org/mathutil v1.5.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= @@ -2784,13 +2786,13 @@ modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJ modernc.org/memory v1.1.1/go.mod h1:/0wo5ibyrQiaoUoH7f9D8dnglAmILJ5/cxZlRECf+Nw= modernc.org/memory v1.2.0/go.mod h1:/0wo5ibyrQiaoUoH7f9D8dnglAmILJ5/cxZlRECf+Nw= modernc.org/memory v1.2.1/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU= -modernc.org/memory v1.8.2 h1:cL9L4bcoAObu4NkxOlKWBWtNHIsnnACGF/TbqQ6sbcI= -modernc.org/memory v1.8.2/go.mod h1:ZbjSvMO5NQ1A2i3bWeDiVMxIorXwdClKE/0SZ+BMotU= +modernc.org/memory v1.9.1 h1:V/Z1solwAVmMW1yttq3nDdZPJqV1rM05Ccq6KMSZ34g= +modernc.org/memory v1.9.1/go.mod h1:/JP4VbVC+K5sU2wZi9bHoq2MAkCnrt2r98UGeSK7Mjw= modernc.org/opt v0.1.1/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= modernc.org/sqlite v1.18.1/go.mod h1:6ho+Gow7oX5V+OiOQ6Tr4xeqbx13UZ6t+Fw9IRUG4d4= -modernc.org/sqlite v1.36.1 h1:bDa8BJUH4lg6EGkLbahKe/8QqoF8p9gArSc6fTqYhyQ= -modernc.org/sqlite v1.36.1/go.mod h1:7MPwH7Z6bREicF9ZVUR78P1IKuxfZ8mRIDHD0iD+8TU= +modernc.org/sqlite v1.37.0 h1:s1TMe7T3Q3ovQiK2Ouz4Jwh7dw4ZDqbebSDTlSJdfjI= +modernc.org/sqlite v1.37.0/go.mod h1:5YiWv+YviqGMuGw4V+PNplcyaJ5v+vQd7TQOgkACoJM= modernc.org/strutil v1.1.1/go.mod h1:DE+MQQ/hjKBZS2zNInV5hhcipt5rLPWkmpbGeW5mmdw= modernc.org/strutil v1.1.3/go.mod h1:MEHNA7PdEnEwLvspRMtWTNnp2nnyvMfkimT1NKNAGbw= modernc.org/tcl v1.13.1/go.mod h1:XOLfOwzhkljL4itZkK6T72ckMgvj0BDsnKNdZVUOecw= diff --git a/site/src/api/api.ts b/site/src/api/api.ts index 9e579c3706de6..efae24bf27db0 100644 --- a/site/src/api/api.ts +++ b/site/src/api/api.ts @@ -1166,7 +1166,7 @@ class ApiMethods { ) ) { const { job } = await this.getWorkspaceBuildByNumber( - build.workspace_owner_name, + build.workspace_owner_username, build.workspace_name, build.build_number, ); diff --git a/site/src/api/queries/workspaces.ts b/site/src/api/queries/workspaces.ts index 91d58172aab51..61bc7f0e70c22 100644 --- a/site/src/api/queries/workspaces.ts +++ b/site/src/api/queries/workspaces.ts @@ -27,12 +27,10 @@ import { checkAuthorization } from "./authCheck"; import { disabledRefetchOptions } from "./util"; import { workspaceBuildsKey } from "./workspaceBuilds"; -export const workspaceByOwnerAndNameKey = (owner: string, name: string) => [ - "workspace", - owner, - name, - "settings", -]; +export const workspaceByOwnerAndNameKey = ( + ownerUsername: string, + name: string, +) => ["workspace", ownerUsername, name, "settings"]; export const workspaceByOwnerAndName = (owner: string, name: string) => { return { @@ -281,7 +279,7 @@ const updateWorkspaceBuild = async ( queryClient: QueryClient, ) => { const workspaceKey = workspaceByOwnerAndNameKey( - build.workspace_owner_name, + build.workspace_owner_username, build.workspace_name, ); const previousData = queryClient.getQueryData(workspaceKey); diff --git a/site/src/api/typesGenerated.ts b/site/src/api/typesGenerated.ts index 5125a554cacc1..6c970e622b1ac 100644 --- a/site/src/api/typesGenerated.ts +++ b/site/src/api/typesGenerated.ts @@ -826,6 +826,7 @@ export const EntitlementsWarningHeader = "X-Coder-Entitlements-Warning"; // From codersdk/deployment.go export type Experiment = + | "ai-tasks" | "agentic-chat" | "auto-fill-parameters" | "dynamic-parameters" @@ -3351,6 +3352,7 @@ export interface WorkspaceAgentContainer { readonly ports: readonly WorkspaceAgentContainerPort[]; readonly status: string; readonly volumes: Record; + readonly devcontainer_status?: WorkspaceAgentDevcontainerStatus; readonly devcontainer_dirty: boolean; } @@ -3618,7 +3620,8 @@ export interface WorkspaceBuild { readonly workspace_id: string; readonly workspace_name: string; readonly workspace_owner_id: string; - readonly workspace_owner_name: string; + readonly workspace_owner_name?: string; + readonly workspace_owner_username: string; readonly workspace_owner_avatar_url?: string; readonly template_version_id: string; readonly template_version_name: string; diff --git a/site/src/modules/dashboard/DashboardLayout.tsx b/site/src/modules/dashboard/DashboardLayout.tsx index 21fc29859f0ea..26a1dd1aa3693 100644 --- a/site/src/modules/dashboard/DashboardLayout.tsx +++ b/site/src/modules/dashboard/DashboardLayout.tsx @@ -8,7 +8,6 @@ import { AnnouncementBanners } from "modules/dashboard/AnnouncementBanners/Annou import { LicenseBanner } from "modules/dashboard/LicenseBanner/LicenseBanner"; import { type FC, type HTMLAttributes, Suspense } from "react"; import { Outlet } from "react-router-dom"; -import { dashboardContentBottomPadding } from "theme/constants"; import { docs } from "utils/docs"; import { DeploymentBanner } from "./DeploymentBanner/DeploymentBanner"; import { Navbar } from "./Navbar/Navbar"; @@ -24,23 +23,10 @@ export const DashboardLayout: FC = () => { {canViewDeployment && } -
+
-
+
}> @@ -111,7 +97,6 @@ export const DashboardFullPage: FC> = ({
[a-zA-Z0-9-]+)\/(?[a-zA-Z0-9-]+)$/, +]; + export const DeploymentBanner: FC = () => { const { permissions } = useAuthenticated(); const deploymentStatsQuery = useQuery(deploymentStats()); @@ -12,8 +23,16 @@ export const DeploymentBanner: FC = () => { ...health(), enabled: permissions.viewDeploymentConfig, }); + const location = useLocation(); + const isHidden = HIDE_DEPLOYMENT_BANNER_PATHS.some((regex) => + regex.test(location.pathname), + ); - if (!permissions.viewDeploymentConfig || !deploymentStatsQuery.data) { + if ( + isHidden || + !permissions.viewDeploymentConfig || + !deploymentStatsQuery.data + ) { return null; } diff --git a/site/src/modules/dashboard/Navbar/NavbarView.tsx b/site/src/modules/dashboard/Navbar/NavbarView.tsx index b55ac23f16bd7..7e56c9643c066 100644 --- a/site/src/modules/dashboard/Navbar/NavbarView.tsx +++ b/site/src/modules/dashboard/Navbar/NavbarView.tsx @@ -1,4 +1,5 @@ import { API } from "api/api"; +import { experiments } from "api/queries/experiments"; import type * as TypesGen from "api/typesGenerated"; import { Button } from "components/Button/Button"; import { ExternalImage } from "components/ExternalImage/ExternalImage"; @@ -6,8 +7,10 @@ import { CoderIcon } from "components/Icons/CoderIcon"; import type { ProxyContextValue } from "contexts/ProxyContext"; import { useAgenticChat } from "contexts/useAgenticChat"; import { useWebpushNotifications } from "contexts/useWebpushNotifications"; +import { useEmbeddedMetadata } from "hooks/useEmbeddedMetadata"; import { NotificationsInbox } from "modules/notifications/NotificationsInbox/NotificationsInbox"; import type { FC } from "react"; +import { useQuery } from "react-query"; import { NavLink, useLocation } from "react-router-dom"; import { cn } from "utils/cn"; import { DeploymentDropdown } from "./DeploymentDropdown"; @@ -141,6 +144,8 @@ interface NavItemsProps { const NavItems: FC = ({ className }) => { const location = useLocation(); const agenticChat = useAgenticChat(); + const { metadata } = useEmbeddedMetadata(); + const experimentsQuery = useQuery(experiments(metadata.experiments)); return ( ); }; diff --git a/site/src/modules/resources/AgentDevcontainerCard.stories.tsx b/site/src/modules/resources/AgentDevcontainerCard.stories.tsx index e965efea75b6d..fdd85d95c4849 100644 --- a/site/src/modules/resources/AgentDevcontainerCard.stories.tsx +++ b/site/src/modules/resources/AgentDevcontainerCard.stories.tsx @@ -31,3 +31,24 @@ export const WithPorts: Story = { }, }, }; + +export const Dirty: Story = { + args: { + container: { + ...MockWorkspaceAgentContainer, + devcontainer_dirty: true, + ports: MockWorkspaceAgentContainerPorts, + }, + }, +}; + +export const Recreating: Story = { + args: { + container: { + ...MockWorkspaceAgentContainer, + devcontainer_dirty: true, + devcontainer_status: "starting", + ports: MockWorkspaceAgentContainerPorts, + }, + }, +}; diff --git a/site/src/modules/resources/AgentDevcontainerCard.tsx b/site/src/modules/resources/AgentDevcontainerCard.tsx index 543004de5c1e2..4891c632bbc2a 100644 --- a/site/src/modules/resources/AgentDevcontainerCard.tsx +++ b/site/src/modules/resources/AgentDevcontainerCard.tsx @@ -3,14 +3,24 @@ import type { WorkspaceAgent, WorkspaceAgentContainer, } from "api/typesGenerated"; +import { Button } from "components/Button/Button"; +import { displayError } from "components/GlobalSnackbar/utils"; +import { + HelpTooltip, + HelpTooltipContent, + HelpTooltipText, + HelpTooltipTitle, + HelpTooltipTrigger, +} from "components/HelpTooltip/HelpTooltip"; import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, } from "components/Tooltip/Tooltip"; -import { ExternalLinkIcon } from "lucide-react"; +import { ExternalLinkIcon, Loader2Icon } from "lucide-react"; import type { FC } from "react"; +import { useEffect, useState } from "react"; import { portForwardURL } from "utils/portForward"; import { AgentButton } from "./AgentButton"; import { AgentDevcontainerSSHButton } from "./SSHButton/SSHButton"; @@ -32,24 +42,103 @@ export const AgentDevcontainerCard: FC = ({ }) => { const folderPath = container.labels["devcontainer.local_folder"]; const containerFolder = container.volumes[folderPath]; + const [isRecreating, setIsRecreating] = useState(false); + + const handleRecreateDevcontainer = async () => { + setIsRecreating(true); + let recreateSucceeded = false; + try { + const response = await fetch( + `/api/v2/workspaceagents/${agent.id}/containers/devcontainers/container/${container.id}/recreate`, + { + method: "POST", + }, + ); + if (!response.ok) { + const errorData = await response.json().catch(() => ({})); + throw new Error( + errorData.message || `Failed to recreate: ${response.statusText}`, + ); + } + // If the request was accepted (e.g. 202), we mark it as succeeded. + // Once complete, the component will unmount, so the spinner will + // disappear with it. + if (response.status === 202) { + recreateSucceeded = true; + } + } catch (error) { + const errorMessage = + error instanceof Error ? error.message : "An unknown error occurred."; + displayError(`Failed to recreate devcontainer: ${errorMessage}`); + console.error("Failed to recreate devcontainer:", error); + } finally { + if (!recreateSucceeded) { + setIsRecreating(false); + } + } + }; + + // If the container is starting, reflect this in the recreate button. + useEffect(() => { + if (container.devcontainer_status === "starting") { + setIsRecreating(true); + } else { + setIsRecreating(false); + } + }, [container.devcontainer_status]); return (
-
-

- {container.name} -

+
+
+

+ {container.name} +

+ {container.devcontainer_dirty && ( + + + Outdated + + + Devcontainer Outdated + + Devcontainer configuration has been modified and is outdated. + Recreate to get an up-to-date container. + + + + )} +
- +
+ + + +
-

Forwarded ports

+

Forwarded ports

{ +describe.skip("useAgentLogs", () => { afterEach(() => { WS.clean(); }); diff --git a/site/src/modules/workspaces/WorkspaceTiming/Chart/utils.ts b/site/src/modules/workspaces/WorkspaceTiming/Chart/utils.ts index 2790701db5265..f125b8d1387f1 100644 --- a/site/src/modules/workspaces/WorkspaceTiming/Chart/utils.ts +++ b/site/src/modules/workspaces/WorkspaceTiming/Chart/utils.ts @@ -1,3 +1,5 @@ +import dayjs from "dayjs"; + export type TimeRange = { startedAt: Date; endedAt: Date; @@ -11,12 +13,26 @@ export const mergeTimeRanges = (ranges: TimeRange[]): TimeRange => { const sortedDurations = ranges .slice() .sort((a, b) => a.startedAt.getTime() - b.startedAt.getTime()); - const start = sortedDurations[0].startedAt; const sortedEndDurations = [...ranges].sort( (a, b) => a.endedAt.getTime() - b.endedAt.getTime(), ); const end = sortedEndDurations[sortedEndDurations.length - 1].endedAt; + + // Ref: #15432: if there start time is the 'zero' value, default + // to the end time. This will result in an 'instant'. + let start: Date = end; + for (const r of sortedDurations) { + if ( + Number.isNaN(r.startedAt.getTime()) || + r.startedAt.getUTCFullYear() <= 1 + ) { + continue; // Skip invalid start dates. + } + start = r.startedAt; + break; + } + return { startedAt: start, endedAt: end }; }; @@ -33,7 +49,12 @@ const second = 1_000; const minute = 60 * second; const hour = 60 * minute; const day = 24 * hour; +const week = 7 * day; +const year = 365 * day; // Unlikely, and leap years won't matter here. + const scales = [ + year, + week, day, hour, 5 * minute, @@ -44,6 +65,8 @@ const scales = [ 100, ]; +const zeroTime: Date = dayjs("0001-01-01T00:00:00Z").toDate(); + const pickScale = (totalTime: number): number => { for (const s of scales) { if (totalTime > s) { @@ -64,6 +87,7 @@ export const formatTime = (time: number): string => { const absTime = Math.abs(time); let unit = ""; let value = 0; + let frac = 2; if (absTime < second) { value = time; @@ -74,15 +98,26 @@ export const formatTime = (time: number): string => { } else if (absTime < hour) { value = time / minute; unit = "m"; + frac = 1; } else if (absTime < day) { value = time / hour; unit = "h"; - } else { + frac = 0; + } else if (absTime < week) { value = time / day; unit = "d"; + frac = 0; + } else if (absTime < year) { + value = time / week; + unit = "w"; + frac = 0; + } else { + value = time / year; + unit = "y"; + frac = 0; } return `${value.toLocaleString(undefined, { - maximumFractionDigits: 2, + maximumFractionDigits: frac, })}${unit}`; }; diff --git a/site/src/modules/workspaces/WorkspaceTiming/StagesChart.tsx b/site/src/modules/workspaces/WorkspaceTiming/StagesChart.tsx index 32e87f9ea5b76..c9e9f8d3a71b2 100644 --- a/site/src/modules/workspaces/WorkspaceTiming/StagesChart.tsx +++ b/site/src/modules/workspaces/WorkspaceTiming/StagesChart.tsx @@ -141,6 +141,7 @@ export const StagesChart: FC = ({ const value = calcDuration(t.range); const offset = calcOffset(t.range, totalRange); + const validDuration = value > 0 && !Number.isNaN(value); return ( = ({ ) : ( )} - {formatTime(calcDuration(t.range))} + {validDuration ? ( + {formatTime(value)} + ) : ( + ({ + color: theme.palette.error.main, + })} + > + Invalid + + )} ); })} diff --git a/site/src/modules/workspaces/WorkspaceTiming/WorkspaceTimings.stories.tsx b/site/src/modules/workspaces/WorkspaceTiming/WorkspaceTimings.stories.tsx index c2d1193d37fc1..36f08b36c0ca0 100644 --- a/site/src/modules/workspaces/WorkspaceTiming/WorkspaceTimings.stories.tsx +++ b/site/src/modules/workspaces/WorkspaceTiming/WorkspaceTimings.stories.tsx @@ -228,3 +228,52 @@ export const MissedAction: Story = { await canvas.findByText("missed action"); }, }; + +// Ref: #15432 +export const InvalidTimeRange: Story = { + args: { + provisionerTimings: [ + { + ...WorkspaceTimingsResponse.provisioner_timings[0], + stage: "init", + started_at: "2025-01-01T00:00:00Z", + ended_at: "2025-01-01T00:01:00Z", + }, + { + ...WorkspaceTimingsResponse.provisioner_timings[0], + stage: "plan", + started_at: "2025-01-01T00:01:00Z", + ended_at: "0001-01-01T00:00:00Z", + }, + { + ...WorkspaceTimingsResponse.provisioner_timings[0], + stage: "graph", + started_at: "0001-01-01T00:00:00Z", + ended_at: "2025-01-01T00:03:00Z", + }, + { + ...WorkspaceTimingsResponse.provisioner_timings[0], + stage: "apply", + started_at: "2025-01-01T00:03:00Z", + ended_at: "2025-01-01T00:04:00Z", + }, + ], + agentConnectionTimings: [ + { + started_at: "2025-01-01T00:05:00Z", + ended_at: "2025-01-01T00:06:00Z", + stage: "connect", + workspace_agent_id: "67e37a9d-ccac-497e-8f48-4093bcc4f3e7", + workspace_agent_name: "main", + }, + ], + agentScriptTimings: [ + { + ...WorkspaceTimingsResponse.agent_script_timings[0], + display_name: "Startup Script 1", + started_at: "0001-01-01T00:00:00Z", + ended_at: "2025-01-01T00:10:00Z", + }, + ], + }, +}; diff --git a/site/src/pages/TasksPage/TasksPage.stories.tsx b/site/src/pages/TasksPage/TasksPage.stories.tsx new file mode 100644 index 0000000000000..4dee0e7fe32a9 --- /dev/null +++ b/site/src/pages/TasksPage/TasksPage.stories.tsx @@ -0,0 +1,191 @@ +import type { Meta, StoryObj } from "@storybook/react"; +import { expect, spyOn, userEvent, within } from "@storybook/test"; +import { + MockTemplate, + MockUserOwner, + MockWorkspace, + MockWorkspaceAppStatus, + mockApiError, +} from "testHelpers/entities"; +import { + withAuthProvider, + withGlobalSnackbar, + withProxyProvider, +} from "testHelpers/storybook"; +import TasksPage, { data } from "./TasksPage"; + +const meta: Meta = { + title: "pages/TasksPage", + component: TasksPage, + decorators: [withAuthProvider], + parameters: { + user: MockUserOwner, + }, +}; + +export default meta; +type Story = StoryObj; + +export const LoadingAITemplates: Story = { + beforeEach: () => { + spyOn(data, "fetchAITemplates").mockImplementation( + () => new Promise((res) => 1000 * 60 * 60), + ); + }, +}; + +export const LoadingAITemplatesError: Story = { + beforeEach: () => { + spyOn(data, "fetchAITemplates").mockRejectedValue( + mockApiError({ + message: "Failed to load AI templates", + detail: "You don't have permission to access this resource.", + }), + ); + }, +}; + +export const EmptyAITemplates: Story = { + beforeEach: () => { + spyOn(data, "fetchAITemplates").mockResolvedValue([]); + }, +}; + +export const LoadingTasks: Story = { + beforeEach: () => { + spyOn(data, "fetchAITemplates").mockResolvedValue([MockTemplate]); + spyOn(data, "fetchTasks").mockImplementation( + () => new Promise((res) => 1000 * 60 * 60), + ); + }, + play: async ({ canvasElement, step }) => { + const canvas = within(canvasElement); + + await step("Select the first AI template", async () => { + const combobox = await canvas.findByRole("combobox"); + expect(combobox).toHaveTextContent(MockTemplate.display_name); + }); + }, +}; + +export const LoadingTasksError: Story = { + beforeEach: () => { + spyOn(data, "fetchAITemplates").mockResolvedValue([MockTemplate]); + spyOn(data, "fetchTasks").mockRejectedValue( + mockApiError({ + message: "Failed to load tasks", + }), + ); + }, +}; + +export const EmptyTasks: Story = { + beforeEach: () => { + spyOn(data, "fetchAITemplates").mockResolvedValue([MockTemplate]); + spyOn(data, "fetchTasks").mockResolvedValue([]); + }, +}; + +export const LoadedTasks: Story = { + decorators: [withProxyProvider()], + beforeEach: () => { + spyOn(data, "fetchAITemplates").mockResolvedValue([MockTemplate]); + spyOn(data, "fetchTasks").mockResolvedValue(MockTasks); + }, +}; + +export const CreateTaskSuccessfully: Story = { + decorators: [withProxyProvider()], + beforeEach: () => { + spyOn(data, "fetchAITemplates").mockResolvedValue([MockTemplate]); + spyOn(data, "fetchTasks").mockResolvedValue(MockTasks); + spyOn(data, "createTask").mockImplementation((prompt: string) => { + return Promise.resolve({ + prompt, + workspace: { + ...MockWorkspace, + latest_app_status: { + ...MockWorkspaceAppStatus, + message: "Task created successfully!", + }, + }, + }); + }); + }, + play: async ({ canvasElement, step }) => { + const canvas = within(canvasElement); + + await step("Run task", async () => { + const prompt = await canvas.findByLabelText(/prompt/i); + await userEvent.type(prompt, "Create a new task"); + const submitButton = canvas.getByRole("button", { name: /run task/i }); + await userEvent.click(submitButton); + }); + + await step("Verify task in the table", async () => { + await canvas.findByRole("row", { + name: /create a new task/i, + }); + }); + }, +}; + +export const CreateTaskError: Story = { + decorators: [withProxyProvider(), withGlobalSnackbar], + beforeEach: () => { + spyOn(data, "fetchAITemplates").mockResolvedValue([MockTemplate]); + spyOn(data, "fetchTasks").mockResolvedValue(MockTasks); + spyOn(data, "createTask").mockRejectedValue( + mockApiError({ + message: "Failed to create task", + detail: "You don't have permission to create tasks.", + }), + ); + }, + play: async ({ canvasElement, step }) => { + const canvas = within(canvasElement); + + await step("Run task", async () => { + const prompt = await canvas.findByLabelText(/prompt/i); + await userEvent.type(prompt, "Create a new task"); + const submitButton = canvas.getByRole("button", { name: /run task/i }); + await userEvent.click(submitButton); + }); + + await step("Verify error", async () => { + await canvas.findByText(/failed to create task/i); + }); + }, +}; + +const MockTasks = [ + { + workspace: { + ...MockWorkspace, + latest_app_status: MockWorkspaceAppStatus, + }, + prompt: "Create competitors page", + }, + { + workspace: { + ...MockWorkspace, + id: "workspace-2", + latest_app_status: { + ...MockWorkspaceAppStatus, + message: "Avatar size fixed!", + }, + }, + prompt: "Fix user avatar size", + }, + { + workspace: { + ...MockWorkspace, + id: "workspace-3", + latest_app_status: { + ...MockWorkspaceAppStatus, + message: "Accessibility issues fixed!", + }, + }, + prompt: "Fix accessibility issues", + }, +]; diff --git a/site/src/pages/TasksPage/TasksPage.tsx b/site/src/pages/TasksPage/TasksPage.tsx new file mode 100644 index 0000000000000..b1adcd790fe75 --- /dev/null +++ b/site/src/pages/TasksPage/TasksPage.tsx @@ -0,0 +1,539 @@ +import { API } from "api/api"; +import { getErrorDetail, getErrorMessage } from "api/errors"; +import { disabledRefetchOptions } from "api/queries/util"; +import type { + Template, + Workspace, + WorkspaceAgent, + WorkspaceApp, +} from "api/typesGenerated"; +import { Avatar } from "components/Avatar/Avatar"; +import { AvatarData } from "components/Avatar/AvatarData"; +import { Button } from "components/Button/Button"; +import { ExternalImage } from "components/ExternalImage/ExternalImage"; +import { displayError } from "components/GlobalSnackbar/utils"; +import { Margins } from "components/Margins/Margins"; +import { + PageHeader, + PageHeaderSubtitle, + PageHeaderTitle, +} from "components/PageHeader/PageHeader"; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "components/Select/Select"; +import { Spinner } from "components/Spinner/Spinner"; +import { + Table, + TableBody, + TableCell, + TableHead, + TableHeader, + TableRow, +} from "components/Table/Table"; +import { + Tooltip, + TooltipContent, + TooltipProvider, + TooltipTrigger, +} from "components/Tooltip/Tooltip"; +import { useAuthenticated } from "hooks"; +import { ExternalLinkIcon, RotateCcwIcon, SendIcon } from "lucide-react"; +import { useAppLink } from "modules/apps/useAppLink"; +import { WorkspaceAppStatus } from "modules/workspaces/WorkspaceAppStatus/WorkspaceAppStatus"; +import type { FC, PropsWithChildren, ReactNode } from "react"; +import { Helmet } from "react-helmet-async"; +import { useMutation, useQuery, useQueryClient } from "react-query"; +import { pageTitle } from "utils/page"; +import { relativeTime } from "utils/time"; + +const TasksPage: FC = () => { + const { + data: templates, + error, + refetch, + } = useQuery({ + queryKey: ["templates", "ai"], + queryFn: data.fetchAITemplates, + ...disabledRefetchOptions, + }); + + let content: ReactNode = null; + + if (error) { + const message = getErrorMessage(error, "Error loading AI templates"); + const detail = getErrorDetail(error) ?? "Please, try again"; + + content = ( +
+
+

+ {message} +

+ {detail} + +
+
+ ); + } else if (templates) { + content = + templates.length === 0 ? ( +
+
+

+ No AI templates found +

+ + Create an AI template to get started + + +
+
+ ) : ( + <> + + + + ); + } else { + content = ( +
+
+ +

+ Loading AI templates +

+ + This might take a few minutes + +
+
+ ); + } + + return ( + <> + + {pageTitle("AI Tasks")} + + + + + Read the docs + + } + > + Tasks + Automate tasks with AI + + + {content} + + + ); +}; + +type CreateTaskMutationFnProps = { prompt: string; templateId: string }; + +type TaskFormProps = { + templates: Template[]; +}; + +const TaskForm: FC = ({ templates }) => { + const { user } = useAuthenticated(); + const queryClient = useQueryClient(); + + const createTaskMutation = useMutation({ + mutationFn: async ({ prompt, templateId }: CreateTaskMutationFnProps) => + data.createTask(prompt, user.id, templateId), + onSuccess: (newTask) => { + // The current data loading is heavy, so we manually update the cache to + // avoid re-fetching. Once we improve data loading, we can replace the + // manual update with queryClient.invalidateQueries. + queryClient.setQueryData(["tasks"], (oldTasks = []) => { + return [newTask, ...oldTasks]; + }); + }, + }); + + const onSubmit = async (e: React.FormEvent) => { + e.preventDefault(); + + const form = e.currentTarget; + const formData = new FormData(form); + const prompt = formData.get("prompt") as string; + const templateID = formData.get("templateID") as string; + + if (!prompt || !templateID) { + return; + } + + try { + await createTaskMutation.mutateAsync({ + prompt, + templateId: templateID, + }); + form.reset(); + } catch (error) { + const message = getErrorMessage(error, "Error creating task"); + const detail = getErrorDetail(error) ?? "Please, try again"; + displayError(message, detail); + } + }; + + return ( +
+
+ +