ci: add ability to debug SSH sessions in CI (#47875)
Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
This commit is contained in:
parent
1bf39c1341
commit
2d855cd680
7 changed files with 214 additions and 0 deletions
20
.github/actions/ssh-debug/action.yml
vendored
Normal file
20
.github/actions/ssh-debug/action.yml
vendored
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
name: Debug via SSH
|
||||||
|
description: Setup a SSH server with a tunnel to access it to debug via SSH.
|
||||||
|
inputs:
|
||||||
|
tunnel:
|
||||||
|
description: 'Enable SSH tunneling via cloudflared'
|
||||||
|
required: true
|
||||||
|
default: 'false'
|
||||||
|
timeout:
|
||||||
|
description: 'SSH session timeout in minutes'
|
||||||
|
required: false
|
||||||
|
type: number
|
||||||
|
default: 60
|
||||||
|
runs:
|
||||||
|
using: composite
|
||||||
|
steps:
|
||||||
|
- run: $GITHUB_ACTION_PATH/setup-ssh.sh
|
||||||
|
shell: bash
|
||||||
|
env:
|
||||||
|
TUNNEL: ${{ inputs.tunnel }}
|
||||||
|
TIMEOUT: ${{ inputs.timeout }}
|
4
.github/actions/ssh-debug/bashrc
vendored
Normal file
4
.github/actions/ssh-debug/bashrc
vendored
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
# If we're in an interactive SSH session and we're not already in tmux and there's no explicit SSH command, auto attach tmux
|
||||||
|
if [ -n "$SSH_TTY" ] && [ -z "$TMUX" ] && [ -z "$SSH_ORIGINAL_COMMAND" ]; then
|
||||||
|
exec tmux attach || exec tmux
|
||||||
|
fi
|
140
.github/actions/ssh-debug/setup-ssh.sh
vendored
Executable file
140
.github/actions/ssh-debug/setup-ssh.sh
vendored
Executable file
|
@ -0,0 +1,140 @@
|
||||||
|
#!/bin/bash -e
|
||||||
|
|
||||||
|
get_authorized_keys() {
|
||||||
|
if [ -z "$AUTHORIZED_USERS" ] || ! echo "$AUTHORIZED_USERS" | grep -q "\b$GITHUB_ACTOR\b"; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
api_response=$(curl -s "https://api.github.com/users/$GITHUB_ACTOR/keys")
|
||||||
|
|
||||||
|
if echo "$api_response" | jq -e 'type == "object" and has("message")' >/dev/null; then
|
||||||
|
error_msg=$(echo "$api_response" | jq -r '.message')
|
||||||
|
echo "Error: $error_msg"
|
||||||
|
return 1
|
||||||
|
else
|
||||||
|
echo "$api_response" | jq -r '.[].key'
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
authorized_keys=$(get_authorized_keys "$GITHUB_ACTOR")
|
||||||
|
|
||||||
|
if [ -n "$authorized_keys" ]; then
|
||||||
|
echo "Configured SSH key(s) for user: $GITHUB_ACTOR"
|
||||||
|
else
|
||||||
|
echo "Error: User '$GITHUB_ACTOR' is not authorized to access this debug session."
|
||||||
|
echo "Authorized users: $AUTHORIZED_USERS"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$TUNNEL" != "true" ]; then
|
||||||
|
echo "SSH tunneling is disabled. Set enable-tunnel: true to enable remote access."
|
||||||
|
echo "Local SSH server would be available on localhost:2222 if this were a local environment."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "SSH tunneling enabled. Setting up remote access..."
|
||||||
|
|
||||||
|
EXTERNAL_DEPS="curl jq ssh-keygen"
|
||||||
|
|
||||||
|
for dep in $EXTERNAL_DEPS; do
|
||||||
|
if ! command -v "$dep" > /dev/null 2>&1; then
|
||||||
|
echo "Command $dep not installed on the system!" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
cd "$GITHUB_ACTION_PATH"
|
||||||
|
|
||||||
|
bashrc_path=$(pwd)/bashrc
|
||||||
|
|
||||||
|
# Source `bashrc` to auto start tmux on SSH login.
|
||||||
|
if ! grep -q "$bashrc_path" ~/.bash_profile; then
|
||||||
|
echo >> ~/.bash_profile # On macOS runner there's no newline at the end of the file
|
||||||
|
echo "source \"$bashrc_path\"" >> ~/.bash_profile
|
||||||
|
fi
|
||||||
|
|
||||||
|
OS=$(uname -s | tr '[:upper:]' '[:lower:]')
|
||||||
|
ARCH=$(uname -m)
|
||||||
|
|
||||||
|
if [ "$ARCH" = "x86_64" ]; then
|
||||||
|
ARCH="amd64"
|
||||||
|
elif [ "$ARCH" = "aarch64" ]; then
|
||||||
|
ARCH="arm64"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Install tmux on macOS runners if not present.
|
||||||
|
if [ "$OS" = "darwin" ] && ! command -v tmux > /dev/null 2>&1; then
|
||||||
|
echo "Installing tmux..."
|
||||||
|
brew install tmux
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$OS" = "darwin" ]; then
|
||||||
|
cloudflared_url="https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-${OS}-${ARCH}.tgz"
|
||||||
|
echo "Downloading \`cloudflared\` from <$cloudflared_url>..."
|
||||||
|
curl --location --silent --output cloudflared.tgz "$cloudflared_url"
|
||||||
|
tar xf cloudflared.tgz
|
||||||
|
rm cloudflared.tgz
|
||||||
|
else
|
||||||
|
cloudflared_url="https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-${OS}-${ARCH}"
|
||||||
|
echo "Downloading \`cloudflared\` from <$cloudflared_url>..."
|
||||||
|
curl --location --silent --output cloudflared "$cloudflared_url"
|
||||||
|
fi
|
||||||
|
|
||||||
|
chmod +x cloudflared
|
||||||
|
|
||||||
|
echo "Setting up SSH key for authorized user: $GITHUB_ACTOR"
|
||||||
|
echo "$authorized_keys" > authorized_keys
|
||||||
|
|
||||||
|
echo 'Creating SSH server key...'
|
||||||
|
ssh-keygen -q -f ssh_host_rsa_key -N ''
|
||||||
|
|
||||||
|
echo 'Creating SSH server config...'
|
||||||
|
sed "s,\$PWD,$PWD,;s,\$USER,$USER," sshd_config.template > sshd_config
|
||||||
|
|
||||||
|
echo 'Starting SSH server...'
|
||||||
|
/usr/sbin/sshd -f sshd_config -D &
|
||||||
|
sshd_pid=$!
|
||||||
|
|
||||||
|
echo 'Starting tmux session...'
|
||||||
|
(cd "$GITHUB_WORKSPACE" && tmux new-session -d -s debug)
|
||||||
|
|
||||||
|
#if no cloudflare tunnel token is provided, exit
|
||||||
|
if [ -z "$CLOUDFLARE_TUNNEL_TOKEN" ]; then
|
||||||
|
echo "Error: required CLOUDFLARE_TUNNEL_TOKEN not found"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo 'Starting Cloudflare tunnel...'
|
||||||
|
|
||||||
|
./cloudflared tunnel --no-autoupdate run --token "$CLOUDFLARE_TUNNEL_TOKEN" 2>&1 | tee cloudflared.log | sed -u 's/^/cloudflared: /' &
|
||||||
|
cloudflared_pid=$!
|
||||||
|
|
||||||
|
url="$TUNNEL_HOSTNAME"
|
||||||
|
|
||||||
|
public_key=$(cut -d' ' -f1,2 < ssh_host_rsa_key.pub)
|
||||||
|
|
||||||
|
(
|
||||||
|
echo ' '
|
||||||
|
echo ' '
|
||||||
|
echo '🔗 SSH Debug Session Ready!'
|
||||||
|
echo '━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'
|
||||||
|
echo ' '
|
||||||
|
echo '📋 Copy and run this command to connect:'
|
||||||
|
echo ' '
|
||||||
|
if [ -n "$TUNNEL_HOSTNAME" ]; then
|
||||||
|
echo "ssh-keygen -R action-ssh-debug && echo 'action-ssh-debug $public_key' >> ~/.ssh/known_hosts && ssh -o ProxyCommand='cloudflared access tcp --hostname $url' runner@action-ssh-debug"
|
||||||
|
else
|
||||||
|
echo "ssh-keygen -R action-ssh-debug && echo 'action-ssh-debug $public_key' >> ~/.ssh/known_hosts && ssh -o ProxyCommand='cloudflared access tcp --hostname $url' runner@action-ssh-debug"
|
||||||
|
fi
|
||||||
|
echo ' '
|
||||||
|
echo "⏰ Session expires automatically in $TIMEOUT minutes"
|
||||||
|
echo '━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'
|
||||||
|
echo ' '
|
||||||
|
echo ' '
|
||||||
|
) | cat
|
||||||
|
|
||||||
|
echo 'Starting SSH session in background...'
|
||||||
|
./ssh-session.sh "$sshd_pid" "$cloudflared_pid" $TIMEOUT &
|
||||||
|
|
||||||
|
echo 'SSH session is running in background. GitHub Action will continue.'
|
||||||
|
echo 'Session will auto-cleanup after timeout or when processes end.'
|
21
.github/actions/ssh-debug/ssh-session.sh
vendored
Executable file
21
.github/actions/ssh-debug/ssh-session.sh
vendored
Executable file
|
@ -0,0 +1,21 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
SSHD_PID=$1
|
||||||
|
CLOUDFLARED_PID=$2
|
||||||
|
SESSION_TIMEOUT=${3:-3600}
|
||||||
|
|
||||||
|
# Wait for timeout or until processes die.
|
||||||
|
sleep "$SESSION_TIMEOUT" &
|
||||||
|
SLEEP_PID=$!
|
||||||
|
|
||||||
|
# Monitor if SSH or cloudflared dies early.
|
||||||
|
while kill -0 "$SSHD_PID" 2>/dev/null && kill -0 "$CLOUDFLARED_PID" 2>/dev/null && kill -0 "$SLEEP_PID" 2>/dev/null; do
|
||||||
|
sleep 10
|
||||||
|
done
|
||||||
|
|
||||||
|
# Cleanup.
|
||||||
|
kill "$SLEEP_PID" 2>/dev/null || true
|
||||||
|
kill "$SSHD_PID" 2>/dev/null || true
|
||||||
|
kill "$CLOUDFLARED_PID" 2>/dev/null || true
|
||||||
|
|
||||||
|
echo "SSH session ended"
|
9
.github/actions/ssh-debug/sshd_config.template
vendored
Normal file
9
.github/actions/ssh-debug/sshd_config.template
vendored
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
Port 2222
|
||||||
|
HostKey $PWD/ssh_host_rsa_key
|
||||||
|
PidFile $PWD/sshd.pid
|
||||||
|
|
||||||
|
# Only allow single user
|
||||||
|
AllowUsers $USER
|
||||||
|
|
||||||
|
# Only allow those keys
|
||||||
|
AuthorizedKeysFile $PWD/authorized_keys
|
|
@ -73,6 +73,7 @@ env:
|
||||||
SUDOWOODO_EXCHANGE_TOKEN: ${{ secrets.SUDOWOODO_EXCHANGE_TOKEN }}
|
SUDOWOODO_EXCHANGE_TOKEN: ${{ secrets.SUDOWOODO_EXCHANGE_TOKEN }}
|
||||||
GCLIENT_EXTRA_ARGS: ${{ inputs.target-platform == 'macos' && '--custom-var=checkout_mac=True --custom-var=host_os=mac' || inputs.target-platform == 'win' && '--custom-var=checkout_win=True' || '--custom-var=checkout_arm=True --custom-var=checkout_arm64=True' }}
|
GCLIENT_EXTRA_ARGS: ${{ inputs.target-platform == 'macos' && '--custom-var=checkout_mac=True --custom-var=host_os=mac' || inputs.target-platform == 'win' && '--custom-var=checkout_win=True' || '--custom-var=checkout_arm=True --custom-var=checkout_arm64=True' }}
|
||||||
ELECTRON_OUT_DIR: Default
|
ELECTRON_OUT_DIR: Default
|
||||||
|
ACTIONS_STEP_DEBUG: ${{ secrets.ACTIONS_STEP_DEBUG }}
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
|
@ -94,6 +95,15 @@ jobs:
|
||||||
path: src/electron
|
path: src/electron
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
ref: ${{ github.event.pull_request.head.sha }}
|
ref: ${{ github.event.pull_request.head.sha }}
|
||||||
|
- name: Setup SSH Debugging
|
||||||
|
if: ${{ inputs.target-platform == 'macos' && env.ACTIONS_STEP_DEBUG == 'true' }}
|
||||||
|
uses: ./src/electron/.github/actions/ssh-debug
|
||||||
|
with:
|
||||||
|
tunnel: 'true'
|
||||||
|
env:
|
||||||
|
CLOUDFLARE_TUNNEL_TOKEN: ${{ secrets.CLOUDFLARE_TUNNEL_TOKEN }}
|
||||||
|
TUNNEL_HOSTNAME: ${{ secrets.CLOUDFLARED_SSH_HOSTNAME }}
|
||||||
|
AUTHORIZED_USERS: ${{ secrets.SSH_DEBUG_AUTHORIZED_USERS }}
|
||||||
- name: Free up space (macOS)
|
- name: Free up space (macOS)
|
||||||
if: ${{ inputs.target-platform == 'macos' }}
|
if: ${{ inputs.target-platform == 'macos' }}
|
||||||
uses: ./src/electron/.github/actions/free-space-macos
|
uses: ./src/electron/.github/actions/free-space-macos
|
||||||
|
|
|
@ -40,6 +40,7 @@ env:
|
||||||
CHROMIUM_GIT_COOKIE_WINDOWS_STRING: ${{ secrets.CHROMIUM_GIT_COOKIE_WINDOWS_STRING }}
|
CHROMIUM_GIT_COOKIE_WINDOWS_STRING: ${{ secrets.CHROMIUM_GIT_COOKIE_WINDOWS_STRING }}
|
||||||
ELECTRON_OUT_DIR: Default
|
ELECTRON_OUT_DIR: Default
|
||||||
ELECTRON_RBE_JWT: ${{ secrets.ELECTRON_RBE_JWT }}
|
ELECTRON_RBE_JWT: ${{ secrets.ELECTRON_RBE_JWT }}
|
||||||
|
ACTIONS_STEP_DEBUG: ${{ secrets.ACTIONS_STEP_DEBUG }}
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
test:
|
test:
|
||||||
|
@ -124,6 +125,15 @@ jobs:
|
||||||
path: src/electron
|
path: src/electron
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
ref: ${{ github.event.pull_request.head.sha }}
|
ref: ${{ github.event.pull_request.head.sha }}
|
||||||
|
- name: Setup SSH Debugging
|
||||||
|
if: ${{ inputs.target-platform == 'macos' && env.ACTIONS_STEP_DEBUG == 'true' }}
|
||||||
|
uses: ./src/electron/.github/actions/ssh-debug
|
||||||
|
with:
|
||||||
|
tunnel: 'true'
|
||||||
|
env:
|
||||||
|
CLOUDFLARE_TUNNEL_TOKEN: ${{ secrets.CLOUDFLARE_TUNNEL_TOKEN }}
|
||||||
|
TUNNEL_HOSTNAME: ${{ secrets.CLOUDFLARED_SSH_HOSTNAME }}
|
||||||
|
AUTHORIZED_USERS: ${{ secrets.SSH_DEBUG_AUTHORIZED_USERS }}
|
||||||
- name: Install Dependencies
|
- name: Install Dependencies
|
||||||
uses: ./src/electron/.github/actions/install-dependencies
|
uses: ./src/electron/.github/actions/install-dependencies
|
||||||
- name: Set Chromium Git Cookie
|
- name: Set Chromium Git Cookie
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue