Sysadmin tool for managing Redmine installations, migrations, and updates.
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

314 lignes
12 KiB

  1. #!/usr/bin/env bash
  2. ###############################################################################
  3. # #
  4. # Redmine stable automatic installation script #
  5. # #
  6. # Designed for stressed VUAs with little to no wish to copypaste a gadzillion #
  7. # UNIX commands. #
  8. # #
  9. # Copyright (c) 2020 - Bryan Pedini #
  10. # #
  11. ###############################################################################
  12. _parse_params() {
  13. VERBOSE=true
  14. MIGRATE=false
  15. NO_CONFIGURE_MARIADB=false
  16. NO_CREATE_USER=false
  17. REDMINE_VERSION="4.1.0"
  18. REDMINE_INSTALL_DIR="/opt/redmine"
  19. for par in "$@"; do
  20. case "$par" in
  21. "-h" | "--help" | "--usage")
  22. _print_usage
  23. ;;
  24. "-q" | "--quiet")
  25. VERBOSE=false
  26. shift
  27. ;;
  28. "--redmine-version")
  29. [[ "$2:0:1" = "-" ]] && _print_usage
  30. REDMINE_VERSION="$2"
  31. shift
  32. shift
  33. ;;
  34. "--install-dir")
  35. [[ "$2:0:1" = "-" ]] && _print_usage
  36. REDMINE_INSTALL_DIR="$2"
  37. shift
  38. shift
  39. ;;
  40. "--no-configure-mariadb")
  41. NO_CONFIGURE_MARIADB=true
  42. shift
  43. ;;
  44. "--no-create-user")
  45. NO_CREATE_USER=true
  46. shift
  47. ;;
  48. "--migrate")
  49. MIGRATE=true
  50. shift
  51. ;;
  52. "--migration-source-sql")
  53. [[ "$2:0:1" = "-" ]] && _print_usage
  54. MIGRATION_SQL="$2"
  55. shift
  56. shift
  57. ;;
  58. "--migration-source-dir")
  59. [[ "$2:0:1" = "-" ]] && _print_usage
  60. MIGRATION_DIR="$2"
  61. shift
  62. shift
  63. ;;
  64. esac
  65. done
  66. if [[ ( "$MIGRATE" = true ) && ( "$MIGRATION_SQL" = "" ) ]]; then
  67. _print_usage "flag --migration passed but either \
  68. --migration-source-sql or
  69. --migration-source-dir not provided." 1
  70. fi
  71. }
  72. _print_usage() {
  73. echo "Usage: $0 [OPTIONS]"
  74. echo
  75. echo "OPTIONS"
  76. echo " -h, --help, --usage Prints this help message and exits"
  77. echo " -q, --quiet Turns off verbose logging (default: True)"
  78. echo " --redmine-version Install a custom version of the Redmine
  79. project (default: latest)"
  80. echo " --install-dir Specifies a custom path for installation
  81. (default: /opt/redmine)"
  82. echo " --no-configure-mariadb Does not launch the default MariaDB server
  83. configuration scriptlet (default: False)"
  84. echo " --no-create-user Does not create a \`redmine\` user
  85. (default: False)"
  86. echo " --migrate Launches a different procedure for
  87. installation, which also import previous
  88. existing data (default: False)"
  89. echo " --migration-source-sql Specifies the path of the SQL database
  90. export (default: \$PWD/redmine.sql)"
  91. echo " --migration-source-dir Specifies the path of the previous redmine
  92. installation. It can either be a different
  93. path, or the same as the current installation
  94. (default: /opt/redmine)"
  95. echo
  96. echo "Encountered error:"
  97. if [ "$1" ]; then
  98. echo " $1"
  99. if [ "$2" ]; then
  100. exit $2
  101. fi
  102. fi
  103. exit 0
  104. }
  105. # Update current system
  106. _update_system() {
  107. [[ "$VERBOSE" = true ]] && echo "Updating current system"
  108. ERR=$( { apt update 1>/dev/null; } 2>&1 | grep -v "stable CLI interface" )
  109. [[ "$ERR" ]] && echo "Error during package cache update: $ERR"
  110. ERR=$( { apt -y upgrade 1>/dev/null; } 2>&1 | grep -v \
  111. "stable CLI interface" )
  112. [[ "$ERR" ]] && echo "Error during system package updates: $ERR"
  113. }
  114. # Install required packages to run Redmine and Ruby on Rails underneath
  115. _install_rails_environment() {
  116. [[ "$VERBOSE" = true ]] && echo "Installing required packages to run \
  117. Redmine and Ruby on Rails underneath"
  118. ERR=$( { apt -y install build-essential ruby-dev libxslt1-dev \
  119. libmariadb-dev libxml2-dev zlib1g-dev imagemagick \
  120. libmagickwand-dev curl apache2 libapache2-mod-passenger \
  121. mariadb-client mariadb-server 1>/dev/null; } 2>&1 | grep -v \
  122. "stable CLI interface" )
  123. [[ "$ERR" ]] && echo "Error during package installations: $ERR"
  124. }
  125. # Add an unprivileged user to run the Redmine app afterwards
  126. _add_redmine_user() {
  127. [[ "$VERBOSE" = true ]] && echo "Adding an unprivileged user to run the \
  128. Redmine app afterwards"
  129. useradd -r -m -d "$REDMINE_INSTALL_DIR" -s /usr/bin/bash redmine &>/dev/null && \
  130. usermod -aG redmine www-data &>/dev/null
  131. }
  132. # Ask the user for mysql `root` password and configure mysql in unattended mode
  133. _configure_mariadb_server() {
  134. read -sp 'Please type a `root` password for mysql database: ' \
  135. MYSQL_ROOT_PASSWORD && echo ""
  136. [[ "$VERBOSE" = true ]] && echo "Configuring mysql in unattended mode"
  137. ERR=$( { apt -y install expect >/dev/null; } 2>&1 | grep -v \
  138. "stable CLI interface" )
  139. [[ "$ERR" ]] && echo "Error during package installations: $ERR"
  140. SECURE_MYSQL=$(expect -c "
  141. set timeout 10
  142. spawn mysql_secure_installation
  143. expect \"Enter current password for root (enter for none):\"
  144. send \"\r\"
  145. expect \"Set root password?\"
  146. send \"y\r\"
  147. expect \"New password:\"
  148. send \"$MYSQL_ROOT_PASSWORD\r\"
  149. expect \"Re-enter new password:\"
  150. send \"$MYSQL_ROOT_PASSWORD\r\"
  151. expect \"Remove anonymous users?\"
  152. send \"y\r\"
  153. expect \"Disallow root login remotely?\"
  154. send \"y\r\"
  155. expect \"Remove test database and access to it?\"
  156. send \"y\r\"
  157. expect \"Reload privilege tables now?\"
  158. send \"y\r\"
  159. expect eof
  160. ")
  161. ERR=$( { echo "$SECURE_MYSQL" 1>/dev/null; } 2>&1 )
  162. [[ "$ERR" ]] && echo "Error during mysql_initialization: $ERR"
  163. ERR=$( { apt -y remove --purge expect >/dev/null; } 2>&1 | \
  164. grep -v "stable CLI interface" )
  165. [[ "$ERR" ]] && echo "Error during package removals: $ERR"
  166. unset SECURE_MYSQL
  167. }
  168. # Create Redmine database with associated login
  169. _configure_redmine_database() {
  170. [[ "$VERBOSE" = true ]] && echo "Creating Redmine database with \
  171. associated login"
  172. MYSQL_ROOT_USER="root"
  173. QUERY="command=password&format=plain&scheme=rrnnnrrnrnnnrrnrnnrr"
  174. REDMINE_ADMIN_PASSWORD=$(curl -s \
  175. "https://www.passwordrandom.com/query?$QUERY")
  176. SQL="CREATE DATABASE redmine;"
  177. mysql -u$MYSQL_ROOT_USER -p$MYSQL_ROOT_PASSWORD -e "$SQL"
  178. SQL="GRANT ALL PRIVILEGES ON redmine.* TO redmine_admin@localhost
  179. IDENTIFIED BY '$REDMINE_ADMIN_PASSWORD';"
  180. mysql -u$MYSQL_ROOT_USER -p$MYSQL_ROOT_PASSWORD -e "$SQL"
  181. SQL="FLUSH PRIVILEGES;"
  182. mysql -u$MYSQL_ROOT_USER -p$MYSQL_ROOT_PASSWORD -e "$SQL"
  183. unset SQL; unset MYSQL_ROOT_USER; unset MYSQL_ROOT_PASSWORD; unset QUERY
  184. }
  185. # Download Redmine on temporary directory, extract it on /opt and reconfigure
  186. # default config parameters with real values
  187. _download_redmine() {
  188. [[ "$VERBOSE" = true ]] && echo "Downloading Redmine on temporary \
  189. directory"
  190. ERR=$( { wget -q \
  191. http://www.redmine.org/releases/redmine-${REDMINE_VERSION}.tar.gz -P \
  192. /tmp/ && wget -q \
  193. http://www.redmine.org/releases/redmine-${REDMINE_VERSION}.tar.gz.md5 \
  194. -P /tmp/ && cd /tmp/ && md5sum -c \
  195. redmine-${REDMINE_VERSION}.tar.gz.md5 1>/dev/null; } 2>&1 )
  196. [[ "$ERR" ]] && echo "Error downloading or verifying signature of \
  197. Redmine: $ERR"
  198. rm /tmp/redmine-${REDMINE_VERSION}.tar.gz.md5
  199. [[ "$VERBOSE" = true ]] && echo "Extracting Redmine on \
  200. $REDMINE_INSTALL_DIR"
  201. mkdir "$REDMINE_INSTALL_DIR" && chown redmine:redmine \
  202. "$REDMINE_INSTALL_DIR"
  203. sudo -u redmine tar xzf /tmp/redmine-${REDMINE_VERSION}.tar.gz -C \
  204. "$REDMINE_INSTALL_DIR" --strip-components=1
  205. [[ "$VERBOSE" = true ]] && echo "Configuring Redmine"
  206. sudo -u redmine cp \
  207. "$REDMINE_INSTALL_DIR"/config/configuration.yml{.example,} && sudo \
  208. -u redmine cp "$REDMINE_INSTALL_DIR"/config/database.yml{.example,} \
  209. && sudo -u redmine cp \
  210. "$REDMINE_INSTALL_DIR"/public/dispatch.fcgi{.example,}
  211. sed -i "0,/root/s//redmine_admin/" \
  212. "$REDMINE_INSTALL_DIR"/config/database.yml
  213. sed -i "0,/\"\"/s//\"$REDMINE_ADMIN_PASSWORD\"/" \
  214. "$REDMINE_INSTALL_DIR"/config/database.yml
  215. unset REDMINE_ADMIN_PASSWORD
  216. unset REDMINE_VERSION
  217. }
  218. _configure_redmine_permissions() {
  219. # Create required Redmine folders and set correct permissions
  220. [[ "$VERBOSE" = true ]] && echo "Creating required Redmine folders and \
  221. set correct permissions"
  222. sudo -u redmine mkdir -p tmp/pdf
  223. sudo -u redmine mkdir -p public/plugin_assets
  224. }
  225. _configure_redmine_dependencies() {
  226. # Install required Ruby files
  227. [[ "$VERBOSE" = true ]] && echo "Installing required Ruby files"
  228. cd "$REDMINE_INSTALL_DIR"
  229. ERR=$( { gem install bundler 1>/dev/null; } 2>&1 )
  230. [[ "$ERR" ]] && echo "Error while installing bundler Gem: $ERR"
  231. sudo -u redmine bundle config set path "vendor/bundle"
  232. sudo -u redmine bundle config set without "development test"
  233. ERR=$( { sudo -u redmine bundle install 1>/dev/null; } 2>&1 )
  234. [[ "$ERR" ]] && echo "Error while installing Redmine bundled Gems: $ERR"
  235. sudo -u redmine bundle exec rake generate_secret_token 1>/dev/null
  236. sudo -u redmine RAILS_ENV=production bundle exec rake db:migrate \
  237. 1>/dev/null
  238. # Only if not migrating, load default redmine data
  239. [[ ! "$MIGRATE" ]] && sudo -u redmine RAILS_ENV=production \
  240. REDMINE_LANG=en bundle exec rake redmine:load_default_data 1>/dev/null
  241. }
  242. # Configure Apache2 to run Redmine
  243. _configure_apache2() {
  244. # Ask the user for Apache2 Redmine hostname
  245. read -p 'Please type the FQDN Redmine should run on: ' \
  246. REDMINE_HOSTNAME && echo ""
  247. [[ "$VERBOSE" = true ]] && echo "Configuring Apache2 to run Redmine"
  248. cat << "EOF" > /etc/apache2/sites-available/redmine.conf
  249. <VirtualHost *:80>
  250. ServerName $REDMINE_HOSTNAME
  251. RailsEnv production
  252. DocumentRoot "$REDMINE_INSTALL_DIR/public"
  253. <Directory "$REDMINE_INSTALL_DIR/public">
  254. Allow from all
  255. Require all granted
  256. </Directory>
  257. ErrorLog \${APACHE_LOG_DIR}/redmine_error.log
  258. CustomLog \${APACHE_LOG_DIR}/redmine_access.log combined
  259. </VirtualHost>
  260. EOF
  261. unset REDMINE_HOSTNAME; unset REDMINE_INSTALL_DIR
  262. }
  263. # Enable passenger module if not already done
  264. _enable_redmine() {
  265. [[ "$VERBOSE" = true ]] && echo "Enabling passenger module if not already \
  266. done"
  267. [[ ! "$( apache2ctl -M | grep -i passenger )" ]] && a2enmod passenger
  268. # Enable redmine website over Apache2
  269. [[ "$VERBOSE" = true ]] && echo "Enabling redmine website over Apache2"
  270. a2ensite redmine 1>/dev/null
  271. systemctl reload apache2
  272. }
  273. # Main program function, calls all other functions in the correct order
  274. _main() {
  275. _update_system
  276. _install_rails_environment
  277. [[ "$NO_CREATE_USER" = false ]] && _add_redmine_user
  278. [[ "$NO_CONFIGURE_MARIADB" = false ]] && _configure_mariadb_server
  279. _configure_redmine_database
  280. _download_redmine
  281. _configure_redmine_permissions
  282. _configure_redmine_dependencies
  283. _configure_apache2
  284. _enable_redmine
  285. }
  286. # Program execution
  287. _parse_params $@
  288. _main