Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .github/workflows/docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,14 @@ jobs:
outputs:
version-name: ${{ steps.ver.outputs.version-name }}
steps:
# fetch-depth: 0 brings the full history *and tags* — a build-command may
# derive a version from `git describe` (this repo's docs do, to pin the
# tutorial's snippets at the latest release), and the default shallow clone
# has no tags. Tags are cheap; fetch them unconditionally so any consumer's
# build-command can rely on them.
- uses: actions/checkout@v7
with:
fetch-depth: 0

# uv is installed unconditionally (Node/npx is already on the runner) so every
# docs build has the same baseline env regardless of build-command. With no
Expand Down
66 changes: 66 additions & 0 deletions docs/_ext/inject-latest-tag.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/**
* inject-latest-tag — a build-only MyST transform for *this repo's own docs*.
*
* The tutorial's copy-paste snippets pin this project at a release tag. Rather
* than hard-coding (and forever bumping) a version, the source carries the
* sentinel `__LATEST_TAG__` and this transform swaps it for the repo's latest
* tag at build time, so a reader copying the blocks gets a real, current ref.
*
* Unlike `plugins/version-switcher.mjs` (which is isomorphic — it also runs in
* the browser), this file only ever runs under MyST (Node) at build time, so it
* is free to touch git. It is loaded from `docs/myst.yml`, not shipped.
*
* The tag is read straight from `git describe` (CI checks out with fetch-depth 0
* in docs.yml, so the tags are present). When no tag can be resolved the sentinel
* is left in place and a warning is emitted, so a missing tag is loud rather than
* silently shipping `__LATEST_TAG__` to readers.
*/
import { execSync } from "node:child_process";

const SENTINEL = "__LATEST_TAG__";

/** The latest release tag (`git describe --tags --abbrev=0`), or null. */
function resolveLatestTag() {
try {
return execSync("git describe --tags --abbrev=0", {
encoding: "utf8",
stdio: ["ignore", "pipe", "ignore"],
}).trim();
} catch {
return null;
}
}

/** Replace the sentinel in every string `value` in the tree (text + code nodes). */
function replaceInTree(node, tag) {
if (!node || typeof node !== "object") return;
if (typeof node.value === "string" && node.value.includes(SENTINEL)) {
node.value = node.value.split(SENTINEL).join(tag);
}
if (Array.isArray(node.children)) {
for (const child of node.children) replaceInTree(child, tag);
}
}

const injectLatestTag = {
name: "inject-latest-tag",
doc: "Replace __LATEST_TAG__ with this repo's latest git tag at build time.",
stage: "document",
plugin: () => (tree, vfile) => {
const tag = resolveLatestTag();
if (!tag) {
vfile?.message?.(
`inject-latest-tag: no tag resolved from git describe; leaving ${SENTINEL} as-is.`,
);
return;
}
replaceInTree(tree, tag);
},
};

const plugin = {
name: "inject-latest-tag",
transforms: [injectLatestTag],
};

export default plugin;
2 changes: 2 additions & 0 deletions docs/myst.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ project:
# makes MyST localize this same file as the anywidget runtime.
plugins:
- ../plugins/version-switcher.mjs
# Build-only: swaps __LATEST_TAG__ in the tutorial for this repo's latest tag.
- ./_ext/inject-latest-tag.mjs
toc:
- file: index.md
- file: tutorials.md
Expand Down
19 changes: 11 additions & 8 deletions docs/tutorials/adding-to-a-fresh-repo.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@ version switcher and a versioned GitHub Pages deployment. By the end you will ha
- a `stable/` alias pointing at your latest release.

It assumes a repo that already builds docs with `myst build --html` from a `docs/`
directory. Replace `ORG/REPO` throughout, and pin a real `<tag>` from this project's
[releases](https://github.com/DiamondLightSource/myst-version-switcher-plugin/releases).
directory. Replace `ORG/REPO` throughout. The snippets below are pinned to the
latest release (`__LATEST_TAG__`); bump that pin to any version from this project's
[releases](https://github.com/DiamondLightSource/myst-version-switcher-plugin/releases)
when you need a different one.

## 1. Add the plugin to your MyST project

Expand All @@ -19,7 +21,7 @@ In `docs/myst.yml`, load the plugin from its release asset and route a navbar pa
# docs/myst.yml
project:
plugins:
- https://github.com/DiamondLightSource/myst-version-switcher-plugin/releases/download/<tag>/version-switcher.mjs
- https://github.com/DiamondLightSource/myst-version-switcher-plugin/releases/download/__LATEST_TAG__/version-switcher.mjs
site:
template: book-theme
parts:
Expand Down Expand Up @@ -79,7 +81,7 @@ on:

jobs:
docs: # Call the docs building workflow directly
uses: DiamondLightSource/myst-version-switcher-plugin/.github/workflows/docs.yml@<tag>
uses: DiamondLightSource/myst-version-switcher-plugin/.github/workflows/docs.yml@__LATEST_TAG__
with:
# Whatever turns your sources into docs/_build/html at $BASE_URL.
# uv and Node are preinstalled, so this can be make / tox / npx / npm.
Expand All @@ -88,7 +90,7 @@ jobs:
release:
needs: [docs]
if: github.ref_type == 'tag' # tag pushes only
uses: DiamondLightSource/myst-version-switcher-plugin/.github/workflows/release.yml@<tag>
uses: DiamondLightSource/myst-version-switcher-plugin/.github/workflows/release.yml@__LATEST_TAG__
permissions:
contents: write # create the Release + attach assets

Expand All @@ -109,10 +111,11 @@ jobs:
### `publish-dispatch.yml`

`publish.yml` is the engine and owns all the branching; this is a thin **shim** — the
only thing that calls it, and the one place you pin `@<tag>`. It has to exist as a file
only thing that calls it, and the one place you pin `@__LATEST_TAG__`. It has to exist as a file
in your repo (not just be `uses:`'d) because the tag trampoline re-dispatches it as a
`workflow_dispatch`, and a reusable workflow can't be dispatched cross-repo. Copy it
verbatim, changing only `<tag>`:
verbatim; the only thing to keep current is the `publish.yml` pin, already set to
`__LATEST_TAG__`:

```yaml
# .github/workflows/publish-dispatch.yml
Expand All @@ -126,7 +129,7 @@ on:
pr: { description: "Fork PR to approve + preview (empty = re-deploy)", required: false, default: "" }
jobs:
publish:
uses: DiamondLightSource/myst-version-switcher-plugin/.github/workflows/publish.yml@<tag>
uses: DiamondLightSource/myst-version-switcher-plugin/.github/workflows/publish.yml@__LATEST_TAG__
with:
version-name: ${{ inputs.version-name }} # "" on dispatch → pure durable gather
pr: ${{ inputs.pr }} # set (dispatch) → pin that fork head SHA
Expand Down
Loading