Repository Guidelines
Project Structure & Module Organization
- The Gradle build expects JVM sources in
src/main/javaand supporting Groovy/utility scripts insrc/main/scriptsandsrc/bin. - Application resources (icons, templates) should live under
src/main/resources, while test fixtures sit insrc/test/{java,groovy,resources,sql}. - Executable examples go in
examples/, shared docs live indocs/, and generated artifacts stay inbuild/(never commit them). - Launcher assets for packaged runtimes are kept in
platform/andinstall.shshows how to assemble a platform-specific bundle.
Build, Test, and Development Commands
./gradlew build -g ./.gradle-usercompiles Java/Groovy sources, runs the full test suite, and produces jars inbuild/libs.- Use
./gradlew run -g ./.gradle-userfor a fast developer launch that opens the JavaFX UI with your local classes. ./gradlew runtimeZip -g ./.gradle-usercreates the distributable runtime zips underbuild/. Run./gradlew dependencyUpdates -Drevision=release -g ./.gradle-userbefore touchingversion.propertiesto see vetted upgrades.- If javafx is not supported in the environment, use targeted testing,
./gradlew test --tests "se.alipsa.gade.utils.*" -g ./.gradle-userruns only specified packages or classes. - Always run
./gradlew test -g ./.gradle-userwhen a task is finished to validate changes. - macOS Note: TestFX tests are automatically skipped on macOS due to known SIGTRAP crashes in Monocle. Tests pass successfully but JVM crashes during shutdown. Run
./gradlew test -DskipTestFx=false -g ./.gradle-userto force enable (tests will pass but build may report failure due to crash). Run./gradlew test -DskipTestFx=true -g ./.gradle-useron any platform to skip TestFX tests.
Runtime Distribution & Launcher Scripts
- The
./gradlew runtimeZip -g ./.gradle-usertask creates platform-specific runtime packages underbuild/that bundle the application, dependencies, and a JDK. - Each distribution contains launcher scripts that must be configured with the correct JVM arguments for Java 21 compatibility with the Gradle Tooling API.
Critical Scripts:
src/bin/cponly/gade.sh- Unix/Linux/macOS launcher script (manually maintained, copied to runtime distributions)src/bin/cponly/gade.cmd- Windows launcher script (manually maintained, copied to runtime distributions)build/image/gade-*/bin/gade- Auto-generated Unix scripts (created by beryx runtime plugin, configured viabuild.gradle)build/image/gade-*/bin/gade.bat- Auto-generated Windows scripts (created by beryx runtime plugin, configured viabuild.gradle)
JVM Arguments for Java 21: The main Gade application (NOT the splash screen) requires these JVM args to allow Gradle Tooling API access to internal JDK modules:
--enable-native-access=javafx.graphics,javafx.media,javafx.web,ALL-UNNAMED
--add-opens=java.base/java.lang=ALL-UNNAMED
--add-opens=java.base/java.util=ALL-UNNAMED
--add-opens=java.base/java.io=ALL-UNNAMED
--add-opens=java.base/java.net=ALL-UNNAMED
These are configured in:
build.gradle-applicationDefaultJvmArgsfor./gradlew runand runtime plugin auto-generated scriptssrc/bin/cponly/gade.sh- Main application commands only (lines ~100-130, not splash screen)src/bin/cponly/gade.cmd- Main application command only (lines ~41-50, not splash screen)
Important: The splash screen commands only need --enable-native-access for JavaFX, not the --add-opens flags.
Only the main Gade application needs full module access for the Gradle Tooling API. See docs/GRADLE_TOOLING_API_JAVA21.md for details.
Script Execution & Runtime Architecture
Gade executes Groovy scripts in a subprocess (RuntimeProcessRunner) that communicates with the main UI process via XML over TCP sockets (see ProtocolXml). The subprocess classpath is constructed by GroovyRuntimeManager.buildClassPathEntries().
Runtime types (RuntimeType enum): GADE, GRADLE, MAVEN, CUSTOM.
Key classes and their roles:
GroovyRuntimeManager(console/GroovyRuntimeManager.java) - Orchestrates runtime lifecycle: creates the classloader, builds the subprocess classpath, and starts theRuntimeProcessRunner. TheresetClassloaderAndGroovy()method is the main entry point for runtime initialization.RuntimeClassLoaderFactory(runtime/RuntimeClassLoaderFactory.java) - Creates aGroovyClassLoaderper runtime type. All classloaders useClassUtils.getBootstrapClassLoader()as parent for isolation from Gade application classes.RuntimeProcessRunner(runtime/RuntimeProcessRunner.java) - Manages the long-lived subprocess: starts a JVM with the constructed classpath, connects via TCP socket, and provides asynceval()for script execution.GadeRunnerMain(runner/GadeRunnerMain.java) - Entry point for the subprocess JVM. Receives scripts over the socket and evaluates them in aGroovyShell.
Classpath construction (buildClassPathEntries):
- GADE runtime: Only boot JAR on
-cp. Groovy/Ivy + engine JAR loaded into ProcessRootLoader. Users add dependencies via@Grab. - Gradle runtime: Only boot JAR on
-cp. Groovy/Ivy from deps + engine JAR loaded into ProcessRootLoader. Project dependencies resolved via Gradle Tooling API. - Maven runtime: Only boot JAR on
-cp. Groovy/Ivy from deps + engine JAR loaded into ProcessRootLoader. Project dependencies resolved from pom.xml. - Custom runtime: Boot JAR + engine JAR + Groovy home jars + configured additional jars + classloader URLs on
-cp.
Dependency resolution helpers:
GradleUtils/GradleDependencyResolver(utils/gradle/) - Resolves classpath via Gradle Tooling API.MavenClasspathUtils(utils/maven/) - Resolves classpath from pom.xml viaMavenUtils.- Upstream dependency policy: If a bug involves a dependency with
groupIdstarting withse.alipsa, do not add a local workaround in Gade as the primary fix. Fix it in the relatedse.alipsa*project and report it there (preferably as a GitHub issue) before or alongside any temporary mitigation.
Distribution layout (after ./gradlew runtimeZip):
lib/groovy/- Groovy and Ivy jars for the subprocess classpath.lib/runtimes/-gade-runner-boot.jar(JDK-only bootstrap, on subprocess-cp) andgade-runner-engine.jar(Groovy-dependent engine, loaded into ProcessRootLoader).lib/app/- Gade application jars (never included in subprocess classpath).
Coding Style & Naming Conventions
- Java and Groovy code both follow two-space indents, braces on the same line, and descriptive camelCase identifiers (
matrixLoader,semanticVersion). - Prefer
finalwhere fields should not mutate, and useOptional/nulldefensively around UI state. - Logging should go through Log4j2’s
LogManager(private static final Logger log = ...). - Resource bundles, icons, and templates belong in
src/main/resourceswith lowercase dashed filenames.
Testing Guidelines
- UI-light logic belongs in
src/test/javawith namingClassNameTestor behavior-orientedFeatureXTest. - Groovy scripts under test can mirror their runtime location inside
src/test/groovy. - Use JUnit Jupiter APIs and keep tests deterministic—mock filesystem or Gradle calls where feasible.
- When a bug is reported and no test currently fails for it, add a regression test that reproduces and guards against the issue whenever feasible.
- Run
./gradlew test(or./gradlew test --tests "se.alipsa.gade.utils.*"for a subset) before opening a PR and attach new fixtures tosrc/test/resourcesrather than embedding literals.
Commit & Pull Request Guidelines
- Match the existing history: short, imperative commit subjects ("Ensure ivy runtime is available to isolated scripts"), optional context in the body, and reference issues with
#idwhen applicable. - Each PR should describe purpose, highlight risky areas, enumerate manual/automated tests, and include UI screenshots or screen recordings when user-facing panes change.
- Keep PRs focused; split refactors from feature changes, and check
todo.mdfor related tasks before submitting.
Documentation Guidelines
- Feature explanations, improvement summaries, and architectural decisions should be documented in
docs/improvements/unless otherwise instructed. - Use clear markdown formatting with sections for Problem, Solution, Testing, and Impact.
- Include code examples where helpful, and link to related files with relative paths.
- Keep the root directory clean—only essential files like README.md, CLAUDE.md, and AGENTS.md belong there.
Security & Configuration Tips
- Never commit secrets or developer-specific tweaks; put overrides in
env.sh/env.cmdalongsidegade.shand keep them untracked. - Use
JAVA_OPTSto adjust memory or HiDPI scaling, and prefer JDK 21+ to match the toolchain enforced inbuild.gradle. - When configuring JDBC connections or REST endpoints for tests, point them at local containers or sanitized fixtures so the repo stays reproducible.