TASK-1623 — Hotfix upload image Livewire stockage distant - #624
Merged
cslucki merged 3 commits intoSep 23, 2026
Merged
Conversation
…1623)
Test ROUGE avant correction. Les quatre chemins d'upload image passent
l'objet fichier a Intervention Image, qui le resout par getPathname().
TemporaryUploadedFile::getPathname() rend Storage::disk($tmp)->path($p), et
FilesystemAdapter::path() ne fait que prefixer avec la racine du disque :
absolue en local, VIDE sur s3/gcs. Le chemin rendu est alors relatif —
« livewire-tmp/xxx.png » — et Intervention leve DirectoryNotFoundException.
Le point qui a demande le plus de soin : aucun test existant ne POUVAIT
attraper ce bug. Livewire::test()->set('image', ...) passe par
FileUploadConfiguration::disk(), qui rend « tmp-for-tests » en dur des que
l'app tourne en test, et storage() en fait un Storage::fake() local. Le
harnais de test de Livewire ne peut pas produire la condition de production ;
un test ecrit par ce chemin serait reste vert quoi qu'il arrive.
Le banc construit donc un vrai TemporaryUploadedFile sur un disque dont le
FilesystemAdapter recoit root vide : path() rend un chemin relatif, comme s3,
sans reseau — tandis que le contenu est reellement servi. C'est exactement ce
que fait un stockage distant.
5 rouges, avec l'exception et le message exacts du rapport PROD. Les 2 verts
comptent autant : la premisse et le cas local passent deja, donc le banc ne
rougit pas par construction.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JqwjarKwhSbpzdPxLAmNh3
…hemin (TASK-1623)
Quatre corrections ciblees, une ligne chacune : Image::decode($file) devient
Image::decode($file->get()). C'est l'idiome deja en place dans le depot
(GenerateServiceThumbnail lit son image par $disk->get($path)), et il ne
demande ni service generique, ni abstraction, ni refactor.
Sur un stockage temporaire distant, TemporaryUploadedFile::getPathname()
rend « livewire-tmp/xxx.png » — un chemin qui n'existe localement nulle part,
parce que FilesystemAdapter::path() ne fait que prefixer avec la racine du
disque, et que cette racine est vide sur s3/gcs. Le contenu, lui, a toujours
ete lisible : c'est lui qu'il fallait passer.
Le banc de reproduction passe de 5 rouges a 7 verts, et le cas local est
verifie avec le meme objet de production sur un disque a racine absolue.
Sabotage : redonner l'objet fichier a LoopChat fait rougir la garde de
non-retour, qui balaie les quatre surfaces d'un coup.
Hors scope, verifie et non modifie : ServiceController et ProfileController
recoivent un UploadedFile d'une requete HTTP classique, dans le tmp local de
PHP — ils ne presentent pas ce defaut.
118 verts en PostgreSQL. Le seul rouge voisin, « workspace cards shell », est
porte par #[Group('ci-known-red')] et documente comme deja rouge sur develop
avant TASK-1112 : il est hors gate GitHub, et hors de cette TASK.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JqwjarKwhSbpzdPxLAmNh3
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JqwjarKwhSbpzdPxLAmNh3
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
ROOT_CAUSE — mesurée, pas supposée
Sur un environnement où Livewire stocke ses fichiers temporaires sur un disque distant, tout upload d'image levait :
Le mécanisme tient en deux lignes de vendor :
path()ne fait que préfixer avec la racine du disque. Absolue en local → le chemin existe. Vide sur s3/gcs → le chemin rendu est relatif (livewire-tmp/xxx.png). Intervention résout unSplFileInfopar songetPathname(), testeis_dir(dirname($path)), et lève.Pourquoi aucun test existant ne POUVAIT l'attraper
Livewire::test(...)->set('image', $f)passe parFileUploadConfiguration::disk(), qui rend'tmp-for-tests'en dur dès que l'app tourne en test, etstorage()en fait unStorage::fake()— donc un disque local. Le harnais de test de Livewire ne peut structurellement pas produire la condition de production.Le banc construit donc un vrai
TemporaryUploadedFilesur un disque dont leFilesystemAdapterreçoitrootvide :path()rend un chemin relatif, comme s3, sans réseau, tandis que le contenu est réellement servi.FIX
Quatre corrections d'une ligne :
Image::decode($file)→Image::decode($file->get()). Le contenu, jamais le chemin. C'est l'idiome déjà en place (GenerateServiceThumbnaillit son image par$disk->get($path)). Pas de service générique, pas d'abstraction, pas de refactor.Hors scope, vérifié et non modifié :
ServiceControlleretProfileControllerreçoivent unUploadedFiled'une requête HTTP classique, dans le tmp local de PHP — pas le même défaut.Mesures
#[Group('ci-known-red')]pré-existant sur develop (rejoué sur le fichier nu pour le prouver)Recette navigateur réelle
ChatLoop et création d'annonce sur
https://test.laravel: image jointe, aperçu, envoi,RIFF Web/P VP8 640x400produit dans les deux cas. Zéro erreur console, zéro erreur Laravel. Artefacts de recette supprimés en base.L'environnement local utilise un disque temporaire local : la recette navigateur couvre les critères 1, 3, 4 et 6 ; le critère 2 (stockage distant) est couvert par le banc, seul endroit où la condition soit reproductible sans S3.
Multi-tenant
Aucune ligne touchée. Organization = Tenant, Loop != Tenant,
community_id= dette legacy temporaire, aucune migration Community → Organization.🤖 Generated with Claude Code
https://claude.ai/code/session_01JqwjarKwhSbpzdPxLAmNh3