Skip to content
Open
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
21 changes: 20 additions & 1 deletion internal/devbox/docgen/docgen.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ package docgen

import (
_ "embed"
"maps"
"os"
"strings"
"text/template"

"go.jetify.com/devbox/internal/devbox"
Expand Down Expand Up @@ -55,13 +57,30 @@ func GenerateReadme(
"Description": devbox.Config().Root.Description,
"Scripts": devbox.Config().Scripts().
WithRelativePaths(devbox.ProjectDir()),
"EnvVars": devbox.Config().Env(),
"EnvVars": envWithRelativePaths(devbox.Config().Env(), devbox.ProjectDir()),
"InitHook": devbox.Config().InitHook(),
"Packages": devbox.TopLevelPackages(),
// TODO add includes
})
}

// envWithRelativePaths returns a copy of env where occurrences of the absolute
// project directory are replaced with ".". This keeps generated READMEs free of
// machine-specific absolute paths (such as a user's home directory) that plugins
// expand into environment variables like PGDATA and PGHOST. It mirrors the
// behavior of configfile.Scripts.WithRelativePaths, which is already applied to
// scripts in the generated README.
func envWithRelativePaths(env map[string]string, projectDir string) map[string]string {
if projectDir == "" {
return maps.Clone(env)
}
result := make(map[string]string, len(env))
for key, value := range env {
result[key] = strings.ReplaceAll(value, projectDir, ".")
}
return result
}

func SaveDefaultReadmeTemplate(outputPath string) error {
if outputPath == "" {
outputPath = defaultTemplateName
Expand Down
44 changes: 44 additions & 0 deletions internal/devbox/docgen/docgen_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package docgen

import (
"maps"
"testing"
)

func TestEnvWithRelativePaths(t *testing.T) {
projectDir := "/home/user/myproject"

t.Run("replaces project dir with relative path", func(t *testing.T) {
env := map[string]string{
"PGDATA": projectDir + "/.devbox/virtenv/postgresql/data",
"PGHOST": projectDir + "/.devbox/virtenv/postgresql",
"PGPORT": "5432",
}
got := envWithRelativePaths(env, projectDir)
want := map[string]string{
"PGDATA": "./.devbox/virtenv/postgresql/data",
"PGHOST": "./.devbox/virtenv/postgresql",
"PGPORT": "5432",
}
if !maps.Equal(got, want) {
t.Errorf("envWithRelativePaths() = %v, want %v", got, want)
}
})

t.Run("does not mutate the input map", func(t *testing.T) {
original := projectDir + "/.devbox/virtenv/postgresql"
env := map[string]string{"PGHOST": original}
envWithRelativePaths(env, projectDir)
if env["PGHOST"] != original {
t.Errorf("input map was mutated: PGHOST = %q, want %q", env["PGHOST"], original)
}
})

t.Run("empty project dir returns env unchanged", func(t *testing.T) {
env := map[string]string{"PGHOST": "/some/abs/path"}
got := envWithRelativePaths(env, "")
if !maps.Equal(got, env) {
t.Errorf("envWithRelativePaths() = %v, want %v", got, env)
}
})
}
Loading