diff --git a/.github/ISSUE_TEMPLATE/01-mvp-foundation.md b/.github/ISSUE_TEMPLATE/01-mvp-foundation.md new file mode 100644 index 0000000..66fe0de --- /dev/null +++ b/.github/ISSUE_TEMPLATE/01-mvp-foundation.md @@ -0,0 +1,15 @@ +--- +name: MVP Foundation +about: Fondations monorepo et architecture feature-first +title: "[MVP] Fondations monorepo Solid + Capacitor" +labels: ["mvp", "foundation"] +assignees: [] +--- + +## Objectif +Mettre en place la base technique feature-first et DDD simple. + +## Critères d'acceptation +- [ ] Workspaces apps/packages opérationnels +- [ ] Domain immutable pour base/variant/recipe +- [ ] Scripts qualité exécutables diff --git a/.github/ISSUE_TEMPLATE/02-builder-flow.md b/.github/ISSUE_TEMPLATE/02-builder-flow.md new file mode 100644 index 0000000..d9c349e --- /dev/null +++ b/.github/ISSUE_TEMPLATE/02-builder-flow.md @@ -0,0 +1,16 @@ +--- +name: Builder Flow +about: Parcours base -> variante -> recette +title: "[MVP] Builder flow 3 étapes" +labels: ["mvp", "feature"] +assignees: [] +--- + +## Objectif +Implémenter le wizard de construction bento. + +## Critères d'acceptation +- [ ] Filtre salé/sucré base +- [ ] Variante filtrée par base +- [ ] Recette fusionnée avec ordre de priorité +- [ ] Partage + impression + quantité diff --git a/.github/ISSUE_TEMPLATE/03-quality-security.md b/.github/ISSUE_TEMPLATE/03-quality-security.md new file mode 100644 index 0000000..58cb074 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/03-quality-security.md @@ -0,0 +1,15 @@ +--- +name: Quality and Security +about: CI quality gates et audits sécurité +title: "[MVP] Quality gates + security audits" +labels: ["mvp", "quality", "security"] +assignees: [] +--- + +## Objectif +Garantir qualité, sécurité et non-régression. + +## Critères d'acceptation +- [ ] Unit/integration/e2e en CI +- [ ] SAST + dependencies + secrets scan +- [ ] Lighthouse perf/seo/a11y bloquant diff --git a/.github/ISSUE_TEMPLATE/04-refactor-builder-solid.md b/.github/ISSUE_TEMPLATE/04-refactor-builder-solid.md new file mode 100644 index 0000000..cd58116 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/04-refactor-builder-solid.md @@ -0,0 +1,33 @@ +--- +name: Refactor Builder SOLID +about: Découpage SOLID du flux builder (BuilderFlow) +title: "[Refactor] Builder — découpage SOLID et responsabilités claires" +labels: ["refactor"] +assignees: [] +--- + +## Contexte + +La logique et l’UI du parcours builder sont concentrées dans `apps/web/src/ui/BuilderFlow.tsx` (fichier très volumineux). Le domaine pur existe déjà dans `packages/features/builder/src/domain.ts`. + +## Objectif + +Réduire la surface « tout-en-un » du flux builder et clarifier les frontières entre UI, état du wizard, et domaine (`@ben-to/builder`). + +## Pistes techniques + +- Extraire par **responsabilité unique** : étapes (base / variante / recette), barre d’actions, feuille recette, navigation « Continuer », synchronisation reset (`builder-reset-bus`). +- Introduire des **hooks / ports** testables (ex. `useBuilderSteps`, `useRecipeSheet`) pour isoler les effets et la lecture du catalogue. +- **DIP** : l’UI consomme le domaine (`domain.ts`) et des interfaces stables pour le catalogue, sans détails de parsing dispersés. +- Conserver / étendre les tests (`*.unit.test.ts`, e2e, Gherkin `@int-builder-*`). + +## Critères d’acceptation + +- [ ] Aucun fichier du flux builder > ~250–300 lignes sans justification documentée en commentaire de module. +- [ ] Tests unitaires / intégration et e2e verts ; pas de régression sur le parcours dans `specs/features/builder.feature`. + +## Références + +- `apps/web/src/ui/BuilderFlow.tsx` +- `packages/features/builder/src/domain.ts` +- `specs/features/builder.feature` diff --git a/.github/ISSUE_TEMPLATE/05-architecture-no-index-barrels.md b/.github/ISSUE_TEMPLATE/05-architecture-no-index-barrels.md new file mode 100644 index 0000000..c7d3990 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/05-architecture-no-index-barrels.md @@ -0,0 +1,36 @@ +--- +name: Architecture sans barrels index +about: Supprimer les index.ts barrel et verrouiller par règle / CI +title: "[Archi] Supprimer les index.ts barrel et verrouiller par règle projet" +labels: ["architecture"] +assignees: [] +--- + +## Contexte + +Les packages exposent des barrels `src/index.ts` avec `export * from "..."`. Les alias Vite dans `apps/web/vite.config.ts` pointent vers ces fichiers. Ce pattern complique le suivi des imports réels et la détection de code inutilisé. + +## Objectif + +Supprimer les barrels `index.ts`, migrer vers des imports **explicites**, et **verrouiller** la réintroduction du pattern (règles projet + CI). + +## Travail technique + +1. Remplacer les alias Vite (et chemins TypeScript si besoin) par des modules canoniques nommés (ex. `@ben-to/builder/domain`, `@ben-to/recipes/catalog`) ou par des sous-chemins `package.json` **`exports`** sans fichier nommé `index.ts`. +2. Mettre à jour tous les imports (`apps/web`, `packages/**`). +3. Supprimer les anciens `**/src/index.ts` après migration. + +## Règles / outillage + +- Étendre `.cursor/rules/ben-to-engineering.mdc` : pas de nouveau barrel `index.ts` ; imports depuis modules nommés. +- Optionnel : ESLint `no-restricted-imports` ou script `scripts/quality/` qui échoue si un `**/src/index.ts` barrel réapparaît. + +## Critères d’acceptation + +- [ ] Plus de `export * from` dans des `index.ts` à la racine des packages features/shared concernés. +- [ ] CI (`check:quality` ou lint) empêche la réintroduction du pattern (selon le niveau de durcissement retenu). + +## Références + +- `apps/web/vite.config.ts` +- `packages/features/*/src/index.ts`, `packages/shared/*/src/index.ts` diff --git a/.github/ISSUE_TEMPLATE/06-seo-recipe-og-twitter.md b/.github/ISSUE_TEMPLATE/06-seo-recipe-og-twitter.md new file mode 100644 index 0000000..d179f98 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/06-seo-recipe-og-twitter.md @@ -0,0 +1,38 @@ +--- +name: SEO recettes OG / réseaux sociaux +about: Métadonnées Open Graph et Twitter par URL /r/... +title: "[SEO] Métadonnées OG/Twitter par recette (/r/...)" +labels: ["seo"] +assignees: [] +--- + +## Contexte + +Les métadonnées Open Graph et Twitter sont définies une seule fois dans `apps/web/index.html`. Les URLs de recette `/r/...` doivent exposer **titre**, **description** et **image** adaptés au partage. + +**Contrainte SPA** : modifier `` ou les meta **uniquement en JavaScript après chargement** ne suffit souvent pas pour les robots d’aperçu (Facebook, X, LinkedIn) : ils lisent le HTML initial. + +## Objectif + +Pour chaque recette partageable, fournir des métadonnées correctes : `og:title`, `og:description`, `og:image`, `og:url`, `twitter:card` (idéalement `summary_large_image` si visuel adapté), éventuellement `canonical`. + +## Options à trancher (documenter le choix dans README ou `docs/`) + +| Approche | Effort | Aperçus réseaux | +|----------|--------|-----------------| +| Pré-rendu statique des routes `/r/*` au build (catalogue / pipeline recettes) | Moyen | Bon pour crawlers statiques | +| Middleware / route serveur (Worker, reverse proxy, petite app Node) injectant le HTML minimal avec meta | Variable | Très bon si bien déployé | +| Meta uniquement côté client (Solid) | Faible | Insuffisant pour la majorité des partages | + +Données : titres localisés, description courte, images de couverture produites par le pipeline recettes. + +## Critères d’acceptation + +- [ ] Partager une URL `/r/...` affiche un aperçu avec **titre et image recette** (validation manuelle : Meta Sharing Debugger / Twitter Card Validator ou équivalent). +- [ ] Balises OG/Twitter cohérentes (`og:type`, `og:url`, etc.). +- [ ] Documentation de la stratégie retenue (build vs serveur). + +## Références + +- `apps/web/index.html` +- Parcours e2e et URL partagée dans `tests/e2e/builder.e2e.ts` diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..fde5006 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,16 @@ +version: 2 +updates: + - package-ecosystem: npm + directory: "/" + schedule: + interval: weekly + open-pull-requests-limit: 15 + groups: + npm-all: + patterns: + - "*" + + - package-ecosystem: github-actions + directory: "/" + schedule: + interval: weekly diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 0000000..6431daa --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,13 @@ +## Ticket + +- [ ] Issue GitHub liée : #_____ +- [ ] Branche nommée de façon explicite (ex. `feat/issue-___-…`) + +## Vérifications + +- [ ] `npm run test:ci` exécuté localement avec succès +- [ ] Impact **mobile Android** pris en compte si pertinent (Capacitor / Gradle) + +## Notes pour les relecteurs + +<!-- Contexte, captures, risques --> diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..93d1f73 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,158 @@ +name: CI + +on: + pull_request: + push: + branches: [main] + +jobs: + quality: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v7 + - uses: actions/setup-node@v7 + with: + node-version: 22 + cache: npm + - run: npm ci + - run: npm run pipeline:recipes + - run: npm run check:quality + - run: npm run lint + - run: npm run typecheck + - run: npm run test:coverage + - name: Upload coverage + uses: actions/upload-artifact@v7 + with: + name: coverage-summary + path: | + coverage/coverage-summary.json + coverage/lcov.info + + e2e-and-a11y: + runs-on: ubuntu-latest + needs: quality + steps: + - uses: actions/checkout@v7 + - uses: actions/setup-node@v7 + with: + node-version: 22 + cache: npm + - run: npm ci + - run: npm run pipeline:recipes + - run: npx playwright install --with-deps + - run: npm run test:e2e + - name: Upload quality summaries (a11y + perf) + uses: actions/upload-artifact@v7 + with: + name: quality-e2e + path: quality-out/ + if-no-files-found: warn + + lighthouse: + runs-on: ubuntu-latest + needs: quality + steps: + - uses: actions/checkout@v7 + - uses: actions/setup-node@v7 + with: + node-version: 22 + cache: npm + - run: npm ci + - run: npm run audit:lighthouse + # .lighthouseci est dans .gitignore : upload-artifact exclut ces fichiers → aucun artefact « lighthouse-ci ». + # Copie vers un dossier non ignoré pour forcer l’upload réel des LHR. + - name: Stage LHCI reports for artifact + run: | + rm -rf __lhci_artifact__ + mkdir -p __lhci_artifact__ + cp -R .lighthouseci/. __lhci_artifact__/ + test "$(find __lhci_artifact__ -type f | wc -l)" -gt 0 || { echo "::error::LHCI n'a produit aucun fichier"; exit 1; } + - name: Upload Lighthouse CI output + uses: actions/upload-artifact@v7 + with: + name: lighthouse-ci + path: __lhci_artifact__ + if-no-files-found: error + + android-smoke: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v7 + - uses: actions/setup-node@v7 + with: + node-version: 22 + cache: npm + - uses: actions/setup-java@v5 + with: + distribution: temurin + java-version: 21 + - name: Setup Android SDK + uses: android-actions/setup-android@v4 + - run: sdkmanager "platforms;android-35" "build-tools;35.0.0" + - run: npm ci + - run: npm run pipeline:recipes + - run: npm run build:web + - run: npm run mobile:sync + - name: Gradle assembleDebug + working-directory: apps/mobile/android + run: chmod +x gradlew && ./gradlew assembleDebug --no-daemon + + pull-request-report: + if: github.event_name == 'pull_request' + needs: [quality, e2e-and-a11y, lighthouse] + runs-on: ubuntu-latest + permissions: + contents: read + pull-requests: write + steps: + - uses: actions/checkout@v7 + - uses: actions/setup-node@v7 + with: + node-version: 22 + cache: npm + - run: npm ci + - run: mkdir -p incoming + - run: npm audit --json > incoming/audit.json || true + # Même version majeure que upload-artifact (@v7) : v8 peut ne pas résoudre les artefacts v7. + - uses: actions/download-artifact@v8 + with: + name: coverage-summary + path: incoming/coverage + - uses: actions/download-artifact@v8 + with: + name: quality-e2e + path: incoming/e2e + - uses: actions/download-artifact@v8 + with: + name: lighthouse-ci + path: incoming/lhci + - run: node scripts/ci/pr-quality-report.mjs incoming incoming/pr-body.md + - uses: actions/github-script@v9 + with: + script: | + const fs = require('fs'); + const marker = '<!-- ben-to-quality-report -->'; + const body = marker + '\n' + fs.readFileSync('incoming/pr-body.md','utf8'); + const issue_number = context.payload.pull_request.number; + const { data: comments } = await github.rest.issues.listComments({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number, + per_page: 100 + }); + const existing = comments.find(c => c.body.includes(marker)); + if (existing) { + await github.rest.issues.updateComment({ + owner: context.repo.owner, + repo: context.repo.repo, + comment_id: existing.id, + body + }); + } else { + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number, + body + }); + } diff --git a/.github/workflows/dependency-review.yml b/.github/workflows/dependency-review.yml new file mode 100644 index 0000000..cd14776 --- /dev/null +++ b/.github/workflows/dependency-review.yml @@ -0,0 +1,22 @@ +name: Dependency review + +on: + pull_request: + +permissions: + contents: read + +jobs: + dependency-review: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v7 + - uses: actions/setup-node@v7 + with: + node-version: 22 + cache: npm + # L’action officielle `dependency-review-action` suppose Dependency graph + GitHub Advanced Security + # (réglages du dépôt). Sans GHAS elle échoue toujours sur les dépôts non éligibles. + # Même garde-fou que Security / deps-and-secrets : lockfile installable + npm audit. + - run: npm ci + - run: npm audit --audit-level=high diff --git a/.github/workflows/mobile-release.yml b/.github/workflows/mobile-release.yml new file mode 100644 index 0000000..8d4d8c3 --- /dev/null +++ b/.github/workflows/mobile-release.yml @@ -0,0 +1,50 @@ +name: Mobile Release + +on: + push: + tags: + - "mobile-v*" + workflow_dispatch: + +jobs: + android: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v7 + - uses: actions/setup-node@v7 + with: + node-version: 22 + cache: npm + - uses: actions/setup-java@v5 + with: + distribution: temurin + java-version: 21 + - name: Setup Android SDK + uses: android-actions/setup-android@v4 + - run: sdkmanager "platforms;android-35" "build-tools;35.0.0" + - run: npm ci + - run: npm run build:web + - run: npm run mobile:sync + - name: Gradle bundleDebug (sans keystore Play — artefact de validation CI) + working-directory: apps/mobile/android + run: chmod +x gradlew && ./gradlew bundleDebug --no-daemon + - uses: actions/upload-artifact@v7 + with: + name: android-bundle-debug + path: apps/mobile/android/app/build/outputs/bundle/debug/*.aab + if-no-files-found: error + + ios: + runs-on: macos-latest + steps: + - uses: actions/checkout@v7 + - uses: actions/setup-node@v7 + with: + node-version: 22 + cache: npm + - run: npm ci + - run: npm run build:web + - run: npm run mobile:sync + - name: iOS — projet non versionné ou machine locale + run: | + if [ -d apps/mobile/ios/App ]; then echo "Projet iOS présent — ouvrir Xcode pour archive."; else echo "Pas de dossier ios dans ce clone — ignorer pour Android-only."; fi diff --git a/.github/workflows/security.yml b/.github/workflows/security.yml new file mode 100644 index 0000000..9863f19 --- /dev/null +++ b/.github/workflows/security.yml @@ -0,0 +1,51 @@ +name: Security + +on: + pull_request: + push: + branches: [main] + schedule: + - cron: "0 5 * * 1" + +jobs: + codeql: + # PR depuis fork : pas d’upload SARIF vers le dépôt cible (permissions GitHub). + # Dépôt sans « Code scanning » activé (Settings → Code security) : l’upload échoue ; + # `upload: false` garde l’analyse CodeQL sans appeler l’API. Quand le scanning est + # activé sur le repo, retirer `upload: false` (comportement par défaut = upload). + if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository + runs-on: ubuntu-latest + permissions: + actions: read + contents: read + security-events: write + steps: + - uses: actions/checkout@v7 + - uses: github/codeql-action/init@v4 + with: + languages: javascript-typescript + - uses: github/codeql-action/analyze@v4 + with: + upload: false + + deps-and-secrets: + runs-on: ubuntu-latest + permissions: + contents: read + pull-requests: read + steps: + # Historique complet : gitleaks compare github.event.before..after sur push ; + # un clone depth=1 ne contient pas le parent, d’où « unknown revision ». + - uses: actions/checkout@v7 + with: + fetch-depth: 0 + - uses: actions/setup-node@v7 + with: + node-version: 22 + - run: npm ci + - run: npm run audit:deps + - name: Secret scan + # v2 — SHA épinglé (supply chain) ; tag : https://github.com/gitleaks/gitleaks-action/releases + uses: gitleaks/gitleaks-action@e0c47f4f8be36e29cdc102c57e68cb5cbf0e8d1e + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}