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
48 changes: 48 additions & 0 deletions .github/workflows/changelog-check.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# Copyright (c) 2022-2026, The Isaac Lab Project Developers (https://github.com/isaac-sim/IsaacLab/blob/main/CONTRIBUTORS.md).
# All rights reserved.
#
# SPDX-License-Identifier: BSD-3-Clause

name: Changelog Fragment Check

on:
pull_request:
types: [opened, synchronize, reopened]
workflow_dispatch:
inputs:
base_ref:
description: 'Base branch to diff against'
required: true
default: 'develop'

concurrency:
group: changelog-check-${{ github.ref }}
cancel-in-progress: true

permissions:
contents: read

jobs:
check-fragments:
name: Check changelog fragments
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
# Full history needed to diff against the base branch
fetch-depth: 0

- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5
with:
python-version: "3.12"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 github.base_ref is empty for workflow_dispatch triggers

The Fetch base branch step uses ${{ github.base_ref }}, which is only populated for pull_request events. When this workflow is triggered via workflow_dispatch, github.base_ref is an empty string, so the step effectively runs git fetch origin with no branch argument — either fetching all branches unexpectedly or silently failing. The Verify changelog fragments step immediately after correctly uses ${{ github.event.inputs.base_ref }}, but the fetch using the wrong context means origin/<base_ref> won't exist when the checker runs git diff --name-only origin/<base_ref>...HEAD.

Suggested change
- name: Fetch base branch
run: git fetch origin ${{ github.event.inputs.base_ref }}

- name: Resolve base ref
id: base
run: echo "ref=${{ github.event.inputs.base_ref || github.base_ref }}" >> "$GITHUB_OUTPUT"

- name: Fetch base branch
run: git fetch origin ${{ steps.base.outputs.ref }}

- name: Verify changelog fragments
run: python3 tools/changelog/cli.py check ${{ steps.base.outputs.ref }}
119 changes: 119 additions & 0 deletions .github/workflows/nightly-changelog.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
# Copyright (c) 2022-2026, The Isaac Lab Project Developers (https://github.com/isaac-sim/IsaacLab/blob/main/CONTRIBUTORS.md).
# All rights reserved.
#
# SPDX-License-Identifier: BSD-3-Clause

# Nightly auto-compile: rolls accumulated fragments under
# ``source/<pkg>/changelog.d/`` into per-package ``CHANGELOG.rst`` entries,
# bumps each ``extension.toml``, deletes consumed fragments, and pushes the
# result back to ``develop``. Keeps the develop branch's changelog current
# without requiring a maintainer to run ``compile`` by hand.
#
# The push uses ``CHANGELOG_PAT`` (a personal access token / fine-grained
# GitHub App token with ``contents:write`` on this repo) when it's
# available so downstream CI runs on the auto-commit. Falls back to
# ``GITHUB_TOKEN`` — sufficient for the push itself, but pushes signed
# with ``GITHUB_TOKEN`` do not trigger workflow runs on the resulting
# commit, which is by design (avoids infinite loops) but means the
# Docker / docs rebuild won't re-trigger off the nightly's auto-commit.

name: Nightly Changelog Compilation

on:
schedule:
# Run nightly at 5 AM UTC (one hour after daily-compatibility, so we
# don't compete for runner capacity).
- cron: '0 5 * * *'
workflow_dispatch:
inputs:
dry_run:
description: 'Preview only — do not commit / push'
required: false
type: boolean
default: false

concurrency:
# Only one nightly compile may be in flight at a time. ``cancel-in-progress``
# is intentionally false: if a previous run is still finishing its push, we
# queue rather than abort it mid-commit.
group: nightly-changelog
cancel-in-progress: false

permissions:
contents: write

jobs:
compile-changelog:
name: Compile changelog fragments
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
# Operate on develop, not the repo's default branch. Scheduled
# workflows fire from the default branch's workflow file by
# default, but we want the *checkout* to be develop so the
# compile sees develop's accumulated fragments and the push
# writes back to develop.
ref: develop
# Use a PAT so the auto-commit triggers downstream CI; falls back
# to GITHUB_TOKEN which is sufficient for the push itself.
token: ${{ secrets.CHANGELOG_PAT || secrets.GITHUB_TOKEN }}
# Full history so the compiler can resolve each fragment's merge
# time via ``git log --diff-filter=A --first-parent``.
fetch-depth: 0

- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5
Comment on lines +60 to +66
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Nightly runs on the default branch, not develop

The actions/checkout step has no ref: field, so GitHub's schedule trigger checks out the repository's default branch (typically main). The PR description explicitly says the nightly "pushes the result to develop", but the workflow silently operates on whatever the default branch is. If the default branch is not develop, the nightly will compile main's (empty) fragment dirs, find nothing, and the real accumulated fragments on develop are never compiled. The git push without a refspec would also push to origin/<default_branch>, not origin/develop.

Add an explicit ref to the checkout and a target branch to the push:

Suggested change
# to GITHUB_TOKEN which is sufficient for the push itself.
token: ${{ secrets.CHANGELOG_PAT || secrets.GITHUB_TOKEN }}
# Full history so the compiler can resolve each fragment's merge
# time via ``git log --diff-filter=A --first-parent``.
fetch-depth: 0
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
ref: develop
token: ${{ secrets.CHANGELOG_PAT || secrets.GITHUB_TOKEN }}
fetch-depth: 0

And in the "Commit and push" step:

git push origin HEAD:develop

with:
python-version: "3.12"

- name: Compile fragments
run: |
ARGS="--all"
if [ "${{ inputs.dry_run }}" = "true" ]; then
ARGS="$ARGS --dry-run"
fi
echo "Running: python3 tools/changelog/cli.py compile $ARGS"
python3 tools/changelog/cli.py compile $ARGS

- name: Commit and push if fragments were compiled
if: inputs.dry_run != 'true'
run: |
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git add source/*/changelog.d/ \
source/*/docs/CHANGELOG.rst \
source/*/config/extension.toml
if git diff --staged --quiet; then
echo "No changelog fragments found — nothing to commit."
else
# Convention for CI-driven auto-commits on this repo:
# [CI][<Specific Action>] <one-line summary>
# The leading ``[CI]`` tag groups every machine-driven commit
# (so future automations — auto image rebuilds, auto publish,
# etc. — all share the prefix and are easy to find/filter in
# ``git log --grep``). The second tag names the specific
# action. The trigger event suffix (``schedule`` vs
# ``workflow_dispatch``) is preserved for traceability.
#
# The body lists every package that bumped, derived from the
# staged ``extension.toml`` diff so the entries are accurate
# regardless of which packages happen to have pending
# fragments this run.
MSG_FILE=$(mktemp)
{
echo "[CI][Auto Version Bump] Compile changelog fragments (${{ github.event_name }})"
echo
echo "Bumped packages:"
for tom in $(git diff --staged --name-only -- 'source/*/config/extension.toml'); do
pkg=$(echo "$tom" | sed -E 's|source/([^/]+)/config/extension.toml|\1|')
old=$(git diff --staged "$tom" | awk -F'"' '/^-version/{print $2; exit}')
new=$(git diff --staged "$tom" | awk -F'"' '/^\+version/{print $2; exit}')
echo "- $pkg: $old → $new"
done
} > "$MSG_FILE"
git commit -F "$MSG_FILE"
# Push explicitly to develop so we don't accidentally write
# to the source ref of a workflow_dispatch run.
git push origin HEAD:develop
fi
24 changes: 14 additions & 10 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,23 +89,27 @@ Proper workflow:

## Changelog

- **Update `CHANGELOG.rst` for every change** targeting the source directory. Each extension has its own changelog at `source/<package>/docs/CHANGELOG.rst` (e.g. `source/isaaclab/docs/CHANGELOG.rst`, `source/isaaclab_physx/docs/CHANGELOG.rst`).
- **Always create a new version heading.** Never add entries to an existing version — they are released and immutable. Bump the patch version (e.g. `1.5.0` → `1.5.1`) and use today's date.
- **Bump `config/extension.toml` to match.** When creating a new changelog version, update the `version` field in `source/<package>/config/extension.toml` to the same version string.
- **Determine which changelog(s) to update** by looking at which `source/<package>/` directories your changes touch. A single PR may require entries in multiple changelogs.
- **Do not edit `CHANGELOG.rst` or `config/extension.toml` directly.** Each PR adds a fragment file under `source/<package>/changelog.d/`; the changelog and version are compiled by the nightly CI workflow.
- **Add one fragment per touched package.** Pick any short, unique slug for the filename — your branch name (with `/` replaced by `-`) is a good default. The filename suffix declares the bump tier; within a batch the highest tier wins for the package.

| Filename | Effect |
|---|---|
| `source/<pkg>/changelog.d/<slug>.rst` | patch bump |
| `source/<pkg>/changelog.d/<slug>.minor.rst` | minor bump |
| `source/<pkg>/changelog.d/<slug>.major.rst` | major bump |
| `source/<pkg>/changelog.d/<slug>.skip` | no entry, no bump (CI / docs / test-only) |

- Use **past tense** matching the section header: "Added X", "Fixed Y", "Changed Z".
- Place entries under the correct category: `Added`, `Changed`, `Deprecated`, `Removed`, or `Fixed`.
- Avoid internal implementation details users wouldn't understand.
- **For `Deprecated`, `Changed`, and `Removed` entries, include migration guidance.**
- Example: "Deprecated `Articulation.A` in favor of `Articulation.B`."
- **Breaking changes** belong in `Changed`, prefixed with `**Breaking:**`.
- Use Sphinx cross-reference roles for class/method/module names.

### RST formatting reference

```
X.Y.Z (YYYY-MM-DD)
~~~~~~~~~~~~~~~~~~

Added
^^^^^

Expand All @@ -119,10 +123,10 @@ Fixed
```

Key formatting rules:
- Version heading: underline with `~` (tildes), must be at least as long as the heading text.
- Category heading: underline with `^` (carets).
- Category heading: underline with `^` (carets), at least as long as the heading text.
- Entries: `* ` prefix, continuation lines indented by 2 spaces.
- Blank line between the last entry and the next version heading.

See `tools/changelog/test/integration/` for worked examples that double as integration-test fixtures.

## Commit and Pull Request Guidelines

Expand Down
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Loading
Loading