5.5 KiB
Contributing
Thanks for wanting to help. storebound is my own toolchain that I open-sourced, so it stays small and opinionated, but fixes, store rule updates and new engines / AI tools are very welcome.
Setup
bun install # Bun 1.1+; playwright is a dev dependency (renders, web capture)
bun run ci:quick # inner loop: typecheck + the tests your change touches + scrub (seconds)
bun run ci # full gate before pushing: every check and test (adapters, API tables, typecheck,
# desktop build and boot check, Rust host when it changed, scrub)
Which one when. Run bun run ci:quick while you work: it diffs against origin/main (committed, staged,
unstaged and new files), maps the changed files to the tests that import them (directly, through other modules, or
by naming a fixture or page) and runs those, plus tsc (incremental) and the scrub. A change to package.json,
bun.lock, bunfig.toml, tsconfig.json or the test preload runs every test. Run the full bun run ci before every
push and check its exit code: it is the gate, ci:quick is not.
tools/ci.ts runs the steps as a task graph (independent steps at the same time, output prefixed with the step
name, the first failure stops the rest, a time-per-step summary at the end). The tests run as parallel shards
(CPU cores - 1, at most 8; STOREBOUND_CI_SHARDS=n overrides) balanced by the durations of the last run, kept in
node_modules/.cache/storebound-ci/; the browser suites get a shard each. So tests must be shard-safe: temp folders
from mkdtempSync, servers on port 0, no fixed sleeps (wait for the event), no state shared through files in the
checkout. The desktop Rust host (cargo test, clippy) runs only when apps/desktop/src-tauri/ or
tools/build-sidecars.ts changed against origin/main; CI=full bun run ci always runs it (Forgejo CI does).
Try the screenshot and video pipeline on the demo game without any store account: see examples/demo-game/README.md.
Rules of the house
- Bun + TypeScript strict, zero runtime dependencies.
fetchagainst the official store APIs,node:built-ins, external tools (ffmpeg,xcrun,adb) only through argv builders. Playwright is resolved from the user's project. - Pure where possible. Rules, argv builders, plans and parsers are pure functions with tests in
tests/; the IO stays in the command'srun. Tests never need a store account, a simulator or a network. - Every write command takes
--dryand is idempotent. New MCP write tools default todry_run: true. Nothing public or irreversible (submit, release, rollout resume / complete, cancel, delete) goes into MCP. - Store facts need a source. When you change a size, limit or rule, cite the official page in the code comment and the skill (Apple Developer / App Store Connect Help, Play Console Help, API references) with the date you checked it.
- Skills are the single source. Edit
skills/*/SKILL.mdordocs/agents-core.md, then runbun tools/gen-adapters.ts; never editAGENTS.md,GEMINI.md, the plugin manifests or the Cursor / Copilot files by hand. Frontmatter isname+descriptiononly. - New or renamed flags: run
bun tools/gen-flags.ts. The CLI rejects a flag a command does not read (cli/data/flags.json, generated from the commands and their imports incli/commands.ts); a flag read with a computed name goes intoDYNAMICin that tool.tests/cli-help.test.tsfails on a stale table, on a command whose--helpdoes anything but print its usage, and on an MCP / desktop tool that passes a flag its command does not take. - No secrets, no real ids, no personal data in code, tests, fixtures or examples: use
com.example.*,example.comand obviously fake values.bun run ciruns the scrub. - No hand-written SVG. Charts come from visx, icons from Phosphor (
@phosphor-icons/reactin the desktop app, one per-icon import each; the site's icons are Phosphor's own files from@phosphor-icons/core), the App Store and Google Play marks from simple-icons (vendored with the licences bybun tools/site-icons.tsintosite/img/icons/andapps/desktop/src/ui/store-marks/, byte-equal to the packages), diagrams from Mermaid (docs/diagrams/*.mmd, rendered bybun tools/diagrams.ts), the desktop fixtures' art frombun tools/fixture-art.ts. The logo files (app icons, favicons, header marks,site/og.png) are PNG / ICO / ICNS fromtools/brand-assets.py(brand/README.md).tests/no-handmade-svg.test.tsfails on new inline<svg>/<path>drawing inapps/desktop/srcandsite/*.htmland on SVG files that are not vendored or rendered. These libraries are desktop dependencies (bundled by Vite) or devDependencies; the CLI package keeps zero runtime dependencies. - Every bug found in real use becomes a guardrail. Add an entry to
docs/pitfalls.mdand toskills/store-pitfalls/SKILL.md(symptom, cause, what to do, what guards it) and, where possible, a check or a test with recorded store answers that fails without the fix. A--dryrun prints the plan (dryPlanincli/lib/dry.ts), not only a count;tests/pitfalls.test.tsenforces it. - Plain English in docs and messages, short sentences, no marketing words.
Pull requests
One topic per PR, bun run ci green, a line in CHANGELOG.md under an "Unreleased" heading, and for anything
that talks to a store: how you verified it (--dry output, a test app, or "not verified" said plainly).