2021-11-03 02:40:46 +00:00
#!/usr/bin/env bash
2021-07-26 01:15:54 +00:00
# A tool to create a new python project with less setup than usual.
# v1.0 Jul 2021 by Cronocide
2021-07-21 01:41:35 +00:00
2021-07-26 00:57:19 +00:00
# Boilerplate functions from bash_profile for convenience
2021-07-21 01:41:35 +00:00
OS = " $( uname -a) "
[ [ " $OS " = = *"iPhone" * || " $OS " = = *"iPad" * ] ] && export OS = "iOS"
[ [ " $OS " = = *"ndroid" * ] ] && export OS = "Android"
[ [ " $OS " = = *"kali" * ] ] && export OS = "Kali"
[ [ " $OS " = = *"indows" * ] ] && export OS = "Windows"
[ [ " $OS " = = *"arwin" * ] ] && export OS = "macOS"
[ [ " $OS " = = *"BSD" * ] ] && export OS = "BSD"
[ [ " $OS " = = *"inux" * ] ] && export OS = "Linux"
__no_req( ) {
[ [ " $( type $1 2>/dev/null) " = = "" ] ] && return 0;
return 1
}
__missing_reqs( ) {
for i in " $@ " ; do
[ [ " $0 " != " $i " ] ] && __no_req " $i " && echo " $i is required to perform this function. " && return 0;
done ;
return 1
}
__missing_sed( ) {
__no_req "sed" && __no_req "gsed" && echo "sed or gsed is required to perform this function." && return 0
}
2021-11-03 03:26:44 +00:00
__require_bash( ) {
__no_req "sort" && return 1;
[ [ $( printf " $1 \n $BASH_VERSION " | sort -V | head -n 1) != " $1 " ] ] && return 1;
return 0
}
2021-07-21 01:41:35 +00:00
sed_i( ) {
__missing_sed && return 1;
if [ [ " $OS " = = "macOS" ] ] ; then
if [ [ $( type gsed 2>/dev/null) != "" ] ] ; then
2021-07-26 00:27:31 +00:00
gsed -i " $@ " ;
2021-07-21 01:41:35 +00:00
else
2021-11-03 02:28:59 +00:00
sed -i '' " $@ " ;
2021-07-21 01:41:35 +00:00
fi ;
else
sed -i $@ ;
fi
}
# Project creation
new_python_tool( ) {
2021-11-03 03:26:44 +00:00
# Check that we can run this tool
if ! $( __require_bash "4.3" ) ; then echo "Bash 4.3 or greater is required to run this script." && return 1; fi
2021-07-21 01:41:35 +00:00
__missing_reqs 'sed git find grep sort mv' && return 1
2021-07-26 00:34:15 +00:00
if [ [ $( pwd ) = = *"python-tool" * ] ] ; then
echo "This script should not be run within the python-tool directory. Please move it to a neutral location to run." && return 1
2021-07-21 01:41:35 +00:00
fi
2021-11-03 02:43:19 +00:00
[ -z " $1 " ] && echo "Usage: new_python_tool <name_of_tool>" && return 1
2021-07-21 01:41:35 +00:00
NAME = " $1 "
2022-04-07 23:18:10 +00:00
if [ [ $( echo " $1 " | sed 's#^[a-z0-9\_]*$##' ) = = " $NAME " ] ] ; then
2021-07-21 01:41:35 +00:00
echo "Tool name '" " $NAME " "' is invalid." && return 1
fi
2021-11-03 03:26:44 +00:00
# Download and rename the template repo
2021-07-26 00:34:15 +00:00
if [ ! -d ./python-tool ] ; then
2021-07-21 01:41:35 +00:00
echo "Downloading template..."
2021-07-26 00:34:15 +00:00
git clone 'https://github.com/Cronocide/python-tool.git'
2021-07-21 01:41:35 +00:00
fi
echo "Renaming project..."
# Rather novel approach from https://stackoverflow.com/a/53734138
2021-07-26 00:34:15 +00:00
find ./ -depth -name '*python-tool*' | while IFS = read -r i; do mv $i ${ i %python-tool* } $NAME ${ i ##*python-tool } ; done
2021-07-21 01:41:35 +00:00
# "I'm assuming you have no spaces in the project name because you're not an idiot."
2021-07-26 00:34:15 +00:00
for i in $( grep -r 'python-tool' ./" $NAME " | grep -v '.git' | cut -d \: -f 1 | sort -u) ; do
sed_i " s#python-tool# $NAME #g " " $i "
2021-07-21 01:41:35 +00:00
done
mv ./" $NAME " /com.cronocide." $NAME " .plist ./" $NAME " /com." $USER " ." $NAME " .plist
echo "Configuring project..."
2021-07-26 00:27:31 +00:00
2021-11-03 03:26:44 +00:00
# Record setup instructions
declare -a PYTHON_TOOL_SETUP_INSTRUCTIONS
2021-11-03 03:53:01 +00:00
declare -a PYTHON_TOOL_WARNINGS
2021-11-03 03:26:44 +00:00
2021-07-26 00:27:31 +00:00
# Describe the project
echo "Describe the project: "
read DESCRIPTION
sed_i "s/## ->.*//g" ./" $NAME " /README.md
sed_i " s/## Description/## $DESCRIPTION /g " ./" $NAME " /README.md
sed_i " s/\(.*\)description='',/\1description=' $DESCRIPTION ',/g " ./" $NAME " /setup.py
2021-07-21 01:41:35 +00:00
# Configure package as a module?
while [ [ " $PYTHON_MODULE " != 'y' && " $PYTHON_MODULE " != "n" ] ] ; do
echo "Set up project as a module? (in addition to a singular executable script) (y/n)"
read PYTHON_MODULE
done
if [ " $PYTHON_MODULE " = = 'y' ] ; then
mkdir ./" $NAME " /" $NAME "
2021-11-03 03:42:36 +00:00
echo " from $NAME . $NAME import * " > ./" $NAME " /" $NAME " /__init__.py
2021-11-03 04:32:38 +00:00
echo " # Write your modular code (classes, functions, etc) here. They'll be automatically imported in bin/ $NAME " > ./" $NAME " /" $NAME " /" $NAME " .py
2021-11-04 00:31:32 +00:00
PYTHON_TOOL_SETUP_INSTRUCTIONS += ( " Put your main classes and functionality in $NAME / $NAME .py and import/use that functionality in bin/ $NAME " )
PYTHON_TOOL_WARNINGS += ( 'You can use this package as a library! Classes you define in ' " $NAME / $NAME .py " " can be imported with 'import $NAME .class_name " )
2021-11-03 03:26:44 +00:00
else
2021-11-04 00:31:32 +00:00
PYTHON_TOOL_SETUP_INSTRUCTIONS += ( " Put your main classes and functionality in bin/ $NAME " )
2021-11-03 04:49:14 +00:00
sed_i " s#import $NAME ##g " ./" $NAME " /bin/" $NAME "
2021-07-21 01:41:35 +00:00
fi
2021-07-26 00:27:31 +00:00
2021-07-21 01:41:35 +00:00
# Configure package to install as a persistent service?
while [ [ " $SERVICE_FILE " != 'y' && " $SERVICE_FILE " != "n" ] ] ; do
2021-11-03 04:32:38 +00:00
echo "Install a persistent service file? (systemd/launchd services ONLY) (y/n)"
2021-07-21 01:41:35 +00:00
read SERVICE_FILE
done
if [ " $SERVICE_FILE " = = 'y' ] ; then
2021-07-26 00:47:45 +00:00
sed_i "s#INSTALL=\"\(.*\)\"#INSTALL=\"\1services \"#g" ./" $NAME " /setup.sh
2021-07-21 01:41:35 +00:00
# Configure service file to log output to file?
while [ [ " $SERVICE_LOG " != 'y' && " $SERVICE_LOG " != "n" ] ] ; do
echo "Configure the service file to log output? (y/n)"
read SERVICE_LOG
done
if [ " $SERVICE_LOG " != 'y' ] ; then
sed_i 's#.*Standard.*##g' ./" $NAME " /com." $USER " ." $NAME " .plist
sed_i 's#.*/var/log.*##g' ./" $NAME " /com." $USER " ." $NAME " .plist
sed_i 's#.*[sS]yslog.*##g' ./" $NAME " /" $NAME " .service
fi
# Don't forget to configure the service files.
2021-11-04 00:31:32 +00:00
PYTHON_TOOL_SETUP_INSTRUCTIONS += ( " Modify your service files ( $NAME .service and com. $USER . $NAME .plist) to schedule when the tool should run. By default, they run at boot and stay alive. " )
2021-07-21 01:41:35 +00:00
else
# Remove the custom setup scripts
2021-07-26 00:27:31 +00:00
rm ./" $NAME " /com." $USER " ." $NAME " .plist
2021-07-21 01:41:35 +00:00
rm ./" $NAME " /" $NAME " .service
fi
2021-07-26 00:27:31 +00:00
2021-07-21 01:41:35 +00:00
# Configure package to load plugins from plugins directory?
while [ [ " $USE_PLUGINS " != 'y' && " $USE_PLUGINS " != "n" ] ] ; do
echo "Configure package to load plugins? (y/n)"
read USE_PLUGINS
done
if [ " $USE_PLUGINS " = = 'y' ] ; then
2021-07-26 00:27:31 +00:00
# You've made Thomas Hatch proud.
2021-07-21 01:41:35 +00:00
! [ -d ./" $NAME " /" $NAME " ] && mkdir ./" $NAME " /" $NAME "
2021-11-03 04:32:38 +00:00
sed_i "s/.*##PLUGIN_\(.*\)/\1/g" ./" $NAME " /bin/" $NAME "
2021-07-21 01:41:35 +00:00
mkdir ./" $NAME " /" $NAME " /plugins
2021-11-03 04:49:14 +00:00
touch ./" $NAME " /" $NAME " /plugins/plugin.py
2021-11-04 00:31:32 +00:00
PYTHON_TOOL_SETUP_INSTRUCTIONS += ( " Set your plugin class by setting the plugin_class variable in bin/ $NAME and defining the class in $NAME /plugins/*.py files. " )
PYTHON_TOOL_WARNINGS += ( " At least one .py file needs to exist in $NAME /plugins/ for the plugin directory to be packaged for install. 'plugin.py' is provided for this purpose, but can be removed if you have other plugins. The file can be empty. " )
2022-05-16 19:38:56 +00:00
PYTHON_TOOL_WARNINGS += ( "When installing this package, you MUST use 'pip install -e' to link the executable to the package directory. This allows your plugins to work." )
2021-11-03 05:07:44 +00:00
if [ [ " $PYTHON_MODULE " = = 'y' ] ] ; then
2021-11-04 00:31:32 +00:00
PYTHON_TOOL_WARNINGS += ( " Migrating the plugin-loading logic from bin/ $NAME to $NAME / $NAME .py might be a good idea if you want your plugins to be loaded as part of your library. " )
2021-11-03 05:07:44 +00:00
fi
2021-07-21 01:41:35 +00:00
else :
sed_i 's#.*plugins.*##g' ./" $NAME " /setup.py
2021-11-04 00:39:29 +00:00
sed_i "/.*##PLUGIN_\(.*\)/d" ./" $NAME " /bin/" $NAME "
2021-07-21 01:41:35 +00:00
fi
2021-07-26 00:27:31 +00:00
2021-07-21 01:41:35 +00:00
# Configure package to install a default configuration file?
2021-07-26 00:27:31 +00:00
while [ [ " $INSTALL_CONFIG " != 'y' && " $INSTALL_CONFIG " != "n" ] ] ; do
2021-11-03 03:42:36 +00:00
echo "Configure package to use and install a config file? (y/n)"
2021-07-26 00:27:31 +00:00
read INSTALL_CONFIG
done
if [ " $INSTALL_CONFIG " = = 'y' ] ; then
sed_i "s#INSTALL=\"\(.*\)\"#INSTALL=\"\1config \"#g" ./" $NAME " /setup.sh
2021-11-03 03:53:01 +00:00
sed_i "s/.*##CONFIG_\(.*\)/\1/g" ./" $NAME " /bin/" $NAME "
2021-11-04 00:31:32 +00:00
PYTHON_TOOL_WARNINGS += ( "You'll need to install a copy of the config file yourself for debugging, as config.yml will only be installed when the package is installed." )
2021-11-03 02:37:08 +00:00
else
2021-11-04 00:39:29 +00:00
sed_i "/.*##CONFIG_\(.*\)/d" ./" $NAME " /bin/" $NAME "
2021-11-03 02:37:08 +00:00
rm ./" $NAME " /config.yml
2021-07-26 00:27:31 +00:00
fi
# Remove setup.sh if our tool doesn't need it to install.
2021-07-26 00:34:15 +00:00
if [ [ " $SERVICE_FILE " != 'y' && " $INSTALL_CONFIG " != 'y' ] ] ; then
2021-11-03 02:28:59 +00:00
sed_i 's#cmdclass=.*##g' ./" $NAME " /setup.py
2021-07-26 00:27:31 +00:00
rm ./" $NAME " /setup.sh
2021-11-03 04:32:38 +00:00
else
2021-11-04 00:31:32 +00:00
PYTHON_TOOL_WARNINGS += ( "The target system will need to have bash to run the postinstall actions you've chosen." )
2021-07-26 00:27:31 +00:00
fi
2021-07-21 01:41:35 +00:00
# Initialize the git repo
rm -rf ./" $NAME " /.git
git init ./" $NAME "
2021-07-26 01:15:54 +00:00
# Delete self from project_file
[ -f ./" $NAME " /new_python_tool.sh ] && echo "Removing project setup script..." && rm ./" $NAME " /new_python_tool.sh
2021-11-04 00:31:32 +00:00
echo " Python tool $NAME is ready to start! "
PYTHON_TOOL_SETUP_INSTRUCTIONS += ( "Add a remote to the git repo (git remote add origin <url>)" )
PYTHON_TOOL_SETUP_INSTRUCTIONS += ( "Update your library dependencies in setup.py as you add them to your project." )
2021-07-26 01:15:54 +00:00
2021-07-21 01:41:35 +00:00
# Next steps
echo "Next steps:"
2021-11-04 00:31:32 +00:00
OLDIFS = $IFS
2021-11-04 00:58:33 +00:00
IFS = $'\n'
2021-11-04 00:31:32 +00:00
for ( ( i = 0; i < ${# PYTHON_TOOL_SETUP_INSTRUCTIONS [@] } ; i++) ) ; do
STEPCOUNT = $(( " $i " + 1 ))
echo " $STEPCOUNT . ${ PYTHON_TOOL_SETUP_INSTRUCTIONS [ $i ] } "
done
STEPCOUNT = $(( $STEPCOUNT + 1 ))
echo " $STEPCOUNT . ... " && STEPCOUNT = $(( $STEPCOUNT + 1 )) && echo " $STEPCOUNT . Profit " && echo
IFS = $OLDIFS
2021-11-04 00:58:33 +00:00
if [ [ ${# PYTHON_TOOL_WARNINGS [@] } -gt 0 ] ] ; then
OLDIFS = $IFS
IFS = $'\n'
echo "Things to consider:"
for ( ( i = 0; i < ${# PYTHON_TOOL_WARNINGS [@] } ; i++) ) ; do
echo " * ${ PYTHON_TOOL_WARNINGS [ $i ] } "
done
IFS = $OLDIFS
fi
2021-07-21 01:41:35 +00:00
}
new_python_tool " $1 "