Skip to content

cards.ipynb (Windows Native) #9

cards.ipynb (Windows Native)

cards.ipynb (Windows Native) #9

Workflow file for this run

# File: cards-win.yml
# Code: Claude Code
# Review: Ryoichi Ando ([email protected])
# License: Apache v2.0
name: cards.ipynb (Windows Native)
on:
workflow_dispatch:
inputs:
instance_type:
description: 'EC2 instance type'
required: true
default: 'g6e.2xlarge'
type: choice
options:
- g6.2xlarge
- g6e.2xlarge
region:
description: 'AWS Region'
required: true
default: 'us-east-2'
type: choice
options:
- us-east-1
- us-east-2
- ap-northeast-1
jobs:
run-example:
name: Run cards (Windows)
runs-on: ubuntu-latest
permissions:
id-token: write
contents: read
env:
AWS_REGION: ${{ github.event.inputs.region }}
INSTANCE_TYPE: ${{ github.event.inputs.instance_type }}
BRANCH: ${{ github.ref_name }}
EXAMPLE: cards
WORKDIR: C:\ppf-contact-solver
USER: Administrator
steps:
- name: Show input parameters
run: |
echo "## Input Parameters"
echo "Example: cards"
echo "Branch: ${{ github.ref_name }}"
echo "Instance Type: ${{ github.event.inputs.instance_type }}"
echo "Region: ${{ github.event.inputs.region }}"
- name: Checkout repository
uses: actions/checkout@v4
- name: Configure AWS credentials via OIDC
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ secrets.AWS_ROLE_ARN }}
aws-region: ${{ env.AWS_REGION }}
- name: Verify AWS authentication
run: |
echo "Testing AWS authentication..."
aws sts get-caller-identity
echo "AWS Region: $AWS_REGION"
echo "Instance Type: $INSTANCE_TYPE"
echo "Branch: $BRANCH"
echo "Example: $EXAMPLE"
- name: Get GitHub Actions runner public IP
id: runner-ip
run: |
echo "Fetching GitHub Actions runner public IP..."
RUNNER_IP=$(curl -s --max-time 10 https://checkip.amazonaws.com | tr -d '\n')
if [ -z "$RUNNER_IP" ]; then
echo "ERROR: Failed to get IP from checkip.amazonaws.com"
exit 1
fi
echo "::add-mask::$RUNNER_IP"
echo "RUNNER_IP=$RUNNER_IP" >> $GITHUB_OUTPUT
echo "GitHub Actions Runner IP: $RUNNER_IP"
- name: Find Windows Server 2025 AMI
id: ami
run: |
echo "Finding latest Windows Server 2025 AMI..."
AMI_ID=$(aws ec2 describe-images \
--owners amazon \
--filters \
"Name=name,Values=Windows_Server-2025-English-Full-Base-*" \
"Name=state,Values=available" \
--query 'sort_by(Images, &CreationDate)[-1].ImageId' \
--region "$AWS_REGION" \
--output text)
if [ "$AMI_ID" = "None" ] || [ -z "$AMI_ID" ]; then
echo "ERROR: Windows Server 2025 AMI not found in region $AWS_REGION"
exit 1
fi
echo "AMI_ID=$AMI_ID" >> $GITHUB_OUTPUT
echo "Found AMI: $AMI_ID"
- name: Get default VPC ID
id: vpc
run: |
echo "Getting default VPC ID..."
VPC_ID=$(aws ec2 describe-vpcs \
--filters "Name=isDefault,Values=true" \
--query 'Vpcs[0].VpcId' \
--region "$AWS_REGION" \
--output text)
if [ "$VPC_ID" = "None" ] || [ -z "$VPC_ID" ]; then
echo "ERROR: Default VPC not found in region $AWS_REGION"
exit 1
fi
echo "VPC_ID=$VPC_ID" >> $GITHUB_OUTPUT
echo "Default VPC: $VPC_ID"
- name: Generate unique identifiers
id: ids
run: |
TIMESTAMP=$(date +%Y%m%d%H%M%S)
RANDOM_SUFFIX=$(head /dev/urandom | tr -dc a-z0-9 | head -c 6)
TEMP_INSTANCE_ID="temp-${TIMESTAMP}-${RANDOM_SUFFIX}"
SSH_PORT=$((10001 + RANDOM % 55535))
echo "::add-mask::$SSH_PORT"
echo "TIMESTAMP=$TIMESTAMP" >> $GITHUB_OUTPUT
echo "TEMP_INSTANCE_ID=$TEMP_INSTANCE_ID" >> $GITHUB_OUTPUT
echo "SSH_PORT=$SSH_PORT" >> $GITHUB_OUTPUT
echo "Temporary Instance ID: $TEMP_INSTANCE_ID"
echo "SSH Port: $SSH_PORT"
- name: Setup persistent security group
id: security-group
run: |
echo "Setting up persistent security group 'github-actions-windows-persistent'..."
SG_NAME="github-actions-windows-persistent"
SG_DESCRIPTION="Persistent security group for GitHub Actions Windows builds with dynamic rules"
SG_ID=$(aws ec2 describe-security-groups \
--filters "Name=group-name,Values=$SG_NAME" \
--query 'SecurityGroups[0].GroupId' \
--region "$AWS_REGION" \
--output text || echo "")
if [ "$SG_ID" = "None" ] || [ -z "$SG_ID" ]; then
echo "Security group does not exist. Creating new one..."
SG_ID=$(aws ec2 create-security-group \
--group-name "$SG_NAME" \
--description "$SG_DESCRIPTION" \
--vpc-id "${{ steps.vpc.outputs.VPC_ID }}" \
--query 'GroupId' \
--region "$AWS_REGION" \
--output text)
echo "Security Group created: $SG_ID"
aws ec2 create-tags \
--resources "$SG_ID" \
--tags \
"Key=Name,Value=$SG_NAME" \
"Key=ManagedBy,Value=GitHubActions" \
"Key=Purpose,Value=WindowsBuildPersistentDynamicRules" \
"Key=CreatedAt,Value=$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
--region "$AWS_REGION"
echo "Security Group tagged successfully"
else
echo "Using existing security group: $SG_ID"
fi
echo "SG_ID=$SG_ID" >> $GITHUB_OUTPUT
echo "Adding ingress rule for runner IP on port ${{ steps.ids.outputs.SSH_PORT }}"
aws ec2 authorize-security-group-ingress \
--group-id "$SG_ID" \
--ip-permissions \
"IpProtocol=tcp,FromPort=${{ steps.ids.outputs.SSH_PORT }},ToPort=${{ steps.ids.outputs.SSH_PORT }},IpRanges=[{CidrIp=${{ steps.runner-ip.outputs.RUNNER_IP }}/32,Description='GHA Run ${{ github.run_id }} Port ${{ steps.ids.outputs.SSH_PORT }}'}]" \
--region "$AWS_REGION" 2>&1 || echo "Note: Rule may already exist"
echo "RUNNER_IP_CIDR=${{ steps.runner-ip.outputs.RUNNER_IP }}/32" >> $GITHUB_OUTPUT
echo "SSH_PORT=${{ steps.ids.outputs.SSH_PORT }}" >> $GITHUB_OUTPUT
echo "SSH ingress rule added successfully"
- name: Retrieve SSH key from Parameter Store
id: keypair
run: |
echo "Retrieving SSH private key from AWS Systems Manager..."
aws ssm get-parameter \
--name "/github-actions/ec2/ssh-key" \
--with-decryption \
--query 'Parameter.Value' \
--region "$AWS_REGION" \
--output text > /tmp/github-actions-ec2.pem
chmod 600 /tmp/github-actions-ec2.pem
echo "SSH key retrieved successfully"
echo "KEY_PATH=/tmp/github-actions-ec2.pem" >> $GITHUB_OUTPUT
- name: Create Windows user data script
run: |
SSH_PORT="${{ steps.ids.outputs.SSH_PORT }}"
sed "s/SSH_PORT_PLACEHOLDER/$SSH_PORT/g" .github/workflows/scripts/win/user-data.ps1 > /tmp/user-data.ps1
echo "User data script created with SSH port $SSH_PORT"
- name: Launch EC2 instance
id: instance
run: |
echo "Launching Windows EC2 instance..."
INSTANCE_ID=$(aws ec2 run-instances \
--image-id "${{ steps.ami.outputs.AMI_ID }}" \
--instance-type "$INSTANCE_TYPE" \
--key-name "${{ secrets.AWS_KEY_PAIR_NAME }}" \
--security-group-ids "${{ steps.security-group.outputs.SG_ID }}" \
--user-data file:///tmp/user-data.ps1 \
--block-device-mappings "DeviceName=/dev/sda1,Ebs={VolumeSize=100,VolumeType=gp3,DeleteOnTermination=true}" \
--tag-specifications \
"ResourceType=instance,Tags=[{Key=Name,Value=gpu-runner-win-cards-${{ steps.ids.outputs.TIMESTAMP }}},{Key=ManagedBy,Value=GitHubActions},{Key=Purpose,Value=WindowsGPURunner},{Key=Workflow,Value=${{ github.workflow }}},{Key=RunId,Value=${{ github.run_id }}},{Key=Branch,Value=${{ env.BRANCH }}},{Key=Example,Value=cards},{Key=SSHPort,Value=${{ steps.ids.outputs.SSH_PORT }}}]" \
"ResourceType=volume,Tags=[{Key=Name,Value=gpu-runner-win-cards-${{ steps.ids.outputs.TIMESTAMP }}-volume},{Key=ManagedBy,Value=GitHubActions},{Key=Purpose,Value=WindowsGPURunner}]" \
--instance-initiated-shutdown-behavior terminate \
--query 'Instances[0].InstanceId' \
--region "$AWS_REGION" \
--output text)
echo "INSTANCE_ID=$INSTANCE_ID" >> $GITHUB_OUTPUT
echo "Instance launched: $INSTANCE_ID"
- name: Wait for instance to be running
run: |
echo "Waiting for instance to be running..."
aws ec2 wait instance-running \
--instance-ids "${{ steps.instance.outputs.INSTANCE_ID }}" \
--region "$AWS_REGION"
PUBLIC_IP=$(aws ec2 describe-instances \
--instance-ids "${{ steps.instance.outputs.INSTANCE_ID }}" \
--query 'Reservations[0].Instances[0].PublicIpAddress' \
--region "$AWS_REGION" \
--output text)
echo "::add-mask::$PUBLIC_IP"
echo "PUBLIC_IP=$PUBLIC_IP" >> $GITHUB_ENV
echo "Instance is running at: $PUBLIC_IP"
- name: Wait for SSH
run: |
echo "Waiting for SSH to be ready..."
KEY_PATH="${{ steps.keypair.outputs.KEY_PATH }}"
SSH_PORT="${{ steps.ids.outputs.SSH_PORT }}"
echo "Waiting for SSH on port $SSH_PORT..."
MAX_SSH_ATTEMPTS=60
ATTEMPT=0
while [ $ATTEMPT -lt $MAX_SSH_ATTEMPTS ]; do
if ssh -p $SSH_PORT -o ConnectTimeout=5 -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \
-o ServerAliveInterval=60 -o ServerAliveCountMax=10 \
-o BatchMode=yes -i "$KEY_PATH" Administrator@$PUBLIC_IP "echo SSH_READY" 2>/dev/null | grep -q SSH_READY; then
echo "SSH connection established!"
break
fi
ATTEMPT=$((ATTEMPT + 1))
if [ $ATTEMPT -eq $MAX_SSH_ATTEMPTS ]; then
echo "Failed to establish SSH connection"
exit 1
fi
echo "SSH not ready (attempt $ATTEMPT/$MAX_SSH_ATTEMPTS), waiting 30s..."
sleep 30
done
echo "Waiting for SSH setup completion..."
MAX_SETUP_ATTEMPTS=30
ATTEMPT=0
while [ $ATTEMPT -lt $MAX_SETUP_ATTEMPTS ]; do
if ssh -p $SSH_PORT -o ConnectTimeout=10 -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \
-o ServerAliveInterval=60 -o ServerAliveCountMax=10 \
-i "$KEY_PATH" Administrator@$PUBLIC_IP \
"if (Test-Path C:\\ssh_ready.txt) { echo READY } else { echo NOT_READY }" 2>/dev/null | grep -q READY; then
echo "SSH setup complete!"
break
fi
ATTEMPT=$((ATTEMPT + 1))
if [ $ATTEMPT -eq $MAX_SETUP_ATTEMPTS ]; then
echo "SSH setup timed out, continuing anyway..."
break
fi
echo "SSH setup not complete (attempt $ATTEMPT/$MAX_SETUP_ATTEMPTS), waiting 30s..."
sleep 30
done
- name: Install NVIDIA driver only (no CUDA toolkit)
run: |
echo "Installing NVIDIA driver (this will take a few minutes)..."
SSH_PORT="${{ steps.ids.outputs.SSH_PORT }}"
KEY_PATH="${{ steps.keypair.outputs.KEY_PATH }}"
scp -P $SSH_PORT -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \
-o ServerAliveInterval=60 -o ServerAliveCountMax=10 \
-i "$KEY_PATH" .github/workflows/scripts/win/install-nvidia-driver.ps1 Administrator@$PUBLIC_IP:C:/install_driver.ps1
ssh -p $SSH_PORT -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \
-o ServerAliveInterval=60 -o ServerAliveCountMax=10 \
-i "$KEY_PATH" Administrator@$PUBLIC_IP \
"powershell -ExecutionPolicy Bypass -File C:/install_driver.ps1"
- name: Create archive of repository
run: |
echo "Creating repository archive..."
git archive --format=zip --output=/tmp/repo.zip HEAD
- name: Transfer repository to instance
run: |
echo "Transferring repository to instance..."
SSH_PORT="${{ steps.ids.outputs.SSH_PORT }}"
KEY_PATH="${{ steps.keypair.outputs.KEY_PATH }}"
scp -P $SSH_PORT -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \
-o ServerAliveInterval=60 -o ServerAliveCountMax=10 \
-i "$KEY_PATH" /tmp/repo.zip Administrator@$PUBLIC_IP:C:/source.zip
ssh -p $SSH_PORT -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \
-o ServerAliveInterval=60 -o ServerAliveCountMax=10 \
-i "$KEY_PATH" Administrator@$PUBLIC_IP \
"powershell -Command \"if (Test-Path 'C:\\ppf-contact-solver') { Remove-Item -Recurse -Force 'C:\\ppf-contact-solver' }; New-Item -ItemType Directory -Path 'C:\\ppf-contact-solver' -Force; Expand-Archive -Path 'C:\\source.zip' -DestinationPath 'C:\\ppf-contact-solver' -Force; Remove-Item 'C:\\source.zip'\""
- name: Run warmup.bat
run: |
echo "Running warmup.bat..."
SSH_PORT="${{ steps.ids.outputs.SSH_PORT }}"
KEY_PATH="${{ steps.keypair.outputs.KEY_PATH }}"
ssh -p $SSH_PORT -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \
-o ServerAliveInterval=60 -o ServerAliveCountMax=10 \
-o ServerAliveInterval=60 -i "$KEY_PATH" Administrator@$PUBLIC_IP \
"cmd /c 'cd C:\\ppf-contact-solver\\build-win-native && warmup.bat /nopause'"
- name: Run build.bat
run: |
echo "Running build.bat..."
SSH_PORT="${{ steps.ids.outputs.SSH_PORT }}"
KEY_PATH="${{ steps.keypair.outputs.KEY_PATH }}"
ssh -p $SSH_PORT -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \
-o ServerAliveInterval=60 -o ServerAliveCountMax=10 \
-o ServerAliveInterval=60 -i "$KEY_PATH" Administrator@$PUBLIC_IP \
"cmd /c 'cd C:\\ppf-contact-solver\\build-win-native && build.bat /nopause'"
- name: Convert assertion notebook to Python script
run: |
echo "Converting assertion notebook: examples/fail-examples/assertion.ipynb"
SSH_PORT="${{ steps.ids.outputs.SSH_PORT }}"
KEY_PATH="${{ steps.keypair.outputs.KEY_PATH }}"
# Use the same conversion pattern as main examples
cat > /tmp/convert_assertion.ps1 << 'EOFPS1'
$ErrorActionPreference = "Stop"
Set-Location C:\ppf-contact-solver
$env:PATH = "C:\ppf-contact-solver\build-win-native\python;C:\ppf-contact-solver\build-win-native\python\Scripts;" + $env:PATH
New-Item -ItemType Directory -Path "C:\ci" -Force | Out-Null
Write-Host "Converting assertion.ipynb to Python script..."
& C:\ppf-contact-solver\build-win-native\python\python.exe -m jupyter nbconvert --to python "examples/fail-examples/assertion.ipynb" --output "C:\ci\assertion_base.py"
$header = "import sys`nimport os`nsys.path.insert(0, r'C:\ppf-contact-solver')`nsys.path.insert(0, r'C:\ppf-contact-solver\frontend')`nos.environ['PYTHONPATH'] = r'C:\ppf-contact-solver;C:\ppf-contact-solver\frontend;' + os.environ.get('PYTHONPATH', '')"
$baseContent = Get-Content "C:\ci\assertion_base.py" -Raw
$header + "`n" + $baseContent | Set-Content "C:\ci\assertion.py"
Write-Host "Assertion script prepared at C:\ci\assertion.py"
EOFPS1
scp -P $SSH_PORT -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \
-o ServerAliveInterval=60 -o ServerAliveCountMax=10 \
-i "$KEY_PATH" /tmp/convert_assertion.ps1 Administrator@$PUBLIC_IP:C:/convert_assertion.ps1
ssh -p $SSH_PORT -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \
-o ServerAliveInterval=60 -o ServerAliveCountMax=10 \
-i "$KEY_PATH" Administrator@$PUBLIC_IP \
"powershell -ExecutionPolicy Bypass -File C:/convert_assertion.ps1"
- name: Run assertion test (expect failure)
run: |
echo "Running assertion test to verify error propagation via SSH..."
echo "This test uses the same execution pattern as main examples"
echo "Expected result: FAILURE (AssertionError)"
SSH_PORT="${{ steps.ids.outputs.SSH_PORT }}"
KEY_PATH="${{ steps.keypair.outputs.KEY_PATH }}"
# Create script that runs the same way as main examples
cat > /tmp/run_assertion.ps1 << 'EOFPS1'
Set-Location C:\ppf-contact-solver
"assertion" | Set-Content "frontend\.CI"
& C:\ppf-contact-solver\build-win-native\python\python.exe C:\ci\assertion.py 2>&1 | Tee-Object -FilePath "C:\ci\assertion.log"
exit $LASTEXITCODE
EOFPS1
scp -P $SSH_PORT -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \
-o ServerAliveInterval=60 -o ServerAliveCountMax=10 \
-i "$KEY_PATH" /tmp/run_assertion.ps1 Administrator@$PUBLIC_IP:C:/run_assertion.ps1
# Run and expect failure
if ssh -p $SSH_PORT -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \
-o ServerAliveInterval=60 -o ServerAliveCountMax=10 \
-i "$KEY_PATH" Administrator@$PUBLIC_IP \
"powershell -ExecutionPolicy Bypass -File C:/run_assertion.ps1"; then
echo "ERROR: Assertion test should have failed but succeeded"
echo "This means errors are NOT being propagated correctly!"
exit 1
else
echo "SUCCESS: Assertion test failed as expected"
echo "Error propagation via SSH is working correctly"
echo "Main example tests can now proceed with confidence"
fi
- name: Convert notebook to Python script
run: |
echo "Converting cards.ipynb to Python script..."
SSH_PORT="${{ steps.ids.outputs.SSH_PORT }}"
KEY_PATH="${{ steps.keypair.outputs.KEY_PATH }}"
sed "s/EXAMPLE_PLACEHOLDER/cards/g" .github/workflows/scripts/win/convert-notebook.ps1 > /tmp/convert_notebook.ps1
scp -P $SSH_PORT -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \
-o ServerAliveInterval=60 -o ServerAliveCountMax=10 \
-i "$KEY_PATH" /tmp/convert_notebook.ps1 Administrator@$PUBLIC_IP:C:/convert_notebook.ps1
ssh -p $SSH_PORT -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \
-o ServerAliveInterval=60 -o ServerAliveCountMax=10 \
-o ServerAliveInterval=60 -i "$KEY_PATH" Administrator@$PUBLIC_IP \
"powershell -ExecutionPolicy Bypass -File C:/convert_notebook.ps1"
- name: Run 1st iteration
run: |
echo "Running 1st iteration of cards"
SSH_PORT="${{ steps.ids.outputs.SSH_PORT }}"
KEY_PATH="${{ steps.keypair.outputs.KEY_PATH }}"
sed -e "s/EXAMPLE_PLACEHOLDER/cards/g" -e "s/ITERATION_PLACEHOLDER/1st/g" -e "s/ITERATION_NUM_PLACEHOLDER/1/g" \
.github/workflows/scripts/win/run-iteration.ps1 > /tmp/run_iteration.ps1
scp -P $SSH_PORT -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \
-o ServerAliveInterval=60 -o ServerAliveCountMax=10 \
-i "$KEY_PATH" /tmp/run_iteration.ps1 Administrator@$PUBLIC_IP:C:/run_iteration.ps1
ssh -p $SSH_PORT -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \
-o ServerAliveInterval=60 -o ServerAliveCountMax=10 \
-o ServerAliveInterval=60 -i "$KEY_PATH" Administrator@$PUBLIC_IP \
"powershell -ExecutionPolicy Bypass -File C:/run_iteration.ps1"
- name: Run 2nd iteration
run: |
echo "Running 2nd iteration of cards"
SSH_PORT="${{ steps.ids.outputs.SSH_PORT }}"
KEY_PATH="${{ steps.keypair.outputs.KEY_PATH }}"
sed -e "s/EXAMPLE_PLACEHOLDER/cards/g" -e "s/ITERATION_PLACEHOLDER/2nd/g" -e "s/ITERATION_NUM_PLACEHOLDER/2/g" \
.github/workflows/scripts/win/run-iteration.ps1 > /tmp/run_iteration.ps1
scp -P $SSH_PORT -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \
-o ServerAliveInterval=60 -o ServerAliveCountMax=10 \
-i "$KEY_PATH" /tmp/run_iteration.ps1 Administrator@$PUBLIC_IP:C:/run_iteration.ps1
ssh -p $SSH_PORT -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \
-o ServerAliveInterval=60 -o ServerAliveCountMax=10 \
-o ServerAliveInterval=60 -i "$KEY_PATH" Administrator@$PUBLIC_IP \
"powershell -ExecutionPolicy Bypass -File C:/run_iteration.ps1"
- name: Run 3rd iteration
run: |
echo "Running 3rd iteration of cards"
SSH_PORT="${{ steps.ids.outputs.SSH_PORT }}"
KEY_PATH="${{ steps.keypair.outputs.KEY_PATH }}"
sed -e "s/EXAMPLE_PLACEHOLDER/cards/g" -e "s/ITERATION_PLACEHOLDER/3rd/g" -e "s/ITERATION_NUM_PLACEHOLDER/3/g" \
.github/workflows/scripts/win/run-iteration.ps1 > /tmp/run_iteration.ps1
scp -P $SSH_PORT -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \
-o ServerAliveInterval=60 -o ServerAliveCountMax=10 \
-i "$KEY_PATH" /tmp/run_iteration.ps1 Administrator@$PUBLIC_IP:C:/run_iteration.ps1
ssh -p $SSH_PORT -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \
-o ServerAliveInterval=60 -o ServerAliveCountMax=10 \
-o ServerAliveInterval=60 -i "$KEY_PATH" Administrator@$PUBLIC_IP \
"powershell -ExecutionPolicy Bypass -File C:/run_iteration.ps1"
- name: Run 4th iteration
run: |
echo "Running 4th iteration of cards"
SSH_PORT="${{ steps.ids.outputs.SSH_PORT }}"
KEY_PATH="${{ steps.keypair.outputs.KEY_PATH }}"
sed -e "s/EXAMPLE_PLACEHOLDER/cards/g" -e "s/ITERATION_PLACEHOLDER/4th/g" -e "s/ITERATION_NUM_PLACEHOLDER/4/g" \
.github/workflows/scripts/win/run-iteration.ps1 > /tmp/run_iteration.ps1
scp -P $SSH_PORT -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \
-o ServerAliveInterval=60 -o ServerAliveCountMax=10 \
-i "$KEY_PATH" /tmp/run_iteration.ps1 Administrator@$PUBLIC_IP:C:/run_iteration.ps1
ssh -p $SSH_PORT -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \
-o ServerAliveInterval=60 -o ServerAliveCountMax=10 \
-o ServerAliveInterval=60 -i "$KEY_PATH" Administrator@$PUBLIC_IP \
"powershell -ExecutionPolicy Bypass -File C:/run_iteration.ps1"
- name: Run 5th iteration
run: |
echo "Running 5th iteration of cards"
SSH_PORT="${{ steps.ids.outputs.SSH_PORT }}"
KEY_PATH="${{ steps.keypair.outputs.KEY_PATH }}"
sed -e "s/EXAMPLE_PLACEHOLDER/cards/g" -e "s/ITERATION_PLACEHOLDER/5th/g" -e "s/ITERATION_NUM_PLACEHOLDER/5/g" \
.github/workflows/scripts/win/run-iteration.ps1 > /tmp/run_iteration.ps1
scp -P $SSH_PORT -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \
-o ServerAliveInterval=60 -o ServerAliveCountMax=10 \
-i "$KEY_PATH" /tmp/run_iteration.ps1 Administrator@$PUBLIC_IP:C:/run_iteration.ps1
ssh -p $SSH_PORT -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \
-o ServerAliveInterval=60 -o ServerAliveCountMax=10 \
-o ServerAliveInterval=60 -i "$KEY_PATH" Administrator@$PUBLIC_IP \
"powershell -ExecutionPolicy Bypass -File C:/run_iteration.ps1"
- name: Run 6th iteration
run: |
echo "Running 6th iteration of cards"
SSH_PORT="${{ steps.ids.outputs.SSH_PORT }}"
KEY_PATH="${{ steps.keypair.outputs.KEY_PATH }}"
sed -e "s/EXAMPLE_PLACEHOLDER/cards/g" -e "s/ITERATION_PLACEHOLDER/6th/g" -e "s/ITERATION_NUM_PLACEHOLDER/6/g" \
.github/workflows/scripts/win/run-iteration.ps1 > /tmp/run_iteration.ps1
scp -P $SSH_PORT -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \
-o ServerAliveInterval=60 -o ServerAliveCountMax=10 \
-i "$KEY_PATH" /tmp/run_iteration.ps1 Administrator@$PUBLIC_IP:C:/run_iteration.ps1
ssh -p $SSH_PORT -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \
-o ServerAliveInterval=60 -o ServerAliveCountMax=10 \
-o ServerAliveInterval=60 -i "$KEY_PATH" Administrator@$PUBLIC_IP \
"powershell -ExecutionPolicy Bypass -File C:/run_iteration.ps1"
- name: Run 7th iteration
run: |
echo "Running 7th iteration of cards"
SSH_PORT="${{ steps.ids.outputs.SSH_PORT }}"
KEY_PATH="${{ steps.keypair.outputs.KEY_PATH }}"
sed -e "s/EXAMPLE_PLACEHOLDER/cards/g" -e "s/ITERATION_PLACEHOLDER/7th/g" -e "s/ITERATION_NUM_PLACEHOLDER/7/g" \
.github/workflows/scripts/win/run-iteration.ps1 > /tmp/run_iteration.ps1
scp -P $SSH_PORT -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \
-o ServerAliveInterval=60 -o ServerAliveCountMax=10 \
-i "$KEY_PATH" /tmp/run_iteration.ps1 Administrator@$PUBLIC_IP:C:/run_iteration.ps1
ssh -p $SSH_PORT -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \
-o ServerAliveInterval=60 -o ServerAliveCountMax=10 \
-o ServerAliveInterval=60 -i "$KEY_PATH" Administrator@$PUBLIC_IP \
"powershell -ExecutionPolicy Bypass -File C:/run_iteration.ps1"
- name: Run 8th iteration
run: |
echo "Running 8th iteration of cards"
SSH_PORT="${{ steps.ids.outputs.SSH_PORT }}"
KEY_PATH="${{ steps.keypair.outputs.KEY_PATH }}"
sed -e "s/EXAMPLE_PLACEHOLDER/cards/g" -e "s/ITERATION_PLACEHOLDER/8th/g" -e "s/ITERATION_NUM_PLACEHOLDER/8/g" \
.github/workflows/scripts/win/run-iteration.ps1 > /tmp/run_iteration.ps1
scp -P $SSH_PORT -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \
-o ServerAliveInterval=60 -o ServerAliveCountMax=10 \
-i "$KEY_PATH" /tmp/run_iteration.ps1 Administrator@$PUBLIC_IP:C:/run_iteration.ps1
ssh -p $SSH_PORT -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \
-o ServerAliveInterval=60 -o ServerAliveCountMax=10 \
-o ServerAliveInterval=60 -i "$KEY_PATH" Administrator@$PUBLIC_IP \
"powershell -ExecutionPolicy Bypass -File C:/run_iteration.ps1"
- name: Run 9th iteration
run: |
echo "Running 9th iteration of cards"
SSH_PORT="${{ steps.ids.outputs.SSH_PORT }}"
KEY_PATH="${{ steps.keypair.outputs.KEY_PATH }}"
sed -e "s/EXAMPLE_PLACEHOLDER/cards/g" -e "s/ITERATION_PLACEHOLDER/9th/g" -e "s/ITERATION_NUM_PLACEHOLDER/9/g" \
.github/workflows/scripts/win/run-iteration.ps1 > /tmp/run_iteration.ps1
scp -P $SSH_PORT -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \
-o ServerAliveInterval=60 -o ServerAliveCountMax=10 \
-i "$KEY_PATH" /tmp/run_iteration.ps1 Administrator@$PUBLIC_IP:C:/run_iteration.ps1
ssh -p $SSH_PORT -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \
-o ServerAliveInterval=60 -o ServerAliveCountMax=10 \
-o ServerAliveInterval=60 -i "$KEY_PATH" Administrator@$PUBLIC_IP \
"powershell -ExecutionPolicy Bypass -File C:/run_iteration.ps1"
- name: Run 10th iteration
run: |
echo "Running 10th iteration of cards"
SSH_PORT="${{ steps.ids.outputs.SSH_PORT }}"
KEY_PATH="${{ steps.keypair.outputs.KEY_PATH }}"
sed -e "s/EXAMPLE_PLACEHOLDER/cards/g" -e "s/ITERATION_PLACEHOLDER/10th/g" -e "s/ITERATION_NUM_PLACEHOLDER/10/g" \
.github/workflows/scripts/win/run-iteration.ps1 > /tmp/run_iteration.ps1
scp -P $SSH_PORT -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \
-o ServerAliveInterval=60 -o ServerAliveCountMax=10 \
-i "$KEY_PATH" /tmp/run_iteration.ps1 Administrator@$PUBLIC_IP:C:/run_iteration.ps1
ssh -p $SSH_PORT -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \
-o ServerAliveInterval=60 -o ServerAliveCountMax=10 \
-o ServerAliveInterval=60 -i "$KEY_PATH" Administrator@$PUBLIC_IP \
"powershell -ExecutionPolicy Bypass -File C:/run_iteration.ps1"
- name: Collect results
if: success() || failure()
run: |
echo "Collecting results..."
SSH_PORT="${{ steps.ids.outputs.SSH_PORT }}"
KEY_PATH="${{ steps.keypair.outputs.KEY_PATH }}"
mkdir -p ci
# Delete large binary files on remote before copying to save bandwidth
# CI output is in project-relative cache: C:\ppf-contact-solver\cache\ppf-cts\ci
ssh -p $SSH_PORT -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \
-o ServerAliveInterval=60 -o ServerAliveCountMax=10 \
-i "$KEY_PATH" Administrator@$PUBLIC_IP \
"powershell -Command \"Get-ChildItem -Path C:\\ppf-contact-solver\\cache\\ppf-cts\\ci -Recurse -Include '*.bin','*.pickle','*.ply','*.gz' -ErrorAction SilentlyContinue | Remove-Item -Force\"" || true
# Copy CI output from ppf-cts cache directory
scp -P $SSH_PORT -r -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \
-o ServerAliveInterval=60 -o ServerAliveCountMax=10 \
-i "$KEY_PATH" "Administrator@$PUBLIC_IP:C:/ppf-contact-solver/cache/ppf-cts/ci/*" ./ci/ || echo "No ppf-cts CI files found"
# Also copy logs and scripts from C:\ci
scp -P $SSH_PORT -r -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \
-o ServerAliveInterval=60 -o ServerAliveCountMax=10 \
-i "$KEY_PATH" "Administrator@$PUBLIC_IP:C:/ci/*" ./ci/ || echo "No script/log files found"
echo "## Collected Files:"
ls -laR ci/ || echo "No files collected"
- name: Upload artifact
if: success() || failure()
uses: actions/upload-artifact@v4
with:
name: ci-win-cards
path: ci
retention-days: 3
- name: GPU information
if: success() || failure()
run: |
SSH_PORT="${{ steps.ids.outputs.SSH_PORT }}"
KEY_PATH="${{ steps.keypair.outputs.KEY_PATH }}"
ssh -p $SSH_PORT -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \
-o ServerAliveInterval=60 -o ServerAliveCountMax=10 \
-i "$KEY_PATH" Administrator@$PUBLIC_IP "nvidia-smi" || echo "Failed to get GPU info"
- name: Re-authenticate for cleanup
if: always()
continue-on-error: true
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ secrets.AWS_ROLE_ARN }}
aws-region: ${{ env.AWS_REGION }}
- name: Cleanup - Terminate Instance
if: always()
continue-on-error: true
run: |
if [ -n "${{ steps.instance.outputs.INSTANCE_ID }}" ]; then
echo "Terminating instance: ${{ steps.instance.outputs.INSTANCE_ID }}"
aws ec2 terminate-instances \
--instance-ids "${{ steps.instance.outputs.INSTANCE_ID }}" \
--region "$AWS_REGION" || true
fi
- name: Cleanup - Remove Ingress Rules
if: always()
continue-on-error: true
run: |
if [ -n "${{ steps.security-group.outputs.SG_ID }}" ] && [ -n "${{ steps.security-group.outputs.RUNNER_IP_CIDR }}" ]; then
echo "Removing ingress rules..."
aws ec2 revoke-security-group-ingress \
--group-id "${{ steps.security-group.outputs.SG_ID }}" \
--ip-permissions \
"IpProtocol=tcp,FromPort=${{ steps.security-group.outputs.SSH_PORT }},ToPort=${{ steps.security-group.outputs.SSH_PORT }},IpRanges=[{CidrIp=${{ steps.security-group.outputs.RUNNER_IP_CIDR }}}]" \
--region "$AWS_REGION" 2>&1 || echo "Rule may have been removed"
fi
- name: Cleanup - Remove Local SSH Key
if: always()
continue-on-error: true
run: |
rm -f "${{ steps.keypair.outputs.KEY_PATH }}"
- name: Summary
if: always()
run: |
echo "## Workflow Summary"
echo "- Example: cards"
echo "- Region: $AWS_REGION"
echo "- Instance Type: $INSTANCE_TYPE"
echo "- Branch: $BRANCH"
echo "- Instance ID: ${{ steps.instance.outputs.INSTANCE_ID || 'Not launched' }}"