diff --git a/.env b/.env index 4174c07f2..4550c22ff 100644 --- a/.env +++ b/.env @@ -12,7 +12,7 @@ PROJECT_NAME="airstack" # If you've run ./airstack.sh setup, then this will auto-generate from the git commit hash every time a change is made # to a Dockerfile or docker-compose.yaml file. Otherwise this can also be set explicitly to make a release version. # auto-generated from git commit hash -VERSION="0.20.6" +VERSION="0.20.7" # Image-tag discriminator ONLY (appears in the image tag suffix, e.g. ..._robot-x86-64_dev). # No Dockerfile consumes it: "prebuilt" does NOT bake the built ros_ws into the image today — # a real prebuilt (workspace-baked) stage is future work. Keep "dev" (mounted code, built live). diff --git a/.github/workflows/deploy_docs_from_develop.yaml b/.github/workflows/deploy_docs_from_develop.yaml index f312c9d1b..4944f7c27 100644 --- a/.github/workflows/deploy_docs_from_develop.yaml +++ b/.github/workflows/deploy_docs_from_develop.yaml @@ -108,6 +108,10 @@ jobs: git config --global user.name "Docs Deploy" git config --global user.email "docs.deploy@example.co.uk" - name: Build Docs Website + env: + # "Edit this page" links on the develop docs edit the develop + # branch (mkdocs.yml defaults edit_uri to edit/main/). + DOCS_EDIT_URI: edit/develop/ run: | VERSION=$(grep -m1 '^VERSION=' .env | cut -d= -f2- | tr -d '"') mike deploy --push --title "${VERSION} (unstable)" develop diff --git a/.github/workflows/deploy_docs_from_main.yaml b/.github/workflows/deploy_docs_from_main.yaml index 232d75285..432bf1aca 100644 --- a/.github/workflows/deploy_docs_from_main.yaml +++ b/.github/workflows/deploy_docs_from_main.yaml @@ -8,6 +8,7 @@ on: - "stacks/**" - "tools/gen_docs_catalog.py" - ".github/workflows/deploy_docs_from_main.yaml" + - ".github/workflows/templates/root-redirect.html" branches: - main # Serialize gh-pages pushes: concurrent docs deploys race on the branch @@ -74,8 +75,11 @@ jobs: mike deploy --push --update-aliases --title "${VERSION}" "${SLUG}" main # Root URL redirects to the PINNED slug (not the moving alias): URLs # people land on and copy stay valid across future releases. /main/ - # remains as a moving alias for deep links. - mike set-default "${SLUG}" --push + # remains as a moving alias for deep links. The custom template adds + # a real and Open Graph tags so a shared docs.theairlab.org + # link previews properly instead of as "Redirecting" (mike renders + # it with {{href}} = the slug). + mike set-default "${SLUG}" --push -T .github/workflows/templates/root-redirect.html # mike re-sorts versions.json on every deploy, so pin develop — the # higher, unreleased version — back to the top of the version selector # after each deploy. diff --git a/.github/workflows/templates/root-redirect.html b/.github/workflows/templates/root-redirect.html new file mode 100644 index 000000000..a5a499eb0 --- /dev/null +++ b/.github/workflows/templates/root-redirect.html @@ -0,0 +1,40 @@ +<!DOCTYPE html> +<!-- Custom template for `mike set-default -T` (deploy_docs_from_main.yaml). + Same instant redirect as mike's default, plus real <title>/description + and Open Graph / Twitter Card tags: link-preview scrapers (Slack, + WhatsApp, iMessage, ...) fetch this page's raw HTML and neither run + JavaScript nor follow meta-refresh, so without these tags a shared + https://docs.theairlab.org/ link previews as just "Redirecting". + mike substitutes {{href}} with the default version's slug (e.g. "0.20/"). + The og:image points at the moving /main/ alias so the URL stays valid + across releases without editing this file. --> +<html lang="en"> +<head> + <meta charset="utf-8"> + <title>AirStack Documentation + + + + + + + + + + + + + + + + + + Redirecting to {{href}}... + + diff --git a/docs/hooks/social_meta_redirect_stub.py b/docs/hooks/social_meta_redirect_stub.py new file mode 100644 index 000000000..389292f7d --- /dev/null +++ b/docs/hooks/social_meta_redirect_stub.py @@ -0,0 +1,73 @@ +"""Add link-preview metadata to the version-root redirect stub. + +With ``docs_dir: .`` the site's version root (e.g. ``/0.20/``) is a bare +redirect page generated by mkdocs-redirects (``index.md`` -> ``docs/index.md``) +titled "Redirecting...". Link-preview scrapers (Slack, WhatsApp, iMessage, ...) +read that page's raw HTML and neither run JavaScript nor follow meta-refresh, +so a shared version-root URL previews as just "Redirecting...". + +This hook rewrites the generated ``/index.html`` after the build: +a real title plus the same Open Graph / Twitter Card block the theme override +(docs/overrides/main.html) puts on regular pages. MkDocs runs hooks after +plugins for each event, so the redirect stub already exists on_post_build. +""" + +import logging +import os +import re + +log = logging.getLogger("mkdocs.hooks.social_meta_redirect_stub") + +TITLE = "AirStack Documentation" +DESCRIPTION = ( + "An open-source ROS 2 autonomy stack for aerial robots — simulation, " + "ground control, and layered onboard autonomy that launch as one system. " + "By the AirLab at Carnegie Mellon University." +) +# The moving /main/ alias keeps this absolute URL valid across releases +# (mike copies real asset files into alias directories). +IMAGE = "https://docs.theairlab.org/main/docs/assets/media/splash-poster.jpg" + +META_BLOCK = f"""\ + + + + + + + + + + + + +""" + + +def on_post_build(config, **kwargs): + index_path = os.path.join(config["site_dir"], "index.html") + if not os.path.isfile(index_path): + log.warning("site index.html not found; skipping social meta injection") + return + with open(index_path, encoding="utf-8") as f: + html = f.read() + if "Redirecting" not in html: + # Not the redirect stub this hook expects (e.g. the root became a + # real page) — leave it alone. + log.info("site index.html is not a redirect stub; skipping") + return + html, n_title = re.subn( + r"[^<]*", f"{TITLE}", html, count=1 + ) + html, n_head = re.subn( + r"", META_BLOCK + "", html, count=1, flags=re.IGNORECASE + ) + if not (n_title and n_head): + log.warning( + "could not inject social meta into redirect stub " + "(title replaced: %d, head found: %d)", n_title, n_head + ) + return + with open(index_path, "w", encoding="utf-8") as f: + f.write(html) + log.info("injected social meta into version-root redirect stub") diff --git a/docs/overrides/main.html b/docs/overrides/main.html new file mode 100644 index 000000000..7e7603d40 --- /dev/null +++ b/docs/overrides/main.html @@ -0,0 +1,37 @@ +{% extends "base.html" %} + +{#- + Open Graph / Twitter Card tags for link previews (Slack, WhatsApp, + iMessage, ...). Material only emits these via its `social` plugin, which + we don't run — this block provides them statically on every page. + home.html extends this template, so the whole site inherits the tags. + og:image uses the moving /main/ alias (mike copies real asset files into + alias dirs) so the absolute URL stays valid across releases. + og:url is deliberately omitted: site_url doesn't include the mike version + slug, so a computed canonical would point at a non-existent path. +-#} +{% block extrahead %} + {% if page and page.meta and page.meta.title %} + {% set og_title = page.meta.title ~ " - " ~ config.site_name %} + {% elif page and page.title and not page.is_homepage %} + {% set og_title = page.title ~ " - " ~ config.site_name %} + {% else %} + {% set og_title = config.site_name %} + {% endif %} + {% if page and page.meta and page.meta.description %} + {% set og_description = page.meta.description %} + {% else %} + {% set og_description = config.site_description %} + {% endif %} + + + + + + + + + + + +{% endblock %} diff --git a/docs/release_notes/index.md b/docs/release_notes/index.md index 983632cb7..5cc036570 100644 --- a/docs/release_notes/index.md +++ b/docs/release_notes/index.md @@ -21,6 +21,32 @@ its own notes. --> - Nothing yet. +## 0.20.7 — 2026-08-30 + +- **Fixed 404s on every README-sourced docs page.** The `exclude_docs` + pattern intended to hide the repo-root `README.md` used gitignore + semantics, so it silently excluded *every* `README.md` in the tree — + 404ing the System Test Suite (`/tests/`), OSMO Lab Admin Guide + (`/osmo/`), and all autonomy package reference pages. The pattern is + now anchored to the repo root (`/README.md`). +- **Shared docs links now show real previews.** Sharing + `docs.theairlab.org` in Slack/WhatsApp/iMessage previewed as + "Redirecting" — preview scrapers don't run JavaScript, and the site + root, the version root (`/0.20/`), and every content page lacked + Open Graph metadata. The root redirect now deploys from a template + with proper title/description/image tags (`mike set-default -T`), a + build hook enriches the version-root redirect stub, and a theme + override (`docs/overrides/main.html`) plus `site_description` add + per-page Open Graph / Twitter Card tags across the site. Note: + previously shared links may keep the stale preview until the apps' + link caches expire. +- **"Edit this page" button on every docs page.** Each page now shows a + pencil icon (Material `content.action.edit` + `edit_uri`) linking to + GitHub's editor for that page's source file; contributors without + write access get GitHub's fork-and-propose flow. Pages built from + `main` link to `edit/main/`, the develop docs to `edit/develop/` + (via `DOCS_EDIT_URI` in the develop deploy workflow). + ## 0.20.6 — 2026-08-29 - **Release notes now carry a dated section per hotfix.** Added the diff --git a/mkdocs.yml b/mkdocs.yml index 28eaa2bcd..06db3374d 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -2,12 +2,19 @@ copyright: Copyright © 2024 - 2030 AirLab CMU docs_dir: . site_name: AirStack +site_description: >- + AirStack is an open-source ROS 2 autonomy stack for aerial robots from the + AirLab at Carnegie Mellon University — simulation, ground control, and + layered onboard autonomy that launch as one system. site_dir: ../site site_url: "https://docs.theairlab.org/docs/" # Trailing slash is recommended exclude_docs: | # The repo README is GitHub-facing; the site's home page is docs/index.md, - # and same-dir would otherwise collide on the root URL. - README.md + # and same-dir would otherwise collide on the root URL. Patterns are + # gitignore-style: the leading slash anchors this to the repo root only — + # a bare README.md would exclude every package/tests/osmo README the nav + # serves as pages. + /README.md **/ros_ws/build **/docker/Foxglove **/ros_ws/install @@ -40,6 +47,9 @@ extra_css: # build time, so each mike-deployed docs version shows only its own notes. hooks: - docs/hooks/release_notes_current_version.py + # Give the version-root redirect stub (/X.Y/ -> docs/) a real title and + # Open Graph tags so shared links preview properly (Slack, WhatsApp, ...). + - docs/hooks/social_meta_redirect_stub.py markdown_extensions: - admonition - attr_list @@ -243,6 +253,11 @@ plugins: 'docs/tutorials/index.md': 'docs/getting_started/tutorials_reference.md' repo_name: castacks/AirStack repo_url: https://github.com/castacks/AirStack +# Per-page "Edit this page" pencil (theme feature content.action.edit). +# docs_dir is the repo root, so page paths are already repo-relative. +# GitHub shows contributors without write access its fork-and-edit flow. +# The develop docs deploy overrides the branch via DOCS_EDIT_URI. +edit_uri: !ENV [DOCS_EDIT_URI, "edit/main/"] theme: favicon: docs/assets/StackedWhite.png custom_dir: docs/overrides @@ -266,6 +281,7 @@ theme: - content.code.copy - content.code.annotate - content.tooltips + - content.action.edit font: text: Inter code: JetBrains Mono