Merge pull request #372 from nhooyr/fix-sudo-86ee

install.sh: Improve default $PREFIX behaviour
This commit is contained in:
Anmol Sethi 2022-12-06 00:22:35 -08:00 committed by GitHub
commit 030f95441d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 372 additions and 228 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"
@ -475,7 +452,7 @@ fetch_release_info() {
fi
log "fetching info on $VERSION version of $REPO"
RELEASE_INFO=$(mktemp -d)/release-info.json
RELEASE_INFO=$(mktempd)/release-info.json
if [ "$VERSION" = latest ]; then
release_info_url="https://api.github.com/repos/$REPO/releases/$VERSION"
else

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

@ -250,10 +250,29 @@ init_remote_macos() {
fi
sleep 5
done
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'
if sh_c ssh "$REMOTE_HOST" uname -m | grep -qF arm64; then
shellenv=$(sh_c ssh "$REMOTE_HOST" /opt/homebrew/bin/brew shellenv)
else
shellenv=$(sh_c ssh "$REMOTE_HOST" /usr/local/bin/brew shellenv)
fi
if ! echo "$shellenv" | sh_c ssh "$REMOTE_HOST" "IFS= read -r regex\; \"grep -qF \\\"\\\$regex\\\" ~/.zshrc\""; then
echo "$shellenv" | sh_c ssh "$REMOTE_HOST" "\"(echo && cat) >> ~/.zshrc\""
fi
# macOS is a joke.
sh_c ssh "$REMOTE_HOST" '"rm -f ~/.ssh/environment"'
sh_c ssh "$REMOTE_HOST" '"echo PATH=\$HOME/.local/bin:\$(. ~/.zshrc && echo "\$PATH") >\$HOME/.ssh/environment"'
sh_c ssh "$REMOTE_HOST" '"echo MANPATH=\$HOME/.local/share/man:\$(. ~/.zshrc && echo "\$MANPATH") >>\$HOME/.ssh/environment"'
sh_c ssh "$REMOTE_HOST" "sudo sed -i.bak '\"s/#PermitUserEnvironment no/PermitUserEnvironment yes/\"' /etc/ssh/sshd_config"
sh_c ssh "$REMOTE_HOST" "sudo launchctl stop com.openssh.sshd"
sh_c ssh "$REMOTE_HOST" brew update
sh_c ssh "$REMOTE_HOST" brew upgrade
sh_c ssh "$REMOTE_HOST" 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

@ -14,6 +14,7 @@ set -eu
# install.sh was bundled together from
#
# - ./ci/sub/lib/rand.sh
# - ./ci/sub/lib/temp.sh
# - ./ci/sub/lib/log.sh
# - ./ci/sub/lib/flag.sh
# - ./ci/sub/lib/release.sh
@ -30,6 +31,7 @@ EOF
# script.
sh_c cat \
./ci/sub/lib/rand.sh \
./ci/sub/lib/temp.sh \
./ci/sub/lib/log.sh \
./ci/sub/lib/flag.sh \
./ci/sub/lib/release.sh \

View file

@ -13,6 +13,7 @@ sh_c cat >./ci/release/template/scripts/lib.sh <<EOF
# lib.sh was bundled together from
#
# - ./ci/sub/lib/rand.sh
# - ./ci/sub/lib/temp.sh
# - ./ci/sub/lib/log.sh
# - ./ci/sub/lib/release.sh
#
@ -25,6 +26,7 @@ EOF
# script.
sh_c cat \
./ci/sub/lib/rand.sh \
./ci/sub/lib/temp.sh \
./ci/sub/lib/log.sh \
./ci/sub/lib/release.sh \
\| sed "-e'/^\. /d'" \>\>./ci/release/template/scripts/lib.sh

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

@ -6,6 +6,7 @@
# lib.sh was bundled together from
#
# - ./ci/sub/lib/rand.sh
# - ./ci/sub/lib/temp.sh
# - ./ci/sub/lib/log.sh
# - ./ci/sub/lib/release.sh
#
@ -22,12 +23,15 @@ pick() {
seed="$1"
shift
seed_file="$(mktemp)"
echo "$seed" >"$seed_file"
# We add 16 more bytes to the seed file for sufficient entropy. Otherwise both Cygwin's
seed_file="$(mktempd)/pickseed"
# 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"
@ -37,6 +41,44 @@ pick() {
| head -n1
}
#!/bin/sh
if [ "${LIB_TEMP-}" ]; then
return 0
fi
LIB_TEMP=1
ensure_tmpdir() {
if [ -n "${_TMPDIR-}" ]; then
return
fi
_TMPDIR=$(mktemp -d)
export _TMPDIR
trap temp_exittrap EXIT
}
temp_exittrap() {
if [ -n "${_TMPDIR-}" ]; then
rm -r "$_TMPDIR"
fi
}
temppath() {
ensure_tmpdir
while true; do
temppath=$_TMPDIR/$(</dev/urandom head -c8 | base64)
if [ ! -e "$temppath" ]; then
echo "$temppath"
return
fi
done
}
mktempd() {
tp=$(temppath)
mkdir -p "$tp"
echo "$tp"
}
#!/bin/sh
if [ "${LIB_LOG-}" ]; then
return 0
fi
@ -89,16 +131,20 @@ _echo() {
printf '%s\n' "$*"
}
# 1-6 are regular and 9-14 are bright.
get_rand_color() {
colors=""
ncolors=$(command tput colors)
if [ "${TERM_COLORS+x}" != x ]; then
TERM_COLORS=""
export TERM_COLORS
ncolors=$(TERM=${TERM:-xterm-256color} command tput colors)
if [ "$ncolors" -ge 8 ]; then
colors="$colors 1 2 3 4 5 6"
# 1-6 are regular
TERM_COLORS="$TERM_COLORS 1 2 3 4 5 6"
elif [ "$ncolors" -ge 16 ]; then
colors="$colors 9 10 11 12 13 14"
# 9-14 are bright.
TERM_COLORS="$TERM_COLORS 9 10 11 12 13 14"
fi
pick "$*" $colors
fi
pick "$*" $TERM_COLORS
}
echop() {
@ -117,14 +163,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
)}
@ -215,7 +259,7 @@ sudo_sh_c() {
sh_c "su root -c '$*'"
else
caterr <<EOF
This script needs to run the following command as root:
Unable to run the following command as root:
$*
Please install doas, sudo, or su.
EOF
@ -245,11 +289,8 @@ humanpath() {
}
hide() {
out="$(mktemp)"
set +e
"$@" >"$out" 2>&1
code="$?"
set -e
out="$(mktempd)/hideout"
capcode "$@" >"$out" 2>&1
if [ "$code" -eq 0 ]; then
return
fi
@ -267,7 +308,7 @@ echo_dur() {
sponge() {
dst="$1"
tmp="$(mktemp)"
tmp="$(mktempd)/sponge"
cat > "$tmp"
cat "$tmp" > "$dst"
}
@ -302,36 +343,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=$OS;;
esac
}
os() {
ensure_goarch() {
if [ -n "${GOARCH-}" ]; then
return
fi
ensure_arch
case "$ARCH" in
*) export GOARCH=$ARCH;;
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 +415,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 b29dc9d5bfce87576fd15b3cee2249762b7f0d3d

View file

@ -236,6 +236,9 @@ func (gs *dslGenState) randShape() string {
if s != "image" {
return s
}
if s != "sequence_diagram" {
return s
}
}
}

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

@ -7,6 +7,7 @@ set -eu
# install.sh was bundled together from
#
# - ./ci/sub/lib/rand.sh
# - ./ci/sub/lib/temp.sh
# - ./ci/sub/lib/log.sh
# - ./ci/sub/lib/flag.sh
# - ./ci/sub/lib/release.sh
@ -27,12 +28,15 @@ pick() {
seed="$1"
shift
seed_file="$(mktemp)"
echo "$seed" >"$seed_file"
# We add 16 more bytes to the seed file for sufficient entropy. Otherwise both Cygwin's
seed_file="$(mktempd)/pickseed"
# 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"
@ -42,6 +46,44 @@ pick() {
| head -n1
}
#!/bin/sh
if [ "${LIB_TEMP-}" ]; then
return 0
fi
LIB_TEMP=1
ensure_tmpdir() {
if [ -n "${_TMPDIR-}" ]; then
return
fi
_TMPDIR=$(mktemp -d)
export _TMPDIR
trap temp_exittrap EXIT
}
temp_exittrap() {
if [ -n "${_TMPDIR-}" ]; then
rm -r "$_TMPDIR"
fi
}
temppath() {
ensure_tmpdir
while true; do
temppath=$_TMPDIR/$(</dev/urandom head -c8 | base64)
if [ ! -e "$temppath" ]; then
echo "$temppath"
return
fi
done
}
mktempd() {
tp=$(temppath)
mkdir -p "$tp"
echo "$tp"
}
#!/bin/sh
if [ "${LIB_LOG-}" ]; then
return 0
fi
@ -94,16 +136,20 @@ _echo() {
printf '%s\n' "$*"
}
# 1-6 are regular and 9-14 are bright.
get_rand_color() {
colors=""
ncolors=$(command tput colors)
if [ "${TERM_COLORS+x}" != x ]; then
TERM_COLORS=""
export TERM_COLORS
ncolors=$(TERM=${TERM:-xterm-256color} command tput colors)
if [ "$ncolors" -ge 8 ]; then
colors="$colors 1 2 3 4 5 6"
# 1-6 are regular
TERM_COLORS="$TERM_COLORS 1 2 3 4 5 6"
elif [ "$ncolors" -ge 16 ]; then
colors="$colors 9 10 11 12 13 14"
# 9-14 are bright.
TERM_COLORS="$TERM_COLORS 9 10 11 12 13 14"
fi
pick "$*" $colors
fi
pick "$*" $TERM_COLORS
}
echop() {
@ -122,14 +168,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
)}
@ -220,7 +264,7 @@ sudo_sh_c() {
sh_c "su root -c '$*'"
else
caterr <<EOF
This script needs to run the following command as root:
Unable to run the following command as root:
$*
Please install doas, sudo, or su.
EOF
@ -250,11 +294,8 @@ humanpath() {
}
hide() {
out="$(mktemp)"
set +e
"$@" >"$out" 2>&1
code="$?"
set -e
out="$(mktempd)/hideout"
capcode "$@" >"$out" 2>&1
if [ "$code" -eq 0 ]; then
return
fi
@ -272,7 +313,7 @@ echo_dur() {
sponge() {
dst="$1"
tmp="$(mktemp)"
tmp="$(mktempd)/sponge"
cat > "$tmp"
cat "$tmp" > "$dst"
}
@ -307,6 +348,10 @@ capcode() {
code=$?
set -e
}
strjoin() {
(IFS="$1"; shift; echo "$*")
}
#!/bin/sh
if [ "${LIB_FLAG-}" ]; then
return 0
@ -444,30 +489,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=$OS;;
esac
}
os() {
ensure_goarch() {
if [ -n "${GOARCH-}" ]; then
return
fi
ensure_arch
case "$ARCH" in
*) export GOARCH=$ARCH;;
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 +551,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 +593,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 +603,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 +633,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 +668,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 +733,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 +897,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 +940,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 +988,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 +1006,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 +1015,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"
@ -964,7 +1031,7 @@ fetch_release_info() {
fi
log "fetching info on $VERSION version of $REPO"
RELEASE_INFO=$(mktemp -d)/release-info.json
RELEASE_INFO=$(mktempd)/release-info.json
if [ "$VERSION" = latest ]; then
release_info_url="https://api.github.com/repos/$REPO/releases/$VERSION"
else