dev 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339
  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)
  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} shortcut for running clear and 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 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 pissible 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 "Django application appears to already be running on http://127.0.0.1:8000"
  114. elif [[ $port = $port_postgresql ]]; then
  115. error "PostgreSQL appears to already be running on port $port."
  116. fi
  117. exit 1
  118. fi
  119. done
  120. docker-compose stop
  121. docker-compose build "${@:2}"
  122. docker-compose run --rm misago ./dev init_in_docker
  123. }
  124. # Initialization step that has to occur inside docker
  125. init_in_docker() {
  126. require_in_docker
  127. wait_for_db
  128. # initialize django project
  129. python misago/bin/misago-start-devproject.py
  130. # move items of interest up one level
  131. mv devproject/devproject devproject_tmp
  132. mv devproject/avatargallery ./avatargallery
  133. mv devproject/media ./media
  134. mv devproject/userdata ./userdata
  135. mv devproject/manage.py ./manage.py
  136. rm -rf devproject
  137. mv devproject_tmp devproject
  138. # migrate the DB
  139. python manage.py migrate
  140. # create superuser Admin with password "password"
  141. python manage.py createsuperuser --username $username --email admin@example.com --password $password
  142. echo
  143. echo "================================================================================"
  144. echo
  145. echo "You can now start the development server using:"
  146. echo
  147. echo " docker-compose up"
  148. echo
  149. echo "Running server will be available in the browser under the http://127.0.0.1:8000 address."
  150. echo
  151. echo "Default superuser has been created with following credentials:"
  152. echo
  153. echo "Username: $username"
  154. echo "Password: $password"
  155. echo
  156. echo "For development project configuration see files in the \"devproject\" directory."
  157. echo
  158. }
  159. # Clear existing dev project
  160. clear() {
  161. echo -e "${RED}Warning:${NORMAL} You are going clear current development project."
  162. echo
  163. will_delete_files=false
  164. for dev_path in "${dev_paths[@]}"; do
  165. if [ -e $dev_path ]; then
  166. will_delete_files=true
  167. fi
  168. done
  169. if [[ $will_delete_files = true ]]; then
  170. echo "Following files and directories will be deleted:"
  171. for dev_path in "${dev_paths[@]}"; do
  172. if [ -e $dev_path ]; then
  173. echo " $dev_path"
  174. fi
  175. done
  176. echo
  177. fi
  178. echo "This will also stop and remove docker containers used by this project."
  179. echo
  180. echo "Enter \"y\" to confirm:"
  181. read confirmation
  182. if [[ $confirmation = "y" ]]; then
  183. for dev_path in "${dev_paths[@]}"; do
  184. if [ -e $dev_path ]; then
  185. echo "Removing $dev_path"
  186. rm -rf $dev_path
  187. fi
  188. done
  189. docker-compose stop
  190. docker-compose down --remove-orphans
  191. else
  192. echo "Operation canceled."
  193. fi
  194. }
  195. # Rebuild docker containers
  196. rebuild() {
  197. docker-compose stop
  198. docker-compose build "${@:2}"
  199. }
  200. # Run tests suite
  201. test() {
  202. docker-compose run --rm misago runtests.py $1
  203. docker-compose stop
  204. }
  205. # Make messages
  206. makemessages() {
  207. docker-compose run --rm --no-deps misago ./dev makemessages_in_docker $1
  208. }
  209. # Docker part of makemessages
  210. makemessages_in_docker() {
  211. require_in_docker
  212. echo "Extracting messages for $1 language:"
  213. cd ./misago
  214. echo "Processing .py and .html files..."
  215. django-admin.py makemessages -l $1 -e html,txt,py > /dev/null
  216. echo "Processing .js files..."
  217. django-admin.py makemessages -l $1 -d djangojs > /dev/null
  218. }
  219. # Compile messages
  220. compilemessages() {
  221. docker-compose run --rm --no-deps misago ./dev compilemessages_in_docker
  222. }
  223. # Docker part of compile messages
  224. compilemessages_in_docker() {
  225. require_in_docker
  226. cd ./misago
  227. django-admin.py compilemessages
  228. }
  229. # Pull translation files from transifex
  230. txpull() {
  231. tx pull
  232. compilemessages
  233. }
  234. # Push translation sources to transifex
  235. txpush() {
  236. tx push --source
  237. }
  238. # Shortcut for starting bash session in running container
  239. docker_run_bash() {
  240. docker exec -it misago_misago_1 bash
  241. }
  242. # Shortcut for docker-compose run...
  243. docker_run() {
  244. docker-compose run --rm misago "${@:2}"
  245. }
  246. # Shortcut for psql
  247. docker_run_psql() {
  248. docker-compose run --rm misago ./dev psql_in_docker
  249. }
  250. psql_in_docker() {
  251. wait_for_db
  252. PGPASSWORD=$POSTGRES_PASSWORD psql --username $POSTGRES_USER --host $POSTGRES_HOST $POSTGRES_DB
  253. }
  254. # Command dispatcher
  255. if [[ $1 ]]; then
  256. if [[ $1 = "init" ]]; then
  257. init $@
  258. elif [[ $1 = "init_in_docker" ]]; then
  259. init_in_docker
  260. elif [[ $1 = "clear" ]]; then
  261. clear
  262. elif [[ $1 = "reset" ]]; then
  263. clear
  264. init $@
  265. elif [[ $1 = "rebuild" ]]; then
  266. rebuild $@
  267. elif [[ $1 = "test" ]]; then
  268. test $2
  269. elif [[ $1 = "makemessages" ]]; then
  270. makemessages ${2:-en}
  271. elif [[ $1 = "makemessages_in_docker" ]]; then
  272. makemessages_in_docker $2
  273. elif [[ $1 = "compilemessages" ]]; then
  274. compilemessages
  275. elif [[ $1 = "compilemessages_in_docker" ]]; then
  276. compilemessages_in_docker
  277. elif [[ $1 = "txpull" ]]; then
  278. txpull
  279. elif [[ $1 = "txpush" ]]; then
  280. txpush
  281. elif [[ $1 = "txsync" ]]; then
  282. makemessages
  283. txpush
  284. txpull
  285. compilemessages
  286. elif [[ $1 = "bash" ]]; then
  287. docker_run_bash
  288. elif [[ $1 = "run" ]]; then
  289. docker_run $@
  290. elif [[ $1 = "psql" ]]; then
  291. docker_run_psql
  292. elif [[ $1 = "psql_in_docker" ]]; then
  293. psql_in_docker
  294. else
  295. invalid_argument $1
  296. fi
  297. else
  298. intro
  299. fi