FIX: Reorder yarn/bundle install for compatibility (#642)
[discourse_docker.git] / launcher
1 #!/usr/bin/env bash
2
3 usage () {
4 echo "Usage: launcher COMMAND CONFIG [--skip-prereqs] [--docker-args STRING]"
5 echo "Commands:"
6 echo " start: Start/initialize a container"
7 echo " stop: Stop a running container"
8 echo " restart: Restart a container"
9 echo " destroy: Stop and remove a container"
10 echo " enter: Open a shell to run commands inside the container"
11 echo " logs: View the Docker logs for a container"
12 echo " bootstrap: Bootstrap a container for the config based on a template"
13 echo " run: Run the given command with the config in the context of the last bootstrapped image"
14 echo " rebuild: Rebuild a container (destroy old, bootstrap, start new)"
15 echo " cleanup: Remove all containers that have stopped for > 24 hours"
16 echo " start-cmd: Generate docker command used to start container"
17 echo
18 echo "Options:"
19 echo " --skip-prereqs Don't check launcher prerequisites"
20 echo " --docker-args Extra arguments to pass when running docker"
21 echo " --skip-mac-address Don't assign a mac address"
22 echo " --run-image Override the image used for running the container"
23 exit 1
24 }
25
26 # for potential re-exec later
27 SAVED_ARGV=("$@")
28
29 command=$1
30 config=$2
31
32 # user_args_argv is assigned once when the argument vector is parsed.
33 user_args_argv=""
34 # user_args is mutable: its value may change when templates are parsed.
35 # Superset of user_args_argv.
36 user_args=""
37
38 user_run_image=""
39
40 if [[ $command == "run" ]]; then
41 run_command=$3
42 fi
43
44 while [ ${#} -gt 0 ]; do
45 case "${1}" in
46 --debug)
47 DEBUG="1"
48 ;;
49 --skip-prereqs)
50 SKIP_PREREQS="1"
51 ;;
52 --skip-mac-address)
53 SKIP_MAC_ADDRESS="1"
54 ;;
55 --docker-args)
56 user_args_argv="$2"
57 user_args="$user_args_argv"
58 shift
59 ;;
60 --run-image)
61 user_run_image="$2"
62 shift
63 ;;
64 esac
65
66 shift 1
67 done
68
69 if [ -z "$command" -o -z "$config" -a "$command" != "cleanup" ]; then
70 usage
71 fi
72
73 # Docker doesn't like uppercase characters, spaces or special characters, catch it now before we build everything out and then find out
74 re='[[:upper:]/ !@#$%^&*()+~`=]'
75 if [[ $config =~ $re ]];
76 then
77 echo
78 echo "ERROR: Config name '$config' must not contain upper case characters, spaces or special characters. Correct config name and rerun $0."
79 echo
80 exit 1
81 fi
82
83 cd "$(dirname "$0")"
84
85 pups_version='v1.0.3'
86 docker_min_version='17.03.1'
87 docker_rec_version='17.06.2'
88 git_min_version='1.8.0'
89 git_rec_version='1.8.0'
90
91 config_file=containers/"$config".yml
92 cidbootstrap=cids/"$config"_bootstrap.cid
93 local_discourse=local_discourse
94 image="discourse/base:2.0.20220818-0047"
95 docker_path=`which docker.io 2> /dev/null || which docker`
96 git_path=`which git`
97
98 if [ "${SUPERVISED}" = "true" ]; then
99 restart_policy="--restart=no"
100 attach_on_start="-a"
101 attach_on_run="-a stdout -a stderr"
102 else
103 attach_on_run="-d"
104 fi
105
106 if [ -n "$DOCKER_HOST" ]; then
107 docker_ip=`sed -e 's/^tcp:\/\/\(.*\):.*$/\1/' <<< "$DOCKER_HOST"`
108 elif [ -x "$(which ip 2>/dev/null)" ]; then
109 docker_ip=`ip addr show docker0 | \
110 grep 'inet ' | \
111 awk '{ split($2,a,"/"); print a[1] }';`
112 else
113 docker_ip=`ifconfig | \
114 grep -B1 "inet addr" | \
115 awk '{ if ( $1 == "inet" ) { print $2 } else if ( $2 == "Link" ) { printf "%s:" ,$1 } }' | \
116 grep docker0 | \
117 awk -F: '{ print $3 }';`
118 fi
119
120 # From https://stackoverflow.com/a/44660519/702738
121 compare_version() {
122 if [[ $1 == $2 ]]; then
123 return 1
124 fi
125 local IFS=.
126 local i a=(${1%%[^0-9.]*}) b=(${2%%[^0-9.]*})
127 local arem=${1#${1%%[^0-9.]*}} brem=${2#${2%%[^0-9.]*}}
128 for ((i=0; i<${#a[@]} || i<${#b[@]}; i++)); do
129 if ((10#${a[i]:-0} < 10#${b[i]:-0})); then
130 return 1
131 elif ((10#${a[i]:-0} > 10#${b[i]:-0})); then
132 return 0
133 fi
134 done
135 if [ "$arem" '<' "$brem" ]; then
136 return 1
137 elif [ "$arem" '>' "$brem" ]; then
138 return 0
139 fi
140 return 1
141 }
142
143 fatal () {
144 echo -e "\n$1\n"
145 exit "${2:-1}"
146 }
147
148 install_docker() {
149 echo "Docker is not installed, you will need to install Docker in order to run Launcher"
150 echo "See https://docs.docker.com/installation/"
151 exit 1
152 }
153
154 pull_image() {
155 # Add a single retry to work around dockerhub TLS errors
156 $docker_path pull $image || $docker_path pull $image
157 }
158
159 check_prereqs() {
160
161 if [ -z $docker_path ]; then
162 install_docker
163 fi
164
165 # 1. docker daemon running?
166 # we send stderr to /dev/null cause we don't care about warnings,
167 # it usually complains about swap which does not matter
168 test=`$docker_path info 2> /dev/null`
169 if [[ $? -ne 0 ]] ; then
170 echo "Cannot connect to the docker daemon - verify it is running and you have access"
171 exit 1
172 fi
173
174 # 2. running an approved storage driver?
175 if ! $docker_path info 2> /dev/null | egrep -q 'Storage Driver: (btrfs|aufs|zfs|overlay2)$'; then
176 echo "Your Docker installation is not using a supported storage driver. If we were to proceed you may have a broken install."
177 echo "overlay2 is the recommended storage driver, although zfs and aufs may work as well."
178 echo "Other storage drivers are known to be problematic."
179 echo "You can tell what filesystem you are using by running \"docker info\" and looking at the 'Storage Driver' line."
180 echo
181 echo "If you wish to continue anyway using your existing unsupported storage driver,"
182 echo "read the source code of launcher and figure out how to bypass this check."
183 exit 1
184 fi
185
186 # 3. running recommended docker version
187 test=($($docker_path --version)) # Get docker version string
188 test=${test[2]//,/} # Get version alone and strip comma if exists
189
190 # At least minimum docker version
191 if compare_version "${docker_min_version}" "${test}"; then
192 echo "ERROR: Docker version ${test} not supported, please upgrade to at least ${docker_min_version}, or recommended ${docker_rec_version}"
193 exit 1
194 fi
195
196 # Recommend newer docker version
197 if compare_version "${docker_rec_version}" "${test}"; then
198 echo "WARNING: Docker version ${test} deprecated, recommend upgrade to ${docker_rec_version} or newer."
199 fi
200
201 arm=false
202 case $(uname -m) in
203 armv7l)
204 echo "ERROR: 32bit arm is not supported. Check if your hardware support arm64, which is supported in experimental capacity."
205 exit 1
206 ;;
207 aarch64 | arm64)
208 echo "WARNING: Support for aarch64 is experimental at the moment. Please report any problems at https://meta.discourse.org/tag/arm "
209 image="discourse/base:aarch64"
210 arm=true
211 read -n 1 -s -r -p "Press any key to continue"
212 ;;
213 x86_64)
214 echo "x86_64 arch detected."
215 ;;
216 *)
217 echo "ERROR: unknown arch detected."
218 exit 1
219 ;;
220 esac
221
222
223 # 4. discourse docker image is downloaded
224 test=`$docker_path images | awk '{print $1 ":" $2 }' | grep "$image"`
225
226 # arm experimental support is on a fixed tag, always pull
227 if [ -z "$test" ] || [ $arm = true ]; then
228 echo
229 echo "WARNING: We are about to start downloading the Discourse base image"
230 echo "This process may take anywhere between a few minutes to an hour, depending on your network speed"
231 echo
232 echo "Please be patient"
233 echo
234
235 pull_image
236 fi
237
238 # 5. running recommended git version
239 test=($($git_path --version)) # Get git version string
240 test=${test[2]//,/} # Get version alone and strip comma if exists
241
242 # At least minimum version
243 if compare_version "${git_min_version}" "${test}"; then
244 echo "ERROR: Git version ${test} not supported, please upgrade to at least ${git_min_version}, or recommended ${git_rec_version}"
245 exit 1
246 fi
247
248 # Recommend best version
249 if compare_version "${git_rec_version}" "${test}"; then
250 echo "WARNING: Git version ${test} deprecated, recommend upgrade to ${git_rec_version} or newer."
251 fi
252
253 # 6. able to attach stderr / out / tty
254 test=`$docker_path run $user_args -i --rm -a stdout -a stderr $image echo working`
255 if [[ "$test" =~ "working" ]] ; then : ; else
256 echo "Your Docker installation is not working correctly"
257 echo
258 echo "See: https://meta.discourse.org/t/docker-error-on-bootstrap/13657/18?u=sam"
259 exit 1
260 fi
261
262 # 7. enough space for the bootstrap on docker folder
263 folder=`$docker_path info --format '{{.DockerRootDir}}'`
264 safe_folder=${folder:-/var/lib/docker}
265 if [[ -d $safe_folder && $(stat -f --format="%a*%S" $safe_folder)/1024**3 -lt 5 ]] ; then
266 echo "You have less than 5GB of free space on the disk where $safe_folder is located. You will need more space to continue"
267 df -h $safe_folder
268 echo
269 if tty >/dev/null; then
270 read -p "Would you like to attempt to recover space by cleaning docker images and containers in the system? (y/N)" -n 1 -r
271 echo
272 if [[ $REPLY =~ ^[Yy]$ ]]
273 then
274 $docker_path container prune --force --filter until=24h >/dev/null
275 $docker_path image prune --all --force --filter until=24h >/dev/null
276 echo "If the cleanup was successful, you may try again now"
277 fi
278 fi
279 exit 1
280 fi
281
282 # 8. container definition file is accessible and is not insecure (world-readable)
283 if [[ ! -e "$config_file" || ! -r "$config_file" ]]; then
284 echo "ERROR: $config_file does not exist or is not readable."
285 echo
286 echo "Available configs ( `cd containers && ls -dm *.yml | tr -s '\n' ' ' | awk '{ gsub(/\.yml/, ""); print }'`)"
287 exit 1
288 elif [[ "$(find $config_file -perm -004)" ]]; then
289 echo "WARNING: $config_file file is world-readable. You can secure this file by running: chmod o-rwx $config_file"
290 fi
291 }
292
293
294 if [ -z "$SKIP_PREREQS" ] && [ "$command" != "cleanup" ]; then
295 check_prereqs
296 fi
297
298 set_volumes() {
299 volumes=`cat $config_file | $docker_path run $user_args --rm -i -a stdout -a stdin $image ruby -e \
300 "require 'yaml'; puts YAML.load(STDIN.readlines.join)['volumes'].map{|v| '-v ' << v['volume']['host'] << ':' << v['volume']['guest'] << ' '}.join"`
301 }
302
303 set_links() {
304 links=`cat $config_file | $docker_path run $user_args --rm -i -a stdout -a stdin $image ruby -e \
305 "require 'yaml'; puts YAML.load(STDIN.readlines.join)['links'].map{|l| '--link ' << l['link']['name'] << ':' << l['link']['alias'] << ' '}.join"`
306 }
307
308 find_templates() {
309 local templates=`cat $1 | $docker_path run $user_args --rm -i -a stdin -a stdout $image ruby -e \
310 "require 'yaml'; puts YAML.load(STDIN.readlines.join)['templates']"`
311
312 local arrTemplates=${templates// / }
313
314 if [ ! -z "$templates" ]; then
315 echo $templates
316 else
317 echo ""
318 fi
319 }
320
321 set_template_info() {
322 templates=$(find_templates $config_file)
323
324 arrTemplates=(${templates// / })
325 config_data=$(cat $config_file)
326
327 input="hack: true"
328
329 for template in "${arrTemplates[@]}"
330 do
331 [ ! -z $template ] && {
332 input="$input _FILE_SEPERATOR_ $(cat $template)"
333 }
334 done
335
336 # we always want our config file last so it takes priority
337 input="$input _FILE_SEPERATOR_ $config_data"
338
339 read -r -d '' env_ruby << 'RUBY'
340 require 'yaml'
341
342 input=STDIN.readlines.join
343 # default to UTF-8 for the dbs sake
344 env = {'LANG' => 'en_US.UTF-8'}
345 input.split('_FILE_SEPERATOR_').each do |yml|
346 yml.strip!
347 begin
348 env.merge!(YAML.load(yml)['env'] || {})
349 rescue Psych::SyntaxError => e
350 puts e
351 puts "*ERROR."
352 rescue => e
353 puts yml
354 p e
355 end
356 end
357 env.each{|k,v| puts "*ERROR." if v.is_a?(Hash)}
358 puts env.map{|k,v| "-e\n#{k}=#{v}" }.join("\n")
359 RUBY
360
361 tmp_input_file=$(mktemp)
362
363 echo "$input" > "$tmp_input_file"
364 raw=`exec cat "$tmp_input_file" | $docker_path run $user_args --rm -i -a stdin -a stdout $image ruby -e "$env_ruby"`
365
366 rm -f "$tmp_input_file"
367
368 env=()
369 ok=1
370 while read i; do
371 if [ "$i" == "*ERROR." ]; then
372 ok=0
373 elif [ -n "$i" ]; then
374 env[${#env[@]}]="${i//\{\{config\}\}/${config}}"
375 fi
376 done <<< "$raw"
377
378 if [ "$ok" -ne 1 ]; then
379 echo "${env[@]}"
380 echo "YAML syntax error. Please check your containers/*.yml config files."
381 exit 1
382 fi
383
384 # labels
385 read -r -d '' labels_ruby << 'RUBY'
386 require 'yaml'
387
388 input=STDIN.readlines.join
389 labels = {}
390 input.split('_FILE_SEPERATOR_').each do |yml|
391 yml.strip!
392 begin
393 labels.merge!(YAML.load(yml)['labels'] || {})
394 rescue Psych::SyntaxError => e
395 puts e
396 puts "*ERROR."
397 rescue => e
398 puts yml
399 p e
400 end
401 end
402 puts labels.map{|k,v| "-l\n#{k}=#{v}" }.join("\n")
403 RUBY
404
405 tmp_input_file=$(mktemp)
406
407 echo "$input" > "$tmp_input_file"
408 raw=`exec cat "$tmp_input_file" | $docker_path run $user_args --rm -i -a stdin -a stdout $image ruby -e "$labels_ruby"`
409
410 rm -f "$tmp_input_file"
411
412 labels=()
413 ok=1
414 while read i; do
415 if [ "$i" == "*ERROR." ]; then
416 ok=0
417 elif [ -n "$i" ]; then
418 labels[${#labels[@]}]=$(echo $i | sed s/{{config}}/${config}/g)
419 fi
420 done <<< "$raw"
421
422 if [ "$ok" -ne 1 ]; then
423 echo "${labels[@]}"
424 echo "YAML syntax error. Please check your containers/*.yml config files."
425 exit 1
426 fi
427
428 # expose
429 read -r -d '' ports_ruby << 'RUBY'
430 require 'yaml'
431
432 input=STDIN.readlines.join
433 ports = []
434 input.split('_FILE_SEPERATOR_').each do |yml|
435 yml.strip!
436 begin
437 ports += (YAML.load(yml)['expose'] || [])
438 rescue Psych::SyntaxError => e
439 puts e
440 puts "*ERROR."
441 rescue => e
442 puts yml
443 p e
444 end
445 end
446 puts ports.map { |p| p.to_s.include?(':') ? "-p\n#{p}" : "--expose\n#{p}" }.join("\n")
447 RUBY
448
449 tmp_input_file=$(mktemp)
450
451 echo "$input" > "$tmp_input_file"
452 raw=`exec cat "$tmp_input_file" | $docker_path run $user_args --rm -i -a stdin -a stdout $image ruby -e "$ports_ruby"`
453
454 rm -f "$tmp_input_file"
455
456 ports=()
457 ok=1
458 while read i; do
459 if [ "$i" == "*ERROR." ]; then
460 ok=0
461 elif [ -n "$i" ]; then
462 ports[${#ports[@]}]=$i
463 fi
464 done <<< "$raw"
465
466 if [ "$ok" -ne 1 ]; then
467 echo "${ports[@]}"
468 echo "YAML syntax error. Please check your containers/*.yml config files."
469 exit 1
470 fi
471
472 merge_user_args
473 }
474
475 if [ -z $docker_path ]; then
476 install_docker
477 fi
478
479 [ "$command" == "cleanup" ] && {
480 $docker_path container prune --filter until=1h
481 $docker_path image prune --all --filter until=1h
482
483 if [ -d /var/discourse/shared/standalone/postgres_data_old ]; then
484 echo
485 echo "Old PostgreSQL backup data cluster detected taking up $(du -hs /var/discourse/shared/standalone/postgres_data_old | awk '{print $1}') detected"
486 read -p "Would you like to remove it? (y/N): " -n 1 -r && echo
487
488 if [[ $REPLY =~ ^[Yy]$ ]]; then
489 echo "removing old PostgreSQL data cluster at /var/discourse/shared/standalone/postgres_data_old..."
490 rm -rf /var/discourse/shared/standalone/postgres_data_old*
491 else
492 exit 1
493 fi
494 fi
495
496 exit 0
497 }
498
499 docker_version=($($docker_path --version))
500 docker_version=${test[2]//,/}
501 restart_policy=${restart_policy:---restart=always}
502
503 set_existing_container(){
504 existing=`$docker_path ps -a | awk '{ print $1, $(NF) }' | grep " $config$" | awk '{ print $1 }'`
505 }
506
507 run_stop() {
508
509 set_existing_container
510
511 if [ ! -z $existing ]
512 then
513 (
514 set -x
515 $docker_path stop -t 30 $config
516 )
517 else
518 echo "$config was not started !"
519 echo "./discourse-doctor may help diagnose the problem."
520 exit 1
521 fi
522 }
523
524 set_run_image() {
525 run_image=`cat $config_file | $docker_path run $user_args --rm -i -a stdin -a stdout $image ruby -e \
526 "require 'yaml'; puts YAML.load(STDIN.readlines.join)['run_image']"`
527
528 if [ -n "$user_run_image" ]; then
529 run_image=$user_run_image
530 elif [ -z "$run_image" ]; then
531 run_image="$local_discourse/$config"
532 fi
533 }
534
535 set_boot_command() {
536 boot_command=`cat $config_file | $docker_path run $user_args --rm -i -a stdin -a stdout $image ruby -e \
537 "require 'yaml'; puts YAML.load(STDIN.readlines.join)['boot_command']"`
538
539 if [ -z "$boot_command" ]; then
540
541 no_boot_command=`cat $config_file | $docker_path run $user_args --rm -i -a stdin -a stdout $image ruby -e \
542 "require 'yaml'; puts YAML.load(STDIN.readlines.join)['no_boot_command']"`
543
544 if [ -z "$no_boot_command" ]; then
545 boot_command="/sbin/boot"
546 fi
547 fi
548 }
549
550 merge_user_args() {
551 local docker_args
552
553 docker_args=`cat $config_file | $docker_path run $user_args --rm -i -a stdout -a stdin $image ruby -e \
554 "require 'yaml'; puts YAML.load(STDIN.readlines.join)['docker_args']"`
555
556 if [[ -n "$docker_args" ]]; then
557 user_args="$user_args_argv $docker_args"
558 fi
559 }
560
561 run_start() {
562
563 if [ -z "$START_CMD_ONLY" ]
564 then
565 existing=`$docker_path ps | awk '{ print $1, $(NF) }' | grep " $config$" | awk '{ print $1 }'`
566 echo $existing
567 if [ ! -z $existing ]
568 then
569 echo "Nothing to do, your container has already started!"
570 exit 0
571 fi
572
573 existing=`$docker_path ps -a | awk '{ print $1, $(NF) }' | grep " $config$" | awk '{ print $1 }'`
574 if [ ! -z $existing ]
575 then
576 echo "starting up existing container"
577 (
578 set -x
579 $docker_path start $config
580 )
581 exit 0
582 fi
583 fi
584
585 set_template_info
586 set_volumes
587 set_links
588 set_run_image
589 set_boot_command
590
591 # get hostname and settings from container configuration
592 for envar in "${env[@]}"
593 do
594 if [[ $envar == DOCKER_USE_HOSTNAME* ]] || [[ $envar == DISCOURSE_HOSTNAME* ]]
595 then
596 # use as environment variable
597 eval $envar
598 fi
599 done
600
601 (
602 hostname=`hostname -s`
603 # overwrite hostname
604 if [ "$DOCKER_USE_HOSTNAME" = "true" ]
605 then
606 hostname=$DISCOURSE_HOSTNAME
607 else
608 hostname=$hostname-$config
609 fi
610
611 # we got to normalize so we only have allowed strings, this is more comprehensive but lets see how bash does first
612 # hostname=`$docker_path run $user_args --rm $image ruby -e 'print ARGV[0].gsub(/[^a-zA-Z-]/, "-")' $hostname`
613 # docker added more hostname rules
614 hostname=${hostname//_/-}
615
616
617 if [ -z "$SKIP_MAC_ADDRESS" ] ; then
618 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/'")"
619 fi
620
621 if [ ! -z "$START_CMD_ONLY" ] ; then
622 docker_path="true"
623 fi
624
625 set -x
626
627 $docker_path run --shm-size=512m $links $attach_on_run $restart_policy "${env[@]}" "${labels[@]}" -h "$hostname" \
628 -e DOCKER_HOST_IP="$docker_ip" --name $config -t "${ports[@]}" $volumes $mac_address $user_args \
629 $run_image $boot_command
630
631 )
632 exit 0
633
634 }
635
636 run_run() {
637 set_template_info
638 set_volumes
639 set_links
640 set_run_image
641
642 unset ERR
643 (exec $docker_path run --rm --shm-size=512m $user_args $links "${env[@]}" -e DOCKER_HOST_IP="$docker_ip" -i -a stdin -a stdout -a stderr $volumes $run_image \
644 /bin/bash -c "$run_command") || ERR=$?
645
646 if [[ $ERR > 0 ]]; then
647 exit 1
648 fi
649 }
650
651 run_bootstrap() {
652 set_template_info
653
654 base_image=`cat $config_file | $docker_path run $user_args --rm -i -a stdin -a stdout $image ruby -e \
655 "require 'yaml'; puts YAML.load(STDIN.readlines.join)['base_image']"`
656
657 update_pups=`cat $config_file | $docker_path run $user_args --rm -i -a stdin -a stdout $image ruby -e \
658 "require 'yaml'; puts YAML.load(STDIN.readlines.join)['update_pups']"`
659
660 if [[ ! X"" = X"$base_image" ]]; then
661 image=$base_image
662 fi
663
664 # the base_image may not always be discourse/base,
665 # let's ensure we always build from the latest
666 pull_image
667
668 set_volumes
669 set_links
670
671 if $docker_path run $user_args --rm -i $image gem which pups; then
672 pups_command="/usr/local/bin/pups --stdin"
673 else
674 # Fallback to git pull method here if `pups` was not installed by gem in base image
675 pups_command="cd /pups &&"
676 if [[ ! "false" = $update_pups ]]; then
677 pups_command="$pups_command git pull && git checkout $pups_version &&"
678 fi
679 pups_command="$pups_command /pups/bin/pups --stdin"
680 fi
681
682 echo $pups_command
683
684 declare -i BOOTSTRAP_EXITCODE
685 rm -f $cidbootstrap
686
687 echo "$input" | $docker_path run --shm-size=512m $user_args $links "${env[@]}" -e DOCKER_HOST_IP="$docker_ip" --cidfile "$cidbootstrap" -i -a stdin -a stdout -a stderr $volumes $image \
688 /bin/bash -c "$pups_command"
689 BOOTSTRAP_EXITCODE=$?
690
691 CONTAINER_ID=$(cat "$cidbootstrap")
692 rm -f "$cidbootstrap"
693
694 # magic exit code that indicates a retry
695 if [[ $BOOTSTRAP_EXITCODE -eq 77 ]]; then
696 $docker_path rm "$CONTAINER_ID"
697 exit 77
698 elif [[ $BOOTSTRAP_EXITCODE -gt 0 ]]; then
699 echo "bootstrap failed with exit code $BOOTSTRAP_EXITCODE"
700 echo "** FAILED TO BOOTSTRAP ** please scroll up and look for earlier error messages, there may be more than one."
701 echo "./discourse-doctor may help diagnose the problem."
702
703 if [[ -n "$DEBUG" ]]; then
704 if $docker_path commit "$CONTAINER_ID" $local_discourse/$config-debug; then
705 echo "** DEBUG ** Maintaining image for diagnostics $local_discourse/$config-debug"
706 else
707 echo "** DEBUG ** Failed to commit container $CONTAINER_ID for diagnostics"
708 fi
709 fi
710
711 $docker_path rm "$CONTAINER_ID"
712 exit 1
713 fi
714
715 sleep 5
716
717 $docker_path commit \
718 -c "LABEL org.opencontainers.image.created=\"$(TZ=UTC date -Iseconds)\"" \
719 "$CONTAINER_ID" \
720 $local_discourse/$config || fatal "FAILED TO COMMIT $CONTAINER_ID"
721 $docker_path rm "$CONTAINER_ID"
722 }
723
724 case "$command" in
725 bootstrap)
726 run_bootstrap
727 echo "Successfully bootstrapped, to startup use ./launcher start $config"
728 exit 0
729 ;;
730
731 run)
732 run_run
733 exit 0
734 ;;
735
736 enter)
737 exec $docker_path exec -it $config /bin/bash --login
738 ;;
739
740 stop)
741 run_stop
742 exit 0
743 ;;
744
745 logs)
746
747 $docker_path logs $config
748 exit 0
749 ;;
750
751 restart)
752 run_stop
753 run_start
754 exit 0
755 ;;
756
757 start-cmd)
758 START_CMD_ONLY="1"
759 run_start
760 exit 0;
761 ;;
762
763 start)
764 run_start
765 exit 0
766 ;;
767
768 rebuild)
769 if [ "$(git symbolic-ref --short HEAD)" == "master" ]; then
770 git branch -m master main
771 git fetch origin
772 git branch -u origin/main main
773 git remote set-head origin -a
774 fi
775
776 if [ "$(git symbolic-ref --short HEAD)" == "main" ]; then
777 echo "Ensuring launcher is up to date"
778
779 git remote update
780
781 LOCAL=$(git rev-parse HEAD)
782 REMOTE=$(git rev-parse @{u})
783 BASE=$(git merge-base HEAD @{u})
784
785 if [ $LOCAL = $REMOTE ]; then
786 echo "Launcher is up-to-date"
787
788 elif [ $LOCAL = $BASE ]; then
789 echo "Updating Launcher..."
790 git pull || (echo 'failed to update' && exit 1)
791
792 echo "Launcher updated, restarting..."
793 exec "$0" "${SAVED_ARGV[@]}"
794
795 elif [ $REMOTE = $BASE ]; then
796 echo "Your version of Launcher is ahead of origin"
797
798 else
799 echo "Launcher has diverged source, this is only expected in Dev mode"
800 fi
801
802 fi
803
804 set_existing_container
805
806 if [ ! -z $existing ]
807 then
808 echo "Stopping old container"
809 (
810 set -x
811 $docker_path stop -t 60 $config
812 )
813 fi
814
815 run_bootstrap
816
817 if [ ! -z $existing ]
818 then
819 echo "Removing old container"
820 (
821 set -x
822 $docker_path rm $config
823 )
824 fi
825
826 run_start
827 exit 0
828 ;;
829
830
831 destroy)
832 (set -x; $docker_path stop -t 10 $config && $docker_path rm $config) || (echo "$config was not found" && exit 0)
833 exit 0
834 ;;
835 esac
836
837 usage