Skip to content

chore: adopt the crazy-goat repository standard - #246

Merged
s2x merged 17 commits into
mainfrom
chore/standard-process
Oct 2, 2026
Merged

s2x merged 17 commits into
mainfrom
chore/standard-process

Conversation

@s2x

@s2x s2x commented Oct 2, 2026

Copy link
Copy Markdown
Member

Closes #245

Rolls out the shared crazy-goat standard: docs/workflow.md, docs/release-workflow.md, AGENTS.md, bin/ scripts, bin/lint.sh (PHP-CS-Fixer, Rector, PHPStan level 2, clang-format, shellcheck, hadolint), build.yml with changes/docs/lint jobs and ci-ok, release notes from CHANGELOG, Dependabot.

Formatting commits are behaviour-neutral (PHP compared token-wise; C/C++ differ only in whitespace and macro line continuations).

s2x added a commit that referenced this pull request Oct 3, 2026
bin/lint.sh started both tools with the normal `php`, so every php.ini on the
machine was loaded into the process that does the analysis. This repository also
builds a PHP extension (php-ext/, the legacy `zvec`), and a contributor can have a
copy of it installed and enabled in php.ini — the same reason the test suite needs
`php -n` (#188). What the tools see then depends on the machine instead of on the
working tree, which is how a Rector finding in #246 showed up in CI but not
locally. AGENTS.md mentioned `php -n` for the tests only.

The tools now run with `PHPRC` pointing at an ini that is generated per run and
holds nothing, which replaces the machine's php.ini for the process bin/lint.sh
starts and for the ones PHPStan starts for itself: `PHPRC` is an environment
variable and is inherited, while `-n` is not — PHPStan restarts with
`PHP_BINARY -d ...` and starts parallel workers, both of which load the machine's
php.ini again (verified with `ps` while it runs).

`php -n` on its own is not usable here: PHPStan and Rector need `PhpToken` from the
tokenizer extension, which the CI image provides through a conf.d ini, and `-n`
drops those — the first push of this branch failed `lint` with
`Class "PhpToken" not found`. conf.d is still scanned, so extensions installed that
way stay available.

What the replaced php.ini took with it is added back explicitly: `memory_limit=-1`
instead of the built-in 128M, and FFI (`-d extension=ffi` plus `extension=ffi` in
the generated ini) only where the current PHP has none in that same environment —
phpstan.neon ignores undefined FFI methods and properties, not a missing FFI class,
so without FFI the analysis would report errors on src/. The CI lint job now asks
setup-php for FFI, so the analysis has it however the runner provides it.

No step, level or rule changed, and PHP-CS-Fixer is untouched: it works on the token
stream and never resolves the project's classes. Verified locally with the legacy
zvec extension loaded: bin/lint.sh and bin/lint.sh --fix pass, the analysis no
longer resolves the extension's classes, tokenizer and FFI are still available, and
tests/test_docs_consistency.phpt passes with the CHANGELOG entry. shellcheck clean.
s2x added a commit that referenced this pull request Oct 3, 2026
bin/lint.sh started both tools with the normal `php`, so every php.ini on the
machine was loaded into the process that does the analysis. This repository also
builds a PHP extension (php-ext/, the legacy `zvec`), and a contributor can have a
copy of it installed and enabled in php.ini — the same reason the test suite needs
`php -n` (#188). What the tools see then depends on the machine instead of on the
working tree, which is how a Rector finding in #246 showed up in CI but not
locally. AGENTS.md mentioned `php -n` for the tests only.

The tools now run with `PHPRC` pointing at an ini that is generated per run, which
replaces the machine's php.ini for the process bin/lint.sh starts and for the ones
PHPStan starts for itself: `PHPRC` is an environment variable and is inherited,
while `-n` is not — PHPStan restarts with `PHP_BINARY -d ...` and starts parallel
workers, both of which load the machine's php.ini again (verified with `ps` while
it runs).

`php -n` on its own is not usable here: PHPStan needs the Phar class and Rector
needs `PhpToken` from the tokenizer extension, and on the CI image those come from
conf.d, which `-n` drops — the first push of this branch failed `lint` with
`Class "Phar" not found` and `Class "PhpToken" not found`. conf.d is still scanned
instead, so extensions installed that way stay available.

What the replaced php.ini took with it is put back explicitly: `extension_dir`
(on a Homebrew/pecl setup it is set in php.ini while the conf.d entries name their
extension without a path, so every one of them would otherwise fail to load),
`memory_limit=-1` instead of the built-in 128M, and FFI (`-d extension=ffi` plus
`extension=ffi` in the generated ini) only where the current PHP has none in that
same environment — phpstan.neon ignores undefined FFI methods and properties, not a
missing FFI class, so without FFI the analysis would report errors on src/. The CI
lint job now asks setup-php for FFI, so the analysis has it however the runner
provides it.

No step, level or rule changed, and PHP-CS-Fixer is untouched: it works on the token
stream and never resolves the project's classes. Verified locally with the legacy
zvec extension loaded: bin/lint.sh and bin/lint.sh --fix pass without a warning,
the analysis no longer resolves the extension's classes, and Phar, tokenizer, FFI
and xdebug are all still available. tests/test_docs_consistency.phpt passes with the
CHANGELOG entry. shellcheck clean.
s2x added a commit that referenced this pull request Oct 3, 2026
bin/lint.sh started both tools with the normal `php`, so every php.ini on the
machine was loaded into the process that does the analysis. This repository also
builds a PHP extension (php-ext/, the legacy `zvec`), and a contributor can have a
copy of it installed and enabled in php.ini — the same reason the test suite needs
`php -n` (#188). What the tools see then depends on the machine instead of on the
working tree, which is how a Rector finding in #246 showed up in CI but not
locally. AGENTS.md mentioned `php -n` for the tests only.

The tools now run with `PHPRC` pointing at an ini that is generated per run, which
replaces the machine's php.ini for the process bin/lint.sh starts and for the ones
PHPStan starts for itself: `PHPRC` is an environment variable and is inherited,
while `-n` is not — PHPStan restarts with `PHP_BINARY -d ...` and starts parallel
workers, both of which load the machine's php.ini again (verified with `ps` while
it runs).

`php -n` on its own is not usable here: PHPStan needs the Phar class and Rector
needs `PhpToken` from the tokenizer extension, and on the CI image those come from
conf.d, which `-n` drops — the first push of this branch failed `lint` with
`Class "Phar" not found` and `Class "PhpToken" not found`. conf.d is still scanned
instead, so extensions installed that way stay available.

What the replaced php.ini took with it is put back explicitly: `extension_dir`
(on a Homebrew/pecl setup it is set in php.ini while the conf.d entries name their
extension without a path, so every one of them would otherwise fail to load),
`memory_limit=-1` instead of the built-in 128M, and FFI (`-d extension=ffi` plus
`extension=ffi` in the generated ini) only where the current PHP has none in that
same environment — phpstan.neon ignores undefined FFI methods and properties, not a
missing FFI class, so without FFI the analysis would report errors on src/. The CI
lint job now asks setup-php for FFI, so the analysis has it however the runner
provides it.

No step, level or rule changed, and PHP-CS-Fixer is untouched: it works on the token
stream and never resolves the project's classes. Verified locally with the legacy
zvec extension loaded: bin/lint.sh and bin/lint.sh --fix pass without a warning,
the analysis no longer resolves the extension's classes, and Phar, tokenizer, FFI
and xdebug are all still available. tests/test_docs_consistency.phpt passes with the
CHANGELOG entry. shellcheck clean.
s2x added a commit that referenced this pull request Oct 3, 2026
…ni (#259)

Closes #258.

## What changed

`bin/lint.sh` started PHPStan and Rector with the normal `php`, so every
`php.ini` on the
machine was loaded into the process that does the analysis. This
repository also builds a
PHP extension (`php-ext/`, the legacy `zvec`), and a contributor can
have a copy of it
installed and enabled in `php.ini` — the same reason the test suite
needs `-n` (#188).
What the tools see then depends on the machine instead of on the working
tree. `AGENTS.md`
mentioned `php -n` for the tests only.

**This changes what the lint gate runs.** Both tools now run with
`PHPRC` pointing at an
ini that is generated per run and holds nothing, so the machine's
`php.ini` is not read.
No step, level or rule changed:

- the `phpstan` and `rector` steps, and the `rector` call in the `--fix`
branch, go
  through one `php_analysis` helper;
- `memory_limit=-1` replaces the built-in 128M, because the replaced
`php.ini` is where
  the real limit usually is;
- `extension_dir` is carried into the generated ini: on a Homebrew/pecl
setup it is set in
`php.ini` while the conf.d entries name their extension without a path,
so without it every
extension installed that way would fail to load (round 3 of the review
caught exactly
  that, and the resulting xdebug warning);
- FFI is added back only where the current PHP has none in that same
environment, as
`extension=ffi` in the generated ini — every one of those processes
loads it, so it is not
also passed as `-d`, or PHP would load it twice. `phpstan.neon` ignores
undefined FFI
methods and properties, not a missing FFI class, so without FFI the
analysis would report
  errors on `src/`;
- the CI `lint` job asks `setup-php` for FFI, so the analysis has it
however the runner
  provides it;
- PHP-CS-Fixer is untouched: it works on the token stream and never
resolves the project's
  classes.

## Why not `php -n`

`php -n` was the first attempt and it does not work here, for two
separate reasons.

1. **It drops what the tools need.** PHPStan needs the `Phar` class and
Rector needs
`PhpToken` from the tokenizer extension, which the `ubuntu-24.04`
setup-php image
provides through conf.d inis. The first push of this branch failed
`lint` with
`Class "Phar" not found` and `Class "PhpToken" not found` — see the
comment below.
2. **It does not reach the processes PHPStan starts for itself.**
PHPStan restarts with
`PHP_BINARY -d memory_limit=… -d phpstan.restarted=1 …` and starts
parallel workers;
neither carries `-n`, so those processes loaded the machine's `php.ini`
again. Verified
   with `ps -A` while `php -n vendor/bin/phpstan analyse` runs.

`PHPRC` is an environment variable and *is* inherited, so it replaces
`php.ini` in the
restart and in the workers too. A probe file calling a method only the
loaded extension
could know confirms it: PHPStan resolves the class without the isolation
and does not
resolve it with it.

conf.d is still scanned, so an extension installed that way (tokenizer
on CI, FFI,
xdebug) stays available — which is why the generated ini carries the
machine's
`extension_dir` over: on a Homebrew/pecl setup that is itself set in
`php.ini`. An `extension=zvec.so` line in `php.ini` — the #188 case of a
`make install` plus a manual line — no longer reaches the tools; one in
conf.d still
would. That residual gap is stated in `AGENTS.md`.

## What this does not claim

The extension does not currently shadow `src/`: `php-ext/` registers its
15 classes in the
**global** namespace (`ZVecSchema`, `ZVecDoc`, …) while `src/` is
namespaced
`CrazyGoat\ZVec\`, so there is no name collision. The real shadowing in
#188 is a runtime
one — `src/ZVec.php:9` returns early when `extension_loaded('zvec')`, so
the barrel file
never loads the FFI-only classes — and that is what breaks the *test
suite*. PHPStan and
Rector parse `src/` and never run that guard.

So this is not a fix for an observable wrong result today; it removes
the machine
dependency that made the #246 finding (a Rector change in CI that the
local run did not
report) possible, and it produces the same verdict either way on this
machine. That is
also why no regression test comes with it: there is no assertion that
would fail before
the change and pass after it.

`findings.md` records the rest, including that `composer.lock` is
gitignored here, so the
local tool versions float and have to be ruled out before blaming an
extension.

## Verified

- `bin/lint.sh` and `bin/lint.sh --fix` pass on PHP 8.5.10 with the
legacy `zvec` extension
  loaded, and `--fix` changes no file.
- Under the isolation: `zvec` not loaded and PHPStan does not resolve
the extension's
classes, while `Phar`, `tokenizer`, FFI and xdebug are available and
`memory_limit` is
  `-1`; no startup warning is printed.
- `tests/test_docs_consistency.phpt` passes with the CHANGELOG entry.
- `shellcheck` and `bash -n` clean.
- The full `.phpt` suite cannot run in a fresh worktree (it needs `sdk/`
and
`ffi/build/`, built by `bin/worktree-setup.sh`); the change touches no
`src/`, `ffi/` or
  `tests/` file.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ci: add static analysis, linters and formatter checks (bin/lint.sh)

1 participant