Keel × GitHub Actions
Operator-facing guide for the workflow templates in
keel/templates/github-actions/. Covers
secrets setup, self-hosted runner decisions, and Actions cost notes.
Available templates
| Template | What it does | Trigger | Typical runtime |
|---|---|---|---|
keel-ota-publish.yml | Build JS bundles + publish OTA update to both platforms | push to main | 3–6 min |
keel-submit-stores.yml | Native build (.ipa + .aab) + submit to App Store, Google Play, Huawei, Xiaomi, OPPO, vivo, 应用宝 | push tag v*.*.* | 30–60 min |
keel-pr-check.yml | Typecheck + lint + dry-run build (fast feedback) | pull_request | 2–4 min |
keel-cert-healthcheck.yml | Weekly probe of CDN/Update endpoints + TLS expiry | cron + manual | < 1 min |
To install a template, copy it into .github/workflows/ of your project
repo and populate the required secrets. See the directory’s
README.md for one-line install
commands.
Setting up GitHub Secrets
Repo Settings → Secrets and variables → Actions → New repository secret. Use Environments for tag-triggered deploys so you can require manual approval before store submission.
Per-template required secrets
keel-ota-publish.yml
| Secret | Source | How to obtain |
|---|---|---|
KEEL_API_KEY | Keel Cloud | Create on the dashboard’s “API tokens” page; copy the keel_live_... string into the secret. In the workflow, keel login --ci picks it up from this env var. |
KEEL_PROJECT | Keel Cloud | <team-slug>/<project-slug> from keel.json |
keel-pr-check.yml — same two as above.
keel-submit-stores.yml — KEEL_API_KEY + KEEL_PROJECT plus:
| Secret | Source | Format | Notes |
|---|---|---|---|
ASC_KEY_ID | App Store Connect → Users and Access → Keys | 10-char string | |
ASC_ISSUER_ID | Same page, top of the table | UUID | |
ASC_KEY_P8_B64 | .p8 download from ASC | base64 of the file | base64 < AuthKey_XXX.p8 | pbcopy |
GOOGLE_PLAY_JSON_B64 | GCP service account with Play Console role | base64 of the JSON | |
HUAWEI_CLIENT_ID / HUAWEI_CLIENT_SECRET | AppGallery Connect → API client | strings | |
XIAOMI_USERNAME / XIAOMI_PASSWORD | dev.mi.com publisher account | strings | Recommend dedicated CI sub-account |
OPPO_CLIENT_ID / OPPO_CLIENT_SECRET | open.oppomobile.com console | strings | |
VIVO_ACCESS_KEY / VIVO_ACCESS_SECRET | dev.vivo.com.cn → API 配置 | strings | |
YYB_USERNAME / YYB_PASSWORD | 应用宝 (Tencent) publisher | strings |
P8 keys and service-account JSON must be base64-encoded before
storing. GitHub’s secret editor preserves the bytes but downstream
shell interpolation can mangle embedded newlines; base64 sidesteps that.
The template decodes them with printf … | base64 -d at the start of
the submit job.
keel-cert-healthcheck.yml
| Secret | Source |
|---|---|
KEEL_PROJECT | Same as above |
SLACK_WEBHOOK_URL | Slack workspace → Apps → Incoming Webhooks |
Credential schema cross-reference
The names and shapes above match the keys keel submit looks up in
~/.keel/credentials.yml. Authoritative schema lives in
submit.md §11. If you find a
mismatch, that doc is the source of truth — file an issue against the
template.
Self-hosted runners
GitHub-hosted runners cover most needs. Use self-hosted when:
- iOS native builds (
keel-submit-stores.yml’sbuildjob): the template usesmacos-14(Apple Silicon) GitHub-hosted runner. Self-host only if you need a non-public provisioning profile setup or your codebase pulls private CocoaPods from inside a corporate VPN. - China-region network access: 国内市场 submission endpoints (Huawei,
Xiaomi, etc.) are usually reachable from
ubuntu-latest. If you hit TLS / rate-limit issues from GitHub’s US/EU IPs, run an ECS / 阿里云 self-hosted runner in mainland China. - Long native builds (> 60 min wall time): pre-warmed self-hosted
runners avoid the cold
pod install+ Gradle dependency download.
Self-hosted setup is out of scope here; see GitHub’s docs. Tag the
runner with self-hosted, macOS, arm64 (or self-hosted, linux, cn)
and change runs-on: in the template accordingly.
Cost notes
GitHub Actions free tier (public repos: unlimited; private repos: 2,000 min/month on Free plan, 3,000 on Pro, 50,000 on Team/Enterprise):
| Runner | Multiplier | Effective cost |
|---|---|---|
ubuntu-latest | 1× | cheapest — use for everything except iOS native build |
windows-latest | 2× | not needed for Keel |
macos-13 (Intel) | 10× | avoid — Apple Silicon is faster and same cost-per-minute |
macos-14 (M1) | 10× | required for iOS native build, ~2× faster than Intel |
macos-14-large | 12× | only if pod install truly bottlenecks |
Typical monthly burn for a small team:
- 50 PRs × 3 min ×
ubuntu-latest= 150 min - 30 main-branch pushes × 5 min ×
ubuntu-latest= 150 min - 4 releases × 40 min, of which 15 min on
macos-14= 4 × 25 ubuntu + 4 × 15 × 10 = 100 + 600 = 700 min - Weekly healthcheck: negligible
→ comfortably under the 3,000-min Pro plan. The macOS multiplier is
the single biggest lever; minimize macos-14 job duration by doing
all non-Xcode work on ubuntu-latest (the template already does this
— only the build job lands on macOS).
See also
submit.md—keel submitCLI surface + market listsubmit.md§11 — authoritative credential schema (keys + types + where the CLI reads them)update.md— OTA pipeline thatkeel publishwrites todeployment-sop.md— human-checklist version of the same release flow (for the “no GHA” case)