Org-wide GitHub Actions configuration and shared reusable workflows.
iOS builds run on macOS runners provided by Sylphx Runners, not on
GitHub-hosted macOS runners. The reusable workflow asks for the labels
[self-hosted, sylphx, macos, standard]. Xcode version and capacity are set by
Sylphx Runners.
.github/workflows/firebase-app-distribution.yml: reusable workflow that uploads an Android APK to Firebase App Distribution. The caller stores its APK directory with the pinned run-store action in the same workflow run, grantsid-token: write, and passes that entry asartifact-name. The workflow header includes a paired producer/caller example. Entries last seven days; a missing or unreadable entry fails before Firebase authentication. This is a transient handoff, with no historical-build durability promise.workflow-templates/ci-fast-path.yml: the starter for a repository's CI - docs-only pull requests skip heavy jobs, superseded runs cancel, heavy jobs wait for the cheap gate,ci-okis the required check. It calls the reusablechanges.yml,cache-toolchainandneeds-passinSylphxAI/.github; the recipe is itsdocs/ci-template.md..github/workflows/content-checks.yml: this repository's own content checks. Every Cubeage repository carries its own copy calling the sharedSylphxAI/.githubactions:plain-language(advisory, never blocks) andzh-hant(advisory until 2026-10-02, then a required check)..github/actions/game-standard/: the shared check that keeps a title'sgame-standard.tomlin step with the Cubeage Game Standard: the module MUST rules it answers, its declared first-download and cold-start budgets, and its live-ops calendar. It carries rule ids, levels and module names only, and title repositories call it from a copy ofworkflow-templates/game-standard.yml(a private repository runs it onsylphx-linux-standard). Every pull request runs modepr; a release workflow passesmode: releaseand the built artifact, which must then fit the declared budget. Its tests run in.github/workflows/game-standard-action.yml..github/actions/ftl-daily-budget/: the guard a title's Test Lab step runs immediately beforegcloud firebase test. It counts the day's Test Lab steps in the project (midnight to midnight, America/Los_Angeles) from the Tool Results API and fails the job when the run would pass the daily allowance - physical 3, virtual 10 - with the message that spending needs owner approval. It fails closed whenever it cannot prove the count. Its tests run in.github/workflows/ftl-daily-budget-action.yml.scripts/pages-publish.sh: the one Cloudflare Pages upload every Keel title'sscripts/deploy_pages.shcalls, fetched at a pinned commit fromhttps://raw.githubusercontent.com/Cubeage/.github/<sha>/scripts/pages-publish.sh. It takes onlyCLOUDFLARE_API_TOKEN(Pages Edit): the repository secret in CI,op://Sylphx/wdzsmplds7qlnrjvjhfdkjqqxu/credentialon the desk. The Global API Key is owner-only and the script clears it before uploading. Usage:pages-publish.sh <dir> <project> [branch] [-- <wrangler flags>]; workflows fetch it the same way and call it in place ofwrangler pages deploy.- Repositories with several workflows also carry a
ci-okworkflow: the one required check of the merge queue, which waits for every other check. sylphx-linux-controlis the reserved control pool: its Kueue queue never lends and never borrows, so a seconds-long gate never waits for quota behind build jobs. Merge gates run there and nowhere else. Analways()verdict still runs after its run is cancelled, and that run keeps its concurrency group until it does, so on a busy build pool a superseded run on a shared label held the next one (SylphxAI/agents#4222, 2026-10-02).ci-okis the last job to finish on every run in a repository with several workflows, so it is the gate that belongs on the control pool; the pool exists for exactly this job shape.
Go to your repo → Settings → Secrets and variables → Actions and add:
| Secret | Value |
|---|---|
BUILD_CERTIFICATE_BASE64 |
Your Apple Distribution .p12 cert, base64 encoded |
P12_PASSWORD |
Password you set when exporting the .p12 |
PROVISIONING_PROFILE_BASE64 |
Your App Store .mobileprovision, base64 encoded |
KEYCHAIN_PASSWORD |
Any random strong string (e.g. openssl rand -base64 32) |
How to encode your files:
# On your Mac:
base64 -i Distribution.p12 | pbcopy # → paste as BUILD_CERTIFICATE_BASE64
base64 -i AppStore.mobileprovision | pbcopy # → paste as PROVISIONING_PROFILE_BASE64How to export your .p12:
- Open Keychain Access
- Find your "Apple Distribution: ..." certificate
- Right-click → Export → save as
.p12with a password - Encode it:
base64 -i YourCert.p12 | pbcopy
Copy .github/workflows/ → create .github/workflows/ios-build.yml in your repo:
name: iOS Build
on:
push:
branches: [main]
workflow_dispatch:
jobs:
build:
uses: Cubeage/.github/.github/workflows/ios-sign-and-build.yml@main
with:
scheme: YOUR_SCHEME_NAME # replace with your Xcode scheme
export-method: app-store # app-store | ad-hoc | development
secrets:
BUILD_CERTIFICATE_BASE64: ${{ secrets.BUILD_CERTIFICATE_BASE64 }}
P12_PASSWORD: ${{ secrets.P12_PASSWORD }}
PROVISIONING_PROFILE_BASE64: ${{ secrets.PROVISIONING_PROFILE_BASE64 }}
KEYCHAIN_PASSWORD: ${{ secrets.KEYCHAIN_PASSWORD }}That's it. The reusable workflow handles everything else.
Each build gets a completely isolated temporary keychain that is:
- Created fresh for every build run (name includes
run_id + run_attempt) - Deleted immediately after build completes (even on failure)
- Never shared between concurrent builds or different orgs
This is the same pattern used by GitHub's hosted runners and Fastlane match.
What runs on the runner:
- Xcode + system tools (persistent)
- Your source code (via
actions/checkout, deleted after job) - Your signing cert (temp keychain only, deleted after job)
- Your provisioning profile (UUID-named file, deleted after job)
What never persists on the runner:
- Your Apple ID credentials
- Your signing certificates
- Your provisioning profiles
For unit tests or simulator builds, no secrets needed:
- name: Test
run: |
xcodebuild test \
-scheme MyApp \
-destination 'platform=iOS Simulator,name=iPhone 16' \
-configuration Debug| Input | Default | Description |
|---|---|---|
scheme |
(required) | Xcode scheme name |
configuration |
Release |
Debug or Release |
export-method |
app-store |
app-store, ad-hoc, development, enterprise |
xcode-path |
/Applications/Xcode.app |
Path to Xcode.app |
Each repo manages its own secrets independently. Different repos in different orgs can have different Apple Developer accounts — there's no shared state on the runner.
Runners are managed by Sylphx Runners. A runner defect or an Xcode update
request goes to a SylphxAI/cloud issue labelled platform-request.