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
32 changes: 21 additions & 11 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ jobs:
- name: "Output Agent IP" # in the event your agent has network issues, you can use this to debug
run: curl -s https://api.ipify.org
- name: "📥 Checkout repository"
uses: actions/checkout@v4
uses: actions/checkout@v6
- name: "Export .sdkmanrc properties"
uses: apache/grails-github-actions/export-gradle-properties@asf
with:
Expand All @@ -28,14 +28,20 @@ jobs:
- name: "Determine Java Version"
run: echo "SDKMANRC_java=${{ env.SDKMANRC_java }}" | sed 's/-.*//' >> $GITHUB_ENV
- name: "☕️ Setup JDK"
uses: actions/setup-java@v4
uses: actions/setup-java@v5
with:
distribution: ${{ env.JAVA_DISTRIBUTION }}
java-version: ${{ env.SDKMANRC_java }}
- name: 'Ensure Common Build Date' # to ensure a reproducible build
run: echo "SOURCE_DATE_EPOCH=$(git log -1 --pretty=%ct)" >> "$GITHUB_ENV"
- name: "Export gradle.properties properties"
uses: apache/grails-github-actions/export-gradle-properties@asf
- name: "🐘 Setup Gradle"
uses: gradle/actions/setup-gradle@v4
uses: gradle/actions/setup-gradle@v5
with:
build-scan-publish: ${{ env.ciBuildScanPublish }}
build-scan-terms-of-use-url: ${{ env.ciBuildScanTermsOfUseUrl }}
build-scan-terms-of-use-agree: ${{ env.ciBuildScanTermsOfUseAgree }}
- name: "🔨 Build project without tests"
if: ${{ contains(github.event.head_commit.message, '[skip tests]') }}
run: >
Expand All @@ -53,15 +59,16 @@ jobs:
--rerun-tasks
-PskipCodeStyle
publish:
# only run the publish task on this repo instead of forks
# only run the publishing task on this repo (not on forks)
if: github.repository_owner == 'grails-plugins' && (github.event_name == 'push' || github.event_name == 'workflow_dispatch')
needs: [ build ]
needs: build
name: "Publish Snapshot"
runs-on: ubuntu-24.04
steps:
- name: "Output Agent IP" # in the event your agent has network issues, you can use this to debug
run: curl -s https://api.ipify.org
- name: "📥 Checkout repository"
uses: actions/checkout@v4
uses: actions/checkout@v6
- name: "Export .sdkmanrc properties"
uses: apache/grails-github-actions/export-gradle-properties@asf
with:
Expand All @@ -70,30 +77,33 @@ jobs:
- name: "Determine Java Version"
run: echo "SDKMANRC_java=${{ env.SDKMANRC_java }}" | sed 's/-.*//' >> $GITHUB_ENV
- name: "☕️ Setup JDK"
uses: actions/setup-java@v4
uses: actions/setup-java@v5
with:
distribution: ${{ env.JAVA_DISTRIBUTION }}
java-version: ${{ env.SDKMANRC_java }}
- name: 'Ensure Common Build Date' # to ensure a reproducible build
run: echo "SOURCE_DATE_EPOCH=$(git log -1 --pretty=%ct)" >> "$GITHUB_ENV"
- name: "Export gradle.properties properties"
uses: apache/grails-github-actions/export-gradle-properties@asf
- name: "🐘 Setup Gradle"
uses: gradle/actions/setup-gradle@v4
uses: gradle/actions/setup-gradle@v5
with:
build-scan-publish: ${{ env.ciBuildScanPublish }}
build-scan-terms-of-use-url: ${{ env.ciBuildScanTermsOfUseUrl }}
build-scan-terms-of-use-agree: ${{ env.ciBuildScanTermsOfUseAgree }}
- name: "📤 Publish Gradle Snapshot Artifacts"
env:
GRAILS_PUBLISH_RELEASE: 'false'
MAVEN_PUBLISH_URL: 'https://central.sonatype.com/repository/maven-snapshots/'
MAVEN_PUBLISH_USERNAME: ${{ secrets.NEXUS_PUBLISH_USERNAME }}
MAVEN_PUBLISH_PASSWORD: ${{ secrets.NEXUS_PUBLISH_PASSWORD }}
working-directory: './plugin'
run: >
../gradlew publish
--no-build-cache
--rerun-tasks
- name: "📜 Generate Documentation"
if: success()
run: ./gradlew docs
- name: "🚀 Publish to Github Pages"
if: success()
uses: apache/grails-github-actions/deploy-github-pages@asf
env:
GRADLE_PUBLISH_RELEASE: 'false'
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: "Coverage"
name: "Code Coverage"
on:
push:
branches:
Expand All @@ -15,7 +15,7 @@ jobs:
runs-on: ubuntu-24.04
steps:
- name: "📥 Checkout repository"
uses: actions/checkout@v4
uses: actions/checkout@v6
- name: "Export .sdkmanrc properties"
uses: apache/grails-github-actions/export-gradle-properties@asf
with:
Expand All @@ -24,24 +24,30 @@ jobs:
- name: "Determine Java Version"
run: echo "SDKMANRC_java=${{ env.SDKMANRC_java }}" | sed 's/-.*//' >> $GITHUB_ENV
- name: "☕️ Setup JDK"
uses: actions/setup-java@v4
uses: actions/setup-java@v5
with:
distribution: liberica
java-version: ${{ env.SDKMANRC_java }}
- name: "Export gradle.properties properties"
uses: apache/grails-github-actions/export-gradle-properties@asf
- name: "🐘 Setup Gradle"
uses: gradle/actions/setup-gradle@v4
uses: gradle/actions/setup-gradle@v5
with:
build-scan-publish: ${{ env.ciBuildScanPublish }}
build-scan-terms-of-use-url: ${{ env.ciBuildScanTermsOfUseUrl }}
build-scan-terms-of-use-agree: ${{ env.ciBuildScanTermsOfUseAgree }}
- name: "🔨 Build and run tests"
run: >
./gradlew build
--continue
--stacktrace
-PskipCodeStyle
- name: "📊 Post coverage summary"
- name: "📊 Post code coverage summary"
if: always()
run: |
REPORT="coverage/build/reports/jacoco/jacocoAggregatedReport/jacocoAggregatedReport.xml"
REPORT="code-coverage/build/reports/jacoco/jacocoAggregatedReport/jacocoAggregatedReport.xml"
if [ ! -f "$REPORT" ]; then
echo "::warning::Coverage report not found at $REPORT"
echo "::warning::Code Coverage report not found at $REPORT"
exit 0
fi

Expand Down
12 changes: 9 additions & 3 deletions .github/workflows/code-style.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ jobs:
runs-on: ubuntu-24.04
steps:
- name: "📥 Checkout repository"
uses: actions/checkout@v4
uses: actions/checkout@v6
- name: "Export .sdkmanrc properties"
uses: apache/grails-github-actions/export-gradle-properties@asf
with:
Expand All @@ -24,12 +24,18 @@ jobs:
- name: "Determine Java Version"
run: echo "SDKMANRC_java=${{ env.SDKMANRC_java }}" | sed 's/-.*//' >> $GITHUB_ENV
- name: "☕️ Setup JDK"
uses: actions/setup-java@v4
uses: actions/setup-java@v5
with:
distribution: liberica
java-version: ${{ env.SDKMANRC_java }}
- name: "Export gradle.properties properties"
uses: apache/grails-github-actions/export-gradle-properties@asf
- name: "🐘 Setup Gradle"
uses: gradle/actions/setup-gradle@v4
uses: gradle/actions/setup-gradle@v5
with:
build-scan-publish: ${{ env.ciBuildScanPublish }}
build-scan-terms-of-use-url: ${{ env.ciBuildScanTermsOfUseUrl }}
build-scan-terms-of-use-agree: ${{ env.ciBuildScanTermsOfUseAgree }}
- name: "🎨 Run code style checks"
run: >
./gradlew codeStyle
Expand Down
64 changes: 31 additions & 33 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ on:
types: [ published ]
permissions: { }
env:
# to prevent throttling of the github api, include the github token in an environment variable since the build will check for it
# To prevent throttling of the GitHub api,
# include the GitHub token in an environment variable
# since the build will check for it
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GRAILS_PUBLISH_RELEASE: 'true'
JAVA_DISTRIBUTION: 'liberica'
Expand All @@ -19,7 +21,7 @@ jobs:
name: "Stage Jar Files"
permissions:
packages: read # pre-release workflow
contents: write # to create release
contents: write # to create a release
issues: write # to modify milestones
runs-on: ubuntu-24.04
steps:
Expand All @@ -28,7 +30,7 @@ jobs:
- name: "Output Agent IP" # in the event RAO blocks this agent, this can be used to debug it
run: curl -s https://api.ipify.org
- name: "📥 Checkout repository"
uses: actions/checkout@v4
uses: actions/checkout@v6
with:
token: ${{ secrets.GITHUB_TOKEN }}
ref: ${{ env.TAG }}
Expand All @@ -49,12 +51,18 @@ jobs:
- name: "Determine Java Version"
run: echo "SDKMANRC_java=${{ env.SDKMANRC_java }}" | sed 's/-.*//' >> $GITHUB_ENV
- name: "☕️ Setup JDK"
uses: actions/setup-java@v4
uses: actions/setup-java@v5
with:
distribution: ${{ env.JAVA_DISTRIBUTION }}
java-version: ${{ env.SDKMANRC_java }}
- name: "Export gradle.properties properties"
uses: apache/grails-github-actions/export-gradle-properties@asf
- name: "🐘 Setup Gradle"
uses: gradle/actions/setup-gradle@v4
uses: gradle/actions/setup-gradle@v5
with:
build-scan-publish: ${{ env.ciBuildScanPublish }}
build-scan-terms-of-use-url: ${{ env.ciBuildScanTermsOfUseUrl }}
build-scan-terms-of-use-agree: ${{ env.ciBuildScanTermsOfUseAgree }}
- name: "⚙️ Run pre-release"
uses: apache/grails-github-actions/pre-release@asf
env:
Expand All @@ -71,7 +79,6 @@ jobs:
./gradlew
-Psigning.secretKeyRingFile=${{ github.workspace }}/secring.gpg
publishMavenPublicationToSonatypeRepository
publishPluginMavenPublicationToSonatypeRepository
closeSonatypeStagingRepository
- name: "Generate Build Date file"
run: echo "$SOURCE_DATE_EPOCH" >> build/BUILD_DATE.txt
Expand All @@ -92,7 +99,7 @@ jobs:
- name: "📝 Establish release version"
run: echo "VERSION=${TAG#v}" >> "$GITHUB_ENV"
- name: "📥 Checkout repository"
uses: actions/checkout@v4
uses: actions/checkout@v6
with:
token: ${{ secrets.GITHUB_TOKEN }}
ref: ${{ env.TAG }}
Expand All @@ -104,12 +111,18 @@ jobs:
- name: "Determine Java Version"
run: echo "SDKMANRC_java=${{ env.SDKMANRC_java }}" | sed 's/-.*//' >> $GITHUB_ENV
- name: "☕️ Setup JDK"
uses: actions/setup-java@v4
uses: actions/setup-java@v5
with:
distribution: ${{ env.JAVA_DISTRIBUTION }}
java-version: ${{ env.SDKMANRC_java }}
- name: "Export gradle.properties properties"
uses: apache/grails-github-actions/export-gradle-properties@asf
- name: "🐘 Setup Gradle"
uses: gradle/actions/setup-gradle@v4
uses: gradle/actions/setup-gradle@v5
with:
build-scan-publish: ${{ env.ciBuildScanPublish }}
build-scan-terms-of-use-url: ${{ env.ciBuildScanTermsOfUseUrl }}
build-scan-terms-of-use-agree: ${{ env.ciBuildScanTermsOfUseAgree }}
- name: "📤 Release staging repository"
env:
NEXUS_PUBLISH_USERNAME: ${{ secrets.MAVEN_USERNAME }}
Expand All @@ -131,7 +144,7 @@ jobs:
- name: "📝 Establish release version"
run: echo "VERSION=${TAG#v}" >> "$GITHUB_ENV"
- name: "📥 Checkout repository"
uses: actions/checkout@v4
uses: actions/checkout@v6
with:
token: ${{ secrets.GITHUB_TOKEN }}
ref: ${{ env.TAG }}
Expand All @@ -143,12 +156,18 @@ jobs:
- name: "Determine Java Version"
run: echo "SDKMANRC_java=${{ env.SDKMANRC_java }}" | sed 's/-.*//' >> $GITHUB_ENV
- name: "☕️ Setup JDK"
uses: actions/setup-java@v4
uses: actions/setup-java@v5
with:
distribution: ${{ env.JAVA_DISTRIBUTION }}
java-version: ${{ env.SDKMANRC_java }}
- name: "Export gradle.properties properties"
uses: apache/grails-github-actions/export-gradle-properties@asf
- name: "🐘 Setup Gradle"
uses: gradle/actions/setup-gradle@v4
uses: gradle/actions/setup-gradle@v5
with:
build-scan-publish: ${{ env.ciBuildScanPublish }}
build-scan-terms-of-use-url: ${{ env.ciBuildScanTermsOfUseUrl }}
build-scan-terms-of-use-agree: ${{ env.ciBuildScanTermsOfUseAgree }}
- name: "🔨 Build Documentation"
run: ./gradlew docs
- name: "🚀 Publish to Github Pages"
Expand All @@ -168,26 +187,5 @@ jobs:
issues: write # required for milestone closing
pull-requests: write # to create the PR that will increment the version
steps:
- name: "📝 Establish release version"
run: echo "VERSION=${TAG#v}" >> "$GITHUB_ENV"
- name: "📥 Checkout repository"
uses: actions/checkout@v4
with:
token: ${{ secrets.GITHUB_TOKEN }}
ref: ${{ env.TAG }}
- name: "Export .sdkmanrc properties"
uses: apache/grails-github-actions/export-gradle-properties@asf
with:
file: ".sdkmanrc"
prefix: "SDKMANRC_"
- name: "Determine Java Version"
run: echo "SDKMANRC_java=${{ env.SDKMANRC_java }}" | sed 's/-.*//' >> $GITHUB_ENV
- name: "☕️ Setup JDK"
uses: actions/setup-java@v4
with:
distribution: ${{ env.JAVA_DISTRIBUTION }}
java-version: ${{ env.SDKMANRC_java }}
- name: "🐘 Setup Gradle"
uses: gradle/actions/setup-gradle@v4
- name: "⚙️ Run post-release"
uses: apache/grails-github-actions/post-release@asf
34 changes: 16 additions & 18 deletions .skills/example-apps.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,14 +47,14 @@ The `examples/` directory can contain more than one app. Different apps can test
All apps under `examples/` are auto-discovered by `settings.gradle`:

```groovy
def examples = file('examples').list()
def examples = file('examples').listFiles({ it.directory } as FileFilter)
examples.each { example ->
include example
project(":$example").projectDir = file("examples/$example")
include example.name
project(":$example.name").projectDir = file("examples/$example.name")
}
```

New apps are also automatically included in coverage aggregation -- `coverage/build.gradle` discovers all example apps
New apps are also automatically included in coverage aggregation -- `code-coverage/build.gradle` discovers all example apps
under `examples/` at configuration time, so no manual registration is needed.

## Project Structure
Expand Down Expand Up @@ -90,9 +90,7 @@ Example apps apply convention plugins and declare their own dependencies:

```groovy
plugins {
id 'org.grails.plugins.servertiming.compile'
id 'org.grails.plugins.servertiming.testing'
id 'org.grails.plugins.servertiming.example'
id 'config.example-app'
}

version = projectVersion
Expand All @@ -119,7 +117,7 @@ dependencies {

Key patterns:

- Apply `compile`, `testing`, and `example` convention plugins
- Apply `example-app` convention plugin
- Depend on the plugin via `project(':grails-server-timing')`
- NEVER apply `project-publish` -- example apps are not published
- NEVER apply `plugin` -- example apps are applications, not plugins
Expand Down Expand Up @@ -159,7 +157,7 @@ class ServerTimingIntegrationSpec extends Specification {
### What to test in integration tests

- HTTP headers are present and correctly formatted
- Timing values are within expected ranges (e.g., slow action >= 200ms)
- Timing values are within expected ranges (e.g., slow action >= 200 ms)
- Different response types (GSP views, JSON, plain text) all include headers
- Static assets include `other`/`total` metrics but not `action`/`view`
- Header format matches the W3C Server Timing specification
Expand All @@ -169,10 +167,10 @@ class ServerTimingIntegrationSpec extends Specification {
### Integration test patterns

1. **Use `RestTemplate` or similar HTTP client** -- test real HTTP round-trips
2. **Verify headers, not internals** -- assert on `Server-Timing` header values, not internal class state
2. **Verify headers, not internals** assert on `Server-Timing` header values, not internal class state
3. **Use timing thresholds, not exact values** -- assert `>= 200ms`, never `== 203ms`
4. **Test edge cases** -- static assets, JSON responses, redirects, errors
5. **Extract helper methods** -- centralize header parsing (e.g., `extractDuration()`)
4. **Test edge cases** static assets, JSON responses, redirects, errors
5. **Extract helper methods** centralize header parsing (e.g., `extractDuration()`)

### Test organization

Expand All @@ -185,12 +183,12 @@ class ServerTimingIntegrationSpec extends Specification {

Example apps should include purpose-built controllers and views that exercise the plugin's features:

- **Fast actions** -- verify baseline header presence
- **Slow actions** (with `Thread.sleep()`) -- verify timing accuracy
- **Variable delay actions** -- parameterized timing tests
- **Slow views** (GSP with embedded sleep) -- verify view timing separation
- **JSON/text responses** -- verify non-GSP response types
- **Multiple operations** -- verify timing accumulation
- **Fast actions** verify baseline header presence
- **Slow actions** (with `Thread.sleep()`) verify timing accuracy
- **Variable delay actions** parameterized timing tests
- **Slow views** (GSP with embedded sleep) verify view timing separation
- **JSON/text responses** verify non-GSP response types
- **Multiple operations** verify timing accumulation

These are test fixtures that live in the example app, NOT in the plugin project.

Expand Down
Loading
Loading