From 43a02f48a9da93f29862c62fb989839161e50852 Mon Sep 17 00:00:00 2001 From: Anuj Sahni Date: Wed, 21 Oct 2026 11:44:10 -0700 Subject: [PATCH] branch PR is must now --- CONTRIBUTING.md | 6 +++--- README.md | 4 ++-- capellactl/cli/env.py | 2 +- docs/release-1.0.0/00-architecture.md | 4 ++-- docs/release-1.0.0/API-COVERAGE.md | 2 +- .../CAP-001-package-scaffold-cli-ci.md | 2 +- .../CAP-033-docs-examples-runbook.md | 2 +- .../CAP-050-protect-main-branch.md | 17 ++++++++++------ docs/release-1.0.0/{INDEX.md => README.md} | 20 +++++++++---------- scripts/update-api-coverage.py | 2 +- scripts/update-story-index.py | 8 ++++---- 11 files changed, 37 insertions(+), 32 deletions(-) rename docs/release-1.0.0/{INDEX.md => README.md} (96%) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 14faa42..81a1f98 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -2,7 +2,7 @@ ## Progress must always be visible -`docs/release-/INDEX.md` is generated from the header table of each story file +`docs/release-/README.md` is generated from the header table of each story file (`docs/release-/CAP-NNN-*.md`). It must reflect reality at every point, not only at the end of a release. @@ -14,7 +14,7 @@ at every point, not only at the end of a release. - In the same change, regenerate the API coverage reports: `python3 scripts/update-api-coverage.py` (or `make coverage`). `docs/release-1.0.0/API-COVERAGE.md` derives every command's and operation's status from the story statuses, so a story that becomes Implemented updates it. -- Never edit INDEX.md or the API coverage reports by hand. CI runs +- Never edit README.md or the API coverage reports by hand. CI runs `python3 scripts/update-story-index.py --check docs/release-1.0.0` and `python3 scripts/update-api-coverage.py --check` and fails when any of them is stale. @@ -88,7 +88,7 @@ the updated `THIRD_PARTY_NOTICES.md`. CI fails when the file is stale or a depen ```bash python3.12 -m venv .venv && .venv/bin/pip install -e '.[dev]' make check # lint, types, tests, AC coverage, INDEX and API coverage checks -make index # regenerate INDEX.md +make index # regenerate README.md make coverage # regenerate the API coverage reports (docs/release-*/API-COVERAGE*.md) make notices # regenerate THIRD_PARTY_NOTICES.md after a dependency change make docs # regenerate docs/reference.md after a schema change diff --git a/README.md b/README.md index c03528c..a4a9b8e 100644 --- a/README.md +++ b/README.md @@ -93,7 +93,7 @@ Passwords are never written to `capella.yaml` or printed: give them as `--passwo | [Examples](docs/release-1.0.0/examples/) | [Annotated reference](docs/release-1.0.0/examples/capella.yaml), [single-node dev](docs/release-1.0.0/examples/single-node-dev.yaml), [GCP production](docs/release-1.0.0/examples/gcp-prod.yaml), [Azure production](docs/release-1.0.0/examples/azure-prod.yaml), [minimal](docs/release-1.0.0/examples/minimal-cluster.yaml) | | [Migrating from the scale script](docs/migrating-from-scale-script.md) | `scale_capella_cluster.sh` → capellactl | | [Architecture](docs/release-1.0.0/00-architecture.md) | Design, idempotency rules, test conventions | -| [INDEX.md](docs/release-1.0.0/INDEX.md) | Release plan and user stories | +| [Release README](docs/release-1.0.0/README.md) | Release plan and user stories | | [CONTRIBUTING.md](CONTRIBUTING.md) | How to build, test and change the project | Couchbase also publishes an official Terraform provider for Capella; [capellactl or Terraform?](docs/terraform.md) explains when to prefer it. @@ -110,7 +110,7 @@ Couchbase also publishes an official Terraform provider for Capella; [capellactl | [capellactl/importer/](capellactl/importer/), [output/](capellactl/output/), [help/](capellactl/help/) | `import`/`sync` writer; rendering, redaction and passwords; `capellactl help` pages | | [tests/](tests/) | `unit/`, `contract/` (mocked HTTP), `scenario/` (CLI against [the fake Capella](tests/fakes/capella.py)), `integration/` (real Capella) | | [scripts/](scripts/) | Generators and CI checks: story index, API coverage, AC coverage, third-party notices | -| [docs/](docs/) | [Architecture](docs/release-1.0.0/00-architecture.md), user stories (`CAP-NNN-*.md`), [INDEX.md](docs/release-1.0.0/INDEX.md), [API coverage](docs/release-1.0.0/API-COVERAGE.md), [runbook](docs/runbook.md), [examples](docs/release-1.0.0/examples/) | +| [docs/](docs/) | [Architecture](docs/release-1.0.0/00-architecture.md), user stories (`CAP-NNN-*.md`), [release plan](docs/release-1.0.0/README.md), [API coverage](docs/release-1.0.0/API-COVERAGE.md), [runbook](docs/runbook.md), [examples](docs/release-1.0.0/examples/) | Build, test and documentation conventions: [CONTRIBUTING.md](CONTRIBUTING.md) and the [Makefile](Makefile). diff --git a/capellactl/cli/env.py b/capellactl/cli/env.py index 3f05fc4..bd7c15e 100644 --- a/capellactl/cli/env.py +++ b/capellactl/cli/env.py @@ -29,7 +29,7 @@ def not_yet(story: str, what: str) -> CliExit: return CliExit(ExitCode.ERROR, f"{what} is not available in this build yet (planned in {story}; see " - "docs/release-1.0.0/INDEX.md)") + "docs/release-1.0.0/README.md)") # ------------------------------------------------------------------ helpers diff --git a/docs/release-1.0.0/00-architecture.md b/docs/release-1.0.0/00-architecture.md index f04b25c..3373d43 100644 --- a/docs/release-1.0.0/00-architecture.md +++ b/docs/release-1.0.0/00-architecture.md @@ -1,6 +1,6 @@ # capellactl 1.0.0 — Architecture & Technical Decisions -This document is the shared reference for every story in `docs/release-1.0.0`. Stories link back here instead of repeating the language decision, the idempotency model, conventions and the test strategy. The story list and delivery phases are in [INDEX.md](INDEX.md). How to use the tool is in the repository [README.md](../../README.md). +This document is the shared reference for every story in `docs/release-1.0.0`. Stories link back here instead of repeating the language decision, the idempotency model, conventions and the test strategy. The story list and delivery phases are in the release [README.md](README.md). How to use the tool is in the repository [README.md](../../README.md). API: Capella Management API v4.0, `https://cloudapi.cloud.couchbase.com` ([reference](https://docs.couchbase.com/cloud/management-api-reference/index.html)). Every endpoint, status code, field and enum named in the stories was taken from the published OpenAPI spec (`openapi.external.generated.yaml`, v4.0). @@ -420,7 +420,7 @@ Every acceptance criterion `ACn` in story `CAP-NNN` has at least one test named test__ac_ ``` -in the file named in the story's test table, and is tagged `@pytest.mark.story("CAP-NNN", "AC")`. A CI check (CAP-001) fails if an AC of an `Implemented` story has no tagged test, and lists the open ACs of `In progress` stories, so the story files, INDEX.md and the test suite cannot drift apart. +in the file named in the story's test table, and is tagged `@pytest.mark.story("CAP-NNN", "AC")`. A CI check (CAP-001) fails if an AC of an `Implemented` story has no tagged test, and lists the open ACs of `In progress` stories, so the story files, README.md and the test suite cannot drift apart. ### 12.3 Standard assertions diff --git a/docs/release-1.0.0/API-COVERAGE.md b/docs/release-1.0.0/API-COVERAGE.md index dde797d..9e29b99 100644 --- a/docs/release-1.0.0/API-COVERAGE.md +++ b/docs/release-1.0.0/API-COVERAGE.md @@ -1,6 +1,6 @@ # Management API coverage — operational database -> Generated by `scripts/update-api-coverage.py` from the Capella Management API v4.0 spec ([source](https://docs.couchbase.com/cloud/management-api-reference/_attachments/openapi.external.generated.yaml), snapshot of 2026-10-06 in [docs/api](../api/management-api-v4.0-operations.json)), the command dictionary in that script and the **Status** of each story. Every in-scope operation in the spec appears exactly once. Don't edit by hand: change a story's status and run `make coverage`. CI fails when this file is stale. Story progress: [INDEX.md](INDEX.md). +> Generated by `scripts/update-api-coverage.py` from the Capella Management API v4.0 spec ([source](https://docs.couchbase.com/cloud/management-api-reference/_attachments/openapi.external.generated.yaml), snapshot of 2026-10-06 in [docs/api](../api/management-api-v4.0-operations.json)), the command dictionary in that script and the **Status** of each story. Every in-scope operation in the spec appears exactly once. Don't edit by hand: change a story's status and run `make coverage`. CI fails when this file is stale. Story progress: [README.md](README.md). ## Scope diff --git a/docs/release-1.0.0/CAP-001-package-scaffold-cli-ci.md b/docs/release-1.0.0/CAP-001-package-scaffold-cli-ci.md index ac67d56..5a4430e 100644 --- a/docs/release-1.0.0/CAP-001-package-scaffold-cli-ci.md +++ b/docs/release-1.0.0/CAP-001-package-scaffold-cli-ci.md @@ -89,7 +89,7 @@ As in [00-architecture.md §4](00-architecture.md#4-architecture), plus: ## Implementation notes (as built) - Verified: wheel installs and runs on Python 3.12 (macOS) and on Python 3.10 and 3.13 (Linux containers). Windows is covered by the CI matrix, which has not run yet because the repository has no remote. - Container image: runs as uid 10001 and adds 42 MB to `python:3.12-slim`. AC6 originally said 'under 150 MB', which no image can meet because the base alone is about 150 MB uncompressed; the AC now measures what the tool adds (< 50 MB). -- AC5: the coverage check fails for `Implemented` stories and lists the open ACs of `In progress` stories, so stories can be marked `In progress` as soon as work starts (INDEX.md rule). +- AC5: the coverage check fails for `Implemented` stories and lists the open ACs of `In progress` stories, so stories can be marked `In progress` as soon as work starts (README.md rule). ### Tests by AC diff --git a/docs/release-1.0.0/CAP-033-docs-examples-runbook.md b/docs/release-1.0.0/CAP-033-docs-examples-runbook.md index 519a63d..d80d05e 100644 --- a/docs/release-1.0.0/CAP-033-docs-examples-runbook.md +++ b/docs/release-1.0.0/CAP-033-docs-examples-runbook.md @@ -70,7 +70,7 @@ As a **customer engineer new to Capella**, I want clear examples and a short run ## Implementation notes (as built) - AC5 changed (2026-10-08): the manual timing became an automated end-to-end run of the guide against the fake Capella (two paths: create a project and cluster, or `init`), run in CI. The 60-minute walkthrough by a new - engineer stays in the release checklist (INDEX.md) as a release check. + engineer stays in the release checklist (README.md) as a release check. - The cheatsheet was merged into `docs/runbook.md` (2026-10-08) and removed: Part 1 day-2 tasks with expected output, Part 2 the command reference, Part 3 the 20-minute demo (Azure). README, getting-started and the CAP-042 AC8 test point to the runbook. diff --git a/docs/release-1.0.0/CAP-050-protect-main-branch.md b/docs/release-1.0.0/CAP-050-protect-main-branch.md index 301022e..69522ca 100644 --- a/docs/release-1.0.0/CAP-050-protect-main-branch.md +++ b/docs/release-1.0.0/CAP-050-protect-main-branch.md @@ -2,7 +2,7 @@ | Epic | Priority | Points | Depends on | Status | |---|---|---|---|---| -| E9 Lifecycle & release | P0 | 3 | CAP-001, CAP-043 | In progress | +| E9 Lifecycle & release | P0 | 3 | CAP-001, CAP-043 | Implemented | ## User story As the **maintainer opening capellactl to the public after release 1.0.0**, I want every change to `main` to arrive through a pull request from a branch that has been reviewed and has passed CI, so that no one, including me or another admin, can push or merge unreviewed code into the branch that releases are tagged from. @@ -102,10 +102,15 @@ Add to the 1.0.0 checklist in `scripts/update-story-index.py`: "`main` and `v*` - ~~Apply the rulesets before the repository becomes public, or as the first step afterwards?~~ Answered (2026-10-21): before, as recommended. If the organisation's plan doesn't allow rulesets on a private repository, `make rulesets` fails with an error from GitHub; apply them as the first step after making it public. ## Implementation notes (as built) -- **Still open:** applying the rulesets to GitHub, then the manual AC1 check. As the repository admin: - `brew install gh && gh auth login`, `make rulesets`, then `make rulesets-check` (green) and - `git push origin HEAD:main` (refused). Add the secret `RULESETS_TOKEN` (a fine-grained token for this repository, - Administration: read-only), which the release workflow needs on a tag. Then set the story to Implemented. +- Applied 2026-10-21 by the repository admin with `make rulesets`, while the repository is still private (the + organisation's plan allows it). `make rulesets-check` is green against GitHub, and the settings read back as + merge commits off and merged branches deleted. AC1's manual check: the admin's `git push origin main` was + refused with `GH013: Repository rule violations`: "Changes must be made through a pull request" and + "6 of 6 required status checks are expected". +- **Not yet done (not an AC):** the repository secret `RULESETS_TOKEN` (a fine-grained token for this repository, + Administration: read-only). Until it exists, every release tag, `-rc` tags included, fails in the `check` job. + Also still manual: Settings → Actions → General → approval for fork pull request workflows from first-time + contributors. - **Four rulesets instead of two** (`.github/rulesets/`). A ruleset bypass skips every rule in that ruleset, so a single `main` ruleset with the maintainer as a bypass actor would also let them merge with failing CI. Split: - `main`: no bypass. Pull request required (no approval count; resolved conversations; squash or rebase), the @@ -135,7 +140,7 @@ Add to the 1.0.0 checklist in `scripts/update-story-index.py`: "`main` and `v*` | AC | Tests | |---|---| -| AC1 | `test_rulesets_ac1_main_blocks_push_force_and_delete`; manual push check after `make rulesets` (still to do) | +| AC1 | `test_rulesets_ac1_main_blocks_push_force_and_delete`; manual push check (2026-10-21: refused, GH013) | | AC2 | `test_rulesets_ac2_review_rules`, `test_rulesets_ac2_required_checks_match_ci_matrix` | | AC3 | `test_rulesets_ac3_codeowners_cover_sensitive_paths` | | AC4 | `test_rulesets_ac4_release_tags_immutable` | diff --git a/docs/release-1.0.0/INDEX.md b/docs/release-1.0.0/README.md similarity index 96% rename from docs/release-1.0.0/INDEX.md rename to docs/release-1.0.0/README.md index e442300..489c439 100644 --- a/docs/release-1.0.0/INDEX.md +++ b/docs/release-1.0.0/README.md @@ -9,17 +9,17 @@ Architecture, domain model, conventions, NFRs and the global Definition of Done: | Metric | Stories | Story points | |---|---|---| | **Total** | **50** | **195** | -| ✅ Implemented | 48 | 187 | -| 🚧 In progress | 2 | 8 | +| ✅ Implemented | 49 | 190 | +| 🚧 In progress | 1 | 5 | | ⬜ Not started | 0 | 0 | -| Not yet implemented (in progress + not started) | 2 | 8 | -| **Completion** | 96% | 96% | +| Not yet implemented (in progress + not started) | 1 | 5 | +| **Completion** | 98% | 97% | ### By priority | Priority | Meaning | Stories | Points | Implemented (stories / points) | |---|---|---|---|---| -| P0 | Must have — release blocker | 43 | 171 | 41 / 163 | +| P0 | Must have — release blocker | 43 | 171 | 42 / 166 | | P1 | Should have — ships in 1.0 unless explicitly descoped | 7 | 24 | 7 / 24 | ### By epic @@ -34,7 +34,7 @@ Architecture, domain model, conventions, NFRs and the global Definition of Done: | E6 Network access | 1 | 3 | 1 / 3 | | E7 Data model | 4 | 14 | 4 / 14 | | E8 Database credentials | 4 | 12 | 4 / 12 | -| E9 Lifecycle & release | 11 | 44 | 9 / 36 | +| E9 Lifecycle & release | 11 | 44 | 10 / 39 | ## Stories by priority @@ -84,7 +84,7 @@ Architecture, domain model, conventions, NFRs and the global Definition of Done: | 40 | ~~[CAP-044](CAP-044-license-and-notices.md)~~ | ~~Licence, notices and third-party attributions~~ | ~~E9 Lifecycle & release~~ | ~~1~~ | ✅ Implemented | | 41 | ~~[CAP-046](CAP-046-credential-commands.md)~~ | ~~Database credential commands: list, get, create, update~~ | ~~E8 Database credentials~~ | ~~3~~ | ✅ Implemented | | 42 | ~~[CAP-048](CAP-048-track-resources-by-id.md)~~ | ~~Track projects and clusters by id so renames in the Capella UI are not mistaken for new resources~~ | ~~E9 Lifecycle & release~~ | ~~5~~ | ✅ Implemented | -| 43 | [CAP-050](CAP-050-protect-main-branch.md) | Protect `main`: changes only through a reviewed pull request | E9 Lifecycle & release | 3 | 🚧 In progress | +| 43 | ~~[CAP-050](CAP-050-protect-main-branch.md)~~ | ~~Protect `main`: changes only through a reviewed pull request~~ | ~~E9 Lifecycle & release~~ | ~~3~~ | ✅ Implemented | ### P1 — Should have — ships in 1.0 unless explicitly descoped (7 stories, 24 points) @@ -108,7 +108,7 @@ Stories are delivered in phases; each phase ends with something an operator can | Phase 1 — Project & cluster | ~~CAP-009~~, ~~CAP-039~~, ~~CAP-040~~, ~~CAP-010~~, ~~CAP-011~~, ~~CAP-035~~, ~~CAP-012~~, ~~CAP-038~~, ~~CAP-041~~, ~~CAP-049~~, ~~CAP-013~~, ~~CAP-014~~, ~~CAP-022~~ | 53 | 13/13 (53 pts) | ✅ Done | Answer the init wizard, then create, edit, protect and delete a project and cluster with its IP allowlist, idempotently | | Phase 2 — Scale & hibernate | ~~CAP-015~~, ~~CAP-016~~, ~~CAP-017~~, ~~CAP-018~~, ~~CAP-019~~, ~~CAP-020~~, ~~CAP-021~~ | 26 | 7/7 (26 pts) | ✅ Done | Scale clusters out, in and up safely; power them off and on; run a weekly hibernation schedule | | Phase 3 — Data model & access | ~~CAP-023~~, ~~CAP-024~~, ~~CAP-025~~, ~~CAP-026~~, ~~CAP-027~~, ~~CAP-046~~, ~~CAP-028~~, ~~CAP-029~~ | 26 | 8/8 (26 pts) | ✅ Done | Buckets, scopes, collections and least-privilege database credentials, ready for applications | -| Phase 4 — Adopt, harden & ship | ~~CAP-030~~, ~~CAP-036~~, ~~CAP-047~~, ~~CAP-048~~, ~~CAP-031~~, ~~CAP-042~~, CAP-032, ~~CAP-044~~, ~~CAP-043~~, ~~CAP-045~~, ~~CAP-033~~, CAP-050 | 47 | 10/12 (39 pts) | 🚧 Current | Import existing clusters with a no-change round trip, one-command build and teardown, drift reporting, nightly integration gate and customer docs | +| Phase 4 — Adopt, harden & ship | ~~CAP-030~~, ~~CAP-036~~, ~~CAP-047~~, ~~CAP-048~~, ~~CAP-031~~, ~~CAP-042~~, CAP-032, ~~CAP-044~~, ~~CAP-043~~, ~~CAP-045~~, ~~CAP-033~~, ~~CAP-050~~ | 47 | 11/12 (42 pts) | 🚧 Current | Import existing clusters with a no-change round trip, one-command build and teardown, drift reporting, nightly integration gate and customer docs | ## Stories by phase @@ -180,7 +180,7 @@ _Buckets, scopes, collections and least-privilege database credentials, ready fo | 7 | [CAP-028](CAP-028-password-rotation.md) | Password rotation | P0 | E8 Database credentials | 3 | CAP-027, CAP-008 | ✅ Implemented | | 8 | [CAP-029](CAP-029-delete-database-credential.md) | Delete database credential | P0 | E8 Database credentials | 1 | CAP-027 | ✅ Implemented | -### Phase 4 — Adopt, harden & ship — 🚧 Current (10/12 stories, 39/47 points) +### Phase 4 — Adopt, harden & ship — 🚧 Current (11/12 stories, 42/47 points) _Import existing clusters with a no-change round trip, one-command build and teardown, drift reporting, nightly integration gate and customer docs._ @@ -197,7 +197,7 @@ _Import existing clusters with a no-change round trip, one-command build and tea | 9 | [CAP-043](CAP-043-build-distributables.md) | One-command build of the distributables (source release) | P0 | E9 Lifecycle & release | 3 | CAP-001, CAP-044 | ✅ Implemented | | 10 | [CAP-045](CAP-045-standalone-executables.md) | Standalone executables for macOS, Linux and Windows | P1 | E9 Lifecycle & release | 5 | CAP-043, CAP-044 | ✅ Implemented | | 11 | [CAP-033](CAP-033-docs-examples-runbook.md) | Examples, runbook & customer documentation | P0 | E9 Lifecycle & release | 3 | CAP-030 | ✅ Implemented | -| 12 | [CAP-050](CAP-050-protect-main-branch.md) | Protect `main`: changes only through a reviewed pull request | P0 | E9 Lifecycle & release | 3 | CAP-001, CAP-043 | 🚧 In progress | +| 12 | [CAP-050](CAP-050-protect-main-branch.md) | Protect `main`: changes only through a reviewed pull request | P0 | E9 Lifecycle & release | 3 | CAP-001, CAP-043 | ✅ Implemented | ## Release checklist diff --git a/scripts/update-api-coverage.py b/scripts/update-api-coverage.py index 6394cf3..29610c9 100644 --- a/scripts/update-api-coverage.py +++ b/scripts/update-api-coverage.py @@ -646,7 +646,7 @@ def count(rows: list[dict[str, Any]]) -> list[int]: "[docs/api](../api/management-api-v4.0-operations.json)), the command dictionary in that script and the " "**Status** of each story. Every in-scope operation in the spec appears exactly once. Don't edit by hand: " "change a story's status and run `make coverage`. CI fails when this file is stale. Story progress: " - "[INDEX.md](INDEX.md).\n") + "[README.md](README.md).\n") w("## Scope\n") w(f"The spec has **{len(all_ops)} operations**. Release 1.0.0 is about the operational database, so this report " f"covers the **{len(ops)} operational-database operations**. Two groups are left out:\n") diff --git a/scripts/update-story-index.py b/scripts/update-story-index.py index da02bb0..628904e 100755 --- a/scripts/update-story-index.py +++ b/scripts/update-story-index.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -"""Regenerate docs//INDEX.md from the story files' header tables. +"""Regenerate docs//README.md from the story files' header tables. Each story file (CAP-NNN-*.md) starts with: @@ -20,7 +20,7 @@ Adapted from omni-data-bus/scripts/update-story-index.py. Usage: python3 scripts/update-story-index.py [docs/release-1.0.0] - python3 scripts/update-story-index.py --check [docs/release-1.0.0] # CI: fail if INDEX.md is stale + python3 scripts/update-story-index.py --check [docs/release-1.0.0] # CI: fail if README.md is stale """ import re import sys @@ -249,7 +249,7 @@ def main() -> None: if sorted(phased) != [s["id"] for s in stories]: sys.exit(f"every story must be in exactly one phase; check RELEASES: {sorted(set(s['id'] for s in stories) ^ set(phased))}") rendered = render(release, stories) - index = release_dir / "INDEX.md" + index = release_dir / "README.md" if check: current = index.read_text(encoding="utf-8") if index.exists() else "" if current != rendered: @@ -259,7 +259,7 @@ def main() -> None: return index.write_text(rendered, encoding="utf-8") live = [s for s in stories if s["status"] != "Descoped"] - print(f"INDEX.md updated: {len(live)} stories, {sum(s['points'] for s in live)} points" + print(f"README.md updated: {len(live)} stories, {sum(s['points'] for s in live)} points" + (f" ({len(stories) - len(live)} descoped)" if len(live) < len(stories) else ""))