Skip to content

Commit e6ec957

Browse files
stirbySasSwartDanielleMaywoodCopilotethanndickson
authored
Cherry-pick for release 2.25 (#19169)
Co-authored-by: Sas Swart <sas.swart.cdk@gmail.com> Co-authored-by: Danielle Maywood <danielle@themaywoods.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: Ethan <39577870+ethanndickson@users.noreply.github.com> Co-authored-by: Hugo Dutka <hugo@coder.com> Co-authored-by: Thomas Kosiewski <tk@coder.com> Co-authored-by: Cian Johnston <cian@coder.com>
1 parent f1cf81c commit e6ec957

File tree

14 files changed

+555
-28
lines changed

14 files changed

+555
-28
lines changed

.github/workflows/ci.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -340,6 +340,11 @@ jobs:
340340
- name: Disable Spotlight Indexing
341341
if: runner.os == 'macOS'
342342
run: |
343+
enabled=$(sudo mdutil -a -s | grep "Indexing enabled" | wc -l)
344+
if [ $enabled -eq 0 ]; then
345+
echo "Spotlight indexing is already disabled"
346+
exit 0
347+
fi
343348
sudo mdutil -a -i off
344349
sudo mdutil -X /
345350
sudo launchctl bootout system /System/Library/LaunchDaemons/com.apple.metadata.mds.plist

.github/workflows/release.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ jobs:
6060
- name: Switch XCode Version
6161
uses: maxim-lobanov/setup-xcode@60606e260d2fc5762a71e64e74b2174e8ea3c8bd # v1.6.0
6262
with:
63-
xcode-version: "16.0.0"
63+
xcode-version: "16.1.0"
6464

6565
- name: Setup Go
6666
uses: ./.github/actions/setup-go
@@ -655,7 +655,7 @@ jobs:
655655
detached_signature="${binary}.asc"
656656
gcloud storage cp "./site/out/bin/${binary}" "gs://releases.coder.com/coder-cli/${version}/${binary}"
657657
gcloud storage cp "./site/out/bin/${detached_signature}" "gs://releases.coder.com/coder-cli/${version}/${detached_signature}"
658-
done
658+
done
659659
660660
- name: Publish release
661661
run: |

agent/agentcontainers/api.go

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,8 @@ type API struct {
7777
subAgentURL string
7878
subAgentEnv []string
7979

80-
projectDiscovery bool // If we should perform project discovery or not.
80+
projectDiscovery bool // If we should perform project discovery or not.
81+
discoveryAutostart bool // If we should autostart discovered projects.
8182

8283
ownerName string
8384
workspaceName string
@@ -144,7 +145,8 @@ func WithCommandEnv(ce CommandEnv) Option {
144145
strings.HasPrefix(s, "CODER_AGENT_TOKEN=") ||
145146
strings.HasPrefix(s, "CODER_AGENT_AUTH=") ||
146147
strings.HasPrefix(s, "CODER_AGENT_DEVCONTAINERS_ENABLE=") ||
147-
strings.HasPrefix(s, "CODER_AGENT_DEVCONTAINERS_PROJECT_DISCOVERY_ENABLE=")
148+
strings.HasPrefix(s, "CODER_AGENT_DEVCONTAINERS_PROJECT_DISCOVERY_ENABLE=") ||
149+
strings.HasPrefix(s, "CODER_AGENT_DEVCONTAINERS_DISCOVERY_AUTOSTART_ENABLE=")
148150
})
149151
return shell, dir, env, nil
150152
}
@@ -287,6 +289,14 @@ func WithProjectDiscovery(projectDiscovery bool) Option {
287289
}
288290
}
289291

292+
// WithDiscoveryAutostart sets if the API should attempt to autostart
293+
// projects that have been discovered
294+
func WithDiscoveryAutostart(discoveryAutostart bool) Option {
295+
return func(api *API) {
296+
api.discoveryAutostart = discoveryAutostart
297+
}
298+
}
299+
290300
// ScriptLogger is an interface for sending devcontainer logs to the
291301
// controlplane.
292302
type ScriptLogger interface {
@@ -542,11 +552,13 @@ func (api *API) discoverDevcontainersInProject(projectPath string) error {
542552
Container: nil,
543553
}
544554

545-
config, err := api.dccli.ReadConfig(api.ctx, workspaceFolder, path, []string{})
546-
if err != nil {
547-
logger.Error(api.ctx, "read project configuration", slog.Error(err))
548-
} else if config.Configuration.Customizations.Coder.AutoStart {
549-
dc.Status = codersdk.WorkspaceAgentDevcontainerStatusStarting
555+
if api.discoveryAutostart {
556+
config, err := api.dccli.ReadConfig(api.ctx, workspaceFolder, path, []string{})
557+
if err != nil {
558+
logger.Error(api.ctx, "read project configuration", slog.Error(err))
559+
} else if config.Configuration.Customizations.Coder.AutoStart {
560+
dc.Status = codersdk.WorkspaceAgentDevcontainerStatusStarting
561+
}
550562
}
551563

552564
api.knownDevcontainers[workspaceFolder] = dc

agent/agentcontainers/api_test.go

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3792,6 +3792,7 @@ func TestDevcontainerDiscovery(t *testing.T) {
37923792
agentcontainers.WithContainerCLI(&fakeContainerCLI{}),
37933793
agentcontainers.WithDevcontainerCLI(mDCCLI),
37943794
agentcontainers.WithProjectDiscovery(true),
3795+
agentcontainers.WithDiscoveryAutostart(true),
37953796
)
37963797
api.Start()
37973798
defer api.Close()
@@ -3813,5 +3814,74 @@ func TestDevcontainerDiscovery(t *testing.T) {
38133814
// Then: We expect the mock infra to not fail.
38143815
})
38153816
}
3817+
3818+
t.Run("Disabled", func(t *testing.T) {
3819+
t.Parallel()
3820+
var (
3821+
ctx = testutil.Context(t, testutil.WaitShort)
3822+
logger = testutil.Logger(t)
3823+
mClock = quartz.NewMock(t)
3824+
mDCCLI = acmock.NewMockDevcontainerCLI(gomock.NewController(t))
3825+
3826+
fs = map[string]string{
3827+
"/home/coder/.git/HEAD": "",
3828+
"/home/coder/.devcontainer/devcontainer.json": "",
3829+
}
3830+
3831+
r = chi.NewRouter()
3832+
)
3833+
3834+
// We expect that neither `ReadConfig`, nor `Up` are called as we
3835+
// have explicitly disabled the agentcontainers API from attempting
3836+
// to autostart devcontainers that it discovers.
3837+
mDCCLI.EXPECT().ReadConfig(gomock.Any(),
3838+
"/home/coder",
3839+
"/home/coder/.devcontainer/devcontainer.json",
3840+
[]string{},
3841+
).Return(agentcontainers.DevcontainerConfig{
3842+
Configuration: agentcontainers.DevcontainerConfiguration{
3843+
Customizations: agentcontainers.DevcontainerCustomizations{
3844+
Coder: agentcontainers.CoderCustomization{
3845+
AutoStart: true,
3846+
},
3847+
},
3848+
},
3849+
}, nil).Times(0)
3850+
3851+
mDCCLI.EXPECT().Up(gomock.Any(),
3852+
"/home/coder",
3853+
"/home/coder/.devcontainer/devcontainer.json",
3854+
gomock.Any(),
3855+
).Return("", nil).Times(0)
3856+
3857+
api := agentcontainers.NewAPI(logger,
3858+
agentcontainers.WithClock(mClock),
3859+
agentcontainers.WithWatcher(watcher.NewNoop()),
3860+
agentcontainers.WithFileSystem(initFS(t, fs)),
3861+
agentcontainers.WithManifestInfo("owner", "workspace", "parent-agent", "/home/coder"),
3862+
agentcontainers.WithContainerCLI(&fakeContainerCLI{}),
3863+
agentcontainers.WithDevcontainerCLI(mDCCLI),
3864+
agentcontainers.WithProjectDiscovery(true),
3865+
agentcontainers.WithDiscoveryAutostart(false),
3866+
)
3867+
api.Start()
3868+
defer api.Close()
3869+
r.Mount("/", api.Routes())
3870+
3871+
// When: All expected dev containers have been found.
3872+
require.Eventuallyf(t, func() bool {
3873+
req := httptest.NewRequest(http.MethodGet, "/", nil).WithContext(ctx)
3874+
rec := httptest.NewRecorder()
3875+
r.ServeHTTP(rec, req)
3876+
3877+
got := codersdk.WorkspaceAgentListContainersResponse{}
3878+
err := json.NewDecoder(rec.Body).Decode(&got)
3879+
require.NoError(t, err)
3880+
3881+
return len(got.Devcontainers) >= 1
3882+
}, testutil.WaitShort, testutil.IntervalFast, "dev containers never found")
3883+
3884+
// Then: We expect the mock infra to not fail.
3885+
})
38163886
})
38173887
}

cli/agent.go

Lines changed: 26 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -40,23 +40,24 @@ import (
4040

4141
func (r *RootCmd) workspaceAgent() *serpent.Command {
4242
var (
43-
auth string
44-
logDir string
45-
scriptDataDir string
46-
pprofAddress string
47-
noReap bool
48-
sshMaxTimeout time.Duration
49-
tailnetListenPort int64
50-
prometheusAddress string
51-
debugAddress string
52-
slogHumanPath string
53-
slogJSONPath string
54-
slogStackdriverPath string
55-
blockFileTransfer bool
56-
agentHeaderCommand string
57-
agentHeader []string
58-
devcontainers bool
59-
devcontainerProjectDiscovery bool
43+
auth string
44+
logDir string
45+
scriptDataDir string
46+
pprofAddress string
47+
noReap bool
48+
sshMaxTimeout time.Duration
49+
tailnetListenPort int64
50+
prometheusAddress string
51+
debugAddress string
52+
slogHumanPath string
53+
slogJSONPath string
54+
slogStackdriverPath string
55+
blockFileTransfer bool
56+
agentHeaderCommand string
57+
agentHeader []string
58+
devcontainers bool
59+
devcontainerProjectDiscovery bool
60+
devcontainerDiscoveryAutostart bool
6061
)
6162
cmd := &serpent.Command{
6263
Use: "agent",
@@ -366,6 +367,7 @@ func (r *RootCmd) workspaceAgent() *serpent.Command {
366367
DevcontainerAPIOptions: []agentcontainers.Option{
367368
agentcontainers.WithSubAgentURL(r.agentURL.String()),
368369
agentcontainers.WithProjectDiscovery(devcontainerProjectDiscovery),
370+
agentcontainers.WithDiscoveryAutostart(devcontainerDiscoveryAutostart),
369371
},
370372
})
371373

@@ -519,6 +521,13 @@ func (r *RootCmd) workspaceAgent() *serpent.Command {
519521
Description: "Allow the agent to search the filesystem for devcontainer projects.",
520522
Value: serpent.BoolOf(&devcontainerProjectDiscovery),
521523
},
524+
{
525+
Flag: "devcontainers-discovery-autostart-enable",
526+
Default: "false",
527+
Env: "CODER_AGENT_DEVCONTAINERS_DISCOVERY_AUTOSTART_ENABLE",
528+
Description: "Allow the agent to autostart devcontainer projects it discovers based on their configuration.",
529+
Value: serpent.BoolOf(&devcontainerDiscoveryAutostart),
530+
},
522531
}
523532

524533
return cmd

cli/testdata/coder_agent_--help.golden

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,10 @@ OPTIONS:
3333
--debug-address string, $CODER_AGENT_DEBUG_ADDRESS (default: 127.0.0.1:2113)
3434
The bind address to serve a debug HTTP server.
3535

36+
--devcontainers-discovery-autostart-enable bool, $CODER_AGENT_DEVCONTAINERS_DISCOVERY_AUTOSTART_ENABLE (default: false)
37+
Allow the agent to autostart devcontainer projects it discovers based
38+
on their configuration.
39+
3640
--devcontainers-enable bool, $CODER_AGENT_DEVCONTAINERS_ENABLE (default: true)
3741
Allow the agent to automatically detect running devcontainers.
3842

coderd/dynamicparameters/error.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,14 @@ func tagValidationError(diags hcl.Diagnostics) *DiagnosticError {
2626
}
2727
}
2828

29+
func presetValidationError(diags hcl.Diagnostics) *DiagnosticError {
30+
return &DiagnosticError{
31+
Message: "Unable to validate presets",
32+
Diagnostics: diags,
33+
KeyedDiagnostics: make(map[string]hcl.Diagnostics),
34+
}
35+
}
36+
2937
type DiagnosticError struct {
3038
// Message is the human-readable message that will be returned to the user.
3139
Message string

coderd/dynamicparameters/presets.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package dynamicparameters
2+
3+
import (
4+
"github.com/hashicorp/hcl/v2"
5+
6+
"github.com/coder/preview"
7+
)
8+
9+
// CheckPresets extracts the preset related diagnostics from a template version preset
10+
func CheckPresets(output *preview.Output, diags hcl.Diagnostics) *DiagnosticError {
11+
de := presetValidationError(diags)
12+
if output == nil {
13+
return de
14+
}
15+
16+
presets := output.Presets
17+
for _, preset := range presets {
18+
if hcl.Diagnostics(preset.Diagnostics).HasErrors() {
19+
de.Extend(preset.Name, hcl.Diagnostics(preset.Diagnostics))
20+
}
21+
}
22+
23+
if de.HasError() {
24+
return de
25+
}
26+
27+
return nil
28+
}

coderd/dynamicparameters/tags.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@ import (
1111

1212
func CheckTags(output *preview.Output, diags hcl.Diagnostics) *DiagnosticError {
1313
de := tagValidationError(diags)
14+
if output == nil {
15+
return de
16+
}
17+
1418
failedTags := output.WorkspaceTags.UnusableTags()
1519
if len(failedTags) == 0 && !de.HasError() {
1620
return nil // No errors, all is good!

coderd/templateversions.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1822,6 +1822,14 @@ func (api *API) dynamicTemplateVersionTags(ctx context.Context, rw http.Response
18221822
return nil, false
18231823
}
18241824

1825+
// Fails early if presets are invalid to prevent downstream workspace creation errors
1826+
presetErr := dynamicparameters.CheckPresets(output, nil)
1827+
if presetErr != nil {
1828+
code, resp := presetErr.Response()
1829+
httpapi.Write(ctx, rw, code, resp)
1830+
return nil, false
1831+
}
1832+
18251833
return output.WorkspaceTags.Tags(), true
18261834
}
18271835

0 commit comments

Comments
 (0)