What the user saw
the broken call
fetch('http://localhost:8080/')
browser console
ERR_CONNECTION_REFUSED
localhost:8080
Why it wasn’t simple
Go layer
program.go
generates
project files
Vite layer
app.tsx.tmpl
runs in
browser
Vite blocks any env var without
VITE_
prefix.
program.go
had to write the bridge.
The fix
program.go — reads root .env, writes frontend/.env
globalEnvPath := filepath.Join(projectPath, ".env")
vitePort := "8080" // Default fallback
if strings.HasPrefix(line, "PORT=") {
vitePort = strings.SplitN(line, "=", 2)[1]
}
frontendEnvContent := fmt.Sprintf("VITE_PORT=%s\n", vitePort)
os.WriteFile(filepath.Join(frontendPath, ".env"), ...)
app.tsx.tmpl — the visible change (react & react+tailwind)
- fetch('http://localhost:8080/')
+ fetch(`http://localhost:${import.meta.env.VITE_PORT}/`)
3 CI workflow files — trailing /dev/null was silently breaking builds
- script -q /dev/null -c "go run ..." /dev/null
+ script -q /dev/null -c "go run ..."
Scope
7 files
+36 −5 lines
program.go · app.tsx.tmpl (×2) · 3 CI workflows · react-vite.md
What I learned
- Vite’s
VITE_prefix requirement and why it exists — security: don’t accidentally expose server env vars to the browser bundle. - Go’s code generation pattern:
program.godoesn’t just copy files, it reads and transforms env state at generation time. - How to test GitHub Actions locally — asked in the PR, learned about
act. - Trailing
/dev/nullinscript -cbreaks CI — silent failure, hard to spot without running it.