From 14dd8d9ddb456393af924458abc117d354c53f92 Mon Sep 17 00:00:00 2001 From: Gracjan Sadowicz Date: Tue, 5 May 2026 17:47:51 +0200 Subject: [PATCH 1/8] RavenDB-26535: add release pipeline + workflow lint + dry-run + CLAUDE.md --- .github/workflows/lint-workflows.yml | 26 ++++ .github/workflows/prepare-release.yml | 153 +++++++++++++++++++ .github/workflows/release-dryrun.yml | 46 ++++++ .github/workflows/release.yml | 208 ++++++++++++++++++++++++++ CLAUDE.md | 67 +++++++++ 5 files changed, 500 insertions(+) create mode 100644 .github/workflows/lint-workflows.yml create mode 100644 .github/workflows/prepare-release.yml create mode 100644 .github/workflows/release-dryrun.yml create mode 100644 .github/workflows/release.yml create mode 100644 CLAUDE.md diff --git a/.github/workflows/lint-workflows.yml b/.github/workflows/lint-workflows.yml new file mode 100644 index 0000000..715b032 --- /dev/null +++ b/.github/workflows/lint-workflows.yml @@ -0,0 +1,26 @@ +name: Lint workflows + +# Runs on every workflow-touching PR. Catches YAML errors, deprecated action versions, +# unset secrets, and shellcheck warnings on inline `run:` blocks. + +on: + pull_request: + paths: + - '.github/workflows/**' + +permissions: + contents: read + +jobs: + actionlint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Download actionlint + run: | + bash <(curl -sSL https://raw.githubusercontent.com/rhysd/actionlint/main/scripts/download-actionlint.bash) 1.7.7 + ./actionlint -version + + - name: Run actionlint + run: ./actionlint -color diff --git a/.github/workflows/prepare-release.yml b/.github/workflows/prepare-release.yml new file mode 100644 index 0000000..177b7a7 --- /dev/null +++ b/.github/workflows/prepare-release.yml @@ -0,0 +1,153 @@ +name: Prepare Release + +# Phase 1 of the two-phase release flow. +# Trigger: Actions tab -> Run workflow with target version. +# Output: a `release: vX.Y.Z` PR labeled `release:auto`, carrying source bumps + helm chart. +# Merge fires the Release workflow; remove the label first to delay publishing. +# dry_run=true: apply bumps + upload bundle/helm artifacts; no branch/PR/label mutation. + +on: + workflow_dispatch: + inputs: + version: + description: 'Version to release (e.g. 1.1.0, no v prefix)' + required: true + type: string + dry_run: + description: 'Dry run (no PR / branch / label mutations)' + required: false + type: boolean + default: false + workflow_call: + inputs: + version: + required: true + type: string + dry_run: + required: false + type: boolean + default: false + +permissions: + contents: write + pull-requests: write + +jobs: + prepare: + runs-on: ubuntu-latest + env: + VERSION: ${{ inputs.version }} + steps: + - uses: actions/checkout@v4 + + - uses: azure/setup-helm@v4 + + - name: Read previous version from Makefile + id: prev + run: | + OLD=$(awk -F'?= ' '/^VERSION \?=/ {print $2}' Makefile) + echo "old=$OLD" >> "$GITHUB_OUTPUT" + echo "Previous version: $OLD" + echo "New version: $VERSION" + if [ "${{ inputs.dry_run }}" = "true" ]; then + echo "Mode: DRY RUN (no PR / branch / label mutations)" + fi + + - name: Bump source files + env: + OLD: ${{ steps.prev.outputs.old }} + run: | + sed -i "s/^VERSION ?=.*/VERSION ?= ${VERSION}/" Makefile + sed -i "s/^version:.*/version: ${VERSION}/" helm/chart/Chart.yaml + sed -i "s/^appVersion:.*/appVersion: \"${VERSION}\"/" helm/chart/Chart.yaml + sed -i "/^| [0-9]\+\.[0-9]\+\.[0-9]\+ /c\\| ${VERSION} | :white_check_mark: |" SECURITY.md + + - name: Update OLM bundle + env: + OLD: ${{ steps.prev.outputs.old }} + run: | + mv "bundle/ravendb-operator/${OLD}" "bundle/ravendb-operator/${VERSION}" + CSV="bundle/ravendb-operator/${VERSION}/manifests/ravendb-operator.clusterserviceversion.yaml" + sed -i "s|ravendb-operator:${OLD}|ravendb-operator:${VERSION}|g" "$CSV" + sed -i "s|ravendb-operator.v${OLD}|ravendb-operator.v${VERSION}|g" "$CSV" + sed -i "s|^ version: ${OLD}\$| version: ${VERSION}|" "$CSV" + sed -i "s|createdAt: \".*\"|createdAt: \"$(date -u +%Y-%m-%dT%H:%M:%SZ)\"|" "$CSV" + + - name: Package helm chart + run: | + helm package helm/chart -d helm/ + helm repo index helm/ \ + --url https://ravendb.github.io/ravendb-operator/helm \ + --merge helm/index.yaml + + - name: Verify no stale OLD refs in new bundle + env: + OLD: ${{ steps.prev.outputs.old }} + run: | + if grep -nrE "(^|[^0-9])${OLD}([^0-9]|\$)" "bundle/ravendb-operator/${VERSION}"; then + echo "::error::Stale ${OLD} references found in bundle/ravendb-operator/${VERSION}/" + exit 1 + fi + + # ─── Real-mode mutations (skipped on dry_run) ─── + + - name: Ensure release:auto label exists + if: ${{ !inputs.dry_run }} + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + gh label create "release:auto" \ + --description "On merge, fires the Release workflow automatically. Remove before merging to delay publishing." \ + --color "0e8a16" \ + || true # already exists is fine + + - name: Open release PR + if: ${{ !inputs.dry_run }} + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + BRANCH="release/v${VERSION}" + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + git checkout -b "$BRANCH" + git add Makefile helm/ bundle/ SECURITY.md + git commit -m "release: v${VERSION}" + git push origin "$BRANCH" + gh pr create \ + --title "release: v${VERSION}" \ + --base main \ + --head "$BRANCH" \ + --label "release:auto" \ + --body "Auto-generated bumps for v${VERSION}. + + On merge with the **release:auto** label still attached, the Release workflow fires automatically: + builds and pushes the multi-arch image, runs a kind-based smoke test, tags v${VERSION}, and + publishes a GitHub Release with \`install.yaml\` attached. + + Remove the **release:auto** label before merging to delay publishing. You can fire publishing + later by running the Release workflow manually with version \`${VERSION}\`." + + # ─── Dry-run-only outputs ─── + + - name: Dry run — upload artifacts + if: ${{ inputs.dry_run }} + uses: actions/upload-artifact@v4 + with: + name: prepare-release-${{ inputs.version }} + path: | + bundle/ravendb-operator/${{ inputs.version }}/ + helm/ravendb-operator-${{ inputs.version }}.tgz + helm/index.yaml + + - name: Dry run — summary + if: ${{ inputs.dry_run }} + run: | + echo "DRY RUN SUMMARY" + echo "===============" + echo "Would have:" + echo " - Created branch: release/v${VERSION}" + echo " - Committed: Makefile, helm/, bundle/, SECURITY.md" + echo " - Opened PR: 'release: v${VERSION}' with label release:auto" + echo "" + echo "Working-tree diff vs HEAD:" + git diff --stat HEAD diff --git a/.github/workflows/release-dryrun.yml b/.github/workflows/release-dryrun.yml new file mode 100644 index 0000000..3a508ad --- /dev/null +++ b/.github/workflows/release-dryrun.yml @@ -0,0 +1,46 @@ +name: Release dry-run + +# Biweekly dry-run of the full pipeline. Catches deprecated actions, broken URLs, +# and YAML drift before they bite a real release. Opens a `ci-broken` issue on failure. + +on: + schedule: + - cron: '0 6 1,15 * *' # 1st and 15th of each month, 06:00 UTC + workflow_dispatch: + +permissions: + contents: read + issues: write + +jobs: + prepare: + uses: ./.github/workflows/prepare-release.yml + with: + version: '0.0.0-dryrun' + dry_run: true + secrets: inherit + + publish: + needs: prepare + uses: ./.github/workflows/release.yml + with: + version: '0.0.0-dryrun' + dry_run: true + secrets: inherit + + notify-on-failure: + needs: [prepare, publish] + if: failure() && github.event_name == 'schedule' + runs-on: ubuntu-latest + steps: + - uses: actions/github-script@v7 + with: + script: | + const runUrl = `${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`; + await github.rest.issues.create({ + owner: context.repo.owner, + repo: context.repo.repo, + title: 'Biweekly release dry-run failed', + body: `The scheduled release dry-run failed.\n\nRun: ${runUrl}\n\nThis usually means the release pipeline has drifted (deprecated action, removed input, broken external URL). Check the run logs and fix before the next real release.`, + labels: ['ci-broken'] + }); diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..473e14d --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,208 @@ +name: Release + +# Phase 2 of the two-phase release flow. +# Triggers: merge of a `release:auto`-labeled PR, manual workflow_dispatch, or +# workflow_call from release-dryrun.yml (always dry_run=true). +# Real-mode: pushes multi-arch ravendb/ravendb-operator:{vX.Y.Z,latest}, runs kind +# smoke test, tags vX.Y.Z, publishes Release with install.yaml attached. +# Dry-run: single-arch local build + same smoke test; skips push/tag/release. + +on: + pull_request: + types: [closed] + branches: [main] + workflow_dispatch: + inputs: + version: + description: 'Version to release (must match Makefile VERSION on main)' + required: true + type: string + dry_run: + description: 'Dry run (no push / tag / Release)' + required: false + type: boolean + default: false + workflow_call: + inputs: + version: + required: true + type: string + dry_run: + required: false + type: boolean + default: false + +permissions: + contents: write + +jobs: + publish: + if: | + inputs.dry_run + || github.event_name == 'workflow_dispatch' + || (github.event_name == 'pull_request' + && github.event.pull_request.merged == true + && contains(github.event.pull_request.labels.*.name, 'release:auto')) + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + ref: main + fetch-depth: 0 + + - name: Determine version + id: ver + env: + BRANCH: ${{ github.event.pull_request.head.ref }} + run: | + if [ -n "${{ inputs.version }}" ]; then + VERSION="${{ inputs.version }}" + else + # Auto-fired from labeled PR merge; branch is release/vX.Y.Z. + VERSION="${BRANCH#release/v}" + fi + if [ -z "${VERSION}" ] || [ "${VERSION}" = "${BRANCH}" ]; then + echo "::error::Could not derive version (branch=${BRANCH}, input=${{ inputs.version }})" + exit 1 + fi + echo "version=${VERSION}" >> "$GITHUB_OUTPUT" + echo "img=ravendb/ravendb-operator:${VERSION}" >> "$GITHUB_OUTPUT" + if [ "${{ inputs.dry_run }}" = "true" ]; then + echo "Mode: DRY RUN (no push / tag / Release)" + else + echo "Releasing v${VERSION}" + fi + + - name: Verify Makefile VERSION matches target + env: + VERSION: ${{ steps.ver.outputs.version }} + run: | + MAKE_VERSION=$(awk -F'?= ' '/^VERSION \?=/ {print $2}' Makefile) + if [ "$MAKE_VERSION" != "$VERSION" ]; then + echo "::error::Makefile VERSION ($MAKE_VERSION) != target ($VERSION). Did the prepare-release PR not merge cleanly?" + exit 1 + fi + + - uses: docker/setup-qemu-action@v3 + - uses: docker/setup-buildx-action@v3 + + - name: Log in to DockerHub + if: ${{ !inputs.dry_run }} + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + + - name: Build and push manager image (real, multi-arch) + if: ${{ !inputs.dry_run }} + uses: docker/build-push-action@v6 + with: + context: . + platforms: linux/amd64,linux/arm64 + push: true + tags: | + ravendb/ravendb-operator:${{ steps.ver.outputs.version }} + ravendb/ravendb-operator:latest + + - name: Build manager image (dry run, single-arch, local load) + if: ${{ inputs.dry_run }} + uses: docker/build-push-action@v6 + with: + context: . + platforms: linux/amd64 + push: false + load: true + tags: ravendb/ravendb-operator:${{ steps.ver.outputs.version }} + + - name: Verify multi-arch manifests landed + if: ${{ !inputs.dry_run }} + env: + IMG: ${{ steps.ver.outputs.img }} + run: | + ARCHS=$(docker buildx imagetools inspect "${IMG}" | awk '/Platform:/ {print $2}') + echo "Detected platforms:" + echo "$ARCHS" + echo "$ARCHS" | grep -q '^linux/amd64$' || { echo "::error::amd64 manifest missing"; exit 1; } + echo "$ARCHS" | grep -q '^linux/arm64$' || { echo "::error::arm64 manifest missing"; exit 1; } + + - name: Build dist/install.yaml + env: + IMG: ${{ steps.ver.outputs.img }} + run: make build-installer IMG="${IMG}" + + - name: Smoke test on Kind + uses: helm/kind-action@v1 + with: + cluster_name: smoke + + - name: Smoke (dry run) — load locally-built image into kind + if: ${{ inputs.dry_run }} + run: | + kind load docker-image ravendb/ravendb-operator:${{ steps.ver.outputs.version }} --name smoke + + - name: Smoke — install cert-manager + run: | + kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.16.0/cert-manager.yaml + kubectl -n cert-manager rollout status deploy/cert-manager --timeout=2m + kubectl -n cert-manager rollout status deploy/cert-manager-webhook --timeout=2m + kubectl -n cert-manager rollout status deploy/cert-manager-cainjector --timeout=2m + + - name: Smoke — apply install.yaml + verify operator pod becomes Ready + run: | + kubectl apply -f dist/install.yaml + NS=$(kubectl get deploy -A -o jsonpath='{range .items[?(@.metadata.name=="ravendb-operator-controller-manager")]}{.metadata.namespace}{"\n"}{end}' | head -1) + if [ -z "$NS" ]; then + echo "::error::ravendb-operator-controller-manager deployment not found" + kubectl get deploy -A + exit 1 + fi + echo "Manager deployment lives in namespace: $NS" + kubectl -n "$NS" rollout status deploy/ravendb-operator-controller-manager --timeout=3m + + # ─── Real-mode publishing (skipped on dry_run) ─── + + - name: Push git tag + if: ${{ !inputs.dry_run }} + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + VERSION: ${{ steps.ver.outputs.version }} + run: | + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + git tag -a "v${VERSION}" -m "Release v${VERSION}" + git push origin "v${VERSION}" + + - name: Publish GitHub Release with install.yaml + if: ${{ !inputs.dry_run }} + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + VERSION: ${{ steps.ver.outputs.version }} + run: | + gh release create "v${VERSION}" \ + --title "v${VERSION}" \ + --generate-notes \ + dist/install.yaml + + # ─── Dry-run-only outputs ─── + + - name: Dry run — upload install.yaml + if: ${{ inputs.dry_run }} + uses: actions/upload-artifact@v4 + with: + name: release-${{ steps.ver.outputs.version }} + path: dist/install.yaml + + - name: Dry run — summary + if: ${{ inputs.dry_run }} + env: + VERSION: ${{ steps.ver.outputs.version }} + run: | + echo "DRY RUN SUMMARY" + echo "===============" + echo "Would have:" + echo " - Pushed: ravendb/ravendb-operator:${VERSION} (multi-arch)" + echo " - Pushed: ravendb/ravendb-operator:latest" + echo " - Tagged: v${VERSION}" + echo " - Released: v${VERSION} on GitHub with install.yaml attached" + echo "" + echo "Smoke test (cert-manager + install.yaml + rollout) passed against the locally-built image." diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..84a428c --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,67 @@ +# RavenDB Operator — Claude context + +Go controller (kubebuilder, `go.kubebuilder.io/v4`) that reconciles `RavenDBCluster` CRs into running RavenDB clusters. Shipped as both an OLM bundle and a Helm chart. + +## Layout + +| Path | Contents | +| --- | --- | +| `api/v1/` | `RavenDBCluster` CRD types + admission webhook glue. Field-level `+operator-sdk:csv:customresourcedefinitions:` markers drive CSV descriptors. | +| `cmd/main.go` | Manager entrypoint | +| `internal/controller/` | Reconciliation loop | +| `pkg/actor/` | Actor-style sub-reconcilers (RBAC, secrets, certs) | +| `pkg/director/` | Coordinates actors | +| `pkg/resource/` | Builders for k8s resources (StatefulSet, Service, RBAC, …) | +| `pkg/scripts/` | Bash mounted into RavenDB pods (init, cert update, discovery) | +| `pkg/webhook/{adapter,validator}/` | Admission webhook plumbing and validators | +| `config/` | Kustomize manifests (CRD, RBAC, manager, webhook, manifests bases) | +| `config/manifests/bases/...csv.yaml` | CSV base — operator-level metadata (description, icon, keywords, links, maintainers). `make bundle` merges marker-derived descriptors into this. | +| `config/samples/` | CR examples folded into CSV `alm-examples` by operator-sdk. New samples must be registered in `config/samples/kustomization.yaml`. | +| `helm/chart/` | Helm chart (parallel to `config/`, not auto-generated from it) | +| `helm/` | Packaged `.tgz` artifacts and `index.yaml` (served via GH Pages) | +| `bundle/ravendb-operator//` | Current OLM bundle. Generated by `make bundle` + `make bundle-relocate`. | +| `bundle/ravendb-operator/ci.yaml` | OperatorHub config — `updateGraph: semver-mode` | +| `.github/workflows/operator-ci.yml` | CI: tests + e2e on PR / push to main | +| `.github/workflows/lint-workflows.yml` | actionlint on PRs touching `.github/workflows/**` | +| `.github/workflows/prepare-release.yml` | Release phase 1: bumps + `make bundle` + helm package + opens release PR labeled `release:auto`. First step is `make verify-samples` — gates the entire release. | +| `.github/workflows/release.yml` | Release phase 2: auto-fires on merge of a `release:auto`-labeled PR (or manual `workflow_dispatch`). Multi-arch image push + multi-arch verify + kind smoke test + git tag + published GitHub Release with `install.yaml` attached | +| `.github/workflows/release-dryrun.yml` | Biweekly cron that runs prepare-release + release with `dry_run=true` to surface pipeline rot. Opens an issue on failure. | +| `docker/release.{sh,ps1}` | Legacy DockerHub release scripts. Superseded by the workflows above; keep as a manual fallback. | +| `test/e2e/` | E2E tests (kustomize + helm install paths, namespace isolation, upgrades) | +| `test/samples/` | Strict-unmarshal samples against api/v1 types. Run by `make verify-samples`. | + +## Common commands + +```bash +make manifests # Regenerate CRDs/RBAC/webhook from Go annotations +make generate # Regenerate DeepCopy methods +make test # envtest unit tests +make test-e2e # Kind-based e2e +make lint # golangci-lint +make build # Build manager binary into bin/ +make verify-samples # Strict-unmarshal config/samples/ against api/v1 types (no envtest) +make bundle VERSION= IMG=docker.io/ravendb/ravendb-operator: CHANNELS=stable DEFAULT_CHANNEL=stable +make bundle-relocate VERSION= # Move flat bundle/ output into nested bundle/ravendb-operator// +``` + +Never run `docker/release.sh`, `make docker-push-*`, `make bundle-push`, `make catalog-push`, `helm push`, `gh release create`, or `git push` (commits, branches, or tags) without an explicit per-action go-ahead. Routine releases happen by clicking **Prepare Release** in the [Actions tab](https://github.com/ravendb/ravendb-operator/actions), reviewing the auto-generated PR, and merging it — the Release workflow auto-fires on merge if the `release:auto` label is still attached. Remove the label before merging to delay publishing. + +## Conventions + +- Webhook validators live in `pkg/webhook/validator/`. The adapter in `pkg/webhook/adapter/` wires them to controller-runtime. New validators go through the adapter, not directly on CRD types. +- One `RavenDBCluster` per namespace. Enforced in `pkg/webhook/validator/namespace_validator.go`. +- Secret lookups are namespace-scoped — never fall back to the operator's namespace. +- Helm chart and `config/` are maintained in parallel. RBAC/env/volume changes must land in both. +- CSV descriptors are emitted from `+operator-sdk:csv:customresourcedefinitions:displayName="..."` markers on api/v1 fields. Operator-level metadata (description, icon, keywords, links, maintainers, minKubeVersion) lives in `config/manifests/bases/...csv.yaml` and is preserved across `make bundle` runs. + +## Repo gotchas + +- `bundle/ravendb-operator/ci.yaml` uses `updateGraph: semver-mode`. Do not add `spec.replaces` to the CSV. +- `helm/index.yaml` is committed and served from `https://ravendb.github.io/ravendb-operator/helm/`. ArtifactHub crawls it via `helm/artifacthub-repo.yml`. +- `Makefile` `IMAGE_TAG_BASE` defaults to `ravendb.io/ravendb-operator` — not a real registry. Bundle/catalog targets need it overridden to `docker.io/ravendb/ravendb-operator`. +- The OLM bundle uses a nested layout (`bundle/ravendb-operator//{manifests,metadata,tests}/`). `make bundle` writes operator-sdk's flat output at repo root; `make bundle-relocate` moves it into the nested layout and rewrites `bundle.Dockerfile` `COPY` paths. +- `make bundle` rewrites `config/manager/kustomization.yaml` (image tag) as a side-effect of `kustomize edit set image`. The release workflow reverts that file post-bundle so the tracked default stays at `:latest`. +- The marker parser rejects `description="..."` at customresourcedefinitions level — only `displayName=` is supported. Per-field descriptions must live in the base file (or be accepted as lost). +- operator-sdk markers emit descriptor paths as `nodes[0].field`; OLM convention is `nodes[].field` ("applies to all array elements"). `prepare-release.yml` post-processes the CSV with a scoped sed to fix this. Same workflow re-injects the `containerImage:` annotation that operator-sdk does not emit. +- `make bundle` is Linux/macOS only — operator-sdk doesn't ship Windows binaries. Use WSL or run via the `prepare-release.yml` workflow. +- `controller-gen paths=./...` fails on Windows for this repo (interaction with the hostname-less `module ravendb-operator` in `go.mod`). Use `paths='ravendb-operator/...'` if running on Windows. From a0029a3671d70818c8f93f71389c26867c2d23ca Mon Sep 17 00:00:00 2001 From: Gracjan Sadowicz Date: Tue, 5 May 2026 17:48:27 +0200 Subject: [PATCH 2/8] RavenDB-26535: fix make bundle prerequisites (samples + bundle-relocate) --- Makefile | 26 +++++++++++++++++-- config/samples/kustomization.yaml | 3 ++- .../ravendb_v1_ravendbcluster_selfsigned.yaml | 2 +- 3 files changed, 27 insertions(+), 4 deletions(-) diff --git a/Makefile b/Makefile index a0316e6..f044e88 100644 --- a/Makefile +++ b/Makefile @@ -302,9 +302,31 @@ bundle: manifests kustomize operator-sdk ## Generate bundle manifests and metada $(KUSTOMIZE) build config/manifests | $(OPERATOR_SDK) generate bundle $(BUNDLE_GEN_FLAGS) $(OPERATOR_SDK) bundle validate ./bundle +# operator-sdk emits a flat bundle/ + bundle.Dockerfile at repo root; this repo ships +# bundle/ravendb-operator/$(VERSION)/. Relocate moves the output and fixes COPY paths. +.PHONY: bundle-relocate +bundle-relocate: ## Move flat bundle/ output into bundle/ravendb-operator/$(VERSION)/. + @if [ ! -d bundle/manifests ] || [ ! -f bundle.Dockerfile ]; then \ + echo "::error::No flat bundle/ output to relocate. Run 'make bundle' first."; exit 1; \ + fi + @mkdir -p bundle/ravendb-operator/$(VERSION) + @rm -rf bundle/ravendb-operator/$(VERSION)/manifests \ + bundle/ravendb-operator/$(VERSION)/metadata \ + bundle/ravendb-operator/$(VERSION)/tests \ + bundle/ravendb-operator/$(VERSION)/bundle.Dockerfile + @mv bundle/manifests bundle/ravendb-operator/$(VERSION)/manifests + @mv bundle/metadata bundle/ravendb-operator/$(VERSION)/metadata + @mv bundle/tests bundle/ravendb-operator/$(VERSION)/tests + @mv bundle.Dockerfile bundle/ravendb-operator/$(VERSION)/bundle.Dockerfile + @sed -i 's|^COPY bundle/|COPY ./|' bundle/ravendb-operator/$(VERSION)/bundle.Dockerfile + @echo "Relocated bundle to bundle/ravendb-operator/$(VERSION)/" + .PHONY: bundle-build -bundle-build: ## Build the bundle image. - docker build -f bundle.Dockerfile -t $(BUNDLE_IMG) . +bundle-build: ## Build bundle image (run bundle-relocate first). + docker build \ + -f bundle/ravendb-operator/$(VERSION)/bundle.Dockerfile \ + -t $(BUNDLE_IMG) \ + bundle/ravendb-operator/$(VERSION) .PHONY: bundle-push bundle-push: ## Push the bundle image. diff --git a/config/samples/kustomization.yaml b/config/samples/kustomization.yaml index 61d689f..c421894 100644 --- a/config/samples/kustomization.yaml +++ b/config/samples/kustomization.yaml @@ -1,4 +1,5 @@ ## Append samples of your project ## resources: -- ravendb_v1_ravendbcluster.yaml +- ravendb_v1_ravendbcluster_letsencrypt.yaml +- ravendb_v1_ravendbcluster_selfsigned.yaml # +kubebuilder:scaffold:manifestskustomizesamples diff --git a/config/samples/ravendb_v1_ravendbcluster_selfsigned.yaml b/config/samples/ravendb_v1_ravendbcluster_selfsigned.yaml index 3737951..cedf20c 100644 --- a/config/samples/ravendb_v1_ravendbcluster_selfsigned.yaml +++ b/config/samples/ravendb_v1_ravendbcluster_selfsigned.yaml @@ -3,7 +3,7 @@ kind: RavenDBCluster metadata: labels: app.kubernetes.io/name: ravendb-operator - name: ravendbcluster-sample + name: ravendbcluster-sample-selfsigned namespace: ravendb spec: nodes: From f11495f809613bea3b31cdf13410068fe8eec901 Mon Sep 17 00:00:00 2001 From: Gracjan Sadowicz Date: Tue, 5 May 2026 17:48:34 +0200 Subject: [PATCH 3/8] RavenDB-26535: regenerate OLM bundle from api/v1 markers --- api/v1/cluster_spec.go | 13 + api/v1/cluster_types.go | 20 +- api/v1/external_access_types.go | 5 + api/v1/node_types.go | 4 + api/v1/storage_types.go | 8 + .../ravendb-operator/1.0.0/bundle.Dockerfile | 5 +- ...avendb-operator.clusterserviceversion.yaml | 388 ++++++++++-------- .../1.0.0/metadata/annotations.yaml | 2 +- ...avendb-operator.clusterserviceversion.yaml | 137 ++++++- 9 files changed, 397 insertions(+), 185 deletions(-) diff --git a/api/v1/cluster_spec.go b/api/v1/cluster_spec.go index 95a8d97..4d659fb 100644 --- a/api/v1/cluster_spec.go +++ b/api/v1/cluster_spec.go @@ -19,49 +19,62 @@ package v1 type RavenDBClusterSpec struct { // +kubebuilder:validation:Required // +kubebuilder:validation:MinLength=1 + // +operator-sdk:csv:customresourcedefinitions:type=spec,displayName="RavenDB Image" Image string `json:"image"` // +kubebuilder:validation:Required // +kubebuilder:validation:Enum=Always;IfNotPresent + // +operator-sdk:csv:customresourcedefinitions:type=spec,displayName="Image Pull Policy" ImagePullPolicy string `json:"imagePullPolicy"` // +kubebuilder:validation:Required // +kubebuilder:validation:Enum=LetsEncrypt;None + // +operator-sdk:csv:customresourcedefinitions:type=spec,displayName="Security Mode" Mode ClusterMode `json:"mode"` // +kubebuilder:validation:Optional // +kubebuilder:validation:Pattern=`^[^@\s]+@[^@\s]+\.[^@\s]+$` + // +operator-sdk:csv:customresourcedefinitions:type=spec,displayName="Let's Encrypt Email" Email *string `json:"email,omitempty"` // +kubebuilder:validation:Required // +kubebuilder:validation:MinLength=1 + // +operator-sdk:csv:customresourcedefinitions:type=spec,displayName="License Secret" LicenseSecretRef string `json:"licenseSecretRef"` // +kubebuilder:validation:Optional + // +operator-sdk:csv:customresourcedefinitions:type=spec,displayName="Cluster Certificate Secret" ClusterCertSecretRef *string `json:"clusterCertSecretRef,omitempty"` // +kubebuilder:validation:Required // +kubebuilder:validation:MinLength=1 + // +operator-sdk:csv:customresourcedefinitions:type=spec,displayName="Cluster Domain" Domain string `json:"domain"` // +kubebuilder:validation:Required // +kubebuilder:validation:MinItems=1 + // +operator-sdk:csv:customresourcedefinitions:type=spec,displayName="Nodes" Nodes []RavenDBNode `json:"nodes,omitempty"` // +kubebuilder:validation:Optional + // +operator-sdk:csv:customresourcedefinitions:type=spec,displayName="Additional Environment Variables" Env map[string]string `json:"env,omitempty"` // +kubebuilder:validation:Optional + // +operator-sdk:csv:customresourcedefinitions:type=spec,displayName="External Access Configuration" ExternalAccessConfiguration *ExternalAccessConfiguration `json:"externalAccessConfiguration,omitempty"` // +kubebuilder:validation:Required + // +operator-sdk:csv:customresourcedefinitions:type=spec,displayName="Storage" StorageSpec StorageSpec `json:"storage"` // +kubebuilder:validation:Required // +kubebuilder:validation:MinLength=1 + // +operator-sdk:csv:customresourcedefinitions:type=spec,displayName="Client Certificate Secret" ClientCertSecretRef string `json:"clientCertSecretRef"` // +kubebuilder:validation:Optional + // +operator-sdk:csv:customresourcedefinitions:type=spec,displayName="CA Certificate Secret" CACertSecretRef *string `json:"caCertSecretRef,omitempty"` // // +kubebuilder:validation:Optional diff --git a/api/v1/cluster_types.go b/api/v1/cluster_types.go index 9adb2cc..3a84130 100644 --- a/api/v1/cluster_types.go +++ b/api/v1/cluster_types.go @@ -20,6 +20,7 @@ import metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" // +kubebuilder:object:root=true // +kubebuilder:subresource:status +// +operator-sdk:csv:customresourcedefinitions:displayName="Raven DBCluster",resources={{StatefulSet,v1,ravendb-},{Service,v1,ravendb-},{Ingress,v1,ravendb},{ServiceAccount,v1,ravendb-ops-sa},{Job,v1,ravendb-cluster-init},{ConfigMap,v1,ravendb-bootstrapper-hook},{ConfigMap,v1,ravendb-cert-hook}} type RavenDBCluster struct { metav1.TypeMeta `json:",inline"` @@ -39,9 +40,18 @@ type RavenDBClusterList struct { type RavenDBClusterStatus struct { // +kubebuilder:validation:Enum=Deploying;Running;Error - Phase ClusterPhase `json:"phase,omitempty"` - Message string `json:"message,omitempty"` - ObservedGeneration int64 `json:"observedGeneration,omitempty"` - Nodes []RavenDBNodeStatus `json:"nodes,omitempty"` - Conditions []metav1.Condition `json:"conditions,omitempty"` + // +operator-sdk:csv:customresourcedefinitions:type=status,displayName="Cluster Phase" + Phase ClusterPhase `json:"phase,omitempty"` + + // +operator-sdk:csv:customresourcedefinitions:type=status,displayName="Status Message" + Message string `json:"message,omitempty"` + + // +operator-sdk:csv:customresourcedefinitions:type=status,displayName="Observed Generation" + ObservedGeneration int64 `json:"observedGeneration,omitempty"` + + // +operator-sdk:csv:customresourcedefinitions:type=status,displayName="Node Statuses" + Nodes []RavenDBNodeStatus `json:"nodes,omitempty"` + + // +operator-sdk:csv:customresourcedefinitions:type=status,displayName="Cluster Conditions" + Conditions []metav1.Condition `json:"conditions,omitempty"` } diff --git a/api/v1/external_access_types.go b/api/v1/external_access_types.go index 820451b..4f90c5c 100644 --- a/api/v1/external_access_types.go +++ b/api/v1/external_access_types.go @@ -19,6 +19,7 @@ package v1 type ExternalAccessConfiguration struct { // +kubebuilder:validation:Required // +kubebuilder:validation:Enum=aws-nlb;azure-lb;ingress-controller + // +operator-sdk:csv:customresourcedefinitions:type=spec,displayName="External Access Type" Type ExternalAccessType `json:"type"` // +kubebuilder:validation:Optional @@ -34,6 +35,7 @@ type ExternalAccessConfiguration struct { type AWSExternalAccessContext struct { // +kubebuilder:validation:Required // +kubebuilder:validation:MinItems=1 + // +operator-sdk:csv:customresourcedefinitions:type=spec,displayName="AWS Node Mappings" NodeMappings []AWSNodeMapping `json:"nodeMappings"` } @@ -57,6 +59,7 @@ type AWSNodeMapping struct { type AzureExternalAccessContext struct { // +kubebuilder:validation:Required // +kubebuilder:validation:MinItems=1 + // +operator-sdk:csv:customresourcedefinitions:type=spec,displayName="Azure Node Mappings" NodeMappings []AzureNodeMapping `json:"nodeMappings"` } @@ -74,8 +77,10 @@ type IngressControllerContext struct { // +kubebuilder:validation:Enum=nginx;traefik;haproxy // +kubebuilder:validation:Required // +kubebuilder:validation:MinLength=1 + // +operator-sdk:csv:customresourcedefinitions:type=spec,displayName="Ingress Class" IngressClassName string `json:"ingressClassName"` // +kubebuilder:validation:Optional + // +operator-sdk:csv:customresourcedefinitions:type=spec,displayName="Ingress Annotations" AdditionalAnnotations map[string]string `json:"additionalAnnotations,omitempty"` } diff --git a/api/v1/node_types.go b/api/v1/node_types.go index a58aeee..403550e 100644 --- a/api/v1/node_types.go +++ b/api/v1/node_types.go @@ -22,17 +22,21 @@ type RavenDBNode struct { // +kubebuilder:validation:Required // +kubebuilder:validation:MinLength=1 // +kubebuilder:validation:MaxLength=4 + // +operator-sdk:csv:customresourcedefinitions:type=spec,displayName="Node Tag" Tag string `json:"tag"` // +kubebuilder:validation:Required // +kubebuilder:validation:MinLength=1 + // +operator-sdk:csv:customresourcedefinitions:type=spec,displayName="Public HTTPS URL" PublicServerUrl string `json:"publicServerUrl"` // +kubebuilder:validation:Required // +kubebuilder:validation:MinLength=1 + // +operator-sdk:csv:customresourcedefinitions:type=spec,displayName="Public TCP URL" PublicServerUrlTcp string `json:"publicServerUrlTcp"` // +kubebuilder:validation:Optional + // +operator-sdk:csv:customresourcedefinitions:type=spec,displayName="Node Certificate Secret" CertSecretRef *string `json:"certSecretRef,omitempty"` } diff --git a/api/v1/storage_types.go b/api/v1/storage_types.go index bc17c33..516c1c1 100644 --- a/api/v1/storage_types.go +++ b/api/v1/storage_types.go @@ -29,21 +29,26 @@ type StorageSpec struct { Logs *LogsSpec `json:"logs,omitempty"` // +kubebuilder:validation:Optional + // +operator-sdk:csv:customresourcedefinitions:type=spec,displayName="Additional Volumes" AdditionalVolumes *[]AdditionalVolume `json:"additionalVolumes,omitempty"` } type VolumeSpec struct { // +kubebuilder:validation:Required // +kubebuilder:validation:Pattern=`^\d+(Ei|Pi|Ti|Gi|Mi|Ki)$` + // +operator-sdk:csv:customresourcedefinitions:type=spec,displayName="Volume Size" Size string `json:"size"` // +kubebuilder:validation:Optional + // +operator-sdk:csv:customresourcedefinitions:type=spec,displayName="StorageClass" StorageClassName *string `json:"storageClassName,omitempty"` // +kubebuilder:validation:Optional + // +operator-sdk:csv:customresourcedefinitions:type=spec,displayName="Access Modes" AccessModes *[]string `json:"accessModes,omitempty"` // +kubebuilder:validation:Optional + // +operator-sdk:csv:customresourcedefinitions:type=spec,displayName="VolumeAttributesClass" VolumeAttributesClassName *string `json:"volumeAttributesClassName,omitempty"` } @@ -59,14 +64,17 @@ type LogSettings struct { VolumeSpec `json:",inline"` // +kubebuilder:validation:Optional + // +operator-sdk:csv:customresourcedefinitions:type=spec,displayName="Logs Path" Path *string `json:"path,omitempty"` } type AdditionalVolume struct { // +kubebuilder:validation:Required + // +operator-sdk:csv:customresourcedefinitions:type=spec,displayName="Additional Volume Name" Name string `json:"name"` // +kubebuilder:validation:Required + // +operator-sdk:csv:customresourcedefinitions:type=spec,displayName="Additional Volume Mount Path" MountPath string `json:"mountPath"` // +kubebuilder:validation:Optional diff --git a/bundle/ravendb-operator/1.0.0/bundle.Dockerfile b/bundle/ravendb-operator/1.0.0/bundle.Dockerfile index e7e2651..82c27de 100644 --- a/bundle/ravendb-operator/1.0.0/bundle.Dockerfile +++ b/bundle/ravendb-operator/1.0.0/bundle.Dockerfile @@ -5,8 +5,9 @@ LABEL operators.operatorframework.io.bundle.mediatype.v1=registry+v1 LABEL operators.operatorframework.io.bundle.manifests.v1=manifests/ LABEL operators.operatorframework.io.bundle.metadata.v1=metadata/ LABEL operators.operatorframework.io.bundle.package.v1=ravendb-operator -LABEL operators.operatorframework.io.bundle.channels.v1=alpha -LABEL operators.operatorframework.io.metrics.builder=operator-sdk-v1.42.0 +LABEL operators.operatorframework.io.bundle.channels.v1=stable +LABEL operators.operatorframework.io.bundle.channel.default.v1=stable +LABEL operators.operatorframework.io.metrics.builder=operator-sdk-v1.38.0 LABEL operators.operatorframework.io.metrics.mediatype.v1=metrics+v1 LABEL operators.operatorframework.io.metrics.project_layout=go.kubebuilder.io/v4 diff --git a/bundle/ravendb-operator/1.0.0/manifests/ravendb-operator.clusterserviceversion.yaml b/bundle/ravendb-operator/1.0.0/manifests/ravendb-operator.clusterserviceversion.yaml index b4286f2..139deee 100644 --- a/bundle/ravendb-operator/1.0.0/manifests/ravendb-operator.clusterserviceversion.yaml +++ b/bundle/ravendb-operator/1.0.0/manifests/ravendb-operator.clusterserviceversion.yaml @@ -2,55 +2,102 @@ apiVersion: operators.coreos.com/v1alpha1 kind: ClusterServiceVersion metadata: annotations: - alm-examples: '[ + alm-examples: |- + [ { "apiVersion": "ravendb.ravendb.io/v1", "kind": "RavenDBCluster", "metadata": { "labels": { - "app.kubernetes.io/name": "ravendb-operator", - "app.kubernetes.io/managed-by": "kustomize" - }, - "annotations": { - "ravendb.io/upgrade-ping-interval": "5s" + "app.kubernetes.io/name": "ravendb-operator" }, "name": "ravendbcluster-sample", "namespace": "ravendb" }, "spec": { + "clientCertSecretRef": "ravendb-client-cert", + "domain": "domain.development.run", + "email": "user@ravendb.net", + "externalAccessConfiguration": { + "ingressControllerContext": { + "ingressClassName": "nginx" + }, + "type": "ingress-controller" + }, + "image": "ravendb/ravendb:latest", + "imagePullPolicy": "IfNotPresent", + "licenseSecretRef": "ravendb-license", + "mode": "LetsEncrypt", "nodes": [ { - "tag": "a", + "certSecretRef": "ravendb-certs-a", "publicServerUrl": "https://a.domain.development.run:443", "publicServerUrlTcp": "tcp://a-tcp.domain.development.run:443", - "certSecretRef": "ravendb-certs-a" + "tag": "a" }, { - "tag": "b", + "certSecretRef": "ravendb-certs-b", "publicServerUrl": "https://b.domain.development.run:443", "publicServerUrlTcp": "tcp://b-tcp.domain.development.run:443", - "certSecretRef": "ravendb-certs-b" + "tag": "b" }, { - "tag": "c", + "certSecretRef": "ravendb-certs-c", "publicServerUrl": "https://c.domain.development.run:443", "publicServerUrlTcp": "tcp://c-tcp.domain.development.run:443", - "certSecretRef": "ravendb-certs-c" + "tag": "c" } ], - "image": "ravendb/ravendb:latest", - "imagePullPolicy": "IfNotPresent", - "mode": "LetsEncrypt", - "email": "user@ravendb.net", - "licenseSecretRef": "ravendb-license", - "domain": "domain.development.run", + "storage": { + "data": { + "size": "10Gi", + "storageClassName": "local-path" + } + } + } + }, + { + "apiVersion": "ravendb.ravendb.io/v1", + "kind": "RavenDBCluster", + "metadata": { + "labels": { + "app.kubernetes.io/name": "ravendb-operator" + }, + "name": "ravendbcluster-sample-selfsigned", + "namespace": "ravendb" + }, + "spec": { + "caCertSecretRef": "ravendb-ca-cert", "clientCertSecretRef": "ravendb-client-cert", + "clusterCertSecretRef": "ravendb-cert", + "domain": "domainselfsigned.development.run", "externalAccessConfiguration": { - "type": "ingress-controller", "ingressControllerContext": { "ingressClassName": "nginx" - } + }, + "type": "ingress-controller" }, + "image": "ravendb/ravendb:latest", + "imagePullPolicy": "IfNotPresent", + "licenseSecretRef": "ravendb-license", + "mode": "None", + "nodes": [ + { + "publicServerUrl": "https://a.domainselfsigned.development.run:443", + "publicServerUrlTcp": "tcp://a-tcp.domainselfsigned.development.run:443", + "tag": "a" + }, + { + "publicServerUrl": "https://b.domainselfsigned.development.run:443", + "publicServerUrlTcp": "tcp://b-tcp.domainselfsigned.development.run:443", + "tag": "b" + }, + { + "publicServerUrl": "https://c.domainselfsigned.development.run:443", + "publicServerUrlTcp": "tcp://c-tcp.domainselfsigned.development.run:443", + "tag": "c" + } + ], "storage": { "data": { "size": "10Gi", @@ -59,13 +106,12 @@ metadata: } } } - ]' + ] capabilities: Basic Install categories: Database - createdAt: "2025-11-19T10:56:14Z" - operators.operatorframework.io/builder: operator-sdk-v1.42.0 + createdAt: "2026-05-05T14:42:56Z" + operators.operatorframework.io/builder: operator-sdk-v1.38.0 operators.operatorframework.io/project_layout: go.kubebuilder.io/v4 - containerImage: docker.io/ravendb/ravendb-operator:1.0.0 name: ravendb-operator.v1.0.0 namespace: placeholder spec: @@ -75,152 +121,121 @@ spec: - displayName: Raven DBCluster kind: RavenDBCluster name: ravendbclusters.ravendb.ravendb.io - version: v1 - description: Represents a RavenDB cluster resources: - - kind: StatefulSet - name: ravendb- - version: v1 - - kind: Service - name: ravendb- - version: v1 - - kind: Ingress - name: ravendb - version: v1 - - kind: ServiceAccount - name: ravendb-ops-sa - version: v1 - - kind: Job - name: ravendb-cluster-init - version: v1 - - kind: ConfigMap - name: ravendb-bootstrapper-hook - version: v1 - - kind: ConfigMap - name: ravendb-cert-hook - version: v1 + - kind: Ingress + name: ravendb + version: v1 + - kind: Service + name: ravendb- + version: v1 + - kind: StatefulSet + name: ravendb- + version: v1 + - kind: ConfigMap + name: ravendb-bootstrapper-hook + version: v1 + - kind: ConfigMap + name: ravendb-cert-hook + version: v1 + - kind: Job + name: ravendb-cluster-init + version: v1 + - kind: ServiceAccount + name: ravendb-ops-sa + version: v1 specDescriptors: - - path: image - displayName: RavenDB Image - description: Container image to use for RavenDB nodes. - - path: imagePullPolicy - displayName: Image Pull Policy - description: Image pull policy for the RavenDB container. - - path: mode - displayName: Security Mode - description: Cluster security mode (LetsEncrypt or None(self-signed)). - - path: email - displayName: Let's Encrypt Email - description: Email used for Let's Encrypt certificate registration when mode is LetsEncrypt. - - path: licenseSecretRef - displayName: License Secret - description: Name of the Secret containing the RavenDB license.json. - - path: clusterCertSecretRef - displayName: Cluster Certificate Secret - description: Secret containing the shared cluster server certificate used in self-signed mode. - - path: clientCertSecretRef - displayName: Client Certificate Secret - description: Secret containing the admin client certificate (PFX) used by the bootstrapper. - - path: caCertSecretRef - displayName: CA Certificate Secret - description: Secret containing the CA certificate in self-signed mode. - - path: domain - displayName: Cluster Domain - description: DNS suffix used for per-node hostnames (e.g. a., a-tcp.). - - path: nodes - displayName: Nodes - description: List of RavenDB nodes with tags and public URLs. - - path: nodes[].tag - displayName: Node Tag - description: Logical RavenDB node tag (1–4 characters) used for naming and routing. - - path: nodes[].publicServerUrl - displayName: Public HTTPS URL - description: Public HTTPS URL of the node (e.g. https://a.example.com). - - path: nodes[].publicServerUrlTcp - displayName: Public TCP URL - description: Public TCP URL of the node (e.g. tcp://a-tcp.example.com:38888). - - path: nodes[].certSecretRef - displayName: Node Certificate Secret - description: per-node Secret containing the server certificate; used in LetsEncrypt mode. - - path: env - displayName: Additional Environment Variables - description: Extra environment variables to inject into the RavenDB container. - - path: externalAccessConfiguration - displayName: External Access Configuration - description: Provider-specific configuration for exposing nodes externally (AWS NLB, Azure LB, or Ingress controller). - - path: externalAccessConfiguration.type - displayName: External Access Type - description: Type of external access (aws-nlb, azure-lb, ingress-controller). - - path: externalAccessConfiguration.awsExternalAccessContext.nodeMappings - displayName: AWS Node Mappings - description: AWS NLB configuration per node tag, including EIP allocation and subnet. - - path: externalAccessConfiguration.azureExternalAccessContext.nodeMappings - displayName: Azure Node Mappings - description: Azure LoadBalancer IP assignment per node tag. - - path: externalAccessConfiguration.ingressControllerContext.ingressClassName - displayName: Ingress Class - description: IngressClass used when exposing nodes via Ingress (nginx, traefik, haproxy). - - path: externalAccessConfiguration.ingressControllerContext.additionalAnnotations - displayName: Ingress Annotations - description: Additional annotations applied to the generated Ingress resource. - - path: storage - displayName: Storage - description: Storage configuration for data, logs, and additional volumes. - - path: storage.data.size - displayName: Data Volume Size - description: Persistent volume size for the main RavenDB data directory. - - path: storage.data.storageClassName - displayName: Data StorageClass - description: StorageClass used for the data volume. - - path: storage.data.accessModes - displayName: Data Access Modes - description: PVC access modes for the data volume (e.g. ReadWriteOnce). - - path: storage.data.volumeAttributesClassName - displayName: Data VolumeAttributesClass - description: Optional VolumeAttributesClass for the data PVC. - - path: storage.logs.ravendb.size - displayName: Logs Volume Size - description: Persistent volume size for RavenDB logs. - - path: storage.logs.ravendb.path - displayName: Logs Path - description: Mount path for RavenDB logs inside the container. - - path: storage.logs.audit.size - displayName: Audit Logs Volume Size - description: Persistent volume size for audit logs. - - path: storage.logs.audit.path - displayName: Audit Logs Path - description: Mount path for audit logs inside the container. - - path: storage.additionalVolumes - displayName: Additional Volumes - description: Extra volumes mounted into the RavenDB pod from ConfigMaps, Secrets, or PVCs. - - path: storage.additionalVolumes[].name - displayName: Additional Volume Name - description: Name of the additional volume. - - path: storage.additionalVolumes[].mountPath - displayName: Additional Volume Mount Path - description: Path at which the additional volume is mounted. + - displayName: CA Certificate Secret + path: caCertSecretRef + - displayName: Client Certificate Secret + path: clientCertSecretRef + - displayName: Cluster Certificate Secret + path: clusterCertSecretRef + - displayName: Cluster Domain + path: domain + - displayName: Let's Encrypt Email + path: email + - displayName: Additional Environment Variables + path: env + - displayName: External Access Configuration + path: externalAccessConfiguration + - displayName: AWS Node Mappings + path: externalAccessConfiguration.awsExternalAccessContext.nodeMappings + - displayName: Azure Node Mappings + path: externalAccessConfiguration.azureExternalAccessContext.nodeMappings + - displayName: Ingress Annotations + path: externalAccessConfiguration.ingressControllerContext.additionalAnnotations + - displayName: Ingress Class + path: externalAccessConfiguration.ingressControllerContext.ingressClassName + - displayName: External Access Type + path: externalAccessConfiguration.type + - displayName: RavenDB Image + path: image + - displayName: Image Pull Policy + path: imagePullPolicy + - displayName: License Secret + path: licenseSecretRef + - displayName: Security Mode + path: mode + - displayName: Nodes + path: nodes + - displayName: Node Certificate Secret + path: nodes[0].certSecretRef + - displayName: Public HTTPS URL + path: nodes[0].publicServerUrl + - displayName: Public TCP URL + path: nodes[0].publicServerUrlTcp + - displayName: Node Tag + path: nodes[0].tag + - displayName: Storage + path: storage + - displayName: Additional Volumes + path: storage.additionalVolumes + - displayName: Additional Volume Mount Path + path: storage.additionalVolumes.mountPath + - displayName: Additional Volume Name + path: storage.additionalVolumes.name + - displayName: Access Modes + path: storage.data.accessModes + - displayName: Volume Size + path: storage.data.size + - displayName: StorageClass + path: storage.data.storageClassName + - displayName: VolumeAttributesClass + path: storage.data.volumeAttributesClassName + - displayName: Access Modes + path: storage.logs.audit.accessModes + - displayName: Logs Path + path: storage.logs.audit.path + - displayName: Volume Size + path: storage.logs.audit.size + - displayName: StorageClass + path: storage.logs.audit.storageClassName + - displayName: VolumeAttributesClass + path: storage.logs.audit.volumeAttributesClassName + - displayName: Access Modes + path: storage.logs.ravendb.accessModes + - displayName: Logs Path + path: storage.logs.ravendb.path + - displayName: Volume Size + path: storage.logs.ravendb.size + - displayName: StorageClass + path: storage.logs.ravendb.storageClassName + - displayName: VolumeAttributesClass + path: storage.logs.ravendb.volumeAttributesClassName statusDescriptors: - - path: phase - displayName: Cluster Phase - description: High-level phase of the cluster lifecycle (Deploying, Running, Error). - - path: conditions - displayName: Cluster Conditions - description: Detailed status conditions such as Ready, Progressing, Degraded. - - path: message - displayName: Status Message - description: Human-readable summary of the current cluster state. - - path: nodes - displayName: Node Statuses - description: Per-node status including phase, last attempted image and last error. - - path: observedGeneration - displayName: Observed Generation - description: Last spec generation observed by the controller. - description: > - RavenDB Operator automates the lifecycle of secure RavenDB clusters on Kubernetes, - including cluster bootstrap, certificate management, storage provisioning, and - rolling upgrades. It reconciles RavenDBCluster custom resources into StatefulSets, - Services, Ingresses, and Jobs, and continuously evaluates cluster health and - readiness so you can operate RavenDB in a Kubernetes-native way. + - displayName: Cluster Conditions + path: conditions + - displayName: Status Message + path: message + - displayName: Node Statuses + path: nodes + - displayName: Observed Generation + path: observedGeneration + - displayName: Cluster Phase + path: phase + version: v1 + description: | + RavenDB Operator automates the lifecycle of secure RavenDB clusters on Kubernetes, including cluster bootstrap, certificate management, storage provisioning, and rolling upgrades. It reconciles RavenDBCluster custom resources into StatefulSets, Services, Ingresses, and Jobs, and continuously evaluates cluster health and readiness so you can operate RavenDB in a Kubernetes-native way. displayName: RavenDB Operator icon: - base64data: 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 @@ -317,7 +332,7 @@ spec: - patch - update - apiGroups: - - "" + - events.k8s.io resources: - events verbs: @@ -374,7 +389,38 @@ spec: - update - patch - delete - serviceAccountName: ravendb-operator-sa + - apiGroups: + - "" + resources: + - pods/exec + verbs: + - create + - apiGroups: + - "" + resources: + - serviceaccounts + verbs: + - get + - list + - watch + - create + - update + - patch + - delete + - apiGroups: + - rbac.authorization.k8s.io + resources: + - roles + - rolebindings + verbs: + - get + - list + - watch + - create + - update + - patch + - delete + serviceAccountName: ravendb-operator-ravendb-operator-sa deployments: - label: app: ravendb-operator @@ -435,7 +481,7 @@ spec: readOnly: true securityContext: runAsNonRoot: true - serviceAccountName: ravendb-operator-sa + serviceAccountName: ravendb-operator-ravendb-operator-sa terminationGracePeriodSeconds: 10 volumes: - name: cert @@ -475,7 +521,7 @@ spec: verbs: - create - patch - serviceAccountName: ravendb-operator-sa + serviceAccountName: ravendb-operator-ravendb-operator-sa strategy: deployment installModes: - supported: false diff --git a/bundle/ravendb-operator/1.0.0/metadata/annotations.yaml b/bundle/ravendb-operator/1.0.0/metadata/annotations.yaml index a75bd93..49a1a32 100644 --- a/bundle/ravendb-operator/1.0.0/metadata/annotations.yaml +++ b/bundle/ravendb-operator/1.0.0/metadata/annotations.yaml @@ -6,7 +6,7 @@ annotations: operators.operatorframework.io.bundle.package.v1: ravendb-operator operators.operatorframework.io.bundle.channels.v1: stable operators.operatorframework.io.bundle.channel.default.v1: stable - operators.operatorframework.io.metrics.builder: operator-sdk-v1.42.0 + operators.operatorframework.io.metrics.builder: operator-sdk-v1.38.0 operators.operatorframework.io.metrics.mediatype.v1: metrics+v1 operators.operatorframework.io.metrics.project_layout: go.kubebuilder.io/v4 diff --git a/config/manifests/bases/ravendb-operator.clusterserviceversion.yaml b/config/manifests/bases/ravendb-operator.clusterserviceversion.yaml index 7249bd6..ad57d24 100644 --- a/config/manifests/bases/ravendb-operator.clusterserviceversion.yaml +++ b/config/manifests/bases/ravendb-operator.clusterserviceversion.yaml @@ -4,6 +4,7 @@ metadata: annotations: alm-examples: '[]' capabilities: Basic Install + categories: Database name: ravendb-operator.v0.0.0 namespace: placeholder spec: @@ -13,12 +14,125 @@ spec: - displayName: Raven DBCluster kind: RavenDBCluster name: ravendbclusters.ravendb.ravendb.io + resources: + - kind: Ingress + name: ravendb + version: v1 + - kind: Service + name: ravendb- + version: v1 + - kind: StatefulSet + name: ravendb- + version: v1 + - kind: ConfigMap + name: ravendb-bootstrapper-hook + version: v1 + - kind: ConfigMap + name: ravendb-cert-hook + version: v1 + - kind: Job + name: ravendb-cluster-init + version: v1 + - kind: ServiceAccount + name: ravendb-ops-sa + version: v1 + specDescriptors: + - displayName: CA Certificate Secret + path: caCertSecretRef + - displayName: Client Certificate Secret + path: clientCertSecretRef + - displayName: Cluster Certificate Secret + path: clusterCertSecretRef + - displayName: Cluster Domain + path: domain + - displayName: Let's Encrypt Email + path: email + - displayName: Additional Environment Variables + path: env + - displayName: External Access Configuration + path: externalAccessConfiguration + - displayName: AWS Node Mappings + path: externalAccessConfiguration.awsExternalAccessContext.nodeMappings + - displayName: Azure Node Mappings + path: externalAccessConfiguration.azureExternalAccessContext.nodeMappings + - displayName: Ingress Annotations + path: externalAccessConfiguration.ingressControllerContext.additionalAnnotations + - displayName: Ingress Class + path: externalAccessConfiguration.ingressControllerContext.ingressClassName + - displayName: External Access Type + path: externalAccessConfiguration.type + - displayName: RavenDB Image + path: image + - displayName: Image Pull Policy + path: imagePullPolicy + - displayName: License Secret + path: licenseSecretRef + - displayName: Security Mode + path: mode + - displayName: Nodes + path: nodes + - displayName: Node Certificate Secret + path: nodes[0].certSecretRef + - displayName: Public HTTPS URL + path: nodes[0].publicServerUrl + - displayName: Public TCP URL + path: nodes[0].publicServerUrlTcp + - displayName: Node Tag + path: nodes[0].tag + - displayName: Storage + path: storage + - displayName: Additional Volumes + path: storage.additionalVolumes + - displayName: Additional Volume Mount Path + path: storage.additionalVolumes.mountPath + - displayName: Additional Volume Name + path: storage.additionalVolumes.name + - displayName: Access Modes + path: storage.data.accessModes + - displayName: Volume Size + path: storage.data.size + - displayName: StorageClass + path: storage.data.storageClassName + - displayName: VolumeAttributesClass + path: storage.data.volumeAttributesClassName + - displayName: Access Modes + path: storage.logs.audit.accessModes + - displayName: Logs Path + path: storage.logs.audit.path + - displayName: Volume Size + path: storage.logs.audit.size + - displayName: StorageClass + path: storage.logs.audit.storageClassName + - displayName: VolumeAttributesClass + path: storage.logs.audit.volumeAttributesClassName + - displayName: Access Modes + path: storage.logs.ravendb.accessModes + - displayName: Logs Path + path: storage.logs.ravendb.path + - displayName: Volume Size + path: storage.logs.ravendb.size + - displayName: StorageClass + path: storage.logs.ravendb.storageClassName + - displayName: VolumeAttributesClass + path: storage.logs.ravendb.volumeAttributesClassName + statusDescriptors: + - displayName: Cluster Conditions + path: conditions + - displayName: Status Message + path: message + - displayName: Node Statuses + path: nodes + - displayName: Observed Generation + path: observedGeneration + - displayName: Cluster Phase + path: phase version: v1 - description: ravendb-operator - displayName: ravendb-operator + description: | + RavenDB Operator automates the lifecycle of secure RavenDB clusters on Kubernetes, including cluster bootstrap, certificate management, storage provisioning, and rolling upgrades. It reconciles RavenDBCluster custom resources into StatefulSets, Services, Ingresses, and Jobs, and continuously evaluates cluster health and readiness so you can operate RavenDB in a Kubernetes-native way. + displayName: RavenDB Operator icon: - - base64data: "" - mediatype: "" + - base64data: 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 + mediatype: image/png install: spec: deployments: null @@ -33,11 +147,22 @@ spec: - supported: true type: AllNamespaces keywords: - - ravendb-operator + - database + - nosql + - document-database + - nosql-data-storage links: - name: Ravendb Operator - url: https://ravendb-operator.domain + url: https://github.com/ravendb/ravendb-operator + - name: RavenDB Documentation + url: https://ravendb.net/docs + maintainers: + - email: omer.ratsaby@ravendb.net + name: Omer Ratsaby (thegoldenplatypus) + - email: support@ravendb.net + name: RavenDB Team maturity: alpha + minKubeVersion: 1.27.0 provider: name: ravendb-operator version: 0.0.0 From 9f9efaa0b6163a6e341f5cc6f1cdd2d6b0dc7e4c Mon Sep 17 00:00:00 2001 From: Gracjan Sadowicz Date: Tue, 5 May 2026 17:48:55 +0200 Subject: [PATCH 4/8] RavenDB-26535: switch prepare-release to make bundle --- .github/workflows/prepare-release.yml | 31 ++++++++++++++++++++------- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/.github/workflows/prepare-release.yml b/.github/workflows/prepare-release.yml index 177b7a7..d6c81a5 100644 --- a/.github/workflows/prepare-release.yml +++ b/.github/workflows/prepare-release.yml @@ -42,6 +42,11 @@ jobs: - uses: azure/setup-helm@v4 + - uses: actions/setup-go@v5 + with: + go-version: '1.24.3' + cache: true + - name: Read previous version from Makefile id: prev run: | @@ -62,16 +67,26 @@ jobs: sed -i "s/^appVersion:.*/appVersion: \"${VERSION}\"/" helm/chart/Chart.yaml sed -i "/^| [0-9]\+\.[0-9]\+\.[0-9]\+ /c\\| ${VERSION} | :white_check_mark: |" SECURITY.md - - name: Update OLM bundle + - name: Regenerate OLM bundle env: OLD: ${{ steps.prev.outputs.old }} run: | - mv "bundle/ravendb-operator/${OLD}" "bundle/ravendb-operator/${VERSION}" + make bundle \ + VERSION="${VERSION}" \ + IMG="docker.io/ravendb/ravendb-operator:${VERSION}" \ + CHANNELS=stable \ + DEFAULT_CHANNEL=stable + make bundle-relocate VERSION="${VERSION}" + if [ "${OLD}" != "${VERSION}" ]; then + rm -rf "bundle/ravendb-operator/${OLD}" + fi CSV="bundle/ravendb-operator/${VERSION}/manifests/ravendb-operator.clusterserviceversion.yaml" - sed -i "s|ravendb-operator:${OLD}|ravendb-operator:${VERSION}|g" "$CSV" - sed -i "s|ravendb-operator.v${OLD}|ravendb-operator.v${VERSION}|g" "$CSV" - sed -i "s|^ version: ${OLD}\$| version: ${VERSION}|" "$CSV" - sed -i "s|createdAt: \".*\"|createdAt: \"$(date -u +%Y-%m-%dT%H:%M:%SZ)\"|" "$CSV" + # operator-sdk omits containerImage; OperatorHub uses it for catalog display. + sed -i "/^ capabilities:/i\\ containerImage: docker.io/ravendb/ravendb-operator:${VERSION}" "$CSV" + # operator-sdk emits nodes[0].field; OLM convention is nodes[]. (descriptor lines only). + sed -i -E '/^ path: /s|\[0\]|[]|g' "$CSV" + # make bundle rewrites the kustomization image tag — revert to keep :latest tracked. + git checkout -- config/manager/kustomization.yaml - name: Package helm chart run: | @@ -110,7 +125,7 @@ jobs: git config user.name "github-actions[bot]" git config user.email "41898282+github-actions[bot]@users.noreply.github.com" git checkout -b "$BRANCH" - git add Makefile helm/ bundle/ SECURITY.md + git add Makefile helm/ bundle/ SECURITY.md config/ git commit -m "release: v${VERSION}" git push origin "$BRANCH" gh pr create \ @@ -146,7 +161,7 @@ jobs: echo "===============" echo "Would have:" echo " - Created branch: release/v${VERSION}" - echo " - Committed: Makefile, helm/, bundle/, SECURITY.md" + echo " - Committed: Makefile, helm/, bundle/, SECURITY.md, config/" echo " - Opened PR: 'release: v${VERSION}' with label release:auto" echo "" echo "Working-tree diff vs HEAD:" From f45c6f5f37a3d29c8e07a645f203b14b82d4c730 Mon Sep 17 00:00:00 2001 From: Gracjan Sadowicz Date: Tue, 5 May 2026 17:49:28 +0200 Subject: [PATCH 5/8] RavenDB-26535: gate prepare-release on config/samples being current --- .github/workflows/prepare-release.yml | 4 +++ Makefile | 4 +++ test/samples/samples_test.go | 41 +++++++++++++++++++++++++++ 3 files changed, 49 insertions(+) create mode 100644 test/samples/samples_test.go diff --git a/.github/workflows/prepare-release.yml b/.github/workflows/prepare-release.yml index d6c81a5..f1ccde3 100644 --- a/.github/workflows/prepare-release.yml +++ b/.github/workflows/prepare-release.yml @@ -47,6 +47,10 @@ jobs: go-version: '1.24.3' cache: true + # Stale samples ship a broken "Try sample CR" form to OperatorHub. Gate first. + - name: Verify config/samples are current + run: make verify-samples + - name: Read previous version from Makefile id: prev run: | diff --git a/Makefile b/Makefile index f044e88..5aa66c7 100644 --- a/Makefile +++ b/Makefile @@ -120,6 +120,10 @@ vet: ## Run go vet against code. test: manifests generate fmt vet envtest ## Run tests. KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) --bin-dir $(LOCALBIN) -p path)" go test $$(go list ./... | grep -v /e2e) -coverprofile cover.out +.PHONY: verify-samples +verify-samples: ## Strict-unmarshal config/samples/* against api/v1 (no envtest). + go test -count=1 ./test/samples/... + # Utilize Kind or modify the e2e tests to load the image locally, enabling compatibility with other vendors. .PHONY: test-e2e # Run the e2e tests against a Kind k8s instance that is spun up. test-e2e: diff --git a/test/samples/samples_test.go b/test/samples/samples_test.go new file mode 100644 index 0000000..a0f4c02 --- /dev/null +++ b/test/samples/samples_test.go @@ -0,0 +1,41 @@ +package samples_test + +import ( + "os" + "path/filepath" + "runtime" + "testing" + + v1 "ravendb-operator/api/v1" + + "sigs.k8s.io/yaml" +) + +// Strict-unmarshal config/samples/* against api/v1. Samples ride into the CSV's +// alm-examples; a renamed CRD field would silently ship a broken "Try sample CR" +// form. Own package keeps it envtest-free so prepare-release.yml can gate on it. +func TestConfigSamplesValid(t *testing.T) { + _, thisFile, _, _ := runtime.Caller(0) + repoRoot := filepath.Join(filepath.Dir(thisFile), "..", "..") + pattern := filepath.Join(repoRoot, "config/samples/ravendb_v1_ravendbcluster*.yaml") + + matches, err := filepath.Glob(pattern) + if err != nil { + t.Fatal(err) + } + if len(matches) == 0 { + t.Fatalf("no samples matched %q", pattern) + } + for _, p := range matches { + t.Run(filepath.Base(p), func(t *testing.T) { + data, err := os.ReadFile(p) + if err != nil { + t.Fatal(err) + } + var c v1.RavenDBCluster + if err := yaml.UnmarshalStrict(data, &c); err != nil { + t.Errorf("strict unmarshal failed: %v", err) + } + }) + } +} From 51c951b552c20e26091c751699f31614ba3a2aa1 Mon Sep 17 00:00:00 2001 From: Gracjan Sadowicz Date: Tue, 5 May 2026 17:58:37 +0200 Subject: [PATCH 6/8] RavenDB-26535: switch release PR to peter-evans; drop SECURITY.md from auto-bumps --- .github/workflows/prepare-release.yml | 44 ++++++++++++--------------- 1 file changed, 19 insertions(+), 25 deletions(-) diff --git a/.github/workflows/prepare-release.yml b/.github/workflows/prepare-release.yml index f1ccde3..5e37435 100644 --- a/.github/workflows/prepare-release.yml +++ b/.github/workflows/prepare-release.yml @@ -69,7 +69,7 @@ jobs: sed -i "s/^VERSION ?=.*/VERSION ?= ${VERSION}/" Makefile sed -i "s/^version:.*/version: ${VERSION}/" helm/chart/Chart.yaml sed -i "s/^appVersion:.*/appVersion: \"${VERSION}\"/" helm/chart/Chart.yaml - sed -i "/^| [0-9]\+\.[0-9]\+\.[0-9]\+ /c\\| ${VERSION} | :white_check_mark: |" SECURITY.md + # SECURITY.md is bumped manually after the GitHub Release is published. - name: Regenerate OLM bundle env: @@ -122,29 +122,23 @@ jobs: - name: Open release PR if: ${{ !inputs.dry_run }} - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - BRANCH="release/v${VERSION}" - git config user.name "github-actions[bot]" - git config user.email "41898282+github-actions[bot]@users.noreply.github.com" - git checkout -b "$BRANCH" - git add Makefile helm/ bundle/ SECURITY.md config/ - git commit -m "release: v${VERSION}" - git push origin "$BRANCH" - gh pr create \ - --title "release: v${VERSION}" \ - --base main \ - --head "$BRANCH" \ - --label "release:auto" \ - --body "Auto-generated bumps for v${VERSION}. - - On merge with the **release:auto** label still attached, the Release workflow fires automatically: - builds and pushes the multi-arch image, runs a kind-based smoke test, tags v${VERSION}, and - publishes a GitHub Release with \`install.yaml\` attached. - - Remove the **release:auto** label before merging to delay publishing. You can fire publishing - later by running the Release workflow manually with version \`${VERSION}\`." + # peter-evans/create-pull-request v8.1.1 — pinned to SHA per supply-chain best practice. + # Auto-stages modified files, commits as github-actions[bot], pushes, opens (or + # updates) the PR. Idempotent: re-running for the same VERSION updates in place. + uses: peter-evans/create-pull-request@5f6978faf089d4d20b00c7766989d076bb2fc7f1 + with: + branch: release/v${{ env.VERSION }} + base: main + title: "release: v${{ env.VERSION }}" + commit-message: "release: v${{ env.VERSION }}" + labels: release:auto + body: | + Auto-generated bumps for v${{ env.VERSION }}. + + On merge with the **release:auto** label still attached, the Release workflow fires: + multi-arch image push, kind smoke test, tag, GitHub Release with `install.yaml`. + Remove the label before merging to delay publishing. SECURITY.md is bumped + manually after publishing. # ─── Dry-run-only outputs ─── @@ -165,7 +159,7 @@ jobs: echo "===============" echo "Would have:" echo " - Created branch: release/v${VERSION}" - echo " - Committed: Makefile, helm/, bundle/, SECURITY.md, config/" + echo " - Committed: Makefile, helm/, bundle/, config/" echo " - Opened PR: 'release: v${VERSION}' with label release:auto" echo "" echo "Working-tree diff vs HEAD:" From 2a5fe50f7d1664e6a8c3166325b8aa88167a588a Mon Sep 17 00:00:00 2001 From: Gracjan Sadowicz Date: Tue, 5 May 2026 18:40:03 +0200 Subject: [PATCH 7/8] RavenDB-26535: add pre-release-report skill --- .claude/skills/pre-release-report.md | 90 ++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 .claude/skills/pre-release-report.md diff --git a/.claude/skills/pre-release-report.md b/.claude/skills/pre-release-report.md new file mode 100644 index 0000000..63ac8fa --- /dev/null +++ b/.claude/skills/pre-release-report.md @@ -0,0 +1,90 @@ +--- +name: pre-release-report +description: Run if user wants to prepare for release. Six-pass audit of the repo before cutting a release. Passes 1-2 (CSV markers, samples) propose changes for user approval and commit them. Passes 3-6 (breaking changes, Helm parity, reviewers, dependencies) report findings; the user directs follow-up. +--- + +Six-pass pre-release audit. Run them in order. End with a structured summary. + +Use the current branch's ticket prefix (e.g., `RavenDB-26535`) for any commit subjects. Determine it from `git branch --show-current`. + +## Pass 1 — CSV markers (action: propose → confirm → apply → commit) + +Walk `api/v1/*.go`. For every exported field with a JSON tag, decide whether it needs a `+operator-sdk:csv:customresourcedefinitions:` marker. + +**Skip:** +- `Spec`, `Status`, `Items`, `TypeMeta`, `ObjectMeta`, `ListMeta` — structural plumbing. +- Embedded k8s core types (`corev1.VolumeSource`, `corev1.EnvVar`, `corev1.SecretReference`). +- Pure container fields whose descriptors come from nested children (e.g., `Data`, `Logs`, `RavenDB`, `Audit` in `StorageSpec` — descriptors live on `Size`, `StorageClassName`, etc., reached via `storage.data.size` etc.). + +**Mark:** spec fields → `type=spec`, status fields → `type=status`. Format: +`// +operator-sdk:csv:customresourcedefinitions:type=,displayName=""` + +DisplayName: CamelCase → "Space-Separated Words"; keep acronyms (URL, ID, IP, EIP, CA, TCP, DNS); prefer domain-clear (`CertSecretRef` → "Certificate Secret"). Match the style of existing markers in the same file. Do NOT add `description="..."` — operator-sdk's parser rejects it at this level. + +Show proposals as `file:line field → marker`, ask for confirmation/overrides, apply, run `make verify-samples` and (if available) `make bundle` in WSL to verify operator-sdk parses everything. Commit: `: refresh CSV markers in api/v1`. + +## Pass 2 — Samples (action: validate + propose updates → confirm → apply → commit) + +First, run `make verify-samples`. If it fails, the strict-unmarshal error names the file/field — fix and re-run. + +Then, judgment pass: diff `api/v1/` spec types against the most recent release tag (`git describe --tags --abbrev=0`). + +For incremental additions (new optional fields, new array entries on existing types), ask: "Should this field appear in `ravendb_v1_ravendbcluster_letsencrypt.yaml`, `..._selfsigned.yaml`, both, or neither?" + +For changes that introduce a fundamentally distinct use case — a new `mode:` value, a new external-access provider type, a new storage backend, a new auth scheme — propose adding a NEW sample file: `ravendb_v1_ravendbcluster_.yaml`. The user may also request a new sample even for incremental changes if they want a separate example. Register any new sample in `config/samples/kustomization.yaml` so operator-sdk folds it into alm-examples. + +Apply approved additions/files. Re-run `make verify-samples`. Commit: `: update config/samples for new spec fields`. + +## Pass 3 — CRD breaking-change review (report only) + +Render the CRD at the most recent release tag and at HEAD, then diff. Render with `kustomize build config/crd | yq` (or operator-sdk equivalent). Surface: + +| Change | Severity | Why it matters | +|---|---|---| +| Removed field | High | Breaks users who set it | +| Type change | High | Wire-incompatible | +| New required field, no default | High | Breaks existing CRs on upgrade | +| Narrowed enum or smaller min/max | Medium | Breaks users at the boundary | +| Changed default | Medium | Silent behavior change | + +Output severity, field path, before → after. Do NOT auto-fix — these are deliberate decisions (keep with a major version bump, document migration, or revert). + +## Pass 4 — Helm chart parity (report only) + +`helm/chart/templates/` and `config/` are maintained in parallel. Diff them across: +- **RBAC**: verbs/resources/apiGroups in `config/rbac/role.yaml` vs `helm/chart/templates/rbac.yaml` (or equivalent). +- **Manager Deployment**: env vars, volume mounts, args, resource limits in `config/manager/manager.yaml` vs `helm/chart/templates/deployment.yaml`. +- **Webhook**: cert-manager Issuer/Certificate refs, service ports. + +Report drift as `area | config has | helm has | risk`. Do NOT auto-fix — which side is canonical depends on what the change was for. + +## Pass 5 — Reviewer list (report + interactive) + +Read `bundle/ravendb-operator/ci.yaml`. Surface the current `reviewers:` list (or note absence — without it OperatorHub auto-merge doesn't fire). Ask: "Is this list current? Anyone to add or remove?" If the user wants edits, propose, confirm, apply, commit `: refresh OperatorHub reviewers list`. + +## Pass 6 — Dependencies audit (report + interactive, top-level only) + +Surface pin vs latest for these only — don't go deep into transitive Go modules: + +- `OPERATOR_SDK_VERSION` in `Makefile` vs latest `operator-framework/operator-sdk` GitHub release. +- `CONTROLLER_TOOLS_VERSION`, `KUSTOMIZE_VERSION`, `ENVTEST_VERSION`, `GOLANGCI_LINT_VERSION` in `Makefile`. +- `go` directive in `go.mod` vs current stable Go. +- cert-manager version in `.github/workflows/release.yml` smoke-test URL. +- GitHub Action versions in `.github/workflows/*.yml` — surface floating tags (`@v3`, `@v4`); recommend SHA pinning where missing. + +Report as `dep | pinned | latest | gap | recommendation`. Recommendation tone: "consider bumping" or "looks current". Do NOT auto-bump — bumps need a deliberate test cycle, not a release-prep slipstream. Ask the user if they want to schedule any bumps as separate work. + +## Final summary + +``` +Pre-release report — +============================= +Pass 1 (CSV markers): +Pass 2 (Samples): +Pass 3 (CRD breaking): +Pass 4 (Helm parity): +Pass 5 (Reviewers): +Pass 6 (Dependencies): +``` + +After this report, the user has a clear picture of what's actionable before invoking `prepare-release.yml`. From 7b404a6698550fb4c9a4f754ab79baecb0a5330f Mon Sep 17 00:00:00 2001 From: Gracjan Sadowicz Date: Tue, 5 May 2026 21:28:15 +0200 Subject: [PATCH 8/8] RavenDB-26535: fix shellcheck warnings in operator-ci.yml --- .github/workflows/operator-ci.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/operator-ci.yml b/.github/workflows/operator-ci.yml index 9495e93..2b51efc 100644 --- a/.github/workflows/operator-ci.yml +++ b/.github/workflows/operator-ci.yml @@ -36,7 +36,7 @@ jobs: - name: Install gotestsum (if cache miss) if: steps.cache-gts.outputs.cache-hit != 'true' - run: go install gotest.tools/gotestsum@${GOTESTSUM_VERSION} + run: go install "gotest.tools/gotestsum@${GOTESTSUM_VERSION}" - name: Install setup-envtest tool run: go install sigs.k8s.io/controller-runtime/tools/setup-envtest@latest @@ -49,7 +49,7 @@ jobs: key: ${{ runner.os }}-envtest-${{ env.K8S_VER }} - name: Select envtest Kubernetes assets - run: echo "KUBEBUILDER_ASSETS=$(setup-envtest use -p path ${K8S_VER})" >> "$GITHUB_ENV" + run: echo "KUBEBUILDER_ASSETS=$(setup-envtest use -p path "${K8S_VER}")" >> "$GITHUB_ENV" - name: Cache Go build and module download uses: actions/cache@v4 @@ -100,8 +100,8 @@ jobs: set -euo pipefail common_args=( --namespace ravendb-operator-system - --set controllerManager.image.repository=ravendb/ravendb-operator - --set controllerManager.image.tag=ci-${GITHUB_SHA} + --set controllerManager.image.repository=ravendb/ravendb-operator + --set controllerManager.image.tag="ci-${GITHUB_SHA}" --set targetNamespace=test-namespace --set provisioning.mode=LetsEncrypt --set "provisioning.nodeTags={a,b,c}"