diff --git a/.github/workflows/build-and-deploy.yml b/.github/workflows/build-and-deploy.yml index 955a5b50..2cc6809a 100644 --- a/.github/workflows/build-and-deploy.yml +++ b/.github/workflows/build-and-deploy.yml @@ -2,11 +2,17 @@ name: Build and deploy on: push: tags: - - "*" + - "v[0-9][0-9].[0-9][0-9].[0-9][0-9]" branches: - main pull_request: workflow_dispatch: + inputs: + dry-run: + description: "dry-run: build and log what would be published without uploading or flushing the CDN" + required: false + type: boolean + default: false # Required shell entrypoint to have properly activated conda environments defaults: @@ -17,9 +23,19 @@ permissions: id-token: write contents: read +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + +env: + DEPLOYMENT_DOCS_BUILD_STABLE: ${{ github.ref_type == 'tag' && 'true' || 'false' }} + RAPIDS_DOCS_PUBLISH: "true" + DRY_RUN: ${{ inputs.dry-run || false }} + UPLOAD: ${{ (github.event_name == 'push' || github.event_name == 'workflow_dispatch') && (github.ref == 'refs/heads/main' || github.ref_type == 'tag') }} + jobs: build: name: Build (and deploy) + if: ${{ github.repository == 'rapidsai/deployment' }} runs-on: ubuntu-latest steps: @@ -33,35 +49,21 @@ jobs: with: enable-cache: false + # the release tags feed the version switcher data (see extensions/rapids_docs_publishing.py) - name: Build - env: - DEPLOYMENT_DOCS_BUILD_STABLE: ${{ startsWith(github.event.ref, 'refs/tags/') && 'true' || 'false' }} - run: uv run make dirhtml SPHINXOPTS="-W --keep-going -n" - - - uses: aws-actions/configure-aws-credentials@7474bc4690e29a8392af63c5b98e7449536d5c3a # v4.3.1 - if: ${{ github.repository == 'rapidsai/deployment' && (github.event_name == 'push' || github.event_name == 'workflow_dispatch') }} - with: - role-to-assume: ${{ vars.AWS_ROLE_ARN }} - aws-region: ${{ vars.AWS_REGION }} - role-duration-seconds: 3600 # 1h - - - name: Set version - id: set-version - run: echo "VERSION=${{ startsWith(github.event.ref, 'refs/tags/') && 'stable' || 'nightly' }}" >> "$GITHUB_OUTPUT" + run: RAPIDS_DOCS_RELEASE_TAGS="$(git tag --list)" uv run make dirhtml SPHINXOPTS="-W --keep-going -n" - - name: Sync HTML files to S3 - env: - VERSION: ${{ steps.set-version.outputs.VERSION }} - if: ${{ github.repository == 'rapidsai/deployment' && (github.event_name == 'push' || github.event_name == 'workflow_dispatch') }} - run: aws s3 sync --no-progress --delete build/dirhtml "s3://rapidsai-docs/deployment/html/${VERSION}" + - name: Read publish manifest + id: publish + run: cat build/publish/publish.env | tee -a "${GITHUB_OUTPUT}" - name: Download gha-tools with git clone run: | git clone https://github.com/rapidsai/gha-tools.git -b main /tmp/gha-tools echo "/tmp/gha-tools/tools" >> "${GITHUB_PATH}" - - name: Publish to docs.nvidia.com - if: ${{ github.repository == 'rapidsai/deployment' && (github.event_name == 'push' || github.event_name == 'workflow_dispatch') }} + - name: Publish docs to docs.nvidia.com/datascience/deployment/${{ steps.publish.outputs.TARGET }} + if: ${{ env.UPLOAD == 'true' }} uses: rapidsai/shared-actions/publish-docs@main with: akamai-access-token: ${{ secrets.NVIDIA_DOCS_AKAMAI_ACCESS_TOKEN }} @@ -69,12 +71,30 @@ jobs: akamai-client-token: ${{ secrets.NVIDIA_DOCS_AKAMAI_CLIENT_TOKEN }} akamai-emails-to-notify: ${{ secrets.NVIDIA_DOCS_AKAMAI_EMAILS_TO_NOTIFY }} akamai-host: ${{ secrets.NVIDIA_DOCS_AKAMAI_HOST }} - akamai-request-name: rapidsai-deployment-${{ steps.set-version.outputs.VERSION }} - dry-run: false + akamai-request-name: rapidsai-deployment-${{ github.run_id }}-${{ steps.publish.outputs.TARGET }} + dry-run: ${{ env.DRY_RUN }} source-path: build/dirhtml target-aws-access-key-id: ${{ secrets.NVIDIA_DOCS_AWS_ACCESS_KEY_ID }} target-aws-region: ${{ secrets.NVIDIA_DOCS_AWS_REGION }} target-aws-secret-access-key: ${{ secrets.NVIDIA_DOCS_AWS_SECRET_ACCESS_KEY }} target-s3-bucket: ${{ secrets.NVIDIA_DOCS_S3_BUCKET }} - target-s3-key-suffix: ${{ steps.set-version.outputs.VERSION }} # stable or nightly + target-s3-key-suffix: ${{ steps.publish.outputs.TARGET }} # latest or YY.MM + target-s3-key: datascience/deployment + + - name: Publish versions.json + if: ${{ env.UPLOAD == 'true' && steps.publish.outputs.VERSIONS_JSON == 'true' }} + uses: rapidsai/shared-actions/publish-one-doc@main + with: + akamai-access-token: ${{ secrets.NVIDIA_DOCS_AKAMAI_ACCESS_TOKEN }} + akamai-client-secret: ${{ secrets.NVIDIA_DOCS_AKAMAI_CLIENT_SECRET }} + akamai-client-token: ${{ secrets.NVIDIA_DOCS_AKAMAI_CLIENT_TOKEN }} + akamai-emails-to-notify: ${{ secrets.NVIDIA_DOCS_AKAMAI_EMAILS_TO_NOTIFY }} + akamai-host: ${{ secrets.NVIDIA_DOCS_AKAMAI_HOST }} + akamai-request-name: rapidsai-deployment-${{ github.run_id }}-versions-json + dry-run: ${{ env.DRY_RUN }} + file-path: build/publish/versions.json + target-aws-access-key-id: ${{ secrets.NVIDIA_DOCS_AWS_ACCESS_KEY_ID }} + target-aws-region: ${{ secrets.NVIDIA_DOCS_AWS_REGION }} + target-aws-secret-access-key: ${{ secrets.NVIDIA_DOCS_AWS_SECRET_ACCESS_KEY }} + target-s3-bucket: ${{ secrets.NVIDIA_DOCS_S3_BUCKET }} target-s3-key: datascience/deployment diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 6aa26737..f21bc7eb 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -148,9 +148,11 @@ markdownlint.............................................................Passed ## Releasing -This repository is continuously deployed to the [nightly docs at docs.rapids.ai](https://docs.rapids.ai/deployment/nightly/) via the [build-and-deploy](https://github.com/rapidsai/deployment/blob/main/.github/workflows/build-and-deploy.yml) workflow. All commits to main are built to static HTML and pushed to the [`deployment/nightly` subdirectory in the rapidsai/docs repo](https://github.com/rapidsai/docs/tree/gh-pages/deployment) which in turn is published to GitHub Pages. +This repository is continuously deployed via the [build-and-deploy](https://github.com/rapidsai/deployment/blob/main/.github/workflows/build-and-deploy.yml) workflow. Every commit to `main` is built to static HTML and published to the [latest documentation at docs.nvidia.com](https://docs.nvidia.com/datascience/deployment/latest/). -We can also update the [stable documentation at docs.rapids.ai](https://docs.rapids.ai/deployment/stable/) by creating and pushing a tag which will cause the `build-and-deploy` workflow to push to the [`deployment/stable` subdirectory](https://github.com/rapidsai/docs/tree/gh-pages/deployment) instead. +Pushing a release tag (`vYY.MM.PP`, for example `v26.08.00`) builds that commit with the stable configuration and publishes it to `https://docs.nvidia.com/datascience/deployment/YY.MM/`. Released versions stay available side by side and the version switcher in the navigation bar moves between them. Alpha tags (`vYY.MM.PPa`) do not publish anything. The switcher data (`versions.json`) and the publish target are produced by the build itself, see `extensions/rapids_docs_publishing.py`; the workflow only uploads them. + +The old `docs.rapids.ai/deployment/{stable,nightly}/` URLs redirect to the latest documentation on docs.nvidia.com. Those redirects live in the [rapidsai/docs](https://github.com/rapidsai/docs) repository (`_redirects`). The RAPIDS versions for things like container images and install instructions are templated into the documentation pages and are stored in `source/conf.py`. @@ -183,23 +185,39 @@ In those cases, use `~~~` with no spaces, like this: For more, see the docs on [dask-cuda](https://docs.rapids.ai/api/dask-cuda/~~~rapids_api_docs_version~~~/install.html) ``` -All builds will use the nightly section by default which allows you to test with the latest and greatest containers when developing locally or previewing nightly docs builds. To build the docs using the stable images you need to set the environment variable `DEPLOYMENT_DOCS_BUILD_STABLE` to `true`. This is done automatically when building from a tag in CI. +All builds will use the nightly section by default which allows you to test with the latest and greatest containers when developing locally or previewing nightly docs builds. To build the docs using the stable images you need to set the environment variable `DEPLOYMENT_DOCS_BUILD_STABLE` to `true`. This is done automatically when building from a tag in CI. The version switcher in the navigation bar only appears in CI builds (the workflow sets `RAPIDS_DOCS_PUBLISH=true`). Local and preview builds leave it out because the browser would refuse to load `versions.json` from docs.nvidia.com across origins. + +To reproduce a CI build locally, including the `build/publish` directory with `versions.json`, run: + +```bash +RAPIDS_DOCS_PUBLISH=true RAPIDS_DOCS_RELEASE_TAGS="$(git tag --list)" uv run make dirhtml SPHINXOPTS="-W --keep-going -n" +``` Before you publish a new version for a release ensure that the latest container images are available and then update the `stable` config to use the new release version and update `nightly` to use the next upcoming nightly. -Then you can push a tag to release. +Then push a release tag. The tag must have the form `vYY.MM.PP` for the workflow to publish it. ```bash -# Set next version number +# Set the release version # See https://docs.rapids.ai/resources/versions/ and past releases for version scheme -export RELEASE=x.x.x +export RELEASE=vYY.MM.00 -# Create tags -git commit --allow-empty -m "Release $RELEASE" +# Create the tag git tag -a $RELEASE -m "Version $RELEASE" # Push -git push upstream --tags +git push upstream $RELEASE +``` + +### Fixing a released version + +To correct the documentation of a release that has already been published, branch from its tag, commit the fix and push a new patch tag. The patch number is dropped from the published path, so `v26.08.01` republishes `https://docs.nvidia.com/datascience/deployment/26.08/`. + +```bash +git checkout -b fix-26.08 v26.08.00 +# ... commit the fix ... +git tag -a v26.08.01 -m "Version v26.08.01" +git push upstream v26.08.01 ``` ## Developer Certificate of Origin diff --git a/README.md b/README.md index 71b32876..b5b72b72 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # RAPIDS Deployment Documentation This repository contains the source for the -[RAPIDS Deployment Documentation](https://docs.rapids.ai/deployment/stable/). +[RAPIDS Deployment Documentation](https://docs.nvidia.com/datascience/deployment/latest/). It explains how to install, configure, and operate RAPIDS across local systems, GPU clusters, and managed compute services. @@ -60,9 +60,11 @@ uv run sphinx-autobuild -b dirhtml source build/html ## Published Documentation -- [Stable documentation](https://docs.rapids.ai/deployment/stable/) is published - from release tags. -- [Nightly documentation](https://docs.rapids.ai/deployment/nightly/) is - published from the `main` branch. +- [Latest documentation](https://docs.nvidia.com/datascience/deployment/latest/) + is published from the `main` branch. +- Released documentation is published from `vYY.MM.PP` release tags to + `https://docs.nvidia.com/datascience/deployment/YY.MM/`, for example + [26.08](https://docs.nvidia.com/datascience/deployment/26.08/). Use the + version switcher in the navigation bar to move between releases. See [CONTRIBUTING.md](CONTRIBUTING.md) for instructions on building, writing, linting, and releasing. diff --git a/extensions/rapids_docs_publishing.py b/extensions/rapids_docs_publishing.py new file mode 100644 index 00000000..0478a267 --- /dev/null +++ b/extensions/rapids_docs_publishing.py @@ -0,0 +1,124 @@ +""" +Publish this build to docs.nvidia.com: configure the navbar version switcher +and write the files CI needs, only when ``rapids_docs_publishing`` is enabled +(conf.py turns it on for CI builds). + +The switcher is populated by the browser from ``/versions.json`` +and highlights the entry whose ``version`` matches this build (the nightly +version on main, shown as "latest"; the stable version on a release). + +After the HTML build, everything below lands in ``build/publish``: + + publish.env TARGET=YY.MM / latest, the directory this build publishes + to, read by the workflow into step outputs + versions.json data for the navbar version switcher: "latest" first, then + every released version (from ``rapids_docs_release_tags``, + the repository's git tags) at or above + ``rapids_docs_first_version``, newest first and marked preferred + +``versions.json`` is skipped for a patch release of an older version, so an +old checkout can never overwrite the "latest" label with a stale value. +""" + +import json +import re +import shutil +from pathlib import Path +from typing import TYPE_CHECKING + +from sphinx.util import logging + +if TYPE_CHECKING: + import sphinx + +logger = logging.getLogger(__name__) + +RELEASE_TAG = re.compile(r"^v(\d\d\.\d\d)\.\d\d$") + + +def _as_tuple(version: str) -> tuple[int, ...]: + """``"26.08"`` -> ``(26, 8)``, so versions compare numerically rather than as text.""" + return tuple(int(part) for part in version.split(".")) + + +def released_versions(tags: list[str], first_version: str) -> list[str]: + """``YY.MM`` of every release tag at or above ``first_version``, newest first.""" + versions = {match.group(1) for match in map(RELEASE_TAG.match, tags) if match} + versions = {v for v in versions if _as_tuple(v) >= _as_tuple(first_version)} + return sorted(versions, key=_as_tuple, reverse=True) + + +def versions_json( + docs_url: str, latest_version: str, released: list[str] +) -> list[dict]: + """Switcher entries: ``latest`` first, then ``released``, its newest marked preferred.""" + entries = [{"name": v, "url": f"{docs_url}/{v}/", "version": v} for v in released] + if entries: + entries[0]["preferred"] = "true" + return [ + {"name": "latest", "url": f"{docs_url}/latest/", "version": latest_version}, + *entries, + ] + + +def configure_switcher(_app: "sphinx.application.Sphinx", config) -> None: + """Point the theme's version switcher at versions.json for publishing builds.""" + # config-inited handlers receive (app, config); only the config is needed here + if not config.rapids_docs_publishing: + return + config.html_theme_options["switcher"] = { + "json_url": f"{config.rapids_docs_url.rstrip('/')}/versions.json", + "version_match": config.rapids_version["rapids_version"], + } + # CI builds with -W; do not let a failed fetch of versions.json fail the build. + config.html_theme_options["check_switcher"] = False + + +def write_publish_files(app: "sphinx.application.Sphinx", exception) -> None: + """After a publishing build, write build/publish/{publish.env,versions.json} for CI.""" + if exception is not None or app.builder.format != "html": + return + if not app.config.rapids_docs_publishing: + return + + docs_url = app.config.rapids_docs_url.rstrip("/") + version = app.config.rapids_version["rapids_version"] + stable = app.config.rapids_version["rapids_api_docs_version"] == "stable" + target = version if stable else "latest" + + publish_dir = Path(app.outdir).parent / "publish" + shutil.rmtree(publish_dir, ignore_errors=True) + publish_dir.mkdir(parents=True) + + released = released_versions( + app.config.rapids_docs_release_tags, app.config.rapids_docs_first_version + ) + if stable and version not in released: + # the tag that triggered a release build must be visible, or the switcher + # would silently omit the version being published + logger.warning( + "no release tag v%s.* found at or above rapids_docs_first_version=%s", + version, + app.config.rapids_docs_first_version, + ) + # A patch release of an older version leaves the switcher data alone. + write_versions = not (stable and released and version != released[0]) + if write_versions: + data = versions_json(docs_url, app.config.rapids_docs_latest_version, released) + (publish_dir / "versions.json").write_text(json.dumps(data, indent=2) + "\n") + + (publish_dir / "publish.env").write_text( + f"TARGET={target}\nVERSIONS_JSON={str(write_versions).lower()}\n" + ) + + +def setup(app: "sphinx.application.Sphinx") -> dict: + """Register the ``rapids_docs_*`` config values and hook into the build.""" + app.add_config_value("rapids_docs_publishing", False, "html") + app.add_config_value("rapids_docs_url", "", "html") + app.add_config_value("rapids_docs_first_version", "", "html") + app.add_config_value("rapids_docs_latest_version", "", "html") + app.add_config_value("rapids_docs_release_tags", [], "html") + app.connect("config-inited", configure_switcher) + app.connect("build-finished", write_publish_files) + return {"version": "0.1", "parallel_read_safe": True, "parallel_write_safe": True} diff --git a/scripts/gen_release_checklist_issue.py b/scripts/gen_release_checklist_issue.py index 2f3622da..7dcfc663 100755 --- a/scripts/gen_release_checklist_issue.py +++ b/scripts/gen_release_checklist_issue.py @@ -18,7 +18,7 @@ ## Verify pages -- Look at the nightly build of each page listed below + - Look at the latest build of each page listed below - Check page renders correctly - Check for spelling/grammar problems - Check that the instructions work as expected @@ -61,7 +61,7 @@ file_info = { "file": file, - "url": "https://docs.rapids.ai/deployment/nightly/" + rel_path, + "url": "https://docs.nvidia.com/datascience/deployment/latest/" + rel_path, "priority": priority, } if priority in priority_lists: diff --git a/source/_templates/version-switcher.html b/source/_templates/version-switcher.html deleted file mode 100644 index 382d546d..00000000 --- a/source/_templates/version-switcher.html +++ /dev/null @@ -1,49 +0,0 @@ -{# Static version switcher; active item set at build time via -``deployment_version_label`` in conf.py. #} {%- set button_id = -unique_html_id("pst-version-switcher-button") -%} {%- set dropdown_id = -unique_html_id("pst-version-switcher-list") -%} {%- set current = -deployment_version_label | default("dev") -%} - diff --git a/source/conf.py b/source/conf.py index 25ed5518..4daae4ed 100644 --- a/source/conf.py +++ b/source/conf.py @@ -98,6 +98,7 @@ "rapids_grid_toctree", "rapids_version_templating", "rapids_admonitions", + "rapids_docs_publishing", "sphinx_reredirects", "sphinx_llm.txt", ] @@ -154,24 +155,15 @@ ], } -# The navbar version switcher is a static template override -# (``_templates/version-switcher.html``) with hardcoded links straight to the -# published nightly/stable docs. -# Three-state detection via DEPLOYMENT_DOCS_BUILD_STABLE: -# "true" → stable (CI tag build) -# "false" → nightly (CI non-tag build; CI always sets the var explicitly) -# unset → dev (local or ReadTheDocs preview build) -_stable_env = os.environ.get("DEPLOYMENT_DOCS_BUILD_STABLE") -if _stable_env == "true": - deployment_version_label = "stable" -elif _stable_env == "false": - deployment_version_label = "nightly" -else: - deployment_version_label = "dev" - -html_context = { - "deployment_version_label": deployment_version_label, -} +# Publishing to docs.nvidia.com and the navbar version switcher, handled by +# extensions/rapids_docs_publishing.py. Only CI builds publish or get the +# switcher; the workflow turns both on by setting RAPIDS_DOCS_PUBLISH=true. +# The switcher lists releases from rapids_docs_first_version upward. +rapids_docs_publishing = os.environ.get("RAPIDS_DOCS_PUBLISH") == "true" +rapids_docs_url = "https://docs.nvidia.com/datascience/deployment" +rapids_docs_first_version = "26.08" +rapids_docs_latest_version = nightly_version +rapids_docs_release_tags = os.environ.get("RAPIDS_DOCS_RELEASE_TAGS", "").split() html_sidebars = { "examples/index": ["sidebar-nav-bs", "notebooks-tag-filter"],