Skip to main content

dependency-upgrade

Perform a sweeping dependency upgrade of the Solr Operator - bump Go modules and pinned tool versions together, regenerate CRDs, RBAC, deepcopy code and the license list, fix the test fixtures that k8s API bumps break, and record the user-visible effects in both Helm charts' artifacthub.io/changes changelog. Use this instead of accepting many small Dependabot PRs, and especially before cutting a release.

Source facts

Repository
apache/solr-operator
Last source activity
November 6, 2026 at 12:48
Detected SKILL.md language
English
Stars
285
Forks
150

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

Showing SKILL.md

SKILL.md
Source instructions · Read-only preview
name
dependency-upgrade
description
Perform a sweeping dependency upgrade of the Solr Operator - bump Go modules and pinned tool versions together, regenerate CRDs, RBAC, deepcopy code and the license list, fix the test fixtures that k8s API bumps break, and record the user-visible effects in both Helm charts' artifacthub.io/changes changelog. Use this instead of accepting many small Dependabot PRs, and especially before cutting a release.
when_to_use
The user asks to upgrade dependencies, bump Go modules, update k8s/controller-runtime/Helm libraries, refresh the dependency license list, handle a major-version or breaking dependency change, or prepare dependencies ahead of a release. Also when a dependency PR fails "make check-licenses" or "make check-git".
# Sweeping dependency upgrade Upgrade everything in one reviewed pass: one CRD regeneration, one license-list regeneration, one set of fixture repairs, one changelog entry. The changelog is the step most often forgotten and the main reason this skill exists. Read `AGENTS.md` first — where it and this skill disagree, the repo wins. ## Who owns what | Tier | What | Owner | |---|---|---| | A | GitHub Actions, the release wizard's `requirements.txt` | Dependabot (`.github/dependabot.yml`). Not yours: leave its PRs alone, never bump a `uses:` SHA or a wizard pin during a sweep. | | B | Go modules, Makefile tool pins, envtest assets | This skill. `make list-upgrades` detects them. | | C | Base images, kind/Kubernetes versions in the test scripts, the Zookeeper server image, docs matrices | This skill, but nothing upstream to check against. `make list-upgrades` locates them by pattern and prints current values; bump what is clearly stale. `PATTERN NOT FOUND` or `MISSING FILE` means the table in the script needs updating. | Out of scope: `DefaultSolrVersion`, chart `appVersion`, the operator's image tag — all release process (`hack/release/version/propagate_version.sh`). Also the declared minimum Kubernetes version, below. ## Step 0: branch from upstream main ```bash git remote -v | awk '/apache\/solr-operator(\.git)? \(fetch\)/ {print $1}' git status --short git fetch <remote> main git switch -c dependency-upgrade-<yyyy-mm> <remote>/main ``` The remote is `upstream` in some clones, `apache` or `origin` in others; if the first command prints nothing, ask rather than guess. Stop and ask if tracked files are modified (untracked scratch files are fine). Branch from `<remote>/main`, never local `main`. Do not `git pull`. Do not commit to `main`. ## Preflight All five. Each one silently ruins the run. 1. **Delete `vendor/`.** Not vendored, not gitignored, so stale copies linger. Breaks three things: Go switches to `-mod=vendor` (`inconsistent vendoring`); `hack/check_license.sh` has no `vendor/` exclusion so `make lint` exits 255 on thousands of third-party files; `make check-git` sees only tracked files, so CI stays green and warns you of nothing. Also check `go env GOFLAGS`. 2. **Match the Go toolchain to `go.mod`.** The Makefile fails at parse time, blocking *every* target including `make help`, unless `major.minor` from the `go` directive is a substring of `go version`. Newer fails as hard as older. Install it, or set `GOTOOLCHAIN=go1.<minor>.0`. 3. **`make clean`.** Tool installation is a bare file-existence check, so stale binaries in `bin/` survive a version bump and regenerate CRDs with the old `controller-gen`. 4. **Docker running, network available.** `make lint` needs Docker (`check-helm`, `check-docs`), `make test` downloads envtest assets, `mod-tidy` and `fetch-licenses-list` reach the network. 5. **`helm` and `yq` on `PATH`.** `make install-dependencies` installs neither. ## Decide the scope Run `make list-upgrades`, or `./hack/list-upgrades.sh` when the Go-version gate blocks make. It writes nothing, exits 0, and prints every section even when one of them fails. **Take every patch and minor bump without asking.** No interview, no per-module approval, and never hold one back because its release notes are long. ### Resolve the Go toolchain first Decide the target Go version before judging any other candidate. A dependency's newest release may require a newer Go than `go.mod` pins; taking it anyway silently pulls a second toolchain, and the Makefile's gate then rejects the mismatch. `make list-upgrades` marks those candidates `[needs Go X.Y]` — compared against the *current* directive, so if you raise `go` in `go.mod`, re-run it and judge everything else against the new floor. Raising Go is its own decision: it moves `build/Dockerfile` and the `go-version` matrix in CI, and forces every contributor to update. If Go stays put, take the newest dependency version that still builds on it rather than skipping the module — e.g. kustomize v5.8.3 needs Go 1.27, while v5.8.0 needs only 1.24. ### Linked sets These move together — bumping one alone produces a tidy that resolves and a build that fails: - **The Kubernetes set:** `k8s.io/api`, `apimachinery`, `client-go`, plus the indirect `k8s.io/*` entries (`apiextensions-apiserver`, `apiserver`, `cli-runtime`, `component-base`, `kubectl`) — all on one version. `helm.sh/helm/v3` pulls several, so Helm moves in lockstep. - **`sigs.k8s.io/controller-runtime`** — also decides the `setup-envtest` release branch. - **`github.com/onsi/ginkgo/v2`** — the Makefile derives the installed ginkgo version from it. - **`github.com/pravega/zookeeper-operator`** — see below. Read each candidate's release notes for the span crossed. ### A new major is the user's call Never fold one in unilaterally, and never take the minor without saying the major exists. Cost it out in minutes: 1. Migration notes for the major boundary only, not every release in the span. 2. Blast radius: `grep -rln '<module path>' --include='*.go' .` 3. What it drags along: a `controller-runtime` major usually forces the Kubernetes set, and `helm.sh/helm` pulls `k8s.io/*`, so one major becomes four. 4. User-visible effects: CRD schema, generated RBAC, minimum Kubernetes version. 5. Whether the old major is still maintained. EOL means staying put is the risk. Present per module: current → available major, which breaking changes touch this codebase, file count, knock-on bumps, recommendation. Offer three options — take it now, take the minor and leave the major to a dedicated PR, or hold the module. On a toss-up recommend the minor plus a separate PR — a module-path change is unreviewable buried in regenerated CRDs. Recommend taking it now when the migration is mechanical, the old major is unmaintained, or a wanted bump requires it; say which. A major taken gets its own commit and its own PR paragraph. ## The Zookeeper Operator is pinned in a dozen places `versions.props` is the source of truth and `make check-zk-op-version` enforces most, not all, of the copies. If it moves, update every one of `versions.props`, `go.mod`, `config/dependencies/zookeeper_cluster_crd.yaml` (both the `tree/` comment and the `operator.zookeeper.pravega.io/version` annotation), `helm/solr-operator/Chart.yaml` (the `dependencies:` entry), `helm/solr-operator/README.md`, `NOTICE` and `docs/modules/development/pages/development.adoc`, then run that target. - The **`v` prefix is inconsistent on purpose**: `v0.0.0` in `versions.props`, `go.mod`, the CRD file, `NOTICE` and the chart README; bare `0.0.0` in `Chart.yaml` and `development.adoc`. The check enforces both forms and hints when you get it wrong. - The ZookeeperCluster CRD is regenerated by **no** make target. Run `hack/zk-operator/update-crd.sh` by hand; it needs network. - `tests/scripts/manage_e2e_tests.sh` pins the Zookeeper *server* image separately, unchecked. ## The minimum Kubernetes version is not a dependency The `k8s.io/*` libraries are an ordinary dependency, but the declared minimum supported Kubernetes is a support promise: `kubeVersion` in both charts plus the matrix in `docs/modules/upgrade-notes/pages/upgrade-notes.adoc`, enforced by Helm at install time, so raising it stops existing users from upgrading. Nothing links the two, so a library bump cannot raise the floor by accident — but it widens the gap between what the operator is built against and what it claims to support. `make list-upgrades` prints declared, built and tested side by side: report that gap, never close it. **Never edit `kubeVersion` or the matrix during a sweep.** Raising the floor is its own PR, timed to a release, with an upgrade-notes entry and a `changed` entry in both charts. The exception is a bump that *forces* the floor — new libraries dropping an API the operator uses, or CRD schema older API servers reject — and then you stop, because the supported range is the first decision. ## Tier C notes Two that need care rather than a bump: - The `golang` builder image in `build/Dockerfile` moves together with the `go` directive in `go.mod` and the `go-version` matrix in CI, because the Makefile's gate rejects a mismatch either way. - `artifacthub/ah` in the Makefile is **untagged**, so `make lint` pulls `:latest`. Pinning it is worthwhile but a deliberate change of its own. Some Tier C pins have already drifted (kind and Kubernetes differ between the e2e and smoke-test scripts; `hack/test-upgrade.sh` names an old Zookeeper Operator). Do not fold a drift cleanup into a sweep — file it separately, or the diff stops being reviewable. ## Do the upgrade 1. `make list-upgrades`. Settle the Go toolchain target first, then the rest of the scope, including any major-version interview. 2. Edit `go.mod`, and `versions.props` if the Zookeeper Operator moves. 3. `make mod-tidy`. 4. If the Zookeeper Operator moved: `hack/zk-operator/update-crd.sh`, update the files listed above, then `make check-zk-op-version` until it passes. 5. `make install-dependencies` — installs the tools at the new pinned and derived versions. 6. `make prepare` — the one command that makes a PR pass lint. Runs `fmt`, `generate`, `manifests`, `fetch-licenses-list` and `mod-tidy` in the right order. 7. `make test`, then repair fixtures. 8. Add or consolidate the changelog entries. 9. `make lint`, then `make test` again. ### Regenerate, never hand-edit `make manifests` writes `config/` **and** `helm/` from one `controller-gen` run, transforming on the way, and `make check-manifests` diffs both trees recursively — a partial regeneration fails. Never hand-edit `helm/solr-operator/crds/crds.yaml` (the three `config/crd/bases` files concatenated under a header) or `helm/solr-operator/templates/role.yaml` (`config/rbac/role.yaml` in Helm conditionals with templated names). A `controller-gen` bump alone rewrites every CRD, because the annotation script stamps a version line into each; several thousand changed lines is normal. ### Lint targets run serially `check-mod`, `check-manifests` and `check-generated` each delete a shared scratch directory, so `make -j` has them destroy each other's work. `check-mod` also moves `go.mod`/`go.sum` aside, re-tidies, then moves the originals back — interrupt it mid-way and the tree keeps the tidied files while the originals sit in a scratch directory `make clean` deletes. After interrupting, check `git diff go.mod` first. ### The license list is platform-sensitive `fetch-licenses-list` is prefixed `GOOS=linux GOARCH=amd64` deliberately: `go-licenses` walks only the current platform's build graph, so regenerating on macOS or ARM silently omits Linux-only dependencies and CI's byte-exact diff then fails. Always use the make target, never `go-licenses` directly. Do not re-sort the CSV; it is in the generator's native order. `make check-licenses` fails two ways. A `<`/`>` diff means the CSV is stale — regenerate it. A disallowed-license complaint means a new transitive dependency landed under a license outside the allow-list, and **there is no override**: find a version that avoids it, or stop and raise it with the PMC. Never edit the allow-list to make a build pass. ## Fixing what a Kubernetes bump breaks The controller tests boot a real API server through envtest and load the real CRDs, so an API bump changes behaviour, not just signatures. Expect to repair test fixtures, not production code — past sweeps needed a toleration operator changed from `Exists` to `Equal`, and storage resource requests added to PVC templates. One failure at a time, and for each ask whether it is a fixture encoding an old default or a real regression. A regression means fixing the controller and a changelog entry; so does any change to generated RBAC, which is user-visible. All controller and e2e specs use focused Ginkgo nodes (`FDescribe`, `FContext`, `FIt`), so `GINKGO_EDITOR_INTEGRATION=true` is load-bearing. `make test` sets it; `go test ./controllers/...` by hand without it fails confusingly on programmatic focus. ## Changelog: both charts Add entries to `artifacthub.io/changes` in **both** `helm/solr-operator/Chart.yaml` and `helm/solr/Chart.yaml` — a YAML block scalar holding a list. `kind` accepts exactly `added`, `changed`, `deprecated`, `removed`, `fixed`, `security`. Follow the existing entries, including the `links:` convention of a `Github Issue` and/or `Github PR` pair. Malformed YAML fails `make check-helm`. Write for a user deciding whether to upgrade, not a reviewer reading the diff: - Tight wording. One line per entry, no prose, no restating the diff. - Never list library bumps individually. One `changed` entry summarising the sweep. - Several sweeps land in one release cycle. If that grouped entry is already there from an earlier one, update it instead of adding a second — the net span since the last release, not this PR's delta — and add this PR to its `links:`. The separate-entry rules below still apply. - A separate entry for anything changing behaviour or requiring action: new RBAC permissions, a bundled Zookeeper Operator bump, a changed default. - A CVE fix uses `kind: security` **and** flips `artifacthub.io/containsSecurityUpdates` to `"true"` in that chart. Once set in a cycle it stays set; a later CVE-free sweep must not flip it back. - A sweep with no user-visible effect still gets one `changed` entry. If the bundled Zookeeper Operator moved, also add an entry to `docs/modules/upgrade-notes/pages/upgrade-notes.adoc`; its matrices are hand-maintained. Release-time interaction: the release process wipes `artifacthub.io/changes` to a placeholder just after a release branch is cut, so an entry added to `main` after that lands in the *next* release. An upgrade belonging to the release being cut must merge before that step. That placeholder is two dummy entries (`Addition 1`, `Change 2`), not an empty list — replace them, don't append. ## Verify before you commit 1. `make lint`, serially. Covers zk version consistency, tidy, formatting, licenses, manifests, generated code, charts, docs. 2. `make test`. 3. `git status --short`, then stage deliberately by naming paths. **Never `git add -A`** — the tree often still holds an untracked `vendor/` (6500+ files) or scratch notes. Confirm **both** `git diff --exit-code` and `git status --short` are clean; CI's `check-git` only checks the former, so an untracked generated file passes CI and breaks the next person. Treat any failure as unfinished work. Add `make smoke-test` if packaging could be affected; it needs Docker, kind and network, unpacks the source release and runs `make check` inside it. CI coverage: build-and-check runs on every PR; smoke-test skips doc-only paths but runs for `go.mod`, `go.sum`, `Makefile`, the license CSV and `helm/`. **The e2e workflow is permanently disabled** and reports as skipped, so it is evidence of nothing — if runtime behaviour could be affected, run `make e2e-tests` locally and say so in the PR. ## Commit, then ask before pushing One commit, or a few logically separate ones, with generated output in the same commit as the change that generated it. Message is a line or two: imperative subject under ~70 characters matching the existing log (`Upgrade almost all operator dependencies`), at most a sentence of body. Detail belongs in the PR description. The fork is the remote whose URL is not `apache/solr-operator`: ```bash git remote -v | grep -v apache/solr-operator ``` Several candidates or none means ask which. **Never push without the user agreeing in this conversation**, and never push to the apache remote. After pushing, propose the PR description and let the user open the PR unless they ask you to. ### PR description A reviewer cannot read thousands of lines of regenerated CRDs, so the description says what to check instead. Well under a screenful: - One sentence: which dependency sets moved, and why now — routine sweep, pre-release, CVE. - The sets and version spans crossed, not one bullet per module. - Everything not mechanical: each major bump and what it required; each changed fixture, and whether it was stale or a real behaviour change; any RBAC or default that moved. - One line naming the changelog entries added. - What you ran: `make lint`, `make test`, and `make e2e-tests` if you did — CI's e2e is disabled. Leave out anything visible from the file list, and never paste regenerated diffs. Write each paragraph and bullet as one unwrapped line; GitHub reflows them.
View on GitHub