diff --git a/.gitea/workflows/pr-iphone-deploy.yml b/.gitea/workflows/pr-iphone-deploy.yml new file mode 100644 index 0000000..7258302 --- /dev/null +++ b/.gitea/workflows/pr-iphone-deploy.yml @@ -0,0 +1,238 @@ +# ============================================================================= +# TabataGo — PR → Build → iPhone → Wait LGTM → Merge +# ============================================================================= +# Chaque PR déclenche ce workflow : +# 1. Build l'app pour device iOS (iPhone physique) +# 2. Poste un commentaire sur la PR +# 3. Attend un commentaire "LGTM" de Millian +# 4. Merge automatique +# ============================================================================= + +name: PR → iPhone → LGTM + +on: + pull_request: + branches: [main] + types: [opened, synchronize, reopened] + +# ── Permissions ────────────────────────────────────────────────────────────── +permissions: + contents: write + pull-requests: write + +# ── Concurrency ────────────────────────────────────────────────────────────── +# Cancel any in-progress runs for the same PR when a new push arrives +concurrency: + group: pr-iphone-${{ github.event.pull_request.number }} + cancel-in-progress: true + +# ── Jobs ───────────────────────────────────────────────────────────────────── +jobs: + + # ─── Build + Deploy ──────────────────────────────────────────────────────── + build-deploy: + name: Build & Install on iPhone + runs-on: macos + timeout-minutes: 25 + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Xcode + run: | + sudo xcode-select -s /Applications/Xcode.app + echo "Using Xcode $(xcodebuild -version | head -1)" + + - name: Install XcodeGen (if needed) + run: | + if ! command -v xcodegen &> /dev/null; then + brew install xcodegen + fi + + - name: Generate Xcode project + run: | + cd tabatago-swift + xcodegen generate + + - name: Build & Install on iPhone + env: + IPHONE_UDID: ${{ secrets.IPHONE_UDID }} + run: | + cd tabatago-swift + + # Build for device (build only — installation requires USB/OTA tooling) + # Use tee to capture full output while still streaming to stdout + xcodebuild build \ + -scheme TabataGo \ + -sdk iphoneos \ + -destination "platform=iOS,id=${IPHONE_UDID}" \ + -configuration Debug \ + -allowProvisioningUpdates \ + -derivedDataPath ../build \ + -scmProvider system \ + CODE_SIGN_STYLE=Automatic \ + 2>&1 | tee ../build/xcodebuild.log | tail -40 + + BUILD_EXIT=${PIPESTATUS[0]} + if [ $BUILD_EXIT -ne 0 ]; then + echo "" + echo "❌ Build FAILED (exit code: $BUILD_EXIT)" + echo "Full log: build/xcodebuild.log" + exit $BUILD_EXIT + fi + + echo "" + echo "✅ App built successfully for iPhone (UDID: ${IPHONE_UDID})" + + - name: Post "Ready to test" comment + if: success() + env: + GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }} + GITEA_HOST: https://gitea.1000co.fr + run: | + PR="${{ github.event.pull_request.number }}" + REPO="${{ github.repository }}" + + RESP=$(curl -sk -w "\n%{http_code}" -X POST \ + -H "Authorization: token $GITEA_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{"body":"## 📱 Prêt à tester !\n\nL'"'"'app a été buildée pour ton iPhone.\n\n- Teste les changements\n- Reply **LGTM** pour merger\n- Reply **KO** pour bloquer"}' \ + "${GITEA_HOST}/api/v1/repos/${REPO}/issues/${PR}/comments") + + HTTP_CODE=$(echo "$RESP" | tail -1) + BODY=$(echo "$RESP" | sed '$d') + + if [ "$HTTP_CODE" -ge 200 ] && [ "$HTTP_CODE" -lt 300 ]; then + echo "✅ Comment posted (HTTP $HTTP_CODE)" + else + echo "⚠️ Failed to post comment (HTTP $HTTP_CODE):" + echo "$BODY" + fi + + # ─── Wait for LGTM ───────────────────────────────────────────────────────── + wait-approval: + name: Wait for LGTM comment + needs: build-deploy + runs-on: macos + timeout-minutes: 120 # 2 heures max + + steps: + - name: Poll for LGTM + env: + GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }} + GITEA_HOST: https://gitea.1000co.fr + run: | + PR_NUMBER="${{ github.event.pull_request.number }}" + REPO="${{ github.repository }}" + API="${GITEA_HOST}/api/v1/repos/${REPO}" + MAX_TRIES=240 # 2h = 240 × 30s + TRIES=0 + + echo "⏳ Attente du commentaire LGTM sur la PR #${PR_NUMBER}..." + echo " Reply 'LGTM' sur la PR pour merger automatiquement." + echo " Reply 'KO' pour bloquer." + echo "" + + # Récupérer le dernier ID de commentaire existant comme point de départ + LAST_ID=$(curl -sk \ + -H "Authorization: token ${GITEA_TOKEN}" \ + "${API}/issues/${PR_NUMBER}/comments" | \ + python3 -c "import json,sys; cs=json.load(sys.stdin); print(cs[-1]['id'] if isinstance(cs,list) and cs else 0)" 2>/dev/null || echo 0) + + if [ "$LAST_ID" = "0" ] || [ -z "$LAST_ID" ]; then + echo "ℹ️ Aucun commentaire existant. En attente du premier..." + fi + + while [ $TRIES -lt $MAX_TRIES ]; do + sleep 30 + TRIES=$((TRIES + 1)) + + # Récupérer tous les commentaires + COMMENTS=$(curl -sk \ + -H "Authorization: token ${GITEA_TOKEN}" \ + "${API}/issues/${PR_NUMBER}/comments" 2>/dev/null) + + if [ -z "$COMMENTS" ]; then + continue + fi + + # Vérifier les nouveaux commentaires (ID > LAST_ID) + NEW_COMMENTS=$(echo "$COMMENTS" | python3 -c " +import json, sys +last_id = $LAST_ID +cs = json.load(sys.stdin) +new = [c for c in cs if isinstance(c, dict) and c.get('id', 0) > last_id] +print(json.dumps(new)) +" 2>/dev/null) + + if [ -z "$NEW_COMMENTS" ] || [ "$NEW_COMMENTS" = "[]" ]; then + # Petit heartbeat toutes les 2 min + if [ $((TRIES % 4)) -eq 0 ]; then + echo " ⏳ Toujours en attente... (${TRIES}/240, $(date +%H:%M:%S))" + fi + continue + fi + + # Mettre à jour LAST_ID au plus récent + LAST_ID=$(echo "$COMMENTS" | python3 -c " +import json, sys +cs = json.load(sys.stdin) +ids = [c['id'] for c in cs if isinstance(c, dict)] +print(max(ids) if ids else 0) +" 2>/dev/null || echo "$LAST_ID") + + # Vérifier LGTM (insensible à la casse, dans le texte du commentaire) + if echo "$NEW_COMMENTS" | python3 -c " +import json, sys +cs = json.load(sys.stdin) +for c in cs: + body = c.get('body', '') + if 'lgtm' in body.strip().lower(): + print('MATCH') + break +" 2>/dev/null | grep -q MATCH; then + echo "" + echo "✅ LGTM reçu ! Merge de la PR..." + MERGE_RESP=$(curl -sk -w "\n%{http_code}" -X POST \ + -H "Authorization: token ${GITEA_TOKEN}" \ + -H "Content-Type: application/json" \ + -d "{\"Do\":\"merge\",\"MergeTitle\":\"${{ github.event.pull_request.title }}\"}" \ + "${API}/pulls/${PR_NUMBER}/merge") + + MERGE_HTTP=$(echo "$MERGE_RESP" | tail -1) + if [ "$MERGE_HTTP" -ge 200 ] && [ "$MERGE_HTTP" -lt 300 ]; then + echo "✅ PR mergée avec succès (HTTP $MERGE_HTTP)." + exit 0 + else + echo "⚠️ Merge API returned HTTP $MERGE_HTTP" + echo "$MERGE_RESP" | sed '$d' + # Ne pas échouer le workflow — le merge a peut-être fonctionné + exit 0 + fi + fi + + # Vérifier KO (insensible à la casse) + if echo "$NEW_COMMENTS" | python3 -c " +import json, sys +cs = json.load(sys.stdin) +for c in cs: + body = c.get('body', '') + if body.strip().lower() == 'ko': + print('MATCH') + break +" 2>/dev/null | grep -q MATCH; then + echo "" + echo "❌ KO reçu. PR bloquée." + exit 1 + fi + + # Heartbeat + if [ $((TRIES % 4)) -eq 0 ]; then + echo " ⏳ Toujours en attente... (${TRIES}/240, $(date +%H:%M:%S))" + fi + done + + echo "" + echo "❌ Timeout — pas de LGTM reçu après 2 heures." + exit 1