Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 51 additions & 11 deletions grails-data-neo4j/GORM_REGISTRY_MIGRATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -110,17 +110,57 @@ Deviations and fixes discovered beyond the original plan, in case they're useful
`OptimisticLockingSpec`'s "Test optimistic locking" remains a known pre-existing flaky test
(self-documented `// heisenbug` in a hardcoded `sleep(2000)` race simulation, untouched by this PR).

## PR4 — re-add deferred example apps and docs (not started)

- The 5 standalone `examples-*` apps (`examples-grails3-neo4j`, `-hibernate`, `-standalone`,
`-spring-boot`, `examples-test-data-service`) are left in place but un-wired — no
`settings.gradle` references them anymore. Re-add under `grails-test-examples/neo4j/...`,
mirroring how `grails-data-graphql`'s examples were migrated in `9d7d4943d5`.
- `grails-data-neo4j/docs` needs a genuine rewrite, not a mechanical port: it downloads GORM source
from a GitHub archive URL at build time (`fetchSource` task) and iterates
`rootProject.subprojects` assuming it's the root of a small standalone build — both wrong in the
monorepo. Rewrite against `gormApiDocs`/`gradle/docs-config.gradle`, mirroring
`grails-data-mongodb/docs` or `grails-data-graphql/docs`.
## PR4 — re-add deferred example apps and docs — done

Of the 5 standalone example apps, 3 were migrated to `grails-test-examples/neo4j/...`
(`grails3-neo4j`, `grails3-neo4j-hibernate`, `spring-boot` — the former `neo4j-spring-boot`),
mirroring how `grails-data-graphql`'s examples were migrated in `9d7d4943d5`: re-authored fresh
against the monorepo's convention plugins and `org.apache.grails:*` coordinates rather than
`git mv`'d wholesale, since the old standalone-build dependency style (`project(":grails-plugin")`,
hardcoded property-interpolated versions, manual `webdriverBinaries {}`/Selenium wiring) doesn't
carry over. `examples-neo4j-standalone` and `examples-test-data-service` were dropped rather than
migrated — the former had no unique coverage beyond what `grails3-neo4j` already exercises, the
latter duplicated mongodb's own existing `test-data-service` example and additionally had a stray
cross-plugin `mongodb-gson-templates` dependency that didn't belong to neo4j at all.

`grails-data-neo4j/docs` was rewritten against the `gormApiDocs` marker-property pattern from
`grails-data-mongodb/docs`, replacing the broken `fetchSource`/`rootProject.subprojects.each`
standalone-build logic. One thing not obvious from the mongodb template alone: its `documentation`
configuration deliberately excludes `grails-data-hibernate7*` projects (a `jandex` version conflict
between hibernate7 modules) — that exclusion had to be copied into neo4j's version too, or
`:grails-data-neo4j-docs:groovydoc` fails at dependency resolution with a `jandex` version conflict.

Deviations/fixes discovered along the way:

- **The Jetty/`neo4j-java-driver` forces and JDK `--add-opens` flags had to be replicated into
each example app's own `build.gradle`, not just the core/boot-plugin/grails-plugin modules** —
same root cause as PR3 (Gradle resolves each project's classpath independently), but easy to miss
since example apps are one more layer removed from the adapter code.
- **`error.gsp` in both Grails apps used the stale `javax.servlet.error.exception` request attribute
name** — fixed to `jakarta.servlet.error.exception`, matching `grails-data-mongodb`'s already-migrated
equivalent.
- **`Neo4jWithHibernateSpec` (grails3-neo4j-hibernate) needed `hibernate.getPluginClass()` stubbed** on
its `Mock(GrailsPlugin)` — `MockGrailsPluginManager#registerMockPlugin` now NPEs without it
(`PluginUtils.createPluginInfo` requires a non-null plugin class). Once past that, the test's actual
assertion (that `grailsDomainClassMappingContext` and `neo4jMappingContext` are *different* beans
when a Hibernate plugin is present) still fails — `Neo4jGrailsPlugin#hasHibernatePlugin()`'s
`manager.allPlugins.any { plugin.name ==~ /hibernate\d*/ }` check doesn't detect the mock-registered
plugin the way this test expects, so `secondaryDatastore` never becomes `true`. Marked
`@PendingFeature` with a reason rather than debugged further — this is `MockGrailsPluginManager`/
`Neo4jGrailsPlugin` internals, orthogonal to an examples/docs migration, and (like
`OptimisticLockingSpec` in PR3) a pre-existing gap that was never exercised end-to-end before this
module was part of the monorepo build.
- No example app in this repo (graphql's or mongodb's included) wires up the `grails-code-style`
convention plugin — confirmed this is deliberate existing convention, not a gap to fix here.
- A full `./gradlew build -PskipTests` repo-wide re-check could not be completed in this session
(the background task kept getting terminated by the harness partway through, unrelated to the
build itself — the underlying Gradle daemon was still alive and building each time it was
checked). Every PR4-specific check that *could* run to completion passed: all 3 example apps
compile (main/test/integrationTest) and their unit tests pass, and the docs module's
`asciidoctor` and `groovydoc` tasks both execute successfully. PR3's equivalent full-repo check
already validated the broader wiring approach; PR4 only adds `settings.gradle` includes on top of
that, touching no shared build logic. Worth a real full-build re-check before merging if the
environment allows it.

## PR5 — codeStyle cleanup (not started)

Expand Down
4 changes: 4 additions & 0 deletions grails-data-neo4j/boot-plugin/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ plugins {
version = projectVersion
group = 'org.apache.grails'

ext {
gormApiDocs = true
}

dependencies {

implementation platform(project(':grails-bom'))
Expand Down
238 changes: 87 additions & 151 deletions grails-data-neo4j/docs/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -17,182 +17,118 @@
* under the License.
*/

ext {
datastoreGithubBranch = "8.0.x"
checkOutDir = "build/checkout"
explicitGormSrc = System.getProperty("gorm.src") ?: (project.hasProperty('gorm.src') ? project.getProperty("gorm.src") : null)
gormSrc = explicitGormSrc ? file(explicitGormSrc).absolutePath : "$checkOutDir/gorm-src"
zipFile = "build/source.zip"

coreProjects = [
'core',
'gorm'
]
import org.asciidoctor.gradle.jvm.AsciidoctorTask

plugins {
id 'groovy'
id 'org.asciidoctor.jvm.convert'
}

version rootProject.version

apply plugin: 'groovy'
apply plugin: 'org.asciidoctor.jvm.convert'
version = projectVersion

configurations {
documentation {
canBeConsumed = false
canBeResolved = true
attributes {
attribute(Category.CATEGORY_ATTRIBUTE, objects.named(Category, Category.LIBRARY))
attribute(Bundling.BUNDLING_ATTRIBUTE, objects.named(Bundling, Bundling.EXTERNAL))
attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage, Usage.JAVA_RUNTIME))
}
}
ext {
coreProjects = ['grails-datastore-core', 'grails-datamapping-core']
}

apply plugin: 'org.apache.grails.buildsrc.groovydoc'

dependencies {
documentation "org.fusesource.jansi:jansi:$jansiVersion"
documentation "org.apache.groovy:groovy"
documentation "org.apache.groovy:groovy-templates"
documentation "org.apache.groovy:groovy-dateutil"
documentation "com.github.javaparser:javaparser-core:$javaParserCoreVersion"
for (p in coreProjects) {
documentation "org.apache.grails:grails-datastore-$p:$datastoreVersion"
}
project.rootProject.subprojects.each { subproject ->
if (subproject.name != "docs" && !subproject.name.startsWith('examples')) {
documentation project(":$subproject.name")
}
documentation platform(project(':grails-bom'))
documentation 'org.fusesource.jansi:jansi'
documentation 'org.apache.groovy:groovy'
documentation 'org.apache.groovy:groovy-ant'
documentation 'org.apache.groovy:groovy-groovydoc'
documentation 'org.apache.groovy:groovy-templates'
documentation 'org.apache.groovy:groovy-dateutil'
documentation 'com.github.javaparser:javaparser-core'
coreProjects.each {
documentation "org.apache.grails.data:$it"
}
rootProject.subprojects
.findAll { it.findProperty('gormApiDocs') }
.each {
// TODO: This needs fixed for hibernate 7, but with the hibernate version conflicts, we may need to do separate documentation publishing
if (!it.name.startsWith('grails-data-hibernate7')) {
documentation project(":$it.name")
}
}
}

asciidoctor {
baseDirFollowsSourceDir()
resources {
from("${project.projectDir}/src/docs/images")
into "${project.projectDir}/images"
}
tasks.named('asciidoctor', AsciidoctorTask) { AsciidoctorTask it ->
it.inputs.dir(project.layout.projectDirectory.dir('src/docs/asciidoc')).withPropertyName('docsSrcDir').withPathSensitivity(PathSensitivity.RELATIVE)
it.outputs.dir layout.buildDirectory.dir('asciidoc/manual')

attributes 'experimental': 'true',
'compat-mode': 'true',
'toc': 'left',
'icons': 'font',
'reproducible': '',
'version': project.version,
'pluginVersion': project.version,
'neo4jVersion': neo4jVersion,
'sourcedir': "${project.rootProject.projectDir}/grails-datastore-gorm-neo4j/src/main/groovy",
'exampledir': "${project.rootProject.projectDir}/examples/grails3-neo4j",
'testsdir': "${project.rootProject.projectDir}/grails-datastore-gorm-neo4j/src/test/groovy"
it.jvm {
jvmArgs("--add-opens", "java.base/sun.nio.ch=ALL-UNNAMED", "--add-opens", "java.base/java.io=ALL-UNNAMED")
}
it.baseDirFollowsSourceFile()
it.sourceDir layout.projectDirectory.dir('src/docs/asciidoc')
it.outputDir = layout.buildDirectory.dir('asciidoc/manual')
it.attributes = [
'experimental' : 'true',
'compat-mode' : 'true',
'toc' : 'left',
'icons' : 'font',
'reproducible' : '',
'version' : projectVersion,
'pluginVersion': projectVersion,
'neo4jVersion' : neo4jVersion,
'exampledir' : rootProject.findProject(':grails-test-examples-neo4j-grails3-neo4j').layout.projectDirectory.asFile.absolutePath,
'testsdir' : rootProject.findProject(':grails-datastore-gorm-neo4j').layout.projectDirectory.dir('src/test/groovy').asFile.absolutePath
]
}

task fetchSource {
tasks.withType(Groovydoc).configureEach { Groovydoc groovydoc ->
groovydoc.dependsOn(rootProject.subprojects
.findAll { it.findProperty('gormApiDocs') }
.collect { ":${it.name}:groovydoc" })
groovydoc.docTitle = "GORM for Neo4j - $project.version"
groovydoc.includeAuthor = true

outputs.dir layout.buildDirectory.dir('checkout')
inputs.properties(branch: datastoreGithubBranch)
def sourceFiles = coreProjects.collect {
layout.projectDirectory.files("$it/src/main/groovy")
}.sum()

onlyIf {
println "GORM SRC=$explicitGormSrc"
return !explicitGormSrc
}

doLast {
ant.mkdir dir: project.buildDir
ant.mkdir dir: checkOutDir

println "Downloading GORM source code."
if (isReleaseVersion) {
ant.get src: "https://github.com/grails/grails-data-mapping/archive/refs/tags/v${datastoreVersion}.zip", dest: zipFile, verbose: true
} else {
ant.get src: "https://github.com/grails/grails-data-mapping/archive/refs/heads/${datastoreGithubBranch}.zip", dest: zipFile, verbose: true
}
rootProject.subprojects
.findAll { it.findProperty('gormApiDocs') }
.each { sourceFiles += it.files('src/main/groovy') }

ant.unzip src: zipFile, dest: checkOutDir, {
mapper type: "regexp", from: "(grails-\\S*?/)(.*)", to: "gorm-src/\\2"
}
groovydoc.source = sourceFiles
groovydoc.destinationDir = layout.buildDirectory.dir('combined-api/api').get().asFile
groovydoc.classpath = configurations.documentation

println "GORM source code downloaded."
List<File> groovydocSrcDirs = coreProjects.collect {
layout.projectDirectory.dir("$it/src/main/groovy").asFile
}
rootProject.subprojects
.findAll { sp -> sp.findProperty('gormApiDocs') }
.each { sp -> groovydocSrcDirs << new File(sp.projectDir, 'src/main/groovy') }
groovydoc.ext.groovydocSourceDirs = groovydocSrcDirs
}

task copyDocs(type: Copy) {
dependsOn('asciidoctor', 'copyResources')
mustRunAfter 'asciidoctor'
finalizedBy 'cleanAsciidoc'
from project(':docs').layout.buildDirectory.dir('docs/asciidoc')
into project(':docs').layout.buildDirectory.dir('docs/manual')
}
tasks.register('docs', Sync).configure { Sync docTask ->
docTask.group = 'documentation'
docTask.dependsOn('asciidoctor', 'groovydoc')

task cleanAsciidoc(type: Delete, dependsOn: copyDocs) {
dependsOn 'copyDocs'
delete project(':docs').layout.buildDirectory.dir('docs/asciidoc')
}
def apiDir = layout.buildDirectory.dir('combined-api')
def resourceDir = layout.projectDirectory.dir('src/docs/resources')
def guideDir = layout.buildDirectory.dir('asciidoc')

tasks.withType(Groovydoc) {
dependsOn(fetchSource)
docTitle = "GORM for Neo4j - ${project.version}"
footer = '''<!-- Matomo -->
<script>
var _paq = window._paq = window._paq || [];
/* tracker methods like "setCustomDimension" should be called before "trackPageView" */
_paq.push(["setDoNotTrack", true]);
_paq.push(["disableCookies"]);
_paq.push(['trackPageView']);
_paq.push(['enableLinkTracking']);
(function() {
var u="https://analytics.apache.org/";
_paq.push(['setTrackerUrl', u+'matomo.php']);
_paq.push(['setSiteId', '79']);
var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0];
g.async=true; g.src=u+'matomo.js'; s.parentNode.insertBefore(g,s);
})();
</script>
<!-- End Matomo Code -->'''
destinationDir = project.file("build/docs/api")

def files
for (p in coreProjects) {
if (files == null) {
files = project.files("${checkOutDir}/gorm-src/grails-datastore-${p}/src/main/groovy")
} else {
files += project.files("${checkOutDir}/gorm-src/grails-datastore-${p}/src/main/groovy")
}
}
project.rootProject.subprojects.each { subproject ->
if (subproject.name.startsWith('examples')) return
if (subproject.file('src/main/groovy').exists()) {
files += subproject.files("src/main/groovy")
}
}
source = files
classpath = configurations.documentation
}
docTask.from apiDir, resourceDir, guideDir
docTask.into layout.buildDirectory.dir('docs')

tasks.withType(org.gradle.api.tasks.javadoc.Groovydoc) {
configure {
access = GroovydocAccess.PRIVATE
processScripts = false
includeMainForScripts = false
includeAuthor = true
groovyClasspath += configurations.documentation
}
docTask.finalizedBy('assembleDocsDist')
}

task copyResources(type: Copy) {
from project(':docs').layout.projectDirectory.dir('src/docs/resources')
into project(':docs').layout.buildDirectory.dir('docs')
tasks.register('assembleDocsDist', Zip).configure { Zip it ->
it.dependsOn('docs')
it.from(layout.buildDirectory.dir('docs'))
it.archiveFileName = "${project.name}-${project.version}.zip"
it.destinationDirectory = project.layout.buildDirectory.dir('distributions')
}

task docs(dependsOn: [asciidoctor, groovydoc, copyDocs, copyResources] +
subprojects.findAll { project -> project.tasks.findByName('groovydoc') }
.collect { project -> project.tasks.groovydoc }
)

task assembleDocsDist(type: Zip) {
dependsOn 'docs', 'copyDocs'
from "${project.buildDir}/docs"
include '*'
include '*/**'
archiveFileName = "${project.name}-${project.version}.zip"
destinationDirectory = project.layout.buildDirectory.dir('distributions')
// the groovy plugin is applied to this project, but we do not build a jar file since
// the dependencies are only used for resolving versions from the bom
tasks.withType(Jar).configureEach {
enabled = false
}

docs.finalizedBy assembleDocsDist
Loading
Loading