X-Git-Url: https://vcs.fsf.org/?a=blobdiff_plain;f=launcher;h=850041abe6580917cd3e94249e1f7e25731c8136;hb=378d927b3cb9a84ffedc22d6fbcc36f9a97fe90e;hp=e349b7b139fa2fad095ee715c24f9b692b981e23;hpb=1d8470ddf8c96ba7bbe96b032bfbd11e8e60f59c;p=discourse_docker.git diff --git a/launcher b/launcher index e349b7b..850041a 100755 --- a/launcher +++ b/launcher @@ -1,27 +1,48 @@ #!/bin/bash usage () { - echo "Usage: launcher COMMAND CONFIG [--skip-prereqs]" + echo "Usage: launcher COMMAND CONFIG [--skip-prereqs] [--docker-args STRING]" echo "Commands:" echo " start: Start/initialize a container" echo " stop: Stop a running container" echo " restart: Restart a container" echo " destroy: Stop and remove a container" - echo " enter: Use nsenter to enter a container" - echo " logs: Docker logs for container" + echo " enter: Open a shell to run commands inside the container" + echo " logs: View the Docker logs for a container" echo " bootstrap: Bootstrap a container for the config based on a template" echo " rebuild: Rebuild a container (destroy old, bootstrap, start new)" echo " cleanup: Remove all containers that have stopped for > 24 hours" echo echo "Options:" - echo " --skip-prereqs Don't check prerequisites or resource requirements" - echo " --docker-args Extra arguments to pass when running docker" + echo " --skip-prereqs Don't check launcher prerequisites" + echo " --docker-args Extra arguments to pass when running docker" + echo " --skip-mac-address Don't assign a mac address" exit 1 } command=$1 config=$2 -opt=$3 +user_args="" + +while [ ${#} -gt 0 ]; do + case "${1}" in + --debug) + DEBUG="1" + ;; + --skip-prereqs) + SKIP_PREREQS="1" + ;; + --skip-mac-address) + SKIP_MAC_ADDRESS="1" + ;; + --docker-args) + user_args="$2" + shift + ;; + esac + + shift 1 +done # Docker doesn't like uppercase characters, spaces or special characters, catch it now before we build everything out and then find out re='[A-Z/ !@#$%^&*()+~`=]' @@ -35,16 +56,17 @@ fi cd "$(dirname "$0")" -docker_min_version='1.6.0' -docker_rec_version='1.6.0' +docker_min_version='1.8.0' +docker_rec_version='1.8.0' +git_min_version='1.8.0' +git_rec_version='1.8.0' config_file=containers/"$config".yml cidbootstrap=cids/"$config"_bootstrap.cid local_discourse=local_discourse -image=discourse/discourse:1.0.17 +image=discourse/base:2.0.20170531 docker_path=`which docker.io || which docker` -template_path=samples/standalone.yml -changelog=/tmp/changelog # used to test whether sed did anything +git_path=`which git` if [ "${SUPERVISED}" = "true" ]; then restart_policy="--restart=no" @@ -91,17 +113,12 @@ compare_version() { install_docker() { - - echo "Docker is not installed, you will need to install Docker in order to run Discourse" - echo "Please visit https://docs.docker.com/installation/ for instructions on how to do this for your system" - echo - echo "If you are running a recent Ubuntu Server, try the following:" - echo "sudo apt-get install docker-engine" - + echo "Docker is not installed, you will need to install Docker in order to run Launcher" + echo "See https://docs.docker.com/installation/" exit 1 } -prereqs() { +check_prereqs() { if [ -z $docker_path ]; then install_docker @@ -116,15 +133,15 @@ prereqs() { exit 1 fi - # 2. running aufs or btrfs - test=`$docker_path info 2> /dev/null | grep 'Driver: '` - if [[ "$test" =~ [aufs|btrfs|zfs|overlay] ]] ; then : ; else - echo "Your Docker installation is not using a supported filesystem if we were to proceed you may have a broken install." - echo "aufs is the recommended filesystem you should be using (zfs/btrfs and overlay may work as well)" - echo "You can tell what filesystem you are using by running \"docker info\" and looking at the driver" + # 2. running an approved storage driver? + if ! $docker_path info 2> /dev/null | egrep -q '^Storage Driver: (aufs|btrfs|zfs|overlay|overlay2)$'; then + echo "Your Docker installation is not using a supported storage driver. If we were to proceed you may have a broken install." + echo "aufs is the recommended storage driver, although zfs/btrfs/overlay and overlay2 may work as well." + echo "Other storage drivers are known to be problematic." + echo "You can tell what filesystem you are using by running \"docker info\" and looking at the 'Storage Driver' line." echo - echo "If you wish to continue anyway using your existing unsupported filesystem, " - echo "read the source code of launcher and figure out how to bypass this." + echo "If you wish to continue anyway using your existing unsupported storage driver," + echo "read the source code of launcher and figure out how to bypass this check." exit 1 fi @@ -132,13 +149,13 @@ prereqs() { test=($($docker_path --version)) # Get docker version string test=${test[2]//,/} # Get version alone and strip comma if exists - # At least minimum version + # At least minimum docker version if compare_version "${docker_min_version}" "${test}"; then echo "ERROR: Docker version ${test} not supported, please upgrade to at least ${docker_min_version}, or recommended ${docker_rec_version}" exit 1 fi - # Recommend best version + # Recommend newer docker version if compare_version "${docker_rec_version}" "${test}"; then echo "WARNING: Docker version ${test} deprecated, recommend upgrade to ${docker_rec_version} or newer." fi @@ -153,88 +170,37 @@ prereqs() { echo echo "Please be patient" echo - fi - # 5. able to attach stderr / out / tty - test=`$docker_path run $user_args -i --rm -a stdout -a stderr $image echo working` - if [[ "$test" =~ "working" ]] ; then : ; else - echo "Your Docker installation is not working correctly" - echo - echo "See: https://meta.discourse.org/t/docker-error-on-bootstrap/13657/18?u=sam" - exit 1 - fi - -} - -check_resources() { - # Memory - resources="ok" - avail_mem="$(LANG=C free -m | grep '^Mem:' | awk '{print $2}')" - if [ "$avail_mem" -lt 900 ]; then - resources="insufficient" - echo "WARNING: You do not appear to have sufficient memory to run Discourse." - echo - echo "Your system may not work properly, or future upgrades of Discourse may" - echo "not complete successfully." - echo - echo "See https://github.com/discourse/discourse/blob/master/docs/INSTALL-cloud.md#create-new-cloud-server" - elif [ "$avail_mem" -lt 1800 ]; then - total_swap="$(LANG=C free -m | grep ^Swap: | awk '{print $2}')" - if [ "$total_swap" -lt 1000 ]; then - resources="insufficient" - echo "WARNING: You must have at least 1GB of swap when running with less" - echo "than 2GB of RAM." - echo - echo "Your system may not work properly, or future upgrades of Discourse may" - echo "not complete successfully." - echo - echo "See https://github.com/discourse/discourse/blob/master/docs/INSTALL-cloud.md#set-up-swap-if-needed" - fi - fi + # 5. running recommended git version + test=($($git_path --version)) # Get git version string + test=${test[2]//,/} # Get version alone and strip comma if exists - # Disk space - free_disk="$(df /var | tail -n 1 | awk '{print $4}')" - if [ "$free_disk" -lt 5000 ]; then - resources="insufficient" - echo "WARNING: You must have at least 5GB of *free* disk space to run Discourse." - echo - echo "Insufficient disk space may result in problems running your site, and may" - echo "not even allow Discourse installation to complete successfully." - echo - echo "Please free up some space, or expand your disk, before continuing." - echo - echo "Run \`apt-get autoremove && apt-get autoclean\` to clean up unused packages and \`./launcher cleanup\` to remove stale Docker containers." + # At least minimum version + if compare_version "${git_min_version}" "${test}"; then + echo "ERROR: Git version ${test} not supported, please upgrade to at least ${git_min_version}, or recommended ${git_rec_version}" exit 1 fi - if [ -t 0 ] && [ "$resources" != "ok" ]; then - echo - read -p "Press ENTER to continue, or Ctrl-C to exit and give your system more resources" + # Recommend best version + if compare_version "${git_rec_version}" "${test}"; then + echo "WARNING: Git version ${test} deprecated, recommend upgrade to ${git_rec_version} or newer." fi -} - -check_ports() { - local valid=$(netstat -tln | awk '{print $4}' | grep ":${1}\$") - if [ -n "$valid" ]; then - echo "Launcher has detected that port ${1} is in use." + # 6. able to attach stderr / out / tty + test=`$docker_path run $user_args -i --rm -a stdout -a stderr $image echo working` + if [[ "$test" =~ "working" ]] ; then : ; else + echo "Your Docker installation is not working correctly" echo - echo "If you are trying to run Discourse simultaneously with another web server like Apache or nginx, you will need to bind to a different port." - echo "See https://meta.discourse.org/t/17247 for help." - echo "To continue anyway, re-run Launcher with --skip-prereqs" + echo "See: https://meta.discourse.org/t/docker-error-on-bootstrap/13657/18?u=sam" exit 1 fi + } -if [ "$opt" != "--skip-prereqs" ] ; then - prereqs -fi -if [ "$opt" == "--docker-args" ] ; then - user_args=$4 -else - user_args="" +if [ -z "$SKIP_PREREQS" ] ; then + check_prereqs fi host_run() { @@ -278,11 +244,31 @@ set_links() { "require 'yaml'; puts YAML.load(STDIN.readlines.join)['links'].map{|l| '--link ' << l['link']['name'] << ':' << l['link']['alias'] << ' '}.join"` } -set_template_info() { - - templates=`cat $config_file | $docker_path run $user_args --rm -i -a stdin -a stdout $image ruby -e \ +find_templates() { + local templates=`cat $1 | $docker_path run $user_args --rm -i -a stdin -a stdout $image ruby -e \ "require 'yaml'; puts YAML.load(STDIN.readlines.join)['templates']"` + local arrTemplates=${templates// / } + + if [ ! -z "$templates" ]; then + for template in "${arrTemplates[@]}" + do + local nested_templates=$(find_templates $template) + + if [ ! -z "$nested_templates" ]; then + templates="$templates $nested_templates" + fi + done + + echo $templates + else + echo "" + fi +} + +set_template_info() { + templates=$(find_templates $config_file) + arrTemplates=(${templates// / }) config_data=$(cat $config_file) @@ -333,7 +319,46 @@ RUBY if [ "$ok" -ne 1 ]; then echo "${env[@]}" - echo "YAML syntax error. Please check your /var/discourse/containers/*.yml config files." + echo "YAML syntax error. Please check your containers/*.yml config files." + exit 1 + fi + + read -r -d '' labels_ruby << 'RUBY' + require 'yaml' + + input=STDIN.readlines.join + # default to UTF-8 for the dbs sake + labels = {} + input.split('_FILE_SEPERATOR_').each do |yml| + yml.strip! + begin + labels.merge!(YAML.load(yml)['labels'] || {}) + rescue Psych::SyntaxError => e + puts e + puts "*ERROR." + rescue => e + puts yml + p e + end + end + puts labels.map{|k,v| "-l\n#{k}=#{v}" }.join("\n") +RUBY + + raw=`exec echo "$input" | $docker_path run $user_args --rm -i -a stdin -a stdout $image ruby -e "$labels_ruby"` + + labels=() + ok=1 + while read i; do + if [ "$i" == "*ERROR." ]; then + ok=0 + elif [ -n "$i" ]; then + labels[${#labels[@]}]=$i + fi + done <<< "$raw" + + if [ "$ok" -ne 1 ]; then + echo "${labels[@]}" + echo "YAML syntax error. Please check your containers/*.yml config files." exit 1 fi } @@ -365,16 +390,17 @@ fi exit 0 } -[ $# -lt 2 ] && { +if [ -z "$command" -a -z "$config" ]; then usage -} +fi -if [[ ! -e $config_file && $command -ne "memconfig" ]] - then +if [ ! "$command" == "setup" ]; then + if [[ ! -e $config_file ]]; then echo "Config file was not found, ensure $config_file exists" echo echo "Available configs ( `cd containers && ls -dm *.yml | tr -s '\n' ' ' | awk '{ gsub(/\.yml/, ""); print }'`)" exit 1 + fi fi docker_version=($($docker_path --version)) @@ -425,190 +451,6 @@ set_boot_command() { fi } -scale_ram_and_cpu() { - - # grab info about total system ram and physical (NOT LOGICAL!) CPU cores - avail_mem="$(LANG=C free -m | grep '^Mem:' | awk '{print $2}')" - avail_gb=$(( $avail_mem / 950 )) - avail_cores=`cat /proc/cpuinfo | grep "cpu cores" | uniq | awk '{print $4}'` - echo "Found ${avail_gb}GB of memory and $avail_cores physical CPU cores" - - # db_shared_buffers: 128MB for 1GB, 256MB for 2GB, or 256MB * GB, max 4096MB - if [ "$avail_gb" -eq "1" ] - then - db_shared_buffers=128 - else - if [ "$avail_gb" -eq "2" ] - then - db_shared_buffers=256 - else - db_shared_buffers=$(( 256 * $avail_gb )) - fi - fi - db_shared_buffers=$(( db_shared_buffers < 4096 ? db_shared_buffers : 4096 )) - - sed -i -e "s/^ #db_shared_buffers:.*/ db_shared_buffers: \"${db_shared_buffers}MB\"/w $changelog" $config_file - if [ -s $changelog ] - then - echo "setting db_shared_buffers = ${db_shared_buffers}MB based on detected CPU/RAM" - rm $changelog - fi - - - # UNICORN_WORKERS: 2 * GB for 2GB or less, or 2 * CPU, max 8 - if [ "$avail_gb" -le "2" ] - then - unicorn_workers=$(( 2 * $avail_gb )) - else - unicorn_workers=$(( 2 * $avail_cores )) - fi - unicorn_workers=$(( unicorn_workers < 8 ? unicorn_workers : 8 )) - - sed -i -e "s/^ #UNICORN_WORKERS:.*/ UNICORN_WORKERS: ${unicorn_workers}/w $changelog" $config_file - if [ -s $changelog ] - then - echo "setting UNICORN_WORKERS = ${unicorn_workers} based on detected CPU/RAM" - rm $changelog - fi - -} - -# JP -set_config() { - if [ -f ~/tmp/debugjp ] - then - echo "THIS CODE SHOULD NEVER BE COMMITTED!!" - fi - if grep -q "DISCOURSE_HOSTNAME: 'discourse.example.com'" $config_file - then - local hostname="discourse.example.com" - else - echo "DISCOURSE_HOSTNAME set. Not changing." - fi - if grep -q "DISCOURSE_DEVELOPER_EMAILS: 'me@example.com'" $config_file - then - local developer_emails="me@example.com" - else - echo "DISCOURSE_DEVELOPER_EMAILS set. Not changing." - fi - if grep -q "DISCOURSE_SMTP_ADDRESS: smtp.example.com" $config_file - then - local smtp_address="smtp.example.com" - else - echo "DISCOURSE_SMTP_ADDRESS set. Not changing." - fi - if grep -q "#DISCOURSE_SMTP_USER_NAME: user@example.com" $config_file - then - local smtp_user_name="user@example.com" - else - echo "SMTP_USER_NAME set. Not changing." - fi - if grep -q "#DISCOURSE_SMTP_PASSWORD: pa\$\$word" $config_file - then - local smtp_password="pa\$\$word" - else - echo "DISCOURSE_SMTP_PASSWORD set. Not changing." - fi - if grep -q "#LETSENCRYPT_ACCOUNT_EMAIL:" $config_file - then - local letsencrypt_account_email="your.email@example.com" - else - echo "#LETSENCRYPT_ACCOUNT_EMAIL set. Not changing." - fi - #- "templates/web.ssl.template.yml" - #- "templates/web.letsencrypt.ssl.template.yml" - #DISCOURSE_CDN_URL: //discourse-cdn.example.com - - local config_ok="n" - local new_value="" - local letsencrypt_status="change to enable" - while [ $config_ok == "n" ] - do - echo "Getting config" - if [ ! -z $hostname ] - then - read -p "hostname: [$hostname]: " new_value - if [ ! -z $new_value ] - then - hostname=$new_value - else - echo "Unchanged." - fi - fi - if [ ! -z $developer_emails ] - then - read -p "developer_emails [$developer_emails]: " new_value - if [ ! -z $new_value ] - then - developer_emails=$new_value - fi - fi - if [ ! -z $smtp_address ] - then - read -p "smtp_address [$smtp_address]: " new_value - if [ ! -z $new_value ] - then - smtp_address=$new_value - fi - fi - if [ ! -z $smtp_user_name ] - then - read -p "smtp_user_name [$smtp_user_name]: " new_value - if [ ! -z $new_value ] - then - smtp_user_name=$new_value - fi - fi - if [ ! -z $smtp_password ] - then - read -p "smtp_password [$smtp_password]: " new_value - if [ ! -z $new_value ] - then - smtp_password=$new_value - fi - fi - if [ ! -z $letsencrypt_account_email ] - then - read -p "letsencrypt_account_email ($letsencrypt_status) [$letsencrypt_account_email]: " new_value - if [ ! -z $new_value ] - then - letsencrypt_account_emails=$new_value - letsencrypt_status="enabled" - echo "Letsencrypt enabled." - else - echo "letsencrypt unchanged" - fi - fi - - echo -e "\n\nThat's it! Everything is set. How does it look?\n" - - echo "DISCOURSE_HOSTNAME: $hostname" - echo "DISCOURSE_DEVELOPER_EMAILS: $developer_emails" - echo "DISCOURSE_SMTP_ADDRESS: $smtp_address" - echo "DISCOURSE_SMTP_USER_NAME: $smtp_user_name" - echo "DISCOURSE_SMTP_PASSWORD: $smtp_password" - echo -e "DISCOURSE_SMTP_ENABLE_START_TLS: $smtp_enable_start_tls\c" - if [ $letsencrypt_status == "enabled" ] - then - echo "LETSENCRYPT_ACCOUNT_EMAIL: $letsencrypt_account_emails" - echo "LETSENCRYPT enabled." - else - echo "LETSENCRYPT not enabled." - fi - echo - read -p "Enter to continue with these settings, 'N' to edit or ^C to start again:" config_ok - done - - echo "CONFIG DONE! BUT NOT WRITTEN" - echo "THIS CODE SHOULD NEVER BE COMMITTED!!" - - if [ -f ~/tmp/debugjp ] - then - echo "IF you are reading this you should delete this line and the next one." - exit - fi -} - run_start() { existing=`$docker_path ps | awk '{ print $1, $(NF) }' | grep " $config$" | awk '{ print $1 }'` @@ -631,19 +473,9 @@ run_start() { fi host_run - ports=`cat $config_file | $docker_path run $user_args --rm -i -a stdout -a stdin $image ruby -e \ - "require 'yaml'; puts YAML.load(STDIN.readlines.join)['expose'].map{|p| \"-p #{p}\"}.join(' ')"` - IFS='-p ' read -a array <<< "$ports" - for element in "${array[@]}" - do - IFS=':' read -a args <<< "$element" - if [ "${#args[@]}" == "2" ]; then - check_ports "${args[0]}" - elif [ "${#args[@]}" == "3" ]; then - check_ports "${args[1]}" - fi - done + ports=`cat $config_file | $docker_path run $user_args --rm -i -a stdout -a stdin $image ruby -e \ + "require 'yaml'; puts YAML.load(STDIN.readlines.join)['expose'].map{|p| \"-p #{p}\"}.join(' ')"` docker_args=`cat $config_file | $docker_path run $user_args --rm -i -a stdout -a stdin $image ruby -e \ "require 'yaml'; puts YAML.load(STDIN.readlines.join)['docker_args']"` @@ -677,60 +509,26 @@ run_start() { # we got to normalize so we only have allowed strings, this is more comprehensive but lets see how bash does first # hostname=`$docker_path run $user_args --rm $image ruby -e 'print ARGV[0].gsub(/[^a-zA-Z-]/, "-")' $hostname` # docker added more hostname rules - hostname=${hostname/_/-} + hostname=${hostname//_/-} + + + if [ -z "$SKIP_MAC_ADDRESS" ] ; then + mac_address="--mac-address $($docker_path run $user_args -i --rm -a stdout -a stderr $image /bin/sh -c "echo $hostname | md5sum | sed 's/^\(..\)\(..\)\(..\)\(..\)\(..\).*$/02:\1:\2:\3:\4:\5/'")" + fi set -x - $docker_path run $user_args $links $attach_on_run $restart_policy "${env[@]}" -h "$hostname" \ - -e DOCKER_HOST_IP=$docker_ip --name $config -t $ports $volumes $docker_args $run_image $boot_command + $docker_path run $links $attach_on_run $restart_policy "${env[@]}" "${labels[@]}" -h "$hostname" \ + -e DOCKER_HOST_IP="$docker_ip" --name $config -t $ports $volumes $mac_address $docker_args $user_args \ + $run_image $boot_command ) exit 0 } -valid_config_check() { - - valid_config="y" - for x in DISCOURSE_SMTP_ADDRESS DISCOURSE_SMTP_USER_NAME DISCOURSE_SMTP_PASSWORD \ - DISCOURSE_DEVELOPER_EMAILS DISCOURSE_HOSTNAME - do - mail_var=`grep "^ $x:" $config_file` - local result=$? - local default="example.com" - if (( result == 0 )) - then - if [[ $mail_var = *"$default"* ]] - then - echo "Warning: $x left at incorrect default of example.com" - valid_config="n" - fi - else - echo "Warning: $x not configured" - valid_config="n" - fi - done - if [ -t 0 ] && [ "$valid_config" != "y" ]; then - echo - read -p "Press Ctrl-C to exit and edit $config_file or ENTER to continue" - fi -} run_bootstrap() { - # Does your system meet the minimum requirements? - if [ "$opt" != "--skip-prereqs" ] ; then - check_resources - fi - - # run config wizard - set_config - - # is our configuration file valid? - valid_config_check - - # make minor scaling adjustments for RAM and CPU - scale_ram_and_cpu - # I got no frigging clue what this does, ask Sam Saffron. It RUNS STUFF ON THE HOST I GUESS? host_run @@ -763,11 +561,31 @@ run_bootstrap() { echo $run_command - (exec echo "$input" | $docker_path run $user_args $links "${env[@]}" -e DOCKER_HOST_IP=$docker_ip --cidfile $cidbootstrap -i -a stdin -a stdout -a stderr $volumes $image \ - /bin/bash -c "$run_command") \ - || ($docker_path rm `cat $cidbootstrap` && rm $cidbootstrap) + unset ERR + (exec echo "$input" | $docker_path run $user_args $links "${env[@]}" -e DOCKER_HOST_IP="$docker_ip" --cidfile $cidbootstrap -i -a stdin -a stdout -a stderr $volumes $image \ + /bin/bash -c "$run_command") || ERR=$? + + unset FAILED + # magic exit code that indicates a retry + if [[ "$ERR" == 77 ]]; then + $docker_path rm `cat $cidbootstrap` + rm $cidbootstrap + exit 77 + elif [[ "$ERR" > 0 ]]; then + FAILED=TRUE + fi + + if [[ $FAILED = "TRUE" ]]; then + if [[ ! -z "$DEBUG" ]]; then + $docker_path commit `cat $cidbootstrap` $local_discourse/$config-debug || echo 'FAILED TO COMMIT' + echo "** DEBUG ** Maintaining image for diagnostics $local_discourse/$config-debug" + fi - [ ! -e $cidbootstrap ] && echo "** FAILED TO BOOTSTRAP ** please scroll up and look for earlier error messages, there may be more than one" && exit 1 + $docker_path rm `cat $cidbootstrap` + rm $cidbootstrap + echo "** FAILED TO BOOTSTRAP ** please scroll up and look for earlier error messages, there may be more than one" + exit 1 + fi sleep 5 @@ -812,7 +630,7 @@ case "$command" in rebuild) if [ "$(git symbolic-ref --short HEAD)" == "master" ]; then - echo "Ensuring discourse docker is up to date" + echo "Ensuring launcher is up to date" git remote update @@ -821,18 +639,23 @@ case "$command" in BASE=$(git merge-base @ @{u}) if [ $LOCAL = $REMOTE ]; then - echo "Discourse Docker is up-to-date" + echo "Launcher is up-to-date" elif [ $LOCAL = $BASE ]; then - echo "Updating Discourse Docker" + echo "Updating Launcher" git pull || (echo 'failed to update' && exit 1) - exec /bin/bash $0 $@ + + for (( i=${#BASH_ARGV[@]}-1,j=0; i>=0,j<${#BASH_ARGV[@]}; i--,j++ )) + do + args[$j]=${BASH_ARGV[$i]} + done + exec /bin/bash $0 "${args[@]}" # $@ is empty, because of shift at the beginning. Use BASH_ARGV instead. elif [ $REMOTE = $BASE ]; then - echo "Your version of Discourse Docker is ahead of origin" + echo "Your version of Launcher is ahead of origin" else - echo "Discourse Docker has diverged source, this is only expected in Dev mode" + echo "Launcher has diverged source, this is only expected in Dev mode" fi fi