🐛 fix(projectdo): disabled npm run if to always use npm run

This commit is contained in:
Sergio Laín 2023-11-16 15:40:25 +01:00
parent d357133ae1
commit de8a417063
No known key found for this signature in database
GPG key ID: 14C9B8080681777B

View file

@ -23,38 +23,38 @@ QUIET=false
DRY_RUN=false DRY_RUN=false
PROJECT_ROOT="${PROJECT_ROOT:-}" PROJECT_ROOT="${PROJECT_ROOT:-}"
ACTION="" ACTION=""
TOOL_ARGS="" # Arguments to pass along to the tool TOOL_ARGS="" # Arguments to pass along to the tool
IS_TOOL=false # True if ACTION is 'tool' as this action is somewhat special. IS_TOOL=false # True if ACTION is 'tool' as this action is somewhat special.
has_command() { has_command() {
command -v "$1" >/dev/null 2>&1 command -v "$1" >/dev/null 2>&1
} }
# When running the appropriate external tool this function is used which # When running the appropriate external tool this function is used which
# evaluates the given command while respecting $QUIET and $DRY_RUN. # evaluates the given command while respecting $QUIET and $DRY_RUN.
execute() { execute() {
if [ $QUIET = false ]; then if [ $QUIET = false ]; then
echo "$1" $TOOL_ARGS echo "$1" $TOOL_ARGS
fi fi
if [ $DRY_RUN = false ]; then if [ $DRY_RUN = false ]; then
if [ $QUIET = false ]; then if [ $QUIET = false ]; then
eval "$1" $TOOL_ARGS eval "$1" $TOOL_ARGS
else else
eval "$1" $TOOL_ARGS > /dev/null eval "$1" $TOOL_ARGS >/dev/null
fi fi
fi fi
} }
# Takes the name of a tool and an action. If the action is `tool` it is # Takes the name of a tool and an action. If the action is `tool` it is
# ignored. It handles the common case where the tool is invoked as `tool # ignored. It handles the common case where the tool is invoked as `tool
# action`. # action`.
execute_command() { execute_command() {
if [ "$2" = tool ]; then if [ "$2" = tool ]; then
execute "$1" execute "$1"
else else
execute "$1 $2" execute "$1 $2"
fi fi
exit 0 exit 0
} }
# Every supported tool requires a function `try_tool` where `tool` is a name # Every supported tool requires a function `try_tool` where `tool` is a name
@ -71,241 +71,247 @@ execute_command() {
# JavaScript + NodeJS # JavaScript + NodeJS
try_nodejs() { try_nodejs() {
if [ ! -f package.json ]; then if [ ! -f package.json ]; then
return 1 return 1
fi fi
if [ -f yarn.lock ]; then if [ -f yarn.lock ]; then
tool=yarn tool=yarn
else else
tool=npm tool=npm
fi fi
if ! has_command $tool; then if ! has_command $tool; then
echo "Found a package.json file but '$tool' is not installed." echo "Found a package.json file but '$tool' is not installed."
exit 1 exit 1
fi fi
local node_action="$ACTION" local node_action="$ACTION"
# Only the "run" action need translation, the others match 1-to-1 # Only the "run" action need translation, the others match 1-to-1
if [ "$ACTION" = run ]; then # if [ "$ACTION" = run ]; then
node_action=start # node_action=start
fi # fi
# We check if the package.json file contains an appropriate script. We use # We check if the package.json file contains an appropriate script. We use
# grep for this. The check is not 100% bulletproof, but it's very close. We # grep for this. The check is not 100% bulletproof, but it's very close. We
# could've used `npm run` to get the authorative list of the scripts but # could've used `npm run` to get the authorative list of the scripts but
# invoking `npm` is two orders of magnitude slower which leads to a # invoking `npm` is two orders of magnitude slower which leads to a
# noticeable delay. # noticeable delay.
if ! $IS_TOOL && ! grep -q "^[[:space:]]*\"${node_action}\":" package.json; then # if ! $IS_TOOL && ! grep -q "^[[:space:]]*\"${node_action}\":" package.json; then
return 0 # return 0
fi # fi
execute_command "$tool" "$node_action" execute_command "$tool" "$node_action"
} }
# Rust + Cargo # Rust + Cargo
try_cargo() { try_cargo() {
if [ -f Cargo.toml ]; then if [ -f Cargo.toml ]; then
execute_command cargo "$ACTION" execute_command cargo "$ACTION"
fi fi
} }
# CMake # CMake
try_cmake() { try_cmake() {
if [ -f CMakeLists.txt ] && [ "$ACTION" = test ]; then if [ -f CMakeLists.txt ] && [ "$ACTION" = test ]; then
if [ -f build ]; then if [ -f build ]; then
execute "cmake --build build --target test" execute "cmake --build build --target test"
else else
execute "cmake --build . --target test" execute "cmake --build . --target test"
fi fi
exit exit
fi fi
} }
# Haskell + Stack # Haskell + Stack
try_stack() { try_stack() {
if [ -f package.yaml ] && [ -f stack.yaml ]; then if [ -f package.yaml ] && [ -f stack.yaml ]; then
execute_command stack "$ACTION" execute_command stack "$ACTION"
fi fi
} }
# Haskell + Cabal # Haskell + Cabal
try_cabal() { try_cabal() {
cabal_file="$(find ./ -maxdepth 1 -name "*.cabal" 2> /dev/null | wc -l)" cabal_file="$(find ./ -maxdepth 1 -name "*.cabal" 2>/dev/null | wc -l)"
if [ "$cabal_file" -gt 0 ] && [ ! -f stack.yml ]; then if [ "$cabal_file" -gt 0 ] && [ ! -f stack.yml ]; then
execute_command cabal "$ACTION" execute_command cabal "$ACTION"
fi fi
} }
# Maven # Maven
try_maven() { try_maven() {
if [ -f pom.xml ]; then if [ -f pom.xml ]; then
case $ACTION in case $ACTION in
build) build)
execute "mvn compile" execute "mvn compile"
exit ;; exit
test) ;;
execute "mvn test" test)
exit ;; execute "mvn test"
run) exit
echo "projectdo does not know how to run a project with Maven." ;;
exit run)
esac echo "projectdo does not know how to run a project with Maven."
fi exit
;;
esac
fi
} }
# Clojure + Leiningen # Clojure + Leiningen
try_lein() { try_lein() {
if [ -f project.clj ] && [ "$ACTION" = test ]; then if [ -f project.clj ] && [ "$ACTION" = test ]; then
execute "lein test" execute "lein test"
fi fi
} }
# Makefile # Makefile
has_make_target() { has_make_target() {
target="${1?}" target="${1?}"
output=$(make -n "${target}" 2>&1) output=$(make -n "${target}" 2>&1)
exit_code=$? exit_code=$?
if [ $exit_code -ne 0 ]; then if [ $exit_code -ne 0 ]; then
return $exit_code return $exit_code
fi fi
# If there is a file with the name of the target we're looking for but no # If there is a file with the name of the target we're looking for but no
# actual target with that name, make will exit successfully with that # actual target with that name, make will exit successfully with that
# message. We need to consider that case as a "target not found". Note that # message. We need to consider that case as a "target not found". Note that
# the way the target is quoted in the output (`test' vs 'test') can differ # the way the target is quoted in the output (`test' vs 'test') can differ
# across OSes so we only check a prefix up to the problematic quotes. # across OSes so we only check a prefix up to the problematic quotes.
if expr "$output" : "make: Nothing to be done for" 1> /dev/null; then if expr "$output" : "make: Nothing to be done for" 1>/dev/null; then
return 1 return 1
fi fi
return 0 return 0
} }
try_makefile() { try_makefile() {
if [ -f Makefile ]; then if [ -f Makefile ]; then
if ! has_command "make"; then if ! has_command "make"; then
echo "Found a Makefile but 'make' is not installed." echo "Found a Makefile but 'make' is not installed."
exit 1 exit 1
fi fi
if $IS_TOOL || [ "$ACTION" = build ]; then if $IS_TOOL || [ "$ACTION" = build ]; then
# For make "build" is the default action when running `make` # For make "build" is the default action when running `make`
execute "make" execute "make"
exit exit
elif [ "$ACTION" = test ]; then elif [ "$ACTION" = test ]; then
# Let's see if the makefile contains a test or check target # Let's see if the makefile contains a test or check target
if has_make_target "test"; then if has_make_target "test"; then
execute "make test" execute "make test"
exit exit
elif has_make_target "check"; then elif has_make_target "check"; then
execute "make check" execute "make check"
exit exit
fi fi
fi fi
fi fi
return 1 return 1
} }
# Python # Python
try_python() { try_python() {
if [ -f pyproject.toml ]; then if [ -f pyproject.toml ]; then
if grep -q -m 1 "^\[tool.poetry\]$" pyproject.toml; then if grep -q -m 1 "^\[tool.poetry\]$" pyproject.toml; then
case $ACTION in case $ACTION in
build) build)
execute "poetry build" execute "poetry build"
exit ;; exit
test) ;;
# TODO: There are other Python test frameworks, it would be nice to test)
# detect and run the right one. # TODO: There are other Python test frameworks, it would be nice to
execute "poetry run pytest" # detect and run the right one.
exit ;; execute "poetry run pytest"
run) exit
echo "projectdo does not know how to run a project with Poetry." ;;
exit run)
esac echo "projectdo does not know how to run a project with Poetry."
else exit
echo "Found a pyproject.toml file but projectdo is not sure how to run it." ;;
exit esac
fi else
return 1 echo "Found a pyproject.toml file but projectdo is not sure how to run it."
fi exit
fi
return 1
fi
} }
# Go # Go
try_go() { try_go() {
if [ -f go.mod ] && [ "$ACTION" = test ]; then if [ -f go.mod ] && [ "$ACTION" = test ]; then
# We detect Makefiles before we detect Go, so here we know that the Go # We detect Makefiles before we detect Go, so here we know that the Go
# project is _not_ tested by a Makefile. # project is _not_ tested by a Makefile.
# Check if the project uses Mage. A magefile could in theory have any name, # Check if the project uses Mage. A magefile could in theory have any name,
# but `magefile.go` seems to be the convention. # but `magefile.go` seems to be the convention.
if grep -q -m 1 '^func Check(' magefile.go; then if grep -q -m 1 '^func Check(' magefile.go; then
execute "mage check" execute "mage check"
exit exit
elif grep -q -m 1 '^func Test(' magefile.go; then elif grep -q -m 1 '^func Test(' magefile.go; then
execute "mage test" execute "mage test"
exit exit
fi fi
execute "go test" execute "go test"
exit exit
fi fi
} }
# LaTeX # LaTeX
try_latex() { try_latex() {
if [ -f Tectonic.toml ] && [ "$ACTION" = build ]; then if [ -f Tectonic.toml ] && [ "$ACTION" = build ]; then
execute "tectonic -X build" execute "tectonic -X build"
exit exit
fi fi
} }
# End of list of tools # End of list of tools
detect_and_run() { detect_and_run() {
try_nodejs try_nodejs
try_cargo try_cargo
try_stack try_stack
try_cabal try_cabal
try_cmake try_cmake
try_maven try_maven
try_lein try_lein
try_makefile try_makefile
try_python try_python
try_go try_go
try_latex try_latex
} }
set_project_root() { set_project_root() {
if [ -n "$PROJECT_ROOT" ]; then if [ -n "$PROJECT_ROOT" ]; then
return return
fi fi
if has_command git; then if has_command git; then
# Find the root of the git repository if we're inside one. If we're in a # Find the root of the git repository if we're inside one. If we're in a
# git submodule then the root of the outer git repo is used. If we're not # git submodule then the root of the outer git repo is used. If we're not
# in a git repo the git command will not output anything and $PROJECT_ROOT # in a git repo the git command will not output anything and $PROJECT_ROOT
# is set to the empty string which is fine. # is set to the empty string which is fine.
PROJECT_ROOT=$(git rev-parse --show-superproject-working-tree --show-toplevel 2> /dev/null | head -n 1) PROJECT_ROOT=$(git rev-parse --show-superproject-working-tree --show-toplevel 2>/dev/null | head -n 1)
fi fi
} }
nothing_found() { nothing_found() {
echo "No way to $ACTION found :'(" echo "No way to $ACTION found :'("
exit 1 exit 1
} }
display_version() { display_version() {
echo "$(basename "$0") version $VERSION" echo "$(basename "$0") version $VERSION"
} }
display_help() { display_help() {
echo "Usage: $(basename "$0") [options] [action] [tool-arguments] echo "Usage: $(basename "$0") [options] [action] [tool-arguments]
Options: Options:
-h, --help Display this help. -h, --help Display this help.
-n, -d, --dry-run Do not execute any commands with side-effects. -n, -d, --dry-run Do not execute any commands with side-effects.
@ -322,64 +328,77 @@ Tool arguments:
# Main execution starts here # Main execution starts here
while getopts dhnqv-: c while getopts dhnqv-: c; do
do case $c in
case $c in d) DRY_RUN=true ;;
d) DRY_RUN=true ;; h)
h) display_help; exit 0 ;; display_help
n) DRY_RUN=true ;; exit 0
q) QUIET=true ;; ;;
v) display_version; exit 0 ;; n) DRY_RUN=true ;;
-) case $OPTARG in q) QUIET=true ;;
help) display_help; exit 0 ;; v)
dry-run) DRY_RUN=true ;; display_version
quiet) QUIET=true ;; exit 0
version) display_version; exit 0 ;; ;;
'' ) break ;; # "--" should terminate argument processing -) case $OPTARG in
* ) echo "Illegal option --$OPTARG" >&2; exit 1 ;; help)
esac ;; display_help
\?) exit 1 ;; exit 0
esac ;;
dry-run) DRY_RUN=true ;;
quiet) QUIET=true ;;
version)
display_version
exit 0
;;
'') break ;; # "--" should terminate argument processing
*)
echo "Illegal option --$OPTARG" >&2
exit 1
;;
esac ;;
\?) exit 1 ;;
esac
done done
shift $((OPTIND-1)) # Shift away the parsed option arguments shift $((OPTIND - 1)) # Shift away the parsed option arguments
if [ "$1" = test ] || if [ "$1" = test ] ||
[ "$1" = run ] || [ "$1" = run ] ||
[ "$1" = build ] || [ "$1" = build ] ||
[ "$1" = tool ]; then [ "$1" = tool ]; then
ACTION=$1 ACTION=$1
if [ "$1" = tool ]; then if [ "$1" = tool ]; then
IS_TOOL=true IS_TOOL=true
fi fi
shift 1 # Remove the action from the arguments shift 1 # Remove the action from the arguments
TOOL_ARGS=$@ TOOL_ARGS=$@
else else
if [ -z "$1" ]; then if [ -z "$1" ]; then
echo "No action specified." echo "No action specified."
else else
echo "$1 is not a valid action." echo "$1 is not a valid action."
fi fi
echo "" echo ""
echo "The valid actions are: build, run, test, tool" echo "The valid actions are: build, run, test, tool"
echo "" echo ""
echo "Example: projectdo test" echo "Example: projectdo test"
exit 1 exit 1
fi fi
set_project_root set_project_root
while : while :; do
do # We don't want to do anything if we are in the home or root directory
# We don't want to do anything if we are in the home or root directory if [ "$PWD" = "$HOME" ] || [ "$PWD" = / ]; then
if [ "$PWD" = "$HOME" ] || [ "$PWD" = / ]; then nothing_found
nothing_found fi
fi detect_and_run
detect_and_run # If we didn't detect a tool to run in this directory we go up one directory
# If we didn't detect a tool to run in this directory we go up one directory # while ensuring that we don't leave the project root
# while ensuring that we don't leave the project root if [ "$PWD" = "$PROJECT_ROOT" ]; then
if [ "$PWD" = "$PROJECT_ROOT" ]; then nothing_found
nothing_found fi
fi cd ..
cd ..
done done