Skip to content

Commit cdaa853

Browse files
committed
fix(env): implement dual-layer script normalization for Windows compatibility
Add recursive host-side line ending normalization during setup and a robust container-side normalization step using sed on startup. This ensures all .sh scripts use LF line endings, resolving the 'bash\r' execution error on Windows hosts. Also includes minor lint fix in docker_patch.go.
1 parent ef99f3a commit cdaa853

5 files changed

Lines changed: 47 additions & 15 deletions

File tree

pkg/git/git.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -246,8 +246,9 @@ func NormalizeLineEndings(path string) error {
246246
return err
247247
}
248248

249-
// Simple CRLF to LF replacement
250-
normalized := strings.ReplaceAll(string(data), "\r\n", "\n")
249+
// Robust line ending normalization: strip all \r characters and
250+
// then ensure the file uses LF (\n) correctly.
251+
normalized := strings.ReplaceAll(string(data), "\r", "")
251252

252253
if normalized == string(data) {
253254
return nil // No change needed

pkg/setup/deploy.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,12 @@ func DeployWorld(c container.ContainerClient, workspace string) error {
2828
return fmt.Errorf("sui-playground container is not running (ExitCode: %d, ExitErr: %v). Last 50 lines of logs:\n%s", exitCode, exitErr, lastLogs)
2929
}
3030

31+
// 0. Ensure all scripts in the container have LF line endings.
32+
// This protects against Windows host-side drift (CRLF).
33+
if err := NormalizeContainerScripts(c, container.ContainerSuiPlayground); err != nil {
34+
ui.Warn.Println(fmt.Sprintf("Script normalization failed (continuing): %v", err))
35+
}
36+
3137
// 0. Remove stale Move.lock files so the Sui CLI resolves framework
3238
// dependencies from the installed binary instead of pinned git revisions
3339
// that may no longer exist upstream.
@@ -88,3 +94,19 @@ func DeployWorld(c container.ContainerClient, workspace string) error {
8894

8995
return nil
9096
}
97+
98+
// NormalizeContainerScripts ensures all .sh files in the /workspace directory
99+
// inside the container have LF line endings. This is a critical safety net
100+
// for Windows users where bind-mounted scripts might drift to CRLF.
101+
func NormalizeContainerScripts(c container.ContainerClient, containerName string) error {
102+
ui.Debug.Println(fmt.Sprintf("Normalizing script line endings in container %s...", containerName))
103+
104+
// Find all .sh files and use sed to strip all \r characters.
105+
// This ensures scripts are safe for execution regardless of host OS.
106+
cmd := []string{
107+
"/bin/bash", "-c",
108+
"find /workspace -name '*.sh' -exec sed -i 's/\\r//g' {} +",
109+
}
110+
111+
return c.Exec(context.Background(), containerName, cmd)
112+
}

pkg/setup/docker_patch.go

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,9 +62,7 @@ func patchDockerfile(dockerDir string) {
6262
if !strings.Contains(content, "postgresql-client") {
6363
content = strings.Replace(content, "dos2unix \\", "dos2unix \\\n postgresql-client \\", 1)
6464
}
65-
if strings.Contains(content, "ENV SUI_CONFIG_DIR=/root/.sui") {
66-
content = strings.Replace(content, "ENV SUI_CONFIG_DIR=/root/.sui", "ENV SUI_CONFIG_DIR=/workspace/.sui", 1)
67-
}
65+
content = strings.Replace(content, "ENV SUI_CONFIG_DIR=/root/.sui", "ENV SUI_CONFIG_DIR=/workspace/.sui", 1)
6866
// Safety net: inject a sed command into the Dockerfile that globally
6967
// replaces the bind-mount .env.sui path with the internal config-dir path
7068
// at build time. This uses a broad global replacement (not just the

pkg/setup/setup.go

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -112,16 +112,21 @@ func CloneRepositories(g git.GitClient, workspace string) error {
112112
}
113113

114114
// Correct line ending drift for critical shell scripts
115-
scriptsToNormalize := []string{
116-
filepath.Join(builderScaffoldPath, "docker/scripts/generate-world-env.sh"),
117-
}
118-
119-
for _, script := range scriptsToNormalize {
120-
if _, err := os.Stat(script); err == nil {
121-
ui.Debug.Printfln("Normalizing line endings for %s", script)
122-
git.NormalizeLineEndings(script)
123-
}
124-
}
115+
normalizeScripts := func(root string) {
116+
_ = filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
117+
if err != nil {
118+
return nil
119+
}
120+
if !info.IsDir() && strings.HasSuffix(strings.ToLower(info.Name()), ".sh") {
121+
ui.Debug.Printfln("Normalizing line endings for %s", path)
122+
git.NormalizeLineEndings(path)
123+
}
124+
return nil
125+
})
126+
}
127+
128+
normalizeScripts(builderScaffoldPath)
129+
normalizeScripts(worldContractsPath)
125130

126131
return nil
127132
}

pkg/setup/start.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,12 @@ func startSuiDev(c container.ContainerClient, ctx context.Context, workspace, do
131131
return fmt.Errorf("failed to start sui-playground container: %w", err)
132132
}
133133

134+
// 0. Ensure all scripts in the container have LF line endings.
135+
// This protects against Windows host-side drift (CRLF).
136+
if err := NormalizeContainerScripts(c, container.ContainerSuiPlayground); err != nil {
137+
ui.Warn.Println(fmt.Sprintf("Script normalization failed (continuing): %v", err))
138+
}
139+
134140
// Give the container a moment to start, then verify it is still running
135141
// before entering the (potentially long) log-wait loop.
136142
time.Sleep(10 * time.Second)

0 commit comments

Comments
 (0)