From c0768dd7f5313126d1f5914976dc4ea6bf5affd1 Mon Sep 17 00:00:00 2001 From: Imani Manyara Date: Mon, 5 Oct 2026 05:29:03 -0400 Subject: [PATCH 1/2] Warn once per deprecated route name and limiter The bare env-kit.* route-name fallback logged a warning on every resolution, so a page with N links logged N lines per request, and the bare env-kit limiter logged on every throttled request, which never stops on an Octane or queue worker. Both now log once per name for the life of the booted application. --- CHANGELOG.md | 7 +++++ docs/api.md | 3 +- src/Providers/EnvKitWebUIServiceProvider.php | 32 +++++++++++++++++--- tests/Feature/NamingConventionTest.php | 32 ++++++++++++++++++++ 4 files changed, 69 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 22efbe7..7fd33a4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -32,6 +32,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - The bare `env-kit` rate limiter. It is still registered, delegates to the same limit, and logs a warning naming `laranail-env-kit-webui.api`. Earliest removal: the next minor after 0.1. +### Fixed + +- The deprecated-name warnings fired on every resolution, so a page with N links to a bare + `env-kit.*` route name logged N lines per request, and the bare `env-kit` limiter logged once per + throttled request for the life of an Octane or queue worker. Each deprecated name now warns once + for the life of the booted application. + ## [0.1.0] - 2026-07-11 ### Fixed diff --git a/docs/api.md b/docs/api.md index 9b644bd..874dc23 100644 --- a/docs/api.md +++ b/docs/api.md @@ -68,7 +68,8 @@ route('laranail-env-kit-webui.keys.show', ['key' => 'APP_NAME']); > **Deprecated aliases.** The bare names these replaced — `env-kit.keys.*`, `env-kit.panel` and > the `env-kit` limiter — still work: `route('env-kit.panel')` resolves to the scoped route and -> `throttle:env-kit` applies the same limit, each logging a warning that names the replacement. +> `throttle:env-kit` applies the same limit, each logging a warning that names the replacement +> (once per name for the life of the booted application, not once per link or request). > They are removed no earlier than the next minor after 0.1. Two things do not follow the alias: > `Route::has('env-kit.panel')` and `request()->routeIs('env-kit.*')` ask the route collection > directly, so use the scoped names there. diff --git a/src/Providers/EnvKitWebUIServiceProvider.php b/src/Providers/EnvKitWebUIServiceProvider.php index 241b8e3..122542c 100644 --- a/src/Providers/EnvKitWebUIServiceProvider.php +++ b/src/Providers/EnvKitWebUIServiceProvider.php @@ -27,6 +27,16 @@ final class EnvKitWebUIServiceProvider extends PackageServiceProvider { + /** + * Deprecated names already warned about. Held for the life of the booted + * application -- one request under PHP-FPM, one worker under Octane or a + * queue worker -- so a page with N links to a bare route name logs one + * line, not N. + * + * @var array + */ + private array $warnedDeprecations = []; + public function configurePackage(Package $package): void { $package @@ -96,8 +106,8 @@ private function registerThrottle(): void * same limit and logs a warning naming the replacement. Earliest * removal: the next minor after 0.1. */ - RateLimiter::for(RegisteredNames::LEGACY_LIMITER, static function (Request $request) use ($limit): Limit { - Log::warning(sprintf( + RateLimiter::for(RegisteredNames::LEGACY_LIMITER, function (Request $request) use ($limit): Limit { + $this->warnDeprecatedOnce('limiter:' . RegisteredNames::LEGACY_LIMITER, sprintf( '[laranail/env-kit-webui] The rate limiter "%s" is deprecated; use "throttle:%s" instead.', RegisteredNames::LEGACY_LIMITER, RegisteredNames::LIMITER, @@ -127,11 +137,11 @@ private function resolveBareRouteNames(): void $previous = (fn (): mixed => $this->missingNamedRouteResolver)->call($url); URL::resolveMissingNamedRoutesUsing( - static function (string $name, mixed $parameters, ?bool $absolute) use ($previous): ?string { + function (string $name, mixed $parameters, ?bool $absolute) use ($previous): ?string { $scoped = RegisteredNames::scopedRouteFor($name); if ($scoped !== null && Route::has($scoped)) { - Log::warning(sprintf( + $this->warnDeprecatedOnce('route:' . $name, sprintf( '[laranail/env-kit-webui] The route name "%s" is deprecated; use "%s" instead.', $name, $scoped, @@ -151,6 +161,20 @@ static function (string $name, mixed $parameters, ?bool $absolute) use ($previou ); } + /** + * Log a deprecation once per name for the life of the booted application. + */ + private function warnDeprecatedOnce(string $key, string $message): void + { + if (isset($this->warnedDeprecations[$key])) { + return; + } + + $this->warnedDeprecations[$key] = true; + + Log::warning($message); + } + /** * @param list $fallbackMiddleware * @param list $prepend diff --git a/tests/Feature/NamingConventionTest.php b/tests/Feature/NamingConventionTest.php index 5fda57b..2818312 100644 --- a/tests/Feature/NamingConventionTest.php +++ b/tests/Feature/NamingConventionTest.php @@ -171,3 +171,35 @@ function registeredLimiterNames(): array ->withArgs(static fn (string $message): bool => str_contains($message, RegisteredNames::LIMITER)) ->atLeast()->once(); }); + +it('warns once per deprecated route name, however many links resolve it', function (): void { + Log::spy(); + + // A page with N links to the same bare name must not log N lines. + foreach (range(1, 3) as $ignored) { + route('env-kit.panel'); + route('env-kit.keys.index'); + } + + Log::shouldHaveReceived('warning') + ->withArgs(static fn (string $message): bool => str_contains($message, '"env-kit.panel"')) + ->once(); + Log::shouldHaveReceived('warning') + ->withArgs(static fn (string $message): bool => str_contains($message, '"env-kit.keys.index"')) + ->once(); +}); + +it('warns once for the deprecated env-kit limiter, however many requests it throttles', function (): void { + $legacy = app(RateLimiter::class)->limiter(RegisteredNames::LEGACY_LIMITER); + $request = Illuminate\Http\Request::create('/'); + + Log::spy(); + + foreach (range(1, 3) as $ignored) { + $legacy($request); + } + + Log::shouldHaveReceived('warning') + ->withArgs(static fn (string $message): bool => str_contains($message, RegisteredNames::LEGACY_LIMITER)) + ->once(); +}); From 71a35d58598e5b4a892a85d4aaf742cd34d8fa4c Mon Sep 17 00:00:00 2001 From: Imani Manyara Date: Mon, 5 Oct 2026 05:53:06 -0400 Subject: [PATCH 2/2] Configure the engine at laranail.env-kit in the test suite laranail/env-kit now reads its configuration only at laranail.env-kit.*; the old bare env-kit.* reads were the defect that left its shipped config inert. This suite had worked around it by setting the bare keys, so against a fresh resolve every override (temp .env path, schema, allowlist, hidden keys, value limit) was ignored and 13 tests ran on defaults. main fails the same way. No runtime code read the bare keys; tests and one docs line move. --- CHANGELOG.md | 7 +++++++ docs/api.md | 2 +- tests/Feature/LivewireTest.php | 2 +- tests/Feature/PolicyTest.php | 4 ++-- tests/Feature/SchemaValidationTest.php | 4 ++-- tests/Feature/WebUiCoverageTest.php | 2 +- tests/TestCase.php | 10 +++++----- 7 files changed, 19 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7fd33a4..c44093d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -38,6 +38,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 `env-kit.*` route name logged N lines per request, and the bare `env-kit` limiter logged once per throttled request for the life of an Octane or queue worker. Each deprecated name now warns once for the life of the booted application. +- The suite configured the engine through the bare `env-kit.*` keys (`path`, `backup_path`, + `audit.enabled`, `auto_backup`, `hidden_keys`, `editable_keys`, `schema`, + `limits.max_value_length`). `laranail/env-kit` now reads its configuration only at + `laranail.env-kit.*`, so against a fresh resolve every one of those overrides was ignored and 13 + tests ran against the defaults: no temp `.env`, no schema, no allowlist. The tests, and the + `docs/api.md` mention of the schema key, now use `laranail.env-kit.*`. No runtime code read the + bare keys. ## [0.1.0] - 2026-07-11 diff --git a/docs/api.md b/docs/api.md index 874dc23..9cf8841 100644 --- a/docs/api.md +++ b/docs/api.md @@ -77,7 +77,7 @@ route('laranail-env-kit-webui.keys.show', ['key' => 'APP_NAME']); ## Validation & status codes Input is validated with the **headless rules** (`ValidEnvKey`, `ValidEnvValue`, -and `MatchesEnvSchema` — so a configured `env-kit.schema` is enforced over the API +and `MatchesEnvSchema` — so a configured `laranail.env-kit.schema` is enforced over the API exactly as on the CLI; it is a no-op until a schema is defined) before the engine is touched, and engine guards map to HTTP statuses: diff --git a/tests/Feature/LivewireTest.php b/tests/Feature/LivewireTest.php index ba065f5..00ae886 100644 --- a/tests/Feature/LivewireTest.php +++ b/tests/Feature/LivewireTest.php @@ -62,7 +62,7 @@ }); it('surfaces a guard failure inline instead of crashing', function () { - $this->bindEnv("DB_PASSWORD=secret\n", ['env-kit.auto_backup' => false]); + $this->bindEnv("DB_PASSWORD=secret\n", ['laranail.env-kit.auto_backup' => false]); Livewire::test(EnvKitPanelComponent::class) ->call('startEditing', 'DB_PASSWORD') // protected key diff --git a/tests/Feature/PolicyTest.php b/tests/Feature/PolicyTest.php index 73413ca..b5e77cb 100644 --- a/tests/Feature/PolicyTest.php +++ b/tests/Feature/PolicyTest.php @@ -10,7 +10,7 @@ it('honours a custom hidden_keys pattern in the API (masking)', function () { $this->bindEnv("APP_NAME=Acme\nWIDGET_API=topsecret123\n"); - config(['env-kit.hidden_keys' => ['WIDGET_*']]); + config(['laranail.env-kit.hidden_keys' => ['WIDGET_*']]); $response = $this->getJson('api/v1/env-kit/keys')->assertOk(); @@ -21,7 +21,7 @@ it('returns 403 for a write outside the editable allowlist', function () { $this->bindEnv("A=1\n"); - config(['env-kit.editable_keys' => ['APP_*']]); + config(['laranail.env-kit.editable_keys' => ['APP_*']]); $this->postJson('api/v1/env-kit/keys', ['key' => 'DB_HOST', 'value' => 'x'])->assertForbidden(); diff --git a/tests/Feature/SchemaValidationTest.php b/tests/Feature/SchemaValidationTest.php index 407c942..6d6f631 100644 --- a/tests/Feature/SchemaValidationTest.php +++ b/tests/Feature/SchemaValidationTest.php @@ -12,7 +12,7 @@ it('rejects a stored value that violates the engine schema', function () { $this->bindEnv("A=1\n"); - config(['env-kit.schema' => ['PORT' => 'integer']]); + config(['laranail.env-kit.schema' => ['PORT' => 'integer']]); $this->postJson('api/v1/env-kit/keys', ['key' => 'PORT', 'value' => 'not-an-int']) ->assertStatus(422) @@ -24,7 +24,7 @@ it('validates an updated value against the schema', function () { $this->bindEnv("PORT=8080\n"); - config(['env-kit.schema' => ['PORT' => 'integer']]); + config(['laranail.env-kit.schema' => ['PORT' => 'integer']]); $this->putJson('api/v1/env-kit/keys/PORT', ['value' => 'nope']) ->assertStatus(422) diff --git a/tests/Feature/WebUiCoverageTest.php b/tests/Feature/WebUiCoverageTest.php index 148b720..ad2e98d 100644 --- a/tests/Feature/WebUiCoverageTest.php +++ b/tests/Feature/WebUiCoverageTest.php @@ -50,7 +50,7 @@ it('maps an engine validation failure (value too long) to 422', function () { $this->bindEnv("A=1\n"); - config(['laranail.env-kit-webui.enabled' => true, 'env-kit.limits.max_value_length' => 5]); + config(['laranail.env-kit-webui.enabled' => true, 'laranail.env-kit.limits.max_value_length' => 5]); $this->postJson('api/v1/env-kit/keys', ['key' => 'NEW', 'value' => 'waytoolong'])->assertStatus(422); }); diff --git a/tests/TestCase.php b/tests/TestCase.php index 828a322..c25985c 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -35,7 +35,7 @@ protected function defineEnvironment($app): void // load the engine defaults explicitly so tests mirror production. $engineConfig = dirname(__DIR__) . '/vendor/laranail/env-kit/config/env-kit.php'; if (is_file($engineConfig)) { - $app['config']->set('env-kit', require $engineConfig); + $app['config']->set('laranail.env-kit', require $engineConfig); } } @@ -48,10 +48,10 @@ protected function bindEnv(string $contents): string file_put_contents($path, $contents); config([ - 'env-kit.path' => $path, - 'env-kit.backup_path' => $dir . '/backups', - 'env-kit.audit.enabled' => false, - 'env-kit.auto_backup' => false, + 'laranail.env-kit.path' => $path, + 'laranail.env-kit.backup_path' => $dir . '/backups', + 'laranail.env-kit.audit.enabled' => false, + 'laranail.env-kit.auto_backup' => false, ]); $this->app->forgetInstance(EnvKitInterface::class);