dev 9.6 KB

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