#!/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" 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-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, does nothing if project already exists." echo " ${BOLD}afterinit${NORMAL} repeat help message displayed after init command is complete." echo " ${BOLD}remove${NORMAL} if dev project exists, remove its files and docker containers." echo " ${BOLD}rebuild${NORMAL} rebuild docker containers." echo " ${BOLD}reset${NORMAL} run remove 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." 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 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 } # 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 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 \"remove\" option to remove any possible remaining files and try again." echo 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 "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 # 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 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:8000 address." echo echo "Default superuser has been created with this username and password:" echo echo "Username: $username" echo "Password: $password" echo echo "Development project directories:" echo echo "devproject configuration files for development instance." echo "media user uploaded files." echo "userdata working directory for user data exports." echo "avatargallery example avatar gallery." 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 } # Remove existing dev project remove() { echo -e "${RED}Warning:${NORMAL} You are going remove 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 "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 --pull --force-rm "${@: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 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 } # 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 = "remove" ]]; then remove elif [[ $1 = "reset" ]]; then remove 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 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 else invalid_argument $1 fi else intro fi