Files
KisaniCal/docs/CICD.md
2026-07-12 22:57:13 +00:00

3.2 KiB
Raw Blame History

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

  1. feature/* branch (local). Branch off develop. A local .git/hooks/pre-push hook rejects any direct push to main, so nothing reaches the protected branch outside a PR.

  2. PR → develop. develop is the default branch and the integration target for all feature work.

  3. CI runs.gitea/workflows/ci.yml on the self-hosted macOS runner. It runs xcodebuild build then xcodebuild test (the KisaniCalTests target on an iOS simulator). The result is published as a commit status named build-and-test.

  4. Merge gate. Gitea branch protection blocks the merge until:

    • the build-and-test status 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 develop and main.

  5. Promote developmain via a second PR when a release is ready. It passes through the identical gate.

  6. Release runs.gitea/workflows/release.yml fires only on push to main (i.e. after a PR merges). It archives, exports an IPA via ExportOptions.plist, and uploads to TestFlight (currently a commented-out placeholder awaiting the App Store Connect API-key secrets).

Two automation halves

  • CI (steps 34) 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_VERSION in project.yml must increase for every TestFlight upload — App Store Connect rejects a repeated build number.

See also CONTRIBUTING.md for the branch workflow.