corentin_wakdo/.forgejo/workflows/ci.yml
Imugiii d4b02a76c6
All checks were successful
CI / secret-scan (push) Successful in 7s
CI / php-lint (push) Successful in 17s
CI / static-tests (push) Successful in 46s
CI / secret-scan (pull_request) Successful in 8s
CI / php-lint (pull_request) Successful in 20s
CI / static-tests (pull_request) Successful in 45s
CI / auto-merge (push) Has been skipped
CI / auto-merge (pull_request) Successful in 5s
ci: run DB integration tests against an ephemeral MariaDB service
Avant ce commit, le job static-tests lancait phpunit sans base ni
WAKDO_DB_TESTS=1 : les 7 tests d'integration tests/Integration/*DbTest
s'auto-skippaient (13 skips), et le SQL porteur de securite n'etait valide
par AUCUN test en CI -- upsert atomique du throttle (login + PIN), predicat
RBAC AND r.is_active=1, audit_log dans la meme transaction, FK RESTRICT/CASCADE.
Une regression dans ce SQL passait la CI au vert (le double FakeDatabase
n'execute pas le SQL).

Ce commit provisionne un service MariaDB 11.4 ephemere, applique le schema
(db/migrations) puis le seed (db/seeds), et lance phpunit avec WAKDO_DB_TESTS=1
+ DB_*, ajoute le pilote pdo_mysql (php-mysql) et le client mariadb. L'option
--fail-on-skipped garantit qu'un skip silencieux d'un *DbTest fait desormais
echouer la CI au lieu de la laisser verte.

Recette validee localement sur une MariaDB 11.4 vierge : migrations + seeds
appliques proprement (22 tables, 5 roles, 53 produits), phpunit = 188 tests /
525 assertions / 0 skip / 0 echec (vs 188 / 448 / 13 skip sans base).
2026-06-16 10:40:09 +00:00

176 lines
8.2 KiB
YAML

name: CI
# CI Wakdo - Forgejo Actions (runner stark-wakdo, label `docker`).
# Strategie solo dev : PR obligatoire + auto-merge sur CI verte (voir SECURITY.md).
#
# Etat des jobs selon la phase projet :
# - secret-scan : fonctionnel des maintenant (gitleaks scanne tout le depot)
# - php-lint : fonctionnel sur les fichiers PHP presents (stubs P1, code P2+)
# - static-tests: PHPStan + PHPUnit GARDES - s'activent quand P2 ajoute
# composer.json / phpstan.neon / tests + phpunit.xml
on:
pull_request:
branches: [dev, main]
# `labeled` : permet au job auto-merge de s'evaluer quand on pose le label.
types: [opened, synchronize, reopened, labeled]
push:
# dev/main : porte de merge. feat|fix|ci|refactor : feedback avant la PR.
branches: [dev, main, 'feat/**', 'fix/**', 'ci/**', 'refactor/**']
jobs:
secret-scan:
runs-on: docker
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Install tools
run: |
apt-get update -qq
apt-get install -y -qq curl ca-certificates tar >/dev/null
- name: Install gitleaks
run: |
VER=8.21.2
curl -sSL "https://github.com/gitleaks/gitleaks/releases/download/v${VER}/gitleaks_${VER}_linux_x64.tar.gz" -o /tmp/gl.tgz
tar -xzf /tmp/gl.tgz -C /usr/local/bin gitleaks
gitleaks version
- name: Scan for secrets
run: gitleaks detect --config .gitleaks.toml --redact --no-banner --verbose
php-lint:
runs-on: docker
steps:
- uses: actions/checkout@v4
- name: Install PHP CLI
run: |
apt-get update -qq
apt-get install -y -qq php-cli >/dev/null
php --version
- name: Lint all PHP files
run: |
set -eu
files=$(find . -path ./node_modules -prune -o -name '*.php' -print)
if [ -z "$files" ]; then echo "No PHP files yet - skip"; exit 0; fi
echo "$files" | while IFS= read -r f; do
[ -z "$f" ] && continue
php -l "$f"
done
static-tests:
runs-on: docker
# COMPOSER-LESS (decision 4 / 5, PROJECT_CONTEXT.md) : PHPStan et PHPUnit
# tournent depuis leur .phar autonome telecharge ici, jamais via Composer.
# Versions epinglees pour des CI reproductibles (pas de "latest").
#
# Service MariaDB ephemere : le schema (db/migrations) et le seed (db/seeds)
# y sont appliques, puis PHPUnit tourne avec WAKDO_DB_TESTS=1 pour que les
# tests d'integration (tests/Integration/*DbTest) s'executent REELLEMENT.
# Sans base, ils s'auto-skippent et le SQL porteur de securite (throttle,
# RBAC is_active, audit in-transaction, FK) n'est jamais valide en CI.
# Identifiants ci-dessous : ephemeres, CI uniquement, jamais des secrets.
services:
mariadb:
image: mariadb:11.4
env:
MARIADB_ROOT_PASSWORD: root
MARIADB_DATABASE: wakdo_test
MARIADB_USER: wakdo
MARIADB_PASSWORD: wakdo
env:
PHPUNIT_VERSION: "11.5.2"
PHPSTAN_VERSION: "1.12.27"
# Connexion des tests d'integration au service `mariadb` ci-dessus
# (Database lit ces DB_* via getenv ; cf. src/app/Core/Database.php).
WAKDO_DB_TESTS: "1"
DB_HOST: mariadb
DB_PORT: "3306"
DB_NAME: wakdo_test
DB_USER: wakdo
DB_PASSWORD: wakdo
steps:
- uses: actions/checkout@v4
- name: PHPStan (guarded)
run: |
set -eu
if [ ! -f phpstan.neon ]; then
echo "PHPStan skipped: no phpstan.neon yet (activates in P2)"
exit 0
fi
echo "phpstan.neon detected - running PHPStan ${PHPSTAN_VERSION} via .phar"
apt-get update -qq && apt-get install -y -qq php-cli php-xml php-mbstring curl ca-certificates >/dev/null
# PHPUnit phar present pour que phpstan.neon (scanDirectories phar://phpunit.phar)
# resolve les symboles PHPUnit\Framework\* utilises sous tests/.
curl -sSL "https://phar.phpunit.de/phpunit-${PHPUNIT_VERSION}.phar" -o phpunit.phar
curl -sSL "https://github.com/phpstan/phpstan/releases/download/${PHPSTAN_VERSION}/phpstan.phar" -o phpstan.phar
php phpstan.phar --version
# memory_limit=-1 : l'analyse parallele depasse les 128M par defaut du php-cli.
php -d memory_limit=-1 phpstan.phar analyse --no-progress --error-format=raw
- name: PHPUnit (guarded, avec tests d'integration DB)
run: |
set -eu
if [ ! -d tests ] || [ ! -f phpunit.xml ]; then
echo "PHPUnit skipped: no tests/ + phpunit.xml yet (activates in P2)"
exit 0
fi
echo "phpunit.xml + tests/ detected - running PHPUnit ${PHPUNIT_VERSION} via .phar"
# php-mysql = pilote pdo_mysql requis par les *DbTest ; mariadb-client
# pour appliquer schema + seed au service mariadb.
apt-get update -qq && apt-get install -y -qq php-cli php-xml php-mbstring php-mysql mariadb-client curl ca-certificates >/dev/null
# Attente active que le service MariaDB reponde (en plus du lien de service).
echo "Attente du service MariaDB ${DB_HOST}:${DB_PORT} ..."
ready=0
for i in $(seq 1 30); do
if mariadb -h"${DB_HOST}" -P"${DB_PORT}" -u"${DB_USER}" -p"${DB_PASSWORD}" -e "SELECT 1" "${DB_NAME}" >/dev/null 2>&1; then
echo "MariaDB pret (tentative ${i})."; ready=1; break
fi
sleep 2
done
[ "${ready}" = 1 ] || { echo "ERREUR: MariaDB injoignable apres 60s"; exit 1; }
# Schema (db/migrations) puis seed (db/seeds), ordre lexicographique.
for f in db/migrations/*.sql; do
echo "migrate $(basename "$f")"
mariadb -h"${DB_HOST}" -P"${DB_PORT}" -u"${DB_USER}" -p"${DB_PASSWORD}" "${DB_NAME}" < "$f"
done
for f in db/seeds/*.sql; do
echo "seed $(basename "$f")"
mariadb -h"${DB_HOST}" -P"${DB_PORT}" -u"${DB_USER}" -p"${DB_PASSWORD}" "${DB_NAME}" < "$f"
done
curl -sSL "https://phar.phpunit.de/phpunit-${PHPUNIT_VERSION}.phar" -o phpunit.phar
php phpunit.phar --version
# --fail-on-skipped : si un *DbTest s'auto-skippe (base injoignable), la
# CI echoue au lieu de masquer le trou derriere un vert. C'est le coeur
# du correctif : plus aucun skip silencieux des chemins securite.
php phpunit.phar -c phpunit.xml --fail-on-skipped
auto-merge:
# Fusion automatique OPT-IN : poser le label `auto-merge` sur la PR.
# Ne s'execute que si les 3 checks passent (needs).
# IMPORTANT : le filtrage par label se fait DANS le step via l'API, pas dans
# `if:` — l'expression contains(github.event.pull_request.labels.*.name, ...)
# de Forgejo n'est pas fiable (elle s'evalue a vrai meme sans label, ce qui
# fusionnait toute PR verte). La verification shell sur l'API est le vrai gate.
needs: [secret-scan, php-lint, static-tests]
if: github.event_name == 'pull_request'
runs-on: docker
steps:
- name: Install curl
run: apt-get update -qq && apt-get install -y -qq curl ca-certificates >/dev/null
- name: Merge PR (squash) si label auto-merge present et CI verte
run: |
API="${{ github.server_url }}/api/v1/repos/${{ github.repository }}"
PR="${{ github.event.pull_request.number }}"
TOKEN="${{ secrets.FORGEJO_TOKEN }}"
labels=$(curl -s -H "Authorization: token $TOKEN" "$API/issues/$PR/labels")
if ! printf '%s' "$labels" | grep -q '"name"[[:space:]]*:[[:space:]]*"auto-merge"'; then
echo "Pas de label 'auto-merge' sur la PR #$PR -> relecture manuelle, pas de fusion auto."
exit 0
fi
echo "Label 'auto-merge' present + CI verte -> fusion de la PR #$PR"
code=$(curl -s -o /tmp/resp -w "%{http_code}" -X POST \
-H "Authorization: token $TOKEN" \
-H "Content-Type: application/json" \
-d '{"Do":"squash","delete_branch_after_merge":true}' \
"$API/pulls/$PR/merge")
echo "merge HTTP $code"; cat /tmp/resp || true; echo
[ "$code" = "200" ] || { echo "auto-merge failed (HTTP $code)"; exit 1; }
echo "PR #$PR mergee."