{"id":50782839,"url":"https://github.com/lod-softworks/available-runner-action","last_synced_at":"2026-06-12T05:02:02.512Z","repository":{"id":361907549,"uuid":"1255325655","full_name":"lod-softworks/available-runner-action","owner":"lod-softworks","description":"A GitHub Action which determines the availability of a requested GitHub Actions runner.","archived":false,"fork":false,"pushed_at":"2026-06-01T18:37:10.000Z","size":44,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-01T20:19:06.378Z","etag":null,"topics":["actions","github-actions","github-runners"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/lod-softworks.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-05-31T17:33:49.000Z","updated_at":"2026-06-01T18:22:42.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/lod-softworks/available-runner-action","commit_stats":null,"previous_names":["lod-softworks/available-runner-action"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/lod-softworks/available-runner-action","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lod-softworks%2Favailable-runner-action","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lod-softworks%2Favailable-runner-action/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lod-softworks%2Favailable-runner-action/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lod-softworks%2Favailable-runner-action/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/lod-softworks","download_url":"https://codeload.github.com/lod-softworks/available-runner-action/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lod-softworks%2Favailable-runner-action/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34229624,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-12T02:00:06.859Z","response_time":109,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["actions","github-actions","github-runners"],"created_at":"2026-06-12T05:01:59.608Z","updated_at":"2026-06-12T05:02:02.507Z","avatar_url":"https://github.com/lod-softworks.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# available-runner-action\n\n![Node.js](https://img.shields.io/badge/node.js-20-brightgreen?logo=nodedotjs)\n\nA GitHub Action that detects whether a preferred self-hosted runner is available and automatically falls back to a GitHub-hosted runner when none can be found. Use it to keep CI/CD pipelines running even when on-premises infrastructure is offline.\n\n---\n\n## Why this action exists\n\nGitHub Actions has no native runner failover. When a workflow specifies a self-hosted runner label and no matching runner is online, the job stays queued indefinitely. This action solves that problem without requiring any external tooling, dedicated infrastructure, or complex workflow logic.\n\nTypical use case: a team runs build jobs on a private server for speed and cost reasons. When that server is down for maintenance, deployments should not block — they should silently fall back to GitHub-hosted runners and continue.\n\n---\n\n## How it works\n\nThe action runs as a small, fast pre-job step. It queries the GitHub Actions API for all self-hosted runners visible to the workflow (at repository and organization scope), checks which ones are online and not busy, and returns a `runner` output that the dependent job uses in its `runs-on` field.\n\n```\nWorkflow\n   │\n   ▼\navailable-runner-action\n   │\n   ▼\nGitHub REST API\n   │\n   ├─ matching runner found ──► return labels  (e.g. [\"self-hosted\",\"linux\"])\n   │\n   └─ no match              ──► return fallback (e.g. \"ubuntu-latest\")\n```\n\nThe result is a JSON-encoded string. The dependent job wraps it with `fromJSON()`, which GitHub Actions evaluates to either an array (for self-hosted runners) or a plain string (for GitHub-hosted runners) — both are valid `runs-on` values.\n\n---\n\n## Permissions\n\nThe action only reads runner data. No write permissions are needed.\n\n```yaml\npermissions:\n  actions: read\n```\n\nThe default `GITHUB_TOKEN` is sufficient for repository-scope runner queries. For organization-scope runner access a personal access token with `manage_runners:org` read scope may be required depending on the organization's settings.\n\n---\n\n## Inputs\n\n| Input | Type | Default | Description |\n|-------|------|---------|-------------|\n| `preferred-labels` | multiline string | `self-hosted` | Newline-separated list of runner labels. A runner must match **all** labels to qualify. |\n| `fallback-runner` | string | `ubuntu-latest` | Runner to use when no self-hosted runner is available. Any valid `runs-on` value is accepted. |\n| `require-idle` | boolean | `true` | When `true`, busy runners are excluded. Set to `false` to allow selecting a runner that is currently executing another job. |\n| `fail-if-no-runner` | boolean | `false` | When `true`, the action fails the workflow instead of using the fallback runner. Useful for jobs that must run on specific infrastructure. |\n| `github-token` | string | `github.token` | Token for GitHub API calls. Override when you need organization-level runner access with a PAT. |\n\n## Outputs\n\n| Output | Values | Description |\n|--------|--------|-------------|\n| `runner` | JSON string or array | Pass directly to `runs-on` via `fromJSON()`. Returns a JSON array for self-hosted runners and a JSON string for GitHub-hosted runners. |\n| `selected-type` | `self-hosted` \\| `github-hosted` | Indicates which kind of runner was selected. Useful for conditional steps in the dependent job. |\n| `diagnostics` | string | Human-readable explanation of the selection, including runner counts and any API errors. |\n\n---\n\n## Usage examples\n\n### Minimal setup\n\nTwo jobs: the first selects a runner, the second uses it.\n\n```yaml\njobs:\n  select-runner:\n    runs-on: ubuntu-latest\n    outputs:\n      runner: ${{ steps.pick.outputs.runner }}\n    steps:\n      - id: pick\n        uses: lod-softworks/available-runner-action@v1\n\n  build:\n    needs: select-runner\n    runs-on: ${{ fromJSON(needs.select-runner.outputs.runner) }}\n    steps:\n      - uses: actions/checkout@v4\n      - run: dotnet build\n```\n\n---\n\n### Custom runner labels\n\nRequire a runner that has **all three** labels.\n\n```yaml\n- id: pick\n  uses: lod-softworks/available-runner-action@v1\n  with:\n    preferred-labels: |\n      self-hosted\n      windows\n      build-server\n```\n\n---\n\n### Custom fallback runner\n\nFall back to a Windows GitHub-hosted runner instead of the default Linux one.\n\n```yaml\n- id: pick\n  uses: lod-softworks/available-runner-action@v1\n  with:\n    preferred-labels: |\n      self-hosted\n      windows\n    fallback-runner: windows-latest\n```\n\n---\n\n### Allow busy runners\n\nAccept a runner that is currently executing another job. Useful when the queue matters less than staying on self-hosted infrastructure.\n\n```yaml\n- id: pick\n  uses: lod-softworks/available-runner-action@v1\n  with:\n    require-idle: 'false'\n```\n\n---\n\n### Require self-hosted infrastructure\n\nFail the workflow immediately if no self-hosted runner is available, rather than falling back to GitHub-hosted.\n\n```yaml\n- id: pick\n  uses: lod-softworks/available-runner-action@v1\n  with:\n    fail-if-no-runner: 'true'\n```\n\n---\n\n### Branch on runner type\n\nUse `selected-type` to adjust downstream step behaviour depending on which runner was selected.\n\n```yaml\njobs:\n  select-runner:\n    runs-on: ubuntu-latest\n    outputs:\n      runner: ${{ steps.pick.outputs.runner }}\n      runner_type: ${{ steps.pick.outputs.selected-type }}\n    steps:\n      - id: pick\n        uses: lod-softworks/available-runner-action@v1\n\n  build:\n    needs: select-runner\n    runs-on: ${{ fromJSON(needs.select-runner.outputs.runner) }}\n    steps:\n      - uses: actions/checkout@v4\n\n      - name: Cache (self-hosted NFS path)\n        if: needs.select-runner.outputs.runner_type == 'self-hosted'\n        uses: actions/cache@v4\n        with:\n          path: /mnt/nfs/cache\n          key: build-${{ hashFiles('**/*.csproj') }}\n\n      - run: dotnet build\n```\n\n---\n\n### Organization-level runners with a PAT\n\n```yaml\n- id: pick\n  uses: lod-softworks/available-runner-action@v1\n  with:\n    preferred-labels: self-hosted\n    github-token: ${{ secrets.ORG_RUNNER_PAT }}\n```\n\n---\n\n## Diagnostics output\n\nThe `diagnostics` output explains why a particular runner was selected. It is useful for debugging runner selection in pull request or deployment logs.\n\nExample when a self-hosted runner was found:\n\n```\nFound 2 available runner(s) matching labels:\n[self-hosted, linux]\n\nSelected:\nself-hosted\n```\n\nExample when no runner was available:\n\n```\nNo online, idle runner found matching labels:\n[self-hosted, windows, build-server]\n\nUsing fallback:\nubuntu-latest\n```\n\nExample when the API was unreachable:\n\n```\nNo online, idle runner found matching labels:\n[self-hosted, linux]\n\nAPI errors encountered:\n  Repository runners API failed: 403 Forbidden\n\nUsing fallback:\nubuntu-latest\n```\n\n---\n\n## Frequently asked questions\n\n**Does the action reserve the runner it selects?**\nNo. The action only reads runner state at the moment it runs. It does not lock or reserve runners. A runner can become busy between selection and actual job dispatch. If that happens the job queues normally — which is the same behaviour GitHub Actions uses today.\n\n**Does it work with GitHub Enterprise Server?**\nThe action uses the standard `@actions/github` Octokit client which respects the `GITHUB_API_URL` environment variable. GHES support should work as long as the API is accessible from the runner executing the selection step.\n\n**What happens if both the repo and org API calls fail?**\nThe action falls back to the configured fallback runner and records the errors in the `diagnostics` output. The workflow continues.\n\n**Can I use this in a reusable workflow?**\nYes. Pass the `runner` output through as a workflow output and use it with `fromJSON()` in the caller.\n\n---\n\n## Development\n\n```bash\n# Install dependencies\nnpm install\n\n# Type check\nnpm run typecheck\n\n# Run tests\nnpm test\n\n# Build distribution bundle (required before committing)\nnpm run build\n```\n\nThe `dist/index.js` bundle is committed to the repository so that GitHub Actions can execute the action without a build step. Always run `npm run build` and commit the updated `dist/` before pushing changes to the action logic.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flod-softworks%2Favailable-runner-action","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flod-softworks%2Favailable-runner-action","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flod-softworks%2Favailable-runner-action/lists"}