Skip to content
Open
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
7 changes: 7 additions & 0 deletions .changelog/phar-build-parity.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
category: fixed
type: patch
---

Compare the committed PHAR's complete stub and contents with the fresh reviewed
build before execution, rejecting forged self-reported distribution manifests.
7 changes: 7 additions & 0 deletions .changelog/phar-cache-lifetime.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
category: fixed
type: patch
---

Keep compiled formatter caches inside the extracted runtime so repeated checks
and fixes clean them up, preserving source-install cache reuse and explicit overrides.
7 changes: 7 additions & 0 deletions .changelog/phar-consumer-rules.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
category: fixed
type: patch
---

Document explicit consumer ECS autoloading and qualify project rules and
development rule packages against the compiled executable with real Composer mappings.
8 changes: 8 additions & 0 deletions .changelog/phar-distribution-investigation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
category: added
type: minor
---

Build an executable PHAR with PHP tooling and a direct Composer binary target,
move bundled libraries to development dependencies, preserve all copyable
resources, and ship the ready executable in standard Composer distributions.
7 changes: 7 additions & 0 deletions .changelog/phar-locked-composer.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
category: fixed
type: patch
---

Build with the locked Composer executable so global Composer updates cannot
change the compiled payload's generated lock metadata.
7 changes: 6 additions & 1 deletion .gitattributes
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
* text=auto
*.php text eol=lf
/bin/code-standards text eol=lf
/builds/code-standards binary

# Local state and synchronized copies never belong to package distributions.
/.git export-ignore
Expand All @@ -12,6 +13,7 @@

# Contributor-only configuration and test infrastructure.
/tests/ export-ignore
/scripts/phar/ export-ignore
/.github/workflows/ export-ignore
/AGENTS.md export-ignore
/skills.json export-ignore
Expand All @@ -21,4 +23,7 @@
/.gitattributes export-ignore
/.changelog/ export-ignore

# Keep src/, bin/, config/, resources/, composer.lock, README and its Dash image.
# Ready distributions retain the PHAR, manifests, complete resources and documentation.
/src/ export-ignore
/config/ export-ignore
/bin/ export-ignore
2 changes: 1 addition & 1 deletion .github/workflows/code-standards-analysis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ jobs:
env:
COMPOSER_HOME: ${{ runner.temp }}/standards-analysis-composer-home
COMPOSER_CACHE_DIR: ${{ runner.temp }}/standards-analysis-composer-cache
run: composer install --no-dev --no-plugins --no-scripts --no-interaction --prefer-dist --no-progress
run: composer install --no-plugins --no-scripts --no-interaction --prefer-dist --no-progress
- name: Check out exact consumer head
if: inputs.magic-numbers || inputs.rector
uses: actions/checkout@v7
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/code-standards-check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ jobs:
env:
COMPOSER_HOME: ${{ runner.temp }}/standards-composer-home
COMPOSER_CACHE_DIR: ${{ runner.temp }}/standards-composer-cache
run: composer install --no-dev --no-plugins --no-scripts --no-interaction --prefer-dist --no-progress
run: composer install --no-plugins --no-scripts --no-interaction --prefer-dist --no-progress
- name: Check out exact consumer head
uses: actions/checkout@v7
with:
Expand Down
19 changes: 19 additions & 0 deletions .github/workflows/repository-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,25 @@ jobs:
run: |
php "$STANDARDS_ROOT/bin/code-standards" normalize-composer "--directory=$STANDARDS_DIRECTORY"
- run: composer check
- name: Qualify ready PHAR distribution and native runtime
env:
COMPOSER_HOME: ${{ runner.temp }}/standards-phar-composer-home
COMPOSER_CACHE_DIR: ${{ runner.temp }}/standards-composer-cache
run: |
php -d phar.readonly=0 scripts/phar/build.php "$RUNNER_TEMP/code-standards"
php scripts/phar/verify.php "$RUNNER_TEMP/code-standards"
- name: Retain the reviewed PHP 8.3 build
if: matrix.php == '8.3'
uses: actions/upload-artifact@v7
with:
name: code-standards-${{ github.sha }}
path: ${{ runner.temp }}/code-standards
compression-level: 0
if-no-files-found: error
- name: Qualify the committed Composer distribution
run: |
php scripts/phar/compare.php "$RUNNER_TEMP/code-standards" builds/code-standards
php scripts/phar/verify.php builds/code-standards
- name: Provision isolated Twig evaluation dependencies
run: composer install --working-dir=tests/Evaluation/Twig --no-dev --no-plugins --no-scripts --no-interaction --prefer-dist --no-progress
env:
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
/vendor/
/builds/*.phar
/.build/
/.phpunit.cache/

Expand Down
67 changes: 44 additions & 23 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,19 +30,32 @@ composer require --dev fast-forward/code-standards
vendor/bin/code-standards check --all
```

CPX can invoke the installed package directly with
`cpx fast-forward/code-standards list`. The CLI resolves Composer dependencies
from its installation, including when run outside the package checkout.

The GitHub `v0` workflow channel and the Composer package are separate
distribution mechanisms.
The compiled executable is installed directly through Composer's binary proxy.
Optional scripts keep checks convenient without building the tool in consumers:

```json
{
"scripts": {
"standards:check": "code-standards check --all",
"standards:fix": "code-standards fix --all"
}
}
```

Requires PHP 8.3 or later in the 8.x series, Git and Composer runtime API 2.2+
for installed binary proxies. Composer resolves the tool with the project's
dependency constraints; syntax is checked using the executing PHP binary.
No PHP migration rules are enabled. Review compatibility with the
application's minimum PHP before adopting a preset.
Run `composer standards:check`; apply formatting explicitly with
`composer standards:fix`. Global installation uses
`composer global require fast-forward/code-standards`, then the binary in
`composer global config bin-dir --absolute`.

The GitHub `v0` workflow channel and the Composer package are separate
publications. See the [PHAR distribution contract](docs/phar-distribution.rst)
for the committed binary and CI build contract. A merge does not itself publish a Composer release.
Copyable workflow callers live in [resources/workflows](resources/workflows/README.md).

Requires PHP ^8.3, Git and the PHP extensions declared in `composer.json`,
including Phar and zlib. Bundled libraries do not enter the consumer's dependency
graph. Executable configurations may load consumer classes; this is no namespace sandbox.
Syntax uses the executing PHP binary. No PHP migration rules are enabled.
Workflow guards and their contributor tests additionally require Bash and jq.
Hosted Ubuntu runners provide both; the Composer library and CLI use the PHP
dependencies declared in this package.
Expand Down Expand Up @@ -82,12 +95,15 @@ See the [structured check contract](docs/structured-check.rst). Text is the
default; `fix` uses text output.
Use `--format=json-pretty` for the same report with indentation; see
[result presentation](docs/process-contracts.rst#structured-check-presentation).
The default preset stores its ECS cache beneath the system temporary directory,
Source installations store the default ECS cache beneath the system temporary directory,
in `fast-forward-code-standards/<SHA-256 of the executing checkout path>/<owned-policy fingerprint>`.
The fingerprint includes the installed preset and owned checker paths and source
bytes. Different checkouts cannot invalidate each other's cache, and a changed
owned rule cannot reuse an earlier passing result. Native
`withCache()` overrides remain supported and apply last; share cache directories
owned rule cannot reuse an earlier passing result. The compiled executable keeps
its default cache inside the extracted runtime and removes it at normal shutdown;
separate invocations do not reuse it or accumulate persistent policy directories.
See the [cache lifetime contract](docs/phar-distribution.rst#filesystem-runtime).
Native `withCache()` overrides remain supported and apply last; share cache directories
only when their invalidation and concurrent use are intentionally coordinated.
`fix` explicitly applies ECS fixes. It does not commit, push or update dependencies.
After each successful formatter batch, a read-only check verifies the result.
Expand Down Expand Up @@ -118,8 +134,6 @@ declare(strict_types=1);
use FastForward\CodeStandards\Config\ECSConfig;
use Symplify\EasyCodingStandard\Configuration\ECSConfigBuilder;

require __DIR__ . '/vendor/autoload.php';

return ECSConfig::configure(static function (ECSConfigBuilder $config): void {
// Local rule imports/overrides go last; review their semantic risk.
$config->withConfiguredRule(
Expand All @@ -132,10 +146,18 @@ return ECSConfig::configure(static function (ECSConfigBuilder $config): void {
Select it explicitly with `--config=ecs.php`. PHP configuration is executable
code and belongs in an unprivileged environment. The CLI defaults to its own
preset, does not discover or overwrite consumer configuration, and never runs
both ECS and a competing PHP-CS-Fixer pass. Standalone ECS users may import the
same library preset and set their own paths. CLI selection uses the scope below;
both ECS and a competing PHP-CS-Fixer pass. The preset API is available inside
the compiled tool's selected configuration. The package does not expose these
classes through the consumer autoloader. CLI selection uses the scope below;
ECS's configured paths do not expand the CLI's explicit selected file list.

To use project rules or rule packages installed in `require-dev`, explicitly load
the project's `vendor/autoload.php` inside the selected configuration before
registering those rules. Composer includes project `autoload` and `autoload-dev`
classes in a development installation. See the
[consumer configuration contract](docs/phar-distribution.rst#consumer-ecs-configuration)
for an example and compatibility limits.

For braced multiline `if`, `elseif`, `while` and `do-while` conditions, the
native PSR-12 wrapper places the first expression on the next line and the
closing parenthesis on its own line (`) {`, or `);` for do-while). The native
Expand All @@ -160,10 +182,9 @@ The formatter worker uses ECS's public factory and command APIs with this tool's
own Composer installation. An isolated tool does not implicitly load the target
checkout's `vendor/autoload.php`. Explicit PHP configuration retains the checkout
as `getcwd()`, keeps `__DIR__` and relative paths intact, and may deliberately
require consumer autoloaders or register custom checkers. When the tool is a
development dependency in the consumer's own Composer graph, that installation's
normal binary-proxy bootstrap remains intentional; it is not a separate import
of another checkout's dependency graph.
require consumer autoloaders or register custom checkers. Composer's proxy
includes the compiled executable, which boots its own extracted runtime rather
than loading the consumer autoloader.

## 🔌 Integration and file scope

Expand All @@ -187,7 +208,7 @@ of another checkout's dependency graph.
Start with the [usage guide](docs/index.rst). See [policy and differences](docs/policy.rst), [workflow/ref adoption](docs/automation.rst)
and the [consumer skill](resources/skills/fast-forward-code-standards/SKILL.md).
The [architecture decision](docs/architecture.rst) defines library/tool dependency
directions and CPX/autoload limits.
directions and executable-configuration limits.
The formatter preserves descriptive PHPDoc and inherited documentation.
The separate opt-in [`refactor` command](docs/rector.rst) previews an explicit
native Rector configuration and requires `--apply` for semantic writes. It is
Expand Down
Binary file added builds/code-standards
Binary file not shown.
46 changes: 35 additions & 11 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,46 +38,52 @@
],
"require": {
"php": "^8.3",
"ext-filter": "*",
"ext-hash": "*",
"ext-iconv": "*",
"ext-json": "*",
"ext-openssl": "*",
"ext-pcre": "*",
"ext-phar": "*",
"ext-reflection": "*",
"ext-tokenizer": "*",
"ext-zlib": "*"
},
"require-dev": {
"composer-runtime-api": "^2.2",
"composer/composer": "^2.10.3",
"container-interop/service-provider": "^0.4.1",
"ergebnis/json": "^1.7.0",
"ergebnis/json-normalizer": "^4.11.0",
"ergebnis/json-printer": "^3.9.1",
"fast-forward/container": "^1.10.1",
"llm/skills": "^1.15",
"phpstan/phpstan": "^2.3.3",
"phpunit/phpunit": "^12.5",
"povils/phpmnd": "^3.6.1",
"psr/container": "^1.1 || ^2.0",
"rector/rector": "^2.8.2",
"symfony/console": "^7.4 || ^8.0",
"symfony/filesystem": "^7.4 || ^8.0",
"symfony/process": "^7.4 || ^8.0",
"symplify/easy-coding-standard": "^13.3.8"
},
"require-dev": {
"llm/skills": "^1.15",
"phpunit/phpunit": "^12.5",
"symfony/filesystem": "^7.4 || ^8.0"
},
"suggest": {
"ext-dom": "Generate native XML reports for optional magic-number analysis.",
"llm/skills": "Optionally synchronize the bundled agent skill in consumer projects."
},
"minimum-stability": "stable",
"autoload": {
"psr-4": {
"FastForward\\CodeStandards\\": "src/"
}
},
"autoload-dev": {
"psr-4": {
"FastForward\\CodeStandards\\": "src/",
"FastForward\\CodeStandards\\Tests\\": "tests/"
},
"exclude-from-classmap": [
"/tests/Fixtures/"
]
},
"bin": [
"bin/code-standards"
"builds/code-standards"
],
"config": {
"allow-plugins": {
Expand All @@ -89,11 +95,29 @@
"sort-packages": true
},
"extra": {
"phar": {
"runtime-packages": [
"composer/composer",
"container-interop/service-provider",
"ergebnis/json",
"ergebnis/json-normalizer",
"ergebnis/json-printer",
"fast-forward/container",
"phpstan/phpstan",
"povils/phpmnd",
"psr/container",
"rector/rector",
"symfony/console",
"symfony/process",
"symplify/easy-coding-standard"
]
},
"skills": {
"source": "resources/skills"
}
},
"scripts": {
"build": "@php -d phar.readonly=0 scripts/phar/build.php",
"check": [
"@composer validate --strict",
"@composer check-platform-reqs",
Expand Down
Loading
Loading