From 1a8820f707da7646c3db498d2371d895d992917f Mon Sep 17 00:00:00 2001 From: Ivan Bochkarev Date: Thu, 16 Jul 2026 08:30:06 +0600 Subject: [PATCH] feat(ci): add PHP lint/smoke and vueManager ESLint gate on PR Closes #337 --- .github/PULL_REQUEST_TEMPLATE.md | 20 +++++- .github/workflows/ci.yml | 65 +++++++++++++++++++ core/components/minishop3/composer.json | 4 ++ core/components/minishop3/scripts/ci-php.sh | 14 ++++ .../tests/PhinxCharsetConfigTest.php | 13 +--- .../tests/PhinxMigrationTablePrefixTest.php | 38 ++++------- core/components/minishop3/tests/run-smoke.php | 48 ++++++++++++++ .../tests/support/modx_phinx_stub.php | 33 ++++++++++ vueManager/package.json | 1 + 9 files changed, 197 insertions(+), 39 deletions(-) create mode 100644 .github/workflows/ci.yml create mode 100755 core/components/minishop3/scripts/ci-php.sh create mode 100644 core/components/minishop3/tests/run-smoke.php create mode 100644 core/components/minishop3/tests/support/modx_phinx_stub.php diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 9b6dbff3..dd9a6e0a 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -19,8 +19,22 @@ Closes #(номер issue) Опишите тесты, которые вы провели для проверки изменений. +Локальный CI-гейт (без MODX/MySQL), тот же набор что в `.github/workflows/ci.yml`: + +```bash +cd core/components/minishop3 +composer install +composer ci:php + +cd ../../../vueManager +npm ci +npm run lint:ci +``` + +PHPStan в этот гейт не входит (отдельный follow-up). + - [ ] Ручное тестирование -- [ ] Автоматические тесты (PHPStan, ESLint) +- [ ] Автоматические тесты (`composer ci:php`, `npm run lint:ci` / GitHub Actions CI) - [ ] Тестирование на разных версиях PHP/MODX **Конфигурация тестирования:** @@ -40,8 +54,8 @@ Closes #(номер issue) - [ ] Добавлены/обновлены комментарии в сложных местах - [ ] Изменения не ломают существующую функциональность - [ ] Лексиконы добавлены на **двух языках** (ru/en) -- [ ] PHPStan проходит без новых ошибок -- [ ] ESLint проходит без ошибок (для JS/Vue изменений) +- [ ] PHPStan проходит без новых ошибок (локально; в CI пока нет) +- [ ] ESLint проходит без ошибок (`npm run lint:ci` для Vue) - [ ] Обновлён CHANGELOG.md (для значимых изменений) ## Дополнительные заметки diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 00000000..7a08e0a7 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,65 @@ +name: CI + +on: + pull_request: + push: + branches: + - beta + - master + - main + +concurrency: + group: ci-${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +permissions: + contents: read + +jobs: + php: + name: PHP lint + smoke + runs-on: ubuntu-latest + defaults: + run: + working-directory: core/components/minishop3 + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: '8.2' + coverage: none + tools: composer:v2 + + - name: Install Composer dependencies + run: composer install --no-interaction --prefer-dist --no-progress + + - name: PHP syntax check + smoke tests + run: composer ci:php + + vue: + name: vueManager lint + runs-on: ubuntu-latest + defaults: + run: + working-directory: vueManager + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Node + uses: actions/setup-node@v4 + with: + node-version: '20' + cache: npm + cache-dependency-path: vueManager/package-lock.json + + - name: Install npm dependencies + run: npm ci + + - name: ESLint + run: npm run lint:ci diff --git a/core/components/minishop3/composer.json b/core/components/minishop3/composer.json index cad936c3..501075d6 100644 --- a/core/components/minishop3/composer.json +++ b/core/components/minishop3/composer.json @@ -23,6 +23,10 @@ "MiniShop3\\": "src/" } }, + "scripts": { + "test:smoke": "php tests/run-smoke.php", + "ci:php": "bash scripts/ci-php.sh" + }, "minimum-stability": "stable", "prefer-stable": true } diff --git a/core/components/minishop3/scripts/ci-php.sh b/core/components/minishop3/scripts/ci-php.sh new file mode 100755 index 00000000..6498c860 --- /dev/null +++ b/core/components/minishop3/scripts/ci-php.sh @@ -0,0 +1,14 @@ +#!/usr/bin/env bash +# PHP CI gate: syntax check + smoke tests (no MODX/MySQL). +set -euo pipefail + +ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +cd "$ROOT" + +count="$(find src migrations tests -type f -name '*.php' | wc -l | tr -d ' ')" +find src migrations tests -type f -name '*.php' -print0 \ + | xargs -0 -n1 php -l >/dev/null +php -l phinx.php >/dev/null +echo "OK php -l (${count} under src/migrations/tests + phinx.php)" + +composer test:smoke diff --git a/core/components/minishop3/tests/PhinxCharsetConfigTest.php b/core/components/minishop3/tests/PhinxCharsetConfigTest.php index b746fbb9..d6b4bae3 100644 --- a/core/components/minishop3/tests/PhinxCharsetConfigTest.php +++ b/core/components/minishop3/tests/PhinxCharsetConfigTest.php @@ -8,22 +8,15 @@ declare(strict_types=1); +require __DIR__ . '/support/modx_phinx_stub.php'; + $fail = static function (string $message): never { fwrite(STDERR, "FAIL: {$message}\n"); exit(1); }; $buildConfig = static function (array $options): array { - $modx = new class ($options) { - public function __construct(private readonly array $options) - { - } - - public function getOption(string $key, mixed $options = null, mixed $default = null): mixed - { - return $this->options[$key] ?? $default; - } - }; + $modx = ms3_create_modx_phinx_stub($options); return require __DIR__ . '/../phinx.php'; }; diff --git a/core/components/minishop3/tests/PhinxMigrationTablePrefixTest.php b/core/components/minishop3/tests/PhinxMigrationTablePrefixTest.php index 6df313d2..9a465d4a 100644 --- a/core/components/minishop3/tests/PhinxMigrationTablePrefixTest.php +++ b/core/components/minishop3/tests/PhinxMigrationTablePrefixTest.php @@ -8,6 +8,8 @@ declare(strict_types=1); +require __DIR__ . '/support/modx_phinx_stub.php'; + $fail = static function (string $message): never { fwrite(STDERR, "FAIL: {$message}\n"); exit(1); @@ -55,32 +57,16 @@ public function exec(string $sql): int string $tablePrefix, ?object $pdo = null, ): array { - $modx = new class ($tablePrefix, $pdo) { - public function __construct( - private readonly string $tablePrefix, - public readonly ?object $pdo, - ) { - } - - public function getOption(string $key, mixed $options = null, mixed $default = null): mixed - { - return match ($key) { - 'table_prefix' => $this->tablePrefix, - 'host' => 'localhost', - 'dbname' => 'modx', - 'username' => 'user', - 'password' => 'password', - 'port' => '3306', - 'charset' => 'utf8mb4', - 'collation' => 'utf8mb4_unicode_ci', - default => $default, - }; - } - - public function log(int $level, string $message): void - { - } - }; + $modx = ms3_create_modx_phinx_stub([ + 'table_prefix' => $tablePrefix, + 'host' => 'localhost', + 'dbname' => 'modx', + 'username' => 'user', + 'password' => 'password', + 'port' => '3306', + 'charset' => 'utf8mb4', + 'collation' => 'utf8mb4_unicode_ci', + ], $pdo); return require __DIR__ . '/../phinx.php'; }; diff --git a/core/components/minishop3/tests/run-smoke.php b/core/components/minishop3/tests/run-smoke.php new file mode 100644 index 00000000..a64ab990 --- /dev/null +++ b/core/components/minishop3/tests/run-smoke.php @@ -0,0 +1,48 @@ + 0) { + fwrite(STDERR, "Smoke tests failed: {$failed}\n"); + exit(1); +} + +fwrite(STDOUT, 'OK smoke tests (' . count($files) . ")\n"); +exit(0); diff --git a/core/components/minishop3/tests/support/modx_phinx_stub.php b/core/components/minishop3/tests/support/modx_phinx_stub.php new file mode 100644 index 00000000..df843276 --- /dev/null +++ b/core/components/minishop3/tests/support/modx_phinx_stub.php @@ -0,0 +1,33 @@ + $options Values returned by getOption($key). + */ +function ms3_create_modx_phinx_stub(array $options, ?object $pdo = null): object +{ + return new class ($options, $pdo) { + /** + * @param array $options + */ + public function __construct( + private readonly array $options, + public readonly ?object $pdo, + ) { + } + + public function getOption(string $key, mixed $options = null, mixed $default = null): mixed + { + return $this->options[$key] ?? $default; + } + + public function log(int $level, string $message): void + { + } + }; +} diff --git a/vueManager/package.json b/vueManager/package.json index 370f116e..16c12b91 100644 --- a/vueManager/package.json +++ b/vueManager/package.json @@ -9,6 +9,7 @@ "build": "vite build", "preview": "vite preview", "lint": "eslint . --fix", + "lint:ci": "eslint . --max-warnings 0", "format": "prettier --write \"src/**/*.{js,vue,scss,css}\"", "format:check": "prettier --check \"src/**/*.{js,vue,scss,css}\"", "lint:all": "npm run lint && npm run format:check && npm run lint:scss",