Skip to content

docs: add LeRobot and SO-101 getting-started guide #1161

docs: add LeRobot and SO-101 getting-started guide

docs: add LeRobot and SO-101 getting-started guide #1161

Workflow file for this run

# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0
#
# Build docs and Teleop web app, then deploy to GitHub Pages.
# - Push to main/release/v* tag (canonical repo): deploy docs at site root, web app at /client/<slug>/.
# Tag pushes also promote /client/stable/ to the highest v* tag.
# - Same-repo PR: auto-deploy preview to preview/pr-<N>/.
# - Fork PR: build only; a maintainer can deploy via `/preview-docs` (see
# docs-preview-on-demand.yaml).
#
# Builds are gated on changed paths by the `changes` job; gh-pages
# `keep_files: true` preserves whichever artifact was skipped.
#
# Web client uses the private CloudXR SDK for pushes and for PRs whose
# author is in ALLOWED_CLOUDXR_JS_PR_AUTHORS (Repo Settings → Variables);
# all other PRs fall back to the public SDK.
name: Build & deploy docs
on:
push:
branches:
- main
- 'release/**'
tags:
- 'v[1-9]*.[0-9]*.[0-9]*'
pull_request:
types: [opened, synchronize, reopened]
paths:
- "docs/**"
- ".github/workflows/docs.yaml"
- ".github/actions/build-cloudxr-web-client/**"
- "deps/cloudxr/webxr_client/**"
- "deps/cloudxr/.env.default"
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
check-repo:
name: Check repo
runs-on: ubuntu-latest
permissions: {}
outputs:
is-canonical-repo: ${{ steps.is-canonical-repo.outputs.defined }}
is-deploy-branch: ${{ steps.is-deploy-branch.outputs.defined }}
use_ngc_key: ${{ steps.ngc-allowlist.outputs.use_ngc_key }}
steps:
- id: is-canonical-repo
if: github.repository == 'NVIDIA/IsaacTeleop'
run: echo "defined=true" >> "$GITHUB_OUTPUT"
- id: is-deploy-branch
if: >-
github.ref == 'refs/heads/main'
|| startsWith(github.ref, 'refs/heads/release/')
|| startsWith(github.ref, 'refs/tags/v')
run: echo "defined=true" >> "$GITHUB_OUTPUT"
- id: ngc-allowlist
run: |
if [ "${{ github.event_name }}" != 'pull_request' ]; then
echo "use_ngc_key=true" >> "$GITHUB_OUTPUT"
elif [ "${{ github.event.pull_request.head.repo.full_name }}" != "${{ github.repository }}" ]; then
echo "use_ngc_key=false" >> "$GITHUB_OUTPUT"
else
ACTOR="${{ github.actor }}"
ALLOWED="${{ vars.ALLOWED_CLOUDXR_JS_PR_AUTHORS }}"
# Normalize: remove spaces so "alice, bob" matches like "alice,bob"
ALLOWED="${ALLOWED// /}"
case ",${ALLOWED}," in
*",${ACTOR},"*) echo "use_ngc_key=true" >> "$GITHUB_OUTPUT" ;;
*) echo "use_ngc_key=false" >> "$GITHUB_OUTPUT" ;;
esac
fi
# gh-pages keep_files preserves whichever artifact isn't rebuilt this run.
changes:
name: Detect changed paths
runs-on: ubuntu-latest
permissions: {}
outputs:
docs: ${{ steps.filter.outputs.docs }}
webapp: ${{ steps.filter.outputs.webapp }}
steps:
- name: Checkout (with history)
uses: actions/checkout@v6
with:
fetch-depth: 0
- id: filter
env:
EVENT: ${{ github.event_name }}
BEFORE: ${{ github.event.before }}
BASE_REF: ${{ github.base_ref }}
run: |
set -euo pipefail
if [ "$EVENT" = 'pull_request' ]; then
git fetch --no-tags --depth=1 origin "$BASE_REF"
base="$(git merge-base FETCH_HEAD HEAD || true)"
elif [ -n "$BEFORE" ] && [ "$BEFORE" != '0000000000000000000000000000000000000000' ]; then
base="$BEFORE"
else
base="" # new branch / force push: treat everything as changed
fi
files="$([ -n "$base" ] && git diff --name-only "$base" HEAD || git ls-tree -r --name-only HEAD)"
docs=false; webapp=false
while IFS= read -r f; do
case "$f" in
docs/*|.github/workflows/docs.yaml) docs=true ;;
esac
case "$f" in
deps/cloudxr/webxr_client/*|deps/cloudxr/.env.default|.github/actions/build-cloudxr-web-client/*|.github/workflows/docs.yaml)
webapp=true ;;
esac
done <<< "$files"
echo "docs=$docs" >> "$GITHUB_OUTPUT"
echo "webapp=$webapp" >> "$GITHUB_OUTPUT"
echo "::notice::changes: docs=$docs webapp=$webapp (base=${base:-<none>})"
build-docs:
name: Build Docs
runs-on: ubuntu-latest
needs: [check-repo, changes]
if: needs.changes.outputs.docs == 'true'
steps:
- name: Checkout code
uses: actions/checkout@v6
- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: "3.11"
- name: Install docs requirements
working-directory: ./docs
run: pip install -r requirements.txt
- name: Build docs for the current branch
working-directory: ./docs
run: make current-docs
- name: Generate multi-version docs
working-directory: ./docs
if: needs.check-repo.outputs.is-canonical-repo == 'true' && needs.check-repo.outputs.is-deploy-branch == 'true'
run: |
git fetch --prune --unshallow --tags
make multi-docs
- name: Upload docs artifact
uses: actions/upload-artifact@v6
with:
name: docs-html
path: ./docs/build
build-app:
name: Build Teleop Web App
runs-on: ubuntu-latest
needs: [check-repo, changes]
if: needs.changes.outputs.webapp == 'true'
steps:
- name: Checkout code
uses: actions/checkout@v6
# Allowlist no → public only. Allowlist yes + NGC_API_KEY → private only.
- name: Build CloudXR web client
uses: ./.github/actions/build-cloudxr-web-client
with:
ngc-api-key: ${{ needs.check-repo.outputs.use_ngc_key == 'true' && secrets.NGC_TELEOP_CORE_GITHUB_SERVICE_KEY || '' }}
- name: Upload web app artifact
uses: actions/upload-artifact@v6
with:
name: webapp
path: deps/cloudxr/webxr_client/build
deploy-docs:
name: Deploy docs
runs-on: ubuntu-latest
needs: [check-repo, changes, build-docs, build-app]
if: >-
!cancelled()
&& needs.check-repo.outputs.is-canonical-repo == 'true'
&& (needs.check-repo.outputs.is-deploy-branch == 'true' || github.event_name == 'pull_request')
&& needs.build-docs.result != 'failure'
&& needs.build-app.result != 'failure'
&& (needs.build-docs.result == 'success' || needs.build-app.result == 'success')
permissions:
contents: write # push to gh-pages
steps:
- name: Checkout (for tag lookup)
if: github.event_name == 'push' && github.ref_type == 'tag'
uses: actions/checkout@v6
with:
fetch-depth: 1
- name: Download docs artifact
if: needs.build-docs.result == 'success'
uses: actions/download-artifact@v7
with:
name: docs-html
path: ./docs/build
- name: Download web app artifact
if: needs.build-app.result == 'success'
uses: actions/download-artifact@v7
with:
name: webapp
path: ./webapp
# Web client lands at client/<slug>/: tag → v1.2.3, main → main, release branch → release-1.3.x.
# Tag pushes also (re)point client/stable/ at the highest v* tag.
# The root client/ redirect is owned by the main branch deploy and points at ./stable/.
- name: Place web app under versioned subpath
if: needs.build-app.result == 'success'
run: |
set -euo pipefail
CLIENT_BASE=./docs/build/client
if [ "${{ github.event_name }}" = 'pull_request' ]; then
mkdir -p ./docs/build/current/client
cp -r ./webapp/. ./docs/build/current/client/
exit 0
fi
if [ "$GITHUB_REF_TYPE" = 'tag' ]; then
target="$GITHUB_REF_NAME"
else
target="${GITHUB_REF_NAME//\//-}"
fi
mkdir -p "$CLIENT_BASE/$target"
cp -r ./webapp/. "$CLIENT_BASE/$target/"
echo "Deployed web client to /client/$target/"
if [ "$GITHUB_REF" = 'refs/heads/main' ]; then
cat > "$CLIENT_BASE/index.html" <<'HTML'
<!-- SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -->
<!-- SPDX-License-Identifier: Apache-2.0 -->
<!DOCTYPE html>
<html>
<head>
<title>Redirecting to the stable Isaac Teleop web client</title>
<meta charset="utf-8">
<meta http-equiv="refresh" content="0; url=./stable/">
<link rel="canonical" href="./stable/">
</head>
<body>
<p>Redirecting to <a href="./stable/">the stable Isaac Teleop web client</a>.</p>
<script>location.replace('./stable/' + location.search + location.hash);</script>
</body>
</html>
HTML
fi
if [ "$GITHUB_REF_TYPE" = 'tag' ]; then
stable_slug="$(git ls-remote --tags origin 'v[1-9]*.[0-9]*.[0-9]*' \
| awk -F'refs/tags/' '{print $2}' \
| sed 's/\^{}$//' | sort -uV | tail -n1)"
if [ -z "$stable_slug" ]; then
echo "::warning::No v* tags resolved; leaving client/stable/ untouched."
exit 0
fi
mkdir -p "$CLIENT_BASE/stable"
cat > "$CLIENT_BASE/stable/index.html" <<HTML
<!-- SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -->
<!-- SPDX-License-Identifier: Apache-2.0 -->
<!DOCTYPE html>
<html>
<head>
<title>Redirecting to the stable Isaac Teleop web client (${stable_slug})</title>
<meta charset="utf-8">
<meta http-equiv="refresh" content="0; url=../${stable_slug}/">
<link rel="canonical" href="../${stable_slug}/">
</head>
<body>
<p>Redirecting to <a href="../${stable_slug}/">the stable Isaac Teleop web client</a>.</p>
<script>location.replace('../${stable_slug}/' + location.search + location.hash);</script>
</body>
</html>
HTML
echo "Stable now points to /client/${stable_slug}/"
fi
- name: Deploy to gh-pages
if: github.event_name == 'push' && needs.check-repo.outputs.is-deploy-branch == 'true'
uses: peaceiris/actions-gh-pages@v4
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./docs/build
keep_files: true
# Fork PRs have a read-only GITHUB_TOKEN and can't push to gh-pages, so
# the auto-deploy is gated to same-repo PRs. Fork PR previews are
# handled by docs-preview-on-demand.yaml (slash-command triggered).
- name: Deploy PR preview to gh-pages
if: >-
github.event_name == 'pull_request'
&& github.event.pull_request.head.repo.full_name == github.repository
uses: peaceiris/actions-gh-pages@v4
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./docs/build/current
destination_dir: preview/pr-${{ github.event.pull_request.number }}
keep_files: true
- name: Add preview URL to workflow summary
if: >-
github.event_name == 'pull_request'
&& github.event.pull_request.head.repo.full_name == github.repository
run: |
base="https://${{ github.repository_owner }}.github.io/${{ github.event.repository.name }}/preview/pr-${{ github.event.pull_request.number }}/"
client_url="${base}client/"
{
echo "## Docs preview"
echo
echo "Built docs for this PR are available at:"
echo
echo "**${base}**"
echo
echo "Teleop web app (subpath \`/client/\`): **${client_url}**"
echo
echo "(It may take a minute to appear after the workflow completes.)"
} >> "$GITHUB_STEP_SUMMARY"
- name: Note manual-trigger requirement for fork PRs
if: >-
github.event_name == 'pull_request'
&& github.event.pull_request.head.repo.full_name != github.repository
run: |
{
echo "## Docs preview"
echo
echo "This PR is from a fork, so the preview is **not auto-deployed**."
echo "A maintainer with write access can comment \`/preview-docs\` on"
echo "the PR to deploy a preview to:"
echo
echo "**https://nvidia.github.io/IsaacTeleop/preview/pr-${{ github.event.pull_request.number }}/**"
} >> "$GITHUB_STEP_SUMMARY"