Task 4 of the init-container JAR fetch plan: migrate AppService.uploadJar
off direct filesystem writes onto the ArtifactStore abstraction so future
backends (OCI/Zot, S3) can swap in without touching service or controller
code.
- AppService constructor now takes (AppRepository, AppVersionRepository,
ArtifactStore, tenantId[, CreateGuard]). The store owns layout and the
locator string written into app_versions.jar_path.
- uploadJar buffers the request body once for hashing + storage, then
writes a scratch temp file solely for RuntimeDetector (which still
takes a Path); scratch is unconditionally deleted in finally.
- Add coordinatesFor(AppVersion) helper so downstream callers (Task 5+)
can derive ArtifactCoordinates without knowing the tenant binding.
- Remove resolveJarPath. DeploymentExecutor now reads jarPath directly
off the AppVersion record; the clean cut to download-URL delivery
lands in Task 11.
- RuntimeBeanConfig wires a FilesystemArtifactStore bean rooted at
cameleer.server.runtime.jarstoragepath and threads tenantId into the
AppService bean.