#!/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') 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 -- < /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 printf %s '$CI_DOCKER_TOKEN' | docker login -u terrastruct --password-stdin # For building images cross platform from the arm64 instance. # We could use QEMU with: # sudo -E apt-get install -y qemu qemu-user-static # But we don't as playwright dependencies do not install on QEMU on either arm64 or amd64. if [ "\$(uname -m)" = aarch64 ]; then if [ "\$(stat -c '%a' ~/.ssh/id_ed25519 2>/dev/null)" != 600 ]; then echo '$CI_TSTRUCT_ID_ED25519' >~/.ssh/id_ed25519 chmod 600 ~/.ssh/id_ed25519 fi if ! docker context ls | grep -qF ci-d2-linux-amd64; then docker context create ci-d2-linux-amd64 --docker "host=ssh://$CI_D2_LINUX_AMD64" fi if ! docker buildx ls | grep -qF 'd2 *'; then docker buildx create --use --name d2 --platform linux/arm64 default fi if ! docker buildx inspect d2 | grep -qF ci-d2-linux-amd64; then docker buildx create --append --name d2 --platform linux/amd64 ci-d2-linux-amd64 fi fi 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" # ubuntu has $PATH hard coded in /etc/environment for some reason. It takes precedence # over ~/.ssh/environment. sh_c ssh "$REMOTE_HOST" "sudo rm -f /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 < 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 -- < 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 <