6 Commits

Author SHA1 Message Date
Millian Lamiaux
bebe381100 fix(ci): drop node from brew install — unused, triggers gemini-cli rebuild
All checks were successful
CI / YouTube Worker (pull_request) Has been skipped
CI / Deploy (pull_request) Has been skipped
CI / Detect Changes (pull_request) Successful in 4s
PR → Build → devicectl Deploy → LGTM / Build & Deploy to iPhone (devicectl) (pull_request) Successful in 1m39s
PR → Build → devicectl Deploy → LGTM / Wait for LGTM comment (pull_request) Successful in 33s
Run failed at 'brew install xcodegen node' AFTER both formulae upgraded
successfully under ARM: brew's 'installed dependents check' then tried to
rebuild gemini-cli (0.40.1 -> 0.46.0, an unrelated dev tool on the runner
that depends on node) and exited 1.

Verified node is NOT used by any step in pr-iphone-deploy.yml — only
xcodegen is. The only node references in the repo are in
scripts/prepare-release.cjs and scripts/verify-admin-web-docker.sh,
neither of which runs in this workflow. Node was a historical leftover.

Drop node from the brew install. No node upgrade = no dependents check =
no gemini-cli rebuild. Also sync AGENTS.md §5 step 4.
2026-07-19 20:54:17 +02:00
Millian Lamiaux
437ec130fe fix(ci): run brew under ARM + drop ios-deploy, use devicectl only
Some checks failed
CI / Detect Changes (pull_request) Successful in 4s
CI / YouTube Worker (pull_request) Has been skipped
CI / Deploy (pull_request) Has been skipped
PR → Build → devicectl Deploy → LGTM / Build & Deploy to iPhone (devicectl) (pull_request) Failing after 30s
PR → Build → devicectl Deploy → LGTM / Wait for LGTM comment (pull_request) Has been skipped
Two related fixes to the Install tools / Deploy steps:

1. Rosetta/ARM-prefix conflict (unblocks run #142 failure)
   Run #142 failed at 'brew install':
     Cannot install under Rosetta 2 in ARM default prefix (/opt/homebrew)!
   The act_runner shell runs under Rosetta 2 on Apple Silicon, but Homebrew
   is installed natively in /opt/homebrew (ARM prefix). Brew refuses to mix
   architectures and exits 1 — surfaced now because xcodegen/node registered
   as outdated and brew tried to upgrade them.
   Prefix the brew call with 'arch -arm64' (brew's own suggested workaround)
   so the subprocess runs natively regardless of the parent shell's arch.

2. Drop ios-deploy, use xcrun devicectl only
   The deploy step previously tried devicectl (WiFi) first, then fell back
   to ios-deploy (USB). devicectl handles both network and wired discovery
   natively, so the separate USB-fallback tool is unnecessary — removed
   from the brew install and the deploy step.
   Implication: a devicectl failure = deploy failure (no second tool to
   fall back to). Acceptable: devicectl covers both transports.

Also updates workflow name/job labels and the 'Prêt à tester' comment to
drop 'WiFi/USB' wording, and aligns AGENTS.md §5 step 4 (arch -arm64 +
tool list) and step 7 (devicectl only).
2026-07-19 20:47:38 +02:00
Millian Lamiaux
244270488a fix(ci): only run pr-iphone-deploy when tabatago-swift or the workflow changes
Some checks failed
CI / Detect Changes (pull_request) Successful in 3s
CI / YouTube Worker (pull_request) Has been skipped
CI / Deploy (pull_request) Has been skipped
PR → Build → WiFi/USB Deploy → LGTM / Build & Deploy to iPhone (WiFi/USB) (pull_request) Failing after 14s
PR → Build → WiFi/USB Deploy → LGTM / Wait for LGTM comment (pull_request) Has been skipped
Add a paths: filter to the pull_request trigger so the iOS build+deploy
only fires when the PR actually touches:
  - tabatago-swift/**   (the iOS/watchOS app source)
  - .github/workflows/pr-iphone-deploy.yml  (this workflow itself)

PRs touching only AGENTS.md, admin-web/, youtube-worker/, supabase/,
docs/, or root config no longer spin up the macOS runner — no point
rebuilding and deploying an unchanged .app.

Including the workflow file itself in paths: ensures a PR that only
edits the workflow logic still triggers it (so this branch's own PR
will run to validate the changes).
2026-07-19 18:44:28 +02:00
Millian Lamiaux
756b1a405c docs(agents): align CI docs (§5/§7/§8) with real workflow behavior
Rewrite AGENTS.md to match the corrected pr-iphone-deploy.yml:

§5 Workflow:
- Clean step: 'GARDE build/spm-cache' (not ../build/spm-cache), explicit
  warning never to 'rm -rf build/', note that Xcode global cache is spared.
- New step 5: write Secrets.xcconfig from Gitea secrets before xcodegen.
- Scheme lists 4 targets (TabataGo, TabataGoWidget, TabataGoWatch,
  TabataGoWatchWidget) — clarify TabataGoWidget is iOS, not watchOS.
- wait-approval: re-fetches all comments each cycle, regex \bLGTM\b/\bKO\b
  anywhere in body, squash-merge.
- Document concurrency block and timeouts.

§5 Secrets table:
- Replace false 'Injectés via Config/Secrets.xcconfig → Info.plist' with
  the real flow (CI writes file from secrets, Info.plist reads $(VAR)).
- Add historical-exposure warning: Secrets.xcconfig was committed before
  the gitignore rule; key rotation recommended.

§5 Pitfalls + §8 anti-patterns:
- 'rm -rf build/' (not just 'rm -rf cache SPM').
- Don't nuke ~/Library/Caches/com.apple.dt.Xcode.
- permissions: block is intentionally absent (no-op under Gitea API).
- Don't remove concurrency block.
- Don't anchor LGTM grep to body start.
- {"Do":"squash"} not {"Do":"merge"}.
- Scheme must list all 4 targets.

§7 rule #9: expanded with the CI-writes-file flow.
2026-07-19 16:51:02 +02:00
Millian Lamiaux
0b476f5871 fix(project.yml): build watch targets in TabataGo scheme
The TabataGo scheme only listed TabataGo + TabataGoWidget (iOS), so
xcodebuild -scheme TabataGo never compiled the watchOS app in CI. Add
TabataGoWatch + TabataGoWatchWidget to the build targets so watch-side
breakage is caught before deploy.

Note: TabataGoWidget is a real iOS widget target (defined at project.yml
line 165, app-extension/iOS), NOT a phantom — the prior review was wrong
on that point. The real gap was the missing watch targets.

Embed deps were already correct (TabataGo embeds TabataGoWatch, which
embeds TabataGoWatchWidget). Verified locally with xcodegen 2.45.4:
the generated xcscheme now lists all 4 BlueprintNames.

⚠️ Risk: if the watch target has latent build issues (never compiled in
CI before), this will surface them on the next PR. Desirable — catch
before merge, not in the field.
2026-07-19 16:50:47 +02:00
Millian Lamiaux
d50e53e4ad fix(ci): pr-iphone-deploy — cache, secrets, LGTM regex, concurrency, squash
10 fixes to .github/workflows/pr-iphone-deploy.yml:

Cache & build hygiene:
- Stop deleting build/spm-cache (was wiped by 'rm -rf build/'); clean only
  build/derived. RevenueCat (~1.1 GiB) is finally actually preserved.
- Stop nuking ~/Library/Caches/com.apple.dt.Xcode (slows every next build).
- Drop '|| true' on brew install — fail loudly if brew is down.
- Bump build-deploy timeout 20m → 30m for cold builds.

Secrets:
- New step writes Config/Secrets.xcconfig from Gitea secrets (SUPABASE_URL,
  SUPABASE_ANON_KEY, REVENUECAT_API_KEY, POSTHOG_API_KEY) before xcodegen.
  Preflight fails loud if a required secret is missing. Heredoc body at
  column 0 (xcconfig keys are whitespace-sensitive).

LGTM/KO handling:
- Regex now matches anywhere in body with word boundary: \bLGTM\b / \bKO\b.
  'Tested, LGTM!' counts; 'KOM' / 'LGTMX' / 'OK' do not.
- Re-fetch ALL comments each poll (not since_id) to catch edits.
- Squash-merge ({"Do":"squash"}) instead of merge commit.

Workflow structure:
- Add concurrency block (per-PR, cancel-in-progress) — prevents double-merge
  and stacked pipelines on successive pushes.
- Remove no-op 'permissions:' block (all mutations go via Gitea API with
  PR_API_TOKEN, native GITHUB_TOKEN is never used).
2026-07-19 16:50:36 +02:00
4 changed files with 26 additions and 99 deletions

View File

@@ -8,11 +8,12 @@ on:
pull_request:
branches: [main]
types: [opened, synchronize, reopened]
# NOTE: no trigger-level `paths:` here — Gitea Actions does not reliably
# honor `on.pull_request.paths:` filters (the workflow silently fails to
# trigger even when changed files match). Path gating is done at the job
# level via dorny/paths-filter@v3 in the `changes` job below (same pattern
# as ci.yml). Docs/backend-only PRs then skip the macOS runner.
# Only run the iOS build+deploy when the PR actually touches the Swift app
# or this workflow itself. Docs-only / backend-only / admin-web-only PRs
# skip the runner entirely (no point rebuilding an unchanged .app).
paths:
- 'tabatago-swift/**'
- '.github/workflows/pr-iphone-deploy.yml'
# 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
@@ -25,29 +26,8 @@ concurrency:
jobs:
# ── Path filter — determines whether the macOS build+deploy is worth running ──
# Gitea Actions does not honor trigger-level `on.pull_request.paths:`, so we
# gate at the job level with dorny/paths-filter (same pattern as ci.yml).
changes:
name: Detect Changes
runs-on: ubuntu-latest
outputs:
ios: ${{ steps.filter.outputs.ios }}
steps:
- uses: actions/checkout@v4
- uses: dorny/paths-filter@v3
id: filter
with:
filters: |
ios:
- 'tabatago-swift/**'
- '.github/workflows/pr-iphone-deploy.yml'
build-deploy:
name: Build & Deploy to iPhone (devicectl)
needs: changes
if: needs.changes.outputs.ios == 'true'
runs-on: macos
timeout-minutes: 30
@@ -192,25 +172,15 @@ jobs:
run: |
PR="${{ github.event.pull_request.number }}"
REPO="${{ github.repository }}"
# The HTML comment is invisible in Gitea's rendered markdown but is
# present in the raw body — wait-approval uses it as a sentinel to
# skip THIS bot comment when scanning for LGTM/KO (otherwise the
# instruction text "Reply LGTM pour merger" would self-trigger a
# merge ~30s after deploy). DO NOT remove or reword without also
# updating the scanner in the wait-approval job.
curl -s -X POST \
-H "Authorization: token ${GT_TOKEN}" \
-H "Content-Type: application/json" \
-d "{\"body\":\"## 📱 Prêt à tester !\\n\\nL'app est déployée sur l'iPhone (devicectl).\\n\\n<!-- tabatago:ready-to-test -->\\n\\n- Teste les changements\\n- Reply **LGTM** pour merger\\n- Reply **KO** pour bloquer\"}" \
-d "{\"body\":\"## 📱 Prêt à tester !\\n\\nL'app est déployée sur l'iPhone (devicectl).\\n\\n- Teste les changements\\n- Reply **LGTM** pour merger\\n- Reply **KO** pour bloquer\"}" \
"${GITEA_URL}/api/v1/repos/${REPO}/issues/${PR}/comments"
wait-approval:
name: Wait for LGTM comment
needs: build-deploy
# Only run when build-deploy actually deployed. Without this, a skipped
# build-deploy (filtered out by `changes`) would still launch this job and
# the merge poll would run against a PR that was never deployed to device.
if: needs.build-deploy.result == 'success'
runs-on: macos
timeout-minutes: 120
@@ -232,19 +202,6 @@ jobs:
# 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.
#
# Parsing is done with python3 (preinstalled on the macOS runner and
# already used by scripts/ci-status.py) rather than grep over raw JSON:
# - the bot's "Ready to test" comment body literally contains the
# words LGTM/KO as instructions to the reviewer, so any naive
# body grep would self-match and auto-merge ~30s after deploy.
# We skip it via the <!-- tabatago:ready-to-test --> sentinel
# embedded by the build-deploy job;
# - json.loads avoids false matches when the literal "body" key or
# trigger words appear inside another string field.
# KO is checked BEFORE LGTM so a PR with both signals blocks (matches
# the existing "KO blocks" intent — a reviewer who flip-flops shouldn't
# merge just because an older LGTM is still in the history).
while [ $TRIES -lt $MAX ]; do
sleep 30
@@ -253,50 +210,23 @@ jobs:
COMMENTS=$(curl -s -H "Authorization: token ${GT_TOKEN}" \
"${API}/issues/${PR}/comments?limit=50&page=1")
DECISION=$(python3 -c '
import json, re, sys
MARKER = "<!-- tabatago:ready-to-test -->"
try:
comments = json.loads(sys.argv[1] or "[]")
except Exception:
comments = []
lgtm = re.compile(r"\bLGTM\b", re.IGNORECASE)
ko = re.compile(r"\bKO\b", re.IGNORECASE)
hit_lgtm = hit_ko = False
for c in comments:
body = c.get("body") or ""
if MARKER in body:
# Skip bot comments — their body contains LGTM/KO as reviewer
# instructions and would otherwise self-trigger a merge.
continue
if ko.search(body):
hit_ko = True
if lgtm.search(body):
hit_lgtm = True
if hit_ko:
print("KO")
elif hit_lgtm:
print("LGTM")
else:
print("PENDING")
' "$COMMENTS")
# 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\":\"squash\"}" \
"${API}/pulls/${PR}/merge"
echo "✅ Mergée (squash)."
exit 0
fi
case "$DECISION" in
LGTM)
echo "✅ LGTM reçu ! Squash-merge..."
curl -s -X POST \
-H "Authorization: token ${GT_TOKEN}" \
-H "Content-Type: application/json" \
-d "{\"Do\":\"squash\"}" \
"${API}/pulls/${PR}/merge"
echo "✅ Mergée (squash)."
exit 0
;;
KO)
echo "❌ KO reçu. Bloquée."
exit 1
;;
esac
if echo "$COMMENTS" | grep -qiE '"body":[[:space:]]*"[^"]*\bKO\b'; then
echo "❌ KO reçu. Bloquée."
exit 1
fi
if [ $((TRIES % 4)) -eq 0 ]; then
echo " ⏳ ... (${TRIES}/240, $(date +%H:%M))"

View File

@@ -167,7 +167,7 @@ Complications : `TabataGoComplication`.
6. **xcodegen generate**`xcodebuild -resolvePackageDependencies`**build** (`-scheme TabataGo`, Debug, auto-provisioning, team `2MJF39L8VY`). Le scheme build les 4 targets : `TabataGo` (app iOS), `TabataGoWidget` (widget iOS, vrai target — ne pas confondre avec `TabataGoWatchWidget` qui est watchOS), `TabataGoWatch` (app watchOS), `TabataGoWatchWidget` (complication watchOS).
7. **Deploy iPhone** UDID `00008120-000925CE3672201E` : `xcrun devicectl device install app` uniquement. `devicectl` gère nativement la découverte WiFi (réseau) et filaire (USB-C/Thunderbolt) — pas de fallback `ios-deploy`.
8. **Post comment** "Prêt à tester" sur la PR.
9. **Job `wait-approval`** : poll (30s, max 240 = 2h). **Re-fetche TOUS les comments** chaque cycle (pas `since_id`) pour attraper aussi les edits (un reviewer passant de "KO" à "LGTM"). Le parsing se fait en **python3** (`json.loads` + regex, pas de `grep` sur JSON brut). **Le commentaire bot "Prêt à tester" porte un marker sentinel `<!-- tabatago:ready-to-test -->`** et est **ignoré** par le scanner — sinon son propre body (qui mentionne LGTM/KO comme instructions au reviewer) déclencherait un auto-merge ~30s après le deploy (bug historique). KO est checké **avant** LGTM (un PR avec les deux signaux bloque). Match LGTM/KO n'importe où dans le body (regex `\bLGTM\b` / `\bKO\b`, case-insensitive) — "Tested, LGTM!" compte ; "KOM" ne compte pas. `LGTM`**squash-merge** (`{"Do":"squash"}`). `KO` → blocage. Timeout → fail.
9. **Job `wait-approval`** : poll (30s, max 240 = 2h). **Re-fetche TOUS les comments** chaque cycle (pas `since_id`) pour attraper aussi les edits (un reviewer passant de "KO" à "LGTM"). Match LGTM/KO n'importe où dans le body (regex `\bLGTM\b` / `\bKO\b`, case-insensitive) — "Tested, LGTM!" compte ; "KOM" ne compte pas. `LGTM`**squash-merge** (`{"Do":"squash"}`). `KO` → blocage. Timeout → fail.
### Secrets
@@ -187,7 +187,6 @@ Complications : `TabataGoComplication`.
- `-skipPackagePluginValidation -allowProvisioningUpdates`.
- **Concurrency** : le bloc `concurrency:` est volontaire — ne pas le retirer, sinon les pushes successifs empilent des pipelines et tentent des double-merges.
- **LGTM/KO regex** : matche n'importe où dans le body avec `\b...\b` (word boundary). Ne pas revenir à un `grep '"body": *"LGTM"'` ancré — il raterait "Tested, LGTM!".
- **Self-match sentinel** : le commentaire bot "Prêt à tester" contient littéralement `Reply **LGTM** pour merger` et `Reply **KO**` — sans précaution, le scanner matche son **propre** commentaire et auto-merge ~30s après le deploy. Le body porte donc un marker `<!-- tabatago:ready-to-test -->` que `wait-approval` ignore. **Tout commentaire bot posté par ce workflow doit porter ce marker** ; ne pas le retirer ni poster d'autre commentaire contenant LGTM/KO sans marker.
- **Squash-merge** (`{"Do":"squash"}`) — pas merge commit ni rebase. Garde l'historique `main` linéaire.
- **Comment edits** : `wait-approval` re-fetche tous les comments chaque cycle (pas `since_id`) pour attraper les edits.
@@ -250,8 +249,6 @@ Node.js (`server.js`, `package.json`, `Dockerfile`). Télécharge l'audio de pla
| Compter sur un bloc `permissions:` natif Gitea Actions | Tout passe par l'API Gitea avec `PR_API_TOKEN` ; le bloc natif serait no-op |
| Retirer le bloc `concurrency:` | Permet double-merge et pipelines empilés |
| Ancre le grep LGTM au début du body (`'"body": *"LGTM"'`) | Regex `\bLGTM\b` n'importe où dans le body (attrape "Tested, LGTM!") |
| Poster un commentaire bot dont le body contient "LGTM"/"KO" sans le marker `<!-- tabatago:ready-to-test -->` | Le scanner bot-scan ignore les comments portant le marker — tout commentaire bot du workflow doit l'inclure (sinon auto-merge en ~30s) |
| Scanner les comments en `grep` sur le JSON brut | `python3` + `json.loads` (gère unicode/quotes, pas de false match sur la clé `"body"`) |
| `{"Do":"merge"}` (merge commit) | `{"Do":"squash"}` pour un `main` linéaire |
| Supprimer le scheme explicite dans `project.yml` | xcodegen 2.45.4 n'en crée pas — scheme doit lister `TabataGo`/`TabataGoWidget`/`TabataGoWatch`/`TabataGoWatchWidget` |
| Compter sur `admin-web/` pour la app iOS | Dashboard admin séparé, communique via Supabase uniquement |

View File

@@ -1,6 +1,6 @@
{
"name": "my-app",
"version": "1.0.1",
"version": "1.0.0",
"private": true,
"scripts": {
"dev": "next dev",

View File

@@ -1,3 +1,3 @@
{
"version": "1.0.1"
"version": "1.0.0"
}