diff --git a/.agents/skills/prepare-code-freeze/SKILL.md b/.agents/skills/prepare-code-freeze/SKILL.md new file mode 100644 index 000000000..0e2523ed2 --- /dev/null +++ b/.agents/skills/prepare-code-freeze/SKILL.md @@ -0,0 +1,63 @@ +--- +name: prepare-code-freeze +description: Prepare a NeMo Flow code freeze by creating the release branch, updating nightly alpha branch config, bumping main to the next version, and opening the required PR +author: NVIDIA Corporation and Affiliates +license: Apache-2.0 +--- + +# Prepare Code Freeze + +Use this skill when the user asks to start, prepare, or automate a NeMo Flow +code freeze. + +## Companion Guidance + +Use `update-project-version` for version bump semantics and `prepare-pr` before +opening the PR. + +## Workflow + +This workflow assumes `upstream` is the NVIDIA repository remote +(`NVIDIA/NeMo-Flow`). The `origin` remote can be a maintainer's personal fork. + +1. Confirm or infer the target release version from `upstream/main:Cargo.toml`. + Derive the release branch as `release/.`. +2. Prompt for `` if the user did not provide it. This is the + version that `main` moves to after the release branch is cut. +3. Fetch the latest `main` and create the release branch from `upstream/main`: + + ```bash + git fetch upstream main + git branch release/. upstream/main + git push upstream release/. + ``` + + If the remote release branch already exists, verify it points where expected + before continuing. +4. Create a PR branch from latest `upstream/main`, for example + `docs/code-freeze-.`. +5. Update `.github/nightly-alpha-branches.yaml` to include the new release + branch. +6. Run `just set-version ` to bump all release-versioned package + surfaces on `main`. +7. Validate with targeted checks: + + ```bash + ruby -e 'require "yaml"; YAML.load_file(".github/nightly-alpha-branches.yaml"); YAML.load_file(".github/workflows/nightly-alpha-tag.yaml")' + just set-version + git diff --check + ``` + +8. Open a PR targeting `main` using `.github/pull_request_template.md`. The PR + must mention: + - the new release branch + - the nightly alpha branch config update + - the `just set-version ` bump + - that release-bound PRs now target the new `release/*` branch + +## Guardrails + +- Do not create release tags. Code freeze only creates the branch and the main + PR. +- Do not target the code-freeze PR at the release branch. It targets `main`. +- Do not leave uncommitted user changes mixed into the code-freeze PR branch. diff --git a/.agents/skills/update-project-version/SKILL.md b/.agents/skills/update-project-version/SKILL.md index 50309eb8f..fd0de0833 100644 --- a/.agents/skills/update-project-version/SKILL.md +++ b/.agents/skills/update-project-version/SKILL.md @@ -25,9 +25,12 @@ pre-release or build-metadata variants used during packaging. workspace version changes. - `crates/node/package.json` carries its own npm package version and must stay aligned with the workspace-root `package-lock.json`. -- `package-lock.json` records the Node package version under - `packages["crates/node"].version`. The workspace-root lockfile may not have a - top-level `version` field. +- `integrations/openclaw/package.json` carries the OpenClaw npm plugin version + and must stay aligned with the workspace-root `package-lock.json`. +- `package-lock.json` records Node package versions under + `packages["crates/node"].version` and + `packages["integrations/openclaw"].version`. The workspace-root lockfile may + not have a top-level `version` field. - `crates/wasm/package.json` is a local dev manifest. Do not treat it as the publishable package manifest unless it gains an explicit `version` field. - The publishable WebAssembly npm package version is derived from @@ -44,11 +47,14 @@ pre-release or build-metadata variants used during packaging. - `workspace.dependencies.nemo-flow-adaptive.version` - `workspace.dependencies.nemo-flow-ffi.version` - `crates/node/package.json` `version` + - `integrations/openclaw/package.json` `version` - `package-lock.json` `packages["crates/node"].version` + - `package-lock.json` `packages["integrations/openclaw"].version` 3. If editing helper code, keep `set_project_version`, - `set_cargo_workspace_version`, and `set_node_package_version` aligned with - those same fields. `set_npm_package_version` remains the reusable npm JSON - helper for Node and WebAssembly packaging recipes. + `set_cargo_workspace_version`, and `set_node_package_versions` aligned with + those same fields. `set_node_package_version` remains a compatibility alias. + `set_npm_package_version` remains the reusable npm JSON helper for Node and + WebAssembly packaging recipes. 4. Refresh generated surfaces: - Run `cargo check --workspace` to refresh `Cargo.lock` if workspace package entries changed. @@ -69,7 +75,7 @@ pre-release or build-metadata variants used during packaging. ## Validation - `rg -n '^version =|nemo-flow = \\{ version =|nemo-flow-adaptive = \\{ version =' Cargo.toml` -- `rg -n '\"version\"' crates/node/package.json package-lock.json` +- `rg -n '\"version\"' crates/node/package.json integrations/openclaw/package.json package-lock.json` - `cargo check --workspace` - If Rust attribution files are expected to stay current: `./scripts/generate_attributions.sh rust` @@ -101,6 +107,7 @@ pre-release or build-metadata variants used during packaging. - `package.json` - `package-lock.json` - `crates/node/package.json` +- `integrations/openclaw/package.json` - `crates/wasm/Cargo.toml` - `crates/wasm/package.json` - `crates/wasm/scripts/prepare_pkg.mjs` diff --git a/.github/nightly-alpha-branches.yaml b/.github/nightly-alpha-branches.yaml new file mode 100644 index 000000000..04baa981f --- /dev/null +++ b/.github/nightly-alpha-branches.yaml @@ -0,0 +1,5 @@ +# SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +branches: + - main diff --git a/.github/workflows/nightly-alpha-tag.yaml b/.github/workflows/nightly-alpha-tag.yaml index 7f57dfa38..cf00d1bc1 100644 --- a/.github/workflows/nightly-alpha-tag.yaml +++ b/.github/workflows/nightly-alpha-tag.yaml @@ -14,10 +14,34 @@ concurrency: cancel-in-progress: false jobs: + nightly-alpha-config: + name: Load nightly alpha branches + runs-on: ubuntu-latest + permissions: + contents: read + outputs: + branches: ${{ steps.config.outputs.branches }} + steps: + - name: Checkout + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + with: + persist-credentials: false + + - name: Load nightly alpha branch config + id: config + run: | + set -euo pipefail + branches_json="$(ruby -ryaml -rjson -e 'puts JSON.generate(YAML.load_file(".github/nightly-alpha-branches.yaml").fetch("branches"))')" + printf 'branches=%s\n' "$branches_json" >> "$GITHUB_OUTPUT" + tag-nightly-alpha: name: Tag nightly alpha - if: ${{ github.ref == 'refs/heads/main' }} + needs: nightly-alpha-config runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + branch: ${{ fromJSON(needs.nightly-alpha-config.outputs.branches) }} permissions: contents: read steps: @@ -26,9 +50,11 @@ jobs: with: fetch-depth: 0 persist-credentials: false + ref: ${{ matrix.branch }} - name: Create nightly alpha tag env: + NEMO_FLOW_NIGHTLY_BRANCH: ${{ matrix.branch }} NEMO_FLOW_NIGHTLY_TAG_TOKEN: ${{ secrets.NEMO_FLOW_NIGHTLY_TAG_TOKEN }} run: | set -euo pipefail @@ -50,9 +76,18 @@ jobs: tag_date="$(date -u +%Y%m%d)" tag="${version}-alpha.${tag_date}" + target_sha="$(git rev-parse HEAD)" if git ls-remote --exit-code --tags origin "refs/tags/${tag}" >/dev/null 2>&1; then - echo "Nightly alpha tag already exists: ${tag}" - exit 0 + existing_sha="$(git ls-remote --tags origin "refs/tags/${tag}^{}" | awk '{print $1}' || true)" + if [[ -z "$existing_sha" ]]; then + existing_sha="$(git ls-remote --tags origin "refs/tags/${tag}" | awk '{print $1}' || true)" + fi + if [[ "$existing_sha" == "$target_sha" ]]; then + echo "Nightly alpha tag already exists for ${NEMO_FLOW_NIGHTLY_BRANCH}: ${tag}" + exit 0 + fi + echo "Error: nightly alpha tag ${tag} already exists at ${existing_sha}, not ${NEMO_FLOW_NIGHTLY_BRANCH} HEAD ${target_sha}" >&2 + exit 1 fi git config user.name "github-actions[bot]" @@ -61,7 +96,7 @@ jobs: --annotate \ --message "NeMo Flow ${version} Nightly ${tag_date}" \ "$tag" \ - "$GITHUB_SHA" + "$target_sha" git remote set-url origin "https://x-access-token:${NEMO_FLOW_NIGHTLY_TAG_TOKEN}@github.com/${GITHUB_REPOSITORY}.git" git push origin "refs/tags/${tag}" - echo "Created nightly alpha tag: ${tag}" + echo "Created nightly alpha tag for ${NEMO_FLOW_NIGHTLY_BRANCH}: ${tag}" diff --git a/RELEASING.md b/RELEASING.md index 90677732a..824e620ed 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -32,7 +32,7 @@ The release pipeline publishes these package surfaces from a tag push: |---|---| | crates.io | `nemo-flow`, `nemo-flow-adaptive`, `nemo-flow-ffi`, `nemo-flow-cli` | | PyPI | `nemo-flow` | -| npm | `nemo-flow-node`, `nemo-flow-wasm` | +| npm | `nemo-flow-node`, `nemo-flow-openclaw`, `nemo-flow-wasm` | | GitHub Pages | The documentation site, including the versioned docs build | Go remains source-first. There is no separate Go package-manager publication @@ -51,6 +51,8 @@ NeMo Flow versions are anchored on the workspace SemVer in the repository root - `crates/node/package.json` carries the base npm version for the Node.js package. The repository-root `package-lock.json` carries the npm workspace lock entries and must be updated with it. +- `integrations/openclaw/package.json` carries the base npm version for the + OpenClaw plugin package and must stay aligned with the same release version. - The Python package version is derived at packaging time. `pyproject.toml` stays `dynamic = ["version"]` in the repository, and the packaging recipe writes a concrete version into `pyproject.toml` and `crates/python/Cargo.toml` @@ -76,12 +78,50 @@ CI rejects tags that do not match the required format. The tag text must match the version that the packaging jobs publish. +Release tags for a frozen release line should be created from the matching +`release/*` branch, not from `main`. + +## Code Freeze + +When code freeze begins for a target release, create a release branch from the +latest `main` commit. Name the branch from the target release major and minor +version: + +These examples assume `upstream` is the NVIDIA repository remote +(`NVIDIA/NeMo-Flow`). The `origin` remote is usually a maintainer's personal +fork. + +```bash +git fetch upstream main +git checkout -b release/0.2 upstream/main +git push upstream release/0.2 +``` + +After creating the release branch, open a PR against `main` that does both of +the following: + +1. Add the new `release/*` branch to + [`.github/nightly-alpha-branches.yaml`](.github/nightly-alpha-branches.yaml) + so nightly alpha tags continue for the frozen release line. +2. Bump all package versions on `main` to the next release line: + + ```bash + just set-version + ``` + +New PRs that must go into the upcoming release must target the new `release/*` +branch. Changes intended for later releases should continue to target `main`. + +When a release branch no longer needs nightly alpha tags, open a PR against +`main` to remove that branch from +[`.github/nightly-alpha-branches.yaml`](.github/nightly-alpha-branches.yaml). + ## Before You Cut A Release Before you create a release tag, confirm the following: -1. The intended release commit is already on `main` or on the release branch - you intend to tag. +1. The intended release commit is already on the release branch you intend to + tag. For frozen release lines, tag the matching `release/*` branch. 2. The release commit contains the final version bump, docs updates, and any public API changes that belong in the release. 3. The working tree you use for local validation is clean or disposable. @@ -97,20 +137,31 @@ Before you create a release tag, confirm the following: ## Prepare The Release Commit -Update the versioned source files in the release PR or release-prep commit: - -1. Update the root [`Cargo.toml`](Cargo.toml) workspace version. -2. Update the root [`Cargo.toml`](Cargo.toml) `workspace.dependencies` versions - for `nemo-flow`, `nemo-flow-adaptive`, `nemo-flow-ffi`, and - `nemo-flow-cli`. -3. Update [`crates/node/package.json`](crates/node/package.json) and the - `crates/node` entry in the root [`package-lock.json`](package-lock.json) to - the same release version. -4. Review docs and snippets that mention explicit versions, including: - - [`README.md`](README.md) - - [`CONTRIBUTING.md`](CONTRIBUTING.md) - - [`docs/getting-started/installation.md`](docs/getting-started/installation.md) - - Any binding README or example that pins a release number +Update the versioned source files in the release PR or release-prep commit. +Prefer the repository helper: + +```bash +just set-version +``` + +The helper updates: + +1. The root [`Cargo.toml`](Cargo.toml) workspace version. +2. The root [`Cargo.toml`](Cargo.toml) `workspace.dependencies` versions for + `nemo-flow`, `nemo-flow-adaptive`, `nemo-flow-ffi`, and `nemo-flow-cli`. +3. [`crates/node/package.json`](crates/node/package.json) and the `crates/node` + entry in the root [`package-lock.json`](package-lock.json) to the same + release version. +4. [`integrations/openclaw/package.json`](integrations/openclaw/package.json) + and the `integrations/openclaw` entry in the root + [`package-lock.json`](package-lock.json) to the same release version. + +Review docs and snippets that mention explicit versions, including: + +- [`README.md`](README.md) +- [`CONTRIBUTING.md`](CONTRIBUTING.md) +- [`docs/getting-started/installation.md`](docs/getting-started/installation.md) +- Any binding README or example that pins a release number Do not commit a static Python package version into `pyproject.toml` just to cut the release. The packaging workflow stamps that file during the build. @@ -135,6 +186,7 @@ If you want to validate the packaging recipes before pushing a tag, run: ```bash just --set output_dir "$PWD/target/release-artifacts" --set ref_name 0.1.0 package-node +just --set output_dir "$PWD/target/release-artifacts" --set ref_name 0.1.0 package-openclaw just --set output_dir "$PWD/target/release-artifacts" --set ref_name 0.1.0 package-python just --set output_dir "$PWD/target/release-artifacts" --set ref_name 0.1.0 package-wasm ``` @@ -150,18 +202,18 @@ After the release commit is merged and validated, create and push the raw SemVer tag: ```bash -git fetch origin -git checkout main -git pull --ff-only +git fetch upstream release/0.1 +git checkout release/0.1 +git pull --ff-only upstream release/0.1 git tag 0.1.0 -git push origin 0.1.0 +git push upstream 0.1.0 ``` Use the prerelease form when needed: ```bash git tag 0.1.0-rc.1 -git push origin 0.1.0-rc.1 +git push upstream 0.1.0-rc.1 ``` ## What CI Does On A Tag Push @@ -178,6 +230,7 @@ The release pipeline then: 3. Builds and uploads the versioned GitHub Pages documentation artifact. 4. Builds publishable package artifacts with the exact tag version: - `package-node` packs the npm Node.js package. + - `package-openclaw` packs the npm OpenClaw plugin package. - `package-python` builds platform wheels. - `package-wasm` packs the npm WebAssembly package. 5. Publishes packages from the top-level workflow after the reusable packaging @@ -188,8 +241,8 @@ The release pipeline then: the top-level workflow - `publish-python` uploads the wheel artifacts to PyPI with trusted publishing from the top-level workflow - - `publish-npm` publishes the Node.js and WebAssembly npm packages through npm - trusted publishing from the top-level workflow + - `publish-npm` publishes the Node.js, OpenClaw plugin, and WebAssembly npm + packages through npm trusted publishing from the top-level workflow - Stable tags publish to the npm `latest` dist-tag - Prerelease tags such as `0.1.0-rc.1` publish to the npm `next` dist-tag so they do not become the default upgrade target @@ -210,8 +263,8 @@ The workflow boundary is split intentionally: npm trusted publishing has its own registry-side constraints: - Each npm package can only have one trusted publisher configured at a time. -- Because this repository publishes both `nemo-flow-node` and - `nemo-flow-wasm`, configure trusted publishers for both packages before +- Because this repository publishes `nemo-flow-node`, `nemo-flow-openclaw`, and + `nemo-flow-wasm`, configure trusted publishers for all three packages before pushing a release tag. - npm trusted publishing currently supports GitHub-hosted runners, not self-hosted runners. @@ -242,7 +295,8 @@ After the release is live, verify: 1. The expected crates are visible on crates.io. 2. The `nemo-flow` wheel is visible on PyPI. -3. The `nemo-flow-node` and `nemo-flow-wasm` packages are visible on npm. +3. The `nemo-flow-node`, `nemo-flow-openclaw`, and `nemo-flow-wasm` packages + are visible on npm. 4. The GitHub Pages deployment completed successfully. 5. The GitHub Release page is complete and accurate. diff --git a/justfile b/justfile index f30b62d0b..d4a2f7a61 100644 --- a/justfile +++ b/justfile @@ -437,15 +437,20 @@ PY rm -f "$metadata_file" } -set_node_package_version() { +set_node_package_versions() { local version="$1" set_npm_package_version crates/node/package.json package-lock.json "$version" crates/node + set_npm_package_version integrations/openclaw/package.json package-lock.json "$version" integrations/openclaw +} + +set_node_package_version() { + set_node_package_versions "$1" } set_project_version() { local version="$1" set_cargo_workspace_version "$version" - set_node_package_version "$version" + set_node_package_versions "$version" } set_python_package_version() {