Keep only the relevant environment variables in the container cache key#5927
Keep only the relevant environment variables in the container cache key#5927SanderMuller wants to merge 1 commit into
Conversation
The container cache key included the entire environment (minus a hand-maintained denylist - phpstan#4924, phpstan#4925), so any unrelated env var that differs between invocations (CI runners inject per-job vars on every run) flipped the key and forced a full container recompile + ignoreErrors re-validation. The container only depends on the environment through (a) %env.X% references in loaded configs and (b) env read at build time by a CompilerExtension (PHPStan reads only PHPSTAN_FNSR, in FnsrExtension::beforeCompile()). Keep exactly those env vars in the cache key - the %env.NAME% names (matching Nette's %([\w.-]*)% grammar) plus self::BUILD_TIME_ENV_VARIABLES - and drop the rest. The container parameters still carry the full env, so %env.*% resolution is unaffected; this only narrows the key. ContainerCacheKeyEnvGuardTest enforces that every env var read at build time by a CompilerExtension is declared, so a future build-time getenv() cannot silently reuse a stale container. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
8a335c8 to
c373f1f
Compare
| private function relevantEnvVariableNamesForCacheKey(): ?array | ||
| { | ||
| $names = self::BUILD_TIME_ENV_VARIABLES; | ||
| foreach ($this->allConfigFiles as $file) { |
There was a problem hiding this comment.
This isn't a great way to do this. I think the tracking of used %env... should be somewhere deeper in the DIC compiler. I'm not entirely sure how to implement it. This would have to be researched where Nette DI actually expands this parameters and whether we can efficiently tap into it.
There was a problem hiding this comment.
@ondrejmirtes shall I apply the suggestion in this comment below, or do you have another direction you want me to explore?
|
Agreed, scanning config text for I tried tapping into Nette's expansion directly first, and it's awkward. What does work cleanly is to stop baking env into the container at all: move The one case it doesn't cover is env read at build time via That removes the regex entirely. I can push it to this PR if the direction looks right. |
The compiled DI container's cache key includes the entire environment, minus a hand-maintained denylist (#4924, #4925). Any unrelated env var that differs between invocations flips the key, and CI runners inject per-job vars on every run. So the container recompiles on every "warm" run: Nette compiles hundreds of service definitions, writes the ~300 KB container, and re-runs the build-time
ValidateIgnoredErrorsExtensionover every inline ignore pattern.This continues the denylist work in #4924 / #4925 from the allowlist side. A denylist is incremental (every volatile var has to be found and added), so instead keep only the env the container actually depends on and drop the rest.
The container depends on the environment in two ways:
%env.X%references in loaded configs, which Nette resolves into the container.FnsrExtension::beforeCompile()readsgetenv('PHPSTAN_FNSR')and rewiresNodeScopeResolver.Dropping the whole env unless a config uses
%env.*%would silently breakPHPSTAN_FNSR: with no%env.*%in config, env leaves the key, so togglingPHPSTAN_FNSRreuses a stale container and the rewiring is ignored. The current whole-env key does track this (the value is in the key), so dropping it would be a regression.This keeps the vars referenced via
%env.NAME%in loaded configs, plus the build-time vars PHPStan reads itself (today onlyPHPSTAN_FNSR), and drops the rest:PHPSTAN_FNSRstill recompiles.%env.*%, only the referenced var stays in the key, not the whole env (the old key kept everything in that case).%([\w.-]*)%name grammar, so a ref like%env.MY-VAR%is not missed.The change is one method plus a const in
Configurator.php, and a new test (ContainerCacheKeyEnvGuardTest) that fails if a CompilerExtension reads a build-time env var that is not declared or routed through%env.*. That stops this from regressing later.One residual case: a CompilerExtension that reads
getenv()directly at build time, not through%env.*%and not declared, would still have its var dropped. That is the contract (build-time env that affects the container goes through%env.*or is declared), and the guard test enforces it for PHPStan's own code. The denylist already carries this risk for its 7 vars; this does not extend it to the whole environment.@staabm this is your area (#4924, #4925). I'd value your read on whether completing the denylist from the allowlist side is the right call here.