diff --git a/.circleci/config.yml b/.circleci/config.yml index 77925a07..fe70e0b3 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -24,8 +24,9 @@ jobs: default: "test-alpine" type: string executor: + size: medium name: win/server-2022 - version: 2023.04.1 + version: 2023.11.1 steps: - checkout - run: diff --git a/.githooks/pre-commit/cli-docs-up-to-date b/.githooks/pre-commit/cli-docs-up-to-date index d0480d3c..3aac0508 100755 --- a/.githooks/pre-commit/cli-docs-up-to-date +++ b/.githooks/pre-commit/cli-docs-up-to-date @@ -34,7 +34,7 @@ for file in $STAGED_FILES; do exit 1 } - git add "$repoDir/docs/cli/"* + # git add "$repoDir/docs/cli/"* echo "* Docs regenerated." >&2 else diff --git a/README.md b/README.md index 5ee009d5..efe39059 100644 --- a/README.md +++ b/README.md @@ -1048,7 +1048,7 @@ curl -sL https://raw.githubusercontent.com/gabyx/githooks/main/scripts/install.s The installer always maintains a Githooks clone inside `/release` for its automatic update logic. The specified custom clone URL and branch will then be used for further updates in the above example (see -[update machanics](#update-mechanics)). +[update mechanics](#update-mechanics)). Because the installer **always** downloads the latest release (here from another URL/branch), it needs deploy settings to know where to get the binaries from. diff --git a/docs/cli/git_hooks_installer.md b/docs/cli/git_hooks_installer.md index ec5a6ace..10162c5e 100644 --- a/docs/cli/git_hooks_installer.md +++ b/docs/cli/git_hooks_installer.md @@ -4,11 +4,10 @@ Githooks installer application. ### Synopsis -Githooks installer application. -It downloads the Githooks artifacts of the current version -from a deploy source and verifies its checksums and signature. -Then it calls the installer on the new version which -will then run the installation procedure for Githooks. +Githooks installer application. It downloads the Githooks artifacts of the +current version from a deploy source and verifies its checksums and signature. +Then it calls the installer on the new version which will then run the +installation procedure for Githooks. See further information at https://github.com/gabyx/githooks/blob/main/README.md @@ -28,7 +27,7 @@ git hooks installer [flags] --skip-install-into-existing Skip installation into existing repositories defined by a search path. --prefix string Githooks installation prefix such that - `/.githooks` will be the installation directory. + `/.githooks` will be the installation directory (env. variables e.g. `$X` and '~' allowed). --template-dir string The preferred template directory to use. --maintained-hooks strings A set of hook names which are maintained in the template directory. Any argument can be a hook name ``, `all` or `server`. @@ -62,6 +61,6 @@ git hooks installer [flags] ### SEE ALSO -* [git hooks](git_hooks.md) - Githooks CLI application +- [git hooks](git_hooks.md) - Githooks CLI application -###### Auto generated by spf13/cobra +###### Auto generated by spf13/cobra diff --git a/docs/dialog/dialog.md b/docs/dialog/dialog.md index 98a5b7a0..b5775132 100644 --- a/docs/dialog/dialog.md +++ b/docs/dialog/dialog.md @@ -22,12 +22,11 @@ dialog ### SEE ALSO -- [dialog entry](dialog_entry.md) - Shows a entry dialog. -- [dialog file-save](dialog_file-save.md) - Shows a file save dialog. -- [dialog file-selection](dialog_file-selection.md) - Shows a file selection - dialog. -- [dialog message](dialog_message.md) - Shows a message dialog. -- [dialog notify](dialog_notify.md) - Shows a notification. -- [dialog options](dialog_options.md) - Shows a options selection dialog. - -###### Auto generated by spf13/cobra +* [dialog entry](dialog_entry.md) - Shows a entry dialog. +* [dialog file-save](dialog_file-save.md) - Shows a file save dialog. +* [dialog file-selection](dialog_file-selection.md) - Shows a file selection dialog. +* [dialog message](dialog_message.md) - Shows a message dialog. +* [dialog notify](dialog_notify.md) - Shows a notification. +* [dialog options](dialog_options.md) - Shows a options selection dialog. + +###### Auto generated by spf13/cobra diff --git a/docs/dialog/dialog_entry.md b/docs/dialog/dialog_entry.md index 6ffe7d36..0574bf03 100644 --- a/docs/dialog/dialog_entry.md +++ b/docs/dialog/dialog_entry.md @@ -4,16 +4,16 @@ Shows a entry dialog. ### Synopsis -Shows a entry dialog similar to `zenity`. Currently extra buttons are not -supported on all platforms. Unix/Windows supports multiple extra buttons, MacOS -does not. +Shows a entry dialog similar to `zenity`. +Currently extra buttons are not supported on all platforms. +Unix/Windows supports multiple extra buttons, MacOS does not. # Exit Codes: - `0` : User pressed `Ok`. - `1` : User pressed `Cancel` or closed the dialog. -- `2` : The user pressed an extra button. The output contains the index of that - button. +- `2` : The user pressed an extra button. + The output contains the index of that button. ``` dialog entry @@ -54,6 +54,6 @@ dialog entry ### SEE ALSO -- [dialog](dialog.md) - Githooks dialog application similar to `zenity`. +* [dialog](dialog.md) - Githooks dialog application similar to `zenity`. -###### Auto generated by spf13/cobra +###### Auto generated by spf13/cobra diff --git a/docs/dialog/dialog_file-save.md b/docs/dialog/dialog_file-save.md index 7c469002..46640194 100644 --- a/docs/dialog/dialog_file-save.md +++ b/docs/dialog/dialog_file-save.md @@ -5,11 +5,11 @@ Shows a file save dialog. ### Synopsis Shows a file save dialog similar to `zenity`. - # Exit Codes: -- `0` : User pressed `Ok`. The output contains the selected paths separated by - `--separator`. All paths use forward slashes on any platform. +- `0` : User pressed `Ok`. The output contains the selected paths + separated by `--separator`. All paths use forward slashes + on any platform. - `1` : User pressed `Cancel` or closed the dialog. - `5` : The dialog was closed due to timeout. @@ -48,6 +48,6 @@ dialog file-save ### SEE ALSO -- [dialog](dialog.md) - Githooks dialog application similar to `zenity`. +* [dialog](dialog.md) - Githooks dialog application similar to `zenity`. -###### Auto generated by spf13/cobra +###### Auto generated by spf13/cobra diff --git a/docs/dialog/dialog_file-selection.md b/docs/dialog/dialog_file-selection.md index 66bbf083..b510aca9 100644 --- a/docs/dialog/dialog_file-selection.md +++ b/docs/dialog/dialog_file-selection.md @@ -8,8 +8,9 @@ Shows a file selection dialog similar to `zenity`. # Exit Codes: -- `0` : User pressed `Ok`. The output contains the selected paths separated by - `--separator`. All paths use forward slashes on any platform. +- `0` : User pressed `Ok`. The output contains the selected paths + separated by `--separator`. All paths use forward slashes + on any platform. - `1` : User pressed `Cancel` or closed the dialog. - `5` : The dialog was closed due to timeout. @@ -47,6 +48,6 @@ dialog file-selection ### SEE ALSO -- [dialog](dialog.md) - Githooks dialog application similar to `zenity`. +* [dialog](dialog.md) - Githooks dialog application similar to `zenity`. -###### Auto generated by spf13/cobra +###### Auto generated by spf13/cobra diff --git a/docs/dialog/dialog_message.md b/docs/dialog/dialog_message.md index e7af2a0e..2ef9d47e 100644 --- a/docs/dialog/dialog_message.md +++ b/docs/dialog/dialog_message.md @@ -6,15 +6,16 @@ Shows a message dialog. Shows a message dialog similar to `zenity`. -Currently only one extra button is supported on all platforms. Only Unix -supports multiple extra buttons. Use `options` to have more choices. +Currently only one extra button is supported on all platforms. +Only Unix supports multiple extra buttons. +Use `options` to have more choices. # Exit Codes: - `0` : User pressed `Ok`. - `1` : User pressed `Cancel` or closed the dialog. -- `2` : The user pressed an extra button. The output contains the index of that - button. +- `2` : The user pressed an extra button. + The output contains the index of that button. ``` dialog message @@ -54,6 +55,6 @@ dialog message ### SEE ALSO -- [dialog](dialog.md) - Githooks dialog application similar to `zenity`. +* [dialog](dialog.md) - Githooks dialog application similar to `zenity`. -###### Auto generated by spf13/cobra +###### Auto generated by spf13/cobra diff --git a/docs/dialog/dialog_notify.md b/docs/dialog/dialog_notify.md index 68c40ca5..3226903c 100644 --- a/docs/dialog/dialog_notify.md +++ b/docs/dialog/dialog_notify.md @@ -38,6 +38,6 @@ dialog notify ### SEE ALSO -- [dialog](dialog.md) - Githooks dialog application similar to `zenity`. +* [dialog](dialog.md) - Githooks dialog application similar to `zenity`. -###### Auto generated by spf13/cobra +###### Auto generated by spf13/cobra diff --git a/docs/dialog/dialog_options.md b/docs/dialog/dialog_options.md index 3aeeec7c..546492c8 100644 --- a/docs/dialog/dialog_options.md +++ b/docs/dialog/dialog_options.md @@ -6,17 +6,18 @@ Shows a options selection dialog. Shows a list selection dialog similar to `zenity`. -Extra buttons are only supported on Unix and Windows. If not using `--multiple` -you can also use the button style options with `--style 1` which uses buttons -instead of a listbox. +Extra buttons are only supported on Unix and Windows. +If not using `--multiple` you can also use the +button style options with `--style 1` which uses buttons instead +of a listbox. # Exit Codes: - `0` : `Ok` was pressed. The output contains the indices of the selected items - separated by `--separator`. + separated by `--separator`. - `1` : `Cancel` was pressed or the dialog was closed. -- `2` : The user pressed an extra button. The output contains the index of that - button on the first line. +- `2` : The user pressed an extra button. + The output contains the index of that button on the first line. - `5` : The dialog was closed due to timeout. ``` @@ -61,6 +62,6 @@ dialog options ### SEE ALSO -- [dialog](dialog.md) - Githooks dialog application similar to `zenity`. +* [dialog](dialog.md) - Githooks dialog application similar to `zenity`. -###### Auto generated by spf13/cobra +###### Auto generated by spf13/cobra diff --git a/githooks/apps/runner/runner.go b/githooks/apps/runner/runner.go index 134e77e5..30e35c02 100644 --- a/githooks/apps/runner/runner.go +++ b/githooks/apps/runner/runner.go @@ -19,7 +19,6 @@ import ( "strings" "time" - "github.com/mitchellh/go-homedir" "github.com/pbenner/threadpool" "github.com/pkg/math" ) @@ -196,15 +195,8 @@ func setupSettings(repoPath string) (HookSettings, UISettings) { func getInstallDir(gitx *git.Context) string { installDir := hooks.GetInstallDir(gitx) - setDefault := func() { - usr, err := homedir.Dir() - cm.AssertNoErrorPanic(err, "Could not get home directory.") - usr = filepath.ToSlash(usr) - installDir = path.Join(usr, hooks.HooksDirName) - } - if strs.IsEmpty(installDir) { - setDefault() + installDir, _ = hooks.GetDefaultInstallDir() } else if exists, err := cm.IsPathExisting(installDir); !exists { log.AssertNoError(err, @@ -214,7 +206,7 @@ func getInstallDir(gitx *git.Context) string { "Install directory at '%s' is missing.", installDir) - setDefault() + installDir, _ = hooks.GetDefaultInstallDir() log.WarnF( "Falling back to default directory at '%s'.\n"+ diff --git a/githooks/cmd/common/context.go b/githooks/cmd/common/context.go index 095ca84e..752405ed 100644 --- a/githooks/cmd/common/context.go +++ b/githooks/cmd/common/context.go @@ -12,8 +12,10 @@ type CmdContext struct { Cwd string // The current working directory. GitX *git.Context // The git context in the current working directory. - InstallDir string // The install directory. - CloneDir string // The release clone dir inside the install dir. + InstallDir string // The install directory. + InstallDirRaw string // The raw install directory with out env. variables expanded. + + CloneDir string // The release clone dir inside the install dir. Log cm.ILogContext // The log context. LogStats cm.ILogStats // The statistics of the log context. diff --git a/githooks/cmd/common/install/install-prompts.go b/githooks/cmd/common/install/install-prompts.go index 531082c0..b63b6d27 100644 --- a/githooks/cmd/common/install/install-prompts.go +++ b/githooks/cmd/common/install/install-prompts.go @@ -1,6 +1,8 @@ package install import ( + "os" + "path/filepath" "strings" "time" @@ -43,6 +45,7 @@ func PromptExistingRepos( } homeDir, err := homedir.Dir() + homeDir = filepath.ToSlash(homeDir) cm.AssertNoErrorPanic(err, "Could not get home directory.") searchDir := gitx.GetConfig(hooks.GitCKPreviousSearchDir, git.GlobalScope) @@ -77,13 +80,13 @@ func PromptExistingRepos( } searchDir, err = promptx.ShowEntry( - "Where do you want to start the search?", + "Where do you want to start the search\n(env. variables are expanded)?", searchDir, prompt.CreateValidatorIsDirectory(homeDir)) log.AssertNoError(err, "Could not show prompt.") } - searchDir = cm.ReplaceTildeWith(searchDir, homeDir) + searchDir = filepath.ToSlash(os.ExpandEnv(cm.ReplaceTildeWith(searchDir, homeDir))) if !cm.IsDirectory(searchDir) { log.WarnF("Search directory\n'%s'\nis not a directory.\n" + existingWarn[idx]) diff --git a/githooks/cmd/common/install/load-install.go b/githooks/cmd/common/install/load-install.go deleted file mode 100644 index 1a2c1382..00000000 --- a/githooks/cmd/common/install/load-install.go +++ /dev/null @@ -1,35 +0,0 @@ -package install - -import ( - "path" - "path/filepath" - - cm "github.com/gabyx/githooks/githooks/common" - "github.com/gabyx/githooks/githooks/git" - "github.com/gabyx/githooks/githooks/hooks" - strs "github.com/gabyx/githooks/githooks/strings" - - "github.com/mitchellh/go-homedir" -) - -// LoadInstallDir loads the install directory and uses a default if -// it does not exist. -func LoadInstallDir(log cm.ILogContext, gitx *git.Context) (installDir string) { - - installDir = hooks.GetInstallDir(gitx) - - if !cm.IsDirectory(installDir) { - - if strs.IsNotEmpty(installDir) { - log.WarnF("Install directory '%s' does not exist.\n"+ - "Githooks installation is corrupt!\n"+ - "Using default location '~/.githooks'.", installDir) - } - - home, err := homedir.Dir() - cm.AssertNoErrorPanic(err, "Could not get home directory.") - installDir = path.Join(filepath.ToSlash(home), hooks.HooksDirName) - } - - return -} diff --git a/githooks/cmd/common/install/template-dir.go b/githooks/cmd/common/install/template-dir.go index e1a9e0d0..79dd3a9d 100644 --- a/githooks/cmd/common/install/template-dir.go +++ b/githooks/cmd/common/install/template-dir.go @@ -16,7 +16,7 @@ import ( func CheckTemplateDir(targetDir string, subFolderIfExists string) (string, error) { if strs.IsNotEmpty(targetDir) { - targetDir, err := cm.ReplaceTilde(targetDir) + targetDir, err := cm.ReplaceTilde(targetDir, false) if err != nil { return "", cm.ErrorF("Could not replace tilde '~' in '%s'.", targetDir) } diff --git a/githooks/cmd/installer/installer.go b/githooks/cmd/installer/installer.go index fc3d25b1..2b018891 100644 --- a/githooks/cmd/installer/installer.go +++ b/githooks/cmd/installer/installer.go @@ -19,7 +19,6 @@ import ( "github.com/gabyx/githooks/githooks/updates" "github.com/gabyx/githooks/githooks/updates/download" - "github.com/mitchellh/go-homedir" "github.com/spf13/cobra" "github.com/spf13/pflag" "github.com/spf13/viper" @@ -108,7 +107,8 @@ func defineArguments(cmd *cobra.Command, vi *viper.Viper) { cmd.PersistentFlags().String( "prefix", "", "Githooks installation prefix such that\n"+ - "'/.githooks' will be the installation directory.") + "'/.githooks' will be the installation directory "+ + "(env. variables e.g. '$X' and '~' allowed).") cm.AssertNoErrorPanic(cmd.MarkPersistentFlagDirname("prefix")) cmd.PersistentFlags().String( @@ -229,9 +229,10 @@ func validateArgs(log cm.ILogContext, cmd *cobra.Command, args *Arguments) { func setupSettings( log cm.ILogContext, - gitx *git.Context, + cmd *ccm.CmdContext, args *Arguments) (Settings, install.UISettings) { + gitx := cmd.GitX var promptx prompt.IContext var err error @@ -246,16 +247,20 @@ func setupSettings( } var installDir string + var installDirRaw string + // First check if we already have // an install directory set (from --prefix) if strs.IsNotEmpty(args.InstallPrefix) { - var err error - args.InstallPrefix, err = cm.ReplaceTilde(filepath.ToSlash(args.InstallPrefix)) + + args.InstallPrefix, err = cm.ReplaceTilde(args.InstallPrefix, true) log.AssertNoErrorPanic(err, "Could not replace '~' character in path.") - installDir = path.Join(args.InstallPrefix, ".githooks") + + installDirRaw = path.Join(args.InstallPrefix, ".githooks") + installDir = filepath.ToSlash(os.ExpandEnv(installDirRaw)) } else { - installDir = install.LoadInstallDir(log, gitx) + installDir, installDirRaw = cmd.InstallDir, cmd.InstallDirRaw } // Remove temporary directory if existing @@ -269,6 +274,7 @@ func setupSettings( return Settings{ GitX: gitx, InstallDir: installDir, + InstallDirRaw: installDirRaw, CloneDir: hooks.GetReleaseCloneDir(installDir), TempDir: tempDir, LFSHooksCache: lfsHooksCache, @@ -696,20 +702,18 @@ func searchTemplateDirOnDisk(log cm.ILogContext, promptx prompt.IContext) string func setupNewTemplateDir(log cm.ILogContext, installDir string, promptx prompt.IContext) string { templateDir := path.Join(installDir, "templates") - homeDir, err := homedir.Dir() - cm.AssertNoErrorPanic(err, "Could not get home directory.") - if promptx != nil { var err error templateDir, err = promptx.ShowEntry( - "Enter the target folder", + "Enter the target folder ('~' and env. variables '$X' allowed)", templateDir, nil) log.AssertNoErrorF(err, "Could not show prompt.") } - templateDir = cm.ReplaceTildeWith(templateDir, homeDir) + templateDir, err := cm.ReplaceTilde(templateDir, false) log.AssertNoErrorPanicF(err, "Could not replace tilde '~' in '%s'.", templateDir) + templateDir = filepath.ToSlash(os.ExpandEnv(templateDir)) return templateDir } @@ -1474,13 +1478,13 @@ func runInstall(cmd *cobra.Command, ctx *ccm.CmdContext, vi *viper.Viper) error } log.InfoF("Log file: '%s'", args.Log) - settings, uiSettings := setupSettings(log, ctx.GitX, &args) + settings, uiSettings := setupSettings(log, ctx, &args) log.DebugF("Arguments: %+v", args) log.DebugF("Settings: %+v", settings) if !args.DryRun { - setInstallDir(log, ctx.GitX, settings.InstallDir) + setInstallDir(log, ctx.GitX, settings.InstallDirRaw) } if !args.InternalPostDispatch { diff --git a/githooks/cmd/installer/settings.go b/githooks/cmd/installer/settings.go index 54bffa12..c1e97a30 100644 --- a/githooks/cmd/installer/settings.go +++ b/githooks/cmd/installer/settings.go @@ -13,6 +13,7 @@ type InstallSet = strs.StringSet type Settings struct { GitX *git.Context // The git command context. InstallDir string // The install directory. + InstallDirRaw string // The raw install directory with env. variables inside. CloneDir string // The release clone dir inside the install dir. TempDir string // The temporary directory inside the install dir. LFSHooksCache hooks.LFSHooksCache // LFS hooks cache if 'git lfs' is available. diff --git a/githooks/cmd/root.go b/githooks/cmd/root.go index 483e5c23..969318c2 100644 --- a/githooks/cmd/root.go +++ b/githooks/cmd/root.go @@ -5,7 +5,6 @@ import ( "github.com/gabyx/githooks/githooks/build" ccm "github.com/gabyx/githooks/githooks/cmd/common" - inst "github.com/gabyx/githooks/githooks/cmd/common/install" "github.com/gabyx/githooks/githooks/cmd/config" "github.com/gabyx/githooks/githooks/cmd/disable" "github.com/gabyx/githooks/githooks/cmd/exec" @@ -44,7 +43,7 @@ func NewSettings( log.AssertNoError(git.SanitizeOsEnv(), "Could not sanitize OS environment.") gitx := git.NewCtx() - installDir := inst.LoadInstallDir(log, gitx) + installDir, installDirRaw := hooks.LoadInstallDir(log, gitx) promptx, err = prompt.CreateContext(log, false, false) log.AssertNoErrorF(err, "Prompt setup failed -> using fallback.") @@ -53,6 +52,7 @@ func NewSettings( Cwd: cwd, GitX: gitx, InstallDir: installDir, + InstallDirRaw: installDirRaw, CloneDir: hooks.GetReleaseCloneDir(installDir), PromptCtx: promptx, Log: log, diff --git a/githooks/cmd/uninstaller/uninstaller.go b/githooks/cmd/uninstaller/uninstaller.go index 0307ae61..56f24e08 100644 --- a/githooks/cmd/uninstaller/uninstaller.go +++ b/githooks/cmd/uninstaller/uninstaller.go @@ -77,10 +77,11 @@ func defineArguments(cmd *cobra.Command, vi *viper.Viper) { setupMockFlags(cmd, vi) } -func setupSettings(log cm.ILogContext, gitx *git.Context, args *Arguments) (Settings, UISettings) { +func setupSettings(log cm.ILogContext, ctx *ccm.CmdContext, args *Arguments) (Settings, UISettings) { var promptx prompt.IContext var err error + gitx := ctx.GitX log.AssertNoErrorPanic(err, "Could not get current working directory.") @@ -89,24 +90,22 @@ func setupSettings(log cm.ILogContext, gitx *git.Context, args *Arguments) (Sett log.AssertNoErrorF(err, "Prompt setup failed -> using fallback.") } - installDir := install.LoadInstallDir(log, gitx) - // Safety check. - log.PanicIfF(!strings.Contains(installDir, ".githooks"), + log.PanicIfF(!strings.Contains(ctx.InstallDir, ".githooks"), "Uninstall path at '%s' needs to contain '.githooks'.") // Remove temporary directory if existing - tempDir, err := hooks.CleanTemporaryDir(installDir) + tempDir, err := hooks.CleanTemporaryDir(ctx.InstallDir) log.AssertNoErrorPanicF(err, - "Could not clean temporary directory in '%s'", installDir) + "Could not clean temporary directory in '%s'", ctx.InstallDir) - lfsHooksCache, err := hooks.NewLFSHooksCache(hooks.GetTemporaryDir(installDir)) + lfsHooksCache, err := hooks.NewLFSHooksCache(hooks.GetTemporaryDir(ctx.InstallDir)) log.AssertNoErrorPanicF(err, "Could not create LFS hooks cache.") return Settings{ Gitx: gitx, - InstallDir: installDir, - CloneDir: hooks.GetReleaseCloneDir(installDir), + InstallDir: ctx.InstallDir, + CloneDir: hooks.GetReleaseCloneDir(ctx.InstallDir), TempDir: tempDir, UninstalledGitDirs: make(UninstallSet, 10), // nolint: gomnd LFSHooksCache: lfsHooksCache}, @@ -384,7 +383,7 @@ func runUninstall(ctx *ccm.CmdContext, vi *viper.Viper) { log.DebugF("Arguments: %+v", args) - settings, uiSettings := setupSettings(log, ctx.GitX, &args) + settings, uiSettings := setupSettings(log, ctx, &args) if !args.InternalPostDispatch { if isDispatched := runDispatchedInstall(log, &settings, &args); isDispatched { diff --git a/githooks/common/path.go b/githooks/common/path.go index f892f9d8..bc8ef691 100644 --- a/githooks/common/path.go +++ b/githooks/common/path.go @@ -165,15 +165,28 @@ func ReplaceTildeWith(p string, repl string) string { } // ReplaceTilde replaces a prefix tilde '~' character in a path -// with the home dir. -func ReplaceTilde(p string) (string, error) { +// with the home dir, and if `useHome=true` then `$HOME` is inserted if +// the env. variable. +func ReplaceTilde(p string, replaceWithEnv bool) (string, error) { if strings.HasPrefix(p, "~") { - usr, err := homedir.Dir() - if err != nil { - return p, err + usr := os.Getenv("HOME") + + if strs.IsEmpty(usr) { + replaceWithEnv = false + + var err error + usr, err = homedir.Dir() + if err != nil { + return p, err + } + usr = filepath.ToSlash(usr) } - return path.Join(filepath.ToSlash(usr), strings.TrimPrefix(p, "~")), nil + if replaceWithEnv { + return path.Join("$HOME", strings.TrimPrefix(p, "~")), nil + } else { + return path.Join(usr, strings.TrimPrefix(p, "~")), nil + } } return p, nil diff --git a/githooks/git/git.go b/githooks/git/git.go index abd34d54..77894f12 100644 --- a/githooks/git/git.go +++ b/githooks/git/git.go @@ -184,6 +184,19 @@ func (c *Context) GetConfigRegex(regex string, scope ConfigScope) (res []KeyValu func (c *Context) SetConfig(key string, value interface{}, scope ConfigScope) error { cm.DebugAssert(scope != Traverse, "Wrong scope.") + old_value := c.GetConfig(key, scope) + if old_value == value { + // If already the same, dont do anything. + // We dont want to change the `.gitconfig` if we dont need to. + // See https://github.com/gabyx/Githooks/issues/142. + // Careful, we have a cache which gets used, + // if the content in the Git config is different + // after the cache was loaded, we dont set the new value. + // Since we control our own Git config keys and only the CLI and installer set values + // its not such a problem. + return nil + } + s := strs.Fmt("%v", value) if c.cache != nil { c.cache.Set(key, s, scope) diff --git a/githooks/hooks/executables.go b/githooks/hooks/executables.go index 75026aec..ac8f26b5 100644 --- a/githooks/hooks/executables.go +++ b/githooks/hooks/executables.go @@ -9,9 +9,13 @@ import ( cm "github.com/gabyx/githooks/githooks/common" ) +func getCLIExecutablePath(installDir string) string { + return path.Join(GetBinaryDir(installDir), "cli") +} + // GetCLIExecutable gets the global Githooks CLI executable. func GetCLIExecutable(installDir string) cm.Executable { - p := path.Join(GetBinaryDir(installDir), "cli") + p := getCLIExecutablePath(installDir) if runtime.GOOS == cm.WindowsOsName { p += cm.WindowsExecutableSuffix } diff --git a/githooks/hooks/githooks.go b/githooks/hooks/githooks.go index db1dc71d..118f7ef5 100644 --- a/githooks/hooks/githooks.go +++ b/githooks/hooks/githooks.go @@ -10,6 +10,7 @@ import ( cm "github.com/gabyx/githooks/githooks/common" "github.com/gabyx/githooks/githooks/git" strs "github.com/gabyx/githooks/githooks/strings" + "github.com/mitchellh/go-homedir" ) // HooksDirName denotes the directory name used for repository specific hooks. @@ -162,13 +163,64 @@ func GetSharedGithooksDir(repoDir string) (dir string) { return } +// LoadInstallDir loads the install directory and uses a default if +// it does not exist. +func LoadInstallDir(log cm.ILogContext, gitx *git.Context) (installDir string, installDirRaw string) { + + installDir, installDirRaw = GetInstallDirWithRaw(gitx) + + if !cm.IsDirectory(installDir) { + + if strs.IsNotEmpty(installDir) { + log.WarnF("Install directory '%s' does not exist.\n"+ + "Githooks installation is corrupt!\n"+ + "Using default location '~/.githooks'.", installDir) + } + + installDir, installDirRaw = GetDefaultInstallDir() + } + + return +} + +// / Get the default install dir. +func GetDefaultInstallDir() (installDir, installDirRaw string) { + home, err := homedir.Dir() + cm.AssertNoErrorPanic(err, "Could not get home directory.") + home = filepath.ToSlash(home) + + installDir = path.Join(home, HooksDirName) + + if home == filepath.ToSlash(os.Getenv("HOME")) { + // Home env. variable is the same as what we got above. + // use it instead. + installDirRaw = path.Join("$HOME", HooksDirName) + } else { + installDirRaw = path.Join(installDir, HooksDirName) + } + + return +} + // GetInstallDir returns the Githooks install directory. func GetInstallDir(gitx *git.Context) string { - return filepath.ToSlash(gitx.GetConfig(GitCKInstallDir, git.GlobalScope)) + p, _ := GetInstallDirWithRaw(gitx) + + return p +} + +// GetInstallDirWithRaw returns the Githooks install directory, +// but also with unexpanded env. variables form. +func GetInstallDirWithRaw(gitx *git.Context) (string, string) { + raw := filepath.ToSlash(gitx.GetConfig(GitCKInstallDir, git.GlobalScope)) + + return filepath.ToSlash(os.ExpandEnv(raw)), raw } // SetInstallDir sets the global Githooks install directory. func SetInstallDir(gitx *git.Context, path string) error { + cm.DebugAssertF(path == filepath.ToSlash(path), "Filepath must be '/' delimted.") + return gitx.SetConfig(GitCKInstallDir, path, git.GlobalScope) } diff --git a/githooks/hooks/wrapper.go b/githooks/hooks/wrapper.go index 04698867..fbb3c9c1 100644 --- a/githooks/hooks/wrapper.go +++ b/githooks/hooks/wrapper.go @@ -101,7 +101,7 @@ func disableHookIfLFSDetected( return } -// Moves existing hook `dest` to `dir(path)/GetHookReplacementFileName(dest)` +// Moves existing hook `dest` to `dir(dest)/GetHookReplacementFileName(dest)` // if its not a Githooks run-wrapper. // If it is a run-wrapper dont do anything. func moveExistingHooks( diff --git a/githooks/prompt/validation.go b/githooks/prompt/validation.go index 839170b2..26768a9d 100644 --- a/githooks/prompt/validation.go +++ b/githooks/prompt/validation.go @@ -1,6 +1,9 @@ package prompt import ( + "os" + "path/filepath" + cm "github.com/gabyx/githooks/githooks/common" strs "github.com/gabyx/githooks/githooks/strings" @@ -44,7 +47,7 @@ var ValidatorAnswerNotEmpty AnswerValidator = func(s string) error { // which checks existing paths. func CreateValidatorIsDirectory(tildeRepl string) AnswerValidator { return func(s string) error { - s = cm.ReplaceTildeWith(s, tildeRepl) + s = filepath.ToSlash(os.ExpandEnv(cm.ReplaceTildeWith(s, tildeRepl))) if !cm.IsDirectory(s) { return NewValidationError("Answer must be an existing directory.") } diff --git a/tests/steps/step-103.sh b/tests/steps/step-103.sh index 345a175a..7328393a 100755 --- a/tests/steps/step-103.sh +++ b/tests/steps/step-103.sh @@ -118,7 +118,7 @@ fi # Remove all shared hooks "$GH_INSTALL_BIN_DIR/cli" shared purge || exit 1 -echo "Commiting" +echo "Committing" # Make a commit echo A >A || exit 1 git add A || exit 1 @@ -126,7 +126,7 @@ OUTPUT=$(git commit -a -m "Test" 2>&1) # shellcheck disable=SC2181 if [ $? -eq 0 ] || ! echo "$OUTPUT" | grep -q "needs shared hooks in:"; then - echo "! Expected to fail on not availabe shared hooks. output:" + echo "! Expected to fail on not available shared hooks. output:" echo "$OUTPUT" exit 1 fi @@ -136,7 +136,7 @@ OUTPUT=$(GITHOOKS_SKIP_NON_EXISTING_SHARED_HOOKS=true git commit -a -m "Test" 2> # shellcheck disable=SC2181 if [ $? -ne 0 ]; then - echo "! Expected to skip on not availabe shared hooks. output:" + echo "! Expected to skip on not available shared hooks. output:" echo "$OUTPUT" exit 1 fi diff --git a/tests/steps/step-106.sh b/tests/steps/step-106.sh index 2a6fa884..5120979a 100755 --- a/tests/steps/step-106.sh +++ b/tests/steps/step-106.sh @@ -54,7 +54,7 @@ cd "$GH_TEST_TMP/test106-lfs" && if [ -n "$GH_ON_WINDOWS" ]; then # On windows replace the original git-lfs completely, - # because git.exe perturbates the PATH + # because git.exe perturbs the PATH ORIGINAL_GIT_LFS=$(cygpath -m "$(command -v git-lfs)") cp -f "$ORIGINAL_GIT_LFS" "$GH_TEST_TMP/test106-lfs/git-lfs-backup" && cp -f "$GH_TEST_TMP/test106-lfs/git-lfs.exe" "$ORIGINAL_GIT_LFS" || exit 4 diff --git a/tests/steps/step-114.sh b/tests/steps/step-114.sh index 8bd2ce16..52ee5316 100755 --- a/tests/steps/step-114.sh +++ b/tests/steps/step-114.sh @@ -51,7 +51,7 @@ if ! git -C "$GH_TEST_REPO" reset --hard v9.9.1 >/dev/null; then exit 1 fi -rm -rf ~/.githooks/templates/hooks/* # Remove to see if the correct folder gets choosen +rm -rf ~/.githooks/templates/hooks/* # Remove to see if the correct folder gets chosen if ! "$GH_INSTALL_BIN_DIR/cli" update --yes; then echo "! Failed to run the update" diff --git a/tests/steps/step-116.sh b/tests/steps/step-116.sh index 0774d77b..de39bb01 100755 --- a/tests/steps/step-116.sh +++ b/tests/steps/step-116.sh @@ -49,7 +49,7 @@ if ! grep -qE '.+/test *116.1/.git$' "$REGISTER_FILE"; then fi # Test that a first git action registers repo 2 -# and repo 1 ist still registered +# and repo 1 is still registered mkdir -p "$GH_TEST_TMP/test116.2" && cd "$GH_TEST_TMP/test116.2" && git init && git commit --allow-empty -m 'Initial commit' || diff --git a/tests/steps/step-139.sh b/tests/steps/step-139.sh new file mode 100755 index 00000000..5df952fe --- /dev/null +++ b/tests/steps/step-139.sh @@ -0,0 +1,65 @@ +#!/usr/bin/env bash +# Test: +# Run a simple install and install,check env. vars in `installDir`. +# https://github.com/gabyx/Githooks/issues/142 + +TEST_DIR=$(cd "$(dirname "$0")/.." && pwd) +# shellcheck disable=SC1091 +. "$TEST_DIR/general.sh" + +acceptAllTrustPrompts || exit 1 + +# run the default install +"$GH_TEST_BIN/cli" installer || exit 1 + +installDir=$(git config githooks.installDir) +# shellcheck disable=SC2088,SC2016 +if ! echo "$installDir" | grep '\$HOME'; then + echo "! Expected ~/ to be part of install dir: $installDir" + exit 1 +fi + +# Make some whitespace changes to the global gitconfig +# to test that it does not get updated. +sed -i -E "s/githooks\.installDir/ githooks\.installDir/g" ~/.gitconfig || exit 1 +mkdir -p "$GH_TEST_TMP/test139" && cp ~/.gitconfig "$GH_TEST_TMP/test139/" + +# Set server to 9.9.1 to trigger update. +if ! git -C "$GH_TEST_REPO" reset --hard v9.9.1 >/dev/null; then + echo "! Could not reset server to trigger update." + exit 1 +fi + +# Update to version 9.9.1 +echo "Update to version 9.9.1" +CURRENT="$(git -C ~/.githooks/release rev-parse HEAD)" +if ! "$GH_INSTALL_BIN_DIR/cli" update --yes; then + echo "! Failed to run the update" +fi +AFTER="$(git -C ~/.githooks/release rev-parse HEAD)" + +if [ "$CURRENT" = "$AFTER" ] || + [ "$(git -C "$GH_TEST_REPO" rev-parse v9.9.1)" != "$AFTER" ]; then + echo "! Release clone was not updated, but it should have!" + exit 1 +fi + +if ! git diff --exit-code \ + ~/.gitconfig "$GH_TEST_TMP/test139/.gitconfig"; then + + echo "! Update suddenly changes the Git config, it should not have changed: Output:" + git diff ~/.gitconfig "$GH_TEST_TMP/test139/.gitconfig" + + exit 1 +fi + +# Install again with prefix and check if the raw entered install directory is +# maintained. +"$GH_TEST_BIN/cli" installer --prefix "\$GH_TEST_TMP/test139" || exit 1 + +installDir=$(git config githooks.installDir) +if ! echo "$installDir" | grep "\$GH_TEST_TMP"; then + echo "! Expected \$GH_TEST_TMP to be part of install dir: $installDir" + cat ~/.gitconfig + exit 1 +fi