Skip to content

Commit

Permalink
feat: add stage_fixed option (#445)
Browse files Browse the repository at this point in the history
  • Loading branch information
mrexox authored Mar 15, 2023
1 parent 74600a8 commit 1ea9571
Show file tree
Hide file tree
Showing 8 changed files with 221 additions and 18 deletions.
22 changes: 22 additions & 0 deletions docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,15 @@
- [`root`](#root)
- [`exclude`](#exclude)
- [`fail_text`](#fail_text)
- [`stage_fixed`](#stage_fixed)
- [`interactive`](#interactive)
- [Script](#script)
- [`runner`](#runner)
- [`skip`](#skip)
- [`tags`](#tags)
- [`env`](#env)
- [`fail_text`](#fail_text)
- [`stage_fixed`](#stage_fixed)
- [`interactive`](#interactive)
- [Examples](#examples)
- [More info](#more-info)
Expand Down Expand Up @@ -901,6 +903,26 @@ SUMMARY: (done in 0.01 seconds)
🥊 lint: Add node executable to $PATH env
```

### `stage_fixed`

**Default: `false`**

> Used **only for `pre-commit`** hook. Is ignored for other hooks.

When set to `true` lefthook will automatically call `git add` on files after running the command or script. For a command if [`files`](#files) option was specified, the specified command will be used to retrieve files for `git add`. For scripts and commands without [`files`](#files) option `{staged_files}` template will be used. All filters ([`glob`](#glob), [`exclude`](#exclude)) will be applied if specified.

**Example**

```yml
# lefthook.yml
pre-commit:
commands:
lint:
run: npm run lint --fix {staged_files}
stage_fixed: true
```

### `interactive`

**Default: `false`**
Expand Down
1 change: 1 addition & 0 deletions internal/config/command.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ type Command struct {

FailText string `mapstructure:"fail_text"`
Interactive bool `mapstructure:"interactive"`
StageFixed bool `mapstructure:"stage_fixed"`
}

func (c Command) Validate() error {
Expand Down
1 change: 1 addition & 0 deletions internal/config/script.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ type Script struct {

FailText string `mapstructure:"fail_text"`
Interactive bool `mapstructure:"interactive"`
StageFixed bool `mapstructure:"stage_fixed"`
}

func (s Script) DoSkip(gitState git.State) bool {
Expand Down
12 changes: 12 additions & 0 deletions internal/git/repository.go
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,18 @@ func (r *Repository) DropUnstagedStash() error {
return nil
}

func (r *Repository) AddFiles(files []string) error {
if len(files) == 0 {
return nil
}

_, err := r.Git.CmdArgs(
append([]string{"git", "add"}, files...)...,
)

return err
}

// FilesByCommand accepts git command and returns its result as a list of filepaths.
func (r *Repository) FilesByCommand(command string) ([]string, error) {
lines, err := r.Git.CmdLines(command)
Expand Down
2 changes: 1 addition & 1 deletion internal/lefthook/runner/executor.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ import (
type ExecuteOptions struct {
name, root, failText string
args []string
interactive bool
env map[string]string
interactive bool
}

// Executor provides an interface for command execution.
Expand Down
19 changes: 14 additions & 5 deletions internal/lefthook/runner/prepare_command.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,12 @@ import (
"github.com/evilmartians/lefthook/internal/log"
)

func (r *Runner) prepareCommand(name string, command *config.Command) ([]string, error) {
type commandArgs struct {
all []string
files []string
}

func (r *Runner) prepareCommand(name string, command *config.Command) (*commandArgs, error) {
if command.Skip != nil && command.DoSkip(r.Repo.State()) {
return nil, errors.New("settings")
}
Expand All @@ -34,14 +39,14 @@ func (r *Runner) prepareCommand(name string, command *config.Command) ([]string,
log.Error(err)
return nil, errors.New("error")
}
if len(args) == 0 {
if args == nil || len(args.all) == 0 {
return nil, errors.New("no files for inspection")
}

return args, nil
}

func (r *Runner) buildCommandArgs(command *config.Command) ([]string, error) {
func (r *Runner) buildCommandArgs(command *config.Command) (*commandArgs, error) {
filesCommand := r.Hook.Files
if command.Files != "" {
filesCommand = command.Files
Expand All @@ -56,6 +61,7 @@ func (r *Runner) buildCommandArgs(command *config.Command) ([]string, error) {
},
}

filteredFiles := []string{}
runString := command.Run
for filesType, filesFn := range filesTypeToFn {
// Checking substitutions and skipping execution if it is empty.
Expand All @@ -76,7 +82,7 @@ func (r *Runner) buildCommandArgs(command *config.Command) ([]string, error) {
if len(filesPrepared) == 0 {
return nil, nil
}

filteredFiles = append(filteredFiles, filesPrepared...)
runString = replaceQuoted(runString, filesType, filesPrepared)
}
}
Expand All @@ -88,7 +94,10 @@ func (r *Runner) buildCommandArgs(command *config.Command) ([]string, error) {

log.Debug("[lefthook] executing: ", runString)

return strings.Split(runString, " "), nil
return &commandArgs{
files: filteredFiles,
all: strings.Split(runString, " "),
}, nil
}

func prepareFiles(command *config.Command, files []string) []string {
Expand Down
42 changes: 36 additions & 6 deletions internal/lefthook/runner/runner.go
Original file line number Diff line number Diff line change
Expand Up @@ -279,14 +279,26 @@ func (r *Runner) runScript(script *config.Script, path string, file os.FileInfo)
defer log.StartSpinner()
}

r.run(ExecuteOptions{
finished := r.run(ExecuteOptions{
name: file.Name(),
root: r.Repo.RootPath,
args: args,
failText: script.FailText,
interactive: script.Interactive && !r.DisableTTY,
env: script.Env,
}, r.Hook.Follow)

if finished && config.HookUsesStagedFiles(r.HookName) && script.StageFixed {
files, err := r.Repo.StagedFiles()
if err != nil {
log.Warn("Couldn't stage fixed files:", err)
return
}

if err := r.Repo.AddFiles(files); err != nil {
log.Warn("Couldn't stage fixed files:", err)
}
}
}

func (r *Runner) runCommands() {
Expand Down Expand Up @@ -346,17 +358,33 @@ func (r *Runner) runCommand(name string, command *config.Command) {
defer log.StartSpinner()
}

r.run(ExecuteOptions{
finished := r.run(ExecuteOptions{
name: name,
root: filepath.Join(r.Repo.RootPath, command.Root),
args: args,
args: args.all,
failText: command.FailText,
interactive: command.Interactive && !r.DisableTTY,
env: command.Env,
}, r.Hook.Follow)

if finished && config.HookUsesStagedFiles(r.HookName) && command.StageFixed {
files := args.files
if len(files) == 0 {
stagedFiles, err := r.Repo.StagedFiles()
if err != nil {
log.Warn("Couldn't stage fixed files:", err)
return
}
files = prepareFiles(command, stagedFiles)
}

if err := r.Repo.AddFiles(files); err != nil {
log.Warn("Couldn't stage fixed files:", err)
}
}
}

func (r *Runner) run(opts ExecuteOptions, follow bool) {
func (r *Runner) run(opts ExecuteOptions, follow bool) bool {
log.SetName(opts.name)
defer log.UnsetName(opts.name)

Expand All @@ -368,7 +396,7 @@ func (r *Runner) run(opts ExecuteOptions, follow bool) {
} else {
r.success(opts.name)
}
return
return err == nil
}

out := bytes.NewBuffer(make([]byte, 0))
Expand All @@ -384,14 +412,16 @@ func (r *Runner) run(opts ExecuteOptions, follow bool) {
}

if err == nil && r.SkipSettings.SkipExecution() {
return
return false
}

log.Infof("%s\n%s", execName, out)
if err != nil {
log.Infof("%s", err)
}
log.Infof("\n")

return err == nil
}

// Returns whether two arrays have at least one similar element.
Expand Down
Loading

0 comments on commit 1ea9571

Please sign in to comment.