Skip to content

Latest commit

 

History

13 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

solari-ci

Measure a GitHub Actions job on Solari microVMs and pick its CPU size from the curve. Reports per-step speed, cost, run history, and static findings.

Showcase: https://moazessam376-dev.github.io/solari-showcase/

Solari CI speed versus cost curve

What it does

  • Inspects workflow shape, matrix cells, runner labels, history, and static findings.
  • Runs one selected Linux job natively in isolated Solari microVMs at several CPU sizes.
  • Renders a speed-versus-cost curve as terminal output, Markdown, JSON, and an optional PNG.

Why

GitHub's Actions platform fee is $0.002/minute effective 2026-03-01, and measuring the job first makes the trade-off between wall-clock time and compute cost visible before changing a workflow. Measuring before tuning is inspired by Blacksmith's [code]smith CI Tuning.

Install

uv pip install -e .
cp .env.example .env

Set SOLARI_API_KEY in .env and log in with gh auth login. GITHUB_TOKEN (or the output of gh auth token) lets solci clone private repositories inside the sandbox; the token is injected into the clone URL inside the microVM and masked in logs. For example, export GITHUB_TOKEN=$(gh auth token) also helps the gh CLI avoid GitHub API rate limits; never paste it into reports.

Usage

Check the local setup:

solci doctor
▮ SOLCI /// DOCTOR  check this Solari setup
  CHECK             STATUS    DETAIL
  SOLARI_API_KEY    PASS      slr_live_...xxxx
  gh auth           PASS      authenticated
  Solari sandbox    PASS      create -> nproc=1 -> delete in 2.66s

Inspect a remote repository:

solci inspect moazessam376-dev/crosstalk
◎ moazessam376-dev/crosstalk
WORKFLOWS / JOBS
  WORKFLOW    JOB ID  RUNS-ON            STEPS  MATRIX  SERVICES
  ci.yml      test    ${{ matrix.os }}       6  yes     -
FINDINGS
  low  NO_TIMEOUT   job  Job `test` has no job-level timeout.
HISTORY BASELINE
  runs 18  median 122.0 s  p90 160.0 s  failure rate 17%  est. runs/month 600.0
  GitHub $/run $0.0000  GitHub $/month $0.00
public repo: GitHub-hosted minutes are free; private-rate reference $0.0300/run

solci inspect owner/repo --job check on a Blacksmith-hosted job (moazessam376-dev/t3code) surfaces the BIG_RUNNER finding for blacksmith-8vcpu-ubuntu-2404, mentioning the vendor by name.

Run the selected job at several sizes and write artifacts:

solci run moazessam376-dev/crosstalk --job test --cpu 1,2,4,8 \
  --json docs/crosstalk.json --md docs/crosstalk.md --chart docs/crosstalk.png
▤ RESULTS moazessam376-dev/crosstalk / test
  CPU  MEM MB  BOOT   ONLINE  TOTAL    SOLARI/RUN  SOLARI/MONTH  SPEEDUP VS 1
    1   2,048  1.9 s    0.0s  191.5 s  $0.0030     $1.8188       1.00x
    2   2,048  1.9 s    2.6s  197.5 s  $0.0050     $3.0289       0.97x
    4   4,096  0.2 s    3.1s  228.3 s  $0.0117     $7.0006       -
    8   8,192  0.3 s    2.6s  277.7 s  $0.0284     $17.0323      -
  GitHub baseline: median 122.0 s, p90 160.0 s, 18 runs, $0.0000/run, 600.0 runs/month.

RECOMMENDATION
  Use 1 vCPU: 191 s for $0.0030 per run, within 10% of the 1 vCPU time (191 s)
  at 100% of its cost. GitHub ubuntu-latest median is 122 s (free on public repos; private-rate reference $0.030/run).

The job is not CPU-bound: 191 s at 1 vCPU versus 197 s at 2 vCPU, so 1 vCPU is the size to use at $0.003/run. The 4 and 8 vCPU runs hit the repository's own flaky PTY test (tests/harness/submit-turn.test.ts) at the same point, so their totals are excluded from the recommendation. See the per-step table in docs/crosstalk.md for the full log tail.

A second headline run against moazessam376-dev/Gym-App (typecheck, Node 22 shim, private repo) shows the opposite, CPU-bound shape: a clean 67 s -> 48 s -> 45 s curve as vCPU increases, recommendation 2 vCPU (48 s, $0.0012/run, 53% of the 4 vCPU cost, within 10% of its time); see docs/gym-app.md.

Jobs with services: or a container: refuse to run and exit 3:

$ solci run moazessam376-dev/Gym-App --job rls --cpu 2
error solci cannot run this job because service containers/Docker are not
available; solci runs steps natively in a microVM
$ echo $?
3

Cloud Chrome for browser jobs

Pass --cloud-browser to solci run or solci agent when a job uses Playwright (including @playwright/test or pytest-playwright), Puppeteer, or browser-use. solci creates a Solari cloud Chrome session for each browser test step, injects the CDP endpoint through environment variables and preloads, and releases the session after the step. The checked out repository is never edited. npx playwright install and playwright install steps are reported as skipped because Chromium is already provided by Solari. Cypress is detected but continues to use its local browser, because Cypress cannot attach to this remote CDP session. Chromium projects only. Firefox and WebKit projects will fail because the local browser install is skipped.

For a Playwright webServer config with a literal port or localhost url, solci maps http://localhost:N and http://127.0.0.1:N to the sandbox preview URL. Use --expose-port N for a server that is not declared in the Playwright config. This localhost mapping is best effort, and the cloud browser cannot reach the sandbox's loopback address directly without it.

A cloud-browser session can drop after roughly 10 minutes. solci keeps the scope to one session per browser test step and releases it in cleanup. Browser usage is billed separately at $0.10/hour on the Starter plan; the terminal and Markdown reports show the session count, seconds, and cost for each CPU size.

A full report from a real cloud-browser run is in docs/cloud-browser-demo.md.

Agent mode

solci agent gathers the inspect evidence, measures the selected job across the requested CPU sizes, and asks a brain to propose the smallest evidence-based edit to the selected workflow YAML.

The codex brain uses the local codex CLI with gpt-5.6-luna. The gemini brain uses the Gemini REST API with GOOGLE_API_KEY and gemini-2.5-pro. Use --brain codex or --brain gemini to choose one. Without --brain, solci prefers a codex binary on PATH, then Gemini when GOOGLE_API_KEY is set.

solci agent moazessam376-dev/crosstalk --job test --cpu 1,2,4 --brain codex --effort medium --dry-run

Add --pr to create a branch, commit the workflow edit and curve chart, push the branch, and open a GitHub pull request. --dry-run still clones the base branch so the brain sees the real workflow, but skips branch, commit, push, and PR creation. The brain may return no change when the evidence does not justify an edit. --pr commits with your local git identity and pushes to origin under your gh login, so it needs push access to the repository; there is no fork fallback.

For safety, the agent only ever edits the single selected file under .github/workflows. A human always reviews and merges the pull request; solci never merges anything itself.

Real example: Gym-App PR #111 was opened by solci agent moazessam376-dev/Gym-App --job typecheck --cpu 1,2,4 --pr. From the 67/48/45 s curve and a 35% historical failure rate, the model added timeout-minutes: 15 and a concurrency group with cancel-in-progress, and nothing else. Gym-App is a private repository, so that link resolves only for its owner.

Public example: crosstalk PR #43 was opened by solci agent moazessam376-dev/crosstalk --job test --cpu 1,2 --pr. That run measured 196.2 s at 1 vCPU and 192.1 s at 2. An earlier run of the same job measured 191 s and 197 s: the two sizes swap places across runs and stay within 3% of each other, which is what a job that is not CPU-bound looks like. The model proposed timeout-minutes: 15 and nothing else. Full dry-run output is in docs/agent-demo.md.

If every sandbox size in the sweep fails (for example a transient 429), the agent refuses to propose a change from history alone and exits with code 4. Pass --allow-history-only to proceed anyway.

How it works

solci run uses a mini-runner with one Solari microVM per requested size. The repository is cloned into /work/repo; workflow run steps execute natively in the Linux VM. The first matrix value is substituted when it is a literal list value (expression-valued axes, e.g. os: ${{ fromJSON(...) }}, are skipped), and only one matrix cell is measured.

The action shims are:

  • actions/setup-python
  • astral-sh/setup-uv
  • actions/setup-node
  • pnpm/action-setup
  • oven-sh/setup-bun

actions/checkout is handled by the runner's clone step. actions/cache, artifact upload/download, codecov/*, and unsupported actions are skipped or no-op with a note. Each exec call is capped at 24 seconds; long steps use nohup, a log file, and polling. For sizes above one vCPU, the runner polls nproc for CPU hot-plug completion with a bounded wait (typically 1-15 s, bounded at 40 s). Sandbox cleanup is guaranteed unless --keep is explicitly supplied; cleanup failures are recorded in the result. Per-step scripts export the step's own environment first and then source the shim environment file last, so that tool paths installed by a shim (e.g. actions/setup-node's /opt/node/bin) correctly take priority over the runner's baseline PATH instead of being clobbered by it.

Limits

  • No Docker, job containers, or service containers; such jobs are reported as SERVICES_UNSUPPORTED and solci run exits 3.
  • Linux microVMs only.
  • One matrix cell only: the first literal value for each list-valued axis.
  • Actions are shimmed/skipped as listed above; arbitrary third-party actions are not executed.
  • The Starter plan runs at most 2 concurrent VMs by default.
  • Live GitHub and Solari access is required for inspect owner/repo and run.

Findings codes

Code Severity Meaning
NO_CACHE_SETUP medium A supported setup action does not enable dependency caching.
UNPINNED_ACTION low An action uses a mutable or missing ref.
NO_TIMEOUT low The job has no job-level timeout.
NO_CONCURRENCY low A pull-request workflow lacks a cancellation group.
FULL_CLONE low Checkout requests the complete repository history.
SLOW_INSTALL_HINT info pip install or non-lockfile npm install may be slower.
HIGH_FAILURE_RATE high The history baseline has a failure rate above 20%.
BIG_RUNNER medium The job requests a large -cores/vcpu runner (GitHub or Blacksmith).
SERVICES_UNSUPPORTED info The job uses Docker or service containers unavailable in Solari.
MATRIX_NOTE info Only the first matrix cell was measured.
BROWSER_JOB info Browser tests install a local browser; use --cloud-browser for Solari cloud Chrome.

Solari notes

Facts learned running this tool against real jobs: the Solari exec API call itself takes up to roughly 28 seconds wall-clock, so solci clamps its own request timeout to 24 seconds and polls detached scripts via a log/exit-file pattern instead of blocking on exec; vCPU hot-plug after boot is typically observed within 1-3 seconds and is bounded by a longer wait, up to 40 seconds, before the runner gives up waiting; a fresh sandbox's HOME is unset by default (the runner and shims set it explicitly to /root where needed); default memory is 2,048 MB or 1,024 MB per requested vCPU, whichever is larger; a base sandbox has roughly 2.2 GB of disk available before cloning a repository; and there is no Docker or container runtime available inside a sandbox, which is why jobs with services:/container: are refused rather than attempted.

License

MIT. See LICENSE.

Author: Moaz Essam

About

Measure a GitHub Actions job on Solari microVMs at 1, 2, 4 and 8 vCPU and pick the cheapest size that meets a time budget.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages