Merge branch 'master' into bernie/export-png
This commit is contained in:
commit
e8340ff610
28 changed files with 1099 additions and 394 deletions
26
.github/workflows/ci.yml
vendored
26
.github/workflows/ci.yml
vendored
|
|
@ -11,7 +11,7 @@ jobs:
|
|||
- uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: recursive
|
||||
- run: TERM=xterm-256color ./make.sh assert-linear
|
||||
- run: COLOR=1 ./make.sh assert-linear
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets._GITHUB_TOKEN }}
|
||||
DISCORD_WEBHOOK_URL: ${{ secrets.DISCORD_WEBHOOK_URL }}
|
||||
|
|
@ -25,7 +25,21 @@ jobs:
|
|||
with:
|
||||
go-version-file: ./go.mod
|
||||
cache: true
|
||||
- run: TERM=xterm-256color ./make.sh fmt
|
||||
- run: COLOR=1 ./make.sh fmt
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets._GITHUB_TOKEN }}
|
||||
DISCORD_WEBHOOK_URL: ${{ secrets.DISCORD_WEBHOOK_URL }}
|
||||
gen:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: recursive
|
||||
- uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version-file: ./go.mod
|
||||
cache: true
|
||||
- run: COLOR=1 ./make.sh gen
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets._GITHUB_TOKEN }}
|
||||
DISCORD_WEBHOOK_URL: ${{ secrets.DISCORD_WEBHOOK_URL }}
|
||||
|
|
@ -39,7 +53,7 @@ jobs:
|
|||
with:
|
||||
go-version-file: ./go.mod
|
||||
cache: true
|
||||
- run: TERM=xterm-256color ./make.sh lint
|
||||
- run: COLOR=1 ./make.sh lint
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets._GITHUB_TOKEN }}
|
||||
DISCORD_WEBHOOK_URL: ${{ secrets.DISCORD_WEBHOOK_URL }}
|
||||
|
|
@ -53,7 +67,7 @@ jobs:
|
|||
with:
|
||||
go-version-file: ./go.mod
|
||||
cache: true
|
||||
- run: TERM=xterm-256color ./make.sh build
|
||||
- run: COLOR=1 ./make.sh build
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets._GITHUB_TOKEN }}
|
||||
DISCORD_WEBHOOK_URL: ${{ secrets.DISCORD_WEBHOOK_URL }}
|
||||
|
|
@ -67,7 +81,7 @@ jobs:
|
|||
with:
|
||||
go-version-file: ./go.mod
|
||||
cache: true
|
||||
- run: TERM=xterm-256color ./make.sh test
|
||||
- run: COLOR=1 ./make.sh test
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets._GITHUB_TOKEN }}
|
||||
DISCORD_WEBHOOK_URL: ${{ secrets.DISCORD_WEBHOOK_URL }}
|
||||
|
|
@ -86,7 +100,7 @@ jobs:
|
|||
with:
|
||||
go-version-file: ./go.mod
|
||||
cache: true
|
||||
- run: TERM=xterm-256color ./make.sh race
|
||||
- run: COLOR=1 ./make.sh race
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets._GITHUB_TOKEN }}
|
||||
DISCORD_WEBHOOK_URL: ${{ secrets.DISCORD_WEBHOOK_URL }}
|
||||
|
|
|
|||
2
.github/workflows/daily.yml
vendored
2
.github/workflows/daily.yml
vendored
|
|
@ -20,7 +20,7 @@ jobs:
|
|||
with:
|
||||
go-version-file: ./go.mod
|
||||
cache: true
|
||||
- run: CI_ALL=1 TERM=xterm-256color ./make.sh
|
||||
- run: CI_ALL=1 COLOR=1 ./make.sh
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets._GITHUB_TOKEN }}
|
||||
DISCORD_WEBHOOK_URL: ${{ secrets.DISCORD_WEBHOOK_URL }}
|
||||
|
|
|
|||
5
Makefile
5
Makefile
|
|
@ -1,7 +1,7 @@
|
|||
.POSIX:
|
||||
|
||||
.PHONY: all
|
||||
all: fmt lint build test
|
||||
all: fmt gen lint build test
|
||||
ifdef CI
|
||||
all: assert-linear
|
||||
endif
|
||||
|
|
@ -9,6 +9,9 @@ endif
|
|||
.PHONY: fmt
|
||||
fmt:
|
||||
prefix "$@" ./ci/sub/fmt/make.sh
|
||||
.PHONY: gen
|
||||
gen:
|
||||
prefix "$@" ./ci/gen.sh
|
||||
.PHONY: lint
|
||||
lint:
|
||||
prefix "$@" go vet --composites=false ./...
|
||||
|
|
|
|||
30
README.md
30
README.md
|
|
@ -22,8 +22,6 @@
|
|||
|
||||
- [Quickstart](#quickstart)
|
||||
- [Install](#install)
|
||||
* [Install script](#install-script)
|
||||
* [Install from source](#install-from-source)
|
||||
- [D2 as a library](#d2-as-a-library)
|
||||
- [Themes](#themes)
|
||||
- [Fonts](#fonts)
|
||||
|
|
@ -58,42 +56,20 @@ A browser window will open with `out.svg` and live-reload on changes to `in.d2`.
|
|||
|
||||
## Install
|
||||
|
||||
### Install script
|
||||
|
||||
The recommended way to install is to run our install script, which will figure out the
|
||||
best way to install based on your machine.
|
||||
The easiest way to install is with our install script:
|
||||
|
||||
```sh
|
||||
# With --dry-run the install script will print the commands it will use
|
||||
# to install without actually installing so you know what it's going to do.
|
||||
curl -fsSL https://d2lang.com/install.sh | sh -s -- --dry-run
|
||||
# If things look good, install for real.
|
||||
curl -fsSL https://d2lang.com/install.sh | sh -s --
|
||||
```
|
||||
|
||||
We have precompiled binaries on the [releases](https://github.com/terrastruct/d2/releases)
|
||||
page for macOS and Linux. For both amd64 and arm64. We will release package manager
|
||||
distributions like .rpm, .deb soon. We also want to get D2 on Homebrew for macOS
|
||||
and release a docker image.
|
||||
|
||||
To uninstall:
|
||||
|
||||
```sh
|
||||
curl -fsSL https://d2lang.com/install.sh | sh -s -- --uninstall --dry-run
|
||||
# If things look good, uninstall for real.
|
||||
curl -fsSL https://d2lang.com/install.sh | sh -s -- --uninstall
|
||||
```
|
||||
|
||||
> warn: Our binary releases aren't fully portable like normal Go binaries due to the C
|
||||
> dependency on v8go for executing dagre.
|
||||
|
||||
### Install from source
|
||||
|
||||
Alternatively, you can install from source:
|
||||
|
||||
```sh
|
||||
go install oss.terrastruct.com/d2
|
||||
```
|
||||
For detailed installation docs, with alternative methods and examples for each OS, see
|
||||
[./docs/INSTALL.md](./docs/INSTALL.md).
|
||||
|
||||
## D2 as a library
|
||||
|
||||
|
|
|
|||
11
ci/gen.sh
Executable file
11
ci/gen.sh
Executable file
|
|
@ -0,0 +1,11 @@
|
|||
#!/bin/sh
|
||||
set -eu
|
||||
cd -- "$(dirname "$0")/.."
|
||||
. ./ci/sub/lib.sh
|
||||
|
||||
./ci/release/gen_install.sh
|
||||
./ci/release/gen_template_lib.sh
|
||||
|
||||
if [ -n "${CI-}" ]; then
|
||||
git_assert_clean
|
||||
fi
|
||||
|
|
@ -18,8 +18,11 @@ usage: $arg0 [--dry-run] [--version vX.X.X] [--edge] [--method detect] [--prefix
|
|||
[--tala latest] [--force] [--uninstall]
|
||||
|
||||
install.sh automates the installation of D2 onto your system. It currently only supports
|
||||
the installation of standalone releases from GitHub. If you pass --edge, it will clone the
|
||||
source, build a release and install from it.
|
||||
the installation of standalone releases from GitHub and via Homebrew on macOS. See the
|
||||
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.
|
||||
|
||||
Flags:
|
||||
|
||||
|
|
@ -29,6 +32,8 @@ Flags:
|
|||
|
||||
--version vX.X.X
|
||||
Pass to have install.sh install the given version instead of the latest version.
|
||||
warn: The version may not be obeyed with package manager installations. Use
|
||||
--method=standalone to enforce the version.
|
||||
|
||||
--edge
|
||||
Pass to build and install D2 from source. This will still use --method if set to detect
|
||||
|
|
@ -36,14 +41,15 @@ Flags:
|
|||
if an unsupported package manager is used. To install from source like a dev would,
|
||||
use go install oss.terrastruct.com/d2
|
||||
note: currently unimplemented.
|
||||
warn: incompatible with --tala as TALA is closed source.
|
||||
|
||||
--method [detect | standalone]
|
||||
--method [detect | standalone | homebrew ]
|
||||
Pass to control the method by which to install. Right now we only support standalone
|
||||
releases from GitHub but later we'll add support for brew, rpm, deb and more.
|
||||
note: currently unimplemented.
|
||||
|
||||
- detect is currently unimplemented but would use your OS's package manager
|
||||
automatically.
|
||||
- detect will use your OS's package manager automatically.
|
||||
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.
|
||||
|
|
@ -51,16 +57,19 @@ Flags:
|
|||
--prefix /usr/local
|
||||
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.
|
||||
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
|
||||
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.
|
||||
|
||||
--tala [latest]
|
||||
Install Terrastruct's closed source TALA for improved layouts.
|
||||
See https://github.com/terrastruct/TALA
|
||||
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
|
||||
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
|
||||
|
|
@ -73,6 +82,7 @@ Flags:
|
|||
as for installation. i.e if you used --method standalone you must again use --method
|
||||
standalone for uninstallation. With detect, the install script will try to use the OS
|
||||
package manager to uninstall instead.
|
||||
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>
|
||||
|
|
@ -85,9 +95,7 @@ EOF
|
|||
}
|
||||
|
||||
main() {
|
||||
METHOD=standalone
|
||||
while :; do
|
||||
flag_parse "$@"
|
||||
while flag_parse "$@"; do
|
||||
case "$FLAG" in
|
||||
h|help)
|
||||
help
|
||||
|
|
@ -114,8 +122,6 @@ main() {
|
|||
method)
|
||||
flag_nonemptyarg && shift "$FLAGSHIFT"
|
||||
METHOD=$FLAGARG
|
||||
echoerr "$FLAGRAW is currently unimplemented"
|
||||
return 1
|
||||
;;
|
||||
prefix)
|
||||
flag_nonemptyarg && shift "$FLAGSHIFT"
|
||||
|
|
@ -129,15 +135,12 @@ main() {
|
|||
flag_noarg && shift "$FLAGSHIFT"
|
||||
UNINSTALL=1
|
||||
;;
|
||||
'')
|
||||
shift "$FLAGSHIFT"
|
||||
break
|
||||
;;
|
||||
*)
|
||||
flag_errusage "unrecognized flag $FLAGRAW"
|
||||
;;
|
||||
esac
|
||||
done
|
||||
shift "$FLAGSHIFT"
|
||||
|
||||
if [ $# -gt 0 ]; then
|
||||
flag_errusage "no arguments are accepted"
|
||||
|
|
@ -153,49 +156,86 @@ main() {
|
|||
PREFIX=${PREFIX:-/usr/local}
|
||||
CACHE_DIR=$(cache_dir)
|
||||
mkdir -p "$CACHE_DIR"
|
||||
METHOD=${METHOD:-detect}
|
||||
INSTALL_DIR=$PREFIX/lib/d2
|
||||
|
||||
case $METHOD in
|
||||
detect)
|
||||
case "$OS" in
|
||||
macos)
|
||||
if command -v brew >/dev/null; then
|
||||
log "detected macOS with homebrew, using homebrew for (un)installation"
|
||||
METHOD=homebrew
|
||||
else
|
||||
warn "detected macOS without homebrew, falling back to --method=standalone"
|
||||
METHOD=standalone
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
warn "unrecognized OS $OS, falling back to --method=standalone"
|
||||
METHOD=standalone
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
standalone) ;;
|
||||
homebrew) ;;
|
||||
*)
|
||||
echoerr "unknown (un)installation method $METHOD"
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
|
||||
if [ -n "${UNINSTALL-}" ]; then
|
||||
uninstall
|
||||
return 0
|
||||
if [ -n "${DRY_RUN-}" ]; then
|
||||
log "Rerun without --dry-run to execute printed commands and perform uninstall."
|
||||
fi
|
||||
|
||||
VERSION=${VERSION:-latest}
|
||||
if [ "$VERSION" = latest ]; then
|
||||
header "fetching latest release info"
|
||||
fetch_release_info
|
||||
fi
|
||||
|
||||
else
|
||||
install
|
||||
if [ -n "${DRY_RUN-}" ]; then
|
||||
log "Rerun without --dry-run to execute printed commands and perform install."
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
install() {
|
||||
install_d2
|
||||
case $METHOD in
|
||||
standalone)
|
||||
install_d2_standalone
|
||||
if [ -n "${TALA-}" ]; then
|
||||
# Run in subshell to avoid overwriting VERSION.
|
||||
TALA_VERSION="$( install_tala && echo "$VERSION" )"
|
||||
TALA_VERSION="$( RELEASE_INFO= install_tala_standalone && echo "$VERSION" )"
|
||||
fi
|
||||
;;
|
||||
homebrew)
|
||||
install_d2_brew
|
||||
if [ -n "${TALA-}" ]; then install_tala_brew; fi
|
||||
;;
|
||||
esac
|
||||
|
||||
COLOR=2 header success
|
||||
FGCOLOR=2 bigheader 'next steps'
|
||||
case $METHOD in
|
||||
standalone) install_post_standalone ;;
|
||||
homebrew) install_post_brew ;;
|
||||
esac
|
||||
}
|
||||
|
||||
install_post_standalone() {
|
||||
log "d2-$VERSION-$OS-$ARCH has been successfully installed into $PREFIX"
|
||||
if [ -n "${TALA-}" ]; then
|
||||
log "tala-$TALA_VERSION-$OS-$ARCH has been successfully installed into $PREFIX"
|
||||
fi
|
||||
log "Rerun this install script with --uninstall to uninstall"
|
||||
log "Rerun this install script with --uninstall to uninstall."
|
||||
log
|
||||
if ! echo "$PATH" | grep -qF "$PREFIX/bin"; then
|
||||
logcat >&2 <<EOF
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
NEXT STEPS
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
Extend your \$PATH to use d2:
|
||||
export PATH=$PREFIX/bin:\$PATH
|
||||
Then run:
|
||||
${TALA+D2_LAYOUT=tala }d2 --help
|
||||
EOF
|
||||
else
|
||||
log " Run ${TALA+D2_LAYOUT=tala }d2 --help for usage."
|
||||
log "Run ${TALA+D2_LAYOUT=tala }d2 --help for usage."
|
||||
fi
|
||||
if ! manpath | grep -qF "$PREFIX/share/man"; then
|
||||
logcat >&2 <<EOF
|
||||
|
|
@ -208,36 +248,61 @@ EOF
|
|||
log " man d2plugin-tala"
|
||||
fi
|
||||
else
|
||||
log " Run man d2 for detailed docs."
|
||||
log "Run man d2 for detailed docs."
|
||||
if [ -n "${TALA-}" ]; then
|
||||
log " Run man d2plugin-tala for detailed docs."
|
||||
log "Run man d2plugin-tala for detailed TALA docs."
|
||||
fi
|
||||
fi
|
||||
logcat >&2 <<EOF
|
||||
|
||||
Something not working? Please let us know:
|
||||
https://github.com/terrastruct/d2/issues/new
|
||||
https://github.com/terrastruct/d2/issues
|
||||
https://github.com/terrastruct/d2/discussions
|
||||
https://discord.gg/NF6X8K4eDq
|
||||
EOF
|
||||
}
|
||||
|
||||
install_d2() {
|
||||
install_post_brew() {
|
||||
log "d2 has been successfully installed with homebrew."
|
||||
if [ -n "${TALA-}" ]; then
|
||||
log "tala has been successfully installed with homebrew."
|
||||
fi
|
||||
log "Rerun this install script with --uninstall to uninstall."
|
||||
log
|
||||
log "Run ${TALA+D2_LAYOUT=tala }d2 --help for usage."
|
||||
log "Run man d2 for detailed docs."
|
||||
if [ -n "${TALA-}" ]; then
|
||||
log "Run man d2plugin-tala for detailed TALA docs."
|
||||
fi
|
||||
logcat >&2 <<EOF
|
||||
|
||||
Something not working? Please let us know:
|
||||
https://github.com/terrastruct/d2/issues
|
||||
https://github.com/terrastruct/d2/discussions
|
||||
https://discord.gg/NF6X8K4eDq
|
||||
EOF
|
||||
}
|
||||
|
||||
install_d2_standalone() {
|
||||
VERSION=${VERSION:-latest}
|
||||
header "installing d2-$VERSION"
|
||||
|
||||
if [ "$VERSION" = latest ]; then
|
||||
fetch_release_info
|
||||
fi
|
||||
|
||||
if command -v d2 >/dev/null; then
|
||||
INSTALLED_VERSION="$(d2 version)"
|
||||
if [ ! "${FORCE-}" -a "$VERSION" = "$INSTALLED_VERSION" ]; then
|
||||
log "skipping installation as version $VERSION is already installed."
|
||||
log "skipping installation as d2 $VERSION is already installed."
|
||||
return 0
|
||||
fi
|
||||
log "uninstalling $INSTALLED_VERSION to install $VERSION"
|
||||
if ! uninstall_d2; then
|
||||
warn "failed to uninstall $INSTALLED_VERSION"
|
||||
log "uninstalling d2 $INSTALLED_VERSION to install $VERSION"
|
||||
if ! uninstall_d2_standalone; then
|
||||
warn "failed to uninstall d2 $INSTALLED_VERSION"
|
||||
fi
|
||||
fi
|
||||
|
||||
header "installing d2-$VERSION"
|
||||
install_standalone_d2
|
||||
}
|
||||
|
||||
install_standalone_d2() {
|
||||
ARCHIVE="d2-$VERSION-$OS-$ARCH.tar.gz"
|
||||
log "installing standalone release $ARCHIVE from github"
|
||||
|
||||
|
|
@ -256,19 +321,38 @@ install_standalone_d2() {
|
|||
"$sh_c" sh -c "'cd \"$INSTALL_DIR/d2-$VERSION\" && make install PREFIX=\"$PREFIX\"'"
|
||||
}
|
||||
|
||||
install_tala() {
|
||||
REPO="${REPO_TALA:-terrastruct/TALA}"
|
||||
VERSION=$TALA
|
||||
RELEASE_INFO=
|
||||
fetch_release_info
|
||||
header "installing tala-$VERSION"
|
||||
install_standalone_tala
|
||||
install_d2_brew() {
|
||||
header "installing d2 with homebrew"
|
||||
sh_c brew tap terrastruct/d2
|
||||
sh_c brew install d2
|
||||
}
|
||||
|
||||
install_standalone_tala() {
|
||||
install_tala_standalone() {
|
||||
REPO="${REPO_TALA:-terrastruct/tala}"
|
||||
VERSION=$TALA
|
||||
|
||||
header "installing tala-$VERSION"
|
||||
|
||||
if [ "$VERSION" = latest ]; then
|
||||
fetch_release_info
|
||||
fi
|
||||
|
||||
if command -v d2plugin-tala >/dev/null; then
|
||||
INSTALLED_VERSION="$(d2plugin-tala --version)"
|
||||
if [ ! "${FORCE-}" -a "$VERSION" = "$INSTALLED_VERSION" ]; then
|
||||
log "skipping installation as tala $VERSION is already installed."
|
||||
return 0
|
||||
fi
|
||||
log "uninstalling tala $INSTALLED_VERSION to install $VERSION"
|
||||
if ! uninstall_tala_standalone; then
|
||||
warn "failed to uninstall tala $INSTALLED_VERSION"
|
||||
fi
|
||||
fi
|
||||
|
||||
ARCHIVE="tala-$VERSION-$OS-$ARCH.tar.gz"
|
||||
log "installing standalone release $ARCHIVE from github"
|
||||
|
||||
fetch_release_info
|
||||
asset_line=$(sh_c 'cat "$RELEASE_INFO" | grep -n "$ARCHIVE" | cut -d: -f1 | head -n1')
|
||||
asset_url=$(sh_c 'sed -n $((asset_line-3))p "$RELEASE_INFO" | sed "s/^.*: \"\(.*\)\",$/\1/g"')
|
||||
|
||||
|
|
@ -284,36 +368,40 @@ install_standalone_tala() {
|
|||
"$sh_c" sh -c "'cd \"$INSTALL_DIR/tala-$VERSION\" && make install PREFIX=\"$PREFIX\"'"
|
||||
}
|
||||
|
||||
install_tala_brew() {
|
||||
header "installing tala with homebrew"
|
||||
sh_c brew tap terrastruct/d2
|
||||
sh_c brew install tala
|
||||
}
|
||||
|
||||
uninstall() {
|
||||
# We uninstall tala first as package managers require that it be uninstalled before
|
||||
# uninstalling d2 as TALA depends on d2.
|
||||
if command -v d2plugin-tala >/dev/null; then
|
||||
INSTALLED_VERSION="$(d2plugin-tala --version)"
|
||||
header "uninstalling tala-$INSTALLED_VERSION"
|
||||
case $METHOD in
|
||||
standalone) uninstall_tala_standalone ;;
|
||||
homebrew) uninstall_tala_brew ;;
|
||||
esac
|
||||
elif [ "${TALA-}" ]; then
|
||||
warn "no version of tala installed"
|
||||
fi
|
||||
|
||||
if ! command -v d2 >/dev/null; then
|
||||
warn "no version of d2 installed"
|
||||
return 0
|
||||
fi
|
||||
|
||||
INSTALLED_VERSION="$(d2 --version)"
|
||||
if ! uninstall_d2; then
|
||||
echoerr "failed to uninstall $INSTALLED_VERSION"
|
||||
return 1
|
||||
fi
|
||||
if [ "${TALA-}" ]; then
|
||||
if ! command -v d2plugin-tala >/dev/null; then
|
||||
warn "no version of tala installed"
|
||||
return 0
|
||||
fi
|
||||
INSTALLED_VERSION="$(d2plugin-tala --version)"
|
||||
if ! uninstall_tala; then
|
||||
echoerr "failed to uninstall tala $INSTALLED_VERSION"
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
uninstall_d2() {
|
||||
header "uninstalling d2-$INSTALLED_VERSION"
|
||||
uninstall_standalone_d2
|
||||
case $METHOD in
|
||||
standalone) uninstall_d2_standalone ;;
|
||||
homebrew) uninstall_d2_brew ;;
|
||||
esac
|
||||
}
|
||||
|
||||
uninstall_standalone_d2() {
|
||||
uninstall_d2_standalone() {
|
||||
log "uninstalling standalone release of d2-$INSTALLED_VERSION"
|
||||
|
||||
if [ ! -e "$INSTALL_DIR/d2-$INSTALLED_VERSION" ]; then
|
||||
|
|
@ -331,12 +419,11 @@ uninstall_standalone_d2() {
|
|||
"$sh_c" rm -rf "$INSTALL_DIR/d2-$INSTALLED_VERSION"
|
||||
}
|
||||
|
||||
uninstall_tala() {
|
||||
header "uninstalling tala-$INSTALLED_VERSION"
|
||||
uninstall_standalone_tala
|
||||
uninstall_d2_brew() {
|
||||
sh_c brew remove d2
|
||||
}
|
||||
|
||||
uninstall_standalone_tala() {
|
||||
uninstall_tala_standalone() {
|
||||
log "uninstalling standalone release tala-$INSTALLED_VERSION"
|
||||
|
||||
if [ ! -e "$INSTALL_DIR/tala-$INSTALLED_VERSION" ]; then
|
||||
|
|
@ -354,6 +441,10 @@ uninstall_standalone_tala() {
|
|||
"$sh_c" rm -rf "$INSTALL_DIR/tala-$INSTALLED_VERSION"
|
||||
}
|
||||
|
||||
uninstall_tala_brew() {
|
||||
sh_c brew remove tala
|
||||
}
|
||||
|
||||
is_prefix_writable() {
|
||||
sh_c "mkdir -p '$INSTALL_DIR' 2>/dev/null" || true
|
||||
# The reason for checking whether $INSTALL_DIR is writable is that on macOS you have
|
||||
|
|
@ -409,4 +500,9 @@ fetch_gh() {
|
|||
sh_c mv "$file.inprogress" "$file"
|
||||
}
|
||||
|
||||
brew() {
|
||||
# Makes brew sane.
|
||||
HOMEBREW_NO_INSTALL_CLEANUP=1 HOMEBREW_NO_AUTO_UPDATE=1 command brew "$@"
|
||||
}
|
||||
|
||||
main "$@"
|
||||
|
|
|
|||
|
|
@ -42,8 +42,7 @@ EOF
|
|||
}
|
||||
|
||||
main() {
|
||||
while :; do
|
||||
flag_parse "$@"
|
||||
while flag_parse "$@"; do
|
||||
case "$FLAG" in
|
||||
h|help)
|
||||
help
|
||||
|
|
@ -78,16 +77,12 @@ main() {
|
|||
flag_noarg && shift "$FLAGSHIFT"
|
||||
LOCKFILE_FORCE=1
|
||||
;;
|
||||
'')
|
||||
shift "$FLAGSHIFT"
|
||||
break
|
||||
;;
|
||||
*)
|
||||
flag_errusage "unrecognized flag $FLAGRAW"
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
shift "$FLAGSHIFT"
|
||||
if [ $# -gt 0 ]; then
|
||||
flag_errusage "no arguments are accepted"
|
||||
fi
|
||||
|
|
@ -169,16 +164,16 @@ build_local() {
|
|||
|
||||
build_remote_macos() {
|
||||
sh_c lockfile_ssh "$REMOTE_HOST" .d2-build-lock
|
||||
trap unlockfile_ssh EXIT
|
||||
sh_c ssh "$REMOTE_HOST" mkdir -p src
|
||||
sh_c rsync --archive --human-readable --delete ./ "$REMOTE_HOST:src/d2/"
|
||||
sh_c ssh "$REMOTE_HOST" "DRY_RUN=${DRY_RUN-} \
|
||||
sh_c ssh "$REMOTE_HOST" "COLOR=${COLOR-} \
|
||||
TERM=${TERM-} \
|
||||
DRY_RUN=${DRY_RUN-} \
|
||||
HW_BUILD_DIR=$HW_BUILD_DIR \
|
||||
VERSION=$VERSION \
|
||||
OS=$OS \
|
||||
ARCH=$ARCH \
|
||||
ARCHIVE=$ARCHIVE \
|
||||
TERM=$TERM \
|
||||
PATH=\\\"/usr/local/bin:/usr/local/sbin:/opt/homebrew/bin:/opt/homebrew/sbin\\\${PATH+:\\\$PATH}\\\" \
|
||||
./src/d2/ci/release/_build.sh"
|
||||
sh_c mkdir -p "$HW_BUILD_DIR"
|
||||
|
|
@ -187,16 +182,16 @@ 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
|
||||
trap unlockfile_ssh EXIT
|
||||
sh_c ssh "$REMOTE_HOST" mkdir -p src
|
||||
sh_c rsync --archive --human-readable --delete ./ "$REMOTE_HOST:src/d2/"
|
||||
sh_c ssh "$REMOTE_HOST" "DRY_RUN=${DRY_RUN-} \
|
||||
sh_c ssh "$REMOTE_HOST" "COLOR=${COLOR-} \
|
||||
TERM=${TERM-} \
|
||||
DRY_RUN=${DRY_RUN-} \
|
||||
HW_BUILD_DIR=$HW_BUILD_DIR \
|
||||
VERSION=$VERSION \
|
||||
OS=$OS \
|
||||
ARCH=$ARCH \
|
||||
ARCHIVE=$ARCHIVE \
|
||||
TERM=$TERM \
|
||||
./src/d2/ci/release/build_docker.sh"
|
||||
sh_c mkdir -p "$HW_BUILD_DIR"
|
||||
sh_c rsync --archive --human-readable "$REMOTE_HOST:src/d2/$ARCHIVE" "$ARCHIVE"
|
||||
|
|
|
|||
|
|
@ -13,5 +13,4 @@ docker_run \
|
|||
-e OS \
|
||||
-e ARCH \
|
||||
-e ARCHIVE \
|
||||
-e TERM \
|
||||
"$tag" ./src/d2/ci/release/_build.sh
|
||||
|
|
|
|||
|
|
@ -12,8 +12,7 @@ EOF
|
|||
}
|
||||
|
||||
main() {
|
||||
while :; do
|
||||
flag_parse "$@"
|
||||
while flag_parse "$@"; do
|
||||
case "$FLAG" in
|
||||
h|help)
|
||||
help
|
||||
|
|
@ -27,15 +26,12 @@ main() {
|
|||
flag_nonemptyarg && shift "$FLAGSHIFT"
|
||||
KEY_FILE=$FLAGARG
|
||||
;;
|
||||
'')
|
||||
shift "$FLAGSHIFT"
|
||||
break
|
||||
;;
|
||||
*)
|
||||
flag_errusage "unrecognized flag $FLAGRAW"
|
||||
;;
|
||||
esac
|
||||
done
|
||||
shift "$FLAGSHIFT"
|
||||
if [ -z "${KEY_FILE-}" ]; then
|
||||
echoerr "-i is required"
|
||||
exit 1
|
||||
|
|
|
|||
|
|
@ -12,8 +12,7 @@ EOF
|
|||
}
|
||||
|
||||
main() {
|
||||
while :; do
|
||||
flag_parse "$@"
|
||||
while flag_parse "$@"; do
|
||||
case "$FLAG" in
|
||||
h|help)
|
||||
help
|
||||
|
|
@ -27,15 +26,12 @@ main() {
|
|||
flag_noarg && shift "$FLAGSHIFT"
|
||||
SKIP_CREATE=1
|
||||
;;
|
||||
'')
|
||||
shift "$FLAGSHIFT"
|
||||
break
|
||||
;;
|
||||
*)
|
||||
flag_errusage "unrecognized flag $FLAGRAW"
|
||||
;;
|
||||
esac
|
||||
done
|
||||
shift "$FLAGSHIFT"
|
||||
if [ $# -gt 0 ]; then
|
||||
flag_errusage "no arguments are accepted"
|
||||
fi
|
||||
|
|
@ -204,7 +200,7 @@ init_remote_hosts() {
|
|||
header macos-arm64
|
||||
REMOTE_HOST=$TSTRUCT_MACOS_ARM64_BUILDER init_remote_macos
|
||||
|
||||
COLOR=2 header summary
|
||||
FGCOLOR=2 header summary
|
||||
log "export TSTRUCT_LINUX_AMD64_BUILDER=$TSTRUCT_LINUX_AMD64_BUILDER"
|
||||
log "export TSTRUCT_LINUX_ARM64_BUILDER=$TSTRUCT_LINUX_ARM64_BUILDER"
|
||||
log "export TSTRUCT_MACOS_AMD64_BUILDER=$TSTRUCT_MACOS_AMD64_BUILDER"
|
||||
|
|
|
|||
|
|
@ -12,8 +12,7 @@ EOF
|
|||
}
|
||||
|
||||
main() {
|
||||
while :; do
|
||||
flag_parse "$@"
|
||||
while flag_parse "$@"; do
|
||||
case "$FLAG" in
|
||||
h|help)
|
||||
help
|
||||
|
|
@ -27,15 +26,12 @@ main() {
|
|||
flag_reqarg && shift "$FLAGSHIFT"
|
||||
JOBFILTER="$FLAGARG"
|
||||
;;
|
||||
'')
|
||||
shift "$FLAGSHIFT"
|
||||
break
|
||||
;;
|
||||
*)
|
||||
flag_errusage "unrecognized flag $FLAGRAW"
|
||||
;;
|
||||
esac
|
||||
done
|
||||
shift "$FLAGSHIFT"
|
||||
|
||||
REMOTE_HOST=$TSTRUCT_LINUX_AMD64_BUILDER; runjob linux-amd64 ssh "$REMOTE_HOST" "$@"
|
||||
REMOTE_HOST=$TSTRUCT_LINUX_ARM64_BUILDER; runjob linux-arm64 ssh "$REMOTE_HOST" "$@"
|
||||
|
|
|
|||
|
|
@ -7,7 +7,8 @@ For v0.0.99 we focused on X, Y and Z. Enjoy!
|
|||
|
||||
#### Improvements 🔧
|
||||
|
||||
- Add table columns indices in edges between SQL Tables so that layout engines can route exactly between them
|
||||
- Equivalency between flags and environment variables. You can set either one for all
|
||||
options (flags take precedence).
|
||||
|
||||
#### Bugfixes 🔴
|
||||
|
||||
|
|
|
|||
29
ci/release/gen_template_lib.sh
Executable file
29
ci/release/gen_template_lib.sh
Executable file
|
|
@ -0,0 +1,29 @@
|
|||
#!/bin/sh
|
||||
set -eu
|
||||
cd -- "$(dirname "$0")/../.."
|
||||
. ./ci/sub/lib.sh
|
||||
|
||||
sh_c chmod +w ./ci/release/template/scripts/lib.sh
|
||||
sh_c cat >./ci/release/template/scripts/lib.sh <<EOF
|
||||
#!/bin/sh
|
||||
|
||||
# *************
|
||||
# DO NOT EDIT
|
||||
#
|
||||
# lib.sh was bundled together from
|
||||
#
|
||||
# - ./ci/sub/lib/rand.sh
|
||||
# - ./ci/sub/lib/log.sh
|
||||
#
|
||||
# Generated by ./ci/release/gen_template_lib.sh.
|
||||
# *************
|
||||
|
||||
EOF
|
||||
|
||||
# sed removes the sourcing dependency lines as we're bundled everything into a single
|
||||
# script.
|
||||
sh_c cat \
|
||||
./ci/sub/lib/rand.sh \
|
||||
./ci/sub/lib/log.sh \
|
||||
\| sed "-e'/^\. /d'" \>\>./ci/release/template/scripts/lib.sh
|
||||
sh_c chmod -w ./ci/release/template/scripts/lib.sh
|
||||
|
|
@ -40,10 +40,22 @@ See more docs, the source code and license at
|
|||
.It Fl w , -watch Ar false
|
||||
Watch for changes to input and live reload. Use
|
||||
.Ev $PORT and Ev $HOST to specify the listening address.
|
||||
.Ev $D2_PORT and $D2_HOST are also accepted and take priority. Default is localhost:0
|
||||
.It Fl h , -host Ar localhost
|
||||
Host listening address when used with
|
||||
.Ar watch
|
||||
.Ns .
|
||||
.It Fl p , -port Ar 0
|
||||
Port listening address when used with
|
||||
.Ar watch
|
||||
.Ns .
|
||||
.It Fl t , -theme Ar 0
|
||||
Set the diagram theme to the passed integer. For a list of available options, see
|
||||
.Lk https://oss.terrastruct.com/d2
|
||||
.Ns .
|
||||
.It Fl l , -layout Ar dagre
|
||||
Set the diagram layout engine to the passed string. For a list of available options, run
|
||||
.Ar layout
|
||||
.Ns .
|
||||
.It Fl b , -bundle Ar true
|
||||
Bundle all assets and layers into the output svg.
|
||||
.It Fl d , -debug
|
||||
|
|
|
|||
|
|
@ -1,8 +1,21 @@
|
|||
#!/bin/sh
|
||||
|
||||
if [ -n "${DEBUG-}" ]; then
|
||||
set -x
|
||||
# *************
|
||||
# DO NOT EDIT
|
||||
#
|
||||
# lib.sh was bundled together from
|
||||
#
|
||||
# - ./ci/sub/lib/rand.sh
|
||||
# - ./ci/sub/lib/log.sh
|
||||
#
|
||||
# Generated by ./ci/release/gen_template_lib.sh.
|
||||
# *************
|
||||
|
||||
#!/bin/sh
|
||||
if [ "${LIB_RAND-}" ]; then
|
||||
return 0
|
||||
fi
|
||||
LIB_RAND=1
|
||||
|
||||
rand() {
|
||||
seed="$1"
|
||||
|
|
@ -14,15 +27,51 @@ rand() {
|
|||
}
|
||||
|
||||
pick() {
|
||||
if ! command -v shuf >/dev/null || ! command -v md5sum >/dev/null; then
|
||||
eval "_echo \"\$3\""
|
||||
return
|
||||
fi
|
||||
|
||||
seed="$1"
|
||||
shift
|
||||
i="$(rand "$seed" "1-$#")"
|
||||
eval "_echo \"\$$i\""
|
||||
}
|
||||
#!/bin/sh
|
||||
if [ "${LIB_LOG-}" ]; then
|
||||
return 0
|
||||
fi
|
||||
LIB_LOG=1
|
||||
|
||||
if [ -n "${DEBUG-}" ]; then
|
||||
set -x
|
||||
fi
|
||||
|
||||
tput() {
|
||||
if [ -n "$TERM" ]; then
|
||||
command tput "$@"
|
||||
if should_color; then
|
||||
TERM=${TERM:-xterm-256color} command tput "$@"
|
||||
fi
|
||||
}
|
||||
|
||||
should_color() {
|
||||
if [ -n "${COLOR-}" ]; then
|
||||
if [ "$COLOR" = 0 -o "$COLOR" = false ]; then
|
||||
_COLOR=
|
||||
return 1
|
||||
elif [ "$COLOR" = 1 -o "$COLOR" = true ]; then
|
||||
_COLOR=1
|
||||
return 0
|
||||
else
|
||||
printf '$COLOR must be 0, 1, false or true but got %s' "$COLOR" >&2
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -t 1 ]; then
|
||||
_COLOR=1
|
||||
return 0
|
||||
else
|
||||
_COLOR=
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
|
|
@ -59,14 +108,14 @@ printfp() {(
|
|||
prefix="$1"
|
||||
shift
|
||||
|
||||
if [ -z "${COLOR:-}" ]; then
|
||||
COLOR="$(get_rand_color "$prefix")"
|
||||
if [ -z "${FGCOLOR-}" ]; then
|
||||
FGCOLOR="$(get_rand_color "$prefix")"
|
||||
fi
|
||||
printf '%s' "$(setaf "$COLOR" "$prefix")"
|
||||
|
||||
if [ $# -gt 0 ]; then
|
||||
printf ': '
|
||||
printf "$@"
|
||||
should_color || true
|
||||
if [ $# -eq 0 ]; then
|
||||
printf '%s' "$(COLOR=${_COLOR-} setaf "$FGCOLOR" "$prefix")"
|
||||
else
|
||||
printf '%s: %s\n' "$(COLOR=${_COLOR-} setaf "$FGCOLOR" "$prefix")" "$(printf "$@")"
|
||||
fi
|
||||
)}
|
||||
|
||||
|
|
@ -74,13 +123,8 @@ catp() {
|
|||
prefix="$1"
|
||||
shift
|
||||
|
||||
printfp "$prefix"
|
||||
printf ': '
|
||||
read -r line
|
||||
_echo "$line"
|
||||
|
||||
indent=$(repeat ' ' 2)
|
||||
sed "s/^/$indent/"
|
||||
should_color || true
|
||||
sed "s/^/$(COLOR=${_COLOR-} printfp "$prefix" '')/"
|
||||
}
|
||||
|
||||
repeat() {
|
||||
|
|
@ -94,48 +138,150 @@ strlen() {
|
|||
}
|
||||
|
||||
echoerr() {
|
||||
COLOR=1 echop err "$*" >&2
|
||||
FGCOLOR=1 logp err "$*" | humanpath>&2
|
||||
}
|
||||
|
||||
caterr() {
|
||||
COLOR=1 catp err "$@" >&2
|
||||
FGCOLOR=1 logpcat err "$@" | humanpath >&2
|
||||
}
|
||||
|
||||
printferr() {
|
||||
COLOR=1 printfp err "$@" >&2
|
||||
FGCOLOR=1 logfp err "$@" | humanpath >&2
|
||||
}
|
||||
|
||||
logp() {
|
||||
echop "$@" >&2
|
||||
should_color >&2 || true
|
||||
COLOR=${_COLOR-} echop "$@" | humanpath >&2
|
||||
}
|
||||
|
||||
logfp() {
|
||||
printfp "$@" >&2
|
||||
should_color >&2 || true
|
||||
COLOR=${_COLOR-} printfp "$@" | humanpath >&2
|
||||
}
|
||||
|
||||
logpcat() {
|
||||
catp "$@" >&2
|
||||
should_color >&2 || true
|
||||
COLOR=${_COLOR-} catp "$@" | humanpath >&2
|
||||
}
|
||||
|
||||
log() {
|
||||
COLOR=5 logp log "$@"
|
||||
FGCOLOR=5 logp log "$@"
|
||||
}
|
||||
|
||||
logf() {
|
||||
COLOR=5 logfp log "$@"
|
||||
FGCOLOR=5 logfp log "$@"
|
||||
}
|
||||
|
||||
logcat() {
|
||||
COLOR=5 catp log "$@" >&2
|
||||
FGCOLOR=5 logpcat log "$@"
|
||||
}
|
||||
|
||||
warn() {
|
||||
FGCOLOR=3 logp warn "$@"
|
||||
}
|
||||
|
||||
warnf() {
|
||||
FGCOLOR=3 logfp warn "$@"
|
||||
}
|
||||
|
||||
warncat() {
|
||||
FGCOLOR=3 logpcat warn "$@"
|
||||
}
|
||||
|
||||
sh_c() {
|
||||
COLOR=3 logp exec "$*"
|
||||
FGCOLOR=3 logp exec "$*"
|
||||
if [ -z "${DRY_RUN-}" ]; then
|
||||
"$@"
|
||||
eval "$@"
|
||||
fi
|
||||
}
|
||||
|
||||
sudo_sh_c() {
|
||||
if [ "$(id -u)" -eq 0 ]; then
|
||||
sh_c "$@"
|
||||
elif command -v doas >/dev/null; then
|
||||
sh_c "doas $*"
|
||||
elif command -v sudo >/dev/null; then
|
||||
sh_c "sudo $*"
|
||||
elif command -v su >/dev/null; then
|
||||
sh_c "su root -c '$*'"
|
||||
else
|
||||
caterr <<EOF
|
||||
This script needs to run the following command as root:
|
||||
$*
|
||||
Please install doas, sudo, or su.
|
||||
EOF
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
header() {
|
||||
logp "/* $1 */"
|
||||
}
|
||||
|
||||
bigheader() {
|
||||
logp "/**
|
||||
* $1
|
||||
**/"
|
||||
}
|
||||
|
||||
# humanpath replaces all occurrences of " $HOME" with " ~"
|
||||
# and all occurrences of '$HOME' with the literal '$HOME'.
|
||||
humanpath() {
|
||||
if [ -z "${HOME-}" ]; then
|
||||
cat
|
||||
else
|
||||
sed -e "s# $HOME# ~#g" -e "s#$HOME#\$HOME#g"
|
||||
fi
|
||||
}
|
||||
|
||||
hide() {
|
||||
out="$(mktemp)"
|
||||
set +e
|
||||
"$@" >"$out" 2>&1
|
||||
code="$?"
|
||||
set -e
|
||||
if [ "$code" -eq 0 ]; then
|
||||
return
|
||||
fi
|
||||
cat "$out" >&2
|
||||
return "$code"
|
||||
}
|
||||
|
||||
echo_dur() {
|
||||
local dur=$1
|
||||
local h=$((dur/60/60))
|
||||
local m=$((dur/60%60))
|
||||
local s=$((dur%60))
|
||||
printf '%dh%dm%ds' "$h" "$m" "$s"
|
||||
}
|
||||
|
||||
sponge() {
|
||||
dst="$1"
|
||||
tmp="$(mktemp)"
|
||||
cat > "$tmp"
|
||||
cat "$tmp" > "$dst"
|
||||
}
|
||||
|
||||
stripansi() {
|
||||
# First regex gets rid of standard xterm escape sequences for controlling
|
||||
# visual attributes.
|
||||
# The second regex I'm not 100% sure, the reference says it selects the US
|
||||
# encoding but I'm not sure why that's necessary or why it always occurs
|
||||
# in tput sgr0 before the standard escape sequence.
|
||||
# See tput sgr0 | xxd
|
||||
sed -e $'s/\x1b\[[0-9;]*m//g' -e $'s/\x1b(.//g'
|
||||
}
|
||||
|
||||
runtty() {
|
||||
case "$(uname)" in
|
||||
Darwin)
|
||||
script -q /dev/null "$@"
|
||||
;;
|
||||
Linux)
|
||||
script -eqc "$*"
|
||||
;;
|
||||
*)
|
||||
echoerr "runtty: unsupported OS $(uname)"
|
||||
return 1
|
||||
esac
|
||||
}
|
||||
|
|
|
|||
2
ci/sub
2
ci/sub
|
|
@ -1 +1 @@
|
|||
Subproject commit df51b90892737ebe9feca3dd982bcdfc7f684834
|
||||
Subproject commit 824046d952b1442c76a057553591652c889fb7cb
|
||||
|
|
@ -28,13 +28,13 @@ Subcommands:
|
|||
%[1]s layout [layout name] - Display long help for a particular layout engine
|
||||
|
||||
See more docs and the source code at https://oss.terrastruct.com/d2
|
||||
`, ms.Name, ms.FlagHelp())
|
||||
`, ms.Name, ms.Opts.Defaults())
|
||||
}
|
||||
|
||||
func layoutHelp(ctx context.Context, ms *xmain.State) error {
|
||||
if len(ms.FlagSet.Args()) == 1 {
|
||||
if len(ms.Opts.Flags.Args()) == 1 {
|
||||
return shortLayoutHelp(ctx, ms)
|
||||
} else if len(ms.FlagSet.Args()) == 2 {
|
||||
} else if len(ms.Opts.Flags.Args()) == 2 {
|
||||
return longLayoutHelp(ctx, ms)
|
||||
} else {
|
||||
return pluginSubcommand(ctx, ms)
|
||||
|
|
@ -61,7 +61,7 @@ func shortLayoutHelp(ctx context.Context, ms *xmain.State) error {
|
|||
%s
|
||||
|
||||
Usage:
|
||||
To use a particular layout engine, set the environment variable D2_LAYOUT=[layout name].
|
||||
To use a particular layout engine, set the environment variable D2_LAYOUT=[name] or flag --layout=[name].
|
||||
|
||||
Example:
|
||||
D2_LAYOUT=dagre d2 in.d2 out.svg
|
||||
|
|
@ -75,7 +75,7 @@ See more docs at https://oss.terrastruct.com/d2
|
|||
}
|
||||
|
||||
func longLayoutHelp(ctx context.Context, ms *xmain.State) error {
|
||||
layout := ms.FlagSet.Arg(1)
|
||||
layout := ms.Opts.Flags.Arg(1)
|
||||
plugin, path, err := d2plugin.FindPlugin(ctx, layout)
|
||||
if errors.Is(err, exec.ErrNotFound) {
|
||||
return layoutNotFound(ctx, layout)
|
||||
|
|
@ -119,13 +119,13 @@ For more information on setup, please visit https://github.com/terrastruct/d2.`,
|
|||
}
|
||||
|
||||
func pluginSubcommand(ctx context.Context, ms *xmain.State) error {
|
||||
layout := ms.FlagSet.Arg(1)
|
||||
layout := ms.Opts.Flags.Arg(1)
|
||||
plugin, _, err := d2plugin.FindPlugin(ctx, layout)
|
||||
if errors.Is(err, exec.ErrNotFound) {
|
||||
return layoutNotFound(ctx, layout)
|
||||
}
|
||||
|
||||
ms.Args = ms.FlagSet.Args()[2:]
|
||||
ms.Opts.Args = ms.Opts.Flags.Args()[2:]
|
||||
return d2plugin.Serve(plugin)(ctx, ms)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ import (
|
|||
"fmt"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
|
|
@ -32,19 +31,38 @@ func run(ctx context.Context, ms *xmain.State) (err error) {
|
|||
// :(
|
||||
ctx = xmain.DiscardSlog(ctx)
|
||||
|
||||
watchFlag := ms.FlagSet.BoolP("watch", "w", false, "watch for changes to input and live reload. Use $PORT and $HOST to specify the listening address.\n$D2_PORT and $D2_HOST are also accepted and take priority. Default is localhost:0")
|
||||
themeFlag := ms.FlagSet.Int64P("theme", "t", 0, "set the diagram theme. For a list of available options, see https://oss.terrastruct.com/d2")
|
||||
bundleFlag := ms.FlagSet.BoolP("bundle", "b", true, "when outputting SVG, bundle all assets and layers into the output file")
|
||||
versionFlag := ms.FlagSet.BoolP("version", "v", false, "get the version")
|
||||
debugFlag := ms.FlagSet.BoolP("debug", "d", false, "print debug logs")
|
||||
err = ms.FlagSet.Parse(ms.Args)
|
||||
// These should be kept up-to-date with the d2 man page
|
||||
watchFlag, err := ms.Opts.Bool("D2_WATCH", "watch", "w", false, "watch for changes to input and live reload. Use $HOST and $PORT to specify the listening address.\n(default localhost:0, which is will open on a randomly available local port).")
|
||||
if err != nil {
|
||||
return xmain.UsageErrorf(err.Error())
|
||||
}
|
||||
hostFlag := ms.Opts.String("HOST", "host", "h", "localhost", "host listening address when used with watch")
|
||||
portFlag := ms.Opts.String("PORT", "port", "p", "0", "port listening address when used with watch")
|
||||
bundleFlag, err := ms.Opts.Bool("D2_BUNDLE", "bundle", "b", true, "when outputting SVG, bundle all assets and layers into the output file.")
|
||||
if err != nil {
|
||||
return xmain.UsageErrorf(err.Error())
|
||||
}
|
||||
debugFlag, err := ms.Opts.Bool("DEBUG", "debug", "d", false, "print debug logs.")
|
||||
if err != nil {
|
||||
return xmain.UsageErrorf(err.Error())
|
||||
}
|
||||
layoutFlag := ms.Opts.String("D2_LAYOUT", "layout", "l", "dagre", `the layout engine used.`)
|
||||
themeFlag, err := ms.Opts.Int64("D2_THEME", "theme", "t", 0, "the diagram theme ID. For a list of available options, see https://oss.terrastruct.com/d2")
|
||||
if err != nil {
|
||||
return xmain.UsageErrorf(err.Error())
|
||||
}
|
||||
versionFlag, err := ms.Opts.Bool("", "version", "v", false, "get the version")
|
||||
if err != nil {
|
||||
return xmain.UsageErrorf(err.Error())
|
||||
}
|
||||
|
||||
err = ms.Opts.Flags.Parse(ms.Opts.Args)
|
||||
if !errors.Is(err, pflag.ErrHelp) && err != nil {
|
||||
return xmain.UsageErrorf("failed to parse flags: %v", err)
|
||||
}
|
||||
|
||||
if len(ms.FlagSet.Args()) > 0 {
|
||||
switch ms.FlagSet.Arg(0) {
|
||||
if len(ms.Opts.Flags.Args()) > 0 {
|
||||
switch ms.Opts.Flags.Arg(0) {
|
||||
case "layout":
|
||||
return layoutHelp(ctx, ms)
|
||||
}
|
||||
|
|
@ -62,25 +80,26 @@ func run(ctx context.Context, ms *xmain.State) (err error) {
|
|||
var inputPath string
|
||||
var outputPath string
|
||||
|
||||
if len(ms.FlagSet.Args()) == 0 {
|
||||
if len(ms.Opts.Flags.Args()) == 0 {
|
||||
if versionFlag != nil && *versionFlag {
|
||||
fmt.Println(version.Version)
|
||||
return nil
|
||||
}
|
||||
help(ms)
|
||||
return nil
|
||||
} else if len(ms.FlagSet.Args()) >= 3 {
|
||||
} else if len(ms.Opts.Flags.Args()) >= 3 {
|
||||
return xmain.UsageErrorf("too many arguments passed")
|
||||
}
|
||||
if len(ms.FlagSet.Args()) >= 1 {
|
||||
if ms.FlagSet.Arg(0) == "version" {
|
||||
|
||||
if len(ms.Opts.Flags.Args()) >= 1 {
|
||||
if ms.Opts.Flags.Arg(0) == "version" {
|
||||
fmt.Println(version.Version)
|
||||
return nil
|
||||
}
|
||||
inputPath = ms.FlagSet.Arg(0)
|
||||
inputPath = ms.Opts.Flags.Arg(0)
|
||||
}
|
||||
if len(ms.FlagSet.Args()) >= 2 {
|
||||
outputPath = ms.FlagSet.Arg(1)
|
||||
if len(ms.Opts.Flags.Args()) >= 2 {
|
||||
outputPath = ms.Opts.Flags.Arg(1)
|
||||
} else {
|
||||
if inputPath == "-" {
|
||||
outputPath = "-"
|
||||
|
|
@ -93,16 +112,11 @@ func run(ctx context.Context, ms *xmain.State) (err error) {
|
|||
if match == (d2themes.Theme{}) {
|
||||
return xmain.UsageErrorf("-t[heme] could not be found. The available options are:\n%s\nYou provided: %d", d2themescatalog.CLIString(), *themeFlag)
|
||||
}
|
||||
ms.Env.Setenv("D2_THEME", fmt.Sprintf("%d", *themeFlag))
|
||||
ms.Log.Debug.Printf("using theme %s (ID: %d)", match.Name, *themeFlag)
|
||||
|
||||
envD2Layout := ms.Env.Getenv("D2_LAYOUT")
|
||||
if envD2Layout == "" {
|
||||
envD2Layout = "dagre"
|
||||
}
|
||||
|
||||
plugin, path, err := d2plugin.FindPlugin(ctx, envD2Layout)
|
||||
plugin, path, err := d2plugin.FindPlugin(ctx, *layoutFlag)
|
||||
if errors.Is(err, exec.ErrNotFound) {
|
||||
return layoutNotFound(ctx, envD2Layout)
|
||||
return layoutNotFound(ctx, *layoutFlag)
|
||||
} else if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -111,7 +125,7 @@ func run(ctx context.Context, ms *xmain.State) (err error) {
|
|||
if path != "" {
|
||||
pluginLocation = fmt.Sprintf("executable plugin at %s", humanPath(path))
|
||||
}
|
||||
ms.Log.Debug.Printf("using layout plugin %s (%s)", envD2Layout, pluginLocation)
|
||||
ms.Log.Debug.Printf("using layout plugin %s (%s)", *layoutFlag, pluginLocation)
|
||||
|
||||
var pw png.Playwright
|
||||
if filepath.Ext(outputPath) == ".png" {
|
||||
|
|
@ -133,7 +147,15 @@ func run(ctx context.Context, ms *xmain.State) (err error) {
|
|||
return xmain.UsageErrorf("-w[atch] cannot be combined with reading input from stdin")
|
||||
}
|
||||
ms.Env.Setenv("LOG_TIMESTAMPS", "1")
|
||||
w, err := newWatcher(ctx, ms, plugin, inputPath, outputPath, pw)
|
||||
w, err := newWatcher(ctx, ms, watcherOpts{
|
||||
layoutPlugin: plugin,
|
||||
themeID: *themeFlag,
|
||||
host: *hostFlag,
|
||||
port: *portFlag,
|
||||
inputPath: inputPath,
|
||||
outputPath: outputPath,
|
||||
pw: pw,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -147,7 +169,7 @@ func run(ctx context.Context, ms *xmain.State) (err error) {
|
|||
_ = 343
|
||||
}
|
||||
|
||||
_, err = compile(ctx, ms, plugin, inputPath, outputPath, pw.Page)
|
||||
_, err = compile(ctx, ms, plugin, *themeFlag, inputPath, outputPath, pw.Page)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -156,7 +178,7 @@ func run(ctx context.Context, ms *xmain.State) (err error) {
|
|||
return nil
|
||||
}
|
||||
|
||||
func compile(ctx context.Context, ms *xmain.State, plugin d2plugin.Plugin, inputPath, outputPath string, page playwright.Page) ([]byte, error) {
|
||||
func compile(ctx context.Context, ms *xmain.State, plugin d2plugin.Plugin, themeID int64, inputPath, outputPath string, page playwright.Page) ([]byte, error) {
|
||||
input, err := ms.ReadPath(inputPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
@ -167,7 +189,6 @@ func compile(ctx context.Context, ms *xmain.State, plugin d2plugin.Plugin, input
|
|||
return nil, err
|
||||
}
|
||||
|
||||
themeID, _ := strconv.ParseInt(ms.Env.Getenv("D2_THEME"), 10, 64)
|
||||
d, err := d2.Compile(ctx, string(input), &d2.CompileOptions{
|
||||
Layout: plugin.Layout,
|
||||
Ruler: ruler,
|
||||
|
|
@ -197,7 +218,7 @@ func compile(ctx context.Context, ms *xmain.State, plugin d2plugin.Plugin, input
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
return svg, nil
|
||||
}
|
||||
|
||||
// newExt must include leading .
|
||||
|
|
|
|||
|
|
@ -35,6 +35,16 @@ var devMode = false
|
|||
//go:embed static
|
||||
var staticFS embed.FS
|
||||
|
||||
type watcherOpts struct {
|
||||
layoutPlugin d2plugin.Plugin
|
||||
themeID int64
|
||||
host string
|
||||
port string
|
||||
inputPath string
|
||||
outputPath string
|
||||
pw png.Playwright
|
||||
}
|
||||
|
||||
type watcher struct {
|
||||
ctx context.Context
|
||||
cancel context.CancelFunc
|
||||
|
|
@ -42,9 +52,7 @@ type watcher struct {
|
|||
devMode bool
|
||||
|
||||
ms *xmain.State
|
||||
layoutPlugin d2plugin.Plugin
|
||||
inputPath string
|
||||
outputPath string
|
||||
watcherOpts
|
||||
|
||||
compileCh chan struct{}
|
||||
|
||||
|
|
@ -62,8 +70,6 @@ type watcher struct {
|
|||
|
||||
resMu sync.Mutex
|
||||
res *compileResult
|
||||
|
||||
pw png.Playwright
|
||||
}
|
||||
|
||||
type compileResult struct {
|
||||
|
|
@ -71,7 +77,7 @@ type compileResult struct {
|
|||
SVG string `json:"svg"`
|
||||
}
|
||||
|
||||
func newWatcher(ctx context.Context, ms *xmain.State, layoutPlugin d2plugin.Plugin, inputPath, outputPath string, pw png.Playwright) (*watcher, error) {
|
||||
func newWatcher(ctx context.Context, ms *xmain.State, opts watcherOpts) (*watcher, error) {
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
|
||||
w := &watcher{
|
||||
|
|
@ -80,13 +86,10 @@ func newWatcher(ctx context.Context, ms *xmain.State, layoutPlugin d2plugin.Plug
|
|||
devMode: devMode,
|
||||
|
||||
ms: ms,
|
||||
layoutPlugin: layoutPlugin,
|
||||
inputPath: inputPath,
|
||||
outputPath: outputPath,
|
||||
watcherOpts: opts,
|
||||
|
||||
compileCh: make(chan struct{}, 1),
|
||||
wsclients: make(map[*wsclient]struct{}),
|
||||
pw: pw,
|
||||
}
|
||||
err := w.init()
|
||||
if err != nil {
|
||||
|
|
@ -342,7 +345,7 @@ func (w *watcher) compileLoop(ctx context.Context) error {
|
|||
w.pw = newPW
|
||||
}
|
||||
|
||||
b, err := compile(ctx, w.ms, w.layoutPlugin, w.inputPath, w.outputPath, w.pw.Page)
|
||||
b, err := compile(ctx, w.ms, w.layoutPlugin, w.themeID, w.inputPath, w.outputPath, w.pw.Page)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("failed to %scompile: %w", recompiledPrefix, err)
|
||||
w.ms.Log.Error.Print(err)
|
||||
|
|
@ -368,18 +371,7 @@ func (w *watcher) compileLoop(ctx context.Context) error {
|
|||
}
|
||||
|
||||
func (w *watcher) listen() error {
|
||||
host := "localhost"
|
||||
port := "0"
|
||||
hostEnv := w.ms.Env.Getenv("HOST")
|
||||
if hostEnv != "" {
|
||||
host = hostEnv
|
||||
}
|
||||
portEnv := w.ms.Env.Getenv("PORT")
|
||||
if portEnv != "" {
|
||||
port = portEnv
|
||||
}
|
||||
|
||||
l, err := net.Listen("tcp", net.JoinHostPort(host, port))
|
||||
l, err := net.Listen("tcp", net.JoinHostPort(w.host, w.port))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,12 +19,12 @@ import (
|
|||
// Also see execPlugin in exec.go for the d2 binary plugin protocol.
|
||||
func Serve(p Plugin) func(context.Context, *xmain.State) error {
|
||||
return func(ctx context.Context, ms *xmain.State) (err error) {
|
||||
if len(ms.Args) < 1 {
|
||||
if len(ms.Opts.Flags.Args()) < 1 {
|
||||
return errors.New("expected first argument to plugin binary to be function name")
|
||||
}
|
||||
reqFunc := ms.Args[0]
|
||||
reqFunc := ms.Opts.Flags.Arg(0)
|
||||
|
||||
switch ms.Args[0] {
|
||||
switch ms.Opts.Flags.Arg(0) {
|
||||
case "info":
|
||||
return info(ctx, p, ms)
|
||||
case "layout":
|
||||
|
|
|
|||
|
|
@ -65,8 +65,7 @@ language. Sometimes it gives controversial sentences -- don't use those.
|
|||
Script to generate one line of random text:
|
||||
```
|
||||
ipsum1() {
|
||||
fortune | head -n1 | sed 's/^ *//;s/ *$//' | tr -d '\n' | pbcopy
|
||||
echo "$(pbpaste -Prefer txt)"
|
||||
fortune | head -n1 | sed 's/^ *//;s/ *$//' | tr -d '\n' | tee /dev/stderr | pbcopy
|
||||
}
|
||||
```
|
||||
|
||||
|
|
|
|||
91
docs/INSTALL.md
Normal file
91
docs/INSTALL.md
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
# install
|
||||
|
||||
This file documents all the ways by which you can install D2.
|
||||
|
||||
<!-- toc -->
|
||||
|
||||
- [install.sh](#installsh)
|
||||
- [macOS (Homebrew)](#macos-homebrew)
|
||||
- [Standalone](#standalone)
|
||||
- [From source](#from-source)
|
||||
|
||||
<!-- tocstop -->
|
||||
|
||||
## install.sh
|
||||
|
||||
The recommended and easiest way to install is with our install script, which will detect
|
||||
the OS and architecture you're on and use the best method:
|
||||
|
||||
```sh
|
||||
# With --dry-run the install script will print the commands it will use
|
||||
# to install without actually installing so you know what it's going to do.
|
||||
curl -fsSL https://d2lang.com/install.sh | sh -s -- --dry-run
|
||||
# If things look good, install for real.
|
||||
curl -fsSL https://d2lang.com/install.sh | sh -s --
|
||||
```
|
||||
|
||||
For help on the terminal run, including the supported package managers and detection
|
||||
methods:
|
||||
|
||||
```sh
|
||||
curl -fsSL https://d2lang.com/install.sh | sh -s -- --help
|
||||
```
|
||||
|
||||
## macOS (Homebrew)
|
||||
|
||||
If you're on macOS, you can alternatively install with `brew`. (the install script above
|
||||
does this automatically if you have `brew` installed).
|
||||
|
||||
```sh
|
||||
brew tap terrastruct/d2
|
||||
brew install d2
|
||||
```
|
||||
|
||||
## Standalone
|
||||
|
||||
We publish standalone release archives with every release on Github.
|
||||
Download the `.tar.gz` release for your OS/ARCH combination and then run the following
|
||||
inside the extracted directory to install:
|
||||
|
||||
```sh
|
||||
make install
|
||||
```
|
||||
|
||||
Run the following to uninstall:
|
||||
|
||||
```sh
|
||||
make uninstall
|
||||
```
|
||||
|
||||
You will be prompted for sudo/su/doas if root permissions are required for installation.
|
||||
You can control the Unix hierarchy installation path with `PREFIX=`. For example:
|
||||
|
||||
```
|
||||
# Install under ~/.local.
|
||||
# Binaries will be at ~/.local/bin
|
||||
# And manpages will be under ~/.local/share/man
|
||||
# And supporting data like icons and fonts at ~/.local/share/d2
|
||||
make install PREFIX=$HOME/.local
|
||||
```
|
||||
|
||||
The install script places the standalone release into `$PREFIX/lib/d2/d2-<version>`
|
||||
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 portable like normal Go binaries due to the C
|
||||
> dependency on v8go for executing dagre.
|
||||
|
||||
## From source
|
||||
|
||||
Alternatively, you can always install from source:
|
||||
|
||||
```sh
|
||||
go install oss.terrastruct.com/d2/cmd/d2@latest
|
||||
```
|
||||
|
||||
## Coming soon
|
||||
|
||||
- Docker image
|
||||
- Windows install
|
||||
- rpm and deb packages
|
||||
- homebrew core
|
||||
2
go.sum
2
go.sum
|
|
@ -778,6 +778,8 @@ honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9
|
|||
nhooyr.io/websocket v1.8.7 h1:usjR2uOr/zjjkVMy0lW+PPohFok7PCow5sDjLgX4P4g=
|
||||
nhooyr.io/websocket v1.8.7/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0=
|
||||
oss.terrastruct.com/cmdlog v0.0.0-20221116181457-07977d95ac37 h1:Xy1JKJHc4hcuwi57s0BvGUY16GjxTtBmLUybsuGDU7E=
|
||||
oss.terrastruct.com/cmdlog v0.0.0-20221116181457-07977d95ac37 h1:Xy1JKJHc4hcuwi57s0BvGUY16GjxTtBmLUybsuGDU7E=
|
||||
oss.terrastruct.com/cmdlog v0.0.0-20221116181457-07977d95ac37/go.mod h1:ROL3yxl2X+S3O+Rls00qdX6aMh+p1dF8IdxDRwDDpsg=
|
||||
oss.terrastruct.com/cmdlog v0.0.0-20221116181457-07977d95ac37/go.mod h1:ROL3yxl2X+S3O+Rls00qdX6aMh+p1dF8IdxDRwDDpsg=
|
||||
oss.terrastruct.com/diff v1.0.2-0.20221116222035-8bf4dd3ab541 h1:I9B1O1IJ6spivIQxbFRZmbhAwVeLwrcQRR1JbYUOvrI=
|
||||
oss.terrastruct.com/diff v1.0.2-0.20221116222035-8bf4dd3ab541/go.mod h1:ags2QDy/T6jr69hT6bpmAmhr2H98n9o8Atf3QlUJPiU=
|
||||
|
|
|
|||
360
install.sh
360
install.sh
|
|
@ -54,8 +54,30 @@ if [ -n "${DEBUG-}" ]; then
|
|||
fi
|
||||
|
||||
tput() {
|
||||
if [ -n "$TERM" ]; then
|
||||
command tput "$@"
|
||||
if should_color; then
|
||||
TERM=${TERM:-xterm-256color} command tput "$@"
|
||||
fi
|
||||
}
|
||||
|
||||
should_color() {
|
||||
if [ -n "${COLOR-}" ]; then
|
||||
if [ "$COLOR" = 0 -o "$COLOR" = false ]; then
|
||||
_COLOR=
|
||||
return 1
|
||||
elif [ "$COLOR" = 1 -o "$COLOR" = true ]; then
|
||||
_COLOR=1
|
||||
return 0
|
||||
else
|
||||
printf '$COLOR must be 0, 1, false or true but got %s' "$COLOR" >&2
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -t 1 ]; then
|
||||
_COLOR=1
|
||||
return 0
|
||||
else
|
||||
_COLOR=
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
|
|
@ -92,14 +114,14 @@ printfp() {(
|
|||
prefix="$1"
|
||||
shift
|
||||
|
||||
if [ -z "${COLOR:-}" ]; then
|
||||
COLOR="$(get_rand_color "$prefix")"
|
||||
if [ -z "${FGCOLOR-}" ]; then
|
||||
FGCOLOR="$(get_rand_color "$prefix")"
|
||||
fi
|
||||
printf '%s' "$(setaf "$COLOR" "$prefix")"
|
||||
|
||||
if [ $# -gt 0 ]; then
|
||||
printf ': '
|
||||
printf "$@"
|
||||
should_color || true
|
||||
if [ $# -eq 0 ]; then
|
||||
printf '%s' "$(COLOR=${_COLOR-} setaf "$FGCOLOR" "$prefix")"
|
||||
else
|
||||
printf '%s: %s\n' "$(COLOR=${_COLOR-} setaf "$FGCOLOR" "$prefix")" "$(printf "$@")"
|
||||
fi
|
||||
)}
|
||||
|
||||
|
|
@ -107,7 +129,8 @@ catp() {
|
|||
prefix="$1"
|
||||
shift
|
||||
|
||||
sed "s/^/$(printfp "$prefix" '')/"
|
||||
should_color || true
|
||||
sed "s/^/$(COLOR=${_COLOR-} printfp "$prefix" '')/"
|
||||
}
|
||||
|
||||
repeat() {
|
||||
|
|
@ -121,51 +144,58 @@ strlen() {
|
|||
}
|
||||
|
||||
echoerr() {
|
||||
COLOR=1 echop err "$*" | humanpath>&2
|
||||
FGCOLOR=1 logp err "$*" | humanpath>&2
|
||||
}
|
||||
|
||||
caterr() {
|
||||
COLOR=1 catp err "$@" | humanpath >&2
|
||||
FGCOLOR=1 logpcat err "$@" | humanpath >&2
|
||||
}
|
||||
|
||||
printferr() {
|
||||
COLOR=1 printfp err "$@" | humanpath >&2
|
||||
FGCOLOR=1 logfp err "$@" | humanpath >&2
|
||||
}
|
||||
|
||||
logp() {
|
||||
echop "$@" | humanpath >&2
|
||||
should_color >&2 || true
|
||||
COLOR=${_COLOR-} echop "$@" | humanpath >&2
|
||||
}
|
||||
|
||||
logfp() {
|
||||
printfp "$@" | humanpath >&2
|
||||
should_color >&2 || true
|
||||
COLOR=${_COLOR-} printfp "$@" | humanpath >&2
|
||||
}
|
||||
|
||||
logpcat() {
|
||||
catp "$@" | humanpath >&2
|
||||
should_color >&2 || true
|
||||
COLOR=${_COLOR-} catp "$@" | humanpath >&2
|
||||
}
|
||||
|
||||
log() {
|
||||
COLOR=5 logp log "$@"
|
||||
FGCOLOR=5 logp log "$@"
|
||||
}
|
||||
|
||||
logf() {
|
||||
COLOR=5 logfp log "$@"
|
||||
FGCOLOR=5 logfp log "$@"
|
||||
}
|
||||
|
||||
logcat() {
|
||||
COLOR=5 logpcat log "$@"
|
||||
FGCOLOR=5 logpcat log "$@"
|
||||
}
|
||||
|
||||
warn() {
|
||||
COLOR=3 logp warn "$@"
|
||||
FGCOLOR=3 logp warn "$@"
|
||||
}
|
||||
|
||||
warnf() {
|
||||
COLOR=3 logfp warn "$@"
|
||||
FGCOLOR=3 logfp warn "$@"
|
||||
}
|
||||
|
||||
warncat() {
|
||||
FGCOLOR=3 logpcat warn "$@"
|
||||
}
|
||||
|
||||
sh_c() {
|
||||
COLOR=3 logp exec "$*"
|
||||
FGCOLOR=3 logp exec "$*"
|
||||
if [ -z "${DRY_RUN-}" ]; then
|
||||
eval "$@"
|
||||
fi
|
||||
|
|
@ -194,6 +224,12 @@ header() {
|
|||
logp "/* $1 */"
|
||||
}
|
||||
|
||||
bigheader() {
|
||||
logp "/**
|
||||
* $1
|
||||
**/"
|
||||
}
|
||||
|
||||
# humanpath replaces all occurrences of " $HOME" with " ~"
|
||||
# and all occurrences of '$HOME' with the literal '$HOME'.
|
||||
humanpath() {
|
||||
|
|
@ -280,9 +316,8 @@ LIB_FLAG=1
|
|||
# FLAGSHIFT contains the number by which the arguments should be shifted to
|
||||
# start at the next flag/argument
|
||||
#
|
||||
# After each call check $FLAG for the name of the parsed flag.
|
||||
# If empty, then no more flags are left.
|
||||
# Still, call shift "$FLAGSHIFT" in case there was a --
|
||||
# flag_parse exits with a non zero code when there are no more flags
|
||||
# to be parsed. Still, call shift "$FLAGSHIFT" in case there was a --
|
||||
#
|
||||
# If the argument for the flag is optional, then use ${FLAGARG-} to access
|
||||
# the argument if one was passed. Use ${FLAGARG+x} = x to check if it was set.
|
||||
|
|
@ -310,18 +345,15 @@ flag_parse() {
|
|||
# Remove everything before first equal sign.
|
||||
FLAGARG="${1#*=}"
|
||||
FLAGSHIFT=1
|
||||
return 0
|
||||
;;
|
||||
-)
|
||||
FLAG=
|
||||
FLAGRAW=
|
||||
unset FLAGARG
|
||||
FLAGSHIFT=0
|
||||
return 1
|
||||
;;
|
||||
--)
|
||||
FLAG=
|
||||
FLAGRAW=
|
||||
unset FLAGARG
|
||||
FLAGSHIFT=1
|
||||
return 1
|
||||
;;
|
||||
-*)
|
||||
# Remove leading hyphens.
|
||||
|
|
@ -343,15 +375,13 @@ flag_parse() {
|
|||
;;
|
||||
esac
|
||||
fi
|
||||
return 0
|
||||
;;
|
||||
*)
|
||||
FLAG=
|
||||
FLAGRAW=
|
||||
unset FLAGARG
|
||||
FLAGSHIFT=0
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
return 0
|
||||
}
|
||||
|
||||
flag_reqarg() {
|
||||
|
|
@ -452,8 +482,11 @@ usage: $arg0 [--dry-run] [--version vX.X.X] [--edge] [--method detect] [--prefix
|
|||
[--tala latest] [--force] [--uninstall]
|
||||
|
||||
install.sh automates the installation of D2 onto your system. It currently only supports
|
||||
the installation of standalone releases from GitHub. If you pass --edge, it will clone the
|
||||
source, build a release and install from it.
|
||||
the installation of standalone releases from GitHub and via Homebrew on macOS. See the
|
||||
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.
|
||||
|
||||
Flags:
|
||||
|
||||
|
|
@ -463,6 +496,8 @@ Flags:
|
|||
|
||||
--version vX.X.X
|
||||
Pass to have install.sh install the given version instead of the latest version.
|
||||
warn: The version may not be obeyed with package manager installations. Use
|
||||
--method=standalone to enforce the version.
|
||||
|
||||
--edge
|
||||
Pass to build and install D2 from source. This will still use --method if set to detect
|
||||
|
|
@ -470,14 +505,15 @@ Flags:
|
|||
if an unsupported package manager is used. To install from source like a dev would,
|
||||
use go install oss.terrastruct.com/d2
|
||||
note: currently unimplemented.
|
||||
warn: incompatible with --tala as TALA is closed source.
|
||||
|
||||
--method [detect | standalone]
|
||||
--method [detect | standalone | homebrew ]
|
||||
Pass to control the method by which to install. Right now we only support standalone
|
||||
releases from GitHub but later we'll add support for brew, rpm, deb and more.
|
||||
note: currently unimplemented.
|
||||
|
||||
- detect is currently unimplemented but would use your OS's package manager
|
||||
automatically.
|
||||
- detect will use your OS's package manager automatically.
|
||||
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.
|
||||
|
|
@ -485,16 +521,19 @@ Flags:
|
|||
--prefix /usr/local
|
||||
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.
|
||||
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
|
||||
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.
|
||||
|
||||
--tala [latest]
|
||||
Install Terrastruct's closed source TALA for improved layouts.
|
||||
See https://github.com/terrastruct/TALA
|
||||
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
|
||||
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
|
||||
|
|
@ -507,6 +546,7 @@ Flags:
|
|||
as for installation. i.e if you used --method standalone you must again use --method
|
||||
standalone for uninstallation. With detect, the install script will try to use the OS
|
||||
package manager to uninstall instead.
|
||||
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>
|
||||
|
|
@ -519,9 +559,7 @@ EOF
|
|||
}
|
||||
|
||||
main() {
|
||||
METHOD=standalone
|
||||
while :; do
|
||||
flag_parse "$@"
|
||||
while flag_parse "$@"; do
|
||||
case "$FLAG" in
|
||||
h|help)
|
||||
help
|
||||
|
|
@ -548,8 +586,6 @@ main() {
|
|||
method)
|
||||
flag_nonemptyarg && shift "$FLAGSHIFT"
|
||||
METHOD=$FLAGARG
|
||||
echoerr "$FLAGRAW is currently unimplemented"
|
||||
return 1
|
||||
;;
|
||||
prefix)
|
||||
flag_nonemptyarg && shift "$FLAGSHIFT"
|
||||
|
|
@ -563,15 +599,12 @@ main() {
|
|||
flag_noarg && shift "$FLAGSHIFT"
|
||||
UNINSTALL=1
|
||||
;;
|
||||
'')
|
||||
shift "$FLAGSHIFT"
|
||||
break
|
||||
;;
|
||||
*)
|
||||
flag_errusage "unrecognized flag $FLAGRAW"
|
||||
;;
|
||||
esac
|
||||
done
|
||||
shift "$FLAGSHIFT"
|
||||
|
||||
if [ $# -gt 0 ]; then
|
||||
flag_errusage "no arguments are accepted"
|
||||
|
|
@ -587,49 +620,86 @@ main() {
|
|||
PREFIX=${PREFIX:-/usr/local}
|
||||
CACHE_DIR=$(cache_dir)
|
||||
mkdir -p "$CACHE_DIR"
|
||||
METHOD=${METHOD:-detect}
|
||||
INSTALL_DIR=$PREFIX/lib/d2
|
||||
|
||||
case $METHOD in
|
||||
detect)
|
||||
case "$OS" in
|
||||
macos)
|
||||
if command -v brew >/dev/null; then
|
||||
log "detected macOS with homebrew, using homebrew for (un)installation"
|
||||
METHOD=homebrew
|
||||
else
|
||||
warn "detected macOS without homebrew, falling back to --method=standalone"
|
||||
METHOD=standalone
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
warn "unrecognized OS $OS, falling back to --method=standalone"
|
||||
METHOD=standalone
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
standalone) ;;
|
||||
homebrew) ;;
|
||||
*)
|
||||
echoerr "unknown (un)installation method $METHOD"
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
|
||||
if [ -n "${UNINSTALL-}" ]; then
|
||||
uninstall
|
||||
return 0
|
||||
if [ -n "${DRY_RUN-}" ]; then
|
||||
log "Rerun without --dry-run to execute printed commands and perform uninstall."
|
||||
fi
|
||||
|
||||
VERSION=${VERSION:-latest}
|
||||
if [ "$VERSION" = latest ]; then
|
||||
header "fetching latest release info"
|
||||
fetch_release_info
|
||||
fi
|
||||
|
||||
else
|
||||
install
|
||||
if [ -n "${DRY_RUN-}" ]; then
|
||||
log "Rerun without --dry-run to execute printed commands and perform install."
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
install() {
|
||||
install_d2
|
||||
case $METHOD in
|
||||
standalone)
|
||||
install_d2_standalone
|
||||
if [ -n "${TALA-}" ]; then
|
||||
# Run in subshell to avoid overwriting VERSION.
|
||||
TALA_VERSION="$( install_tala && echo "$VERSION" )"
|
||||
TALA_VERSION="$( RELEASE_INFO= install_tala_standalone && echo "$VERSION" )"
|
||||
fi
|
||||
;;
|
||||
homebrew)
|
||||
install_d2_brew
|
||||
if [ -n "${TALA-}" ]; then install_tala_brew; fi
|
||||
;;
|
||||
esac
|
||||
|
||||
COLOR=2 header success
|
||||
FGCOLOR=2 bigheader 'next steps'
|
||||
case $METHOD in
|
||||
standalone) install_post_standalone ;;
|
||||
homebrew) install_post_brew ;;
|
||||
esac
|
||||
}
|
||||
|
||||
install_post_standalone() {
|
||||
log "d2-$VERSION-$OS-$ARCH has been successfully installed into $PREFIX"
|
||||
if [ -n "${TALA-}" ]; then
|
||||
log "tala-$TALA_VERSION-$OS-$ARCH has been successfully installed into $PREFIX"
|
||||
fi
|
||||
log "Rerun this install script with --uninstall to uninstall"
|
||||
log "Rerun this install script with --uninstall to uninstall."
|
||||
log
|
||||
if ! echo "$PATH" | grep -qF "$PREFIX/bin"; then
|
||||
logcat >&2 <<EOF
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
NEXT STEPS
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
Extend your \$PATH to use d2:
|
||||
export PATH=$PREFIX/bin:\$PATH
|
||||
Then run:
|
||||
${TALA+D2_LAYOUT=tala }d2 --help
|
||||
EOF
|
||||
else
|
||||
log " Run ${TALA+D2_LAYOUT=tala }d2 --help for usage."
|
||||
log "Run ${TALA+D2_LAYOUT=tala }d2 --help for usage."
|
||||
fi
|
||||
if ! manpath | grep -qF "$PREFIX/share/man"; then
|
||||
logcat >&2 <<EOF
|
||||
|
|
@ -642,36 +712,61 @@ EOF
|
|||
log " man d2plugin-tala"
|
||||
fi
|
||||
else
|
||||
log " Run man d2 for detailed docs."
|
||||
log "Run man d2 for detailed docs."
|
||||
if [ -n "${TALA-}" ]; then
|
||||
log " Run man d2plugin-tala for detailed docs."
|
||||
log "Run man d2plugin-tala for detailed TALA docs."
|
||||
fi
|
||||
fi
|
||||
logcat >&2 <<EOF
|
||||
|
||||
Something not working? Please let us know:
|
||||
https://github.com/terrastruct/d2/issues/new
|
||||
https://github.com/terrastruct/d2/issues
|
||||
https://github.com/terrastruct/d2/discussions
|
||||
https://discord.gg/NF6X8K4eDq
|
||||
EOF
|
||||
}
|
||||
|
||||
install_d2() {
|
||||
install_post_brew() {
|
||||
log "d2 has been successfully installed with homebrew."
|
||||
if [ -n "${TALA-}" ]; then
|
||||
log "tala has been successfully installed with homebrew."
|
||||
fi
|
||||
log "Rerun this install script with --uninstall to uninstall."
|
||||
log
|
||||
log "Run ${TALA+D2_LAYOUT=tala }d2 --help for usage."
|
||||
log "Run man d2 for detailed docs."
|
||||
if [ -n "${TALA-}" ]; then
|
||||
log "Run man d2plugin-tala for detailed TALA docs."
|
||||
fi
|
||||
logcat >&2 <<EOF
|
||||
|
||||
Something not working? Please let us know:
|
||||
https://github.com/terrastruct/d2/issues
|
||||
https://github.com/terrastruct/d2/discussions
|
||||
https://discord.gg/NF6X8K4eDq
|
||||
EOF
|
||||
}
|
||||
|
||||
install_d2_standalone() {
|
||||
VERSION=${VERSION:-latest}
|
||||
header "installing d2-$VERSION"
|
||||
|
||||
if [ "$VERSION" = latest ]; then
|
||||
fetch_release_info
|
||||
fi
|
||||
|
||||
if command -v d2 >/dev/null; then
|
||||
INSTALLED_VERSION="$(d2 version)"
|
||||
if [ ! "${FORCE-}" -a "$VERSION" = "$INSTALLED_VERSION" ]; then
|
||||
log "skipping installation as version $VERSION is already installed."
|
||||
log "skipping installation as d2 $VERSION is already installed."
|
||||
return 0
|
||||
fi
|
||||
log "uninstalling $INSTALLED_VERSION to install $VERSION"
|
||||
if ! uninstall_d2; then
|
||||
warn "failed to uninstall $INSTALLED_VERSION"
|
||||
log "uninstalling d2 $INSTALLED_VERSION to install $VERSION"
|
||||
if ! uninstall_d2_standalone; then
|
||||
warn "failed to uninstall d2 $INSTALLED_VERSION"
|
||||
fi
|
||||
fi
|
||||
|
||||
header "installing d2-$VERSION"
|
||||
install_standalone_d2
|
||||
}
|
||||
|
||||
install_standalone_d2() {
|
||||
ARCHIVE="d2-$VERSION-$OS-$ARCH.tar.gz"
|
||||
log "installing standalone release $ARCHIVE from github"
|
||||
|
||||
|
|
@ -690,19 +785,38 @@ install_standalone_d2() {
|
|||
"$sh_c" sh -c "'cd \"$INSTALL_DIR/d2-$VERSION\" && make install PREFIX=\"$PREFIX\"'"
|
||||
}
|
||||
|
||||
install_tala() {
|
||||
REPO="${REPO_TALA:-terrastruct/TALA}"
|
||||
VERSION=$TALA
|
||||
RELEASE_INFO=
|
||||
fetch_release_info
|
||||
header "installing tala-$VERSION"
|
||||
install_standalone_tala
|
||||
install_d2_brew() {
|
||||
header "installing d2 with homebrew"
|
||||
sh_c brew tap terrastruct/d2
|
||||
sh_c brew install d2
|
||||
}
|
||||
|
||||
install_standalone_tala() {
|
||||
install_tala_standalone() {
|
||||
REPO="${REPO_TALA:-terrastruct/tala}"
|
||||
VERSION=$TALA
|
||||
|
||||
header "installing tala-$VERSION"
|
||||
|
||||
if [ "$VERSION" = latest ]; then
|
||||
fetch_release_info
|
||||
fi
|
||||
|
||||
if command -v d2plugin-tala >/dev/null; then
|
||||
INSTALLED_VERSION="$(d2plugin-tala --version)"
|
||||
if [ ! "${FORCE-}" -a "$VERSION" = "$INSTALLED_VERSION" ]; then
|
||||
log "skipping installation as tala $VERSION is already installed."
|
||||
return 0
|
||||
fi
|
||||
log "uninstalling tala $INSTALLED_VERSION to install $VERSION"
|
||||
if ! uninstall_tala_standalone; then
|
||||
warn "failed to uninstall tala $INSTALLED_VERSION"
|
||||
fi
|
||||
fi
|
||||
|
||||
ARCHIVE="tala-$VERSION-$OS-$ARCH.tar.gz"
|
||||
log "installing standalone release $ARCHIVE from github"
|
||||
|
||||
fetch_release_info
|
||||
asset_line=$(sh_c 'cat "$RELEASE_INFO" | grep -n "$ARCHIVE" | cut -d: -f1 | head -n1')
|
||||
asset_url=$(sh_c 'sed -n $((asset_line-3))p "$RELEASE_INFO" | sed "s/^.*: \"\(.*\)\",$/\1/g"')
|
||||
|
||||
|
|
@ -718,36 +832,40 @@ install_standalone_tala() {
|
|||
"$sh_c" sh -c "'cd \"$INSTALL_DIR/tala-$VERSION\" && make install PREFIX=\"$PREFIX\"'"
|
||||
}
|
||||
|
||||
install_tala_brew() {
|
||||
header "installing tala with homebrew"
|
||||
sh_c brew tap terrastruct/d2
|
||||
sh_c brew install tala
|
||||
}
|
||||
|
||||
uninstall() {
|
||||
# We uninstall tala first as package managers require that it be uninstalled before
|
||||
# uninstalling d2 as TALA depends on d2.
|
||||
if command -v d2plugin-tala >/dev/null; then
|
||||
INSTALLED_VERSION="$(d2plugin-tala --version)"
|
||||
header "uninstalling tala-$INSTALLED_VERSION"
|
||||
case $METHOD in
|
||||
standalone) uninstall_tala_standalone ;;
|
||||
homebrew) uninstall_tala_brew ;;
|
||||
esac
|
||||
elif [ "${TALA-}" ]; then
|
||||
warn "no version of tala installed"
|
||||
fi
|
||||
|
||||
if ! command -v d2 >/dev/null; then
|
||||
warn "no version of d2 installed"
|
||||
return 0
|
||||
fi
|
||||
|
||||
INSTALLED_VERSION="$(d2 --version)"
|
||||
if ! uninstall_d2; then
|
||||
echoerr "failed to uninstall $INSTALLED_VERSION"
|
||||
return 1
|
||||
fi
|
||||
if [ "${TALA-}" ]; then
|
||||
if ! command -v d2plugin-tala >/dev/null; then
|
||||
warn "no version of tala installed"
|
||||
return 0
|
||||
fi
|
||||
INSTALLED_VERSION="$(d2plugin-tala --version)"
|
||||
if ! uninstall_tala; then
|
||||
echoerr "failed to uninstall tala $INSTALLED_VERSION"
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
uninstall_d2() {
|
||||
header "uninstalling d2-$INSTALLED_VERSION"
|
||||
uninstall_standalone_d2
|
||||
case $METHOD in
|
||||
standalone) uninstall_d2_standalone ;;
|
||||
homebrew) uninstall_d2_brew ;;
|
||||
esac
|
||||
}
|
||||
|
||||
uninstall_standalone_d2() {
|
||||
uninstall_d2_standalone() {
|
||||
log "uninstalling standalone release of d2-$INSTALLED_VERSION"
|
||||
|
||||
if [ ! -e "$INSTALL_DIR/d2-$INSTALLED_VERSION" ]; then
|
||||
|
|
@ -765,12 +883,11 @@ uninstall_standalone_d2() {
|
|||
"$sh_c" rm -rf "$INSTALL_DIR/d2-$INSTALLED_VERSION"
|
||||
}
|
||||
|
||||
uninstall_tala() {
|
||||
header "uninstalling tala-$INSTALLED_VERSION"
|
||||
uninstall_standalone_tala
|
||||
uninstall_d2_brew() {
|
||||
sh_c brew remove d2
|
||||
}
|
||||
|
||||
uninstall_standalone_tala() {
|
||||
uninstall_tala_standalone() {
|
||||
log "uninstalling standalone release tala-$INSTALLED_VERSION"
|
||||
|
||||
if [ ! -e "$INSTALL_DIR/tala-$INSTALLED_VERSION" ]; then
|
||||
|
|
@ -788,6 +905,10 @@ uninstall_standalone_tala() {
|
|||
"$sh_c" rm -rf "$INSTALL_DIR/tala-$INSTALLED_VERSION"
|
||||
}
|
||||
|
||||
uninstall_tala_brew() {
|
||||
sh_c brew remove tala
|
||||
}
|
||||
|
||||
is_prefix_writable() {
|
||||
sh_c "mkdir -p '$INSTALL_DIR' 2>/dev/null" || true
|
||||
# The reason for checking whether $INSTALL_DIR is writable is that on macOS you have
|
||||
|
|
@ -843,4 +964,9 @@ fetch_gh() {
|
|||
sh_c mv "$file.inprogress" "$file"
|
||||
}
|
||||
|
||||
brew() {
|
||||
# Makes brew sane.
|
||||
HOMEBREW_NO_INSTALL_CLEANUP=1 HOMEBREW_NO_AUTO_UPDATE=1 command brew "$@"
|
||||
}
|
||||
|
||||
main "$@"
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
package version
|
||||
|
||||
// Pre-built binaries will have version set during build time.
|
||||
var Version = "master (built from source)"
|
||||
var Version = "????"
|
||||
|
|
|
|||
45
lib/xmain/flag_helpers.go
Normal file
45
lib/xmain/flag_helpers.go
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
// flag_helpers.go are private functions from pflag/flag.go
|
||||
package xmain
|
||||
|
||||
import "strings"
|
||||
|
||||
func wrap(i, w int, s string) string {
|
||||
if w == 0 {
|
||||
return strings.Replace(s, "\n", "\n"+strings.Repeat(" ", i), -1)
|
||||
}
|
||||
wrap := w - i
|
||||
var r, l string
|
||||
if wrap < 24 {
|
||||
i = 16
|
||||
wrap = w - i
|
||||
r += "\n" + strings.Repeat(" ", i)
|
||||
}
|
||||
if wrap < 24 {
|
||||
return strings.Replace(s, "\n", r, -1)
|
||||
}
|
||||
slop := 5
|
||||
wrap = wrap - slop
|
||||
l, s = wrapN(wrap, slop, s)
|
||||
r = r + strings.Replace(l, "\n", "\n"+strings.Repeat(" ", i), -1)
|
||||
for s != "" {
|
||||
var t string
|
||||
t, s = wrapN(wrap, slop, s)
|
||||
r = r + "\n" + strings.Repeat(" ", i) + strings.Replace(t, "\n", "\n"+strings.Repeat(" ", i), -1)
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
func wrapN(i, slop int, s string) (string, string) {
|
||||
if i+slop > len(s) {
|
||||
return s, ""
|
||||
}
|
||||
w := strings.LastIndexAny(s[:i], " \t\n")
|
||||
if w <= 0 {
|
||||
return s, ""
|
||||
}
|
||||
nlPos := strings.LastIndex(s[:i], "\n")
|
||||
if nlPos > 0 && nlPos < w {
|
||||
return s[:nlPos], s[nlPos+1:]
|
||||
}
|
||||
return s[:w], s[w+1:]
|
||||
}
|
||||
173
lib/xmain/opts.go
Normal file
173
lib/xmain/opts.go
Normal file
|
|
@ -0,0 +1,173 @@
|
|||
package xmain
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/pflag"
|
||||
"oss.terrastruct.com/cmdlog"
|
||||
"oss.terrastruct.com/xos"
|
||||
)
|
||||
|
||||
type Opts struct {
|
||||
Args []string
|
||||
Flags *pflag.FlagSet
|
||||
env *xos.Env
|
||||
log *cmdlog.Logger
|
||||
|
||||
flagEnv map[string]string
|
||||
}
|
||||
|
||||
func NewOpts(env *xos.Env, log *cmdlog.Logger, args []string) *Opts {
|
||||
flags := pflag.NewFlagSet("", pflag.ContinueOnError)
|
||||
flags.SortFlags = false
|
||||
flags.Usage = func() {}
|
||||
flags.SetOutput(io.Discard)
|
||||
return &Opts{
|
||||
Args: args,
|
||||
Flags: flags,
|
||||
env: env,
|
||||
log: log,
|
||||
flagEnv: make(map[string]string),
|
||||
}
|
||||
}
|
||||
|
||||
// Mostly copy pasted pasted from pflag.FlagUsagesWrapped
|
||||
// with modifications for env var
|
||||
func (o *Opts) Defaults() string {
|
||||
buf := new(bytes.Buffer)
|
||||
|
||||
var lines []string
|
||||
|
||||
maxlen := 0
|
||||
maxEnvLen := 0
|
||||
o.Flags.VisitAll(func(flag *pflag.Flag) {
|
||||
if flag.Hidden {
|
||||
return
|
||||
}
|
||||
|
||||
line := ""
|
||||
if flag.Shorthand != "" && flag.ShorthandDeprecated == "" {
|
||||
line = fmt.Sprintf(" -%s, --%s", flag.Shorthand, flag.Name)
|
||||
} else {
|
||||
line = fmt.Sprintf(" --%s", flag.Name)
|
||||
}
|
||||
|
||||
varname, usage := pflag.UnquoteUsage(flag)
|
||||
if varname != "" {
|
||||
line += " " + varname
|
||||
}
|
||||
if flag.NoOptDefVal != "" {
|
||||
switch flag.Value.Type() {
|
||||
case "string":
|
||||
line += fmt.Sprintf("[=\"%s\"]", flag.NoOptDefVal)
|
||||
case "bool":
|
||||
if flag.NoOptDefVal != "true" {
|
||||
line += fmt.Sprintf("[=%s]", flag.NoOptDefVal)
|
||||
}
|
||||
case "count":
|
||||
if flag.NoOptDefVal != "+1" {
|
||||
line += fmt.Sprintf("[=%s]", flag.NoOptDefVal)
|
||||
}
|
||||
default:
|
||||
line += fmt.Sprintf("[=%s]", flag.NoOptDefVal)
|
||||
}
|
||||
}
|
||||
|
||||
line += "\x00"
|
||||
|
||||
if len(line) > maxlen {
|
||||
maxlen = len(line)
|
||||
}
|
||||
|
||||
if e, ok := o.flagEnv[flag.Name]; ok {
|
||||
line += fmt.Sprintf("$%s", e)
|
||||
}
|
||||
|
||||
line += "\x01"
|
||||
|
||||
if len(line) > maxEnvLen {
|
||||
maxEnvLen = len(line)
|
||||
}
|
||||
|
||||
line += usage
|
||||
if flag.Value.Type() == "string" {
|
||||
line += fmt.Sprintf(" (default %q)", flag.DefValue)
|
||||
} else {
|
||||
line += fmt.Sprintf(" (default %s)", flag.DefValue)
|
||||
}
|
||||
if len(flag.Deprecated) != 0 {
|
||||
line += fmt.Sprintf(" (DEPRECATED: %s)", flag.Deprecated)
|
||||
}
|
||||
|
||||
lines = append(lines, line)
|
||||
})
|
||||
|
||||
for _, line := range lines {
|
||||
sidx1 := strings.Index(line, "\x00")
|
||||
sidx2 := strings.Index(line, "\x01")
|
||||
spacing1 := strings.Repeat(" ", maxlen-sidx1)
|
||||
spacing2 := strings.Repeat(" ", (maxEnvLen-maxlen)-sidx2+sidx1)
|
||||
fmt.Fprintln(buf, line[:sidx1], spacing1, line[sidx1+1:sidx2], spacing2, wrap(maxEnvLen+3, 0, line[sidx2+1:]))
|
||||
}
|
||||
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
func (o *Opts) getEnv(flag, k string) string {
|
||||
if k != "" {
|
||||
o.flagEnv[flag] = k
|
||||
return o.env.Getenv(k)
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (o *Opts) Int64(envKey, flag, shortFlag string, defaultVal int64, usage string) (*int64, error) {
|
||||
if env := o.getEnv(flag, envKey); env != "" {
|
||||
envVal, err := strconv.ParseInt(env, 10, 64)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(`invalid environment variable %s. Expected int64. Found "%v".`, envKey, envVal)
|
||||
}
|
||||
defaultVal = envVal
|
||||
}
|
||||
|
||||
return o.Flags.Int64P(flag, shortFlag, defaultVal, usage), nil
|
||||
}
|
||||
|
||||
func (o *Opts) String(envKey, flag, shortFlag string, defaultVal, usage string) *string {
|
||||
if env := o.getEnv(flag, envKey); env != "" {
|
||||
defaultVal = env
|
||||
}
|
||||
|
||||
return o.Flags.StringP(flag, shortFlag, defaultVal, usage)
|
||||
}
|
||||
|
||||
func (o *Opts) Bool(envKey, flag, shortFlag string, defaultVal bool, usage string) (*bool, error) {
|
||||
if env := o.getEnv(flag, envKey); env != "" {
|
||||
if !boolyEnv(env) {
|
||||
return nil, fmt.Errorf(`invalid environment variable %s. Expected bool. Found "%s".`, envKey, env)
|
||||
}
|
||||
if truthyEnv(env) {
|
||||
defaultVal = true
|
||||
} else {
|
||||
defaultVal = false
|
||||
}
|
||||
}
|
||||
|
||||
return o.Flags.BoolP(flag, shortFlag, defaultVal, usage), nil
|
||||
}
|
||||
|
||||
func boolyEnv(s string) bool {
|
||||
return falseyEnv(s) || truthyEnv(s)
|
||||
}
|
||||
|
||||
func falseyEnv(s string) bool {
|
||||
return s == "0" || s == "false"
|
||||
}
|
||||
|
||||
func truthyEnv(s string) bool {
|
||||
return s == "1" || s == "true"
|
||||
}
|
||||
|
|
@ -9,13 +9,11 @@ import (
|
|||
"io"
|
||||
"os"
|
||||
"os/signal"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"cdr.dev/slog"
|
||||
"cdr.dev/slog/sloggers/sloghuman"
|
||||
"github.com/spf13/pflag"
|
||||
|
||||
"oss.terrastruct.com/xos"
|
||||
|
||||
|
|
@ -42,13 +40,9 @@ func Main(run RunFunc) {
|
|||
Stderr: os.Stderr,
|
||||
|
||||
Env: xos.NewEnv(os.Environ()),
|
||||
FlagSet: pflag.NewFlagSet("", pflag.ContinueOnError),
|
||||
Args: args,
|
||||
}
|
||||
ms.Log = cmdlog.Log(ms.Env, os.Stderr)
|
||||
ms.FlagSet.SortFlags = false
|
||||
ms.FlagSet.Usage = func() {}
|
||||
ms.FlagSet.SetOutput(io.Discard)
|
||||
ms.Opts = NewOpts(ms.Env, ms.Log, args)
|
||||
|
||||
sigs := make(chan os.Signal, 1)
|
||||
signal.Notify(sigs, os.Interrupt, syscall.SIGTERM)
|
||||
|
|
@ -90,8 +84,7 @@ type State struct {
|
|||
|
||||
Log *cmdlog.Logger
|
||||
Env *xos.Env
|
||||
Args []string
|
||||
FlagSet *pflag.FlagSet
|
||||
Opts *Opts
|
||||
}
|
||||
|
||||
func (ms *State) Main(ctx context.Context, sigs <-chan os.Signal, run func(context.Context, *State) error) error {
|
||||
|
|
@ -129,13 +122,6 @@ func (ms *State) Main(ctx context.Context, sigs <-chan os.Signal, run func(conte
|
|||
}
|
||||
}
|
||||
|
||||
func (ms *State) FlagHelp() string {
|
||||
b := &strings.Builder{}
|
||||
ms.FlagSet.SetOutput(b)
|
||||
ms.FlagSet.PrintDefaults()
|
||||
return b.String()
|
||||
}
|
||||
|
||||
type ExitError struct {
|
||||
Code int `json:"code"`
|
||||
Message string `json:"message"`
|
||||
|
|
|
|||
Loading…
Reference in a new issue