dev 11 KB

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