Skip to content
Open
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
86 changes: 8 additions & 78 deletions .github/workflows/release-tags.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,23 +7,13 @@ on:
push:
branches:
- main
- 'release-**/v*'
tags:
- '*-v*'
- '**/v*'
workflow_dispatch:
inputs:
operation:
description: Release operation to run.
required: false
default: branch-cut
type: choice
options:
- branch-cut
- self-managed-branch-cut
- auto
service:
description: Optional service id or service_name for operation=auto. Branch-cut operations select a fixed service.
description: Optional service id or service_name to scope the run to. Defaults to every registered service.
required: false
type: string

Expand All @@ -47,18 +37,18 @@ env:

jobs:
# NVIDIA self-hosted runners. Eligible without a conditional because this
# workflow has no pull_request trigger: it fires on pushes to main and to
# release branches, on tags, and on manual dispatch, all of which run trusted
# reviewed code. The bazel matrix needs an event-conditional runs-on for
# exactly that reason; this does not.
# workflow has no pull_request trigger: it fires on pushes to main, on tags,
# and on manual dispatch, all of which run trusted reviewed code. The bazel
# matrix needs an event-conditional runs-on for exactly that reason; this
# does not.
#
# cpu4 rather than cpu16: these jobs shell out to the GitHub API to cut tags
# and create releases. They are latency-bound, not CPU-bound, and were sitting
# in the GitHub-hosted queue behind the build matrix while doing almost no
# work.
service-release:
name: service release automation
if: github.ref_type != 'tag' && (github.event_name != 'workflow_dispatch' || inputs.operation == 'auto')
if: github.ref_type != 'tag'
runs-on: linux-amd64-cpu4
permissions:
contents: write
Expand Down Expand Up @@ -95,78 +85,18 @@ jobs:

- name: Run service release automation
env:
NVCF_GITHUB_RELEASE_SERVICE: ${{ github.event_name == 'workflow_dispatch' && inputs.service || '' }}
NVCF_GITHUB_RELEASE_SERVICE: ${{ inputs.service || '' }}
run: |
set -euo pipefail
if [ "${NVCF_GITHUB_AUTO_TAGGING_ENABLED}" = "true" ] && \
[ "${NVCF_GITHUB_RELEASE_DRY_RUN}" = "false" ] && \
[ "${NV_GITHUB_TOKEN_CONFIGURED}" != "true" ]; then
echo "ERROR: set secret NV_GITHUB_TOKEN before enabling GitHub auto-tag publish mode." >&2
echo "The default GITHUB_TOKEN cannot trigger the release-branch/tag workflows that publish release artifacts." >&2
echo "The default GITHUB_TOKEN cannot trigger the tag workflow that publishes release notes." >&2
exit 1
fi
./tools/ci/github-release auto

release-branch-cut:
name: NVCA release branch cut
if: github.event_name == 'workflow_dispatch' && inputs.operation == 'branch-cut'
runs-on: linux-amd64-cpu4
permissions:
contents: write
pull-requests: write
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
ref: ${{ github.event.repository.default_branch }}
token: ${{ secrets.NV_GITHUB_TOKEN || github.token }}

- name: Cut release branch
env:
NVCF_GITHUB_RELEASE_SERVICE: nvca
NV_GITHUB_TOKEN_CONFIGURED: ${{ secrets.NV_GITHUB_TOKEN != '' }}
GITHUB_DEFAULT_BRANCH: ${{ github.event.repository.default_branch }}
run: |
set -euo pipefail
if [ "${NV_GITHUB_TOKEN_CONFIGURED}" != "true" ]; then
echo "ERROR: set secret NV_GITHUB_TOKEN before cutting a GitHub release branch." >&2
echo "The default GITHUB_TOKEN cannot trigger CI for the generated VERSION bump PR." >&2
exit 1
fi
if [ -z "${NVCF_GITHUB_RELEASE_SERVICE}" ]; then
echo "ERROR: service is required when operation=branch-cut." >&2
exit 1
fi
./tools/ci/github-release branch-cut --service "${NVCF_GITHUB_RELEASE_SERVICE}"

self-managed-release-branch-cut:
name: self-managed stack release branch cut
if: github.event_name == 'workflow_dispatch' && inputs.operation == 'self-managed-branch-cut'
runs-on: linux-amd64-cpu4
permissions:
contents: write
pull-requests: write
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
ref: ${{ github.event.repository.default_branch }}
token: ${{ secrets.NV_GITHUB_TOKEN || github.token }}

- name: Cut release branch
env:
NVCF_GITHUB_RELEASE_SERVICE: nvcf-self-managed-stack
NV_GITHUB_TOKEN_CONFIGURED: ${{ secrets.NV_GITHUB_TOKEN != '' }}
GITHUB_DEFAULT_BRANCH: ${{ github.event.repository.default_branch }}
run: |
set -euo pipefail
if [ "${NV_GITHUB_TOKEN_CONFIGURED}" != "true" ]; then
echo "ERROR: set secret NV_GITHUB_TOKEN before cutting a GitHub release branch." >&2
echo "The default GITHUB_TOKEN cannot trigger CI for the generated VERSION bump PR." >&2
exit 1
fi
./tools/ci/github-release branch-cut --service "${NVCF_GITHUB_RELEASE_SERVICE}"

tag-release-notes:
name: tag release notes
if: github.ref_type == 'tag'
Expand Down
150 changes: 63 additions & 87 deletions RELEASE.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,52 +14,42 @@ authoritative behavior; this document summarizes it for contributors.

Releases are commit-triggered, not calendar-triggered. There is no fixed
weekly or monthly cadence. On every push to `main`, the `service-release` job
runs `./tools/ci/github-release auto`. Each subproject registered in
runs `./tools/ci/github-release auto`. Every subproject registered in
[`tools/ci/github-release-subprojects.json`](tools/ci/github-release-subprojects.json)
uses exactly one of two release models. They are mutually exclusive: the
automation branches on a subproject's `dev_prerelease` flag and only ever
runs one path for it, never both.

### Semantic-release model

Most subprojects, for example `src/clis/nvcf-cli`. Walks the commits since
the subproject's last release tag and decides whether to cut a new version.
uses the same model: semantic-release walks the commits since the
subproject's last release tag and decides whether to cut a new version.

- Commits typed `feat`, `fix`, or `perf` (the "customer" commit types defined
in [`CONTRIBUTING.md`](CONTRIBUTING.md#how-to-select-a-commit-type)) trigger
a new stable release tag for the subproject they touch.
- Commits typed `docs`, `build`, `test`, `refactor`, `ci`, `chore`, `style`,
or `revert` (the "foundational" types) do not trigger a release on their
own.
- A commit whose type carries a `!` breaking marker triggers a release
regardless of type.

Only commits that touch a subproject's own path count toward its version.
`semantic-release-monorepo` scopes the commit analysis to the subtree, so a
`fix(grpc-proxy):` commit cannot release `nvcf-cli` and vice versa.

### Dev-prerelease model

Only `nvca` and the three Helm stacks under `deploy/stacks/`:
`nvcf-compute-plane`, `self-managed`, and `observability`. A push to `main`
never cuts a stable release, regardless of commit type. It only bumps a
`-dev.N` prerelease tag off the stable base version recorded in
the subproject's `VERSION` file. In practice this means dev prereleases land
many times per day during active development; for example, at the time of
writing, `src/compute-plane-services/nvca` had cut five `-dev.N` prereleases
in a single day. A stable release for one of these four subprojects is cut
only when a commit lands on that subproject's release branch (see Branch
Naming), which bumps the patch version automatically. Until a release branch
exists for one of these subprojects, only dev prereleases accumulate; there
is no stable-release path on `main` for them.
`nvca` and the three Helm stacks under `deploy/stacks/` previously used a
separate model: a push to `main` only bumped a `-dev.N` prerelease read from
a `VERSION` file, and a stable version was cut only on a release branch.
Those subprojects now release from `main` like every other one, the `VERSION`
files are gone, and the `-dev.N` tags they already published remain in the
repository as history.

Release notes are generated from commit messages (semantic-release
conventions) and attached to the GitHub Release for each tag, dev
prereleases included.
conventions) and attached to the GitHub Release for each tag.

## Branch Naming

- `main`: the active development branch. All pull requests target `main`,
except hotfixes (see [`CONTRIBUTING.md`](CONTRIBUTING.md#step-2-create-a-branch)).
- `release-<service-path>/vMAJOR.MINOR`: a maintenance branch for one
subproject's release train, used by the dev-prerelease release model (see
Release Cadence). Pushes to a branch matching `release-**/v*` run the same
release automation, scoped to that subproject, so patch fixes on a
maintenance train publish their own stable tags.
subproject's release train. These branches still run the build, test, lint,
and scan workflows, but they no longer cut release tags. Release automation
runs on `main` only.

Real examples from this repository:

Expand All @@ -70,61 +60,37 @@ Real examples from this repository:
The separator between the service id and the version can vary by how a
subproject registers its release metadata; for example
`release-nvcf-cassandra-migrations-v0.10` uses a flattened id instead of a
path segment. That branch also predates this subproject's current
configuration, which does not use the dev-prerelease model, so treat it as
historical evidence of the naming pattern rather than a live example. Check
a subproject's entry in
path segment. Check a subproject's entry in
[`tools/ci/github-release-subprojects.json`](tools/ci/github-release-subprojects.json)
for its current release model and exact branch name.
for its exact tag prefix and branch name.

Tags follow the matching format `<service-path>/vMAJOR.MINOR.PATCH`, for
example `src/clis/nvcf-cli/v1.15.11` (semantic-release model) and
`src/compute-plane-services/nvca/v3.3.0-dev.184` (dev-prerelease model).
example `src/clis/nvcf-cli/v1.15.11` and
`src/compute-plane-services/nvca/v3.4.0`.

## Who Can Trigger a Release

Automatic, semantic-release subprojects: any contributor whose reviewed pull
request merges to `main` has triggered a release for the subprojects their
commits touch, as long as at least one commit is a `feat`, `fix`, or `perf`
type. No separate release action is needed after merge.

Automatic, dev-prerelease subprojects (`nvca` and the three stacks under
`deploy/stacks/`): merging any pull request to `main` only produces a dev
prerelease tag, regardless of commit type. Triggering an actual stable
release for one of these subprojects requires a commit to land on that
subproject's release branch instead of `main`. See Manual below for who can
create that branch in the first place.
Any contributor whose reviewed pull request merges to `main` has triggered a
release for the subprojects their commits touch, as long as at least one
commit is a `feat`, `fix`, or `perf` type. No separate release action is
needed after merge.

Manual: `.github/workflows/release-tags.yml` also accepts a
`workflow_dispatch` trigger with three operations:

- `auto`: re-run the same automatic logic on demand, optionally scoped to one
service.
- `branch-cut`: cut a new maintenance release branch and open the follow-up
version-bump pull request for the `nvca` service.
- `self-managed-branch-cut`: the same branch-cut flow for the
`nvcf-self-managed-stack` service.

Both branch-cut operations only work for a dev-prerelease subproject; the
underlying `branch-cut` command refuses to run against a subproject that
does not use that release model.
`workflow_dispatch` trigger that re-runs the same automatic logic on demand,
optionally scoped to a single service through the `service` input. This is a
recovery path for a run that failed or was cancelled, not a way to force a
version that the commits do not justify.

`workflow_dispatch` requires GitHub write access to the repository. In this
repository that access is granted through organization team membership:
maintainers (`NVIDIA/nvcf-dev` and `NVIDIA/nvcf-admin`) can cut a release
branch. Only `nvca` and `nvcf-self-managed-stack` have a dedicated
`workflow_dispatch` operation; for `nvcf-compute-plane-stack` and
`nvcf-observability-stack`, a maintainer runs
`tools/ci/github-release branch-cut --service <id>` directly, since no CI
job wires up that operation for them yet. An external contributor with
a fix for an already-cut release branch needs a maintainer to either create
the branch or merge a
pull request that targets an existing one. Branch-cut operations
additionally require the `NV_GITHUB_TOKEN` repository secret to be
configured, because the default `GITHUB_TOKEN` cannot trigger CI on the
generated version-bump pull request. Area ownership for review is defined in
maintainers (`NVIDIA/nvcf-dev` and `NVIDIA/nvcf-admin`) can run it. Area
ownership for review is defined in
[`.github/CODEOWNERS`](.github/CODEOWNERS).

Publishing additionally requires the `NV_GITHUB_TOKEN` repository secret,
because a tag pushed with the default `GITHUB_TOKEN` does not start the
follow-up tag workflow that creates release notes.

## Artifact Destinations

GitHub tags and GitHub Releases are the primary release artifact, one per
Expand All @@ -145,19 +111,29 @@ step tied to a version tag.

## Backport Policy

Applies only to the four dev-prerelease subprojects (`nvca` and the three
stacks under `deploy/stacks/`), since those are the only subprojects that
cut release branches at all (see Release Cadence). Semantic-release
subprojects have no maintenance branch to backport to; a fix for one of
them ships by merging to `main` like any other change.

Support window: for each of the four dev-prerelease subprojects, only the
latest minor release train and the one before it (N and N-1) are
maintained. A release branch older than N-1 is effectively end of life and
does not receive further backports.

Mechanism: a fix lands on `main` first. To reach a supported release
branch, apply it there directly, for example by cherry-picking the commit
to the `release-*` branch, following the same commit and review
conventions as `main`. There is no automation that backports a commit for
you.
A fix lands on `main` first, where it releases normally for every subproject
it touches.

Maintenance branches are the exception, not the default. Most subprojects do
not have one: a fix ships by merging to `main` like any other change. Open a
maintenance branch only when a released train needs a fix that cannot wait
for, or must not carry, the current state of `main`.

Support window: where a subproject does maintain release branches, only the
latest minor release train and the one before it (N and N-1) are maintained.
A release branch older than N-1 is effectively end of life and does not
receive further backports.

Mechanism: cherry-pick the commit from `main` onto the `release-*` branch,
following the same commit and review conventions as `main`. Because release
automation runs on `main` only, a maintainer then creates the patch tag by
hand:

```sh
git tag <service-path>/vMAJOR.MINOR.PATCH <commit-on-the-release-branch>
git push origin <service-path>/vMAJOR.MINOR.PATCH
```

Push the tag with a token that can start workflows, so the tag workflow
creates the matching GitHub Release. There is no automation that backports a
commit or cuts a maintenance release for you.
1 change: 0 additions & 1 deletion deploy/stacks/nvcf-compute-plane/VERSION

This file was deleted.

1 change: 0 additions & 1 deletion deploy/stacks/observability/VERSION

This file was deleted.

1 change: 0 additions & 1 deletion deploy/stacks/self-managed/VERSION

This file was deleted.

Loading
Loading