mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
feat: collect workspace logs in support bundles (#26694)
Add workspace-side file collection to `coder support bundle` via repeatable --workspace-file flags. The agent resolves the requested paths or globs inside the remote workspace and streams back a tar with a manifest and the collected files; nothing is read from the machine running the command. - Add POST /api/v0/bundle-files to the agent's agentfiles package. - Expand env vars in the agent's environment; paths must then be absolute or start with ~/ (the agent user's home directory). - Support ** globs and tail oversized files. - Record requested patterns, per-path errors, truncation, and the applied limits in a manifest. - Unpack the archive into the bundle under agent/workspace_files/, recording dropped entries in collection_errors.txt. - Write a manifest-only archive marking collection as unsupported for agents that predate the endpoint. - Bound collection: 64 KB request body, 10000 files, 10 MiB per file, 100 MiB total including archive overhead, 110 MiB client-side read cap, 5 minute timeout. Closes #26020
This commit is contained in:
@@ -9,8 +9,16 @@ import (
|
||||
)
|
||||
|
||||
// lexicalPath returns raw as a cleaned, absolute path with ~
|
||||
// expanded and symlinks left unresolved.
|
||||
// expanded against the current user's home and symlinks left
|
||||
// unresolved.
|
||||
func lexicalPath(raw string) (string, error) {
|
||||
return lexicalPathIn(os.UserHomeDir, raw)
|
||||
}
|
||||
|
||||
// lexicalPathIn is lexicalPath with home injected. home is called
|
||||
// only when a ~ prefix needs expanding, so an absolute path resolves
|
||||
// even when home is unavailable.
|
||||
func lexicalPathIn(home func() (string, error), raw string) (string, error) {
|
||||
raw = strings.TrimSpace(raw)
|
||||
if raw == "" {
|
||||
return "", xerrors.New("path is empty")
|
||||
@@ -18,14 +26,14 @@ func lexicalPath(raw string) (string, error) {
|
||||
|
||||
// ~user forms are intentionally unsupported.
|
||||
if raw == "~" || strings.HasPrefix(raw, "~/") {
|
||||
home, err := os.UserHomeDir()
|
||||
h, err := home()
|
||||
if err != nil {
|
||||
return "", xerrors.Errorf("expand home dir: %w", err)
|
||||
}
|
||||
if raw == "~" {
|
||||
raw = home
|
||||
raw = h
|
||||
} else {
|
||||
raw = filepath.Join(home, raw[2:])
|
||||
raw = filepath.Join(h, raw[2:])
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,7 +48,19 @@ func lexicalPath(raw string) (string, error) {
|
||||
// CanonicalizePath returns lexicalPath with symlinks resolved
|
||||
// when the target exists.
|
||||
func CanonicalizePath(raw string) (string, error) {
|
||||
cleaned, err := lexicalPath(raw)
|
||||
return resolveCanonicalPath(os.UserHomeDir, raw)
|
||||
}
|
||||
|
||||
// CanonicalizePathIn is CanonicalizePath with ~ expanded against
|
||||
// the given home directory instead of the current user's.
|
||||
func CanonicalizePathIn(home string, raw string) (string, error) {
|
||||
return resolveCanonicalPath(func() (string, error) { return home, nil }, raw)
|
||||
}
|
||||
|
||||
// resolveCanonicalPath implements both CanonicalizePath and
|
||||
// CanonicalizePathIn.
|
||||
func resolveCanonicalPath(home func() (string, error), raw string) (string, error) {
|
||||
cleaned, err := lexicalPathIn(home, raw)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
@@ -66,6 +66,17 @@ func TestCanonicalizePath_BareTildeExpandsToHome(t *testing.T) {
|
||||
require.Equal(t, want, got)
|
||||
}
|
||||
|
||||
func TestCanonicalizePathIn_ExpandsAgainstGivenHome(t *testing.T) {
|
||||
t.Parallel()
|
||||
home := testutil.TempDirResolved(t)
|
||||
got, err := agentcontext.CanonicalizePathIn(home, "~/.coder")
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, filepath.Join(home, ".coder"), got)
|
||||
|
||||
_, err = agentcontext.CanonicalizePathIn(home, "relative/path")
|
||||
require.Error(t, err)
|
||||
}
|
||||
|
||||
func TestCanonicalizePath_FollowsSymlinks(t *testing.T) {
|
||||
t.Parallel()
|
||||
if runtime.GOOS == "windows" {
|
||||
|
||||
Reference in New Issue
Block a user