Skip to content
Merged
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
65 changes: 27 additions & 38 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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=<name>` 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)
Expand Down Expand Up @@ -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=<name>` 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

Expand Down Expand Up @@ -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

Expand Down
2 changes: 1 addition & 1 deletion docs/release.md
Original file line number Diff line number Diff line change
Expand Up @@ -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).

Expand Down
14 changes: 14 additions & 0 deletions docs/upgrading.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading