🐛 fix(projectdo): disabled npm run if to always use npm run
This commit is contained in:
parent
d357133ae1
commit
de8a417063
1 changed files with 249 additions and 230 deletions
|
@ -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
|
||||||
|
|
Loading…
Add table
Reference in a new issue