dev 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369
  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_MISAGO_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}afterinit${NORMAL} display help message displayed after init command is complete."
  67. echo " ${BOLD}clear${NORMAL} if dev project exists, delete it's files and destroy docker containers."
  68. echo " ${BOLD}rebuild${NORMAL} rebuild docker containers."
  69. echo " ${BOLD}reset${NORMAL} run clear followed by init."
  70. echo
  71. 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."
  72. echo
  73. echo "Testing:"
  74. echo
  75. echo " ${BOLD}test${NORMAL} run tests suite."
  76. echo " ${BOLD}test module${NORMAL} run tests suite in specified python module, eg. misago.users."
  77. echo
  78. echo "Translations:"
  79. echo
  80. echo " ${BOLD}makemessages${NORMAL} update translation files for \"en\" language."
  81. echo " ${BOLD}makemessages lang${NORMAL} update translation files for \"lang\" language."
  82. echo " ${BOLD}compilemessages${NORMAL} compile translation files to \"mo\" format."
  83. echo " ${BOLD}txpull${NORMAL} pull translations from Transifex."
  84. echo " ${BOLD}txpush${NORMAL} push new source files to Transifex."
  85. echo " ${BOLD}txsync${NORMAL} runs entire process of syncing translations."
  86. echo
  87. echo "Shortcuts:"
  88. echo
  89. echo " ${BOLD}bash${NORMAL} shortcut for entering bash session inside running Misago container."
  90. echo " ${BOLD}run${NORMAL} shortcut for \"docker-compose run --rm misago\"."
  91. echo " ${BOLD}psql${NORMAL} shortcut for dev database's psql."
  92. echo
  93. }
  94. # Handle invalid option message
  95. invalid_argument() {
  96. echo -e "Invalid argument: ${RED}$1${NORMAL}"
  97. echo "Please run this script without any arguments to see the list of available arguments."
  98. exit 1
  99. }
  100. # Initialize new dev project
  101. init() {
  102. if [[ $2 = "message" ]]; then
  103. after_init_message
  104. exit 0
  105. fi
  106. for dev_path in "${dev_paths[@]}"; do
  107. if [ -e $dev_path ]; then
  108. error "Dev project already exists, or was not deleted completely."
  109. echo
  110. echo "Please use \"clear\" option to clear any possible remaining files and try again."
  111. exit 1
  112. fi
  113. done
  114. for port in "${required_ports[@]}"; do
  115. nc "127.0.0.1" "$port" < /dev/null
  116. if [[ $? = "0" ]]; then
  117. if [[ $port = $port_django ]]; then
  118. error "Other application appears to already be running on http://127.0.0.1:8000"
  119. elif [[ $port = $port_postgresql ]]; then
  120. error "Other PostgreSQL instance appears to already be running on port $port."
  121. fi
  122. echo
  123. echo "Please stop other process and try again."
  124. exit 1
  125. fi
  126. done
  127. docker-compose stop
  128. docker-compose build --pull --force-rm "${@:2}"
  129. docker-compose run --rm misago ./dev init_in_docker
  130. }
  131. # Initialization step that has to occur inside docker
  132. init_in_docker() {
  133. require_in_docker
  134. wait_for_db
  135. # initialize django project
  136. python misago/bin/misago-start-devproject.py
  137. # move items of interest up one level
  138. mv devproject/devproject devproject_tmp
  139. mv devproject/avatargallery ./avatargallery
  140. mv devproject/media ./media
  141. mv devproject/userdata ./userdata
  142. mv devproject/manage.py ./manage.py
  143. rm -rf devproject
  144. mv devproject_tmp devproject
  145. # migrate the DB
  146. python manage.py migrate
  147. # create superuser Admin with password "password"
  148. python manage.py createsuperuser --username $username --email admin@example.com --password $password
  149. # display after init message
  150. echo
  151. echo "================================================================================"
  152. after_init_message
  153. }
  154. # After-init message
  155. after_init_message() {
  156. echo
  157. echo "You can now start the development server using:"
  158. echo
  159. echo " docker-compose up"
  160. echo
  161. echo "Running server will be available in the browser under the http://127.0.0.1:8000 address."
  162. echo
  163. echo "Default superuser has been created with this username and password:"
  164. echo
  165. echo "Username: $username"
  166. echo "Password: $password"
  167. echo
  168. echo "Development project directories:"
  169. echo
  170. echo "devproject contains configuration files for development instance."
  171. echo "media user uploaded files."
  172. echo "userdata working directory for user data exports."
  173. echo "avatargallery example avatar gallery."
  174. echo
  175. echo "To connect to development database use following credentials:"
  176. echo
  177. echo "User: misago"
  178. echo "Password: misago"
  179. echo "Database: misago"
  180. echo "Host: postgres"
  181. echo "Port: 5432"
  182. echo
  183. echo "Note: development server must be running for connection to be possible."
  184. echo
  185. }
  186. # Clear existing dev project
  187. clear() {
  188. echo -e "${RED}Warning:${NORMAL} You are going clear current development project."
  189. echo
  190. will_delete_files=false
  191. for dev_path in "${dev_paths[@]}"; do
  192. if [ -e $dev_path ]; then
  193. will_delete_files=true
  194. fi
  195. done
  196. if [[ $will_delete_files = true ]]; then
  197. echo "Following files and directories will be deleted:"
  198. for dev_path in "${dev_paths[@]}"; do
  199. if [ -e $dev_path ]; then
  200. echo " $dev_path"
  201. fi
  202. done
  203. echo
  204. fi
  205. echo "This will also stop and remove docker containers used by this project."
  206. echo
  207. echo "Enter \"y\" to confirm:"
  208. read confirmation
  209. if [[ $confirmation = "y" ]]; then
  210. for dev_path in "${dev_paths[@]}"; do
  211. if [ -e $dev_path ]; then
  212. echo "Removing $dev_path"
  213. rm -rf $dev_path
  214. fi
  215. done
  216. docker-compose stop
  217. docker-compose down --remove-orphans
  218. else
  219. echo "Operation canceled."
  220. fi
  221. }
  222. # Rebuild docker containers
  223. rebuild() {
  224. docker-compose stop
  225. docker-compose build --pull --force-rm "${@:2}"
  226. }
  227. # Run tests suite
  228. test() {
  229. docker-compose run --rm misago runtests.py $1
  230. docker-compose stop
  231. }
  232. # Make messages
  233. makemessages() {
  234. docker-compose run --rm --no-deps misago ./dev makemessages_in_docker $1
  235. }
  236. # Docker part of makemessages
  237. makemessages_in_docker() {
  238. require_in_docker
  239. echo "Extracting messages for $1 language:"
  240. cd ./misago
  241. echo "Processing .py and .html files..."
  242. django-admin.py makemessages -l $1 -e html,txt,py > /dev/null
  243. echo "Processing .js files..."
  244. django-admin.py makemessages -l $1 -d djangojs > /dev/null
  245. }
  246. # Compile messages
  247. compilemessages() {
  248. docker-compose run --rm --no-deps misago ./dev compilemessages_in_docker
  249. }
  250. # Docker part of compile messages
  251. compilemessages_in_docker() {
  252. require_in_docker
  253. cd ./misago
  254. django-admin.py compilemessages
  255. }
  256. # Pull translation files from transifex
  257. txpull() {
  258. tx pull
  259. compilemessages
  260. }
  261. # Push translation sources to transifex
  262. txpush() {
  263. tx push --source
  264. }
  265. # Shortcut for starting bash session in running container
  266. docker_run_bash() {
  267. docker exec -it misago_misago_1 bash
  268. }
  269. # Shortcut for docker-compose run...
  270. docker_run() {
  271. docker-compose run --rm misago "${@:2}"
  272. }
  273. # Shortcut for psql
  274. docker_run_psql() {
  275. docker-compose run --rm misago ./dev psql_in_docker
  276. }
  277. psql_in_docker() {
  278. wait_for_db
  279. PGPASSWORD=$POSTGRES_PASSWORD psql --username $POSTGRES_USER --host $POSTGRES_HOST $POSTGRES_DB
  280. }
  281. # Command dispatcher
  282. if [[ $1 ]]; then
  283. if [[ $1 = "init" ]]; then
  284. init $@
  285. elif [[ $1 = "init_in_docker" ]]; then
  286. init_in_docker
  287. elif [[ $1 = "afterinit" ]]; then
  288. after_init_message
  289. elif [[ $1 = "clear" ]]; then
  290. clear
  291. elif [[ $1 = "reset" ]]; then
  292. clear
  293. init $@
  294. elif [[ $1 = "rebuild" ]]; then
  295. rebuild $@
  296. elif [[ $1 = "test" ]]; then
  297. test $2
  298. elif [[ $1 = "makemessages" ]]; then
  299. makemessages ${2:-en}
  300. elif [[ $1 = "makemessages_in_docker" ]]; then
  301. makemessages_in_docker $2
  302. elif [[ $1 = "compilemessages" ]]; then
  303. compilemessages
  304. elif [[ $1 = "compilemessages_in_docker" ]]; then
  305. compilemessages_in_docker
  306. elif [[ $1 = "txpull" ]]; then
  307. txpull
  308. elif [[ $1 = "txpush" ]]; then
  309. txpush
  310. elif [[ $1 = "txsync" ]]; then
  311. makemessages
  312. txpush
  313. txpull
  314. compilemessages
  315. elif [[ $1 = "bash" ]]; then
  316. docker_run_bash
  317. elif [[ $1 = "run" ]]; then
  318. docker_run $@
  319. elif [[ $1 = "psql" ]]; then
  320. docker_run_psql
  321. elif [[ $1 = "psql_in_docker" ]]; then
  322. psql_in_docker
  323. else
  324. invalid_argument $1
  325. fi
  326. else
  327. intro
  328. fi