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
4 changes: 3 additions & 1 deletion chunk/chunk.go
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,9 @@ func (chunk *ExecutableChunk) WriteBashScript(basedir string, script_name string
}

// bubble up the env after the script execution
_, err = writer.WriteString("echo \"### ENV ###\"\n")
// include newline in the echo output itself to ensure ENV marker starts on its own line
// even if user script doesn't end with newline (e.g. echo -n)
_, err = writer.WriteString("echo \"\n### ENV ###\"\n")
_, err = writer.WriteString("printenv\n")
if err != nil {
return err
Expand Down
5 changes: 5 additions & 0 deletions chunk/command.go
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,11 @@ func (command *RunningCommand) Wait() error {
command.Ctx.Cfg.Env = bashEnvVars

if len(newLines) > 0 {
// Remove the trailing empty line that precedes the ENV marker (added by our \n in the echo)
// This prevents an extra blank line when the ENV section is stripped
if len(newLines) > 0 && newLines[len(newLines)-1] == "" {
newLines = newLines[:len(newLines)-1]
}
command.Stdout = strings.Join(newLines, "\n")
command.Stdout = command.Stdout + "\n"
} else {
Expand Down
60 changes: 60 additions & 0 deletions chunk/command_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -348,4 +348,64 @@ func TestRunningCommand(t *testing.T) {
err = runningCmd.Wait()
assert.Error(t, err, "Expected an error when waiting for a failing command")
})

t.Run("bash env extraction with echo -n command", func(t *testing.T) {
// Regression test for issue #25: ensure ENV marker doesn't get appended
// to command output when the command doesn't end with a newline
tmpDirs := make(map[string]string)

cfg := &config.Config{MinutesToTimeout: 1, Env: []string{}}
ui := view.NewView("mock")

chunk := ExecutableChunk{
Runtime: "bash",
Content: []string{"echo -n 'TEST'"},
Context: &runnercontext.Context{
Cfg: cfg,
RView: ui,
},
}
err := chunk.PrepareForExecution(tmpDirs)
assert.NoError(t, err, "Expected no error when preparing chunk for execution")

err = chunk.ExecuteSequential()
assert.NoError(t, err, "Expected no error when executing bash chunk")

// Verify the output is just "TEST\n" without the ENV marker appended to it
// The trailing \n is added by the command processing logic
assert.Equal(t, "TEST\n", chunk.Commands[0].Stdout, "Expected stdout to be 'TEST\\n' without ENV marker concatenated")
// Crucially, verify it's NOT "TEST### ENV ###" which was the bug
assert.NotContains(t, chunk.Commands[0].Stdout, "### ENV ###", "Expected ENV marker to be stripped from output")

// Verify ENV section was still extracted (should have environment variables)
assert.NotEmpty(t, chunk.Context.Cfg.Env, "Expected environment variables to be extracted")
})

t.Run("bash env extraction with normal echo command", func(t *testing.T) {
// Ensure normal commands (with newline) don't get extra blank lines
tmpDirs := make(map[string]string)

cfg := &config.Config{MinutesToTimeout: 1, Env: []string{}}
ui := view.NewView("mock")

chunk := ExecutableChunk{
Runtime: "bash",
Content: []string{"echo 'NORMAL'"},
Context: &runnercontext.Context{
Cfg: cfg,
RView: ui,
},
}
err := chunk.PrepareForExecution(tmpDirs)
assert.NoError(t, err, "Expected no error when preparing chunk for execution")

err = chunk.ExecuteSequential()
assert.NoError(t, err, "Expected no error when executing bash chunk")

// Verify the output doesn't have extra blank lines
assert.Equal(t, "NORMAL\n", chunk.Commands[0].Stdout, "Expected stdout to be 'NORMAL\\n' without extra blank lines")

// Verify ENV section was still extracted
assert.NotEmpty(t, chunk.Context.Cfg.Env, "Expected environment variables to be extracted")
})
}
Loading