Skip to content

Replace legacy *.grails.org URLs with their canonical grails.apache.org targets#502

Merged
jamesfredley merged 1 commit intomasterfrom
fix-redirect-links
May 3, 2026
Merged

Replace legacy *.grails.org URLs with their canonical grails.apache.org targets#502
jamesfredley merged 1 commit intomasterfrom
fix-redirect-links

Conversation

@jamesfredley
Copy link
Copy Markdown
Contributor

Summary

The Plugins link in the site header pointed at https://plugins.grails.org, which is kept alive only as a meta-refresh redirect back to https://grails.apache.org/plugins.html. Many other links across guides, blog posts, and the shared chrome had the same shape - they targeted a legacy *.grails.org host that exists today only to bounce visitors to the canonical grails.apache.org URL. Each redirect costs the user an extra hop, hurts SEO, and breaks if the legacy hosts ever go away.

This PR replaces every redirect-bound URL in the source tree with its canonical grails.apache.org destination so internal links resolve directly.

239 files changed, 358 insertions(+), 358 deletions(-) - a strict 1-for-1 URL swap (no content rewriting).

Concrete fix the user asked for

Before:

<li><a href='https://plugins.grails.org'>Plugins</a></li>

After:

<li><a href='https://grails.apache.org/plugins.html'>Plugins</a></li>

Verified in build/dist/index.html line 92 after running ./gradlew build.

URL mapping applied

Old (redirects back to grails.apache.org) New (canonical)
plugins.grails.org grails.apache.org/plugins.html
guides.grails.org<path> grails.apache.org/guides<path> (path-preserving)
docs.grails.org<path> grails.apache.org/docs<path> (path-preserving)
views.grails.org/* grails.apache.org/docs/latest/guide/theWebLayer.html#gson
async.grails.org/* grails.apache.org/docs/latest/guide/async.html
testing.grails.org/* grails.apache.org/docs/latest/guide/testing.html
gsp.grails.org/* grails.apache.org/docs/latest/guide/theWebLayer.html#gsp
grails.org/<X>.html grails.apache.org/<X>.html
grails.org/learn.html grails.apache.org/learning.html (page renamed)
grails.org/plugin/<X> grails.apache.org/plugins.html (legacy 404)
grails.org/blog<path> grails.apache.org/blog<path>
grails.org/ and bare grails.org grails.apache.org/
www.grails.org<path> grails.apache.org<path>

The four subdomains views/async/testing/gsp.grails.org are mapped to the equivalent anchor on the canonical site because the legacy paths are dropped at redirect time anyway.

Intentionally NOT touched

  • gorm.grails.org/* - some paths (notably /latest/neo4j/manual/...) have no canonical replacement on grails.apache.org because GORM for Neo4j has not been updated for Grails 7/8. The gorm.grails.org URL is still the current canonical destination for those readers.
  • start.grails.org, slack.grails.org, repo.grails.org, prev-snapshot.grails.org - canonical hosts that serve content directly (Forge, Slack signup, JFrog Maven repo, Grails 8 snapshot Forge).
  • grails.org/buildstatus.html - redirects to a GitHub README, not back to grails.apache.org, so it falls outside the redirect-back-to-here scope of this change.
  • buildSrc/src/test/resources/parity-baseline/ - golden HTML snapshot of the legacy site, used by ParityCheckGuideTask as a regression-test fixture; rewriting it would corrupt the source-of-truth the renderer is diffed against.
  • Buildscripts that intentionally reference guides.grails.org as the legacy base for the redirect-stub generator and parity baseline: AdHocFixtureDiff.groovy, GenerateRedirectsManifestTask.groovy, GenerateRedirectStubsTask.groovy, ParityCheckGuideTask.groovy, StructuralDiffGuidesTask.groovy.
  • README.md and buildSrc/VENDOR.md - both document the migration itself.
  • minutes/20210321-tab.md - frozen TAB historical record.

How it was done

A repeatable Groovy script walked the source tree applying ordered regex replacements (kept in C:\Users\james\AppData\Local\Temp\opencode\rewrite-redirect-links.groovy while writing this PR). The script also emitted an inventory of every *.grails.org reference it deliberately left untouched so reviewers can sanity-check the skip list.

Files changed by directory

Directory Files
guides/ 228
posts/ 6
buildSrc/ 4 (kapa.ai widget disclaimer in chrome HTML)
templates/ 1 (the header)

Verification

  • ./gradlew validateGuides -PvalidationMode=both -> 93 guides parsed, 0 errors
  • ./gradlew build -> renderSite succeeds; rendered Plugins link in build/dist/index.html is now https://grails.apache.org/plugins.html

A final rg https?://[a-z0-9-]+\.grails\.org over the source tree shows the only remaining matches are in the explicitly-preserved files listed above.

…rg targets

The Plugins link in the site header pointed at https://plugins.grails.org,
which is kept alive only as a meta-refresh redirect back to
https://grails.apache.org/plugins.html. Many other internal links in the
guides, blog posts, and shared chrome had the same shape - they targeted a
legacy *.grails.org host that exists today only to bounce visitors to the
canonical grails.apache.org URL. Each of those redirects costs the user
an extra hop, hurts SEO, and breaks if any of the legacy hosts ever go
away.

Replace each redirect-bound URL with its canonical destination so internal
links resolve directly:

- plugins.grails.org -> grails.apache.org/plugins.html (paths under
  /plugin/<vendor>/<name> are 404, so they collapse to the landing page)
- guides.grails.org<path> -> grails.apache.org/guides<path> (path-preserving)
- docs.grails.org<path> -> grails.apache.org/docs<path> (path-preserving)
- views.grails.org<path> -> grails.apache.org/docs/latest/guide/theWebLayer.html#gson
- async.grails.org<path> -> grails.apache.org/docs/latest/guide/async.html
- testing.grails.org<path> -> grails.apache.org/docs/latest/guide/testing.html
- gsp.grails.org<path> -> grails.apache.org/docs/latest/guide/theWebLayer.html#gsp
  (the four subdomains above all redirect to the same fixed anchor on the
  canonical site - the legacy path component is dropped at redirect time
  anyway, so we point straight at the anchor)
- grails.org/<X>.html -> grails.apache.org/<X>.html
- grails.org/learn.html -> grails.apache.org/learning.html (page renamed)
- grails.org/plugin/<X> -> grails.apache.org/plugins.html (legacy 404)
- grails.org/blog<path> -> grails.apache.org/blog<path>
- grails.org/ root and bare grails.org -> grails.apache.org/

Intentionally NOT touched:

- gorm.grails.org/* - some paths (notably /latest/neo4j/manual/...) have
  no canonical replacement on grails.apache.org because GORM for Neo4j
  has not been updated for Grails 7/8. The gorm.grails.org URL is still
  the current canonical destination for those readers.
- start.grails.org, slack.grails.org, repo.grails.org,
  prev-snapshot.grails.org - these are canonical hosts that serve content
  directly (the Forge, the Slack signup, the JFrog Maven repo, and the
  Grails 8 snapshot Forge).
- grails.org/buildstatus.html - redirects to a GitHub README, not back
  to grails.apache.org, so it falls outside the redirect-back-to-here
  scope of this change.
- buildSrc/src/test/resources/parity-baseline/ - golden HTML snapshot of
  the legacy site, used by ParityCheckGuideTask as a regression-test
  fixture; rewriting it would corrupt the source-of-truth the renderer
  is diffed against.
- buildSrc/src/main/groovy/website/qa/AdHocFixtureDiff.groovy,
  buildSrc/src/main/groovy/website/gradle/tasks/Generate{Redirects,
  RedirectStubs}{Manifest,}Task.groovy, ParityCheckGuideTask.groovy, and
  StructuralDiffGuidesTask.groovy - these intentionally reference
  guides.grails.org as the LEGACY_BASE for the redirect-stub generator
  and parity baseline.
- README.md and buildSrc/VENDOR.md - both document the migration itself,
  so the legacy URLs need to remain readable.
- minutes/20210321-tab.md - frozen historical record from the TAB.

Verified locally with `./gradlew validateGuides -PvalidationMode=both`
(93 guides parsed, 0 errors) and `./gradlew build` (renderSite succeeds;
the rendered Plugins link in build/dist/index.html is now
https://grails.apache.org/plugins.html as expected).

Assisted-by: claude-code:claude-opus-4-7
Copilot AI review requested due to automatic review settings May 3, 2026 23:31
@jamesfredley jamesfredley merged commit 584dbff into master May 3, 2026
5 checks passed
@jamesfredley jamesfredley deleted the fix-redirect-links branch May 3, 2026 23:45
@jamesfredley jamesfredley review requested due to automatic review settings May 3, 2026 23:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant