Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 75 additions & 0 deletions .claude/commands/add-php-version.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
---
description: Add a new PHP version to CI (DependenciesVersions, Dockerfile, phpstan configs)
argument-hint: <php-version>
allowed-tools: [Read, Edit, Write, Glob, Grep, Bash]
---

Add PHP version **$ARGUMENTS** to the CI pipeline. Follow each step exactly.

## Step 1: Validate input

The argument must be a PHP version like `8.5`. If `$ARGUMENTS` is empty or does not match the pattern `X.Y` (single digit dot single digit), stop and tell the user the correct usage: `/add-php-version 8.5`.

Derive these values from the version `X.Y`:
- **MAJOR** = X (e.g. `8`)
- **MINOR** = Y (e.g. `5`)
- **phpVersion int** = X0Y00 (e.g. `80500`) — pad minor to two digits, append two zeros

## Step 2: Update `bin/DependenciesVersions.php`

Read the file. In the `$this->phpVersions` chain (around line 33-38), add `->add('X.Y')` immediately before `->setReadOnly()`. Follow the existing indentation (16 spaces).

## Step 3: Update `docker/ci/Dockerfile`

Read the file. Find the last `# PHP X.Y` comment block in the `apt-get install` section (the block with `phpX.Y-cli`, `phpX.Y-dom`, `phpX.Y-mbstring`). Add a new block immediately after it with the same pattern:

```
# PHP X.Y
phpX.Y-cli \
phpX.Y-dom \
phpX.Y-mbstring \
```

Make sure the backslash continuation is preserved on the line before the new block.

## Step 4: Create phpstan config files

1. Glob `config/ci/phpstan/php-*.neon` to find all existing Symfony versions in use.
2. Extract the unique Symfony versions (e.g. `7.0`, `7.1`, `7.2`, `7.3`).
3. For each Symfony version `Z.A`, create `config/ci/phpstan/php-X.Y.symfony-Z.A.neon` with this exact content (no trailing newline after the last line):

```
includes:
- common.neon
parameters:
phpVersion: <phpVersion int>
tmpDir: ../../../var/ci/phpstan/php-X-Y/symfony-Z-A
```

Where dashes replace dots in directory names (e.g. `php-8-5/symfony-7-0`).

## Step 5: Update `changelog.md`

Read the file. Under the `### master` heading, add a line:

```
- Add compatibility for PHP X.Y
```

If similar lines already exist, add it after the last one.

## Step 6: Build Docker image

Run `bin/ci/docker` to rebuild the CI Docker image with the new PHP version. This may take several minutes.

## Step 7: Run CI validation

Run `bin/ci/validate` to verify everything passes. If it fails, fix the errors and re-run until it passes.

## Step 8: Report

List all files that were modified or created.

At the very end, display this reminder message in bold yellow (using markdown):

> **⚠️ Don't forget to push the Docker image: `bin/ci/docker --push`**
102 changes: 102 additions & 0 deletions .claude/commands/add-symfony-version.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
---
description: Add a new Symfony version to CI (DependenciesVersions, Dockerfile, phpstan configs)
argument-hint: <symfony-version>
allowed-tools: [Read, Edit, Write, Glob, Grep, Bash]
---

Add Symfony version **$ARGUMENTS** to the CI pipeline. Follow each step exactly.

## Step 1: Validate input

The argument must be a Symfony version like `7.4`. If `$ARGUMENTS` is empty or does not match the pattern `X.Y` (single digit dot single digit), stop and tell the user the correct usage: `/add-symfony-version 7.4`.

Derive these values from the version `X.Y`:
- **MAJOR** = X (e.g. `7`)
- **MINOR** = Y (e.g. `4`)
- **ENV suffix** = X_Y (e.g. `7_4`)
- **dir suffix** = X-Y (e.g. `7-4`)

## Step 2: Update `composer.json`

Read the file. Update the `require` section so that `symfony/console` and `symfony/process` version constraints include the new version X.Y. If the new version is already within the existing constraint range, no change is needed. If adding a new major version (e.g. Y is `0`), add `|| ^X.0` to the constraint — this covers all future minors of that major (e.g. `^8.0` covers 8.0, 8.1, 8.2, etc.). If adding a minor within an existing major, extend the upper bound to cover `X.Y.*` (e.g. change `<8.0.0` to `<X.(Y+1).0`).

## Step 3: Update `bin/DependenciesVersions.php`

Read the file. In the `$this->symfonyVersions` chain, add `->add('X.Y')` immediately before `->setReadOnly()`. Follow the existing indentation (16 spaces).

## Step 4: Update `docker/ci/Dockerfile`

Read the file. Three sections need updating:

### 4a: Add environment variable

Find the last `ENV COMPOSER_HOME_SYMFONY_` line. Add immediately after it:

```
ENV COMPOSER_HOME_SYMFONY_X_Y=/composer/symfony-X-Y
```

### 4b: Add composer global require

Find the `# Install Symfony components` section. Add a new line after the last `composer global require` line, following the existing pattern:

```
&& COMPOSER_HOME=${COMPOSER_HOME_SYMFONY_X_Y} composer global require symfony/process:X.Y.* symfony/console:X.Y.* \
```

Make sure the backslash continuation is preserved on the previous line.

### 4c: Add cleanup block

Find the last `rm -rf` block for a Symfony version (the one with `COMPOSER_HOME_SYMFONY_`). Add a new block immediately after it, before `&& rm -rf /tmp/*`:

```
&& rm -rf \
${COMPOSER_HOME_SYMFONY_X_Y}/.htaccess \
${COMPOSER_HOME_SYMFONY_X_Y}/cache \
${COMPOSER_HOME_SYMFONY_X_Y}/composer.json \
${COMPOSER_HOME_SYMFONY_X_Y}/composer.lock \
```

## Step 5: Create phpstan config files

1. Glob `config/ci/phpstan/php-*.neon` to find all existing PHP versions in use.
2. Extract the unique PHP versions (e.g. `8.2`, `8.3`, `8.4`, `8.5`).
3. For each PHP version, derive **phpVersion int** = X0Y00 (e.g. `80200` for `8.2`) — pad minor to two digits, append two zeros.
4. For each PHP version `P.Q`, create `config/ci/phpstan/php-P.Q.symfony-X.Y.neon` with this exact content (no trailing newline after the last line):

```
includes:
- common.neon
parameters:
phpVersion: <phpVersion int>
tmpDir: ../../../var/ci/phpstan/php-P-Q/symfony-X-Y
```

Where dashes replace dots in directory names (e.g. `php-8-2/symfony-7-4`).

## Step 6: Update `changelog.md`

Read the file. Under the `### master` heading, add a line:

```
- Add compatibility for Symfony X.Y
```

If similar lines already exist, add it after the last one.

## Step 7: Build Docker image

Run `bin/ci/docker` to rebuild the CI Docker image with the new Symfony version. This may take several minutes.

## Step 8: Run CI validation

Run `bin/ci/validate` to verify everything passes. If it fails, fix the errors and re-run until it passes.

## Step 9: Report

List all files that were modified or created.

At the very end, display this reminder message in bold yellow (using markdown):

> **⚠️ Don't forget to push the Docker image: `bin/ci/docker --push`**
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/.claude/settings.local.json
/var/
!/var/.gitkeep
/vendor/
60 changes: 60 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Project Overview

PHP library for running shell processes in parallel with dependency management between them. Built on Symfony Console/Process components. The library itself is used to run its own CI validation (self-dogfooding).

## Common Commands

**Run tests locally (requires PHP 8.2+ and ext-pcntl):**
```bash
composer install
vendor/bin/phpunit -c config/ci/phpunit.xml
```

**Run full CI validation (runs all checks in parallel using this library itself):**
```bash
bin/ci/env # install dependencies
bin/ci/validate # runs phpcs, phpstan, phpunit, composer checks, etc. in parallel
```

**Run individual CI tools:**
```bash
bin/ci/phpcs # code style (steevanb custom sniffs)
bin/ci/phpstan # static analysis (level 9)
bin/ci/phpunit-coverage # tests with coverage
```

CI tests across a matrix: PHP 8.2/8.3/8.4 × Symfony 7.0/7.1/7.2/7.3.

## Architecture

### Core Components

**`ParallelProcessesApplication`** (`src/Console/Application/`) — Main orchestrator. Extends Symfony `SingleCommandApplication`, implements `SignalableCommandInterface`. Uses a fluent builder interface to add processes, configure timeout/refresh interval/max parallel processes, then `run()`. Handles SIGINT for graceful shutdown.

**`Process`** (`src/Process/Process.php`) — Extends Symfony `Process`, implements `ProcessInterface`. Each process has configurable output verbosity levels (standard, error, failure, canceled), a `StartCondition` for dependency management, and cancellation support.

**`StartCondition`** (`src/Process/StartCondition.php`) — Controls when a process can start via three dependency collections:
- `processesTerminated` — start after these finish (regardless of success/failure)
- `processesSuccessful` — start only after these succeed
- `processesFailed` — start only after these fail

**`BootstrapProcess` / `TearDownProcess`** — Special process types. Bootstrap processes automatically become dependencies (via `processesSuccessful`) for all standard processes. TearDown processes automatically wait for all standard processes to terminate.

**Theme system** (`src/Console/Application/Theme/`) — Strategy pattern. `DefaultTheme` shows real-time colored status; `SummaryTheme` shows minimal output (used in CI). Custom themes implement `ThemeInterface`. Selected via `--theme` CLI option.

**`ProcessInterfaceCollection`** — Type-safe collection with filtering: `getReady()`, `getStarted()`, `countRunning()`.

## Code Conventions

- `declare(strict_types=1)` on every file
- PHP 8.2+ features: constructor property promotion, `readonly`, enums, `#[\Override]` attribute
- PHPStan level 9 (strictest)
- Fluent interface pattern (methods return `static`)
- Interfaces suffixed with `Interface`, no `Abstract` prefix convention
- Tests: one test class per method, `@covers` annotation required, test directory mirrors `src/` structure
- PHPUnit config: `config/ci/phpunit.xml` (with coverage), `config/release/phpunit.xml`
- PHPStan configs: `config/ci/phpstan/` (per PHP/Symfony version combination)
3 changes: 3 additions & 0 deletions bin/DependenciesVersions.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ public function __construct(array $argv)
->add('8.2')
->add('8.3')
->add('8.4')
->add('8.5')
->setReadOnly();
}

Expand All @@ -45,6 +46,8 @@ public function __construct(array $argv)
->add('7.1')
->add('7.2')
->add('7.3')
->add('7.4')
->add('8.0')
->setReadOnly();
}
}
Expand Down
7 changes: 3 additions & 4 deletions bin/ci/phpstan
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,12 @@ clearCache=false
for arg in "$@"; do
if [ "${arg:0:6}" == "--php=" ]; then
phpVersion="${arg:6}"
phpstanParameters="${phpstanParameters} ${arg}"
elif [ "${arg:0:10}" == "--symfony=" ]; then
symfonyVersion="${arg:10}"
phpstanParameters="${phpstanParameters} ${arg}"
elif [ "${arg}" == "--clear-cache" ]; then
clearCache=true
else
phpstanParameters="${phpstanParameters} ${arg}"
fi
done

Expand All @@ -36,6 +36,5 @@ else
"${PHPSTAN_BIN_PATH}" \
analyse \
--ansi \
--configuration config/ci/phpstan/php-"${phpVersion}".symfony-"${symfonyVersion}".neon \
${phpstanParameters}
--configuration config/ci/phpstan/php-"${phpVersion}".symfony-"${symfonyVersion}".neon
fi
2 changes: 1 addition & 1 deletion bin/ci/phpstan.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
require $rootPath . '/vendor/autoload.php';
require $rootPath . '/bin/DependenciesVersions.php';

function createPhpstanProcesses($dependenciesVersions): ProcessInterfaceCollection
function createPhpstanProcesses(DependenciesVersions $dependenciesVersions): ProcessInterfaceCollection
{
$return = new ProcessInterfaceCollection();
foreach ($dependenciesVersions->getPhpVersions()->toArray() as $phpVersion) {
Expand Down
2 changes: 2 additions & 0 deletions changelog.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
### master

- Add compatibility for PHP 8.5, Symfony 7.4 and 8.0

### [1.1.0](../../compare/1.0.1...1.1.0) - 2025-11-16

- Add `docker` and `docker compose` in `alpine` Docker image
Expand Down
4 changes: 2 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@
"php": "^8.2",
"ext-pcntl": "*",
"steevanb/php-collection": "^6.0",
"symfony/console": "^7.0",
"symfony/process": "7.0.0 || >=7.1.5 <8.0.0"
"symfony/console": "^7.0 || ^8.0",
"symfony/process": "7.0.0 || >=7.1.5 <8.0.0 || ^8.0"
},
"require-dev": {
"ext-simplexml": "*",
Expand Down
5 changes: 5 additions & 0 deletions config/ci/phpstan/php-8.2.symfony-7.4.neon
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
includes:
- common.neon
parameters:
phpVersion: 80200
tmpDir: ../../../var/ci/phpstan/php-8-2/symfony-7-4
5 changes: 5 additions & 0 deletions config/ci/phpstan/php-8.2.symfony-8.0.neon
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
includes:
- common.neon
parameters:
phpVersion: 80200
tmpDir: ../../../var/ci/phpstan/php-8-2/symfony-8-0
5 changes: 5 additions & 0 deletions config/ci/phpstan/php-8.3.symfony-7.4.neon
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
includes:
- common.neon
parameters:
phpVersion: 80300
tmpDir: ../../../var/ci/phpstan/php-8-3/symfony-7-4
5 changes: 5 additions & 0 deletions config/ci/phpstan/php-8.3.symfony-8.0.neon
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
includes:
- common.neon
parameters:
phpVersion: 80300
tmpDir: ../../../var/ci/phpstan/php-8-3/symfony-8-0
5 changes: 5 additions & 0 deletions config/ci/phpstan/php-8.4.symfony-7.4.neon
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
includes:
- common.neon
parameters:
phpVersion: 80400
tmpDir: ../../../var/ci/phpstan/php-8-4/symfony-7-4
5 changes: 5 additions & 0 deletions config/ci/phpstan/php-8.4.symfony-8.0.neon
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
includes:
- common.neon
parameters:
phpVersion: 80400
tmpDir: ../../../var/ci/phpstan/php-8-4/symfony-8-0
5 changes: 5 additions & 0 deletions config/ci/phpstan/php-8.5.symfony-7.0.neon
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
includes:
- common.neon
parameters:
phpVersion: 80500
tmpDir: ../../../var/ci/phpstan/php-8-5/symfony-7-0
5 changes: 5 additions & 0 deletions config/ci/phpstan/php-8.5.symfony-7.1.neon
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
includes:
- common.neon
parameters:
phpVersion: 80500
tmpDir: ../../../var/ci/phpstan/php-8-5/symfony-7-1
5 changes: 5 additions & 0 deletions config/ci/phpstan/php-8.5.symfony-7.2.neon
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
includes:
- common.neon
parameters:
phpVersion: 80500
tmpDir: ../../../var/ci/phpstan/php-8-5/symfony-7-2
5 changes: 5 additions & 0 deletions config/ci/phpstan/php-8.5.symfony-7.3.neon
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
includes:
- common.neon
parameters:
phpVersion: 80500
tmpDir: ../../../var/ci/phpstan/php-8-5/symfony-7-3
5 changes: 5 additions & 0 deletions config/ci/phpstan/php-8.5.symfony-7.4.neon
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
includes:
- common.neon
parameters:
phpVersion: 80500
tmpDir: ../../../var/ci/phpstan/php-8-5/symfony-7-4
5 changes: 5 additions & 0 deletions config/ci/phpstan/php-8.5.symfony-8.0.neon
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
includes:
- common.neon
parameters:
phpVersion: 80500
tmpDir: ../../../var/ci/phpstan/php-8-5/symfony-8-0
Loading