fix: resolve first-run experience errors (#721, #771, #730)

- cmdInit (--init) now substitutes EDITOR_PATH, PAGER_PATH, and
  WORK_PATH instead of leaving them as literal strings
- Installer now substitutes WORK_PATH and always creates personal
  and work directories regardless of community cheatsheet choice
- When community cheatsheets are declined, the community cheatpath
  is commented out in the generated config
- config.New() skips nonexistent cheatpaths with a warning instead
  of hard-erroring on EvalSymlinks failure

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Christopher Allen Lane
2026-02-14 20:42:33 -05:00
parent 8eafa5adfe
commit 00ec2c130d
5 changed files with 74 additions and 20 deletions

View File

@@ -64,7 +64,8 @@ func New(_ map[string]interface{}, confPath string, resolve bool) (Config, error
}
// process cheatpaths
for i, cheatpath := range conf.Cheatpaths {
var validPaths []cp.Cheatpath
for _, cheatpath := range conf.Cheatpaths {
// expand ~ in config paths
expanded, err := homedir.Expand(cheatpath.Path)
@@ -83,6 +84,14 @@ func New(_ map[string]interface{}, confPath string, resolve bool) (Config, error
if resolve {
evaled, err := filepath.EvalSymlinks(expanded)
if err != nil {
// if the path simply doesn't exist, warn and skip it
if os.IsNotExist(err) {
fmt.Fprintf(os.Stderr,
"WARNING: cheatpath '%s' does not exist, skipping\n",
expanded,
)
continue
}
return Config{}, fmt.Errorf(
"failed to resolve symlink: %s: %v",
expanded,
@@ -93,8 +102,10 @@ func New(_ map[string]interface{}, confPath string, resolve bool) (Config, error
expanded = evaled
}
conf.Cheatpaths[i].Path = expanded
cheatpath.Path = expanded
validPaths = append(validPaths, cheatpath)
}
conf.Cheatpaths = validPaths
// trim editor whitespace
conf.Editor = strings.TrimSpace(conf.Editor)

View File

@@ -189,10 +189,14 @@ cheatpaths:
t.Fatalf("failed to write config: %v", err)
}
// Load config with symlink resolution should fail
_, err = New(map[string]interface{}{}, configFile, true)
if err == nil {
t.Error("expected error for broken symlink, got nil")
// Load config with symlink resolution should skip the broken cheatpath
// (warn to stderr) rather than hard-error
conf, err := New(map[string]interface{}{}, configFile, true)
if err != nil {
t.Errorf("expected no error for broken symlink (should skip), got: %v", err)
}
if len(conf.Cheatpaths) != 0 {
t.Errorf("expected broken cheatpath to be filtered out, got %d cheatpaths", len(conf.Cheatpaths))
}
}