dev 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318
  1. #!/bin/bash
  2. # "dev" is an utility script speeding up different development tasks and actions.
  3. # To find out what options are available, run it without any arguments.
  4. # Text styles
  5. RED='\033[0;31m'
  6. BOLD=$(tput bold)
  7. NORMAL=$(tput sgr0)
  8. # Required ports
  9. # Some tasks test for those ports before continuing
  10. port_django=${MISAGO_DEVSERVER_PORT:-8000}
  11. port_postgresql=5432
  12. required_ports=($port_postgresql $port_django)
  13. # Default superuser
  14. username="Admin"
  15. password="password"
  16. email="admin@example.com"
  17. # Utility functions used by action commands
  18. error() {
  19. echo -e "${RED}Error:${NORMAL} $1"
  20. }
  21. require_in_docker() {
  22. if [[ ! $IN_MISAGO_DOCKER = 1 ]]; then
  23. error "This command can only be ran inside the running Misago docker container."
  24. exit 1
  25. fi
  26. }
  27. wait_for_db() {
  28. require_in_docker
  29. export PGPASSWORD=$POSTGRES_PASSWORD
  30. RETRIES=10
  31. until psql -h $POSTGRES_HOST -U $POSTGRES_USER -d $POSTGRES_DB -c "select 1" > /dev/null 2>&1 || [ $RETRIES -eq 0 ]; do
  32. echo "Waiting for PostgreSQL to start, $((RETRIES--)) remaining attempts..."
  33. sleep 2
  34. done
  35. }
  36. # Check if user has docker-compose
  37. if [[ ! $IN_MISAGO_DOCKER = 1 ]]; then
  38. if ! command -v docker-compose >/dev/null 2>&1; then
  39. error "You need to have Docker installed to use this tool."
  40. echo
  41. echo "Docker release for your system can be downloaded for free from this page:"
  42. echo "https://www.docker.com/get-started"
  43. echo
  44. exit 1
  45. fi
  46. fi
  47. # Commands
  48. intro() {
  49. echo "Usage: ./dev [arg] ..."
  50. echo "Arguments grouped by type:"
  51. echo
  52. echo "Development project:"
  53. echo
  54. echo " ${BOLD}init${NORMAL} initialize dev database for development."
  55. echo " ${BOLD}afterinit${NORMAL} repeat help message displayed after init command is complete."
  56. echo " ${BOLD}clear${NORMAL} clear media and userdata dirs and destroy docker containers."
  57. echo " ${BOLD}rebuild${NORMAL} rebuild docker containers."
  58. echo " ${BOLD}reset${NORMAL} run clear followed by init."
  59. echo
  60. 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."
  61. echo
  62. echo "Testing:"
  63. echo
  64. echo " ${BOLD}test${NORMAL} run tests suite using pytest."
  65. echo
  66. echo "Translations:"
  67. echo
  68. echo " ${BOLD}makemessages${NORMAL} update translation files for \"en\" language."
  69. echo " ${BOLD}makemessages lang${NORMAL} update translation files for \"lang\" language."
  70. echo " ${BOLD}compilemessages${NORMAL} compile translation files to \"mo\" format."
  71. echo " ${BOLD}txpull${NORMAL} pull translations from Transifex."
  72. echo " ${BOLD}txpush${NORMAL} push new source files to Transifex."
  73. echo " ${BOLD}txsync${NORMAL} runs entire process of syncing translations with Transifex."
  74. echo
  75. echo "Shortcuts:"
  76. echo
  77. echo " ${BOLD}manage.py${NORMAL} runs \"python manage.py\" inside docker."
  78. echo " ${BOLD}bash${NORMAL} starts bash session inside running Misago container."
  79. echo " ${BOLD}run${NORMAL} runs \"docker-compose run --rm misago\"."
  80. echo " ${BOLD}psql${NORMAL} runs psql connected to development database."
  81. echo
  82. }
  83. # Handle invalid argument
  84. invalid_argument() {
  85. echo -e "Invalid argument: ${RED}$1${NORMAL}"
  86. echo "Please run this script without any arguments to see the list of available arguments."
  87. exit 1
  88. }
  89. # Initialize new dev project
  90. init() {
  91. for port in "${required_ports[@]}"; do
  92. nc "127.0.0.1" "$port" < /dev/null
  93. if [[ $? = "0" ]]; then
  94. if [[ $port = $port_django ]]; then
  95. error "Other application appears to already be running on http://127.0.0.1:${port_django}"
  96. elif [[ $port = $port_postgresql ]]; then
  97. error "PostgreSQL appears to already be running on the port $port."
  98. echo
  99. echo "Misago runs its own PostgreSQL instance in the docker container and uses port $port to expose it to other programs."
  100. echo "Please stop your PostgreSQL server and try again."
  101. echo
  102. fi
  103. exit 1
  104. fi
  105. done
  106. docker-compose stop
  107. docker-compose build --pull --force-rm "${@:2}"
  108. docker-compose run --rm misago ./dev init_in_docker
  109. }
  110. # Initialization step that has to occur inside docker
  111. init_in_docker() {
  112. require_in_docker
  113. wait_for_db
  114. # migrate the database
  115. python manage.py migrate
  116. # create superuser Admin with password "password"
  117. python manage.py createsuperuser --username $username --email $email --password $password
  118. # display after init message
  119. echo
  120. echo "================================================================================"
  121. after_init_message
  122. }
  123. # After-init message
  124. after_init_message() {
  125. echo
  126. echo "You can now start the development server using:"
  127. echo
  128. echo " docker-compose up"
  129. echo
  130. echo "Running server will be available in the browser under the http://127.0.0.1:${port_django} address."
  131. echo
  132. echo "Default superuser has been created with this username and password:"
  133. echo
  134. echo "Username: $username"
  135. echo "Password: $password"
  136. echo
  137. echo "To connect to development database use following credentials:"
  138. echo
  139. echo "User: misago"
  140. echo "Password: misago"
  141. echo "Database: misago"
  142. echo "Host: postgres"
  143. echo "Port: 5432"
  144. echo
  145. echo "Note: development server must be running for connection to be possible."
  146. echo
  147. }
  148. # Clear existing dev project
  149. clear() {
  150. echo -e "${RED}Warning:${NORMAL} You are going to delete media files created during development and destroy docker containers."
  151. echo
  152. devproject_path="$(pwd)/devproject"
  153. echo "Following files and directories will be deleted:"
  154. find $devproject_path/media -mindepth 1 ! -name '.gitignore'
  155. find $devproject_path/userdata -mindepth 1 ! -name '.gitignore'
  156. echo
  157. echo "Enter \"y\" to confirm:"
  158. read confirmation
  159. if [[ $confirmation = "y" ]]; then
  160. docker-compose stop
  161. docker-compose down --remove-orphans
  162. find $devproject_path/media -mindepth 1 ! -name '.gitignore' -delete
  163. find $devproject_path/userdata -mindepth 1 ! -name '.gitignore' -delete
  164. else
  165. echo "Operation canceled."
  166. fi
  167. }
  168. # Rebuild docker containers
  169. rebuild() {
  170. docker-compose stop
  171. docker-compose build --pull --force-rm "${@:2}"
  172. }
  173. # Run tests suite
  174. test() {
  175. docker-compose run --rm misago pytest "${@:2}"
  176. }
  177. # Make messages
  178. makemessages() {
  179. docker-compose run --rm --no-deps misago ./dev makemessages_in_docker $1
  180. }
  181. # Docker part of makemessages
  182. makemessages_in_docker() {
  183. require_in_docker
  184. echo "Extracting messages for $1 language:"
  185. cd ./misago
  186. echo "Processing .py and .html files..."
  187. django-admin.py makemessages -l $1 -e html,txt,py > /dev/null
  188. echo "Processing .js files..."
  189. django-admin.py makemessages -l $1 -d djangojs > /dev/null
  190. }
  191. # Compile messages
  192. compilemessages() {
  193. docker-compose run --rm --no-deps misago ./dev compilemessages_in_docker
  194. }
  195. # Docker part of compile messages
  196. compilemessages_in_docker() {
  197. require_in_docker
  198. cd ./misago
  199. django-admin.py compilemessages
  200. }
  201. # Pull translation files from transifex
  202. txpull() {
  203. tx pull
  204. compilemessages
  205. }
  206. # Push translation sources to transifex
  207. txpush() {
  208. tx push --source
  209. }
  210. # Shortcut for starting bash session in running container
  211. run_bash() {
  212. docker exec -it misago_misago_1 bash
  213. }
  214. # Shortcut for docker-compose run --rm misago python manage.py
  215. run_managepy() {
  216. docker-compose run --rm misago python manage.py "${@:2}"
  217. }
  218. # Shortcut for docker-compose run --rm misago...
  219. docker_run() {
  220. docker-compose run --rm misago "${@:2}"
  221. }
  222. # Shortcut for psql
  223. run_psql() {
  224. docker-compose run --rm misago ./dev psql_in_docker
  225. }
  226. # Docker part of psql shortcut
  227. psql_in_docker() {
  228. wait_for_db
  229. PGPASSWORD=$POSTGRES_PASSWORD psql --username $POSTGRES_USER --host $POSTGRES_HOST $POSTGRES_DB
  230. }
  231. # Command dispatcher
  232. if [[ $1 ]]; then
  233. if [[ $1 = "init" ]]; then
  234. init $@
  235. elif [[ $1 = "init_in_docker" ]]; then
  236. init_in_docker
  237. elif [[ $1 = "afterinit" ]]; then
  238. after_init_message
  239. elif [[ $1 = "clear" ]]; then
  240. clear
  241. elif [[ $1 = "reset" ]]; then
  242. clear
  243. init $@
  244. elif [[ $1 = "rebuild" ]]; then
  245. rebuild $@
  246. elif [[ $1 = "test" ]]; then
  247. test $@
  248. elif [[ $1 = "makemessages" ]]; then
  249. makemessages ${2:-en}
  250. elif [[ $1 = "makemessages_in_docker" ]]; then
  251. makemessages_in_docker $2
  252. elif [[ $1 = "compilemessages" ]]; then
  253. compilemessages
  254. elif [[ $1 = "compilemessages_in_docker" ]]; then
  255. compilemessages_in_docker
  256. elif [[ $1 = "txpull" ]]; then
  257. txpull
  258. elif [[ $1 = "txpush" ]]; then
  259. txpush
  260. elif [[ $1 = "txsync" ]]; then
  261. makemessages en
  262. txpush
  263. txpull
  264. elif [[ $1 = "bash" ]]; then
  265. run_bash
  266. elif [[ $1 = "manage.py" ]]; then
  267. run_managepy $@
  268. elif [[ $1 = "run" ]]; then
  269. docker_run $@
  270. elif [[ $1 = "psql" ]]; then
  271. run_psql
  272. elif [[ $1 = "psql_in_docker" ]]; then
  273. psql_in_docker
  274. else
  275. invalid_argument $1
  276. fi
  277. else
  278. intro
  279. fi