diff --git a/.github/workflows/pr-iphone-deploy.yml b/.github/workflows/pr-iphone-deploy.yml index d7b6b42..9ed691e 100644 --- a/.github/workflows/pr-iphone-deploy.yml +++ b/.github/workflows/pr-iphone-deploy.yml @@ -9,16 +9,21 @@ on: branches: [main] types: [opened, synchronize, reopened] -permissions: - contents: write - pull-requests: write +# Every mutation (comments, merge) goes through the Gitea API with PR_API_TOKEN, +# never the runner's native GITHUB_TOKEN — so a `permissions:` block would be +# a no-op here. Intentionally omitted. +concurrency: + # Per-PR grouping: superseded pushes cancel the previous pipeline, different + # PRs still run in parallel. Prevents double-merges and wasted runner time. + group: pr-iphone-deploy-${{ github.event.pull_request.number }} + cancel-in-progress: true jobs: build-deploy: name: Build & Deploy to iPhone (WiFi/USB) runs-on: macos - timeout-minutes: 20 + timeout-minutes: 30 steps: - name: Checkout @@ -42,9 +47,11 @@ jobs: # Act runner caches rm -rf ~/.cache/act/*/hostexecutor/tabatago-swift/build 2>/dev/null || true - # SPM caches are KEPT between runs — RevenueCat is 1.10 GiB and takes + # SPM cache is KEPT between runs — RevenueCat is ~1.10 GiB and takes # 5+ minutes to clone; re-cloning on every CI run would waste time - # and cause random failures on the act_runner sandbox. + # and cause random failures on the act_runner sandbox. It lives at + # build/spm-cache (see -clonedSourcePackagesDirPath in the Build step) + # so NEVER `rm -rf build/` — only delete build/derived. # Xcode DerivedData — suppression COMPLÈTE (le bundle ID a changé, # l'ancien dossier TabataGo-* peut être ignoré par le nouveau build) @@ -52,10 +59,12 @@ jobs: # Module cache Xcode — des .pcm stale peuvent segfault le linker rm -rf ~/Library/Developer/Xcode/DerivedData/ModuleCache.noindex 2>/dev/null || true - rm -rf ~/Library/Caches/com.apple.dt.Xcode + # NOTE: do NOT `rm -rf ~/Library/Caches/com.apple.dt.Xcode` — that is the + # shared global cache; wiping it slows every subsequent build. - # Build artifacts locaux (utilisés par le step "Build app") - rm -rf build/ + # Build artifacts locaux — sparing build/spm-cache (see above). + rm -rf build/derived + rm -rf build/*.log 2>/dev/null || true rm -rf .build rm -rf tabatago-swift/.build rm -rf tabatago-swift/TabataGo.xcodeproj @@ -65,11 +74,40 @@ jobs: find . -name "Package.resolved" -delete 2>/dev/null || true find . -name ".package.resolved" -delete 2>/dev/null || true - echo "✅ Caches nettoyés" + echo "✅ Caches nettoyés (SPM cache préservé)" - name: Install tools (xcodegen, node, ios-deploy) run: | - brew install xcodegen node ios-deploy || true + # No `|| true` — if brew is down, fail loudly here instead of producing + # confusing "command not found" errors in the next steps. + brew install xcodegen node ios-deploy + + - name: Write Secrets.xcconfig from Gitea secrets + env: + SUPABASE_URL: ${{ secrets.SUPABASE_URL }} + SUPABASE_ANON_KEY: ${{ secrets.SUPABASE_ANON_KEY }} + REVENUECAT_API_KEY: ${{ secrets.REVENUECAT_API_KEY }} + POSTHOG_API_KEY: ${{ secrets.POSTHOG_API_KEY }} + run: | + # Preflight: fail loudly if a secret is missing, rather than producing + # an .app that silently can't reach Supabase/RevenueCat at runtime. + for v in SUPABASE_URL SUPABASE_ANON_KEY REVENUECAT_API_KEY; do + val="$(printenv "$v")" + if [ -z "$val" ]; then + echo "❌ Secret missing: $v. Add it to Gitea repo secrets." + exit 1 + fi + done + # POSTHOG_API_KEY may legitimately be empty (PostHog not yet wired). + # NOTE: heredoc body MUST start at column 0 — xcconfig keys are + # whitespace-sensitive, leading spaces would break the build setting. + cat > tabatago-swift/Config/Secrets.xcconfig </dev/null || echo 0) + # Re-fetch ALL comments each cycle (not since_id) so we also catch + # edits — e.g. a reviewer changing "KO" → "LGTM". Negligible cost for + # PRs with <50 comments. while [ $TRIES -lt $MAX ]; do sleep 30 TRIES=$((TRIES + 1)) COMMENTS=$(curl -s -H "Authorization: token ${GT_TOKEN}" \ - "${API}/issues/${PR}/comments?since_id=${LAST_ID}") + "${API}/issues/${PR}/comments?limit=50&page=1") - if echo "$COMMENTS" | grep -qi '"body": *"LGTM"'; then - echo "✅ LGTM reçu ! Merge..." + # Match LGTM/KO anywhere in the body (with a word boundary so "KOM" + # or "LGTMX" don't trigger). Case-insensitive. + if echo "$COMMENTS" | grep -qiE '"body":[[:space:]]*"[^"]*\bLGTM\b'; then + echo "✅ LGTM reçu ! Squash-merge..." curl -s -X POST \ -H "Authorization: token ${GT_TOKEN}" \ -H "Content-Type: application/json" \ - -d "{\"Do\":\"merge\"}" \ + -d "{\"Do\":\"squash\"}" \ "${API}/pulls/${PR}/merge" - echo "✅ Mergée." + echo "✅ Mergée (squash)." exit 0 fi - if echo "$COMMENTS" | grep -qi '"body": *"KO"'; then + if echo "$COMMENTS" | grep -qiE '"body":[[:space:]]*"[^"]*\bKO\b'; then echo "❌ KO reçu. Bloquée." exit 1 fi