Skip to content

Commit

Permalink
✨ (#45) Support user-supplied filters for built-ins
Browse files Browse the repository at this point in the history
This means double-filter support.

Fix: #45
  • Loading branch information
MicahElliott committed Nov 7, 2024
1 parent fccc151 commit 2b908bf
Show file tree
Hide file tree
Showing 3 changed files with 75 additions and 47 deletions.
2 changes: 1 addition & 1 deletion .capt/share.sh
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ pre_commit=(
mdlint
fixcheck
# -markdownlint
wscheck
'wscheck(md|sh)'
# "fixmes: git-confirm.sh ## check for FIXMEs etc"
)

Expand Down
46 changes: 34 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Captain

> _Captain_ is a simple, convenient, transparent opt-in approach to client-
> and CI-side **git-hook management**, with just a single, tiny,
> and CI-side **git-hook management**, with just a single, small,
> dependency-free shell script to download. Suited for sharing across a team,
> extensible for individuals. Supports all
> [common git hooks](https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks)
Expand All @@ -24,7 +24,9 @@
⠀⠀⠀⠀⠀⠀⠀⠈⠉⠛⠛⠛⠛⠋⠉⠀⠀⠀⠀⠀
```

## One-minute E-Z Quick-Start Guide (very easy, point your team here)
## One-minute E-Z Quick-Start Guide if Captain already set up

_(Easy, point your team here.)_

*SITUATION*: Captain was already set up in a repo you use, and you want to
start enabling its checks (AKA triggers). (Or you're a curmudgeon: You won't
Expand All @@ -34,12 +36,12 @@ miss out on the fun!)
```shell
# Install the capt command (a small zsh script)
cd ~/src # or somewhere like that where you keep clones
git clone https://github.com/MicahElliott/captain # to get all tooling
git clone https://github.com/MicahElliott/captain # to get all tooling, easy to update
print 'path+=~/src/captain/bin' >> ~/.zshrc # or something/somewhere like that
# OR, put that ^^^ into a .envrc file and use https://github.com/direnv/direnv for your proj
# OR, for just the capt script (sufficient for some projects that don't need extra goodies):
# cd /somewhere/on/your/PATH
# wget https://raw.githubusercontent.com/MicahElliott/captain/main/capt && chmod +x capt
# wget https://raw.githubusercontent.com/MicahElliott/captain/main/bin/capt && chmod +x capt

# Point git to the new hooks
cd your-project-root # like you always do
Expand All @@ -58,6 +60,8 @@ If there are any "triggers" (linters, formatters, informers, etc) being
invoked that you don't have installed yet, Captain should kindly let you know
more details.

---

OR, if you're looking to be the one to introduce Captain git-hook management to a
project, read on....

Expand Down Expand Up @@ -300,15 +304,32 @@ Now onto the simple `.capt/share.sh` control file at the root of your repo

### Trigger Spec

There is a tiny DSL that is used for each "trigger" in a control file.
There is a tiny DSL that is used to specify each "trigger" in a control file.
Here's an example of a custom trigger. The first part, up to the colon is the
name and optional glob filter. The second part is the command to run, and a
trailing comment docstring. This one says to run the `clj-kondo` linter (named
`lint` in the output) specifically on Clojure files that were changed as part
of the staged commit.

```
'lint(clj|cljs): clj-kondo $CAPT_FILES_CHANGED & ## lint clojure files'
^^^^ ^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^ ^^^^^^^^^^^^^^^^^^^^^
NAME FILTERS COMMAND CONCURRENCY COMMENT
```

And here's a couple built in triggers. Built-ins have pre-defined commands,
filters, and docs. Note that there is no colon after the name spec for
built-ins. The quotes are optional, but needed if you include a filter. The
first says to run the "whitespace checker" on Ruby, Clojure, and Javascript
files only. The second runs the standard Clojure linter (`clj-kondo`) on all
staged Clojure files.

```
'lint(clj|cljs): clj-kondo $CAPT_CHANGES &' # linting of files
^^^^ ^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^ ^ ^^^^^^^^^^^^^^^^^^
NAME FILTERS COMMAND CONCURRENCY COMMENT
'wscheck(rb|clj|js)'
cljlint
```

Note that this syntax looks almost exactly like the standard [git conventional
Note that this syntax looks kinda like the standard [git conventional
commits](https://gist.github.com/qoomon/5dfcdf8eec66a051ecd85625518cfd13) DSL.

### Example Team Control File
Expand All @@ -322,14 +343,15 @@ pre_commit=(
'lint: clj-kondo $CAPT_CHANGES &' # linting of files being committed
'format(clj|cljc): cljfmt &' # reformat or check for poor formatting
'fixmes: git-confirm.sh' # look for/prompt on FIXMEs etc
markdownlint # built-in config with implicit filter
mdlint # built-in config with implicit md filter
'wslint(py|sql)' # built-in with py and sql file filter
'test-suite: run-minimal-test-suite $CAPT_CHANGES'
)
# params: tmp-message-file-path, commit-type, sha
# Build a commit message based on branch name standardized format.
prepare_commit_msg=(
# you/TEAM-123_FIX_lang_undo-the-widget-munging => fix(lang): Undo the widget munging #123
branch2message
br2msg
)
# params: tmp-message-file-path
# Validate your project state or commit message before allowing a commit to go through
Expand All @@ -352,7 +374,7 @@ post_rewrite=(
# params: NONE
# Set up your working directory properly for your project environment
post_checkout=(
"mig-alert(sql): alert-migrations-pending.zsh" # inform that action is needed
"migalert(sql): alert-migrations-pending.zsh" # inform that action is needed
)
# Use to validate a set of ref updates before a push occurs
pre_push=(
Expand Down
74 changes: 40 additions & 34 deletions bin/capt
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,7 @@ print_filtereds() {
dbg "filtered files:" $@
local shorts=( $(shortnames $@) )
# print " $fg[yellow][${@$(shortnames $@)}]$reset_color"
if [[ -n $CAPT_VERBOSE ]]
if [[ -n $CAPT_VERBOSE ]]
then print -n " $fg[yellow][$shorts]$reset_color\n"
elif [[ $shorts[1] == ∞ ]]
then print -n " $fg[yellow][∞]$reset_color"
Expand Down Expand Up @@ -354,6 +354,35 @@ print_cmd_and_help () {
fi
}

# There are 2 filters available: user-specified (front) and built-in (back, after colon)
get_combined_filtereds () {
# filter1 only applies for user-spec'd
local filter1 filter2 # start then both empty
if [[ $name =~ '\(.+\)' ]] # remove front name and trailing :, if filter present
then filter1=$(print $name | ssed 's/.*(/(/;s/://')
fi
# NAME FILTER CMD
# User spec is like: mdlint(md|mkd): mdl ...
# Built-in command DSL is like: mdlint (md|mkd)mdl ...
# Set up filter2 (built-in)
dsl=$builtin_cannons[$print_name]
if [[ $dsl =~ '^\(' ]]
then filter2=$(print $dsl | ssed 's/).*/)/') # built-in filter, if exists
fi
# Combine 2 into one: f1='(clj|cljs)' f2='(csv|sql) -> (clj|cljs)(csv|sql) -> (clj|cljs|csv|sql)
filter=$( ssed -r 's/\)\(/|/' <<< ${filter1}${filter2} )
dbg "\n" filter: $filter :: CAPT_FILES_CHANGED: $CAPT_FILES_CHANGED
if [[ $filter != '('* ]]
then filtered_files=( $CAPT_FILES_CHANGED )
else filtered_files=( $(print -l $CAPT_FILES_CHANGED | sgrep -E "\.$filter$") )
fi
CAPT_FILES_CHANGED=( $filtered_files )
if [[ $#filtered_files < 1 ]]
then print " ($suit Not seein any matchin files staged, skippin.)"
return 1
fi
}

# Run the scripts from control file
run_scripts () {
# print active_githook: $active_githook
Expand Down Expand Up @@ -422,38 +451,24 @@ run_scripts () {
# ayen "$fg[yellow]⚳⚳⚳ ${(U)print_name} $reset_color" # uhooks
# printf " $fg[yellow]⚳⚳⚳ %-${max_cmd_len}s$reset_color" ${(U)print_name} # uhooks
printf "$reset_color$fg_bold[blue]$icon$reset_color $fg[yellow]⚳⚳⚳ %-${max_cmd_len}s$reset_color" ${(U)print_name} # uhooks
# Built-in triggers are the only ones with no colon
if [[ ! $name == *: ]]; then dbg "built-in plain checker with no spec:" $name
suit=♠; print -n " $suit"
# Need to fix up cmd since no spec, and maybe include filtered files
script=$inv[1]
just_buitins=${(k)builtin_cannons}
if ! (($just_buitins[(Ie)$name])); then
print; aye "$fg[red]ERROR: Unable to find built-in trigger: $bold_color$name$reset_color" ; exit 1
fi
# NAME FILTER CMD
# User spec is like: mdlint(md|mkd): mdl ...
# DSL is actually like: (md|mkd)mdl ...
dsl=$builtin_cannons[$name]
filter=$(print $dsl | ssed 's/).*/)/')
# print xxx $filter
# non-greedy, just the very first part up to (, and remove ending arg(s)
cmd=$(print $dsl | ssed -r 's/^\([^)]+\)//')
cmd0=$(print $cmd | ssed -r 's/ .*//')
dbg "cmd0: "$cmd0
# Many antics follow to handle filters and changes for built-ins
if [[ $filter != '('* ]]
# FIXME Remove filtered_files altogether and just keep rewriting CAPT_FILES_CHANGED
then filtered_files=( $CAPT_FILES_CHANGED )
else filtered_files=( $(print -l $CAPT_FILES_CHANGED | sgrep -E "\.$filter$") ); fi
if [[ $#filtered_files < 1 && -n $CAPT_FILES_CHANGED ]]; then
print " ($suit Not seein any matchin files staged, skippin.)"
continue
if ! (($just_buitins[(Ie)$print_name])); then
print; aye "$fg[red]ERROR: Unable to find built-in trigger: $bold_color$print_name$reset_color"
exit 1
fi

# TODO Add special casing for specific hooks needing special treatment
if [[ $active_githook =~ 'commit-msg' ]]; then : ; fi

CAPT_FILES_CHANGED=( $filtered_files )
# cmd="$cmd $filtered_files"
get_combined_filtereds || continue
cmd=$(print $dsl | ssed -r 's/^\([^)]+\)//')
cmd0=$(print $cmd | ssed -r 's/ .*//')

print_filtereds $filtered_files
# Remove end of help and prep for printing on non-verbose
helpmsg=$( ssed -r 's/ \(.*\)//' <<< $builtin_helps[$print_name] )
Expand All @@ -464,15 +479,7 @@ run_scripts () {

elif [[ $name == *\(*\)* ]]; then dbg "custom-rigged filtered checker"
suit=♥; print -n " $suit"
filter=$(print $name | ssed 's/.*(/(/;s/://') # remove front name and trailing :
dbg filter: $filter; dbg CAPT_FILES_CHANGED: $CAPT_FILES_CHANGED
filtered_files=( $(print -l $CAPT_FILES_CHANGED | sgrep -E "\.$filter$") )
CAPT_FILES_CHANGED=( $filtered_files )
if [[ $#filtered_files < 1 ]]
then print " ($suit Not seein any matchin files staged, skippin.)"; continue
else print_filtereds $filtered_files
print_cmd_and_help
fi
get_combined_filtereds || continue
elif [[ $line == *CAPT_FILES_CHANGED* || ! $name == *: ]]; then dbg "unfiltered checker"
suit=♦; print -n " $suit"
filtered_files=( $CAPT_FILES_CHANGED )
Expand Down Expand Up @@ -611,7 +618,6 @@ fi

# Optionally run user hooks if they exist
if [[ -f $captfilelocal ]] # Run user-local hooks
# TODO test interactive
then runlevel=$captfilelocal
icon='<*‿*>'
select_hook
Expand Down

0 comments on commit 2b908bf

Please sign in to comment.