#!/bin/bash # "dev" is an utility script speeding up different development tasks and actions. # To find out what options are available, run it without any arguments. # Text styles RED='\033[0;31m' BOLD=$(tput bold) NORMAL=$(tput sgr0) # Required ports # Some tasks test for those ports before continuing port_django=${MISAGO_DEVSERVER_PORT:-8000} port_postgresql=5432 required_ports=($port_postgresql $port_django) # Default superuser username="Admin" password="password" email="admin@example.com" # Utility functions used by action commands error() { echo -e "${RED}Error:${NORMAL} $1" } require_in_docker() { if [[ ! $IN_MISAGO_DOCKER = 1 ]]; then error "This command can only be ran inside the running Misago docker container." exit 1 fi } wait_for_db() { require_in_docker export PGPASSWORD=$POSTGRES_PASSWORD RETRIES=10 until psql -h $POSTGRES_HOST -U $POSTGRES_USER -d $POSTGRES_DB -c "select 1" > /dev/null 2>&1 || [ $RETRIES -eq 0 ]; do echo "Waiting for PostgreSQL to start, $((RETRIES--)) remaining attempts..." sleep 2 done } # Check if user has docker-compose if [[ ! $IN_MISAGO_DOCKER = 1 ]]; then if ! command -v docker >/dev/null 2>&1; then error "You need to have Docker installed to use this tool." echo echo "Docker release for your system can be downloaded for free from this page:" echo "https://www.docker.com/get-started" echo exit 1 fi if ! command -v docker-compose >/dev/null 2>&1; then error "You need to have Docker Compose installed to use this tool." echo echo "Docker release for your system can be downloaded for free from this page:" echo "https://www.docker.com/get-started" echo exit 1 fi fi # Commands intro() { echo "Usage: ./dev [arg] ..." echo "Arguments grouped by type:" echo echo "Development project:" echo echo " ${BOLD}init${NORMAL} initialize dev database for development." echo " ${BOLD}afterinit${NORMAL} repeat help message displayed after init command is complete." echo " ${BOLD}clear${NORMAL} clear media and userdata dirs and destroy docker containers." echo " ${BOLD}rebuild${NORMAL} rebuild docker containers." echo " ${BOLD}reset${NORMAL} run clear followed by init." echo echo " Both init and rebuild args can be followed with any number of extra args and options that should be appended to docker-compose build." echo echo "Testing:" echo echo " ${BOLD}test${NORMAL} run tests suite using pytest." echo echo "Translations:" echo echo " ${BOLD}makemessages${NORMAL} update translation files for \"en\" language." echo " ${BOLD}makemessages lang${NORMAL} update translation files for \"lang\" language." echo " ${BOLD}compilemessages${NORMAL} compile translation files to \"mo\" format." echo " ${BOLD}txpull${NORMAL} pull translations from Transifex." echo " ${BOLD}txpush${NORMAL} push new source files to Transifex." echo " ${BOLD}txsync${NORMAL} runs entire process of syncing translations with Transifex." echo echo "Shortcuts:" echo echo " ${BOLD}manage.py${NORMAL} runs \"python manage.py\" inside docker." echo " ${BOLD}bash${NORMAL} starts bash session inside running Misago container." echo " ${BOLD}run${NORMAL} runs \"docker-compose run --rm misago\"." echo " ${BOLD}psql${NORMAL} runs psql connected to development database." echo " ${BOLD}pyfmt${NORMAL} runs isort + black on python code." echo " ${BOLD}fakedata${NORMAL} populates database with testing data." echo " ${BOLD}fakebigdata${NORMAL} populates database with LARGE amount of testing data." echo } # Handle invalid argument invalid_argument() { echo -e "Invalid argument: ${RED}$1${NORMAL}" echo "Please run this script without any arguments to see the list of available arguments." exit 1 } # Initialize new dev project init() { for port in "${required_ports[@]}"; do nc "127.0.0.1" "$port" < /dev/null if [[ $? = "0" ]]; then if [[ $port = $port_django ]]; then error "Other application appears to already be running on http://127.0.0.1:${port_django}" elif [[ $port = $port_postgresql ]]; then error "PostgreSQL appears to already be running on the port $port." echo echo "Misago runs its own PostgreSQL instance in the docker container and uses port $port to expose it to other programs." echo "Please stop your PostgreSQL server and try again." echo fi exit 1 fi done docker-compose stop docker-compose build --pull --force-rm "${@:2}" docker-compose run --rm misago ./dev init_in_docker } # Initialization step that has to occur inside docker init_in_docker() { require_in_docker wait_for_db # migrate the database python manage.py migrate # create superuser Admin with password "password" python manage.py createsuperuser --username $username --email $email --password $password # display after init message echo echo "================================================================================" after_init_message } # After-init message after_init_message() { echo echo "You can now start the development server using:" echo echo " docker-compose up" echo echo "Running server will be available in the browser under the http://127.0.0.1:${port_django} address." echo echo "Default superuser has been created with this username and password:" echo echo "Username: $username" echo "Password: $password" echo echo "To connect to development database use following credentials:" echo echo "User: misago" echo "Password: misago" echo "Database: misago" echo "Host: postgres" echo "Port: 5432" echo echo "Note: development server must be running for connection to be possible." echo } # Clear existing dev project clear() { echo -e "${RED}Warning:${NORMAL} You are going to delete media files created during development and destroy docker containers." echo devproject_path="$(pwd)/devproject" echo "Enter \"y\" to confirm:" read confirmation if [[ $confirmation = "y" ]]; then docker-compose stop docker-compose down --remove-orphans find $devproject_path/media -mindepth 1 ! -name '.gitignore' -delete find $devproject_path/userdata -mindepth 1 ! -name '.gitignore' -delete else echo "Operation canceled." fi } # Rebuild docker containers rebuild() { docker-compose stop docker-compose build --pull --force-rm "${@:2}" } # Run tests suite test() { docker-compose run --rm misago pytest "${@:2}" } # Make messages makemessages() { docker-compose run --rm --no-deps misago ./dev makemessages_in_docker $1 } # Docker part of makemessages makemessages_in_docker() { require_in_docker cd ./misago if [[ $1 ]]; then echo "Extracting messages for $1 language:" echo "Processing .py and .html files..." django-admin makemessages -l $1 --no-obsolete -e html,txt,py > /dev/null echo "Processing .js files..." django-admin makemessages -l $1 --no-obsolete -d djangojs > /dev/null else echo "Extracting messages for all languages:" django-admin makemessages --all --no-obsolete -e html,txt,py > /dev/null django-admin makemessages --all --no-obsolete -d djangojs > /dev/null fi } # Compile messages compilemessages() { docker-compose run --rm --no-deps misago ./dev compilemessages_in_docker } # Docker part of compile messages compilemessages_in_docker() { require_in_docker cd ./misago django-admin compilemessages } # Pull translation files from transifex txpull() { tx pull -a compilemessages } # Push translation sources to transifex txpush() { tx push --source } # Shortcut for starting bash session in running container run_bash() { docker exec -it misago_misago_1 bash } # Shortcut for docker-compose run --rm misago python manage.py run_managepy() { docker-compose run --rm misago python manage.py "${@:2}" } # Shortcut for docker-compose run --rm misago... docker_run() { docker-compose run --rm misago "${@:2}" } # Shortcut for psql run_psql() { docker-compose run --rm misago ./dev psql_in_docker } # Docker part of psql shortcut psql_in_docker() { wait_for_db PGPASSWORD=$POSTGRES_PASSWORD psql --username $POSTGRES_USER --host $POSTGRES_HOST $POSTGRES_DB } # Shortcut for creating small dev forum create_fake_data() { docker-compose run --rm misago python manage.py createfakecategories 7 docker-compose run --rm misago python manage.py createfakecategories 12 1 docker-compose run --rm misago python manage.py createfakehistory 600 } # Shortcut for creating big dev forum create_fake_bigdata() { docker-compose run --rm misago python manage.py createfakecategories 48 docker-compose run --rm misago python manage.py createfakecategories 24 1 docker-compose run --rm misago python manage.py createfakehistory 2190 120 } # Command dispatcher if [[ $1 ]]; then if [[ $1 = "init" ]]; then init $@ elif [[ $1 = "init_in_docker" ]]; then init_in_docker elif [[ $1 = "afterinit" ]]; then after_init_message elif [[ $1 = "clear" ]]; then clear elif [[ $1 = "reset" ]]; then clear init $@ elif [[ $1 = "rebuild" ]]; then rebuild $@ elif [[ $1 = "test" ]]; then test $@ elif [[ $1 = "makemessages" ]]; then makemessages $2 elif [[ $1 = "makemessages_in_docker" ]]; then makemessages_in_docker $2 elif [[ $1 = "compilemessages" ]]; then compilemessages elif [[ $1 = "compilemessages_in_docker" ]]; then compilemessages_in_docker elif [[ $1 = "txpull" ]]; then txpull elif [[ $1 = "txpush" ]]; then txpush elif [[ $1 = "txsync" ]]; then rm -rf ./misago/locale/en makemessages en txpush txpull compilemessages elif [[ $1 = "bash" ]]; then run_bash elif [[ $1 = "manage.py" ]]; then run_managepy $@ elif [[ $1 = "run" ]]; then docker_run $@ elif [[ $1 = "psql" ]]; then run_psql elif [[ $1 = "psql_in_docker" ]]; then psql_in_docker elif [[ $1 = "pyfmt" ]]; then isort -rc misago black devproject misago elif [[ $1 = "fakedata" ]]; then create_fake_data elif [[ $1 = "fakebigdata" ]]; then create_fake_bigdata else invalid_argument $1 fi else intro fi