# TestFlight Release CI Gitea Actions publishes iOS releases from tags that match: ```sh release/vN.N.N ``` For example: ```sh git tag release/v1.10.0 git push origin release/v1.10.0 ``` The release job runs on the `xcode` runner label, creates the runner user's login keychain from Gitea secrets, makes that keychain the user default for the duration of the job, installs the App Store provisioning profile in both the legacy MobileDevice directory and the Xcode UserData directory used by newer Xcode releases, builds and uploads the app with fastlane, then creates or updates the matching Gitea release with the generated IPA as an asset. The job restores the previous user default keychain and deletes the user login keychain and installed profiles in an `always()` cleanup step. No signing material is installed into the system keychain. Required repository secrets: ```text APP_STORE_CONNECT_API_KEY_ID APP_STORE_CONNECT_API_ISSUER_ID APP_STORE_CONNECT_API_KEY_CONTENT APPSTORE_CERTIFICATES_FILE_BASE64 APPSTORE_CERTIFICATES_PASSWORD APPSTORE_PROVISIONING_PROFILE_BASE64 ``` Generate or refresh the signing assets locally with: ```sh cd ios fastlane ios create_ci_signing ``` The generated `build/signing/ci-secrets.env` file is ignored by Git. Copy its certificate and provisioning profile values into the repository secrets listed above. The workflow uses the `Sybil AppStore CI` provisioning profile name by default. Fastlane keeps two signing names separate. `SYBIL_CODE_SIGN_IDENTITY` is the exact certificate common name used when exporting a local p12 for secrets, while `SYBIL_XCODE_CODE_SIGN_IDENTITY` defaults to the certificate SHA-1 fingerprint that Xcode uses during archive. `SYBIL_EXPORT_SIGNING_CERTIFICATE` defaults to the generic `Apple Distribution` selector used in the export options. The Release signing settings are also present in `Apps/Sybil/project.yml` so XcodeGen emits a manually signed App Store archive configuration. CI passes the installed provisioning profile UUID to Fastlane as `SYBIL_PROVISIONING_PROFILE_UUID`; Fastlane writes that UUID into the generated project before archiving. CI also passes the temporary keychain path as `CODE_SIGN_KEYCHAIN` so Xcode searches the disposable keychain for the imported Distribution identity. If the Apple team has reached the Distribution certificate limit, set `SYBIL_SIGNING_CERTIFICATE_ID` to the portal id for a certificate whose private key exists in the local login keychain before running `create_ci_signing`. The lane will export the local identity and create the provisioning profile against that existing certificate instead of creating another Distribution certificate. If `create_ci_signing` fails with an expired or missing agreement error, the Apple Developer Program account holder must accept the current agreements in App Store Connect before new certificates or provisioning profiles can be created through the API. The workflow uses Gitea's built-in `GITEA_TOKEN` for release creation and asset upload, with `contents: write` permissions. In Gitea this covers release asset publication.