diff --git a/.mise/tasks/fmt/_default b/.mise/tasks/fmt/_default new file mode 100755 index 00000000..718eb6d8 --- /dev/null +++ b/.mise/tasks/fmt/_default @@ -0,0 +1,3 @@ +#!/usr/bin/env bash +#MISE description="Run all formatters." +#MISE depends=[{ task = "fmt:*", optional = true }] diff --git a/.mise/tasks/fmt/bash b/.mise/tasks/fmt/bash new file mode 100755 index 00000000..b307f7dd --- /dev/null +++ b/.mise/tasks/fmt/bash @@ -0,0 +1,9 @@ +#!/usr/bin/env bash +#MISE description="Formats shell scripts." + +set -euo pipefail + +DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)" +SHELL_DIR="$DIR/../../../shell" + +exec "$SHELL_DIR/run-formatter.sh" "$SHELL_DIR/linters/bash.sh" diff --git a/.mise/tasks/fmt/go b/.mise/tasks/fmt/go new file mode 100755 index 00000000..540c6c76 --- /dev/null +++ b/.mise/tasks/fmt/go @@ -0,0 +1,9 @@ +#!/usr/bin/env bash +#MISE description="Formats Go source files." + +set -euo pipefail + +DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)" +SHELL_DIR="$DIR/../../../shell" + +exec "$SHELL_DIR/run-formatter.sh" "$SHELL_DIR/linters/go.sh" diff --git a/.mise/tasks/fmt/js b/.mise/tasks/fmt/js new file mode 100755 index 00000000..dcf59f58 --- /dev/null +++ b/.mise/tasks/fmt/js @@ -0,0 +1,9 @@ +#!/usr/bin/env bash +#MISE description="Formats JavaScript files." + +set -euo pipefail + +DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)" +SHELL_DIR="$DIR/../../../shell" + +exec "$SHELL_DIR/run-formatter.sh" "$SHELL_DIR/linters/js.sh" diff --git a/.mise/tasks/fmt/jsonnet b/.mise/tasks/fmt/jsonnet new file mode 100755 index 00000000..e3cb04d8 --- /dev/null +++ b/.mise/tasks/fmt/jsonnet @@ -0,0 +1,9 @@ +#!/usr/bin/env bash +#MISE description="Formats Jsonnet files." + +set -euo pipefail + +DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)" +SHELL_DIR="$DIR/../../../shell" + +exec "$SHELL_DIR/run-formatter.sh" "$SHELL_DIR/linters/jsonnet.sh" diff --git a/.mise/tasks/fmt/prettier b/.mise/tasks/fmt/prettier new file mode 100755 index 00000000..8c4a8a1f --- /dev/null +++ b/.mise/tasks/fmt/prettier @@ -0,0 +1,9 @@ +#!/usr/bin/env bash +#MISE description="Formats YAML, JSON, Markdown, and TypeScript files." + +set -euo pipefail + +DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)" +SHELL_DIR="$DIR/../../../shell" + +exec "$SHELL_DIR/run-formatter.sh" "$SHELL_DIR/linters/prettier.sh" diff --git a/.mise/tasks/fmt/protobuf b/.mise/tasks/fmt/protobuf new file mode 100755 index 00000000..ffc7a4d4 --- /dev/null +++ b/.mise/tasks/fmt/protobuf @@ -0,0 +1,9 @@ +#!/usr/bin/env bash +#MISE description="Formats Protobuf files." + +set -euo pipefail + +DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)" +SHELL_DIR="$DIR/../../../shell" + +exec "$SHELL_DIR/run-formatter.sh" "$SHELL_DIR/linters/protobuf.sh" diff --git a/.mise/tasks/fmt/terraform b/.mise/tasks/fmt/terraform new file mode 100755 index 00000000..ed570a1b --- /dev/null +++ b/.mise/tasks/fmt/terraform @@ -0,0 +1,9 @@ +#!/usr/bin/env bash +#MISE description="Formats Terraform files." + +set -euo pipefail + +DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)" +SHELL_DIR="$DIR/../../../shell" + +exec "$SHELL_DIR/run-formatter.sh" "$SHELL_DIR/linters/terraform.sh" diff --git a/.mise/tasks/fmt/toml b/.mise/tasks/fmt/toml new file mode 100755 index 00000000..25ff56f0 --- /dev/null +++ b/.mise/tasks/fmt/toml @@ -0,0 +1,9 @@ +#!/usr/bin/env bash +#MISE description="Formats TOML files." + +set -euo pipefail + +DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)" +SHELL_DIR="$DIR/../../../shell" + +exec "$SHELL_DIR/run-formatter.sh" "$SHELL_DIR/linters/toml.sh" diff --git a/docs/linters.md b/docs/linters.md index 88d41a70..4ffc438b 100644 --- a/docs/linters.md +++ b/docs/linters.md @@ -20,3 +20,39 @@ To add a linter place the linter shell script in `scripts/linters/.s The linter will be discovered when globbing `.sh` files run with the built-in linters. Follow the conventions of the existing linter shell scripts when creating the new linter. + +## Running formatters via mise + +`mise run fmt` is the mise-native equivalent of `make fmt`. `mise run fmt:` +(e.g. `mise run fmt:go`, `mise run fmt:bash`) runs a single formatter. Run +`mise tasks ls` to see the full list of `fmt:*` tasks. + +Unlike `make fmt`, which auto-discovers `scripts/linters/*.sh` by globbing, +`mise run fmt` only runs formatters registered as `fmt:` mise tasks. A +project-specific linter (added as above) needs its own `fmt:` task to be +picked up by `mise run fmt`, via either: + +1. A file-based task at `.mise/tasks/fmt/` that delegates to devbase's + shared runner via `scripts/shell-wrapper.sh` (the same wrapper `make fmt` + itself uses to reach devbase's vendored shell scripts): + + ```bash + #!/usr/bin/env bash + #MISE description="Formats files." + + set -euo pipefail + + exec "$MISE_PROJECT_ROOT/scripts/shell-wrapper.sh" run-formatter.sh "$MISE_PROJECT_ROOT/scripts/linters/.sh" + ``` + +2. A plain TOML task in your own `mise.toml`, with no dependency on the + `extensions`/`formatter()` convention at all: + + ```toml + [tasks."fmt:"] + description = "Formats files." + run = "your-formatter-command --write ." + ``` + +Both forms are discovered identically by `mise run fmt`, since mise treats +file-based and TOML-defined tasks the same way for task lookup. diff --git a/shell/fmt.sh b/shell/fmt.sh index c58d58e3..cc6d5bdb 100755 --- a/shell/fmt.sh +++ b/shell/fmt.sh @@ -8,16 +8,10 @@ DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)" # shellcheck source=./lib/bootstrap.sh source "$DIR/lib/bootstrap.sh" -# shellcheck source=./lib/github.sh -source "$DIR/lib/github.sh" # shellcheck source=./lib/logging.sh source "$DIR/lib/logging.sh" -# shellcheck source=./lib/mise.sh -source "$DIR/lib/mise.sh" # shellcheck source=./lib/shell.sh source "$DIR/lib/shell.sh" -# shellcheck source=./lib/version.sh -source "$DIR/lib/version.sh" # add extra (per project) linters linters=("$DIR/linters"/*.sh) @@ -32,42 +26,7 @@ info "Running formatters" started_at="$(get_time_ms)" for linterScript in "${linters[@]}"; do - - # We use a sub-shell to prevent inheriting - # the changes to functions/variables to the parent - # (this) script - ( - # Note: These are modified by the source'd language file - # extensions are the extensions this linter should run on - extensions=() - - # Why: Dynamic - # shellcheck disable=SC1090 - source "$linterScript" - - matched=false - if [[ "$(find_files_with_extensions "${extensions[@]}" | wc -l | tr -d ' ')" -gt 0 ]]; then - matched=true - fi - - if [[ $matched == "false" ]]; then - exit 0 - fi - - # Note: extensions is set by the linter. - # Why: We're OK with declaring and assigning. - # shellcheck disable=SC2155,SC2001 - extensionsString=$(sed 's/ /,./g' <<<"${extensions[*]}" | sed 's/^/./') - - # show is used by run_command as metadata to be shown along with the command name - show=$extensionsString - - # Set by the language file - if ! formatter; then - error "Formatter failed to run" - exit 1 - fi - ) + "$DIR/run-formatter.sh" "$linterScript" || exit 1 done finished_at="$(get_time_ms)" duration="$((finished_at - started_at))" diff --git a/shell/run-formatter.sh b/shell/run-formatter.sh new file mode 100755 index 00000000..700a0ba7 --- /dev/null +++ b/shell/run-formatter.sh @@ -0,0 +1,53 @@ +#!/usr/bin/env bash +# Runs the formatter() function defined by a single linter script (see +# shell/linters/*.sh) against the files it declares via `extensions`. +# +# Used by shell/fmt.sh (in series) and by the per-formatter mise tasks +# under .mise/tasks/fmt/ (which mise may run in parallel). +set -e -o pipefail + +DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)" + +# shellcheck source=./lib/bootstrap.sh +source "$DIR/lib/bootstrap.sh" +# shellcheck source=./lib/github.sh +source "$DIR/lib/github.sh" +# shellcheck source=./lib/logging.sh +source "$DIR/lib/logging.sh" +# shellcheck source=./lib/mise.sh +source "$DIR/lib/mise.sh" +# shellcheck source=./lib/shell.sh +source "$DIR/lib/shell.sh" +# shellcheck source=./lib/version.sh +source "$DIR/lib/version.sh" + +linterScript="${1:-}" +if [[ -z $linterScript ]]; then + fatal "Usage: run-formatter.sh " +fi + +# Note: extensions is set by the linter script sourced below. +extensions=() + +# Why: Dynamic +# shellcheck disable=SC1090 +source "$linterScript" + +if [[ "$(find_files_with_extensions "${extensions[@]}" | wc -l | tr -d ' ')" -eq 0 ]]; then + exit 0 +fi + +# Note: extensions is set by the linter. +extensionsPrefixed=("${extensions[@]/#/.}") +IFS=, +extensionsString="${extensionsPrefixed[*]}" +unset IFS + +# show is used by run_command as metadata to be shown along with the command name +show=$extensionsString + +# Set by the linter script. +if ! formatter; then + error "Formatter failed to run" + exit 1 +fi