dev 11 KB

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