Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion cmd/workspace/up/up.go
Original file line number Diff line number Diff line change
Expand Up @@ -515,16 +515,20 @@ func reportErr(err error, _ bool, _ io.Writer) error {
// emitUpResult writes the JSON result envelope for a completed `up` invocation.
func emitUpResult(wctx *workspaceContext, ideURL string, out io.Writer) {
containerID := config2.GetContainerID(wctx.result)
workdir := wctx.workdir
var warnings []string
recovery := false
if wctx.result != nil {
warnings = wctx.result.HostWarnings
recovery = wctx.result.RecoveryContainer
if workdir == "" && wctx.result.SubstitutionContext != nil {
workdir = wctx.result.SubstitutionContext.ContainerWorkspaceFolder
}
}
_ = config2.WriteResultJSON(out, config2.ResultEnvelope{
ContainerID: containerID,
RemoteUser: wctx.user,
RemoteWorkspaceFolder: wctx.workdir,
RemoteWorkspaceFolder: workdir,
URL: ideURL,
Warnings: warnings,
Recovery: recovery,
Expand Down
130 changes: 130 additions & 0 deletions cmd/workspace/up/up_workdir_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
package up

import (
"bytes"
"encoding/json"
"testing"

"github.com/devsy-org/devsy/cmd/flags"
devcconfig "github.com/devsy-org/devsy/pkg/devcontainer/config"
"github.com/devsy-org/devsy/pkg/provider"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

type workspaceFolderCase struct {
name string
resolvedFolder string
configFolder string
subpath string
override string
noMergedConfig bool
wantWorkdir string
want string
}

func TestUpResultWorkspaceFolder(t *testing.T) {
const (
defaultFolder = "/workspaces/project"
customFolder = "/src/project"
overrideFolder = "/override/project"
gitSubpath = "app"
literalFolder = "/src/$(printf marker)"
)
tests := []workspaceFolderCase{
{name: "default folder", resolvedFolder: defaultFolder, want: defaultFolder},
{name: "custom mount folder", resolvedFolder: customFolder, want: customFolder},
{name: "literal mount folder", resolvedFolder: literalFolder, want: literalFolder},
{
name: "no merged config",
resolvedFolder: defaultFolder,
noMergedConfig: true,
want: defaultFolder,
},
{
name: "explicit folder",
resolvedFolder: defaultFolder,
configFolder: customFolder,
wantWorkdir: customFolder, want: customFolder,
},
{
name: "git subpath",
resolvedFolder: defaultFolder,
subpath: gitSubpath,
wantWorkdir: defaultFolder + "/app", want: defaultFolder + "/app",
},
{
name: "git subpath with explicit folder",
resolvedFolder: customFolder,
configFolder: customFolder,
subpath: gitSubpath,
wantWorkdir: customFolder + "/app",
want: customFolder + "/app",
},
{
name: "CLI override",
resolvedFolder: defaultFolder,
override: overrideFolder,
wantWorkdir: overrideFolder, want: overrideFolder,
},
{
name: "CLI override takes precedence",
resolvedFolder: customFolder,
configFolder: customFolder,
subpath: gitSubpath,
override: overrideFolder,
wantWorkdir: overrideFolder, want: overrideFolder,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
assertUpResultFolder(t, tt)
})
}
}

func assertUpResultFolder(t *testing.T, test workspaceFolderCase) {
t.Helper()
result := &devcconfig.Result{
SubstitutionContext: &devcconfig.SubstitutionContext{
ContainerWorkspaceFolder: test.resolvedFolder,
},
}
if !test.noMergedConfig {
result.MergedConfig = &devcconfig.MergedDevContainerConfig{
DevContainerConfigBase: devcconfig.DevContainerConfigBase{
WorkspaceFolder: test.configFolder,
},
}
}
workspace := &provider.Workspace{
Source: provider.WorkspaceSource{GitSubPath: test.subpath},
}
client := prepareWorkspaceClient{workspaceConfig: workspace}
cmd := &UpCmd{GlobalFlags: &flags.GlobalFlags{}}
cmd.WorkspaceFolder = test.override
workdir := cmd.resolveWorkdir(result, client)
assert.Equal(t, test.wantWorkdir, workdir, "folder used by SSH")

var output bytes.Buffer
wctx := &workspaceContext{result: result, workdir: workdir}
emitUpResult(wctx, "", &output)
assert.Equal(t, test.wantWorkdir, wctx.workdir, "JSON emission must preserve SSH folder")
var envelope devcconfig.ResultEnvelope
require.NoError(t, json.Unmarshal(output.Bytes(), &envelope))
assert.Equal(t, "success", envelope.Outcome)
assert.Equal(t, test.want, envelope.RemoteWorkspaceFolder)
}

func TestEmitUpResultWithoutSubstitutionContext(t *testing.T) {
for _, result := range []*devcconfig.Result{nil, {}} {
for _, workdir := range []string{"", "/explicit"} {
var output bytes.Buffer
wctx := &workspaceContext{result: result, workdir: workdir}
emitUpResult(wctx, "", &output)
var envelope devcconfig.ResultEnvelope
require.NoError(t, json.Unmarshal(output.Bytes(), &envelope))
assert.Equal(t, workdir, envelope.RemoteWorkspaceFolder)
}
}
}
4 changes: 2 additions & 2 deletions e2e/tests/up/up_behaviors.go
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,7 @@ var _ = ginkgo.Describe("up command behaviors", ginkgo.Label("up-behaviors"), fu
framework.ExpectNoError(err)

gomega.Expect(envelope.Outcome).To(gomega.Equal("success"))
gomega.Expect(envelope.RemoteWorkspaceFolder).NotTo(gomega.BeEmpty())
statusCount := 0
for _, line := range lines[:len(lines)-1] {
var status struct {
Expand Down Expand Up @@ -229,8 +230,7 @@ var _ = ginkgo.Describe("up command behaviors", ginkgo.Label("up-behaviors"), fu
"expected exactly one success envelope in stdout, got %d", len(matched))
gomega.Expect(matched[0].ContainerID).NotTo(gomega.BeEmpty())
gomega.Expect(matched[0].RemoteUser).NotTo(gomega.BeEmpty())
// RemoteWorkspaceFolder may legitimately be empty when the
// devcontainer.json doesn't set a workspaceFolder.
gomega.Expect(matched[0].RemoteWorkspaceFolder).NotTo(gomega.BeEmpty())
}, ginkgo.SpecTimeout(framework.TimeoutLong()))

ginkgo.It("suppresses JSON envelope with --result-format plain", func(ctx context.Context) {
Expand Down
Loading