Skip to content

feat: add dependency updater command #84

feat: add dependency updater command

feat: add dependency updater command #84

Workflow file for this run

name: PHPUnit
on:
pull_request:
# Runs on main too — the badges job needs the coverage + mutation artifacts
# produced by the phpunit/mutation jobs to publish the badge data.
push:
branches: [main]
workflow_dispatch:
concurrency:
# Cancel superseded PR runs; let main pushes finish so the badges job
# (publishes coverage/MSI to the badges branch) is never cut off.
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
permissions:
contents: read
jobs:
phpunit:
name: Unit Tests (PHP ${{ matrix.php-version }})
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
php-version: ["8.3", "8.4"]
include:
- php-version: "8.4"
coverage: true
steps:
- name: Checkout code
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
- name: Set up PHP
uses: shivammathur/setup-php@accd6127cb78bee3e8082180cb391013d204ef9f # v2
with:
php-version: ${{ matrix.php-version }}
tools: composer:v2
coverage: ${{ matrix.coverage && 'pcov' || 'none' }}
- name: Cache Composer packages
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
with:
path: ~/.composer/cache/files
key: ${{ runner.os }}-composer-phpunit-${{ matrix.php-version }}-${{ hashFiles('composer.json') }}
restore-keys: ${{ runner.os }}-composer-phpunit-${{ matrix.php-version }}
- name: Install dev dependencies
run: composer install --no-interaction --no-progress
- name: Run PHPUnit
if: ${{ !matrix.coverage }}
run: vendor/bin/phpunit
- name: Run PHPUnit with coverage
if: ${{ matrix.coverage }}
# mkdir: the text report is written with a bare file_put_contents and
# relies on the Clover/HTML writers having created reports/ first.
run: |
mkdir -p reports
vendor/bin/phpunit \
--coverage-text=reports/coverage.txt \
--coverage-html reports/coverage \
--coverage-clover reports/clover.xml
- name: Enforce minimum line coverage
if: ${{ matrix.coverage }}
# Quality ratchet — raise the threshold as coverage grows.
run: php tests/coverage-checker.php reports/clover.xml 78
- name: Publish coverage summary
if: ${{ matrix.coverage && always() }}
run: |
if [[ -f reports/coverage.txt ]]; then
{
echo '## Code Coverage'
echo '```'
cat reports/coverage.txt
echo '```'
} >> "$GITHUB_STEP_SUMMARY"
fi
- name: Upload coverage report
if: ${{ matrix.coverage && always() }}
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: coverage-report
path: |
reports/coverage
reports/clover.xml
if-no-files-found: ignore
mutation:
name: Mutation Tests (Infection)
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
- name: Set up PHP
uses: shivammathur/setup-php@accd6127cb78bee3e8082180cb391013d204ef9f # v2
with:
php-version: "8.4"
tools: composer:v2
coverage: pcov
- name: Cache Composer packages
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
with:
path: ~/.composer/cache/files
key: ${{ runner.os }}-composer-phpunit-8.4-${{ hashFiles('composer.json') }}
restore-keys: ${{ runner.os }}-composer-phpunit-8.4
- name: Install dev dependencies
run: composer install --no-interaction --no-progress
- name: Run Infection
run: vendor/bin/infection --threads=max --logger-github --no-progress
- name: Publish mutation summary
if: always()
run: |
if [[ -f reports/infection/summary.log ]]; then
{
echo '## Mutation Testing'
echo '```'
cat reports/infection/summary.log
echo '```'
} >> "$GITHUB_STEP_SUMMARY"
fi
- name: Upload mutation report
if: always()
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: mutation-report
path: reports/infection
if-no-files-found: ignore
# Publishes shields.io endpoint JSON (coverage %, mutation score) to the
# "badges" branch; the README badges read from there via raw.githubusercontent.
badges:
name: Publish badge data
if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }}
needs: [phpunit, mutation]
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Download coverage report
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: coverage-report
path: reports
- name: Download mutation report
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: mutation-report
path: reports/infection
- name: Generate badge JSON
run: |
mkdir -p badge-data
python3 - <<'EOF'
import json
import re
import xml.etree.ElementTree as ET
def color(value, bands):
for limit, name in bands:
if value < limit:
return name
return "brightgreen"
metrics = ET.parse("reports/clover.xml").getroot().find("./project/metrics")
statements = int(metrics.get("statements"))
covered = int(metrics.get("coveredstatements"))
coverage = covered / statements * 100 if statements else 0.0
summary = open("reports/infection/summary.log").read()
def count(label):
return int(re.search(rf"^{label}: (\d+)$", summary, re.M).group(1))
total = count("Total")
detected = (
count("Killed by Test Framework")
+ count("Killed by Static Analysis")
+ count("Errored")
+ count("Timed Out")
)
covered_mutants = total - count("Not Covered")
msi = detected / covered_mutants * 100 if covered_mutants else 0.0
badges = {
"coverage.json": ("coverage", coverage, [(20, "red"), (50, "orange"), (75, "yellow")]),
"msi.json": ("mutation score", msi, [(50, "red"), (70, "orange"), (85, "yellow")]),
}
for filename, (label, value, bands) in badges.items():
with open(f"badge-data/{filename}", "w") as handle:
json.dump(
{
"schemaVersion": 1,
"label": label,
"message": f"{value:.1f}%",
"color": color(value, bands),
},
handle,
)
EOF
- name: Push badge data to badges branch
env:
GH_TOKEN: ${{ github.token }}
run: |
remote="https://x-access-token:${GH_TOKEN}@github.com/${GITHUB_REPOSITORY}.git"
if ! git clone --depth 1 --branch badges "${remote}" badges-branch; then
mkdir badges-branch
git -C badges-branch init -b badges
git -C badges-branch remote add origin "${remote}"
fi
cp badge-data/*.json badges-branch/
cd badges-branch
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git add .
git diff --cached --quiet || git commit -m "chore: update badge data"
git push origin badges