A CI pipeline that fits your PHP project
AxonPHP CLI reads your composer.json, works out which PHP versions and tools you use, and writes the pipeline for your CI service. No config file, no questions.
- GitHub Actions
- GitLab CI
- Bitbucket Pipelines
- CircleCI
$ vendor/bin/axonphp ci:init github AxonPHP CLI · GitHub Actions ============================ Project /home/you/acme-library PHP versions 8.2, 8.3, 8.4, 8.5 (from "php": "^8.2") Extensions intl Tests PHPUnit Static analysis PHPStan Code style PHP-CS-Fixer Branches main Extras coverage (at least 90%), lowest dependencies [OK] Created .github/workflows/ci.yml
- 4CI services
- 21tools detected
- 0config files needed
- 100%line coverage, enforced
Your composer.json already says what CI should run
Most pipelines start as a copy of the one from the last project, fixed up by hand. AxonPHP derives it from what the project declares.
1 Read
{
"name": "acme/library",
"require": {
"php": "^8.2",
"ext-intl": "*"
},
"require-dev": {
"phpunit/phpunit": "^12.0",
"phpstan/phpstan": "^2.0",
"friendsofphp/php-cs-fixer": "^3.0"
},
"extra": {
"axonphp": {
"min-coverage": 90,
"lowest": true
}
}
}The php constraint, every ext-* requirement and the packages you require.
2 Derive
- PHP matrix
- 8.28.38.48.5
- Tools
- PHPUnitPHPStanPHP-CS-Fixer
- Extras
- coverage ≥ 90%lowest dependencies
Which PHP versions to test, which extensions to install and which tools to run.
3 Write
- GitHub Actions
.github/workflows/ci.yml - GitLab CI
.gitlab-ci.yml - Bitbucket Pipelines
bitbucket-pipelines.yml - CircleCI
.circleci/config.yml
One file for your CI service. It is plain YAML and yours to edit.
Pick a project, see its pipeline
This is the real output, rendered by the same code the command runs. Nothing here is written by hand.
A package that supports four PHP versions, measures coverage and tests its lowest dependencies.
{
"name": "acme/library",
"require": {
"php": "^8.2",
"ext-intl": "*"
},
"require-dev": {
"phpunit/phpunit": "^12.0",
"phpstan/phpstan": "^2.0",
"friendsofphp/php-cs-fixer": "^3.0"
},
"extra": {
"axonphp": {
"min-coverage": 90,
"lowest": true
}
}
}
# Generated by AxonPHP CLI (https://github.com/maximilianfeix/AxonPHPCLI). Edit freely.
name: CI
on:
push:
branches: ['main']
pull_request:
permissions:
contents: read
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
quality:
name: Code quality
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v7
- name: Set up PHP
uses: shivammathur/setup-php@v2
with:
php-version: '8.5'
extensions: intl
coverage: none
- name: Install dependencies
uses: ramsey/composer-install@v4
- name: Static analysis (PHPStan)
run: vendor/bin/phpstan analyse --no-progress
- name: Code style (PHP-CS-Fixer)
run: vendor/bin/php-cs-fixer check --diff
tests:
name: Tests (PHP ${{ matrix.php }}, ${{ matrix.dependencies }} dependencies)
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
php: ['8.2', '8.3', '8.4', '8.5']
dependencies: ['highest']
include:
- php: '8.2'
dependencies: 'lowest'
steps:
- name: Checkout
uses: actions/checkout@v7
- name: Set up PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php }}
extensions: intl
coverage: none
- name: Validate composer.json
run: composer validate --strict
- name: Install dependencies
uses: ramsey/composer-install@v4
with:
dependency-versions: ${{ matrix.dependencies }}
- name: Tests (PHPUnit)
run: vendor/bin/phpunit
coverage:
name: Code coverage
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v7
- name: Set up PHP
uses: shivammathur/setup-php@v2
with:
php-version: '8.5'
extensions: intl
coverage: pcov
- name: Install dependencies
uses: ramsey/composer-install@v4
- name: Code coverage (PHPUnit)
run: vendor/bin/phpunit --coverage-text --coverage-clover=coverage.xml
- name: Upload coverage report
uses: actions/upload-artifact@v7
with:
name: coverage
path: coverage.xml
- name: Require 90% line coverage
run: php -r '$m = simplexml_load_file("coverage.xml")->project->metrics; $c = 100 * (int) $m["coveredstatements"] / max(1, (int) $m["statements"]); printf("Line coverage %.2f%%, required 90%%\n", $c); exit((int) ($c < 90));'
# Generated by AxonPHP CLI (https://github.com/maximilianfeix/AxonPHPCLI). Edit freely.
stages:
- quality
- test
workflow:
rules:
- if: $CI_PIPELINE_SOURCE == 'merge_request_event'
- if: $CI_COMMIT_BRANCH && $CI_OPEN_MERGE_REQUESTS
when: never
- if: $CI_COMMIT_TAG
- if: $CI_COMMIT_BRANCH == 'main'
variables:
COMPOSER_ALLOW_SUPERUSER: '1'
COMPOSER_NO_INTERACTION: '1'
COMPOSER_CACHE_DIR: $CI_PROJECT_DIR/.composer-cache
default:
cache:
key:
files:
- composer.lock
- composer.json
paths:
- .composer-cache/
before_script:
- curl -sSLf -o /usr/local/bin/install-php-extensions https://github.com/mlocati/docker-php-extension-installer/releases/latest/download/install-php-extensions
- chmod +x /usr/local/bin/install-php-extensions
- install-php-extensions @composer zip intl
- composer install --prefer-dist --no-progress
quality:
stage: quality
image: php:8.5-cli
script:
- vendor/bin/phpstan analyse --no-progress
- vendor/bin/php-cs-fixer check --diff
tests:
stage: test
image: php:${PHP_VERSION}-cli
parallel:
matrix:
- PHP_VERSION: ['8.2', '8.3', '8.4', '8.5']
script:
- composer validate --strict
- vendor/bin/phpunit
tests:lowest:
stage: test
image: php:8.2-cli
before_script:
- curl -sSLf -o /usr/local/bin/install-php-extensions https://github.com/mlocati/docker-php-extension-installer/releases/latest/download/install-php-extensions
- chmod +x /usr/local/bin/install-php-extensions
- install-php-extensions @composer zip intl
- composer update --prefer-lowest --prefer-stable --prefer-dist --no-progress
script:
- vendor/bin/phpunit
coverage:
stage: test
image: php:8.5-cli
script:
- install-php-extensions pcov
- vendor/bin/phpunit --coverage-text --coverage-clover=coverage.xml
- php -r '$m = simplexml_load_file("coverage.xml")->project->metrics; $c = 100 * (int) $m["coveredstatements"] / max(1, (int) $m["statements"]); printf("Line coverage %.2f%%, required 90%%\n", $c); exit((int) ($c < 90));'
coverage: '/^\s*(?:Lines|Total):\s*\d+\.\d+\s*%/'
artifacts:
when: always
paths:
- coverage.xml
# Generated by AxonPHP CLI (https://github.com/maximilianfeix/AxonPHPCLI). Edit freely.
image: php:8.5-cli
definitions:
steps:
- step: &quality
name: Code quality
image: php:8.5-cli
caches:
- composer
script:
- export COMPOSER_ALLOW_SUPERUSER=1
- curl -sSLf -o /usr/local/bin/install-php-extensions https://github.com/mlocati/docker-php-extension-installer/releases/latest/download/install-php-extensions
- chmod +x /usr/local/bin/install-php-extensions
- install-php-extensions @composer zip intl
- composer install --prefer-dist --no-progress
- vendor/bin/phpstan analyse --no-progress
- vendor/bin/php-cs-fixer check --diff
- step: &tests-php-8-2
name: Tests (PHP 8.2)
image: php:8.2-cli
caches:
- composer
script:
- export COMPOSER_ALLOW_SUPERUSER=1
- curl -sSLf -o /usr/local/bin/install-php-extensions https://github.com/mlocati/docker-php-extension-installer/releases/latest/download/install-php-extensions
- chmod +x /usr/local/bin/install-php-extensions
- install-php-extensions @composer zip intl
- composer install --prefer-dist --no-progress
- composer validate --strict
- vendor/bin/phpunit
- step: &tests-php-8-3
name: Tests (PHP 8.3)
image: php:8.3-cli
caches:
- composer
script:
- export COMPOSER_ALLOW_SUPERUSER=1
- curl -sSLf -o /usr/local/bin/install-php-extensions https://github.com/mlocati/docker-php-extension-installer/releases/latest/download/install-php-extensions
- chmod +x /usr/local/bin/install-php-extensions
- install-php-extensions @composer zip intl
- composer install --prefer-dist --no-progress
- composer validate --strict
- vendor/bin/phpunit
- step: &tests-php-8-4
name: Tests (PHP 8.4)
image: php:8.4-cli
caches:
- composer
script:
- export COMPOSER_ALLOW_SUPERUSER=1
- curl -sSLf -o /usr/local/bin/install-php-extensions https://github.com/mlocati/docker-php-extension-installer/releases/latest/download/install-php-extensions
- chmod +x /usr/local/bin/install-php-extensions
- install-php-extensions @composer zip intl
- composer install --prefer-dist --no-progress
- composer validate --strict
- vendor/bin/phpunit
- step: &tests-php-8-5
name: Tests (PHP 8.5)
image: php:8.5-cli
caches:
- composer
script:
- export COMPOSER_ALLOW_SUPERUSER=1
- curl -sSLf -o /usr/local/bin/install-php-extensions https://github.com/mlocati/docker-php-extension-installer/releases/latest/download/install-php-extensions
- chmod +x /usr/local/bin/install-php-extensions
- install-php-extensions @composer zip intl
- composer install --prefer-dist --no-progress
- composer validate --strict
- vendor/bin/phpunit
- step: &tests-lowest
name: Tests (PHP 8.2, lowest dependencies)
image: php:8.2-cli
caches:
- composer
script:
- export COMPOSER_ALLOW_SUPERUSER=1
- curl -sSLf -o /usr/local/bin/install-php-extensions https://github.com/mlocati/docker-php-extension-installer/releases/latest/download/install-php-extensions
- chmod +x /usr/local/bin/install-php-extensions
- install-php-extensions @composer zip intl
- composer update --prefer-lowest --prefer-stable --prefer-dist --no-progress
- vendor/bin/phpunit
- step: &coverage
name: Code coverage
image: php:8.5-cli
caches:
- composer
script:
- export COMPOSER_ALLOW_SUPERUSER=1
- curl -sSLf -o /usr/local/bin/install-php-extensions https://github.com/mlocati/docker-php-extension-installer/releases/latest/download/install-php-extensions
- chmod +x /usr/local/bin/install-php-extensions
- install-php-extensions @composer zip intl
- composer install --prefer-dist --no-progress
- install-php-extensions pcov
- vendor/bin/phpunit --coverage-text --coverage-clover=coverage.xml
- php -r '$m = simplexml_load_file("coverage.xml")->project->metrics; $c = 100 * (int) $m["coveredstatements"] / max(1, (int) $m["statements"]); printf("Line coverage %.2f%%, required 90%%\n", $c); exit((int) ($c < 90));'
pipelines:
pull-requests:
'**':
- step: *quality
- parallel:
- step: *tests-php-8-2
- step: *tests-php-8-3
- step: *tests-php-8-4
- step: *tests-php-8-5
- step: *tests-lowest
- step: *coverage
branches:
'main':
- step: *quality
- parallel:
- step: *tests-php-8-2
- step: *tests-php-8-3
- step: *tests-php-8-4
- step: *tests-php-8-5
- step: *tests-lowest
- step: *coverage
# Generated by AxonPHP CLI (https://github.com/maximilianfeix/AxonPHPCLI). Edit freely.
version: 2.1
jobs:
quality:
docker:
- image: php:8.5-cli
environment:
COMPOSER_ALLOW_SUPERUSER: '1'
COMPOSER_NO_INTERACTION: '1'
COMPOSER_CACHE_DIR: /tmp/composer-cache
steps:
- checkout
- run:
name: Set up PHP
command: |
curl -sSLf -o /usr/local/bin/install-php-extensions https://github.com/mlocati/docker-php-extension-installer/releases/latest/download/install-php-extensions
chmod +x /usr/local/bin/install-php-extensions
install-php-extensions @composer zip intl
- restore_cache:
keys:
- composer-{{ checksum "composer.json" }}
- composer-
- run:
name: Install dependencies
command: composer install --prefer-dist --no-progress
- save_cache:
key: composer-{{ checksum "composer.json" }}
paths:
- /tmp/composer-cache
- run:
name: Static analysis (PHPStan)
command: vendor/bin/phpstan analyse --no-progress
- run:
name: Code style (PHP-CS-Fixer)
command: vendor/bin/php-cs-fixer check --diff
tests:
parameters:
php:
type: string
docker:
- image: php:<< parameters.php >>-cli
environment:
COMPOSER_ALLOW_SUPERUSER: '1'
COMPOSER_NO_INTERACTION: '1'
COMPOSER_CACHE_DIR: /tmp/composer-cache
steps:
- checkout
- run:
name: Set up PHP
command: |
curl -sSLf -o /usr/local/bin/install-php-extensions https://github.com/mlocati/docker-php-extension-installer/releases/latest/download/install-php-extensions
chmod +x /usr/local/bin/install-php-extensions
install-php-extensions @composer zip intl
- restore_cache:
keys:
- composer-{{ checksum "composer.json" }}
- composer-
- run:
name: Install dependencies
command: composer install --prefer-dist --no-progress
- save_cache:
key: composer-{{ checksum "composer.json" }}
paths:
- /tmp/composer-cache
- run:
name: Validate composer.json
command: composer validate --strict
- run:
name: Tests (PHPUnit)
command: vendor/bin/phpunit
tests-lowest:
docker:
- image: php:8.2-cli
environment:
COMPOSER_ALLOW_SUPERUSER: '1'
COMPOSER_NO_INTERACTION: '1'
COMPOSER_CACHE_DIR: /tmp/composer-cache
steps:
- checkout
- run:
name: Set up PHP
command: |
curl -sSLf -o /usr/local/bin/install-php-extensions https://github.com/mlocati/docker-php-extension-installer/releases/latest/download/install-php-extensions
chmod +x /usr/local/bin/install-php-extensions
install-php-extensions @composer zip intl
- restore_cache:
keys:
- composer-{{ checksum "composer.json" }}
- composer-
- run:
name: Install dependencies
command: composer update --prefer-lowest --prefer-stable --prefer-dist --no-progress
- save_cache:
key: composer-{{ checksum "composer.json" }}
paths:
- /tmp/composer-cache
- run:
name: Tests (PHPUnit)
command: vendor/bin/phpunit
coverage:
docker:
- image: php:8.5-cli
environment:
COMPOSER_ALLOW_SUPERUSER: '1'
COMPOSER_NO_INTERACTION: '1'
COMPOSER_CACHE_DIR: /tmp/composer-cache
steps:
- checkout
- run:
name: Set up PHP
command: |
curl -sSLf -o /usr/local/bin/install-php-extensions https://github.com/mlocati/docker-php-extension-installer/releases/latest/download/install-php-extensions
chmod +x /usr/local/bin/install-php-extensions
install-php-extensions @composer zip intl
- restore_cache:
keys:
- composer-{{ checksum "composer.json" }}
- composer-
- run:
name: Install dependencies
command: composer install --prefer-dist --no-progress
- save_cache:
key: composer-{{ checksum "composer.json" }}
paths:
- /tmp/composer-cache
- run:
name: Install PCOV
command: install-php-extensions pcov
- run:
name: Code coverage (PHPUnit)
command: vendor/bin/phpunit --coverage-text --coverage-clover=coverage.xml
- store_artifacts:
path: coverage.xml
- run:
name: Require 90% line coverage
command: php -r '$m = simplexml_load_file("coverage.xml")->project->metrics; $c = 100 * (int) $m["coveredstatements"] / max(1, (int) $m["statements"]); printf("Line coverage %.2f%%, required 90%%\n", $c); exit((int) ($c < 90));'
workflows:
ci:
jobs:
- quality
- tests:
name: tests-php-<< matrix.php >>
matrix:
parameters:
php: ['8.2', '8.3', '8.4', '8.5']
- tests-lowest
- coverage
Pest, Larastan and Pint, with a security audit before anything else runs.
{
"name": "acme/shop",
"require": {
"php": "^8.3",
"ext-pdo": "*",
"laravel/framework": "^12.0"
},
"require-dev": {
"pestphp/pest": "^4.0",
"larastan/larastan": "^3.0",
"laravel/pint": "^1.0"
},
"extra": {
"axonphp": {
"audit": true
}
}
}
# Generated by AxonPHP CLI (https://github.com/maximilianfeix/AxonPHPCLI). Edit freely.
name: CI
on:
push:
branches: ['main']
pull_request:
permissions:
contents: read
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
quality:
name: Code quality
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v7
- name: Set up PHP
uses: shivammathur/setup-php@v2
with:
php-version: '8.5'
extensions: pdo
coverage: none
- name: Install dependencies
uses: ramsey/composer-install@v4
- name: Security audit (Composer)
run: composer audit
- name: Static analysis (PHPStan)
run: vendor/bin/phpstan analyse --no-progress
- name: Code style (Pint)
run: vendor/bin/pint --test
tests:
name: Tests (PHP ${{ matrix.php }})
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
php: ['8.3', '8.4', '8.5']
steps:
- name: Checkout
uses: actions/checkout@v7
- name: Set up PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php }}
extensions: pdo
coverage: none
- name: Validate composer.json
run: composer validate --strict
- name: Install dependencies
uses: ramsey/composer-install@v4
- name: Tests (Pest)
run: vendor/bin/pest
# Generated by AxonPHP CLI (https://github.com/maximilianfeix/AxonPHPCLI). Edit freely.
stages:
- quality
- test
workflow:
rules:
- if: $CI_PIPELINE_SOURCE == 'merge_request_event'
- if: $CI_COMMIT_BRANCH && $CI_OPEN_MERGE_REQUESTS
when: never
- if: $CI_COMMIT_TAG
- if: $CI_COMMIT_BRANCH == 'main'
variables:
COMPOSER_ALLOW_SUPERUSER: '1'
COMPOSER_NO_INTERACTION: '1'
COMPOSER_CACHE_DIR: $CI_PROJECT_DIR/.composer-cache
default:
cache:
key:
files:
- composer.lock
- composer.json
paths:
- .composer-cache/
before_script:
- curl -sSLf -o /usr/local/bin/install-php-extensions https://github.com/mlocati/docker-php-extension-installer/releases/latest/download/install-php-extensions
- chmod +x /usr/local/bin/install-php-extensions
- install-php-extensions @composer zip pdo
- composer install --prefer-dist --no-progress
quality:
stage: quality
image: php:8.5-cli
script:
- composer audit
- vendor/bin/phpstan analyse --no-progress
- vendor/bin/pint --test
tests:
stage: test
image: php:${PHP_VERSION}-cli
parallel:
matrix:
- PHP_VERSION: ['8.3', '8.4', '8.5']
script:
- composer validate --strict
- vendor/bin/pest
# Generated by AxonPHP CLI (https://github.com/maximilianfeix/AxonPHPCLI). Edit freely.
image: php:8.5-cli
definitions:
steps:
- step: &quality
name: Code quality
image: php:8.5-cli
caches:
- composer
script:
- export COMPOSER_ALLOW_SUPERUSER=1
- curl -sSLf -o /usr/local/bin/install-php-extensions https://github.com/mlocati/docker-php-extension-installer/releases/latest/download/install-php-extensions
- chmod +x /usr/local/bin/install-php-extensions
- install-php-extensions @composer zip pdo
- composer install --prefer-dist --no-progress
- composer audit
- vendor/bin/phpstan analyse --no-progress
- vendor/bin/pint --test
- step: &tests-php-8-3
name: Tests (PHP 8.3)
image: php:8.3-cli
caches:
- composer
script:
- export COMPOSER_ALLOW_SUPERUSER=1
- curl -sSLf -o /usr/local/bin/install-php-extensions https://github.com/mlocati/docker-php-extension-installer/releases/latest/download/install-php-extensions
- chmod +x /usr/local/bin/install-php-extensions
- install-php-extensions @composer zip pdo
- composer install --prefer-dist --no-progress
- composer validate --strict
- vendor/bin/pest
- step: &tests-php-8-4
name: Tests (PHP 8.4)
image: php:8.4-cli
caches:
- composer
script:
- export COMPOSER_ALLOW_SUPERUSER=1
- curl -sSLf -o /usr/local/bin/install-php-extensions https://github.com/mlocati/docker-php-extension-installer/releases/latest/download/install-php-extensions
- chmod +x /usr/local/bin/install-php-extensions
- install-php-extensions @composer zip pdo
- composer install --prefer-dist --no-progress
- composer validate --strict
- vendor/bin/pest
- step: &tests-php-8-5
name: Tests (PHP 8.5)
image: php:8.5-cli
caches:
- composer
script:
- export COMPOSER_ALLOW_SUPERUSER=1
- curl -sSLf -o /usr/local/bin/install-php-extensions https://github.com/mlocati/docker-php-extension-installer/releases/latest/download/install-php-extensions
- chmod +x /usr/local/bin/install-php-extensions
- install-php-extensions @composer zip pdo
- composer install --prefer-dist --no-progress
- composer validate --strict
- vendor/bin/pest
pipelines:
pull-requests:
'**':
- step: *quality
- parallel:
- step: *tests-php-8-3
- step: *tests-php-8-4
- step: *tests-php-8-5
branches:
'main':
- step: *quality
- parallel:
- step: *tests-php-8-3
- step: *tests-php-8-4
- step: *tests-php-8-5
# Generated by AxonPHP CLI (https://github.com/maximilianfeix/AxonPHPCLI). Edit freely.
version: 2.1
jobs:
quality:
docker:
- image: php:8.5-cli
environment:
COMPOSER_ALLOW_SUPERUSER: '1'
COMPOSER_NO_INTERACTION: '1'
COMPOSER_CACHE_DIR: /tmp/composer-cache
steps:
- checkout
- run:
name: Set up PHP
command: |
curl -sSLf -o /usr/local/bin/install-php-extensions https://github.com/mlocati/docker-php-extension-installer/releases/latest/download/install-php-extensions
chmod +x /usr/local/bin/install-php-extensions
install-php-extensions @composer zip pdo
- restore_cache:
keys:
- composer-{{ checksum "composer.json" }}
- composer-
- run:
name: Install dependencies
command: composer install --prefer-dist --no-progress
- save_cache:
key: composer-{{ checksum "composer.json" }}
paths:
- /tmp/composer-cache
- run:
name: Security audit (Composer)
command: composer audit
- run:
name: Static analysis (PHPStan)
command: vendor/bin/phpstan analyse --no-progress
- run:
name: Code style (Pint)
command: vendor/bin/pint --test
tests:
parameters:
php:
type: string
docker:
- image: php:<< parameters.php >>-cli
environment:
COMPOSER_ALLOW_SUPERUSER: '1'
COMPOSER_NO_INTERACTION: '1'
COMPOSER_CACHE_DIR: /tmp/composer-cache
steps:
- checkout
- run:
name: Set up PHP
command: |
curl -sSLf -o /usr/local/bin/install-php-extensions https://github.com/mlocati/docker-php-extension-installer/releases/latest/download/install-php-extensions
chmod +x /usr/local/bin/install-php-extensions
install-php-extensions @composer zip pdo
- restore_cache:
keys:
- composer-{{ checksum "composer.json" }}
- composer-
- run:
name: Install dependencies
command: composer install --prefer-dist --no-progress
- save_cache:
key: composer-{{ checksum "composer.json" }}
paths:
- /tmp/composer-cache
- run:
name: Validate composer.json
command: composer validate --strict
- run:
name: Tests (Pest)
command: vendor/bin/pest
workflows:
ci:
jobs:
- quality
- tests:
name: tests-php-<< matrix.php >>
matrix:
parameters:
php: ['8.3', '8.4', '8.5']
One PHP version, architecture rules, dependency checks and two long-lived branches.
{
"name": "acme/api",
"require": {
"php": ">=8.4",
"ext-ctype": "*",
"ext-iconv": "*",
"symfony/framework-bundle": "^7.3"
},
"require-dev": {
"phpunit/phpunit": "^12.0",
"phpstan/phpstan": "^2.0",
"deptrac/deptrac": "^3.0",
"friendsofphp/php-cs-fixer": "^3.0",
"icanhazstring/composer-unused": "^0.9"
},
"extra": {
"axonphp": {
"branches": [
"main",
"develop"
],
"audit": true
}
}
}
# Generated by AxonPHP CLI (https://github.com/maximilianfeix/AxonPHPCLI). Edit freely.
name: CI
on:
push:
branches: ['main', 'develop']
pull_request:
permissions:
contents: read
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
quality:
name: Code quality
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v7
- name: Set up PHP
uses: shivammathur/setup-php@v2
with:
php-version: '8.5'
extensions: ctype, iconv
coverage: none
- name: Install dependencies
uses: ramsey/composer-install@v4
- name: Security audit (Composer)
run: composer audit
- name: Static analysis (PHPStan)
run: vendor/bin/phpstan analyse --no-progress
- name: Static analysis (Deptrac)
run: vendor/bin/deptrac analyse --no-progress
- name: Code style (PHP-CS-Fixer)
run: vendor/bin/php-cs-fixer check --diff
- name: Dependencies (Composer Unused)
run: vendor/bin/composer-unused --no-progress
tests:
name: Tests (PHP ${{ matrix.php }})
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
php: ['8.4', '8.5']
steps:
- name: Checkout
uses: actions/checkout@v7
- name: Set up PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php }}
extensions: ctype, iconv
coverage: none
- name: Validate composer.json
run: composer validate --strict
- name: Install dependencies
uses: ramsey/composer-install@v4
- name: Tests (PHPUnit)
run: vendor/bin/phpunit
# Generated by AxonPHP CLI (https://github.com/maximilianfeix/AxonPHPCLI). Edit freely.
stages:
- quality
- test
workflow:
rules:
- if: $CI_PIPELINE_SOURCE == 'merge_request_event'
- if: $CI_COMMIT_BRANCH && $CI_OPEN_MERGE_REQUESTS
when: never
- if: $CI_COMMIT_TAG
- if: $CI_COMMIT_BRANCH == 'main'
- if: $CI_COMMIT_BRANCH == 'develop'
variables:
COMPOSER_ALLOW_SUPERUSER: '1'
COMPOSER_NO_INTERACTION: '1'
COMPOSER_CACHE_DIR: $CI_PROJECT_DIR/.composer-cache
default:
cache:
key:
files:
- composer.lock
- composer.json
paths:
- .composer-cache/
before_script:
- curl -sSLf -o /usr/local/bin/install-php-extensions https://github.com/mlocati/docker-php-extension-installer/releases/latest/download/install-php-extensions
- chmod +x /usr/local/bin/install-php-extensions
- install-php-extensions @composer zip ctype iconv
- composer install --prefer-dist --no-progress
quality:
stage: quality
image: php:8.5-cli
script:
- composer audit
- vendor/bin/phpstan analyse --no-progress
- vendor/bin/deptrac analyse --no-progress
- vendor/bin/php-cs-fixer check --diff
- vendor/bin/composer-unused --no-progress
tests:
stage: test
image: php:${PHP_VERSION}-cli
parallel:
matrix:
- PHP_VERSION: ['8.4', '8.5']
script:
- composer validate --strict
- vendor/bin/phpunit
# Generated by AxonPHP CLI (https://github.com/maximilianfeix/AxonPHPCLI). Edit freely.
image: php:8.5-cli
definitions:
steps:
- step: &quality
name: Code quality
image: php:8.5-cli
caches:
- composer
script:
- export COMPOSER_ALLOW_SUPERUSER=1
- curl -sSLf -o /usr/local/bin/install-php-extensions https://github.com/mlocati/docker-php-extension-installer/releases/latest/download/install-php-extensions
- chmod +x /usr/local/bin/install-php-extensions
- install-php-extensions @composer zip ctype iconv
- composer install --prefer-dist --no-progress
- composer audit
- vendor/bin/phpstan analyse --no-progress
- vendor/bin/deptrac analyse --no-progress
- vendor/bin/php-cs-fixer check --diff
- vendor/bin/composer-unused --no-progress
- step: &tests-php-8-4
name: Tests (PHP 8.4)
image: php:8.4-cli
caches:
- composer
script:
- export COMPOSER_ALLOW_SUPERUSER=1
- curl -sSLf -o /usr/local/bin/install-php-extensions https://github.com/mlocati/docker-php-extension-installer/releases/latest/download/install-php-extensions
- chmod +x /usr/local/bin/install-php-extensions
- install-php-extensions @composer zip ctype iconv
- composer install --prefer-dist --no-progress
- composer validate --strict
- vendor/bin/phpunit
- step: &tests-php-8-5
name: Tests (PHP 8.5)
image: php:8.5-cli
caches:
- composer
script:
- export COMPOSER_ALLOW_SUPERUSER=1
- curl -sSLf -o /usr/local/bin/install-php-extensions https://github.com/mlocati/docker-php-extension-installer/releases/latest/download/install-php-extensions
- chmod +x /usr/local/bin/install-php-extensions
- install-php-extensions @composer zip ctype iconv
- composer install --prefer-dist --no-progress
- composer validate --strict
- vendor/bin/phpunit
pipelines:
pull-requests:
'**':
- step: *quality
- parallel:
- step: *tests-php-8-4
- step: *tests-php-8-5
branches:
'main':
- step: *quality
- parallel:
- step: *tests-php-8-4
- step: *tests-php-8-5
'develop':
- step: *quality
- parallel:
- step: *tests-php-8-4
- step: *tests-php-8-5
# Generated by AxonPHP CLI (https://github.com/maximilianfeix/AxonPHPCLI). Edit freely.
version: 2.1
jobs:
quality:
docker:
- image: php:8.5-cli
environment:
COMPOSER_ALLOW_SUPERUSER: '1'
COMPOSER_NO_INTERACTION: '1'
COMPOSER_CACHE_DIR: /tmp/composer-cache
steps:
- checkout
- run:
name: Set up PHP
command: |
curl -sSLf -o /usr/local/bin/install-php-extensions https://github.com/mlocati/docker-php-extension-installer/releases/latest/download/install-php-extensions
chmod +x /usr/local/bin/install-php-extensions
install-php-extensions @composer zip ctype iconv
- restore_cache:
keys:
- composer-{{ checksum "composer.json" }}
- composer-
- run:
name: Install dependencies
command: composer install --prefer-dist --no-progress
- save_cache:
key: composer-{{ checksum "composer.json" }}
paths:
- /tmp/composer-cache
- run:
name: Security audit (Composer)
command: composer audit
- run:
name: Static analysis (PHPStan)
command: vendor/bin/phpstan analyse --no-progress
- run:
name: Static analysis (Deptrac)
command: vendor/bin/deptrac analyse --no-progress
- run:
name: Code style (PHP-CS-Fixer)
command: vendor/bin/php-cs-fixer check --diff
- run:
name: Dependencies (Composer Unused)
command: vendor/bin/composer-unused --no-progress
tests:
parameters:
php:
type: string
docker:
- image: php:<< parameters.php >>-cli
environment:
COMPOSER_ALLOW_SUPERUSER: '1'
COMPOSER_NO_INTERACTION: '1'
COMPOSER_CACHE_DIR: /tmp/composer-cache
steps:
- checkout
- run:
name: Set up PHP
command: |
curl -sSLf -o /usr/local/bin/install-php-extensions https://github.com/mlocati/docker-php-extension-installer/releases/latest/download/install-php-extensions
chmod +x /usr/local/bin/install-php-extensions
install-php-extensions @composer zip ctype iconv
- restore_cache:
keys:
- composer-{{ checksum "composer.json" }}
- composer-
- run:
name: Install dependencies
command: composer install --prefer-dist --no-progress
- save_cache:
key: composer-{{ checksum "composer.json" }}
paths:
- /tmp/composer-cache
- run:
name: Validate composer.json
command: composer validate --strict
- run:
name: Tests (PHPUnit)
command: vendor/bin/phpunit
workflows:
ci:
jobs:
- quality
- tests:
name: tests-php-<< matrix.php >>
matrix:
parameters:
php: ['8.4', '8.5']
Nothing to detect yet. The pipeline lints every PHP file, so it is useful from day one.
This project has no composer.json.
# Generated by AxonPHP CLI (https://github.com/maximilianfeix/AxonPHPCLI). Edit freely.
name: CI
on:
push:
branches: ['main']
pull_request:
permissions:
contents: read
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
tests:
name: Tests (PHP ${{ matrix.php }})
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
php: ['8.2', '8.3', '8.4', '8.5']
steps:
- name: Checkout
uses: actions/checkout@v7
- name: Set up PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php }}
coverage: none
- name: Lint PHP files
run: find . -type f -name '*.php' -not -path './vendor/*' -print0 | xargs -0 -n1 php -l
# Generated by AxonPHP CLI (https://github.com/maximilianfeix/AxonPHPCLI). Edit freely.
stages:
- test
workflow:
rules:
- if: $CI_PIPELINE_SOURCE == 'merge_request_event'
- if: $CI_COMMIT_BRANCH && $CI_OPEN_MERGE_REQUESTS
when: never
- if: $CI_COMMIT_TAG
- if: $CI_COMMIT_BRANCH == 'main'
tests:
stage: test
image: php:${PHP_VERSION}-cli
parallel:
matrix:
- PHP_VERSION: ['8.2', '8.3', '8.4', '8.5']
script:
- find . -type f -name '*.php' -not -path './vendor/*' -print0 | xargs -0 -n1 php -l
# Generated by AxonPHP CLI (https://github.com/maximilianfeix/AxonPHPCLI). Edit freely.
image: php:8.5-cli
definitions:
steps:
- step: &tests-php-8-2
name: Tests (PHP 8.2)
image: php:8.2-cli
script:
- find . -type f -name '*.php' -not -path './vendor/*' -print0 | xargs -0 -n1 php -l
- step: &tests-php-8-3
name: Tests (PHP 8.3)
image: php:8.3-cli
script:
- find . -type f -name '*.php' -not -path './vendor/*' -print0 | xargs -0 -n1 php -l
- step: &tests-php-8-4
name: Tests (PHP 8.4)
image: php:8.4-cli
script:
- find . -type f -name '*.php' -not -path './vendor/*' -print0 | xargs -0 -n1 php -l
- step: &tests-php-8-5
name: Tests (PHP 8.5)
image: php:8.5-cli
script:
- find . -type f -name '*.php' -not -path './vendor/*' -print0 | xargs -0 -n1 php -l
pipelines:
pull-requests:
'**':
- parallel:
- step: *tests-php-8-2
- step: *tests-php-8-3
- step: *tests-php-8-4
- step: *tests-php-8-5
branches:
'main':
- parallel:
- step: *tests-php-8-2
- step: *tests-php-8-3
- step: *tests-php-8-4
- step: *tests-php-8-5
# Generated by AxonPHP CLI (https://github.com/maximilianfeix/AxonPHPCLI). Edit freely.
version: 2.1
jobs:
tests:
parameters:
php:
type: string
docker:
- image: php:<< parameters.php >>-cli
steps:
- checkout
- run:
name: Lint PHP files
command: find . -type f -name '*.php' -not -path './vendor/*' -print0 | xargs -0 -n1 php -l
workflows:
ci:
jobs:
- tests:
name: tests-php-<< matrix.php >>
matrix:
parameters:
php: ['8.2', '8.3', '8.4', '8.5']
Everything a good pipeline has, none of the copy and paste
A matrix that follows your constraint
"php": "^8.2" becomes a test matrix of 8.2, 8.3, 8.4 and 8.5. Narrow the constraint and the matrix narrows with it, on every provider.
The tools you already use
21 tools across tests, static analysis, code style and dependency checks. Installed means it runs.
A pipeline that stays in sync
ci:check fails when the committed file drifts from the project. ci:update brings it back.
Coverage with a floor
A coverage job on the newest PHP version, and a minimum the job has to reach.
Careful defaults
Dependency caching, least-privilege permissions, cancelled superseded runs and no duplicate runs for pull requests.
Plain YAML that belongs to you
No plugin, no runtime, no lock-in. The result is one readable file for your CI service. Nothing is overwritten unless you confirm it, and --dry-run prints the result first.
The pipeline changes when the project does
Add a tool or drop a PHP version and the committed pipeline is out of date. One command tells you, the other fixes it.
$ composer require --dev phpstan/phpstan $ vendor/bin/axonphp ci:check ✗ .github/workflows/ci.yml is out of date - in your file + expected jobs: + quality: + name: Code quality + runs-on: ubuntu-latest … + - name: Static analysis (PHPStan) + run: vendor/bin/phpstan analyse --no-progress [ERROR] The pipeline does not match the project.
$ vendor/bin/axonphp ci:update ✓ .github/workflows/ci.yml updated ✓ .gitlab-ci.yml updated ✓ bitbucket-pipelines.yml is up to date [OK] Updated 2 pipelines. Review the changes and commit them. $ vendor/bin/axonphp ci:check ✓ .github/workflows/ci.yml is up to date ✓ .gitlab-ci.yml is up to date ✓ bitbucket-pipelines.yml is up to date
21 tools, found by the packages you require
Tests run on every PHP version in the matrix. Everything else runs once, on the newest version.
| Tool | Type | Package | Command |
|---|---|---|---|
| Pest | Tests | pestphp/pest |
vendor/bin/pest |
| Codeception | Tests | codeception/codeception |
vendor/bin/codecept run |
| ParaTest | Tests | brianium/paratest |
vendor/bin/paratest |
| PHPUnit | Tests | phpunit/phpunit |
vendor/bin/phpunit |
| PHPUnit Bridge | Tests | symfony/phpunit-bridge |
vendor/bin/simple-phpunit |
| PHPSpec | Tests | phpspec/phpspec |
vendor/bin/phpspec run --no-interaction |
| Behat | Tests | behat/behat |
vendor/bin/behat --no-interaction |
| PHPStan | Static analysis | phpstan/phpstanlarastan/larastannunomaduro/larastan |
vendor/bin/phpstan analyse --no-progress |
| Psalm | Static analysis | vimeo/psalm |
vendor/bin/psalm --no-progress |
| Rector | Static analysis | rector/rector |
vendor/bin/rector process --dry-run |
| Deptrac | Static analysis | deptrac/deptracqossmic/deptrac-shim |
vendor/bin/deptrac analyse --no-progress |
| PHPArkitect | Static analysis | phparkitect/phparkitect |
vendor/bin/phparkitect check |
| PHP-CS-Fixer | Code style | friendsofphp/php-cs-fixerphp-cs-fixer/shim |
vendor/bin/php-cs-fixer check --diff |
| Pint | Code style | laravel/pint |
vendor/bin/pint --test |
| ECS | Code style | symplify/easy-coding-standard |
vendor/bin/ecs check |
| PHP_CodeSniffer | Code style | squizlabs/php_codesniffer |
vendor/bin/phpcs |
| Twig-CS-Fixer | Code style | vincentlanglet/twig-cs-fixer |
vendor/bin/twig-cs-fixer lint |
| Composer Normalize | Dependencies | ergebnis/composer-normalize |
composer normalize --dry-run |
| Composer Require Checker | Dependencies | maglnet/composer-require-checker |
vendor/bin/composer-require-checker check |
| Composer Unused | Dependencies | icanhazstring/composer-unused |
vendor/bin/composer-unused --no-progress |
| Composer Dependency Analyser | Dependencies | shipmonk/composer-dependency-analyser |
vendor/bin/composer-dependency-analyser |
Five commands, one job each
ci:init
Generates the pipeline. Leave the provider out and it asks which one you use.
$ axonphp ci:init gitlab $ axonphp ci:init github --dry-run $ axonphp ci:init circleci --min-coverage 90
ci:check
Fails when a committed pipeline no longer matches the project. Made for CI.
$ axonphp ci:check $ axonphp ci:check --format github $ axonphp ci:check --format json
ci:update
Rewrites the pipelines that are out of date and leaves the others alone.
$ axonphp ci:update $ axonphp ci:update --dry-run $ axonphp ci:update gitlab
inspect
Shows what was detected without writing anything. JSON output for scripts.
$ axonphp inspect $ axonphp inspect --format json | jq '.php.versions'
tools
Lists every tool and provider the installed version supports.
$ axonphp tools $ axonphp tools --format json
Optional, and in the file you already have
AxonPHP needs no configuration. When you want choices to stick, put them under extra.axonphp in composer.json. Command line options still win.
phppins the versions to test.brancheslists the branches whose pushes trigger the pipeline.coverage,lowestandauditswitch on the extra jobs.min-coverageis the line coverage the coverage job has to reach.
{
"extra": {
"axonphp": {
"branches": ["main", "develop"],
"min-coverage": 90,
"lowest": true,
"audit": true
}
}
}
Three commands to your first green run
Requires PHP 8.2 or newer. Install it as a development dependency of the project you want a pipeline for.
-
Add the repository
$composer config repositories.axonphp vcs https://github.com/maximilianfeix/AxonPHPCLI -
Require the package
$composer require --dev maxim/axonphp-cli -
Generate your pipeline
$vendor/bin/axonphp ci:init
The package is not on Packagist yet, which is why the repository is added first. A PHAR is attached to every release.