diff --git a/CHANGELOG.md b/CHANGELOG.md index a7468f6..7ec7432 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,44 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] -### Added - -- **Runtime overrides.** `override()`, `forgetOverride()`, `hasOverride()`, `clearOverrides()` and - `withOverrides()` on the `Settings` handle hold a value in memory for the current request or job, - ahead of every stored layer. An override is keyed like a stored value (store, owner, key, locale) - and follows the same owner, parents and global walk, so a global override reaches every owner - that has none of its own. It is validated and cast as a write is (including `values.max_bytes`), - never written, cached, audited, versioned or encrypted, needs no write ability, wins over locks - and precedence, and is not exposed over the REST API. `explain()` reports the layer `override`, - `laranail::settings.show` marks it, typed settings `load()` reads it and `save()` never persists - it, and the fake supports it. The new `Services\Resolution\Overrides` is bound scoped, so Octane - and the queue worker drop overrides between requests and jobs. See - `docs/tools/runtime-overrides.md`. - -## [0.1.1] - 2026-11-14 - -### Added - -- **Importers read from filesystem disks.** `laranail::settings.import --disk=` reads an - export file, or the `toolkit` importer's JSON file, from any configured disk, with the source as - the path on it; local paths work as before. In PHP, the new `Contracts\DiskImporter` adds - `readFromDisk(?string $disk, ?string $path, StoreName $store)`, which `ToolkitJsonImporter` - implements through the injected filesystem factory. With no source, the `toolkit` importer reads - toolkit's own file from `laranail.toolkit.settings`' disk and path when that config is loaded, - otherwise `laranail/settings.json` on `local`; toolkit itself is not needed. The command's - `source` argument is now optional. The REST import takes a document, so it has no disk option. - -### Fixed - -- **Importers no longer overwrite colliding keys in silence.** Source keys that normalise to the - same settings key (differing by case, `-`, `_` or spaces, or toolkit's nested and dotted spelling - of one path) made the later one win with no report. The `spatie`, `toolkit` and `akaunting` - importers now fail with `InvalidImport` naming every colliding group - (`[Mail-Host, mail_host] -> mail_host`) before anything is written, in a dry run and when - applied. Input without collisions imports exactly as before. `ImportedKeys::values()` applies the - same check for a custom importer. - -## [0.1.0] - 2026-11-11 +## [0.1.0] - 2026-11-14 The first public release. `Added` describes the package; `Changed`, `Removed`, `Fixed` and `Security` matter only to a checkout taken before the first tag, and [Upgrading](docs/upgrading.md) @@ -126,6 +89,25 @@ has the steps for one. - `values.max_bytes` (`LARANAIL_SETTINGS_MAX_VALUE_BYTES`, default 1 MiB, `null` for no limit): every write path (the PHP API, REST, import, promotion, approvals) refuses a value whose encoded payload is larger, with `InvalidSettingValue` (REST `422 invalid-value`). +- **Runtime overrides.** `override()`, `forgetOverride()`, `hasOverride()`, `clearOverrides()` and + `withOverrides()` on the `Settings` handle hold a value in memory for the current request or job, + ahead of every stored layer. An override is keyed like a stored value (store, owner, key, locale) + and follows the same owner, parents and global walk, so a global override reaches every owner + that has none of its own. It is validated and cast as a write is (including `values.max_bytes`), + never written, cached, audited, versioned or encrypted, needs no write ability, wins over locks + and precedence, and is not exposed over the REST API. `explain()` reports the layer `override`, + `laranail::settings.show` marks it, typed settings `load()` reads it and `save()` never persists + it, and the fake supports it. The new `Services\Resolution\Overrides` is bound scoped, so Octane + and the queue worker drop overrides between requests and jobs. See + `docs/tools/runtime-overrides.md`. +- **Importers read from filesystem disks.** `laranail::settings.import --disk=` reads an + export file, or the `toolkit` importer's JSON file, from any configured disk, with the source as + the path on it; local paths work as before. In PHP, the new `Contracts\DiskImporter` adds + `readFromDisk(?string $disk, ?string $path, StoreName $store)`, which `ToolkitJsonImporter` + implements through the injected filesystem factory. With no source, the `toolkit` importer reads + toolkit's own file from `laranail.toolkit.settings`' disk and path when that config is loaded, + otherwise `laranail/settings.json` on `local`; toolkit itself is not needed. The command's + `source` argument is now optional. The REST import takes a document, so it has no disk option. ### Changed @@ -194,6 +176,13 @@ Each was removed before the first tag; none was in a tagged release. The `StreamExport` action is new; `export()` still returns the `ExportDocument`. (SP-10) - Two overlapping `sync-definitions` runs no longer fail on each other's rows: the second waits up to five seconds for a cache lock, then reports `busy` and changes nothing. (SP-11) +- **Importers no longer overwrite colliding keys in silence.** Source keys that normalise to the + same settings key (differing by case, `-`, `_` or spaces, or toolkit's nested and dotted spelling + of one path) made the later one win with no report. The `spatie`, `toolkit` and `akaunting` + importers now fail with `InvalidImport` naming every colliding group + (`[Mail-Host, mail_host] -> mail_host`) before anything is written, in a dry run and when + applied. Input without collisions imports exactly as before. `ImportedKeys::values()` applies the + same check for a custom importer. ### Security diff --git a/docs/release.md b/docs/release.md index 6027a72..f166341 100644 --- a/docs/release.md +++ b/docs/release.md @@ -4,7 +4,7 @@ How `laranail/settings` is versioned and released. ## Versioning -Semantic versioning. Every release is a new, increasing tag (`v0.1.0`, `v0.1.1`, …). A published +Semantic versioning. Every release is a new, increasing tag (`v0.1.0`, then `v0.1.1`, …). A published tag is never moved, deleted or reused: the next fix gets the next number. Breaking changes before 1.0 bump the minor version and are listed in [Upgrading](upgrading.md). diff --git a/docs/upgrading.md b/docs/upgrading.md index df98540..3161593 100644 --- a/docs/upgrading.md +++ b/docs/upgrading.md @@ -9,6 +9,20 @@ Changelog sections `Added`, `Changed`, `Deprecated`, `Removed`, `Fixed` and `Sec minor release (`0.1` to `0.2`) may break the API; the section of that release says what breaks and what to do, and this page lists the steps. +## If you installed 0.1.0 or 0.1.1 before 2026-11-14 + +The first `v0.1.0` (2026-11-11) and `v0.1.1` (2026-11-14) were withdrawn, and the history was +squashed into a single `v0.1.0`, published 2026-11-14, that holds everything both carried, plus runtime +overrides. Composer caches an archive per version name, so an install from the old tags keeps the +old code until the cache is cleared: + +```bash +composer clear-cache && composer update laranail/settings +``` + +Your `^0.1` constraint does not change. Nothing else is needed: the API, config keys and tables +are the same as in the withdrawn `v0.1.1`. + ## From a pre-release checkout to 0.1.0 0.1.0 is the first tagged release. A checkout taken before it may carry config keys, classes and