dev 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326
  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=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 project for development."
  55. echo " ${BOLD}afterinit${NORMAL} repeat help message displayed after init command is complete."
  56. echo " ${BOLD}remove${NORMAL} if dev project exists, remove its files and docker containers."
  57. echo " ${BOLD}rebuild${NORMAL} rebuild docker containers."
  58. echo " ${BOLD}reset${NORMAL} run remove 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."
  65. echo " ${BOLD}test module${NORMAL} run tests suite in specified python module, eg. misago.users."
  66. echo
  67. echo "Translations:"
  68. echo
  69. echo " ${BOLD}makemessages${NORMAL} update translation files for \"en\" language."
  70. echo " ${BOLD}makemessages lang${NORMAL} update translation files for \"lang\" language."
  71. echo " ${BOLD}compilemessages${NORMAL} compile translation files to \"mo\" format."
  72. echo " ${BOLD}txpull${NORMAL} pull translations from Transifex."
  73. echo " ${BOLD}txpush${NORMAL} push new source files to Transifex."
  74. echo " ${BOLD}txsync${NORMAL} runs entire process of syncing translations with Transifex."
  75. echo
  76. echo "Shortcuts:"
  77. echo
  78. echo " ${BOLD}manage.py${NORMAL} runs \"python manage.py\" inside docker."
  79. echo " ${BOLD}bash${NORMAL} starts bash session inside running Misago container."
  80. echo " ${BOLD}run${NORMAL} runs \"docker-compose run --rm misago\"."
  81. echo " ${BOLD}psql${NORMAL} runs psql connected to development database."
  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:8000"
  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:8000 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 "Development project directories:"
  139. echo
  140. echo "devproject configuration files for development instance."
  141. echo "media user uploaded files."
  142. echo "userdata working directory for user data exports."
  143. echo "avatargallery example avatar gallery."
  144. echo
  145. echo "To connect to development database use following credentials:"
  146. echo
  147. echo "User: misago"
  148. echo "Password: misago"
  149. echo "Database: misago"
  150. echo "Host: postgres"
  151. echo "Port: 5432"
  152. echo
  153. echo "Note: development server must be running for connection to be possible."
  154. echo
  155. }
  156. # Remove existing dev project
  157. remove() {
  158. echo -e "${RED}Warning:${NORMAL} You are going remove current development project."
  159. echo
  160. if [[ $will_delete_files = true ]]; then
  161. echo "Following files and directories will be deleted:"
  162. find ./devproject/media ! -name '.gitignode'
  163. find ./devproject/userdata ! -name '.gitignode'
  164. echo
  165. fi
  166. echo "Enter \"y\" to confirm:"
  167. read confirmation
  168. if [[ $confirmation = "y" ]]; then
  169. docker-compose stop
  170. docker-compose down --remove-orphans
  171. find ./devproject/media ! -name '.gitignode' -delete
  172. find ./devproject/userdata ! -name '.gitignode' -delete
  173. else
  174. echo "Operation canceled."
  175. fi
  176. }
  177. # Rebuild docker containers
  178. rebuild() {
  179. docker-compose stop
  180. docker-compose build --pull --force-rm "${@:2}"
  181. }
  182. # Run tests suite
  183. test() {
  184. docker-compose run --rm misago runtests.py $1
  185. docker-compose stop
  186. }
  187. # Make messages
  188. makemessages() {
  189. docker-compose run --rm --no-deps misago ./dev makemessages_in_docker $1
  190. }
  191. # Docker part of makemessages
  192. makemessages_in_docker() {
  193. require_in_docker
  194. echo "Extracting messages for $1 language:"
  195. cd ./misago
  196. echo "Processing .py and .html files..."
  197. django-admin.py makemessages -l $1 -e html,txt,py > /dev/null
  198. echo "Processing .js files..."
  199. django-admin.py makemessages -l $1 -d djangojs > /dev/null
  200. }
  201. # Compile messages
  202. compilemessages() {
  203. docker-compose run --rm --no-deps misago ./dev compilemessages_in_docker
  204. }
  205. # Docker part of compile messages
  206. compilemessages_in_docker() {
  207. require_in_docker
  208. cd ./misago
  209. django-admin.py compilemessages
  210. }
  211. # Pull translation files from transifex
  212. txpull() {
  213. tx pull
  214. compilemessages
  215. }
  216. # Push translation sources to transifex
  217. txpush() {
  218. tx push --source
  219. }
  220. # Shortcut for starting bash session in running container
  221. run_bash() {
  222. docker exec -it misago_misago_1 bash
  223. }
  224. # Shortcut for docker-compose run --rm misago python manage.py
  225. run_managepy() {
  226. docker-compose run --rm misago python manage.py "${@:2}"
  227. }
  228. # Shortcut for docker-compose run --rm misago...
  229. docker_run() {
  230. docker-compose run --rm misago "${@:2}"
  231. }
  232. # Shortcut for psql
  233. run_psql() {
  234. docker-compose run --rm misago ./dev psql_in_docker
  235. }
  236. # Docker part of psql shortcut
  237. psql_in_docker() {
  238. wait_for_db
  239. PGPASSWORD=$POSTGRES_PASSWORD psql --username $POSTGRES_USER --host $POSTGRES_HOST $POSTGRES_DB
  240. }
  241. # Command dispatcher
  242. if [[ $1 ]]; then
  243. if [[ $1 = "init" ]]; then
  244. init $@
  245. elif [[ $1 = "init_in_docker" ]]; then
  246. init_in_docker
  247. elif [[ $1 = "afterinit" ]]; then
  248. after_init_message
  249. elif [[ $1 = "remove" ]]; then
  250. remove
  251. elif [[ $1 = "reset" ]]; then
  252. remove
  253. init $@
  254. elif [[ $1 = "rebuild" ]]; then
  255. rebuild $@
  256. elif [[ $1 = "test" ]]; then
  257. test $2
  258. elif [[ $1 = "makemessages" ]]; then
  259. makemessages ${2:-en}
  260. elif [[ $1 = "makemessages_in_docker" ]]; then
  261. makemessages_in_docker $2
  262. elif [[ $1 = "compilemessages" ]]; then
  263. compilemessages
  264. elif [[ $1 = "compilemessages_in_docker" ]]; then
  265. compilemessages_in_docker
  266. elif [[ $1 = "txpull" ]]; then
  267. txpull
  268. elif [[ $1 = "txpush" ]]; then
  269. txpush
  270. elif [[ $1 = "txsync" ]]; then
  271. makemessages en
  272. txpush
  273. txpull
  274. elif [[ $1 = "bash" ]]; then
  275. run_bash
  276. elif [[ $1 = "manage.py" ]]; then
  277. run_managepy $@
  278. elif [[ $1 = "run" ]]; then
  279. docker_run $@
  280. elif [[ $1 = "psql" ]]; then
  281. run_psql
  282. elif [[ $1 = "psql_in_docker" ]]; then
  283. psql_in_docker
  284. else
  285. invalid_argument $1
  286. fi
  287. else
  288. intro
  289. fi