| @@ -0,0 +1,313 @@ | |||
| #!/usr/bin/env bash | |||
| ############################################################################### | |||
| # # | |||
| # Redmine stable automatic installation script # | |||
| # # | |||
| # Designed for stressed VUAs with little to no wish to copypaste a gadzillion # | |||
| # UNIX commands. # | |||
| # # | |||
| # Copyright (c) 2020 - Bryan Pedini # | |||
| # # | |||
| ############################################################################### | |||
| _parse_params() { | |||
| VERBOSE=true | |||
| MIGRATE=false | |||
| NO_CONFIGURE_MARIADB=false | |||
| NO_CREATE_USER=false | |||
| REDMINE_VERSION="4.1.0" | |||
| REDMINE_INSTALL_DIR="/opt/redmine" | |||
| for par in "$@"; do | |||
| case "$par" in | |||
| "-h" | "--help" | "--usage") | |||
| _print_usage | |||
| ;; | |||
| "-q" | "--quiet") | |||
| VERBOSE=false | |||
| shift | |||
| ;; | |||
| "--redmine-version") | |||
| [[ "$2:0:1" = "-" ]] && _print_usage | |||
| REDMINE_VERSION="$2" | |||
| shift | |||
| shift | |||
| ;; | |||
| "--install-dir") | |||
| [[ "$2:0:1" = "-" ]] && _print_usage | |||
| REDMINE_INSTALL_DIR="$2" | |||
| shift | |||
| shift | |||
| ;; | |||
| "--no-configure-mariadb") | |||
| NO_CONFIGURE_MARIADB=true | |||
| shift | |||
| ;; | |||
| "--no-create-user") | |||
| NO_CREATE_USER=true | |||
| shift | |||
| ;; | |||
| "--migrate") | |||
| MIGRATE=true | |||
| shift | |||
| ;; | |||
| "--migration-source-sql") | |||
| [[ "$2:0:1" = "-" ]] && _print_usage | |||
| MIGRATION_SQL="$2" | |||
| shift | |||
| shift | |||
| ;; | |||
| "--migration-source-dir") | |||
| [[ "$2:0:1" = "-" ]] && _print_usage | |||
| MIGRATION_DIR="$2" | |||
| shift | |||
| shift | |||
| ;; | |||
| esac | |||
| done | |||
| if [[ ( "$MIGRATE" = true ) && ( "$MIGRATION_SQL" = "" ) ]]; then | |||
| _print_usage "flag --migration passed but either \ | |||
| --migration-source-sql or | |||
| --migration-source-dir not provided." 1 | |||
| fi | |||
| } | |||
| _print_usage() { | |||
| echo "Usage: $0 [OPTIONS]" | |||
| echo | |||
| echo "OPTIONS" | |||
| echo " -h, --help, --usage Prints this help message and exits" | |||
| echo " -q, --quiet Turns off verbose logging (default: True)" | |||
| echo " --redmine-version Install a custom version of the Redmine | |||
| project (default: latest)" | |||
| echo " --install-dir Specifies a custom path for installation | |||
| (default: /opt/redmine)" | |||
| echo " --no-configure-mariadb Does not launch the default MariaDB server | |||
| configuration scriptlet (default: False)" | |||
| echo " --no-create-user Does not create a \`redmine\` user | |||
| (default: False)" | |||
| echo " --migrate Launches a different procedure for | |||
| installation, which also import previous | |||
| existing data (default: False)" | |||
| echo " --migration-source-sql Specifies the path of the SQL database | |||
| export (default: \$PWD/redmine.sql)" | |||
| echo " --migration-source-dir Specifies the path of the previous redmine | |||
| installation. It can either be a different | |||
| path, or the same as the current installation | |||
| (default: /opt/redmine)" | |||
| echo | |||
| echo "Encountered error:" | |||
| if [ "$1" ]; then | |||
| echo " $1" | |||
| if [ "$2" ]; then | |||
| exit $2 | |||
| fi | |||
| fi | |||
| exit 0 | |||
| } | |||
| # Update current system | |||
| _update_system() { | |||
| [[ "$VERBOSE" = true ]] && echo "Updating current system" | |||
| ERR=$( { apt update 1>/dev/null; } 2>&1 | grep -v "stable CLI interface" ) | |||
| [[ "$ERR" ]] && echo "Error during package cache update: $ERR" | |||
| ERR=$( { apt -y upgrade 1>/dev/null; } 2>&1 | grep -v \ | |||
| "stable CLI interface" ) | |||
| [[ "$ERR" ]] && echo "Error during system package updates: $ERR" | |||
| } | |||
| # Install required packages to run Redmine and Ruby on Rails underneath | |||
| _install_rails_environment() { | |||
| [[ "$VERBOSE" = true ]] && echo "Installing required packages to run \ | |||
| Redmine and Ruby on Rails underneath" | |||
| ERR=$( { apt -y install build-essential ruby-dev libxslt1-dev \ | |||
| libmariadb-dev libxml2-dev zlib1g-dev imagemagick \ | |||
| libmagickwand-dev curl apache2 libapache2-mod-passenger \ | |||
| mariadb-client mariadb-server 1>/dev/null; } 2>&1 | grep -v \ | |||
| "stable CLI interface" ) | |||
| [[ "$ERR" ]] && echo "Error during package installations: $ERR" | |||
| } | |||
| # Add an unprivileged user to run the Redmine app afterwards | |||
| _add_redmine_user() { | |||
| [[ "$VERBOSE" = true ]] && echo "Adding an unprivileged user to run the \ | |||
| Redmine app afterwards" | |||
| useradd -r -m -d "$REDMINE_INSTALL_DIR" -s /usr/bin/bash redmine &>/dev/null && \ | |||
| usermod -aG redmine www-data &>/dev/null | |||
| } | |||
| # Ask the user for mysql `root` password and configure mysql in unattended mode | |||
| _configure_mariadb_server() { | |||
| read -sp 'Please type a `root` password for mysql database: ' \ | |||
| MYSQL_ROOT_PASSWORD && echo "" | |||
| [[ "$VERBOSE" = true ]] && echo "Configuring mysql in unattended mode" | |||
| ERR=$( { apt -y install expect >/dev/null; } 2>&1 | grep -v \ | |||
| "stable CLI interface" ) | |||
| [[ "$ERR" ]] && echo "Error during package installations: $ERR" | |||
| SECURE_MYSQL=$(expect -c " | |||
| set timeout 10 | |||
| spawn mysql_secure_installation | |||
| expect \"Enter current password for root (enter for none):\" | |||
| send \"\r\" | |||
| expect \"Set root password?\" | |||
| send \"y\r\" | |||
| expect \"New password:\" | |||
| send \"$MYSQL_ROOT_PASSWORD\r\" | |||
| expect \"Re-enter new password:\" | |||
| send \"$MYSQL_ROOT_PASSWORD\r\" | |||
| expect \"Remove anonymous users?\" | |||
| send \"y\r\" | |||
| expect \"Disallow root login remotely?\" | |||
| send \"y\r\" | |||
| expect \"Remove test database and access to it?\" | |||
| send \"y\r\" | |||
| expect \"Reload privilege tables now?\" | |||
| send \"y\r\" | |||
| expect eof | |||
| ") | |||
| ERR=$( { echo "$SECURE_MYSQL" 1>/dev/null; } 2>&1 ) | |||
| [[ "$ERR" ]] && echo "Error during mysql_initialization: $ERR" | |||
| ERR=$( { apt -y remove --purge expect >/dev/null; } 2>&1 | \ | |||
| grep -v "stable CLI interface" ) | |||
| [[ "$ERR" ]] && echo "Error during package removals: $ERR" | |||
| unset SECURE_MYSQL | |||
| } | |||
| # Create Redmine database with associated login | |||
| _configure_redmine_database() { | |||
| [[ "$VERBOSE" = true ]] && echo "Creating Redmine database with \ | |||
| associated login" | |||
| MYSQL_ROOT_USER="root" | |||
| QUERY="command=password&format=plain&scheme=rrnnnrrnrnnnrrnrnnrr" | |||
| REDMINE_ADMIN_PASSWORD=$(curl -s \ | |||
| "https://www.passwordrandom.com/query?$QUERY") | |||
| SQL="CREATE DATABASE redmine;" | |||
| mysql -u$MYSQL_ROOT_USER -p$MYSQL_ROOT_PASSWORD -e "$SQL" | |||
| SQL="GRANT ALL PRIVILEGES ON redmine.* TO redmine_admin@localhost | |||
| IDENTIFIED BY '$REDMINE_ADMIN_PASSWORD';" | |||
| mysql -u$MYSQL_ROOT_USER -p$MYSQL_ROOT_PASSWORD -e "$SQL" | |||
| SQL="FLUSH PRIVILEGES;" | |||
| mysql -u$MYSQL_ROOT_USER -p$MYSQL_ROOT_PASSWORD -e "$SQL" | |||
| unset SQL; unset MYSQL_ROOT_USER; unset MYSQL_ROOT_PASSWORD; unset QUERY | |||
| } | |||
| # Download Redmine on temporary directory, extract it on /opt and reconfigure | |||
| # default config parameters with real values | |||
| _download_redmine() { | |||
| [[ "$VERBOSE" = true ]] && echo "Downloading Redmine on temporary \ | |||
| directory" | |||
| ERR=$( { wget -q \ | |||
| http://www.redmine.org/releases/redmine-${REDMINE_VERSION}.tar.gz -P \ | |||
| /tmp/ && wget -q \ | |||
| http://www.redmine.org/releases/redmine-${REDMINE_VERSION}.tar.gz.md5 \ | |||
| -P /tmp/ && cd /tmp/ && md5sum -c \ | |||
| redmine-${REDMINE_VERSION}.tar.gz.md5 1>/dev/null; } 2>&1 ) | |||
| [[ "$ERR" ]] && echo "Error downloading or verifying signature of \ | |||
| Redmine: $ERR" | |||
| rm /tmp/redmine-${REDMINE_VERSION}.tar.gz.md5 | |||
| [[ "$VERBOSE" = true ]] && echo "Extracting Redmine on \ | |||
| $REDMINE_INSTALL_DIR" | |||
| mkdir "$REDMINE_INSTALL_DIR" && chown redmine:redmine \ | |||
| "$REDMINE_INSTALL_DIR" | |||
| sudo -u redmine tar xzf /tmp/redmine-${REDMINE_VERSION}.tar.gz -C \ | |||
| "$REDMINE_INSTALL_DIR" --strip-components=1 | |||
| [[ "$VERBOSE" = true ]] && echo "Configuring Redmine" | |||
| sudo -u redmine cp \ | |||
| "$REDMINE_INSTALL_DIR"/config/configuration.yml{.example,} && sudo \ | |||
| -u redmine cp "$REDMINE_INSTALL_DIR"/config/database.yml{.example,} \ | |||
| && sudo -u redmine cp \ | |||
| "$REDMINE_INSTALL_DIR"/public/dispatch.fcgi{.example,} | |||
| sed -i "0,/root/s//redmine_admin/" \ | |||
| "$REDMINE_INSTALL_DIR"/config/database.yml | |||
| sed -i "0,/\"\"/s//\"$REDMINE_ADMIN_PASSWORD\"/" \ | |||
| "$REDMINE_INSTALL_DIR"/config/database.yml | |||
| unset REDMINE_ADMIN_PASSWORD | |||
| unset REDMINE_VERSION | |||
| } | |||
| _configure_redmine_permissions() { | |||
| # Create required Redmine folders and set correct permissions | |||
| [[ "$VERBOSE" = true ]] && echo "Creating required Redmine folders and \ | |||
| set correct permissions" | |||
| sudo -u redmine mkdir -p tmp/pdf | |||
| sudo -u redmine mkdir -p public/plugin_assets | |||
| } | |||
| _configure_redmine_dependencies() { | |||
| # Install required Ruby files | |||
| [[ "$VERBOSE" = true ]] && echo "Installing required Ruby files" | |||
| cd "$REDMINE_INSTALL_DIR" | |||
| ERR=$( { gem install bundler 1>/dev/null; } 2>&1 ) | |||
| [[ "$ERR" ]] && echo "Error while installing bundler Gem: $ERR" | |||
| sudo -u redmine bundle config set path "vendor/bundle" | |||
| sudo -u redmine bundle config set without "development test" | |||
| ERR=$( { sudo -u redmine bundle install 1>/dev/null; } 2>&1 ) | |||
| [[ "$ERR" ]] && echo "Error while installing Redmine bundled Gems: $ERR" | |||
| sudo -u redmine bundle exec rake generate_secret_token 1>/dev/null | |||
| sudo -u redmine RAILS_ENV=production bundle exec rake db:migrate \ | |||
| 1>/dev/null | |||
| # Only if not migrating, load default redmine data | |||
| [[ ! "$MIGRATE" ]] && sudo -u redmine RAILS_ENV=production \ | |||
| REDMINE_LANG=en bundle exec rake redmine:load_default_data 1>/dev/null | |||
| } | |||
| # Configure Apache2 to run Redmine | |||
| _configure_apache2() { | |||
| # Ask the user for Apache2 Redmine hostname | |||
| read -p 'Please type the FQDN Redmine should run on: ' \ | |||
| REDMINE_HOSTNAME && echo "" | |||
| [[ "$VERBOSE" = true ]] && echo "Configuring Apache2 to run Redmine" | |||
| cat << "EOF" > /etc/apache2/sites-available/redmine.conf | |||
| <VirtualHost *:80> | |||
| ServerName $REDMINE_HOSTNAME | |||
| RailsEnv production | |||
| DocumentRoot "$REDMINE_INSTALL_DIR/public" | |||
| <Directory "$REDMINE_INSTALL_DIR/public"> | |||
| Allow from all | |||
| Require all granted | |||
| </Directory> | |||
| ErrorLog \${APACHE_LOG_DIR}/redmine_error.log | |||
| CustomLog \${APACHE_LOG_DIR}/redmine_access.log combined | |||
| </VirtualHost> | |||
| EOF | |||
| unset REDMINE_HOSTNAME; unset REDMINE_INSTALL_DIR | |||
| } | |||
| # Enable passenger module if not already done | |||
| _enable_redmine() { | |||
| [[ "$VERBOSE" = true ]] && echo "Enabling passenger module if not already \ | |||
| done" | |||
| [[ ! "$( apache2ctl -M | grep -i passenger )" ]] && a2enmod passenger | |||
| # Enable redmine website over Apache2 | |||
| [[ "$VERBOSE" = true ]] && echo "Enabling redmine website over Apache2" | |||
| a2ensite redmine 1>/dev/null | |||
| systemctl reload apache2 | |||
| } | |||
| # Main program function, calls all other functions in the correct order | |||
| _main() { | |||
| _update_system | |||
| _install_rails_environment | |||
| [[ "$NO_CREATE_USER" = false ]] && _add_redmine_user | |||
| [[ "$NO_CONFIGURE_MARIADB" = false ]] && _configure_mariadb_server | |||
| _configure_redmine_database | |||
| _download_redmine | |||
| _configure_redmine_permissions | |||
| _configure_redmine_dependencies | |||
| _configure_apache2 | |||
| _enable_redmine | |||
| } | |||
| # Program execution | |||
| _parse_params $@ | |||
| _main | |||