Skip to content

Capture Gateway Screen #4

Capture Gateway Screen

Capture Gateway Screen #4

name: Capture Gateway Screen
on:
workflow_dispatch:
inputs:
screen_action:
description: Optional interaction before capturing the screen
required: false
default: capture-only
type: choice
options:
- capture-only
- resend-notification
- challenge-response
- qr-code
env:
GCP_PROJECT_ID: interactivebrokersquant
GCP_WORKLOAD_IDENTITY_PROVIDER: projects/303168642265/locations/global/workloadIdentityPools/github-actions/providers/github-ibkr-gateway-main
GCP_WORKLOAD_IDENTITY_SERVICE_ACCOUNT: ibkr-gateway-deploy@interactivebrokersquant.iam.gserviceaccount.com
jobs:
capture:
runs-on: ubuntu-latest
timeout-minutes: 8
permissions:
contents: read
id-token: write
env:
GCE_USER: ${{ vars.IB_GATEWAY_GCE_USER }}
GCE_INSTANCE_NAME: ${{ vars.IB_GATEWAY_INSTANCE_NAME }}
GCE_ZONE: ${{ vars.IB_GATEWAY_ZONE }}
SSH_PRIVATE_KEY_SECRET_NAME: ${{ vars.IB_GATEWAY_SSH_PRIVATE_KEY_SECRET_NAME }}
SCREEN_ACTION: ${{ github.event.inputs.screen_action }}
steps:
- name: Authenticate to Google Cloud
uses: google-github-actions/auth@v3
with:
workload_identity_provider: ${{ env.GCP_WORKLOAD_IDENTITY_PROVIDER }}
service_account: ${{ env.GCP_WORKLOAD_IDENTITY_SERVICE_ACCOUNT }}
- name: Set up gcloud
uses: google-github-actions/setup-gcloud@v3
with:
project_id: ${{ env.GCP_PROJECT_ID }}
version: '>= 416.0.0'
- name: Prepare SSH key
run: |
set -euo pipefail
if [ -z "${SSH_PRIVATE_KEY_SECRET_NAME:-}" ]; then
echo "SSH_PRIVATE_KEY_SECRET_NAME is required." >&2
exit 1
fi
install -d -m 700 "$RUNNER_TEMP/ssh"
SSH_KEY_FILE="$RUNNER_TEMP/ssh/google_compute_engine"
ssh_private_key="$(gcloud secrets versions access latest \
--project "${GCP_PROJECT_ID}" \
--secret "${SSH_PRIVATE_KEY_SECRET_NAME}")"
printf '%s\n' "$ssh_private_key" | tr -d '\r' > "$SSH_KEY_FILE"
chmod 600 "$SSH_KEY_FILE"
ssh-keygen -y -f "$SSH_KEY_FILE" > "$SSH_KEY_FILE.pub"
chmod 644 "$SSH_KEY_FILE.pub"
echo "SSH_KEY_FILE=$SSH_KEY_FILE" >> "$GITHUB_ENV"
- name: Capture X11 screen
run: |
set -euo pipefail
for var_name in GCE_USER GCE_INSTANCE_NAME GCE_ZONE; do
if [ -z "${!var_name:-}" ]; then
echo "${var_name} is required." >&2
exit 1
fi
done
REMOTE_TARGET="${GCE_USER}@${GCE_INSTANCE_NAME}"
SSH_FLAGS=(
--project "${GCP_PROJECT_ID}"
--zone "${GCE_ZONE}"
--quiet
--tunnel-through-iap
--ssh-key-file "${SSH_KEY_FILE}"
--ssh-flag="-o ServerAliveInterval=30"
--ssh-flag="-o ServerAliveCountMax=4"
--ssh-flag="-o TCPKeepAlive=yes"
)
SCP_FLAGS=(
--project "${GCP_PROJECT_ID}"
--zone "${GCE_ZONE}"
--quiet
--tunnel-through-iap
--ssh-key-file "${SSH_KEY_FILE}"
--scp-flag="-o ServerAliveInterval=30"
--scp-flag="-o ServerAliveCountMax=4"
--scp-flag="-o TCPKeepAlive=yes"
)
remote_png="/tmp/ibkr-gateway-screen.png"
local_png="$RUNNER_TEMP/ibkr-gateway-screen.png"
REMOTE_COMMAND=$(cat <<'EOF'
set -euo pipefail
sudo docker exec -u root ib-gateway bash -lc '
set -euo pipefail
export DEBIAN_FRONTEND=noninteractive
if ! command -v xdotool >/dev/null 2>&1 || ! command -v xwd >/dev/null 2>&1 || ! command -v xwdtopnm >/dev/null 2>&1 || ! command -v pnmtopng >/dev/null 2>&1; then
apt-get update
apt-get install -y --no-install-recommends xdotool x11-apps netpbm
apt-get clean
rm -rf /var/lib/apt/lists/*
fi
export DISPLAY=:1
screen_action=__SCREEN_ACTION__
if [ "$screen_action" != "capture-only" ]; then
window_id=$(xdotool search --name "Second Factor Authentication" 2>/dev/null | tail -n 1 || true)
if [ -z "$window_id" ]; then
echo "Second Factor Authentication window not found." >&2
exit 1
fi
geometry=$(xdotool getwindowgeometry --shell "$window_id")
width=$(printf "%s\n" "$geometry" | awk -F= "/^WIDTH=/{print \\$2}")
height=$(printf "%s\n" "$geometry" | awk -F= "/^HEIGHT=/{print \\$2}")
case "$screen_action" in
resend-notification)
click_x=$((width * 42 / 100))
click_y=$((height * 78 / 100))
;;
challenge-response)
click_x=$((width * 48 / 100))
click_y=$((height * 83 / 100))
;;
qr-code)
click_x=$((width * 42 / 100))
click_y=$((height * 87 / 100))
;;
*)
echo "Unsupported screen action: $screen_action" >&2
exit 1
;;
esac
xdotool windowactivate --sync "$window_id"
xdotool mousemove --window "$window_id" "$click_x" "$click_y"
xdotool click 1
sleep 3
fi
xwd -root -silent -out /tmp/ibkr-gateway-screen.xwd
xwdtopnm /tmp/ibkr-gateway-screen.xwd | pnmtopng > /tmp/ibkr-gateway-screen.png
chmod 0644 /tmp/ibkr-gateway-screen.png
'
sudo docker cp ib-gateway:/tmp/ibkr-gateway-screen.png /tmp/ibkr-gateway-screen.png
sudo chmod 0644 /tmp/ibkr-gateway-screen.png
ls -lh /tmp/ibkr-gateway-screen.png
EOF
)
screen_action_quoted="$(printf '%q' "${SCREEN_ACTION:-capture-only}")"
REMOTE_COMMAND="${REMOTE_COMMAND/__SCREEN_ACTION__/${screen_action_quoted}}"
gcloud compute ssh "${REMOTE_TARGET}" "${SSH_FLAGS[@]}" --command "${REMOTE_COMMAND}"
gcloud compute scp "${REMOTE_TARGET}:${remote_png}" "${local_png}" "${SCP_FLAGS[@]}"
echo "SCREENSHOT_PATH=${local_png}" >> "$GITHUB_ENV"
- name: Upload screenshot
uses: actions/upload-artifact@v4
with:
name: ibkr-gateway-screen
path: ${{ env.SCREENSHOT_PATH }}
if-no-files-found: error