Skip to content

v1.54.0 - Okab

Choose a tag to compare

@MichaelSowah MichaelSowah released this 10 Jun 22:16
· 114 commits to main since this release
b9fde57

[1.54.0] - 2026-06-10 — Okab

Theme: Real extension seams — overridable container defaults, the entitlement contract, and a storage provider registry. A coordinated release in three movements: (1) a container-precedence fix that makes every "core default + extension override" seam actually overridable (extension definitions previously lost silently to core bindings); (2) the Glueful\Entitlements core seam — a contract-only extension point for commercial capability gates, consumed by the forthcoming glueful/subscriptions; and (3) a storage driver registry with the s3/gcs/azure factories extracted to first-party provider packs (breaking — lean core, same playbook as 1.52). Staying in 1.x per the pre-public breaking-changes policy — see Upgrade Notes.

Breaking Changes

  • Core ships only the local/memory storage drivers. The s3, gcs, and azure driver factories (and the embedded S3 presign logic) have been removed from core and extracted to first-party packs. glueful/storage-s3 ships alongside this release (it also covers R2 / MinIO / Spaces / Wasabi via presets); glueful/storage-gcs and glueful/storage-azure follow shortly. A disk using one of those drivers now fails fast with UnsupportedStorageDriverException naming the package to install (e.g. composer require glueful/storage-s3). The s3 stub disk in config/storage.php is now commented out (core's default config only declares disks core can create). Upgrade: after updating the framework, composer require glueful/storage-s3 (or the gcs/azure pack once published) for whichever driver your app uses. Apps on local/memory only are unaffected.

Added

  • Entitlement seam (Glueful\Entitlements). New core extension point: EntitlementCheckerInterface (allows() / limit(), explicit tenant uuid) with an absent-allow NullEntitlementChecker default bound in CoreProvider. Lets extensions and app code gate commercial capabilities without depending on any specific subscriptions package. Core ships the contract only -- no consumer, no tenant/plan awareness. glueful/subscriptions provides the real checker. (Override of the default relies on the container precedence fix in the same release.)
  • Storage driver registry + provider seam. New Glueful\Storage\Contracts\StorageDriverFactoryInterface (driver identity, construction, available(), features()), StorageDriverRegistryInterface (+ StorageDriverRegistry::withBuiltIns()), and optional capability contracts NativeSignedUrlProviderInterface / StorageHealthCheckInterface. StorageManager now resolves every disk through the registry (last-registered-wins per driver), accepts a nullable registry (defaults to built-ins -- new StorageManager($config, $pathGuard) is unchanged), drives putStream() through features()['supports_atomic_move'] (default true), and exposes the registry via drivers(). Extensions register factories tagged storage.driver_factory; StorageProvider collects them after the local/memory built-ins and reverses the tagged iterator so higher tag priority wins same-driver collisions. FlysystemStorage delegates writes to StorageManager::putStream() and resolves native signed URLs via NativeSignedUrlProviderInterface (falling back to the app URL on null or provider errors).
  • storage:test [disk] command. Read-only by default (reports driver registration, adapter available(), and a non-mutating liveness probe via the health-check capability); --write opts into a write/read/delete smoke test. Never prints secrets.
  • Optional native_url in the blob API. Additive, default-off, per-disk, visibility-scoped (config('uploads.native_urls')): public blobs may serve a direct provider URL; private blobs only with a bounded TTL. The app-signed /blobs/{uuid} URL stays the always-available, access-controlled path.

Fixed

  • Container precedence: extension definitions now override core defaults. ContainerFactory merged extension service definitions with +=, which kept the core binding on key collision and silently dropped extension overrides -- making core default bindings (UserProviderInterface -> NullUserProvider, and every "core default + extension override" seam) un-overridable through the normal provider path. Now merged with array_replace (extension-over-core); ApplicationContext is re-pinned post-merge so a framework-managed key cannot be clobbered. Within-extension precedence is unchanged.
  • Blob uploads now persist the actual effective storage disk. FileUploader now prefers its explicit storageDriver when recording blobs.storage_type, so per-request or manually constructed uploaders no longer store the configured uploads.disk when the file was written to a different disk.

Upgrade Notes

  • Cloud storage drivers need their provider pack. If any disk uses driver: s3 (including R2/MinIO/Spaces/Wasabi setups), composer require glueful/storage-s3 after updating — its presets cover the S3-compatible providers. gcs/azure users should hold this upgrade until glueful/storage-gcs / glueful/storage-azure publish (following shortly). Apps on local/memory need nothing.
  • Refresh the command manifest on deploy. This release adds storage:test; a storage/cache/glueful_commands_manifest.php generated before the upgrade will not know it. Run php glueful commands:cache as part of the deploy (cache:clear does not refresh the command manifest).
  • Regenerate the precompiled container on deploy. The extension-over-core precedence fix only takes effect once the compiled container artifact is rebuilt — a container compiled before 1.54.0 still encodes the old (core-wins) merge. Run php glueful di:container:compile --force (or delete the cached glueful_compiled_container.php artifact) as part of the deploy.
  • Extension authors: your container definitions now actually override core defaults. Binding the same service id as a core provider previously lost silently; it now wins (last-layer-wins). This is what makes UserProviderInterface, EntitlementCheckerInterface, the storage factories, and every other "core default + extension override" seam real. Audit any extension that unintentionally reuses a core service id.
  • Optional env: UPLOADS_NATIVE_MAX_PRIVATE_TTL (default 900) caps the TTL of native provider URLs for private blobs. The whole native_url feature is default-off per disk — no action unless you opt a disk in via uploads.native_urls.disks.
  • No core migrations; no required env changes.