From 8f3f85e5023fd57e0e3b570fd983125d4eab00df Mon Sep 17 00:00:00 2001 From: Rahul Shishodia Date: Sun, 18 Jan 2026 11:13:49 +0530 Subject: [PATCH] upgraded grails version to 7 --- .github/workflows/gradle.yml | 95 +++++++ .github/workflows/release.yml | 163 ++++++++++++ build.gradle | 120 ++++----- examples/functional-test-app/build.gradle | 76 ++++++ .../grails-app/conf/application.yml | 119 +++++++++ .../grails-app/conf/logback-spring.xml | 37 +++ .../grails-app/conf/spring/resources.groovy | 8 + .../test/ApplicationController.groovy | 0 .../controllers/test/BookController.groovy | 0 .../controllers/test/UrlMappings.groovy | 0 .../grails-app/domain/test/Book.groovy | 4 + .../grails-app/i18n/messages.properties | 0 .../grails-app/i18n/messages_cs_CZ.properties | 0 .../grails-app/i18n/messages_da.properties | 0 .../grails-app/i18n/messages_de.properties | 0 .../grails-app/i18n/messages_es.properties | 0 .../grails-app/i18n/messages_fr.properties | 0 .../grails-app/i18n/messages_it.properties | 0 .../grails-app/i18n/messages_ja.properties | 0 .../grails-app/i18n/messages_nb.properties | 0 .../grails-app/i18n/messages_nl.properties | 0 .../grails-app/i18n/messages_pl.properties | 0 .../grails-app/i18n/messages_pt_BR.properties | 0 .../grails-app/i18n/messages_pt_PT.properties | 0 .../grails-app/i18n/messages_ru.properties | 0 .../grails-app/i18n/messages_sv.properties | 0 .../grails-app/i18n/messages_th.properties | 0 .../grails-app/i18n/messages_zh_CN.properties | 0 .../grails-app/init/test/Application.groovy | 0 .../grails-app/init/test/BootStrap.groovy | 0 .../grails-app/views/application/index.gson | 0 .../grails-app/views/book/bookDisplay.gson | 0 .../grails-app/views/error.gson | 0 .../grails-app/views/errors/_errors.gson | 0 .../grails-app/views/notFound.gson | 0 .../grails-app/views/object/_object.gson | 0 .../groovy/test/BookControllerSpec.groovy | 11 +- .../BookControllerAllowedMethodsSpec.groovy | 8 +- examples/plugin-test-app/build.gradle | 46 ++++ .../grails-app/conf/application.yml | 91 +++++++ .../grails-app/conf/logback-spring.xml | 19 ++ .../grails-app/conf/spring/resources.groovy | 3 + .../cacheheaders/TestController.groovy | 2 +- .../plugins/cacheheaders/Application.groovy | 2 - .../plugins/cacheheaders/BootStrap.groovy | 17 ++ .../cacheheaders/CacheMethodsSpec.groovy | 9 +- .../CacheHeadersServiceSpec.groovy | 6 +- functional-test-app/build.gradle | 66 ----- functional-test-app/gradle.properties | 3 - .../gradle/wrapper/gradle-wrapper.jar | Bin 52818 -> 0 bytes .../gradle/wrapper/gradle-wrapper.properties | 6 - functional-test-app/gradlew | 160 ------------ functional-test-app/gradlew.bat | 90 ------- .../grails-app/conf/application.yml | 116 --------- .../grails-app/conf/logback.groovy | 36 --- .../grails-app/conf/spring/resources.groovy | 3 - functional-test-app/grails-wrapper.jar | Bin 5463 -> 0 bytes functional-test-app/grailsw | 151 ----------- functional-test-app/grailsw.bat | 89 ------- functional-test-app/settings.gradle | 1 - gradle.properties | 13 +- gradle/cache-headers.gradle | 16 -- gradle/wrapper/gradle-wrapper.properties | 2 +- grails-app/conf/application.yml | 89 ------- plugin/build.gradle | 51 ++++ .../cacheheaders/CacheHeadersService.groovy | 3 + {src => plugin/src}/docs/asciidoc/index.adoc | 2 +- .../CacheHeadersGrailsPlugin.groovy | 16 +- .../cacheheaders/CacheHeadersTrait.groovy | 0 .../WithCacheHeadersDelegate.groovy | 0 .../CacheHeadersServiceSpec.groovy | 246 ++++++++++++++++++ settings.gradle | 34 ++- version.txt | 1 - 73 files changed, 1092 insertions(+), 938 deletions(-) create mode 100644 .github/workflows/gradle.yml create mode 100644 .github/workflows/release.yml create mode 100644 examples/functional-test-app/build.gradle create mode 100644 examples/functional-test-app/grails-app/conf/application.yml create mode 100644 examples/functional-test-app/grails-app/conf/logback-spring.xml create mode 100644 examples/functional-test-app/grails-app/conf/spring/resources.groovy rename {functional-test-app => examples/functional-test-app}/grails-app/controllers/test/ApplicationController.groovy (100%) rename {functional-test-app => examples/functional-test-app}/grails-app/controllers/test/BookController.groovy (100%) rename {functional-test-app => examples/functional-test-app}/grails-app/controllers/test/UrlMappings.groovy (100%) rename {functional-test-app => examples/functional-test-app}/grails-app/domain/test/Book.groovy (62%) rename {functional-test-app => examples/functional-test-app}/grails-app/i18n/messages.properties (100%) rename {functional-test-app => examples/functional-test-app}/grails-app/i18n/messages_cs_CZ.properties (100%) rename {functional-test-app => examples/functional-test-app}/grails-app/i18n/messages_da.properties (100%) rename {functional-test-app => examples/functional-test-app}/grails-app/i18n/messages_de.properties (100%) rename {functional-test-app => examples/functional-test-app}/grails-app/i18n/messages_es.properties (100%) rename {functional-test-app => examples/functional-test-app}/grails-app/i18n/messages_fr.properties (100%) rename {functional-test-app => examples/functional-test-app}/grails-app/i18n/messages_it.properties (100%) rename {functional-test-app => examples/functional-test-app}/grails-app/i18n/messages_ja.properties (100%) rename {functional-test-app => examples/functional-test-app}/grails-app/i18n/messages_nb.properties (100%) rename {functional-test-app => examples/functional-test-app}/grails-app/i18n/messages_nl.properties (100%) rename {functional-test-app => examples/functional-test-app}/grails-app/i18n/messages_pl.properties (100%) rename {functional-test-app => examples/functional-test-app}/grails-app/i18n/messages_pt_BR.properties (100%) rename {functional-test-app => examples/functional-test-app}/grails-app/i18n/messages_pt_PT.properties (100%) rename {functional-test-app => examples/functional-test-app}/grails-app/i18n/messages_ru.properties (100%) rename {functional-test-app => examples/functional-test-app}/grails-app/i18n/messages_sv.properties (100%) rename {functional-test-app => examples/functional-test-app}/grails-app/i18n/messages_th.properties (100%) rename {functional-test-app => examples/functional-test-app}/grails-app/i18n/messages_zh_CN.properties (100%) rename {functional-test-app => examples/functional-test-app}/grails-app/init/test/Application.groovy (100%) rename {functional-test-app => examples/functional-test-app}/grails-app/init/test/BootStrap.groovy (100%) rename {functional-test-app => examples/functional-test-app}/grails-app/views/application/index.gson (100%) rename {functional-test-app => examples/functional-test-app}/grails-app/views/book/bookDisplay.gson (100%) rename {functional-test-app => examples/functional-test-app}/grails-app/views/error.gson (100%) rename {functional-test-app => examples/functional-test-app}/grails-app/views/errors/_errors.gson (100%) rename {functional-test-app => examples/functional-test-app}/grails-app/views/notFound.gson (100%) rename {functional-test-app => examples/functional-test-app}/grails-app/views/object/_object.gson (100%) rename {functional-test-app => examples/functional-test-app}/src/integration-test/groovy/test/BookControllerSpec.groovy (71%) rename {functional-test-app => examples/functional-test-app}/src/test/groovy/test/BookControllerAllowedMethodsSpec.groovy (68%) create mode 100644 examples/plugin-test-app/build.gradle create mode 100644 examples/plugin-test-app/grails-app/conf/application.yml create mode 100644 examples/plugin-test-app/grails-app/conf/logback-spring.xml create mode 100644 examples/plugin-test-app/grails-app/conf/spring/resources.groovy rename {grails-app/controllers/com/grailsrocks => examples/plugin-test-app/grails-app/controllers/grails/plugins}/cacheheaders/TestController.groovy (94%) rename {grails-app => examples/plugin-test-app/grails-app}/init/grails/plugins/cacheheaders/Application.groovy (84%) create mode 100644 examples/plugin-test-app/grails-app/init/grails/plugins/cacheheaders/BootStrap.groovy rename {src/integration-test/groovy => examples/plugin-test-app/src/integration-test/groovy/grails/plugins}/cacheheaders/CacheMethodsSpec.groovy (92%) rename {src => examples/plugin-test-app/src}/test/groovy/grails/plugins/cacheheaders/CacheHeadersServiceSpec.groovy (96%) delete mode 100644 functional-test-app/build.gradle delete mode 100644 functional-test-app/gradle.properties delete mode 100644 functional-test-app/gradle/wrapper/gradle-wrapper.jar delete mode 100644 functional-test-app/gradle/wrapper/gradle-wrapper.properties delete mode 100755 functional-test-app/gradlew delete mode 100755 functional-test-app/gradlew.bat delete mode 100644 functional-test-app/grails-app/conf/application.yml delete mode 100644 functional-test-app/grails-app/conf/logback.groovy delete mode 100644 functional-test-app/grails-app/conf/spring/resources.groovy delete mode 100644 functional-test-app/grails-wrapper.jar delete mode 100755 functional-test-app/grailsw delete mode 100755 functional-test-app/grailsw.bat delete mode 100644 functional-test-app/settings.gradle delete mode 100644 gradle/cache-headers.gradle delete mode 100644 grails-app/conf/application.yml create mode 100644 plugin/build.gradle rename {grails-app => plugin/grails-app}/services/grails/plugins/cacheheaders/CacheHeadersService.groovy (99%) rename {src => plugin/src}/docs/asciidoc/index.adoc (99%) rename {src => plugin/src}/main/groovy/grails/plugins/cacheheaders/CacheHeadersGrailsPlugin.groovy (77%) rename {src => plugin/src}/main/groovy/grails/plugins/cacheheaders/CacheHeadersTrait.groovy (100%) rename {src => plugin/src}/main/groovy/grails/plugins/cacheheaders/WithCacheHeadersDelegate.groovy (100%) create mode 100644 plugin/src/test/groovy/grails/plugins/cacheheaders/CacheHeadersServiceSpec.groovy delete mode 100644 version.txt diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml new file mode 100644 index 0000000..c7f57d3 --- /dev/null +++ b/.github/workflows/gradle.yml @@ -0,0 +1,95 @@ +name: "Java CI" +on: + push: + branches: + - '[5-9].[0-9].x' + pull_request: + branches: + - '[5-9].[0-9].x' + workflow_dispatch: +env: + GIT_USER_NAME: 'grails-build' + GIT_USER_EMAIL: 'grails-build@users.noreply.github.com' +jobs: + build: + runs-on: ubuntu-latest + strategy: + matrix: + java: ['17', '21'] + steps: + - name: "📥 Checkout the repository" + uses: actions/checkout@v4 + - name: "☕️ Setup JDK" + uses: actions/setup-java@v4 + with: + distribution: 'liberica' + java-version: ${{ matrix.java }} + - name: "🐘 Setup Gradle" + uses: gradle/actions/setup-gradle@v4 + - name: "🔨 Run Base Tests" + env: + DEVELOCITY_ACCESS_KEY: ${{ secrets.DEVELOCITY_ACCESS_KEY }} + DEVELOCITY_BUILD_CACHE_NODE_USER: ${{ secrets.DEVELOCITY_BUILD_CACHE_NODE_USER }} + DEVELOCITY_BUILD_CACHE_NODE_KEY: ${{ secrets.DEVELOCITY_BUILD_CACHE_NODE_KEY }} + run: ./gradlew check --continue + - name: "☄️ Upload Base Tests Results - cache-headers" + if: ${{ always() }} + uses: actions/upload-artifact@v4 + with: + name: testreport-cache-headers-${{ matrix.java }} + path: examples/plugin/build/reports/tests + - name: "☄️ Upload Base Tests Results - standalone app for plugin" + if: ${{ always() }} + uses: actions/upload-artifact@v4 + with: + name: testreport-plugin-test-app-${{ matrix.java }} + path: examples/plugin-test-app/build/reports/tests + - name: "☄️ Upload Base Tests Results - functional app for plugin" + if: ${{ always() }} + uses: actions/upload-artifact@v4 + with: + name: testreport-functional-test-app-${{ matrix.java }} + path: examples/functional-test-app/build/reports/tests + publish: + if: github.event_name == 'push' + needs: build + runs-on: ubuntu-latest + permissions: + contents: read + steps: + - name: "📥 Checkout the repository" + uses: actions/checkout@v4 + - name: "☕️ Setup JDK" + uses: actions/setup-java@v4 + with: + distribution: 'liberica' + java-version: '17' + - name: "🐘 Setup Gradle" + uses: gradle/actions/setup-gradle@v4 + - name: "📤 Publish to Snapshot (repo.grails.org)" + env: + GRAILS_PUBLISH_RELEASE: 'false' + DEVELOCITY_ACCESS_KEY: ${{ secrets.DEVELOCITY_ACCESS_KEY }} + MAVEN_PUBLISH_USERNAME: ${{ secrets.MAVEN_PUBLISH_USERNAME }} + MAVEN_PUBLISH_PASSWORD: ${{ secrets.MAVEN_PUBLISH_PASSWORD }} + MAVEN_PUBLISH_URL: ${{ secrets.MAVEN_PUBLISH_SNAPSHOT_URL }} + working-directory: ./plugin + run: ../gradlew publish + - name: "📜 Generate Documentation" + if: success() + env: + DEVELOCITY_ACCESS_KEY: ${{ secrets.DEVELOCITY_ACCESS_KEY }} + working-directory: ./plugin + run: ../gradlew docs + - name: "🚀 Publish to Github Pages" + if: success() + uses: grails/github-pages-deploy-action@grails + env: + SKIP_SNAPSHOT: ${{ contains(needs.publish.outputs.release_version, 'M') }} + TARGET_REPOSITORY: ${{ github.repository }} + GH_TOKEN: ${{ secrets.GH_TOKEN }} + BRANCH: gh-pages + FOLDER: plugin/build/docs + DOC_FOLDER: gh-pages + COMMIT_EMAIL: ${{ env.GIT_USER_EMAIL }} + COMMIT_NAME: ${{ env.GIT_USER_NAME }} \ No newline at end of file diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..8a8bc1f --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,163 @@ +name: "Release" +on: + release: + types: [published] +env: + GIT_USER_NAME: 'grails-build' + GIT_USER_EMAIL: 'grails-build@users.noreply.github.com' +jobs: + publish: + permissions: + contents: write # to create release + issues: write # to modify milestones + runs-on: ubuntu-latest + outputs: + release_version: ${{ steps.release_version.outputs.value }} + target_branch: ${{ steps.extract_branch.outputs.value }} + extract_repository_name: ${{ steps.extract_repository_name.outputs.repository_name }} + env: + GIT_USER_NAME: 'grails-build' + GIT_USER_EMAIL: 'grails-build@users.noreply.github.com' + steps: + - name: "📥 Checkout the repository" + uses: actions/checkout@v4 + with: + token: ${{ secrets.GH_TOKEN }} + - name: "Extract repository name" + id: extract_repository_name + run: | + echo "repository_name=${GITHUB_REPOSITORY##*/}" >> $GITHUB_OUTPUT + - name: "☕️ Setup JDK" + uses: actions/setup-java@v4 + with: + distribution: 'temurin' + java-version: '17' + - name: "🐘 Setup Gradle" + uses: gradle/actions/setup-gradle@v4 + - name: "📝 Store the target branch" + id: extract_branch + run: | + echo "Determining Target Branch" + TARGET_BRANCH=${GITHUB_REF#refs/heads/} + echo $TARGET_BRANCH + echo "value=${TARGET_BRANCH}" >> $GITHUB_OUTPUT + - name: "📝Set the current release version" + id: release_version + run: echo "value=${GITHUB_REF:11}" >> $GITHUB_OUTPUT + - name: "⚙️ Run pre-release" + uses: grails/github-actions/pre-release@main + with: + token: ${{ secrets.GITHUB_TOKEN }} + - 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" + if: success() + id: assemble + env: + DEVELOCITY_BUILD_CACHE_NODE_USER: ${{ secrets.DEVELOCITY_BUILD_CACHE_NODE_USER }} + DEVELOCITY_BUILD_CACHE_NODE_KEY: ${{ secrets.DEVELOCITY_BUILD_CACHE_NODE_KEY }} + run: ./gradlew -U assemble -Psigning.secretKeyRingFile=${{ github.workspace }}/secring.gpg -Psigning.keyId=${{ secrets.SIGNING_KEY }} + - name: "🚀 Publish to Maven Central" + id: publish + env: + GRAILS_PUBLISH_RELEASE: 'true' + NEXUS_PUBLISH_USERNAME: ${{ secrets.NEXUS_PUBLISH_USERNAME }} + NEXUS_PUBLISH_PASSWORD: ${{ secrets.NEXUS_PUBLISH_PASSWORD }} + NEXUS_PUBLISH_URL: ${{ secrets.NEXUS_PUBLISH_RELEASE_URL }} + NEXUS_PUBLISH_STAGING_PROFILE_ID: ${{ secrets.NEXUS_PUBLISH_STAGING_PROFILE_ID }} + 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 }} + DEVELOCITY_BUILD_CACHE_NODE_USER: ${{ secrets.DEVELOCITY_BUILD_CACHE_NODE_USER }} + DEVELOCITY_BUILD_CACHE_NODE_KEY: ${{ secrets.DEVELOCITY_BUILD_CACHE_NODE_KEY }} + run: > + ./gradlew + -Psigning.keyId=${{ secrets.SIGNING_KEY }} + -Psigning.secretKeyRingFile=${{ github.workspace }}/secring.gpg + publishToSonatype + closeSonatypeStagingRepository + release: + needs: publish + runs-on: ubuntu-latest + permissions: + contents: read + steps: + - name: "📥 Checkout repository" + uses: actions/checkout@v4 + - name: "☕️ Setup JDK" + uses: actions/setup-java@v4 + with: + distribution: liberica + java-version: 17 + - name: "📥 Checkout repository" + uses: actions/checkout@v4 + with: + token: ${{ secrets.GH_TOKEN }} + ref: v${{ needs.publish.outputs.release_version }} + - name: "🐘 Setup Gradle" + uses: gradle/actions/setup-gradle@v4 + with: + develocity-access-key: ${{ secrets.DEVELOCITY_ACCESS_KEY }} + - name: "🏆 Release staging repository" + env: + GRAILS_PUBLISH_RELEASE: 'true' + DEVELOCITY_BUILD_CACHE_NODE_USER: ${{ secrets.DEVELOCITY_BUILD_CACHE_NODE_USER }} + DEVELOCITY_BUILD_CACHE_NODE_KEY: ${{ secrets.DEVELOCITY_BUILD_CACHE_NODE_KEY }} + NEXUS_PUBLISH_USERNAME: ${{ secrets.NEXUS_PUBLISH_USERNAME }} + NEXUS_PUBLISH_PASSWORD: ${{ secrets.NEXUS_PUBLISH_PASSWORD }} + NEXUS_PUBLISH_URL: ${{ secrets.NEXUS_PUBLISH_RELEASE_URL }} + NEXUS_PUBLISH_STAGING_PROFILE_ID: ${{ secrets.NEXUS_PUBLISH_STAGING_PROFILE_ID }} + NEXUS_PUBLISH_DESCRIPTION: '${{ needs.publish.outputs.extract_repository_name }}:${{ needs.publish.outputs.release_version }}' + run: > + ./gradlew + findSonatypeStagingRepository + releaseSonatypeStagingRepository + - name: "⚙️Run post-release" + if: success() + uses: grails/github-actions/post-release@main + with: + token: ${{ secrets.GITHUB_TOKEN }} + env: + SNAPSHOT_SUFFIX: -SNAPSHOT + docs: + needs: publish + runs-on: ubuntu-latest + permissions: + contents: write + steps: + - name: "📥 Checkout the repository" + uses: actions/checkout@v4 + with: + token: ${{ secrets.GH_TOKEN }} + ref: v${{ needs.publish.outputs.release_version }} + - name: "☕️ Setup JDK" + uses: actions/setup-java@v4 + with: + distribution: 'liberica' + java-version: '17' + - name: "🐘 Setup Gradle" + uses: gradle/actions/setup-gradle@v4 + - name: "📜 Generate Documentation" + env: + DEVELOCITY_BUILD_CACHE_NODE_USER: ${{ secrets.DEVELOCITY_BUILD_CACHE_NODE_USER }} + DEVELOCITY_BUILD_CACHE_NODE_KEY: ${{ secrets.DEVELOCITY_BUILD_CACHE_NODE_KEY }} + working-directory: ./plugin + run: ../gradlew docs + - name: "🚀 Publish to Github Pages" + if: success() + uses: grails/github-pages-deploy-action@v2 + env: + SKIP_SNAPSHOT: ${{ contains(needs.publish.outputs.release_version, 'M') }} + # if multiple releases are being done, this is the last branch - 1 version + #SKIP_LATEST: ${{ !startsWith(needs.publish.outputs.target_branch, '6.2') }} + TARGET_REPOSITORY: ${{ github.repository }} + GH_TOKEN: ${{ secrets.GH_TOKEN }} + BRANCH: gh-pages + FOLDER: plugin/build/docs + DOC_FOLDER: gh-pages + COMMIT_EMAIL: ${{ env.GIT_USER_EMAIL }} + COMMIT_NAME: ${{ env.GIT_USER_NAME }} + VERSION: ${{ needs.publish.outputs.release_version }} \ No newline at end of file diff --git a/build.gradle b/build.gradle index 6b05bf8..5c64a13 100644 --- a/build.gradle +++ b/build.gradle @@ -1,94 +1,62 @@ buildscript { repositories { - mavenLocal() - maven { url "https://repo.grails.org/grails/core" } + maven { + url = 'https://repository.apache.org/content/groups/snapshots' + content { + includeVersionByRegex 'org[.]apache[.]((grails)|(groovy)).*', '.*', '.*-SNAPSHOT' + } + } + maven { url = 'https://repo.grails.org/grails/restricted' } } dependencies { - classpath "org.grails:grails-gradle-plugin:$grailsVersion" - classpath "org.asciidoctor:asciidoctor-gradle-plugin:$asciidoctorGradlePluginVersion" - classpath "org.jfrog.buildinfo:build-info-extractor-gradle:4.4.0" - + classpath platform("org.apache.grails:grails-bom:$grailsVersion") + classpath "org.apache.grails:grails-gradle-plugins" + classpath 'org.apache.grails.gradle:grails-publish' } } -def versionTxt = file('version.txt') -version versionTxt.exists() ? versionTxt.text.trim() : '0.1' -group "org.grails.plugins" - -apply plugin:"eclipse" -apply plugin:"idea" -apply plugin:"org.grails.grails-plugin" -apply plugin:"org.grails.grails-plugin-publish" -apply from: "${rootProject.projectDir}/gradle/docs.gradle" -apply plugin: 'org.asciidoctor.convert' -apply plugin: "com.jfrog.artifactory" -repositories { - mavenLocal() - maven { url "https://repo.grails.org/grails/core" } +allprojects { + repositories { + mavenCentral() + maven { + url = 'https://repository.apache.org/content/groups/snapshots' + content { + includeVersionByRegex 'org[.]apache[.]((grails)|(groovy)).*', '.*', '.*-SNAPSHOT' + } + } + maven { url = 'https://repo.grails.org/grails/restricted' } +// mavenLocal() // for local testing, do not commit uncommented + } } -dependencies { - provided "org.springframework.boot:spring-boot-starter-logging" - provided "org.springframework.boot:spring-boot-starter-actuator" - provided "org.springframework.boot:spring-boot-autoconfigure" - provided "org.springframework.boot:spring-boot-starter-tomcat" - - provided "org.grails:grails-web-boot" - provided "org.grails:grails-dependencies" - - testCompile "org.grails:grails-plugin-testing" +version projectVersion +group "org.apache.grails" - console "org.grails:grails-console" - - profile "org.grails.profiles:web-plugin" -} +subprojects { subproject -> + subproject.version = rootProject.version + subproject.group = rootProject.group -bootRun { - jvmArgs('-Dspring.output.ansi.enabled=always') - addResources = true -} -// enable if you wish to package this plugin as a standalone application -bootRepackage.enabled = false -grailsPublish { - user = System.getenv('BINTRAY_USER') ?: project.bintrayUser - key = System.getenv('BINTRAY_KEY') ?: project.bintrayKey - portalUser = System.getenv('GRAILS_PORTAL_USER') ?: project.grailsPortalUser - portalPassword = System.getenv('GRAILS_PORTAL_PASSWORD') ?: project.grailsPortalPassword - repo = 'plugins' - userOrg = "grails" - githubSlug = 'grails-plugins/cache-headers' - license { - name = 'Apache-2.0' + tasks.withType(Test).configureEach { + useJUnitPlatform() } - title = 'Grails Cache Headers Plugin' - desc = 'Improve your application performance with browser caching, with easy ways to set caching headers\n' + - 'in controller responses' - developers = [sdelamo: 'Sergio del Amo'] -} + tasks.matching { it.name == "integrationTest" && it instanceof Test }.configureEach { + shouldRunAfter tasks.test + useJUnitPlatform() + } -artifactory { - contextUrl = 'http://oss.jfrog.org' - publish { - repository { - repoKey = 'oss-snapshot-local' - username = System.getenv("BINTRAY_USER") ?: project.bintrayUser - password = System.getenv("BINTRAY_KEY") ?: project.bintrayKey - } - defaults { - publications('maven') + if (subproject.name == 'cache-headers') { + apply plugin: "org.apache.grails.gradle.grails-publish" + grailsPublish { + githubSlug = 'grails-plugins/cache-headers' + license { + name = 'Apache-2.0' + } + title = 'Grails Cache Headers Plugin' + desc = 'Improve your application performance with browser caching, with easy ways to set caching headers\n' + + 'in controller responses' + developers = [sdelamo: 'Sergio del Amo', rahulshishodia: 'Rahul Shishodia'] } } } -task docs { - dependsOn 'asciidoctor' -} - -jar { - exclude "*TestController**" -} - - -apply from: "${rootProject.projectDir}/gradle/integrationTestVerbose.gradle" -apply from: "${rootProject.projectDir}/gradle/testVerbose.gradle" \ No newline at end of file diff --git a/examples/functional-test-app/build.gradle b/examples/functional-test-app/build.gradle new file mode 100644 index 0000000..b44c8da --- /dev/null +++ b/examples/functional-test-app/build.gradle @@ -0,0 +1,76 @@ +buildscript { + repositories { + mavenLocal() + maven { url "https://repo.grails.org/grails/restricted" } + } + dependencies { + classpath platform("org.apache.grails:grails-bom:$grailsVersion") + classpath "org.apache.grails:grails-gradle-plugins" + classpath "org.apache.grails:grails-data-hibernate5" + } +} + +plugins { + id 'eclipse' + id 'idea' + id 'war' +} + +apply plugin: "org.apache.grails.gradle.grails-web" +apply plugin: "org.apache.grails.gradle.grails-gson" + +repositories { + mavenLocal() + maven { url "https://repo.grails.org/grails/core" } +} + +grails { + plugins { + implementation project(':cache-headers') + } +} + +dependencies { + implementation platform("org.apache.grails:grails-bom:$grailsVersion") + + implementation "org.springframework.boot:spring-boot-starter-logging" + implementation "org.springframework.boot:spring-boot-autoconfigure" + implementation "org.springframework.boot:spring-boot-starter-actuator" + implementation "org.springframework.boot:spring-boot-starter-tomcat" + + implementation "org.apache.grails:grails-core" + implementation "org.apache.grails:grails-url-mappings" + implementation "org.apache.grails:grails-codecs" + implementation "org.apache.grails:grails-rest-transforms" + implementation "org.apache.grails:grails-interceptors" + implementation "org.apache.grails:grails-services" + implementation "org.apache.grails:grails-datasource" + implementation "org.apache.grails:grails-databinding" + implementation "org.apache.grails:grails-async" + implementation "org.apache.grails:grails-web-boot" + implementation "org.apache.grails:grails-logging" + implementation "org.apache.grails:grails-views-gson" + implementation "org.apache.grails:grails-data-mongodb-gson-templates" + implementation "org.apache.grails:grails-cache" + implementation "org.apache.grails:grails-data-hibernate5" + implementation 'org.hibernate:hibernate-ehcache:5.6.15.Final' + + console "org.grails:grails-console" + + profile "org.grails.profiles:rest-api" + runtimeOnly "com.h2database:h2" + + testImplementation "org.apache.grails:grails-testing-support-web" + testImplementation "org.apache.grails:grails-testing-support-views-gson" + testImplementation "org.apache.grails:grails-geb" + testImplementation "org.seleniumhq.selenium:htmlunit-driver:4.13.0" + testImplementation 'org.htmlunit:htmlunit:4.21.0' +} + +apply from: "${rootDir}/gradle/integrationTestVerbose.gradle" +apply from: "${rootDir}/gradle/testVerbose.gradle" + +bootRun { + jvmArgs('-Dspring.output.ansi.enabled=always') + sourceResources sourceSets.main +} diff --git a/examples/functional-test-app/grails-app/conf/application.yml b/examples/functional-test-app/grails-app/conf/application.yml new file mode 100644 index 0000000..cdcb4bd --- /dev/null +++ b/examples/functional-test-app/grails-app/conf/application.yml @@ -0,0 +1,119 @@ +--- +grails: + profile: rest-api + codegen: + defaultPackage: test + spring: + transactionManagement: + proxies: false + gorm: + # Whether to autowire entities. + # Disabled by default for performance reasons. + autowire: false + reactor: + # Whether to translate GORM events into Reactor events + # Disabled by default for performance reasons + events: false +info: + app: + name: '@info.app.name@' + version: '@info.app.version@' + grailsVersion: '@info.app.grailsVersion@' +spring: + main: + banner-mode: "off" + groovy: + template: + check-template-location: false + +# Spring Actuator Endpoints are Disabled by Default +management: + endpoints: + enabled-by-default: false + jmx: + exposure: + include: "*" + +--- +grails: + mime: + disable: + accept: + header: + userAgents: + - Gecko + - WebKit + - Presto + - Trident + types: + json: + - application/json + - text/json + hal: + - application/hal+json + - application/hal+xml + xml: + - text/xml + - application/xml + atom: application/atom+xml + css: text/css + csv: text/csv + js: text/javascript + rss: application/rss+xml + text: text/plain + all: '*/*' + urlmapping: + cache: + maxsize: 1000 + controllers: + defaultScope: singleton + converters: + encoding: UTF-8 + +--- +hibernate: + cache: + queries: false + use_second_level_cache: true + use_query_cache: false + region.factory_class: org.hibernate.cache.ehcache.SingletonEhCacheRegionFactory + +dataSource: + pooled: true + jmxExport: true + driverClassName: org.h2.Driver + dialect: org.hibernate.dialect.H2Dialect + username: sa + password: '' + +environments: + development: + dataSource: + dbCreate: create-drop + url: jdbc:h2:mem:testDb;DB_CLOSE_DELAY=-1;LOCK_TIMEOUT=10000;DB_CLOSE_ON_EXIT=FALSE + test: + dataSource: + dbCreate: update + url: jdbc:h2:mem:testDb;DB_CLOSE_DELAY=-1;LOCK_TIMEOUT=10000;DB_CLOSE_ON_EXIT=FALSE + production: + dataSource: + dbCreate: none + url: jdbc:h2:./prodDb;LOCK_TIMEOUT=10000;DB_CLOSE_ON_EXIT=FALSE + properties: + jmxEnabled: true + initialSize: 5 + maxActive: 50 + minIdle: 5 + maxIdle: 25 + maxWait: 10000 + maxAge: 600000 + timeBetweenEvictionRunsMillis: 5000 + minEvictableIdleTimeMillis: 60000 + validationQuery: SELECT 1 + validationQueryTimeout: 3 + validationInterval: 15000 + testOnBorrow: true + testWhileIdle: true + testOnReturn: false + jdbcInterceptors: ConnectionState + defaultTransactionIsolation: 2 # TRANSACTION_READ_COMMITTED diff --git a/examples/functional-test-app/grails-app/conf/logback-spring.xml b/examples/functional-test-app/grails-app/conf/logback-spring.xml new file mode 100644 index 0000000..5913114 --- /dev/null +++ b/examples/functional-test-app/grails-app/conf/logback-spring.xml @@ -0,0 +1,37 @@ + + + + + + + + + + UTF-8 + + %clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(%5p) %clr(---){faint} [%15.15t] %clr(%-40.40logger{39}){cyan} : %m%n%wex + + + + + + + ${LOG_DIR:-build}/stacktrace.log + true + + %level %logger - %msg%n + + + + + + + + + + + + + diff --git a/examples/functional-test-app/grails-app/conf/spring/resources.groovy b/examples/functional-test-app/grails-app/conf/spring/resources.groovy new file mode 100644 index 0000000..2a54949 --- /dev/null +++ b/examples/functional-test-app/grails-app/conf/spring/resources.groovy @@ -0,0 +1,8 @@ +package spring + +import grails.plugin.json.view.mvc.JsonViewResolver + +// Place your Spring DSL code here +beans = { + jsonSmartViewResolver(JsonViewResolver) +} diff --git a/functional-test-app/grails-app/controllers/test/ApplicationController.groovy b/examples/functional-test-app/grails-app/controllers/test/ApplicationController.groovy similarity index 100% rename from functional-test-app/grails-app/controllers/test/ApplicationController.groovy rename to examples/functional-test-app/grails-app/controllers/test/ApplicationController.groovy diff --git a/functional-test-app/grails-app/controllers/test/BookController.groovy b/examples/functional-test-app/grails-app/controllers/test/BookController.groovy similarity index 100% rename from functional-test-app/grails-app/controllers/test/BookController.groovy rename to examples/functional-test-app/grails-app/controllers/test/BookController.groovy diff --git a/functional-test-app/grails-app/controllers/test/UrlMappings.groovy b/examples/functional-test-app/grails-app/controllers/test/UrlMappings.groovy similarity index 100% rename from functional-test-app/grails-app/controllers/test/UrlMappings.groovy rename to examples/functional-test-app/grails-app/controllers/test/UrlMappings.groovy diff --git a/functional-test-app/grails-app/domain/test/Book.groovy b/examples/functional-test-app/grails-app/domain/test/Book.groovy similarity index 62% rename from functional-test-app/grails-app/domain/test/Book.groovy rename to examples/functional-test-app/grails-app/domain/test/Book.groovy index 70fa871..af533c1 100644 --- a/functional-test-app/grails-app/domain/test/Book.groovy +++ b/examples/functional-test-app/grails-app/domain/test/Book.groovy @@ -4,4 +4,8 @@ class Book { String name Date dateCreated Date lastUpdated + + static mapping = { + table 'books' + } } \ No newline at end of file diff --git a/functional-test-app/grails-app/i18n/messages.properties b/examples/functional-test-app/grails-app/i18n/messages.properties similarity index 100% rename from functional-test-app/grails-app/i18n/messages.properties rename to examples/functional-test-app/grails-app/i18n/messages.properties diff --git a/functional-test-app/grails-app/i18n/messages_cs_CZ.properties b/examples/functional-test-app/grails-app/i18n/messages_cs_CZ.properties similarity index 100% rename from functional-test-app/grails-app/i18n/messages_cs_CZ.properties rename to examples/functional-test-app/grails-app/i18n/messages_cs_CZ.properties diff --git a/functional-test-app/grails-app/i18n/messages_da.properties b/examples/functional-test-app/grails-app/i18n/messages_da.properties similarity index 100% rename from functional-test-app/grails-app/i18n/messages_da.properties rename to examples/functional-test-app/grails-app/i18n/messages_da.properties diff --git a/functional-test-app/grails-app/i18n/messages_de.properties b/examples/functional-test-app/grails-app/i18n/messages_de.properties similarity index 100% rename from functional-test-app/grails-app/i18n/messages_de.properties rename to examples/functional-test-app/grails-app/i18n/messages_de.properties diff --git a/functional-test-app/grails-app/i18n/messages_es.properties b/examples/functional-test-app/grails-app/i18n/messages_es.properties similarity index 100% rename from functional-test-app/grails-app/i18n/messages_es.properties rename to examples/functional-test-app/grails-app/i18n/messages_es.properties diff --git a/functional-test-app/grails-app/i18n/messages_fr.properties b/examples/functional-test-app/grails-app/i18n/messages_fr.properties similarity index 100% rename from functional-test-app/grails-app/i18n/messages_fr.properties rename to examples/functional-test-app/grails-app/i18n/messages_fr.properties diff --git a/functional-test-app/grails-app/i18n/messages_it.properties b/examples/functional-test-app/grails-app/i18n/messages_it.properties similarity index 100% rename from functional-test-app/grails-app/i18n/messages_it.properties rename to examples/functional-test-app/grails-app/i18n/messages_it.properties diff --git a/functional-test-app/grails-app/i18n/messages_ja.properties b/examples/functional-test-app/grails-app/i18n/messages_ja.properties similarity index 100% rename from functional-test-app/grails-app/i18n/messages_ja.properties rename to examples/functional-test-app/grails-app/i18n/messages_ja.properties diff --git a/functional-test-app/grails-app/i18n/messages_nb.properties b/examples/functional-test-app/grails-app/i18n/messages_nb.properties similarity index 100% rename from functional-test-app/grails-app/i18n/messages_nb.properties rename to examples/functional-test-app/grails-app/i18n/messages_nb.properties diff --git a/functional-test-app/grails-app/i18n/messages_nl.properties b/examples/functional-test-app/grails-app/i18n/messages_nl.properties similarity index 100% rename from functional-test-app/grails-app/i18n/messages_nl.properties rename to examples/functional-test-app/grails-app/i18n/messages_nl.properties diff --git a/functional-test-app/grails-app/i18n/messages_pl.properties b/examples/functional-test-app/grails-app/i18n/messages_pl.properties similarity index 100% rename from functional-test-app/grails-app/i18n/messages_pl.properties rename to examples/functional-test-app/grails-app/i18n/messages_pl.properties diff --git a/functional-test-app/grails-app/i18n/messages_pt_BR.properties b/examples/functional-test-app/grails-app/i18n/messages_pt_BR.properties similarity index 100% rename from functional-test-app/grails-app/i18n/messages_pt_BR.properties rename to examples/functional-test-app/grails-app/i18n/messages_pt_BR.properties diff --git a/functional-test-app/grails-app/i18n/messages_pt_PT.properties b/examples/functional-test-app/grails-app/i18n/messages_pt_PT.properties similarity index 100% rename from functional-test-app/grails-app/i18n/messages_pt_PT.properties rename to examples/functional-test-app/grails-app/i18n/messages_pt_PT.properties diff --git a/functional-test-app/grails-app/i18n/messages_ru.properties b/examples/functional-test-app/grails-app/i18n/messages_ru.properties similarity index 100% rename from functional-test-app/grails-app/i18n/messages_ru.properties rename to examples/functional-test-app/grails-app/i18n/messages_ru.properties diff --git a/functional-test-app/grails-app/i18n/messages_sv.properties b/examples/functional-test-app/grails-app/i18n/messages_sv.properties similarity index 100% rename from functional-test-app/grails-app/i18n/messages_sv.properties rename to examples/functional-test-app/grails-app/i18n/messages_sv.properties diff --git a/functional-test-app/grails-app/i18n/messages_th.properties b/examples/functional-test-app/grails-app/i18n/messages_th.properties similarity index 100% rename from functional-test-app/grails-app/i18n/messages_th.properties rename to examples/functional-test-app/grails-app/i18n/messages_th.properties diff --git a/functional-test-app/grails-app/i18n/messages_zh_CN.properties b/examples/functional-test-app/grails-app/i18n/messages_zh_CN.properties similarity index 100% rename from functional-test-app/grails-app/i18n/messages_zh_CN.properties rename to examples/functional-test-app/grails-app/i18n/messages_zh_CN.properties diff --git a/functional-test-app/grails-app/init/test/Application.groovy b/examples/functional-test-app/grails-app/init/test/Application.groovy similarity index 100% rename from functional-test-app/grails-app/init/test/Application.groovy rename to examples/functional-test-app/grails-app/init/test/Application.groovy diff --git a/functional-test-app/grails-app/init/test/BootStrap.groovy b/examples/functional-test-app/grails-app/init/test/BootStrap.groovy similarity index 100% rename from functional-test-app/grails-app/init/test/BootStrap.groovy rename to examples/functional-test-app/grails-app/init/test/BootStrap.groovy diff --git a/functional-test-app/grails-app/views/application/index.gson b/examples/functional-test-app/grails-app/views/application/index.gson similarity index 100% rename from functional-test-app/grails-app/views/application/index.gson rename to examples/functional-test-app/grails-app/views/application/index.gson diff --git a/functional-test-app/grails-app/views/book/bookDisplay.gson b/examples/functional-test-app/grails-app/views/book/bookDisplay.gson similarity index 100% rename from functional-test-app/grails-app/views/book/bookDisplay.gson rename to examples/functional-test-app/grails-app/views/book/bookDisplay.gson diff --git a/functional-test-app/grails-app/views/error.gson b/examples/functional-test-app/grails-app/views/error.gson similarity index 100% rename from functional-test-app/grails-app/views/error.gson rename to examples/functional-test-app/grails-app/views/error.gson diff --git a/functional-test-app/grails-app/views/errors/_errors.gson b/examples/functional-test-app/grails-app/views/errors/_errors.gson similarity index 100% rename from functional-test-app/grails-app/views/errors/_errors.gson rename to examples/functional-test-app/grails-app/views/errors/_errors.gson diff --git a/functional-test-app/grails-app/views/notFound.gson b/examples/functional-test-app/grails-app/views/notFound.gson similarity index 100% rename from functional-test-app/grails-app/views/notFound.gson rename to examples/functional-test-app/grails-app/views/notFound.gson diff --git a/functional-test-app/grails-app/views/object/_object.gson b/examples/functional-test-app/grails-app/views/object/_object.gson similarity index 100% rename from functional-test-app/grails-app/views/object/_object.gson rename to examples/functional-test-app/grails-app/views/object/_object.gson diff --git a/functional-test-app/src/integration-test/groovy/test/BookControllerSpec.groovy b/examples/functional-test-app/src/integration-test/groovy/test/BookControllerSpec.groovy similarity index 71% rename from functional-test-app/src/integration-test/groovy/test/BookControllerSpec.groovy rename to examples/functional-test-app/src/integration-test/groovy/test/BookControllerSpec.groovy index 5c21a66..e682920 100644 --- a/functional-test-app/src/integration-test/groovy/test/BookControllerSpec.groovy +++ b/examples/functional-test-app/src/integration-test/groovy/test/BookControllerSpec.groovy @@ -1,8 +1,8 @@ package test -import grails.plugins.rest.client.RestBuilder -import grails.test.mixin.integration.Integration -import grails.transaction.Rollback +import grails.gorm.transactions.Rollback +import grails.testing.mixin.integration.Integration +import org.springframework.web.client.RestTemplate import spock.lang.Specification @Rollback @@ -19,14 +19,13 @@ class BookControllerSpec extends Specification { def "book show"() { given: - RestBuilder rest = new RestBuilder() + RestTemplate rest = new RestTemplate() expect: Book.read(1) when: - - def resp = rest.get("http://localhost:${serverPort}/book/1") + def resp = rest.getForEntity("http://localhost:${serverPort}/book/1", String) then: resp.statusCode.value() == 200 diff --git a/functional-test-app/src/test/groovy/test/BookControllerAllowedMethodsSpec.groovy b/examples/functional-test-app/src/test/groovy/test/BookControllerAllowedMethodsSpec.groovy similarity index 68% rename from functional-test-app/src/test/groovy/test/BookControllerAllowedMethodsSpec.groovy rename to examples/functional-test-app/src/test/groovy/test/BookControllerAllowedMethodsSpec.groovy index 65d0801..7acd832 100644 --- a/functional-test-app/src/test/groovy/test/BookControllerAllowedMethodsSpec.groovy +++ b/examples/functional-test-app/src/test/groovy/test/BookControllerAllowedMethodsSpec.groovy @@ -1,13 +1,11 @@ package test -import grails.test.mixin.TestFor +import grails.testing.web.controllers.ControllerUnitTest import spock.lang.Specification import spock.lang.Unroll -import static javax.servlet.http.HttpServletResponse.SC_METHOD_NOT_ALLOWED -import static javax.servlet.http.HttpServletResponse.SC_NOT_FOUND +import static jakarta.servlet.http.HttpServletResponse.SC_METHOD_NOT_ALLOWED -@TestFor(BookController) -class BookControllerAllowedMethodsSpec extends Specification { +class BookControllerAllowedMethodsSpec extends Specification implements ControllerUnitTest { @Unroll def "test TestController.show does not accept #method requests"(String method) { diff --git a/examples/plugin-test-app/build.gradle b/examples/plugin-test-app/build.gradle new file mode 100644 index 0000000..e56a038 --- /dev/null +++ b/examples/plugin-test-app/build.gradle @@ -0,0 +1,46 @@ +plugins { + id 'eclipse' + id 'idea' +} + +apply plugin:"org.apache.grails.gradle.grails-web" + +grails { + plugins { + implementation project(':cache-headers') + } +} + +dependencies { + implementation platform("org.apache.grails:grails-bom:$grailsVersion") + + implementation "org.apache.grails:grails-dependencies-starter-web" + + 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.apache.groovy:groovy-dateutil:5.0.3") + + implementation "io.methvin:directory-watcher:0.3.0" + implementation "net.java.dev.jna:jna:5.18.1" + + testImplementation "org.apache.grails:grails-testing-support-web" + testImplementation "org.spockframework:spock-core" + + console "org.apache.grails:grails-console" + + profile "org.apache.grails.profiles:web-plugin" +} + +bootRun { + jvmArgs('-Dspring.output.ansi.enabled=always') + sourceResources sourceSets.main +} + +bootJar.enabled = true + +apply from: "${rootProject.projectDir}/gradle/integrationTestVerbose.gradle" diff --git a/examples/plugin-test-app/grails-app/conf/application.yml b/examples/plugin-test-app/grails-app/conf/application.yml new file mode 100644 index 0000000..34283e8 --- /dev/null +++ b/examples/plugin-test-app/grails-app/conf/application.yml @@ -0,0 +1,91 @@ +--- +grails: + profile: web-plugin + codegen: + defaultPackage: grails.plugins.cacheheaders + spring: + transactionManagement: + proxies: false + gorm: + # Whether to autowire entities. + # Disabled by default for performance reasons. + autowire: false + reactor: + # Whether to translate GORM events into Reactor events + # Disabled by default for performance reasons + events: false +info: + app: + name: '@info.app.name@' + version: '@info.app.version@' + grailsVersion: '@info.app.grailsVersion@' +spring: + main: + banner-mode: "off" + groovy: + template: + check-template-location: false + +# Spring Actuator Endpoints are Disabled by Default +management: + endpoints: + enabled-by-default: false + jmx: + exposure: + include: "*" + +--- +grails: + 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 + urlmapping: + cache: + maxsize: 1000 + controllers: + defaultScope: singleton + converters: + encoding: UTF-8 + views: + default: + codec: html + gsp: + encoding: UTF-8 + htmlcodec: xml + codecs: + expression: html + scriptlets: html + taglib: none + staticparts: none +spring: + jmx: + unique-names: true diff --git a/examples/plugin-test-app/grails-app/conf/logback-spring.xml b/examples/plugin-test-app/grails-app/conf/logback-spring.xml new file mode 100644 index 0000000..cc7c9e0 --- /dev/null +++ b/examples/plugin-test-app/grails-app/conf/logback-spring.xml @@ -0,0 +1,19 @@ + + + + + + true + + ${CONSOLE_LOG_THRESHOLD} + + + ${CONSOLE_LOG_PATTERN} + ${CONSOLE_LOG_CHARSET} + + + + + + + diff --git a/examples/plugin-test-app/grails-app/conf/spring/resources.groovy b/examples/plugin-test-app/grails-app/conf/spring/resources.groovy new file mode 100644 index 0000000..302e501 --- /dev/null +++ b/examples/plugin-test-app/grails-app/conf/spring/resources.groovy @@ -0,0 +1,3 @@ +beans = { + +} diff --git a/grails-app/controllers/com/grailsrocks/cacheheaders/TestController.groovy b/examples/plugin-test-app/grails-app/controllers/grails/plugins/cacheheaders/TestController.groovy similarity index 94% rename from grails-app/controllers/com/grailsrocks/cacheheaders/TestController.groovy rename to examples/plugin-test-app/grails-app/controllers/grails/plugins/cacheheaders/TestController.groovy index 75cea6a..cd404eb 100644 --- a/grails-app/controllers/com/grailsrocks/cacheheaders/TestController.groovy +++ b/examples/plugin-test-app/grails-app/controllers/grails/plugins/cacheheaders/TestController.groovy @@ -1,4 +1,4 @@ -package com.grailsrocks.cacheheaders +package grails.plugins.cacheheaders import grails.plugins.cacheheaders.CacheHeadersTrait diff --git a/grails-app/init/grails/plugins/cacheheaders/Application.groovy b/examples/plugin-test-app/grails-app/init/grails/plugins/cacheheaders/Application.groovy similarity index 84% rename from grails-app/init/grails/plugins/cacheheaders/Application.groovy rename to examples/plugin-test-app/grails-app/init/grails/plugins/cacheheaders/Application.groovy index f9416c9..bbd4d40 100644 --- a/grails-app/init/grails/plugins/cacheheaders/Application.groovy +++ b/examples/plugin-test-app/grails-app/init/grails/plugins/cacheheaders/Application.groovy @@ -2,11 +2,9 @@ package grails.plugins.cacheheaders import grails.boot.GrailsApp import grails.boot.config.GrailsAutoConfiguration -import grails.plugins.metadata.PluginSource import groovy.transform.CompileStatic @CompileStatic -@PluginSource class Application extends GrailsAutoConfiguration { static void main(String[] args) { GrailsApp.run(Application, args) diff --git a/examples/plugin-test-app/grails-app/init/grails/plugins/cacheheaders/BootStrap.groovy b/examples/plugin-test-app/grails-app/init/grails/plugins/cacheheaders/BootStrap.groovy new file mode 100644 index 0000000..71d445d --- /dev/null +++ b/examples/plugin-test-app/grails-app/init/grails/plugins/cacheheaders/BootStrap.groovy @@ -0,0 +1,17 @@ +package grails.plugins.cacheheaders + +import groovy.transform.CompileStatic + +@CompileStatic +class BootStrap { + + def init = { + + } + + + def destroy = { + + } + +} diff --git a/src/integration-test/groovy/cacheheaders/CacheMethodsSpec.groovy b/examples/plugin-test-app/src/integration-test/groovy/grails/plugins/cacheheaders/CacheMethodsSpec.groovy similarity index 92% rename from src/integration-test/groovy/cacheheaders/CacheMethodsSpec.groovy rename to examples/plugin-test-app/src/integration-test/groovy/grails/plugins/cacheheaders/CacheMethodsSpec.groovy index 08ae2ea..3f5a454 100644 --- a/src/integration-test/groovy/cacheheaders/CacheMethodsSpec.groovy +++ b/examples/plugin-test-app/src/integration-test/groovy/grails/plugins/cacheheaders/CacheMethodsSpec.groovy @@ -1,12 +1,11 @@ -package cacheheaders +package grails.plugins.cacheheaders -import com.grailsrocks.cacheheaders.TestController -import grails.plugins.cacheheaders.CacheHeadersService +import grails.testing.mixin.integration.Integration +import grails.util.Holders import spock.lang.Specification import grails.util.GrailsWebMockUtil import org.springframework.beans.factory.annotation.Autowired import spock.lang.Shared -import grails.test.mixin.integration.Integration import spock.lang.Subject import java.text.SimpleDateFormat @@ -28,7 +27,7 @@ class CacheMethodsSpec extends Specification { def "preset can turn caching off"() { given: - grails.util.Holders.config.cache.headers.presets.presetDeny = false + Holders.config.cache.headers.presets.presetDeny = false when: testController.presetTest1() diff --git a/src/test/groovy/grails/plugins/cacheheaders/CacheHeadersServiceSpec.groovy b/examples/plugin-test-app/src/test/groovy/grails/plugins/cacheheaders/CacheHeadersServiceSpec.groovy similarity index 96% rename from src/test/groovy/grails/plugins/cacheheaders/CacheHeadersServiceSpec.groovy rename to examples/plugin-test-app/src/test/groovy/grails/plugins/cacheheaders/CacheHeadersServiceSpec.groovy index ac01de0..1413efe 100644 --- a/src/test/groovy/grails/plugins/cacheheaders/CacheHeadersServiceSpec.groovy +++ b/examples/plugin-test-app/src/test/groovy/grails/plugins/cacheheaders/CacheHeadersServiceSpec.groovy @@ -1,14 +1,14 @@ package grails.plugins.cacheheaders -import grails.test.mixin.TestFor +import grails.plugins.cacheheaders.CacheHeadersService +import grails.testing.services.ServiceUnitTest import org.springframework.mock.web.MockHttpServletRequest import org.springframework.mock.web.MockHttpServletResponse import spock.lang.Specification import java.text.SimpleDateFormat -@TestFor(CacheHeadersService) -class CacheHeadersServiceSpec extends Specification { +class CacheHeadersServiceSpec extends Specification implements ServiceUnitTest { MockHttpServletRequest req MockHttpServletResponse resp diff --git a/functional-test-app/build.gradle b/functional-test-app/build.gradle deleted file mode 100644 index c2647bf..0000000 --- a/functional-test-app/build.gradle +++ /dev/null @@ -1,66 +0,0 @@ -buildscript { - repositories { - mavenLocal() - maven { url "https://repo.grails.org/grails/core" } - } - dependencies { - classpath "org.grails:grails-gradle-plugin:$grailsVersion" - classpath "org.grails.plugins:hibernate5:${gormVersion-".RELEASE"}" - classpath "org.grails.plugins:views-gradle:1.1.6" - } -} - -version "0.1" -group "test" - -apply plugin:"eclipse" -apply plugin:"idea" -apply plugin:"war" -apply plugin:"org.grails.grails-web" -apply plugin:"org.grails.plugins.views-json" - -repositories { - mavenLocal() - maven { url "https://repo.grails.org/grails/core" } -} - -dependencies { - compile "org.springframework.boot:spring-boot-starter-logging" - compile "org.springframework.boot:spring-boot-autoconfigure" - compile "org.grails:grails-core" - compile "org.springframework.boot:spring-boot-starter-actuator" - compile "org.springframework.boot:spring-boot-starter-tomcat" - compile "org.grails:grails-plugin-url-mappings" - compile "org.grails:grails-plugin-rest" - compile "org.grails:grails-plugin-codecs" - compile "org.grails:grails-plugin-interceptors" - compile "org.grails:grails-plugin-services" - compile "org.grails:grails-plugin-datasource" - compile "org.grails:grails-plugin-databinding" - compile "org.grails:grails-plugin-async" - compile "org.grails:grails-web-boot" - compile "org.grails:grails-logging" - compile "org.grails.plugins:cache" - compile "org.grails.plugins:hibernate5" - compile "org.hibernate:hibernate-core:5.1.3.Final" - compile "org.hibernate:hibernate-ehcache:5.1.3.Final" - compile "org.grails.plugins:views-json" - compile "org.grails.plugins:views-json-templates" - console "org.grails:grails-console" - profile "org.grails.profiles:rest-api" - runtime "com.h2database:h2" - testCompile "org.grails:grails-plugin-testing" - testCompile "org.grails.plugins:geb" - testCompile "org.grails:grails-datastore-rest-client" - testRuntime "org.seleniumhq.selenium:selenium-htmlunit-driver:2.47.1" - testRuntime "net.sourceforge.htmlunit:htmlunit:2.18" -} - -apply from: "../gradle/cache-headers.gradle" -apply from: "../gradle/integrationTestVerbose.gradle" -apply from: "../gradle/testVerbose.gradle" - -bootRun { - jvmArgs('-Dspring.output.ansi.enabled=always') - addResources = true -} diff --git a/functional-test-app/gradle.properties b/functional-test-app/gradle.properties deleted file mode 100644 index 2ec2ead..0000000 --- a/functional-test-app/gradle.properties +++ /dev/null @@ -1,3 +0,0 @@ -grailsVersion=3.2.11 -gormVersion=6.0.12.RELEASE -gradleWrapperVersion=3.4.1 diff --git a/functional-test-app/gradle/wrapper/gradle-wrapper.jar b/functional-test-app/gradle/wrapper/gradle-wrapper.jar deleted file mode 100644 index deedc7fa5e6310eac3148a7dd0b1f069b07364cb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 52818 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≺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 diff --git a/functional-test-app/gradle/wrapper/gradle-wrapper.properties b/functional-test-app/gradle/wrapper/gradle-wrapper.properties deleted file mode 100644 index cc2cec1..0000000 --- a/functional-test-app/gradle/wrapper/gradle-wrapper.properties +++ /dev/null @@ -1,6 +0,0 @@ -#Fri Nov 27 23:09:32 CET 2015 -distributionBase=GRADLE_USER_HOME -distributionPath=wrapper/dists -zipStoreBase=GRADLE_USER_HOME -zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-3.4.1-bin.zip diff --git a/functional-test-app/gradlew b/functional-test-app/gradlew deleted file mode 100755 index 9d82f78..0000000 --- a/functional-test-app/gradlew +++ /dev/null @@ -1,160 +0,0 @@ -#!/usr/bin/env bash - -############################################################################## -## -## Gradle start up script for UN*X -## -############################################################################## - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS="" - -APP_NAME="Gradle" -APP_BASE_NAME=`basename "$0"` - -# Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD="maximum" - -warn ( ) { - echo "$*" -} - -die ( ) { - echo - echo "$*" - echo - exit 1 -} - -# OS specific support (must be 'true' or 'false'). -cygwin=false -msys=false -darwin=false -case "`uname`" in - CYGWIN* ) - cygwin=true - ;; - Darwin* ) - darwin=true - ;; - MINGW* ) - msys=true - ;; -esac - -# Attempt to set APP_HOME -# Resolve links: $0 may be a link -PRG="$0" -# Need this for relative symlinks. -while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG=`dirname "$PRG"`"/$link" - fi -done -SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >/dev/null -APP_HOME="`pwd -P`" -cd "$SAVED" >/dev/null - -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar - -# 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" - which java >/dev/null 2>&1 || 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 - -# Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then - MAX_FD_LIMIT=`ulimit -H -n` - if [ $? -eq 0 ] ; then - if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then - MAX_FD="$MAX_FD_LIMIT" - fi - ulimit -n $MAX_FD - if [ $? -ne 0 ] ; then - warn "Could not set maximum file descriptor limit: $MAX_FD" - fi - else - warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" - fi -fi - -# For Darwin, add options to specify how the application appears in the dock -if $darwin; then - GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" -fi - -# For Cygwin, switch paths to Windows format before running java -if $cygwin ; then - APP_HOME=`cygpath --path --mixed "$APP_HOME"` - CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` - JAVACMD=`cygpath --unix "$JAVACMD"` - - # We build the pattern for arguments to be converted via cygpath - ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` - SEP="" - for dir in $ROOTDIRSRAW ; do - ROOTDIRS="$ROOTDIRS$SEP$dir" - SEP="|" - done - OURCYGPATTERN="(^($ROOTDIRS))" - # Add a user-defined pattern to the cygpath arguments - if [ "$GRADLE_CYGPATTERN" != "" ] ; then - OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" - fi - # Now convert the arguments - kludge to limit ourselves to /bin/sh - i=0 - for arg in "$@" ; do - CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` - CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option - - if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition - eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` - else - eval `echo args$i`="\"$arg\"" - fi - i=$((i+1)) - done - case $i in - (0) set -- ;; - (1) set -- "$args0" ;; - (2) set -- "$args0" "$args1" ;; - (3) set -- "$args0" "$args1" "$args2" ;; - (4) set -- "$args0" "$args1" "$args2" "$args3" ;; - (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; - esac -fi - -# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules -function splitJvmOpts() { - JVM_OPTS=("$@") -} -eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS -JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" - -exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" diff --git a/functional-test-app/gradlew.bat b/functional-test-app/gradlew.bat deleted file mode 100755 index aec9973..0000000 --- a/functional-test-app/gradlew.bat +++ /dev/null @@ -1,90 +0,0 @@ -@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 - -@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= - -set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. -set APP_BASE_NAME=%~n0 -set APP_HOME=%DIRNAME% - -@rem Find java.exe -if defined JAVA_HOME goto findJavaFromJavaHome - -set JAVA_EXE=java.exe -%JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto init - -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:findJavaFromJavaHome -set JAVA_HOME=%JAVA_HOME:"=% -set JAVA_EXE=%JAVA_HOME%/bin/java.exe - -if exist "%JAVA_EXE%" goto init - -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:init -@rem Get command-line arguments, handling Windowz variants - -if not "%OS%" == "Windows_NT" goto win9xME_args -if "%@eval[2+2]" == "4" goto 4NT_args - -:win9xME_args -@rem Slurp the command line arguments. -set CMD_LINE_ARGS= -set _SKIP=2 - -:win9xME_args_slurp -if "x%~1" == "x" goto execute - -set CMD_LINE_ARGS=%* -goto execute - -:4NT_args -@rem Get arguments from the 4NT Shell from JP Software -set CMD_LINE_ARGS=%$ - -:execute -@rem Setup the command line - -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar - -@rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% - -:end -@rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="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! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega diff --git a/functional-test-app/grails-app/conf/application.yml b/functional-test-app/grails-app/conf/application.yml deleted file mode 100644 index 9ce20b0..0000000 --- a/functional-test-app/grails-app/conf/application.yml +++ /dev/null @@ -1,116 +0,0 @@ ---- -grails: - profile: rest-api - codegen: - defaultPackage: test - spring: - transactionManagement: - proxies: false - gorm: - # Whether to autowire entities. - # Disabled by default for performance reasons. - autowire: false - reactor: - # Whether to translate GORM events into Reactor events - # Disabled by default for performance reasons - events: false -info: - app: - name: '@info.app.name@' - version: '@info.app.version@' - grailsVersion: '@info.app.grailsVersion@' -spring: - main: - banner-mode: "off" - groovy: - template: - check-template-location: false - -# Spring Actuator Endpoints are Disabled by Default -endpoints: - enabled: false - jmx: - enabled: true - ---- -grails: - mime: - disable: - accept: - header: - userAgents: - - Gecko - - WebKit - - Presto - - Trident - types: - json: - - application/json - - text/json - hal: - - application/hal+json - - application/hal+xml - xml: - - text/xml - - application/xml - atom: application/atom+xml - css: text/css - csv: text/csv - js: text/javascript - rss: application/rss+xml - text: text/plain - all: '*/*' - urlmapping: - cache: - maxsize: 1000 - controllers: - defaultScope: singleton - converters: - encoding: UTF-8 - ---- -hibernate: - cache: - queries: false - use_second_level_cache: true - use_query_cache: false - region.factory_class: org.hibernate.cache.ehcache.SingletonEhCacheRegionFactory - -dataSource: - pooled: true - jmxExport: true - driverClassName: org.h2.Driver - username: sa - password: - -environments: - development: - dataSource: - dbCreate: create-drop - url: jdbc:h2:mem:devDb;MVCC=TRUE;LOCK_TIMEOUT=10000;DB_CLOSE_ON_EXIT=FALSE - test: - dataSource: - dbCreate: update - url: jdbc:h2:mem:testDb;MVCC=TRUE;LOCK_TIMEOUT=10000;DB_CLOSE_ON_EXIT=FALSE - production: - dataSource: - dbCreate: none - url: jdbc:h2:./prodDb;MVCC=TRUE;LOCK_TIMEOUT=10000;DB_CLOSE_ON_EXIT=FALSE - properties: - jmxEnabled: true - initialSize: 5 - maxActive: 50 - minIdle: 5 - maxIdle: 25 - maxWait: 10000 - maxAge: 600000 - timeBetweenEvictionRunsMillis: 5000 - minEvictableIdleTimeMillis: 60000 - validationQuery: SELECT 1 - validationQueryTimeout: 3 - validationInterval: 15000 - testOnBorrow: true - testWhileIdle: true - testOnReturn: false - jdbcInterceptors: ConnectionState - defaultTransactionIsolation: 2 # TRANSACTION_READ_COMMITTED diff --git a/functional-test-app/grails-app/conf/logback.groovy b/functional-test-app/grails-app/conf/logback.groovy deleted file mode 100644 index 20f85e1..0000000 --- a/functional-test-app/grails-app/conf/logback.groovy +++ /dev/null @@ -1,36 +0,0 @@ -import grails.util.BuildSettings -import grails.util.Environment -import org.springframework.boot.logging.logback.ColorConverter -import org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter - -import java.nio.charset.Charset - -conversionRule 'clr', ColorConverter -conversionRule 'wex', WhitespaceThrowableProxyConverter - -// See http://logback.qos.ch/manual/groovy.html for details on configuration -appender('STDOUT', ConsoleAppender) { - encoder(PatternLayoutEncoder) { - charset = Charset.forName('UTF-8') - - pattern = - '%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} ' + // Date - '%clr(%5p) ' + // Log level - '%clr(---){faint} %clr([%15.15t]){faint} ' + // Thread - '%clr(%-40.40logger{39}){cyan} %clr(:){faint} ' + // Logger - '%m%n%wex' // Message - } -} - -def targetDir = BuildSettings.TARGET_DIR -if (Environment.isDevelopmentMode() && targetDir != null) { - appender("FULL_STACKTRACE", FileAppender) { - file = "${targetDir}/stacktrace.log" - append = true - encoder(PatternLayoutEncoder) { - pattern = "%level %logger - %msg%n" - } - } - logger("StackTrace", ERROR, ['FULL_STACKTRACE'], false) -} -root(ERROR, ['STDOUT']) diff --git a/functional-test-app/grails-app/conf/spring/resources.groovy b/functional-test-app/grails-app/conf/spring/resources.groovy deleted file mode 100644 index fa95006..0000000 --- a/functional-test-app/grails-app/conf/spring/resources.groovy +++ /dev/null @@ -1,3 +0,0 @@ -// Place your Spring DSL code here -beans = { -} diff --git a/functional-test-app/grails-wrapper.jar b/functional-test-app/grails-wrapper.jar deleted file mode 100644 index bc85146c130e69264ce20e079987784615201b35..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5463 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 Q1NO~BaT5_U$?e \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG=`dirname "$PRG"`"/$link" - fi -done -SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >/dev/null -APP_HOME="`pwd -P`" -cd "$SAVED" >/dev/null - -JAR_PATH=$APP_HOME/grails-wrapper.jar - -# 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" - which java >/dev/null 2>&1 || 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 - -# Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then - MAX_FD_LIMIT=`ulimit -H -n` - if [ $? -eq 0 ] ; then - if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then - MAX_FD="$MAX_FD_LIMIT" - fi - ulimit -n $MAX_FD - if [ $? -ne 0 ] ; then - warn "Could not set maximum file descriptor limit: $MAX_FD" - fi - else - warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" - fi -fi - -# For Cygwin, switch paths to Windows format before running java -if $cygwin ; then - APP_HOME=`cygpath --path --mixed "$APP_HOME"` - JAVACMD=`cygpath --unix "$JAVACMD"` - - # We build the pattern for arguments to be converted via cygpath - ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` - SEP="" - for dir in $ROOTDIRSRAW ; do - ROOTDIRS="$ROOTDIRS$SEP$dir" - SEP="|" - done - OURCYGPATTERN="(^($ROOTDIRS))" - # Add a user-defined pattern to the cygpath arguments - if [ "$GRAILS_CYGPATTERN" != "" ] ; then - OURCYGPATTERN="$OURCYGPATTERN|($GRAILS_CYGPATTERN)" - fi - # Now convert the arguments - kludge to limit ourselves to /bin/sh - i=0 - for arg in "$@" ; do - CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` - CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option - - if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition - eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` - else - eval `echo args$i`="\"$arg\"" - fi - i=$((i+1)) - done - case $i in - (0) set -- ;; - (1) set -- "$args0" ;; - (2) set -- "$args0" "$args1" ;; - (3) set -- "$args0" "$args1" "$args2" ;; - (4) set -- "$args0" "$args1" "$args2" "$args3" ;; - (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; - esac -fi - -# Split up the JVM_OPTS And GRAILS_OPTS values into an array, following the shell quoting and substitution rules -function splitJvmOpts() { - JVM_OPTS=("$@") -} -eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRAILS_OPTS - -exec "$JAVACMD" -jar "${JVM_OPTS[@]}" "$JAR_PATH" "$@" diff --git a/functional-test-app/grailsw.bat b/functional-test-app/grailsw.bat deleted file mode 100755 index c48c384..0000000 --- a/functional-test-app/grailsw.bat +++ /dev/null @@ -1,89 +0,0 @@ -@if "%DEBUG%" == "" @echo off -@rem ########################################################################## -@rem -@rem Grails startup script for Windows -@rem -@rem ########################################################################## - -@rem Set local scope for the variables with windows NT shell -if "%OS%"=="Windows_NT" setlocal - -@rem Add default JVM options here. You can also use JAVA_OPTS and GRAILS_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS="-XX:+TieredCompilation" "-XX:TieredStopAtLevel=1" "-XX:CICompilerCount=3" - -set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. -set APP_BASE_NAME=%~n0 -set APP_HOME=%DIRNAME% - -@rem Find java.exe -if defined JAVA_HOME goto findJavaFromJavaHome - -set JAVA_EXE=java.exe -%JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto init - -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:findJavaFromJavaHome -set JAVA_HOME=%JAVA_HOME:"=% -set JAVA_EXE=%JAVA_HOME%/bin/java.exe - -if exist "%JAVA_EXE%" goto init - -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:init -@rem Get command-line arguments, handling Windowz variants - -if not "%OS%" == "Windows_NT" goto win9xME_args -if "%@eval[2+2]" == "4" goto 4NT_args - -:win9xME_args -@rem Slurp the command line arguments. -set CMD_LINE_ARGS= -set _SKIP=2 - -:win9xME_args_slurp -if "x%~1" == "x" goto execute - -set CMD_LINE_ARGS=%* -goto execute - -:4NT_args -@rem Get arguments from the 4NT Shell from JP Software -set CMD_LINE_ARGS=%$ - -:execute -@rem Setup the command line -set JAR_PATH=%APP_HOME%/grails-wrapper.jar - -@rem Execute Grails -"%JAVA_EXE%" -jar %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRAILS_OPTS% %JAR_PATH% %CMD_LINE_ARGS% - -:end -@rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd - -:fail -rem Set variable GRAILS_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -if not "" == "%GRAILS_EXIT_CONSOLE%" exit 1 -exit /b 1 - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega diff --git a/functional-test-app/settings.gradle b/functional-test-app/settings.gradle deleted file mode 100644 index 98c0a46..0000000 --- a/functional-test-app/settings.gradle +++ /dev/null @@ -1 +0,0 @@ -rootProject.name='test' diff --git a/gradle.properties b/gradle.properties index de953dc..4f3e74a 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,10 +1,15 @@ -grailsVersion=3.2.10 -gradleWrapperVersion=3.4.1 -asciidoctorGradlePluginVersion=1.5.3 +projectVersion=3.0.0 +grailsVersion=7.0.5 +gradleWrapperVersion=8.14.2 +asciidoctorJvmVersion=4.0.4 +artifactoryVersion=5.2.5 +customUserDataVersion=2.2.1 +develocityVersion=4.0 websiteUrl=http://grails-plugins.github.io/cache-headers issueTrackerUrl=https://github.com/grails-plugins/cache-headers/issues vcsUrl=https://github.com/grails-plugins/cache-headers bintrayUser= bintrayKey= grailsPortalUser= -grailsPortalPassword= \ No newline at end of file +grailsPortalPassword= +GRAILS_PUBLISH_RELEASE=false diff --git a/gradle/cache-headers.gradle b/gradle/cache-headers.gradle deleted file mode 100644 index ea33932..0000000 --- a/gradle/cache-headers.gradle +++ /dev/null @@ -1,16 +0,0 @@ -File pluginDir = file('.').parentFile -File versionTxt -while (true) { - versionTxt = new File(pluginDir, 'version.txt') - if (versionTxt.exists()) { - break - } - pluginDir = pluginDir.parentFile -} -project.ext.pluginName = pluginDir.name - 'grails-' - -project.ext.pluginVersion = versionTxt.text.trim() - -dependencies { - compile "org.grails.plugins:$pluginName:$pluginVersion" -} \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index b395c81..018086e 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-3.4.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.2-all.zip diff --git a/grails-app/conf/application.yml b/grails-app/conf/application.yml deleted file mode 100644 index cfe636e..0000000 --- a/grails-app/conf/application.yml +++ /dev/null @@ -1,89 +0,0 @@ ---- -grails: - profile: web-plugin - codegen: - defaultPackage: grails.plugins.cacheheaders - spring: - transactionManagement: - proxies: false - gorm: - # Whether to autowire entities. - # Disabled by default for performance reasons. - autowire: false - reactor: - # Whether to translate GORM events into Reactor events - # Disabled by default for performance reasons - events: false -info: - app: - name: '@info.app.name@' - version: '@info.app.version@' - grailsVersion: '@info.app.grailsVersion@' -spring: - main: - banner-mode: "off" - groovy: - template: - check-template-location: false - -# Spring Actuator Endpoints are Disabled by Default -endpoints: - enabled: false - jmx: - enabled: true - ---- -grails: - 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 - urlmapping: - cache: - maxsize: 1000 - controllers: - defaultScope: singleton - converters: - encoding: UTF-8 - views: - default: - codec: html - gsp: - encoding: UTF-8 - htmlcodec: xml - codecs: - expression: html - scriptlets: html - taglib: none - staticparts: none -endpoints: - jmx: - unique-names: true diff --git a/plugin/build.gradle b/plugin/build.gradle new file mode 100644 index 0000000..94c4103 --- /dev/null +++ b/plugin/build.gradle @@ -0,0 +1,51 @@ +plugins { + id 'eclipse' + id 'idea' + id 'java-library' + id 'maven-publish' + id 'org.asciidoctor.jvm.convert' version "$asciidoctorJvmVersion" + id 'com.jfrog.artifactory' version "$artifactoryVersion" +} + +version project.version + +apply plugin:"org.apache.grails.gradle.grails-plugin" +apply plugin: 'org.apache.grails.gradle.grails-exploded' +apply from: "${rootProject.projectDir}/gradle/docs.gradle" + +compileJava.options.release = 17 + +dependencies { + implementation platform("org.apache.grails:grails-bom:$grailsVersion") + compileOnly "org.apache.grails:grails-dependencies-starter-web" + + implementation("org.apache.groovy:groovy-dateutil:4.0.29") + + testImplementation "org.apache.grails:grails-testing-support-web" +} + +grails { + springDependencyManagement = false +} + +bootJar.enabled = false + +artifactory { + contextUrl = 'http://oss.jfrog.org' + publish { + repository { + repoKey = 'oss-snapshot-local' + username = System.getenv("BINTRAY_USER") ?: project.bintrayUser + password = System.getenv("BINTRAY_KEY") ?: project.bintrayKey + } + defaults { + publications('maven') + } + } +} + +tasks.register('docs') { + dependsOn 'asciidoctor' +} + +apply from: "${rootProject.projectDir}/gradle/testVerbose.gradle" diff --git a/grails-app/services/grails/plugins/cacheheaders/CacheHeadersService.groovy b/plugin/grails-app/services/grails/plugins/cacheheaders/CacheHeadersService.groovy similarity index 99% rename from grails-app/services/grails/plugins/cacheheaders/CacheHeadersService.groovy rename to plugin/grails-app/services/grails/plugins/cacheheaders/CacheHeadersService.groovy index 88aaa0e..2a5bdc3 100644 --- a/grails-app/services/grails/plugins/cacheheaders/CacheHeadersService.groovy +++ b/plugin/grails-app/services/grails/plugins/cacheheaders/CacheHeadersService.groovy @@ -1,5 +1,8 @@ package grails.plugins.cacheheaders +import groovy.util.logging.Slf4j + +@Slf4j class CacheHeadersService { boolean enabled = true diff --git a/src/docs/asciidoc/index.adoc b/plugin/src/docs/asciidoc/index.adoc similarity index 99% rename from src/docs/asciidoc/index.adoc rename to plugin/src/docs/asciidoc/index.adoc index 55542a5..d445fb8 100644 --- a/src/docs/asciidoc/index.adoc +++ b/plugin/src/docs/asciidoc/index.adoc @@ -11,7 +11,7 @@ Install the plugin by adding a dependency in `build.gradle` [source, groovy] ---- -compile 'or.grails.plugins:cache-headers:2.0.1' +compile 'org.apache.grails:cache-headers:3.0.0' ---- == Description diff --git a/src/main/groovy/grails/plugins/cacheheaders/CacheHeadersGrailsPlugin.groovy b/plugin/src/main/groovy/grails/plugins/cacheheaders/CacheHeadersGrailsPlugin.groovy similarity index 77% rename from src/main/groovy/grails/plugins/cacheheaders/CacheHeadersGrailsPlugin.groovy rename to plugin/src/main/groovy/grails/plugins/cacheheaders/CacheHeadersGrailsPlugin.groovy index bbf537b..45d492a 100644 --- a/src/main/groovy/grails/plugins/cacheheaders/CacheHeadersGrailsPlugin.groovy +++ b/plugin/src/main/groovy/grails/plugins/cacheheaders/CacheHeadersGrailsPlugin.groovy @@ -10,10 +10,9 @@ import groovy.util.logging.Slf4j class CacheHeadersGrailsPlugin extends Plugin { // the version or versions of Grails the plugin is designed for - def grailsVersion = "3.2.10 > *" + def grailsVersion = "7.0.0 > *" // resources that are excluded from plugin packaging def pluginExcludes = [ - "**/TestController**", "src/docs/**", ] @@ -22,9 +21,14 @@ class CacheHeadersGrailsPlugin extends Plugin { def authorEmail = 'graeme.rocher@gmail.com' def title = 'Caching Headers Plugin' def description = 'Improve your application performance with browser caching, with easy ways to set caching headers in controller responses' - def developers = [[name: "Marc Palmer", email: "marc@grailsrocks.com"], [name: "Graeme Rocher", email: "graeme.rocher@gmail.com"], [name: 'Sergio del Amo', email: 'sergio.delamo@softamo.com']] - def issueManagement = [system: "Github", url: "http://github.com/grails-plugins/grails-cache-headers/issues"] - def scm = [url: 'http://github.com/grails-plugins/grails-cache-headers'] + def developers = [ + [name: "Marc Palmer", email: "marc@grailsrocks.com"], + [name: "Graeme Rocher", email: "graeme.rocher@gmail.com"], + [name: 'Sergio del Amo', email: 'sergio.delamo@softamo.com'], + [name: 'Rahul Shishodia', email: 'rahul.shishodia.10@gmail.com'] + ] + def issueManagement = [system: "Github", url: "http://github.com/grails-plugins/ cache-headers/issues"] + def scm = [url: 'http://github.com/grails-plugins/cache-headers'] def license = "APACHE" def documentation = "http://grails.org/plugin/cache-headers" @@ -45,4 +49,4 @@ class CacheHeadersGrailsPlugin extends Plugin { // Config change might mean that the caching has been turned on/off doWithApplicationContext() } -} \ No newline at end of file +} diff --git a/src/main/groovy/grails/plugins/cacheheaders/CacheHeadersTrait.groovy b/plugin/src/main/groovy/grails/plugins/cacheheaders/CacheHeadersTrait.groovy similarity index 100% rename from src/main/groovy/grails/plugins/cacheheaders/CacheHeadersTrait.groovy rename to plugin/src/main/groovy/grails/plugins/cacheheaders/CacheHeadersTrait.groovy diff --git a/src/main/groovy/grails/plugins/cacheheaders/WithCacheHeadersDelegate.groovy b/plugin/src/main/groovy/grails/plugins/cacheheaders/WithCacheHeadersDelegate.groovy similarity index 100% rename from src/main/groovy/grails/plugins/cacheheaders/WithCacheHeadersDelegate.groovy rename to plugin/src/main/groovy/grails/plugins/cacheheaders/WithCacheHeadersDelegate.groovy diff --git a/plugin/src/test/groovy/grails/plugins/cacheheaders/CacheHeadersServiceSpec.groovy b/plugin/src/test/groovy/grails/plugins/cacheheaders/CacheHeadersServiceSpec.groovy new file mode 100644 index 0000000..1413efe --- /dev/null +++ b/plugin/src/test/groovy/grails/plugins/cacheheaders/CacheHeadersServiceSpec.groovy @@ -0,0 +1,246 @@ +package grails.plugins.cacheheaders + +import grails.plugins.cacheheaders.CacheHeadersService +import grails.testing.services.ServiceUnitTest +import org.springframework.mock.web.MockHttpServletRequest +import org.springframework.mock.web.MockHttpServletResponse +import spock.lang.Specification + +import java.text.SimpleDateFormat + +class CacheHeadersServiceSpec extends Specification implements ServiceUnitTest { + + MockHttpServletRequest req + MockHttpServletResponse resp + Expando context + + private static final String RFC1123_DATE_FORMAT = "EEE, dd MMM yyyy HH:mm:ss zzz" // Always GMT + + void setup() { + req = new MockHttpServletRequest() + resp = new MockHttpServletResponse() + context = new Expando( + request: req, + response: resp, + render: { String s -> resp.outputStream << s.bytes }) + } + + def "withCacheHeaders does not set Last-Modified or ETag if caching is disabled"() { + given: + service.enabled = false + + when: + req.addHeader('If-None-Match', "1234567Z") + + def res = service.withCacheHeaders(context) { + etag { + "1234567Z" + } + generate { + render "Hello!" + } + } + + then: + resp.status == 200 + resp.contentAsString == 'Hello!' + resp.getHeader('Last-Modified') == null + resp.getHeader('ETag') == null + } + + def "request where Header If-None-Match matches ETag"() { + when: + req.addHeader('If-None-Match', "1234567Z") + + def res = service.withCacheHeaders(context) { + etag { + "1234567Z" + } + } + + then: + resp.status == 304 // Not Modified + resp.getHeader('Last-Modified') == null + resp.getHeader('ETag') == null + } + + def "withCacheHeaders ETag does not match last modfied unchanged"() { + + given: + def lastMod = new Date() - 100 + + when: + req.addHeader('If-None-Match', "dsfdsfdsfdsfsd") + // This is an AWFUL hack because spring mock http request/response does not do string <-> date coercion + req.addHeader('If-Modified-Since', lastMod) + + def res = service.withCacheHeaders(context) { + etag { + "1234567Z" + } + + lastModified { + lastMod + } + + generate { + render "Derelict Herds" + } + } + + then: + resp.status == 200 + resp.contentAsString == 'Derelict Herds' + resp.getHeader('ETag') == '1234567Z' + resp.getHeader('Last-Modified') == dateToHTTPDate(lastMod) + + } + + void "withCacheHeaders ETag match Last Modified Changed"() { + given: + def lastMod = new Date() - 100 + + when: + req.addHeader('If-None-Match', "bingo") + // This is an AWFUL hack because spring mock http request/response does not do string <-> date coercion + req.addHeader('If-Modified-Since', lastMod-1) + + def res = service.withCacheHeaders(context) { + etag { + "bingo" + } + + lastModified { + lastMod + } + + generate { + render "Derelict Herds" + } + } + + then: + resp.status == 200 + resp.contentAsString == 'Derelict Herds' + resp.getHeader('ETag') == 'bingo' + resp.getHeader('Last-Modified') == dateToHTTPDate(lastMod) + } + + void "withCacheHeaders ETag match, last Modified unchanged"() { + given: + def lastMod = new Date() - 100 + + when: + req.addHeader('If-None-Match', "bingo") + // This is an AWFUL hack because spring mock http request/response does not do string <-> date coercion + req.addHeader('If-Modified-Since', lastMod) + + def res = service.withCacheHeaders(context) { + etag { + "bingo" + } + + lastModified { + lastMod + } + + generate { + render "Derelict Herds" + } + } + + then: + resp.status == 304 // Not Modified + } + + + def "withCacheHeaders ETag No Match, LastModChanged"() { + given: + def lastMod = new Date() - 100 + + when: + req.addHeader('If-None-Match', "dsfdsfdsfdsfsd") + // This is an AWFUL hack because spring mock http request/response does not do string <-> date coercion + req.addHeader('If-Modified-Since', lastMod-1) + + def res = service.withCacheHeaders(context) { + etag { + "1234567Z" + } + + lastModified { + lastMod + } + + generate { + render "Derelict Herds" + } + } + + then: + resp.status == 200 + resp.contentAsString == 'Derelict Herds' + resp.getHeader('ETag') == '1234567Z' + resp.getHeader('Last-Modified') == dateToHTTPDate(lastMod) + } + + def "testWithCacheHeadersLastModChanged"() { + when: + // This is an AWFUL hack because spring mock http request/response does not do string <-> date coercion + req.addHeader('If-Modified-Since', new Date() - 102) + + def lastMod = new Date() - 100 + + def res = service.withCacheHeaders(context) { + etag { + "OU812" + } + lastModified { + lastMod + } + + generate { + render "Porcelain Heart" + } + } + + then: + resp.status == 200 + resp.contentAsString == 'Porcelain Heart' + resp.getHeader('ETag') == 'OU812' + resp.getHeader('Last-Modified') == dateToHTTPDate(lastMod) + } + + def "testWithCacheHeadersLastModNotNewer"() { + given: + def d = new Date() - 100 + + when: + // This is an AWFUL hack because spring mock http request/response does not do string <-> date coercion + req.addHeader('If-Modified-Since', d) + def lastMod = d + def res = service.withCacheHeaders(context) { + etag { + "5150" + } + lastModified { + lastMod + } + + generate { + render "Hessian Peel" + } + } + + then: + resp.status == 304 // Not Modified + resp.getHeader('Last-Modified') == null + resp.getHeader('ETag') == null + } + + private static String dateToHTTPDate(date) { + def v = new SimpleDateFormat(RFC1123_DATE_FORMAT, Locale.ENGLISH) + v.timeZone = TimeZone.getTimeZone('GMT') + return v.format(date) + } +} diff --git a/settings.gradle b/settings.gradle index 1097938..90fd525 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1 +1,33 @@ -rootProject.name='cache-headers' +plugins { + id "com.gradle.develocity" version "$develocityVersion" + id "com.gradle.common-custom-user-data-gradle-plugin" version "$customUserDataVersion" +} + +def isCI = System.getenv().containsKey('CI') +def isLocal = !isCI + +develocity { + server = 'https://ge.grails.org' + buildScan { + tag('grails') + tag('grails-forge') + publishing.onlyIf { it.authenticated } + uploadInBackground = isLocal + } +} + +buildCache { + local { enabled = isLocal } + remote(develocity.buildCache) { + push = isCI + enabled = true + } +} + +rootProject.name='grails-cache-headers' + +include 'plugin' +include 'examples:plugin-test-app' +include 'examples:functional-test-app' + +findProject(":plugin").name = "cache-headers" diff --git a/version.txt b/version.txt deleted file mode 100644 index e9307ca..0000000 --- a/version.txt +++ /dev/null @@ -1 +0,0 @@ -2.0.2