Lanes, toolchains & version sources
A lane is one buildable target: it names a toolchain adapter, its parameters, and where its version number is read and written.
Last updated 2026-07-02
A lane is one buildable target inside a project — an iOS app, an Android app, a web bundle, a desktop build. It answers three questions: how does this build (its toolchain), where does its version number live (its version source), and where does the result go (its distributions). This article covers the first two; distributions have their own article, Build ≠ Distribute.
Anatomy of a lane
- Label
- A human name — “iOS”, “Android”, “Web”, “Desktop”.
- Working directory
- Where the build runs, relative to the repo root.
- Toolchain
- The adapter that knows how to build this stack, plus its parameters.
- Version source
- The file and format AppRelay reads (and writes) the version from.
- Distributions
- One or more destinations the build ships to.
Toolchains
A toolchain names an adapter by id and carries that adapter's parameters. Adapters are the thin, compiled-in pieces that know a stack's detection signals, default parameter keys, and which built-in recipe to run. The shipped library:
| Adapter | For | Key parameters |
|---|---|---|
xcode | Xcode / Swift apps | container (project or workspace), scheme, bundleId, teamId |
gradle | Android / Kotlin | dsl (Groovy/KTS), moduleDir, applicationId |
reactNative | Bare React Native | platform-specific build settings |
expo | Expo / EAS | profile |
flutter | Flutter | target |
node | Web (React/Next/Vue/Svelte), static, PWA | framework, buildCmd, outDir |
electron | Electron desktop apps | build/make settings |
tauri | Tauri desktop apps | bundleTargets |
macNative | macOS-native apps (DMG, Sparkle appcast) | signing/notarize settings |
customShell | Anything else — you supply the commands | your steps |
Because a lane names its adapter and channels by string id — resolved against a registry — there's no closed list of “platforms” baked in. Stack quirks (Groovy vs. KTS, project vs. workspace, monorepo layouts) are expressed as parameters, not special cases.
Version sources
The version source tells AppRelay exactly where a lane's version number lives, so it can display the current value on the Overview and bump it during a release. Each kind knows how to read and write its own format:
| Version kind | Reads / writes |
|---|---|
pbxproj | Xcode MARKETING_VERSION (and build number) |
gradleLiteral | A literal versionName / versionCode in Gradle |
gradleDerived | A versionName composed from a prefix + versionCode |
packageJson | version in package.json |
pubspec | version: in a Flutter pubspec.yaml |
cargoToml | version in Cargo.toml |
pyproject | version in pyproject.toml |
expoAppConfig | Expo app.json / app.config.* |
appcast | A Sparkle appcast (for macOS-native) |
customRegex | Your own read/write regular expressions — the escape hatch for unusual formats |
pbxproj, KTS, package.json, or pubspec.yaml without corrupting the file is fiddly, so each kind owns its write-back. If your format is unusual, use customRegex, or fall back to a manual bump. If a write ever looks wrong, check it in Source Control before continuing.Editing a lane
In the project editor's Lanes section each lane is a form: pick the toolchain adapter and fill its parameters, choose the version source, and add the list of distributions (each picking a channel, a recipe, and a signing method). Onboarding prefills all of this from what it detected; you're just confirming or refining.
Next steps
- Build ≠ Distribute — add and manage a lane's destinations
- Running, debugging & the Test Console
- Recipes — the steps a lane actually runs