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
91 changes: 91 additions & 0 deletions .github/workflows/docs-version-updater.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
name: docs-version-updater

# Reusable workflow behind the weekly software-version check in the lab's docs repositories.
# Each caller passes the globs of its pages and the Anthropic API key; Claude checks the pages
# against current sources, and any changes that pass markdownlint and the lab brand check go
# into one pull request. See tools/docs-version-updater/README.md.

on:
workflow_call:
inputs:
paths:
description: Space-separated globs of the pages to check, relative to the repository root
type: string
required: true
max-files:
description: Check at most this many pages (0 checks them all)
type: number
default: 0
secrets:
ANTHROPIC_API_KEY:
required: true

permissions:
contents: write
pull-requests: write

concurrency:
group: docs-version-updater-${{ github.repository }}
cancel-in-progress: false

jobs:
update:
name: Check software versions
runs-on: ubuntu-latest
timeout-minutes: 240
steps:
- uses: actions/checkout@v7
with:
path: repo
- uses: actions/checkout@v7
with:
repository: ATR-Lab/.github
path: tools
sparse-checkout: tools/docs-version-updater
- uses: actions/checkout@v7
with:
repository: ATR-Lab/atr-lab-design-skill
path: skill
sparse-checkout: atr-lab-design/scripts
- uses: actions/setup-python@v7
with:
python-version: "3.12"

- name: Install
run: pip install -r tools/tools/docs-version-updater/requirements.txt lxml Pillow numpy

- name: Check the pages with Claude
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
PATHS: ${{ inputs.paths }}
MAX_FILES: ${{ inputs.max-files }}
run: >-
python tools/tools/docs-version-updater/updater.py --root repo --paths "$PATHS"
--max-files "$MAX_FILES" --report "$RUNNER_TEMP/report.md"

# A page that fails either check after the edits goes back to its committed version.
- name: Lint the changed pages
working-directory: repo
run: |
reverted=""
for page in $(git diff --name-only -- '*.md'); do
if ! npx --yes markdownlint-cli2 "$page" \
|| ! python ../skill/atr-lab-design/scripts/brand_check.py "$page"; then
git checkout -- "$page"
reverted="$reverted- \`$page\`\n"
fi
done
if [ -n "$reverted" ]; then
printf '\n### Reverted\n\nThese pages failed markdownlint or the lab brand check after the edits, so their changes were dropped:\n\n%b' "$reverted" >> "$RUNNER_TEMP/report.md"
fi
cat "$RUNNER_TEMP/report.md" >> "$GITHUB_STEP_SUMMARY"

- name: Open or update the pull request
uses: peter-evans/create-pull-request@v8
with:
path: repo
branch: automation/version-updates
delete-branch: true
commit-message: Update software facts from the weekly version check
title: Weekly software version check
body-path: ${{ runner.temp }}/report.md
2 changes: 2 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ Check visibility before linking any other repository:
| `.github/ISSUE_TEMPLATE/` | Default issue forms (`bug_report.yml`, `feature_request.yml`, `docs_fix.yml`) and `config.yml` |
| `.github/PULL_REQUEST_TEMPLATE.md` | Default pull request template |
| `.github/workflows/docs.yml` | Markdown lint and link check for this repository only |
| `.github/workflows/docs-version-updater.yml` | Reusable workflow for the weekly software-version check that the docs repositories call |
| `tools/docs-version-updater/` | The checker (`updater.py`, Claude with web search), its `requirements.txt`, `ruff.toml` and `README.md` |
| `.markdownlint.jsonc` | Markdown lint rules shared by all lab docs repositories |
| `.lycheeignore` | Sites that block automated link checkers (check those links by hand) |
| `AGENTS.md`, `CLAUDE.md` | This file; `CLAUDE.md` imports it |
Expand Down
40 changes: 40 additions & 0 deletions tools/docs-version-updater/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# Docs version updater

Once a week this checks the software facts in the lab's docs repositories (versions, release and end-of-life dates, supported platforms, install and CLI commands) against current primary sources and opens a pull request with any corrections. Claude (`claude-opus-5-5`, effort `medium`) does the checking with web search and web fetch. It edits only the page it was given, cites a source for every change, and leaves lab facts and `[placeholders]` alone.

## How it runs

| Piece | Where |
| --- | --- |
| The checker | `tools/docs-version-updater/updater.py` in this repository |
| Reusable workflow | `.github/workflows/docs-version-updater.yml` in this repository |
| Weekly callers (Mondays, 12:00 UTC) | `.github/workflows/version-check.yml` in getting-started-atr-lab, all-things-ros2 and dev-guidelines |
| API key | Repository secret `ANTHROPIC_API_KEY_ATR_LAB_DOC_UPDATER` in each calling repository |

For each page the workflow:

1. Starts a conversation with Claude that holds the repository's `AGENTS.md` (its writing and sourcing rules) and the page.
2. Lets Claude search the web, fetch release notes and docs, read other pages in the repository, and change the page through an `edit_page` tool. An edit is refused unless its old text appears exactly once, it keeps every `[placeholder]` and it gives an `https://` source.
3. Sets the page's "Last reviewed" footer to the run date if anything changed.

Then it runs markdownlint and the lab brand check on each changed page and drops the changes to any page that fails. Whatever is left goes into one pull request on the branch `automation/version-updates`. The PR body lists every change with its sources and the run's estimated API cost. A later run updates the same pull request until someone merges or closes it.

Pull requests opened by GitHub Actions don't start the repository's own CI. Push a commit to the branch, or close and reopen the pull request, to run it before you merge.

## Run it by hand

From a calling repository's **Actions** tab, run **version-check** and, optionally, give it a narrower glob (for example `tech-stack/gazebo.md`) or a page limit. Locally, from the root of a docs repository:

```bash
python3.12 -m venv .venv && .venv/bin/pip install -r path/to/tools/docs-version-updater/requirements.txt
ANTHROPIC_API_KEY=... .venv/bin/python path/to/tools/docs-version-updater/updater.py --paths "tech-stack/gazebo.md"
```

It edits the pages in place and writes `version-update-report.md`; review with `git diff`.

## Change it

- **Which pages:** the `paths` line in each caller's `version-check.yml`.
- **Model, effort, search limits:** the constants at the top of `updater.py`. The prices there only feed the report's cost estimate.
- **What Claude is told:** `INSTRUCTIONS` in `updater.py`. Each repository's `AGENTS.md` is sent with it, so writing rules belong there.
- **Style:** the lab's Python guide; `ruff.toml` here is a copy of `dev-guidelines/configs/python/ruff.toml`. Run `ruff format` and `ruff check` before you commit.
1 change: 1 addition & 0 deletions tools/docs-version-updater/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
anthropic==2.6.0
57 changes: 57 additions & 0 deletions tools/docs-version-updater/ruff.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# Ruff settings for Python in ATR Lab repos (lint and format).
#
# Tested with ruff 0.16.9 (Sept. 30, 2026). Docs: https://docs.astral.sh/ruff/
#
# The line length and quote style match the ROS 2 Python checks (ament_flake8),
# so code that passes Ruff also passes `colcon test` in a ROS 2 package. Docstrings
# follow the Google style; in ROS 2 packages, run ament_pep257 with
# `--convention google` (see docs/python.md), because its default "ament"
# convention rejects Google-style "Args:" sections.

# Oldest Python we support: 3.10 (Ubuntu 22.04 and ROS 2 Humble). Raise it when
# a repo only targets newer systems (Jazzy on Ubuntu 24.04 ships 3.12).
target-version = "py310"
line-length = 99

[format]
# ROS 2 style: "We pick single quotes over double quotes as long as no escaping
# is necessary." Docstrings keep triple double quotes (PEP 257).
quote-style = "single"
docstring-code-format = true

[lint]
# Ruff 0.16 changed its default rule set, so we list every family we want.
select = [
"E", # pycodestyle errors (what flake8 checks)
"W", # pycodestyle warnings
"F", # Pyflakes: unused imports, undefined names
"I", # isort: import order
"N", # pep8-naming
"D", # pydocstyle: docstrings (Google convention below)
"UP", # pyupgrade: modern syntax for target-version
"B", # flake8-bugbear: likely bugs
"A", # flake8-builtins (also run by ament_flake8)
"C4", # flake8-comprehensions (also run by ament_flake8)
"SIM", # flake8-simplify
"NPY", # NumPy-specific rules
"RUF", # Ruff's own rules
]
ignore = [
"D100", # module docstring: optional, as in ament_pep257
"D104", # package docstring: __init__.py files are often empty
"D105", # magic-method docstring
"D107", # __init__ docstring: document the class instead
]

[lint.pydocstyle]
convention = "google"

[lint.isort]
# Sort `import x` and `from x import y` together by module name, like the
# "google" import-order style that ament_flake8 checks.
force-sort-within-sections = true

[lint.per-file-ignores]
"test/**" = ["D"]
"tests/**" = ["D"]
"setup.py" = ["D"]
Loading
Loading