install.sh: Improve default $PREFIX behaviour

As before we default to /usr/local but now we switch to ~/.local if
/usr/local if not accessible instead of prompting for sudo. Instead
of having an exception for macOS/arm64.

Of course if a user wants to install in /usr/local anyway, they can
still pass --prefix /usr/local to force installation into /usr/local
and prompt for sudo.
This commit is contained in:
Anmol Sethi 2022-12-05 21:51:46 -08:00
parent c0b3badbde
commit 6e52966b52
No known key found for this signature in database
GPG key ID: 25BC68888A99A8BA
13 changed files with 254 additions and 208 deletions

View file

@ -10,10 +10,11 @@ VERSION=$VERSION sh_c eval "'$HW_BUILD_DIR/README.md.sh'" \> "'$HW_BUILD_DIR/REA
sh_c rm -f "$HW_BUILD_DIR/README.md.sh"
sh_c find "$HW_BUILD_DIR" -exec touch {} \\\;
export GOOS=$(goos "$OS")
export GOARCH="$ARCH"
ensure_goos
ensure_goarch
sh_c mkdir -p "$HW_BUILD_DIR/bin"
sh_c go build -ldflags "'-X oss.terrastruct.com/d2/lib/version.Version=$VERSION'" \
sh_c CGO_ENABLED=0 go build \
-ldflags "'-X oss.terrastruct.com/d2/lib/version.Version=$VERSION'" \
-o "$HW_BUILD_DIR/bin/d2" .
ARCHIVE=$PWD/$ARCHIVE

View file

@ -14,7 +14,7 @@ help() {
fi
cat <<EOF
usage: $arg0 [-d|--dry-run] [--version vX.X.X] [--edge] [--method detect] [--prefix /usr/local]
usage: $arg0 [-d|--dry-run] [--version vX.X.X] [--edge] [--method detect] [--prefix path]
[--tala latest] [--force] [--uninstall]
install.sh automates the installation of D2 onto your system. It currently only supports
@ -24,6 +24,9 @@ docs for --detect below for more information
If you pass --edge, it will clone the source, build a release and install from it.
--edge is incompatible with --tala and currently unimplemented.
\$PREFIX in the docs below refers to the path set by --prefix. See docs on the --prefix
flag below for the default.
Flags:
-d, --dry-run
@ -51,30 +54,31 @@ Flags:
So far it only detects macOS and automatically uses homebrew.
- homebrew uses https://brew.sh/ which is a macOS and Linux package manager.
- standalone installs a standalone release archive into the unix hierarchy path
specified by --prefix which defaults to /usr/local
Ensure /usr/local/bin is in your \$PATH to use it.
specified by --prefix
--prefix /usr/local
--prefix path
Controls the unix hierarchy path into which standalone releases are installed.
Defaults to /usr/local. You may also want to use ~/.local to avoid needing sudo.
We use ~/.local by default on arm64 macOS machines as SIP now disables access to
/usr/local. Remember that whatever you use, you must have the bin directory of your
prefix path in \$PATH to execute the d2 binary. For example, if my prefix directory is
/usr/local then my \$PATH must contain /usr/local/bin.
Defaults to /usr/local or ~/.local if /usr/local is not writable by the current user.
Remember that whatever you use, you must have the bin directory of your prefix path in
\$PATH to execute the d2 binary. For example, if my prefix directory is /usr/local then
my \$PATH must contain /usr/local/bin.
You may also need to include \$PREFIX/share/man into \$MANPATH.
install.sh will tell you whether \$PATH or \$MANPATH need to be updated after successful
installation.
--tala [latest]
Install Terrastruct's closed source TALA for improved layouts.
See https://github.com/terrastruct/tala
It optionally takes an argument of the TALA version to install.
Installation obeys all other flags, just like the installation of d2. For example,
the d2plugin-tala binary will be installed into /usr/local/bin/d2plugin-tala
the d2plugin-tala binary will be installed into \$PREFIX/bin/d2plugin-tala
warn: The version may not be obeyed with package manager installations. Use
--method=standalone to enforce the version.
--force:
Force installation over the existing version even if they match. It will attempt a
uninstall first before installing the new version. The installed release tree
will be deleted from /usr/local/lib/d2/d2-<VERSION> but the release archive in
will be deleted from \$PREFIX/lib/d2/d2-<VERSION> but the release archive in
~/.cache/d2/release will remain.
--uninstall:
@ -85,7 +89,7 @@ Flags:
note: tala will also be uninstalled if installed.
All downloaded archives are cached into ~/.cache/d2/release. use \$XDG_CACHE_HOME to change
path of the cached assets. Release archives are unarchived into /usr/local/lib/d2/d2-<VERSION>
path of the cached assets. Release archives are unarchived into \$PREFIX/lib/d2/d2-<VERSION>
note: Deleting the unarchived releases will cause --uninstall to stop working.
@ -150,13 +154,9 @@ main() {
fi
REPO=${REPO:-terrastruct/d2}
OS=$(os)
ARCH=$(arch)
if [ -z "${PREFIX-}" -a "$OS" = macos -a "$ARCH" = arm64 ]; then
# M1 Mac's do not allow modifications to /usr/local even with sudo.
PREFIX=$HOME/.local
fi
PREFIX=${PREFIX:-/usr/local}
ensure_os
ensure_arch
ensure_prefix
CACHE_DIR=$(cache_dir)
mkdir -p "$CACHE_DIR"
METHOD=${METHOD:-detect}
@ -318,11 +318,7 @@ install_d2_standalone() {
asset_url=$(sh_c 'sed -n $((asset_line-3))p "$RELEASE_INFO" | sed "s/^.*: \"\(.*\)\",$/\1/g"')
fetch_gh "$asset_url" "$CACHE_DIR/$ARCHIVE" 'application/octet-stream'
sh_c="sh_c"
if ! is_prefix_writable; then
sh_c="sudo_sh_c"
fi
ensure_prefix_sh_c
"$sh_c" mkdir -p "'$INSTALL_DIR'"
"$sh_c" tar -C "$INSTALL_DIR" -xzf "$CACHE_DIR/$ARCHIVE"
"$sh_c" sh -c "'cd \"$INSTALL_DIR/d2-$VERSION\" && make install PREFIX=\"$PREFIX\"'"
@ -365,11 +361,7 @@ install_tala_standalone() {
fetch_gh "$asset_url" "$CACHE_DIR/$ARCHIVE" 'application/octet-stream'
sh_c="sh_c"
if ! is_prefix_writable; then
sh_c="sudo_sh_c"
fi
ensure_prefix_sh_c
"$sh_c" mkdir -p "'$INSTALL_DIR'"
"$sh_c" tar -C "$INSTALL_DIR" -xzf "$CACHE_DIR/$ARCHIVE"
"$sh_c" sh -c "'cd \"$INSTALL_DIR/tala-$VERSION\" && make install PREFIX=\"$PREFIX\"'"
@ -417,11 +409,7 @@ uninstall_d2_standalone() {
return 1
fi
sh_c="sh_c"
if ! is_prefix_writable; then
sh_c="sudo_sh_c"
fi
ensure_prefix_sh_c
"$sh_c" sh -c "'cd \"$INSTALL_DIR/d2-$INSTALLED_VERSION\" && make uninstall PREFIX=\"$PREFIX\"'"
"$sh_c" rm -rf "$INSTALL_DIR/d2-$INSTALLED_VERSION"
}
@ -439,11 +427,7 @@ uninstall_tala_standalone() {
return 1
fi
sh_c="sh_c"
if ! is_prefix_writable; then
sh_c="sudo_sh_c"
fi
ensure_prefix_sh_c
"$sh_c" sh -c "'cd \"$INSTALL_DIR/tala-$INSTALLED_VERSION\" && make uninstall PREFIX=\"$PREFIX\"'"
"$sh_c" rm -rf "$INSTALL_DIR/tala-$INSTALLED_VERSION"
}
@ -452,13 +436,6 @@ uninstall_tala_brew() {
sh_c brew remove tala
}
is_prefix_writable() {
# The reason for checking whether $INSTALL_DIR is writable is that on macOS you have
# /usr/local owned by root but you don't need root to write to its subdirectories which
# is all we want to do.
is_writable_dir "$INSTALL_DIR"
}
cache_dir() {
if [ -n "${XDG_CACHE_HOME-}" ]; then
echo "$XDG_CACHE_HOME/d2/release"

View file

@ -109,11 +109,14 @@ main() {
VERSION=${VERSION:-$(git_describe_ref)}
BUILD_DIR=ci/release/build/$VERSION
if [ -n "${HOST_ONLY-}" ]; then
runjob $(os)-$(arch) "OS=$(os) ARCH=$(arch) build"
OS=$(os)
ARCH=$(arch)
runjob "$OS-$ARCH" "build"
if [ -n "${INSTALL-}" ]; then
( sh_c make -sC "ci/release/build/$VERSION/$(os)-$(arch)/d2-$VERSION" install)
sh_c make -sC "ci/release/build/$VERSION/$(os)-$(arch)/d2-$VERSION" install
elif [ -n "${UNINSTALL-}" ]; then
( sh_c make -sC "ci/release/build/$VERSION/$(os)-$(arch)/d2-$VERSION" uninstall)
sh_c make -sC "ci/release/build/$VERSION/$(os)-$(arch)/d2-$VERSION" uninstall
fi
return 0
fi
@ -123,7 +126,7 @@ main() {
runjob macos-amd64 'OS=macos ARCH=amd64 build' &
runjob macos-arm64 'OS=macos ARCH=arm64 build' &
runjob windows-amd64 'OS=windows ARCH=amd64 build' &
runjob windows-arm64 'OS=macos ARCH=arm64 build' &
runjob windows-arm64 'OS=windows ARCH=arm64 build' &
waitjobs
}
@ -189,8 +192,7 @@ build_local() {
build_remote_macos() {
sh_c lockfile_ssh "$REMOTE_HOST" .d2-build-lock
sh_c ssh "$REMOTE_HOST" mkdir -p src
sh_c rsync --archive --human-readable --delete ./ "$REMOTE_HOST:src/d2/"
sh_c gitsync "$REMOTE_HOST" src/d2
sh_c ssh "$REMOTE_HOST" "COLOR=${COLOR-} \
TERM=${TERM-} \
DRY_RUN=${DRY_RUN-} \
@ -207,8 +209,7 @@ PATH=\\\"/usr/local/bin:/usr/local/sbin:/opt/homebrew/bin:/opt/homebrew/sbin\\\$
build_remote_linux() {
sh_c lockfile_ssh "$REMOTE_HOST" .d2-build-lock
sh_c ssh "$REMOTE_HOST" mkdir -p src
sh_c rsync --archive --human-readable --delete ./ "$REMOTE_HOST:src/d2/"
sh_c gitsync "$REMOTE_HOST" src/d2
sh_c ssh "$REMOTE_HOST" "COLOR=${COLOR-} \
TERM=${TERM-} \
DRY_RUN=${DRY_RUN-} \

View file

@ -253,7 +253,7 @@ init_remote_macos() {
sh_c ssh "$REMOTE_HOST" '"/bin/bash -c \"\$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)\""'
sh_c ssh "$REMOTE_HOST" 'PATH="/usr/local/bin:/opt/homebrew/bin:\$PATH" brew update'
sh_c ssh "$REMOTE_HOST" 'PATH="/usr/local/bin:/opt/homebrew/bin:\$PATH" brew upgrade'
sh_c ssh "$REMOTE_HOST" 'PATH="/usr/local/bin:/opt/homebrew/bin:\$PATH" brew install go'
sh_c ssh "$REMOTE_HOST" 'PATH="/usr/local/bin:/opt/homebrew/bin:\$PATH" brew install go rsync'
}
main "$@"

View file

@ -57,6 +57,11 @@ PRs when it's a visual change.
- `d2` now lives in the root folder of the repository instead of as a subcommand. So you
can now run `go install oss.terrastruct.com/d2@latest` to install from source.
[#290](https://github.com/terrastruct/d2/pull/290)
- `install.sh` defaults to installation to `/usr/local` as before but now if
`/usr/local` is not accessible to the current user, it will use `~/.local`
instead of prompting for sudo. You can pass `--prefix /usr/local` to force
installation into `/usr/local` with a prompt for sudo.
[#372](https://github.com/terrastruct/d2/pull/372)
#### Bugfixes ⛑️

View file

@ -3,15 +3,13 @@
.PHONY: all
all:
(. ./scripts/lib.sh && echoerr "You must provide a target of install or uninstall for this Makefile")
( . ./scripts/lib.sh && echoerr "You must provide a target of install or uninstall for this Makefile" )
exit 1
PREFIX = $(DESTDIR)/usr/local
.PHONY: install
install:
PREFIX='$(PREFIX)' ./scripts/install.sh
./scripts/install.sh
.PHONY: uninstall
uninstall:
PREFIX='$(PREFIX)' ./scripts/uninstall.sh
./scripts/uninstall.sh

View file

@ -13,16 +13,17 @@ version: $VERSION
\`\`\`sh
make install DRY_RUN=1
# If it looks right, run:
make install
# make install
\`\`\`
Pass \`PREFIX=whatever\` to change the installation prefix.
Pass \`PREFIX=somepath\` to change the installation prefix from the default which is
\`/usr/local\` or \`~/.local\` if \`/usr/local\` is not writable with the current user.
## Uninstall
\`\`\`sh
make uninstall DRY_RUN=1
# If it looks right, run:
make uninstall
# make uninstall
\`\`\`
EOF

View file

@ -4,16 +4,7 @@ cd -- "$(dirname "$0")/.."
. ./scripts/lib.sh
main() {
if [ -z "${PREFIX-}" ]; then
echoerr "\$PREFIX must be set to a unix prefix directory in which to install d2 like /usr/local"
return 1
fi
sh_c="sh_c"
if ! is_writable_dir "$PREFIX/bin"; then
sh_c="sudo_sh_c"
fi
ensure_prefix_sh_c
"$sh_c" mkdir -p "$PREFIX/bin"
"$sh_c" install ./bin/d2 "$PREFIX/bin/d2"
"$sh_c" mkdir -p "$PREFIX/share/man/man1"

View file

@ -23,11 +23,14 @@ pick() {
shift
seed_file="$(mktemp)"
echo "$seed" >"$seed_file"
# We add 16 more bytes to the seed file for sufficient entropy. Otherwise both Cygwin's
# We add 32 more bytes to the seed file for sufficient entropy. Otherwise both Cygwin's
# and MinGW's sort for example complains about the lack of entropy on stderr and writes
# nothing to stdout. I'm sure there are more platforms that would too.
echo "================" >"$seed_file"
#
# We also limit to a max of 32 bytes as otherwise macOS's sort complains that the random
# seed is too large. Probably more platforms too.
(echo "$seed" && echo "================================") | head -c32 >"$seed_file"
while [ $# -gt 0 ]; do
echo "$1"
@ -89,16 +92,20 @@ _echo() {
printf '%s\n' "$*"
}
# 1-6 are regular and 9-14 are bright.
get_rand_color() {
colors=""
ncolors=$(command tput colors)
if [ "$ncolors" -ge 8 ]; then
colors="$colors 1 2 3 4 5 6"
elif [ "$ncolors" -ge 16 ]; then
colors="$colors 9 10 11 12 13 14"
if [ "${TERM_COLORS+x}" != x ]; then
TERM_COLORS=""
export TERM_COLORS
ncolors=$(TERM=${TERM:-xterm-256color} command tput colors)
if [ "$ncolors" -ge 8 ]; then
# 1-6 are regular
TERM_COLORS="$TERM_COLORS 1 2 3 4 5 6"
elif [ "$ncolors" -ge 16 ]; then
# 9-14 are bright.
TERM_COLORS="$TERM_COLORS 9 10 11 12 13 14"
fi
fi
pick "$*" $colors
pick "$*" $TERM_COLORS
}
echop() {
@ -117,14 +124,12 @@ printfp() {(
prefix="$1"
shift
if [ -z "${FGCOLOR-}" ]; then
FGCOLOR="$(get_rand_color "$prefix")"
fi
_FGCOLOR=${FGCOLOR:-$(get_rand_color "$prefix")}
should_color || true
if [ $# -eq 0 ]; then
printf '%s' "$(COLOR=$__COLOR setaf "$FGCOLOR" "$prefix")"
printf '%s' "$(COLOR=$__COLOR setaf "$_FGCOLOR" "$prefix")"
else
printf '%s: %s\n' "$(COLOR=$__COLOR setaf "$FGCOLOR" "$prefix")" "$(printf "$@")"
printf '%s: %s\n' "$(COLOR=$__COLOR setaf "$_FGCOLOR" "$prefix")" "$(printf "$@")"
fi
)}
@ -302,36 +307,60 @@ capcode() {
code=$?
set -e
}
strjoin() {
(IFS="$1"; shift; echo "$*")
}
#!/bin/sh
if [ "${LIB_RELEASE-}" ]; then
return 0
fi
LIB_RELEASE=1
goos() {
case $1 in
macos) echo darwin;;
*) echo $1;;
ensure_goos() {
if [ -n "${GOOS-}" ]; then
return
fi
ensure_os
case "$OS" in
macos) export GOOS=darwin;;
*) export GOOS=$1;;
esac
}
os() {
ensure_goarch() {
if [ -n "${GOARCH-}" ]; then
return
fi
ensure_arch
case "$ARCH" in
*) export GOARCH=$1;;
esac
}
ensure_os() {
if [ -n "${OS-}" ]; then
return
fi
uname=$(uname)
case $uname in
Linux) echo linux;;
Darwin) echo macos;;
FreeBSD) echo freebsd;;
CYGWIN_NT*|MINGW32_NT*) echo windows;;
*) echo "$uname";;
Linux) OS=linux;;
Darwin) OS=macos;;
FreeBSD) OS=freebsd;;
CYGWIN_NT*|MINGW32_NT*) OS=windows;;
*) OS=$uname;;
esac
}
arch() {
ensure_arch() {
if [ -n "${ARCH-}" ]; then
return
fi
uname_m=$(uname -m)
case $uname_m in
aarch64) echo arm64;;
x86_64) echo amd64;;
*) echo "$uname_m";;
aarch64) ARCH=arm64;;
x86_64) ARCH=amd64;;
*) ARCH=$uname_m;;
esac
}
@ -350,9 +379,34 @@ manpath() {
}
is_writable_dir() {
# The path has to exist for -w to succeed.
sh_c "mkdir -p '$1' 2>/dev/null" || true
if [ ! -w "$1" ]; then
return 1
# If it can be created, we can use it.
sh_c "mkdir -p '$1' 2>/dev/null"
}
ensure_prefix() {
if [ -n "${PREFIX-}" ]; then
return
fi
# The reason for checking whether bin is writable is that on macOS you have /usr/local
# owned by root but you don't need root to write to its subdirectories which is all we
# need to do.
if ! is_writable_dir "/usr/local/bin"; then
# This also handles M1 Mac's which do not allow modifications to /usr/local even
# with sudo.
PREFIX=$HOME/.local
else
PREFIX=/usr/local
fi
}
ensure_prefix_sh_c() {
ensure_prefix
sh_c="sh_c"
# The reason for checking whether bin is writable is that on macOS you have /usr/local
# owned by root but you don't need root to write to its subdirectories which is all we
# need to do.
if ! is_writable_dir "$PREFIX/bin"; then
sh_c="sudo_sh_c"
fi
}

View file

@ -4,16 +4,7 @@ cd -- "$(dirname "$0")/.."
. ./scripts/lib.sh
main() {
if [ -z "${PREFIX-}" ]; then
echoerr "\$PREFIX must be set to a unix prefix directory from which to uninstall d2 like /usr/local"
return 1
fi
sh_c="sh_c"
if ! is_writable_dir "$PREFIX/bin"; then
sh_c="sudo_sh_c"
fi
ensure_prefix_sh_c
"$sh_c" rm -f "$PREFIX/bin/d2"
"$sh_c" rm -f "$PREFIX/share/man/man1/d2.1"
}

2
ci/sub

@ -1 +1 @@
Subproject commit ad50bf9f1e302bfb1a4d3c6bb821f90979083265
Subproject commit 153c562ec591b7d32e265fabbd426a6b69e4fde8

View file

@ -47,9 +47,9 @@ We're careful shell programmers and are aware of the many footguns of the Unix s
script was written carefully and with detail. For example, it is not vulnerable to partial
execution and the entire script runs with `set -eu` and very meticulous quoting.
It follows the XDG standards, installs `d2` properly into a Unix hierarchy path (defaulting
to /usr/local though you can use ~/.local to avoid sudo if you'd like) and allows for easy
uninstall.
It follows the XDG standards, installs `d2` properly into a Unix hierarchy path
(`/usr/local` unless `/usr/local` requires sudo in which case `~/.local` is used) and
allows for easy uninstall. You can easily adjust the used path with `--prefix`.
Some other niceties are that it'll tell you if you need to adjust `$PATH` or `$MANPATH` to
access `d2` and its manpages. It can also install
@ -126,10 +126,6 @@ The install script places the standalone release into `$PREFIX/lib/d2/d2-<versio
and we recommend doing the same with manually installed releases so that you
know where the release directory is for easy uninstall.
> warn: Our binary releases aren't fully static like normal Go binaries due to the C
> dependency on v8go for executing dagre. If you're on an older system with an old
> libc, you'll want to install from source.
## From source
You can always install from source:

View file

@ -28,11 +28,14 @@ pick() {
shift
seed_file="$(mktemp)"
echo "$seed" >"$seed_file"
# We add 16 more bytes to the seed file for sufficient entropy. Otherwise both Cygwin's
# We add 32 more bytes to the seed file for sufficient entropy. Otherwise both Cygwin's
# and MinGW's sort for example complains about the lack of entropy on stderr and writes
# nothing to stdout. I'm sure there are more platforms that would too.
echo "================" >"$seed_file"
#
# We also limit to a max of 32 bytes as otherwise macOS's sort complains that the random
# seed is too large. Probably more platforms too.
(echo "$seed" && echo "================================") | head -c32 >"$seed_file"
while [ $# -gt 0 ]; do
echo "$1"
@ -94,16 +97,20 @@ _echo() {
printf '%s\n' "$*"
}
# 1-6 are regular and 9-14 are bright.
get_rand_color() {
colors=""
ncolors=$(command tput colors)
if [ "$ncolors" -ge 8 ]; then
colors="$colors 1 2 3 4 5 6"
elif [ "$ncolors" -ge 16 ]; then
colors="$colors 9 10 11 12 13 14"
if [ "${TERM_COLORS+x}" != x ]; then
TERM_COLORS=""
export TERM_COLORS
ncolors=$(TERM=${TERM:-xterm-256color} command tput colors)
if [ "$ncolors" -ge 8 ]; then
# 1-6 are regular
TERM_COLORS="$TERM_COLORS 1 2 3 4 5 6"
elif [ "$ncolors" -ge 16 ]; then
# 9-14 are bright.
TERM_COLORS="$TERM_COLORS 9 10 11 12 13 14"
fi
fi
pick "$*" $colors
pick "$*" $TERM_COLORS
}
echop() {
@ -122,14 +129,12 @@ printfp() {(
prefix="$1"
shift
if [ -z "${FGCOLOR-}" ]; then
FGCOLOR="$(get_rand_color "$prefix")"
fi
_FGCOLOR=${FGCOLOR:-$(get_rand_color "$prefix")}
should_color || true
if [ $# -eq 0 ]; then
printf '%s' "$(COLOR=$__COLOR setaf "$FGCOLOR" "$prefix")"
printf '%s' "$(COLOR=$__COLOR setaf "$_FGCOLOR" "$prefix")"
else
printf '%s: %s\n' "$(COLOR=$__COLOR setaf "$FGCOLOR" "$prefix")" "$(printf "$@")"
printf '%s: %s\n' "$(COLOR=$__COLOR setaf "$_FGCOLOR" "$prefix")" "$(printf "$@")"
fi
)}
@ -307,6 +312,10 @@ capcode() {
code=$?
set -e
}
strjoin() {
(IFS="$1"; shift; echo "$*")
}
#!/bin/sh
if [ "${LIB_FLAG-}" ]; then
return 0
@ -444,30 +453,50 @@ if [ "${LIB_RELEASE-}" ]; then
fi
LIB_RELEASE=1
goos() {
case $1 in
macos) echo darwin;;
*) echo $1;;
ensure_goos() {
if [ -n "${GOOS-}" ]; then
return
fi
ensure_os
case "$OS" in
macos) export GOOS=darwin;;
*) export GOOS=$1;;
esac
}
os() {
ensure_goarch() {
if [ -n "${GOARCH-}" ]; then
return
fi
ensure_arch
case "$ARCH" in
*) export GOARCH=$1;;
esac
}
ensure_os() {
if [ -n "${OS-}" ]; then
return
fi
uname=$(uname)
case $uname in
Linux) echo linux;;
Darwin) echo macos;;
FreeBSD) echo freebsd;;
CYGWIN_NT*|MINGW32_NT*) echo windows;;
*) echo "$uname";;
Linux) OS=linux;;
Darwin) OS=macos;;
FreeBSD) OS=freebsd;;
CYGWIN_NT*|MINGW32_NT*) OS=windows;;
*) OS=$uname;;
esac
}
arch() {
ensure_arch() {
if [ -n "${ARCH-}" ]; then
return
fi
uname_m=$(uname -m)
case $uname_m in
aarch64) echo arm64;;
x86_64) echo amd64;;
*) echo "$uname_m";;
aarch64) ARCH=arm64;;
x86_64) ARCH=amd64;;
*) ARCH=$uname_m;;
esac
}
@ -486,10 +515,35 @@ manpath() {
}
is_writable_dir() {
# The path has to exist for -w to succeed.
sh_c "mkdir -p '$1' 2>/dev/null" || true
if [ ! -w "$1" ]; then
return 1
# If it can be created, we can use it.
sh_c "mkdir -p '$1' 2>/dev/null"
}
ensure_prefix() {
if [ -n "${PREFIX-}" ]; then
return
fi
# The reason for checking whether bin is writable is that on macOS you have /usr/local
# owned by root but you don't need root to write to its subdirectories which is all we
# need to do.
if ! is_writable_dir "/usr/local/bin"; then
# This also handles M1 Mac's which do not allow modifications to /usr/local even
# with sudo.
PREFIX=$HOME/.local
else
PREFIX=/usr/local
fi
}
ensure_prefix_sh_c() {
ensure_prefix
sh_c="sh_c"
# The reason for checking whether bin is writable is that on macOS you have /usr/local
# owned by root but you don't need root to write to its subdirectories which is all we
# need to do.
if ! is_writable_dir "$PREFIX/bin"; then
sh_c="sudo_sh_c"
fi
}
#!/bin/sh
@ -503,7 +557,7 @@ help() {
fi
cat <<EOF
usage: $arg0 [-d|--dry-run] [--version vX.X.X] [--edge] [--method detect] [--prefix /usr/local]
usage: $arg0 [-d|--dry-run] [--version vX.X.X] [--edge] [--method detect] [--prefix path]
[--tala latest] [--force] [--uninstall]
install.sh automates the installation of D2 onto your system. It currently only supports
@ -513,6 +567,9 @@ docs for --detect below for more information
If you pass --edge, it will clone the source, build a release and install from it.
--edge is incompatible with --tala and currently unimplemented.
\$PREFIX in the docs below refers to the path set by --prefix. See docs on the --prefix
flag below for the default.
Flags:
-d, --dry-run
@ -540,30 +597,31 @@ Flags:
So far it only detects macOS and automatically uses homebrew.
- homebrew uses https://brew.sh/ which is a macOS and Linux package manager.
- standalone installs a standalone release archive into the unix hierarchy path
specified by --prefix which defaults to /usr/local
Ensure /usr/local/bin is in your \$PATH to use it.
specified by --prefix
--prefix /usr/local
--prefix path
Controls the unix hierarchy path into which standalone releases are installed.
Defaults to /usr/local. You may also want to use ~/.local to avoid needing sudo.
We use ~/.local by default on arm64 macOS machines as SIP now disables access to
/usr/local. Remember that whatever you use, you must have the bin directory of your
prefix path in \$PATH to execute the d2 binary. For example, if my prefix directory is
/usr/local then my \$PATH must contain /usr/local/bin.
Defaults to /usr/local or ~/.local if /usr/local is not writable by the current user.
Remember that whatever you use, you must have the bin directory of your prefix path in
\$PATH to execute the d2 binary. For example, if my prefix directory is /usr/local then
my \$PATH must contain /usr/local/bin.
You may also need to include \$PREFIX/share/man into \$MANPATH.
install.sh will tell you whether \$PATH or \$MANPATH need to be updated after successful
installation.
--tala [latest]
Install Terrastruct's closed source TALA for improved layouts.
See https://github.com/terrastruct/tala
It optionally takes an argument of the TALA version to install.
Installation obeys all other flags, just like the installation of d2. For example,
the d2plugin-tala binary will be installed into /usr/local/bin/d2plugin-tala
the d2plugin-tala binary will be installed into \$PREFIX/bin/d2plugin-tala
warn: The version may not be obeyed with package manager installations. Use
--method=standalone to enforce the version.
--force:
Force installation over the existing version even if they match. It will attempt a
uninstall first before installing the new version. The installed release tree
will be deleted from /usr/local/lib/d2/d2-<VERSION> but the release archive in
will be deleted from \$PREFIX/lib/d2/d2-<VERSION> but the release archive in
~/.cache/d2/release will remain.
--uninstall:
@ -574,7 +632,7 @@ Flags:
note: tala will also be uninstalled if installed.
All downloaded archives are cached into ~/.cache/d2/release. use \$XDG_CACHE_HOME to change
path of the cached assets. Release archives are unarchived into /usr/local/lib/d2/d2-<VERSION>
path of the cached assets. Release archives are unarchived into \$PREFIX/lib/d2/d2-<VERSION>
note: Deleting the unarchived releases will cause --uninstall to stop working.
@ -639,13 +697,9 @@ main() {
fi
REPO=${REPO:-terrastruct/d2}
OS=$(os)
ARCH=$(arch)
if [ -z "${PREFIX-}" -a "$OS" = macos -a "$ARCH" = arm64 ]; then
# M1 Mac's do not allow modifications to /usr/local even with sudo.
PREFIX=$HOME/.local
fi
PREFIX=${PREFIX:-/usr/local}
ensure_os
ensure_arch
ensure_prefix
CACHE_DIR=$(cache_dir)
mkdir -p "$CACHE_DIR"
METHOD=${METHOD:-detect}
@ -807,11 +861,7 @@ install_d2_standalone() {
asset_url=$(sh_c 'sed -n $((asset_line-3))p "$RELEASE_INFO" | sed "s/^.*: \"\(.*\)\",$/\1/g"')
fetch_gh "$asset_url" "$CACHE_DIR/$ARCHIVE" 'application/octet-stream'
sh_c="sh_c"
if ! is_prefix_writable; then
sh_c="sudo_sh_c"
fi
ensure_prefix_sh_c
"$sh_c" mkdir -p "'$INSTALL_DIR'"
"$sh_c" tar -C "$INSTALL_DIR" -xzf "$CACHE_DIR/$ARCHIVE"
"$sh_c" sh -c "'cd \"$INSTALL_DIR/d2-$VERSION\" && make install PREFIX=\"$PREFIX\"'"
@ -854,11 +904,7 @@ install_tala_standalone() {
fetch_gh "$asset_url" "$CACHE_DIR/$ARCHIVE" 'application/octet-stream'
sh_c="sh_c"
if ! is_prefix_writable; then
sh_c="sudo_sh_c"
fi
ensure_prefix_sh_c
"$sh_c" mkdir -p "'$INSTALL_DIR'"
"$sh_c" tar -C "$INSTALL_DIR" -xzf "$CACHE_DIR/$ARCHIVE"
"$sh_c" sh -c "'cd \"$INSTALL_DIR/tala-$VERSION\" && make install PREFIX=\"$PREFIX\"'"
@ -906,11 +952,7 @@ uninstall_d2_standalone() {
return 1
fi
sh_c="sh_c"
if ! is_prefix_writable; then
sh_c="sudo_sh_c"
fi
ensure_prefix_sh_c
"$sh_c" sh -c "'cd \"$INSTALL_DIR/d2-$INSTALLED_VERSION\" && make uninstall PREFIX=\"$PREFIX\"'"
"$sh_c" rm -rf "$INSTALL_DIR/d2-$INSTALLED_VERSION"
}
@ -928,11 +970,7 @@ uninstall_tala_standalone() {
return 1
fi
sh_c="sh_c"
if ! is_prefix_writable; then
sh_c="sudo_sh_c"
fi
ensure_prefix_sh_c
"$sh_c" sh -c "'cd \"$INSTALL_DIR/tala-$INSTALLED_VERSION\" && make uninstall PREFIX=\"$PREFIX\"'"
"$sh_c" rm -rf "$INSTALL_DIR/tala-$INSTALLED_VERSION"
}
@ -941,13 +979,6 @@ uninstall_tala_brew() {
sh_c brew remove tala
}
is_prefix_writable() {
# The reason for checking whether $INSTALL_DIR is writable is that on macOS you have
# /usr/local owned by root but you don't need root to write to its subdirectories which
# is all we want to do.
is_writable_dir "$INSTALL_DIR"
}
cache_dir() {
if [ -n "${XDG_CACHE_HOME-}" ]; then
echo "$XDG_CACHE_HOME/d2/release"