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
49 changes: 49 additions & 0 deletions .github/scripts/bump_version.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
"""Set every version string to the given release version.

The chart's `version` and `appVersion`, the API and MCP `__version__`, and
`ui/package.json` with its lockfile all carry the pack's release version;
the "Version strings" CI check fails when they disagree. Merging the change
to `charts/nebari-apps/Chart.yaml` is the release (`release.yaml`).

python3 .github/scripts/bump_version.py 0.2.0
"""

from __future__ import annotations

import re
import sys
from pathlib import Path

ROOT = Path(__file__).resolve().parents[2]
SEMVER = re.compile(r"^\d+\.\d+\.\d+(-[0-9A-Za-z.-]+)?$")

# (file, pattern, count): each pattern's group 1 and 2 surround the version.
EDITS = [
("charts/nebari-apps/Chart.yaml", r'^(version:\s*)[^\s#]+()', 1),
("charts/nebari-apps/Chart.yaml", r'^(appVersion:\s*")[^"]+(")', 1),
("api/src/nebari_apps_api/__init__.py", r'^(__version__ = ")[^"]+(")', 1),
("mcp/src/nebari_apps_mcp/__init__.py", r'^(__version__ = ")[^"]+(")', 1),
("ui/package.json", r'^( "version": ")[^"]+(")', 1),
# The lockfile's top-level version and packages[""].version.
("ui/package-lock.json", r'^( (?: )?"version": ")[^"]+(")', 2),
]


def main() -> int:
if len(sys.argv) != 2 or not SEMVER.match(sys.argv[1]):
print("usage: bump_version.py X.Y.Z", file=sys.stderr)
return 2
version = sys.argv[1]
for path, pattern, count in EDITS:
file = ROOT / path
text, n = re.subn(pattern, rf"\g<1>{version}\g<2>", file.read_text(), count=count, flags=re.M)
if n != count:
print(f"::error::{path}: expected {count} version string(s), found {n}", file=sys.stderr)
return 1
file.write_text(text)
print(f"set every version string to {version}")
return 0


if __name__ == "__main__":
sys.exit(main())
8 changes: 4 additions & 4 deletions .github/scripts/check_versions.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"""Fail when a version string disagrees with .release-please-manifest.json.
"""Fail when a version string disagrees with the chart's `version`.

release-please bumps all of these together; a hand edit to one of them
`bump_version.py` sets all of these together; a hand edit to one of them
would ship mismatched versions.
"""

Expand All @@ -20,7 +20,7 @@ def grep(path: str, pattern: str) -> str | None:


def main() -> int:
want = json.loads((ROOT / ".release-please-manifest.json").read_text())["."]
want = grep("charts/nebari-apps/Chart.yaml", r"^version:\s*\"?([^\"\s#]+)")
lock = json.loads((ROOT / "ui/package-lock.json").read_text())
found = {
"Chart.yaml version": grep("charts/nebari-apps/Chart.yaml", r"^version:\s*\"?([^\"\s#]+)"),
Expand All @@ -35,7 +35,7 @@ def main() -> int:
for k, v in found.items():
print(("ok " if v == want else "FAIL ") + f"{k}: {v}")
if bad:
print(f"::error::version strings must all be {want} (the release-please manifest)")
print(f"::error::version strings must all be {want} (the chart's version; set them with bump_version.py)")
return 1
return 0

Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -429,9 +429,9 @@ jobs:
- name: Paths named in AGENTS.md and the skill exist
run: python3 .github/scripts/check_doc_paths.py

# release-please bumps all of these together; a hand edit to one of them
# bump_version.py sets all of these together; a hand edit to one of them
# would ship mismatched versions.
- name: Every version string matches .release-please-manifest.json
- name: Every version string matches the chart's version
run: python3 .github/scripts/check_versions.py

# One version per runtime: the pin file CI reads must match the image
Expand Down
42 changes: 0 additions & 42 deletions .github/workflows/release-pr.yaml

This file was deleted.

3 changes: 0 additions & 3 deletions .release-please-manifest.json

This file was deleted.

7 changes: 4 additions & 3 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -103,9 +103,10 @@ helm lint charts/nebari-apps --set clusterDomain=nebari.example.com
normalized reference it checked. apps-api (`images.py`) mirrors the parsing and matching
rules for early 422s; change both together, with the shared test cases. Image pods keep the
untrusted-pod rules below and never relax `runAsNonRoot` for a root image.
- **Version strings are owned by release-please.** Never hand-edit the version in
`charts/nebari-apps/Chart.yaml`, the `__version__` files, or `ui/package.json`; the
"Version strings" check compares them with `.release-please-manifest.json`.
- **Version strings move together.** Change the version in `charts/nebari-apps/Chart.yaml`,
the `__version__` files and `ui/package.json` only with
`python3 .github/scripts/bump_version.py X.Y.Z`, in a release PR; the "Version strings"
check compares them with the chart's `version`.
- **Runtime versions are pinned once:** `ui/.node-version`, `api/.python-version`,
`mcp/.python-version`, `docs/.bun-version`, and the `toolchain` line in `operator/go.mod`
(`authz/go.mod` carries the same line).
Expand Down
4 changes: 2 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Changelog

Maintained by [release-please](https://github.com/googleapis/release-please) from the
conventional-commit titles of merged pull requests. See [Releases](README.md#releases).
Each release PR adds its section, written from the Conventional Commit titles of the pull
requests merged since the last release. See [Releases](README.md#releases).

## 0.1.0 (2026-08-26)

Expand Down
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -124,8 +124,9 @@ The full list is in [Getting started](https://packs.nebari.dev/apps-pack/getting

## Releases

[release-please](https://github.com/googleapis/release-please) keeps a `chore: release X.Y.Z`
pull request open; merging it is the release. The chart is published to
A release is a pull request that bumps the version
(`python3 .github/scripts/bump_version.py X.Y.Z`) and adds its `CHANGELOG.md` section; merging
it is the release, as in the other Nebari packs. The chart is published to
`oci://ghcr.io/nebari-dev/apps-pack/charts` and to the
[org Helm repository](https://github.com/nebari-dev/helm-repository), and the chart and images
are signed with cosign
Expand Down
2 changes: 1 addition & 1 deletion api/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ dev = [
requires = ["hatchling"]
build-backend = "hatchling.build"

# The version lives in __init__.py (release-please bumps it), so uv.lock
# The version lives in __init__.py (bump_version.py sets it), so uv.lock
# does not record it and a release needs no relock.
[tool.hatch.version]
path = "src/nebari_apps_api/__init__.py"
Expand Down
2 changes: 1 addition & 1 deletion api/src/nebari_apps_api/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
"""nebari-apps-api - REST API for the Nebari Apps Pack."""

__version__ = "0.1.0" # x-release-please-version
__version__ = "0.1.0"
4 changes: 2 additions & 2 deletions charts/nebari-apps/Chart.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ description: >-
apps-operator that reconciles Apps into Deployments, Services, and
NebariApps.
type: application
version: 0.1.0 # x-release-please-version
appVersion: "0.1.0" # x-release-please-version
version: 0.1.0
appVersion: "0.1.0"
home: https://github.com/nebari-dev/apps-pack
sources:
- https://github.com/nebari-dev/apps-pack
20 changes: 12 additions & 8 deletions docs/src/content/docs/ci-cd.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ description: What CI checks on every pull request, how images and charts are bui
| | UI | Biome, the production build, the bundle budget, vitest and its coverage thresholds, `npm audit --audit-level=high` |
| | UI e2e + a11y | Playwright on Chromium against the UI job's build (apps-api mocked): a user flow or keyboard path that breaks, or a `critical`/`serious` axe violation (WCAG 2.0/2.1 A and AA) on any page in light or dark, at desktop width and at 390px (where a page that scrolls sideways also fails); uploads the HTML report every run |
| | Helm chart | `helm lint`/`helm template` errors, `values.schema.json`, a stale chart README, chart RBAC assertions, the alert rules (promtool), kubeconform on the examples and rendered chart |
| | Repo invariants | a path in `AGENTS.md` or the skill that does not exist, version strings that disagree with the release manifest, a pin file that disagrees with its Dockerfile, actionlint, zizmor |
| | Repo invariants | a path in `AGENTS.md` or the skill that does not exist, version strings that disagree with the chart's `version`, a pin file that disagrees with its Dockerfile, actionlint, zizmor |
| **Images** (`images.yaml`) | one per image | the image does not build, or (`ui`) serves the wrong security headers; on changes to the images' sources only |
| **Docs** (`docs.yml`) | docs | the site's tests or build, or a broken external link (on docs changes only) |

Expand Down Expand Up @@ -44,15 +44,19 @@ The signer identity is the org workflow at the commit this repository pins

## Cutting a release

Releases work as in the other Nebari packs: a pull request bumps the chart's version, and
merging it is the release.

1. **Merge pull requests with Conventional Commit titles.** They are squash-merged, so the
title is the commit, and it decides the next version: `feat:` bumps the minor version
while the pack is below 1.0, `fix:` the patch version.
2. **release-please keeps a release PR open** (`release-pr.yaml`), titled
`chore: release X.Y.Z`. It bumps every version string together (the chart's `version`
and `appVersion`, the API and MCP `__version__`, `ui/package.json` and its lockfile) and
adds the release's section to `CHANGELOG.md`. Without a `RELEASE_PLEASE_TOKEN` secret the
PR is opened with `GITHUB_TOKEN`, which does not start CI; close and reopen it to run the
checks.
while the pack is below 1.0, `fix:` the patch version (a breaking change, `!`, also bumps
the minor version below 1.0).
2. **Open a release PR** titled `chore: release X.Y.Z`. Set every version string with
`python3 .github/scripts/bump_version.py X.Y.Z` (the chart's `version` and `appVersion`,
the API and MCP `__version__`, `ui/package.json` and its lockfile; CI fails if they
disagree), and add the release's section to `CHANGELOG.md` from the feature, fix and
breaking-change titles merged since the last release
(`git log --oneline nebari-apps-<previous>..main`).
3. **Merging the release PR is the release.** The `Chart.yaml` version bump runs
`release.yaml` (the org's `pack-release` workflow), which:
- pins the chart's five image tags to `sha-<release commit>`, built by the same push,
Expand Down
2 changes: 1 addition & 1 deletion mcp/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ dev = [
requires = ["hatchling"]
build-backend = "hatchling.build"

# The version lives in __init__.py (release-please bumps it), so uv.lock
# The version lives in __init__.py (bump_version.py sets it), so uv.lock
# does not record it and a release needs no relock.
[tool.hatch.version]
path = "src/nebari_apps_mcp/__init__.py"
Expand Down
2 changes: 1 addition & 1 deletion mcp/src/nebari_apps_mcp/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
"""nebari-apps-mcp - agent-facing MCP server for the Nebari Apps Pack."""

__version__ = "0.1.0" # x-release-please-version
__version__ = "0.1.0"
34 changes: 0 additions & 34 deletions release-please-config.json

This file was deleted.

2 changes: 1 addition & 1 deletion ui/vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import tailwindcss from "@tailwindcss/vite";
import react from "@vitejs/plugin-react";
import { defineConfig } from "vite";

// The UI's own release, bumped by release-please, baked into the bundle (the
// The UI's own release, set by bump_version.py, baked into the bundle (the
// Settings page shows it next to the API's).
const { version } = JSON.parse(readFileSync(path.resolve(__dirname, "package.json"), "utf8"));

Expand Down
Loading