diff --git a/.github/release-drafter.yml b/.github/release-drafter.yml
new file mode 100644
index 0000000..38a91ce
--- /dev/null
+++ b/.github/release-drafter.yml
@@ -0,0 +1,134 @@
+name-template: $RESOLVED_VERSION
+tag-template: v$RESOLVED_VERSION
+pull-request:
+ title-templates:
+ fix: '🐛 $TITLE (#$NUMBER)'
+ feat: '🚀 $TITLE (#$NUMBER)'
+ default: '$TITLE (#$NUMBER)'
+autolabeler:
+ - label: 'bug'
+ branch:
+ - '/fix\/.+/'
+ title:
+ - '/fix/i'
+ - label: 'improvement'
+ branch:
+ - '/improv\/.+/'
+ title:
+ - '/improv/i'
+ - label: 'feature'
+ branch:
+ - '/feature\/.+/'
+ title:
+ - '/feat/i'
+ - label: 'documentation'
+ branch:
+ - '/docs\/.+/'
+ title:
+ - '/docs/i'
+ - label: 'maintenance'
+ branch:
+ - '/(chore|refactor|style|test|ci|perf|build)\/.+/'
+ title:
+ - '/(chore|refactor|style|test|ci|perf|build)/i'
+ - label: 'chore'
+ branch:
+ - '/chore\/.+/'
+ title:
+ - '/chore/i'
+ - label: 'refactor'
+ branch:
+ - '/refactor\/.+/'
+ title:
+ - '/refactor/i'
+ - label: 'style'
+ branch:
+ - '/style\/.+/'
+ title:
+ - '/style/i'
+ - label: 'test'
+ branch:
+ - '/test\/.+/'
+ title:
+ - '/test/i'
+ - label: 'ci'
+ branch:
+ - '/ci\/.+/'
+ title:
+ - '/ci/i'
+ - label: 'perf'
+ branch:
+ - '/perf\/.+/'
+ title:
+ - '/perf/i'
+ - label: 'build'
+ branch:
+ - '/build\/.+/'
+ title:
+ - '/build/i'
+ - label: 'deps'
+ branch:
+ - '/deps\/.+/'
+ title:
+ - '/deps/i'
+ - label: 'revert'
+ branch:
+ - '/revert\/.+/'
+ title:
+ - '/revert/i'
+categories:
+ - title: '🚀 Features'
+ labels:
+ - 'feature'
+ - "type: enhancement"
+ - "type: new feature"
+ - "type: major"
+ - "type: minor"
+ - title: '💡 Improvements'
+ labels:
+ - 'improvement'
+ - "type: improvement"
+
+ - title: '🐛 Bug Fixes'
+ labels:
+ - 'fix'
+ - 'bug'
+ - "type: bug"
+ - title: '📚 Documentation'
+ labels:
+ - 'docs'
+ - title: '🔧 Maintenance'
+ labels:
+ - 'maintenance'
+ - 'chore'
+ - 'refactor'
+ - 'style'
+ - 'test'
+ - 'ci'
+ - 'perf'
+ - 'build'
+ - "type: ci"
+ - "type: build"
+ - title: '⏪ Reverts'
+ labels:
+ - 'revert'
+change-template: '- $TITLE @$AUTHOR (#$NUMBER)'
+version-resolver:
+ major:
+ labels:
+ - 'type: major'
+ minor:
+ labels:
+ - 'type: minor'
+ patch:
+ labels:
+ - 'type: patch'
+ default: patch
+template: |
+ ## What's Changed
+
+ $CHANGES
+
+ ## Contributors
+
+ $CONTRIBUTORS
\ No newline at end of file
diff --git a/.github/renovate.json b/.github/renovate.json
new file mode 100644
index 0000000..30740f8
--- /dev/null
+++ b/.github/renovate.json
@@ -0,0 +1,61 @@
+{
+ "extends": [
+ "config:base"
+ ],
+ "labels": ["type: dependency upgrade"],
+ "packageRules": [
+ {
+ "matchUpdateTypes": ["major"],
+ "enabled": false
+ },
+ {
+ "matchPackagePatterns": ["*"],
+ "allowedVersions": "!/SNAPSHOT$/"
+ },
+ {
+ "matchPackagePatterns": [
+ "^org\\.codehaus\\.groovy"
+ ],
+ "groupName": "groovy monorepo"
+ },
+ {
+ "matchPackageNames": [
+ "org.grails:grails-bom",
+ "org.grails:grails-bootstrap",
+ "org.grails:grails-codecs",
+ "org.grails:grails-console",
+ "org.grails:grails-core",
+ "org.grails:grails-databinding",
+ "org.grails:grails-dependencies",
+ "org.grails:grails-docs",
+ "org.grails:grails-encoder",
+ "org.grails:grails-gradle-model",
+ "org.grails:grails-logging",
+ "org.grails:grails-plugin-codecs",
+ "org.grails:grails-plugin-controllers",
+ "org.grails:grails-plugin-databinding",
+ "org.grails:grails-plugin-datasource",
+ "org.grails:grails-plugin-domain-class",
+ "org.grails:grails-plugin-i18n",
+ "org.grails:grails-plugin-interceptors",
+ "org.grails:grails-plugin-mimetypes",
+ "org.grails:grails-plugin-rest",
+ "org.grails:grails-plugin-services",
+ "org.grails:grails-plugin-url-mappings",
+ "org.grails:grails-plugin-url-validation",
+ "org.grails:grails-shell",
+ "org.grails:grails-spring",
+ "org.grails:grails-test",
+ "org.grails:grails-validation",
+ "org.grails:grails-web",
+ "org.grails:grails-web-boot",
+ "org.grails:grails-web-common",
+ "org.grails:grails-web-databinding",
+ "org.grails:grails-web-fileupload",
+ "org.grails:grails-web-mvc",
+ "org.grails:grails-web-url-mappings"
+ ],
+ "groupName": "grails monorepo"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml
new file mode 100644
index 0000000..a01e1eb
--- /dev/null
+++ b/.github/workflows/gradle.yml
@@ -0,0 +1,68 @@
+name: "Java CI"
+on:
+ push:
+ branches:
+ - '[4-9]+.[0-9]+.x'
+ pull_request:
+ branches:
+ - '[4-9]+.[0-9]+.x'
+ workflow_dispatch:
+jobs:
+ test_project:
+ name: "Test Project"
+ runs-on: ubuntu-24.04
+ strategy:
+ fail-fast: true
+ matrix:
+ java: [17, 21]
+ steps:
+ - name: "📥 Checkout repository"
+ uses: actions/checkout@v4
+ - name: "☕️ Setup JDK"
+ uses: actions/setup-java@v4
+ with:
+ java-version: ${{ matrix.java }}
+ distribution: liberica
+ - name: "🐘 Setup Gradle"
+ uses: gradle/actions/setup-gradle@v4
+ with:
+ develocity-access-key: ${{ secrets.DEVELOCITY_ACCESS_KEY }}
+ - name: "🏃 Run tests"
+ run: ./gradlew check
+ publish_snapshot:
+ if: github.event_name == 'push' || github.event_name == 'workflow_dispatch'
+ name: "Build Project and Publish Snapshot release"
+ needs: test_project
+ runs-on: ubuntu-24.04
+ permissions:
+ contents: write # updates gh-pages branch
+ packages: write # publishes snapshot to GitHub Packages
+ steps:
+ - name: "📥 Checkout repository"
+ uses: actions/checkout@v4
+ - name: "☕️ Setup JDK"
+ uses: actions/setup-java@v4
+ with:
+ java-version: 17
+ distribution: liberica
+ - name: "🐘 Setup Gradle"
+ uses: gradle/actions/setup-gradle@v4
+ with:
+ develocity-access-key: ${{ secrets.DEVELOCITY_ACCESS_KEY }}
+ - name: "🔨 Build Project"
+ run: ./gradlew build
+ - name: "📤 Publish Snapshot version to Artifactory (repo.grails.org)"
+ env:
+ GRAILS_PUBLISH_RELEASE: 'false'
+ MAVEN_PUBLISH_USERNAME: ${{ secrets.MAVEN_PUBLISH_USERNAME }}
+ MAVEN_PUBLISH_PASSWORD: ${{ secrets.MAVEN_PUBLISH_PASSWORD }}
+ MAVEN_PUBLISH_URL: 'https://repo.grails.org/artifactory/plugins3-snapshots-local'
+ run: ./gradlew publish
+ - name: "📖 Generate Snapshot Documentation"
+ run: ./gradlew docs
+ - name: "📤 Publish Snapshot Documentation to Github Pages"
+ uses: apache/grails-github-actions/deploy-github-pages@asf
+ env:
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ GRADLE_PUBLISH_RELEASE: 'false'
+ SOURCE_FOLDER: build/docs
\ No newline at end of file
diff --git a/.github/workflows/release-notes.yml b/.github/workflows/release-notes.yml
new file mode 100644
index 0000000..e41d6b4
--- /dev/null
+++ b/.github/workflows/release-notes.yml
@@ -0,0 +1,23 @@
+name: "Release Drafter"
+on:
+ issues:
+ types: [closed, reopened]
+ push:
+ branches:
+ - master
+ - '[4-9]+.[0-9]+.x'
+ pull_request:
+ types: [opened, reopened, synchronize]
+ pull_request_target:
+ types: [opened, reopened, synchronize]
+jobs:
+ update_release_draft:
+ permissions:
+ contents: write
+ pull-requests: write
+ runs-on: ubuntu-24.04
+ steps:
+ - name: "📝 Update Release Draft"
+ uses: release-drafter/release-drafter@v6
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
\ No newline at end of file
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
new file mode 100644
index 0000000..ab000b1
--- /dev/null
+++ b/.github/workflows/release.yml
@@ -0,0 +1,130 @@
+name: Release
+on:
+ release:
+ types: [ published ]
+env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ JAVA_VERSION: '17.0.15' # this must be a specific version for reproducible builds
+ RELEASE_TAG_PREFIX: 'v'
+jobs:
+ publish:
+ permissions:
+ packages: read # pre-release workflow
+ contents: write # to create release
+ issues: write # to modify milestones
+ runs-on: ubuntu-latest
+ outputs:
+ release_version: ${{ steps.release_version.outputs.value }}
+ extract_repository_name: ${{ steps.extract_repository_name.outputs.repository_name }}
+ steps:
+ - name: "📝 Store the current release version"
+ id: release_version
+ run: |
+ export RELEASE_VERSION="${{ github.ref_name }}"
+ export RELEASE_VERSION=${RELEASE_VERSION:${#RELEASE_TAG_PREFIX}}
+ echo "Found Release Version: ${RELEASE_VERSION}"
+ echo "value=${RELEASE_VERSION}" >> $GITHUB_OUTPUT
+ - name: "Extract repository name"
+ id: extract_repository_name
+ run: |
+ echo "repository_name=${GITHUB_REPOSITORY##*/}" >> $GITHUB_OUTPUT
+ - name: "📥 Checkout the repository"
+ uses: actions/checkout@v4
+ with:
+ token: ${{ secrets.GITHUB_TOKEN }}
+ ref: v${{ steps.release_version.outputs.value }}
+ - name: 'Ensure Common Build Date' # to ensure a reproducible build
+ run: echo "SOURCE_DATE_EPOCH=$(git log -1 --pretty=%ct)" >> "$GITHUB_ENV"
+ - name: "Ensure source files use common date"
+ run: |
+ find . -depth \( -type f -o -type d \) -exec touch -d "@${SOURCE_DATE_EPOCH}" {} +
+ - name: "☕️ Setup JDK"
+ uses: actions/setup-java@v4
+ with:
+ distribution: liberica
+ java-version: ${{ env.JAVA_VERSION }}
+ - name: "🐘 Setup Gradle"
+ uses: gradle/actions/setup-gradle@v4
+ - name: "⚙️ Run pre-release"
+ uses: grails/github-actions/pre-release@asf
+ env:
+ RELEASE_VERSION: ${{ steps.release_version.outputs.value }}
+ - name: "🔐 Generate key file for artifact signing"
+ env:
+ SECRING_FILE: ${{ secrets.SECRING_FILE }}
+ run: |
+ printf "%s" "$SECRING_FILE" | base64 -d > "${{ github.workspace }}/secring.gpg"
+ - name: "🧩 Run Assemble"
+ id: assemble
+ run: |
+ ./gradlew -U assemble -Psigning.secretKeyRingFile=${{ github.workspace }}/secring.gpg -Psigning.keyId=${{ secrets.SIGNING_KEY }}
+ env:
+ GRAILS_PUBLISH_RELEASE: 'true'
+ SIGNING_KEY: ${{ secrets.SIGNING_KEY }}
+ SIGNING_PASSPHRASE: ${{ secrets.SIGNING_PASSPHRASE }}
+ - name: "📤 Publish to Maven Central"
+ env:
+ GRAILS_PUBLISH_RELEASE: 'true'
+ NEXUS_PUBLISH_USERNAME: ${{ secrets.NEXUS_PUBLISH_USERNAME }}
+ NEXUS_PUBLISH_PASSWORD: ${{ secrets.NEXUS_PUBLISH_PASSWORD }}
+ NEXUS_PUBLISH_URL: 'https://ossrh-staging-api.central.sonatype.com/service/local/'
+ NEXUS_PUBLISH_DESCRIPTION: '${{ steps.extract_repository_name.outputs.repository_name }}:${{ steps.release_version.outputs.value }}'
+ SIGNING_KEY: ${{ secrets.SIGNING_KEY }}
+ SIGNING_PASSPHRASE: ${{ secrets.SIGNING_PASSPHRASE }}
+ run: >
+ ./gradlew
+ -Psigning.keyId=${{ secrets.SIGNING_KEY }}
+ -Psigning.secretKeyRingFile=${{ github.workspace }}/secring.gpg
+ publishMavenPublicationToSonatypeRepository
+ closeSonatypeStagingRepository
+ - name: "Generate Build Date file"
+ run: echo "$SOURCE_DATE_EPOCH" >> build/BUILD_DATE.txt
+ - name: "Upload Build Date file"
+ uses: softprops/action-gh-release@da05d552573ad5aba039eaac05058a918a7bf631
+ with:
+ files: build/BUILD_DATE.txt
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ release:
+ needs: publish
+ runs-on: ubuntu-latest
+ environment: release
+ permissions:
+ contents: write
+ issues: write
+ pull-requests: write
+ steps:
+ - name: "📥 Checkout repository"
+ uses: actions/checkout@v4
+ with:
+ token: ${{ secrets.GITHUB_TOKEN }}
+ ref: v${{ needs.publish.outputs.release_version }}
+ - name: "☕️ Setup JDK"
+ uses: actions/setup-java@v4
+ with:
+ distribution: liberica
+ java-version: ${{ env.JAVA_VERSION }}
+ - name: "🐘 Setup Gradle"
+ uses: gradle/actions/setup-gradle@v4
+ - name: "📤 Release staging repository"
+ env:
+ GRAILS_PUBLISH_RELEASE: 'true'
+ NEXUS_PUBLISH_USERNAME: ${{ secrets.NEXUS_PUBLISH_USERNAME }}
+ NEXUS_PUBLISH_PASSWORD: ${{ secrets.NEXUS_PUBLISH_PASSWORD }}
+ NEXUS_PUBLISH_URL: 'https://ossrh-staging-api.central.sonatype.com/service/local/'
+ NEXUS_PUBLISH_DESCRIPTION: '${{ needs.publish.outputs.extract_repository_name }}:${{ needs.publish.outputs.release_version }}'
+ run: >
+ ./gradlew
+ findSonatypeStagingRepository
+ releaseSonatypeStagingRepository
+ - name: "📖 Generate Documentation"
+ run: ./gradlew docs
+ - name: "📤 Publish Documentation to Github Pages"
+ uses: apache/grails-github-actions/deploy-github-pages@asf
+ env:
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ GRADLE_PUBLISH_RELEASE: 'true'
+ SOURCE_FOLDER: build/docs
+ VERSION: ${{ needs.publish.outputs.release_version }}
+ - name: "⚙️ Run post-release"
+ uses: apache/grails-github-actions/post-release@asf
diff --git a/.gitignore b/.gitignore
index 74835a1..46eac81 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,12 +1,9 @@
build/
.gradle/
-target
-plugin.xml
-docs
*.log
*.zip
.DS_Store
-grails-rendering-*
.settings
.vscode/
bin/
+.idea
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index f1e4dcf..0000000
--- a/.travis.yml
+++ /dev/null
@@ -1,21 +0,0 @@
-sudo: false
-cache:
- directories:
- - $HOME/.gradle
-language: groovy
-jdk:
-- openjdk17
-before_script:
-- rm -rf target
-script: ./travis-build.sh
-env:
- global:
- - GIT_NAME="Graeme Rocher"
- - GIT_EMAIL="graeme.rocher@gmail.com"
- - secure: qutwo7+0pfBh2fo2NrTdMdhK6Z7jGAGmfQUUms5IewycAEcO/XYD61uM9Om8y03puBXeo2aoN9YUtF5C+iou6KaGnLIMNDE6E5h9f36fTUk3RebwwV7cgsyzn4Zzma+EYYw6S2KF0Kj+JHPNDnlEdYLdmVJ1eq4KXVA17t5F8Eg=
- - secure: G5bnlZc73YxAy9acnFuHpsEQ/JeKIawwT4pzv9S8ZIshVYdELWqBZiiDmiDiS1x4WhT1J9THWWy3/0Rp6taYA2IxNRgu4f9kPlv6m+Yb4pCl9E3zDfmdd1nnPeKpL7vrxawWQnF3PqqTX6qkY3PPiIdVUXdN1flUQOC0RoBXKBE=
- - secure: IdiQoWkRWpDrWLNdROmGUgfdKGvccpozSXaicc3KV+Qe/ASyIKq54+oe196yvj5kTzkQqSkcHlt7icHuQfJBNjNCXhwP0TFoE55GoWzNmrJaxmr9jcvXOJzRicWp1ZrzksuoBiLxxyx2mfr3eSR7ByIyBN0aOY8n6Zcda5kE9wY=
- - secure: Y9QjAlhmbRnE94ylUkg2dkOZh4u7aBgE9vol06dya/sjo8PpTtcMx1R5MNPPcPglvCiv0mmnNIu5QWPKWnWd+hp7wEI8NdLze8WL1iLaSIETtimrfuaMxRB8etFSsm9cs8B5SLbpNCqc0uCrA86Rts4hSQTrFIYJ416Ft/E8WSE=
- - secure: o+BWtaPb6AldRYXHU15rBN/p/FIvEbar36LahiiXLKZ+2gTanBZAiDG6E/JuXRzVFqEMhOt8qvUXhpXXcxqHRvRVZqF0oqZJ8ratKwNB3W07ajEKMH8axK+Hxt/2P12Bk62ttDGxI/aFeH74RbQtNmFc+gLTS3O1QoLDLpqlVgk=
- - secure: MBg9I3LeXHMhjhXkf+b8Gyy6afTUHfpsaK5IKXPJr9AycSx8r/CnFQTesdGFcrIhLfDYCmxL7YcA+/Fj9fOhu2bUo9rysG5pOlLmnU7DE56awIeMcdYB7ecty8mWJRSS4MJWsZ8I3kbncIVRORTvfgZfjh5WdmZaCKs11hX9x28=
- - secure: SQc5o2xCxsuz8tlfBrxue7tDjanQ6NiI5/khYYc+7avECv+lH0W1b2Ho6G5ydk3eHQVxrrlF8LVrOeRlxg11UFcSmGoe6v3FO7KzvIXlZgHpq1CAR2Mojfp0YFXrAwvKetbGpiUOI+bMk5RCgUam8f1SWCkubwhQDXOO/T8I5a4=
diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md
index 587d235..5728e28 100644
--- a/CONTRIBUTORS.md
+++ b/CONTRIBUTORS.md
@@ -1,3 +1 @@
-* Luke Daley - Grails Plugin Collective
-* Graeme Rocher - Grails Plugin Collective
-* Randall Dietz - Software Projects
+See https://github.com/gpc/rendering/graphs/contributors for contributors
\ No newline at end of file
diff --git a/build.gradle b/build.gradle
index 823c6dc..741b4ff 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,3 +1,7 @@
+import java.time.Instant
+import java.time.ZoneOffset
+import java.time.format.DateTimeFormatter
+
buildscript {
ext {
grailsVersion = project.grailsVersion
@@ -19,18 +23,23 @@ buildscript {
}
}
-version "3.0.0-SNAPSHOT"
+version = projectVersion
group "org.grails.plugins"
apply plugin: 'maven-publish'
apply plugin:"org.apache.grails.gradle.grails-plugin"
apply plugin:"org.apache.grails.gradle.grails-gsp"
-// Used for publishing to central repository, remove if not needed
-//apply from:'https://raw.githubusercontent.com/grails/grails-profile-repository/master/profiles/plugin/templates/grailsCentralPublishing.gradle'
+apply plugin: 'org.apache.grails.gradle.grails-publish'
+apply plugin: 'java-library'
ext {
- grailsVersion = project.grailsVersion
- gradleWrapperVersion = project.gradleWrapperVersion
+ buildInstant = java.util.Optional.ofNullable(System.getenv("SOURCE_DATE_EPOCH"))
+ .filter(s -> !s.isEmpty())
+ .map(Long::parseLong)
+ .map(Instant::ofEpochSecond)
+ .orElseGet(Instant::now) as Instant
+ formattedBuildDate = DateTimeFormatter.ISO_INSTANT.format(buildInstant)
+ buildDate = buildInstant.atZone(ZoneOffset.UTC) // for reproducible builds
}
repositories {
@@ -49,15 +58,15 @@ dependencies {
compileOnly platform("org.apache.grails:grails-bom:$grailsVersion")
compileOnly 'org.apache.grails:grails-dependencies-starter-web'
+ api('org.xhtmlrenderer:flying-saucer-pdf-openpdf:9.4.0')
+ api("org.apache.pdfbox:pdfbox:3.0.5")
+
+ testImplementation platform("org.apache.grails:grails-bom:$grailsVersion")
testImplementation "org.apache.grails:grails-testing-support-datamapping"
testImplementation "org.spockframework:spock-core"
testImplementation "org.apache.grails:grails-testing-support-web"
- implementation 'org.xhtmlrenderer:flying-saucer-pdf-openpdf:9.1.22'
- testImplementation("org.apache.pdfbox:pdfbox:3.0.5") {
- exclude module:'jempbox'
- }
}
compileJava.options.release = 17
@@ -73,4 +82,36 @@ jar {
tasks.withType(Test).configureEach {
useJUnitPlatform()
+ testLogging {
+ events 'passed', 'skipped', 'failed', 'standardOut', 'standardError'
+ }
+}
+
+groovydoc {
+ excludes = ['**/*GrailsPlugin.groovy', '**/Application.groovy']
}
+
+grailsPublish {
+ githubSlug = 'gpc/rendering'
+ license {
+ name = 'Apache-2.0'
+ }
+ title = 'Rendering Plugin'
+ desc = 'Render GSPs as PDFs, JPEGs, GIFs and PNGs'
+ developers = [
+ ldaley : "Luke Daley",
+ graemerocher: "Graeme Rocher",
+ burtbeckwith: "Burt Beckwith",
+ sbglasius : "Søren Berg Glasius",
+ magx2 : "Martin",
+ Ari651 : "Ari Bustamante",
+ billgonemad : "William Malinowski",
+ ZacharyKlein: "Zachary Klein",
+ halfbaked : "Eamonn O'Connell",
+ codeconsole : "Scott Murphy",
+ ]
+}
+
+compileJava.options.release = javaVersion.toInteger()
+
+apply from: layout.projectDirectory.file('gradle/docs-config.gradle')
diff --git a/buildSrc/build.gradle b/buildSrc/build.gradle
new file mode 100644
index 0000000..766bcee
--- /dev/null
+++ b/buildSrc/build.gradle
@@ -0,0 +1,26 @@
+plugins {
+ id 'groovy-gradle-plugin'
+}
+
+file('../gradle.properties').withInputStream {
+ def gradleProperties = new Properties()
+ gradleProperties.load(it)
+ gradleProperties.each { k, v -> ext.set(k, v) }
+
+}
+
+repositories {
+ maven { url = 'https://repo.grails.org/grails/restricted' }
+ maven {
+ url = 'https://repository.apache.org/content/groups/snapshots'
+ content {
+ includeVersionByRegex('org[.]apache[.](grails|groovy).*', '.*', '.*-SNAPSHOT')
+ }
+ }
+}
+
+dependencies {
+ implementation platform("org.apache.grails:grails-bom:$grailsVersion")
+ implementation "org.asciidoctor.jvm.convert:org.asciidoctor.jvm.convert.gradle.plugin:$asciidoctorGradlePluginVersion"
+ implementation 'org.apache.grails:grails-gradle-plugins'
+}
\ No newline at end of file
diff --git a/example/.gitignore b/example/.gitignore
new file mode 100644
index 0000000..dcdf017
--- /dev/null
+++ b/example/.gitignore
@@ -0,0 +1,45 @@
+### Gradle ###
+.gradle
+build/
+!gradle/wrapper/gradle-wrapper.jar
+!**/src/main/**/build/
+!**/src/test/**/build/
+!**/src/integration-test/**/build/
+
+### STS ###
+.apt_generated
+.classpath
+.factorypath
+.project
+.settings
+.springBeans
+.sts4-cache
+bin/
+!**/src/main/**/bin/
+!**/src/test/**/bin/
+!**/src/integration-test/**/bin/
+
+### IntelliJ IDEA ###
+.idea
+*.iws
+*.iml
+*.ipr
+out/
+!**/src/main/**/out/
+!**/src/test/**/out/
+!**/src/integration-test/**/out/
+
+### NetBeans ###
+/nbproject/private/
+/nbbuild/
+/dist/
+/nbdist/
+/.nb-gradle/
+
+### VS Code ###
+.vscode/
+
+### Other ###
+Thumbs.db
+.DS_Store
+target/
diff --git a/example/README.md b/example/README.md
new file mode 100644
index 0000000..d7b8a8f
--- /dev/null
+++ b/example/README.md
@@ -0,0 +1,25 @@
+## Grails 7.0.0-RC2 Documentation
+
+- [User Guide](https://docs.grails.org/7.0.0-RC2/guide/index.html)
+- [API Reference](https://docs.grails.org/7.0.0-RC2/api/index.html)
+- [Grails Guides](https://guides.grails.org/index.html)
+---
+
+## Feature scaffolding documentation
+
+- [Grails Scaffolding documentation](https://docs.grails.org/7.0.0-RC2/guide/scaffolding.html)
+
+## Feature asset-pipeline-grails documentation
+
+- [Grails Asset Pipeline documentation](https://github.com/wondrify/asset-pipeline#readme)
+
+## Feature spring-boot-devtools documentation
+
+- [Grails SpringBoot Developer Tools documentation](https://docs.spring.io/spring-boot/reference/using/devtools.html)
+
+## Feature geb-with-testcontainers documentation
+
+- [Grails Geb Functional Testing for Grails with Testcontainers documentation](https://github.com/apache/grails-geb#readme)
+
+- [https://groovy.apache.org/geb/manual/current/](https://groovy.apache.org/geb/manual/current/)
+
diff --git a/example/build.gradle b/example/build.gradle
new file mode 100644
index 0000000..4facb35
--- /dev/null
+++ b/example/build.gradle
@@ -0,0 +1,96 @@
+buildscript {
+ repositories {
+ mavenCentral()
+ maven {
+ url = 'https://repo.grails.org/grails/restricted'
+ }
+ }
+ dependencies { // Not Published to Gradle Plugin Portal
+ classpath "cloud.wondrify:asset-pipeline-gradle"
+ classpath platform("org.apache.grails:grails-bom:$grailsVersion")
+ classpath "org.apache.grails:grails-data-hibernate5"
+ classpath "org.apache.grails:grails-gradle-plugins"
+ }
+}
+
+plugins {
+ id "war"
+ id "idea"
+ id "eclipse"
+}
+
+// Not Published to Gradle Plugin Portal
+apply plugin: "org.apache.grails.gradle.grails-web"
+apply plugin: "org.apache.grails.gradle.grails-gsp"
+apply plugin: "cloud.wondrify.asset-pipeline"
+
+group = "example"
+
+repositories {
+ mavenCentral()
+ maven {
+ url = 'https://repo.grails.org/grails/restricted'
+ }
+}
+
+dependencies {
+ profile "org.apache.grails.profiles:web"
+ developmentOnly "org.springframework.boot:spring-boot-devtools" // Spring Boot DevTools may cause performance slowdowns or compatibility issues on larger applications
+ testAndDevelopmentOnly "org.webjars.npm:bootstrap"
+ testAndDevelopmentOnly "org.webjars.npm:bootstrap-icons"
+ testAndDevelopmentOnly "org.webjars.npm:jquery"
+ implementation platform("org.apache.grails:grails-bom:$grailsVersion")
+ implementation "org.apache.grails:grails-core"
+ implementation "org.apache.grails:grails-data-hibernate5"
+ implementation "org.apache.grails:grails-databinding"
+ implementation "org.apache.grails:grails-events"
+ implementation "org.apache.grails:grails-gsp"
+ implementation "org.apache.grails:grails-interceptors"
+ implementation "org.apache.grails:grails-layout"
+ implementation "org.apache.grails:grails-logging"
+ implementation "org.apache.grails:grails-rest-transforms"
+ implementation "org.apache.grails:grails-scaffolding"
+ implementation "org.apache.grails:grails-services"
+ implementation "org.apache.grails:grails-url-mappings"
+ implementation "org.apache.grails:grails-web-boot"
+ implementation "org.springframework.boot:spring-boot-autoconfigure"
+ implementation "org.springframework.boot:spring-boot-starter"
+ implementation "org.springframework.boot:spring-boot-starter-actuator"
+ implementation "org.springframework.boot:spring-boot-starter-logging"
+ implementation "org.springframework.boot:spring-boot-starter-tomcat"
+ implementation "org.springframework.boot:spring-boot-starter-validation"
+ console "org.apache.grails:grails-console"
+ runtimeOnly "cloud.wondrify:asset-pipeline-grails"
+ runtimeOnly "com.h2database:h2"
+ runtimeOnly "com.zaxxer:HikariCP"
+ runtimeOnly "org.fusesource.jansi:jansi"
+ integrationTestImplementation testFixtures("org.apache.grails:grails-geb")
+ testImplementation "org.apache.grails:grails-testing-support-datamapping"
+ testImplementation "org.apache.grails:grails-testing-support-web"
+ testImplementation "org.spockframework:spock-core"
+
+ implementation project(':')
+
+}
+
+compileJava.options.release = 17
+
+tasks.withType(Test).configureEach {
+ useJUnitPlatform()
+}
+
+assets {
+ excludes = [
+ 'webjars/jquery/**',
+ 'webjars/bootstrap/**',
+ 'webjars/bootstrap-icons/**'
+ ]
+ includes = [
+ 'webjars/jquery/*/dist/jquery.js',
+ 'webjars/bootstrap/*/dist/js/bootstrap.bundle.js',
+ 'webjars/bootstrap/*/dist/css/bootstrap.css',
+ 'webjars/bootstrap-icons/*/font/bootstrap-icons.css',
+ 'webjars/bootstrap-icons/*/font/fonts/*',
+ ]
+}
+
diff --git a/example/gradle.properties b/example/gradle.properties
new file mode 100644
index 0000000..6d7deba
--- /dev/null
+++ b/example/gradle.properties
@@ -0,0 +1,6 @@
+grailsVersion=7.0.0-RC2
+version=0.1
+org.gradle.caching=true
+org.gradle.daemon=true
+org.gradle.parallel=true
+org.gradle.jvmargs=-Dfile.encoding=UTF-8 -Xmx1024M
diff --git a/example/gradle/wrapper/gradle-wrapper.jar b/example/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..1b33c55
Binary files /dev/null and b/example/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/example/gradle/wrapper/gradle-wrapper.properties b/example/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..d4081da
--- /dev/null
+++ b/example/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,7 @@
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-bin.zip
+networkTimeout=10000
+validateDistributionUrl=true
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
diff --git a/example/gradlew b/example/gradlew
new file mode 100755
index 0000000..23d15a9
--- /dev/null
+++ b/example/gradlew
@@ -0,0 +1,251 @@
+#!/bin/sh
+
+#
+# Copyright © 2015-2021 the original authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# SPDX-License-Identifier: Apache-2.0
+#
+
+##############################################################################
+#
+# Gradle start up script for POSIX generated by Gradle.
+#
+# Important for running:
+#
+# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
+# noncompliant, but you have some other compliant shell such as ksh or
+# bash, then to run this script, type that shell name before the whole
+# command line, like:
+#
+# ksh Gradle
+#
+# Busybox and similar reduced shells will NOT work, because this script
+# requires all of these POSIX shell features:
+# * functions;
+# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
+# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
+# * compound commands having a testable exit status, especially «case»;
+# * various built-in commands including «command», «set», and «ulimit».
+#
+# Important for patching:
+#
+# (2) This script targets any POSIX shell, so it avoids extensions provided
+# by Bash, Ksh, etc; in particular arrays are avoided.
+#
+# The "traditional" practice of packing multiple parameters into a
+# space-separated string is a well documented source of bugs and security
+# problems, so this is (mostly) avoided, by progressively accumulating
+# options in "$@", and eventually passing that to Java.
+#
+# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
+# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
+# see the in-line comments for details.
+#
+# There are tweaks for specific operating systems such as AIX, CygWin,
+# Darwin, MinGW, and NonStop.
+#
+# (3) This script is generated from the Groovy template
+# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
+# within the Gradle project.
+#
+# You can find Gradle at https://github.com/gradle/gradle/.
+#
+##############################################################################
+
+# Attempt to set APP_HOME
+
+# Resolve links: $0 may be a link
+app_path=$0
+
+# Need this for daisy-chained symlinks.
+while
+ APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
+ [ -h "$app_path" ]
+do
+ ls=$( ls -ld "$app_path" )
+ link=${ls#*' -> '}
+ case $link in #(
+ /*) app_path=$link ;; #(
+ *) app_path=$APP_HOME$link ;;
+ esac
+done
+
+# This is normally unused
+# shellcheck disable=SC2034
+APP_BASE_NAME=${0##*/}
+# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
+APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD=maximum
+
+warn () {
+ echo "$*"
+} >&2
+
+die () {
+ echo
+ echo "$*"
+ echo
+ exit 1
+} >&2
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "$( uname )" in #(
+ CYGWIN* ) cygwin=true ;; #(
+ Darwin* ) darwin=true ;; #(
+ MSYS* | MINGW* ) msys=true ;; #(
+ NONSTOP* ) nonstop=true ;;
+esac
+
+CLASSPATH="\\\"\\\""
+
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD=$JAVA_HOME/jre/sh/java
+ else
+ JAVACMD=$JAVA_HOME/bin/java
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD=java
+ if ! command -v java >/dev/null 2>&1
+ then
+ die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+fi
+
+# Increase the maximum file descriptors if we can.
+if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
+ case $MAX_FD in #(
+ max*)
+ # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
+ # shellcheck disable=SC2039,SC3045
+ MAX_FD=$( ulimit -H -n ) ||
+ warn "Could not query maximum file descriptor limit"
+ esac
+ case $MAX_FD in #(
+ '' | soft) :;; #(
+ *)
+ # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
+ # shellcheck disable=SC2039,SC3045
+ ulimit -n "$MAX_FD" ||
+ warn "Could not set maximum file descriptor limit to $MAX_FD"
+ esac
+fi
+
+# Collect all arguments for the java command, stacking in reverse order:
+# * args from the command line
+# * the main class name
+# * -classpath
+# * -D...appname settings
+# * --module-path (only if needed)
+# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
+
+# For Cygwin or MSYS, switch paths to Windows format before running java
+if "$cygwin" || "$msys" ; then
+ APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
+ CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
+
+ JAVACMD=$( cygpath --unix "$JAVACMD" )
+
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ for arg do
+ if
+ case $arg in #(
+ -*) false ;; # don't mess with options #(
+ /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
+ [ -e "$t" ] ;; #(
+ *) false ;;
+ esac
+ then
+ arg=$( cygpath --path --ignore --mixed "$arg" )
+ fi
+ # Roll the args list around exactly as many times as the number of
+ # args, so each arg winds up back in the position where it started, but
+ # possibly modified.
+ #
+ # NB: a `for` loop captures its iteration list before it begins, so
+ # changing the positional parameters here affects neither the number of
+ # iterations, nor the values presented in `arg`.
+ shift # remove old arg
+ set -- "$@" "$arg" # push replacement arg
+ done
+fi
+
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+
+# Collect all arguments for the java command:
+# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
+# and any embedded shellness will be escaped.
+# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
+# treated as '${Hostname}' itself on the command line.
+
+set -- \
+ "-Dorg.gradle.appname=$APP_BASE_NAME" \
+ -classpath "$CLASSPATH" \
+ -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \
+ "$@"
+
+# Stop when "xargs" is not available.
+if ! command -v xargs >/dev/null 2>&1
+then
+ die "xargs is not available"
+fi
+
+# Use "xargs" to parse quoted args.
+#
+# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
+#
+# In Bash we could simply go:
+#
+# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
+# set -- "${ARGS[@]}" "$@"
+#
+# but POSIX shell has neither arrays nor command substitution, so instead we
+# post-process each arg (as a line of input to sed) to backslash-escape any
+# character that might be a shell metacharacter, then use eval to reverse
+# that process (while maintaining the separation between arguments), and wrap
+# the whole thing up as a single "set" statement.
+#
+# This will of course break if any of these variables contains a newline or
+# an unmatched quote.
+#
+
+eval "set -- $(
+ printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
+ xargs -n1 |
+ sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
+ tr '\n' ' '
+ )" '"$@"'
+
+exec "$JAVACMD" "$@"
diff --git a/example/gradlew.bat b/example/gradlew.bat
new file mode 100644
index 0000000..db3a6ac
--- /dev/null
+++ b/example/gradlew.bat
@@ -0,0 +1,94 @@
+@rem
+@rem Copyright 2015 the original author or authors.
+@rem
+@rem Licensed under the Apache License, Version 2.0 (the "License");
+@rem you may not use this file except in compliance with the License.
+@rem You may obtain a copy of the License at
+@rem
+@rem https://www.apache.org/licenses/LICENSE-2.0
+@rem
+@rem Unless required by applicable law or agreed to in writing, software
+@rem distributed under the License is distributed on an "AS IS" BASIS,
+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@rem See the License for the specific language governing permissions and
+@rem limitations under the License.
+@rem
+@rem SPDX-License-Identifier: Apache-2.0
+@rem
+
+@if "%DEBUG%"=="" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%"=="" set DIRNAME=.
+@rem This is normally unused
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Resolve any "." and ".." in APP_HOME to make it shorter.
+for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if %ERRORLEVEL% equ 0 goto execute
+
+echo. 1>&2
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
+echo. 1>&2
+echo Please set the JAVA_HOME variable in your environment to match the 1>&2
+echo location of your Java installation. 1>&2
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto execute
+
+echo. 1>&2
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
+echo. 1>&2
+echo Please set the JAVA_HOME variable in your environment to match the 1>&2
+echo location of your Java installation. 1>&2
+
+goto fail
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=
+
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
+
+:end
+@rem End local scope for the variables with windows NT shell
+if %ERRORLEVEL% equ 0 goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+set EXIT_CODE=%ERRORLEVEL%
+if %EXIT_CODE% equ 0 set EXIT_CODE=1
+if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
+exit /b %EXIT_CODE%
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/web-app/fonts/arial.ttf b/example/grails-app/assets/fonts/arial.ttf
similarity index 100%
rename from web-app/fonts/arial.ttf
rename to example/grails-app/assets/fonts/arial.ttf
diff --git a/example/grails-app/assets/images/advancedgrails.svg b/example/grails-app/assets/images/advancedgrails.svg
new file mode 100644
index 0000000..8b63ec8
--- /dev/null
+++ b/example/grails-app/assets/images/advancedgrails.svg
@@ -0,0 +1,27 @@
+
+
+
+
diff --git a/example/grails-app/assets/images/apple-touch-icon-retina.png b/example/grails-app/assets/images/apple-touch-icon-retina.png
new file mode 100644
index 0000000..d5bc4c0
Binary files /dev/null and b/example/grails-app/assets/images/apple-touch-icon-retina.png differ
diff --git a/example/grails-app/assets/images/apple-touch-icon.png b/example/grails-app/assets/images/apple-touch-icon.png
new file mode 100644
index 0000000..c3681cc
Binary files /dev/null and b/example/grails-app/assets/images/apple-touch-icon.png differ
diff --git a/example/grails-app/assets/images/documentation.svg b/example/grails-app/assets/images/documentation.svg
new file mode 100644
index 0000000..29bc9d5
--- /dev/null
+++ b/example/grails-app/assets/images/documentation.svg
@@ -0,0 +1,19 @@
+
+
+
+
diff --git a/example/grails-app/assets/images/favicon.ico b/example/grails-app/assets/images/favicon.ico
new file mode 100644
index 0000000..76e4b11
Binary files /dev/null and b/example/grails-app/assets/images/favicon.ico differ
diff --git a/example/grails-app/assets/images/grails-cupsonly-logo-white.svg b/example/grails-app/assets/images/grails-cupsonly-logo-white.svg
new file mode 100644
index 0000000..d3fe882
--- /dev/null
+++ b/example/grails-app/assets/images/grails-cupsonly-logo-white.svg
@@ -0,0 +1,26 @@
+
+
diff --git a/example/grails-app/assets/images/grails.png b/example/grails-app/assets/images/grails.png
new file mode 100644
index 0000000..93df8f9
Binary files /dev/null and b/example/grails-app/assets/images/grails.png differ
diff --git a/example/grails-app/assets/images/grails.svg b/example/grails-app/assets/images/grails.svg
new file mode 100644
index 0000000..79f698b
--- /dev/null
+++ b/example/grails-app/assets/images/grails.svg
@@ -0,0 +1,13 @@
+
+
\ No newline at end of file
diff --git a/example/grails-app/assets/images/slack.svg b/example/grails-app/assets/images/slack.svg
new file mode 100644
index 0000000..34fcf4c
--- /dev/null
+++ b/example/grails-app/assets/images/slack.svg
@@ -0,0 +1,18 @@
+
+
\ No newline at end of file
diff --git a/example/grails-app/assets/javascripts/application.js b/example/grails-app/assets/javascripts/application.js
new file mode 100644
index 0000000..c290381
--- /dev/null
+++ b/example/grails-app/assets/javascripts/application.js
@@ -0,0 +1,20 @@
+// This is a manifest file that'll be compiled into application.js.
+//
+// Any JavaScript file within this directory can be referenced here using a relative path.
+//
+// You're free to add application-wide JavaScript to this file, but it's generally better
+// to create separate JavaScript files as needed.
+//
+//= require webjars/jquery/3.7.1/dist/jquery.js
+//= require webjars/bootstrap/5.3.7/dist/js/bootstrap.bundle
+//= require_self
+
+if (typeof jQuery !== 'undefined') {
+ (function($) {
+ $('#spinner').ajaxStart(function() {
+ $(this).fadeIn();
+ }).ajaxStop(function() {
+ $(this).fadeOut();
+ });
+ })(jQuery);
+}
\ No newline at end of file
diff --git a/example/grails-app/assets/stylesheets/application.css b/example/grails-app/assets/stylesheets/application.css
new file mode 100644
index 0000000..ec60949
--- /dev/null
+++ b/example/grails-app/assets/stylesheets/application.css
@@ -0,0 +1,14 @@
+/*
+* This is a manifest file that'll be compiled into application.css, which will include all the files
+* listed below.
+*
+* Any CSS file within this directory can be referenced here using a relative path.
+*
+* You're free to add application-wide styles to this file and they'll appear at the top of the
+* compiled file, but it's generally better to create a new file per style scope.
+*
+*= require webjars/bootstrap/5.3.7/dist/css/bootstrap
+*= require webjars/bootstrap-icons/1.13.1/font/bootstrap-icons
+*= require grails
+*= require_self
+*/
diff --git a/example/grails-app/assets/stylesheets/errors.css b/example/grails-app/assets/stylesheets/errors.css
new file mode 100644
index 0000000..bd71149
--- /dev/null
+++ b/example/grails-app/assets/stylesheets/errors.css
@@ -0,0 +1,90 @@
+.filename {
+ font-style: italic;
+}
+
+.exceptionMessage {
+ margin: 10px;
+ border: 1px solid #000;
+ padding: 5px;
+ background-color: #E9E9E9;
+}
+
+.stack,
+.snippet {
+ margin: 10px 0;
+}
+
+.stack,
+.snippet {
+ border: 1px solid #ccc;
+}
+
+/* error details */
+.error-details {
+ border: 1px solid #FFAAAA;
+ background-color:#FFF3F3;
+ line-height: 1.5;
+ overflow: hidden;
+ padding: 10px 0 5px 25px;
+}
+
+.error-details dt {
+ clear: left;
+ float: left;
+ font-weight: bold;
+ margin-right: 5px;
+}
+
+.error-details dt:after {
+ content: ":";
+}
+
+.error-details dd {
+ display: block;
+}
+
+/* stack trace */
+.stack {
+ padding: 5px;
+ overflow: auto;
+ height: 300px;
+}
+
+/* code snippet */
+.snippet {
+ background-color: #fff;
+ font-family: monospace;
+}
+
+.snippet .line {
+ display: block;
+}
+
+.snippet .lineNumber {
+ background-color: #ddd;
+ color: #999;
+ display: inline-block;
+ margin-right: 5px;
+ padding: 0 3px;
+ text-align: right;
+ width: 3em;
+}
+
+.snippet .error {
+ background-color: #fff3f3;
+ font-weight: bold;
+}
+
+.snippet .error .lineNumber {
+ background-color: #faa;
+ color: #333;
+ font-weight: bold;
+}
+
+.snippet .line:first-child .lineNumber {
+ padding-top: 5px;
+}
+
+.snippet .line:last-child .lineNumber {
+ padding-bottom: 5px;
+}
\ No newline at end of file
diff --git a/example/grails-app/assets/stylesheets/grails.css b/example/grails-app/assets/stylesheets/grails.css
new file mode 100644
index 0000000..2ab6505
--- /dev/null
+++ b/example/grails-app/assets/stylesheets/grails.css
@@ -0,0 +1,77 @@
+table.scaffold tr>td:first-child, tr>th:first-child {
+ padding-left: 1.25em;
+}
+
+table.scaffold tr>td:last-child, tr>th:last-child {
+ padding-right: 1.25em;
+}
+
+table.scaffold th {
+ background-image: linear-gradient(
+ to bottom,
+ #ffffff 0%,
+ #f8f8f8 30%,
+ #eaeaea 70%,
+ #d4d4d4 100%
+ );
+ border-bottom: 2px solid #b3b3b3; /* Adding a subtle shadow effect */
+ box-shadow: 0 2px 3px rgba(0, 0, 0, 0.1); /* Adding a drop shadow */
+}
+
+[data-bs-theme=dark] table.scaffold th {
+ background-image: linear-gradient(
+ to bottom,
+ #4a4a4a 0%,
+ #3e3e3e 30%,
+ #2a2a2a 70%,
+ #1e1e1e 100%
+ );
+ border-bottom: 2px solid #141414; /* Adding a subtle shadow effect */
+ box-shadow: 0 2px 3px rgba(0, 0, 0, 0.3); /* Adding a drop shadow */
+}
+
+table.scaffold thead th {
+ white-space: nowrap;
+}
+
+table.scaffold th a {
+ display: block;
+ text-decoration: none;
+}
+
+table.scaffold th a:link, th a:visited {
+ color: #666666;
+}
+
+table.scaffold th a:hover, th a:focus {
+ color: #333333;
+}
+
+table.scaffold th.sortable a {
+ background-position: right;
+ background-repeat: no-repeat;
+ padding-right: 1.1em;
+}
+
+table.scaffold th {
+ position: relative;
+}
+
+
+table.scaffold th.asc a:after {
+ content: '▲';
+ position: absolute;
+ right: 10px;
+ font-size: 0.8em;
+}
+
+table.scaffold th.desc a:after {
+ content: '▼';
+ position: absolute;
+ right: 10px;
+ font-size: 0.8em;
+}
+
+table.scaffold th:hover {
+ background: #f5f5f5 !important;
+}
\ No newline at end of file
diff --git a/example/grails-app/conf/application.yml b/example/grails-app/conf/application.yml
new file mode 100644
index 0000000..00e7eb3
--- /dev/null
+++ b/example/grails-app/conf/application.yml
@@ -0,0 +1,82 @@
+info:
+ app:
+ name: '@info.app.name@'
+ version: '@info.app.version@'
+ grailsVersion: '@info.app.grailsVersion@'
+grails:
+ views:
+ default:
+ codec: html
+ gsp:
+ encoding: UTF-8
+ htmlcodec: xml
+ codecs:
+ expression: html
+ scriptlet: html
+ taglib: none
+ staticparts: none
+ mime:
+ disable:
+ accept:
+ header:
+ userAgents:
+ - Gecko
+ - WebKit
+ - Presto
+ - Trident
+ types:
+ all: '*/*'
+ atom: application/atom+xml
+ css: text/css
+ csv: text/csv
+ form: application/x-www-form-urlencoded
+ html:
+ - text/html
+ - application/xhtml+xml
+ js: text/javascript
+ json:
+ - application/json
+ - text/json
+ multipartForm: multipart/form-data
+ pdf: application/pdf
+ rss: application/rss+xml
+ text: text/plain
+ hal:
+ - application/hal+json
+ - application/hal+xml
+ xml:
+ - text/xml
+ - application/xml
+ codegen:
+ defaultPackage: example
+ profile: web
+dataSource:
+ driverClassName: org.h2.Driver
+ username: sa
+ password: ''
+ pooled: true
+ jmxExport: true
+environments:
+ development:
+ dataSource:
+ dbCreate: create-drop
+ url: jdbc:h2:mem:devDb;LOCK_TIMEOUT=10000;DB_CLOSE_ON_EXIT=FALSE
+ test:
+ dataSource:
+ dbCreate: update
+ url: jdbc:h2:mem:testDb;LOCK_TIMEOUT=10000;DB_CLOSE_ON_EXIT=FALSE
+ production:
+ dataSource:
+ dbCreate: none
+ url: jdbc:h2:./prodDb;LOCK_TIMEOUT=10000;DB_CLOSE_ON_EXIT=FALSE
+hibernate:
+ cache:
+ queries: false
+ use_second_level_cache: false
+ use_query_cache: false
+---
+server:
+ servlet:
+ context-path: /rendering
+grails:
+ serverURL: http://localhost:8080/rendering
\ No newline at end of file
diff --git a/example/grails-app/conf/logback-spring.xml b/example/grails-app/conf/logback-spring.xml
new file mode 100644
index 0000000..2f198ed
--- /dev/null
+++ b/example/grails-app/conf/logback-spring.xml
@@ -0,0 +1,39 @@
+
+
Below is an inline image
-It's a red dot.
+