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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 9 additions & 7 deletions conf/guides.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1918,7 +1918,7 @@ guides:
howto: How to Complete the Guide
createApp:
title: Creating the Application
download: Download a Grails 8 Snapshot Starter
download: Download a Grails 8 Starter
dockerOverview:
title: Two Paths to a Container Image
bootBuildImage:
Expand Down Expand Up @@ -2318,7 +2318,7 @@ guides:
howto: How to Complete the Guide
createApp:
title: Creating the Application
download: Download a Grails 8 Snapshot Starter
download: Download a Grails 8 Starter
fieldsPluginAlreadyIncluded: The Fields Plugin in Grails 8
domainModel:
title: The Book and Author Domain Model
Expand Down Expand Up @@ -2550,7 +2550,7 @@ guides:
howto: How to Complete the Guide
createApp:
title: Creating the Application
download: Download a Grails 8 Snapshot Starter
download: Download a Grails 8 Starter
workflowOverview:
title: The Multi-Stage Job Graph
ciYml:
Expand Down Expand Up @@ -3396,6 +3396,8 @@ guides:
title: The Two Webapps Consume shared-core
runningEach:
title: Running and Packaging Each Webapp
devReload:
title: Dev-Mode Auto-Reload Across Projects
testingPerModule:
title: Per-Module Testing
extractingPlugin:
Expand Down Expand Up @@ -4049,7 +4051,7 @@ guides:
howto: How to Complete the Guide
createApp:
title: Creating the Application
download: Download a Grails 8 rest_api Starter
download: Download a Grails 8 Rest API Starter
domainModel:
title: The Book and Author Domain Model
bootstrapData: Bootstrap Sample Data
Expand Down Expand Up @@ -4257,7 +4259,7 @@ guides:

- name: 'grails-spock-test-tour'
title: 'A Spock Test Tour for Grails 8'
subtitle: 'Working examples of every Spock test layer Grails 8 supports - DomainUnitTest, ServiceUnitTest + DataTest, ControllerUnitTest, @Integration + @Rollback, plus parameterised where: data tables.'
subtitle: 'Working examples of every Spock test layer Grails 8 supports - DomainUnitTest, ServiceUnitTest + DataTest, ControllerUnitTest, @Integration + @Rollback, ContainerGebSpec functional tests, plus parameterised where: data tables.'
authors:
- 'James Fredley'
category: 'Grails Testing'
Expand Down Expand Up @@ -4300,7 +4302,7 @@ guides:
integrationTest:
title: '@Integration With @Rollback'
functionalTest:
title: Geb Functional Tests
title: ContainerGebSpec Functional Tests
parameterised:
title: 'Parameterised where: Data Tables'
platform:
Expand Down Expand Up @@ -4421,7 +4423,7 @@ guides:
howto: How to Complete the Guide
createApp:
title: Creating the Application
download: Download a Grails 8 Snapshot Starter
download: Download a Grails 8 Starter
installTailwind:
title: Installing Tailwind CSS 4
configureTailwind:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,9 @@ Three pieces are doing the work:
====
Paketo also exposes ``BPL_JVM_HEAD_ROOM``, ``BPL_JVM_LOADED_CLASS_COUNT``, and ``BPL_JVM_THREAD_COUNT`` as runtime tuning knobs. The defaults are reasonable for a typical Grails app; reach for them only when JVM memory profiles drive the conversation.
====

The Spring Boot Gradle plugin pinned in `grails-core` 8.0.x is `spring-boot-gradle-plugin:4.0.5` (see `git show 8.0.x:dependencies.gradle` for the exact coordinate); `bootBuildImage` is part of that plugin and is enabled automatically by the `org.apache.grails.gradle.web` plugin the `web` profile applies.

Files touched in this chapter:

* `build.gradle`
4 changes: 4 additions & 0 deletions guides/grails-docker-bootbuildimage/v8/guide/compose.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,7 @@ docker compose up
----

The first run pulls the Postgres image, creates the volume, starts Postgres, waits for healthy, then starts the app. Subsequent runs skip the pull.

Files touched in this chapter:

* `compose.yml`
Original file line number Diff line number Diff line change
@@ -1 +1 @@
Generate a fresh Apache Grails 8 web application from link:https://prev-snapshot.grails.org[prev-snapshot.grails.org] with the three features the rest of the guide expects: `postgres` (the JDBC driver and a Postgres datasource template), `testcontainers` (so we can spin up a real Postgres in functional tests), and `database-migration` (Liquibase, so the production image can run schema changes on first start).
Generate a fresh Apache Grails 8 web application from link:https://start.grails.org[start.grails.org] with the three features the rest of the guide expects: `postgres` (the JDBC driver and a Postgres datasource template), `testcontainers` (so we can spin up a real Postgres in functional tests), and `database-migration` (Liquibase, so the production image can run schema changes on first start).
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,7 @@ Two paths produce a runnable container image from the same `bootJar`:
For a typical Grails 8 web app the buildpack path wins on every dimension that matters: smaller images (the buildpack's slim JRE base is ~150 MB), better layering (a code-only change touches only the top layer), and a free non-root user. The Dockerfile path is justified when you have a regulated build that must consume an internal mirror, an air-gapped CI, or a non-buildpack-compatible base image.

This guide leads with `bootBuildImage` and treats the Dockerfile as an alternative.

Files touched in this chapter:

* None
5 changes: 5 additions & 0 deletions guides/grails-docker-bootbuildimage/v8/guide/dockerfile.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,8 @@ The matching `.dockerignore` file excludes `.gradle/`, `build/`, `out/`, and oth
include::../snippets/.dockerignore[]
----
====

Files touched in this chapter:

* `Dockerfile`
* `.dockerignore`
8 changes: 1 addition & 7 deletions guides/grails-docker-bootbuildimage/v8/guide/download.adoc
Original file line number Diff line number Diff line change
@@ -1,10 +1,4 @@
[source,bash]
----
curl -L -o docker-app.zip \
"https://prev-snapshot.grails.org/create/web/example.docker?lang=GROOVY&build=GRADLE&test=SPOCK&javaVersion=JDK_21&features=postgres,testcontainers,database-migration"
unzip docker-app.zip
cd docker
----
Open link:https://start.grails.org[start.grails.org], pick the `web` profile, name the application `docker`, set the package to `example`, choose JDK 21, tick the `postgres`, `testcontainers`, and `database-migration` features, and download the generated zip. Unzip it and `cd` into the `docker` directory.

The forge unpacks into a `docker/` directory containing a vanilla Grails 8 layout plus the three feature-driven additions:

Expand Down
4 changes: 4 additions & 0 deletions guides/grails-docker-bootbuildimage/v8/guide/envProfiles.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,7 @@ Three things to notice:
* The username and password resolve from `SPRING_DATASOURCE_USERNAME` and `SPRING_DATASOURCE_PASSWORD` regardless of profile, with the username defaulting to `postgres` for local development.

The Compose file we will write later passes these three variables to the container. Kubernetes deployments would pass the same variables via a `Secret` and `envFrom: { secretRef: ... }`.

Files touched in this chapter:

* `grails-app/conf/application.yml`
4 changes: 4 additions & 0 deletions guides/grails-docker-bootbuildimage/v8/guide/ghcr.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,7 @@ The first time an image is pushed under a given repo path, GHCR creates the pack
====
The matching CI/CD guide automates this whole flow on every git tag: a `release.yml` workflow runs `bootBuildImage` against the tag's source, pushes to GHCR with the tag name as the image tag, and creates a GitHub Release with the bootJar attached. See the link:../grails-github-actions-cicd/8/guide/index.html[GitHub Actions CI/CD with Grails 8] guide for the workflow file.
====

Files touched in this chapter:

* None (push happens from the developer shell or from CI)
4 changes: 4 additions & 0 deletions guides/grails-docker-bootbuildimage/v8/guide/healthCheck.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,7 @@ The `health.livenessstate` and `health.readinessstate` flags expose the two prob
* `/actuator/health/readiness` answers "should the platform send traffic to this pod?". Returns `DOWN` while the application is starting up, or while the database is unreachable. Use as a Kubernetes `readinessProbe` and as the Docker `HEALTHCHECK`.

For Docker Compose, the `HEALTHCHECK` lives on the service definition (next chapter). For Kubernetes, you would use `httpGet: { path: /actuator/health/readiness, port: 8080 }` instead.

Files touched in this chapter:

* `grails-app/conf/application.yml`
2 changes: 1 addition & 1 deletion guides/grails-docker-bootbuildimage/v8/guide/howto.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ docker compose up

The repository contains two top-level directories:

* `initial/` - the starting Grails 8 snapshot project, generated from `https://prev-snapshot.grails.org` with the `postgres`, `testcontainers`, and `database-migration` features and no further customisations.
* `initial/` - the starting Grails 8 project, generated from link:https://start.grails.org[start.grails.org] with the `postgres`, `testcontainers`, and `database-migration` features and no further customisations.
* `complete/` - the same project with all Docker-related changes from this guide already in place.

Each chapter ends with the file paths it touches relative to the project root, so you can match what you typed against the `complete/` reference.
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,8 @@ environment = [
The `BPE_APPEND` variant *adds* to whatever the buildpack-supplied `JAVA_TOOL_OPTIONS` already contains. The buildpack ships its own memory calculator output (heap, direct memory, metaspace, reserved code cache, thread stacks); appending preserves all of that and just bolts our two flags on the end.

If you switch to the hand-rolled Dockerfile you set the same two flags via `ENV JAVA_TOOL_OPTIONS=...` instead.

Files touched in this chapter:

* `build.gradle` (the `bootBuildImage` block)
* `Dockerfile` (only if you take the alternative path)
4 changes: 4 additions & 0 deletions guides/grails-docker-bootbuildimage/v8/guide/nonRoot.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,7 @@ securityContext:
----

`readOnlyRootFilesystem: true` is the strictest setting. The Paketo image already supports it; the hand-rolled Dockerfile may need a `tmpfs` volume mounted at `/tmp` for things like Tomcat's work directory. The trade-off is yours to make.

Files touched in this chapter:

* None directly (`Dockerfile` already creates the non-root user)
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,7 @@ The `application.yml` we wrote in the env-profiles chapter resolves these three
====
For functional tests, the `testcontainers` feature means you do not need a long-running Postgres at all - Spock specs can spin one up per spec via Testcontainers' Postgres module, run their assertions, and tear it down. The CI/CD guide shows how that integrates with GitHub Actions.
====

Files touched in this chapter:

* `compose.yml`
4 changes: 4 additions & 0 deletions guides/grails-docker-bootbuildimage/v8/guide/runImage.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,7 @@ The first request renders the Grails welcome page. The second returns `{"status"
====
``docker history ghcr.io/grails-guides/grails-docker-bootbuildimage:0.1`` shows you the layer breakdown the buildpack produced. Notice that classes/resources sit on top of dependencies; a code-only change rebuilds only that one layer.
====

Files touched in this chapter:

* None
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,9 @@ By the end of the guide you will have:
* A `compose.yml` stack that runs the image alongside `postgres:16-alpine`, with a Spring Boot Actuator-driven `HEALTHCHECK` and a `depends_on: condition: service_healthy` block so the app waits for the database to be ready before it starts.
* An equivalent multi-stage `Dockerfile` (build stage on `bellsoft/liberica-openjdk-debian:21`, runtime stage on the matching JRE image) that runs the app as UID `10001` and shows what the buildpack does for you when you let it.
* The commands you need to push the image to GitHub Container Registry, and a one-paragraph note on consuming it from the matching CI/CD guide.

The official Grails 8 reference manual at `grails-doc/src/en/guide/deployment.adoc` (and the `deployment/` subdirectory) covers WAR file deployment to a servlet container. This guide complements that by walking the modern OCI-image path that Spring Boot 4's `bootBuildImage` task makes possible.

Files touched in this chapter:

* None
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
As of Apache Grails 7 the Fields plugin is bundled with `grails-core`. You do not need to add a dependency for it. The taglib namespace `f` is registered automatically and the default templates live on the runtime classpath inside `grails-fields.jar`.
As of Apache Grails 7 the Fields plugin is bundled with `grails-core` as the `grails-fields` module. You do not need to add a dependency for it. The taglib namespace `f` is registered automatically by `grails-app/taglib/grails/plugin/formfields/FormFieldsTagLib.groovy` (inside `grails-fields.jar`) and the default templates live on the runtime classpath.

You can confirm the plugin is on the classpath by listing the resolved configurations:

Expand All @@ -7,4 +7,6 @@ You can confirm the plugin is on the classpath by listing the resolved configura
./gradlew dependencies --configuration runtimeClasspath | grep -i fields
----

You should see `org.apache.grails:grails-fields:8.0.0-SNAPSHOT` (or whichever Grails version your project pins) in the output. There is no separate plugin descriptor to register and no `BuildConfig.groovy` to edit.
You should see `org.apache.grails:grails-fields:<version>` matching the Grails version your project pins. There is no separate plugin descriptor to register and no `BuildConfig.groovy` to edit.

The official 8.0.x reference for the plugin lives at `grails-doc/src/en/guide/theWebLayer/fields.adoc` (and the `fields/` subdirectory). That chapter covers the high-level `f:` taglib API; this guide complements it with worked examples of customising the per-property and per-class templates that the plugin's `FormFieldsTemplateService` looks up.
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ cd grails-fields-custom-widgets-and-wrappers/complete

The repository contains two top-level directories:

* `initial/` - the starting Grails 8 snapshot project, generated from `https://prev-snapshot.grails.org` with no customisations applied.
* `initial/` - the starting Grails 8 project, generated from link:https://start.grails.org[start.grails.org] with no customisations applied.
* `complete/` - the same project with all the widgets, wrappers, and table customisations from this guide already in place.

Each chapter ends with the file paths it touches relative to the project root, so you can match what you typed against the `complete/` reference.
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,4 @@ The practical consequence: you customise the _highest-impact_ template at the le

The rest of this guide walks each of those layers in turn.

TIP: If you want to see exactly which path the plugin chose, set the log level on `org.grails.plugin.formfields.FormFieldsTemplateService` to `DEBUG`. Each lookup logs every path it tried in order.
TIP: If you want to see exactly which path the plugin chose, set the log level on `org.grails.plugin.formfields.FormFieldsTemplateService` to `DEBUG`. Each lookup logs every path it tried in order. The lookup logic itself lives in `grails-fields/src/main/groovy/grails/plugin/formfields/FormFieldsTemplateService.groovy` if you want to read the source. The plugin also supports a `_themes/<theme>/` lookup level (controlled by `grails.plugin.fields.theme`) that comes _before_ the per-class layer; see `grails-doc/src/en/guide/theWebLayer/fields.adoc` for the theme story.
2 changes: 1 addition & 1 deletion guides/grails-github-actions-cicd/v8/guide/createApp.adoc
Original file line number Diff line number Diff line change
@@ -1 +1 @@
Generate a fresh Apache Grails 8 web application from link:https://prev-snapshot.grails.org[prev-snapshot.grails.org] with the three features the rest of the guide expects: `postgres`, `testcontainers`, and `geb-with-webdriver-binaries` (so functional tests can drive a headless browser).
Generate a fresh Apache Grails 8 web application from link:https://start.grails.org[start.grails.org] with the two features the rest of the guide expects: `postgres` and `testcontainers`. The functional tests use `ContainerGebSpec` from `org.apache.grails:grails-geb`, which runs the browser in a Testcontainers Selenium container - no `geb-with-webdriver-binaries` feature is needed.
13 changes: 4 additions & 9 deletions guides/grails-github-actions-cicd/v8/guide/download.adoc
Original file line number Diff line number Diff line change
@@ -1,13 +1,8 @@
[source,bash]
----
curl -L -o cicd-app.zip \
"https://prev-snapshot.grails.org/create/web/example.cicd?lang=GROOVY&build=GRADLE&test=SPOCK&javaVersion=JDK_21&features=postgres,testcontainers,geb-with-webdriver-binaries"
unzip cicd-app.zip
cd cicd
----
Open link:https://start.grails.org[start.grails.org], pick the `web` profile, name the application `cicd`, set the package to `example`, choose JDK 21, tick the `postgres` and `testcontainers` features, and download the generated zip. Unzip it and `cd` into the `cicd` directory.

The forge unpacks into a `cicd/` directory containing a vanilla Grails 8 web layout with three feature-driven additions:
The forge unpacks into a `cicd/` directory containing a vanilla Grails 8 web layout with two feature-driven additions:

* `runtimeOnly "org.postgresql:postgresql"` and a Postgres datasource template
* `testImplementation "org.testcontainers:postgresql"` (and `org.testcontainers:spock` + `:testcontainers`)
* `integrationTestImplementation testFixtures("org.apache.grails:grails-geb")` and `com.energizedwork.gebish:webdriver-binaries-gradle-plugin` for downloading Chrome/Firefox driver binaries automatically

Geb container support comes from `testFixtures("org.apache.grails:grails-geb")`, which is already on the test classpath in the standard `web` profile - no extra forge feature is needed.
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
The `functional-test` job boots the application against a Testcontainers-managed Postgres and drives it with Geb 8 specs. Because it does not declare a `services.postgres` block, the runner is a plain Ubuntu VM; Testcontainers spins up Postgres on demand from the test JVM via the local Docker socket (which GitHub-hosted runners expose by default).
The `functional-test` job boots the application against a Testcontainers-managed Postgres and drives it with Geb 8 specs that extend `ContainerGebSpec` (from `testFixtures("org.apache.grails:grails-geb")`). The browser itself runs in a Selenium-Chrome container started by Testcontainers, so the runner never needs Chrome/Firefox installed on the host. Because the job does not declare a `services.postgres` block, the runner is a plain Ubuntu VM; Testcontainers spins up both Postgres and Selenium on demand from the test JVM via the local Docker socket (which GitHub-hosted runners expose by default).

[source,yaml]
----
Expand Down Expand Up @@ -27,4 +27,4 @@ The `functional-test` job boots the application against a Testcontainers-managed

`if: always()` on the geb-reports upload is intentional: even on a green run you may want the screenshots from `build/geb-reports/` to confirm the page rendered as expected. On a red run, those screenshots are how you debug "works on my machine" failures - the runner's screenshot of the failed page is usually enough to spot the missing element.

The `geb-with-webdriver-binaries` feature you selected at the forge step downloads matching Chrome/Firefox driver binaries on the fly via `webdriver-binaries-gradle-plugin`, so no `apt-get install` of the browsers themselves is needed - the plugin handles both the browser and the driver.
No `apt-get install` of Chrome/Firefox is needed - the browser process lives inside the Selenium container Testcontainers pulls (`selenium/standalone-chrome` by default). The first cold run pulls the image (~200 MB) and is slower; subsequent runs reuse the cached layer. The legacy `geb-with-webdriver-binaries` forge feature is obsolete in Grails 8 and is not used here.
2 changes: 1 addition & 1 deletion guides/grails-github-actions-cicd/v8/guide/howto.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,5 @@ cd grails-github-actions-cicd/complete

The repository contains two top-level directories:

* `initial/` - the starting Grails 8 snapshot project, generated from `https://prev-snapshot.grails.org` with the `postgres`, `testcontainers`, and `geb-with-webdriver-binaries` features.
* `initial/` - the starting Grails 8 project, generated from link:https://start.grails.org[start.grails.org] with the `postgres` and `testcontainers` features.
* `complete/` - the same project with the three workflow YAML files added under `.github/`.
Loading
Loading