Skip to main content
.revyl/config.yaml owns the build recipes used by both local and remote builds. revyl init writes a detected starting point. Edit the YAML directly, then run revyl config validate. Direct builds require a project config with named profiles. If the selected file is legacy config, revyl build stops and directs you to preview and run revyl config migrate; it does not translate or rewrite the file implicitly. Each invocation resolves exactly one named profile and one platform recipe:
The command runs in the cloud using the recipe’s toolchain image and caches. Cloud builds require authentication, a Git worktree, and a configured app_id UUID for an accessible app of the same platform. Cloud billing and capacity apply; failures never fall back to local execution. revyl dev remains local unless you pass --remote; test/workflow --build continuations also remain local. For Expo, the native prebuild recipes below run Xcode or Gradle directly and do not require Expo authentication. If you intentionally replace them with an EAS local-build command, install EAS CLI and add an encrypted EXPO_TOKEN reference for unattended remote builds.

A complete example

Profiles and platform recipes

Profiles name a complete build intent such as development, pull-request, or release. Each profile may contain an ios recipe, an android recipe, or both. There is no active or default profile stored in the file or on the server. revyl build applies explicit --profile and --platform values first. When an omitted value has exactly one eligible choice, the CLI selects it. If multiple choices remain, an interactive terminal prompts and a non-interactive invocation fails with the valid choices and the flag to provide.
--platform accepts only ios or android; profile names are passed to --profile.

Build fields

build.framework is required when build is present and applies to every profile. Supported values are ios, android, react_native, expo, and flutter. Build-level env, secrets, and caches are inherited by every recipe. A recipe can add or override them as described below. The authored contract permits app_id and output_path to be absent. A local build-and-upload requires output_path before running commands. A remote build may omit it and use the existing platform artifact-discovery default, but it requires app_id before submission. An interactive local build may run without app_id; after producing its artifact, the existing app-selection flow selects or creates the destination and offers to save that app ID into the selected recipe. Non-interactive local builds require the binding before running commands. All setup and build commands run in order from the project root selected by the nearest .revyl/config.yaml (or by -C). Selecting a recipe never rewrites its commands, publishes project configuration, or registers saved-profile identity with the ordinary build API. The optional post-build local app binding is the only config write in this flow; remote builds require the binding up front and do not select, create, or save an app.

Named build steps

Add name to a command in either list to label it in the remote build’s Steps tab:
Names are optional. Plain command strings still work, and built-in steps can also have a name.

Inheritance

Build-level values are resolved into each recipe deterministically:
  • Recipe env entries replace build-level values with the same key.
  • Effective secrets are the ordered, de-duplicated build-level names followed by recipe names.
  • Caches merge by key. Paths are the ordered, de-duplicated build-level paths followed by recipe paths.
  • A name cannot be both an effective environment key and a secret reference.

Artifact resolution

After the last build command exits successfully, a local build resolves its required output_path. A remote build uses its authored output_path when present, or falls back to the existing platform default:
  • iOS: build/**/*.app; the result must be a simulator .app bundle or a supported authored archive containing one.
  • Android: **/build/outputs/apk/**/*.apk; .aab artifacts are unsupported. Use an .apk.
Use an explicit path or glob that identifies one artifact. Avoid broad globs over cached output directories that can also match stale products.

Environment variables

Non-secret values are exported into the selected recipe’s setup and build commands. Use them for public build configuration such as EXPO_PUBLIC_* or ENVFILE:
Override or add values for one remote invocation:
--env is a remote-only flag. Its values take precedence over the inherited config environment. Local builds use the inherited config environment as authored. Do not put credentials or tokens in env or --env; use secret references instead.

Compilation cache controls

These reserved environment settings control automatic compilation caching: All three default to "1". revyl build --remote --no-cache forces them off for one remote build and also skips configured path caches. See Caching. Do not put credentials or tokens in env or --env; those values are part of the remote build request. Use encrypted build secrets instead.

Build secrets

Create or update an encrypted organization secret through the masked prompt:
For CI, send the value over stdin so it does not appear in process arguments or shell history:
Commit only the reference name:
Add references for one invocation with repeatable --secret flags:
Remote builds validate the names, decrypt the stored values only when the sandbox starts, and inject them into every setup and build command. Local builds read the same effective names from the current process environment and fail before running commands if a referenced value is missing. Revyl does not parse .env.local automatically. Keep it out of version control. List or delete stored names without revealing their values:

Private dependencies

Upload credentials as build secrets and configure authentication before the command that downloads private dependencies. The same setup commands run for CLI uploads and GitHub-triggered builds.

Private Git dependencies and submodules

For HTTPS Git downloads, add an entry to $HOME/.netrc. Choose the example for your Git host. Create the secret with the masked prompt:
Select the name under build.secrets (or the recipe’s secrets):
For GitLab, select GITLAB_TOKEN instead. Use a token with read access to each dependency repository. A GitLab personal access token needs read_repository; a deploy token needs its assigned username instead of oauth2 below. Add the matching setup command to the recipe before dependency installation. These examples add a new host without erasing entries for other services. If that host already has an entry, update it instead of appending a duplicate. GitHub:
GitLab:
These entries authenticate subsequent HTTPS Git commands to the matching host. SSH URLs such as [email protected]:company/sdk.git require SSH authentication. Submodules are separate repositories recorded in .gitmodules; with a Git checkout available on the runner, download them after configuring credentials:
Xcode Swift Package dependencies are a separate mechanism. To use system Git’s authentication when resolving their Git repositories, use xcodebuild -resolvePackageDependencies -scmProvider system with your project’s workspace/project and scheme arguments. Ensure Xcode is actually using system Git: an explicit IDEPackageSupportUseBuiltinSCM=false preference can prevent this on Xcode 26.3. This does not configure authentication for package registries or binary artifact downloads.

Private npm packages

Upload and select an npm token:
Add this literal line to the project’s .npmrc, preserving existing settings:
Commit the variable reference, never the token value. npm reads NPM_TOKEN from the build environment. For another registry, configure its hostname and any required package scope in .npmrc. Using SSH instead: Store a dedicated SSH private key as a build secret and configure SSH before fetching Git dependencies. Register the public key with the Git host, use SSH repository URLs, and configure verified host keys in ~/.ssh/known_hosts. npm registry downloads still need registry credentials.

Remote source

revyl build archives the active Git worktree, including tracked files with current edits and nonignored untracked files, and sends the selected project directory as the runner workdir. This preserves workspace files and shared packages for nested monorepo projects while commands and artifact paths remain relative to the project containing .revyl/config.yaml. It does not publish the config or send saved-profile identity. The profile name and platform remain invocation-local selection inputs. Deleted tracked files are omitted. Never track secrets, and add private runtime files to .gitignore. Git ignore rules do not exclude files that are already tracked. Use encrypted organization secrets rather than including their values in the source or build configuration.

Speed up uploads for large repositories

For monorepos or large repositories, use .revylignore to leave files the build does not need out of the source archive. This reduces upload size and time:
Place .revylignore in the project root containing .revyl/config.yaml, or at the Git worktree root. The project file takes precedence when both exist. Patterns with a leading / are relative to the Git worktree root, even for a nested project. For example, use /apps/mobile/docs/ for apps/mobile/docs/.

CLI reference

Do not combine --local with --remote. --image, --env, --timeout, --detach, and --no-cache are cloud-only. Managing running remote builds:

Timeout

Remote builds are stopped by the server when they exceed their timeout: 60 minutes by default, up to a maximum of four hours. Set a recipe-specific value:
Or override it for one invocation:
Ctrl-C stops following the build; the build keeps running in the cloud. Cancel it with revyl build cancel <build-job-id>.