Skip to content

fix: stops host:install ignoring a custom host directory (#32) #126

fix: stops host:install ignoring a custom host directory (#32)

fix: stops host:install ignoring a custom host directory (#32) #126

Workflow file for this run

name: CI
on:
push:
branches: [main]
tags: ['v*']
pull_request:
# The lock file is gitignored, so dependencies only re-resolve when something
# is pushed — a weekly run surfaces silent dependency rot and audit-ignore
# entries that Symfony has patched in the meantime.
schedule:
- cron: '30 5 * * 1'
permissions:
contents: read
concurrency:
group: ci-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
jobs:
validate:
name: Composer validate
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- name: Checkout
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Setup PHP
uses: shivammathur/setup-php@f3e473d116dcccaddc5834248c87452386958240 # v2
with:
php-version: '8.3'
coverage: none
- name: Validate composer.json
run: composer validate --strict
shellcheck:
name: Shellcheck host runner
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- name: Checkout
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
# shellcheck ships preinstalled on ubuntu-latest. Scoped to the runner
# itself and any real *.sh files under src/tests — none exist today
# (deploytasks:host:generate writes its stub at runtime, from a PHP
# template, not a committed .sh file), so the find is a no-op guard
# against future additions rather than dead weight.
- name: shellcheck runner + host-task scripts
run: |
shellcheck bin/deploy-tasks-host.sh.dist
find src tests -name '*.sh' -print0 | xargs -0 -r shellcheck
changelog:
name: Changelog structure
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- name: Checkout
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
# Keep a Changelog discipline: [Unreleased] holds at most one section per
# change type, and only the six canonical types. Duplicate sections have
# already drifted in once (two independent "### Changed" blocks).
- name: Lint the [Unreleased] section structure
run: |
awk '
/^## / { in_unreleased = ($0 ~ /^## \[Unreleased\]/) }
in_unreleased && /^### / {
type = substr($0, 5)
sub(/[ \t]+$/, "", type)
if (type !~ /^(Added|Changed|Deprecated|Removed|Fixed|Security)$/) {
printf "CHANGELOG.md line %d: unknown section \"%s\"\n", NR, type; bad = 1
} else if (seen[type]++) {
printf "CHANGELOG.md line %d: duplicate section \"%s\"\n", NR, type; bad = 1
}
}
END { if (bad) exit 1; print "CHANGELOG [Unreleased] structure OK" }
' CHANGELOG.md
phpstan:
name: PHPStan
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- name: Checkout
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Setup PHP
uses: shivammathur/setup-php@f3e473d116dcccaddc5834248c87452386958240 # v2
with:
php-version: '8.3'
extensions: mbstring, xml, ctype, iconv, intl
coverage: none
- name: Cache Composer dependencies
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: ~/.composer/cache
key: composer-${{ hashFiles('composer.json') }}
restore-keys: composer-
- name: Cache PHPStan result
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: var/cache/phpstan
key: phpstan-${{ runner.os }}-${{ hashFiles('phpstan.neon', '**/composer.json') }}
restore-keys: phpstan-${{ runner.os }}-
- name: Install dependencies
run: composer install --prefer-dist --no-progress --no-scripts
- name: Run PHPStan
run: vendor/bin/phpstan analyse --no-progress
cs-fixer:
name: CS Fixer
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- name: Checkout
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Setup PHP
uses: shivammathur/setup-php@f3e473d116dcccaddc5834248c87452386958240 # v2
with:
php-version: '8.3'
extensions: mbstring, xml, ctype, iconv, intl
coverage: none
- name: Cache Composer dependencies
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: ~/.composer/cache
key: composer-${{ hashFiles('composer.json') }}
restore-keys: composer-
- name: Cache CS Fixer result
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: .php-cs-fixer.cache
key: php-cs-fixer-${{ runner.os }}-${{ hashFiles('.php-cs-fixer.dist.php', '**/composer.json') }}
restore-keys: php-cs-fixer-${{ runner.os }}-
- name: Install dependencies
run: composer install --prefer-dist --no-progress --no-scripts
- name: Run CS Fixer
run: vendor/bin/php-cs-fixer fix --dry-run --diff
tests:
name: Tests (PHP ${{ matrix.php }}, Symfony ${{ matrix.symfony }}${{ matrix.prefer-lowest && ', prefer-lowest' || '' }})
runs-on: ubuntu-latest
timeout-minutes: 15
# Non-tty runners report an 80-column terminal, so SymfonyStyle wraps long
# literals out of display assertions that pass locally. Pin the width every
# PHPUnit-running job uses to a realistic terminal.
env:
COLUMNS: 120
strategy:
fail-fast: false
matrix:
php: ['8.2', '8.3', '8.4', '8.5']
# Symfony 7.0–7.3 are end-of-life and receive no security patches, so the
# advisory-aware resolver can never install them — only the maintained
# lines (6.4 LTS, 7.4, 8.1) are exercised. 7.4 covers the 7.x API surface
# and 8.1 the 8.x one.
symfony: ['6.4.*', '7.4.*', '8.1.*']
# Symfony 8 requires PHP >= 8.4, so the 8.1 line cannot resolve on the
# older PHP versions of the matrix.
exclude:
- php: '8.2'
symfony: '8.1.*'
- php: '8.3'
symfony: '8.1.*'
include:
- php: '8.2'
symfony: '6.4.*'
prefer-lowest: true
composer-flags: '--prefer-lowest --prefer-stable'
steps:
- name: Checkout
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Setup PHP
uses: shivammathur/setup-php@f3e473d116dcccaddc5834248c87452386958240 # v2
with:
php-version: ${{ matrix.php }}
extensions: mbstring, xml, ctype, iconv, intl, pdo_sqlite
coverage: none
- name: Cache Composer dependencies
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: ~/.composer/cache
key: composer-php${{ matrix.php }}-sf${{ matrix.symfony }}-${{ hashFiles('composer.json') }}
restore-keys: |
composer-php${{ matrix.php }}-sf${{ matrix.symfony }}-
composer-php${{ matrix.php }}-
- name: Set Symfony version constraint
if: matrix.symfony != ''
run: echo "SYMFONY_REQUIRE=${{ matrix.symfony }}" >> $GITHUB_ENV
- name: Install Symfony Flex globally
run: |
composer global config --no-plugins allow-plugins.symfony/flex true
composer global require --no-progress --no-scripts --no-plugins symfony/flex
- name: Install dependencies
run: composer update --prefer-dist --no-progress --no-scripts ${{ matrix.composer-flags }}
- name: Run tests
env:
SYMFONY_DEPRECATIONS_HELPER: ${{ matrix.prefer-lowest && 'disabled=1' || 'max[direct]=0' }}
run: vendor/bin/phpunit
coverage:
name: Coverage
runs-on: ubuntu-latest
timeout-minutes: 15
# Hoisted to job level: the secrets context is not available inside step-level
# `if:` expressions — referencing it there fails the whole workflow at startup.
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
COLUMNS: 120
steps:
- name: Checkout
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Setup PHP
uses: shivammathur/setup-php@f3e473d116dcccaddc5834248c87452386958240 # v2
with:
php-version: '8.3'
extensions: mbstring, xml, ctype, iconv, intl, pdo_sqlite
coverage: pcov
- name: Cache Composer dependencies
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: ~/.composer/cache
key: composer-coverage-${{ hashFiles('composer.json') }}
restore-keys: composer-coverage-
- name: Install dependencies
run: composer install --prefer-dist --no-progress --no-scripts
- name: Run tests with coverage
run: vendor/bin/phpunit --coverage-clover=build/coverage/clover.xml --coverage-text=build/coverage/coverage.txt
- name: Enforce coverage threshold
run: |
php -r '
$xml = simplexml_load_file("build/coverage/clover.xml");
$metrics = $xml->xpath("/coverage/project/metrics")[0];
$covered = (int) $metrics["coveredstatements"];
$total = (int) $metrics["statements"];
$pct = $total > 0 ? ($covered / $total) * 100 : 0;
printf("Line coverage: %.2f%% (%d / %d)\n", $pct, $covered, $total);
exit($pct >= 95 ? 0 : 1);
'
- name: Upload coverage to Codecov
if: env.CODECOV_TOKEN != ''
uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7
with:
files: ./build/coverage/clover.xml
fail_ci_if_error: true
- name: Upload coverage artifact
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
if: always()
with:
name: coverage-clover
path: build/coverage/
retention-days: 14
security:
name: Composer audit
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- name: Checkout
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Setup PHP
uses: shivammathur/setup-php@f3e473d116dcccaddc5834248c87452386958240 # v2
with:
php-version: '8.3'
coverage: none
- name: Install dependencies
run: composer install --prefer-dist --no-progress --no-scripts
- name: Run composer audit
run: composer audit --locked
mutation:
name: Mutation testing
runs-on: ubuntu-latest
timeout-minutes: 30
# By far the slowest job — keep it off pull requests; MSI regressions are
# still caught on every push to main and on the weekly scheduled run.
if: github.event_name != 'pull_request'
env:
COLUMNS: 120
steps:
- name: Checkout
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
fetch-depth: 0
- name: Setup PHP
uses: shivammathur/setup-php@f3e473d116dcccaddc5834248c87452386958240 # v2
with:
php-version: '8.3'
extensions: mbstring, xml, ctype, iconv, intl, pdo_sqlite
coverage: pcov
- name: Cache Composer dependencies
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: ~/.composer/cache
key: composer-mutation-${{ hashFiles('composer.json') }}
restore-keys: composer-mutation-
- name: Install dependencies
run: composer install --prefer-dist --no-progress --no-scripts
- name: Run Infection
# Explicit --threads=4 (ubuntu-latest has 4 vCPUs): --threads=max fails to
# detect the core count on the runner and falls back to a single thread,
# which pushed the run to ~18 min — close to the job timeout.
#
# --show-mutations=0: Infection's console diff colorizer crashes with
# "Incorrectly nested style tag" whenever an escaped mutant's inline diff
# boundary lands right after a backslash (its own `\<` escaping defeats the
# style tag it inserts next). Escaped mutants stay visible as GitHub
# annotations (--logger-github) and in the uploaded infection.log.
run: vendor/bin/infection --threads=4 --logger-github --show-mutations=0
- name: Upload mutation log
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
if: always()
with:
name: infection-log
path: infection.log
retention-days: 14