Skip to content

Commit 6454ffe

Browse files
committed
chore: fix conflict
Signed-off-by: Danny Kopping <dannykopping@gmail.com>
2 parents 1aba76b + 4699393 commit 6454ffe

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

54 files changed

+2216
-558
lines changed

.github/workflows/ci.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -902,7 +902,7 @@ jobs:
902902
# the check to pass. This is desired in PRs, but not in mainline.
903903
- name: Publish to Chromatic (non-mainline)
904904
if: github.ref != 'refs/heads/main' && github.repository_owner == 'coder'
905-
uses: chromaui/action@c50adf8eaa8c2878af3263499a73077854de39d4 # v12.2.0
905+
uses: chromaui/action@b5848056bb67ce5f1cccca8e62a37cbd9dd42871 # v13.0.1
906906
env:
907907
NODE_OPTIONS: "--max_old_space_size=4096"
908908
STORYBOOK: true
@@ -934,7 +934,7 @@ jobs:
934934
# infinitely "in progress" in mainline unless we re-review each build.
935935
- name: Publish to Chromatic (mainline)
936936
if: github.ref == 'refs/heads/main' && github.repository_owner == 'coder'
937-
uses: chromaui/action@c50adf8eaa8c2878af3263499a73077854de39d4 # v12.2.0
937+
uses: chromaui/action@b5848056bb67ce5f1cccca8e62a37cbd9dd42871 # v13.0.1
938938
env:
939939
NODE_OPTIONS: "--max_old_space_size=4096"
940940
STORYBOOK: true

.github/workflows/docker-base.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ jobs:
6060

6161
# This uses OIDC authentication, so no auth variables are required.
6262
- name: Build base Docker image via depot.dev
63-
uses: depot/build-push-action@636daae76684e38c301daa0c5eca1c095b24e780 # v1.14.0
63+
uses: depot/build-push-action@2583627a84956d07561420dcc1d0eb1f2af3fac0 # v1.15.0
6464
with:
6565
project: wl5hnrrkns
6666
context: base-build-context

.github/workflows/docs-ci.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ jobs:
2828
- name: Setup Node
2929
uses: ./.github/actions/setup-node
3030

31-
- uses: tj-actions/changed-files@d52d20fa3f981cb852b861fd8f55308b5fe29637 # v45.0.7
31+
- uses: tj-actions/changed-files@666c9d29007687c52e3c7aa2aac6c0ffcadeadc3 # v45.0.7
3232
id: changed-files
3333
with:
3434
files: |

.github/workflows/dogfood.yaml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ jobs:
3535
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
3636

3737
- name: Setup Nix
38-
uses: nixbuild/nix-quick-install-action@5bb6a3b3abe66fd09bbf250dce8ada94f856a703 # v30
38+
uses: nixbuild/nix-quick-install-action@889f3180bb5f064ee9e3201428d04ae9e41d54ad # v31
3939

4040
- uses: nix-community/cache-nix-action@135667ec418502fa5a3598af6fb9eb733888ce6a # v6.1.3
4141
with:
@@ -72,7 +72,7 @@ jobs:
7272
uses: depot/setup-action@b0b1ea4f69e92ebf5dea3f8713a1b0c37b2126a5 # v1.6.0
7373

7474
- name: Set up Docker Buildx
75-
uses: docker/setup-buildx-action@b5ca514318bd6ebac0fb2aedd5d36ec1b5c232a2 # v3.10.0
75+
uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1
7676

7777
- name: Login to DockerHub
7878
if: github.ref == 'refs/heads/main'
@@ -82,7 +82,7 @@ jobs:
8282
password: ${{ secrets.DOCKERHUB_PASSWORD }}
8383

8484
- name: Build and push Non-Nix image
85-
uses: depot/build-push-action@636daae76684e38c301daa0c5eca1c095b24e780 # v1.14.0
85+
uses: depot/build-push-action@2583627a84956d07561420dcc1d0eb1f2af3fac0 # v1.15.0
8686
with:
8787
project: b4q6ltmpzh
8888
token: ${{ secrets.DEPOT_TOKEN }}

.github/workflows/release.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -364,7 +364,7 @@ jobs:
364364
# This uses OIDC authentication, so no auth variables are required.
365365
- name: Build base Docker image via depot.dev
366366
if: steps.image-base-tag.outputs.tag != ''
367-
uses: depot/build-push-action@636daae76684e38c301daa0c5eca1c095b24e780 # v1.14.0
367+
uses: depot/build-push-action@2583627a84956d07561420dcc1d0eb1f2af3fac0 # v1.15.0
368368
with:
369369
project: wl5hnrrkns
370370
context: base-build-context

agent/agentcontainers/api.go

Lines changed: 40 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"os"
99
"path"
1010
"path/filepath"
11+
"regexp"
1112
"runtime"
1213
"slices"
1314
"strings"
@@ -39,6 +40,8 @@ const (
3940
// by tmpfs or other mounts. This assumes the container root filesystem is
4041
// read-write, which seems sensible for devcontainers.
4142
coderPathInsideContainer = "/.coder-agent/coder"
43+
44+
maxAgentNameLength = 64
4245
)
4346

4447
// API is responsible for container-related operations in the agent.
@@ -583,10 +586,11 @@ func (api *API) processUpdatedContainersLocked(ctx context.Context, updated code
583586
if dc.Container != nil {
584587
if !api.devcontainerNames[dc.Name] {
585588
// If the devcontainer name wasn't set via terraform, we
586-
// use the containers friendly name as a fallback which
587-
// will keep changing as the devcontainer is recreated.
588-
// TODO(mafredri): Parse the container label (i.e. devcontainer.json) for customization.
589-
dc.Name = safeFriendlyName(dc.Container.FriendlyName)
589+
// will attempt to create an agent name based on the workspace
590+
// folder's name. If it is not possible to generate a valid
591+
// agent name based off of the folder name (i.e. no valid characters),
592+
// we will instead fall back to using the container's friendly name.
593+
dc.Name = safeAgentName(path.Base(filepath.ToSlash(dc.WorkspaceFolder)), dc.Container.FriendlyName)
590594
}
591595
}
592596

@@ -631,6 +635,38 @@ func (api *API) processUpdatedContainersLocked(ctx context.Context, updated code
631635
api.containersErr = nil
632636
}
633637

638+
var consecutiveHyphenRegex = regexp.MustCompile("-+")
639+
640+
// `safeAgentName` returns a safe agent name derived from a folder name,
641+
// falling back to the container’s friendly name if needed.
642+
func safeAgentName(name string, friendlyName string) string {
643+
// Keep only ASCII letters and digits, replacing everything
644+
// else with a hyphen.
645+
var sb strings.Builder
646+
for _, r := range strings.ToLower(name) {
647+
if (r >= 'a' && r <= 'z') || (r >= '0' && r <= '9') {
648+
_, _ = sb.WriteRune(r)
649+
} else {
650+
_, _ = sb.WriteRune('-')
651+
}
652+
}
653+
654+
// Remove any consecutive hyphens, and then trim any leading
655+
// and trailing hyphens.
656+
name = consecutiveHyphenRegex.ReplaceAllString(sb.String(), "-")
657+
name = strings.Trim(name, "-")
658+
659+
// Ensure the name of the agent doesn't exceed the maximum agent
660+
// name length.
661+
name = name[:min(len(name), maxAgentNameLength)]
662+
663+
if provisioner.AgentNameRegex.Match([]byte(name)) {
664+
return name
665+
}
666+
667+
return safeFriendlyName(friendlyName)
668+
}
669+
634670
// safeFriendlyName returns a API safe version of the container's
635671
// friendly name.
636672
//
Lines changed: 201 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,201 @@
1+
package agentcontainers
2+
3+
import (
4+
"testing"
5+
6+
"github.com/stretchr/testify/assert"
7+
8+
"github.com/coder/coder/v2/provisioner"
9+
)
10+
11+
func TestSafeAgentName(t *testing.T) {
12+
t.Parallel()
13+
14+
tests := []struct {
15+
name string
16+
folderName string
17+
expected string
18+
}{
19+
// Basic valid names
20+
{
21+
folderName: "simple",
22+
expected: "simple",
23+
},
24+
{
25+
folderName: "with-hyphens",
26+
expected: "with-hyphens",
27+
},
28+
{
29+
folderName: "123numbers",
30+
expected: "123numbers",
31+
},
32+
{
33+
folderName: "mixed123",
34+
expected: "mixed123",
35+
},
36+
37+
// Names that need transformation
38+
{
39+
folderName: "With_Underscores",
40+
expected: "with-underscores",
41+
},
42+
{
43+
folderName: "With Spaces",
44+
expected: "with-spaces",
45+
},
46+
{
47+
folderName: "UPPERCASE",
48+
expected: "uppercase",
49+
},
50+
{
51+
folderName: "Mixed_Case-Name",
52+
expected: "mixed-case-name",
53+
},
54+
55+
// Names with special characters that get replaced
56+
{
57+
folderName: "special@#$chars",
58+
expected: "special-chars",
59+
},
60+
{
61+
folderName: "dots.and.more",
62+
expected: "dots-and-more",
63+
},
64+
{
65+
folderName: "multiple___underscores",
66+
expected: "multiple-underscores",
67+
},
68+
{
69+
folderName: "multiple---hyphens",
70+
expected: "multiple-hyphens",
71+
},
72+
73+
// Edge cases with leading/trailing special chars
74+
{
75+
folderName: "-leading-hyphen",
76+
expected: "leading-hyphen",
77+
},
78+
{
79+
folderName: "trailing-hyphen-",
80+
expected: "trailing-hyphen",
81+
},
82+
{
83+
folderName: "_leading_underscore",
84+
expected: "leading-underscore",
85+
},
86+
{
87+
folderName: "trailing_underscore_",
88+
expected: "trailing-underscore",
89+
},
90+
{
91+
folderName: "---multiple-leading",
92+
expected: "multiple-leading",
93+
},
94+
{
95+
folderName: "trailing-multiple---",
96+
expected: "trailing-multiple",
97+
},
98+
99+
// Complex transformation cases
100+
{
101+
folderName: "___very---complex@@@name___",
102+
expected: "very-complex-name",
103+
},
104+
{
105+
folderName: "my.project-folder_v2",
106+
expected: "my-project-folder-v2",
107+
},
108+
109+
// Empty and fallback cases - now correctly uses friendlyName fallback
110+
{
111+
folderName: "",
112+
expected: "friendly-fallback",
113+
},
114+
{
115+
folderName: "---",
116+
expected: "friendly-fallback",
117+
},
118+
{
119+
folderName: "___",
120+
expected: "friendly-fallback",
121+
},
122+
{
123+
folderName: "@#$",
124+
expected: "friendly-fallback",
125+
},
126+
127+
// Additional edge cases
128+
{
129+
folderName: "a",
130+
expected: "a",
131+
},
132+
{
133+
folderName: "1",
134+
expected: "1",
135+
},
136+
{
137+
folderName: "a1b2c3",
138+
expected: "a1b2c3",
139+
},
140+
{
141+
folderName: "CamelCase",
142+
expected: "camelcase",
143+
},
144+
{
145+
folderName: "snake_case_name",
146+
expected: "snake-case-name",
147+
},
148+
{
149+
folderName: "kebab-case-name",
150+
expected: "kebab-case-name",
151+
},
152+
{
153+
folderName: "mix3d_C4s3-N4m3",
154+
expected: "mix3d-c4s3-n4m3",
155+
},
156+
{
157+
folderName: "123-456-789",
158+
expected: "123-456-789",
159+
},
160+
{
161+
folderName: "abc123def456",
162+
expected: "abc123def456",
163+
},
164+
{
165+
folderName: " spaces everywhere ",
166+
expected: "spaces-everywhere",
167+
},
168+
{
169+
folderName: "unicode-café-naïve",
170+
expected: "unicode-caf-na-ve",
171+
},
172+
{
173+
folderName: "path/with/slashes",
174+
expected: "path-with-slashes",
175+
},
176+
{
177+
folderName: "file.tar.gz",
178+
expected: "file-tar-gz",
179+
},
180+
{
181+
folderName: "version-1.2.3-alpha",
182+
expected: "version-1-2-3-alpha",
183+
},
184+
185+
// Truncation test for names exceeding 64 characters
186+
{
187+
folderName: "this-is-a-very-long-folder-name-that-exceeds-sixty-four-characters-limit-and-should-be-truncated",
188+
expected: "this-is-a-very-long-folder-name-that-exceeds-sixty-four-characte",
189+
},
190+
}
191+
192+
for _, tt := range tests {
193+
t.Run(tt.folderName, func(t *testing.T) {
194+
t.Parallel()
195+
name := safeAgentName(tt.folderName, "friendly-fallback")
196+
197+
assert.Equal(t, tt.expected, name)
198+
assert.True(t, provisioner.AgentNameRegex.Match([]byte(name)))
199+
})
200+
}
201+
}

0 commit comments

Comments
 (0)