Skip to content

Commit

Permalink
use lib/; my-cmd hooks; use printf; more vendor filtering (#13)
Browse files Browse the repository at this point in the history
* Refactored to use include files, removing duplicate code
* Adds my-cmd hooks to invoke custom tools
* Replaces all usages of echo with printf
* Adds more vendor filtering
* Updates project description
* Reverts copy/pasta in go-test hook docs added in gosec commit
* Adds shellcheck hook to lint project files
* Adds shfmt hook to normalize formatting in project files
* Updates copyright year in LICENSE file
  • Loading branch information
TekWizely authored Aug 6, 2021
1 parent ad0f10b commit 6344f32
Show file tree
Hide file tree
Showing 49 changed files with 645 additions and 1,231 deletions.
35 changes: 34 additions & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,43 @@
# See https://pre-commit.com/hooks.html for more hooks
# ==============================================================================
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v2.0.0
hooks:
- id: check-merge-conflict
- id: trailing-whitespace
- id: end-of-file-fixer
- id: check-yaml
# Bash helper hooks (local)
#
- repo: local
hooks:
# shellcheck (aliased to shck)
#
- id: shellcheck
name: Run static analysis (shellcheck)
entry: shellcheck
language: system
files: \.(sh|bash)$
types: [file]
alias: shck
args: [ '-x' ]
# shfmt
#
- id: shfmt
name: Run lint check (shfmt -d)
entry: shfmt
language: system
files: \.(sh|bash)$
types: [file]
args: [ '-i', '0', '-ci', '-sr', '-d' ]
# shfmt -w (must manually invoke)
#
- id: shfmtw
name: Auto-fix lint errors (shfmt -w)
entry: shfmt
language: system
stages: [manual]
files: \.(sh|bash)$
types: [file]
args: [ '-i', '0', '-ci', '-sr', '-kp', '-w' ]
95 changes: 95 additions & 0 deletions .pre-commit-hooks.yaml
Original file line number Diff line number Diff line change
@@ -1,3 +1,98 @@
# ==============================================================================
# my-cmd
# * File-based
# * Executes if any .go files modified
# ==============================================================================
- id: my-cmd
name: 'my-cmd'
entry: my-cmd.sh
types: [go]
exclude: '(^|/)vendor/'
language: 'script'
description: "Run '$ARGS[0] [$ARGS[1:]] $FILE' for each staged .go file"
pass_filenames: true

# ==============================================================================
# my-cmd-mod
# * Folder-Based
# * Recursive
# * Targets first parent folder with a go.mod file
# * Executes if any .go files modified
# * Executes if go.mod modified
# ==============================================================================
- id: my-cmd-mod
name: 'my-cmd-mod'
entry: my-cmd-mod.sh
files: '(\.go$)|(\bgo\.mod$)'
exclude: '(^|/)vendor/'
language: 'script'
description: "Run 'cd $(mod_root $FILE); $ARGS[0] [$ARGS[1:]] ./...' for each staged .go file"
pass_filenames: true
require_serial: true

# ==============================================================================
# my-cmd-pkg
# * Folder-Based
# * Targets folder containing staged file
# * Executes if any .go files modified
# ==============================================================================
- id: my-cmd-pkg
name: 'my-cmd-pkg'
entry: my-cmd-pkg.sh
types: [go]
exclude: '(^|/)vendor/'
language: 'script'
description: "Run '$ARGS[0] [$ARGS[1:]] ./$(dirname $FILE)' for each staged .go file"
pass_filenames: true
require_serial: true

# ==============================================================================
# my-cmd-repo
# * Repo-Based
# * Recursive
# * Executes if any .go files modified
# ==============================================================================
- id: my-cmd-repo
name: 'my-cmd-repo'
entry: my-cmd-repo.sh
types: [go]
exclude: '(^|/)vendor/'
language: 'script'
description: "Run '$ARGS[0] [$ARGS[1:]]' in the repo root folder"
pass_filenames: false

# ==============================================================================
# my-cmd-repo-mod
# * Repo-Based
# * Recursive
# * Targets ALL folders with a go.mod file
# * Executes if any .go files modified
# * Executes if go.mod modified
# ==============================================================================
- id: my-cmd-repo-mod
name: 'my-cmd-repo-mod'
entry: my-cmd-repo-mod.sh
files: '(\.go$)|(\bgo\.mod$)'
exclude: '(^|/)vendor/'
language: 'script'
description: "Run 'cd $(mod_root); $ARGS[0] [$ARGS[1:]] /...' for each module in the repo"
pass_filenames: false

# ==============================================================================
# my-cmd-repo-pkg
# * Repo-Based
# * Recursive
# * Executes if any .go files modified
# ==============================================================================
- id: my-cmd-repo-pkg
name: 'my-cmd-repo-pkg'
entry: my-cmd-repo-pkg.sh
types: [go]
exclude: '(^|/)vendor/'
language: 'script'
description: "Run '$ARGS[0] [$ARGS[1:]] ./...' in repo root folder"
pass_filenames: false

# ==============================================================================
# go-build-mod
# * Folder-Based
Expand Down
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
MIT License

Copyright (c) 2019 TekWize.ly
Copyright (c) 2019-2021 TekWize.ly

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
99 changes: 93 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# Pre-Commit-GoLang [![MIT license](https://img.shields.io/badge/License-MIT-green.svg)](https://github.com/tekwizely/pre-commit-golang/blob/master/LICENSE)

A set of git pre-commit hooks for Golang with support for Modules.
A set of git pre-commit hooks for Golang with support for multi-module monorepos, the ability to pass arguments to all hooks, and the ability to invoke custom go tools.

Requires the [Pre-Commit.com](https://pre-commit.com) Hook Management framework.
Requires the [Pre-Commit.com](https://pre-commit.com) Hook Management Framework.

---------------
## Installation
Expand Down Expand Up @@ -89,6 +89,19 @@ You can copy/paste the following snippet into your `.pre-commit-config.yaml` fil
- id: golangci-lint-pkg
- id: golangci-lint-repo-mod
- id: golangci-lint-repo-pkg
#
# Invoking Custom Go Tools
# - Configured *entirely* through the `args` attribute, ie:
# args: [ go, test ]
# - Use the `name` attribute to provide better messaging when the hook runs
# - Use the `alias` attribute to be able invoke your hook via `pre-commit run`
#
- id: my-cmd
- id: my-cmd-mod
- id: my-cmd-pkg
- id: my-cmd-repo
- id: my-cmd-repo-mod
- id: my-cmd-repo-pkg
```
-----------
Expand Down Expand Up @@ -127,6 +140,7 @@ Hooks have suffixes in their name that indicate their targets:
| \<none> | Files | Targets staged files directly |
| `-mod` | Module | Targets module root folders of staged `.go` files |
| `-pkg` | Package | Targets folders containing staged `.go` files |
| `-repo` | Repo Root | Targets the repo root folder |
| `-repo-mod` | All Modules | Targets all module root folders in the repo |
| `-repo-pkg` | All Packages | Targets all package folders in the repo |

Expand All @@ -136,6 +150,14 @@ Due to OS command-line-length limits, Pre-Commit can invoke a hook multiple time

For file and repo-based hooks, this isn't an issue, but for module and package-based hooks, there is a potential for the hook to run against the same module or package multiple times, duplicating any errors or warnings.

-------------------------
### Invoking Custom Tools
While this project includes builtin hooks for many popular go tools, it's not possible to include builtin hooks for every tool that users might want to use.

To help accommodate those users, this project includes the ability to invoke custom go tools.

See the [my-cmd](#my-cmd) hooks for more information.

--------------------------
### Useful Hook Parameters
```
Expand Down Expand Up @@ -190,6 +212,8 @@ This can be useful, for example, for hooks that display warnings, but don't gene
- [go-revive](#go-revive)
- GolangCI-Lint
- [golangci-lint](#golangci-lint)
- Invoking Custom Tools
- [my-cmd](#my-cmd)
------------
### go-build
Expand All @@ -215,10 +239,10 @@ Automates testing, printing a summary of test resutls.
| Hook ID | Description
|--------------------|------------
| `go-test-mod` | Run `'cd $(mod_root $FILE); gosec [$ARGS] ./...'` for each staged .go file
| `go-test-pkg` | Run `'gosec [$ARGS] ./$(dirname $FILE)'` for each staged .go file
| `go-test-repo-mod` | Run `'cd $(mod_root); gosec [$ARGS] ./...'` for each module in the repo
| `go-test-repo-pkg` | Run `'gosec [$ARGS] ./...'` in repo root folder
| `go-test-mod` | Run `'cd $(mod_root $FILE); go test [$ARGS] ./...'` for each staged .go file
| `go-test-pkg` | Run `'go test [$ARGS] ./$(dirname $FILE)'` for each staged .go file
| `go-test-repo-mod` | Run `'cd $(mod_root); go test [$ARGS] ./...'` for each module in the repo
| `go-test-repo-pkg` | Run `'go test [$ARGS] ./...'` in repo root folder
##### Install
Comes with Golang ( [golang.org](https://golang.org/) )
Expand Down Expand Up @@ -484,6 +508,69 @@ bingo install github.com/golangci/golangci-lint/cmd/golangci-lint
- https://github.com/golangci/golangci-lint#config-file
- `golangci-lint config -h`
----------
### my-cmd
Using the `my-cmd-*` hooks, you can invoke custom go tools in various contexts.
| Hook ID | Description
|-------------------|------------
| `my-cmd` | Run `'$ARGS[0] [$ARGS[1:]] $FILE'` for each staged .go file
| `my-cmd-mod` | Run `'cd $(mod_root $FILE); $ARGS[0] [$ARGS[1:]] ./...'` for each staged .go file
| `my-cmd-pkg` | Run `'$ARGS[0] [$ARGS[1:]] ./$(dirname $FILE)'` for each staged .go file
| `my-cmd-repo` | Run `'$ARGS[0] [$ARGS[1:]]'` in the repo root folder
| `my-cmd-repo-mod` | Run `'cd $(mod_root); $ARGS[0] [$ARGS[1:]] /...'` for each module in the repo
| `my-cmd-repo-pkg` | Run `'$ARGS[0] [$ARGS[1:]] ./...'` in repo root folder
#### Configuring the hooks
The my-cmd hooks are configured **entirely** through the pre-commit `args` attribute, including specifying which tool to run (ie `$ARGS[0]` above)
#### Examples
Here's an example of what it would look like to use the my-cmd hooks to invoke `go test` if it wasn't already included:
_.pre-commit-config.yaml_
```
# ...
hooks:
# Run 'cd $(mod_root $FILE); go test ./...' for each staged .go file
- id: my-cmd-mod
name: go-test-mod
alias: go-test-mod
args: [ go, test ]
```
##### Names &amp; Aliases
It is recommended that you use both `name` and `alias` attributes when defining my-cmd hooks.
The name will provide better messaging when the hook runs.
The alias will enable you to invoke the hook manually from the command-line when needed (see `pre-commit help run`)
##### error-on-output
Some tools, like `gofmt`, `goimports`, and `goreturns`, don't generate error codes, but instead expect the presence of any output to indicate warning/error conditions.
The my-cmd hooks accept an `--error-on-output` argument to indicate this behavior.
Here's an example of what it would look like to use the my-cmd hooks to invoke `gofmt` if it wasn't already included:
_.pre-commit-config.yaml_
```
# ...
hooks:
# Run 'gofmt -l -d $FILE' for each staged .go file
# Treat any output as indication of failure
- id: my-cmd
name: go-fmt
alias: go-fmt
args: [ --error-on-output, gofmt, -l, -d ]
```
**NOTE:** When used, the `--error-on-output` option **must** be the first argument.
----------
## License
Expand Down
72 changes: 4 additions & 68 deletions go-build-mod.sh
Original file line number Diff line number Diff line change
@@ -1,70 +1,6 @@
#!/usr/bin/env bash

# shellcheck disable=SC2034 # vars used by sourced script
error_on_output=0
cmd=(go build -o /dev/null)

export GO111MODULE=on

# Walks up the file path looking for go.mod
#
function find_module_roots() {
for arg in "$@" ; do
local path="${arg}"
if [ "${path}" == "" ]; then
path="."
elif [ -f "${path}" ]; then
path=$(dirname "${path}")
fi
while [ "${path}" != "." ] && [ ! -f "${path}/go.mod" ]; do
path=$(dirname "${path}")
done
if [ -f "${path}/go.mod" ]; then
echo "${path}"
fi
done
}

OPTIONS=()
# If arg doesn't pass [ -f ] check, then it is assumed to be an option
#
while [ $# -gt 0 ] && [ "$1" != "-" ] && [ "$1" != "--" ] && [ ! -f "$1" ]; do
OPTIONS+=("$1")
shift
done

FILES=()
# Assume start of file list (may still be options)
#
while [ $# -gt 0 ] && [ "$1" != "-" ] && [ "$1" != "--" ]; do
FILES+=("$1")
shift
done

# If '--' next, then files = options
#
if [ $# -gt 0 ]; then
if [ "$1" == "-" ] || [ "$1" == "--" ]; then
shift
# Append to previous options
#
OPTIONS=("${OPTIONS[@]}" "${FILES[@]}")
FILES=()
fi
fi

# Any remaining arguments are assumed to be files
#
while [ $# -gt 0 ]; do
FILES+=("$1")
shift
done

errCode=0
for sub in $(find_module_roots "${FILES[@]}" | sort -u) ; do
pushd "${sub}" >/dev/null
"${cmd[@]}" "${OPTIONS[@]}" ./...
if [ $? -ne 0 ]; then
errCode=1
fi
popd >/dev/null
done
exit $errCode
# shellcheck source=lib/cmd-mod.bash
. "$(dirname "${0}")/lib/cmd-mod.bash"
Loading

0 comments on commit 6344f32

Please sign in to comment.