diff --git a/ci/release/_build.sh b/ci/release/_build.sh index 4abfd6dec..584d6b6ee 100755 --- a/ci/release/_build.sh +++ b/ci/release/_build.sh @@ -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 diff --git a/ci/release/_install.sh b/ci/release/_install.sh index 50b4e5e3b..f24d5c8ad 100755 --- a/ci/release/_install.sh +++ b/ci/release/_install.sh @@ -14,7 +14,7 @@ help() { fi cat < but the release archive in + will be deleted from \$PREFIX/lib/d2/d2- 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- +path of the cached assets. Release archives are unarchived into \$PREFIX/lib/d2/d2- 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 diff --git a/ci/release/build.sh b/ci/release/build.sh index 8865e9c33..13c61b790 100755 --- a/ci/release/build.sh +++ b/ci/release/build.sh @@ -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-} \ diff --git a/ci/release/builders/aws_ensure.sh b/ci/release/builders/aws_ensure.sh index bfb630ed0..4a8a46b6f 100755 --- a/ci/release/builders/aws_ensure.sh +++ b/ci/release/builders/aws_ensure.sh @@ -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 "$@" diff --git a/ci/release/changelogs/next.md b/ci/release/changelogs/next.md index 8de782117..65b8a9193 100644 --- a/ci/release/changelogs/next.md +++ b/ci/release/changelogs/next.md @@ -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 ⛑️ diff --git a/ci/release/gen_install.sh b/ci/release/gen_install.sh index 082bdcc52..bd541106e 100755 --- a/ci/release/gen_install.sh +++ b/ci/release/gen_install.sh @@ -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 \ diff --git a/ci/release/gen_template_lib.sh b/ci/release/gen_template_lib.sh index 27e63ac9e..01eced365 100755 --- a/ci/release/gen_template_lib.sh +++ b/ci/release/gen_template_lib.sh @@ -13,6 +13,7 @@ sh_c cat >./ci/release/template/scripts/lib.sh <\>./ci/release/template/scripts/lib.sh diff --git a/ci/release/template/Makefile b/ci/release/template/Makefile index 44f22ddb0..df6648609 100644 --- a/ci/release/template/Makefile +++ b/ci/release/template/Makefile @@ -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 diff --git a/ci/release/template/README.md.sh b/ci/release/template/README.md.sh index ec7cb8a11..2c0cdfb35 100755 --- a/ci/release/template/README.md.sh +++ b/ci/release/template/README.md.sh @@ -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 diff --git a/ci/release/template/scripts/install.sh b/ci/release/template/scripts/install.sh index 4127f25a8..1db6c5d1f 100755 --- a/ci/release/template/scripts/install.sh +++ b/ci/release/template/scripts/install.sh @@ -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" diff --git a/ci/release/template/scripts/lib.sh b/ci/release/template/scripts/lib.sh index 2b3769999..759a95202 100644 --- a/ci/release/template/scripts/lib.sh +++ b/ci/release/template/scripts/lib.sh @@ -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/$("$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 } diff --git a/ci/release/template/scripts/uninstall.sh b/ci/release/template/scripts/uninstall.sh index 56336e8d1..686335c9e 100755 --- a/ci/release/template/scripts/uninstall.sh +++ b/ci/release/template/scripts/uninstall.sh @@ -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" } diff --git a/ci/sub b/ci/sub index ad50bf9f1..b29dc9d5b 160000 --- a/ci/sub +++ b/ci/sub @@ -1 +1 @@ -Subproject commit ad50bf9f1e302bfb1a4d3c6bb821f90979083265 +Subproject commit b29dc9d5bfce87576fd15b3cee2249762b7f0d3d diff --git a/d2chaos/d2chaos.go b/d2chaos/d2chaos.go index 8b6c74f5e..c6cf4937c 100644 --- a/d2chaos/d2chaos.go +++ b/d2chaos/d2chaos.go @@ -236,6 +236,9 @@ func (gs *dslGenState) randShape() string { if s != "image" { return s } + if s != "sequence_diagram" { + return s + } } } diff --git a/docs/INSTALL.md b/docs/INSTALL.md index 97ec69d1b..3238211aa 100644 --- a/docs/INSTALL.md +++ b/docs/INSTALL.md @@ -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- 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: diff --git a/install.sh b/install.sh index 2f577321d..a2788292a 100755 --- a/install.sh +++ b/install.sh @@ -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/$("$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 < but the release archive in + will be deleted from \$PREFIX/lib/d2/d2- 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- +path of the cached assets. Release archives are unarchived into \$PREFIX/lib/d2/d2- 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