diff --git a/.eslintrc.json b/.eslintrc.json new file mode 100644 index 0000000..1d8cba4 --- /dev/null +++ b/.eslintrc.json @@ -0,0 +1,15 @@ +{ + "env": { + "browser": true, + "es2021": true + }, + "extends": "eslint:recommended", + "parserOptions": { + "ecmaVersion": 12, + "sourceType": "module" + }, + "rules": { + "no-unused-vars": "warn", + "no-console": "off" + } +} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..abb8862 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,60 @@ +name: CI + +on: + push: + branches: [ "main", "dev" ] + +permissions: + contents: read + security-events: write + +jobs: + build-test-scan: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: '8.2' + tools: composer, phpstan, phpunit, phpcs + + - name: Cache Composer + uses: actions/cache@v4 + with: + path: ~/.composer/cache + key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }} + restore-keys: ${{ runner.os }}-composer- + + - name: Install PHP dependencies + run: composer install --no-interaction --prefer-dist + + - name: Run PHPStan (static analysis) + run: vendor/bin/phpstan analyse --memory-limit=1G + + - name: Run PHPUnit (unit tests) + run: vendor/bin/phpunit --coverage-clover coverage.xml + + - name: Upload coverage to Codecov + uses: codecov/codecov-action@v4 + with: + files: coverage.xml + fail_ci_if_error: true + + - name: Run Trivy FS scan + uses: aquasecurity/trivy-action@master + with: + scan-type: 'fs' + ignore-unfixed: true + severity: 'HIGH,CRITICAL' + + - name: Build Docker image + run: docker build -t myapp:ci . + + - name: Run Trivy image scan + uses: aquasecurity/trivy-action@master + with: + image-ref: 'myapp:ci' + severity: 'HIGH,CRITICAL' diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 0000000..fa63722 --- /dev/null +++ b/.github/workflows/codeql.yml @@ -0,0 +1,47 @@ +name: "CodeQL" + +on: + push: + branches: [ "main", "dev" ] + pull_request: + branches: [ "main", "dev" ] + schedule: + - cron: '0 0 * * 0' # weekly scan + +permissions: + contents: read + security-events: write + +jobs: + analyze: + name: Analyze PHP and JavaScript + runs-on: ubuntu-latest + permissions: + actions: read + contents: read + security-events: write + + strategy: + fail-fast: false + matrix: + language: [ 'php', 'javascript' ] + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Initialize CodeQL + uses: github/codeql-action/init@v3 + with: + languages: ${{ matrix.language }} + + - name: Autobuild + uses: github/codeql-action/autobuild@v3 + + # If autobuild fails for PHP, uncomment and adjust: + # - run: composer install --no-interaction --prefer-dist + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v3 + with: + category: "/language:${{ matrix.language }}" diff --git a/.github/workflows/mega-linter.yml b/.github/workflows/mega-linter.yml deleted file mode 100644 index 6067802..0000000 --- a/.github/workflows/mega-linter.yml +++ /dev/null @@ -1,85 +0,0 @@ -name: MegaLinter - -on: - push: - branches: [ "main", "dev" ] - pull_request: - -permissions: - contents: write # needed to commit to a fixes branch - pull-requests: write # needed to open PRs with fixes - security-events: write # for SARIF upload (optional) - -jobs: - lint: - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - # Cache Composer (PHP) - - name: Cache Composer - uses: actions/cache@v4 - with: - path: | - ~/.composer/cache - vendor - key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }} - restore-keys: ${{ runner.os }}-composer- - - # Cache npm (JS/CSS linters) - - name: Cache npm - uses: actions/cache@v4 - with: - path: | - ~/.npm - node_modules - key: ${{ runner.os }}-npm-${{ hashFiles('**/package-lock.json') }} - restore-keys: ${{ runner.os }}-npm- - - # Install dev deps if present (WPCS, ESLint plugins, etc.) - - name: Composer install (no scripts) - run: | - if [ -f composer.json ]; then - composer install --no-interaction --no-progress --no-scripts || true - fi - - - name: npm install if present - run: | - if [ -f package.json ]; then - npm ci || npm i - fi - - - name: Run MegaLinter - uses: oxsecurity/megalinter@v8 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - # Only lint changed files - VALIDATE_ALL_CODEBASE: false - # Enable just these linters - ENABLE_LINTERS: >- - PHP_PHPCS, - PHP_PHPLINT, - JAVASCRIPT_ES, - CSS_STYLELINT, - HTML_HTMLHINT - # Limit JS/CSS/HTML to update-api/ only - JAVASCRIPT_ES_FILTER_REGEX_INCLUDE: "^update-api/" - CSS_STYLELINT_FILTER_REGEX_INCLUDE: "^update-api/" - HTML_HTMLHINT_FILTER_REGEX_INCLUDE: "^update-api/" - # PHPCS ruleset file assigns WordPress vs PSR-12 per directory - PHP_PHPCS_CONFIG_FILE: "phpcs.xml" - # Auto-fix only changed files and open a PR with fixes - APPLY_FIXES: all - APPLY_FIXES_EVENT: pull_request - APPLY_FIXES_MODE: pull_request - # Reports (optional) - REPORT_OUTPUT_FOLDER: megalinter-reports - - - name: Upload SARIF (optional) - if: always() - uses: github/codeql-action/upload-sarif@v3 - with: - sarif_file: megalinter-reports/megalinter-report.sarif diff --git a/.github/workflows/pr-auto-fix.yml b/.github/workflows/pr-auto-fix.yml new file mode 100644 index 0000000..8b2ee5a --- /dev/null +++ b/.github/workflows/pr-auto-fix.yml @@ -0,0 +1,70 @@ +name: PR Lint and Auto-Fix + +on: + pull_request: + +permissions: + contents: write + pull-requests: write + +jobs: + lint-fix: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: '8.2' + tools: composer, php-cs-fixer, phpcs + + - name: Cache Composer + uses: actions/cache@v4 + with: + path: ~/.composer/cache + key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }} + restore-keys: ${{ runner.os }}-composer- + + - name: Install PHP dependencies + run: composer install --no-interaction --prefer-dist || true + + - name: Cache npm + uses: actions/cache@v4 + with: + path: | + ~/.npm + node_modules + key: ${{ runner.os }}-npm-${{ hashFiles('**/package-lock.json') }} + restore-keys: ${{ runner.os }}-npm- + + - name: Install npm dependencies + run: if [ -f package.json ]; then npm ci || npm i; fi + + # PHPCS WordPress for mu-plugins/ + - name: Fix PHP (WordPress standard in mu-plugins/) + run: vendor/bin/phpcbf --standard=WordPress mu-plugins || true + + # PHPCS PSR-12 for update-api/ + - name: Fix PHP (PSR-12 standard in update-api/) + run: vendor/bin/phpcbf --standard=PSR12 update-api || true + + - name: PHP-CS-Fixer + run: vendor/bin/php-cs-fixer fix --allow-risky=yes || true + + - name: ESLint Fix + run: npx eslint update-api/**/*.js --fix || true + + - name: Stylelint Fix + run: npx stylelint "update-api/**/*.css" --fix || true + + - name: Commit fixes to PR branch + uses: peter-evans/create-pull-request@v6 + with: + commit-message: "chore: auto-fix lint issues" + branch: lint-fixes + title: "Lint Auto-Fixes" + body: "Automated lint fixes from PR workflow" diff --git a/.mega-linter.yml b/.mega-linter.yml deleted file mode 100644 index 1e470bb..0000000 --- a/.mega-linter.yml +++ /dev/null @@ -1,23 +0,0 @@ -# MegaLinter configuration -# Lints only changed files and only selected linters. -VALIDATE_ALL_CODEBASE: false -ENABLE_LINTERS: > - PHP_PHPCS, - PHP_PHPLINT, - JAVASCRIPT_ES, - CSS_STYLELINT, - HTML_HTMLHINT - -# Restrict web linters to update-api/ -JAVASCRIPT_ES_FILTER_REGEX_INCLUDE: "^update-api/" -CSS_STYLELINT_FILTER_REGEX_INCLUDE: "^update-api/" -HTML_HTMLHINT_FILTER_REGEX_INCLUDE: "^update-api/" - -# Use project ruleset to target different PHP standards per directory -PHP_PHPCS_CONFIG_FILE: "phpcs.xml" - -# Auto-fix behavior controlled from workflow env, can be overridden here if needed -# APPLY_FIXES: all -# APPLY_FIXES_EVENT: pull_request -# APPLY_FIXES_MODE: pull_request -# REPORT_OUTPUT_FOLDER: megalinter-reports diff --git a/.stylelintrc.json b/.stylelintrc.json new file mode 100644 index 0000000..11fceb0 --- /dev/null +++ b/.stylelintrc.json @@ -0,0 +1,7 @@ +{ + "extends": "stylelint-config-standard", + "rules": { + "indentation": 2, + "color-hex-length": "short" + } +} diff --git a/phpcs.xml b/phpcs.xml index c50b2c1..2efcd2c 100644 --- a/phpcs.xml +++ b/phpcs.xml @@ -4,16 +4,13 @@ - mu-plugins update-api - ^mu-plugins/.* - ^update-api/.* diff --git a/phpstan.neon b/phpstan.neon new file mode 100644 index 0000000..729e58f --- /dev/null +++ b/phpstan.neon @@ -0,0 +1,6 @@ +parameters: + level: 6 + paths: + - mu-plugins + - update-api + memoryLimit: 1G diff --git a/phpunit.xml b/phpunit.xml new file mode 100644 index 0000000..550968e --- /dev/null +++ b/phpunit.xml @@ -0,0 +1,11 @@ + + + + + tests + + +