Files
pycursesui/build.sh
T

397 lines
12 KiB
Bash
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env bash
USAGE=$(cat <<-END
USAGE: $0 <command> <options>*
init: install all development dependencies
--no-auto-refresh: skip the step of refreshing this build.sh script from
the tip of master in the boom-pylib repository
--nuke: completely remove the ./usr directory before initializing
edit: launch vim with settings to use the projects version of python
lint: run various programs to catch programming & style errors
test: run automated tests. By default, all tests are run once, and then the
script exits. There are a number of options to change this default. Some
conflict with one another (e.g., --watch and --functional). Whichever is
declared last will take precedence.
Unit tests are those which run completely within the process space of
the test runner (i.e., no disk or network access).
Functional tests are those which require no 3rd-party software be
installed or servers set up, but which otherwise do anything else.
Integration tests have no limits on what they may require.
--all (optional) turn on all test types
--circle-ci: (optional) only run tests which can pass in CircleCI.
Implies --no-integration.
--format: (optional) either "progress" or "documentation"
--messy: (optional) leave scratch directories behind in ./tmp
--watch: (optional) watch all the source files and repeat the tests
when things change. Implies --no-functional and --no-integration.
--(no-)functional: (optional) turn on/off functional tests
--(no-)unit: (optional) turn on/off unit tests
--(no-)integration: (optional) turn on/off integration tests
--none: (optional) turn off all test types
clean: remove temporary or one-off data from the ./tmp directory
python: start a Python REPL console which is sure to use the correct
version of Python pre-initialized with all the correct versions of
this packages dependencies
publish: publish this package as an official release on GemFury. This should
only be performed on the master branch, and only after a change has been
merged which updates the package version.
refresh: refresh this build script and its related helper files. This will
download the latest version from the boom-pylib repo and replace the
files in your current repo.
upgrade: upgrade any Python dependencies which have new versions available
END
)
AUTO_REFRESH="YES"
BUILD_CONFIG=".build_cfg.sh"
CIRCLE_CI="NO"
COMMAND="help"
CONFIRM="NO"
ENV_FILE=".env"
FORMAT="progress"
LINT_INCLUDE="src"
LINT_EXCLUDE="--exclude=.ropeproject"
MESSY="NO"
NUKE="NO"
PYTHON=$(which python)
PYTHON_VERSION="3.6"
TEST_FILE=".pylib.testing"
TEST_FUNCTIONAL="YES"
TEST_INTEGRATION="YES"
TEST_RUN=1
TEST_UNIT="YES"
VIRTUAL_ENV="./usr"
WATCH="NO"
[[ "$USER" == "" ]] && USER=$(whoami)
VIRTUAL_BIN="$VIRTUAL_ENV/bin"
# Helper Functions #####################################################################################################
function build-native-component {
# do nothing. Packages may override this function if they have a native component to build.
return 0
}
function build-python-env {
PYTHON_ENV="MESSY=$MESSY USER=$USER"
}
function check-python-version {
if ! $PYTHON --version | grep -q $PYTHON_VERSION; then
echo "Please update PYTHON in .env to refer to a Python $PYTHON_VERSION executable."
exit 1
fi
return 0
}
function die {
printf "$*\n"
echo
exit 1
}
function execute-functional-tests {
if [[ "$TEST_FUNCTIONAL" == "YES" ]]; then
printf "\nRunning functional tests...\n"
eval $PYTHON_ENV $VIRTUAL_BIN/mamba run src --format=$FORMAT -t functional
return $?
fi
return 0
}
function execute-integration-tests {
if [[ "$TEST_INTEGRATION" == "YES" ]]; then
printf "\nRunning integration tests...\n"
eval $PYTHON_ENV $VIRTUAL_BIN/mamba run src --format=$FORMAT -t integration
return $?
fi
return 0
}
function execute-test-run {
echo "Starting test run $TEST_RUN at $(date):"
echo "unit: $TEST_UNIT, functional: $TEST_FUNCTIONAL, integration: $TEST_INTEGRATION"
build-python-env
build-native-component \
&& execute-unit-tests \
&& execute-functional-tests \
&& execute-integration-tests \
|| return 1
return 0
}
function execute-unit-tests {
if [[ "$TEST_UNIT" == "YES" ]]; then
printf "\nRunning unit tests...\n"
eval $PYTHON_ENV $VIRTUAL_BIN/mamba run src --format=$FORMAT -t unit
return $?
fi
return 0
}
function init-custom {
# do nothing. repos with custom initialization can override this function
return 0
}
function on-unknown-argument {
die "Unknown argument: $1"
}
function usage {
die "$*\nTry \"$0 help\" for details"
}
function watch-source-files {
fswatch -o -r src -e '.*' -i '\.cpp$' -i '\.h$' -i '\.py$' -m poll_monitor
}
function write-build-config {
[[ -e "$BUILD_CONFIG" ]] && rm "$BUILD_CONFIG"
echo "# This file allows you to configure how the build.sh script operates. It will" >> $BUILD_CONFIG
echo "# be sourced immediately before running a build command. Feel free to leave it" >> $BUILD_CONFIG
echo "# empty if you don't need any modifications." >> $BUILD_CONFIG
}
function write-env-file {
[[ -e "$ENV_FILE" ]] && rm "$ENV_FILE"
echo '# The location of a `python` executable for Python v3.6' >> $ENV_FILE
echo "PYTHON=$PYTHON" >> $ENV_FILE
echo '# The authentication token used for uploading to GemFury' >> $ENV_FILE
echo "GEMFURY_TOKEN=$GEMFURY_TOKEN" >> $ENV_FILE
}
# Local Commands #######################################################################################################
function command-build {
build-native-component
}
function command-clean {
if [[ "$CONFIRM" == "YES" ]]; then
rm -rf ./tmp
else
echo "Removing ./tmp... (skipped)"
echo ""
fi
[[ "$CONFIRM" == "YES" ]] || printf "This was a dry run. To actually delete files, add: --confirm\n\n"
}
function command-edit {
source $VIRTUAL_BIN/activate
export VIRTUAL_ENV
$EDITOR
}
function command-help {
echo "$USAGE" | more
echo ""
exit
}
function command-init {
if [[ "$AUTO_REFRESH" == "YES" ]]; then
command-refresh-build || exit 1
fi
check-python-version || exit 1
[[ "$NUKE" == "YES" && -d $VIRTUAL_ENV ]] && rm -rf $VIRTUAL_ENV
$PYTHON -m venv --copies $VIRTUAL_ENV
./usr/bin/pip install --upgrade pip
unset PYTHONPATH
export PYTHONPATH
$VIRTUAL_BIN/pip install -r requirements.txt
init-custom
$VIRTUAL_BIN/pip freeze | grep -v '^-e' > .pip.lock
}
function command-lint {
$VIRTUAL_BIN/pycodestyle $LINT_INCLUDE $LINT_EXCLUDE
(( RESULT = $RESULT + $? ))
$VIRTUAL_BIN/pydocstyle $LINT_INCLUDE
(( RESULT = $RESULT + $? ))
$VIRTUAL_BIN/pyflakes $LINT_INCLUDE
(( RESULT = $RESULT + $? ))
return $RESULT
}
function command-publish {
CURRENT_BRANCH=$(git branch | grep '^\*' | awk '{print $2}')
if [[ "$CURRENT_BRANCH" == "master" ]]; then
[[ "$GEMFURY_TOKEN" == "" ]] && die "Please make sure GEMFURY_TOKEN is defined in $ENV_FILE"
$VIRTUAL_BIN/python setup.py sdist
PACKAGE="dist/$(ls dist | head -n1)"
PUBLISHED_VERSION=$( \
curl -s https://$GEMFURY_TOKEN@pypi.fury.io/boomtechnology/$PACKAGE \
| grep boom-pylib- \
| sed "s/.*$PACKAGE-\([0-9.].*\).tar.gz.*/\1/" \
| tail -n1
)
CURRENT_VERSION=$(cat setup.py | grep version= | sed 's/.*version="\(.*\)".*/\1/')
if [[ "$PUBLISHED_VERSION" != "$CURRENT_VERSION" ]]; then
curl -F package=@$PACKAGE https://$GEMFURY_TOKEN@push.fury.io/boomtechnology \
|| die "\nUploading package failed!"
else
echo "Version $CURRENT_VERSION has already been published, skipping publish..."
fi
rm -rf dist 2>/dev/null
else
echo "Not on master branch, skipping publish..."
fi
}
function command-python {
source $VIRTUAL_BIN/activate
eval $PYTHON_ENV $VIRTUAL_BIN/python
}
function command-refresh-build {
# Install the Git Hooks
pushd .git/hooks >/dev/null
ls ../../src/hooks | while read HOOK_FILE; do
ln -sf "../../src/hooks/$HOOK_FILE" .
done
popd >/dev/null
if cat .git/config | grep 'url =' | head -n1 | grep -q boom-pylib; then
# We're already in the boom-pylib repo.
return 0
fi
# Download the current version of boom-pylib
TMP_REPO=".boom-pylib"
rm -rf $TMP_REPO 2>/dev/null
git clone -q --depth 1 [email protected]:boomtechnology/boom-pylib.git $TMP_REPO
# Copy over the files we need, replacing existing copies
mkdir -p src
rm -rf src/hooks 2>/dev/null
cp $TMP_REPO/build.sh .build.sh.new
cp -R $TMP_REPO/src/hooks src/hooks
# Get rid of the boom-pylib repo
rm -rf $TMP_REPO 2>/dev/null
(sleep 1; mv .build.sh.new build.sh) &
}
function command-test {
RESULT=0
function do-watched-run {
rm -rf ./tmp 2>/dev/null
execute-test-run > $TEST_FILE.out 2>&1
RESULT=$?
clear
cat "$TEST_FILE.out"
rm "$TEST_FILE.out"
rm "$TEST_FILE"
printf "\nWaiting for changes...\n"
}
if [[ "$WATCH" == "YES" ]]; then
clear
execute-test-run
echo "Waiting for changes..."
rm $TEST_FILE 2> /dev/null
watch-source-files | while read FILE; do
if [[ ! -e $TEST_FILE ]]; then
touch $TEST_FILE
(( TEST_RUN = TEST_RUN + 1 ))
printf "\nChanges detected! Re-running tests...\n"
do-watched-run &
fi
done
else
execute-test-run
RESULT=$?
fi
[[ "$MESSY" == "NO" ]] && rm -rf ./tmp 2>/dev/null
return $RESULT
}
function command-upgrade-pip {
$VIRTUAL_BIN/pip install -r requirements.txt --upgrade
$VIRTUAL_BIN/pip freeze | grep -v '^-e' > .pip.lock
}
# Process Arguments ####################################################################################################
[[ -e $ENV_FILE ]] && source $ENV_FILE
[[ -e $BUILD_CONFIG ]] || write-build-config
source $BUILD_CONFIG
write-env-file
while [[ "$1" != "" ]]; do
case "$1" in
build) COMMAND="build";;
clean) COMMAND="clean";;
edit) COMMAND="edit";;
help|--help|-h|-?) COMMAND="help";;
init) COMMAND="init";;
lint) COMMAND="lint";;
python) COMMAND="python";;
publish) COMMAND="publish";;
refresh) COMMAND="refresh-build";;
test) COMMAND="test";;
upgrade) COMMAND="upgrade-pip";;
--all) TEST_UNIT="YES"; TEST_FUNCTIONAL="YES"; TEST_INTEGRATION="YES";;
--no-refresh) AUTO_REFRESH="NO";;
--circle-ci) CIRCLE_CI="YES"; TEST_INTEGRATION="NO";;
--confirm) CONFIRM="YES";;
--format) shift; FORMAT="$1";;
--functional) TEST_FUNCTIONAL="YES";;
--integration) TEST_INTEGRATION="YES";;
--messy) MESSY="YES";;
--no-functional) TEST_FUNCTIONAL="NO";;
--no-integration) TEST_INTEGRATION="NO";;
--no-unit) TEST_UNIT="NO";;
--none) TEST_UNIT="NO"; TEST_FUNCTIONAL="NO"; TEST_INTEGRATION="NO";;
--nuke) NUKE="YES";;
--unit) TEST_UNIT="YES";;
--watch) TEST_FUNCTIONAL="NO"; TEST_INTEGRATION="NO"; WATCH="YES";;
*) on-unknown-argument $1;;
esac
shift
done
# Script ###############################################################################################################
"command-$COMMAND"