diff --git a/README.md b/README.md index b153457..8301b42 100644 --- a/README.md +++ b/README.md @@ -127,6 +127,16 @@ the packages installation state directory passed as their only argument. See the [section on package state](#package-state). The name of the hook specifies the life cycle event that will cause it to run. +The hooks also get called with the following environment variables set. This is +more useful for hooks that run prior to installation, when no package state has +been created. + +- `STOWAWAY_SOURCE` is set the the absolute path containing the package's + source files. +- `STOWAWAY_TARGET` is set the the absolute path where the package will be + installed. +- `STOWAWAY_PACKAGE_ROOT` is set the the absolute path of package root. + The following hoooks are currently available, in the order they are run: - `before_uninstall_all`: Run for each selected package in a `stow --delete` diff --git a/pkg/main.go b/pkg/main.go index bd8606c..3e55c87 100644 --- a/pkg/main.go +++ b/pkg/main.go @@ -2,6 +2,7 @@ package pkg import ( "errors" + "fmt" "io/fs" "os" "os/exec" @@ -145,6 +146,12 @@ func (pkg localPackage) RunHookIfExists(name string) error { } cmd := exec.Command(executable.String(), pkg.State.String()) + + cmd.Env = []string{ + fmt.Sprintf("STOWAWAY_SOURCE=%s", pkg.Source.String()), + fmt.Sprintf("STOWAWAY_TARGET=%s", pkg.Target.String()), + fmt.Sprintf("STOWAWAY_PACKAGE_ROOT=%s", pkg.PackageRoot.String()), + } return cmd.Run() } diff --git a/pkg/main_test.go b/pkg/main_test.go index f079c3f..7fcab06 100644 --- a/pkg/main_test.go +++ b/pkg/main_test.go @@ -149,8 +149,7 @@ func TestRunHook(t *testing.T) { p, err := loader.Load() require.NoError(t, err) - script := `#!/bin/sh -touch "$1/hookran"` + script := "#!/bin/sh\nprintenv > $1/env" writeFile(t, tmp, "bash/hooks/broken", "not executable", 0655) writeFile(t, tmp, "bash/hooks/working", script, 0755) @@ -159,7 +158,19 @@ touch "$1/hookran"` require.NoError(t, p.RunHookIfExists("working")) require.Error(t, p.RunHookIfExists("broken")) - require.FileExists(t, tmp.Join("data/hookran").String()) + contents, err := os.ReadFile(tmp.Join("data/env").String()) + + env := map[string]string{} + for _, line := range strings.Split(string(contents), "\n") { + fields := strings.SplitN(line, "=", 2) + if len(fields) == 2 { + env[fields[0]] = fields[1] + } + } + + require.Equal(t, tmp.Join("bash/src").String(), env["STOWAWAY_SOURCE"]) + require.Equal(t, tmp.Join("bash").String(), env["STOWAWAY_PACKAGE_ROOT"]) + require.Equal(t, tmp.Join("home/user").String(), env["STOWAWAY_TARGET"]) } type InstallTestCase struct { @@ -358,12 +369,16 @@ type MockPackage struct { IsInstalled bool InstallCalled func(string, bool) HookCalled func(string, string) - Name string + PackageName string +} + +func (m *MockPackage) Name() string { + return m.PackageName } func (m *MockPackage) Install() error { if m.InstallCalled != nil { - m.InstallCalled(m.Name, false) + m.InstallCalled(m.PackageName, false) } return nil @@ -371,7 +386,7 @@ func (m *MockPackage) Install() error { func (m *MockPackage) Uninstall() error { if m.InstallCalled != nil { - m.InstallCalled(m.Name, true) + m.InstallCalled(m.PackageName, true) } return nil @@ -383,7 +398,7 @@ func (m *MockPackage) Installed() (bool, error) { func (m *MockPackage) RunHookIfExists(name string) error { if m.HookCalled != nil { - m.HookCalled(m.Name, name) + m.HookCalled(m.PackageName, name) } return nil @@ -448,9 +463,9 @@ func TestStow(t *testing.T) { } pkgs := []Package{ - &MockPackage{Name: "a", IsInstalled: true, HookCalled: hook, InstallCalled: ins}, - &MockPackage{Name: "b", IsInstalled: false, HookCalled: hook, InstallCalled: ins}, - &MockPackage{Name: "c", IsInstalled: true, HookCalled: hook, InstallCalled: ins}, + &MockPackage{PackageName: "a", IsInstalled: true, HookCalled: hook, InstallCalled: ins}, + &MockPackage{PackageName: "b", IsInstalled: false, HookCalled: hook, InstallCalled: ins}, + &MockPackage{PackageName: "c", IsInstalled: true, HookCalled: hook, InstallCalled: ins}, } options := StowOptions{ @@ -492,9 +507,9 @@ func TestStow(t *testing.T) { } pkgs := []Package{ - &MockPackage{Name: "a", IsInstalled: false, HookCalled: hook, InstallCalled: ins}, - &MockPackage{Name: "b", IsInstalled: true, HookCalled: hook, InstallCalled: ins}, - &MockPackage{Name: "c", IsInstalled: false, HookCalled: hook, InstallCalled: ins}, + &MockPackage{PackageName: "a", IsInstalled: false, HookCalled: hook, InstallCalled: ins}, + &MockPackage{PackageName: "b", IsInstalled: true, HookCalled: hook, InstallCalled: ins}, + &MockPackage{PackageName: "c", IsInstalled: false, HookCalled: hook, InstallCalled: ins}, } options := StowOptions{