rstudio / rstudio/helm

Write down and enforce the chart versioning convention

Open
#940 2 comments 0 reactions 1 assignee View on GitHub

@bschwedler is already working on this.

Since Sep 11, 2026.

  • #941 by @bschwedler — open
Dominant language
Markdown
Stars
46
Forks
40
Avg merge
4h 3m
Merged PRs (30d)
6

Description

Problem

This repo already has a versioning convention. While a chart is on 0.x,
breaking changes go in the minor position and everything else goes in the
patch position. It is followed most of the time, it is not written down
anywhere, and the exceptions are getting more frequent.

Counting every release across the three product charts, 31 carry a BREAKING
or DEPRECATED label in NEWS.md, excluding one BETA BREAKING. Of those:

  • 20 shipped as a minor bump, which is the convention working
  • 11 shipped as a patch bump

The eleven: rstudio-connect 0.7.2, 0.7.4, 0.20.1, 0.20.10; rstudio-pm
0.0.4, 0.2.7; rstudio-workbench 0.0.3, 0.0.5, 0.5.22, 0.5.24, 0.21.1.

The three most recent are rstudio-workbench 0.21.1 and rstudio-connect
0.20.10, both tagged 2026-07-14, and rstudio-connect 0.20.1, tagged
2026-05-18. All three are in the charts' current release lines, and they span
two products, so this is not a single team's habit.

The reason is that neither CONTRIBUTING.md nor CLAUDE.md states what the
three positions mean. CONTRIBUTING.md says only:

  • CI requires that the chart version get bumped for any change in the directory.
  • When bumping a chart version, add a ## X.Y.Z entry to the chart's NEWS.md
    describing what changed. CI will check for this.

CLAUDE.md states a narrower version of the same rule, excluding
documentation-only changes per #839. Both mandate that a bump happens.
Neither says which component to bump, and CI enforces only the mechanical
check: that the number changed, not that it changed in the position matching
the impact.

So the convention is carried in maintainers' heads, and it degrades whenever
someone is working fast or is new to the repo. That is a documentation and
tooling gap, not a disagreement about what the right answer is.

Evidence

All from charts/rstudio-connect/NEWS.md unless noted. Dates are tag dates.

Release Tagged Bump What shipped
0.20.0 2026-05-18 08:40 minor **BREAKING** image namespace move, image.tagPrefix removed, tag format changed. The convention working correctly.
0.20.1 2026-05-18 15:44 patch BREAKING: backends.kubernetes.enabled now defaults to true and launcher.enabled now defaults to false. Changes the execution backend for existing installations.
0.20.5 2026-06-08 patch Removed the os field from backends.kubernetes.defaultInitContainer and launcher.defaultInitContainer. image.os had been introduced by the breaking 0.20.0, five patch releases earlier.
0.20.7 2026-06-23 patch Init container rsc-volume mount moved from /opt/rstudio-connect to /opt/rstudio-connect-runtime.
0.20.10 2026-07-14 patch **DEPRECATED**: The chronicleAgent configuration is deprecated and will be removed in a future release. rstudio-workbench 0.21.1 shipped the same deprecation as a patch the same day.
0.20.12 2026-08-04 patch New minimum product version: "Requires Connect 2026.08.0 or later."
0.7.2 2024-05-31 patch BREAKING: local execution only, default R and Python versions upgraded, TensorFlow enabled by default in values.yaml. Generated #521.
0.7.4 2024-07-16 patch BREAKING: local execution only, default Quarto upgraded.

Three things stand out.

0.20.0 and 0.20.1 were seven hours apart. 0.20.0 is in the table
deliberately: the same team, in the same release line, on the same day, got it
right in the morning and wrong that afternoon. That is the clearest possible
statement of the thesis. This is not a claim that anyone here does not know how
to version a chart; it is a claim that an unwritten rule is applied unevenly,
and it forecloses any reading based on different people or different eras.

0.7.2 labelled itself. The release that generated #521 wrote BREAKING in
its own changelog and shipped as a patch anyway. That is the strongest
argument for proposal 2 below: the information needed to catch it was already
present, in the file CI already reads, and nothing looked at it.

0.20.12 is the recurrence. A minimum-product-version requirement in a patch
release is precisely what #521 objected to. It shipped fourteen months after
#521 was closed.

On #521

#521 was filed 2024-06-05 and closed 2025-06-10.

Six weeks after it was filed, and with it still open and unanswered,
rstudio-connect 0.7.4 shipped on 2024-07-16 carrying the same
BREAKING: local execution only label as the release being complained about,
in the same patch position. The 0.20.x cases above are the recurrence after
closure; 0.7.4 is the repeat while the complaint was live.

#521 was closed with an apology for the specific mistake, and with a scope
position worth addressing directly:

This helm chart assumes that the default configurations will be compatible
with the image that posit maintains.

That is a reasonable line to draw for testing. It does not work as a line for
versioning, for three reasons:

  1. A version number that carries information only for users of the default
    image carries information for a minority of production deployments. Custom
    images are the norm in regulated and air-gapped environments, which is a
    large part of the customer base for these charts.
  2. The charts' own READMEs tell every user to pin exactly, not just
    custom-image users, which concedes that ranges are currently unsafe in
    general.
  3. Several of the cases above break default-image users too. 0.20.1 flips the
    execution backend for anyone who upgrades in place, whatever image they run.
What is not in question

The split between version (SemVer, describing the chart) and appVersion
(calendar versioning, describing the product) is correct and should stay. For
the three product charts, the two legitimately move on different schedules.
Classifying every release by whether it bumps the product version or changes
chart behaviour:

Chart Releases Product bump only Chart behaviour Mixed
rstudio-pm 103 29 (28%) 71 (69%) 3
rstudio-connect 180 67 (37%) 109 (61%) 4
rstudio-workbench 149 28 (19%) 114 (77%) 6

Figures are from a heuristic over bullet text and are approximate, but the
direction is unambiguous: roughly two thirds to three quarters of releases in
every chart change the chart's own interface with no product release involved.
Those changes need somewhere to live, and they need to be distinguishable from
image-tag bumps. Making version mirror the CalVer appVersion would destroy
that distinction and would make ^ and ~ meaningless, since under calendar
versioning a caret means "this or anything newer" with no upper bound. Posit's
own Positron extension documentation makes exactly this point.

Note the scope: appVersion is CalVer only for the three product charts.
Current state of all six:

Chart version appVersion
rstudio-connect 0.21.2 2026.09.0
rstudio-pm 0.20.5 2026.09.0
rstudio-workbench 0.22.2 2026.09.0
rstudio-library 0.1.37 0.1.35
rstudio-launcher-rbac 0.2.26 0.2.21
posit-chronicle 0.5.0 (deprecated: true) 2026.01.0

rstudio-library and rstudio-launcher-rbac use SemVer in both fields and
track no customer-facing product. Any policy text needs to say so rather than
asserting CalVer across the board.

The appVersion problem, and why it forces the question of 1.0.0

There is a complication the policy has to address explicitly, because getting
it wrong reproduces #521.

The product image tag is derived from appVersion. The charts say so
themselves. From charts/rstudio-connect/values.yaml:

  # -- Overrides the image tag whose default is the chart appVersion.
  tag: ""

and Connect's 0.20.0 entry: "Image tag format changed from
{tagPrefix}{appVersion} to {appVersion}-{os}".

So a chart release that bumps only appVersion moves the running product from
one calendar version to the next on an in-place helm upgrade. That is what
0.7.2 did, and it is why #521 exists.

It is not a historical problem. rstudio-workbench 0.22.2 was tagged
2026-09-11T01:55:31Z, while this issue was being written. It is a patch bump
off 0.22.1, its entire changelog entry is "Bump Workbench version to
2026.09.0", and it moved appVersion from 2026.08.2 to 2026.09.0. A patch
release moved a production product forward a calendar version this morning.

An appVersion bump therefore cannot honestly be classified as "requires no
user action". But under 0.x there are only two usable positions, minor and
patch, and minor is already spoken for by breaking changes. There is nowhere to
put "the product moved, nothing broke", so on 0.x it has to go in patch, and
the hazard stays.

Under 1.x there are three positions and the problem disappears: major for
breaking, minor for product bumps and new values, patch for template fixes that
change no interface. This is the substantive reason to graduate, and it is why
proposal 3 is not cosmetic.

Proposal

Three parts. Part 1 stands alone. Parts 2 and 3 each depend only on part 1.

1. Write the convention down

Add to CONTRIBUTING.md, and mirror in CLAUDE.md:

Chart versioning

Chart versions are SemVer 2 and describe the chart: its values, its
defaults, and its upgrade behaviour. For the product charts, the product
version lives in appVersion and follows Posit's calendar versioning. The
two are independent. rstudio-library and rstudio-launcher-rbac use SemVer
in both fields.

While a chart is on 0.x (all charts today), there are two positions:

  • Minor (0.21.0): anything that requires the user to act. Removing or
    renaming a value, changing a default in a way that alters running
    behaviour, moving a path, deprecating a value, or introducing a minimum
    product version.
  • Patch (0.21.3): everything else, including appVersion bumps, new
    optional values, and template fixes.

Note that marking a value deprecated and removing it later are both Minor
here. That is not the SemVer rule; it is a consequence of 0.x having only
one signalling position. The two separate once the chart reaches 1.0.0.

Known limitation: because the image tag derives from appVersion, a patch
release can move the running product version. This is a consequence of having
only two positions available on 0.x, and it is the main reason to graduate
to 1.0.0. Consumer-facing guidance for working around it belongs in the
chart READMEs, not here.

Once a chart reaches 1.0.0, the three positions take their ordinary
SemVer meanings:

  • Major: requires the user to act. Removing or renaming a value,
    removing a previously deprecated value, changing a default in a way that
    alters running behaviour, moving a path, or introducing a minimum product
    version.
  • Minor: appVersion bumps, new values, new optional behaviour, new
    resources that are off by default, and marking a value deprecated.
    Backward compatible, but the running product may change.
  • Patch: template fixes, dependency bumps, and anything that changes no
    interface and moves no image tag.

If you are unsure between two levels, choose the higher one.

Two things to be plain about, so they are not discovered in month two.

This is stricter than what the version numbers have delivered, and the expected
effect is more 0.x.0 releases. It is not stricter than the changelogs
already being written: 0.20.1 called itself BREAKING in NEWS.md and then
shipped as a patch. For the labelled cases, all the policy asks is that the
version number agree with the entry the author already wrote.

The unlabelled cases are where it bites, and they cannot be caught by CI. For
example rstudio-pm 0.5.44 set enableMigration: false by default with no
marker of any kind. Under this rule that is a minor, because a default that
changes whether a migration job runs on upgrade is the definition of "requires
the user to know". That is a judgement rule for authors and reviewers, which is
why the "if unsure, choose the higher one" line matters.

2. Enforce the part that is mechanically checkable

tools/dev/check-news-entries.sh already runs in
.github/workflows/chart-test.yaml:

# checks that bumped chart versions have a corresponding NEWS.md entry
- name: Check NEWS.md entries
  if: github.ref != 'refs/heads/main'
  run: ./tools/dev/check-news-entries.sh main

That script already reads current_version from Chart.yaml, reads
base_version via git show origin/${TARGET_BRANCH}:..., and greps
^## ${current_version}$ out of NEWS.md. It has both version numbers and the
entry location in hand. The addition is roughly fifteen lines inside the
existing loop: extract the entry body, test it for a breaking or deprecation
label, and compare the two versions' positions.

While the chart is on 0.x, fail when either label ships with a patch-only
bump. Once on 1.x the two labels separate, matching the policy above: a
BREAKING entry must bump major, a DEPRECATED entry must bump at least
minor.

Match on an anchored label, not any occurrence of the word:

^[[:space:]]*[-*][[:space:]]*\**[[:space:]]*(BREAKING|DEPRECATED)\b

case-insensitively. Real markers in the repo are - BREAKING:,
- **BREAKING**:, - Breaking: (rstudio-pm 0.2.0), - **DEPRECATED**: and
- BETA BREAKING: (rstudio-connect 0.3.0). The last should be exempted; it
is also excluded from the counts in the Problem section above.

An unanchored substring match on "breaking" produces false positives at about a
10% rate. The four in-tree cases are rstudio-connect 0.7.14 and
rstudio-workbench 0.8.6 ("without breaking all the charts that depend on
it"), rstudio-workbench 0.11.1 ("Fix components.positron.version breaking
the server pod"), and rstudio-pm 0.5.13, whose entry reads "This is not a
breaking change since it does not affect how Package Manager serves packages".
An unanchored match would fail a release for explicitly denying it was
breaking.

Worked example: rstudio-connect 0.7.2 wrote - BREAKING: local execution only and bumped 0.7.1 to 0.7.2. The anchored match fires, the positions differ
only in patch, the build fails, and #521 never happens. Same for 0.7.4, 0.20.1,
0.20.10 and rstudio-workbench 0.21.1.

What this does and does not cover. The check catches mislabelled bumps: an
author who writes the marker and then picks the wrong position. It cannot catch
unlabelled ones, such as rstudio-pm 0.5.44 above, because there is nothing in
the entry to match. Part 1 is what covers those, and part 2 does not make it
redundant.

Deeper checking, diffing values.yaml keys between the previous release and
HEAD to catch removals and default changes, could follow.
charts/posit-chronicle/values.schema.json already exists as a pattern, and
#742 and #35 would extend it to the product charts.

3. Graduate the charts to 1.0.0

This is what gives the scheme a third position, which is what resolves the
appVersion hazard described above. It also changes what ^ does: today
^0.20.0 resolves to >=0.20.0 <0.21.0, patch-only; after graduation
^1.20.0 resolves to >=1.20.0 <2.0.0 and tracks minors, so consumers get
product bumps and new values automatically and review only genuine breaking
changes.

The argument for doing it now is interface maturity and how the charts are
already being used, not stability. These are the documented, supported
deployment path for three commercial products, in production at large
customers, with a values interface that has been recognisably the same shape
for years. 0.x advertises "anything may change at any time", which is neither
what Posit means nor how customers are being asked to treat them.

There is also direct precedent. All three product charts were jumped to
0.20.0 in one coordinated release, skipping large ranges: rstudio-connect
from 0.9.5, rstudio-pm from 0.5.57, rstudio-workbench from 0.11.2. A
deliberate cross-chart version reset has already been executed once, so both
the appetite and the release mechanics exist. 1.0.0 is a smaller ask than
what was already done.

Mechanically it is one release per chart, version: 1.0.0 with a NEWS.md
entry stating that no behaviour changed and that the policy above now applies.
It should be announced, not folded into a feature release.

Consumer guidance to update afterwards

Once the policy holds, the READMEs should recommend ^1.20.0 rather than exact
pinning. Not ~1.20.0: that resolves to >=1.20.0 <1.21.0, which is patch-only
and cuts consumers off from the additive minor releases the policy exists to
make safe. ~ is also what 0.x effectively gives them today, so recommending
it would make graduation deliver nothing.

Separately, and worth adding now rather than after graduation: consumers who
need to control the product version independently of the chart version should
pin image.tag, or set versionOverride, which exists in all three product
charts. This is the mitigation for the appVersion hazard, and it belongs in
the READMEs where consumers will see it, not in CONTRIBUTING.md.

Checklist
  • Agree the policy text; add to CONTRIBUTING.md and mirror in CLAUDE.md
  • While editing CONTRIBUTING.md, fix two stale statements:
    • the bump rule no longer applies to documentation-only changes (#839)
    • "Changes to the rstudio-library chart will update all downstream charts
      at the same time (via the file:// syntax in Chart.yaml)" is no longer
      true; dependencies are pinned to published versions (#601)
  • Extend tools/dev/check-news-entries.sh with the anchored
    breaking/deprecation label check
  • Decide on 1.0.0 graduation; if agreed, schedule and announce it
  • Update the pin guidance in charts/_templates.gotmpl, then run
    just docs. Note this is one edit, not six: the READMEs are generated,
    and the preamble is shared. Two near-duplicate blocks need it, the one
    under "To ensure a stable production deployment:" and the one under "To
    ensure reproducibility in your environment and insulate yourself from
    future changes, please:". The section is titled ## For production in
    rstudio-pm and rstudio-workbench but ## Best practices in
    rstudio-connect.
  • In the same file, add consumer-facing guidance that the image tag
    defaults to appVersion, so a chart upgrade can move the product
    version, and that image.tag or versionOverride pins it independently
  • Unrelated but adjacent, since that file is being touched: the second pin
    block renders This protects you from breaking changes** with a stray
    trailing ** and no full stop. The first block has it right.
Open questions
  • Do the charts version together or independently? They were aligned at
    0.20.0 and have already drifted to 0.21.2, 0.20.5 and 0.22.2. If 1.0.0
    goes ahead, is it a single aligned release across the three product charts,
    as 0.20.0 was, or per-chart when each team is ready?
  • Scope. Do rstudio-library and rstudio-launcher-rbac graduate too?
    They are internal dependencies with a different audience, and
    rstudio-library is consumed by pinned version rather than by range, so the
    case is weaker. posit-chronicle is deprecated: true and is out of scope.
  • Does the rstudio-library pin still leave a signal problem? #601 fixed
    the old file:// coupling, and Connect currently pins 0.1.36 while the
    library is at 0.1.37, which is the mechanism working. Worth confirming that a
    breaking library change now surfaces in the consuming chart's own version,
    and what remains open in #237 given that.
Related
  • #521: user report of a breaking change in a patch release, closed without a
    policy change. The direct motivation for this issue.
  • #37: "Explore a branching strategy", which wants version bumps "(1) rarer and
    (2) more meaningful". Open since 2021. This issue covers the "meaningful"
    half.
  • #839: documentation-only changes no longer require a bump. Shipped, and
    reduces release noise, which makes the remaining releases more worth reading.
  • #601: pinned rstudio-library per product chart. Closed 2024-11-01. Changes
    the versioning picture and makes part of #237 stale.
  • #237: library chart dependency management. See the open question above.
  • #34: ArtifactHub-friendly changelog. Complementary; machine-readable
    artifacthub.io/changes annotations could share a source of truth with the
    CI check in proposal 2.
  • #35, #742: values.schema.json. The foundation for stricter interface
    checking later.
  • #257: "Give guidance on how to stay up to date." A usable version policy is a
    large part of the answer.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.