From 00c8c12a0fb9e664022b0a19bd3fbd3ccc66da9d Mon Sep 17 00:00:00 2001 From: enricobuehler Date: Thu, 23 Jul 2026 17:10:04 +0200 Subject: [PATCH] feat(ci/apple): export the iOS .ipa to the release + run artifacts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The iOS leg archives with development signing and re-signs for App Store distribution at export time, but the existing export uses destination=upload (straight to TestFlight) and leaves no .ipa on disk. Add an export step that re-exports the SAME archive with destination=export to produce an App Store distribution-signed .ipa, then: * attach it as the `punktfunk-ios-ipa` run artifact on every build (upload-artifact@v3, since Gitea's backend rejects @v4), and * attach it to the unified Gitea release on vX.Y.Z tags (alongside the DMG), via the shared ensure_release/upsert_asset helpers. Same gate as the archive; warn+skip (never fails the best-effort iOS leg) if the archive is absent. Note: an App Store-signed .ipa installs only via TestFlight/App Store, not by direct sideload — it is a release/archival artifact. Co-Authored-By: Claude Opus 4.8 (1M context) --- .gitea/workflows/release.yml | 70 ++++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/.gitea/workflows/release.yml b/.gitea/workflows/release.yml index 6dd40c6b..f4c4cce8 100644 --- a/.gitea/workflows/release.yml +++ b/.gitea/workflows/release.yml @@ -393,6 +393,76 @@ jobs: -authenticationKeyID "${{ secrets.ASC_API_KEY_ID }}" \ -authenticationKeyIssuerID "${{ secrets.ASC_API_ISSUER_ID }}" + - name: iOS — export .ipa (Gitea release + run artifact) + # The TestFlight step above uploads straight to App Store Connect (destination=upload) and + # leaves NO .ipa on disk. Re-export the SAME archive with destination=export to get an + # App Store distribution-signed .ipa for the Gitea release + the run artifacts. Same gate as + # that archive; a warn+skip (never fails the best-effort iOS leg) if the archive is absent, + # e.g. a workflow_dispatch with testflight=false. NOTE: an App Store-signed .ipa installs + # only via TestFlight/App Store, not by direct sideload — it's a release/archival artifact. + if: gitea.event_name != 'workflow_dispatch' || inputs.testflight == 'true' + id: ios_ipa + run: | + ARCHIVE="$RUNNER_TEMP/Punktfunk-ios.xcarchive" + if [ ! -d "$ARCHIVE" ]; then + echo "::warning::iOS archive not found — skipping .ipa export" + exit 0 + fi + PROFILE="Punktfunk iOS App Store Distribution" + WIDGET_PROFILE="Punktfunk iOS Widgets App Store Distribution" + # destination=export writes the .ipa to -exportPath; otherwise identical manual signing to + # the upload plist (both profiles, Apple Distribution). No ASC key needed — no network. + cat > "$RUNNER_TEMP/export-appstore-ipa.plist" < + + + + methodapp-store-connect + destinationexport + teamID$TEAM_ID + signingStylemanual + signingCertificateApple Distribution + provisioningProfiles + + io.unom.punktfunk$PROFILE + io.unom.punktfunk.widgets$WIDGET_PROFILE + + + + EOF + DEVELOPER_DIR="$XCODE_DEV_DIR" xcodebuild -exportArchive \ + -archivePath "$ARCHIVE" \ + -exportOptionsPlist "$RUNNER_TEMP/export-appstore-ipa.plist" \ + -exportPath "$RUNNER_TEMP/export-ipa" + SRC=$(ls "$RUNNER_TEMP/export-ipa/"*.ipa 2>/dev/null | head -1) + [ -n "$SRC" ] || { echo "::warning::no .ipa was produced by export"; exit 0; } + mkdir -p "$GITHUB_WORKSPACE/dist" + IPA="$GITHUB_WORKSPACE/dist/Punktfunk-$VERSION.ipa" + mv "$SRC" "$IPA" + echo "IPA=$IPA" >> "$GITHUB_ENV" + echo "ipa=dist/Punktfunk-$VERSION.ipa" >> "$GITHUB_OUTPUT" + echo "exported $IPA" + + - name: Attach .ipa to the workflow run + if: steps.ios_ipa.outputs.ipa != '' + # v3, not v4: Gitea's artifact backend identifies as GHES, which upload-artifact@v4 refuses + # (same reason as android.yml / apple.yml). Download is a zip of the .ipa. + uses: actions/upload-artifact@v3 + with: + name: punktfunk-ios-ipa + path: ${{ steps.ios_ipa.outputs.ipa }} + if-no-files-found: warn + retention-days: 30 + + - name: Attach .ipa to the Gitea release (stable tags only) + if: startsWith(gitea.ref, 'refs/tags/v') && steps.ios_ipa.outputs.ipa != '' + env: + GITEA_TOKEN: ${{ secrets.REGISTRY_TOKEN }} + run: | + . scripts/ci/gitea-release.sh + RID=$(ensure_release "$GITHUB_REF_NAME" "$GITHUB_REF_NAME" auto) + upsert_asset "$RID" "$IPA" "Punktfunk-$VERSION.ipa" + - name: tvOS — archive + upload to TestFlight # Canary + stable, the same track as iOS/macOS — the tvOS xcframework slice is now built # on every apple push (above), so this matches the iOS step's gate exactly.