3.2 KiB
CI/CD Flow
How a change travels from a local branch to TestFlight, and where the gates are.
flowchart TD
A["feature/* branch · local<br/><i>pre-push hook blocks main</i>"]
B["Pull request → develop<br/><i>develop is the default branch</i>"]
C["CI · ci.yml (macOS runner)<br/><i>xcodebuild build + test</i>"]
D{"Merge gate<br/>CI green + 1 approval"}
E["Pull request develop → main<br/><i>same gate</i>"]
F["Release · release.yml<br/><i>archive → IPA → TestFlight</i>"]
A -->|git push · open PR| B
B -->|PR triggers CI| C
C -->|status check: build-and-test| D
D -->|promote when ready| E
E -->|on merge to main| F
Steps
-
feature/*branch (local). Branch offdevelop. A local.git/hooks/pre-pushhook rejects any direct push tomain, so nothing reaches the protected branch outside a PR. -
PR →
develop.developis the default branch and the integration target for all feature work. -
CI runs —
.gitea/workflows/ci.ymlon the self-hosted macOS runner. It runsxcodebuild buildthenxcodebuild test(theKisaniCalTeststarget on an iOS simulator). The result is published as a commit status namedbuild-and-test. -
Merge gate. Gitea branch protection blocks the merge until:
- the
build-and-teststatus check is green, - there is ≥ 1 approval,
- there are no rejected reviews, and
- the branch is up to date with its base.
The same gate guards both
developandmain. - the
-
Promote
develop→mainvia a second PR when a release is ready. It passes through the identical gate. -
Release runs —
.gitea/workflows/release.ymlfires only on push tomain(i.e. after a PR merges). It archives, exports an IPA viaExportOptions.plist, and uploads to TestFlight (currently a commented-out placeholder awaiting the App Store Connect API-key secrets).
Two automation halves
- CI (steps 3–4) gates every PR into
main/develop. - Release (step 6) runs only on
main.
Blue/manual steps are actions you take; CI and Release run unattended on the runner.
Requirements & gotchas
- Runner labels. Both workflows use
runs-on: [self-hosted, macOS]. The registered runner must carry both labels or jobs queue forever. Check at Settings → Actions → Runners. - Status-check name. Branch protection requires a check literally named
build-and-test(the CI job name). If the rendered context differs, update the rule (scripts/gitea-setup.sh,STATUS_CONTEXT). - Simulator. The runner needs an installed iOS runtime (Xcode → Settings → Components) — CI auto-selects the first available iPhone simulator.
- Signing. CI builds with
CODE_SIGNING_ALLOWED=NO. The Release job needs a distribution cert + profile in the runner's login keychain. - Build number.
CURRENT_PROJECT_VERSIONinproject.ymlmust increase for every TestFlight upload — App Store Connect rejects a repeated build number.
See also CONTRIBUTING.md for the branch workflow.