Skip to content

Commit 7f22501

Browse files
committed
Merge branch 'main' of github.com:/coder/coder into dk/prebuilds
Signed-off-by: Danny Kopping <dannykopping@gmail.com>
2 parents d36051f + 4b1da9b commit 7f22501

File tree

324 files changed

+3266
-1586
lines changed

Some content is hidden

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

324 files changed

+3266
-1586
lines changed

.github/ISSUE_TEMPLATE/1-bug.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
name: "🐞 Bug"
22
description: "File a bug report."
3-
title: "<title>"
3+
title: "bug: "
44
labels: ["needs-triage"]
55
body:
66
- type: checkboxes

.github/actions/setup-tf/action.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,5 @@ runs:
77
- name: Install Terraform
88
uses: hashicorp/setup-terraform@b9cd54a3c349d3f38e8881555d616ced269862dd # v3.1.2
99
with:
10-
terraform_version: 1.10.5
10+
terraform_version: 1.11.0
1111
terraform_wrapper: false

.github/workflows/ci.yaml

+4-4
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,7 @@ jobs:
178178
echo "LINT_CACHE_DIR=$dir" >> $GITHUB_ENV
179179
180180
- name: golangci-lint cache
181-
uses: actions/cache@0c907a75c2c80ebcb7f088228285e798b750cf8f # v4.2.1
181+
uses: actions/cache@d4323d4df104b026a6aa633fdb11d772146be0bf # v4.2.2
182182
with:
183183
path: |
184184
${{ env.LINT_CACHE_DIR }}
@@ -188,7 +188,7 @@ jobs:
188188
189189
# Check for any typos
190190
- name: Check for typos
191-
uses: crate-ci/typos@212923e4ff05b7fc2294a204405eec047b807138 # v1.29.9
191+
uses: crate-ci/typos@db35ee91e80fbb447f33b0e5fbddb24d2a1a884f # v1.29.10
192192
with:
193193
config: .github/workflows/typos.toml
194194

@@ -1092,7 +1092,7 @@ jobs:
10921092
uses: google-github-actions/setup-gcloud@77e7a554d41e2ee56fc945c52dfd3f33d12def9a # v2.1.4
10931093

10941094
- name: Download dylibs
1095-
uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8
1095+
uses: actions/download-artifact@cc203385981b70ca67e1cc392babf9cc229d5806 # v4.1.9
10961096
with:
10971097
name: dylibs
10981098
path: ./build
@@ -1236,7 +1236,7 @@ jobs:
12361236
version: "2.5.1"
12371237

12381238
- name: Get Cluster Credentials
1239-
uses: google-github-actions/get-gke-credentials@7a108e64ed8546fe38316b4086e91da13f4785e1 # v2.3.1
1239+
uses: google-github-actions/get-gke-credentials@d0cee45012069b163a631894b98904a9e6723729 # v2.3.3
12401240
with:
12411241
cluster_name: dogfood-v2
12421242
location: us-central1-a

.github/workflows/dogfood.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ jobs:
5353
uses: depot/setup-action@b0b1ea4f69e92ebf5dea3f8713a1b0c37b2126a5 # v1.6.0
5454

5555
- name: Set up Docker Buildx
56-
uses: docker/setup-buildx-action@f7ce87c1d6bead3e36075b2ce75da1f6cc28aaca # v3.9.0
56+
uses: docker/setup-buildx-action@b5ca514318bd6ebac0fb2aedd5d36ec1b5c232a2 # v3.10.0
5757

5858
- name: Login to DockerHub
5959
if: github.ref == 'refs/heads/main'

.github/workflows/release.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -286,7 +286,7 @@ jobs:
286286
uses: google-github-actions/setup-gcloud@77e7a554d41e2ee56fc945c52dfd3f33d12def9a # v2.1.4
287287

288288
- name: Download dylibs
289-
uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8
289+
uses: actions/download-artifact@cc203385981b70ca67e1cc392babf9cc229d5806 # v4.1.9
290290
with:
291291
name: dylibs
292292
path: ./build

.github/workflows/scorecard.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,6 @@ jobs:
4747

4848
# Upload the results to GitHub's code scanning dashboard.
4949
- name: "Upload to code-scanning"
50-
uses: github/codeql-action/upload-sarif@b56ba49b26e50535fa1e7f7db0f4f7b4bf65d80d # v3.28.10
50+
uses: github/codeql-action/upload-sarif@6bb031afdd8eb862ea3fc1848194185e076637e5 # v3.28.11
5151
with:
5252
sarif_file: results.sarif

.github/workflows/security.yaml

+3-3
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ jobs:
3838
uses: ./.github/actions/setup-go
3939

4040
- name: Initialize CodeQL
41-
uses: github/codeql-action/init@b56ba49b26e50535fa1e7f7db0f4f7b4bf65d80d # v3.28.10
41+
uses: github/codeql-action/init@6bb031afdd8eb862ea3fc1848194185e076637e5 # v3.28.11
4242
with:
4343
languages: go, javascript
4444

@@ -48,7 +48,7 @@ jobs:
4848
rm Makefile
4949
5050
- name: Perform CodeQL Analysis
51-
uses: github/codeql-action/analyze@b56ba49b26e50535fa1e7f7db0f4f7b4bf65d80d # v3.28.10
51+
uses: github/codeql-action/analyze@6bb031afdd8eb862ea3fc1848194185e076637e5 # v3.28.11
5252

5353
- name: Send Slack notification on failure
5454
if: ${{ failure() }}
@@ -144,7 +144,7 @@ jobs:
144144
severity: "CRITICAL,HIGH"
145145

146146
- name: Upload Trivy scan results to GitHub Security tab
147-
uses: github/codeql-action/upload-sarif@b56ba49b26e50535fa1e7f7db0f4f7b4bf65d80d # v3.28.10
147+
uses: github/codeql-action/upload-sarif@6bb031afdd8eb862ea3fc1848194185e076637e5 # v3.28.11
148148
with:
149149
sarif_file: trivy-results.sarif
150150
category: "Trivy"

agent/agentcontainers/containers_dockercli.go

+7-6
Original file line numberDiff line numberDiff line change
@@ -182,17 +182,18 @@ func devcontainerEnv(ctx context.Context, execer agentexec.Execer, container str
182182
if !ok {
183183
return nil, nil
184184
}
185-
meta := struct {
186-
RemoteEnv map[string]string `json:"remoteEnv"`
187-
}{}
185+
186+
meta := make([]DevContainerMeta, 0)
188187
if err := json.Unmarshal([]byte(rawMeta), &meta); err != nil {
189188
return nil, xerrors.Errorf("unmarshal devcontainer.metadata: %w", err)
190189
}
191190

192191
// The environment variables are stored in the `remoteEnv` key.
193-
env := make([]string, 0, len(meta.RemoteEnv))
194-
for k, v := range meta.RemoteEnv {
195-
env = append(env, fmt.Sprintf("%s=%s", k, v))
192+
env := make([]string, 0)
193+
for _, m := range meta {
194+
for k, v := range m.RemoteEnv {
195+
env = append(env, fmt.Sprintf("%s=%s", k, v))
196+
}
196197
}
197198
slices.Sort(env)
198199
return env, nil

agent/agentcontainers/containers_internal_test.go

+14-6
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ func TestIntegrationDocker(t *testing.T) {
5353
Cmd: []string{"sleep", "infnity"},
5454
Labels: map[string]string{
5555
"com.coder.test": testLabelValue,
56-
"devcontainer.metadata": `{"remoteEnv": {"FOO": "bar", "MULTILINE": "foo\nbar\nbaz"}}`,
56+
"devcontainer.metadata": `[{"remoteEnv": {"FOO": "bar", "MULTILINE": "foo\nbar\nbaz"}}]`,
5757
},
5858
Mounts: []string{testTempDir + ":" + testTempDir},
5959
ExposedPorts: []string{fmt.Sprintf("%d/tcp", testRandPort)},
@@ -437,38 +437,46 @@ func TestDockerEnvInfoer(t *testing.T) {
437437
}{
438438
{
439439
image: "busybox:latest",
440-
labels: map[string]string{`devcontainer.metadata`: `{"remoteEnv": {"FOO": "bar", "MULTILINE": "foo\nbar\nbaz"}}`},
440+
labels: map[string]string{`devcontainer.metadata`: `[{"remoteEnv": {"FOO": "bar", "MULTILINE": "foo\nbar\nbaz"}}]`},
441441

442442
expectedEnv: []string{"FOO=bar", "MULTILINE=foo\nbar\nbaz"},
443443
expectedUsername: "root",
444444
expectedUserShell: "/bin/sh",
445445
},
446446
{
447447
image: "busybox:latest",
448-
labels: map[string]string{`devcontainer.metadata`: `{"remoteEnv": {"FOO": "bar", "MULTILINE": "foo\nbar\nbaz"}}`},
448+
labels: map[string]string{`devcontainer.metadata`: `[{"remoteEnv": {"FOO": "bar", "MULTILINE": "foo\nbar\nbaz"}}]`},
449449
expectedEnv: []string{"FOO=bar", "MULTILINE=foo\nbar\nbaz"},
450450
containerUser: "root",
451451
expectedUsername: "root",
452452
expectedUserShell: "/bin/sh",
453453
},
454454
{
455455
image: "codercom/enterprise-minimal:ubuntu",
456-
labels: map[string]string{`devcontainer.metadata`: `{"remoteEnv": {"FOO": "bar", "MULTILINE": "foo\nbar\nbaz"}}`},
456+
labels: map[string]string{`devcontainer.metadata`: `[{"remoteEnv": {"FOO": "bar", "MULTILINE": "foo\nbar\nbaz"}}]`},
457457
expectedEnv: []string{"FOO=bar", "MULTILINE=foo\nbar\nbaz"},
458458
expectedUsername: "coder",
459459
expectedUserShell: "/bin/bash",
460460
},
461461
{
462462
image: "codercom/enterprise-minimal:ubuntu",
463-
labels: map[string]string{`devcontainer.metadata`: `{"remoteEnv": {"FOO": "bar", "MULTILINE": "foo\nbar\nbaz"}}`},
463+
labels: map[string]string{`devcontainer.metadata`: `[{"remoteEnv": {"FOO": "bar", "MULTILINE": "foo\nbar\nbaz"}}]`},
464464
expectedEnv: []string{"FOO=bar", "MULTILINE=foo\nbar\nbaz"},
465465
containerUser: "coder",
466466
expectedUsername: "coder",
467467
expectedUserShell: "/bin/bash",
468468
},
469469
{
470470
image: "codercom/enterprise-minimal:ubuntu",
471-
labels: map[string]string{`devcontainer.metadata`: `{"remoteEnv": {"FOO": "bar", "MULTILINE": "foo\nbar\nbaz"}}`},
471+
labels: map[string]string{`devcontainer.metadata`: `[{"remoteEnv": {"FOO": "bar", "MULTILINE": "foo\nbar\nbaz"}}]`},
472+
expectedEnv: []string{"FOO=bar", "MULTILINE=foo\nbar\nbaz"},
473+
containerUser: "root",
474+
expectedUsername: "root",
475+
expectedUserShell: "/bin/bash",
476+
},
477+
{
478+
image: "codercom/enterprise-minimal:ubuntu",
479+
labels: map[string]string{`devcontainer.metadata`: `[{"remoteEnv": {"FOO": "bar"}},{"remoteEnv": {"MULTILINE": "foo\nbar\nbaz"}}]`},
472480
expectedEnv: []string{"FOO=bar", "MULTILINE=foo\nbar\nbaz"},
473481
containerUser: "root",
474482
expectedUsername: "root",
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package agentcontainers
2+
3+
type DevContainerMeta struct {
4+
RemoteEnv map[string]string `json:"remoteEnv,omitempty"`
5+
}

agent/api.go

+1
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ func (a *agent) apiHandler() http.Handler {
4141
r.Get("/api/v0/containers", ch.ServeHTTP)
4242
r.Get("/api/v0/listening-ports", lp.handler)
4343
r.Get("/api/v0/netcheck", a.HandleNetcheck)
44+
r.Post("/api/v0/list-directory", a.HandleLS)
4445
r.Get("/debug/logs", a.HandleHTTPDebugLogs)
4546
r.Get("/debug/magicsock", a.HandleHTTPDebugMagicsock)
4647
r.Get("/debug/magicsock/debug-logging/{state}", a.HandleHTTPMagicsockDebugLoggingState)

agent/ls.go

+181
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
package agent
2+
3+
import (
4+
"errors"
5+
"net/http"
6+
"os"
7+
"path/filepath"
8+
"regexp"
9+
"runtime"
10+
"strings"
11+
12+
"github.com/shirou/gopsutil/v4/disk"
13+
"golang.org/x/xerrors"
14+
15+
"github.com/coder/coder/v2/coderd/httpapi"
16+
"github.com/coder/coder/v2/codersdk"
17+
)
18+
19+
var WindowsDriveRegex = regexp.MustCompile(`^[a-zA-Z]:\\$`)
20+
21+
func (*agent) HandleLS(rw http.ResponseWriter, r *http.Request) {
22+
ctx := r.Context()
23+
24+
var query LSRequest
25+
if !httpapi.Read(ctx, rw, r, &query) {
26+
return
27+
}
28+
29+
resp, err := listFiles(query)
30+
if err != nil {
31+
status := http.StatusInternalServerError
32+
switch {
33+
case errors.Is(err, os.ErrNotExist):
34+
status = http.StatusNotFound
35+
case errors.Is(err, os.ErrPermission):
36+
status = http.StatusForbidden
37+
default:
38+
}
39+
httpapi.Write(ctx, rw, status, codersdk.Response{
40+
Message: err.Error(),
41+
})
42+
return
43+
}
44+
45+
httpapi.Write(ctx, rw, http.StatusOK, resp)
46+
}
47+
48+
func listFiles(query LSRequest) (LSResponse, error) {
49+
var fullPath []string
50+
switch query.Relativity {
51+
case LSRelativityHome:
52+
home, err := os.UserHomeDir()
53+
if err != nil {
54+
return LSResponse{}, xerrors.Errorf("failed to get user home directory: %w", err)
55+
}
56+
fullPath = []string{home}
57+
case LSRelativityRoot:
58+
if runtime.GOOS == "windows" {
59+
if len(query.Path) == 0 {
60+
return listDrives()
61+
}
62+
if !WindowsDriveRegex.MatchString(query.Path[0]) {
63+
return LSResponse{}, xerrors.Errorf("invalid drive letter %q", query.Path[0])
64+
}
65+
} else {
66+
fullPath = []string{"/"}
67+
}
68+
default:
69+
return LSResponse{}, xerrors.Errorf("unsupported relativity type %q", query.Relativity)
70+
}
71+
72+
fullPath = append(fullPath, query.Path...)
73+
fullPathRelative := filepath.Join(fullPath...)
74+
absolutePathString, err := filepath.Abs(fullPathRelative)
75+
if err != nil {
76+
return LSResponse{}, xerrors.Errorf("failed to get absolute path of %q: %w", fullPathRelative, err)
77+
}
78+
79+
f, err := os.Open(absolutePathString)
80+
if err != nil {
81+
return LSResponse{}, xerrors.Errorf("failed to open directory %q: %w", absolutePathString, err)
82+
}
83+
defer f.Close()
84+
85+
stat, err := f.Stat()
86+
if err != nil {
87+
return LSResponse{}, xerrors.Errorf("failed to stat directory %q: %w", absolutePathString, err)
88+
}
89+
90+
if !stat.IsDir() {
91+
return LSResponse{}, xerrors.Errorf("path %q is not a directory", absolutePathString)
92+
}
93+
94+
// `contents` may be partially populated even if the operation fails midway.
95+
contents, _ := f.ReadDir(-1)
96+
respContents := make([]LSFile, 0, len(contents))
97+
for _, file := range contents {
98+
respContents = append(respContents, LSFile{
99+
Name: file.Name(),
100+
AbsolutePathString: filepath.Join(absolutePathString, file.Name()),
101+
IsDir: file.IsDir(),
102+
})
103+
}
104+
105+
absolutePath := pathToArray(absolutePathString)
106+
107+
return LSResponse{
108+
AbsolutePath: absolutePath,
109+
AbsolutePathString: absolutePathString,
110+
Contents: respContents,
111+
}, nil
112+
}
113+
114+
func listDrives() (LSResponse, error) {
115+
partitionStats, err := disk.Partitions(true)
116+
if err != nil {
117+
return LSResponse{}, xerrors.Errorf("failed to get partitions: %w", err)
118+
}
119+
contents := make([]LSFile, 0, len(partitionStats))
120+
for _, a := range partitionStats {
121+
// Drive letters on Windows have a trailing separator as part of their name.
122+
// i.e. `os.Open("C:")` does not work, but `os.Open("C:\\")` does.
123+
name := a.Mountpoint + string(os.PathSeparator)
124+
contents = append(contents, LSFile{
125+
Name: name,
126+
AbsolutePathString: name,
127+
IsDir: true,
128+
})
129+
}
130+
131+
return LSResponse{
132+
AbsolutePath: []string{},
133+
AbsolutePathString: "",
134+
Contents: contents,
135+
}, nil
136+
}
137+
138+
func pathToArray(path string) []string {
139+
out := strings.FieldsFunc(path, func(r rune) bool {
140+
return r == os.PathSeparator
141+
})
142+
// Drive letters on Windows have a trailing separator as part of their name.
143+
// i.e. `os.Open("C:")` does not work, but `os.Open("C:\\")` does.
144+
if runtime.GOOS == "windows" && len(out) > 0 {
145+
out[0] += string(os.PathSeparator)
146+
}
147+
return out
148+
}
149+
150+
type LSRequest struct {
151+
// e.g. [], ["repos", "coder"],
152+
Path []string `json:"path"`
153+
// Whether the supplied path is relative to the user's home directory,
154+
// or the root directory.
155+
Relativity LSRelativity `json:"relativity"`
156+
}
157+
158+
type LSResponse struct {
159+
AbsolutePath []string `json:"absolute_path"`
160+
// Returned so clients can display the full path to the user, and
161+
// copy it to configure file sync
162+
// e.g. Windows: "C:\\Users\\coder"
163+
// Linux: "/home/coder"
164+
AbsolutePathString string `json:"absolute_path_string"`
165+
Contents []LSFile `json:"contents"`
166+
}
167+
168+
type LSFile struct {
169+
Name string `json:"name"`
170+
// e.g. "C:\\Users\\coder\\hello.txt"
171+
// "/home/coder/hello.txt"
172+
AbsolutePathString string `json:"absolute_path_string"`
173+
IsDir bool `json:"is_dir"`
174+
}
175+
176+
type LSRelativity string
177+
178+
const (
179+
LSRelativityRoot LSRelativity = "root"
180+
LSRelativityHome LSRelativity = "home"
181+
)

0 commit comments

Comments
 (0)