...
1#!/bin/sh
2set -eu
3
4# *************
5# DO NOT EDIT
6#
7# install.sh was bundled together from
8#
9# - ./ci/sub/lib/rand.sh
10# - ./ci/sub/lib/temp.sh
11# - ./ci/sub/lib/log.sh
12# - ./ci/sub/lib/flag.sh
13# - ./ci/sub/lib/release.sh
14# - ./ci/release/_install.sh
15#
16# The last of which implements the installation logic.
17#
18# Generated by ./ci/release/gen_install.sh.
19# *************
20
21#!/bin/sh
22if [ "${LIB_RAND-}" ]; then
23 return 0
24fi
25LIB_RAND=1
26
27pick() {
28 seed="$1"
29 shift
30
31 seed_file="$(mktempd)/pickseed"
32
33 # We add 32 more bytes to the seed file for sufficient entropy. Otherwise both Cygwin's
34 # and MinGW's sort for example complains about the lack of entropy on stderr and writes
35 # nothing to stdout. I'm sure there are more platforms that would too.
36 #
37 # We also limit to a max of 32 bytes as otherwise macOS's sort complains that the random
38 # seed is too large. Probably more platforms too.
39 (echo "$seed" && echo "================================") | head -c32 >"$seed_file"
40
41 while [ $# -gt 0 ]; do
42 echo "$1"
43 shift
44 done \
45 | sort --sort=random --random-source="$seed_file" \
46 | head -n1
47}
48#!/bin/sh
49if [ "${LIB_TEMP-}" ]; then
50 return 0
51fi
52LIB_TEMP=1
53
54ensure_tmpdir() {
55 if [ -n "${_TMPDIR-}" ]; then
56 return
57 fi
58 _TMPDIR=$(mktemp -d)
59 export _TMPDIR
60}
61
62if [ -z "${_TMPDIR-}" ]; then
63 trap 'rm -Rf "$_TMPDIR"' EXIT
64fi
65ensure_tmpdir
66
67temppath() {
68 while true; do
69 temppath=$_TMPDIR/$(</dev/urandom od -N8 -tx -An -v | tr -d '[:space:]')
70 if [ ! -e "$temppath" ]; then
71 echo "$temppath"
72 return
73 fi
74 done
75}
76
77mktempd() {
78 tp=$(temppath)
79 mkdir -p "$tp"
80 echo "$tp"
81}
82#!/bin/sh
83if [ "${LIB_LOG-}" ]; then
84 return 0
85fi
86LIB_LOG=1
87
88if [ -n "${TRACE-}" ]; then
89 set -x
90fi
91
92tput() {
93 if should_color; then
94 TERM=${TERM:-xterm-256color} command tput "$@"
95 fi
96}
97
98should_color() {
99 if [ -n "${COLOR-}" ]; then
100 if [ "$COLOR" = 1 -o "$COLOR" = true ]; then
101 _COLOR=1
102 __COLOR=1
103 return 0
104 elif [ "$COLOR" = 0 -o "$COLOR" = false ]; then
105 _COLOR=
106 __COLOR=0
107 return 1
108 else
109 printf '$COLOR must be 0, 1, false or true but got %s\n' "$COLOR" >&2
110 fi
111 fi
112
113 if [ -t 1 -a "${TERM-}" != dumb ]; then
114 _COLOR=1
115 __COLOR=1
116 return 0
117 else
118 _COLOR=
119 __COLOR=0
120 return 1
121 fi
122}
123
124setaf() {
125 fg=$1
126 shift
127 printf '%s\n' "$*" | while IFS= read -r line; do
128 tput setaf "$fg"
129 printf '%s' "$line"
130 tput sgr0
131 printf '\n'
132 done
133}
134
135_echo() {
136 printf '%s\n' "$*"
137}
138
139get_rand_color() {
140 if [ "${TERM_COLORS+x}" != x ]; then
141 TERM_COLORS=""
142 export TERM_COLORS
143 ncolors=$(TERM=${TERM:-xterm-256color} command tput colors)
144 if [ "$ncolors" -ge 8 ]; then
145 # 1-6 are regular
146 TERM_COLORS="$TERM_COLORS 1 2 3 4 5 6"
147 elif [ "$ncolors" -ge 16 ]; then
148 # 9-14 are bright.
149 TERM_COLORS="$TERM_COLORS 9 10 11 12 13 14"
150 fi
151 fi
152 pick "$*" $TERM_COLORS
153}
154
155echop() {
156 prefix="$1"
157 shift
158
159 if [ "$#" -gt 0 ]; then
160 printfp "$prefix" "%s\n" "$*"
161 else
162 printfp "$prefix"
163 printf '\n'
164 fi
165}
166
167printfp() {(
168 prefix="$1"
169 shift
170
171 _FGCOLOR=${FGCOLOR:-$(get_rand_color "$prefix")}
172 should_color || true
173 if [ $# -eq 0 ]; then
174 printf '%s' "$(COLOR=$__COLOR setaf "$_FGCOLOR" "$prefix")"
175 else
176 printf '%s: %s\n' "$(COLOR=$__COLOR setaf "$_FGCOLOR" "$prefix")" "$(printf "$@")"
177 fi
178)}
179
180catp() {
181 prefix="$1"
182 shift
183
184 should_color || true
185 sed "s/^/$(COLOR=$__COLOR printfp "$prefix" '')/"
186}
187
188repeat() {
189 char="$1"
190 times="$2"
191 seq -s "$char" "$times" | tr -d '[:digit:]'
192}
193
194strlen() {
195 printf %s "$1" | wc -c
196}
197
198echoerr() {
199 FGCOLOR=1 logp err "$*"
200}
201
202caterr() {
203 FGCOLOR=1 logpcat err "$@"
204}
205
206printferr() {
207 FGCOLOR=1 logfp err "$@"
208}
209
210logp() {
211 should_color >&2 || true
212 COLOR=$__COLOR echop "$@" | humanpath >&2
213}
214
215logfp() {
216 should_color >&2 || true
217 COLOR=$__COLOR printfp "$@" | humanpath >&2
218}
219
220logpcat() {
221 should_color >&2 || true
222 COLOR=$__COLOR catp "$@" | humanpath >&2
223}
224
225log() {
226 FGCOLOR=5 logp log "$@"
227}
228
229logf() {
230 FGCOLOR=5 logfp log "$@"
231}
232
233logcat() {
234 FGCOLOR=5 logpcat log "$@"
235}
236
237warn() {
238 FGCOLOR=3 logp warn "$@"
239}
240
241warnf() {
242 FGCOLOR=3 logfp warn "$@"
243}
244
245warncat() {
246 FGCOLOR=3 logpcat warn "$@"
247}
248
249sh_c() {
250 FGCOLOR=3 logp exec "$*"
251 if [ -z "${DRY_RUN-}" ]; then
252 eval "$@"
253 fi
254}
255
256sudo_sh_c() {
257 if [ "$(id -u)" -eq 0 ]; then
258 sh_c "$@"
259 elif command -v doas >/dev/null; then
260 sh_c "doas $*"
261 elif command -v sudo >/dev/null; then
262 sh_c "sudo $*"
263 elif command -v su >/dev/null; then
264 sh_c "su root -c '$*'"
265 else
266 caterr <<EOF
267Unable to run the following command as root:
268 $*
269Please install doas, sudo, or su.
270EOF
271 return 1
272 fi
273}
274
275header() {
276 FGCOLOR=${FGCOLOR:-4} logp "/* $1 */"
277}
278
279bigheader() {
280 set -- "$(echo "$*" | sed "s/^/ * /")"
281 FGCOLOR=${FGCOLOR:-6} logp "/****************************************************************
282$*
283 ****************************************************************/"
284}
285
286# humanpath replaces all occurrences of " $HOME" with " ~"
287# and all occurrences of '$HOME' with the literal '$HOME'.
288humanpath() {
289 if [ -z "${HOME-}" ]; then
290 cat
291 else
292 sed -e "s# $HOME# ~#g" -e "s#$HOME#\$HOME#g"
293 fi
294}
295
296hide() {
297 out="$(mktempd)/hideout"
298 capcode "$@" >"$out" 2>&1
299 if [ "$code" -eq 0 ]; then
300 return
301 fi
302 cat "$out" >&2
303 return "$code"
304}
305
306hide_stderr() {
307 out="$(mktempd)/hideout"
308 capcode "$@" 2>"$out"
309 if [ "$code" -eq 0 ]; then
310 return
311 fi
312 cat "$out" >&2
313 return "$code"
314}
315
316echo_dur() {
317 local dur=$1
318 local h=$((dur/60/60))
319 local m=$((dur/60%60))
320 local s=$((dur%60))
321 printf '%dh%dm%ds' "$h" "$m" "$s"
322}
323
324sponge() {
325 dst="$1"
326 tmp="$(mktempd)/sponge"
327 cat > "$tmp"
328 cat "$tmp" > "$dst"
329}
330
331stripansi() {
332 # First regex gets rid of standard xterm escape sequences for controlling
333 # visual attributes.
334 # The second regex I'm not 100% sure, the reference says it selects the US
335 # encoding but I'm not sure why that's necessary or why it always occurs
336 # in tput sgr0 before the standard escape sequence.
337 # See tput sgr0 | xxd
338 sed -e $'s/\x1b\[[0-9;]*m//g' -e $'s/\x1b(.//g'
339}
340
341runtty() {
342 case "$(uname)" in
343 Darwin)
344 script -q /dev/null "$@"
345 ;;
346 Linux)
347 script -eqc "$*"
348 ;;
349 *)
350 echoerr "runtty: unsupported OS $(uname)"
351 return 1
352 esac
353}
354
355capcode() {
356 set +e
357 "$@"
358 code=$?
359 set -e
360}
361
362strjoin() {
363 (IFS="$1"; shift; echo "$*")
364}
365#!/bin/sh
366if [ "${LIB_FLAG-}" ]; then
367 return 0
368fi
369LIB_FLAG=1
370
371# flag_parse implements a robust flag parser.
372#
373# For a full fledge example see ../examples/date.sh
374#
375# It differs from getopts(1) in that long form options are supported. Currently the only
376# deficiency is that short combined options are not supported like -xyzq. That would be
377# interpreted as a single -xyzq flag. The other deficiency is lack of support for short
378# flag syntax like -carg where the arg is not separated from the flag. This one is
379# unfixable I believe unfortunately but for combined short flags I have opened
380# https://github.com/terrastruct/ci/issues/6
381#
382# flag_parse stores state in $FLAG, $FLAGRAW, $FLAGARG and $FLAGSHIFT.
383# FLAG contains the name of the flag without hyphens.
384# FLAGRAW contains the name of the flag as passed in with hyphens.
385# FLAGARG contains the argument for the flag if there was any.
386# If there was none, it will not be set.
387# FLAGSHIFT contains the number by which the arguments should be shifted to
388# start at the next flag/argument
389#
390# flag_parse exits with a non zero code when there are no more flags
391# to be parsed. Still, call shift "$FLAGSHIFT" in case there was a --
392#
393# If the argument for the flag is optional, then use ${FLAGARG-} to access
394# the argument if one was passed. Use ${FLAGARG+x} = x to check if it was set.
395# You only need to explicitly check if the flag was set if you care whether the user
396# explicitly passed the empty string as the argument.
397#
398# Otherwise, call one of the flag_*arg functions:
399#
400# If a flag requires an argument, call flag_reqarg
401# - $FLAGARG is guaranteed to be set after.
402# If a flag requires a non empty argument, call flag_nonemptyarg
403# - $FLAGARG is guaranteed to be set to a non empty string after.
404# If a flag should not be passed an argument, call flag_noarg
405# - $FLAGARG is guaranteed to be unset after.
406#
407# And then shift "$FLAGSHIFT"
408flag_parse() {
409 case "${1-}" in
410 -*=*)
411 # Remove everything after first equal sign.
412 FLAG="${1%%=*}"
413 # Remove leading hyphens.
414 FLAG="${FLAG#-}"; FLAG="${FLAG#-}"
415 FLAGRAW="$(flag_fmt)"
416 # Remove everything before first equal sign.
417 FLAGARG="${1#*=}"
418 FLAGSHIFT=1
419 return 0
420 ;;
421 -)
422 FLAGSHIFT=0
423 return 1
424 ;;
425 --)
426 FLAGSHIFT=1
427 return 1
428 ;;
429 -*)
430 # Remove leading hyphens.
431 FLAG="${1#-}"; FLAG="${FLAG#-}"
432 FLAGRAW=$(flag_fmt)
433 unset FLAGARG
434 FLAGSHIFT=1
435 if [ $# -gt 1 ]; then
436 case "$2" in
437 -)
438 FLAGARG="$2"
439 FLAGSHIFT=2
440 ;;
441 -*)
442 ;;
443 *)
444 FLAGARG="$2"
445 FLAGSHIFT=2
446 ;;
447 esac
448 fi
449 return 0
450 ;;
451 *)
452 FLAGSHIFT=0
453 return 1
454 ;;
455 esac
456}
457
458flag_reqarg() {
459 if [ "${FLAGARG+x}" != x ]; then
460 flag_errusage "flag $FLAGRAW requires an argument"
461 fi
462}
463
464flag_nonemptyarg() {
465 flag_reqarg
466 if [ -z "$FLAGARG" ]; then
467 flag_errusage "flag $FLAGRAW requires a non-empty argument"
468 fi
469}
470
471flag_noarg() {
472 if [ "$FLAGSHIFT" -eq 2 ]; then
473 unset FLAGARG
474 FLAGSHIFT=1
475 elif [ "${FLAGARG+x}" = x ]; then
476 # Means an argument was passed via equal sign as in -$FLAG=$FLAGARG
477 flag_errusage "flag $FLAGRAW does not accept an argument"
478 fi
479}
480
481flag_errusage() {
482 caterr <<EOF
483$1
484Run with --help for usage.
485EOF
486 return 1
487}
488
489flag_fmt() {
490 if [ "$(printf %s "$FLAG" | wc -c)" -eq 1 ]; then
491 echo "-$FLAG"
492 else
493 echo "--$FLAG"
494 fi
495}
496#!/bin/sh
497if [ "${LIB_RELEASE-}" ]; then
498 return 0
499fi
500LIB_RELEASE=1
501
502ensure_os() {
503 if [ -n "${OS-}" ]; then
504 # Windows defines OS=Windows_NT.
505 if [ "$OS" = Windows_NT ]; then
506 OS=windows
507 fi
508 return
509 fi
510 uname=$(uname)
511 case $uname in
512 Linux) OS=linux;;
513 Darwin) OS=macos;;
514 FreeBSD) OS=freebsd;;
515 *) OS=$uname;;
516 esac
517}
518
519ensure_arch() {
520 if [ -n "${ARCH-}" ]; then
521 return
522 fi
523 uname_m=$(uname -m)
524 case $uname_m in
525 aarch64) ARCH=arm64;;
526 x86_64) ARCH=amd64;;
527 *) ARCH=$uname_m;;
528 esac
529}
530
531ensure_goos() {
532 if [ -n "${GOOS-}" ]; then
533 return
534 fi
535 ensure_os
536 case "$OS" in
537 macos) export GOOS=darwin;;
538 *) export GOOS=$OS;;
539 esac
540}
541
542ensure_goarch() {
543 if [ -n "${GOARCH-}" ]; then
544 return
545 fi
546 ensure_arch
547 case "$ARCH" in
548 *) export GOARCH=$ARCH;;
549 esac
550}
551
552gh_repo() {
553 gh repo view --json nameWithOwner --template '{{ .nameWithOwner }}'
554}
555
556manpath() {
557 if command -v manpath >/dev/null; then
558 command manpath
559 elif man -w 2>/dev/null; then
560 man -w
561 else
562 echo "${MANPATH-}"
563 fi
564}
565
566is_writable_dir() {
567 mkdir -p "$1" 2>/dev/null
568 # directory must exist otherwise -w returns 1 even for paths that should be writable.
569 [ -w "$1" ]
570}
571
572ensure_prefix() {
573 if [ -n "${PREFIX-}" ]; then
574 return
575 fi
576 # The reason for checking whether lib is writable is that on macOS you have /usr/local
577 # owned by root but you don't need root to write to its subdirectories which is all we
578 # need to do.
579 if ! is_writable_dir "/usr/local/lib"; then
580 # This also handles M1 Mac's which do not allow modifications to /usr/local even
581 # with sudo.
582 PREFIX=$HOME/.local
583 else
584 PREFIX=/usr/local
585 fi
586}
587
588ensure_prefix_sh_c() {
589 ensure_prefix
590
591 sh_c="sh_c"
592 # The reason for checking whether lib is writable is that on macOS you have /usr/local
593 # owned by root but you don't need root to write to its subdirectories which is all we
594 # need to do.
595 if ! is_writable_dir "$PREFIX/lib"; then
596 sh_c="sudo_sh_c"
597 fi
598}
599#!/bin/sh
600set -eu
601
602
603help() {
604 arg0="$0"
605 if [ "$0" = sh ]; then
606 arg0="curl -fsSL https://d2lang.com/install.sh | sh -s --"
607 fi
608
609 cat <<EOF
610usage: $arg0 [-d|--dry-run] [--version vX.X.X] [--edge] [--method detect] [--prefix path]
611 [--tala latest] [--force] [--uninstall] [-x|--trace]
612
613install.sh automates the installation of D2 onto your system. It currently only supports
614the installation of standalone releases from GitHub and via Homebrew on macOS. See the
615docs for --detect below for more information
616
617If you pass --edge, it will clone the source, build a release and install from it.
618--edge is incompatible with --tala and currently unimplemented.
619
620\$PREFIX in the docs below refers to the path set by --prefix. See docs on the --prefix
621flag below for the default.
622
623Flags:
624
625-d, --dry-run
626 Pass to have install.sh show the install method and flags that will be used to install
627 without executing them. Very useful to understand what changes it will make to your system.
628
629--version vX.X.X
630 Pass to have install.sh install the given version instead of the latest version.
631 warn: The version may not be obeyed with package manager installations. Use
632 --method=standalone to enforce the version.
633
634--edge
635 Pass to build and install D2 from source. This will still use --method if set to detect
636 to install the release archive for your OS, whether it's apt, yum, brew or standalone
637 if an unsupported package manager is used.
638
639 To install from source like a dev would, use go install oss.terrastruct.com/d2. There's
640 also ./ci/release/build.sh --install to build and install a proper standalone release
641 including manpages. The proper release will also ensure d2 --version shows the correct
642 version by embedding the commit hash into the binary.
643
644 note: currently unimplemented.
645 warn: incompatible with --tala as TALA is closed source.
646
647--method [detect | standalone | homebrew ]
648 Pass to control the method by which to install. Right now we only support standalone
649 releases from GitHub but later we'll add support for brew, rpm, deb and more.
650
651 - detect will use your OS's package manager automatically.
652 So far it only detects macOS and automatically uses homebrew.
653 - homebrew uses https://brew.sh/ which is a macOS and Linux package manager.
654 - standalone installs a standalone release archive into the unix hierarchy path
655 specified by --prefix
656
657--prefix path
658 Controls the unix hierarchy path into which standalone releases are installed.
659 Defaults to /usr/local or ~/.local if /usr/local is not writable by the current user.
660 Remember that whatever you use, you must have the bin directory of your prefix path in
661 \$PATH to execute the d2 binary. For example, if my prefix directory is /usr/local then
662 my \$PATH must contain /usr/local/bin.
663 You may also need to include \$PREFIX/share/man into \$MANPATH.
664 install.sh will tell you whether \$PATH or \$MANPATH need to be updated after successful
665 installation.
666
667--tala [latest]
668 Install Terrastruct's closed source TALA for improved layouts.
669 See https://github.com/terrastruct/tala
670 It optionally takes an argument of the TALA version to install.
671 Installation obeys all other flags, just like the installation of d2. For example,
672 the d2plugin-tala binary will be installed into \$PREFIX/bin/d2plugin-tala
673 warn: The version may not be obeyed with package manager installations. Use
674 --method=standalone to enforce the version.
675
676--force:
677 Force installation over the existing version even if they match. It will attempt a
678 uninstall first before installing the new version. The installed release tree
679 will be deleted from \$PREFIX/lib/d2/d2-<VERSION> but the release archive in
680 ~/.cache/d2/release will remain.
681
682--uninstall:
683 Uninstall the installed version of d2. The --method and --prefix flags must be the same
684 as for installation. i.e if you used --method standalone you must again use --method
685 standalone for uninstallation. With detect, the install script will try to use the OS
686 package manager to uninstall instead.
687 note: tala will also be uninstalled if installed.
688
689-x, --trace:
690 Run script with set -x.
691
692All downloaded archives are cached into ~/.cache/d2/release. use \$XDG_CACHE_HOME to change
693path of the cached assets. Release archives are unarchived into \$PREFIX/lib/d2/d2-<VERSION>
694
695note: Deleting the unarchived releases will cause --uninstall to stop working.
696
697You can rerun install.sh to update your version of D2. install.sh will avoid reinstalling
698if the installed version is the latest unless --force is passed.
699
700See https://github.com/terrastruct/d2/blob/master/docs/INSTALL.md#security for
701documentation on its security.
702EOF
703}
704
705main() {
706 while flag_parse "$@"; do
707 case "$FLAG" in
708 h|help)
709 help
710 return 0
711 ;;
712 d|dry-run)
713 flag_noarg && shift "$FLAGSHIFT"
714 DRY_RUN=1
715 ;;
716 version)
717 flag_nonemptyarg && shift "$FLAGSHIFT"
718 VERSION=$FLAGARG
719 ;;
720 tala)
721 shift "$FLAGSHIFT"
722 TALA=${FLAGARG:-latest}
723 ;;
724 edge)
725 flag_noarg && shift "$FLAGSHIFT"
726 EDGE=1
727 echoerr "$FLAGRAW is currently unimplemented"
728 return 1
729 ;;
730 method)
731 flag_nonemptyarg && shift "$FLAGSHIFT"
732 METHOD=$FLAGARG
733 ;;
734 prefix)
735 flag_nonemptyarg && shift "$FLAGSHIFT"
736 export PREFIX=$FLAGARG
737 ;;
738 force)
739 flag_noarg && shift "$FLAGSHIFT"
740 FORCE=1
741 ;;
742 uninstall)
743 flag_noarg && shift "$FLAGSHIFT"
744 UNINSTALL=1
745 ;;
746 x|trace)
747 flag_noarg && shift "$FLAGSHIFT"
748 set -x
749 export TRACE=1
750 ;;
751 *)
752 flag_errusage "unrecognized flag $FLAGRAW"
753 ;;
754 esac
755 done
756 shift "$FLAGSHIFT"
757
758 if [ $# -gt 0 ]; then
759 flag_errusage "no arguments are accepted"
760 fi
761
762 REPO=${REPO:-terrastruct/d2}
763 ensure_os
764 ensure_arch
765 ensure_prefix
766 CACHE_DIR=$(cache_dir)
767 mkdir -p "$CACHE_DIR"
768 METHOD=${METHOD:-detect}
769 INSTALL_DIR=$PREFIX/lib/d2
770
771 case $METHOD in
772 detect)
773 case "$OS" in
774 macos)
775 if command -v brew >/dev/null; then
776 log "detected macOS with homebrew, using homebrew for installation"
777 METHOD=homebrew
778 else
779 warn "detected macOS without homebrew, falling back to --method=standalone"
780 METHOD=standalone
781 fi
782 ;;
783 linux|windows)
784 METHOD=standalone
785 ;;
786 *)
787 warn "unrecognized OS $OS, falling back to --method=standalone"
788 METHOD=standalone
789 ;;
790 esac
791 ;;
792 standalone) ;;
793 homebrew) ;;
794 *)
795 echoerr "unknown installation method $METHOD"
796 return 1
797 ;;
798 esac
799
800 if [ -n "${UNINSTALL-}" ]; then
801 uninstall
802 if [ -n "${DRY_RUN-}" ]; then
803 bigheader "Rerun without --dry-run to execute printed commands and perform install."
804 fi
805 else
806 install
807 if [ -n "${DRY_RUN-}" ]; then
808 bigheader "Rerun without --dry-run to execute printed commands and perform install."
809 fi
810 fi
811}
812
813install() {
814 case $METHOD in
815 standalone)
816 install_d2_standalone
817 if [ -n "${TALA-}" ]; then
818 # Run in subshell to avoid overwriting VERSION.
819 TALA_VERSION="$( RELEASE_INFO= install_tala_standalone && echo "$VERSION" )"
820 fi
821 ;;
822 homebrew)
823 install_d2_brew
824 if [ -n "${TALA-}" ]; then install_tala_brew; fi
825 ;;
826 esac
827
828 FGCOLOR=2 bigheader 'next steps'
829 case $METHOD in
830 standalone) install_post_standalone ;;
831 homebrew) install_post_brew ;;
832 esac
833 install_post_warn
834}
835
836install_post_standalone() {
837 log "d2-$VERSION-$OS-$ARCH has been successfully installed into $PREFIX"
838 if [ -n "${TALA-}" ]; then
839 log "tala-$TALA_VERSION-$OS-$ARCH has been successfully installed into $PREFIX"
840 fi
841 log "Rerun this install script with --uninstall to uninstall."
842 log
843 if ! echo "$PATH" | grep -qF "$PREFIX/bin"; then
844 logcat >&2 <<EOF
845Extend your \$PATH to use d2:
846 export PATH=$PREFIX/bin:\$PATH
847Then run:
848 ${TALA:+D2_LAYOUT=tala }d2 --help
849EOF
850 else
851 log "Run ${TALA:+D2_LAYOUT=tala }d2 --help for usage."
852 fi
853 if ! manpath 2>/dev/null | grep -qF "$PREFIX/share/man"; then
854 logcat >&2 <<EOF
855Extend your \$MANPATH to view d2's manpages:
856 export MANPATH=$PREFIX/share/man:\$MANPATH
857Then run:
858 man d2
859EOF
860 if [ -n "${TALA-}" ]; then
861 log " man d2plugin-tala"
862 fi
863 else
864 log "Run man d2 for detailed docs."
865 if [ -n "${TALA-}" ]; then
866 log "Run man d2plugin-tala for detailed TALA docs."
867 fi
868 fi
869}
870
871install_post_brew() {
872 log "d2 has been successfully installed with homebrew."
873 if [ -n "${TALA-}" ]; then
874 log "tala has been successfully installed with homebrew."
875 fi
876 log "Rerun this install script with --uninstall to uninstall."
877 log
878 log "Run ${TALA:+D2_LAYOUT=tala }d2 --help for usage."
879 log "Run man d2 for detailed docs."
880 if [ -n "${TALA-}" ]; then
881 log "Run man d2plugin-tala for detailed TALA docs."
882 fi
883
884 VERSION=$(brew info d2 | head -n1 | cut -d' ' -f4)
885 VERSION=${VERSION%,}
886 if [ -n "${TALA-}" ]; then
887 TALA_VERSION=$(brew info tala | head -n1 | cut -d' ' -f4)
888 TALA_VERSION=${TALA_VERSION%,}
889 fi
890}
891
892install_post_warn() {
893 if command -v d2 >/dev/null; then
894 INSTALLED_VERSION=$(d2 --version)
895 if [ "$INSTALLED_VERSION" != "$VERSION" ]; then
896 warn "newly installed d2 $VERSION is shadowed by d2 $INSTALLED_VERSION in \$PATH"
897 fi
898 fi
899 if [ -n "${TALA-}" ] && command -v d2plugin-tala >/dev/null; then
900 INSTALLED_TALA_VERSION=$(d2plugin-tala --version)
901 if [ "$INSTALLED_TALA_VERSION" != "$TALA_VERSION" ]; then
902 warn "newly installed d2plugin-tala $TALA_VERSION is shadowed by d2plugin-tala $INSTALLED_TALA_VERSION in \$PATH"
903 fi
904 fi
905}
906
907install_d2_standalone() {
908 VERSION=${VERSION:-latest}
909 header "installing d2-$VERSION"
910
911 if [ "$VERSION" = latest ]; then
912 fetch_release_info
913 fi
914
915 if command -v d2 >/dev/null; then
916 INSTALLED_VERSION="$(d2 --version)"
917 if [ ! "${FORCE-}" -a "$VERSION" = "$INSTALLED_VERSION" ]; then
918 log "skipping installation as d2 $VERSION is already installed."
919 return 0
920 fi
921 log "uninstalling d2 $INSTALLED_VERSION to install $VERSION"
922 if ! uninstall_d2_standalone; then
923 warn "failed to uninstall d2 $INSTALLED_VERSION"
924 fi
925 fi
926
927 ARCHIVE="d2-$VERSION-$OS-$ARCH.tar.gz"
928 log "installing standalone release $ARCHIVE from github"
929
930 fetch_release_info
931 asset_line=$(sh_c 'cat "$RELEASE_INFO" | grep -n "$ARCHIVE" | cut -d: -f1 | head -n1')
932 asset_url=$(sh_c 'sed -n $((asset_line-3))p "$RELEASE_INFO" | sed "s/^.*: \"\(.*\)\",$/\1/g"')
933 fetch_gh "$asset_url" "$CACHE_DIR/$ARCHIVE" 'application/octet-stream'
934
935 ensure_prefix_sh_c
936 "$sh_c" mkdir -p "'$INSTALL_DIR'"
937 "$sh_c" tar -C "$INSTALL_DIR" -xzf "$CACHE_DIR/$ARCHIVE"
938 "$sh_c" sh -c "'cd \"$INSTALL_DIR/d2-$VERSION\" && make install PREFIX=\"$PREFIX\"'"
939}
940
941install_d2_brew() {
942 header "installing d2 with homebrew"
943 sh_c brew update
944 sh_c brew install d2
945}
946
947install_tala_standalone() {
948 REPO="${REPO_TALA:-terrastruct/tala}"
949 VERSION=$TALA
950
951 header "installing tala-$VERSION"
952
953 if [ "$VERSION" = latest ]; then
954 fetch_release_info
955 fi
956
957 if command -v d2plugin-tala >/dev/null; then
958 INSTALLED_VERSION="$(d2plugin-tala --version)"
959 if [ ! "${FORCE-}" -a "$VERSION" = "$INSTALLED_VERSION" ]; then
960 log "skipping installation as tala $VERSION is already installed."
961 return 0
962 fi
963 log "uninstalling tala $INSTALLED_VERSION to install $VERSION"
964 if ! uninstall_tala_standalone; then
965 warn "failed to uninstall tala $INSTALLED_VERSION"
966 fi
967 fi
968
969 ARCHIVE="tala-$VERSION-$OS-$ARCH.tar.gz"
970 log "installing standalone release $ARCHIVE from github"
971
972 fetch_release_info
973 asset_line=$(sh_c 'cat "$RELEASE_INFO" | grep -n "$ARCHIVE" | cut -d: -f1 | head -n1')
974 asset_url=$(sh_c 'sed -n $((asset_line-3))p "$RELEASE_INFO" | sed "s/^.*: \"\(.*\)\",$/\1/g"')
975
976 fetch_gh "$asset_url" "$CACHE_DIR/$ARCHIVE" 'application/octet-stream'
977
978 ensure_prefix_sh_c
979 "$sh_c" mkdir -p "'$INSTALL_DIR'"
980 "$sh_c" tar -C "$INSTALL_DIR" -xzf "$CACHE_DIR/$ARCHIVE"
981 "$sh_c" sh -c "'cd \"$INSTALL_DIR/tala-$VERSION\" && make install PREFIX=\"$PREFIX\"'"
982}
983
984install_tala_brew() {
985 header "installing tala with homebrew"
986 sh_c brew update
987 sh_c brew install terrastruct/tap/tala
988}
989
990uninstall() {
991 # We uninstall tala first as package managers require that it be uninstalled before
992 # uninstalling d2 as TALA depends on d2.
993 if command -v d2plugin-tala >/dev/null; then
994 INSTALLED_VERSION="$(d2plugin-tala --version)"
995 header "uninstalling tala-$INSTALLED_VERSION"
996 case $METHOD in
997 standalone) uninstall_tala_standalone ;;
998 homebrew) uninstall_tala_brew ;;
999 esac
1000 elif [ "${TALA-}" ]; then
1001 warn "no version of tala installed"
1002 fi
1003
1004 if ! command -v d2 >/dev/null; then
1005 warn "no version of d2 installed"
1006 return 0
1007 fi
1008
1009 INSTALLED_VERSION="$(d2 --version)"
1010 header "uninstalling d2-$INSTALLED_VERSION"
1011 case $METHOD in
1012 standalone) uninstall_d2_standalone ;;
1013 homebrew) uninstall_d2_brew ;;
1014 esac
1015}
1016
1017uninstall_d2_standalone() {
1018 log "uninstalling standalone release of d2-$INSTALLED_VERSION"
1019
1020 if [ ! -e "$INSTALL_DIR/d2-$INSTALLED_VERSION" ]; then
1021 warn "missing standalone install release directory $INSTALL_DIR/d2-$INSTALLED_VERSION"
1022 warn "d2 must have been installed via some other installation method."
1023 return 1
1024 fi
1025
1026 ensure_prefix_sh_c
1027 "$sh_c" sh -c "'cd \"$INSTALL_DIR/d2-$INSTALLED_VERSION\" && make uninstall PREFIX=\"$PREFIX\"'"
1028 "$sh_c" rm -rf "$INSTALL_DIR/d2-$INSTALLED_VERSION"
1029}
1030
1031uninstall_d2_brew() {
1032 sh_c brew remove d2
1033}
1034
1035uninstall_tala_standalone() {
1036 log "uninstalling standalone release tala-$INSTALLED_VERSION"
1037
1038 if [ ! -e "$INSTALL_DIR/tala-$INSTALLED_VERSION" ]; then
1039 warn "missing standalone install release directory $INSTALL_DIR/tala-$INSTALLED_VERSION"
1040 warn "tala must have been installed via some other installation method."
1041 return 1
1042 fi
1043
1044 ensure_prefix_sh_c
1045 "$sh_c" sh -c "'cd \"$INSTALL_DIR/tala-$INSTALLED_VERSION\" && make uninstall PREFIX=\"$PREFIX\"'"
1046 "$sh_c" rm -rf "$INSTALL_DIR/tala-$INSTALLED_VERSION"
1047}
1048
1049uninstall_tala_brew() {
1050 sh_c brew remove tala
1051}
1052
1053cache_dir() {
1054 if [ -n "${XDG_CACHE_HOME-}" ]; then
1055 echo "$XDG_CACHE_HOME/d2/release"
1056 elif [ -n "${HOME-}" ]; then
1057 echo "$HOME/.cache/d2/release"
1058 else
1059 echo "/tmp/d2-cache/release"
1060 fi
1061}
1062
1063fetch_release_info() {
1064 if [ -n "${RELEASE_INFO-}" ]; then
1065 return 0
1066 fi
1067
1068 log "fetching info on $VERSION version of $REPO"
1069 RELEASE_INFO=$(mktempd)/release-info.json
1070 if [ "$VERSION" = latest ]; then
1071 release_info_url="https://api.github.com/repos/$REPO/releases/$VERSION"
1072 else
1073 release_info_url="https://api.github.com/repos/$REPO/releases/tags/$VERSION"
1074 fi
1075 DRY_RUN= fetch_gh "$release_info_url" "$RELEASE_INFO" \
1076 'application/json'
1077 VERSION=$(cat "$RELEASE_INFO" | grep -m1 tag_name | sed 's/^.*: "\(.*\)",$/\1/g')
1078}
1079
1080curl_gh() {
1081 sh_c curl -fL ${GITHUB_TOKEN:+"-H \"Authorization: Bearer \$GITHUB_TOKEN\""} "$@"
1082}
1083
1084fetch_gh() {
1085 url=$1
1086 file=$2
1087 accept=$3
1088
1089 if [ -e "$file" ]; then
1090 log "reusing $file"
1091 return
1092 fi
1093
1094 curl_gh -#o "$file.inprogress" -C- -H "'Accept: $accept'" "$url"
1095 sh_c mv "$file.inprogress" "$file"
1096}
1097
1098# The main function does more than provide organization. It provides robustness in that if
1099# the install script was to only partial download into sh, sh will not execute it because
1100# main is not invoked until the very last byte.
1101main "$@"
View as plain text