Merge branch 'master' into elk-trace-to-shape-border

This commit is contained in:
Alexander Wang 2022-12-18 20:15:05 -08:00
commit b5fb81bbb0
No known key found for this signature in database
GPG key ID: D89FA31966BDBECE
33 changed files with 1870 additions and 471 deletions

18
ci/release/Dockerfile Normal file
View file

@ -0,0 +1,18 @@
# https://hub.docker.com/repository/docker/terrastruct/d2
FROM debian:latest
ARG TARGETARCH
COPY ./d2-*-linux-$TARGETARCH.tar.gz /tmp
RUN mkdir -p /usr/local/lib/d2 \
&& tar -C /usr/local/lib/d2 -xzf /tmp/d2-*-linux-"$TARGETARCH".tar.gz \
&& /usr/local/lib/d2/d2-*/scripts/install.sh \
&& rm -Rf /tmp/d2-*-linux-"$TARGETARCH".tar.gz
WORKDIR /root/src
EXPOSE 8080
ENV PORT 8080
ENV HOST 0.0.0.0
ENV BROWSER false
ENTRYPOINT ["/usr/local/bin/d2"]

View file

@ -2,7 +2,7 @@
## _install.sh ## _install.sh
The template for the install script in the root of the repository. The template for the install script in the root of the d2 repository.
### gen_install.sh ### gen_install.sh
@ -23,12 +23,12 @@ it depends on from ../sub/lib.
> variables as we must compile d2 directly on each release target to include dagre. > variables as we must compile d2 directly on each release target to include dagre.
> See https://github.com/terrastruct/d2/issues/31 > See https://github.com/terrastruct/d2/issues/31
Use `--host-only` to build only the release for the host's OS-ARCH pair. Use `--host-only` to build only the release for the host's `$OS-$ARCH` pair.
### build_docker.sh ### build_docker.sh
Helper script called by build.sh to build D2 on each linux runner inside Docker. Helper script called by build.sh to build D2 on each linux runner inside Docker.
The Dockerfile is in ./builders/Dockerfile The Dockerfile is in ./linux/Dockerfile
### _build.sh ### _build.sh

545
ci/release/aws/ensure.sh Executable file
View file

@ -0,0 +1,545 @@
#!/bin/sh
set -eu
. "$(dirname "$0")/../../../ci/sub/lib.sh"
cd -- "$(dirname "$0")/../../.."
help() {
cat <<EOF
usage: $0 [--dry-run] [--skip-create] [--skip-init] [--copy-id=id.pub]
[--run=jobregex]
$0 creates and ensures the d2 builders in AWS.
EOF
}
main() {
while flag_parse "$@"; do
case "$FLAG" in
h|help)
help
return 0
;;
x)
flag_noarg && shift "$FLAGSHIFT"
set -x
export TRACE=1
;;
dry-run)
flag_noarg && shift "$FLAGSHIFT"
export DRY_RUN=1
;;
copy-id)
flag_nonemptyarg && shift "$FLAGSHIFT"
ID_PUB_PATH=$FLAGARG
;;
run)
flag_reqarg && shift "$FLAGSHIFT"
JOBFILTER="$FLAGARG"
;;
*)
flag_errusage "unrecognized flag $FLAGRAW"
;;
esac
done
shift "$FLAGSHIFT"
if [ $# -gt 0 ]; then
flag_errusage "no arguments are accepted"
fi
if [ -z "${ID_PUB_PATH-}" ]; then
flag_errusage "--copy-id is required"
fi
JOBNAME=create runjob_filter create_remote_hosts
JOBNAME=init && runjob_filter init_remote_hosts
FGCOLOR=2 header summary
echo "export CI_D2_LINUX_AMD64=$CI_D2_LINUX_AMD64"
echo "export CI_D2_LINUX_ARM64=$CI_D2_LINUX_ARM64"
echo "export CI_D2_MACOS_AMD64=$CI_D2_MACOS_AMD64"
echo "export CI_D2_MACOS_ARM64=$CI_D2_MACOS_ARM64"
echo "export CI_D2_WINDOWS_AMD64=$CI_D2_WINDOWS_AMD64"
}
create_remote_hosts() {
bigheader create_remote_hosts
KEY_NAME=$(aws ec2 describe-key-pairs | jq -r .KeyPairs[0].KeyName)
KEY_NAME_WINDOWS=windows
VPC_ID=$(aws ec2 describe-vpcs | jq -r .Vpcs[0].VpcId)
JOBNAME=$JOBNAME/security-groups runjob_filter create_security_groups
JOBNAME=$JOBNAME/linux/amd64 runjob_filter create_linux_amd64
JOBNAME=$JOBNAME/linux/arm64 runjob_filter create_linux_arm64
JOBNAME=$JOBNAME/macos/amd64 runjob_filter create_macos_amd64
JOBNAME=$JOBNAME/macos/arm64 runjob_filter create_macos_arm64
JOBNAME=$JOBNAME/windows/amd64 runjob_filter create_windows_amd64
}
create_security_groups() {
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 windows-security-group
SG_ID=$(aws ec2 describe-security-groups --group-names windows 2>/dev/null \
| jq -r .SecurityGroups[0].GroupId)
if [ -z "$SG_ID" ]; then
SG_ID=$(sh_c aws ec2 create-security-group \
--group-name windows \
--description windows \
--vpc-id "$VPC_ID" | jq -r .GroupId)
fi
header windows-security-group-ingress
SG_RULES_COUNT=$(aws ec2 describe-security-groups --group-names windows \
| jq -r '.SecurityGroups[0].IpPermissions | length')
if [ "$SG_RULES_COUNT" -ne 2 ]; 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
sh_c aws ec2 authorize-security-group-ingress \
--group-id "$SG_ID" \
--protocol tcp \
--port 3389 \
--cidr 0.0.0.0/0 >/dev/null
fi
}
create_linux_amd64() {
header linux-amd64
REMOTE_NAME=ci-d2-linux-amd64
state=$(aws ec2 describe-instances --filters \
'Name=instance-state-name,Values=pending,running,stopping,stopped' 'Name=tag:Name,Values=ci-d2-linux-amd64' \
| jq -r '.Reservations[].Instances[].State.Name')
if [ -z "$state" ]; then
sh_c aws ec2 run-instances \
--image-id=ami-0ecc74eca1d66d8a6 \
--count=1 \
--instance-type=t3.small \
--security-groups=ssh \
"--key-name=$KEY_NAME" \
--iam-instance-profile 'Name=AmazonSSMRoleForInstancesQuickSetup' \
--block-device-mappings '"DeviceName=/dev/sda1,Ebs={VolumeSize=64,VolumeType=gp3}"' \
--tag-specifications '"ResourceType=instance,Tags=[{Key=Name,Value=ci-d2-linux-amd64}]"' \
'"ResourceType=volume,Tags=[{Key=Name,Value=ci-d2-linux-amd64}]"' >/dev/null
fi
wait_remote_host_ip
log "CI_D2_LINUX_AMD64=ubuntu@$ip"
export CI_D2_LINUX_AMD64=ubuntu@$ip
}
create_linux_arm64() {
header linux-arm64
REMOTE_NAME=ci-d2-linux-arm64
state=$(aws ec2 describe-instances --filters \
'Name=instance-state-name,Values=pending,running,stopping,stopped' 'Name=tag:Name,Values=ci-d2-linux-arm64' \
| jq -r '.Reservations[].Instances[].State.Name')
if [ -z "$state" ]; then
sh_c aws ec2 run-instances \
--image-id=ami-06e2dea2cdda3acda \
--count=1 \
--instance-type=t4g.small \
--security-groups=ssh \
"--key-name=$KEY_NAME" \
--iam-instance-profile 'Name=AmazonSSMRoleForInstancesQuickSetup' \
--block-device-mappings '"DeviceName=/dev/sda1,Ebs={VolumeSize=64,VolumeType=gp3}"' \
--tag-specifications '"ResourceType=instance,Tags=[{Key=Name,Value=ci-d2-linux-arm64}]"' \
'"ResourceType=volume,Tags=[{Key=Name,Value=ci-d2-linux-arm64}]"' >/dev/null
fi
wait_remote_host_ip
log "CI_D2_LINUX_ARM64=ubuntu@$ip"
export CI_D2_LINUX_ARM64=ubuntu@$ip
}
create_macos_amd64() {
header macos-amd64-host
MACOS_AMD64_ID=$(aws ec2 describe-hosts --filter 'Name=state,Values=pending,available' 'Name=tag:Name,Values=ci-d2-macos-amd64' | jq -r '.Hosts[].HostId')
if [ -z "$MACOS_AMD64_ID" ]; then
MACOS_AMD64_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=ci-d2-macos-amd64}]"' \
| jq -r .HostIds[0])
fi
header macos-amd64
REMOTE_NAME=ci-d2-macos-amd64
state=$(aws ec2 describe-instances --filters \
'Name=instance-state-name,Values=pending,running,stopping,stopped' 'Name=tag:Name,Values=ci-d2-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" \
--iam-instance-profile 'Name=AmazonSSMRoleForInstancesQuickSetup' \
--placement "Tenancy=host,HostId=$MACOS_AMD64_ID" \
--block-device-mappings '"DeviceName=/dev/sda1,Ebs={VolumeSize=100,VolumeType=gp3}"' \
--tag-specifications '"ResourceType=instance,Tags=[{Key=Name,Value=ci-d2-macos-amd64}]"' \
'"ResourceType=volume,Tags=[{Key=Name,Value=ci-d2-macos-amd64}]"' >/dev/null
fi
wait_remote_host_ip
log "CI_D2_MACOS_AMD64=ec2-user@$ip"
export CI_D2_MACOS_AMD64=ec2-user@$ip
}
create_macos_arm64() {
header macos-arm64-host
MACOS_ARM64_ID=$(aws ec2 describe-hosts --filter 'Name=state,Values=pending,available' 'Name=tag:Name,Values=ci-d2-macos-arm64' | jq -r '.Hosts[].HostId')
if [ -z "$MACOS_ARM64_ID" ]; then
MACOS_ARM64_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=ci-d2-macos-arm64}]"' \
| jq -r .HostIds[0])
fi
header macos-arm64
REMOTE_NAME=ci-d2-macos-arm64
state=$(aws ec2 describe-instances --filters \
'Name=instance-state-name,Values=pending,running,stopping,stopped' 'Name=tag:Name,Values=ci-d2-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" \
--iam-instance-profile 'Name=AmazonSSMRoleForInstancesQuickSetup' \
--placement "Tenancy=host,HostId=$MACOS_ARM64_ID" \
--block-device-mappings '"DeviceName=/dev/sda1,Ebs={VolumeSize=100,VolumeType=gp3}"' \
--tag-specifications '"ResourceType=instance,Tags=[{Key=Name,Value=ci-d2-macos-arm64}]"' \
'"ResourceType=volume,Tags=[{Key=Name,Value=ci-d2-macos-arm64}]"' >/dev/null
fi
wait_remote_host_ip
log "CI_D2_MACOS_ARM64=ec2-user@$ip"
export CI_D2_MACOS_ARM64=ec2-user@$ip
}
create_windows_amd64() {
header windows-amd64
REMOTE_NAME=ci-d2-windows-amd64
state=$(aws ec2 describe-instances --filters \
'Name=instance-state-name,Values=pending,running,stopping,stopped' "Name=tag:Name,Values=$REMOTE_NAME" \
| jq -r '.Reservations[].Instances[].State.Name')
if [ -z "$state" ]; then
sh_c aws ec2 run-instances \
--image-id=ami-0c5300e833c2b32f3 \
--count=1 \
--instance-type=t3.medium \
--security-groups=windows \
"--key-name=$KEY_NAME_WINDOWS" \
--iam-instance-profile 'Name=AmazonSSMRoleForInstancesQuickSetup' \
--block-device-mappings '"DeviceName=/dev/sda1,Ebs={VolumeSize=64,VolumeType=gp3}"' \
--tag-specifications "'ResourceType=instance,Tags=[{Key=Name,Value=$REMOTE_NAME}]'" \
"'ResourceType=volume,Tags=[{Key=Name,Value=$REMOTE_NAME}]'" >/dev/null
fi
wait_remote_host_ip
log "CI_D2_WINDOWS_AMD64=Administrator@$ip"
export CI_D2_WINDOWS_AMD64=Administrator@$ip
}
wait_remote_host_ip() {
while true; do
ip=$(sh_c aws ec2 describe-instances \
--filters 'Name=instance-state-name,Values=pending,running,stopping,stopped' "Name=tag:Name,Values=$REMOTE_NAME" \
| jq -r '.Reservations[].Instances[].PublicIpAddress')
if [ -n "$ip" ]; then
alloc_static_ip
ip=$(sh_c aws ec2 describe-instances \
--filters 'Name=instance-state-name,Values=pending,running,stopping,stopped' "Name=tag:Name,Values=$REMOTE_NAME" \
| jq -r '.Reservations[].Instances[].PublicIpAddress')
ssh-keygen -R "$ip"
break
fi
sleep 5
done
}
alloc_static_ip() {
allocation_id=$(aws ec2 describe-addresses --filters "Name=tag:Name,Values=$REMOTE_NAME" | jq -r '.Addresses[].AllocationId')
if [ -z "$allocation_id" ]; then
sh_c aws ec2 allocate-address --tag-specifications "'ResourceType=elastic-ip,Tags=[{Key=Name,Value=$REMOTE_NAME}]'"
allocation_id=$(aws ec2 describe-addresses --filters "Name=tag:Name,Values=$REMOTE_NAME" | jq -r '.Addresses[].AllocationId')
fi
instance_id=$(aws ec2 describe-instances \
--filters 'Name=instance-state-name,Values=pending,running,stopping,stopped' "Name=tag:Name,Values=$REMOTE_NAME" \
| jq -r '.Reservations[].Instances[].InstanceId')
aws ec2 associate-address --instance-id "$instance_id" --allocation-id "$allocation_id"
}
init_remote_hosts() {
bigheader init_remote_hosts
JOBNAME=$JOBNAME/linux/amd64 runjob_filter REMOTE_HOST=$CI_D2_LINUX_AMD64 REMOTE_NAME=ci-d2-linux-amd64 init_remote_linux
JOBNAME=$JOBNAME/linux/arm64 runjob_filter REMOTE_HOST=$CI_D2_LINUX_ARM64 REMOTE_NAME=ci-d2-linux-arm64 init_remote_linux
JOBNAME=$JOBNAME/macos/amd64 runjob_filter REMOTE_HOST=$CI_D2_MACOS_AMD64 REMOTE_NAME=ci-d2-macos-amd64 init_remote_macos
JOBNAME=$JOBNAME/macos/arm64 runjob_filter REMOTE_HOST=$CI_D2_MACOS_ARM64 REMOTE_NAME=ci-d2-macos-arm64 init_remote_macos
JOBNAME=$JOBNAME/windows/amd64 runjob_filter REMOTE_HOST=$CI_D2_WINDOWS_AMD64 REMOTE_NAME=ci-d2-windows-amd64 init_remote_windows
# Windows and AWS SSM both defeated me.
FGCOLOR=3 bigheader "WARNING: WINDOWS INITIALIZATION MUST BE COMPLETED MANUALLY OVER RDP AND POWERSHELL!"
}
init_remote_linux() {
header "$REMOTE_NAME"
wait_remote_host
sh_c ssh_copy_id -i="$ID_PUB_PATH" "$REMOTE_HOST"
sh_c ssh "$REMOTE_HOST" sh -s -- <<EOF
set -eux
export DEBIAN_FRONTEND=noninteractive
sudo -E apt-get update -y
sudo -E apt-get dist-upgrade -y
sudo -E apt-get update -y
sudo -E apt-get install -y build-essential rsync
# Docker from https://docs.docker.com/engine/install/ubuntu/
sudo -E apt-get -y install \
ca-certificates \
curl \
gnupg \
lsb-release
sudo mkdir -p /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --yes --dearmor -o /etc/apt/keyrings/docker.gpg
echo \
"deb [arch=\$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
\$(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo -E apt-get update -y
sudo -E apt-get install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
sudo groupadd docker || true
sudo usermod -aG docker \$USER
mkdir -p \$HOME/.local/bin
mkdir -p \$HOME/.local/share/man
EOF
init_remote_env
sh_c ssh "$REMOTE_HOST" sh -s -- <<EOF
set -eux
export DEBIAN_FRONTEND=noninteractive
sudo -E apt-get autoremove -y
EOF
sh_c ssh "$REMOTE_HOST" 'sudo reboot' || true
}
init_remote_macos() {
header "$REMOTE_NAME"
wait_remote_host
sh_c ssh_copy_id -i="$ID_PUB_PATH" "$REMOTE_HOST"
sh_c ssh "$REMOTE_HOST" '"/bin/bash -c \"\$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)\""'
if sh_c ssh "$REMOTE_HOST" uname -m | grep -qF arm64; then
shellenv=$(sh_c ssh "$REMOTE_HOST" /opt/homebrew/bin/brew shellenv)
else
shellenv=$(sh_c ssh "$REMOTE_HOST" /usr/local/bin/brew shellenv)
fi
if ! echo "$shellenv" | sh_c ssh "$REMOTE_HOST" "IFS= read -r regex\; \"grep -qF \\\"\\\$regex\\\" ~/.zshrc\""; then
echo "$shellenv" | sh_c ssh "$REMOTE_HOST" "\"(echo && cat) >> ~/.zshrc\""
fi
if ! sh_c ssh "$REMOTE_HOST" "'grep -qF \\\$HOME/.local ~/.zshrc'"; then
sh_c ssh "$REMOTE_HOST" "\"(echo && cat) >> ~/.zshrc\"" <<EOF
PATH=\$HOME/.local/bin:\$PATH
MANPATH=\$HOME/.local/share/man:\$MANPATH
EOF
fi
init_remote_env
sh_c ssh "$REMOTE_HOST" brew update
sh_c ssh "$REMOTE_HOST" brew upgrade
sh_c ssh "$REMOTE_HOST" brew install go rsync
sh_c ssh "$REMOTE_HOST" 'sudo reboot' || true
}
init_remote_env() {
sh_c ssh "$REMOTE_HOST" '"rm -f ~/.ssh/environment"'
sh_c ssh "$REMOTE_HOST" '"echo PATH=\$(echo \"echo \\\$PATH\" | \"\$SHELL\" -ils) >\$HOME/.ssh/environment"'
sh_c ssh "$REMOTE_HOST" '"echo MANPATH=\$(echo \"echo \\\$MANPATH\" | \"\$SHELL\" -ils) >>\$HOME/.ssh/environment"'
sh_c ssh "$REMOTE_HOST" "sudo sed -i.bak '\"s/#PermitUserEnvironment no/PermitUserEnvironment yes/\"' /etc/ssh/sshd_config"
if sh_c ssh "$REMOTE_HOST" uname | grep -qF Darwin; then
sh_c ssh "$REMOTE_HOST" "sudo launchctl stop com.openssh.sshd"
else
sh_c ssh "$REMOTE_HOST" "sudo systemctl restart sshd"
# ubuntu has $PATH hard coded in /etc/environment for some reason. It takes precedence
# over ~/.ssh/environment.
sh_c ssh "$REMOTE_HOST" "sudo rm /etc/environment"
fi
}
wait_remote_host() {
while true; do
if sh_c ssh "$REMOTE_HOST" true; then
break
fi
sleep 5
done
}
wait_remote_host_windows() {
instance_id=$(aws ec2 describe-instances \
--filters 'Name=instance-state-name,Values=pending,running,stopping,stopped' "Name=tag:Name,Values=$REMOTE_NAME" \
| jq -r '.Reservations[].Instances[].InstanceId')
while true; do
if sh_c aws ssm start-session --target "$instance_id" \
--document-name 'AWS-StartNonInteractiveCommand' \
--parameters "'{\"command\": [\"echo true\"]}'"; then
break
fi
sleep 5
done
}
init_remote_windows() {
header "$REMOTE_NAME"
wait_remote_host_windows
init_ps1=$(cat <<EOF
\$ProgressPreference = 'SilentlyContinue'
# Bootstrap PowerShell v7
if ((\$PSVersionTable.PSVersion).Major -eq 5) {
Invoke-WebRequest -Uri https://www.nuget.org/api/v2/package/Microsoft.UI.Xaml/2.7.3 -OutFile .\microsoft.ui.xaml.2.7.3.zip
Expand-Archive -Force .\microsoft.ui.xaml.2.7.3.zip
Add-AppxPackage .\microsoft.ui.xaml.2.7.3\tools\AppX\x64\Release\Microsoft.UI.Xaml.2.7.appx
Invoke-WebRequest -Uri https://github.com/microsoft/winget-cli/releases/download/v1.3.2691/Microsoft.DesktopAppInstaller_8wekyb3d8bbwe.msixbundle -OutFile .\Microsoft.DesktopAppInstaller_8wekyb3d8bbwe.msixbundle
Invoke-WebRequest -Uri https://github.com/microsoft/winget-cli/releases/download/v1.3.2691/7bcb1a0ab33340daa57fa5b81faec616_License1.xml -OutFile .\7bcb1a0ab33340daa57fa5b81faec616_License1.xml
Invoke-WebRequest -Uri https://aka.ms/Microsoft.VCLibs.x64.14.00.Desktop.appx -OutFile Microsoft.VCLibs.x64.14.00.Desktop.appx
Add-AppxProvisionedPackage -online -PackagePath .\Microsoft.DesktopAppInstaller_8wekyb3d8bbwe.msixbundle -LicensePath .\7bcb1a0ab33340daa57fa5b81faec616_License1.xml -DependencyPackagePath Microsoft.VCLibs.x64.14.00.Desktop.appx
Add-AppxPackage .\Microsoft.DesktopAppInstaller_8wekyb3d8bbwe.msixbundle
winget install --silent --accept-package-agreements --accept-source-agreements Microsoft.DotNet.SDK.7
# Refresh env.
\$env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path","User")
dotnet tool install --global PowerShell --version 7.3.1
pwsh -c 'Enable-ExperimentalFeature PSNativeCommandErrorActionPreference'
pwsh .\Desktop\init.ps1
Exit
}
Set-StrictMode -Version Latest
\$ErrorActionPreference = "Stop"
\$PSNativeCommandUseErrorActionPreference = \$true
if (-Not (Get-Command wix -errorAction SilentlyContinue)) {
dotnet tool install --global wix --version 4.0.0-preview.1
}
Add-WindowsCapability -Online -Name OpenSSH.Server~~~~0.0.1.0
Start-Service sshd
Set-Service -Name sshd -StartupType 'Automatic'
New-ItemProperty -Path "HKLM:\SOFTWARE\OpenSSH" -Name DefaultShell -Value "C:\Users\Administrator\.dotnet\tools\pwsh.exe" -PropertyType String -Force
ConvertFrom-Json -InputObject @'
$(perl -pe 's#\n#\r\n#' "$ID_PUB_PATH" | jq -Rs .)
'@ | Out-File -Encoding utf8 "\$env:ProgramData\ssh\administrators_authorized_keys"
# utf8BOM -> utf8: https://stackoverflow.com/a/34969243/4283659
\$null = New-Item -Force "\$env:ProgramData\ssh\administrators_authorized_keys" -Value (Get-Content -Path "\$env:ProgramData\ssh\administrators_authorized_keys" | Out-String)
get-acl "\$env:ProgramData\ssh\ssh_host_rsa_key" | set-acl "\$env:ProgramData\ssh\administrators_authorized_keys"
if (-Not (Test-Path -Path C:\msys64)) {
Invoke-WebRequest -Uri "https://github.com/msys2/msys2-installer/releases/download/2022-10-28/msys2-x86_64-20221028.exe" -OutFile "./msys2-x86_64.exe"
./msys2-x86_64.exe install --default-answer --confirm-command --root C:\msys64
}
C:\msys64\msys2_shell.cmd -defterm -here -no-start -mingw64 -c 'pacman -Sy --noconfirm base-devel vim rsync man'
C:\msys64\msys2_shell.cmd -defterm -here -no-start -mingw64 -c 'curl -fsSL https://d2lang.com/install.sh | sh -s -- --tala'
C:\msys64\msys2_shell.cmd -defterm -here -no-start -mingw64 -c 'd2 --version'
\$path = (Get-ItemProperty -Path 'Registry::HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\Environment' -Name Path).Path
if (\$path -notlike '*C:\msys64\usr\bin*') {
\$path = "\$path;C:\msys64\usr\bin"
Set-ItemProperty -Path 'Registry::HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\Environment' -Name Path -Value \$path
}
if (\$path -notlike '*C:\msys64\usr\local\bin*') {
\$path = "\$path;C:\msys64\usr\local\bin"
Set-ItemProperty -Path 'Registry::HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\Environment' -Name Path -Value \$path
}
(Get-ItemProperty -Path 'Registry::HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\Environment' -Name Path).Path
Restart-Computer
EOF
# To run a POSIX script:
# ssh "$CI_D2_WINDOWS_AMD64" sh -s -- <<EOF
# wix --version
# EOF
# To run a command in a pure MSYS2 shell:
# ssh "$CI_D2_WINDOWS_AMD64" 'C:\msys64\msys2_shell.cmd -defterm -here -no-start -mingw64 -c "\"d2 --version\""'
# To run a pure MSYS2 shell:
# ssh -t "$CI_D2_WINDOWS_AMD64" 'C:\msys64\msys2_shell.cmd -defterm -here -no-start -mingw64'
# In case MSYS2 improves in the future and allows for noninteractive commands the
# following will set the OpenSSH shell to MSYS2 instead of PowerShell.
#
# Right now, setting MSYS2 to the DefaultShell like this will make it start bash in
# interactive mode always. Even for ssh "$CI_D2_WINDOWS_AMD64" echo hi. And so you'll end
# up with a blank prompt on which to input commands instead of having it execute the
# command you passed in via ssh.
#
# PowerShell as the default is better anyway as it gives us access to both the UNIX
# userspace and Windows tools like wix/dotnet/winget.
#
# To set:
# <<EOF
# echo '@C:\msys64\msys2_shell.cmd -defterm -here -no-start -mingw64' | Out-File C:\msys64\sshd_default_shell.cmd
# # utf8BOM -> utf8: https://stackoverflow.com/a/34969243/4283659
# \$null = New-Item -Force C:\msys64\sshd_default_shell.cmd -Value (Get-Content -Path C:\msys64\sshd_default_shell.cmd | Out-String)
# Set-ItemProperty -Path HKLM:\SOFTWARE\OpenSSH -Name DefaultShell -Value C:\msys64\sshd_default_shell.cmd
# EOF
#
# To undo:
# <<EOF
# Remove-ItemProperty -Path HKLM:\SOFTWARE\OpenSSH -Name DefaultShell
# rm C:\msys64\sshd_default_shell.cmd
# EOF
)
gen_init_ps1=$(cat <<EOF
ConvertFrom-Json -InputObject @'
$(printf %s "$init_ps1" | perl -pe 'chomp if eof' | perl -pe 's#\n#\r\n#' | jq -Rs .)
'@ | Out-File -Encoding utf8 C:\Users\Administrator\Desktop\init.ps1; C:\Users\Administrator\Desktop\init.ps1
EOF
)
# Windows and AWS SSM both defeated me.
FGCOLOR=3 bigheader "WARNING: WINDOWS INITIALIZATION MUST BE COMPLETED MANUALLY OVER RDP AND POWERSHELL!"
warn '1. Obtain Windows RDP password with:'
echo " aws ec2 get-password-data --instance-id \$(aws ec2 describe-instances --filters 'Name=instance-state-name,Values=pending,running,stopping,stopped' "Name=tag:Name,Values=$REMOTE_NAME" | jq -r '.Reservations[].Instances[].InstanceId') --priv-launch-key windows.pem | jq -r .PasswordData" >&2
warn "2. RDP into $REMOTE_HOST and open PowerShell."
warn '3. Generate and execute C:\Users\Administrator\Desktop\init.ps1 with:'
printf '%s\n' "$gen_init_ps1" >&2
warn '4. Run the following to be notified once installation is successful:'
cat <<EOF
until ssh $REMOTE_HOST d2 --version; do echo 'failed: retrying in 5s' && sleep 5; done && printf 'success\a\n'
EOF
}
main "$@"

View file

@ -1,7 +1,7 @@
#!/bin/sh #!/bin/sh
set -eu set -eu
. "$(dirname "$0")/../../../ci/sub/lib.sh"
cd -- "$(dirname "$0")/../../.." cd -- "$(dirname "$0")/../../.."
. ./ci/sub/lib.sh
help() { help() {
cat <<EOF cat <<EOF
@ -33,10 +33,11 @@ main() {
done done
shift "$FLAGSHIFT" shift "$FLAGSHIFT"
REMOTE_HOST=$TSTRUCT_LINUX_AMD64_BUILDER; runjob linux-amd64 ssh "$REMOTE_HOST" "$@" REMOTE_HOST=$CI_HOST_D2_LINUX_AMD64 && runjob linux-amd64 ssh "$REMOTE_HOST" "$@"
REMOTE_HOST=$TSTRUCT_LINUX_ARM64_BUILDER; runjob linux-arm64 ssh "$REMOTE_HOST" "$@" REMOTE_HOST=$CI_HOST_D2_LINUX_ARM64 && runjob linux-arm64 ssh "$REMOTE_HOST" "$@"
REMOTE_HOST=$TSTRUCT_MACOS_AMD64_BUILDER; runjob macos-amd64 ssh "$REMOTE_HOST" "$@" REMOTE_HOST=$CI_HOST_D2_MACOS_AMD64 && runjob macos-amd64 ssh "$REMOTE_HOST" "$@"
REMOTE_HOST=$TSTRUCT_MACOS_ARM64_BUILDER; runjob macos-arm64 ssh "$REMOTE_HOST" "$@" REMOTE_HOST=$CI_HOST_D2_MACOS_ARM64 && runjob macos-arm64 ssh "$REMOTE_HOST" "$@"
REMOTE_HOST=$CI_HOST_D2_WINDOWS_AMD64 && runjob macos-arm64 ssh "$REMOTE_HOST" "$@"
} }
main "$@" main "$@"

View file

@ -1,7 +1,12 @@
#!/bin/sh #!/bin/sh
set -eu set -eu
if [ ! -e "$(dirname "$0")/../../ci/sub/.git" ]; then
set -x
git submodule update --init
set +x
fi
. "$(dirname "$0")/../../ci/sub/lib.sh"
cd -- "$(dirname "$0")/../.." cd -- "$(dirname "$0")/../.."
. ./ci/sub/lib.sh
help() { help() {
cat <<EOF cat <<EOF
@ -20,11 +25,12 @@ Flags:
changed something and need to force rebuild, use this flag. changed something and need to force rebuild, use this flag.
--local --local
By default build.sh uses \$TSTRUCT_MACOS_AMD64_BUILDER, \$TSTRUCT_MACOS_ARM64_BUILDER, By default build.sh uses \$CI_D2_LINUX_AMD64, \$CI_D2_LINUX_ARM64,
\$TSTRUCT_LINUX_AMD64_BUILDER and \$TSTRUCT_LINUX_ARM64_BUILDER to build the release \$CI_D2_MACOS_AMD64 and \$CI_D2_MACOS_ARM64 to build the release
archives. It's required for now due to the following issue: 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 https://github.com/terrastruct/d2/issues/31
locally. warning: This is only for testing purposes, do not use in production! With --local, build.sh will cross compile locally. warning: This is only for testing
purposes, do not use in production!
--host-only --host-only
Use to build the release archive for the host OS-ARCH only. All logging is done to stderr Use to build the release archive for the host OS-ARCH only. All logging is done to stderr
@ -45,6 +51,13 @@ Flags:
--uninstall --uninstall
Ensure a release using --host-only and uninstall it. Ensure a release using --host-only and uninstall it.
--push-docker
Push the built docker image. Unfortunately dockerx requires the multi-arch images be
pushed if required in the same invocation as build. dockerx cannot load multi-arch
images into the daemon for push later. It's not slow though to use --push-docker after
building the image as nearly all artifacts are cached.
Automatically set if called from release.sh
EOF EOF
} }
@ -69,7 +82,7 @@ main() {
;; ;;
run) run)
flag_reqarg && shift "$FLAGSHIFT" flag_reqarg && shift "$FLAGSHIFT"
JOBFILTER="$FLAGARG" JOBFILTER=$FLAGARG
;; ;;
host-only) host-only)
flag_noarg && shift "$FLAGSHIFT" flag_noarg && shift "$FLAGSHIFT"
@ -96,6 +109,10 @@ main() {
HOST_ONLY=1 HOST_ONLY=1
LOCAL=1 LOCAL=1
;; ;;
push-docker)
flag_noarg && shift "$FLAGSHIFT"
PUSH_DOCKER=1
;;
*) *)
flag_errusage "unrecognized flag $FLAGRAW" flag_errusage "unrecognized flag $FLAGRAW"
;; ;;
@ -108,10 +125,13 @@ main() {
VERSION=${VERSION:-$(git_describe_ref)} VERSION=${VERSION:-$(git_describe_ref)}
BUILD_DIR=ci/release/build/$VERSION BUILD_DIR=ci/release/build/$VERSION
sh_c mkdir -p "$BUILD_DIR"
sh_c rm -f ci/release/build/latest
sh_c ln -s "$VERSION" ci/release/build/latest
if [ -n "${HOST_ONLY-}" ]; then if [ -n "${HOST_ONLY-}" ]; then
ensure_os ensure_os
ensure_arch ensure_arch
runjob "$OS-$ARCH" "build" runjob "$OS/$ARCH" "build"
if [ -n "${INSTALL-}" ]; then if [ -n "${INSTALL-}" ]; then
sh_c make -sC "ci/release/build/$VERSION/$OS-$ARCH/d2-$VERSION" install sh_c make -sC "ci/release/build/$VERSION/$OS-$ARCH/d2-$VERSION" install
@ -121,12 +141,16 @@ main() {
return 0 return 0
fi fi
runjob linux-amd64 'OS=linux ARCH=amd64 build' & runjob linux/amd64 'OS=linux ARCH=amd64 build' &
runjob linux-arm64 'OS=linux ARCH=arm64 build' & runjob linux/arm64 'OS=linux ARCH=arm64 build' &
runjob macos-amd64 'OS=macos ARCH=amd64 build' & runjob macos/amd64 'OS=macos ARCH=amd64 build' &
runjob macos-arm64 'OS=macos ARCH=arm64 build' & runjob macos/arm64 'OS=macos ARCH=arm64 build' &
runjob windows-amd64 'OS=windows ARCH=amd64 build' & runjob windows/amd64 'OS=windows ARCH=amd64 build' &
runjob windows-arm64 'OS=windows ARCH=arm64 build' & runjob windows/arm64 'OS=windows ARCH=arm64 build' &
waitjobs
runjob linux/dockerimage 'OS=linux build_docker_image' &
runjob windows/amd64/msi 'OS=windows ARCH=amd64 build_windows_msi' &
waitjobs waitjobs
} }
@ -148,10 +172,10 @@ build() {
macos) macos)
case $ARCH in case $ARCH in
amd64) amd64)
REMOTE_HOST=$TSTRUCT_MACOS_AMD64_BUILDER build_remote_macos REMOTE_HOST=$CI_D2_MACOS_AMD64 build_remote_macos
;; ;;
arm64) arm64)
REMOTE_HOST=$TSTRUCT_MACOS_ARM64_BUILDER build_remote_macos REMOTE_HOST=$CI_D2_MACOS_ARM64 build_remote_macos
;; ;;
*) *)
warn "no builder for OS=$OS ARCH=$ARCH, building locally..." warn "no builder for OS=$OS ARCH=$ARCH, building locally..."
@ -162,10 +186,10 @@ build() {
linux) linux)
case $ARCH in case $ARCH in
amd64) amd64)
REMOTE_HOST=$TSTRUCT_LINUX_AMD64_BUILDER build_remote_linux REMOTE_HOST=$CI_D2_LINUX_AMD64 build_remote_linux
;; ;;
arm64) arm64)
REMOTE_HOST=$TSTRUCT_LINUX_ARM64_BUILDER build_remote_linux REMOTE_HOST=$CI_D2_LINUX_ARM64 build_remote_linux
;; ;;
*) *)
warn "no builder for OS=$OS ARCH=$ARCH, building locally..." warn "no builder for OS=$OS ARCH=$ARCH, building locally..."
@ -218,13 +242,39 @@ VERSION=$VERSION \
OS=$OS \ OS=$OS \
ARCH=$ARCH \ ARCH=$ARCH \
ARCHIVE=$ARCHIVE \ ARCHIVE=$ARCHIVE \
./src/d2/ci/release/build_docker.sh" ./src/d2/ci/release/build_in_docker.sh"
sh_c mkdir -p "$HW_BUILD_DIR" sh_c mkdir -p "$HW_BUILD_DIR"
sh_c rsync --archive --human-readable "$REMOTE_HOST:src/d2/$ARCHIVE" "$ARCHIVE" sh_c rsync --archive --human-readable "$REMOTE_HOST:src/d2/$ARCHIVE" "$ARCHIVE"
)} )}
ssh() { build_docker_image() {
command ssh -o='StrictHostKeyChecking=accept-new' "$@" D2_DOCKER_IMAGE=${D2_DOCKER_IMAGE:-terrastruct/d2}
flags='--load'
if [ -n "${PUSH_DOCKER-}" -o -n "${RELEASE-}" ]; then
flags='--push --platform linux/amd64,linux/arm64'
fi
sh_c docker buildx build $flags -t "$D2_DOCKER_IMAGE:$VERSION" -t "$D2_DOCKER_IMAGE:latest" --build-arg "VERSION=$VERSION" -f ./ci/release/Dockerfile "./ci/release/build/$VERSION"
}
build_windows_msi() {
REMOTE_HOST=$CI_D2_WINDOWS_AMD64
ln -sf "../build/$VERSION/windows-amd64/d2-$VERSION/bin/d2.exe" ./ci/release/windows/d2.exe
sh_c rsync --archive --human-readable --copy-links --delete ./ci/release/windows/ "'$REMOTE_HOST:windows\'"
if ! echo "$VERSION" | grep '[0-9]\.[0-9].[0-9]'; then
WIX_VERSION=0.0.0
else
WIX_VERSION=$VERSION
fi
sh_c ssh "$REMOTE_HOST" "'cd .\\windows && wix build -arch x64 -d D2Version=$WIX_VERSION .\d2.wxs'"
# --files-from shouldn't be necessary but for some reason selecting d2.msi directly
# makes rsync error with:
# ERROR: rejecting unrequested file-list name: .\\windows\\d2.msi
# rsync error: requested action not supported (code 4) at flist.c(1027) [Receiver=3.2.7]
rsync_files=$(mktempd)/rsync-files
echo d2.msi >$rsync_files
sh_c rsync --archive --human-readable --files-from "$rsync_files" "'$REMOTE_HOST:windows\\'" "./ci/release/build/$VERSION/d2-$VERSION-$OS-$ARCH.msi"
} }
main "$@" main "$@"

View file

@ -0,0 +1 @@
**/d2*/

View file

@ -5,7 +5,7 @@ cd -- "$(dirname "$0")/../.."
tag="$(sh_c docker build \ tag="$(sh_c docker build \
--build-arg GOVERSION="1.19.3.linux-$ARCH" \ --build-arg GOVERSION="1.19.3.linux-$ARCH" \
-qf ./ci/release/builders/Dockerfile ./ci/release/builders)" -qf ./ci/release/linux/Dockerfile ./ci/release/linux)"
docker_run \ docker_run \
-e DRY_RUN \ -e DRY_RUN \
-e HW_BUILD_DIR \ -e HW_BUILD_DIR \

View file

@ -1,16 +0,0 @@
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

View file

@ -1,57 +0,0 @@
#!/bin/sh
set -eu
cd -- "$(dirname "$0")/../../.."
. ./ci/sub/lib.sh
help() {
cat <<EOF
usage: $0 [--dry-run] -i keys.pub
$0 copies keys.pub to each builder and then deduplicates its .authorized_keys.
EOF
}
main() {
while flag_parse "$@"; do
case "$FLAG" in
h|help)
help
return 0
;;
dry-run)
flag_noarg && shift "$FLAGSHIFT"
DRY_RUN=1
;;
i)
flag_nonemptyarg && shift "$FLAGSHIFT"
KEY_FILE=$FLAGARG
;;
*)
flag_errusage "unrecognized flag $FLAGRAW"
;;
esac
done
shift "$FLAGSHIFT"
if [ -z "${KEY_FILE-}" ]; then
echoerr "-i is required"
exit 1
fi
header linux-amd64
REMOTE_HOST=$TSTRUCT_LINUX_AMD64_BUILDER copy_keys
header linux-arm64
REMOTE_HOST=$TSTRUCT_LINUX_ARM64_BUILDER copy_keys
header macos-amd64
REMOTE_HOST=$TSTRUCT_MACOS_AMD64_BUILDER copy_keys
header macos-arm64
REMOTE_HOST=$TSTRUCT_MACOS_ARM64_BUILDER copy_keys
}
copy_keys() {
sh_c ssh-copy-id -fi "$KEY_FILE" "$REMOTE_HOST"
sh_c ssh "$REMOTE_HOST" 'cat .ssh/authorized_keys \| sort -u \> .ssh/authorized_keys.dedup'
sh_c ssh "$REMOTE_HOST" 'cp .ssh/authorized_keys.dedup .ssh/authorized_keys'
sh_c ssh "$REMOTE_HOST" 'rm .ssh/authorized_keys.dedup'
}
main "$@"

View file

@ -1,294 +0,0 @@
#!/bin/sh
set -eu
cd -- "$(dirname "$0")/../../.."
. ./ci/sub/lib.sh
help() {
cat <<EOF
usage: $0 [--dry-run] [--skip-create]
$0 creates and ensures the d2 builders in AWS.
EOF
}
main() {
while flag_parse "$@"; do
case "$FLAG" in
h|help)
help
return 0
;;
dry-run)
flag_noarg && shift "$FLAGSHIFT"
DRY_RUN=1
;;
skip-create)
flag_noarg && shift "$FLAGSHIFT"
SKIP_CREATE=1
;;
*)
flag_errusage "unrecognized flag $FLAGRAW"
;;
esac
done
shift "$FLAGSHIFT"
if [ $# -gt 0 ]; then
flag_errusage "no arguments are accepted"
fi
if [ -z "${SKIP_CREATE-}" ]; then
create_remote_hosts
fi
init_remote_hosts
}
create_remote_hosts() {
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-071e6cafc48327ca2 \
--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=admin@$dnsname"
export TSTRUCT_LINUX_AMD64_BUILDER=admin@$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-0e67506f183e5ab60 \
--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=admin@$dnsname"
export TSTRUCT_LINUX_ARM64_BUILDER=admin@$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_remote_hosts() {
header linux-amd64
REMOTE_HOST=$TSTRUCT_LINUX_AMD64_BUILDER init_remote_linux
header linux-arm64
REMOTE_HOST=$TSTRUCT_LINUX_ARM64_BUILDER init_remote_linux
header macos-amd64
REMOTE_HOST=$TSTRUCT_MACOS_AMD64_BUILDER init_remote_macos
header macos-arm64
REMOTE_HOST=$TSTRUCT_MACOS_ARM64_BUILDER init_remote_macos
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"
log "export TSTRUCT_MACOS_ARM64_BUILDER=$TSTRUCT_MACOS_ARM64_BUILDER"
}
init_remote_linux() {
wait_remote_host
sh_c ssh "$REMOTE_HOST" sh -s -- <<EOF
set -eux
export DEBIAN_FRONTEND=noninteractive
sudo -E apt-get update -y
sudo -E apt-get dist-upgrade -y
sudo -E apt-get install -y build-essential rsync
# Docker from https://docs.docker.com/engine/install/debian/
sudo -E apt-get -y install \
ca-certificates \
curl \
gnupg \
lsb-release
sudo mkdir -p /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/debian/gpg | sudo gpg --batch --yes --dearmor -o /etc/apt/keyrings/docker.gpg
echo \
"deb [arch=\$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/debian \
\$(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo -E apt-get update -y
sudo -E apt-get install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
sudo groupadd docker || true
sudo usermod -aG docker \$USER
mkdir -p \$HOME/.local/bin
mkdir -p \$HOME/.local/share/man
EOF
init_remote_env
sh_c ssh "$REMOTE_HOST" 'sudo reboot' || true
}
init_remote_macos() {
wait_remote_host
sh_c ssh "$REMOTE_HOST" '"/bin/bash -c \"\$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)\""'
if sh_c ssh "$REMOTE_HOST" uname -m | grep -qF arm64; then
shellenv=$(sh_c ssh "$REMOTE_HOST" /opt/homebrew/bin/brew shellenv)
else
shellenv=$(sh_c ssh "$REMOTE_HOST" /usr/local/bin/brew shellenv)
fi
if ! echo "$shellenv" | sh_c ssh "$REMOTE_HOST" "IFS= read -r regex\; \"grep -qF \\\"\\\$regex\\\" ~/.zshrc\""; then
echo "$shellenv" | sh_c ssh "$REMOTE_HOST" "\"(echo && cat) >> ~/.zshrc\""
fi
if ! sh_c ssh "$REMOTE_HOST" "'grep -qF \\\$HOME/.local ~/.zshrc'"; then
sh_c ssh "$REMOTE_HOST" "\"(echo && cat) >> ~/.zshrc\"" <<EOF
PATH=\$HOME/.local/bin:\$PATH
MANPATH=\$HOME/.local/share/man:\$MANPATH
EOF
fi
init_remote_env
sh_c ssh "$REMOTE_HOST" brew update
sh_c ssh "$REMOTE_HOST" brew upgrade
sh_c ssh "$REMOTE_HOST" brew install go rsync
}
init_remote_env() {
sh_c ssh "$REMOTE_HOST" '"rm -f ~/.ssh/environment"'
sh_c ssh "$REMOTE_HOST" '"echo PATH=\$(echo \"echo \\\$PATH\" | \"\$SHELL\" -ils) >\$HOME/.ssh/environment"'
sh_c ssh "$REMOTE_HOST" '"echo MANPATH=\$(echo \"echo \\\$MANPATH\" | \"\$SHELL\" -ils) >>\$HOME/.ssh/environment"'
sh_c ssh "$REMOTE_HOST" "sudo sed -i.bak '\"s/#PermitUserEnvironment no/PermitUserEnvironment yes/\"' /etc/ssh/sshd_config"
if sh_c ssh "$REMOTE_HOST" uname | grep -qF Darwin; then
sh_c ssh "$REMOTE_HOST" "sudo launchctl stop com.openssh.sshd"
else
sh_c ssh "$REMOTE_HOST" "sudo systemctl restart sshd"
fi
}
wait_remote_host() {
while true; do
if sh_c ssh "$REMOTE_HOST" true; then
break
fi
sleep 5
done
}
main "$@"

View file

@ -1,21 +1,19 @@
#### Features 🚀 #### Features 🚀
- Diagram padding can now can be configured in the CLI (default 100px). - Diagram padding can now can be configured in the CLI (default 100px). [https://github.com/terrastruct/d2/pull/431](https://github.com/terrastruct/d2/pull/431)
[https://github.com/terrastruct/d2/pull/431](https://github.com/terrastruct/d2/pull/431)
- Connection label backgrounds can now be set with the `style.fill` keyword. [https://github.com/terrastruct/d2/pull/452](https://github.com/terrastruct/d2/pull/452) - Connection label backgrounds can now be set with the `style.fill` keyword. [https://github.com/terrastruct/d2/pull/452](https://github.com/terrastruct/d2/pull/452)
- Add official Docker image. See [./docs/INSTALL.md#docker](./docs/INSTALL.md#docker). [#76](https://github.com/terrastruct/d2/issues/76)
- Add `.msi` installer for convenient installation on Windows. [#379](https://github.com/terrastruct/d2/issues/379)
#### Improvements 🧹 #### Improvements 🧹
- Fmt now preserves leading comment spacing. - `d2 fmt` now preserves leading comment spacing. [#400](https://github.com/terrastruct/d2/issues/400)
[#400](https://github.com/terrastruct/d2/issues/400)
#### Bugfixes ⛑️ #### Bugfixes ⛑️
- Fixed crash when sequence diagrams had no messages. - Fixed crash when sequence diagrams had no messages. [https://github.com/terrastruct/d2/pull/427](https://github.com/terrastruct/d2/pull/427)
[https://github.com/terrastruct/d2/pull/427](https://github.com/terrastruct/d2/pull/427) - Fixed `constraint` keyword setting label. [https://github.com/terrastruct/d2/issues/415](https://github.com/terrastruct/d2/issues/415)
- Fixed `constraint` keyword setting label. - Fixed serialization affecting binary plugins (TALA). [https://github.com/terrastruct/d2/pull/426](https://github.com/terrastruct/d2/pull/426)
[https://github.com/terrastruct/d2/issues/415](https://github.com/terrastruct/d2/issues/415)
- Fixed serialization affecting binary plugins (TALA).
[https://github.com/terrastruct/d2/pull/426](https://github.com/terrastruct/d2/pull/426)
- Fixed connections in elk layouts not going all the way to shape borders. [https://github.com/terrastruct/d2/pull/459](https://github.com/terrastruct/d2/pull/459) - Fixed connections in elk layouts not going all the way to shape borders. [https://github.com/terrastruct/d2/pull/459](https://github.com/terrastruct/d2/pull/459)
- Fixed a connection rendering bug that could happen in firefox when there were no connection labels. [https://github.com/terrastruct/d2/pull/453](https://github.com/terrastruct/d2/pull/453) - Fixed a connection rendering bug that could happen in firefox when there were no connection labels. [https://github.com/terrastruct/d2/pull/453](https://github.com/terrastruct/d2/pull/453)
- Fixed a crash when external connection IDs were prefixes of a sequence diagram ID. [https://github.com/terrastruct/d2/pull/462](https://github.com/terrastruct/d2/pull/462)

View file

@ -8,5 +8,5 @@ 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 RUN tar -C /usr/local -xzf /tmp/go.tar.gz
ENV PATH="/usr/local/go/bin:$PATH" ENV PATH="/usr/local/go/bin:$PATH"
RUN apt-get install -y build-essential RUN apt-get install -y build-essential \
RUN apt-get install -y rsync rsync

View file

@ -20,27 +20,11 @@ EOF
if [ "$OS" = windows ]; then if [ "$OS" = windows ]; then
cat <<EOF cat <<EOF
We currently do not have an \`.msi\` for automatic installation on Windows so this release This release is structured the same as our Unix releases for use with MSYS2.
is structured the same as our Unix releases.
Easiest way to use \`d2\` on Windows is to just \`chdir\` into the bin directory of this release You may find our \`.msi\` installer more convenient as it handles putting \`d2.exe\` into
and invoke \`d2\` like \`./d2 <full-input-file-path>\`
For installation you'll have to add the \`./bin/d2.exe\` binary to your \`\$PATH\`. Or add
the \`./bin\` directory of this release to your \`\$PATH\`.
See https://www.wikihow.com/Change-the-PATH-Environment-Variable-on-Windows
Then you'll be able to call \`d2\` from the commandline in \`cmd.exe\` or \`pwsh.exe\`.
We intend to have a \`.msi\` release installer sometime soon that handles putting \`d2.exe\` into
your \`\$PATH\` for you. your \`\$PATH\` for you.
You can also use \`make install\` to install on Windows after first installing
[MSYS2](https://www.msys2.org/) which emulates a Linux shell for Windows. Its terminal
also enables \`d2\` to show colors in its output. The manpage will also become accessible
with \`man d2\`.
See https://github.com/terrastruct/d2/blob/master/docs/INSTALL.md#windows See https://github.com/terrastruct/d2/blob/master/docs/INSTALL.md#windows
EOF EOF
fi fi

View file

@ -273,7 +273,7 @@ header() {
bigheader() { bigheader() {
set -- "$(echo "$*" | sed "s/^/ * /")" set -- "$(echo "$*" | sed "s/^/ * /")"
FGCOLOR=${FGCOLOR:-3} logp "/**************************************************************** FGCOLOR=${FGCOLOR:-6} logp "/****************************************************************
$* $*
****************************************************************/" ****************************************************************/"
} }

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

@ -0,0 +1 @@
d2.exe

BIN
ci/release/windows/d2.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 95 KiB

BIN
ci/release/windows/d2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

39
ci/release/windows/d2.wxs Normal file
View file

@ -0,0 +1,39 @@
<!--
Wix documentation is nonexistent for v4. What exists is largely out of date and inconsistent.
This file was pieced together from:
1. https://www.firegiant.com/wix/tutorial/getting-started/
- This is for v3, I used wix convert to convert to v4
2. https://wixtoolset.org/docs/reference/schema/wxs/
3. Googling with trial and error
-->
<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
<Package Name="D2" UpgradeCode="ac84fee7-eb67-4f5d-a08d-adef69538690" Language="1033" Codepage="utf-8" Version="$(var.D2Version)" Manufacturer="Terrastruct, Inc." InstallerVersion="200">
<SummaryInformation Keywords="Installer" Description="The D2 Installer" Manufacturer="Terrastruct, Inc." Codepage="1252" />
<Icon Id="d2.ico" SourceFile="d2.ico" />
<Property Id="ARPPRODUCTICON" Value="d2.ico" />
<Media Id="1" Cabinet="D2.cab" EmbedCab="yes" />
<Feature Id="Complete" Level="1">
<ComponentRef Id="Executable" />
</Feature>
<MajorUpgrade AllowSameVersionUpgrades='yes' DowngradeErrorMessage="A later version of [ProductName] is already installed. Setup will now exit."/>
<StandardDirectory Id="ProgramFiles64Folder">
<Directory Id="INSTALLDIR" Name="D2">
<Component Id="Executable" Guid="1090d036-c985-461f-94f6-3121dbcfcb48">
<File Id="D2EXE" Name="d2.exe" Source="d2.exe" KeyPath="yes" />
<Environment
Id="D2PathEntry"
Action="set"
Part="last"
Name="PATH"
Permanent="no"
System="yes"
Value="[INSTALLDIR]" />
</Component>
</Directory>
</StandardDirectory>
</Package>
</Wix>

2
ci/sub

@ -1 +1 @@
Subproject commit 7923b353a829cf289171baff0d0d017ae3d32278 Subproject commit 2db232e4aa5225e22f95ff91914f91b59fe48d07

View file

@ -88,7 +88,7 @@ func layoutSequenceDiagram(g *d2graph.Graph, obj *d2graph.Object) (*sequenceDiag
var edges []*d2graph.Edge var edges []*d2graph.Edge
for _, edge := range g.Edges { for _, edge := range g.Edges {
// both Src and Dst must be inside the sequence diagram // both Src and Dst must be inside the sequence diagram
if strings.HasPrefix(edge.Src.AbsID(), obj.AbsID()) && strings.HasPrefix(edge.Dst.AbsID(), obj.AbsID()) { if obj == g.Root || (strings.HasPrefix(edge.Src.AbsID(), obj.AbsID()+".") && strings.HasPrefix(edge.Dst.AbsID(), obj.AbsID()+".")) {
edges = append(edges, edge) edges = append(edges, edge)
} }
} }

View file

@ -466,8 +466,8 @@ func (sd *sequenceDiagram) routeMessages() error {
} else { } else {
return fmt.Errorf("could not find center of %s", message.Dst.AbsID()) return fmt.Errorf("could not find center of %s", message.Dst.AbsID())
} }
isToDescendant := strings.HasPrefix(message.Dst.AbsID(), message.Src.AbsID()) isToDescendant := strings.HasPrefix(message.Dst.AbsID(), message.Src.AbsID()+".")
isFromDescendant := strings.HasPrefix(message.Src.AbsID(), message.Dst.AbsID()) isFromDescendant := strings.HasPrefix(message.Src.AbsID(), message.Dst.AbsID()+".")
isSelfMessage := message.Src == message.Dst isSelfMessage := message.Src == message.Dst
if isSelfMessage || isToDescendant || isFromDescendant { if isSelfMessage || isToDescendant || isFromDescendant {

View file

@ -1,19 +1,14 @@
# Contributing # Contributing
<!-- toc --> <!-- toc -->
- <a href="#ci" id="toc-ci">CI</a>
- [CI](#ci) - <a href="#flow" id="toc-flow">Flow</a>
- [Flow](#flow) - <a href="#logistics" id="toc-logistics">Logistics</a>
- [Logistics](#logistics) - <a href="#dev" id="toc-dev">Dev</a>
- [Dev](#dev) - <a href="#content" id="toc-content">Content</a>
* [Content](#content) - <a href="#tests" id="toc-tests">Tests</a>
* [Tests](#tests) - <a href="#documentation" id="toc-documentation">Documentation</a>
+ [Running tests](#running-tests) - <a href="#questions" id="toc-questions">Questions</a>
+ [Chaos tests](#chaos-tests)
* [Documentation](#documentation)
* [Questions](#questions)
<!-- tocstop -->
## CI ## CI

View file

@ -14,8 +14,9 @@ You may install `d2` through any of the following methods.
- <a href="#from-source" id="toc-from-source">From source</a> - <a href="#from-source" id="toc-from-source">From source</a>
- <a href="#source-release" id="toc-source-release">Source Release</a> - <a href="#source-release" id="toc-source-release">Source Release</a>
- <a href="#windows" id="toc-windows">Windows</a> - <a href="#windows" id="toc-windows">Windows</a>
- <a href="#msys2" id="toc-msys2">MSYS2</a> - <a href="#release-archives" id="toc-release-archives">Release archives</a>
- <a href="#wsl" id="toc-wsl">WSL</a> - <a href="#wsl" id="toc-wsl">WSL</a>
- <a href="#docker" id="toc-docker">Docker</a>
- <a href="#coming-soon" id="toc-coming-soon">Coming soon</a> - <a href="#coming-soon" id="toc-coming-soon">Coming soon</a>
## install.sh ## install.sh
@ -157,6 +158,8 @@ You can always install from source:
go install oss.terrastruct.com/d2@latest go install oss.terrastruct.com/d2@latest
``` ```
You need at least Go v1.18
### Source Release ### Source Release
To install a release from source clone the repository and then: To install a release from source clone the repository and then:
@ -172,33 +175,23 @@ fonts and icons. Furthermore, when installing a non versioned commit, installing
will ensure that `d2 --version` works correctly by embedding the commit hash into the `d2` will ensure that `d2 --version` works correctly by embedding the commit hash into the `d2`
binary. binary.
Remember, you need at least Go v1.18
## Windows ## Windows
d2 builds and runs on Windows: We have prebuilt releases of d2 available for Windows via `.msi` installers. The installer
will add the `d2` binary to your `$PATH` so that you can execute `d2` in `cmd.exe` or
`pwsh.exe`.
We have prebuilt standalone releases for Windows though they're structured in the same way ### Release archives
as our Unix releases.
Easiest way to use d2 on Windows is to just `chdir` into the bin directory of the release We also have release archives for Windows structured in the same way as our Unix releases
and invoke d2 like `./d2 <full-input-file-path>` for use with MSYS2.
For installation, you'll have to put the `bin/d2.exe` binary into your `$PATH` or add the
`bin` directory of the release into your `$PATH`.
See https://www.wikihow.com/Change-the-PATH-Environment-Variable-on-Windows
Then you'll be able to call `d2` from the commandline in `cmd.exe` or `pwsh.exe`.
We intend to have a `.msi` release installer sometime soon that handles putting `d2` into
your `$PATH` for you.
### MSYS2
<img width="1680" alt="Screenshot 2022-12-06 at 2 55 27 AM" src="https://user-images.githubusercontent.com/10180857/205892927-6f3e116c-1c4a-440a-9972-82c306aa9779.png"> <img width="1680" alt="Screenshot 2022-12-06 at 2 55 27 AM" src="https://user-images.githubusercontent.com/10180857/205892927-6f3e116c-1c4a-440a-9972-82c306aa9779.png">
We recommend using [MSYS2](https://www.msys2.org/) or [Git See [MSYS2](https://www.msys2.org/) or [Git Bash](https://gitforwindows.org/#bash) (Git
Bash](https://gitforwindows.org/#bash) (Git Bash is based on MSYS2) for an improved Bash is based on MSYS2).
terminal experience.
MSYS2 provides a unix style shell environment that is native to Windows (unlike MSYS2 provides a unix style shell environment that is native to Windows (unlike
[Cygwin](https://www.cygwin.com/)). MSYS2 allows `install.sh` to work, enables automatic [Cygwin](https://www.cygwin.com/)). MSYS2 allows `install.sh` to work, enables automatic
@ -216,9 +209,23 @@ under plain Windows.
aka Windows Subsystem for Linux if that's what you prefer. Installation is just like any aka Windows Subsystem for Linux if that's what you prefer. Installation is just like any
other Linux system. other Linux system.
## Docker
https://hub.docker.com/repository/docker/terrastruct/d2
We publish `amd64` and `arm64` images based on `debian:latest` for each release.
Example usage:
```sh
echo 'x -> y' >helloworld.d2
docker run --rm -it -u "$(id -u):$(id -g)" -v "$PWD:/root/src" \
-p 127.0.0.1:8080:8080 terrastruct/d2 --watch helloworld.d2
# Visit http://127.0.0.1:8080
```
## Coming soon ## Coming soon
- Docker image
- rpm and deb packages - rpm and deb packages
- with repositories and standalone - with repositories and standalone
- homebrew core - homebrew core

View file

@ -40,6 +40,18 @@ b.1 -> b.1`,
a: A a: A
b: B`, b: B`,
}, },
{
name: "sequence_diagram_name_crash",
script: `foo: {
shape: sequence_diagram
a -> b
}
foobar: {
shape: sequence_diagram
c -> d
}
foo -> foobar`,
},
} }
runa(t, tcs) runa(t, tcs)

View file

@ -0,0 +1,529 @@
{
"name": "",
"shapes": [
{
"id": "foo",
"type": "sequence_diagram",
"pos": {
"x": 0,
"y": 0
},
"width": 448,
"height": 520,
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 0,
"borderRadius": 0,
"fill": "#FFFFFF",
"stroke": "#0D32B2",
"shadow": false,
"3d": false,
"multiple": false,
"tooltip": "",
"link": "",
"icon": null,
"iconPosition": "",
"blend": false,
"fields": null,
"methods": null,
"columns": null,
"label": "foo",
"fontSize": 28,
"fontFamily": "DEFAULT",
"language": "",
"color": "#0A0F25",
"italic": false,
"bold": false,
"underline": false,
"labelWidth": 43,
"labelHeight": 41,
"labelPosition": "INSIDE_TOP_CENTER",
"zIndex": 0,
"level": 1
},
{
"id": "foobar",
"type": "sequence_diagram",
"pos": {
"x": 0,
"y": 620
},
"width": 448,
"height": 520,
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 0,
"borderRadius": 0,
"fill": "#FFFFFF",
"stroke": "#0D32B2",
"shadow": false,
"3d": false,
"multiple": false,
"tooltip": "",
"link": "",
"icon": null,
"iconPosition": "",
"blend": false,
"fields": null,
"methods": null,
"columns": null,
"label": "foobar",
"fontSize": 28,
"fontFamily": "DEFAULT",
"language": "",
"color": "#0A0F25",
"italic": false,
"bold": false,
"underline": false,
"labelWidth": 84,
"labelHeight": 41,
"labelPosition": "INSIDE_TOP_CENTER",
"zIndex": 0,
"level": 1
},
{
"id": "foo.a",
"type": "",
"pos": {
"x": 24,
"y": 110
},
"width": 150,
"height": 126,
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
"borderRadius": 0,
"fill": "#EDF0FD",
"stroke": "#0D32B2",
"shadow": false,
"3d": false,
"multiple": false,
"tooltip": "",
"link": "",
"icon": null,
"iconPosition": "",
"blend": false,
"fields": null,
"methods": null,
"columns": null,
"label": "a",
"fontSize": 16,
"fontFamily": "DEFAULT",
"language": "",
"color": "#0A0F25",
"italic": false,
"bold": false,
"underline": false,
"labelWidth": 12,
"labelHeight": 26,
"labelPosition": "INSIDE_MIDDLE_CENTER",
"zIndex": 0,
"level": 2
},
{
"id": "foo.b",
"type": "",
"pos": {
"x": 274,
"y": 110
},
"width": 150,
"height": 126,
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
"borderRadius": 0,
"fill": "#EDF0FD",
"stroke": "#0D32B2",
"shadow": false,
"3d": false,
"multiple": false,
"tooltip": "",
"link": "",
"icon": null,
"iconPosition": "",
"blend": false,
"fields": null,
"methods": null,
"columns": null,
"label": "b",
"fontSize": 16,
"fontFamily": "DEFAULT",
"language": "",
"color": "#0A0F25",
"italic": false,
"bold": false,
"underline": false,
"labelWidth": 13,
"labelHeight": 26,
"labelPosition": "INSIDE_MIDDLE_CENTER",
"zIndex": 0,
"level": 2
},
{
"id": "foobar.c",
"type": "",
"pos": {
"x": 24,
"y": 730
},
"width": 150,
"height": 126,
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
"borderRadius": 0,
"fill": "#EDF0FD",
"stroke": "#0D32B2",
"shadow": false,
"3d": false,
"multiple": false,
"tooltip": "",
"link": "",
"icon": null,
"iconPosition": "",
"blend": false,
"fields": null,
"methods": null,
"columns": null,
"label": "c",
"fontSize": 16,
"fontFamily": "DEFAULT",
"language": "",
"color": "#0A0F25",
"italic": false,
"bold": false,
"underline": false,
"labelWidth": 12,
"labelHeight": 26,
"labelPosition": "INSIDE_MIDDLE_CENTER",
"zIndex": 0,
"level": 2
},
{
"id": "foobar.d",
"type": "",
"pos": {
"x": 274,
"y": 730
},
"width": 150,
"height": 126,
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
"borderRadius": 0,
"fill": "#EDF0FD",
"stroke": "#0D32B2",
"shadow": false,
"3d": false,
"multiple": false,
"tooltip": "",
"link": "",
"icon": null,
"iconPosition": "",
"blend": false,
"fields": null,
"methods": null,
"columns": null,
"label": "d",
"fontSize": 16,
"fontFamily": "DEFAULT",
"language": "",
"color": "#0A0F25",
"italic": false,
"bold": false,
"underline": false,
"labelWidth": 13,
"labelHeight": 26,
"labelPosition": "INSIDE_MIDDLE_CENTER",
"zIndex": 0,
"level": 2
}
],
"connections": [
{
"id": "foo.(a -> b)[0]",
"src": "foo.a",
"srcArrow": "none",
"srcLabel": "",
"dst": "foo.b",
"dstArrow": "triangle",
"dstLabel": "",
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
"stroke": "#0D32B2",
"label": "",
"fontSize": 16,
"fontFamily": "DEFAULT",
"language": "",
"color": "#676C7E",
"italic": true,
"bold": false,
"underline": false,
"labelWidth": 0,
"labelHeight": 0,
"labelPosition": "",
"labelPercentage": 0,
"route": [
{
"x": 99,
"y": 366
},
{
"x": 349,
"y": 366
}
],
"animated": false,
"tooltip": "",
"icon": null,
"zIndex": 4
},
{
"id": "foobar.(c -> d)[0]",
"src": "foobar.c",
"srcArrow": "none",
"srcLabel": "",
"dst": "foobar.d",
"dstArrow": "triangle",
"dstLabel": "",
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
"stroke": "#0D32B2",
"label": "",
"fontSize": 16,
"fontFamily": "DEFAULT",
"language": "",
"color": "#676C7E",
"italic": true,
"bold": false,
"underline": false,
"labelWidth": 0,
"labelHeight": 0,
"labelPosition": "",
"labelPercentage": 0,
"route": [
{
"x": 99,
"y": 986
},
{
"x": 349,
"y": 986
}
],
"animated": false,
"tooltip": "",
"icon": null,
"zIndex": 4
},
{
"id": "(foo -> foobar)[0]",
"src": "foo",
"srcArrow": "none",
"srcLabel": "",
"dst": "foobar",
"dstArrow": "triangle",
"dstLabel": "",
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
"stroke": "#0D32B2",
"label": "",
"fontSize": 16,
"fontFamily": "DEFAULT",
"language": "",
"color": "#676C7E",
"italic": true,
"bold": false,
"underline": false,
"labelWidth": 0,
"labelHeight": 0,
"labelPosition": "",
"labelPercentage": 0,
"route": [
{
"x": 224,
"y": 520
},
{
"x": 224,
"y": 560
},
{
"x": 224,
"y": 580
},
{
"x": 224,
"y": 620
}
],
"isCurve": true,
"animated": false,
"tooltip": "",
"icon": null,
"zIndex": 0
},
{
"id": "(foo.a -- )[0]",
"src": "foo.a",
"srcArrow": "none",
"srcLabel": "",
"dst": "a-lifeline-end-2251863791",
"dstArrow": "none",
"dstLabel": "",
"opacity": 1,
"strokeDash": 6,
"strokeWidth": 2,
"stroke": "#0D32B2",
"label": "",
"fontSize": 16,
"fontFamily": "DEFAULT",
"language": "",
"color": "#676C7E",
"italic": true,
"bold": false,
"underline": false,
"labelWidth": 0,
"labelHeight": 0,
"labelPosition": "",
"labelPercentage": 0,
"route": [
{
"x": 99,
"y": 236
},
{
"x": 99,
"y": 496
}
],
"animated": false,
"tooltip": "",
"icon": null,
"zIndex": 1
},
{
"id": "(foo.b -- )[0]",
"src": "foo.b",
"srcArrow": "none",
"srcLabel": "",
"dst": "b-lifeline-end-668380428",
"dstArrow": "none",
"dstLabel": "",
"opacity": 1,
"strokeDash": 6,
"strokeWidth": 2,
"stroke": "#0D32B2",
"label": "",
"fontSize": 16,
"fontFamily": "DEFAULT",
"language": "",
"color": "#676C7E",
"italic": true,
"bold": false,
"underline": false,
"labelWidth": 0,
"labelHeight": 0,
"labelPosition": "",
"labelPercentage": 0,
"route": [
{
"x": 349,
"y": 236
},
{
"x": 349,
"y": 496
}
],
"animated": false,
"tooltip": "",
"icon": null,
"zIndex": 1
},
{
"id": "(foobar.c -- )[0]",
"src": "foobar.c",
"srcArrow": "none",
"srcLabel": "",
"dst": "c-lifeline-end-955173837",
"dstArrow": "none",
"dstLabel": "",
"opacity": 1,
"strokeDash": 6,
"strokeWidth": 2,
"stroke": "#0D32B2",
"label": "",
"fontSize": 16,
"fontFamily": "DEFAULT",
"language": "",
"color": "#676C7E",
"italic": true,
"bold": false,
"underline": false,
"labelWidth": 0,
"labelHeight": 0,
"labelPosition": "",
"labelPercentage": 0,
"route": [
{
"x": 99,
"y": 856
},
{
"x": 99,
"y": 1116
}
],
"animated": false,
"tooltip": "",
"icon": null,
"zIndex": 1
},
{
"id": "(foobar.d -- )[0]",
"src": "foobar.d",
"srcArrow": "none",
"srcLabel": "",
"dst": "d-lifeline-end-2106864010",
"dstArrow": "none",
"dstLabel": "",
"opacity": 1,
"strokeDash": 6,
"strokeWidth": 2,
"stroke": "#0D32B2",
"label": "",
"fontSize": 16,
"fontFamily": "DEFAULT",
"language": "",
"color": "#676C7E",
"italic": true,
"bold": false,
"underline": false,
"labelWidth": 0,
"labelHeight": 0,
"labelPosition": "",
"labelPercentage": 0,
"route": [
{
"x": 349,
"y": 856
},
{
"x": 349,
"y": 1116
}
],
"animated": false,
"tooltip": "",
"icon": null,
"zIndex": 1
}
]
}

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 328 KiB

View file

@ -0,0 +1,520 @@
{
"name": "",
"shapes": [
{
"id": "foo",
"type": "sequence_diagram",
"pos": {
"x": 12,
"y": 12
},
"width": 448,
"height": 520,
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 0,
"borderRadius": 0,
"fill": "#FFFFFF",
"stroke": "#0D32B2",
"shadow": false,
"3d": false,
"multiple": false,
"tooltip": "",
"link": "",
"icon": null,
"iconPosition": "",
"blend": false,
"fields": null,
"methods": null,
"columns": null,
"label": "foo",
"fontSize": 28,
"fontFamily": "DEFAULT",
"language": "",
"color": "#0A0F25",
"italic": false,
"bold": false,
"underline": false,
"labelWidth": 43,
"labelHeight": 41,
"labelPosition": "INSIDE_TOP_CENTER",
"zIndex": 0,
"level": 1
},
{
"id": "foobar",
"type": "sequence_diagram",
"pos": {
"x": 12,
"y": 632
},
"width": 448,
"height": 520,
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 0,
"borderRadius": 0,
"fill": "#FFFFFF",
"stroke": "#0D32B2",
"shadow": false,
"3d": false,
"multiple": false,
"tooltip": "",
"link": "",
"icon": null,
"iconPosition": "",
"blend": false,
"fields": null,
"methods": null,
"columns": null,
"label": "foobar",
"fontSize": 28,
"fontFamily": "DEFAULT",
"language": "",
"color": "#0A0F25",
"italic": false,
"bold": false,
"underline": false,
"labelWidth": 84,
"labelHeight": 41,
"labelPosition": "INSIDE_TOP_CENTER",
"zIndex": 0,
"level": 1
},
{
"id": "foo.a",
"type": "",
"pos": {
"x": 36,
"y": 122
},
"width": 150,
"height": 126,
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
"borderRadius": 0,
"fill": "#EDF0FD",
"stroke": "#0D32B2",
"shadow": false,
"3d": false,
"multiple": false,
"tooltip": "",
"link": "",
"icon": null,
"iconPosition": "",
"blend": false,
"fields": null,
"methods": null,
"columns": null,
"label": "a",
"fontSize": 16,
"fontFamily": "DEFAULT",
"language": "",
"color": "#0A0F25",
"italic": false,
"bold": false,
"underline": false,
"labelWidth": 12,
"labelHeight": 26,
"labelPosition": "INSIDE_MIDDLE_CENTER",
"zIndex": 0,
"level": 2
},
{
"id": "foo.b",
"type": "",
"pos": {
"x": 286,
"y": 122
},
"width": 150,
"height": 126,
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
"borderRadius": 0,
"fill": "#EDF0FD",
"stroke": "#0D32B2",
"shadow": false,
"3d": false,
"multiple": false,
"tooltip": "",
"link": "",
"icon": null,
"iconPosition": "",
"blend": false,
"fields": null,
"methods": null,
"columns": null,
"label": "b",
"fontSize": 16,
"fontFamily": "DEFAULT",
"language": "",
"color": "#0A0F25",
"italic": false,
"bold": false,
"underline": false,
"labelWidth": 13,
"labelHeight": 26,
"labelPosition": "INSIDE_MIDDLE_CENTER",
"zIndex": 0,
"level": 2
},
{
"id": "foobar.c",
"type": "",
"pos": {
"x": 36,
"y": 742
},
"width": 150,
"height": 126,
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
"borderRadius": 0,
"fill": "#EDF0FD",
"stroke": "#0D32B2",
"shadow": false,
"3d": false,
"multiple": false,
"tooltip": "",
"link": "",
"icon": null,
"iconPosition": "",
"blend": false,
"fields": null,
"methods": null,
"columns": null,
"label": "c",
"fontSize": 16,
"fontFamily": "DEFAULT",
"language": "",
"color": "#0A0F25",
"italic": false,
"bold": false,
"underline": false,
"labelWidth": 12,
"labelHeight": 26,
"labelPosition": "INSIDE_MIDDLE_CENTER",
"zIndex": 0,
"level": 2
},
{
"id": "foobar.d",
"type": "",
"pos": {
"x": 286,
"y": 742
},
"width": 150,
"height": 126,
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
"borderRadius": 0,
"fill": "#EDF0FD",
"stroke": "#0D32B2",
"shadow": false,
"3d": false,
"multiple": false,
"tooltip": "",
"link": "",
"icon": null,
"iconPosition": "",
"blend": false,
"fields": null,
"methods": null,
"columns": null,
"label": "d",
"fontSize": 16,
"fontFamily": "DEFAULT",
"language": "",
"color": "#0A0F25",
"italic": false,
"bold": false,
"underline": false,
"labelWidth": 13,
"labelHeight": 26,
"labelPosition": "INSIDE_MIDDLE_CENTER",
"zIndex": 0,
"level": 2
}
],
"connections": [
{
"id": "foo.(a -> b)[0]",
"src": "foo.a",
"srcArrow": "none",
"srcLabel": "",
"dst": "foo.b",
"dstArrow": "triangle",
"dstLabel": "",
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
"stroke": "#0D32B2",
"label": "",
"fontSize": 16,
"fontFamily": "DEFAULT",
"language": "",
"color": "#676C7E",
"italic": true,
"bold": false,
"underline": false,
"labelWidth": 0,
"labelHeight": 0,
"labelPosition": "",
"labelPercentage": 0,
"route": [
{
"x": 111,
"y": 378
},
{
"x": 361,
"y": 378
}
],
"animated": false,
"tooltip": "",
"icon": null,
"zIndex": 4
},
{
"id": "foobar.(c -> d)[0]",
"src": "foobar.c",
"srcArrow": "none",
"srcLabel": "",
"dst": "foobar.d",
"dstArrow": "triangle",
"dstLabel": "",
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
"stroke": "#0D32B2",
"label": "",
"fontSize": 16,
"fontFamily": "DEFAULT",
"language": "",
"color": "#676C7E",
"italic": true,
"bold": false,
"underline": false,
"labelWidth": 0,
"labelHeight": 0,
"labelPosition": "",
"labelPercentage": 0,
"route": [
{
"x": 111,
"y": 998
},
{
"x": 361,
"y": 998
}
],
"animated": false,
"tooltip": "",
"icon": null,
"zIndex": 4
},
{
"id": "(foo -> foobar)[0]",
"src": "foo",
"srcArrow": "none",
"srcLabel": "",
"dst": "foobar",
"dstArrow": "triangle",
"dstLabel": "",
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
"stroke": "#0D32B2",
"label": "",
"fontSize": 16,
"fontFamily": "DEFAULT",
"language": "",
"color": "#676C7E",
"italic": true,
"bold": false,
"underline": false,
"labelWidth": 0,
"labelHeight": 0,
"labelPosition": "",
"labelPercentage": 0,
"route": [
{
"x": 236,
"y": 532
},
{
"x": 236,
"y": 632
}
],
"animated": false,
"tooltip": "",
"icon": null,
"zIndex": 0
},
{
"id": "(foo.a -- )[0]",
"src": "foo.a",
"srcArrow": "none",
"srcLabel": "",
"dst": "a-lifeline-end-2251863791",
"dstArrow": "none",
"dstLabel": "",
"opacity": 1,
"strokeDash": 6,
"strokeWidth": 2,
"stroke": "#0D32B2",
"label": "",
"fontSize": 16,
"fontFamily": "DEFAULT",
"language": "",
"color": "#676C7E",
"italic": true,
"bold": false,
"underline": false,
"labelWidth": 0,
"labelHeight": 0,
"labelPosition": "",
"labelPercentage": 0,
"route": [
{
"x": 111,
"y": 248
},
{
"x": 111,
"y": 508
}
],
"animated": false,
"tooltip": "",
"icon": null,
"zIndex": 1
},
{
"id": "(foo.b -- )[0]",
"src": "foo.b",
"srcArrow": "none",
"srcLabel": "",
"dst": "b-lifeline-end-668380428",
"dstArrow": "none",
"dstLabel": "",
"opacity": 1,
"strokeDash": 6,
"strokeWidth": 2,
"stroke": "#0D32B2",
"label": "",
"fontSize": 16,
"fontFamily": "DEFAULT",
"language": "",
"color": "#676C7E",
"italic": true,
"bold": false,
"underline": false,
"labelWidth": 0,
"labelHeight": 0,
"labelPosition": "",
"labelPercentage": 0,
"route": [
{
"x": 361,
"y": 248
},
{
"x": 361,
"y": 508
}
],
"animated": false,
"tooltip": "",
"icon": null,
"zIndex": 1
},
{
"id": "(foobar.c -- )[0]",
"src": "foobar.c",
"srcArrow": "none",
"srcLabel": "",
"dst": "c-lifeline-end-955173837",
"dstArrow": "none",
"dstLabel": "",
"opacity": 1,
"strokeDash": 6,
"strokeWidth": 2,
"stroke": "#0D32B2",
"label": "",
"fontSize": 16,
"fontFamily": "DEFAULT",
"language": "",
"color": "#676C7E",
"italic": true,
"bold": false,
"underline": false,
"labelWidth": 0,
"labelHeight": 0,
"labelPosition": "",
"labelPercentage": 0,
"route": [
{
"x": 111,
"y": 868
},
{
"x": 111,
"y": 1128
}
],
"animated": false,
"tooltip": "",
"icon": null,
"zIndex": 1
},
{
"id": "(foobar.d -- )[0]",
"src": "foobar.d",
"srcArrow": "none",
"srcLabel": "",
"dst": "d-lifeline-end-2106864010",
"dstArrow": "none",
"dstLabel": "",
"opacity": 1,
"strokeDash": 6,
"strokeWidth": 2,
"stroke": "#0D32B2",
"label": "",
"fontSize": 16,
"fontFamily": "DEFAULT",
"language": "",
"color": "#676C7E",
"italic": true,
"bold": false,
"underline": false,
"labelWidth": 0,
"labelHeight": 0,
"labelPosition": "",
"labelPercentage": 0,
"route": [
{
"x": 361,
"y": 868
},
{
"x": 361,
"y": 1128
}
],
"animated": false,
"tooltip": "",
"icon": null,
"zIndex": 1
}
]
}

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 328 KiB

2
go.mod generated
View file

@ -21,7 +21,7 @@ require (
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2
gonum.org/v1/plot v0.12.0 gonum.org/v1/plot v0.12.0
nhooyr.io/websocket v1.8.7 nhooyr.io/websocket v1.8.7
oss.terrastruct.com/util-go v0.0.0-20221213112904-c09378905bfb oss.terrastruct.com/util-go v0.0.0-20221218203331-558e3b4adc95
) )
require ( require (

4
go.sum generated
View file

@ -806,8 +806,8 @@ honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
nhooyr.io/websocket v1.8.7 h1:usjR2uOr/zjjkVMy0lW+PPohFok7PCow5sDjLgX4P4g= nhooyr.io/websocket v1.8.7 h1:usjR2uOr/zjjkVMy0lW+PPohFok7PCow5sDjLgX4P4g=
nhooyr.io/websocket v1.8.7/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0= nhooyr.io/websocket v1.8.7/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0=
oss.terrastruct.com/util-go v0.0.0-20221213112904-c09378905bfb h1:ybqEeDAI/VyaVgPd5kis5aq+IPhHI3dQmdSxFG6SuuY= oss.terrastruct.com/util-go v0.0.0-20221218203331-558e3b4adc95 h1:Ya/jgBNhNwZ30BsEcyjf2bTT8dtCdTM2WOSMEeSGB3E=
oss.terrastruct.com/util-go v0.0.0-20221213112904-c09378905bfb/go.mod h1:Fwy72FDIOOM4K8F96ScXkxHHppR1CPfUyo9+x9c1PBU= oss.terrastruct.com/util-go v0.0.0-20221218203331-558e3b4adc95/go.mod h1:Fwy72FDIOOM4K8F96ScXkxHHppR1CPfUyo9+x9c1PBU=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
rsc.io/pdf v0.1.1 h1:k1MczvYDUvJBe93bYd7wrZLLUEcLZAuF824/I4e5Xr4= rsc.io/pdf v0.1.1 h1:k1MczvYDUvJBe93bYd7wrZLLUEcLZAuF824/I4e5Xr4=
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=

View file

@ -278,7 +278,7 @@ header() {
bigheader() { bigheader() {
set -- "$(echo "$*" | sed "s/^/ * /")" set -- "$(echo "$*" | sed "s/^/ * /")"
FGCOLOR=${FGCOLOR:-3} logp "/**************************************************************** FGCOLOR=${FGCOLOR:-6} logp "/****************************************************************
$* $*
****************************************************************/" ****************************************************************/"
} }

View file

@ -243,6 +243,10 @@ func compile(ctx context.Context, ms *xmain.State, plugin d2plugin.Plugin, pad,
if err != nil { if err != nil {
return svg, false, err return svg, false, err
} }
} else {
if len(out) > 0 && out[len(out)-1] != '\n' {
out = append(out, '\n')
}
} }
err = ms.WritePath(outputPath, out) err = ms.WritePath(outputPath, out)

View file

@ -7,6 +7,6 @@ if [ ! -e "$(dirname "$0")/ci/sub/.git" ]; then
fi fi
. "$(dirname "$0")/ci/sub/lib.sh" . "$(dirname "$0")/ci/sub/lib.sh"
PATH="$(cd -- "$(dirname "$0")" && pwd)/ci/sub/bin:$PATH" PATH="$(cd -- "$(dirname "$0")" && pwd)/ci/sub/bin:$PATH"
cd "$(dirname "$0")" cd -- "$(dirname "$0")"
_make "$@" _make "$@"