storebound/skills/release-flows/SKILL.md

5.6 KiB

name description
release-flows Repo-local workflows for a mobile app or game - named multi-step flows in store/workflows/*.yml (release, beta, or your own) that run storebound steps in order (next build number, build, upload, wait for processing, release notes, validate, scan, release plan) with progress, dry runs by default, resume after a failure and the owner's clicks (Submit for Review, release, rollout) listed but never executed. Use when the user wants to automate a release or beta routine, replace fastlane lanes, asks for a "one command" release, or wants to resume a failed release run.

Release flows

CLI: storebound flow list | validate | run <name> [NAME=value ...] [--dry | --apply] [--from <step> | --resume]. MCP: flow_list, flow_run (always the safe mode: writes as dry runs; --apply is a CLI command: run it from your shell after the dry run).

Where flows live

  • store/workflows/<name>.yml (one flow per file, named after the file; .yaml, .json and .ts work too), or
  • storebound.workflows.yml|json|ts with several flows (flows: { release: {...}, beta: {...} }).
  • storebound init --profile app|game writes release.yml and beta.yml as starting points.

A flow

description: Build, upload and check one version on both stores
params:
  VERSION: "?"                # "?" = required: flow run release VERSION=1.2.0 (no silent default; --resume reuses the last run's)
steps:
  - id: next-build
    if: apple                 # apple | play | exists <path> | ${VAR} | !${VAR} | ${A} == x; && joins
    run: apple next-build     # a storebound command line, without "storebound"
    capture: BUILD            # the last stdout line becomes ${BUILD}
  - id: build
    run: apple build --version ${VERSION} --build-number ${BUILD}
  - id: web
    sh: npm run build:web     # your own shell command: only runs with --apply
  - id: submit
    owner: "App Store Connect: Submit for Review"   # never run: listed as your click
  - id: beta
    flow: beta                # the steps of another flow (ids become beta.<id>); `private: true` hides a sub-flow
before: []                    # hooks: before / after the steps, onError when a step failed
after: []
onError: []

${config.testflight.internal} is filled by the runner with the internal groups of apple.testflight.groups (empty when none); the beta template uses it when GROUP is empty. The release template has optional steps by param: MAC_TAURI=<src-tauri> adds the macOS build + upload, PLAY_TRACK=beta uploads to Play open testing (default internal); its promote step is an owner step listing the Play promotion. continueOnError: true lets a step fail without stopping the flow. A web build before the native builds (Capacitor: npm run build && npx cap sync) belongs in build.prebuild in the config rather than an sh: step: apple build and play build run it themselves, and within one flow run it runs once (the second build step prints "prebuild skipped (already ran in this flow run)").

Modes

  • flow run release VERSION=1.2.0 (default): read steps (check, validate, scan, release plan, next-build, status ...) run for real; every write step runs with --dry; wait steps check once; shell steps are skipped. Nothing changes. Show this output to the owner. A dry apple build / play build writes no IPA / AAB, so the upload after it (and apple testflight wait / distribute after that) is shown as "planned" instead of failing; a real run is unchanged.
  • --dry: the plan only, nothing runs.
  • --apply: writes and shell steps run for real; each write command still asks its own confirmation in the terminal. Never add --yes to a step (flow validate refuses it).
  • --apply --yes: how you run it after showing the dry run (do it, don't ask: AGENTS.md first rule): every write step gets --yes; reads, waits and owner steps do not, so nothing owner-only runs. --yes never changes the price of an existing product (play products, apple subscriptions, play subscriptions): such a step writes everything else, names each held change ("price change on ( -> ) needs the owner") and exits 3, so the flow stops as declined. After the owner's yes for that diff, run that one command with --allow-price-changes (refused in flow steps, never added by flow run), then flow run <name> --apply --yes --resume. Without --yes and without a terminal, a confirming command declines: the flow stops there with "step N declined: not a terminal and no --yes ...", never "ok" (status declined in --json and the state file).
  • Owner steps never run in any mode: submit, release, phased release, rollout, sending review replies, price imports, inviting testers, Beta App Review, starting experiments, offer codes (AGENTS.md rule 1). They print as "YOUR CLICK" with the exact text, also when written as run:.
  • A failed step stops the flow and names it; after the fix storebound flow run release --resume [--apply] starts at that step with the captured variables of the last run (.storebound/flows/<name>.json). --from <step> starts anywhere.

Writing flows for the owner

  1. Start from the init templates; keep one store concern per step so a resume is cheap.
  2. Pick builds by the captured number (${BUILD}), never "the latest" (AGENTS.md rule 6).
  3. Long uploads: run the flow in a terminal with a log (storebound flow run release --apply > .storebound/flow.log 2>&1 &) and report the progress lines.
  4. storebound flow validate before committing a flow: unknown commands, ${VAR} that no param or earlier capture sets, duplicate ids, sub-flow cycles.