forked from ubergeek77/Lemmy-Easy-Deploy
-
Notifications
You must be signed in to change notification settings - Fork 0
/
deploy.sh
executable file
·1735 lines (1585 loc) · 62.5 KB
/
deploy.sh
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
#!/usr/bin/env bash
LED_CURRENT_VERSION="1.3.3"
# cd to the directory the script is in
SCRIPT_DIR=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &>/dev/null && pwd)
cd $SCRIPT_DIR
load_env() {
if [[ ! -f ./config.env ]]; then
return 1
fi
# Source the config file
source ./config.env
# Check if we have an old environment variable from the previous version of Lemmy-Easy-Deploy
known_old=("BUILD_FROM_SOURCE" "TLS_ENABLED" "LEMMY_NOREPLY_DISPLAY" "LEMMY_NOREPLY_FROM" "USE_EMAIL")
declare -a old_vars
for var in "${known_old[@]}"; do
if [[ -n "${!var}" ]]; then
old_vars+=("$var")
fi
done
# Check if we DON'T have a new environment variable from this version of Lemmy-Easy-Deploy
known_new=("SMTP_TLS_TYPE" "LEMMY_TLS_ENABLED" "SMTP_SERVER" "SMTP_PORT" "SMTP_NOREPLY_DISPLAY" "SMTP_NOREPLY_FROM" "ENABLE_POSTFIX" "ENABLE_EMAIL")
declare -a new_vars
for var in "${known_new[@]}"; do
if [[ -z "${!var}" ]]; then
new_vars+=("$var")
fi
done
# Check if we have old vars
# Ignore this warning if just running diagnostics
if [[ "${RUN_DIAG}" != "1" ]]; then
if [[ ${#old_vars[@]} -gt 0 ]]; then
echo
echo "-------------------------------------------------------------------------------------------"
echo "| !!! WARNING !!! WARNING !!! WARNING !!! WARNING !!! WARNING !!! WARNING !!! WARNING !!!"
echo "|"
echo "| You have variables from an old version of Lemmy-Easy-Deploy that are no longer used:"
# Loop over the array elements and print them line by line
for var in "${old_vars[@]}"; do
echo "| * $var"
done
echo "|"
echo "| Please update your config.env based on the latest config.env.example"
echo "|"
echo "| Here's how:"
echo "|"
echo "| 1. Make a backup of your settings:"
echo "| cp ./config.env ./config.bak"
echo "|"
echo "| 2. Make a new config file based on the template: "
echo "| cp ./config.env.example ./config.env"
echo "|"
echo "| 3. Manually edit config.env, and refer to config.bak for any old settings you had"
echo "|"
echo "| --> This deployment may have unexpected behavior until you do this! <--"
echo "|"
echo "-------------------------------------------------------------------------------------------"
if ! ask_user "Do you want to continue regardless?"; then
exit 0
fi
echo
fi
# Check if we are missing new vars
if [[ ${#new_vars[@]} -gt 0 ]]; then
echo
echo "-------------------------------------------------------------------------------------------"
echo "| !!! WARNING !!! WARNING !!! WARNING !!! WARNING !!! WARNING !!! WARNING !!! WARNING !!!"
echo "|"
echo "| You are missing variables that were introduced in an update to Lemmy-Easy-Deploy"
# Loop over the array elements and print them line by line
for var in "${new_vars[@]}"; do
echo "| * $var"
done
echo "|"
echo "| Please update your config.env based on the latest config.env.example"
echo "|"
echo "| Here's how:"
echo "|"
echo "| 1. Make a backup of your settings:"
echo "| cp ./config.env ./config.bak"
echo "|"
echo "| 2. Make a new config file based on the template: "
echo "| cp ./config.env.example ./config.env"
echo "|"
echo "| 3. Manually edit config.env, and refer to config.bak for any old settings you had"
echo "|"
echo "| --> This deployment may have unexpected behavior until you do this! <--"
echo "|"
echo "-------------------------------------------------------------------------------------------"
if ! ask_user "For these missing settings, the default values will be used. Do you want to continue?"; then
exit 0
fi
echo
fi
fi
# Make sure nothing is missing
# We omit SMTP_LOGIN and SMTP_PASSWORD to allow for anonymous logins
LEMMY_HOSTNAME="${LEMMY_HOSTNAME:-example.com}"
SETUP_SITE_NAME="${SETUP_SITE_NAME:-Lemmy}"
SETUP_ADMIN_USER="${SETUP_ADMIN_USER:-lemmy}"
CADDY_DISABLE_TLS="${CADDY_DISABLE_TLS:-false}"
CADDY_HTTP_PORT="${CADDY_HTTP_PORT:-80}"
CADDY_HTTPS_PORT="${CADDY_HTTPS_PORT:-443}"
LEMMY_TLS_ENABLED="${LEMMY_TLS_ENABLED:-true}"
ENABLE_EMAIL="${ENABLE_EMAIL:-false}"
SMTP_SERVER="${SMTP_SERVER:-postfix}"
SMTP_PORT="${SMTP_PORT:-25}"
SMTP_NOREPLY_DISPLAY="${SMTP_NOREPLY_DISPLAY:-Lemmy NoReply}"
SMTP_NOREPLY_FROM="${SMTP_NOREPLY_FROM:-noreply@${LEMMY_HOSTNAME}}"
SMTP_TLS_TYPE="${SMTP_TLS_TYPE:-none}"
ENABLE_POSTFIX="${ENABLE_POSTFIX:-false}"
POSTGRES_POOL_SIZE="${POSTGRES_POOL_SIZE:-5}"
}
# Check if the system's hostname is problematic or not
hostname_valid() {
if [[ -f /etc/hostname ]]; then
SYSTEM_HOSTNAME="$(cat /etc/hostname | sed -e 's| ||g' | tr -d '\n')"
if [[ "${SYSTEM_HOSTNAME}" == "${LEMMY_HOSTNAME}" ]]; then
return 1
fi
fi
return 0
}
diag_info() {
set +e
load_env
echo ""
echo "==== Docker Information ===="
detect_runtime
echo "==== System Information ===="
OS_FMT="$(uname -s)"
if [[ "${OS_FMT}" != "Linux" ]]; then
OS_FMT="${OS_FMT} (unsupported)"
fi
echo " OS: ${OS_FMT}"
echo " KERNEL: $(uname -r) ($(uname -m))"
HOSTNAME_FMT="OK"
if ! hostname_valid; then
HOSTNAME_FMT="BAD"
fi
echo "HOSTNAME: ${HOSTNAME_FMT}"
SHELL_FMT="$(ps -o pid,comm | grep $$ | rev | cut -d' ' -f 1 | rev)"
if [[ "$?" != "0" ]]; then
SHELL_FMT="$SHELL **(ps unavailable)"
fi
echo " SHELL: $(detect_shell)"
echo " MEMORY:"
if ! command -v free &>/dev/null; then
echo "*** 'free' command unavailable ***"
else
echo "$(free -h)"
fi
echo ""
echo "DISTRO:"
echo "----------------------------"
if [[ ! -f "/etc/os-release" ]]; then
echo "*** /etc/os-release not found ***"
else
cat /etc/os-release | grep --color=never NAME
fi
echo "----------------------------"
echo ""
echo "==== Lemmy-Easy-Deploy Information ===="
echo "Version: $LED_CURRENT_VERSION"
echo ""
docker ps --filter "name=lemmy-easy-deploy" --format "table {{.Image}}\t{{.RunningFor}}\t{{.Status}}"
echo ""
echo "Integrity:"
if ! command -v sha256sum &>/dev/null; then
echo " *** 'sha256sum' command unavailable"
else
echo " $(sha256sum $0)"
for f in ./templates/*; do
echo " $(sha256sum $f)"
done
fi
echo ""
echo "Custom Files: "
if [[ ! -d "./custom" ]] || [ -z "$(ls -A ./custom)" ]; then
echo "*** No custom files ***"
else
ls -lhn ./custom
fi
echo ""
echo "==== Settings ===="
if [[ ! -f "./config.env" ]]; then
echo "*** config.env not found ***"
else
if [[ -n "${CF_API_TOKEN}" ]]; then
USES_CLOUDFLARE="Yes"
else
USES_CLOUDFLARE="No"
fi
echo " CLOUDFLARE: ${USES_CLOUDFLARE}"
echo " CADDY_DISABLE_TLS: ${CADDY_DISABLE_TLS}"
echo " CADDY_HTTP_PORT: ${CADDY_HTTP_PORT}"
echo " CADDY_HTTPS_PORT: ${CADDY_HTTPS_PORT}"
echo " LEMMY_TLS_ENABLED: ${LEMMY_TLS_ENABLED}"
echo " ENABLE_EMAIL: ${ENABLE_EMAIL}"
echo " SMTP_PORT: ${SMTP_PORT}"
echo " ENABLE_POSTFIX: ${ENABLE_POSTFIX}"
echo "POSTGRES_POOL_SIZE: ${POSTGRES_POOL_SIZE}"
fi
echo ""
echo "==== Generated Files ===="
if [[ ! -d "./live" ]] || [ -z "$(ls -A ./live)" ]; then
echo "*** No files generated ***"
else
if [[ -f ./live/version ]]; then
DEPLOY_VERSION="$(cat ./live/version)"
else
DEPLOY_VERSION="(not deployed)"
fi
echo "Deploy Version: ${DEPLOY_VERSION}"
echo ""
ls -lhn ./live/
fi
echo ""
}
get_service_status() {
# Do this check in a loop in case Docker Compose is unreachable
# Can happen on slow systems
# If the stack ultimately cannot be contacted, show status as UNREACHABLE
# Run in a subshell where we cd to ./live first
# Some Docker distributions don't like only having the stack name and "need" to be in the same directory
(
cd ./live
loop_n=0
while [ $loop_n -lt 10 ]; do
unset CONTAINER_ID
unset SVC_STATUS
loop_n=$((loop_n + 1))
CONTAINER_ID="$($COMPOSE_CMD -p "lemmy-easy-deploy" ps -q $1)"
if [ $? -ne 0 ]; then
sleep 5
continue
fi
SVC_STATUS="$(echo $CONTAINER_ID | xargs docker inspect --format='{{ .State.Status }}')"
if [ $? -ne 0 ]; then
sleep 5
continue
fi
if [[ -z "${SVC_STATUS}" ]]; then
sleep 5
continue
fi
break
done
if [[ -z "${SVC_STATUS}" ]]; then
echo "UNREACHABLE"
else
echo "$SVC_STATUS"
fi
)
}
random_string() {
length=32
string=$(cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w "$length" | head -n 1)
echo "$string"
}
detect_runtime() {
# Check for docker or podman
for cmd in "podman" "docker"; do
if $cmd >/dev/null 2>&1; then
RUNTIME_CMD=$cmd
break
fi
done
if [[ -z "${RUNTIME_CMD}" ]]; then
echo >&2 "ERROR: Could not find a container runtime. Did you install Docker?"
echo >&2 "Please click on your server distribution in the list here, then follow the installation instructions:"
echo >&2 " https://docs.docker.com/engine/install/#server"
exit 1
fi
# Check for docker compose or podman compose
if [[ "${RUNTIME_CMD}" == "podman" ]]; then
echo "WARNING: podman will probably work, but I haven't tested it much. It's up to you to make sure all the permissions for podman are correct!"
COMPOSE_CMD="podman-compose"
if $COMPOSE_CMD >/dev/null 2>&1; then
COMPOSE_FOUND="true"
else
echo >&2 "ERROR: podman detected, but podman-compose is not installed. Please install podman-compose!"
exit 1
fi
else
for cmd in "docker compose" "docker-compose"; do
COMPOSE_CMD="${cmd}"
if $COMPOSE_CMD >/dev/null 2>&1; then
COMPOSE_FOUND="true"
break
fi
done
fi
if [[ "${COMPOSE_FOUND}" != "true" ]]; then
echo >&2 "ERROR: Could not find Docker Compose. Is Docker Compose installed?"
echo >&2 "Please click on your server distribution in the list here, then follow the installation instructions:"
echo >&2 " https://docs.docker.com/engine/install/#server"
exit 1
fi
# Grab the runtime versions:
DOCKER_VERSION="$($RUNTIME_CMD --version | head -n 1)"
DOCKER_MAJOR="$(echo ${DOCKER_VERSION#*version } | cut -d '.' -f 1 | tr -cd '[:digit:]')"
COMPOSE_VERSION="$($COMPOSE_CMD version | head -n 1)"
COMPOSE_MAJOR="$(echo ${COMPOSE_VERSION#*version } | cut -d '.' -f 1 | tr -cd '[:digit:]')"
echo "Detected runtime: $RUNTIME_CMD (${DOCKER_VERSION})"
echo "Detected compose: $COMPOSE_CMD (${COMPOSE_VERSION})"
RUNTIME_STATE="ERROR"
if docker run --rm -v "$(pwd):/host:ro" hello-world >/dev/null 2>&1; then
RUNTIME_STATE="OK"
fi
echo " Runtime state: $RUNTIME_STATE"
echo ""
# Warn if using an unsupported Docker version
if ((DOCKER_MAJOR < 20)); then
echo "-----------------------------------------------------------------------"
echo "WARNING: Your version of Docker is outdated and unsupported."
echo ""
echo "Only Docker Engine versions 20 and up are supported by Docker Inc:"
echo " https://endoflife.date/docker-engine"
echo ""
echo "The deployment will likely work regardless, but if you run into issues,"
echo "please install the official version of Docker before filing an issue:"
echo " https://docs.docker.com/engine/install/"
echo ""
echo "-----------------------------------------------------------------------"
fi
# Warn if using an unsupported Compose version
if ((COMPOSE_MAJOR < 2)); then
echo "-----------------------------------------------------------------------"
echo "WARNING: Your version of Docker Compose is outdated and unsupported."
echo ""
echo "Docker Compose v2 has been Generally Available (GA) for over 1 year,"
echo "and as of June 2023, Docker Compose v1 has been officially deprecated"
echo "by Docker Inc."
echo ""
echo "https://www.docker.com/blog/new-docker-compose-v2-and-v1-deprecation/"
echo ""
echo "Popular Linux distributions, such as Debian and Ubuntu, are still distributing"
echo "outdated and unofficial packages of Docker and Docker Compose."
echo ""
echo "However, those packages are neither supported nor endorsed by Docker Inc."
echo ""
echo "Lemmy-Easy-Deploy might still work regardless, but testing is only done"
echo "with Docker Compose v2. Compose v1 is not supported."
echo ""
echo "For the best experience, please install the official version of Docker:"
echo " https://docs.docker.com/engine/install/"
echo ""
echo "-----------------------------------------------------------------------"
fi
}
display_help() {
echo "Usage:"
echo " $0 [options]"
echo ""
echo "Run with no options to check for Lemmy updates and deploy them, and/or restart a stopped deployment."
echo ""
echo "Options:"
echo " -s|--shutdown Shut down a running Lemmy-Easy-Deploy deployment (does not delete data)"
echo " -l|--lemmy-tag <tag> Install a specific version of the Lemmy Backend"
echo " -w|--webui-tag <tag> Install a specific version of the Lemmy WebUI (will use value from --lemmy-tag if missing)"
echo " -f|--force-deploy Skip the update checks and force (re)deploy the latest/specified version (must use this for rc versions!)"
echo " -r|--rebuild Deploy from source, don't update the Git repos, and deploy them as-is, implies -f and ignores -l/-w"
echo " -y|--yes Answer Yes to any prompts asking for confirmation"
echo " -v|--version Prints the current version of Lemmy-Easy-Deploy"
echo " -u|--update Update Lemmy-Easy-Deploy"
echo " -d|--diag Dump diagnostic information for issue reporting, then exit"
echo " -h|--help Show this help message"
exit 1
}
print_version() {
echo ${LED_CURRENT_VERSION:?}
}
self_update() {
# Check for LED updates
LED_UPDATE_CHECK="$(latest_github_tag ubergeek77/Lemmy-Easy-Deploy)"
# Make sure both strings are trackable
if ! is_version_string "${LED_CURRENT_VERSION}" || ! is_version_string "${LED_UPDATE_CHECK}"; then
echo "ERROR: Could not determine upgrade path for ${LED_CURRENT_VERSION} --> ${LED_UPDATE_CHECK}"
exit 1
fi
# Check if this version is newer
if [[ "$(compare_versions ${LED_CURRENT_VERSION} ${LED_UPDATE_CHECK})" != "1" ]]; then
echo "No update available."
exit 0
else
if [[ "${UPDATE_FROM_PROMPT}" != "1" ]]; then
echo ""
echo "--> Update available! (${LED_UPDATE_CHECK})"
fi
fi
if [[ ! -d "./.git" ]]; then
echo >&2 "ERROR: The local .git folder for Lemmy-Easy-Deploy was not found."
echo >&2 "Self-updates are only available if you cloned this repo with git clone:"
echo >&2 " git clone https://github.com/ubergeek77/Lemmy-Easy-Deploy"
exit 1
fi
print_update_error() {
echo >&2 "ERROR: Update failed. Have you modified Lemmy-Easy-Deploy?"
echo >&2 "You can try to reset Lemmy-Easy-Deploy manually, but you will lose any changes you made!"
echo >&2 "Back up any foreign files you may have in this directory, then run these commands:"
echo >&2 " git reset --hard"
echo >&2 " $0 --update"
echo >&2 "If you did not do anything special with your installation, and are confused by this message, please report this:"
echo >&2 " https://github.com/ubergeek77/Lemmy-Easy-Deploy/issues"
}
echo "--> Installing Lemmy-Easy-Deploy ${LED_UPDATE_CHECK}..."
echo "-----------------------------------------------------------"
if ! git checkout main; then
print_update_error
exit 1
fi
if ! git pull; then
print_update_error
exit 1
fi
if ! git fetch --tags --force; then
print_update_error
exit 1
fi
if ! git checkout ${LED_UPDATE_CHECK}; then
print_update_error
exit 1
fi
echo "-----------------------------------------------------------"
echo ""
echo "Update complete! Version ${LED_UPDATE_CHECK} installed."
echo ""
exit 0
}
# Validate if an input is in 0.0.0 format
is_version_string() {
[[ $1 =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]
}
compare_versions() {
IFS='.' read -ra fields <<<"$1"
_major=${fields[0]}
_minor=${fields[1]}
_micro=${fields[2]}
LEFT_VERSION_NUMERIC=$((_major * 10000 + _minor * 1000 + _micro))
IFS='.' read -ra fields <<<"$2"
_major=${fields[0]}
_minor=${fields[1]}
_micro=${fields[2]}
RIGHT_VERSION_NUMERIC=$((_major * 10000 + _minor * 1000 + _micro))
if ((LEFT_VERSION_NUMERIC < RIGHT_VERSION_NUMERIC)); then
echo "1"
else
echo "0"
fi
}
latest_github_tag() {
# Use a github token if supplied
unset CURL_ARGS
if [[ -n "${GITHUB_TOKEN}" ]]; then
CURL_ARGS="-H \"Authorization: Bearer ${GITHUB_TOKEN}\""
fi
ratelimit_error='"message":"API rate limit exceeded for'
RESPONSE="$(curl -s ${CURL_ARGS} https://api.github.com/repos/$1/releases/latest)"
if [[ "${RESPONSE,,}" == *"${ratelimit_error,,}"* ]]; then
echo >&2 ""
echo >&2 "---------------------------------------------------------------------"
echo >&2 "ERROR: GitHub API Rate Limit exceeded. Cannot check latest tag for $1"
echo >&2 ""
echo >&2 "Please do not report this as an issue. If you are on a cloud server,"
echo >&2 "a VM neighbor likely exhausted the rate limit. Please try again later."
echo >&2 "---------------------------------------------------------------------"
echo >&2 ""
exit 1
fi
RESULT=$(echo "${RESPONSE}" | grep '"tag_name":' | sed -E 's/.*"([^"]+)".*/\1/')
# If no result, check the latest tag that doesn't contain the words beta, alpha, or rc
if [[ -z "${RESULT}" ]]; then
RESPONSE="$(curl -s ${CURL_ARGS} https://api.github.com/repos/$1/git/refs/tags)"
while IFS= read -r line; do
if [[ "${line,,}" != *beta* && "${line,,}" != *alpha* && "${line,,}" != *rc* ]]; then
RESULT="$(echo ${line} | cut -d'/' -f3 | tr -d '",')"
break
fi
done <<<$(echo "${RESPONSE}" | grep '"ref":' | tac)
fi
# If still no result, then it probably doesn't exist
if [[ -z "${RESULT}" ]]; then
echo >&2 ""
echo >&2 "---------------------------------------------------------------------"
echo >&2 "ERROR: No tags found for $1"
echo >&2 ""
echo >&2 "Did the repo move?"
echo >&2 "---------------------------------------------------------------------"
echo >&2 ""
exit 1
fi
echo "${RESULT}"
}
ask_user() {
prompt="${1} [Y/n] "
# Always answer yes if the user specified -y
if [ "${ANSWER_YES}" = "1" ]; then
echo "$prompt Y"
return 0
fi
while true; do
printf "%s" "$prompt"
read answer
case "$(echo "$answer" | tr '[:upper:]' '[:lower:]')" in
y | yes | "")
return 0
;;
n | no)
return 1
;;
*)
echo ""
echo "Invalid input. Please enter Y for yes or N for no."
;;
esac
done
}
check_image_arch() {
# If this is an unsupported version of Docker, we can't check the normal way
# Docker versions <24 do not support checking images that have attestation tags
if ((DOCKER_MAJOR < 24)); then
echo "WARNING: Unsupported Docker version; pulling full image first"
if docker pull "$1" >/dev/null 2>&1; then
return 0
else
return 1
fi
fi
# Detect the current docker architecture
if [[ -z "${DOCKER_ARCH}" ]]; then
export DOCKER_ARCH="$(docker version --format '{{.Server.Arch}}')"
export SEARCH_ARCH="${DOCKER_ARCH}"
fi
# Determine if imagetools is available
if [[ -z "${IMAGETOOLS_AVAILABLE}" ]]; then
if docker buildx imagetools >/dev/null 2>&1; then
export IMAGETOOLS_AVAILABLE=1
else
export IMAGETOOLS_AVAILABLE=0
fi
fi
# Determine how to inspect the manifest
if [[ "${IMAGETOOLS_AVAILABLE}" == "1" ]]; then
# If the arch is just arm, search for arm/v7
if [[ "${DOCKER_ARCH}" == "arm" ]]; then
SEARCH_ARCH="arm/v7"
fi
INSPECT_CMD="docker buildx imagetools"
INSPECT_MATCH="Platform: linux/${SEARCH_ARCH}$"
else
INSPECT_CMD="docker manifest"
INSPECT_MATCH="\"architecture\": \"${DOCKER_ARCH}\",$"
fi
# Get the manifest info
MANIFEST=$($INSPECT_CMD inspect "$1" 2>&1)
# Handle non existent images
if echo "$MANIFEST" | grep -iEq 'failed|unauthorized|manifest unknown|no such manifest|not found|error'; then
return 1
fi
# Handle single-arch images
if ! echo "$MANIFEST" | grep -Eq 'Platform|"architecture"'; then
echo "! No reported architecture for $1; assuming linux/amd64"
if [[ "${DOCKER_ARCH}" == "amd64" ]]; then
return 0
else
return 1
fi
fi
# Search for this system's architecture
if echo "$MANIFEST" | grep -q "${INSPECT_MATCH}"; then
return 0
else
return 1
fi
}
# Shut down a deployment
shutdown_deployment() {
cd ./live
$COMPOSE_CMD -p "lemmy-easy-deploy" down
}
# Detect if the user is using any custom files, copy them if needed,
# and modify the docker-compose.yml to use them
install_custom_env() {
if [[ -f ./custom/customCaddy.env ]]; then
echo "--> Found customCaddy.env; passing custom environment variables to 'proxy'"
sed -i -e 's|{{ CADDY_EXTRA_ENV }}|./customCaddy.env|g' ./live/docker-compose.yml
cp ./custom/customCaddy.env ./live
else
sed -i '/{{ CADDY_EXTRA_ENV }}/d' ./live/docker-compose.yml
fi
if [[ -f ./custom/customLemmy.env ]]; then
echo "--> Found customLemmy.env; passing custom environment variables to 'lemmy'"
sed -i -e 's|{{ LEMMY_EXTRA_ENV }}|./customLemmy.env|g' ./live/docker-compose.yml
cp ./custom/customLemmy.env ./live
# Warn the user if they have changed CADDY_HTTP_PORT or CADDY_HTTPS_PORT but have not set LEMMY_CORS_ORIGIN
# Test in a subshell to not mess with any environment variables
(
source ./custom/customLemmy.env
if [[ -z "${LEMMY_CORS_ORIGIN}" ]]; then
if [[ "${CADDY_HTTP_PORT}" != "80" ]] || [[ "${CADDY_HTTPS_PORT}" != "443" ]]; then
echo ""
echo "----------------------------------------------------------------------------------------------------"
echo "WARNING: You have changed one or more ports used by Caddy, but have not specified LEMMY_CORS_ORIGIN"
echo "This may result in your instance throwing errors and becoming unusable."
echo ""
echo "To fix this, create the file './custom/customLemmy.env', and put the following inside of it:"
echo " LEMMY_CORS_ORIGIN=http://<your-domain>:<custom-port>"
echo ""
echo "Change the protocol, domain, and port as needed."
echo "----------------------------------------------------------------------------------------------------"
echo ""
fi
fi
)
else
sed -i '/{{ LEMMY_EXTRA_ENV }}/d' ./live/docker-compose.yml
fi
if [[ -f ./custom/customLemmy-ui.env ]]; then
echo "--> Found customLemmy-ui.env; passing custom environment variables to 'lemmy-ui'"
sed -i -e 's|{{ LEMMY_UI_EXTRA_ENV }}|./customLemmy-ui.env|g' ./live/docker-compose.yml
cp ./custom/customLemmy-ui.env ./live
else
sed -i '/{{ LEMMY_UI_EXTRA_ENV }}/d' ./live/docker-compose.yml
fi
if [[ -f ./custom/customPictrs.env ]]; then
echo "--> Found customPictrs.env; passing custom environment variables to 'pictrs'"
sed -i -e 's|{{ PICTRS_EXTRA_ENV }}|./customPictrs.env|g' ./live/docker-compose.yml
cp ./custom/customPictrs.env ./live
else
sed -i '/{{ PICTRS_EXTRA_ENV }}/d' ./live/docker-compose.yml
fi
if [[ -f ./custom/customPostgres.env ]]; then
echo "--> Found customPostgres.env; passing custom environment variables to 'postgres'"
sed -i -e 's|{{ POSTGRES_EXTRA_ENV }}|./customPostgres.env|g' ./live/docker-compose.yml
cp ./custom/customPostgres.env ./live
else
sed -i '/{{ POSTGRES_EXTRA_ENV }}/d' ./live/docker-compose.yml
fi
if [[ -f ./custom/customPostfix.env ]]; then
echo "--> Found customPostfix.env; passing custom environment variables to 'postfix'"
sed -i -e 's|{{ POSTFIX_EXTRA_ENV }}|./customPostfix.env|g' ./live/docker-compose.yml
cp ./custom/customPostfix.env ./live
else
sed -i '/{{ POSTFIX_EXTRA_ENV }}/d' ./live/docker-compose.yml
fi
if [[ -f ./custom/customPostgresql.conf ]]; then
echo "--> Found customPostgresql.conf; overriding default 'postgresql.conf'"
sed -i -e 's|{{ POSTGRES_CONF }}|./customPostgresql.conf:/var/lib/postgresql/data/postgresql.conf|g' ./live/docker-compose.yml
cp ./custom/customPostgresql.conf ./live
else
sed -i '/{{ POSTGRES_CONF }}/d' ./live/docker-compose.yml
fi
}
# Detect the current shell
detect_shell() {
# Get the current shell
# If for some reason ps fails, we can make an educated guess on $SHELL
DETECTED_SHELL=$(ps -o pid,comm | grep $$ | rev | cut -d' ' -f 1 | rev)
if [ "$?" != "0" ]; then
DETECTED_SHELL="$SHELL"
fi
echo "${DETECTED_SHELL}"
}
# Do compatibility checks
check_compatibility() {
DETECTED_SHELL=$(detect_shell)
# Make sure the user is using bash
case "${DETECTED_SHELL}" in
*bash) ;;
*)
echo ""
echo "|--------------------------------------------------------------------------------------------------"
echo "| !!! WARNING !!! WARNING !!! WARNING !!! WARNING !!! WARNING !!! !!! WARNING !!! WARNING !!!"
echo "|"
echo "| This shell is not Bash. Lemmy-Easy-Deploy is a Bash script that uses features of Bash."
echo "|"
echo "| The current shell has been detected to be: '${DETECTED_SHELL}'"
echo "|"
echo "| If you continue, this script is very likely to break, as it is not compatible with other shells."
echo "|"
echo "| You have the option to proceed anyway, but this could create a broken deployment, or the script"
echo "| could behave in unexpected ways. You have been warned."
echo "|--------------------------------------------------------------------------------------------------"
echo ""
if ! ask_user "Proceed with this unsupported shell?"; then
exit 0
fi
;;
esac
# Check for non-Linux systems (i.e. macos)
DETECTED_OS="$(uname -s)"
if [[ "${DETECTED_OS,,}" != "linux" ]]; then
echo ""
echo "|--------------------------------------------------------------------------------------------------|"
echo "| !!! WARNING !!! WARNING !!! WARNING !!! WARNING !!! WARNING !!! !!! WARNING !!! WARNING !!! |"
echo "| |"
echo "| This system is not a Linux system. You appear to have a \"${DETECTED_OS}\" system. |"
echo "| |"
echo "| Lemmy-Easy-Deploy is intended for use on Linux systems only, and the images it deploys are built |"
echo "| specifically for Linux platforms. |"
echo "| |"
echo "| Unfortunately, compatibility on other systems, especially macOS (Darwin), cannot be guaranteed. |"
echo "| |"
echo "| You have the option to proceed anyway, but this could create a broken deployment, the script |"
echo "| could behave in unexpected ways, or the deployment might not start at all. You have been warned. |"
echo "|--------------------------------------------------------------------------------------------------|"
echo ""
if ! ask_user "Proceed on this unsupported OS?"; then
exit 0
fi
fi
# Check for binaries we absolutely need
REQUIRED_CMDS=("cat" "curl" "sed" "grep" "tr" "cp")
for c in "${REQUIRED_CMDS[@]}"; do
if ! command -v "$c" >/dev/null 2>&1; then
echo >&2 "------------------------------------------------------"
echo >&2 "FATAL ERROR: This system does not have the $c command."
echo >&2 "This script cannot proceed without it. "
echo >&2 "------------------------------------------------------"
exit 1
fi
done
}
# Exit on error
set -e
# parse arguments
while (("$#")); do
case "$1" in
-s | --shutdown)
RUN_SHUTDOWN=1
shift 1
;;
-l | --lemmy-tag)
if [ -n "$2" ] && [ ${2:0:1} != "-" ]; then
BACKEND_TAG_OVERRIDE="$2"
# Let the user specify a git tag to build from source manually
if [[ "${BACKEND_TAG_OVERRIDE,,}" == git:* ]]; then
BACKEND_TAG_OVERRIDE="${BACKEND_TAG_OVERRIDE#"git:"}"
BUILD_BACKEND=1
fi
shift 2
else
echo >&2 "ERROR: Argument for $1 is missing"
exit 1
fi
;;
-w | --webui-tag)
if [ -n "$2" ] && [ ${2:0:1} != "-" ]; then
FRONTEND_TAG_OVERRIDE="$2"
# Let the user specify a git tag to build from source manually
if [[ "${FRONTEND_TAG_OVERRIDE,,}" == git:* ]]; then
FRONTEND_TAG_OVERRIDE="${FRONTEND_TAG_OVERRIDE#"git:"}"
BUILD_FRONTEND=1
fi
shift 2
else
echo >&2 "ERROR: Argument for $1 is missing"
exit 1
fi
;;
-f | --force-deploy)
FORCE_DEPLOY=1
echo
echo "WARNING: Force deploying; this will regenerate configs and deploy again even if there were no updates"
echo "Passwords will NOT be re-generated"
echo
shift 1
;;
-r | --rebuild)
REBUILD_SOURCE=1
FORCE_DEPLOY=1
BUILD_BACKEND=1
BUILD_FRONTEND=1
shift 1
;;
-y | --yes)
ANSWER_YES=1
shift 1
;;
-v | --version)
RUN_PRINT_VERSION=1
shift 1
;;
-u | --update)
RUN_SELF_UPDATE=1
shift 1
;;
-d | --diag)
RUN_DIAG=1
shift 1
;;
-h | --help)
DISPLAY_HELP=1
shift 1
;;
*)
echo >&2 "Unrecognized arguments: $@"
echo
display_help
exit 1
;;
esac
done
# Do what the user wanted after parsing arguments, so order doesn't matter
if [[ "${DISPLAY_HELP}" == "1" ]]; then
display_help
exit 0
fi
if [[ "${RUN_PRINT_VERSION}" == "1" ]]; then
print_version
exit 0
fi
if [[ "${RUN_SHUTDOWN}" == "1" ]]; then
detect_runtime
shutdown_deployment
exit 0
fi
if [[ "${RUN_DIAG}" == "1" ]]; then
diag_info
exit 0
fi
if [[ "${RUN_SELF_UPDATE}" == "1" ]]; then
self_update
exit 0
fi
# Check the current system for compatibility
check_compatibility
# Check for LED updates
LED_UPDATE_CHECK="$(latest_github_tag ubergeek77/Lemmy-Easy-Deploy)"
# Check if this version is newer
if [[ "$(compare_versions ${LED_CURRENT_VERSION} ${LED_UPDATE_CHECK})" == "1" ]]; then
echo
echo "===================================================="
echo "| A Lemmy-Easy-Deploy update is available! |"
echo "| ${LED_CURRENT_VERSION} --> ${LED_UPDATE_CHECK} |"
echo "==================================================="
echo
# Exclude update from unattended yes answers
if [[ "${ANSWER_YES}" != "1" ]]; then
if ask_user "Would you like to cancel the current operation and install the update now?"; then
UPDATE_FROM_PROMPT=1
self_update
exit 0
fi
fi
fi
# Warn user if they are using --rebuild incorrectly
if [[ "${REBUILD_SOURCE}" == "1" ]] && [[ -n "${BACKEND_TAG_OVERRIDE}" ]]; then
echo
echo "WARNING: --rebuild specified, but a --lemmy-tag override has been provided (${BACKEND_TAG_OVERRIDE})"
echo "If the sources do not already exist, this version will be checked out, but it will be ignored otherwise"
echo
fi
if [[ "${REBUILD_SOURCE}" == "1" ]] && [[ -n "${FRONTEND_TAG_OVERRIDE}" ]]; then
echo
echo "WARNING: --rebuild specified, but a --webui-tag override has been provided (${FRONTEND_TAG_OVERRIDE})"
echo "If the sources do not already exist, this version will be checked out, but it will be ignored otherwise"
echo
fi
# If a frontend override wasn't specified, but a backend one was, match the versions
if [[ -z "${FRONTEND_TAG_OVERRIDE}" ]] && [[ -n "${BACKEND_TAG_OVERRIDE}" ]]; then
FRONTEND_TAG_OVERRIDE="${BACKEND_TAG_OVERRIDE}"
BUILD_FRONTEND="${BUILD_BACKEND}"
fi
echo "========================================"
echo "Lemmy-Easy-Deploy by ubergeek77 (v${LED_CURRENT_VERSION})"
echo "========================================"
echo ""
detect_runtime
# If the runtime state is bad, we can't continue
if [[ "${RUNTIME_STATE}" != "OK" ]]; then
echo >&2 "----------------------------------------------------------------------------------------"
echo >&2 "ERROR: Failed to run Docker."
echo >&2 ""
echo >&2 "Something is wrong with your Docker installation."
echo >&2 ""
echo >&2 "Possible fixes:"
echo >&2 " * Add your user to the 'docker' group (recommended)"
echo >&2 " * Reboot or re-log after adding your user to the 'docker' group"
echo >&2 " * Run this script with sudo (try the 'docker' group method first)"
echo >&2 " * Docker is not running or is not enabled (systemctl enable docker && systemctl start docker) "
echo >&2 " * Reboot your machine after installing Docker for the first time"
echo >&2 ""
echo >&2 "Please ensure you can run the following command on your own without errors:"
echo >&2 " docker run --rm -v "\$\(pwd\):/host:ro" hello-world"
echo >&2 ""
echo >&2 "If you see any errors while running that command, please Google the error messages"
echo >&2 "to see if any of the solutions work for you. Once Docker is functional on your system,"
echo >&2 "you can try running Lemmy-Easy-Deploy again."
echo >&2 "----------------------------------------------------------------------------------------"
echo >&2 ""
exit 1
fi
# Yell at the user if they didn't follow instructions
if [[ ! -f "./config.env" ]]; then
echo >&2 "ERROR: ./config.env not found! Did you copy the example config?"
echo " Try: cp ./config.env.example ./config.env"
exit 1
fi
load_env
# Yell at the user if they didn't follow instructions, again
if [[ -z "$LEMMY_HOSTNAME" ]] || [[ "$LEMMY_HOSTNAME" == "example.com" ]]; then
echo >&2 "ERROR: You did not set your hostname in config.env! Do it like this:"
echo >&2 "LEMMY_HOSTNAME=example.com"
exit 1
fi
if [[ $LEMMY_HOSTNAME =~ ^https?: ]]; then
echo >&2 "ERROR: Don't put http/https in config.env! Do it like this:"
echo >&2 "LEMMY_HOSTNAME=example.com"
exit 1
fi
# Check for config oddities
# If the hostname matches the Lemmy hostname, there will be problems