Merge pull request #87 from terrastruct/releases-c9b3

ci/release: Implement
This commit is contained in:
Anmol Sethi 2022-11-14 14:10:02 -05:00 committed by GitHub
commit 4ecd9d7eda
29 changed files with 2170 additions and 12 deletions

View file

@ -42,27 +42,61 @@
## Quickstart (CLI)
To install:
```sh
# With --dryrun 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 -- --dryrun
# If things look good, install for real.
curl -fsSL https://d2lang.com/install.sh | sh -s --
```
The most convenient way to use D2 is to just run it as a CLI executable to
produce SVGs from `.d2` files.
```sh
go install oss.terrastruct.com/d2
echo 'x -> y -> z' > in.d2
d2 --watch in.d2 out.svg
```
A browser window will open with `out.svg` and live-reload on changes to `in.d2`.
### MacOS
### Installing from source
Homebrew package coming soon.
```sh
go install oss.terrastruct.com/d2
```
### Linux/Windows
### Install
We have precompiled binaries on the [releases](https://github.com/terrastruct/d2/releases)
page. D2 will be added to OS-respective package managers soon.
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
For now, if you don't want to install from source, just use our install script:
Pass `--tala` if you want to install our improved but closed source layout engine
tala. See the docs on [layout engine](#layout-engine) below.
```sh
# With --dryrun 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 -- --dryrun
# If things look good, install for real.
curl -fsSL https://d2lang.com/install.sh | sh -s --
```
To uninstall:
```sh
curl -fsSL https://d2lang.com/install.sh | sh -s -- --uninstall --dryrun
# If things look good, install 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.
## Quickstart (library)
@ -155,6 +189,14 @@ any single type like "radial" or "tree" (as almost all layout engines are). For
information and to download & try TALA, see
[https://github.com/terrastruct/TALA](https://github.com/terrastruct/TALA).
You can just pass `--tala` to the install script to install tala as well:
```
curl -fsSL https://d2lang.com/install.sh | sh -s -- --tala --dryrun
# If things look good, install for real.
curl -fsSL https://d2lang.com/install.sh | sh -s -- --tala
```
## Comparison
For a comparison against other popular text-to-diagram tools, see

49
ci/release/README.md Normal file
View file

@ -0,0 +1,49 @@
# release
## _install.sh
The template for the install script in the root of the repository.
### gen_install.sh
Generates the install.sh script in the root of the repository by prepending the libraries
it depends on from ../sub/lib.
## release.sh
- ./release.sh is the top level script to generate a new release.
Run with --help for usage.
## build.sh
- ./build.sh builds the release archives for each platform into ./build/<VERSION>/*.tar.gz
Run with --help for usage.
> note: Remember for production releases you need to set the $TSTRUCT_OS_ARCH_BUILDER
> variables as we must compile d2 directly on each release target to include dagre.
> See https://github.com/terrastruct/d2/issues/31
Use `--host-only` to build only the release for the host's OS-ARCH pair.
### build_docker.sh
Helper script called by build.sh to build D2 on each linux runner inside Docker.
The Dockerfile is in ./builders/Dockerfile
### _build.sh
Called by build.sh (with --local or macOS) or build_docker.sh (on linux) to create the
release archive.
Do not invoke directly. If you want to produce a build for a single platform run build.sh
as so:
```sh
# To only build the linux-amd64 release.
./build.sh --run=linux-amd64
```
```sh
# To only build the linux-amd64 release locally.
./build.sh --local --run=linux-amd64
```

22
ci/release/_build.sh Executable file
View file

@ -0,0 +1,22 @@
#!/bin/sh
set -eu
cd -- "$(dirname "$0")/../.."
. ./ci/sub/lib.sh
sh_c mkdir -p "$HW_BUILD_DIR"
sh_c rsync --recursive --perms --delete \
--human-readable --copy-links ./ci/release/template/ "$HW_BUILD_DIR/"
VERSION=$VERSION sh_c eval "'$HW_BUILD_DIR/README.md.sh'" \> "'$HW_BUILD_DIR/README.md'"
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"
sh_c mkdir -p "$HW_BUILD_DIR/bin"
sh_c go build -ldflags "'-X oss.terrastruct.com/d2/lib/version.Version=$VERSION'" \
-o "$HW_BUILD_DIR/bin/d2" ./cmd/d2
ARCHIVE=$PWD/$ARCHIVE
cd "$(dirname "$HW_BUILD_DIR")"
sh_c tar -czf "$ARCHIVE" "$(basename "$HW_BUILD_DIR")"
cd ->/dev/null

377
ci/release/_install.sh Executable file
View file

@ -0,0 +1,377 @@
#!/bin/sh
set -eu
cd -- "$(dirname "$0")/../sub/lib"
. ./log.sh
. ./flag.sh
. ./release.sh
cd - >/dev/null
help() {
arg0="$0"
if [ "$0" = sh ]; then
arg0="curl -fsSL https://d2lang.com/install.sh | sh -s --"
fi
cat <<EOF
usage: $arg0 [--dry-run] [--version vX.X.X] [--edge] [--method detect] [--prefix /usr/local]
[--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.
Flags:
--dry-run
Pass to have install.sh show the install method and flags that will be used to install
without executing them. Very useful to understand what changes it will make to your system.
--version vX.X.X
Pass to have install.sh install the given version instead of the latest version.
--edge
Pass to build and install D2 from source. This will still use --method if set to detect
to install the release archive for your OS, whether it's apt, yum, brew or standalone
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.
--method [detect | standalone]
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.
- 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.
--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
/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
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
--force:
Force installation over the existing version even if they match. It will attempt a
uninstall first before installing the new version. The installed release tree
will be deleted from /usr/local/lib/d2/d2-<VERSION> but the release archive in
~/.cache/d2/release will remain.
--uninstall:
Uninstall the installed version of d2. The --method and --prefix flags must be the same
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.
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>
note: Deleting the unarchived releases will cause --uninstall to stop working.
You can rerun install.sh to update your version of D2. install.sh will avoid reinstalling
if the installed version is the latest unless --force is passed.
EOF
}
main() {
if [ -n "${DEBUG-}" ]; then
set -x
fi
METHOD=standalone
while :; do
flag_parse "$@"
case "$FLAG" in
h|help)
help
return 0
;;
dry-run)
flag_noarg && shift "$FLAGSHIFT"
DRY_RUN=1
;;
version)
flag_nonemptyarg && shift "$FLAGSHIFT"
VERSION=$FLAGARG
;;
tala)
shift "$FLAGSHIFT"
TALA=${FLAGARG:-latest}
;;
edge)
flag_noarg && shift "$FLAGSHIFT"
EDGE=1
echoerr "$FLAGRAW is currently unimplemented"
return 1
;;
method)
flag_nonemptyarg && shift "$FLAGSHIFT"
METHOD=$FLAGARG
echoerr "$FLAGRAW is currently unimplemented"
return 1
;;
prefix)
flag_nonemptyarg && shift "$FLAGSHIFT"
export PREFIX=$FLAGARG
;;
force)
flag_noarg && shift "$FLAGSHIFT"
FORCE=1
;;
uninstall)
flag_noarg && shift "$FLAGSHIFT"
UNINSTALL=1
;;
'')
shift "$FLAGSHIFT"
break
;;
*)
flag_errusage "unrecognized flag $FLAGRAW"
;;
esac
done
if [ $# -gt 0 ]; then
flag_errusage "no arguments are accepted"
fi
REPO=${REPO:-terrastruct/d2}
PREFIX=${PREFIX:-/usr/local}
OS=$(os)
ARCH=$(arch)
CACHE_DIR=$(cache_dir)
mkdir -p "$CACHE_DIR"
INSTALL_DIR=$PREFIX/lib/d2
if [ -n "${UNINSTALL-}" ]; then
uninstall
return 0
fi
VERSION=${VERSION:-latest}
if [ "$VERSION" = latest ]; then
header "fetching latest release info"
fetch_release_info
fi
install
}
install() {
install_d2
if [ "${TALA-}" ]; then
# Run in subshell to avoid overwriting VERSION.
( install_tala )
fi
COLOR=2 header success
log "d2-$VERSION-$OS-$ARCH has been successfully installed into $PREFIX"
if ! echo "$PATH" | grep -qF "$PREFIX/bin"; then
logcat >&2 <<EOF
Extend your path to use d2:
PATH=$PREFIX/bin:\$PATH
Then run:
d2 --help
EOF
else
log "You may now run d2 --help"
fi
}
install_d2() {
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."
return 0
fi
log "uninstalling $INSTALLED_VERSION to install $VERSION"
if ! uninstall_d2; then
warn "failed to uninstall $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"
fetch_release_info
asset_line=$(cat "$RELEASE_INFO" | grep -n "$ARCHIVE" | cut -d: -f1 | head -n1)
asset_url=$(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
"$sh_c" tar -C "$INSTALL_DIR" -xzf "$CACHE_DIR/$ARCHIVE"
"$sh_c" sh -c "'cd \"$INSTALL_DIR/d2-$VERSION\" && make install PREFIX=\"$PREFIX\"'"
}
install_tala() {
install_standalone_tala
}
install_standalone_tala() {
REPO="${REPO_TALA:-terrastruct/TALA}"
VERSION=$TALA
RELEASE_INFO=
fetch_release_info
ARCHIVE="tala-$VERSION-$OS-$ARCH.tar.gz"
log "installing standalone release $ARCHIVE from github"
asset_line=$(cat "$RELEASE_INFO" | grep -n "$ARCHIVE" | cut -d: -f1 | head -n1)
asset_url=$(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
"$sh_c" tar -C "$INSTALL_DIR" -xzf "$CACHE_DIR/$ARCHIVE"
"$sh_c" sh -c "'cd \"$INSTALL_DIR/tala-$VERSION\" && make install PREFIX=\"$PREFIX\"'"
}
uninstall() {
if ! command -v d2 >/dev/null; then
echoerr "no version of d2 installed"
return 1
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
echoerr "no version of tala installed"
return 1
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
}
uninstall_standalone_d2() {
log "uninstalling standalone release of d2-$INSTALLED_VERSION"
if [ ! -e "$INSTALL_DIR/d2-$INSTALLED_VERSION" ]; then
echoerr "missing standalone install release directory $INSTALL_DIR/d2-$INSTALLED_VERSION"
return 1
fi
sh_c="sh_c"
if ! is_prefix_writable; then
sh_c="sudo_sh_c"
fi
"$sh_c" sh -c "'cd \"$INSTALL_DIR/d2-$INSTALLED_VERSION\" && make uninstall PREFIX=\"$PREFIX\"'"
"$sh_c" rm -rf "$INSTALL_DIR/d2-$INSTALLED_VERSION"
}
uninstall_tala() {
uninstall_standalone_tala
}
uninstall_standalone_tala() {
log "uninstalling standalone release tala-$INSTALLED_VERSION"
if [ ! -e "$INSTALL_DIR/tala-$INSTALLED_VERSION" ]; then
echoerr "missing standalone install release directory $INSTALL_DIR/tala-$INSTALLED_VERSION"
return 1
fi
sh_c="sh_c"
if ! is_prefix_writable; then
sh_c="sudo_sh_c"
fi
"$sh_c" sh -c "'cd \"$INSTALL_DIR/tala-$INSTALLED_VERSION\" && make uninstall PREFIX=\"$PREFIX\"'"
"$sh_c" rm -rf "$INSTALL_DIR/tala-$INSTALLED_VERSION"
}
is_prefix_writable() {
sh_c "mkdir -p '$INSTALL_DIR' 2>/dev/null" || true
# The reason for checking whether bin is writable specifically 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.
if [ ! -w "$PREFIX/bin" ]; then
return 0
fi
}
cache_dir() {
if [ -n "${XDG_CACHE_HOME-}" ]; then
echo "$XDG_CACHE_HOME/d2/release"
elif [ -n "${HOME-}" ]; then
echo "$HOME/.cache/d2/release"
else
echo "/tmp/d2-cache/release"
fi
}
fetch_release_info() {
if [ -n "${RELEASE_INFO-}" ]; then
return 0
fi
log "fetching info on $VERSION version of $REPO"
RELEASE_INFO=$(mktemp -d)/release-info.json
if [ "$VERSION" = latest ]; then
release_info_url="https://api.github.com/repos/$REPO/releases/$VERSION"
else
release_info_url="https://api.github.com/repos/$REPO/releases/tags/$VERSION"
fi
fetch_gh "$release_info_url" "$RELEASE_INFO" \
'application/json'
VERSION=$(cat "$RELEASE_INFO" | grep -m1 tag_name | sed 's/^.*: "\(.*\)",$/\1/g')
}
curl_gh() {
sh_c curl -fL ${GITHUB_TOKEN+"-H \"Authorization: Bearer \$GITHUB_TOKEN\""} "$@"
}
fetch_gh() {
url=$1
file=$2
accept=$3
if [ -e "$file" ]; then
log "reusing $file"
return
fi
curl_gh -#o "$file.inprogress" -C- -H "'Accept: $accept'" "$url"
sh_c mv "$file.inprogress" "$file"
}
main "$@"

192
ci/release/build.sh Executable file
View file

@ -0,0 +1,192 @@
#!/bin/sh
set -eu
cd -- "$(dirname "$0")/../.."
. ./ci/sub/lib.sh
help() {
cat <<EOF
usage: $0 [--rebuild] [--local] [--dry-run] [--run=regex] [--host-only]
$0 builds D2 release archives into ./ci/release/build/<version>/d2-<VERSION>-<OS>-<ARCH>.tar.gz
The version is detected via git describe which will use the git tag for the current
commit if available.
Flags:
--rebuild
By default build.sh will avoid rebuilding finished assets if they already exist but if you
changed something and need to force rebuild, use this flag.
--local
By default build.sh uses \$TSTRUCT_MACOS_AMD64_BUILDER, \$TSTRUCT_MACOS_ARM64_BUILDER,
\$TSTRUCT_LINUX_AMD64_BUILDER and \$TSTRUCT_LINUX_ARM64_BUILDER to build the release
archives. It's required for now due to the following issue:
https://github.com/terrastruct/d2/issues/31 With --local, build.sh will cross compile
locally. warning: This is only for testing purposes, do not use in production!
--host-only
Use to build the release archive for the host OS-ARCH only. All logging is done to stderr
so in a script you can read from stdout to get the path to the release archive.
--run=regex
Use to run only the OS-ARCH jobs that match the given regex. e.g. --run=linux only runs
the linux jobs. --run=linux-amd64 only runs the linux-amd64 job.
--version vX.X.X
Use to overwrite the version detected from git.
EOF
}
main() {
while :; do
flag_parse "$@"
case "$FLAG" in
h|help)
help
return 0
;;
rebuild)
flag_noarg && shift "$FLAGSHIFT"
REBUILD=1
;;
local)
flag_noarg && shift "$FLAGSHIFT"
LOCAL=1
;;
dry-run)
flag_noarg && shift "$FLAGSHIFT"
DRY_RUN=1
;;
run)
flag_reqarg && shift "$FLAGSHIFT"
JOBFILTER="$FLAGARG"
;;
host-only)
flag_noarg && shift "$FLAGSHIFT"
HOST_ONLY=1
LOCAL=1
;;
version)
flag_nonemptyarg && shift "$FLAGSHIFT"
VERSION=$FLAGARG
;;
'')
shift "$FLAGSHIFT"
break
;;
*)
flag_errusage "unrecognized flag $FLAGRAW"
;;
esac
done
if [ $# -gt 0 ]; then
flag_errusage "no arguments are accepted"
fi
VERSION=${VERSION:-$(git_describe_ref)}
BUILD_DIR=ci/release/build/$VERSION
if [ -n "${HOST_ONLY-}" ]; then
runjob $(os)-$(arch) "OS=$(os) ARCH=$(arch) build" &
waitjobs
return 0
fi
runjob linux-amd64 'OS=linux ARCH=amd64 build' &
runjob linux-arm64 'OS=linux ARCH=arm64 build' &
runjob macos-amd64 'OS=macos ARCH=amd64 build' &
runjob macos-arm64 'OS=macos ARCH=arm64 build' &
waitjobs
}
build() {
HW_BUILD_DIR="$BUILD_DIR/$OS-$ARCH/d2-$VERSION"
ARCHIVE="$BUILD_DIR/d2-$VERSION-$OS-$ARCH.tar.gz"
if [ -e "$ARCHIVE" -a -z "${REBUILD-}" ]; then
log "skipping as already built at $ARCHIVE"
return 0
fi
if [ -n "${LOCAL-}" ]; then
build_local
return 0
fi
case $OS in
macos)
case $ARCH in
amd64)
RHOST=$TSTRUCT_MACOS_AMD64_BUILDER build_rhost_macos
;;
arm64)
RHOST=$TSTRUCT_MACOS_ARM64_BUILDER build_rhost_macos
;;
*)
warn "no builder for OS=$OS ARCH=$ARCH, building locally..."
build_local
;;
esac
;;
linux)
case $ARCH in
amd64)
RHOST=$TSTRUCT_LINUX_AMD64_BUILDER build_rhost_linux
;;
arm64)
RHOST=$TSTRUCT_LINUX_ARM64_BUILDER build_rhost_linux
;;
*)
warn "no builder for OS=$OS ARCH=$ARCH, building locally..."
build_local
;;
esac
;;
*)
warn "no builder for OS=$OS, building locally..."
build_local
;;
esac
}
build_local() {
export DRY_RUN \
HW_BUILD_DIR \
VERSION \
OS \
ARCH \
ARCHIVE
sh_c ./ci/release/_build.sh
}
build_rhost_macos() {
sh_c ssh "$RHOST" mkdir -p src
sh_c rsync --archive --human-readable --delete ./ "$RHOST:src/d2/"
sh_c ssh -tttt "$RHOST" "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\${PATH+:\$PATH}\" \
./src/d2/ci/release/_build.sh"
sh_c rsync --archive --human-readable "$RHOST:src/d2/$ARCHIVE" "$ARCHIVE"
}
build_rhost_linux() {
sh_c ssh "$RHOST" mkdir -p src
sh_c rsync --archive --human-readable --delete ./ "$RHOST:src/d2/"
sh_c ssh -tttt "$RHOST" "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 rsync --archive --human-readable "$RHOST:src/d2/$ARCHIVE" "$ARCHIVE"
}
main "$@"

1
ci/release/build/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
*

17
ci/release/build_docker.sh Executable file
View file

@ -0,0 +1,17 @@
#!/bin/sh
set -eu
cd -- "$(dirname "$0")/../.."
. ./ci/sub/lib.sh
tag="$(sh_c docker build \
--build-arg GOVERSION="1.19.3.linux-$ARCH" \
-qf ./ci/release/builders/Dockerfile ./ci/release/builders)"
docker_run \
-e DRY_RUN \
-e HW_BUILD_DIR \
-e VERSION \
-e OS \
-e ARCH \
-e ARCHIVE \
-e TERM \
"$tag" ./src/d2/ci/release/_build.sh

View file

@ -0,0 +1,12 @@
FROM debian:10
RUN apt-get update
RUN apt-get install -y curl
ARG GOVERSION=
RUN curl -fsSL "https://go.dev/dl/go$GOVERSION.tar.gz" >/tmp/go.tar.gz
RUN tar -C /usr/local -xzf /tmp/go.tar.gz
ENV PATH="/usr/local/go/bin:$PATH"
RUN apt-get install -y build-essential
RUN apt-get install -y rsync

View file

@ -0,0 +1,16 @@
FROM centos:7
ARG GOVERSION=
RUN curl -fsSL "https://go.dev/dl/go$GOVERSION.tar.gz" >/tmp/go.tar.gz
RUN tar -C /usr/local -xzf /tmp/go.tar.gz
ENV PATH="/usr/local/go/bin:$PATH"
RUN yum install -y rsync wget
RUN yum groupinstall -y 'Development Tools'
RUN curl -fsSL https://ftp.gnu.org/gnu/gcc/gcc-5.2.0/gcc-5.2.0.tar.gz >/tmp/gcc.tar.gz
RUN tar -C /usr/local -xzf /tmp/gcc.tar.gz
RUN cd /usr/local/gcc-5.2.0 && ./contrib/download_prerequisites && mkdir -p build \
&& cd build && ../configure --disable-multilib && make && make install

236
ci/release/builders/aws_ensure.sh Executable file
View file

@ -0,0 +1,236 @@
#!/bin/sh
set -eu
cd -- "$(dirname "$0")/../../.."
. ./ci/sub/lib.sh
help() {
cat <<EOF
usage: $0 [--dry-run]
$0 creates and ensures the d2 builders in AWS.
EOF
}
main() {
while :; do
flag_parse "$@"
case "$FLAG" in
h|help)
help
return 0
;;
dry-run)
flag_noarg && shift "$FLAGSHIFT"
DRY_RUN=1
;;
'')
shift "$FLAGSHIFT"
break
;;
*)
flag_errusage "unrecognized flag $FLAGRAW"
;;
esac
done
if [ $# -gt 0 ]; then
flag_errusage "no arguments are accepted"
fi
create_rhosts
init_rhosts
}
create_rhosts() {
KEY_NAME=$(aws ec2 describe-key-pairs | jq -r .KeyPairs[0].KeyName)
VPC_ID=$(aws ec2 describe-vpcs | jq -r .Vpcs[0].VpcId)
header security-group
SG_ID=$(aws ec2 describe-security-groups --group-names ssh 2>/dev/null \
| jq -r .SecurityGroups[0].GroupId)
if [ -z "$SG_ID" ]; then
SG_ID=$(sh_c aws ec2 create-security-group \
--group-name ssh \
--description ssh \
--vpc-id "$VPC_ID" | jq -r .GroupId)
fi
header security-group-ingress
SG_RULES_COUNT=$(aws ec2 describe-security-groups --group-names ssh \
| jq -r '.SecurityGroups[0].IpPermissions | length')
if [ "$SG_RULES_COUNT" -eq 0 ]; then
sh_c aws ec2 authorize-security-group-ingress \
--group-id "$SG_ID" \
--protocol tcp \
--port 22 \
--cidr 0.0.0.0/0 >/dev/null
fi
header linux-amd64
state=$(aws ec2 describe-instances --filters \
'Name=instance-state-name,Values=pending,running,stopping,stopped' 'Name=tag:Name,Values=d2-builder-linux-amd64' \
| jq -r '.Reservations[].Instances[].State.Name')
if [ -z "$state" ]; then
sh_c aws ec2 run-instances \
--image-id=ami-0d593311db5abb72b \
--count=1 \
--instance-type=t2.small \
--security-groups=ssh \
"--key-name=$KEY_NAME" \
--tag-specifications '"ResourceType=instance,Tags=[{Key=Name,Value=d2-builder-linux-amd64}]"' \
'"ResourceType=volume,Tags=[{Key=Name,Value=d2-builder-linux-amd64}]"' >/dev/null
fi
while true; do
dnsname=$(sh_c aws ec2 describe-instances \
--filters 'Name=instance-state-name,Values=pending,running,stopping,stopped' 'Name=tag:Name,Values=d2-builder-linux-amd64' \
| jq -r '.Reservations[].Instances[].PublicDnsName')
if [ -n "$dnsname" ]; then
log "TSTRUCT_LINUX_AMD64_BUILDER=ec2-user@$dnsname"
export TSTRUCT_LINUX_AMD64_BUILDER=ec2-user@$dnsname
break
fi
sleep 5
done
header linux-arm64
state=$(aws ec2 describe-instances --filters \
'Name=instance-state-name,Values=pending,running,stopping,stopped' 'Name=tag:Name,Values=d2-builder-linux-arm64' \
| jq -r '.Reservations[].Instances[].State.Name')
if [ -z "$state" ]; then
sh_c aws ec2 run-instances \
--image-id=ami-0efabcf945ffd8831 \
--count=1 \
--instance-type=t4g.small \
--security-groups=ssh \
"--key-name=$KEY_NAME" \
--tag-specifications '"ResourceType=instance,Tags=[{Key=Name,Value=d2-builder-linux-arm64}]"' \
'"ResourceType=volume,Tags=[{Key=Name,Value=d2-builder-linux-arm64}]"' >/dev/null
fi
while true; do
dnsname=$(sh_c aws ec2 describe-instances \
--filters 'Name=instance-state-name,Values=pending,running,stopping,stopped' 'Name=tag:Name,Values=d2-builder-linux-arm64' \
| jq -r '.Reservations[].Instances[].PublicDnsName')
if [ -n "$dnsname" ]; then
log "TSTRUCT_LINUX_ARM64_BUILDER=ec2-user@$dnsname"
export TSTRUCT_LINUX_ARM64_BUILDER=ec2-user@$dnsname
break
fi
sleep 5
done
header "macos-amd64-host"
MACOS_AMD64_HOST_ID=$(aws ec2 describe-hosts --filter 'Name=state,Values=pending,available' 'Name=tag:Name,Values=d2-builder-macos-amd64' | jq -r '.Hosts[].HostId')
if [ -z "$MACOS_AMD64_HOST_ID" ]; then
MACOS_AMD64_HOST_ID=$(sh_c aws ec2 allocate-hosts --instance-type mac1.metal --quantity 1 --availability-zone us-west-2a \
--tag-specifications '"ResourceType=dedicated-host,Tags=[{Key=Name,Value=d2-builder-macos-amd64}]"' \
| jq -r .HostIds[0])
fi
header "macos-arm64-host"
MACOS_ARM64_HOST_ID=$(aws ec2 describe-hosts --filter 'Name=state,Values=pending,available' 'Name=tag:Name,Values=d2-builder-macos-arm64' | jq -r '.Hosts[].HostId')
if [ -z "$MACOS_ARM64_HOST_ID" ]; then
MACOS_ARM64_HOST_ID=$(sh_c aws ec2 allocate-hosts --instance-type mac2.metal --quantity 1 --availability-zone us-west-2a \
--tag-specifications '"ResourceType=dedicated-host,Tags=[{Key=Name,Value=d2-builder-macos-amd64}]"' \
| jq -r .HostIds[0])
fi
header macos-amd64
state=$(aws ec2 describe-instances --filters \
'Name=instance-state-name,Values=pending,running,stopping,stopped' 'Name=tag:Name,Values=d2-builder-macos-amd64' \
| jq -r '.Reservations[].Instances[].State.Name')
if [ -z "$state" ]; then
sh_c aws ec2 run-instances \
--image-id=ami-0dd2ded7568750663 \
--count=1 \
--instance-type=mac1.metal \
--security-groups=ssh \
"--key-name=$KEY_NAME" \
--placement "Tenancy=host,HostId=$MACOS_AMD64_HOST_ID" \
--tag-specifications '"ResourceType=instance,Tags=[{Key=Name,Value=d2-builder-macos-amd64}]"' \
'"ResourceType=volume,Tags=[{Key=Name,Value=d2-builder-macos-amd64}]"' >/dev/null
fi
while true; do
dnsname=$(sh_c aws ec2 describe-instances \
--filters 'Name=instance-state-name,Values=pending,running,stopping,stopped' 'Name=tag:Name,Values=d2-builder-macos-amd64' \
| jq -r '.Reservations[].Instances[].PublicDnsName')
if [ -n "$dnsname" ]; then
log "TSTRUCT_MACOS_AMD64_BUILDER=ec2-user@$dnsname"
export TSTRUCT_MACOS_AMD64_BUILDER=ec2-user@$dnsname
break
fi
sleep 5
done
header macos-arm64
state=$(aws ec2 describe-instances --filters \
'Name=instance-state-name,Values=pending,running,stopping,stopped' 'Name=tag:Name,Values=d2-builder-macos-arm64' \
| jq -r '.Reservations[].Instances[].State.Name')
if [ -z "$state" ]; then
sh_c aws ec2 run-instances \
--image-id=ami-0af0516ff2c43dbbe \
--count=1 \
--instance-type=mac2.metal \
--security-groups=ssh \
"--key-name=$KEY_NAME" \
--placement "Tenancy=host,HostId=$MACOS_ARM64_HOST_ID" \
--tag-specifications '"ResourceType=instance,Tags=[{Key=Name,Value=d2-builder-macos-arm64}]"' \
'"ResourceType=volume,Tags=[{Key=Name,Value=d2-builder-macos-arm64}]"' >/dev/null
fi
while true; do
dnsname=$(sh_c aws ec2 describe-instances \
--filters 'Name=instance-state-name,Values=pending,running,stopping,stopped' 'Name=tag:Name,Values=d2-builder-macos-arm64' \
| jq -r '.Reservations[].Instances[].PublicDnsName')
if [ -n "$dnsname" ]; then
log "TSTRUCT_MACOS_ARM64_BUILDER=ec2-user@$dnsname"
export TSTRUCT_MACOS_ARM64_BUILDER=ec2-user@$dnsname
break
fi
sleep 5
done
}
init_rhosts() {
header linux-amd64
RHOST=$TSTRUCT_LINUX_AMD64_BUILDER init_rhost_linux
header linux-arm64
RHOST=$TSTRUCT_LINUX_ARM64_BUILDER init_rhost_linux
header macos-amd64
RHOST=$TSTRUCT_MACOS_AMD64_BUILDER init_rhost_macos
header macos-arm64
RHOST=$TSTRUCT_MACOS_ARM64_BUILDER init_rhost_macos
COLOR=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"
log "export TSTRUCT_MACOS_ARM64_BUILDER=$TSTRUCT_MACOS_ARM64_BUILDER"
}
init_rhost_linux() {
while true; do
if sh_c ssh "$RHOST" :; then
break
fi
sleep 5
done
sh_c ssh "$RHOST" 'sudo yum upgrade -y'
sh_c ssh "$RHOST" 'sudo yum install -y docker'
sh_c ssh "$RHOST" 'sudo systemctl start docker'
sh_c ssh "$RHOST" 'sudo systemctl enable docker'
sh_c ssh "$RHOST" 'sudo usermod -a -G docker ec2-user'
sh_c ssh "$RHOST" 'sudo reboot' || true
}
init_rhost_macos() {
while true; do
if sh_c ssh "$RHOST" :; then
break
fi
sleep 5
done
sh_c ssh "$RHOST" '": | /bin/bash -c \"\$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)\""'
sh_c ssh "$RHOST" '/usr/local/bin/brew update'
sh_c ssh "$RHOST" '/usr/local/bin/brew upgrade'
sh_c ssh "$RHOST" '/usr/local/bin/brew install go'
}
main "$@"

View file

@ -0,0 +1,43 @@
#!/bin/sh
set -eu
cd -- "$(dirname "$0")/../../.."
. ./ci/sub/lib.sh
help() {
cat <<EOF
usage: $0 [--dry-run] [...args]
$0 runs ssh-copy-id on each builder.
args are passed to ssh-copy-id directly.
EOF
}
main() {
while :; do
flag_parse "$@"
case "$FLAG" in
h|help)
help
return 0
;;
dry-run)
flag_noarg && shift "$FLAGSHIFT"
DRY_RUN=1
;;
'')
shift "$FLAGSHIFT"
break
;;
*)
flag_errusage "unrecognized flag $FLAGRAW"
;;
esac
done
sh_c ssh-copy-id "$@" "\$TSTRUCT_LINUX_AMD64_BUILDER"
sh_c ssh-copy-id "$@" "\$TSTRUCT_LINUX_ARM64_BUILDER"
sh_c ssh-copy-id "$@" "\$TSTRUCT_MACOS_AMD64_BUILDER"
sh_c ssh-copy-id "$@" "\$TSTRUCT_MACOS_ARM64_BUILDER"
}
main "$@"

View file

@ -0,0 +1,13 @@
For v0.0.99 we focused on X, Y and Z. Enjoy!
#### Features 💸
- Now you can easily do x, y and z #9999
#### Improvements 🔧
- Improves something or the other #9999
#### Bugfixes 🔴
- Fixes something or the other #9999

View file

@ -0,0 +1,13 @@
For v0.0.99 we focused on X, Y and Z. Enjoy!
#### Features 💸
- Now you can easily do x, y and z #9999
#### Improvements 🔧
- Improves something or the other #9999
#### Bugfixes 🔴
- Fixes something or the other #9999

View file

@ -0,0 +1,13 @@
For v0.0.99 we focused on X, Y and Z. Enjoy!
#### Features 💸
- Now you can easily do x, y and z #9999
#### Improvements 🔧
- Improves something or the other #9999
#### Bugfixes 🔴
- Fixes something or the other #9999

37
ci/release/gen_install.sh Executable file
View file

@ -0,0 +1,37 @@
#!/bin/sh
set -eu
cd -- "$(dirname "$0")/../.."
. ./ci/sub/lib.sh
cat >./install.sh <<EOF
#!/bin/sh
set -eu
# *************
# DO NOT EDIT
#
# install.sh was bundled together from
#
# - ./ci/sub/lib/rand.sh
# - ./ci/sub/lib/log.sh
# - ./ci/sub/lib/flag.sh
# - ./ci/sub/lib/release.sh
# - ./ci/release/_install.sh
#
# The last of which implements the installation logic.
#
# Generated by ./ci/release/gen_install.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 \
./ci/sub/lib/flag.sh \
./ci/sub/lib/release.sh \
\| sed "-e'/^\. /d'" \>\> ./install.sh
sh_c cat ./ci/release/_install.sh \
\| sed -n "'/cd -- \"\$(dirname/,/cd -/!p'" \>\> install.sh

5
ci/release/release.sh Executable file
View file

@ -0,0 +1,5 @@
#!/bin/sh
set -eu
cd -- "$(dirname "$0")/../.."
./ci/sub/release/release.sh "$@"

View file

@ -0,0 +1 @@
../../../LICENSE.txt

View file

@ -0,0 +1,12 @@
.POSIX:
.SILENT:
PREFIX = $(DESTDIR)/usr/local
.PHONY: install
install:
PREFIX='$(PREFIX)' ./scripts/install.sh
.PHONY: uninstall
uninstall:
PREFIX='$(PREFIX)' ./scripts/uninstall.sh

View file

@ -0,0 +1,28 @@
#!/bin/sh
set -eu
cat <<EOF
# d2
For docs, more installation options and the source code see https://oss.terrastruct.com/d2
version: $VERSION
## Install
\`\`\`sh
make install DRY_RUN=1
# If it looks right, run:
make install
\`\`\`
Pass \`PREFIX=whatever\` to change the installation prefix.
## Uninstall
\`\`\`sh
make uninstall DRY_RUN=1
# If it looks right, run:
make uninstall
\`\`\`
EOF

View file

@ -0,0 +1,62 @@
.Dd $Mdocdate$
.Dt d2 1
.Os
.Sh NAME
.Nm d2
.Nd compiles and renders d2 diagrams into svgs.
.Sh SYNOPSIS
.Nm d2
.Op Fl -watch Ar false
.Op Fl -theme Em 0
.Ar file.d2
.Op Ar file.svg
.Nm d2
.Op Fl -watch Ar false
.Op Fl -theme Em 0
.Ar file.d2
.Op Ar ...
.Nm d2
.Ar layout Op Ar name
.Sh DESCRIPTION
.Nm
compiles and renders
.Ar file.d2
to
.Ar file.svg
.Ns .
.Pp
Pass - to have
.Nm
read from stdin or write to stdout.
.Pp
See more docs, the source code and license at
.Lk https://oss.terrastruct.com/d2
.Sh OPTIONS
.Bl -tag -width Fl
.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 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
.It Fl b , -bundle Ar true
Bundle all assets and layers into the output svg.
.It Fl d , -debug
Print debug logs.
.It Fl h , -help
Print usage information and exit.
.It Fl v , -version
Print version information and exit.
.El
.Sh SUBCOMMANDS
.Bl -tag -width Fl
.It Ar layout
Lists available layout engine options with short help.
.It Ar layout Op Ar name
Display long help for a particular layout engine.
.El
.Sh SEE ALSO
.Xr d2plugin-tala 1
.Sh AUTHORS
Terrastruct Inc.

View file

@ -0,0 +1,17 @@
#!/bin/sh
set -eu
cd -- "$(dirname "$0")/.."
. ./scripts/lib.sh
main() {
if [ ! -e "${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 mkdir -p "$PREFIX"
sh_c install ./bin/d2 "$PREFIX/bin/d2"
sh_c install ./man/d2.1 "$PREFIX/share/man/man1"
}
main "$@"

View file

@ -0,0 +1,137 @@
#!/bin/sh
rand() {
seed="$1"
range="$2"
seed_file="$(mktemp)"
_echo "$seed" | md5sum > "$seed_file"
shuf -i "$range" -n 1 --random-source="$seed_file"
}
pick() {
seed="$1"
shift
i="$(rand "$seed" "1-$#")"
eval "_echo \"\$$i\""
}
tput() {
if [ -n "$TERM" ]; then
command tput "$@"
fi
}
setaf() {
tput setaf "$1"
shift
printf '%s' "$*"
tput sgr0
}
_echo() {
printf '%s\n' "$*"
}
get_rand_color() {
# 1-6 are regular and 9-14 are bright.
# 1,2 and 9,10 are red and green but we use those for success and failure.
pick "$*" 3 4 5 6 11 12 13 14
}
echop() {
prefix="$1"
shift
if [ "$#" -gt 0 ]; then
printfp "$prefix" "%s\n" "$*"
else
printfp "$prefix"
printf '\n'
fi
}
printfp() {(
prefix="$1"
shift
if [ -z "${COLOR:-}" ]; then
COLOR="$(get_rand_color "$prefix")"
fi
printf '%s' "$(setaf "$COLOR" "$prefix")"
if [ $# -gt 0 ]; then
printf ': '
printf "$@"
fi
)}
catp() {
prefix="$1"
shift
printfp "$prefix"
printf ': '
read -r line
_echo "$line"
indent=$(repeat ' ' 2)
sed "s/^/$indent/"
}
repeat() {
char="$1"
times="$2"
seq -s "$char" "$times" | tr -d '[:digit:]'
}
strlen() {
printf %s "$1" | wc -c
}
echoerr() {
COLOR=1 echop err "$*" >&2
}
caterr() {
COLOR=1 catp err "$@" >&2
}
printferr() {
COLOR=1 printfp err "$@" >&2
}
logp() {
echop "$@" >&2
}
logfp() {
printfp "$@" >&2
}
logpcat() {
catp "$@" >&2
}
log() {
COLOR=5 logp log "$@"
}
logf() {
COLOR=5 logfp log "$@"
}
logcat() {
COLOR=5 catp log "$@" >&2
}
sh_c() {
COLOR=3 logp exec "$*"
if [ -z "${DRY_RUN-}" ]; then
"$@"
fi
}
header() {
logp "/* $1 */"
}

View file

@ -0,0 +1,16 @@
#!/bin/sh
set -eu
cd -- "$(dirname "$0")/.."
. ./scripts/lib.sh
main() {
if [ ! -e "${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 rm -f "$PREFIX/bin/d2"
sh_c rm -f "$PREFIX/share/man/man1"
}
main "$@"

2
ci/sub

@ -1 +1 @@
Subproject commit 7ff8380897435e73d53e329a7cd39dc38c7ad227
Subproject commit be28ce747b280c59b8fe89e6b29bb0637984985d

View file

@ -27,7 +27,7 @@ Subcommands:
%[1]s layout - Lists available layout engine options with short help
%[1]s layout [layout name] - Display long help for a particular layout engine
See more docs at https://oss.terrastruct.com/d2
See more docs and the source code at https://oss.terrastruct.com/d2
`, ms.Name, ms.FlagHelp())
}

2
go.mod
View file

@ -28,7 +28,7 @@ require (
oss.terrastruct.com/xjson v0.0.0-20221018000420-4986731c4c4a
oss.terrastruct.com/xos v0.0.0-20221018030138-c96e7ae96e5d
oss.terrastruct.com/xrand v0.0.0-20221020211818-4ac08e618333
rogchap.com/v8go v0.7.0
rogchap.com/v8go v0.7.1-0.20221102201510-1f00b5007d95
)
require (

4
go.sum
View file

@ -786,8 +786,8 @@ oss.terrastruct.com/xos v0.0.0-20221018030138-c96e7ae96e5d h1:rrPTkbAfsRTW1WLoTz
oss.terrastruct.com/xos v0.0.0-20221018030138-c96e7ae96e5d/go.mod h1:uSONPDInIwglnC+0zYs8YOjiUD8ZUSnqDTTI82j7Oro=
oss.terrastruct.com/xrand v0.0.0-20221020211818-4ac08e618333 h1:7EdxwXM75Id1VIN71QbE8bLzZRMs0qD7olnDw5gbI7w=
oss.terrastruct.com/xrand v0.0.0-20221020211818-4ac08e618333/go.mod h1:O7TAoBmlQhoi46RdgVikDcoLRb/vLflhkXCAd+nO4SM=
rogchap.com/v8go v0.7.0 h1:kgjbiO4zE5itA962ze6Hqmbs4HgZbGzmueCXsZtremg=
rogchap.com/v8go v0.7.0/go.mod h1:MxgP3pL2MW4dpme/72QRs8sgNMmM0pRc8DPhcuLWPAs=
rogchap.com/v8go v0.7.1-0.20221102201510-1f00b5007d95 h1:r89YHVIWeQj/A3Nu6462eqARUECJlJkLRk36pfML1xA=
rogchap.com/v8go v0.7.1-0.20221102201510-1f00b5007d95/go.mod h1:MxgP3pL2MW4dpme/72QRs8sgNMmM0pRc8DPhcuLWPAs=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
rsc.io/pdf v0.1.1 h1:k1MczvYDUvJBe93bYd7wrZLLUEcLZAuF824/I4e5Xr4=
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=

791
install.sh Executable file
View file

@ -0,0 +1,791 @@
#!/bin/sh
set -eu
# *************
# DO NOT EDIT
#
# install.sh was bundled together from
#
# - ./ci/sub/lib/rand.sh
# - ./ci/sub/lib/log.sh
# - ./ci/sub/lib/flag.sh
# - ./ci/sub/lib/release.sh
# - ./ci/release/_install.sh
#
# The last of which implements the installation logic.
#
# Generated by ./ci/release/gen_install.sh.
# *************
#!/bin/sh
if [ "${LIB_RAND-}" ]; then
return 0
fi
LIB_RAND=1
rand() {
seed="$1"
range="$2"
seed_file="$(mktemp)"
_echo "$seed" | md5sum > "$seed_file"
shuf -i "$range" -n 1 --random-source="$seed_file"
}
pick() {
seed="$1"
shift
i="$(rand "$seed" "1-$#")"
eval "_echo \"\$$i\""
}
#!/bin/sh
if [ "${LIB_LOG-}" ]; then
return 0
fi
LIB_LOG=1
tput() {
if [ -n "$TERM" ]; then
command tput "$@"
fi
}
setaf() {
tput setaf "$1"
shift
printf '%s' "$*"
tput sgr0
}
_echo() {
printf '%s\n' "$*"
}
get_rand_color() {
# 1-6 are regular and 9-14 are bright.
# 1,2 and 9,10 are red and green but we use those for success and failure.
pick "$*" 3 4 5 6 11 12 13 14
}
echop() {
prefix="$1"
shift
if [ "$#" -gt 0 ]; then
printfp "$prefix" "%s\n" "$*"
else
printfp "$prefix"
printf '\n'
fi
}
printfp() {(
prefix="$1"
shift
if [ -z "${COLOR:-}" ]; then
COLOR="$(get_rand_color "$prefix")"
fi
printf '%s' "$(setaf "$COLOR" "$prefix")"
if [ $# -gt 0 ]; then
printf ': '
printf "$@"
fi
)}
catp() {
prefix="$1"
shift
sed "s/^/$(printfp "$prefix" '')/"
}
repeat() {
char="$1"
times="$2"
seq -s "$char" "$times" | tr -d '[:digit:]'
}
strlen() {
printf %s "$1" | wc -c
}
echoerr() {
COLOR=1 echop err "$*" | humanpath>&2
}
caterr() {
COLOR=1 catp err "$@" | humanpath >&2
}
printferr() {
COLOR=1 printfp err "$@" | humanpath >&2
}
logp() {
echop "$@" | humanpath >&2
}
logfp() {
printfp "$@" | humanpath >&2
}
logpcat() {
catp "$@" | humanpath >&2
}
log() {
COLOR=5 logp log "$@"
}
logf() {
COLOR=5 logfp log "$@"
}
logcat() {
COLOR=5 catp log "$@" >&2
}
warn() {
COLOR=3 logp warn "$@"
}
warnf() {
COLOR=3 logfp warn "$@"
}
sh_c() {
COLOR=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
exit 1
fi
}
header() {
logp "/* $1 */"
}
# humanpath replaces all occurrences of $HOME with ~
humanpath() {
if [ -z "${HOME-}" ]; then
cat
else
sed "s#$HOME#~#g"
fi
}
hide() {
out="$(mktemp)"
set +e
"$@" >"$out" 2>&1
code="$?"
set -e
if [ "$code" -eq 0 ]; then
return
fi
cat "$out" >&2
exit "$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
}
#!/bin/sh
if [ "${LIB_FLAG-}" ]; then
return 0
fi
LIB_FLAG=1
# flag_parse implements a robust flag parser.
#
# For a full fledge example see ../examples/date.sh
#
# It differs from getopts(1) in that long form options are supported. Currently the only
# deficiency is that short combined options are not supported like -xyzq. That would be
# interpreted as a single -xyzq flag. The other deficiency is lack of support for short
# flag syntax like -carg where the arg is not separated from the flag. This one is
# unfixable I believe unfortunately but for combined short flags I have opened
# https://github.com/terrastruct/ci/issues/6
#
# flag_parse stores state in $FLAG, $FLAGRAW, $FLAGARG and $FLAGSHIFT.
# FLAG contains the name of the flag without hyphens.
# FLAGRAW contains the name of the flag as passed in with hyphens.
# FLAGARG contains the argument for the flag if there was any.
# If there was none, it will not be set.
# 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 --
#
# 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.
# You only need to explicitly check if the flag was set if you care whether the user
# explicitly passed the empty string as the argument.
#
# Otherwise, call one of the flag_*arg functions:
#
# If a flag requires an argument, call flag_reqarg
# - $FLAGARG is guaranteed to be set after.
# If a flag requires a non empty argument, call flag_nonemptyarg
# - $FLAGARG is guaranteed to be set to a non empty string after.
# If a flag should not be passed an argument, call flag_noarg
# - $FLAGARG is guaranteed to be unset after.
#
# And then shift "$FLAGSHIFT"
flag_parse() {
case "${1-}" in
-*=*)
# Remove everything after first equal sign.
FLAG="${1%%=*}"
# Remove leading hyphens.
FLAG="${FLAG#-}"; FLAG="${FLAG#-}"
FLAGRAW="$(flag_fmt)"
# Remove everything before first equal sign.
FLAGARG="${1#*=}"
FLAGSHIFT=1
;;
-)
FLAG=
FLAGRAW=
unset FLAGARG
FLAGSHIFT=0
;;
--)
FLAG=
FLAGRAW=
unset FLAGARG
FLAGSHIFT=1
;;
-*)
# Remove leading hyphens.
FLAG="${1#-}"; FLAG="${FLAG#-}"
FLAGRAW=$(flag_fmt)
unset FLAGARG
FLAGSHIFT=1
if [ $# -gt 1 ]; then
case "$2" in
-)
FLAGARG="$2"
FLAGSHIFT=2
;;
-*)
;;
*)
FLAGARG="$2"
FLAGSHIFT=2
;;
esac
fi
;;
*)
FLAG=
FLAGRAW=
unset FLAGARG
FLAGSHIFT=0
;;
esac
return 0
}
flag_reqarg() {
if [ "${FLAGARG+x}" != x ]; then
flag_errusage "flag $FLAGRAW requires an argument"
fi
}
flag_nonemptyarg() {
flag_reqarg
if [ -z "$FLAGARG" ]; then
flag_errusage "flag $FLAGRAW requires a non-empty argument"
fi
}
flag_noarg() {
if [ "$FLAGSHIFT" -eq 2 ]; then
unset FLAGARG
FLAGSHIFT=1
elif [ "${FLAGARG+x}" = x ]; then
# Means an argument was passed via equal sign as in -$FLAG=$FLAGARG
flag_errusage "flag $FLAGRAW does not accept an argument"
fi
}
flag_errusage() {
caterr <<EOF
$1
Run with --help for usage.
EOF
return 1
}
flag_fmt() {
if [ "$(printf %s "$FLAG" | wc -c)" -eq 1 ]; then
echo "-$FLAG"
else
echo "--$FLAG"
fi
}
#!/bin/sh
if [ "${LIB_RELEASE-}" ]; then
return 0
fi
LIB_RELEASE=1
goos() {
case $1 in
macos) echo darwin ;;
*) echo $1 ;;
esac
}
os() {
uname=$(uname)
case $uname in
Linux) echo linux ;;
Darwin) echo macos ;;
FreeBSD) echo freebsd ;;
*) echo "$uname" ;;
esac
}
arch() {
uname_m=$(uname -m)
case $uname_m in
aarch64) echo arm64 ;;
x86_64) echo amd64 ;;
*) echo "$uname_m" ;;
esac
}
gh_repo() {
gh repo view --json nameWithOwner --template '{{ .nameWithOwner }}'
}
#!/bin/sh
set -eu
help() {
arg0="$0"
if [ "$0" = sh ]; then
arg0="curl -fsSL https://d2lang.com/install.sh | sh -s --"
fi
cat <<EOF
usage: $arg0 [--dry-run] [--version vX.X.X] [--edge] [--method detect] [--prefix /usr/local]
[--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.
Flags:
--dry-run
Pass to have install.sh show the install method and flags that will be used to install
without executing them. Very useful to understand what changes it will make to your system.
--version vX.X.X
Pass to have install.sh install the given version instead of the latest version.
--edge
Pass to build and install D2 from source. This will still use --method if set to detect
to install the release archive for your OS, whether it's apt, yum, brew or standalone
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.
--method [detect | standalone]
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.
- 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.
--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
/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
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
--force:
Force installation over the existing version even if they match. It will attempt a
uninstall first before installing the new version. The installed release tree
will be deleted from /usr/local/lib/d2/d2-<VERSION> but the release archive in
~/.cache/d2/release will remain.
--uninstall:
Uninstall the installed version of d2. The --method and --prefix flags must be the same
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.
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>
note: Deleting the unarchived releases will cause --uninstall to stop working.
You can rerun install.sh to update your version of D2. install.sh will avoid reinstalling
if the installed version is the latest unless --force is passed.
EOF
}
main() {
if [ -n "${DEBUG-}" ]; then
set -x
fi
METHOD=standalone
while :; do
flag_parse "$@"
case "$FLAG" in
h|help)
help
return 0
;;
dry-run)
flag_noarg && shift "$FLAGSHIFT"
DRY_RUN=1
;;
version)
flag_nonemptyarg && shift "$FLAGSHIFT"
VERSION=$FLAGARG
;;
tala)
shift "$FLAGSHIFT"
TALA=${FLAGARG:-latest}
;;
edge)
flag_noarg && shift "$FLAGSHIFT"
EDGE=1
echoerr "$FLAGRAW is currently unimplemented"
return 1
;;
method)
flag_nonemptyarg && shift "$FLAGSHIFT"
METHOD=$FLAGARG
echoerr "$FLAGRAW is currently unimplemented"
return 1
;;
prefix)
flag_nonemptyarg && shift "$FLAGSHIFT"
export PREFIX=$FLAGARG
;;
force)
flag_noarg && shift "$FLAGSHIFT"
FORCE=1
;;
uninstall)
flag_noarg && shift "$FLAGSHIFT"
UNINSTALL=1
;;
'')
shift "$FLAGSHIFT"
break
;;
*)
flag_errusage "unrecognized flag $FLAGRAW"
;;
esac
done
if [ $# -gt 0 ]; then
flag_errusage "no arguments are accepted"
fi
REPO=${REPO:-terrastruct/d2}
PREFIX=${PREFIX:-/usr/local}
OS=$(os)
ARCH=$(arch)
CACHE_DIR=$(cache_dir)
mkdir -p "$CACHE_DIR"
INSTALL_DIR=$PREFIX/lib/d2
if [ -n "${UNINSTALL-}" ]; then
uninstall
return 0
fi
VERSION=${VERSION:-latest}
if [ "$VERSION" = latest ]; then
header "fetching latest release info"
fetch_release_info
fi
install
}
install() {
install_d2
if [ "${TALA-}" ]; then
# Run in subshell to avoid overwriting VERSION.
( install_tala )
fi
COLOR=2 header success
log "d2-$VERSION-$OS-$ARCH has been successfully installed into $PREFIX"
if ! echo "$PATH" | grep -qF "$PREFIX/bin"; then
logcat >&2 <<EOF
Extend your path to use d2:
PATH=$PREFIX/bin:\$PATH
Then run:
d2 --help
EOF
else
log "You may now run d2 --help"
fi
}
install_d2() {
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."
return 0
fi
log "uninstalling $INSTALLED_VERSION to install $VERSION"
if ! uninstall_d2; then
warn "failed to uninstall $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"
fetch_release_info
asset_line=$(cat "$RELEASE_INFO" | grep -n "$ARCHIVE" | cut -d: -f1 | head -n1)
asset_url=$(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
"$sh_c" tar -C "$INSTALL_DIR" -xzf "$CACHE_DIR/$ARCHIVE"
"$sh_c" sh -c "'cd \"$INSTALL_DIR/d2-$VERSION\" && make install PREFIX=\"$PREFIX\"'"
}
install_tala() {
install_standalone_tala
}
install_standalone_tala() {
REPO="${REPO_TALA:-terrastruct/TALA}"
VERSION=$TALA
RELEASE_INFO=
fetch_release_info
ARCHIVE="tala-$VERSION-$OS-$ARCH.tar.gz"
log "installing standalone release $ARCHIVE from github"
asset_line=$(cat "$RELEASE_INFO" | grep -n "$ARCHIVE" | cut -d: -f1 | head -n1)
asset_url=$(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
"$sh_c" tar -C "$INSTALL_DIR" -xzf "$CACHE_DIR/$ARCHIVE"
"$sh_c" sh -c "'cd \"$INSTALL_DIR/tala-$VERSION\" && make install PREFIX=\"$PREFIX\"'"
}
uninstall() {
if ! command -v d2 >/dev/null; then
echoerr "no version of d2 installed"
return 1
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
echoerr "no version of tala installed"
return 1
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
}
uninstall_standalone_d2() {
log "uninstalling standalone release of d2-$INSTALLED_VERSION"
if [ ! -e "$INSTALL_DIR/d2-$INSTALLED_VERSION" ]; then
echoerr "missing standalone install release directory $INSTALL_DIR/d2-$INSTALLED_VERSION"
return 1
fi
sh_c="sh_c"
if ! is_prefix_writable; then
sh_c="sudo_sh_c"
fi
"$sh_c" sh -c "'cd \"$INSTALL_DIR/d2-$INSTALLED_VERSION\" && make uninstall PREFIX=\"$PREFIX\"'"
"$sh_c" rm -rf "$INSTALL_DIR/d2-$INSTALLED_VERSION"
}
uninstall_tala() {
uninstall_standalone_tala
}
uninstall_standalone_tala() {
log "uninstalling standalone release tala-$INSTALLED_VERSION"
if [ ! -e "$INSTALL_DIR/tala-$INSTALLED_VERSION" ]; then
echoerr "missing standalone install release directory $INSTALL_DIR/tala-$INSTALLED_VERSION"
return 1
fi
sh_c="sh_c"
if ! is_prefix_writable; then
sh_c="sudo_sh_c"
fi
"$sh_c" sh -c "'cd \"$INSTALL_DIR/tala-$INSTALLED_VERSION\" && make uninstall PREFIX=\"$PREFIX\"'"
"$sh_c" rm -rf "$INSTALL_DIR/tala-$INSTALLED_VERSION"
}
is_prefix_writable() {
sh_c "mkdir -p '$INSTALL_DIR' 2>/dev/null" || true
# The reason for checking whether bin is writable specifically 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.
if [ ! -w "$PREFIX/bin" ]; then
return 0
fi
}
cache_dir() {
if [ -n "${XDG_CACHE_HOME-}" ]; then
echo "$XDG_CACHE_HOME/d2/release"
elif [ -n "${HOME-}" ]; then
echo "$HOME/.cache/d2/release"
else
echo "/tmp/d2-cache/release"
fi
}
fetch_release_info() {
if [ -n "${RELEASE_INFO-}" ]; then
return 0
fi
log "fetching info on $VERSION version of $REPO"
RELEASE_INFO=$(mktemp -d)/release-info.json
if [ "$VERSION" = latest ]; then
release_info_url="https://api.github.com/repos/$REPO/releases/$VERSION"
else
release_info_url="https://api.github.com/repos/$REPO/releases/tags/$VERSION"
fi
fetch_gh "$release_info_url" "$RELEASE_INFO" \
'application/json'
VERSION=$(cat "$RELEASE_INFO" | grep -m1 tag_name | sed 's/^.*: "\(.*\)",$/\1/g')
}
curl_gh() {
sh_c curl -fL ${GITHUB_TOKEN+"-H \"Authorization: Bearer \$GITHUB_TOKEN\""} "$@"
}
fetch_gh() {
url=$1
file=$2
accept=$3
if [ -e "$file" ]; then
log "reusing $file"
return
fi
curl_gh -#o "$file.inprogress" -C- -H "'Accept: $accept'" "$url"
sh_c mv "$file.inprogress" "$file"
}
main "$@"

View file

@ -2,6 +2,7 @@ package version
import (
"context"
"fmt"
"github.com/google/go-github/github"
"oss.terrastruct.com/cmdlog"
@ -11,12 +12,17 @@ import (
var Version = "master (built from source)"
func CheckVersion(ctx context.Context, logger *cmdlog.Logger) {
logger.Info.Printf("D2 version: %s\n", Version)
fmt.Println(Version)
if Version == "master (built from source)" {
return
}
// Install script uses -v to check the version, we shouldn't be checking for
// updates here...
// https://github.com/terrastruct/d2/issues/49#issuecomment-1313229683
return
logger.Info.Printf("Checking for updates...")
latest, err := getLatestVersion(ctx)
if err != nil {