jamesfredley commented on issue #354:
URL:
https://github.com/apache/grails-static-website/issues/354#issuecomment-3904912096
# Grails Guides: Publishing Architecture and Migration Plan
This document describes how Grails guide publishing works today, what's
wrong with
it, and the plan to consolidate everything into
`apache/grails-static-website`.
---
## Table of Contents
### Part 1 -- Current System
- [Numbers at a Glance](#numbers-at-a-glance)
- [Current Architecture Overview](#current-architecture-overview)
- [Complete Repo Inventory](#complete-repo-inventory)
- [Repos with both initial/ and complete/
(79)](#repos-with-both-initial-and-complete-directories-79)
- [Repos with complete/ only
(14)](#repos-with-complete-only----no-initial-14)
- [Repos with neither (1)](#repos-with-neither-initial-nor-complete-1)
- [Repos not published in guides.json
(10)](#repos-in-org-but-not-published-in-guidesjson-10)
- [Known data issues](#known-data-issues)
- [Grails Version Analysis](#grails-version-analysis)
- [Branch distribution across repos](#branch-distribution-across-repos)
- [Published versions in guides.json](#published-versions-in-guidesjson)
- [What "multi-version" really means
today](#what-multi-version-really-means-today)
- [Unpublished branch analysis](#unpublished-branch-analysis)
- [How Publishing Works Today](#how-publishing-works-today)
- [Step 1: Scaffold the guide](#step-1-scaffold-the-guide)
- [Step 2: Create the GitHub repo](#step-2-create-the-github-repo)
- [Step 3: Set up CI manually](#step-3-set-up-ci-manually)
- [Step 4: CI runs and the guide gets
published](#step-4-ci-runs-and-the-guide-gets-published)
- [Step 5: The guide appears on
guides.grails.org](#step-5-the-guide-appears-on-guidesgrailsorg)
- [Current CI/CD Flow Diagram](#current-cicd-flow-diagram)
- [Current Component Reference](#current-component-reference)
- [Current Pain Points](#current-pain-points)
### Part 2 -- Migration Plan
- [Consolidation Plan](#consolidation-plan)
- [Phase 1: Move guide metadata here](#phase-1-move-guide-metadata-here)
- [Phase 2: Move common doc snippets and theme resources
here](#phase-2-move-common-doc-snippets-and-theme-resources-here)
- [Phase 3: Move guide-build.gradle
here](#phase-3-move-guide-buildgradle-here)
- [Phase 4: Move ALL guide AsciiDoc source
here](#phase-4-move-all-guide-asciidoc-source-here)
- [Phase 5: Simplify deployment](#phase-5-simplify-deployment)
- [Phase 6: Strip doc-publishing from guide
repos](#phase-6-strip-doc-publishing-from-guide-repos)
- [Phase 7: Wind down
grails-guides-template](#phase-7-wind-down-grails-guides-template)
- [URL Redirect Plan](#url-redirect-plan)
- [Migration Checklist](#migration-checklist)
### Part 3 -- New System (After Migration)
- [How Publishing Will Work After
Migration](#how-publishing-will-work-after-migration)
- [Adding a new guide
(doc-only)](#adding-a-new-guide-doc-only-no-sample-project)
- [Adding a new guide with sample
projects](#adding-a-new-guide-with-sample-projects)
- [Updating an existing guide](#updating-an-existing-guide)
- [Adding a new Grails version to an existing
guide](#adding-a-new-grails-version-to-an-existing-guide)
- [How versioning simplifies with
migration](#how-versioning-simplifies-with-migration)
- [Comparison: Before vs After](#comparison-before-vs-after)
- [Key Files Reference](#key-files-reference)
---
# Part 1 -- Current System
## Numbers at a Glance
| Metric | Count |
|--------|-------|
| Repos in `grails-guides` org (excluding template) | 94 |
| Repos published in `guides.json` | 84 |
| Total `guides.json` entries (including multi-version) | 125 |
| Single-version guides in `guides.json` | 44 |
| Multi-version guides in `guides.json` | 40 |
| Repos in org but NOT in `guides.json` (unpublished) | 10 |
| Repos with both `initial/` and `complete/` dirs | 79 |
| Repos with `complete/` only (no `initial/`) | 14 |
| Repos with neither `initial/` nor `complete/` | 1 |
| Repos with a `grails6` branch | 3 |
| Repos with a `grails5` branch | 37 |
| Repos with a `grails-4` or `grails4` branch | 27 |
| Repos with a `grails3` branch | 40 |
---
## Current Architecture Overview
Guide publishing spans **three layers**:
1. **94 individual guide repos** in the `grails-guides` GitHub org (e.g.
`grails-guides/creating-your-first-grails-app`) -- each is a standalone
Grails
project containing AsciiDoc source, an `initial/` starter project, and a
`complete/` solution.
2. **`grails-guides/grails-guides-template`** (infrastructure + deployment
repo) --
provides the shared build plugin (`gradle/guide-build.gradle`), guide
scaffolding
script (`create-guide.sh`), CI/CD scripts (`githubactions/`), common
AsciiDoc
snippets (`src/main/docs/`), and theme resources (`src/main/resources/`).
Its **`gh-pages` branch** stores the built HTML for every guide, the
central
`guides.json` registry, and the guides index site served at
`https://guides.grails.org`.
3. **`apache/grails-static-website`** (this repo) -- generates the guides
*index site*
(listing, search, tag/category pages) by fetching `guides.json` at build
time.
The output is pushed back to `grails-guides/grails-guides-template`
`gh-pages`.
> **Note**: Many CI scripts and individual guide `build.gradle` files still
reference
> the old URL `grails/grails-guides`. This was the former location of
> `grails-guides/grails-guides-template` before it was moved to the
`grails-guides`
> GitHub org. GitHub redirects mask this, but the references are stale.
### The `grails-guides` GitHub Org
The `grails-guides` GitHub org contains **95 repositories**:
- **`grails-guides-template`** -- the infrastructure repo described above.
- **94 individual guide repositories** -- each named after the guide topic.
Every individual guide repo in the org:
- Has its own GitHub Actions CI workflow (manually set up -- not generated
by the
scaffolding script)
- May maintain version branches (`master`, `grails3`, `grails4`, `grails5`,
`grails6`)
- Applies `guide-build.gradle` remotely from `grails-guides-template` at
Gradle build time
- Downloads and executes CI scripts from `grails-guides-template` at CI
runtime via `curl`
- Pushes built HTML and metadata to `grails-guides-template` `gh-pages`
---
## Complete Repo Inventory
### Repos with both `initial/` and `complete/` directories (79)
These repos contain runnable sample Grails applications. After migration,
these
repos will **remain in the `grails-guides` org** as sample project repos
(with
doc-publishing stripped out). AsciiDoc source moves to
`grails-static-website`.
| Repo | Published | Repo branches |
|------|-----------|---------------|
| `adding-commit-info` | Yes (master, grails3) | fix-commits-upgrade,
gradle4, grails3, grails-3.2.8, grails-4, master |
| `angular2-combined` | Yes | grail-4, master |
| `building-a-react-app` | Yes (master) | master |
| `building-a-vue-app` | Yes (master) | master, rewrite |
| `building-an-android-client-powered-by-a-grails-backend` | Yes (master) |
master |
| `building-an-ios-objectc-client-powered-by-a-grails-backend` | Yes
(master) | grails5, grails-4, master |
| `building-an-ios-swift-client-powered-by-a-grails-backend` | Yes (master)
| grails5, grails-4, master, upgrade |
| `command-objects-and-forms` | Yes (master, grails3) | grails3, grails-4,
master, tests, upgrade |
| `creating-your-first-grails-app` | Yes (master, grails3) | grails3,
grails4, master, upgrade |
| `database-per-tenant` | Yes (master) | grails5, ilopmar-patch-1, master |
| `discriminator-per-tenant` | Yes (master) | grails5, master |
| `gorm-event-listeners` | Yes (master, grails3) | fix_typos, grails3,
grails-4, many-to-many, master, without-tags |
| `gorm-graphql-with-react-and-apollo` | Yes (master) | master, + many
dependabot branches |
| `gorm-ratpack` | Yes (master, grails3) | grails3, master |
| `grails_api_ai` | **No** | master |
| `grails_i18n` | Yes (master, grails3) | grails3, master |
| `grails_url_mappings` | Yes (master) | grails5, master |
| `grails-android-security` | **No** | master, upgrade |
| `grails-as-docker-container` | Yes (master, grails3) | grails3, grails5,
grails-4, guide, master |
| `grails-async-promises` | Yes (master, grails3) | grails3, master |
| `grails-basicauth` | Yes (master, grails3) | grails3, master |
| `grails-code-coverage` | Yes (master, grails3) | grails3, master |
| `grails-codenarc` | Yes (master) | master |
| `grails-configuration-properties-micronaut` | Yes (grails4, grails6) |
basics-g5-changes, grails4, grails5, grails6, master |
| `grails-controller-testing` | Yes (master, grails3) | grails_three_three,
grails3, master |
| `grails-custom-security-tenant-resolver` | Yes (master, grails3) |
current-user-with-spring-security-service, grails3, master |
| `grails-database-migration` | Yes (grails4, grails3, grails6) | grails3,
grails5, grails6, master |
| `grails-deploy-aws-elastic-beanstalk` | **No** | master |
| `grails-deploy-heroku` | **No** | master |
| `grails-deploy-pws` | **No** | grails5, grails-4, master |
| `grails-docker-external-services` | Yes (master) | grails5, master |
| `grails-dynamic-multiple-datasources` | Yes (master, grails3) | grails3,
grails5, master |
| `grails-elasticsearch` | Yes | master |
| `grails-email` | Yes (master, grails3) | grails3, grails5, master |
| `grails-events` | Yes (master, grails3) | grails3, master |
| `grails-geb-multiple-browsers` | Yes (master, grails3) | grails3, master |
| `grails-google-cloud` | Yes (master) | grails5, grails-4,
grails-gr8-demos, master |
| `grails-google-home` | Yes (master) | alvarosanchez-patch-1, grails5,
grails-4, master |
| `grails-gorm-data-services` | Yes (master, grails3) | grails3, grails-4,
master |
| `grails-ios-objectivec-security` | **No** | master, upgrade |
| `grails-javamelody` | Yes | grails5, master |
| `grails-logicaldelete` | Yes (master, grails3) | grails3, grails5, master |
| `grails-micronaut-http` | Yes (grails5) | bean-inject-error, grails5,
grails6, master |
| `grails-mock-basics` | Yes (master, grails3) | grails3, master |
| `grails-mock-http-server` | Yes (master, grails3) | grails3, grails5,
master |
| `grails-mock-logging-slf4j-test` | Yes (master, grails3) | grails3,
grails5, grails-4-datastore-rest-client, grails-4, master |
| `grails-multi-datasource` | Yes (master, grails3) | error, grails3,
grails5, master |
| `grails-multi-project-build` | Yes (master) | grails5, grails-4, master,
upgrade_guide |
| `grails-oauth-facebook` | **No** | master |
| `grails-oauth-google` | Yes (master) | grails5, master, suggestions |
| `grails-oauth-twitter` | Yes (master) | grails5, master |
| `grails-on-circleci-basics` | Yes (master) | grails5, grails-4, master |
| `grails-on-github-actions` | Yes (master) | grails5, master |
| `grails-on-travis-basics` | Yes (master, grails3) | grails3, grails5,
master |
| `grails-rabbitmq` | Yes | grails5, grails-4, master |
| `grails-restapi-angularjs` | Yes (master) | grails5, grails-4, master |
| `grails-scheduled` | Yes (grails3, master) | grails3, grails5, grails-4,
master, suggestions |
| `grails-schwartz` | Yes | grails5, master |
| `grails-soap` | Yes (master, grails3) | grails3, master |
| `grails-spring-security-core-plugin-custom-authentication` | Yes (master,
grails3) | grails3, grails5, master |
| `grails-taglib-wyswyg-trix` | Yes (master, grails3) | grails3, grails-4,
master |
| `grails-test-domain-class-constraints` | Yes (master, grails3) |
errorCodes, grails_three_three, grails3, grails-4, master, unique-constraint |
| `grails-test-security` | Yes (master, grails3) | grails3, master |
| `grails-tvmlapp` | Yes (master, grails3) | grails3, grails5, master |
| `grails-upload-file` | Yes (master, grails3) | grails3, grails5, grails-4,
master |
| `grails-vs-nodejs` | Yes (master) | master |
| `grails-vue-combined` | Yes (master) | grails-4, master |
| `grails-yourkit-profiling` | Yes (master) | grails5, master |
| `neo4j-movies` | Yes | master, upgrade |
| `querying-gorm-dynamic-finders` | Yes (master, grails3) | grails3, master |
| `react-combined` | Yes | grails-4, master, review |
| `react-router-spring-security` | **No** | master |
| `react-spring-security` | Yes (master) | master, upgrade |
| `rest-hibernate` | Yes (master, grails3) | grails3, grails-4, master |
| `rest-mongodb` | Yes (master) | master, upgrade |
| `server-sent-events` | Yes (master, grails3) | grails3, grails5, grails-4,
master |
| `using-hal-with-json-views` | Yes (master, grails3) | grails3, master,
upgrade |
| `vaadin-grails` | Yes | 3.3.0.RC1, grails-4, master, upgrade |
| `web-app-step-by-step` | **No** | compilestatic, link-to-grails-download,
master, sdkman-link, sett, sett-rev |
### Repos with `complete/` only -- no `initial/` (14)
These repos have a completed sample project but no starter project. The 7
quickcast repos are video-companion code (no interactive tutorial flow).
After
migration, repos with meaningful sample code remain in the org; doc-only
repos
(quickcasts) can be archived entirely.
| Repo | Published | Repo branches | Notes |
|------|-----------|---------------|-------|
| `gorm-without-grails` | Yes (master) | grails3, master | Standalone GORM
demo |
| `grails-configuration-properties` | Yes (master, grails3) | grails3,
master | |
| `grails-elasticbeanstalk` | Yes (master) | grails5, grails-4, master | |
| `grails-file-download-excel` | Yes (master, grails3) | grails3, grails5,
master | |
| `grails-micronaut-kafka` | Yes (master)* | master | *Typo in
`guides.json`: listed as `grails-micronaut-kakfa` |
| `grails-quickcast-logging` | Yes | master | Quickcast (video companion) |
| `grails-quickcasts-angularjs-scaffolding-with-grails-3` | Yes | master |
Quickcast |
| `grails-quickcasts-developing-grails-3-applications-with-intellij-idea` |
Yes | master | Quickcast |
| `grails-quickcasts-interceptors` | Yes | master | Quickcast |
| `grails-quickcasts-json-views` | Yes | master | Quickcast |
| `grails-quickcasts-multi-project-builds` | Yes | master | Quickcast |
| `grails-quickcasts-retrieving-config-values` | Yes | master | Quickcast |
| `using-the-react-profile` | Yes (master) | master | |
| `using-the-vue-profile` | Yes (master, grails3) | dependabot branch,
grails3, master | |
### Repos with neither `initial/` nor `complete/` (1)
| Repo | Published | Repo branches | Notes |
|------|-----------|---------------|-------|
| `grails-acl` | **No** | master | Doc-only, never published |
### Repos in org but NOT published in `guides.json` (10)
These repos exist in the `grails-guides` org but have no corresponding entry
in
`guides.json`, meaning they do not appear on `https://guides.grails.org`.
Their
CI may never have successfully run `updateGuidesJson`, or they may be
incomplete.
| Repo | `initial/` | `complete/` | Branches |
|------|------------|-------------|----------|
| `grails_api_ai` | yes | yes | master |
| `grails-acl` | no | no | master |
| `grails-android-security` | yes | yes | master, upgrade |
| `grails-deploy-aws-elastic-beanstalk` | yes | yes | master |
| `grails-deploy-heroku` | yes | yes | master |
| `grails-deploy-pws` | yes | yes | grails5, grails-4, master |
| `grails-ios-objectivec-security` | yes | yes | master, upgrade |
| `grails-oauth-facebook` | yes | yes | master |
| `react-router-spring-security` | yes | yes | master |
| `web-app-step-by-step` | yes | yes | compilestatic,
link-to-grails-download, master, sdkman-link, sett, sett-rev |
### Known data issues
- **Typo in `guides.json`**: `grails-micronaut-kakfa` should be
`grails-micronaut-kafka`
- **Category case inconsistency**: `guides.json` has both `"Grails +
DevOps"` (6 guides)
and `"Grails + Devops"` (1 guide) -- should be unified
- **Branch value issue**: `grails-on-travis-basics` has a `guides.json`
entry with
`githubBranch` set to `"master,grails3"` (comma inside the value) instead
of being
two separate entries
- **Underscored repo names**: `grails_api_ai`, `grails_i18n`,
`grails_url_mappings`
use underscores instead of hyphens (inconsistent with all other repo names)
---
## Grails Version Analysis
### Branch distribution across repos
Repos use version branches to maintain guides for different Grails versions.
The
branch naming is inconsistent -- some use `grails4`, others use `grails-4`:
| Branch | Repos | Notes |
|--------|-------|-------|
| `master` | 93 | Present in almost every repo; maps to Grails 4 in
`guides.json` |
| `grails3` | 40 | Grails 3.x version |
| `grails-4` | 25 | Grails 4.x version (hyphenated variant) |
| `grails4` | 2 | Grails 4.x version (non-hyphenated variant) |
| `grails5` | 37 | Grails 5.x version |
| `grails6` | 3 | Grails 6.x version |
**Only 3 repos have been updated to Grails 6**:
`grails-configuration-properties-micronaut`,
`grails-database-migration`, and `grails-micronaut-http`.
### Published versions in `guides.json`
The `guides.json` branch-to-major-version mapping (in
`GuidesFetcher.groovy`):
```groovy
BRANCH_TO_MAJOR = [grails3: 3, grails4: 4, master: 4, grails5: 5, grails6: 6]
```
This means `master` = Grails 4 in the published guides. The mapping does NOT
include `grails-4` (hyphenated), so repos with a `grails-4` branch but no
`grails4` branch are effectively not publishing a separate Grails 4 version
--
their `master` branch serves as the Grails 4 version.
Of the 40 multi-version guides in `guides.json`:
- **37** have exactly `master` + `grails3` (Grails 4 + Grails 3)
- **1** has `grails4` + `grails6`
(`grails-configuration-properties-micronaut`)
- **1** has `grails4` + `grails3` + `grails6` (`grails-database-migration`)
- **1** has a data issue (`grails-on-travis-basics` -- comma-separated in
one entry)
### What "multi-version" really means today
In practice, almost all multi-version guides are just Grails 3 vs Grails 4
(`master`).
Despite 37 repos having a `grails5` branch and 25 having a `grails-4`
branch, these
branches have NOT been published to `guides.json` because:
1. The `githubactions-build.sh` template curls `build-guide` from
`grails-guides-template` **`master`** branch
2. The `master` branch version of `build-guide` only handles `master` and
`grails3`
branches for publishing
3. The `6.0.x` branch version handles `grails3`-`grails6` but is not used by
any
guide repo's CI
This means the `grails5` branches in 37 repos and `grails-4` branches in 25
repos
exist in Git but their built docs were **never published**.
### Unpublished branch analysis
The `grails5` and `grails-4` branches were never published to `guides.json`
(see
above), but many contain distinct content that should be considered for
publishing
during migration. The table below shows each branch's relationship to
`master`.
**`grails5` branches (37 repos)**:
| Status | Count | Meaning |
|--------|-------|---------|
| Ahead of master | 35 | Branch has unique commits not in master --
**distinct content** |
| Behind master | 2 | Branch is a subset of master -- no unique content |
| Repo | Ahead | Behind | Status | Commit summary | File-level changes |
|------|-------|--------|--------|----------------|--------------------|
| `building-an-ios-objectc-client-powered-by-a-grails-backend` | 1 | 0 |
ahead | grails5 upgrade | 19 files. Notable: `AnnouncementService.groovy`
modified (5 lines), `AnnouncementControllerSpec.groovy` modified (85 lines) |
| `building-an-ios-swift-client-powered-by-a-grails-backend` | 1 | 0 | ahead
| grails5 upgrade | 20 files. Notable: `AnnouncementService.groovy` modified (4
lines), `AnnouncementControllerSpec.groovy` modified (85 lines) |
| `database-per-tenant` | 1 | 0 | ahead | grails5 upgrade | 20 files.
Notable: codenarc `rules.groovy`, `testrules.groovy`, `codenarc.gradle` removed
|
| `discriminator-per-tenant` | 1 | 0 | ahead | grails5 upgrade | 19 files.
Notable: `tenantSelection.adoc` modified (2 lines) |
| `grails_url_mappings` | 0 | 1 | behind | *(no unique content)* | -- |
| `grails-as-docker-container` | 1 | 0 | ahead | "grails4 upgrade" *(may be
mislabeled)* | 18 files: build infra only |
| `grails-configuration-properties-micronaut` | 4 | 0 | ahead | grails 5
upgrade + CI updates | 14 files. Notable: `gradle.yml` workflow modified,
`githubactions-build.sh` modified |
| `grails-database-migration` | 1 | 0 | ahead | grails5 upgrade | 18 files:
build infra only |
| `grails-deploy-pws` | 1 | 0 | ahead | grails5 upgrade | 20 files. Notable:
`deployWithGradle.adoc` modified (2 lines) |
| `grails-docker-external-services` | 1 | 0 | ahead | grails 5 upgrade | 18
files: build infra only |
| `grails-dynamic-multiple-datasources` | 1 | 0 | ahead | grails5 upgrade |
2 files: 2 `build.gradle` only |
| `grails-elasticbeanstalk` | 1 | 0 | ahead | "grails5 - problem with
management endpoint" *(may be incomplete)* | 11 files: build infra only |
| `grails-email` | 1 | 0 | ahead | grails 5 upgrade | 18 files: build infra
only |
| `grails-file-download-excel` | 1 | 0 | ahead | "grails5 update - geb
issue" *(Geb problem noted)* | 11 files: build infra only |
| `grails-google-cloud` | 1 | 0 | ahead | grails5 upgrade | 19 files.
Notable: `UploadBookFeaturedImageService.groovy` modified (5 lines) |
| `grails-google-home` | 1 | 0 | ahead | grails 5 upgrade | 16 files: build
infra only |
| `grails-javamelody` | 1 | 0 | ahead | grails5 upgrade | 22 files. Notable:
2 `GebConfig.groovy` removed, `gettingStarted.adoc` modified (2 lines) |
| `grails-logicaldelete` | 1 | 0 | ahead | "grails5 upgrade - geb issue"
*(Geb problem noted)* | 18 files: build infra only |
| `grails-micronaut-http` | 4 | 0 | ahead | CI/config updates | 3 files:
`gradle.yml`, `githubactions-build.sh`, `gradle.properties` only |
| `grails-mock-http-server` | 1 | 0 | ahead | grails5 | 19 files. Notable:
`OpenweathermapServiceSpec.groovy` modified (2 lines) |
| `grails-mock-logging-slf4j-test` | 1 | 0 | ahead | grails5 changes | 18
files: build infra only |
| `grails-multi-datasource` | 1 | 0 | ahead | grails 5 upgrade | 19 files.
Notable: `MultipleDataSourceSpec.groovy` modified (16 lines) |
| `grails-multi-project-build` | 1 | 0 | ahead | grails5 upgrade | 11 files:
build infra only |
| `grails-oauth-google` | 1 | 0 | ahead | grails5 upgrade | 20 files.
Notable: `howto.adoc` modified (4 lines) |
| `grails-oauth-twitter` | 2 | 0 | ahead | grails 5 upgrade (2 commits) | 22
files. Notable: `GebConfig.groovy` removed, `howto.adoc` modified (4 lines) |
| `grails-on-circleci-basics` | 1 | 0 | ahead | grails5 upgrade | 18 files:
build infra only |
| `grails-on-github-actions` | 1 | 0 | ahead | grails5 upgrade | 17 files:
build infra only |
| `grails-on-travis-basics` | 1 | 0 | ahead | grails5 upgrade | 20 files.
Notable: `DefaultHomePageSpec.groovy` modified (3 lines),
`integrationTest.adoc` modified (2 lines) |
| `grails-rabbitmq` | 2 | 0 | ahead | grails5 upgrade (2 commits) | 35
files. Notable: `BookPageViewDataServiceSpec.groovy` modified (2 lines),
`buildingAnalytics.adoc` modified (4 lines) |
| `grails-restapi-angularjs` | 1 | 0 | ahead | grails5 upgrade | 19 files:
build infra only |
| `grails-scheduled` | 1 | 0 | ahead | grails 5 upgrade | 18 files: build
infra only |
| `grails-schwartz` | 1 | 0 | ahead | grails5 upgrade | 21 files. Notable: 2
`GebConfig.groovy` removed |
| `grails-spring-security-core-plugin-custom-authentication` | 1 | 0 | ahead
| grails5 upgrade | 18 files: build infra only |
| `grails-tvmlapp` | 1 | 0 | ahead | grails5 upgrade | 18 files. Notable:
`QuickcastSpec.groovy` modified (3 lines) |
| `grails-upload-file` | 1 | 0 | ahead | grails5 upgrade | 19 files.
Notable: codenarc `rules.groovy` removed |
| `grails-yourkit-profiling` | 2 | 0 | ahead | grails5 upgrade (2 commits) |
18 files: build infra only |
| `server-sent-events` | 0 | 1 | behind | *(no unique content)* | -- |
> **Summary**: 616 files across 35 repos. "Build infra" = gradle wrapper,
build.gradle, gradle.properties, application.yml, settings.gradle. Only 15 of
35 repos have any changes beyond build infra. The Grails 5 migration is almost
entirely a build/infrastructure upgrade -- only 10 source/test files were
touched across all repos.
**`grails-4` branches (25 repos)**:
| Status | Count | Meaning |
|--------|-------|---------|
| Diverged from master | 15 | Both branch and master have unique commits --
**distinct content** |
| Behind master | 10 | Branch is a subset of master -- no unique content |
| Repo | Ahead | Behind | Status | Commit summary | File-level changes |
|------|-------|--------|--------|----------------|--------------------|
| `adding-commit-info` | 0 | 16 | behind | *(no unique content)* | -- |
| `building-an-ios-objectc-client-powered-by-a-grails-backend` | 2 | 1 |
diverged | Gradle 5 upgrade, Grails 4 upgrade with client HTTP change | 12
files. Notable: `AnnouncementService.groovy` modified (2 lines),
`AnnouncementControllerSpec.groovy` modified (82 lines) |
| `building-an-ios-swift-client-powered-by-a-grails-backend` | 3 | 1 |
diverged | Gradle 5 upgrade, Grails 4 upgrade with HTTP client change, add view
plugin | 10 files. Notable: `AnnouncementService.groovy` modified (2 lines),
`AnnouncementControllerSpec.groovy` modified (82 lines) |
| `command-objects-and-forms` | 0 | 32 | behind | *(no unique content)* | --
|
| `gorm-event-listeners` | 0 | 23 | behind | *(no unique content)* | -- |
| `grails-as-docker-container` | 0 | 15 | behind | *(no unique content)* |
-- |
| `grails-deploy-pws` | 2 | 1 | diverged | Gradle wrapper upgrade (2
commits) | 9 files: build infra only |
| `grails-elasticbeanstalk` | 3 | 1 | diverged | Gradle 5, Grails 4 (Spring
Boot 2 health actuator change), actuator path fix | 10 files. Notable:
`HealthSpec.groovy` modified (2 lines), `deploy.adoc` modified (2 lines),
`writingTheApp.adoc` modified (88 lines) |
| `grails-google-cloud` | 3 | 1 | diverged | Gradle upgrade, Grails 4
(pathingJar needed), test correction | 11 files. Notable:
`UploadBookFeaturedImageService.groovy` modified (5 lines), `BookSpec.groovy`
modified (8 lines) |
| `grails-google-home` | 3 | 2 | diverged | Gradle 4 upgrade, Grails 4
upgrade, remove codenarc | 10 files. Notable: codenarc `rules.groovy` removed,
`codenarc.gradle` removed |
| `grails-gorm-data-services` | 2 | 22 | diverged | Upgrade to Grails 4 (2
commits) | 11 files. Notable: `BootStrap.groovy` modified (1 line), 2 `.gradle`
cache files removed |
| `grails-mock-logging-slf4j-test` | 0 | 14 | behind | *(no unique content)*
| -- |
| `grails-multi-project-build` | 0 | 28 | behind | *(no unique content)* |
-- |
| `grails-on-circleci-basics` | 0 | 23 | behind | *(no unique content)* | --
|
| `grails-rabbitmq` | 2 | 1 | diverged | Gradle 5 upgrade, Grails 4 upgrade
| 16 files: build infra only (4 subprojects) |
| `grails-restapi-angularjs` | 2 | 1 | diverged | Gradle 5 upgrade, Grails 4
upgrade | 9 files: build infra only |
| `grails-scheduled` | 0 | 4 | behind | *(no unique content)* | -- |
| `grails-taglib-wyswyg-trix` | 0 | 27 | behind | *(no unique content)* | --
|
| `grails-test-domain-class-constraints` | 26 | 42 | diverged | Grails 3.3.8
-> 4.0 SNAPSHOT: Gradle 5, GORM 7, Hibernate 5.3.7, asset-pipeline, codenarc,
remove Geb/mavenLocal, add openjdk11, update views | 83 files. Notable:
`.travis.yml` modified, codenarc `rules.groovy` modified, `HotelSpec.groovy`
modified, `GebConfig.groovy` added, 44 scaffold assets refreshed (images, JS,
CSS, GSP views for Grails 4 templates) |
| `grails-upload-file` | 9 | 24 | diverged | Gradle wrapper, asset-pipeline,
Hibernate core, Grails/GORM versions, replace addResources, codenarc | 20
files. Notable: `HotelController.groovy`, `PointOfInterestController.groovy`,
`RestaurantController.groovy` modified (2 lines each) |
| `grails-vue-combined` | 9 | 1 | diverged | Grails 4 server upgrade, views
compile fix (#208), Grails 4 RC1 + Vue profile 2 | 121 files. Full webpack to
Vue CLI migration: ~25 webpack/eslint/nightwatch/jest configs removed, Vue CLI
configs added, `yarn.lock` added, client assets updated (bootstrap.css 13K
lines, grails.css, Welcome.vue), server `Application.groovy` and
`logback.groovy` modified, `config.adoc` modified |
| `react-combined` | 7 | 3 | diverged | Gradle 5, Grails 4 upgrade, views
compile fix, fix issue #3 | 13 files. Notable: project restructured
(`gradle.properties` moved to `server/`), `writingTheApp.adoc` modified (3
lines) |
| `rest-hibernate` | 20 | 37 | diverged | Gradle 5, Grails 4.0 SNAPSHOT,
GORM 7, remove Geb/mavenLocal, GORM Data Service, @CompileStatic, codenarc, add
func tests | 27 files. Notable: `ProductController.groovy` modified (8 lines),
`BootStrap.groovy` modified (3 lines), `ProductService.groovy` added (9 lines),
`HomeSpec.groovy` added (37 lines), `ProductFunctionalSpec.groovy` added (157
lines), `ProductControllerSpec.groovy` modified (15 lines), codenarc configs
modified |
| `server-sent-events` | 0 | 18 | behind | *(no unique content)* | -- |
| `vaadin-grails` | 2 | 1 | diverged | Gradle 5 + Grails upgrade, guide text
changes | 16 files. Notable: `UrlMappings.groovy` modified,
`DemoGrailsUI.groovy` modified, `README.md` modified, `gettingStarted.adoc`,
`profile.adoc`, `toc.yml` modified |
> **Summary**: 377 files across 15 diverged repos. More diverse than Grails
5 due to the Grails 3-to-4 scope. Outliers: `grails-vue-combined` (121 files,
webpack to Vue CLI migration), `grails-test-domain-class-constraints` (83
files, scaffold assets), `rest-hibernate` (27 files, new service + functional
tests). The 10 "behind" branches are subsets of master with no unique work.
**`grails4` branches (2 repos)**:
| Repo | Ahead | Behind | Status |
|------|-------|--------|--------|
| `creating-your-first-grails-app` | 0 | 30 | behind (no unique content) |
| `grails-configuration-properties-micronaut` | 0 | 0 | identical to master |
Neither `grails4` branch has unique content.
---
## How Publishing Works Today
This is the end-to-end lifecycle of a guide in the current system, from
creation
to appearing on `https://guides.grails.org`:
### Step 1: Scaffold the guide
An author clones `grails-guides-template` and runs `create-guide.sh`:
```bash
./create-guide.sh my-new-guide
```
This script:
1. Runs `grails create-app my-new-guide` to generate a Grails app
2. Renames the app directory to `initial/`
3. Copies `initial/` to `complete/`
4. Writes `settings.gradle` with `include 'complete', 'initial'`
5. Copies template files from `src/main/project/` into the guide root
The template files copied are:
- `build.gradle` -- applies `guide-build.gradle` remotely
- `gradle.properties` -- metadata stub with `TODO` placeholders
- `githubactions-build.sh` -- CI entrypoint (curls scripts at runtime)
- `src/main/docs/guide/` -- AsciiDoc skeleton (`toc.yml`,
`gettingStarted.adoc`, etc.)
**What is NOT created by the script:**
- `.github/workflows/` -- the GitHub Actions workflow YAML must be created
manually
- No CI configuration is auto-generated
### Step 2: Create the GitHub repo
The author manually:
1. Creates a new repository in the `grails-guides` org on GitHub
2. The repo name must match the guide directory name
3. Pushes the scaffolded project
### Step 3: Set up CI manually
The author must manually:
1. Create `.github/workflows/gradle.yml` (or similar) -- there is no
template for this
2. Configure repository secrets: `GH_TOKEN`, `GIT_NAME`, `GIT_EMAIL`
A typical workflow looks like this (from `creating-your-first-grails-app`):
```yaml
name: Java CI with Gradle
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up JDK 11
uses: actions/setup-java@v2
with:
java-version: '11'
distribution: 'adopt'
cache: gradle
- name: Run script
run: ./githubactions-build.sh
env:
GH_TOKEN: ${{ secrets.GH_TOKEN }}
GIT_NAME: ${{ secrets.GIT_NAME }}
GIT_EMAIL: ${{ secrets.GIT_EMAIL }}
```
Note: many of these workflows are outdated (actions/checkout@v2, JDK 11,
adopt
distribution). The `grails-guides-template` README still references Travis
CI as
the CI system, with instructions for `travis encrypt` -- GitHub Actions was
retrofitted without updating the docs.
### Step 4: CI runs and the guide gets published
On push, the workflow runs `./githubactions-build.sh`. This is where it gets
wild:
#### 4a. `githubactions-build.sh` (in the guide repo)
This script does **not** contain any build logic. It downloads and executes
scripts
from `grails-guides-template` at runtime via `curl`:
```bash
#!/bin/bash
set -e
export EXIT_STATUS=0
# Download the build script from grails-guides-template master branch at
runtime
curl -O
https://raw.githubusercontent.com/grails/grails-guides/master/githubactions/build-guide
chmod 777 build-guide
./build-guide || EXIT_STATUS=$?
if [[ $EXIT_STATUS -ne 0 ]]; then
echo "build-guide failed"
exit $EXIT_STATUS
fi
# Download the republish script from grails-guides-template master branch at
runtime
curl -O
https://raw.githubusercontent.com/grails/grails-guides/master/githubactions/republish-guides-website.sh
chmod 777 republish-guides-website.sh
./republish-guides-website.sh || EXIT_STATUS=$?
exit $EXIT_STATUS
```
Every single guide repo has an identical copy of this file. All 94 of them
`curl`
the same two scripts from `grails-guides-template` `master` branch (using
the old
`grails/grails-guides` URL).
#### 4b. `build-guide` (curled from `grails-guides-template`)
This is the actual build logic. It:
1. Runs `./gradlew -Dgeb.env=chromeHeadless check` (tests)
2. Runs `./gradlew publishGuide` (AsciiDoc -> HTML)
3. **Only for specific branches** (`master`, `grails3`, `grails4`,
`grails5`, `grails6`):
- Clones `grails-guides-template` `gh-pages` branch
- Runs `./gradlew updateGuidesJson` -- **this is how a guide gets added to
`guides.json`** (the central registry). The task reads
`gradle.properties` and
upserts the guide's metadata into the JSON file.
- Copies built HTML to the appropriate directory on `gh-pages`:
- `master` branch -> `<guide-name>/` (root, no version prefix)
- `grails3` branch -> `grails3/<guide-name>/`
- `grails4` branch -> `grails4/<guide-name>/`
- etc.
- Commits and pushes to `gh-pages`
**Branch discrepancy**: The `master` branch of `grails-guides-template` has a
`build-guide` that only handles `master` and `grails3`. The `6.0.x` branch
version
handles `grails3`, `grails4`, `grails5`, `grails6` (not `master`). Since the
`githubactions-build.sh` template curls from `master`, guides using that
template
only get the older behavior.
#### 4c. `republish-guides-website.sh` (curled from `grails-guides-template`)
After the guide is built, this script **regenerates the entire guides index
site**:
1. Clones `grails/grails-static-website` (this repo, using old URL) `master`
branch
2. Runs `./gradlew buildGuide` -- which fetches `guides.json` from
`gh-pages` and
generates index/tag/category HTML pages
3. Clones `grails-guides-template` `gh-pages` again
4. Copies `build/dist/*` (the generated index pages) into `gh-pages`
5. Commits and pushes
This means **every single guide CI build triggers a full rebuild of the
entire guides
index site**. And the same rebuild also runs every 2 hours via the cron job
in
`grails-static-website`'s `publish.yml`.
### Step 5: The guide appears on `guides.grails.org`
Once the `gh-pages` push completes, the guide is live:
- Guide HTML: `https://guides.grails.org/<guide-name>/guide/index.html`
- Versioned:
`https://guides.grails.org/grails{N}/<guide-name>/guide/index.html`
- Listed on the index: `https://guides.grails.org/index.html`
- Searchable by tags and categories
The guide was added to `guides.json` by `updateGuidesJson` (step 4b) and the
index
was regenerated by `republish-guides-website.sh` (step 4c), so it shows up
in the
listing immediately.
---
## Current CI/CD Flow Diagram
```
Guide Author pushes to grails-guides/<guide-name>
|
v
.github/workflows/gradle.yml (manually created, per-repo)
|
v
githubactions-build.sh (identical in every repo)
|
|-- curl build-guide from grails-guides-template master (old URL)
|-- curl republish-guides-website.sh from grails-guides-template
master (old URL)
|
v
build-guide (downloaded at runtime)
|
|-- ./gradlew check (run tests)
|-- ./gradlew publishGuide (AsciiDoc -> HTML via
grails-docs)
| |
| |-- build.gradle: apply from (curls guide-build.gradle from
| | grails-guides-template master or 6.0.x, old URL)
| |
| |-- guide-build.gradle:
| | prepareResources (downloads
grails-guides-template
| | as ZIP to get theme
resources)
| | publishGuide (uses
org.grails:grails-docs:6.0.0-RC1)
| |
| v
| build/docs/guide/index.html
|
|-- git clone grails-guides-template gh-pages (old URL)
|-- ./gradlew updateGuidesJson (upserts into guides.json)
|-- cp build/docs -> gh-pages/grails{N}/<guide-name>/
|-- git push gh-pages
|
v
republish-guides-website.sh (downloaded at runtime)
|
|-- git clone grails-static-website (this repo, old URL)
|-- ./gradlew buildGuide
| |
| |-- GuidesFetcher: HTTP GET guides.json from gh-pages
| |-- GuidesTask: generates index.html, categories/*.html,
tags/*.html
| v
| build/dist/
|
|-- git clone grails-guides-template gh-pages (old URL)
|-- cp build/dist/* -> gh-pages/
|-- git push gh-pages
|
v
https://guides.grails.org (GitHub Pages from grails-guides-template
gh-pages)
```
Additionally, `grails-static-website`'s own `publish.yml` runs the same
`./gradlew buildGuide` + push to `grails-guides-template gh-pages`
independently:
- On every push to `master`
- On a cron schedule every 2 hours
- On manual `workflow_dispatch`
---
## Current Component Reference
### `grails-guides-template` structure (source branches: `master`, `6.0.x`)
```
grails-guides-template/
├── create-guide.sh # Scaffolds new guide projects
├── build.gradle # Downloads navbar assets from
grails-navbar repo
├── gradle/
│ └── guide-build.gradle # Shared build plugin applied by all
guides
├── githubactions/
│ ├── build-guide # CI: test + build + publish guide HTML
│ └── republish-guides-website.sh # CI: regenerate guides index site
├── src/main/
│ ├── docs/
│ │ ├── common-completesolution.adoc
│ │ ├── common-gradle.adoc
│ │ ├── common-howto.adoc
│ │ ├── common-installingGrails.adoc
│ │ ├── common-requirements.adoc
│ │ └── ... (~45 common-*.adoc snippets)
│ ├── project/ # Template copied into new guides
│ │ ├── build.gradle
│ │ ├── gradle.properties
│ │ ├── githubactions-build.sh
│ │ └── src/main/docs/guide/
│ │ ├── toc.yml
│ │ ├── gettingStarted.adoc
│ │ ├── howto.adoc
│ │ ├── requirements.adoc
│ │ ├── runningTheApp.adoc
│ │ └── writingTheApp.adoc
│ └── resources/ # Theme assets
│ ├── css/ # guide.css, screen.css, navbar.css,
etc.
│ ├── fonts/ # Archia family + FontAwesome
│ └── img/ # Grails logo, category icons
└── README.md # Still references Travis CI
```
### `grails-guides-template` `gh-pages` branch (deployment)
```
gh-pages/
├── guides.json # Central metadata registry (125
entries, 84 unique guides)
├── index.html # Guides listing (generated by
static-website)
├── categories/*.html # Per-category pages
├── tags/*.html # Per-tag pages
├── sitemap.xml
├── stylesheets/ # Site CSS
├── javascripts/ # Site JS
├── images/ # Site images
├── creating-your-first-grails-app/ # Guide HTML (master branch builds)
│ └── guide/index.html
├── grails3/ # Grails 3 versioned guides
│ └── <guide-name>/guide/index.html
├── grails4/ # Grails 4 versioned guides
├── grails5/ # Grails 5 versioned guides
└── grails6/ # Grails 6 versioned guides
```
### Individual guide repo structure (e.g. `creating-your-first-grails-app`)
```
creating-your-first-grails-app/
├── .github/workflows/
│ └── gradle.yml # Manually created, NOT from template
├── githubactions-build.sh # Curls build-guide + republish at
runtime
├── build.gradle # apply from: guide-build.gradle
(remote, old URL)
├── gradle.properties # Guide metadata
├── settings.gradle # include 'complete', 'initial'
├── initial/ # Starter Grails app
├── complete/ # Completed solution
├── src/main/
│ ├── docs/guide/
│ │ ├── toc.yml
│ │ └── *.adoc # Guide content
│ └── resources/img/ # Guide-specific images
├── gradle/wrapper/ # Gradle wrapper
├── gradlew / gradlew.bat
└── README.md
```
### Guide `build.gradle`
Every guide's `build.gradle` remotely applies the shared build script. Some
reference
`master`, some `6.0.x`:
```groovy
plugins {
id 'org.asciidoctor.convert' version '1.5.3'
}
asciidoctorj {
version = '1.5.4'
}
// NOTE: old URL; actual repo is grails-guides/grails-guides-template
apply
from:"https://raw.githubusercontent.com/grails/grails-guides/master/gradle/guide-build.gradle"
```
### Guide `gradle.properties`
```properties
title=Creating your first Grails Application
subtitle=Learn how to create your first Grails app
authors=Zachary Klein
copyright=Copies of this document may be made for your own use and for...
githubSlug=grails-guides/creating-your-first-grails-app
githubBranch=master
category=Grails Apprentice
tags=mysql,gsp,grails4
publicationDate=23 Jan 2017
```
| Property | Description | Required |
|-------------------|--------------------------------------|----------|
| `title` | Display title | Yes |
| `subtitle` | Short description | Yes |
| `authors` | Comma-separated authors | Yes |
| `githubSlug` | Repo identifier (`grails-guides/...`)| Yes |
| `githubBranch` | Git branch | Yes |
| `tags` | Comma-separated tags | Yes |
| `category` | Must match a predefined category | Yes |
| `publicationDate` | Format: `dd MMM yyyy` | Yes |
### Shared build plugin: `guide-build.gradle`
Applied remotely by every guide. Key tasks:
- **`prepareResources`** -- downloads `grails-guides-template` (branch
`6.0.x`) as
a ZIP from GitHub and extracts theme resources (CSS, fonts, images) and
common
AsciiDoc snippets. This is a network fetch on every build.
- **`copyLocalGuideImgResources`** -- copies guide-specific images from
`src/main/resources/img/` into the extracted build tree.
- **`publishGuide`** -- runs `grails.doc.gradle.PublishGuide` (from
`org.grails:grails-docs:6.0.0-RC1`) to convert AsciiDoc to HTML.
Output: `build/docs/guide/single.html` renamed to
`build/docs/guide/index.html`.
Also creates a redirect `build/docs/index.html`.
- **`updateGuidesJson`** -- reads `gh-pages/guides.json`, upserts the
current guide's
metadata from `gradle.properties`, writes the updated JSON back. This is
how a
guide registers itself in the central metadata file.
Validation at task graph resolution time enforces that all required
properties are
set and not `TODO`.
### Guides index generation (this repo: `grails-static-website`)
The `buildGuides` Gradle task (registered in `GrailsWebsitePlugin`)
orchestrates:
```
buildGuides
├── genGuides (GuidesTask) -- fetches guides.json, generates HTML pages
├── copyAssets (AssetsTask) -- copies CSS/JS/images
└── genSitemap (SitemapTask) -- generates sitemap.xml
```
**`GuidesFetcher`** (`buildSrc/.../model/guides/GuidesFetcher.groovy`):
- Fetches `guides.json` from
`https://raw.githubusercontent.com/grails-guides/grails-guides-template/gh-pages/guides.json`
- Parses each entry into a `GuideDto`
- Groups by `githubSlug`; creates `SingleGuide` (one branch) or
`GrailsVersionedGuide` (multiple branches like grails3/4/5/6)
- Branch-to-version mapping: `grails3`->3, `grails4`->4, `master`->4,
`grails5`->5, `grails6`->6
- Filters out future-dated guides, sorts newest-first
**`GuidesTask`** (`buildSrc/.../tasks/GuidesTask.groovy`):
- Calls `GuidesFetcher.fetchGuides()` and `TagUtils.populateTags(guides)`
- Generates `build/temp/guides.html` (main listing)
- Generates `build/temp/tags/<tag>.html` for each tag
- Generates `build/temp/categories/<category>.html` for each category
- Renders all through the site template into `build/dist/`
**`GuidesPage`** (`buildSrc/.../model/guides/GuidesPage.groovy`):
- Renders HTML using Groovy `MarkupBuilder`
- 14 predefined categories with SVG icons
- Links to `https://guides.grails.org/<name>/guide/index.html` (single)
- Links to `https://guides.grails.org/grails{N}/<slug>/guide/index.html`
(versioned)
- Features: latest guides list (8 most recent), tag cloud, search box,
category grouping
### Main site publishing (`publish.yml` workflow)
The GitHub Actions workflow runs on push to master, every 2 hours via cron,
and on
manual dispatch:
```yaml
# Step 1: Build and publish the main grails.apache.org site
GRADLE_TASK: build
GITHUB_SLUG: apache/grails-website
GH_BRANCH: asf-site-production
# Step 2: Build and publish the guides.grails.org index site
GRADLE_TASK: buildGuide
GITHUB_SLUG: grails-guides/grails-guides-template
GH_BRANCH: gh-pages
```
Both steps use `publish.sh`, which runs the Gradle task, clones the target
repo's
deploy branch, copies `build/dist/` into it, and pushes.
---
## Current Pain Points
1. **Runtime `curl` of CI scripts** -- every guide CI run downloads
`build-guide`
and `republish-guides-website.sh` from GitHub at runtime. The CI logic is
not
committed to the guide repos; it's fetched live. This is fragile (GitHub
outage
= broken CI) and opaque (the actual CI behavior is invisible in the guide
repo).
2. **Remote `apply from` for Gradle builds** -- every guide's `build.gradle`
fetches
`guide-build.gradle` from GitHub at Gradle configuration time. Combined
with
`prepareResources` downloading a ZIP of `grails-guides-template`, a
single guide
build makes 3+ HTTP requests to GitHub before any real work begins.
3. **Cascading index rebuilds** -- every single guide CI build runs
`republish-guides-website.sh`, which clones this entire repo, runs
`./gradlew buildGuide`, and pushes the result. If 5 guides push in the
same hour,
the index gets rebuilt 5 times -- plus once more from the cron job. These
can also
race and cause push conflicts on `gh-pages`.
4. **Stale old-URL references everywhere** -- the `githubactions-build.sh`
in all 94
guide repos curls from `grails/grails-guides` (old URL). The
`build-guide` and
`republish-guides-website.sh` scripts clone `grails/grails-guides` (old
URL) and
`grails/grails-static-website` (old URL). Guide `build.gradle` files
apply from
`grails/grails-guides` (old URL). GitHub redirects mask this but add
fragility.
5. **`master` vs `6.0.x` branch discrepancy in `build-guide`** -- the
`master`
branch version of `build-guide` only handles `master` and `grails3`
branches.
The `6.0.x` branch version handles `grails3`-`grails6` (not `master`).
Since
the curled template references `master`, guides using the default
template only
publish for `master` and `grails3`. This is why only 3 guides have
published
Grails 6 content despite 3 repos having a `grails6` branch, and why 37
repos
with `grails5` branches have never published Grails 5 content.
6. **No workflow template in scaffolding** -- `create-guide.sh` copies
`build.gradle`, `gradle.properties`, `githubactions-build.sh`, and docs,
but does
NOT create `.github/workflows/`. The GitHub Actions workflow must be
created and
configured manually for each guide, leading to inconsistency across 94
repos.
7. **Outdated README** -- the `grails-guides-template` README documents
Travis CI
as the CI system (with `travis encrypt` instructions). GitHub Actions was
retrofitted without updating the documentation.
8. **Outdated guide workflows** -- many guide repos use
`actions/checkout@v2`,
`actions/setup-java@v2`, JDK 11, and `adopt` distribution. These are
years out
of date.
9. **Overloaded `gh-pages` branch** -- `grails-guides-template` `gh-pages`
stores
built guide HTML (pushed by 94 individual guide CIs), `guides.json`
(mutated by
`updateGuidesJson` from each guide CI), AND the index site (pushed by both
`republish-guides-website.sh` and `grails-static-website`'s cron).
Multiple
independent CI pipelines write to the same branch concurrently.
10. **`guides.json` as the integration point** -- metadata is serialized to
JSON in
one repo, fetched via HTTP in another. Changes to the schema require
coordinating
across repos.
11. **Stale `grails-docs` dependency** -- the `PublishGuide` task comes from
`org.grails:grails-docs:6.0.0-RC1`, a Grails-specific doc toolchain.
Modern
AsciiDoctor Gradle plugins are more capable and actively maintained.
12. **94 repos to maintain** -- the `grails-guides` org has 94 individual
guide repos
plus the template. Most target older Grails versions (only 3 have Grails
6
branches). Maintaining permissions, branch policies, and secrets across
all of
them is significant overhead.
13. **10 unpublished repos** -- 10 repos exist in the org but never made it
into
`guides.json`, so they are invisible on the guides site. Their status is
unclear.
14. **Inconsistent branch naming** -- 25 repos use `grails-4` (hyphenated)
while 2
use `grails4` (unhyphenated). The `GuidesFetcher` branch mapping only
recognizes
`grails4`, meaning the 25 repos with `grails-4` branches are not
properly mapped.
---
# Part 2 -- Migration Plan
## Consolidation Plan
### Goal
Move all guide AsciiDoc source and publishing infrastructure into
`grails-static-website` so that:
- All guide AsciiDoc source lives in this repo
- Guide metadata is defined in a single YAML file (`conf/guides.yml`)
- Built guide HTML and the index site are deployed together
- One CI pipeline handles everything
- No runtime `curl` of scripts, no remote `apply from`, no cascading rebuilds
- The `grails-guides` GitHub org retains only repos with
`initial/`+`complete/`
sample applications (79 repos), serving purely as runnable code companions
### Phase 1: Move guide metadata here
**What**: Create `conf/guides.yml` in this repo containing all guide metadata
currently in `guides.json`. Stop fetching from GitHub at build time.
**Changes**:
- Add `conf/guides.yml` with all 84 published guide entries (title, authors,
tags,
category, publication date, Grails versions)
- Fix the `grails-micronaut-kakfa` typo to `grails-micronaut-kafka`
- Unify `"Grails + DevOps"` / `"Grails + Devops"` category case
- Add entries for the 10 currently unpublished repos (or explicitly exclude
them)
- Add a `guides` property to `GrailsWebsiteExtension` pointing to it
- Modify `GuidesFetcher` to read from a local YAML file instead of a remote
JSON URL
- Update `GuidesTask` to accept the local file as an `@InputFile`
**Benefits**: No more network dependency during builds. Metadata changes are
version-controlled with the site. Adding/updating a guide is a simple YAML
edit + PR.
**Impact on `grails-guides` org**: None yet. Individual guide repos continue
to
function as before; their CI just becomes the non-authoritative source for
metadata.
### Phase 2: Move common doc snippets and theme resources here
**What**: Bring `src/main/docs/common-*.adoc` (~45 files) and
`src/main/resources/`
(CSS, fonts, images) from `grails-guides-template` into this repo.
**Changes**:
- Copy `grails-guides-template/src/main/docs/common-*.adoc` to
`guides/common/`
- Copy `grails-guides-template/src/main/resources/` to `guides/resources/`
- Update `guide-build.gradle` (or its replacement) to reference local paths
- Individual guide repos can still exist but would reference resources from
this repo
**Benefits**: Theme and common content are versioned with the site. No ZIP
download
at build time (`prepareResources` task eliminated).
**Impact on `grails-guides-template`**: Its source branches (`master`,
`6.0.x`) become
redundant for `src/main/docs/` and `src/main/resources/`. Only `gh-pages` is
still
needed until Phase 5.
### Phase 3: Move `guide-build.gradle` here
**What**: Bring the shared Gradle build plugin into `buildSrc/` alongside
the existing
site build logic.
**Changes**:
- Port `guide-build.gradle` tasks (`publishGuide`, `updateGuidesJson`) into
`buildSrc/` as proper Gradle task classes (like `GuidesTask` already is)
- Replace the `org.grails:grails-docs` `PublishGuide` dependency with the
standard
`org.asciidoctor.jvm.convert` Gradle plugin
- Individual guide repos would `apply from` this repo (or use a published
plugin)
**Benefits**: Build logic is testable, type-safe Groovy/Java classes. Modern
AsciiDoctor tooling. Single place to update build conventions.
**Impact on `grails-guides-template`**: `gradle/guide-build.gradle`,
`create-guide.sh`, and `githubactions/` become dead code. The repo's source
branches
are now fully superseded.
**Impact on individual guide repos**: Their `build.gradle` must be updated
to point
the `apply from` URL at this repo (or use a published Gradle plugin). This
can be
done incrementally per guide.
### Phase 4: Move ALL guide AsciiDoc source here
**What**: Move the AsciiDoc source from every guide repo into this repo.
This is
**mandatory** for all 84 published guides and optionally for the 10
unpublished ones.
**Changes**:
- Create `guides/<guide-name>/` directories containing `guide/toc.yml` and
`.adoc` files
- Add a Gradle task to build all local guides in one pass
- The `buildGuides` task builds all guide HTML from local AsciiDoc source
- `conf/guides.yml` references the local path for each guide
**Per-guide migration procedure**:
1. **Copy AsciiDoc**: Copy `src/main/docs/guide/` from the guide repo into
`guides/<guide-name>/` in this repo
2. **Copy guide-specific images**: Copy `src/main/resources/img/` into
`guides/<guide-name>/images/`
3. **Update `conf/guides.yml`**: Ensure the guide entry has `source: local`
4. **Remove doc-publishing from the guide repo**: Delete `src/main/docs/`,
`githubactions-build.sh`, the doc-related parts of `build.gradle`, and
`gradle.properties` metadata fields used only for doc publishing
5. **Update the guide repo CI**: If the repo has `initial/`+`complete/`
sample
projects, keep a simplified workflow that only builds/tests the sample
apps
(no more `publishGuide`, `updateGuidesJson`, or
`republish-guides-website.sh`).
If the repo has no sample projects, archive it entirely.
**What happens to each repo category**:
| Category | Count | Action |
|----------|-------|--------|
| Has both `initial/` and `complete/` | 79 | AsciiDoc moves here. Repo keeps
sample projects with simplified CI. |
| Has `complete/` only (non-quickcast) | 7 | AsciiDoc moves here. Strip
doc-publishing, archive the repo. |
| Has `complete/` only (quickcast) | 7 | AsciiDoc moves here. Strip
doc-publishing, archive the repo. |
| Has neither dir | 1 | `grails-acl` -- doc-only, never published. Archive
the repo. |
| Unpublished (in org, not in `guides.json`) | 10 | Evaluate each: migrate
AsciiDoc if the guide has value; archive if abandoned. |
**Proposed structure**:
```
grails-static-website/
├── conf/
│ └── guides.yml <-- all guide metadata (84+ entries)
├── guides/
│ ├── common/ <-- shared AsciiDoc snippets (from
grails-guides-template)
│ ├── resources/ <-- theme CSS, fonts, images (from
grails-guides-template)
│ ├── creating-your-first-grails-app/
│ │ ├── toc.yml
│ │ ├── images/ <-- guide-specific images
│ │ └── guide/*.adoc
│ ├── grails-mock-basics/
│ │ ├── toc.yml
│ │ └── guide/*.adoc
│ └── ... (84+ guide directories)
├── buildSrc/
│ └── .../guides/
│ ├── GuidesFetcher.groovy <-- reads conf/guides.yml
│ ├── GuidesTask.groovy <-- generates index site
│ ├── GuideBuildTask.groovy <-- NEW: builds individual guide HTML
│ └── ...
└── .github/workflows/
└── publish.yml <-- single workflow for site + guides
```
### Phase 5: Simplify deployment
**What**: Deploy everything (main site + guide HTML + guide index) as part of
`grails.apache.org`. All guides will be served at
`https://grails.apache.org/guides/`.
**Changes**:
- Build output produces one unified `build/dist/` tree:
```
build/dist/
├── index.html <-- grails.apache.org homepage
├── guides/
│ ├── index.html <-- grails.apache.org/guides/ listing
│ ├── categories/*.html
│ ├── tags/*.html
│ └── <guide-name>/
│ └── guide/index.html <-- individual guide HTML
└── ...
```
- A single `publish.yml` step pushes to `apache/grails-website`
`asf-site-production`
- Remove the second `publish.sh` step that pushes to
`grails-guides-template` `gh-pages`
- Eliminate `republish-guides-website.sh` -- no more cascading rebuilds
- Update `GUIDES_URL` in `GuidesPage.groovy` from
`https://guides.grails.org` to
`https://grails.apache.org/guides`
**New URL structure**:
- `https://grails.apache.org/guides/` -- guides listing (index)
- `https://grails.apache.org/guides/<guide-name>/guide/index.html` --
individual guide
- `https://grails.apache.org/guides/tags/<tag>.html` -- guides by tag
- `https://grails.apache.org/guides/categories/<category>.html` -- guides by
category
**Benefits**: One deploy, one repo, one domain. No cross-repo push
choreography. No concurrent writes to `gh-pages`. Drastically simpler CI/CD.
### Phase 6: Strip doc-publishing from guide repos
**What**: After AsciiDoc source has been migrated (Phase 4), strip the
doc-publishing
machinery from every guide repo in the `grails-guides` org.
**Per-repo procedure for repos keeping `initial/`+`complete/` (79 repos)**:
1. Delete `src/main/docs/` (AsciiDoc source -- now in
`grails-static-website`)
2. Delete `githubactions-build.sh` (no longer needed)
3. Simplify `build.gradle`:
- Remove the `apply from:` that fetches `guide-build.gradle`
- Remove the `asciidoctor` plugin
- Keep only what's needed to build/test `initial/` and `complete/`
subprojects
4. Remove doc-publishing metadata from `gradle.properties` (title, subtitle,
authors,
githubSlug, githubBranch, category, tags, publicationDate)
5. Update `.github/workflows/gradle.yml`:
- Remove the `./githubactions-build.sh` step
- Replace with `./gradlew check` to just run tests on the sample apps
- Update to modern actions versions (checkout@v4, setup-java@v4, JDK 17+)
- Remove `GH_TOKEN` / `GIT_NAME` / `GIT_EMAIL` secrets (no longer pushing
to gh-pages)
6. Add a `README.md` note: "Guide content is published from
[apache/grails-static-website](https://github.com/apache/grails-static-website).
This repo contains only the sample applications."
**For repos with `complete/` only (14 repos)**:
Same stripping procedure as above, then archive the repo. These repos have no
`initial/` project, so they do not serve as interactive tutorial companions.
The `complete/` code is preserved in the archived repo for reference.
**For repos with neither dir (1 repo: `grails-acl`)**:
Archive the repo. It was never published and has no sample code.
### Phase 7: Wind down `grails-guides-template`
**What**: After consolidation is complete, clean up `grails-guides-template`.
**Impact on `grails-guides/grails-guides-template`**:
- Source branches (`master`, `6.0.x`) are fully superseded after Phases 2-3
--
all build logic, common docs, and theme resources now live in
`grails-static-website`
- `gh-pages` branch continues to serve `guides.grails.org` via GitHub Pages,
but
all HTML files are replaced with meta-refresh redirects to
`grails.apache.org/guides/`
(see URL Redirect Plan below)
- **Action**: Replace all HTML on `gh-pages` with redirect stubs. Archive
the repo's
source branches. The `gh-pages` branch stays live to serve redirects
indefinitely.
**Impact on the `grails-guides` org after all phases**:
| What remains | Count | Purpose |
|-------------|-------|---------|
| Repos with `initial/`+`complete/` sample apps | 79 | Runnable code
companions for guides (no doc-publishing) |
| Repos archived (`complete/`-only, doc-only, obsolete) | 15 | Read-only
reference (14 complete-only + 1 no-code) |
| `grails-guides-template` | 1 | Source branches archived; `gh-pages` stays
live for redirects |
### URL Redirect Plan
Current guide URLs follow the pattern:
```
https://guides.grails.org/<guide-name>/guide/index.html (single
version)
https://guides.grails.org/grails{N}/<guide-name>/guide/index.html (versioned)
```
After consolidation, guides will be served from `grails.apache.org/guides/`.
To
preserve existing links, the `grails-guides-template` `gh-pages` branch
(which
serves `guides.grails.org` via GitHub Pages) will be updated with
meta-refresh
redirects pointing to the new `grails.apache.org` URLs.
**Redirect implementation**: Replace all HTML files on `gh-pages` with
redirect
stubs using `<meta http-equiv="refresh">`:
```html
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="refresh" content="0;
url=https://grails.apache.org/guides/<guide-name>/guide/index.html">
<link rel="canonical"
href="https://grails.apache.org/guides/<guide-name>/guide/index.html">
</head>
<body>
<p>This page has moved to
<a href="https://grails.apache.org/guides/<guide-name>/guide/index.html">
grails.apache.org/guides/<guide-name>/guide/index.html
</a>.
</p>
</body>
</html>
```
This covers:
- `https://guides.grails.org/<guide-name>/guide/index.html` ->
`https://grails.apache.org/guides/<guide-name>/guide/index.html`
- `https://guides.grails.org/grails{N}/<guide-name>/guide/index.html` ->
`https://grails.apache.org/guides/grails{N}/<guide-name>/guide/index.html`
- `https://guides.grails.org/index.html` ->
`https://grails.apache.org/guides/index.html`
- `https://guides.grails.org/tags/*.html` ->
`https://grails.apache.org/guides/tags/*.html`
- `https://guides.grails.org/categories/*.html` ->
`https://grails.apache.org/guides/categories/*.html`
The `gh-pages` branch stays live (not archived) to serve redirects
indefinitely.
The `grails-guides-template` repo itself can still be archived since the
`gh-pages`
branch is independent of the source branches.
---
## Migration Checklist
**Phase 1 -- Metadata**
- [ ] Create `conf/guides.yml` from current `guides.json` (84 published + 10
unpublished to evaluate)
- [ ] Fix typo: `grails-micronaut-kakfa` -> `grails-micronaut-kafka`
- [ ] Unify category: `"Grails + DevOps"` / `"Grails + Devops"`
- [ ] Fix data issue: `grails-on-travis-basics` branch value
`"master,grails3"`
- [ ] Update `GuidesFetcher` to read local YAML
- [ ] Verify index site renders identically
**Phase 2 -- Common docs and resources**
- [ ] Copy ~45 `common-*.adoc` snippets to `guides/common/`
- [ ] Copy theme resources (CSS, fonts, images) to `guides/resources/`
**Phase 3 -- Build plugin**
- [ ] Port `guide-build.gradle` tasks into `buildSrc/`
- [ ] Replace `grails-docs` with standard `asciidoctor-gradle-plugin`
- [ ] Add `GuideBuildTask` for building individual guide HTML
**Phase 4 -- Guide AsciiDoc source (all 84 published guides)**
- [ ] Migrate AsciiDoc from all 79 repos with `initial/`+`complete/`
- [ ] Migrate AsciiDoc from all 14 repos with `complete/`-only
- [ ] Migrate AsciiDoc from `grails-acl` (if worth preserving)
- [ ] Evaluate and migrate the 10 unpublished repos
- [ ] Verify all guides build and render correctly from local source
**Phase 5 -- Deployment**
- [ ] Update `publish.yml` to produce unified output under
`build/dist/guides/`
- [ ] Remove second `publish.sh` step for `grails-guides-template`
- [ ] Update `GUIDES_URL` in `GuidesPage.groovy` to
`https://grails.apache.org/guides`
- [ ] Verify all guide URLs work at `grails.apache.org/guides/`
**Phase 6 -- Strip doc-publishing from guide repos**
- [ ] Strip doc-publishing from 79 repos with `initial/`+`complete/`
- [ ] Simplify CI workflows in all retained repos
- [ ] Archive repos with no meaningful sample code
- [ ] Remove `GH_TOKEN` / `GIT_NAME` / `GIT_EMAIL` secrets from all repos
**Phase 7 -- Wind down template**
- [ ] Replace all HTML files on `grails-guides-template` `gh-pages` with
meta-refresh redirects to `grails.apache.org/guides/`
- [ ] Verify all `guides.grails.org` URLs redirect correctly
- [ ] Archive `grails-guides-template` source branches (`master`, `6.0.x`)
- [ ] Keep `gh-pages` branch live for redirects
- [ ] Add deprecation notice to `grails-guides` org description
---
# Part 3 -- New System (After Migration)
## How Publishing Will Work After Migration
After consolidation, adding or updating a guide is straightforward. There
are no
repos to create, no CI to configure, no secrets to set up, no scripts to
curl.
### Adding a new guide (doc-only, no sample project)
This is the simplest case -- a guide that is pure documentation with no
runnable
`initial/`+`complete/` sample apps.
1. **Create the guide directory**:
```
guides/my-new-grails8-feature/
├── toc.yml
└── guide/
├── intro.adoc
├── writingTheApp.adoc
└── summary.adoc
```
2. **Write `toc.yml`** (table of contents):
```yaml
title: My New Grails 8 Feature
authors:
- Your Name
---
introduction: intro.adoc
writingTheApp:
title: Writing the Application
guide: writingTheApp.adoc
summary: summary.adoc
```
3. **Write `.adoc` files** using AsciiDoc syntax. Use `include::` to pull in
shared snippets from `guides/common/` (e.g. `common-requirements.adoc`).
4. **Add an entry in `conf/guides.yml`**:
```yaml
- name: my-new-grails8-feature
title: "My New Grails 8 Feature"
subtitle: "Learn how to use the new feature in Grails 8"
authors: "Your Name"
category: "Grails Apprentice"
tags: [grails8, new-feature]
publicationDate: "15 Feb 2026"
grailsVersions: [8]
```
5. **Build and preview locally**:
```bash
./gradlew buildGuides
# Open build/dist/guides/my-new-grails8-feature/guide/index.html
```
6. **Submit a PR** to `apache/grails-static-website`. Once merged, the guide
is
automatically built and deployed by `publish.yml`.
That's it. One PR, one repo, automatic deployment.
### Adding a new guide with sample projects
For guides that include runnable `initial/`+`complete/` Grails applications:
1. **Create the guide content in this repo** (same as above -- steps 1-4).
2. **Add `sampleProjectRepo`** to the guide's `conf/guides.yml` entry:
```yaml
- name: my-new-grails8-guide
title: "My New Grails 8 Guide"
subtitle: "A hands-on guide to building with Grails 8"
authors: "Your Name"
category: "Grails Apprentice"
tags: [grails8]
publicationDate: "15 Feb 2026"
grailsVersions: [8]
sampleProjectRepo: "grails-guides/my-new-grails8-guide"
```
3. **Create the sample project repo** in the `grails-guides` org:
```
grails-guides/my-new-grails8-guide/
├── initial/ <-- starter Grails app
├── complete/ <-- finished solution
├── settings.gradle <-- include 'complete', 'initial'
├── build.gradle <-- just builds/tests the sample apps (no doc
publishing)
└── .github/workflows/
└── build.yml <-- runs ./gradlew check only
```
The sample project repo is simple -- no `githubactions-build.sh`, no
`apply from:` remote scripts, no doc-publishing metadata, no secrets
needed
for pushing to `gh-pages`. It just builds and tests the sample apps.
4. **Submit two PRs**:
- PR to `apache/grails-static-website` with the guide content + YAML entry
- Create the sample project repo with `initial/`+`complete/`
### Updating an existing guide
1. **Edit the AsciiDoc** in `guides/<guide-name>/guide/*.adoc`.
2. **Update `conf/guides.yml`** if metadata changed (e.g. adding a new
Grails version).
3. **Submit a PR**. That's it.
If the guide has a sample project repo in `grails-guides`, update the sample
code
there in a separate PR.
### Adding a new Grails version to an existing guide
1. **Update `conf/guides.yml`** to add the version:
```yaml
grailsVersions: [6, 7, 8] # was [6, 7]
```
2. **Update AsciiDoc** if there are version-specific instructions (use
AsciiDoc
conditionals or separate sections as needed).
3. **Update the sample project repo** (if one exists) -- either update the
existing
`initial/`+`complete/` to the new Grails version, or add versioned
directories
(e.g. `complete-grails8/`) if multiple versions need distinct sample code.
4. **Submit a PR**.
### How versioning simplifies with migration
In the current system, multi-version guides require maintaining separate Git
branches
per Grails version, each with its own CI pipeline writing to `gh-pages`.
After
consolidation:
1. **No more multi-branch complexity**: Guide version metadata lives in
`conf/guides.yml` with a simple `grailsVersions: [4, 5, 6]` list. The
AsciiDoc
source is a single copy with version-conditional content if needed.
2. **Publish previously unpublished versions**: The 35 `grails5` branches
with
distinct content and the 15 `grails-4` branches with distinct content can
now
be published by adding their AsciiDoc and sample code during migration.
These
were never published only because the CI scripts didn't handle them.
3. **Explicit version support**: Instead of inferring version from branch
names
with inconsistent patterns (`grails4` vs `grails-4`), each guide declares
supported versions explicitly in `conf/guides.yml`.
4. **Single-pass build**: One Gradle build generates all guide HTML for all
versions,
instead of each version branch triggering separate CI pipelines that race
to
write to `gh-pages`.
5. **Easy to add Grails 7/8 guides**: Adding a new guide version is a YAML
edit,
not a branch + CI + secrets setup.
---
## Comparison: Before vs After
| | Current system | After migration |
|---|---|---|
| **Create a new guide** | Run `create-guide.sh`, create GitHub repo,
manually create workflow YAML, configure 3 secrets, push | Create
`guides/<name>/` dir + `conf/guides.yml` entry, submit PR |
| **Where guide content lives** | Scattered across 94 repos | Single
`guides/` directory in this repo |
| **How it gets published** | Push to guide repo -> CI curls scripts ->
builds AsciiDoc -> pushes to gh-pages -> curls another script -> rebuilds index
-> pushes again | Push to this repo -> CI builds everything -> deploys |
| **CI configuration** | Per-repo workflow YAML + 3 secrets + runtime curl
of scripts | Single `publish.yml` in this repo |
| **Preview changes** | Must push to trigger CI; no local preview |
`./gradlew buildGuides` locally |
| **Add a Grails version** | Create a branch, hope the CI handles it (it
probably won't due to `build-guide` branch discrepancy) | Edit one line in
`conf/guides.yml` |
| **Time to publish** | Minutes (CI cascade across repos) | Seconds (single
build + deploy) |
| **Guide URL** | `guides.grails.org/<name>/guide/index.html` |
`grails.apache.org/guides/<name>/guide/index.html` |
| **Repos to maintain** | 94 guide repos + 1 template repo | 1 repo (this
one) + sample project repos |
---
## Key Files Reference
### grails-static-website (this repo)
| File | Purpose |
|------|---------|
| `buildSrc/.../GrailsWebsitePlugin.groovy` | Registers all tasks including
`genGuides` and `buildGuides` |
| `buildSrc/.../tasks/GuidesTask.groovy` | Generates guide index, tag, and
category HTML pages |
| `buildSrc/.../model/guides/GuidesFetcher.groovy` | Fetches and parses
`guides.json` from GitHub |
| `buildSrc/.../model/guides/GuidesPage.groovy` | Renders guide listing HTML
(categories, tags, search) |
| `buildSrc/.../model/guides/Guide.groovy` | Guide interface (category,
name, title, tags, etc.) |
| `buildSrc/.../model/guides/SingleGuide.groovy` | Guide with one Grails
version |
| `buildSrc/.../model/guides/GrailsVersionedGuide.groovy` | Guide spanning
multiple Grails versions |
| `.github/workflows/publish.yml` | CI: builds main site + guides index,
pushes to deploy branches |
| `publish.sh` | Runs Gradle build, clones deploy repo, copies output,
pushes |
### grails-guides/grails-guides-template (infrastructure + deployment repo)
| File | Branch | Purpose |
|------|--------|---------|
| `gradle/guide-build.gradle` | `master`, `6.0.x` | Shared build script
applied by all guide repos |
| `create-guide.sh` | `master`, `6.0.x` | Scaffolds a new guide project |
| `src/main/docs/common-*.adoc` | `master`, `6.0.x` | ~45 reusable AsciiDoc
snippets included by guides |
| `src/main/resources/` | `master`, `6.0.x` | CSS, fonts, images for guide
HTML theme |
| `src/main/project/` | `master`, `6.0.x` | Template files copied into new
guides (build.gradle, gradle.properties, githubactions-build.sh, docs skeleton)
|
| `githubactions/build-guide` | `master` | CI script: test + build + publish
a single guide + update guides.json. **`master` version handles
`master`+`grails3` only** |
| `githubactions/build-guide` | `6.0.x` | CI script: same but handles
`grails3`-`grails6` (not `master`) |
| `githubactions/republish-guides-website.sh` | `master`, `6.0.x` | CI
script: clone static-website, rebuild index, push to gh-pages |
| `gh-pages` branch | -- | Deployment: built guide HTML, `guides.json` (125
entries), and index site |
| `README.md` | `master`, `6.0.x` | Still references Travis CI as the CI
system |
### Individual guide repos (grails-guides org, 94 repos)
| File | Purpose |
|------|---------|
| `.github/workflows/gradle.yml` | GitHub Actions workflow -- manually
created, NOT from template. Often outdated (actions@v2, JDK 11). |
| `githubactions-build.sh` | CI entrypoint. Identical in every repo. Curls
`build-guide` and `republish-guides-website.sh` from `grails-guides-template`
`master` at runtime. |
| `build.gradle` | Applies `guide-build.gradle` remotely via `apply from:`
URL (old `grails/grails-guides` URL). Some point to `master`, some to `6.0.x`. |
| `gradle.properties` | Guide metadata (title, authors, tags, category,
publicationDate, githubSlug, githubBranch) |
| `settings.gradle` | Includes `complete` and `initial` subprojects |
| `src/main/docs/guide/toc.yml` | Table of contents for the guide |
| `src/main/docs/guide/*.adoc` | AsciiDoc source files |
| `src/main/resources/img/` | Guide-specific images |
| `initial/` | Starter Grails app for the reader to begin with (79 repos) |
| `complete/` | Finished Grails app -- the solution (93 repos) |
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]