From 74e5a7daa255bfb09711ba4cd50e835315e8c13f Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 17 Dec 2025 00:51:04 +0000 Subject: [PATCH 1/4] ci: add mutation testing workflow with Infection - Add Infection to composer.json require-dev dependencies - Create GitHub workflow for mutation testing - Configure workflow to fail if MSI is below 87% - Set threads to max for optimal performance - Run on push to main and pull requests --- .github/workflows/mutation-testing.yml | 44 ++++++++++++++++++++++++++ composer.json | 3 +- 2 files changed, 46 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/mutation-testing.yml diff --git a/.github/workflows/mutation-testing.yml b/.github/workflows/mutation-testing.yml new file mode 100644 index 00000000..7d2777a2 --- /dev/null +++ b/.github/workflows/mutation-testing.yml @@ -0,0 +1,44 @@ +name: Mutation Testing + +on: + push: + branches: + - main + pull_request: + +permissions: + contents: read + +jobs: + mutation-testing: + name: Mutation Testing + runs-on: ubuntu-24.04 + + steps: + - name: Checkout code + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6 + + - name: Setup PHP + uses: shivammathur/setup-php@44454db4f0199b8b9685a5d763dc37cbf79108e1 # v2 + with: + php-version: '8.2' + extensions: json + coverage: xdebug + tools: composer:v2 + + - name: Get composer cache directory + id: composer-cache + run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT + + - name: Cache dependencies + uses: actions/cache@9255dc7a253b0ccc959486e2bca901246202afeb # v5 + with: + path: ${{ steps.composer-cache.outputs.dir }} + key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.json') }} + restore-keys: ${{ runner.os }}-composer- + + - name: Install dependencies + run: composer update -n --prefer-dist + + - name: Run mutation testing + run: vendor/bin/infection --threads=max --min-msi=87 --min-covered-msi=87 diff --git a/composer.json b/composer.json index e02c219c..bfcbf6f3 100644 --- a/composer.json +++ b/composer.json @@ -26,7 +26,8 @@ "phpunit/phpunit": "^11||^12", "guzzlehttp/guzzle": "^7.0", "humbug/box": "^4.6", - "phpstan/phpstan": "^2.0" + "phpstan/phpstan": "^2.0", + "infection/infection": "^0.30" }, "autoload": { "psr-4": { From b53b02e020406d14cb421166d0e0498a654410f3 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 17 Dec 2025 15:32:44 +0000 Subject: [PATCH 2/4] ci: use Infection PHAR instead of Composer dependency - Remove infection/infection from composer.json require-dev - Download Infection PHAR directly in the workflow - Download both PHAR and signature files for verification - Maintain same mutation testing thresholds (87% MSI) --- .github/workflows/mutation-testing.yml | 8 +++++++- composer.json | 3 +-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/.github/workflows/mutation-testing.yml b/.github/workflows/mutation-testing.yml index 7d2777a2..d970f0ea 100644 --- a/.github/workflows/mutation-testing.yml +++ b/.github/workflows/mutation-testing.yml @@ -40,5 +40,11 @@ jobs: - name: Install dependencies run: composer update -n --prefer-dist + - name: Download Infection PHAR + run: | + wget https://github.com/infection/infection/releases/latest/download/infection.phar + wget https://github.com/infection/infection/releases/latest/download/infection.phar.asc + chmod +x infection.phar + - name: Run mutation testing - run: vendor/bin/infection --threads=max --min-msi=87 --min-covered-msi=87 + run: ./infection.phar --threads=max --min-msi=87 --min-covered-msi=87 diff --git a/composer.json b/composer.json index bfcbf6f3..e02c219c 100644 --- a/composer.json +++ b/composer.json @@ -26,8 +26,7 @@ "phpunit/phpunit": "^11||^12", "guzzlehttp/guzzle": "^7.0", "humbug/box": "^4.6", - "phpstan/phpstan": "^2.0", - "infection/infection": "^0.30" + "phpstan/phpstan": "^2.0" }, "autoload": { "psr-4": { From 739bc76f17918ef1d91f3c0ef84bcc27dad88ffa Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 17 Dec 2025 15:34:46 +0000 Subject: [PATCH 3/4] ci: pin Infection to v0.30.0 and add GPG validation - Pin Infection PHAR to version 0.30.0 instead of using latest - Add GPG signature verification with official Infection signing key - Import key from keys.openpgp.org keyserver - Verify signature before making PHAR executable - Convert mutation-testing workflow to workflow_call - Add mutation-testing job to CI workflow alongside tests --- .github/workflows/ci.yml | 3 +++ .github/workflows/mutation-testing.yml | 14 ++++++++------ 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a5d73cec..668b74a1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,3 +13,6 @@ permissions: jobs: test: uses: ./.github/workflows/test.yml + + mutation-testing: + uses: ./.github/workflows/mutation-testing.yml diff --git a/.github/workflows/mutation-testing.yml b/.github/workflows/mutation-testing.yml index d970f0ea..cb4ecb36 100644 --- a/.github/workflows/mutation-testing.yml +++ b/.github/workflows/mutation-testing.yml @@ -1,10 +1,7 @@ name: Mutation Testing on: - push: - branches: - - main - pull_request: + workflow_call: permissions: contents: read @@ -42,8 +39,13 @@ jobs: - name: Download Infection PHAR run: | - wget https://github.com/infection/infection/releases/latest/download/infection.phar - wget https://github.com/infection/infection/releases/latest/download/infection.phar.asc + wget https://github.com/infection/infection/releases/download/0.30.0/infection.phar + wget https://github.com/infection/infection/releases/download/0.30.0/infection.phar.asc + + - name: Validate Infection PHAR + run: | + gpg --keyserver hkps://keys.openpgp.org --recv-keys C6D76C329EBADE2FB9C458CFC5095986493B4AA0 + gpg --with-fingerprint --verify infection.phar.asc infection.phar chmod +x infection.phar - name: Run mutation testing From bb5085f80c5fa8b15daae6ef72fdff8332d17eb4 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 17 Dec 2025 15:38:55 +0000 Subject: [PATCH 4/4] ci: move mutation testing into test workflow - Move mutation-testing job from separate workflow into test.yml - Remove mutation-testing.yml file - Remove mutation-testing workflow call from ci.yml - Mutation testing now shows as "CI / test / Mutation Testing" --- .github/workflows/ci.yml | 3 -- .github/workflows/mutation-testing.yml | 52 -------------------------- .github/workflows/test.yml | 44 ++++++++++++++++++++++ 3 files changed, 44 insertions(+), 55 deletions(-) delete mode 100644 .github/workflows/mutation-testing.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 668b74a1..a5d73cec 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,6 +13,3 @@ permissions: jobs: test: uses: ./.github/workflows/test.yml - - mutation-testing: - uses: ./.github/workflows/mutation-testing.yml diff --git a/.github/workflows/mutation-testing.yml b/.github/workflows/mutation-testing.yml deleted file mode 100644 index cb4ecb36..00000000 --- a/.github/workflows/mutation-testing.yml +++ /dev/null @@ -1,52 +0,0 @@ -name: Mutation Testing - -on: - workflow_call: - -permissions: - contents: read - -jobs: - mutation-testing: - name: Mutation Testing - runs-on: ubuntu-24.04 - - steps: - - name: Checkout code - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6 - - - name: Setup PHP - uses: shivammathur/setup-php@44454db4f0199b8b9685a5d763dc37cbf79108e1 # v2 - with: - php-version: '8.2' - extensions: json - coverage: xdebug - tools: composer:v2 - - - name: Get composer cache directory - id: composer-cache - run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT - - - name: Cache dependencies - uses: actions/cache@9255dc7a253b0ccc959486e2bca901246202afeb # v5 - with: - path: ${{ steps.composer-cache.outputs.dir }} - key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.json') }} - restore-keys: ${{ runner.os }}-composer- - - - name: Install dependencies - run: composer update -n --prefer-dist - - - name: Download Infection PHAR - run: | - wget https://github.com/infection/infection/releases/download/0.30.0/infection.phar - wget https://github.com/infection/infection/releases/download/0.30.0/infection.phar.asc - - - name: Validate Infection PHAR - run: | - gpg --keyserver hkps://keys.openpgp.org --recv-keys C6D76C329EBADE2FB9C458CFC5095986493B4AA0 - gpg --with-fingerprint --verify infection.phar.asc infection.phar - chmod +x infection.phar - - - name: Run mutation testing - run: ./infection.phar --threads=max --min-msi=87 --min-covered-msi=87 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index f0b4c961..23e34d37 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -125,3 +125,47 @@ jobs: - name: Run PHPStan run: vendor/bin/phpstan analyse + + mutation-testing: + name: Mutation Testing + runs-on: ubuntu-24.04 + + steps: + - name: Checkout code + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6 + + - name: Setup PHP + uses: shivammathur/setup-php@44454db4f0199b8b9685a5d763dc37cbf79108e1 # v2 + with: + php-version: '8.2' + extensions: json + coverage: xdebug + tools: composer:v2 + + - name: Get composer cache directory + id: composer-cache + run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT + + - name: Cache dependencies + uses: actions/cache@9255dc7a253b0ccc959486e2bca901246202afeb # v5 + with: + path: ${{ steps.composer-cache.outputs.dir }} + key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.json') }} + restore-keys: ${{ runner.os }}-composer- + + - name: Install dependencies + run: composer update -n --prefer-dist + + - name: Download Infection PHAR + run: | + wget https://github.com/infection/infection/releases/download/0.30.0/infection.phar + wget https://github.com/infection/infection/releases/download/0.30.0/infection.phar.asc + + - name: Validate Infection PHAR + run: | + gpg --keyserver hkps://keys.openpgp.org --recv-keys C6D76C329EBADE2FB9C458CFC5095986493B4AA0 + gpg --with-fingerprint --verify infection.phar.asc infection.phar + chmod +x infection.phar + + - name: Run mutation testing + run: ./infection.phar --threads=max --min-msi=87 --min-covered-msi=87