diff --git a/ci/release/Dockerfile b/ci/release/Dockerfile new file mode 100644 index 000000000..d195fa05b --- /dev/null +++ b/ci/release/Dockerfile @@ -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"] diff --git a/ci/release/README.md b/ci/release/README.md index 4530983e4..4f8b1a56d 100644 --- a/ci/release/README.md +++ b/ci/release/README.md @@ -2,7 +2,7 @@ ## _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 @@ -23,12 +23,12 @@ it depends on from ../sub/lib. > 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. +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 +The Dockerfile is in ./linux/Dockerfile ### _build.sh diff --git a/ci/release/aws/ensure.sh b/ci/release/aws/ensure.sh new file mode 100755 index 000000000..66cc1cb1b --- /dev/null +++ b/ci/release/aws/ensure.sh @@ -0,0 +1,540 @@ +#!/bin/sh +set -eu +. "$(dirname "$0")/../../../ci/sub/lib.sh" +cd -- "$(dirname "$0")/../../.." + +help() { + cat </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') + 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 -- < /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 -- <> ~/.zshrc\"" + fi + if ! sh_c ssh "$REMOTE_HOST" "'grep -qF \\\$HOME/.local ~/.zshrc'"; then + sh_c ssh "$REMOTE_HOST" "\"(echo && cat) >> ~/.zshrc\"" <\$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() { + ssh-keygen -R "${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 < 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' +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 -- < 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: +# <&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 <$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 "$@" diff --git a/ci/release/build/.dockerignore b/ci/release/build/.dockerignore new file mode 100644 index 000000000..73f0caa06 --- /dev/null +++ b/ci/release/build/.dockerignore @@ -0,0 +1 @@ +**/d2*/ diff --git a/ci/release/build_docker.sh b/ci/release/build_in_docker.sh similarity index 81% rename from ci/release/build_docker.sh rename to ci/release/build_in_docker.sh index 2939c5dd0..b8a4f1f6c 100755 --- a/ci/release/build_docker.sh +++ b/ci/release/build_in_docker.sh @@ -5,7 +5,7 @@ cd -- "$(dirname "$0")/../.." tag="$(sh_c docker build \ --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 \ -e DRY_RUN \ -e HW_BUILD_DIR \ diff --git a/ci/release/builders/Dockerfile-centos b/ci/release/builders/Dockerfile-centos deleted file mode 100644 index 66aaf6f17..000000000 --- a/ci/release/builders/Dockerfile-centos +++ /dev/null @@ -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 diff --git a/ci/release/builders/aws_copy_keys.sh b/ci/release/builders/aws_copy_keys.sh deleted file mode 100755 index cc0d548ef..000000000 --- a/ci/release/builders/aws_copy_keys.sh +++ /dev/null @@ -1,57 +0,0 @@ -#!/bin/sh -set -eu -cd -- "$(dirname "$0")/../../.." -. ./ci/sub/lib.sh - -help() { - cat < .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 "$@" diff --git a/ci/release/builders/aws_ensure.sh b/ci/release/builders/aws_ensure.sh deleted file mode 100755 index 63c117abc..000000000 --- a/ci/release/builders/aws_ensure.sh +++ /dev/null @@ -1,294 +0,0 @@ -#!/bin/sh -set -eu -cd -- "$(dirname "$0")/../../.." -. ./ci/sub/lib.sh - -help() { - cat </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 -- < /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\"" <\$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 "$@" diff --git a/ci/release/changelogs/next.md b/ci/release/changelogs/next.md index 14a6c2a39..58d3c237e 100644 --- a/ci/release/changelogs/next.md +++ b/ci/release/changelogs/next.md @@ -1,20 +1,18 @@ #### Features ๐Ÿš€ -- 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) +- 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) - 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 ๐Ÿงน -- Fmt now preserves leading comment spacing. +- `d2 fmt` now preserves leading comment spacing. [#400](https://github.com/terrastruct/d2/issues/400) #### Bugfixes โ›‘๏ธ -- Fixed crash when sequence diagrams had no messages. - [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 serialization affecting binary plugins (TALA). - [https://github.com/terrastruct/d2/pull/426](https://github.com/terrastruct/d2/pull/426) +- Fixed crash when sequence diagrams had no messages. [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 serialization affecting binary plugins (TALA). [https://github.com/terrastruct/d2/pull/426](https://github.com/terrastruct/d2/pull/426) - 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) diff --git a/ci/release/builders/Dockerfile b/ci/release/linux/Dockerfile similarity index 77% rename from ci/release/builders/Dockerfile rename to ci/release/linux/Dockerfile index 5adc0bd25..5bc66a568 100644 --- a/ci/release/builders/Dockerfile +++ b/ci/release/linux/Dockerfile @@ -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 ENV PATH="/usr/local/go/bin:$PATH" -RUN apt-get install -y build-essential -RUN apt-get install -y rsync +RUN apt-get install -y build-essential \ + rsync diff --git a/ci/release/template/scripts/lib.sh b/ci/release/template/scripts/lib.sh index 62ade2f89..699795908 100644 --- a/ci/release/template/scripts/lib.sh +++ b/ci/release/template/scripts/lib.sh @@ -273,7 +273,7 @@ header() { bigheader() { set -- "$(echo "$*" | sed "s/^/ * /")" - FGCOLOR=${FGCOLOR:-3} logp "/**************************************************************** + FGCOLOR=${FGCOLOR:-6} logp "/**************************************************************** $* ****************************************************************/" } diff --git a/ci/release/windows/.gitignore b/ci/release/windows/.gitignore new file mode 100644 index 000000000..55ae023fc --- /dev/null +++ b/ci/release/windows/.gitignore @@ -0,0 +1 @@ +d2.exe diff --git a/ci/release/windows/d2.ico b/ci/release/windows/d2.ico new file mode 100644 index 000000000..bc8335225 Binary files /dev/null and b/ci/release/windows/d2.ico differ diff --git a/ci/release/windows/d2.png b/ci/release/windows/d2.png new file mode 100644 index 000000000..ba83fed7a Binary files /dev/null and b/ci/release/windows/d2.png differ diff --git a/ci/release/windows/d2.wxs b/ci/release/windows/d2.wxs new file mode 100644 index 000000000..1b7b46631 --- /dev/null +++ b/ci/release/windows/d2.wxs @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ci/sub b/ci/sub index 7923b353a..55bf1d39f 160000 --- a/ci/sub +++ b/ci/sub @@ -1 +1 @@ -Subproject commit 7923b353a829cf289171baff0d0d017ae3d32278 +Subproject commit 55bf1d39f3d524804422d4bdb4559cd6ff151b49 diff --git a/docs/INSTALL.md b/docs/INSTALL.md index 30e31d7cd..8141d9fc3 100644 --- a/docs/INSTALL.md +++ b/docs/INSTALL.md @@ -14,8 +14,9 @@ You may install `d2` through any of the following methods. - From source - Source Release - Windows - - MSYS2 + - Release archives - WSL +- Docker - Coming soon ## install.sh @@ -174,31 +175,19 @@ binary. ## 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 -as our Unix releases. +### Release archives -Easiest way to use d2 on Windows is to just `chdir` into the bin directory of the release -and invoke d2 like `./d2 ` - -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 +We also have release archives for Windows structured in the same way as our Unix releases +for use with MSYS2. Screenshot 2022-12-06 at 2 55 27 AM -We recommend using [MSYS2](https://www.msys2.org/) or [Git -Bash](https://gitforwindows.org/#bash) (Git Bash is based on MSYS2) for an improved -terminal experience. +See [MSYS2](https://www.msys2.org/) or [Git Bash](https://gitforwindows.org/#bash) (Git +Bash is based on MSYS2). 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 @@ -216,9 +205,23 @@ under plain Windows. aka Windows Subsystem for Linux if that's what you prefer. Installation is just like any 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 -- Docker image - rpm and deb packages - with repositories and standalone - homebrew core diff --git a/install.sh b/install.sh index bbe0ca02e..026f423c9 100755 --- a/install.sh +++ b/install.sh @@ -278,7 +278,7 @@ header() { bigheader() { set -- "$(echo "$*" | sed "s/^/ * /")" - FGCOLOR=${FGCOLOR:-3} logp "/**************************************************************** + FGCOLOR=${FGCOLOR:-6} logp "/**************************************************************** $* ****************************************************************/" }