#!/bin/bash # devctl is an utility script for automating some 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) # Define dev paths # Those are paths to dirs and files created for dev project dev_paths=( "./avatargallery" "./devproject" "./media" "./static" "./theme" "./userdata" "./cron.txt" "./manage.py" ) # Required ports # Some tasks test for those ports before continuing port_django=8000 port_postgresql=5432 required_ports=($port_postgresql $port_django) # Default superuser username="Admin" password="password" # Some commond shorthands error() { echo -e "${RED}Error:${NORMAL} $1" } require_in_docker() { if [[ ! $IN_DOCKER = 1 ]]; then error "This command can only be ran inside the running Misago 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 postgres server, $((RETRIES--)) remaining attempts..." sleep 2 done } # Check if user has docker-compose if [[ ! $IN_DOCKER = 1 ]]; then if ! command -v docker-compose >/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 fi # Commands intro() { echo "Usage: ./dev [arg] ..." echo "Arguments grouped by type:" echo echo "Development project:" echo echo " ${BOLD}init${NORMAL} initialize new dev project for development, do nothing if project already exists." echo " ${BOLD}clear${NORMAL} if dev project exists, delete it's files 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 forwarded to docker-compose build." echo echo "Testing:" echo echo " ${BOLD}test${NORMAL} run tests suite." echo " ${BOLD}test module${NORMAL} run tests suite in specified python module, eg. misago.users." 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." echo echo "Shortcuts:" echo echo " ${BOLD}bash${NORMAL} shortcut for entering bash session inside running Misago container." echo " ${BOLD}run${NORMAL} shortcut for \"docker-compose run --rm misago\"." echo " ${BOLD}psql${NORMAL} shortcut for dev database's psql." echo } # Handle invalid option message 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 dev_path in "${dev_paths[@]}"; do if [ -e $dev_path ]; then error "Dev project already exists, or was not deleted completely." echo echo "Please use \"clear\" option to clear any possible remaining files and try again." exit 1 fi done 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:8000" elif [[ $port = $port_postgresql ]]; then error "Other PostgreSQL instance appears to already be running on port $port." fi echo echo "Please stop other process and try again." exit 1 fi done docker-compose stop docker-compose build "${@: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 # initialize django project python misago/bin/misago-start-devproject.py # move items of interest up one level mv devproject/devproject devproject_tmp mv devproject/avatargallery ./avatargallery mv devproject/media ./media mv devproject/userdata ./userdata mv devproject/manage.py ./manage.py rm -rf devproject mv devproject_tmp devproject # migrate the DB python manage.py migrate # create superuser Admin with password "password" python manage.py createsuperuser --username $username --email admin@example.com --password $password echo echo "================================================================================" 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:8000 address." echo echo "Default superuser has been created with following credentials:" echo echo "Username: $username" echo "Password: $password" echo echo "For development project configuration see files in the \"devproject\" directory." echo } # Clear existing dev project clear() { echo -e "${RED}Warning:${NORMAL} You are going clear current development project." echo will_delete_files=false for dev_path in "${dev_paths[@]}"; do if [ -e $dev_path ]; then will_delete_files=true fi done if [[ $will_delete_files = true ]]; then echo "Following files and directories will be deleted:" for dev_path in "${dev_paths[@]}"; do if [ -e $dev_path ]; then echo " $dev_path" fi done echo fi echo "This will also stop and remove docker containers used by this project." echo echo "Enter \"y\" to confirm:" read confirmation if [[ $confirmation = "y" ]]; then for dev_path in "${dev_paths[@]}"; do if [ -e $dev_path ]; then echo "Removing $dev_path" rm -rf $dev_path fi done docker-compose stop docker-compose down --remove-orphans else echo "Operation canceled." fi } # Rebuild docker containers rebuild() { docker-compose stop docker-compose build "${@:2}" } # Run tests suite test() { docker-compose run --rm misago runtests.py $1 docker-compose stop } # 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 echo "Extracting messages for $1 language:" cd ./misago echo "Processing .py and .html files..." django-admin.py makemessages -l $1 -e html,txt,py > /dev/null echo "Processing .js files..." django-admin.py makemessages -l $1 -d djangojs > /dev/null } # 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.py compilemessages } # Pull translation files from transifex txpull() { tx pull compilemessages } # Push translation sources to transifex txpush() { tx push --source } # Shortcut for starting bash session in running container docker_run_bash() { docker exec -it misago_misago_1 bash } # Shortcut for docker-compose run... docker_run() { docker-compose run --rm misago "${@:2}" } # Shortcut for psql docker_run_psql() { docker-compose run --rm misago ./dev psql_in_docker } psql_in_docker() { wait_for_db PGPASSWORD=$POSTGRES_PASSWORD psql --username $POSTGRES_USER --host $POSTGRES_HOST $POSTGRES_DB } # Command dispatcher if [[ $1 ]]; then if [[ $1 = "init" ]]; then init $@ elif [[ $1 = "init_in_docker" ]]; then init_in_docker elif [[ $1 = "clear" ]]; then clear elif [[ $1 = "reset" ]]; then clear init $@ elif [[ $1 = "rebuild" ]]; then rebuild $@ elif [[ $1 = "test" ]]; then test $2 elif [[ $1 = "makemessages" ]]; then makemessages ${2:-en} 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 makemessages txpush txpull compilemessages elif [[ $1 = "bash" ]]; then docker_run_bash elif [[ $1 = "run" ]]; then docker_run $@ elif [[ $1 = "psql" ]]; then docker_run_psql elif [[ $1 = "psql_in_docker" ]]; then psql_in_docker else invalid_argument $1 fi else intro fi