name: Start Fly Runner on: workflow_call: outputs: runner-ready: description: "Whether the runner is ready" value: ${{ jobs.start-runner.outputs.runner-ready }} machine-id: description: "The Fly machine ID that was started" value: ${{ jobs.start-runner.outputs.machine-id }} runner-labels: description: "Labels to target the self-hosted runner" value: ${{ jobs.start-runner.outputs.runner-labels }} runner-name: description: "Resolved GitHub runner name" value: ${{ jobs.start-runner.outputs.runner-name }} env: FLY_RUNNER_APP: ivysaur FLY_RUNNER_REGION: iad FLY_RUNNER_IMAGE: registry.fly.io/ivysaur:latest jobs: start-runner: name: Start Fly Runner runs-on: ubuntu-latest permissions: actions: read contents: read outputs: runner-ready: ${{ steps.wait-for-runner.outputs.ready }} machine-id: ${{ steps.machine-management.outputs.machine-id }} runner-labels: ${{ steps.generate-labels.outputs.labels }} runner-name: ${{ steps.wait-for-runner.outputs.runner-name }} steps: - name: Checkout code uses: actions/checkout@v4 - name: Generate unique runner labels id: generate-labels run: | UNIQUE_LABELS='"self-hosted","${{ github.run_id }}"' echo "Generated unique labels: $UNIQUE_LABELS" echo "labels=$UNIQUE_LABELS" >> "$GITHUB_OUTPUT" - name: Setup Fly CLI uses: superfly/flyctl-actions/setup-flyctl@master - name: Get Fly app info id: get-app-info env: FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN_TESTING }} run: | echo "Getting app info for ${FLY_RUNNER_APP}..." flyctl status -a "${FLY_RUNNER_APP}" - name: Set GH_TOKEN in Fly secrets env: FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN_TESTING }} run: | echo "Setting GH_TOKEN in Fly secrets..." flyctl secrets set GH_TOKEN="${{ secrets.GH_TOKEN_ACTIONS }}" -a "${FLY_RUNNER_APP}" - name: Create Fly machine id: machine-management env: FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN_TESTING }} run: | set -euo pipefail # Always create a fresh ephemeral machine FULL_OUTPUT=$(flyctl machines run "${FLY_RUNNER_IMAGE}" \ -a "${FLY_RUNNER_APP}" \ --region "${FLY_RUNNER_REGION}" \ --env RUN_ID=${{ github.run_id }} \ --env TEST_TYPE="honcho-unified-runner" \ --vm-size shared-cpu-8x \ --vm-memory 8192 ) MACHINE_ID=$(echo "$FULL_OUTPUT" | grep "Machine ID:" | awk '{print $3}') echo "Created machine: $MACHINE_ID" echo "machine-id=$MACHINE_ID" >> "$GITHUB_OUTPUT" - name: Wait for runner to be online id: wait-for-runner env: GITHUB_TOKEN: ${{ secrets.GH_TOKEN_ACTIONS }} MAX_WAIT: 420 run: | set -euo pipefail if [ -z "${GITHUB_TOKEN}" ]; then echo "GH_TOKEN secret is required to poll the Actions runner API." exit 1 fi EXPECTED_RUNNER_NAME="honcho-unified-runner-${{ github.run_id }}" echo "Waiting for runner named ${EXPECTED_RUNNER_NAME} to come online..." WAITED=0 RUNNER_NAME="" while [ $WAITED -lt $MAX_WAIT ]; do RESPONSE=$(curl -s \ -H "Authorization: Bearer ${GITHUB_TOKEN}" \ -H "Accept: application/vnd.github.v3+json" \ "https://api.github.com/repos/${{ github.repository }}/actions/runners") if echo "$RESPONSE" | grep -q '"message"'; then echo "API Error: $(echo "$RESPONSE" | jq -r '.message')" exit 1 fi # Find runner with exact name and is online and not busy RUNNER_LINE=$(echo "$RESPONSE" | jq -r --arg runner_name "$EXPECTED_RUNNER_NAME" '.runners[]? | select(.name == $runner_name) | select(.status == "online") | select(.busy == false) | "\(.name)|\(.id)"' | head -n 1) if [ -n "$RUNNER_LINE" ]; then RUNNER_NAME=$(echo "$RUNNER_LINE" | cut -d'|' -f1) echo "✅ Found runner: ${RUNNER_NAME}" echo "ready=true" >> "$GITHUB_OUTPUT" echo "runner-name=${RUNNER_NAME}" >> "$GITHUB_OUTPUT" exit 0 fi echo "⏳ Waiting for runner... (${WAITED}s elapsed)" sleep 15 WAITED=$((WAITED + 15)) done echo "Runner failed to come online within ${MAX_WAIT} seconds" echo "ready=false" >> "$GITHUB_OUTPUT" exit 1