Commits
Tarun Sukhani authored 98b6df0d2d4
fix: support read-only framework installs end-to-end (release 1.13.4)
After installing the v1.13.3 zip to a system path like /opt/play1 (root-owned,
not writable to the invoking user), the framework was unusable: `play new`
aborted on Gradle 9's project-dir-writability check, and even if a user worked
around that, the scaffolded app's first `play run` would re-fail because the
plugin was loaded via pluginManagement.includeBuild("$frameworkPath") — which
compiles the plugin from source and writes into $frameworkPath/.gradle/ +
framework/gradle-plugin/build/. Two symptoms, one architectural cause.
play wrapper (`play new` branch):
Stage a writable scratch dir under $TMPDIR, symlink the framework's read-only
files into it, run Gradle from there. Pass -PframeworkPath=$PLAY_HOME so the
generated app's build.gradle.kts records the *real* framework path (not the
scratch dir, which we delete on exit). Trap chmods before rm -rf so cp-
preserved read-only modes don't block cleanup.
framework/gradle-plugin/build.gradle.kts:
Apply maven-publish, set group + version (inherited from rootProject), and
publish into framework/gradle-plugin-repo/ (a flat Maven layout). Generates
both the implementation jar (org.playframework:gradle-plugin) and the plugin
marker (org.playframework.play1.gradle.plugin) needed for plugins{} resolution.
framework/build.xml package:
Run :gradle-plugin:publishAllPublicationsToFrameworkLocalRepository, copy
the result into the dist before zipping. Generated apps now resolve the
play1 plugin from this repo via pluginManagement.repositories.maven, so they
only *read* from $frameworkPath — no source compile, no writes.
build.gradle.kts (root) PlayNewAppTask:
Accept -PframeworkPath. Generated settings.gradle.kts + build.gradle.kts
switch from `pluginManagement.includeBuild` to a flat-Maven-repo lookup.
Wrapper + Nuxt copies converted from fileSystemOps.copy to manual
walk-and-copy (Gradle's CopySpec preserves source perms, which on a read-
only install means read-only destinations and cascading "Permission denied"
on subsequent writes).
Play1Plugin.kt ExtractPlayModulesTask:
Same perm-preservation reason — replaced fileSystemOps.copy + archiveOps.zipTree
with java.util.zip.ZipFile + walkTopDown. Destination files now land under
the user's umask regardless of source mode.
.gitignore:
Add framework/gradle-plugin-repo/ (new build output).
Verified end-to-end: unzip the dist into /tmp, chmod -R a-w (more restrictive
than a real root-owned install), then `play new mytestingapp --frontend` and
`./gradlew compileJava` both succeed cleanly.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>