#!/bin/ksh93 GL_VERSION='2.6.2' # Since we wanna use focal, which comes with ruby 2.7 we need at least d4a9c5d, # which is ~ 1 week younger than 2.5.6 and ~ 2 weeks older than 2.6.0. #GL_VERSION='master' typeset -r VERSION='1.0' FPROG=${.sh.file} PROG=${FPROG##*/} SDIR=${.sh.file%/*} export LC_CTYPE='C.UTF-8' typeset -A DEFAULTS=( [DATADIR]="/data/greenlight" # SQlite and env store [USR]='webservd' # run as user. systemd [GRP]='webservd' # run as group. systemd [INSTANCE]='bbb' # name of the instance to setup - keep short. ) LIC='[-?'"${VERSION}"' ] [-copyright?Copyright (c) 2020 Jens Elkner. Alle Rechte vorbehalten.] [-license?CDDL 1.0]' for H in log.kshlib man.kshlib ; do X=${SDIR}/$H [[ -r $X ]] && . $X && continue X=${ whence $H; } [[ -z $X ]] && print "$H nicht gefunden - Abbruch." && exit 1 . $X done unset H function showUsage { typeset WHAT="$1" X='--man' [[ -z ${WHAT} ]] && WHAT='MAIN' && X='-?' getopts -a "${PROG}" "${ print ${Man.FUNC[${WHAT}]}; }" OPT $X } function cleanup { [[ -z ${TMP} ]] && return 0 rm -rf ${TMP} } function makeRubyRc { typeset L X= V= gem env |while read L ; do [[ ${L:0:30} == '- USER INSTALLATION DIRECTORY:' ]] || continue X="${L:31}" break done V=${X##*/} if [[ ! -e ~/.ruby_rc ]]; then cat >~/.ruby_rc<~/.ruby_sh_rc Log.info '~/.ruby_sh_rc created.' elif (( VERB )); then Log.info '~/.ruby_sh_rc already exists. Leaving as is.' fi return 0 } function makeGreenLightEnv { typeset SECRET=${ openssl rand -hex 64 ; } CFG="${1:-/tmp/cfg}" \ NAME="${SOPT[INSTANCE]}" X VARS if [[ -s ${CFG} ]]; then (( VERB )) && Log.info "'${CFG}' already exists. Leaving as is." return 0 fi if [[ -z ${ZDOMAIN} ]]; then set -- ${ getent hosts `uname -n`; } shift while [[ -n $1 ]]; do [[ $1 =~ \. ]] && ZDOMAIN="$1" && ZDOMAIN=${ZDOMAIN#*.} && break done fi [[ -z ${BASE_DN} ]] && BASE_DN="ou=${ZDOMAIN//,/,ou=}" cat >${CFG}<>${CFG} else print "${MYSQL//$'\n'/$'\n#'}\n${SQLITE}" >>${CFG} fi while read X ; do [[ $X =~ ^[A-Z_]+= ]] && VARS+=" ${X%%=*}" done <${CFG} print "\nexport ${VARS}" >>${CFG} ln -sf ${CFG} ${REPO}/${CFG##*/} Log.info "'${CFG}' created." Log.warn "Adjust '${CFG}' and '+ systemctl restart greenlight:${NAME}'\n." } function setupSystemd { typeset NAME=${SOPT[INSTANCE]} typeset F=/lib/systemd/system/greenlight:${NAME}.service if [[ -e $F ]]; then (( VERB )) && Log.info "'$F' already exists - leaving as is." return 0 fi Log.info "Creating '$F' ..." cat >$F<4.3.1,bootstrap' # ruby-zeitwerk popper_js mini_portile '--version=>=9.1.0,autoprefixer-rails' '--version=~>0.1.4,tabler-rubygem' # ruby-{execjs,autoprefixer-rails} pagy '--version=~>5.9.0,font-awesome-sass' # ruby-ffi ruby-sassc remote_syslog_logger syslog_protocol random_password i18n-language-mapping '--version=~>0.1.3,omniauth-bn-launcher' '--version=~>0.1.1,omniauth-bn-office365' ) cd ${REPO} || return 1 cp ~admin/etc/Gemfile . for M in ${GEMS[@]} ; do gem install --ignore-dependencies --no-document --minimal-deps \ --without=development,test,assets \ -i ${INSTALL_DIR} ${M//,/ } || (( OOPS++ )) done # fix sass dependency which is in 0.1.4.1 on github cd /var/lib/gems/*/gems/tabler-rubygem-* && \ patch -p1 -i ~admin/etc/greenlight-tabler.patch cd - # finally ${BUNDLE} lock --local return ${OOPS} } function installGemsAsUser { cd ${REPO} || return 1 # + 123 MiB lz4 compressed dev env + 130 MB local gems ${BUNDLE} config set without 'development:test:assets' ${BUNDLE} config build.nokogiri --use-system-libraries # cat ~/.bundle/config ${BUNDLE} install -j20 --path=${INSTALL_DIR} --binstubs=bin/ ${BUNDLE} config set deployment 'true' ${BUNDLE} config --global frozen 1 } function installGreenLight { typeset X if [[ -e ${REPO}/config.ru ]]; then (( VERB )) && Log.info "~/webapp already exists. Skip cloning." else cd ${REPO%/*} git clone https://github.com/bigbluebutton/greenlight.git webapp cd webapp if [[ ${GL_VERSION} != 'master' ]]; then git checkout release-${GL_VERSION} || return 1 fi git checkout -b jel # ruby 2.7 support sed -i -e "/^gem 'bootsnap'/ s/1\..\../1.4.6/" Gemfile # no postgres bloat, but sqlite3 for production as well sed -i -e "/'pg'/ s/^/#/" -e '/sqlite3/ d' \ -e "/^group :production/ i\gem 'sqlite3', '~> 1.4.2'\n" \ Gemfile sed -i -e '/^# workers ENV/ s/^# //' -e '/^# preload_app/ s/^# //' \ config/puma.rb fi X=${ ls -d ${INSTALL_DIR}/ruby/$V/gems/bigbluebutton-api-ruby* 2>/dev/null; } if [[ -z $X ]]; then (( VERB )) && Log.info "Installing GEMs ..." rm -rf ~/.bundle ~/.gem ~/.cache rm -rf ${INSTALL_DIR}/* # motherfucking ruby bundler brain damaged stuff does not work with # system installed Gems, even if version matches. (( 1 )) && installGemsAsUser || installGemsAsSystem rm -rf ~/.bundle/cache/* ${REPO}/tmp/cache/* find ${INSTALL_DIR} -name "*.o" -exec rm -f {} + elif (( VERB )); then Log.info 'GEMs seem to be installed already.' \ "If not, 'rm -rf ${INSTALL_DIR}/*' and try again." fi return 0 # bin/puma -C config/puma.rb } function updateDBandAssets { cd ${REPO} (( VERB )) && Log.info "RAILS_ENV=${RAILS_ENV}" typeset X=${ ${BUNDLE} exec rake db:create 2>&1 ; } if [[ $X =~ already\ exists ]]; then ${BUNDLE} exec rake db:migrate else # if [[ $X =~ ^Created database ]]; then ${BUNDLE} exec rake db:schema:load fi Log.info 'Precompiling assets ...' ${BUNDLE} exec rake assets:precompile } function doMain { [[ -z ${SOPT[DATADIR]} ]] && SOPT[DATADIR]=${DEFAULTS[DATADIR]} [[ -z ${SOPT[GRP]} ]] && SOPT[GRP]=${DEFAULTS[GRP]} [[ -z ${SOPT[USR]} ]] && SOPT[USR]=${DEFAULTS[USR]} [[ -z ${SOPT[INSTANCE]} ]] && SOPT[INSTANCE]=${DEFAULTS[INSTANCE]} [[ ! -d ${SOPT[DATADIR]} ]] && { mkdir -p ${SOPT[DATADIR]} || return 4 ; } if (( ${SOPT[SVCS]} )); then setupSystemd return $? fi if [[ ${LOGNAME} == 'root' || ${ id -u; } == "0" ]]; then Log.fatal "This script should be run by a non-root user like 'webadm'." return 5 fi TMP=${ mktemp -td gl.XXXXXX ; } [[ -z ${TMP} ]] && Log.fatal 'No temp dir - exiting.' && return 6 makeRubyRc && . ~/.ruby_sh_rc || return 7 # setup the app env, which we use in the systemd file as well typeset CFG=${SOPT[DATADIR]}/${SOPT[INSTANCE]}.env makeGreenLightEnv "${CFG}" && . ${CFG} || return 8 # while read X ; do # [[ ${X:0:8} == '#export ' ]] && export ${X:8} && break # done <${CFG} installGreenLight && updateDBandAssets } Man.addFunc MAIN '' '[+NAME?'"${PROG}"' - Greenlight service setup.] [+DESCRIPTION?Simple script to clone greenlight via git, install all required GEMs, initialize/update the instance related database if needed, and to generate a simple systemd script to start/stop the related greenlight server instance. One may run this script multiple times. Software including GEMs get installed only ones, i.e. all instances use the same software but different environment variables, which are honored by greenlight.] [+?This script should be run by an unprivileged user like \awebadm\a except when option \b-s\b is given.] [h:help?Print this help and exit.] [F:functions?Print a list of all functions available.] [T:trace]:[functionList?A comma separated list of functions of this script to trace (convinience for troubleshooting).] [+?] [d:datadir]:[path?Use the given \apath\a to store the config file, user database and certificate incl. key. Default: '"${DEFAULTS[DATADIR]}"'] [g:group]:[name?Run the server using the GID of group \aname\a. Default: '"${DEFAULTS[GRP]}"'] [i:instance]:[name?Setup the environment for the instance with the given \aname\a. Default: '"${DEFAULTS[INSTANCE]}"'.] [m:mysql?If given, prepare MySQL db usage, otherwise use Sqlite3.] [s:svcs?Just setup the systemd service and exit. This action requires write privileges to \b/lib/systemd/system/\b and privileges to reload systemd config.] [u:user]:[name?Run the server using the UID of user \aname\a. Default: '"${DEFAULTS[USR]}"'] [v:verbose?Print some additional infos about what the script is doing.] [+FILES?]{ [+~/.ruby_rc?Ruby related settings for tcsh.] [+~/.ruby_sh_rc?Ruby related settings for bourne shell compatible shells.] [+~webadm/webapp?The default directory, where greenlight gets cloned to (\b$REPO\b).] [+${REPO}/${INAME}.env?Seetings for the Greenlight instance named \aINAME\a.] [+${DATADIR}/${INAME}.sqlite3?Sqlite3 DB for the Greenlight instance \aINAME\a.] [+/lib/systemd/system/greenlight::${INAME}.service?The systemd service management file for the Greenlight instance named \aINAME\a.] } ' unset SOPT TMP VERB; typeset -A SOPT typeset TMP integer VERB=0 typeset X="${ print ${Man.FUNC[MAIN]} ; }" while getopts "${X}" option ; do case "${option}" in h) showUsage MAIN ; exit 0 ;; F) typeset +f ; exit 0 ;; H) if [[ ${OPTARG%_t} != $OPTARG ]]; then ${OPTARG} --man # self-defined types else showUsage "${OPTARG}" # function fi exit 0 ;; T) if [[ ${OPTARG} == 'ALL' ]]; then typeset -ft ${ typeset +f ; } else typeset -ft ${OPTARG//,/ } fi ;; d) SOPT[DATADIR]="${OPTARG}" ;; g) SOPT[GRP]="${OPTARG}" ;; i) SOPT[INSTANCE]="${OPTARG}" ;; m) SOPT[MYSQL]=1 ;; u) SOPT[USR]="${OPTARG}" ;; s) SOPT[SVCS]=1 ;; v) VERB=1 ;; *) showUsage ;; esac done X=$((OPTIND-1)) shift $X ; unset X trap cleanup EXIT doMain "$@"