8000 ci: prefer local Mac runner with offline cloud fallback by hmmhmmhm · Pull Request #13 · hmmhmmhm/tmux-attach-browser · GitHub
[go: up one dir, main page]

Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,17 @@ permissions:
contents: read

jobs:
select-runner:
uses: ./.github/workflows/select-local-runner.yml
with:
cloud-runner: macos-latest
secrets:
status-token: ${{ secrets.LOCAL_RUNNER_STATUS_TOKEN }}

test:
name: Test (${{ matrix.os }})
runs-on: ${{ matrix.os }}
needs: select-runner
runs-on: ${{ matrix.os == 'macos-latest' && fromJSON(needs.select-runner.outputs.runner) || matrix.os }}
strategy:
fail-fast: false
matrix:
Expand Down
56 changes: 56 additions & 0 deletions .github/workflows/local-runner-smoke.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
name: Local runner smoke

on:
push:
branches: [local-runner-fallback]
paths:
- .github/workflows/select-local-runner.yml
- .github/workflows/local-runner-smoke.yml
workflow_dispatch:
inputs:
force-cloud:
description: Exercise the cloud runner without stopping the Mac service
type: boolean
default: false
probe-local:
description: Owner-only installation check that requires the Mac to be online
type: boolean
default: false

permissions:
contents: read

jobs:
select-runner:
uses: ./.github/workflows/select-local-runner.yml
with:
force-cloud: ${{ inputs.force-cloud || false }}
secrets:
status-token: ${{ secrets.LOCAL_RUNNER_STATUS_TOKEN }}
smoke:
needs: select-runner
runs-on: ${{ inputs.probe-local && github.actor == github.repository_owner && fromJSON('["self-hosted", "macOS", "ARM64", "personal-mac"]') || fromJSON(needs.select-runner.outputs.runner) }}
timeout-minutes: 10
steps:
- name: Check selected runner
shell: bash
env:
RUNNER_REASON: ${{ needs.select-runner.outputs.reason }}
EXPECT_LOCAL: ${{ (inputs.probe-local && github.actor == github.repository_owner) || needs.select-runner.outputs.local == 'true' }}
RUNNER_ENVIRONMENT: ${{ runner.environment }}
ACTUAL_RUNNER_NAME: ${{ runner.name }}
ACTUAL_RUNNER_OS: ${{ runner.os }}
ACTUAL_RUNNER_ARCH: ${{ runner.arch }}
run: |
echo "Selection: $RUNNER_REASON"
echo "Runner: $ACTUAL_RUNNER_NAME ($ACTUAL_RUNNER_OS/$ACTUAL_RUNNER_ARCH)"
if [ "$EXPECT_LOCAL" = "true" ]; then
test "$RUNNER_ENVIRONMENT" = "self-hosted"
test "$ACTUAL_RUNNER_OS" = "macOS"
test "$ACTUAL_RUNNER_ARCH" = "ARM64"
else
test "$RUNNER_ENVIRONMENT" = "github-hosted"
fi
printf 'Selection: %s\nRunner: %s (%s/%s)\n' \
"$RUNNER_REASON" "$ACTUAL_RUNNER_NAME" "$ACTUAL_RUNNER_OS" "$ACTUAL_RUNNER_ARCH" \
>> "$GITHUB_STEP_SUMMARY"
84 changes: 84 additions & 0 deletions .github/workflows/select-local-runner.yml
8C32
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
name: Select local runner

on:
workflow_call:
inputs:
cloud-runner:
type: string
default: ubuntu-latest
force-cloud:
type: boolean
default: false
secrets:
status-token:
required: false
outputs:
runner:
value: ${{ jobs.select.outputs.runner }}
local:
value: ${{ jobs.select.outputs.local }}
reason:
value: ${{ jobs.select.outputs.reason }}

permissions:
contents: read

jobs:
select:
runs-on: ubuntu-latest
timeout-minutes: 5
outputs:
runner: ${{ steps.runner.outputs.runner }}
local: ${{ steps.runner.outputs.local }}
reason: ${{ steps.runner.outputs.reason }}
steps:
- name: Select local Mac when online
id: runner
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
env:
CLOUD_RUNNER: ${{ inputs.cloud-runner }}
FORCE_CLOUD: ${{ inputs.force-cloud }}
HAS_STATUS_TOKEN: ${{ secrets.status-token != '' }}
with:
github-token: ${{ secrets.status-token || github.token }}
script: |
const localLabels = ['self-hosted', 'macOS', 'ARM64', 'personal-mac'];
let labels = [process.env.CLOUD_RUNNER];
let reason = 'cloud';
const pr = context.payload.pull_request;
const trusted = context.actor === context.repo.owner &&
(!pr || (pr.user.login === context.repo.owner &&
pr.head.repo?.full_name === `${context.repo.owner}/${context.repo.repo}`));

if (process.env.FORCE_CLOUD === 'true') {
reason = 'forced-cloud';
} else if (!trusted) {
reason = 'untrusted-event';
} else if (process.env.HAS_STATUS_TOKEN !== 'true') {
reason = 'status-token-missing';
core.warning('LOCAL_RUNNER_STATUS_TOKEN is missing; using GitHub-hosted runner.');
} else {
try {
const runners = await github.paginate(github.rest.actions.listSelfHostedRunnersForRepo, {
...context.repo, per_page: 100,
});
const available = runners.some(runner =>
runner.name === `personal-mac-${context.repo.repo}` &&
runner.status === 'online' &&
localLabels.every(label => runner.labels.some(item => item.name === label)));
if (available) {
labels = localLabels;
reason = 'local-online';
} else {
reason = 'local-offline-or-absent';
}
} catch (error) {
reason = 'status-api-unavailable';
core.warning(`Runner status query failed (${error.status || 'network error'}); using GitHub-hosted runner.`);
}
}

core.info(`Runner selection: ${reason} -> ${JSON.stringify(labels)}`);
core.setOutput('runner', JSON.stringify(labels));
core.setOutput('local', String(reason === 'local-online'));
core.setOutput('reason', reason);
Loading
0