Migration Guide
Step-by-step guide to migrate from GitHub-hosted runners
Migrate your workflows from GitHub-hosted runners to StarSling in minutes.
Step 1: Install the StarSling GitHub App
Install the StarSling GitHub App in your organization:
Grant access to the org repositories where you want to use StarSling Runners.
StarSling Runners are not available for personal repositories, only GitHub organizations. If you install the GitHub App in a personal repo, StarSling Runners will not pick up the jobs. Learn why →
Step 2: Update Your Workflow(s)
Option A: Use an AI Prompt
Paste this prompt into any AI coding agent (Claude Code, Cursor, Codex, etc.):
# Migrate GitHub Actions to StarSling Runners
Migrate the user's workflows from GitHub-hosted runners to StarSling Runners.
**Prerequisites:** `gh` CLI authenticated, [StarSling GitHub App](https://github.com/apps/starslingdev) installed on the repo's org.
## Configuration
**Target:** `starsling-ubuntu-24.04` | **Branch:** `migrate-starsling-ubuntu-2404`
**Source runners to replace:** `ubuntu-latest`, `ubuntu-24.04`
Replace all UPPERCASE placeholders (`OWNER`, `REPO`, `BRANCH_NAME`, `HEAD_OID`, `BASE64_CONTENT`, `FILE_NAME`, `N`, `DEFAULT_BRANCH`) with actual values from previous steps.
## Procedure
### Step 1: Confirm GitHub App Installation
Ask the user: "Have you installed the [StarSling GitHub App](https://github.com/apps/starslingdev) on your org? It's required for runners to pick up jobs after merge. If not, please install it first and let me know when you're ready."
**Do not run any commands or proceed to Step 2 until the user explicitly confirms the app is installed.**
### Step 2: Verify CLI Auth
Verify `gh auth status` succeeds. If not, direct the user to install from https://cli.github.com/ and run `gh auth login`.
### Step 3: Get Repository
Ask the user for the repository (`owner/repo`). Then run `gh api repos/OWNER/REPO --jq '.owner.type'`. If the result is `User` (not `Organization`), stop and explain: "StarSling Runners only work with GitHub organization repositories. You can create a free organization at https://github.com/account/organizations/new."
### Step 4: Discover Workflows
Fetch all workflow files in one API call:
```bash
cat <<'QUERY' | gh api graphql --input -
{
"query": "query($owner: String!, $repo: String!) { repository(owner: $owner, name: $repo) { id nameWithOwner defaultBranchRef { name target { oid } } object(expression: \"HEAD:.github/workflows\") { ... on Tree { entries { name object { ... on Blob { text } } } } } } }",
"variables": { "owner": "OWNER", "repo": "REPO" }
}
QUERY
```
Note: `HEAD` in the GraphQL expression is a Git ref, not a placeholder — do not replace it.
If the response is truncated or errors due to size, fall back to the REST API: fetch the default branch and HEAD SHA via `gh api repos/OWNER/REPO --jq '.default_branch'` and `gh api repos/OWNER/REPO/git/ref/heads/DEFAULT_BRANCH --jq '.object.sha'`, then fetch workflow file names via `gh api repos/OWNER/REPO/contents/.github/workflows` and each file individually via `gh api repos/OWNER/REPO/contents/.github/workflows/FILE_NAME`. The response `content` field is base64-encoded — decode it before scanning for `runs-on:` values.
Save `defaultBranchRef.name` (or REST default branch), `.target.oid` / HEAD SHA, and workflow `entries[]`. Scan each `.yml`/`.yaml` file for:
- direct `runs-on:` string values matching supported runners, and
- matrix-driven patterns (e.g., `runs-on: ${{ matrix.os }}`) where the matrix values include supported runner labels.
Show the user a summary including:
- Workflows that will be migrated and which runners are being replaced
- Any Ubuntu runners NOT in the supported list (e.g., `ubuntu-22.04`, `ubuntu-20.04`, larger runners like `ubuntu-latest-16-cores`) listed as "Not migrated — unsupported runner label"
If no direct or matrix-backed supported runners match, stop.
### Step 5: Preview Changes
**Never create a PR without the user confirming changes first.**
Show what will change per workflow:
- Replace supported runners in `runs-on:` string values with `starsling-ubuntu-24.04` (preserve quotes/comments)
- Replace matching `runner:`/`os:` values in `matrix.include` sections
- Skip commented lines
**Complex `runs-on` patterns:**
- **Array syntax** (e.g., `runs-on: [self-hosted, linux, ubuntu-latest]`): Only replace the matching label within the array; do not collapse to a single string
- **Group/labels syntax** (e.g., `runs-on: { group: ..., labels: [...] }`): Skip and flag for manual review
- **Expressions** (`${{ matrix.os }}`, ternary/conditional): If `runs-on` uses `${{ matrix.os }}` and the matrix values are hardcoded runner labels, replace the labels in the matrix definition and flag the workflow for manual verification. If the matrix values come from other expressions, skip entirely and flag for manual review
**YAML fidelity:** Change ONLY the `runs-on` and matrix values. Preserve the original file exactly: same indentation, key ordering, comments, blank lines, and trailing newline. The PR diff should show only the runner label changes.
### Step 6: Create PR
**Branch:** Before creating, check for existing branches: `gh api repos/OWNER/REPO/git/matching-refs/heads/migrate-starsling-ubuntu-2404 --jq '.[].ref'`. If any exist, find the highest numeric suffix and increment by 1 (if none have a suffix, use `-2`). Then create: `gh api repos/OWNER/REPO/git/refs -f ref=refs/heads/BRANCH_NAME -f sha=HEAD_OID`.
**Atomic commit** — all files in one commit, contents base64-encoded:
```bash
cat <<'MUTATION' | gh api graphql --input -
{
"query": "mutation($input: CreateCommitOnBranchInput!) { createCommitOnBranch(input: $input) { commit { oid url } } }",
"variables": {
"input": {
"branch": {
"repositoryNameWithOwner": "OWNER/REPO",
"branchName": "BRANCH_NAME"
},
"message": {
"headline": "Migrate N CI workflows to StarSling Runners",
"body": "Replaced runners per file:\n- file1.yml: ubuntu-latest → starsling-ubuntu-24.04\n- file2.yml: ubuntu-24.04 → starsling-ubuntu-24.04"
},
"expectedHeadOid": "HEAD_OID",
"fileChanges": {
"additions": [
{ "path": ".github/workflows/FILE_NAME", "contents": "BASE64_CONTENT" }
]
}
}
}
}
MUTATION
```
Verify the response contains a valid `commit.oid`. If the mutation returned an `expectedHeadOid` mismatch, re-fetch the HEAD SHA (Step 4) and retry the commit once. For any other errors, report and stop — do not create a PR against a failed commit.
**Commit message:** The headline should read `Migrate N CI workflows to StarSling Runners` where N is the count of modified workflow files (`.yml` + `.yaml`). List each file and the runner label(s) replaced in the body, as shown in the example above.
**PR** via `gh pr create --repo OWNER/REPO --head BRANCH_NAME --base DEFAULT_BRANCH --title "Migrate N CI workflows to StarSling Runners" --body "..."` with this body structure:
```
## Summary
Migrates CI workflows from GitHub-hosted runners to [StarSling Runners](https://docs.starsling.dev) for faster builds and AI-powered optimizations.
## Changes
- `file1.yml`: `ubuntu-latest` → `starsling-ubuntu-24.04`
- `file2.yml`: `ubuntu-24.04` → `starsling-ubuntu-24.04`
## Not Migrated
- `file3.yml`: Uses `${{ matrix.os }}` — requires manual review
(or "All workflows migrated successfully.")
## After Merging
Workflows will automatically run on StarSling Runners. Ensure the [StarSling GitHub App](https://github.com/apps/starslingdev) is installed with access to this repo.
```
### Step 7: Monitor (Optional)
Ask the user if they'd like help monitoring after merge. If yes, explain they can return after merging and you'll check with:
```bash
gh run list --repo OWNER/REPO --branch DEFAULT_BRANCH --limit 5 --json status,conclusion,name,createdAt
```
Look for runs created after the merge. If any show `queued` for more than 2 minutes, suggest checking the GitHub App installation and repo access at https://github.com/apps/starslingdev.
## Error Handling
| Error | Solution |
|-------|----------|
| `gh` not found or not logged in | Install from https://cli.github.com/, run `gh auth login` |
| 403 / insufficient permissions | User needs write access to the repository — check collaborator status or org role |
| App not installed | Install from https://github.com/apps/starslingdev |
| Personal repo (owner type `User`) | StarSling requires an org repo — create one at https://github.com/account/organizations/new |
| Repository not found | Check repo name and permissions |
| No workflows found | Ensure `.github/workflows/` exists |
| Runner not available after merge | Verify app has repo access at https://github.com/apps/starslingdev |
| Rate limit exceeded | Wait a few minutes and retry |
| 422 on branch creation | Branch exists — append a number suffix |
| `expectedHeadOid` mismatch | Re-fetch HEAD SHA and retry the commit |
| `${{ matrix.os }}` / complex `runs-on` | Require manual review — determined at runtime |
| GraphQL response truncated | Fall back to REST API for individual file fetches |Option B: Manual Update
Change your runs-on label from GitHub-hosted to StarSling:
starsling-ubuntu-24.04jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
# ... your build stepsjobs:
build:
runs-on: starsling-ubuntu-24.04
steps:
- uses: actions/checkout@v4
# ... your build stepsLabel Mapping
Use starsling-ubuntu-24.04 to replace any of these GitHub-hosted runner labels:
| GitHub-hosted Label | StarSling Label |
|---|---|
ubuntu-latest | starsling-ubuntu-24.04 |
ubuntu-24.04 | starsling-ubuntu-24.04 |
Compatibility Notes
Fully Compatible
- All
actions/*official actions - Matrix strategies
- Caching with
actions/cache - Artifacts with
actions/upload-artifact - Secrets and environment variables
Minor Differences
| Feature | GitHub-hosted | StarSling |
|---|---|---|
| Default shell | bash | bash |
| Home directory | /home/runner | /home/runner |
| Work directory | /home/runner/work | /home/runner/work |
| Tool cache | /opt/hostedtoolcache | /opt/hostedtoolcache |
Not Supported
- macOS runners (coming soon)
- Windows runners (coming soon)
- GPU runners (coming soon)
Rollback
If you need to rollback, simply change the label back:
# Rollback to GitHub-hosted
runs-on: ubuntu-latestNo other changes required.
Troubleshooting Migration
Runner Not Starting
- Verify the GitHub App is installed
- Check the label format is correct
- Ensure your account has available runner capacity
Slower Than Expected
- Verify caching is working
- Check if dependencies are being re-downloaded
- Use matrix builds to parallelize tests
Action Compatibility Issues
Most actions work unchanged. If an action fails:
- Check the action's documentation for self-hosted runner support
- Ensure required tools are available
- Contact support if issues persist