dev 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322
  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 " ${BOLD}fmtpy${NORMAL} runs isort + black on python code."
  82. echo
  83. }
  84. # Handle invalid argument
  85. invalid_argument() {
  86. echo -e "Invalid argument: ${RED}$1${NORMAL}"
  87. echo "Please run this script without any arguments to see the list of available arguments."
  88. exit 1
  89. }
  90. # Initialize new dev project
  91. init() {
  92. for port in "${required_ports[@]}"; do
  93. nc "127.0.0.1" "$port" < /dev/null
  94. if [[ $? = "0" ]]; then
  95. if [[ $port = $port_django ]]; then
  96. error "Other application appears to already be running on http://127.0.0.1:${port_django}"
  97. elif [[ $port = $port_postgresql ]]; then
  98. error "PostgreSQL appears to already be running on the port $port."
  99. echo
  100. echo "Misago runs its own PostgreSQL instance in the docker container and uses port $port to expose it to other programs."
  101. echo "Please stop your PostgreSQL server and try again."
  102. echo
  103. fi
  104. exit 1
  105. fi
  106. done
  107. docker-compose stop
  108. docker-compose build --pull --force-rm "${@:2}"
  109. docker-compose run --rm misago ./dev init_in_docker
  110. }
  111. # Initialization step that has to occur inside docker
  112. init_in_docker() {
  113. require_in_docker
  114. wait_for_db
  115. # migrate the database
  116. python manage.py migrate
  117. # create superuser Admin with password "password"
  118. python manage.py createsuperuser --username $username --email $email --password $password
  119. # display after init message
  120. echo
  121. echo "================================================================================"
  122. after_init_message
  123. }
  124. # After-init message
  125. after_init_message() {
  126. echo
  127. echo "You can now start the development server using:"
  128. echo
  129. echo " docker-compose up"
  130. echo
  131. echo "Running server will be available in the browser under the http://127.0.0.1:${port_django} address."
  132. echo
  133. echo "Default superuser has been created with this username and password:"
  134. echo
  135. echo "Username: $username"
  136. echo "Password: $password"
  137. echo
  138. echo "To connect to development database use following credentials:"
  139. echo
  140. echo "User: misago"
  141. echo "Password: misago"
  142. echo "Database: misago"
  143. echo "Host: postgres"
  144. echo "Port: 5432"
  145. echo
  146. echo "Note: development server must be running for connection to be possible."
  147. echo
  148. }
  149. # Clear existing dev project
  150. clear() {
  151. echo -e "${RED}Warning:${NORMAL} You are going to delete media files created during development and destroy docker containers."
  152. echo
  153. devproject_path="$(pwd)/devproject"
  154. echo "Following files and directories will be deleted:"
  155. find $devproject_path/media -mindepth 1 ! -name '.gitignore'
  156. find $devproject_path/userdata -mindepth 1 ! -name '.gitignore'
  157. echo
  158. echo "Enter \"y\" to confirm:"
  159. read confirmation
  160. if [[ $confirmation = "y" ]]; then
  161. docker-compose stop
  162. docker-compose down --remove-orphans
  163. find $devproject_path/media -mindepth 1 ! -name '.gitignore' -delete
  164. find $devproject_path/userdata -mindepth 1 ! -name '.gitignore' -delete
  165. else
  166. echo "Operation canceled."
  167. fi
  168. }
  169. # Rebuild docker containers
  170. rebuild() {
  171. docker-compose stop
  172. docker-compose build --pull --force-rm "${@:2}"
  173. }
  174. # Run tests suite
  175. test() {
  176. docker-compose run --rm misago pytest "${@:2}"
  177. }
  178. # Make messages
  179. makemessages() {
  180. docker-compose run --rm --no-deps misago ./dev makemessages_in_docker $1
  181. }
  182. # Docker part of makemessages
  183. makemessages_in_docker() {
  184. require_in_docker
  185. echo "Extracting messages for $1 language:"
  186. cd ./misago
  187. echo "Processing .py and .html files..."
  188. django-admin.py makemessages -l $1 -e html,txt,py > /dev/null
  189. echo "Processing .js files..."
  190. django-admin.py makemessages -l $1 -d djangojs > /dev/null
  191. }
  192. # Compile messages
  193. compilemessages() {
  194. docker-compose run --rm --no-deps misago ./dev compilemessages_in_docker
  195. }
  196. # Docker part of compile messages
  197. compilemessages_in_docker() {
  198. require_in_docker
  199. cd ./misago
  200. django-admin.py compilemessages
  201. }
  202. # Pull translation files from transifex
  203. txpull() {
  204. tx pull
  205. compilemessages
  206. }
  207. # Push translation sources to transifex
  208. txpush() {
  209. tx push --source
  210. }
  211. # Shortcut for starting bash session in running container
  212. run_bash() {
  213. docker exec -it misago_misago_1 bash
  214. }
  215. # Shortcut for docker-compose run --rm misago python manage.py
  216. run_managepy() {
  217. docker-compose run --rm misago python manage.py "${@:2}"
  218. }
  219. # Shortcut for docker-compose run --rm misago...
  220. docker_run() {
  221. docker-compose run --rm misago "${@:2}"
  222. }
  223. # Shortcut for psql
  224. run_psql() {
  225. docker-compose run --rm misago ./dev psql_in_docker
  226. }
  227. # Docker part of psql shortcut
  228. psql_in_docker() {
  229. wait_for_db
  230. PGPASSWORD=$POSTGRES_PASSWORD psql --username $POSTGRES_USER --host $POSTGRES_HOST $POSTGRES_DB
  231. }
  232. # Command dispatcher
  233. if [[ $1 ]]; then
  234. if [[ $1 = "init" ]]; then
  235. init $@
  236. elif [[ $1 = "init_in_docker" ]]; then
  237. init_in_docker
  238. elif [[ $1 = "afterinit" ]]; then
  239. after_init_message
  240. elif [[ $1 = "clear" ]]; then
  241. clear
  242. elif [[ $1 = "reset" ]]; then
  243. clear
  244. init $@
  245. elif [[ $1 = "rebuild" ]]; then
  246. rebuild $@
  247. elif [[ $1 = "test" ]]; then
  248. test $@
  249. elif [[ $1 = "makemessages" ]]; then
  250. makemessages ${2:-en}
  251. elif [[ $1 = "makemessages_in_docker" ]]; then
  252. makemessages_in_docker $2
  253. elif [[ $1 = "compilemessages" ]]; then
  254. compilemessages
  255. elif [[ $1 = "compilemessages_in_docker" ]]; then
  256. compilemessages_in_docker
  257. elif [[ $1 = "txpull" ]]; then
  258. txpull
  259. elif [[ $1 = "txpush" ]]; then
  260. txpush
  261. elif [[ $1 = "txsync" ]]; then
  262. makemessages en
  263. txpush
  264. txpull
  265. elif [[ $1 = "bash" ]]; then
  266. run_bash
  267. elif [[ $1 = "manage.py" ]]; then
  268. run_managepy $@
  269. elif [[ $1 = "run" ]]; then
  270. docker_run $@
  271. elif [[ $1 = "psql" ]]; then
  272. run_psql
  273. elif [[ $1 = "psql_in_docker" ]]; then
  274. psql_in_docker
  275. elif [[ $1 = "fmtpy" ]]; then
  276. isort -rc misago
  277. black devproject misago
  278. else
  279. invalid_argument $1
  280. fi
  281. else
  282. intro
  283. fi