Skip to content

Commit

Permalink
Add maintainable hooks (#74)
Browse files Browse the repository at this point in the history
- Added `--maintained-hooks` to the installer. Replacing `--only-server-hooks := --maintained-hooks server`.
- CLI integeration is still missing.
  • Loading branch information
gabyx authored Dec 12, 2021
1 parent cf5ef7b commit e8ed021
Show file tree
Hide file tree
Showing 27 changed files with 1,021 additions and 151 deletions.
5 changes: 2 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -795,11 +795,10 @@ git config --global core.hooksPath "$(pwd)/hooks"
### Install on the Server

On a server infrastructure where only _bare_ repositories are maintained, it is
best to maintain only server hooks. This can be achieved by installing with the
additional flag `--only-server-hooks` by:
best to maintain only server hooks. This can be achieved by installing with:

```shell
$ cli installer --only-server-hooks
$ cli installer ---maintained-hooks "server"
```

The global template directory then **only** contains the following run-wrappers
Expand Down
12 changes: 10 additions & 2 deletions docs/cli/git_hooks_installer.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,14 @@ git hooks installer [flags]
--prefix string Githooks installation prefix such that
`<prefix>/.githooks` will be the installation directory.
--template-dir string The preferred template directory to use.
--only-server-hooks Only install and maintain server hooks.
--maintained-hooks strings A set of hook names which are maintained in the template directory.
Any argument can be a hook name `<hookName>`, `all` or `server`.
An optional prefix '!' means subtraction from the current set.
The initial value of the internally built set defaults
to all hook names if `all` or `server` is not given as first argument:
- `all` : All hooks supported by Githooks.
- `server` : Only server hooks supported by Githooks.
You can list them seperatly or comma-separated in one argument.
--use-core-hookspath If the install mode `core.hooksPath` should be used.
--clone-url string The clone url from which Githooks should clone
and install/update itself. Githooks tries to
Expand All @@ -39,7 +46,8 @@ git hooks installer [flags]
`--clone-url`. See the documentation for further details.
--build-from-source If the binaries are built from source instead of
downloaded from the deploy url.
--build-tags stringArray Build tags for building from source (get extended with defaults).
--build-tags strings Build tags for building from source (get extended with defaults).
You can list them seperatly or comma-separated in one argument.
--use-pre-release When fetching the latest installer, also consider pre-release versions.
-h, --help help for installer
```
Expand Down
55 changes: 30 additions & 25 deletions githooks/cmd/common/install/wrappers.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
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"
)

// InstallIntoRepo installs run-wrappers into a repositories
Expand All @@ -17,6 +18,7 @@ func InstallIntoRepo(
log cm.ILogContext,
gitx *git.Context,
repoGitDir string,
lfsHooksCache hooks.LFSHooksCache,
nonInteractive bool,
dryRun bool,
skipReadme bool,
Expand All @@ -28,29 +30,39 @@ func InstallIntoRepo(
log.AssertNoErrorPanic(err,
"Could not create hook directory in '%s'.", repoGitDir)
}
gitxR := git.CtxC(repoGitDir)
isBare := gitxR.IsBareRepo()

isBare := git.CtxC(repoGitDir).IsBareRepo()

var hookNames []string
hookNames, err := hooks.GetMaintainedHooks(gitxR, git.Traverse)
log.AssertNoErrorF(err, "Could not get maintained hooks.")
if isBare {
hookNames = hooks.ManagedServerHookNames
} else {
hookNames = hooks.ManagedHookNames
// Filter out all non-relevant hooks for bare repositories.
hookNames = strs.Filter(hookNames, func(s string) bool { return strs.Includes(hooks.ManagedServerHookNames, s) })
// LFS hooks also do not need to be reinstalled
lfsHooksCache = nil
}

if dryRun {
log.InfoF("[dry run] Hooks would have been installed into\n'%s'.",
repoGitDir)
} else {

err := hooks.InstallRunWrappers(
nLFSHooks, err := hooks.InstallRunWrappers(
hookDir, hookNames,
nil,
GetHookDisableCallback(log, gitx, nonInteractive, uiSettings),
lfsHooksCache,
nil)

log.AssertNoErrorPanicF(err, "Could not install run-wrappers into '%s'.", hookDir)
log.InfoF("Githooks run-wrappers installed into '%s'.", hookDir)

if nLFSHooks != 0 {
log.InfoF("Installed '%v' Githooks run-wrapper(s) and '%v' missing LFS hooks into '%s'.",
len(hookNames), nLFSHooks, hookDir)
} else {
log.InfoF("Installed '%v' Githooks run-wrapper(s) into '%s'",
len(hookNames), hookDir)
}
}

// Offer to setup the intro README if running in interactive mode
Expand Down Expand Up @@ -102,32 +114,21 @@ func unregisterRepo(log cm.ILogContext, gitDir string) {
func UninstallFromRepo(
log cm.ILogContext,
gitDir string,
lfsAvailable bool,
lfsHooksCache hooks.LFSHooksCache,
cleanArtefacts bool) bool {

hookDir := path.Join(gitDir, "hooks")

var err error
var nLfsCount int

if cm.IsDirectory(hookDir) {

err := hooks.UninstallRunWrappers(hookDir, hooks.ManagedHookNames)
nLfsCount, err = hooks.UninstallRunWrappers(hookDir, lfsHooksCache)

log.AssertNoErrorF(err,
"Could not uninstall Githooks run-wrappers from\n'%s'.",
hookDir)

if err == nil {

if lfsAvailable {
err = hooks.InstallLFSHooks(gitDir)

log.AssertNoErrorF(err,
"Could not reinstall Git LFS hooks in\n"+
"'%[1]s'.\n"+
"Please try manually by invoking:\n"+
" $ git -C '%[1]s' lfs install", gitDir)

}
}
}

// Always unregister repo.
Expand All @@ -138,7 +139,11 @@ func UninstallFromRepo(
cleanGitConfigInRepo(log, gitDir)
}

log.InfoF("Githooks uninstalled from '%s'.", gitDir)
if nLfsCount != 0 {
log.InfoF("Githooks uninstalled from '%s'.\nLFS hooks have been reinstalled.", gitDir)
} else {
log.InfoF("Githooks uninstalled from '%s'.", gitDir)
}

return true
}
2 changes: 1 addition & 1 deletion githooks/cmd/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -370,7 +370,7 @@ func runTrustAllHooks(ctx *ccm.CmdContext, opts *SetOptions) {

}

// RunUpdate runs an automatic Githooks update.
// RunUpdate enables/disables automatic Githooks update.
func RunUpdate(ctx *ccm.CmdContext, opts *SetOptions) {
const text = "Automatic Githooks update"

Expand Down
12 changes: 9 additions & 3 deletions githooks/cmd/install/install.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,11 @@ func runInstallIntoRepo(ctx *ccm.CmdContext, nonInteractive bool) {
hooks.GitCKUseCoreHooksPath, value, gitDir)

uiSettings := inst.UISettings{PromptCtx: ctx.PromptCtx}
inst.InstallIntoRepo(ctx.Log, ctx.GitX, gitDir, nonInteractive, false, false, &uiSettings)

lfsHooksCache, err := hooks.NewLFSHooksCache(hooks.GetTemporaryDir(ctx.InstallDir))
ctx.Log.AssertNoErrorPanicF(err, "Could not create LFS hooks cache.")

inst.InstallIntoRepo(ctx.Log, ctx.GitX, gitDir, lfsHooksCache, nonInteractive, false, false, &uiSettings)
}

func runUninstallFromRepo(ctx *ccm.CmdContext) {
Expand All @@ -39,8 +43,10 @@ func runUninstallFromRepo(ctx *ccm.CmdContext) {
ctx.Log.AssertNoErrorPanicF(err, "Could not load register file in '%s'.",
ctx.InstallDir)

lfsIsAvailable := git.IsLFSAvailable()
if inst.UninstallFromRepo(ctx.Log, gitDir, lfsIsAvailable, false) {
lfsHooksCache, err := hooks.NewLFSHooksCache(hooks.GetTemporaryDir(ctx.InstallDir))
ctx.Log.AssertNoErrorPanicF(err, "Could not create LFS hooks cache.")

if inst.UninstallFromRepo(ctx.Log, gitDir, lfsHooksCache, false) {

registeredGitDirs.Remove(gitDir)
err := registeredGitDirs.Store(ctx.InstallDir)
Expand Down
2 changes: 1 addition & 1 deletion githooks/cmd/installer/args.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ type Arguments struct {

SkipInstallIntoExisting bool // Skip install into existing repositories.

OnlyServerHooks bool // Only maintain server hooks.
MaintainedHooks []string // Maintain hooks by Githooks.

UseCoreHooksPath bool // Use the `core.hooksPath` for the template dir.

Expand Down
81 changes: 55 additions & 26 deletions githooks/cmd/installer/installer.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,9 +97,16 @@ func defineArguments(cmd *cobra.Command, vi *viper.Viper) {
cmd.PersistentFlags().String(
"template-dir", "",
"The preferred template directory to use.")
cmd.PersistentFlags().Bool(
"only-server-hooks", false,
"Only install and maintain server hooks.")
cmd.PersistentFlags().StringSlice(
"maintained-hooks", nil,
"A set of hook names which are maintained in the template directory.\n"+
"Any argument can be a hook name '<hookName>', 'all' or 'server'.\n"+
"An optional prefix '!' means subtraction from the current set.\n"+
"The initial value of the internally built set defaults\n"+
"to all hook names if 'all' or 'server' is not given as first argument:\n"+
" - 'all' : All hooks supported by Githooks.\n"+
" - 'server' : Only server hooks supported by Githooks.\n"+
"You can list them seperatly or comma-separated in one argument.")
cmd.PersistentFlags().Bool(
"use-core-hookspath", false,
"If the install mode 'core.hooksPath' should be used.")
Expand Down Expand Up @@ -129,9 +136,10 @@ func defineArguments(cmd *cobra.Command, vi *viper.Viper) {
"build-from-source", false,
"If the binaries are built from source instead of\n"+
"downloaded from the deploy url.")
cmd.PersistentFlags().StringArray(
cmd.PersistentFlags().StringSlice(
"build-tags", nil,
"Build tags for building from source (get extended with defaults).")
"Build tags for building from source (get extended with defaults).\n"+
"You can list them seperatly or comma-separated in one argument.")

cmd.PersistentFlags().Bool(
"use-pre-release", false,
Expand All @@ -150,7 +158,7 @@ func defineArguments(cmd *cobra.Command, vi *viper.Viper) {
cm.AssertNoErrorPanic(
vi.BindPFlag("skipInstallIntoExisting", cmd.PersistentFlags().Lookup("skip-install-into-existing")))
cm.AssertNoErrorPanic(
vi.BindPFlag("onlyServerHooks", cmd.PersistentFlags().Lookup("only-server-hooks")))
vi.BindPFlag("maintainedHooks", cmd.PersistentFlags().Lookup("maintained-hooks")))
cm.AssertNoErrorPanic(
vi.BindPFlag("useCoreHooksPath", cmd.PersistentFlags().Lookup("use-core-hookspath")))
cm.AssertNoErrorPanic(
Expand Down Expand Up @@ -194,6 +202,10 @@ func validateArgs(log cm.ILogContext, cmd *cobra.Command, args *Arguments) {
"You cannot build binaries from source together with specifying\n",
"a deploy settings file or deploy api.")

var err error
args.MaintainedHooks, err = hooks.CheckHookNames(args.MaintainedHooks)
log.AssertNoErrorPanic(err,
"Maintained hooks are not valid.")
}

func setupSettings(
Expand Down Expand Up @@ -232,11 +244,15 @@ func setupSettings(
log.AssertNoErrorPanicF(err,
"Could not clean temporary directory in '%s'", installDir)

lfsHooksCache, err := hooks.NewLFSHooksCache(hooks.GetTemporaryDir(installDir))
log.AssertNoErrorPanicF(err, "Could not setup LFS hooks cache.")

return Settings{
GitX: gitx,
InstallDir: installDir,
CloneDir: hooks.GetReleaseCloneDir(installDir),
TempDir: tempDir,
LFSHooksCache: lfsHooksCache,
InstalledGitDirs: make(InstallSet, 10)}, // nolint: gomnd
install.UISettings{PromptCtx: promptx}
}
Expand Down Expand Up @@ -812,7 +828,8 @@ func setupHookTemplates(
hookTemplateDir string,
cloneDir string,
tempDir string,
onlyServerHooks bool,
maintainedHooks []string,
lfsHooksCache hooks.LFSHooksCache,
nonInteractive bool,
dryRun bool,
uiSettings *install.UISettings) {
Expand All @@ -826,30 +843,41 @@ func setupHookTemplates(
log.InfoF("Installing Git hook templates into '%s'.",
hookTemplateDir)

var hookNames []string
if onlyServerHooks {
hookNames = hooks.ManagedServerHookNames
} else {
hookNames = hooks.ManagedHookNames
log.InfoF("Saving Githooks run-wrapper to '%s' :", hookTemplateDir)

var err error
if maintainedHooks == nil {
maintainedHooks, err = hooks.GetMaintainedHooks(gitx, git.GlobalScope)
log.AssertNoError(err, "Could not get config.")
}

log.InfoF("Saving Githooks run-wrapper to '%s' :", hookTemplateDir)
allHooks, err := hooks.UnwrapHookNames(maintainedHooks)
log.AssertNoErrorPanic(err, "Could not build maintained hook list.")

err := hooks.InstallRunWrappers(
nLFSHooks, err := hooks.InstallRunWrappers(
hookTemplateDir,
hookNames,
allHooks,
func(dest string) {
log.InfoF(" %s '%s'", cm.ListItemLiteral, path.Base(dest))
},
install.GetHookDisableCallback(log, gitx, nonInteractive, uiSettings),
lfsHooksCache,
log)

log.AssertNoErrorPanicF(err, "Could not install run-wrappers into '%s'.", hookTemplateDir)

if onlyServerHooks {
err := git.Ctx().SetConfig(hooks.GitCKMaintainOnlyServerHooks, true, git.GlobalScope)
log.AssertNoErrorPanic(err, "Could not set Git config 'githooks.maintainOnlyServerHooks'.")
if nLFSHooks != 0 {
log.InfoF("Installed '%v' Githooks run-wrappers and '%v' missing LFS hooks into '%s'.",
len(allHooks), nLFSHooks, hookTemplateDir)
} else {
log.InfoF("Installed '%v' Githooks run-wrappers into '%s'.",
len(allHooks), hookTemplateDir)
}

// Set maintained hooks in global settings, such that
// local repository Githooks installs are in alignment
// to the setup template directory.
err = hooks.SetMaintainedHooks(gitx, maintainedHooks, git.GlobalScope)
log.AssertNoError(err, "Could not set git config.")
}

func installBinaries(
Expand Down Expand Up @@ -953,7 +981,7 @@ func setupAutomaticUpdate(
func installIntoExistingRepos(
log cm.ILogContext,
gitx *git.Context,
tempDir string,
lfsHooksCache hooks.LFSHooksCache,
nonInteractive bool,
dryRun bool,
skipReadme bool,
Expand All @@ -972,7 +1000,7 @@ func installIntoExistingRepos(
func(gitDir string) {

if install.InstallIntoRepo(
log, gitx, gitDir,
log, gitx, gitDir, lfsHooksCache,
nonInteractive, dryRun,
skipReadme, uiSettings) {

Expand All @@ -986,7 +1014,7 @@ func installIntoExistingRepos(
func installIntoRegisteredRepos(
log cm.ILogContext,
gitx *git.Context,
tempDir string,
lfsHooksCache hooks.LFSHooksCache,
nonInteractive bool,
dryRun bool,
skipReadme bool,
Expand All @@ -1012,7 +1040,7 @@ func installIntoRegisteredRepos(
uiSettings.PromptCtx,
func(gitDir string) {
if install.InstallIntoRepo(
log, gitx, gitDir,
log, gitx, gitDir, lfsHooksCache,
nonInteractive, dryRun,
skipReadme, uiSettings) {

Expand Down Expand Up @@ -1204,7 +1232,8 @@ func runUpdate(
settings.HookTemplateDir,
settings.CloneDir,
settings.TempDir,
args.OnlyServerHooks,
args.MaintainedHooks,
settings.LFSHooksCache,
args.NonInteractive,
args.DryRun,
uiSettings)
Expand All @@ -1219,7 +1248,7 @@ func runUpdate(
installIntoExistingRepos(
log,
gitx,
settings.TempDir,
settings.LFSHooksCache,
args.NonInteractive,
args.DryRun,
false,
Expand All @@ -1233,7 +1262,7 @@ func runUpdate(
installIntoRegisteredRepos(
log,
gitx,
settings.TempDir,
settings.LFSHooksCache,
args.NonInteractive,
args.DryRun,
args.InternalAutoUpdate, // skipReadme if auto-update.
Expand Down
Loading

0 comments on commit e8ed021

Please sign in to comment.