Skip to content

About

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

545 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Micronaut Web

Astro prototype for three separately hostable Micronaut web surfaces:

  • main: product entry point at / and /main/
  • docs: platform documentation catalog and detail pages under /docs/
  • guides: guides catalog and detail pages under /guides/

All surfaces share the same design tokens, React components, docs catalog fixture, and generated guide manifest:

npm run dev
npm run build
npm run build:main
npm run build:docs
npm run build:guides

npm run check validates TypeScript, script formatting, tests, and shared snippet styles. npm run sync:docs-projects refreshes the test fixture at src/data/docs-projects.fixture.json from a local micronaut-projects/micronaut-platform checkout's gradle/libs.versions.toml plus the metadata already stored in the fixture. src/data/docs-projects.fixture.json is the checked-in docs project metadata source so CI can run the docs renderer without an external metadata checkout. npm run build also prepares plain HTML template artifacts under dist/micronaut-web.

Script Development

All repository scripts under scripts/ must be TypeScript files. Add new scripts as .ts, import other local script modules with .ts extensions, invoke them from package.json with node scripts/...ts or node --test scripts/tests/...test.ts, and keep the full npm run typecheck:scripts TypeScript check passing. The script check uses strict TypeScript settings and non-pretty compiler output for CI logs. Formatting is checked with npm run style, which covers scripts/, src/ and tests/ (.ts, .tsx, .astro) plus astro.config.mjs; npm run style:fix rewrites them. Node 24 or newer is assumed for direct TypeScript script execution, matching CI.

Package Scripts

Script Purpose
npm run check Runs TypeScript checks, script formatting, script tests, browser-backed surface tests, and snippet style validation.
npm run snippet-styles Verifies generated docs snippet styles stay shared instead of copied into runtime layout code.
npm run sync:docs-projects Refreshes src/data/docs-projects.fixture.json from a local Micronaut Platform checkout.
npm run render:docs Renders generated docs fragments and the docs project catalog.
npm run render:guides Renders generated guide fragments and the guide manifest.
npm run prepare:generated-docs Alias for render:docs, used by docs preparation flows.
npm run prepare:generated-content Renders docs and guides generated content together.
npm run test:main-site Runs main-site script tests and the main-site Playwright wrapper.
npm run test:main-site:browser Runs the main-site Playwright spec directly.
npm run test:asciidoc Runs shared Asciidoc renderer and macro tests.
npm run test:docs Runs docs script tests and the docs Playwright wrapper.
npm run test:docs:browser Prepares docs generated content and runs the docs Playwright spec.
npm run test:guides Runs guides script tests and the guides Playwright wrapper.
npm run test:guides:browser Prepares guides generated content and runs the guides Playwright spec.
npm run test:deployment Runs deployment, pruning, publishing, and surface-routing script tests.
npm run typecheck Runs the main TypeScript project check.
npm run typecheck:scripts Runs the strict TypeScript check for files under scripts/.
npm run style Checks scripts/, src/ and tests/ formatting with Prettier.
npm run style:fix Rewrites those files with Prettier.
npm run dev Runs the full check, prepares generated content, and starts Astro dev on 127.0.0.1.
npm run build:site Runs checks, prepares generated content, builds Astro, builds the shared header shell, and prepares template artifacts.
npm run build Same as build:site; kept as the default full-site build command.
npm run build:surface Builds and prunes a selected deployment surface.
npm run build:main Builds the standalone main surface.
npm run build:docs Builds the standalone docs surface.
npm run build:guides Builds the standalone guides surface.
npm run prune:surface Prunes an existing dist output for the selected surface.
npm run update:docs-versions Rebuilds the published docs version manifest.
npm run publish:docs-surface Merges a docs surface build into an existing published docs tree.
npm run preview Runs astro preview on 127.0.0.1.

Design Tokens

The shared palette intentionally follows the classic micronaut.io direction:

  • black hero and footer surfaces
  • white content areas
  • Micronaut blue for primary CTAs, active states, links, and section bands
  • restrained cool gray borders and muted text
  • near-black code panels

The logo must stay black on light surfaces and white on dark surfaces. Do not recolor Micronaut logos with blue or other accents; use blue for UI treatments around the brand, not for the mark itself.

Core brand tokens live in src/styles/globals.css:

Brand black:        #000000
Brand white:        #ffffff
Micronaut blue:     #2f73b9
Classic site blue:  #337ab7
Blue hover:         #245e99
Blue deep:          #173f67
Blue soft:          #eaf3fb
Light border:       #d9e1e8
Dark background:    #050505
Dark surface:       #111111
Dark blue:          #66a9e6

Tailwind can access the extra Micronaut-specific variables through the @theme inline mappings, for example bg-micronaut-blue, text-micronaut-blue-deep, bg-hero, bg-blue-band, and bg-code.

Content Catalogs

src/data/docs-projects.fixture.json contains the checked-in docs project catalog, categories, descriptions, repository links, and platform versions used when generated docs output is unavailable.

src/data/generated-guides.fixture.json contains the checked-in guide catalog subset used when generated guide output is unavailable. src/content/generated-guides/manifest.json is produced by npm run render:guides and ignored as generated content.

A guide's snippets resolve against the guide's own directory and, when it exists, the sample project the guides build writes to build/code/<slug>/<slug>-<build tool>-<language> in the guides checkout. That project is the sourcedir the upstream Asciidoctor build uses, and it is the only place to read the Micronaut Starter files a guide never checks in, such as the application class, the generated configuration, and the build scripts. npm run render:guides works without it; only those Starter snippets stay unresolved. Publishing generates it with ./gradlew generateCodeZip, which also writes the per-variant archives each guide links as its download. A guide is never offered as a Maven and Kotlin variant, because the guides build refuses to generate that combination.

Blog Authoring

Blog Posts

Blog posts are Astro Markdown content entries under src/content/main-site/blog. Use a dated path that mirrors the public slug, for example src/content/main-site/blog/2026/06/01/example-post.md. The slug frontmatter field is the canonical route without leading or trailing slashes; href defaults to /${slug}/, so set href only when a post needs an explicit canonical override.

Use this shape for a normal post:

---
slug: 2026/06/01/example-post
title: Example Post Title
description: One or two sentences used in cards, metadata, and search previews.
date: "2026-06-01T09:00:00"
modified: "2026-06-01T09:00:00"
category: release-announcements
categories:
  - release-announcements
tags:
  - release
href: /2026/06/01/example-post/
---

Opening paragraph for the post.

## Section Heading

Body copy can use normal Markdown.

```java
class Example {
}
```

Required blog fields are slug, title, and description. Dates are optional in the schema, but new posts should set date so sorting is predictable; set modified when the published content changes after the initial date. order is an optional tie-breaker when posts have the same date. sourceUrl, wordpressId, and contentSource: wordpress-post are migration metadata for imported posts and are not required for newly authored content.

The Markdown body is rendered through Astro and then passed through the main-site Markdown renderer. Headings, paragraphs, links, lists, blockquotes, tables, horizontal rules, images, inline code, and fenced code blocks are supported. Fenced code blocks are converted to the shared docs snippet card style; prefer a language marker such as java, kotlin, groovy, bash, xml, yaml, or gradle. Some obvious Java, Gradle, and XML snippets can be inferred when the fence has no language, but explicit language markers are easier to maintain.

Use root-relative links for internal routes, such as /docs/, /guides/, or /2026/06/01/example-post/. Links and images pointing at https://micronaut.io/... or root-relative Micronaut paths are rewritten through the deployment base-path helpers during rendering. For local images and downloads, put source files under public/micronaut-assets/main-site/... and reference them from Markdown with /micronaut-assets/main-site/.... Image Markdown should include useful alt text.

Main-Site Pages

Main-site pages are Markdown content entries under src/content/main-site/pages. The route is derived from the file path under that directory: support.md becomes /support/, brand-guidelines/micronaut-logos.md becomes /brand-guidelines/micronaut-logos/, category/release-announcements.md becomes /category/release-announcements/, and an index.md file drops the final index segment.

Use this shape for a normal page:

---
order: 12
title: Example Page
eyebrow: Resources
description: One or two sentences used in cards, metadata, and search previews.
sourceUrl: https://micronaut.io/example/
intro: Optional short lead text for the page header.
redirectFrom:
  - /old-example/
---

# Example Page

Normal Markdown page content.

Required page fields are order, title, eyebrow, description, and sourceUrl. intro feeds the shared main-site page header for authored pages; body Markdown is rendered as page content for normal pages. redirectFrom lists old paths that should redirect to the new route. Adding a page makes the route available and searchable, but it does not automatically add the page to the header or footer; update the relevant navigation data when a new page should be linked globally.

Use layoutVariant: public-markdown only for imported public micronaut.io Markdown pages whose body already carries the page heading or whose route has special archive/FAQ rendering. This layout variant uses the full main-site content width and suppresses the generated wrapper header for ordinary pages. Omit layoutVariant for newly authored pages so they use the default narrower article layout with the generated badge, title, and intro header.

Blog Categories and Archives

category is the primary category slug. It is shown on blog cards and is used by /category/<slug>/ archive pages. categories is an additional list of category slugs; include the primary category there as well so archive membership stays consistent with older imported posts. Category slugs are lower-case kebab-case strings. Existing category pages are:

  • case-studies
  • guest-post
  • microcast
  • micronaut-4
  • micronaut-5
  • micronaut-framework-2
  • release-announcements
  • security-announcements
  • sponsor
  • town-hall-meetings
  • uncategorized
  • webinar

Category archive routes are page-backed. Post metadata decides membership, but it does not create the public category page by itself. To introduce a new public category:

  1. Choose a lower-case kebab-case slug, for example micronaut-6.
  2. Set the post category to that slug and include the same slug in categories.
  3. Add src/content/main-site/pages/category/<slug>.md with the normal main-site page frontmatter so /category/<slug>/ has a title, intro, sections, metadata, and archive content.
  4. Add the page to footer or header navigation only when it should be linked globally.

The category archive lists posts whose primary category matches the slug or whose categories array contains it. The /blog/ page lists the first 24 regular posts, /blog/page/<n>/ pages are generated from the post count, and the success-story category is excluded from the regular blog archive.

tags are lower-case kebab-case slugs used for /tag/<tag>/ archive routes. Tag archive routes are generated from post metadata and do not require separate page files. The global search dialog indexes main-site pages, docs, guides, and guide tags; blog category and tag archives are normal static routes rather than a separate external search service.

Dated blog slugs automatically get legacy redirects from /blog/YYYY-MM-DD-post-slug.html to the canonical dated route. Explicit historical aliases live in src/lib/blog-redirects.ts; add there only when an old WordPress URL does not match the generated dated redirect. The success-story category is special-cased out of the regular /blog/ listing; new production stories should normally be authored as pages under src/content/main-site/pages/micronaut-success-stories/ instead of as blog posts.

Search

Search is static and catalog-backed. There is no external search service; the UI searches data already shipped in the current surface artifact.

The header search is SearchDialog, rendered by SiteHeader in the top-menu island and hydrated on load. It opens from the search button or the Meta/Ctrl+K keyboard shortcut, then navigates by assigning window.location.href through the base-path helpers so the same result paths work in the all-in-one preview, standalone docs, standalone guides, remote-header shells, and GitHub Pages base paths.

The main-site search mode is used on the main and guides surfaces. Its result sets come from:

  • getMainSitePageSummaries(), passed into SiteHeader, for main-site pages. These are available synchronously.
  • /search-index.json, fetched when the dialog first opens. src/pages/search-index.json.ts prerenders it from searchItems() in src/lib/content-catalog.ts, which is backed by src/data/docs-projects.fixture.json for docs projects and synthetic docs sections and by src/data/generated-guides.fixture.json for guides and guide tags.

Neither catalog is imported into the dialog. SiteHeader is hydrated on every page, so a static import would ship both fixtures (roughly 92 KB) to every visitor whether or not search is ever opened.

Main-site search groups results into actions, main-site pages, docs and APIs, guides, and tags. Each command item exposes a searchable value made from its kind, title, description, and terms; the cmdk command component handles the visible filtering. The source groups are capped before rendering to keep the dialog small: up to 80 main-site pages, 80 docs entries, 80 guide entries, and 40 tags. This mode does not read generated docs HTML, so deep generated headings, configuration properties, and API classes are available only in docs search mode.

Docs search mode is used when SiteHeader is rendered with surface="docs". On open it fetches the configured docs search index URL or withBasePath("/docs/search-index.json"). In standalone docs deployments that path is routed to /<line>/search-index.json; in the all-in-one preview it stays under /docs/search-index.json. WebLayout passes both index URLs (docsSearchIndexUrl, siteSearchIndexUrl) as absolute surface URLs, and the remote header shell reads them from data-docs-search-index-url and data-site-search-index-url. If a fetch fails the dialog shows no catalog results; main-site pages stay searchable.

The docs search index route is src/pages/docs/[searchIndex].json.ts. It is prerendered only when docs routes are enabled and returns JSON as { "items": [...] }; because it is prerendered, only the body is emitted and response headers come from the host serving the artifact. It loads the docs project catalog from src/content/generated-docs/project-catalog.json when present, otherwise from src/data/docs-projects.fixture.json, then reads generated HTML from src/content/generated-docs/*.html and calls buildDocsSearchIndex(...) in scripts/docs/search-index.ts.

buildDocsSearchIndex(...) emits project, docs section, and repository items for every project, then extracts richer generated-doc entries from HTML:

  • h1 and h2 headings inside .guide-section-heading become docs results.
  • Table rows whose first cell looks like a configuration property become property results.
  • Links to generated /api/*.html pages become class results.

Generated entries are deduplicated by scope, href, and title, and each project is capped at 1200 generated items. Docs search adds scope chips for all results, projects, docs, properties, classes, and repositories, then filters by lower-case substring over kind, title, description, and terms before rendering up to 240 results.

Guides catalog filtering is separate from the header dialog. LatestGuidesCatalog reads URL parameters on the server: q matches guide title, intro, authors, categories, and tags; category and tag narrow the list; and sort accepts latest, title, or duration. Filter links are generated with filterHref(...), so the selected filters remain URL-addressable.

Deployment and Compatibility

This repository is the only source implementation for the three web surfaces. A normal Astro build can render the all-in-one preview, then scripts/prune-surface.ts rewrites that full dist output into the artifact shape needed by one deploy target.

Production-compatible hosts are:

  • main: https://micronaut.io/
  • docs: https://docs.micronaut.io/
  • guides: https://guides.micronaut.io/

Temporary GitHub Pages hosts keep the repository names in the path:

  • micronaut-web: main site at /micronaut-web/
  • micronaut-docs-v2: docs selector at /micronaut-docs-v2/, release-line folders such as /micronaut-docs-v2/5.0.x/, and /micronaut-docs-v2/latest/ redirecting to the newest of them
  • micronaut-docs-snapshot: snapshot docs at /micronaut-docs-snapshot/snapshot/
  • micronaut-guides-v2: guides at /micronaut-guides-v2/

Surface Split

Surface builds are selected with MICRONAUT_DEPLOY_SURFACE=main|docs|guides. scripts/build-surface.ts sets a matching default ASTRO_BASE, runs the full static build, then prunes the artifact:

  • npm run build:main keeps the homepage, blog/content pages, redirects, .nojekyll, shared branding assets, and the shared header shell under /shell/site-header.js and /shell/site-header.css. It sets MICRONAUT_PREPARE_GENERATED_CONTENT=false by default, so the web deployment does not fetch or render docs/guides content. Dynamic docs/guides route generation is disabled for this surface, and pruning removes any remaining docs, guides, latest-route, and template artifacts from the published Pages artifact.
  • npm run build:docs keeps the docs index, docs project pages, search index, docs version selector, docs redirects, _astro, .nojekyll, and shared docs assets. It prepares only generated docs content and removes unrelated main and guides route trees.
  • npm run build:guides renders the generated guides once under /guides, moves that tree to /latest for the standalone guides artifact, and keeps the root redirect, guide compatibility routes, _astro, .nojekyll, and shared guide assets. It prepares only generated guides content and removes unrelated main and docs route trees.

Every surface publishes its own robots.txt and sitemap. src/pages/robots.txt.ts resolves the Sitemap: line from that build's site and base, and @astrojs/sitemap is filtered to the pages that survive pruning and serialized to the paths they are served from, so the docs sitemap lists /<line>/<project>/ and the guides sitemap lists root-level guides. Pages that ask to stay out of search results are dropped as well: the filter reads the built HTML back and skips anything carrying a robots noindex meta, which covers both the refresh stubs Astro emits for Astro.redirect() and pages passing noindex to WebLayout. Both files are written to the build root, so scripts/prune-surface.ts and scripts/publish-docs-surface.ts carry them across through scripts/shared/crawler-files.ts.

Standalone docs and guides builds do not publish their own header shell. In production they render a small static fallback header, load the header stylesheet and React shell from the configured main-site URL, then mount the current SiteHeader implementation from micronaut-web. This keeps the top navigation owned by the web repository even when docs and guides content are generated from separate repositories. The fallback links use MICRONAUT_MAIN_SITE_URL, MICRONAUT_DOCS_SITE_URL, and MICRONAUT_GUIDES_SITE_URL, so split-repo Pages deployments can point at the correct published artifacts without hard-coded repository names.

GitHub Actions

Workflow Trigger Summary
Validate Build Pull requests Checks script formatting and builds the docs, guides, and main-site surfaces. Generated external content is disabled for this validation build.
Deploy Web Pushes to main or manual dispatch Builds only the web surface, ensures dist/.nojekyll is present, uploads the pruned artifact, and deploys it with GitHub Pages Actions. It does not check out Micronaut Platform or Micronaut Guides.
Deploy Docs Manual dispatch or a platform-released repository-dispatch event Builds one Docs release from Micronaut Platform and publishes it to micronaut-projects/micronaut-docs-v2:gh-pages under its release line, for example 5.0.3 to /5.0.x/. Manual runs take only docs_version; a platform release also supplies the exact source revision. /latest redirects to the newest published line.
Deploy Guides Manual dispatch or a guides-updated repository-dispatch event Runs the guides project's own Gradle build to generate the sample projects and their downloadable archives, renders the guides against them, and publishes everything to micronaut-projects/micronaut-guides-v2:gh-pages. Manual runs choose the source with guides_repository and guides_ref; a guides-updated event uses client_payload.sha to publish the exact upstream commit.
Deploy Snapshot Docs Manual dispatch Builds the docs from the Micronaut Platform default branch and force-pushes them to micronaut-projects/micronaut-docs-snapshot:gh-pages as a single commit, then refreshes the /snapshot entry on the released docs branch.
Publish Upstream Updates Hourly schedule or manual dispatch Polls Micronaut Platform and Micronaut Guides, then starts Deploy Docs for platform releases that are not published yet, Deploy Snapshot Docs when the platform default branch has moved, and Deploy Guides when guides master has moved.

The web target uses GitHub Pages Actions deployment from the uploaded dist artifact. The docs and guides Pages targets branch-deploy to micronaut-projects/micronaut-docs-v2:gh-pages and micronaut-projects/micronaut-guides-v2:gh-pages. Those destinations are workflow-level constants rather than inputs, so a publish cannot be pointed at another branch by accident.

The web workflow uses the repository's GitHub Pages Actions permissions (pages:write and id-token:write) and does not need a branch-publish token. Docs and guides push to a different repository, so both require GH_TOKEN with contents:write access to their publication target.

External source repositories checked out by the manual docs and guides workflows are placed under external/ in the GitHub workspace. actions/checkout paths must stay inside the workspace; do not use ${{ runner.temp }} for those checkouts.

The docs workflow resolves the Platform ref explicitly as a branch first, then as an exact tag, then as a v-prefixed tag such as v4.10.14, and finally as a raw Git ref or SHA. The ref comes from client_payload.sha on a platform release and otherwise from docs_version, so publishing docs_version=4.10.14 resolves the Platform tag v4.10.14.

Routing Inputs

The build reads these deployment inputs:

  • ASTRO_BASE: deployment path used for pages and assets, such as /micronaut-web/, /micronaut-docs-v2/, or /micronaut-guides-v2/. Docs and guides workflows derive it from their publication repository; an existing or configured custom domain uses /.
  • MICRONAUT_DEPLOY_SURFACE: active surface, one of main, docs, guides, or all.
  • MICRONAUT_DOCS_ROOT: docs root in the current artifact. It is /docs for all-in-one preview, /<major>.<minor>.x for a published docs release, and /latest for a local standalone docs build.
  • MICRONAUT_DOCS_LATEST_ROOT: latest docs root, normally /latest.
  • MICRONAUT_GUIDES_ROOT: public guides root in the current artifact. Source guide pages are authored under /guides; standalone guides builds publish that rendered tree at /latest.
  • MICRONAUT_GUIDES_LATEST_ROOT: latest guides root, normally /latest.
  • MICRONAUT_PREPARE_GENERATED_CONTENT: set to false, 0, or none to skip generated docs/guides rendering before Astro starts. build:main sets this to false by default; docs and guides builds leave it enabled but prepare only the generated content for their own surface.
  • DEFAULT_GITHUB_PAGES_ORIGIN: computed by GitHub Actions from the workflow repository owner, for example https://${GITHUB_REPOSITORY_OWNER}.github.io. The current docs or guides surface URL is derived separately from the publication repository.
  • MICRONAUT_GITHUB_PAGES_ORIGIN: effective GitHub Pages host override. When unset, it falls back to DEFAULT_GITHUB_PAGES_ORIGIN.
  • MICRONAUT_MAIN_SITE_URL, MICRONAUT_DOCS_SITE_URL, MICRONAUT_GUIDES_SITE_URL: optional complete surface URL overrides. In docs and guides workflows, the active surface is derived from the publication repository or its custom domain; remaining defaults use MICRONAUT_GITHUB_PAGES_ORIGIN plus the repository name.
  • MICRONAUT_DOCS_BASE, MICRONAUT_GUIDES_BASE: optional repository variables that override the derived docs or guides deployment base.
  • MICRONAUT_DOCS_CUSTOM_DOMAIN, MICRONAUT_GUIDES_CUSTOM_DOMAIN: optional repository variables used when creating a custom-domain deployment. For branch deployments, an existing root CNAME is detected automatically and preserved.
  • MICRONAUT_DOCS_SNAPSHOT_SITE_URL, MICRONAUT_DOCS_SNAPSHOT_CUSTOM_DOMAIN, MICRONAUT_DOCS_SNAPSHOT_BASE: optional repository variables for the snapshot docs deployment. They stand in for the docs surface variables in Deploy Snapshot Docs, whose docs site is the snapshot deployment rather than the released docs host.

All app links should go through src/lib/base-path.ts or src/lib/deployment-config.ts. Those helpers translate /docs, /guides, and /latest links so the same source can run as an all-in-one preview, standalone docs, standalone guides, or the main site linking to external docs/guides.

Docs Versions and Latest

Docs are versioned by release line. The docs publish workflow takes a single input, docs_version, for example 4.10.14. It also accepts a platform-released repository-dispatch event from Micronaut Platform with a client_payload.version in the same format and an optional client_payload.sha for the exact source revision. A version may carry a prerelease suffix, such as 5.2.0-M1.

Each release is published under its major.minor.x line, so 5.0.3 replaces the /5.0.x/ tree 5.0.2 published. A reader's bookmark survives every patch and only has to move when they follow a new minor. /latest is never a copy of the docs: it is a tree of redirects onto the newest line, so opening /latest/core/ lands on /5.2.x/core/ and the URL that gets shared, indexed, and bookmarked names the line the docs came from.

The docs workflow:

  1. Checks out this repository.
  2. Checks out micronaut-projects/micronaut-docs-v2 on gh-pages.
  3. Checks out the requested Micronaut Platform ref for docs sources and metadata.
  4. Resolves the release line for docs_version and refuses the run when that line already publishes a newer release, so a replayed or re-dispatched release event cannot downgrade published docs. Every publication moves /latest only when the version is newer than every retained Platform version, so a later v4 publication cannot replace a v5 /latest, and a republish of an older line leaves /latest alone.
  5. Runs scripts/update-docs-version-manifest.ts against the existing published branch. This rebuilds the selector data from the published lines plus the line currently being published. Because a line folder is named after the line and not the patch inside it, each entry also records the release it was built from, and the selector shows that release — 5.0.3, not 5.0.x — so a reader is told which docs they are actually reading. A line published before the manifest recorded a release falls back to naming itself.
  6. Builds a docs surface with MICRONAUT_DOCS_ROOT=/${docs_line} and MICRONAUT_DOCS_LATEST_ROOT=/latest.
  7. Runs scripts/publish-docs-surface.ts to merge the new version into the published branch.
  8. Commits and pushes the branch.

The docs merge stores deployment metadata with the published artifact. If the base or complete surface URLs change, retained version HTML and text assets are migrated to the new deployment values before unused shared assets are pruned. This allows a one-time move from the repository-path Pages URL to a root custom domain without rebuilding every historical docs source.

The docs workflow does not check out or render Micronaut Guides.

.github/workflows/publish-upstream-updates.yml covers the fact that GitHub Actions cannot subscribe to events in another repository. It runs hourly and can also be started manually. For docs it lists the newest stable patch of every Micronaut Platform major/minor line, then publishes a release only when it supersedes the newest published patch of its own line or opens a line newer than everything published, so major/minor lines that were never published stay unpublished. Each version is dispatched to Deploy Docs oldest first, waiting for the previous publish because a queued run in the docs-pages concurrency group is cancelled when a newer one joins it. For guides it compares .guides-revision on the guides Pages branch, written by Deploy Guides with the revision it built, against master in micronaut-projects/micronaut-guides, and dispatches Deploy Guides for the exact upstream commit when they differ. For snapshot docs it compares .platform-revision on the snapshot Pages branch against the platform default branch and dispatches Deploy Snapshot Docs for the exact upstream commit when they differ, after the release publishes of the same run, because both publishes push the released docs branch. Once Micronaut Platform and Micronaut Guides send platform-released and guides-updated repository-dispatch events, this workflow becomes a safety net rather than the primary trigger.

.github/workflows/republish-published-docs.yml is a manual-only workflow for occasional full refreshes, such as a base-path or shared-asset migration. It is deliberately not scheduled, because a full republish rebuilds every retained line. It reads the releases recorded in the published versions.json together with any exact-version folders left from before docs moved to release lines, sorts them from oldest to newest, and rebuilds them one at a time. Each rebuild waits for the preceding docs publish workflow to succeed before starting, so shared Pages assets and the version manifest are updated deterministically. Only the final, newest version refreshes /latest, because Deploy Docs moves /latest solely for the newest published version.

The published docs branch layout is:

  • /index.html: docs selector/index.
  • /versions.json: compact selector data for published versions. scripts/update-docs-version-manifest.ts regenerates src/data/docs-versions.json from the published docs branch during a docs deploy; the tracked copy is only the static fallback the selector renders before that fetch resolves, so it deliberately carries no patch version and must not be updated per release.
  • /latest/<page>/: redirect onto the same page of the newest published line. Every page of that line is mirrored, so historical /latest/... links keep working; /latest/guide/ is the compatibility alias for Core docs and redirects straight to /<line>/core/.
  • /latest.html: redirect to the newest published line.
  • /snapshot/<page>/: redirect onto the same page of the snapshot Pages site, refreshed by Deploy Snapshot Docs.
  • /<major>.<minor>.x/: docs tree for a published release line, replaced by each new patch of that line.
  • /<major>.<minor>.x.html: compatibility redirect to /<major>.<minor>.x/.
  • /<version>/ and /<version>.html: redirects to the line that replaced an exact version published before docs moved to release lines.
  • /_astro/: Astro-generated scripts and styles.
  • /assets/<hash>/...: shared generated content assets.
  • /assets/... and /docsassets/...: preserved upstream docs assets when already present in the published branch.

Older lines are not copied into new builds. They remain in gh-pages from previous publishes, and the selector links to those existing line folders or legacy external URLs.

Docs Snapshot

Snapshot docs are the same docs surface built from the Micronaut Platform default branch instead of a release. DOCS_SNAPSHOT_SOURCES=true renders every project the catalog names from its own default branch, the branch each project publishes its own snapshot documentation from, rather than from the release tag the catalog pins. Each project then reports the version that branch builds, read from its gradle.properties, so the pages, the project cards, and the source links all describe the unreleased sources.

They are published to micronaut-projects/micronaut-docs-snapshot on gh-pages, as a single force-pushed orphan commit. The tree is rebuilt whenever upstream moves, so keeping its history would add a full docs tree to the repository several times a week; the branch therefore carries exactly one commit, plus a .platform-revision marker recording the built revision. Publish Upstream Updates compares that marker with the platform default branch and starts Deploy Snapshot Docs when they differ, the same way it polls guides.

A GitHub Pages host serves exactly one repository, so https://docs.micronaut.io/snapshot/ cannot be the snapshot repository's tree. Deploy Snapshot Docs mirrors the pages it built into /snapshot/ on the released docs branch as redirect stubs onto the snapshot Pages site, page for page, the way /latest mirrors the newest release line. scripts/publish-docs-snapshot.ts writes that mirror; the stubs preserve the query and fragment, so a deep link such as /snapshot/guide/#nettyClientPipeline lands on the same anchor of the snapshot site. A stub never reuses the destination of a page that is itself a redirect, because a destination inside the snapshot tree is resolved against the snapshot deployment base, which does not exist on the released docs host.

Both this workflow and Deploy Docs push the released docs branch, so Deploy Snapshot Docs shares the docs-pages concurrency group, and the poller dispatches it only after the release publishes of the same run have finished.

Snapshot deployment values default to the publication repository: https://micronaut-projects.github.io/micronaut-docs-snapshot/ with base /micronaut-docs-snapshot/. MICRONAUT_DOCS_SNAPSHOT_SITE_URL, MICRONAUT_DOCS_SNAPSHOT_CUSTOM_DOMAIN, and MICRONAUT_DOCS_SNAPSHOT_BASE override them; an existing CNAME on the snapshot branch is read back before the force push and preserved.

The published snapshot branch layout is:

  • /index.html: docs selector/index.
  • /snapshot/<project>/: snapshot docs tree.
  • /snapshot.html: compatibility redirect to /snapshot/.
  • /.platform-revision: the platform revision this snapshot was built from.
  • /_astro/ and /assets/<hash>/...: Astro-generated and shared generated content assets.

Guides Latest

Guides are currently published as a latest-only surface. The guides workflow accepts the guides repository/ref to build from, checks out micronaut-projects/micronaut-guides-v2 on gh-pages, builds npm run build:guides, replaces the published branch contents with the pruned dist plus a .guides-revision marker recording the built commit, and pushes gh-pages. It does not check out Micronaut Platform or render generated docs.

The published guides branch layout is:

  • /index.html: redirect to /latest/.
  • /latest/: latest guides catalog and generated guide pages.
  • /latest/index.html: compatibility entry point.
  • /latest/<guide>/: guide overview or detail pages.
  • /_astro/: Astro-generated scripts and styles.
  • /assets/<hash>/...: shared generated guide content assets.

Guide ZIP downloads stay as redirects to the production guide ZIP URLs instead of storing ZIP payloads in the GitHub Pages branch.

Shared Assets

There are three asset groups:

  • _astro: Astro-generated JavaScript and CSS. Surface pruning preserves reachable files in this folder, removes unreferenced chunks, and writes .nojekyll so GitHub Pages serves underscore-prefixed paths.
  • public/micronaut-assets: source-controlled brand, icon, and main-site assets. The main surface serves all of them; micronaut-assets/icons/ is also copied into the docs and guides artifacts by prune-surface.ts, because the docs index renders one icon per project and should not fetch above-the-fold imagery from the main-site origin. surface-path-rules.ts keeps those paths surface-local; everything else under micronaut-assets/ resolves to the main site.
  • Generated docs/guides content assets: images and copied resources produced under generated assets folders before pruning.

Docs and guides surface pruning hoists generated docs/guides content assets out of version folders into content-addressed /assets/<hash>/... folders, then rewrites generated HTML to reference those shared files. The hash is based on file content, so identical assets across versions share one folder instead of being duplicated under every version. Docs publication preserves referenced shared hash folders and removes unreferenced generated hash folders so old assets do not accumulate unnecessarily.

UX And Compatibility Contract

This section is the maintainer-facing contract for Micronaut web UI/UX work, legacy URL handling, and production host mapping.

PageSpeed Baselines

Observed baselines before the compatibility pass:

Surface URL Form factor Performance Accessibility Best Practices SEO Notes
Homepage ${MICRONAUT_GITHUB_PAGES_ORIGIN}/micronaut-web/ Desktop 100 93 100 100 Main issues were shared link naming and low-contrast brand opacity patterns.
Homepage ${MICRONAUT_GITHUB_PAGES_ORIGIN}/micronaut-web/ Mobile 96 93 100 100 Cache lifetime is partly hosting-controlled.
Docs index ${MICRONAUT_GITHUB_PAGES_ORIGIN}/micronaut-web/docs/ Mobile 93 95 100 100 Main issues were unnamed generated anchors, image dimensions, and unused hydrated JS.
Core docs ${MICRONAUT_GITHUB_PAGES_ORIGIN}/micronaut-web/docs/core/ Desktop Needs re-run after generated docs render Needs re-run after generated docs render Needs re-run after generated docs render Needs re-run after generated docs render Core is the heaviest generated page and the main DOM-size/performance risk.

Known Core docs findings:

  • The generated Core page can exceed 100k DOM nodes when the full guide is rendered.
  • Generated heading anchor links must have accessible names.
  • Generated docs images should have stable dimensions or get dimensions attached once loaded.
  • Render-blocking and unused JavaScript should be reduced by keeping static page chrome server-rendered where possible and hydrating only interactive controllers.

UI/UX Findings And Intended Fixes

  • Header and footer logo links need explicit accessible names because the visual logo image is decorative.
  • Generated heading anchors need aria-label values derived from heading text.
  • Repeated generic links such as guide card "Read" actions need item-specific labels.
  • Brand-colored text should not rely on reduced opacity where that can lower contrast.
  • Logos, project icons, customer logos, language icons, and generated docs images need stable sizing to reduce layout shift.
  • Docs/Core should favor readable generated content, a predictable sidebar, and active section state without making the generated article part of a large hydrated island.
  • Guides index must remain available at /latest/index.html and support URL-backed filtering with q, category, tag, and sort.

Compatibility Manifest Schema

Route aliases and redirects should be added through src/lib/route-compatibility.ts so compatibility remains centralized.

Each route compatibility entry has:

Field Purpose
id Stable identifier used by tests and route modules.
sourceSurface Surface receiving the legacy URL: main, docs, guides, or assets.
sourcePath Representative legacy path, with {placeholder} markers for route families.
destinationSurface Surface that owns the canonical destination.
previewDestinationPath All-in-one preview target, before ASTRO_BASE is applied.
productionDestinationPath Production target path on destinationSurface.
status Expected status for redirect-style routes, or 200 for production aliases.
behavior canonical, redirect, external-redirect, or alias.
preservesSearch Whether query strings are expected to survive the redirect.
preservesHash client, same-document, or not-available. URL fragments are not sent to servers, so server redirects cannot preserve them.
notes Maintainer context and constraints.

When a legacy route is added, update the manifest and add or update one route module. Avoid duplicating redirect tables across pages.

Compatibility Matrix

Representative old URL Preview behavior Production behavior Notes
https://micronaut.io/core/ Deferred until /core/ routing is resumed Deferred until main-host Core routing is resumed Tracked here as a known legacy URL, but not implemented in this pass.
/core/?q=bean Deferred until /core/ routing is resumed Deferred until main-host Core routing is resumed Query/hash preservation should be covered when this route is reintroduced.
https://docs.micronaut.io/latest/guide/ /micronaut-web/latest/guide/ redirects to /micronaut-web/docs/core/ Redirects to Core docs on the newest published line Every /latest/ page redirects onto the line it mirrors.
https://docs.micronaut.io/latest/guide/index.html#ioc /micronaut-web/latest/guide/index.html redirects to /micronaut-web/docs/core/#ioc in client-capable redirects Redirects to Core docs on the newest line, same section Fragments require client redirect pages.
https://guides.micronaut.io/latest/index.html /micronaut-web/latest/index.html redirects to /micronaut-web/guides/ Redirects to https://guides.micronaut.io/ Query strings are preserved.
https://guides.micronaut.io/latest/tag-security.html Redirects to the generated /guides/ tag route when present, otherwise external production tag URL Redirects to the root-level guide tag Existing tag compatibility stays generated from guide metadata.
https://guides.micronaut.io/latest/micronaut-http-client.html Redirects to slash-style generated /guides/ overview when present, otherwise external production URL Redirects to the root-level guide overview Applies to guide overview .html pages.
https://guides.micronaut.io/latest/micronaut-http-client-gradle-java.html Redirects to slash-style generated /guides/ variant when present, otherwise external production URL Redirects to the root-level guide variant Applies to language/build variants.
https://guides.micronaut.io/latest/micronaut-http-client-gradle-java.zip Redirects to production ZIP URL ZIP remains downloadable at the root ZIP redirects are temporary external redirects.
https://micronaut.io/blog/2020-04-30-introducing-micronaut-launch.html Redirects to the canonical dated post Redirects to the canonical dated post Dated blog aliases are generated from post metadata.
https://micronaut.io/blog/2019-07-18-unleashing-predator-precomputed-data-repositories.html Redirects to /2019/07/18/announcing-micronaut-data/ Same canonical post Explicit historical aliases remain in blog-redirects.
Any supported route with #section Same-document anchors stay intact; client redirect pages preserve hashes across route changes Same rule Server redirects cannot see fragments.

Canonical URL Rules

  • Main-site canonical content uses https://micronaut.io/.
  • Docs canonical catalog pages use https://docs.micronaut.io/; Core legacy docs paths continue to resolve.
  • Guides canonical generated guide URLs use https://guides.micronaut.io/; /latest/ is retained only for redirects.
  • GitHub Pages preview URLs must always be generated with ASTRO_BASE, not hard-coded as production links.
  • Split GitHub Pages deployments must use the repo-name bases until custom domains are enabled: /micronaut-web/, /micronaut-docs-v2/, and /micronaut-guides-v2/.
  • Docs version publishing updates the selector from the Pages branch and preserves shared root assets so older versions do not duplicate the same asset trees.
  • Legacy URLs should prefer permanent redirects unless the destination points to another production host for generated artifacts, where temporary redirects are safer.
  • Main-host /core/ compatibility is intentionally deferred for now; add it through src/lib/route-compatibility.ts when route work resumes.
  • src/layouts/WebLayout.astro is the single place social and structured-data tags are emitted. Open Graph, Twitter card, and JSON-LD values come from the title and description props, so a page that wants a useful share card passes a real description; blog posts and guides also pass pageType="article" and publishedTime. The share image is served from the main site for every surface.

Accessibility And Performance Acceptance Criteria

  • Docs accessibility target: 100 after generated docs are present.
  • No known shared unnamed logo link or generated heading-anchor link remains.
  • Repeated generic links must have accessible names that identify the target item.
  • CLS remains 0 for docs mobile.
  • Docs mobile performance improves from the baseline without introducing new render-blocking script work.
  • Images in static components have explicit intrinsic dimensions or a stable sizing wrapper.
  • Generated docs images receive lazy loading, async decoding, and dimensions when the browser can determine them.

Manual QA Checklist

  • Build with ASTRO_BASE=/micronaut-web/ and verify all preview links keep the base path.
  • Check /, /docs/, /docs/core/, /latest/index.html, /latest/guide/, /latest/guide/index.html, /guides/, a guide detail page, /blog/, a dated blog alias, and a content page.
  • Repeat the checks on desktop and mobile widths.
  • Verify /latest/guide/index.html#ioc preserves the fragment after redirect.
  • Verify guides filters update and restore state with q, category, tag, and sort.
  • Re-run PageSpeed or Lighthouse-style checks for homepage, docs index, and Core docs after deployment assets are available.

Automated Checks

Run:

npm run typecheck
npm run typecheck:scripts
npm run test:main-site
npm run test:asciidoc
npm run test:guides
npm run test:docs
npm run build

This repository keeps shared Asciidoc renderer coverage in test:asciidoc and docs integration coverage in test:docs; use test:docs for planned test:platform-docs coverage unless a separate script is introduced.

Astro Publishing Best Practices

Keep published content canonical. A content page should have one source route, and compatibility URLs such as legacy blog paths should redirect to that route instead of rendering duplicate page bodies. When adding a compatibility route, prefer the centralized redirect and alias helpers so redirects remain noindex and easy to audit.

Route internal links through src/lib/base-path.ts or src/lib/deployment-config.ts. Do not hard-code /docs, /guides, /latest, or GitHub Pages repository prefixes in components or generated HTML; the deployment helpers keep links valid across all-in-one preview, standalone docs, standalone guides, and the main site.

Use Astro islands deliberately. Hydrate only UI that must run in the browser, and prefer static Astro markup for shared layout such as headers, navigation structure, and content shells wherever possible. Reserve client:load for controls that must be interactive immediately; use client:idle or client:visible for lower-priority UI.

Island Usage

Default to static Astro components for route chrome and generated content. Generated docs, generated guides, sidebar link trees, version lists, and article bodies should render as HTML first and should not become React islands just to attach a small behavior.

Use a React island only when the component owns meaningful client state, keyboard handling, focus management, or third-party interactive primitives. Current docs pages intentionally hydrate SiteHeader, DocsSidebarRailControl, and DocsSidebarMobileSheet. The guides catalog renders each card from src/components/web/latest-guide-card.astro and hydrates only GuideVariantMenu, and only for guides that offer more than one variant. Docs scroll-spy behavior lives in src/components/web/docs-scroll-spy.astro as a processed Astro script. Do not reintroduce a docs version switcher island; src/components/web/docs-version-selector.astro renders fallback options statically and uses a processed Astro script to fetch /versions.json.

For small native controls, use an Astro component plus a processed <script> instead of client:*. The script should import shared routing helpers, leave server-rendered fallback markup usable when fetches fail, and enhance all matching instances on the page. If the control can appear inside a hydrated React island, such as content passed into a mobile sheet, use delegated events and handle late DOM insertion through astro:hydrate or a narrowly scoped observer.

When adding, removing, or changing an island, inspect built HTML for <astro-island> markers and update tests that encode the expected island inventory. A docs page should not include DocsVersionSwitcher in component-export, component-url, or bundled source output.

Run generated-content processing at build time. Syntax highlighting, Asciidoctor rendering, properties/YAML/TOML/config conversion, and generated-content transformations must not ship to the browser runtime. Do not include shiki, @shikijs/*, asciidoctor, @asciidoctor/*, or config conversion/parsing libraries in public client bundles.

Use is:inline only for intentional critical scripts, such as the early theme script that prevents a visible color-mode flash. Non-critical scripts should be processed by Astro so they can be bundled, deduplicated, and cached.

To open any Astro-rendered page with a chosen color mode, add ?theme=dark or ?theme=light to its URL. The preset overrides the saved mode and becomes the saved preference for subsequent navigation.

Keep shared CSS focused. Global design tokens and cross-surface primitives belong in src/styles/globals.css, but route-specific styling should stay close to the route or component that needs it. Avoid adding one-off page styles to the global baseline.

Deduplicate public assets before publishing. Prefer canonical asset paths for shared logos, brand files, PDFs, and generated content assets, and keep legacy copies only when external compatibility requires them.

Prune published artifacts. Surface publication should remove unreferenced _astro chunks and unrelated surface output so Pages branches do not accumulate stale JavaScript, CSS, docs, guides, or template artifacts.

Pin npm dependencies. Avoid latest ranges in package.json; use exact or caret-bounded versions so local builds, CI, and Pages publishes resolve the same Astro, React, Vite, and tooling behavior.

About

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages