name: Release Pocket (iOS) permissions: contents: write actions: read on: release: types: - prereleased workflow_dispatch: inputs: testflight: description: Upload to TestFlight type: boolean default: false tag: description: GitHub Release tag (empty to skip) type: string required: false default: '' jobs: build: name: Build iOS runs-on: macos-26 env: PRODUCT_NAME: 'AIRI' VITE_ENABLE_POSTHOG: 'true' UPLOAD_TESTFLIGHT: ${{ github.event_name == 'release' || (github.event_name == 'workflow_dispatch' && inputs.testflight) }} RELEASE_TAG: ${{ github.event.release.tag_name || inputs.tag }} steps: - uses: actions/checkout@v6 - uses: pnpm/action-setup@v4 with: run_install: false - uses: actions/setup-node@v6 with: node-version: lts/* cache: pnpm - run: pnpm install --frozen-lockfile - run: pnpm run build:packages - name: Build assets run: pnpm -F @proj-airi/stage-pocket run build - name: Sync assets to iOS run: pnpm -F @proj-airi/stage-pocket exec cap sync ios - name: Install Apple certificate env: CERT_BASE64: ${{ secrets.APPLE_DISTRIBUTION_CERT_BASE64 }} CERT_PASSWORD: ${{ secrets.APPLE_DISTRIBUTION_CERT_PASSWORD }} run: | CERT_PATH="$RUNNER_TEMP/certificate.p12" KEYCHAIN_PATH="$RUNNER_TEMP/app-signing.keychain-db" KEYCHAIN_PASSWORD="$(openssl rand -hex 16)" echo "$CERT_BASE64" | base64 --decode -o "$CERT_PATH" security create-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH" security set-keychain-settings -lut 3600 "$KEYCHAIN_PATH" security unlock-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH" security import "$CERT_PATH" -k "$KEYCHAIN_PATH" -P "$CERT_PASSWORD" \ -T /usr/bin/codesign -T /usr/bin/security security set-key-partition-list -S apple-tool:,apple: -s -k "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH" security list-keychains -d user -s "$KEYCHAIN_PATH" login.keychain-db echo "KEYCHAIN_PATH=$KEYCHAIN_PATH" >> "$GITHUB_ENV" - name: Install provisioning profile env: PROFILE_BASE64: ${{ secrets.APPLE_PROVISIONING_PROFILE_BASE64 }} run: | PROFILE_PATH="$RUNNER_TEMP/profile.mobileprovision" echo "$PROFILE_BASE64" | base64 --decode -o "$PROFILE_PATH" mkdir -p ~/Library/MobileDevice/Provisioning\ Profiles cp "$PROFILE_PATH" ~/Library/MobileDevice/Provisioning\ Profiles/ - name: Read version from Xcode project run: | PBXPROJ="apps/stage-pocket/ios/App/App.xcodeproj/project.pbxproj" VERSION="$(sed -n 's/.*MARKETING_VERSION = \(.*\);/\1/p' "$PBXPROJ" | head -1 | tr -d ' ')" BUILD_NUMBER="$(sed -n 's/.*CURRENT_PROJECT_VERSION = \(.*\);/\1/p' "$PBXPROJ" | head -1 | tr -d ' ')" echo "VERSION=$VERSION" >> "$GITHUB_ENV" echo "BUILD_NUMBER=$BUILD_NUMBER" >> "$GITHUB_ENV" - name: Archive iOS run: | xcodebuild archive \ -project apps/stage-pocket/ios/App/App.xcodeproj \ -scheme App \ -configuration Release \ -destination "generic/platform=iOS" \ -archivePath "$RUNNER_TEMP/App.xcarchive" \ CURRENT_PROJECT_VERSION=${{ env.BUILD_NUMBER }} - name: Export IPA run: | xcodebuild -exportArchive \ -archivePath "$RUNNER_TEMP/App.xcarchive" \ -exportPath "$RUNNER_TEMP/output" \ -exportOptionsPlist apps/stage-pocket/ios/ExportOptions.plist - name: Rename IPA for distribution run: | IPA_SOURCE="$(find "$RUNNER_TEMP/output" -name '*.ipa' -print -quit)" IPA_NAME="${PRODUCT_NAME}-${VERSION}_(${BUILD_NUMBER})-ios.ipa" mv "$IPA_SOURCE" "$RUNNER_TEMP/output/$IPA_NAME" echo "IPA_NAME=$IPA_NAME" >> "$GITHUB_ENV" - name: Upload IPA artifact uses: actions/upload-artifact@v4 with: name: ${{ env.IPA_NAME }} path: ${{ runner.temp }}/output/${{ env.IPA_NAME }} if-no-files-found: error - name: Install Transporter if: ${{ env.UPLOAD_TESTFLIGHT == 'true' }} run: | curl -fsSL "https://itunesconnect.apple.com/WebObjects/iTunesConnect.woa/ra/resources/download/public/Transporter__OSX/bin/" -o "$RUNNER_TEMP/itmstransporter.pkg" sudo installer -pkg "$RUNNER_TEMP/itmstransporter.pkg" -target / rm -f "$RUNNER_TEMP/itmstransporter.pkg" - name: Upload to TestFlight if: ${{ env.UPLOAD_TESTFLIGHT == 'true' }} uses: apple-actions/upload-testflight-build@v4 with: app-path: ${{ runner.temp }}/output/${{ env.IPA_NAME }} issuer-id: ${{ secrets.APPSTORE_CONNECT_API_ISSUER_ID }} api-key-id: ${{ secrets.APPSTORE_CONNECT_API_KEY_ID }} api-private-key: ${{ secrets.APPSTORE_CONNECT_API_KEY }} - name: Upload to GitHub Releases if: ${{ env.RELEASE_TAG != '' }} uses: softprops/action-gh-release@v2 with: files: ${{ runner.temp }}/output/${{ env.IPA_NAME }} append_body: true fail_on_unmatched_files: true tag_name: ${{ env.RELEASE_TAG }} - name: Clean up signing if: always() run: | if [ -n "$KEYCHAIN_PATH" ]; then security delete-keychain "$KEYCHAIN_PATH" 2>/dev/null || true fi rm -f ~/Library/MobileDevice/Provisioning\ Profiles/*.mobileprovision publish-gitcode-release: name: Publish GitCode Release Mirror needs: build if: ${{ always() && github.repository == 'moeru-ai/airi' && needs.build.result == 'success' && (github.event_name == 'release' || (github.event_name == 'workflow_dispatch' && inputs.tag != '')) }} continue-on-error: true runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 - name: Validate release mirror tools run: | gh --version jq --version curl --version - name: Publish release assets to GitCode env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITCODE_TOKEN: ${{ secrets.GITCODE_TOKEN }} GITCODE_OWNER: ${{ secrets.GITCODE_OWNER }} GITCODE_REPO: ${{ secrets.GITCODE_REPO }} GITCODE_RELEASE_TAG: ${{ github.event.release.tag_name || inputs.tag }} run: bash .github/scripts/publish-gitcode-release.sh