name: iOS Release on: push: tags: - "ios-v*" concurrency: group: ${{ gitea.workflow }}-${{ gitea.ref }} cancel-in-progress: true jobs: # ── archive & release to TestFlight ────────────────────────────────────── # Triggered only on ios-v* tags (e.g. ios-v1.0.0). # Required secrets: # APPLE_CERTIFICATE_BASE64 - Distribution certificate (.p12) base64-encoded # APPLE_CERTIFICATE_PASSWORD - Password for the .p12 file # APPLE_PROVISIONING_PROFILE_BASE64 - App Store distribution profile base64-encoded # KEYCHAIN_PASSWORD - Temporary keychain password (any random string) # ASC_KEY_ID - App Store Connect API key ID # ASC_ISSUER_ID - App Store Connect issuer ID # ASC_PRIVATE_KEY - Contents of the .p8 private key file # APPLE_TEAM_ID - 10-character Apple Developer team ID (GHZXC6FVMU) release: name: Release to TestFlight runs-on: macos-latest steps: - uses: actions/checkout@v4 - name: Install just run: command -v just || brew install just - name: Set build number from run number run: | sed -i '' \ 's/CURRENT_PROJECT_VERSION: .*/CURRENT_PROJECT_VERSION: ${{ gitea.run_number }}/' \ ios/LibNovel/project.yml - name: Import signing certificate env: CERTIFICATE_BASE64: ${{ secrets.APPLE_CERTIFICATE_BASE64 }} CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }} KEYCHAIN_PASSWORD: ${{ secrets.KEYCHAIN_PASSWORD }} run: | KEYCHAIN_PATH=$RUNNER_TEMP/app-signing.keychain-db security create-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH security set-keychain-settings -lut 21600 $KEYCHAIN_PATH security unlock-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH echo "$CERTIFICATE_BASE64" | base64 --decode > $RUNNER_TEMP/cert.p12 security import $RUNNER_TEMP/cert.p12 \ -P "$CERTIFICATE_PASSWORD" \ -A -t cert -f pkcs12 \ -k $KEYCHAIN_PATH security list-keychain -d user -s $KEYCHAIN_PATH - name: Import provisioning profile env: PROFILE_BASE64: ${{ secrets.APPLE_PROVISIONING_PROFILE_BASE64 }} run: | PP_PATH=$RUNNER_TEMP/profile.mobileprovision echo "$PROFILE_BASE64" | base64 --decode > $PP_PATH mkdir -p ~/Library/MobileDevice/Provisioning\ Profiles cp $PP_PATH ~/Library/MobileDevice/Provisioning\ Profiles/ - name: Write App Store Connect API key env: ASC_PRIVATE_KEY: ${{ secrets.ASC_PRIVATE_KEY }} ASC_KEY_ID: ${{ secrets.ASC_KEY_ID }} run: | mkdir -p ~/private_keys echo "$ASC_PRIVATE_KEY" > ~/private_keys/AuthKey_$ASC_KEY_ID.p8 - name: Inject team ID into ExportOptions.plist env: APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }} run: | /usr/libexec/PlistBuddy -c \ "Set :teamID $APPLE_TEAM_ID" \ ios/LibNovel/ExportOptions.plist - name: Archive env: USER: runner run: just ios-archive - name: Export IPA run: | xcodebuild -exportArchive \ -archivePath $RUNNER_TEMP/LibNovel.xcarchive \ -exportPath $RUNNER_TEMP/ipa \ -exportOptionsPlist ios/LibNovel/ExportOptions.plist - name: Upload to TestFlight env: ASC_KEY_ID: ${{ secrets.ASC_KEY_ID }} ASC_ISSUER_ID: ${{ secrets.ASC_ISSUER_ID }} run: | xcrun altool --upload-app \ --type ios \ --file $RUNNER_TEMP/ipa/LibNovel.ipa \ --apiKey "$ASC_KEY_ID" \ --apiIssuer "$ASC_ISSUER_ID" - name: Upload IPA artifact uses: actions/upload-artifact@v4 with: name: LibNovel-${{ gitea.ref_name }}.ipa path: ${{ env.RUNNER_TEMP }}/ipa/LibNovel.ipa retention-days: 30 - name: Cleanup keychain if: always() run: security delete-keychain $RUNNER_TEMP/app-signing.keychain-db