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
8 changes: 3 additions & 5 deletions internal/archtest/baseline/dryrun.txt
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
# Baseline for archtest rule "dryrun".
# Each line is <file>:<line> # reason for a known existing violation.
# Each line is <file>:<line> of a known existing violation.
# Regenerate: ARCHTEST_UPDATE_BASELINE=1 go test ./internal/archtest/...

# Audited exemptions: read-only diagnostic runners — doctor never mutates user state.
internal/doctor/doctor.go:50 # read-only diagnostic probe; doctor does not mutate user state
internal/doctor/doctor.go:54 # read-only diagnostic probe; doctor does not mutate user state
internal/doctor/doctor.go:50
internal/doctor/doctor.go:54
4 changes: 2 additions & 2 deletions internal/archtest/baseline/no-direct-exec.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ internal/diff/compare.go:253
internal/dotfiles/dotfiles.go:23
internal/dotfiles/dotfiles.go:32
internal/dotfiles/dotfiles.go:66
internal/dotfiles/dotfiles.go:290
internal/dotfiles/dotfiles.go:388
internal/dotfiles/dotfiles.go:318
internal/dotfiles/dotfiles.go:416
internal/installer/step_system.go:85
internal/npm/npm.go:22
internal/permissions/screen_recording_cgo.go:21
Expand Down
6 changes: 3 additions & 3 deletions internal/archtest/baseline/no-raw-http.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Baseline for archtest rule "no-raw-http".
# Each line is <file>:<line> # reason for a known existing violation.
# Each line is <file>:<line> of a known existing violation.
# Regenerate: ARCHTEST_UPDATE_BASELINE=1 go test ./internal/archtest/...
internal/config/packages_remote.go:84 # injects the default transport into a test-swappable client; round-trip uses httputil.Do
internal/config/remote.go:57 # injects the default transport into a version-header transport; round-trip uses httputil.Do
internal/config/packages_remote.go:84
internal/config/remote.go:57
28 changes: 28 additions & 0 deletions internal/dotfiles/dotfiles.go
Original file line number Diff line number Diff line change
Expand Up @@ -225,13 +225,41 @@ func Link(dryRun bool) error {
return fmt.Errorf("dotfiles directory not found: %s", dotfilesPath)
}

if hasMakefile(dotfilesPath) {
return linkWithMake(dotfilesPath, dryRun)
}

if hasStowPackages(dotfilesPath) {
return linkWithStow(dotfilesPath, dryRun)
}

return linkDirect(dotfilesPath, dryRun)
}

func hasMakefile(dotfilesPath string) bool {
data, err := os.ReadFile(filepath.Join(dotfilesPath, "Makefile"))
if err != nil {
return false
}
for _, line := range strings.Split(string(data), "\n") {
if strings.HasPrefix(line, "install:") {
return true
}
}
return false
}

func linkWithMake(dotfilesPath string, dryRun bool) error {
if dryRun {
ui.DryRunMsg("Would run make install in %s", dotfilesPath)
return nil
}
if err := system.RunCommandInDir(dotfilesPath, "make", "install"); err != nil {
return fmt.Errorf("make install: %w", err)
}
return nil
}

func hasStowPackages(dotfilesPath string) bool {
entries, err := os.ReadDir(dotfilesPath)
if err != nil {
Expand Down
48 changes: 48 additions & 0 deletions internal/dotfiles/dotfiles_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,54 @@ func TestLink_DryRun(t *testing.T) {
assert.True(t, os.IsNotExist(err))
}

func TestHasMakefile_WithInstallTarget(t *testing.T) {
tmpDir := t.TempDir()
err := os.WriteFile(filepath.Join(tmpDir, "Makefile"), []byte("install:\n\techo done\n"), 0644)
require.NoError(t, err)
assert.True(t, hasMakefile(tmpDir))
}

func TestHasMakefile_WithoutInstallTarget(t *testing.T) {
tmpDir := t.TempDir()
err := os.WriteFile(filepath.Join(tmpDir, "Makefile"), []byte("build:\n\techo build\n"), 0644)
require.NoError(t, err)
assert.False(t, hasMakefile(tmpDir))
}

func TestHasMakefile_NoMakefile(t *testing.T) {
tmpDir := t.TempDir()
assert.False(t, hasMakefile(tmpDir))
}

func TestLinkWithMake_DryRun(t *testing.T) {
tmpHome := t.TempDir()
t.Setenv("HOME", tmpHome)

dotfilesPath := filepath.Join(tmpHome, defaultDotfilesDir)
require.NoError(t, os.MkdirAll(dotfilesPath, 0755))
require.NoError(t, os.WriteFile(filepath.Join(dotfilesPath, "Makefile"), []byte("install:\n\techo done\n"), 0644))

err := linkWithMake(dotfilesPath, true)
assert.NoError(t, err)
}

func TestLink_UsesMakefileOverStow(t *testing.T) {
tmpHome := t.TempDir()
t.Setenv("HOME", tmpHome)

dotfilesPath := filepath.Join(tmpHome, defaultDotfilesDir)
require.NoError(t, os.MkdirAll(dotfilesPath, 0755))

// Repo has both a Makefile and a stow package — Makefile wins (dry run).
require.NoError(t, os.WriteFile(filepath.Join(dotfilesPath, "Makefile"), []byte("install:\n\techo done\n"), 0644))
pkgDir := filepath.Join(dotfilesPath, "vim")
require.NoError(t, os.MkdirAll(pkgDir, 0755))
require.NoError(t, os.WriteFile(filepath.Join(pkgDir, ".vimrc"), []byte(""), 0644))

err := Link(true)
assert.NoError(t, err)
}

func TestHasStowPackages_Empty(t *testing.T) {
tmpDir := t.TempDir()
result := hasStowPackages(tmpDir)
Expand Down
10 changes: 10 additions & 0 deletions internal/system/system.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,16 @@ func RunCommandContext(ctx context.Context, name string, args ...string) error {
return cmd.Run()
}

// RunCommandInDir runs name with args in the given working directory,
// forwarding stdout and stderr to the terminal.
func RunCommandInDir(dir string, name string, args ...string) error {
cmd := exec.CommandContext(context.Background(), name, args...) //nolint:gosec // intentional generic runner; callers are responsible for validating name and args
cmd.Dir = dir
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
return cmd.Run()
}

func RunCommandSilent(name string, args ...string) (string, error) {
return RunCommandSilentContext(context.Background(), name, args...)
}
Expand Down
Loading