Troupeby AliveDocsSign in

Troupe Cloud

Builds

Troupe Cloud builds an app from a git ref or an uploaded tarball and keeps the result as an artifact that a device installs or you download. This page covers sources, targets, the expo and xcode presets and their options, logs and artifacts.

How a build runs

  1. Probe. A sandbox fetches the source and reads what it is: the commit, the app roots, the frameworks. Nothing of yours runs yet.
  2. Plan. A preset turns those facts and your request into a list of typed steps, pinned to the probed commit.
  3. Build. A fresh build machine runs the steps and uploads the outputs.
  4. Artifact. Troupe computes each output's SHA-256 itself and records it as an artifact.
StatusMeaning
awaitingSourceA tarball build waits for its upload
queuedWaiting to be inspected, or for a build machine
starting, runningOn a build machine; statusDetail names the running step
succeededIts artifacts are ready
failedYour project's build failed, or the source cannot be built; failure says why
erroredTroupe's side failed; the build was not at fault
canceledSomeone canceled it

Start a build

In the web app, open a project and choose Start a build: the repository URL and ref, the platform, the target and the configuration. More options holds the app root, the preset and its options, the timeout and the caches. The build's page then shows its status, its phases, its artifacts and its log as it is written.

Over the API it is builds.create, which needs builds:write. Every role holds it.

Shell
curl https://api.troupe.ali.ve/v1/builds/create \
  --header "Authorization: Bearer $TROUPE_API_KEY" \
  --header "Troupe-Contract: 1.0" \
  --header "Content-Type: application/json" \
  --data '{
    "projectId": "prj_…",
    "source": { "kind": "git", "url": "https://github.com/acme/shop", "ref": "main" },
    "osFamily": "ios",
    "target": "simulator"
  }'

The answer is the build, with its buildId (bld_…) and the status queued. Read it again with builds.get until it ends:

Shell
until curl --silent "https://api.troupe.ali.ve/v1/builds/get?projectId=prj_…&buildId=bld_…" \
    --header "Authorization: Bearer $TROUPE_API_KEY" |
  jq --exit-status '.build.status | test("succeeded|failed|errored|canceled")' > /dev/null
do sleep 10; done

Sources

A git ref. url is an https:// address the build can read without credentials: no user name or password, no query, no IP address. ref is one of:

RefBuilt as
A branch name, mainrefs/heads/main
A full ref, refs/tags/v1.2.0That ref
A commit idThat commit

A bare name is always a branch, so a tag called main is never built by mistake. A name git would read as something else, such as tags/v1 or HEAD, is refused: write the full ref. Submodules and Git LFS are not supported yet; a request that sets submodules or lfs is refused.

A tarball. For a private repository or a working tree, upload the source. The web app starts builds from a repository only; a tarball build goes through the API:

  1. Call builds.create with "source": { "kind": "tarball" }. The build is awaitingSource, and the answer carries an upload session with its uploadId.
  2. Call uploads.grant with the uploadId, the name source.tar.gz, the content type application/gzip, the file's size in bytes and its sha256. It returns a signed PUT request: a URL and headers.
  3. Send the file with that request.
  4. Call uploads.complete. The build moves to queued.

The tarball is a gzipped tar of the source tree, with the tree's root at the archive's root, of at most 2 GiB. The upload must finish within an hour. Uploaded sources are kept for 7 days.

Targets

osFamilytargetOutputPreset
iossimulatorA simulator app, as a gzipped tarexpo, xcode
iosstoreAn .ipa for App Store Connect, signed as it is builtexpo
androidemulatorAn APKexpo
androiddeviceInternalAn APK for internal distributionexpo
androidstoreAn AAB for Playexpo

No preset builds an iOS deviceInternal target yet: such a build fails with target-unsupported.

configuration is release or debug. The expo preset builds release unless you ask otherwise: the JavaScript bundle is embedded, so the app runs with no bundler. debug is offered for the simulator and the emulator and expects a bundler. The xcode preset builds debug by default.

A store or deviceInternal build must say what it signs with: a signing credential's id in signing, or "none". An iOS store build cannot be "none". See Signing credentials.

Presets

A preset recognises a kind of project and knows the steps that build it. Leave preset out and the probe's facts choose one; name it to be sure.

expo

For an Expo app: an app root whose package.json depends on expo. The preset accepts Expo SDK 53 to 58; another SDK fails with sdk-unsupported.

StepWhat it does
checkoutThe probed commit
installDependencies from the committed lockfile (Bun, pnpm, Yarn or npm), frozen, with lifecycle scripts off
fingerprintThe project's Expo fingerprint, recorded in the build's metadata
prebuildexpo prebuild for the platform, clean by default
pods, then xcode.build-simiOS simulator: CocoaPods, then xcodebuild for the generic iOS Simulator
gradle.assemble or gradle.bundleAndroid: the project's own Gradle wrapper builds the APK or the AAB
verifyiOS: reads the built app's bundle id, versions and URL schemes into the artifact's metadata
uploadStores the output

A source without a committed lockfile fails. An emulator build compiles native code for arm64-v8a unless architectures says otherwise.

OptionMeaning
schemeThe Xcode scheme to build; by default the generated workspace's one scheme
cleanRegenerate the native project from scratch; true by default
architecturesAndroid: the ABIs to build native code for, comma-separated, from armeabi-v7a, arm64-v8a, x86 and x86_64

xcode

For a native iOS app: an Xcode project or workspace in the source, or an XcodeGen spec that generates one. It builds the iOS simulator target only, and is chosen only where the source holds no Expo or React Native app. The build is the project's own xcodebuild: there is no install or CocoaPods step, so a project that uses CocoaPods commits its Pods directory. Swift packages resolve from GitHub.

OptionMeaning
projectThe .xcodeproj, .xcworkspace or XcodeGen spec (.yml) to build, relative to the app root; by default the only one there
schemeThe scheme to build; by default the project's one shared scheme, else its own name

A React Native app that does not use Expo has no preset yet.

Options

All of these are optional fields of builds.create.

FieldMeaning
appRootThe app's directory inside the source, such as apps/mobile; detected when left out
configurationrelease or debug
preset, presetOptionsThe preset and its options. Options are checked at once when you name the preset, otherwise when the build is planned
timeoutMsThe longest the build may run: 60000 to 10800000 (3 hours), 3600000 (1 hour) by default
cache{ "restore": true, "save": true } by default; a store build neither restores nor saves unless asked
triggerThe branch, defaultBranch or pullRequest that caused the build; it scopes the dependency caches
signingA signing credential's id or "none"; required for store and deviceInternal, refused for the others
envUp to 100 environment values of up to 8192 characters for the native steps, never for install
stepOverridesUp to 16 changes to the planned steps: insert before or after, replace, remove

Values in env are never stored or returned: the build keeps only their names, in envNames, and its log masks the values. They can end up inside the app if your project compiles them in. Names the build machine sets itself, such as PATH, HOME, NODE_OPTIONS and JAVA_HOME, are refused.

What a build can reach

A build's network is limited to the hosts its preset lists, on port 443, and the host of its own source.

PresetHosts
expoThe npm registry, the CocoaPods CDN, React Native's repository, Maven Central and GitHub; for Android also Gradle's distributions and plugin portal, Google's Maven and JitPack
xcodeGitHub, for Swift packages

A dependency from a private registry, or from another host, fails the step that fetches it.

Follow a build

builds.logs replays the log and then follows it, as an event stream:

Shell
curl --no-buffer "https://api.troupe.ali.ve/v1/builds/logs?projectId=prj_…&buildId=bld_…" \
  --header "Authorization: Bearer $TROUPE_API_KEY" \
  --header "Accept: text/event-stream"

Each line carries a seq, a time, the step's id, the stream it came from (stdout, stderr, runner or controller) and the text. A follow ends when the build ends, and after 40 minutes at the latest: open it again with since set to the last seq you read. follow=false returns what is there and ends.

When a build fails, failure holds a reason, a message and, when a step failed, its stepId.

reasonMeaning
unknown-ref, source-not-found, source-unreachableThe source could not be fetched
not-detectedNo preset recognises the source, or the app root is not an app
target-unsupported, configuration-unsupported, sdk-unsupportedThe preset does not build this
preset-options, step-overrideAn option or an override does not fit the preset
install-failed, prebuild-failed, pods-failed, xcodebuild-failed, gradle-failedThat step failed; read its log
timed-outThe build ran longer than timeoutMs
signing-refused, signing-failedThe credential may not sign this build, or signing failed

builds.cancel ends an unfinished build and stops its machine. builds.list lists a project's builds, newest first.

Artifacts

A finished build names its artifacts in artifactIds. artifacts.list and artifacts.get return each one's format, size and SHA-256 and, where the build read them, the app's bundle id, version, build number and URL schemes, and how it was signed.

  • Install it. Name the artifact in install on sessions.create, or in apps.install: { "artifactId": "art_…" }. See installing an app.
  • Download it. artifacts.download returns a signed URL that works for 10 minutes.

An artifact is kept for 30 days; its expiresAt says until when. It needs artifacts:read to read or download.

Limits

LimitValue
Builds at onceThe builds limit: see limits
Waiting for the upload, then for a machineOne hour each, then the build is errored
Running timetimeoutMs, 3 hours at most
Uploaded source2 GiB
One artifact4 GiB
Dependency cachesKept for 14 days

Build time is metered as build_seconds: see usage.

Next

esc

    Type to search every page, the protocol reference and the cloud API.