Skip to content

fix(ipa): clarify endpoint version lifecycle entries - #1370

Merged
andmatei merged 7 commits into
mainfrom
fix/changelog-version-lifecycle
Jul 10, 2026
Merged

fix(ipa): clarify endpoint version lifecycle entries#1370
andmatei merged 7 commits into
mainfrom
fix/changelog-version-lifecycle

Conversation

@andmatei

@andmatei andmatei commented Jul 8, 2026

Copy link
Copy Markdown
Collaborator

Context

Addresses the changelog-generation part of CLOUDP-386733

Problem

  • Each changelog entry carries a changeType (release / update / deprecate / remove) that tells readers what kind of change hit an endpoint version.
  • It was decided globally per run: if the two compared specs had different active versions, every change in that run was tagged release — including routine schema tweaks.
  • When a new API version was actually released, the human-facing changelog.json did not record it as a release of the new version, and it never emitted a deprecation entry for the superseded version — so consumers couldn't tell from the changelog that a version was superseded/sunsetting.
  • Net effect is the customer confusion in the ticket: "what do the tags RELEASED or UPDATED mean?"

What changed

changeType is now derived per endpoint version from the actual change, and a genuine version release now surfaces both sides of the transition in changelog.json:

changeType When it applies
release A brand-new endpoint is added, or a new API version of an existing endpoint is released
deprecate A prior version of an endpoint is deprecated because a newer version was released; it sunsets on that version's own sunset date
remove An endpoint version is removed once it reaches its sunset date
update Any other change to an existing endpoint version (schema/field changes, etc.)

Example

GET /api/atlas/v2/groups/{groupId}/clusters, when API version 2024-08-05 is released (superseding 2024-05-30):

Before — one bucket, tagged release with only schema diffs, no deprecation anywhere:

{ "version": "2024-08-05", "changeType": "release", "changes": [
  { "changeCode": "response-property-one-of-added", "change": "added ... to the response body 'oneOf' list ..." },
  // ... only schema changes; nothing says a version was released, nothing about the old version
]}

After — the new version is an explicit release and the prior version is deprecated with structured metadata (each with its own sunset date):

{ "version": "2024-08-05", "changeType": "release", "changes": [
  { "changeCode": "endpoint-version-added",
    "change": "API version 2024-08-05 was added. It replaces API version 2023-02-01.",
    "replacesVersion": "2023-02-01" },
  { "changeCode": "response-property-one-of-added", "change": "added ... to the response body 'oneOf' list ..." }
  // ... plus the same schema changes
]},
{ "version": "2024-05-30", "changeType": "deprecate", "changes": [
  { "changeCode": "endpoint-deprecated",
    "change": "API version 2023-02-01 is deprecated and sunsets on 2099-01-01. Use API version 2024-08-05 instead.",
    "deprecatedVersion": "2023-02-01", "sunsetDate": "2099-01-01", "replacedByVersion": "2024-08-05" }
]}

New machine-readable lifecycle fields per entry: deprecatedVersion, sunsetDate, replacedByVersion, replacesVersion (feeding the future bump.sh changelog, DOP-5532).

Fixtures

Changelog e2e fixtures under tools/cli/test/data/changelog/<scenario>/output/ are golden files (the e2e test diffs CLI output against them; there is no auto-update flag). They were regenerated by running the changelog CLI on each scenario's inputs and copying the output back into <scenario>/output/ (changelog.json, internal/changelog-all.jsonchangelog-all.json, each version-diff/<from>_<to>.json<from>_<to>.json). Scenarios with a stable version bump (new-api-version, new-api-preview-version, rename-api-version) now show the release + deprecate pair; same-api-version and new-upcoming-version (no stable bump) are unchanged apart from the reworded endpoint-version-added text in their per-version diffs.

Verification

  • go test ./changelog ./changelog/outputfilter (tools/foas)
  • go test ./internal/... (tools/cli)
  • make e2e-test E2E_EXTRA_ARGS='-run TestChangelog' (tools/cli)

@andmatei andmatei changed the title fix(changelog): clarify endpoint version lifecycle entries fix(ipa): clarify endpoint version lifecycle entries Jul 9, 2026
@andmatei
andmatei force-pushed the fix/changelog-version-lifecycle branch 3 times, most recently from ddb09ce to de5fa71 Compare July 9, 2026 00:50
@andmatei
andmatei force-pushed the fix/changelog-version-lifecycle branch from de5fa71 to 74d3d8b Compare July 9, 2026 01:06
…dedup

Address code review feedback on the endpoint version lifecycle changes:
- Add direct unit tests for operationVersion and versionFromMediaType
  (x-xgen-version precedence, content-type fallback, latest-version
  selection across response/request media types, unversioned cases).
- Document operationVersion semantics and why x-xgen-version is preferred
  (survives spec normalization).
- Document the per-OperationID dedup in newRevisionChanges and
  newDeprecatedByNewerVersionOasDiffEntries.
- Co-locate endpointRemovedCode with the other endpoint change codes.
@andmatei
andmatei force-pushed the fix/changelog-version-lifecycle branch from 2469a56 to fb9b984 Compare July 9, 2026 12:47
@andmatei
andmatei marked this pull request as ready for review July 9, 2026 15:27
@andmatei
andmatei requested a review from a team as a code owner July 9, 2026 15:27
@@ -115,6 +115,20 @@
}
]
},
{
"path": "/api/atlas/v2/groups/{groupId}/clusters/{clusterName}",

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All these changes are from regenerating the e2e test fixtures

andmatei added 5 commits July 9, 2026 18:21
When a new API version becomes active between changelog runs, the
base-vs-revision comparison compared the superseded version from the base
spec set (where it is not yet deprecated), so no version-transition signal
was produced and the release showed up as ordinary 'update' entries. The
transition comparison's result was also never written back to
BaseChangelog, so the sunset/manual pass discarded it.

Fix the version-transition branch of NewEntriesWithRunDate to:
- load the superseded version from the revision spec set, where it carries
  the deprecated + x-sunset markers that let the normalized diff surface the
  transition;
- re-seed from the original base changelog so the transition replaces,
  rather than duplicates, the same-version comparison's schema changes;
- store the transition result back into BaseChangelog so it is not dropped
  by the subsequent sunset/manual pass.

The affected endpoints now render two entries per the Versioned API
Changelog spec: the new version as 'release' (endpoint-version-added) and
the superseded version as 'deprecate' (endpoint-deprecated, with sunset and
replaced-by metadata). Refresh changelog/changelog-all fixtures for the
new-api-version, new-api-preview-version and rename-api-version scenarios;
same-api-version and new-upcoming-version are unchanged (no stable bump).

Verified: go test ./changelog/... (tools/foas) and
make e2e-test E2E_EXTRA_ARGS='-run TestChangelog' (tools/cli) both pass.
Make the lifecycle messages reference the released version (which matches
the changelog version bucket) and state that all previous versions are
deprecated, instead of naming a single superseded resource version. This
avoids confusing a consumer pinned to an intermediate API version that
resolves to an older resource version.

- endpoint-version-added: "A new version <v> of this resource was released."
- endpoint-deprecated: "A new version <v> of this resource was released. All
  previous versions are deprecated and marked for removal on <sunset>."

Structured fields (deprecatedVersion/sunsetDate/replacedByVersion/
replacesVersion) are unchanged. Refresh changelog e2e fixtures for the new
wording (version-diff files across all scenarios plus changelog/changelog-all
for the scenarios with a stable version bump).
…ipeline

Add a pipeline-level test that calls changelog.NewEntriesWithRunDate directly
(no CLI binary) against the new-api-version scenario and asserts that a version
bump records the new version as a 'release' (endpoint-version-added) and the
superseded version as a 'deprecate' (endpoint-deprecated with its own sunset
date and replaced-by version). This guards the code's output independently of
the golden fixtures, at the orchestration layer merge_test.go does not cover.

Includes small local helpers (findChangelogPath/findChangelogVersion/findChange).
@andmatei
andmatei merged commit 6269cd6 into main Jul 10, 2026
14 checks passed
@andmatei
andmatei deleted the fix/changelog-version-lifecycle branch July 10, 2026 13:37
Comment on lines -291 to -295
// deprecation by newer version occurs only when
// base_version is different than revision_version
if m.BaseMetadata.ActiveVersion == m.RevisionMetadata.ActiveVersion {
return nil
}

@andreaangiolillo andreaangiolillo Jul 13, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is if m.BaseMetadata.ActiveVersion == m.RevisionMetadata.ActiveVersion no longer valid? If the active versions of the base and revision are the same, a new API version wasn't released. I'm not sure why this piece of code was removed 🤔 . @andmatei

for _, change := range changes {
// Normalized versioned specs surface "old version deprecated, new version active" as reactivation.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we keep the comment that clarify what normalization mean in this context?

Comment on lines +366 to +368
func endpointVersionLifecycle(
change *outputfilter.OasDiffEntry,
operationConfig map[string]*outputfilter.OperationConfigs) (baseVersion, revisionVersion string, ok bool) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nit] Defining a new struct with the output fields would help with the readability of this code. The output list is quite long


baseVersion = conf.Base.Version
revisionVersion = conf.Revision.Version
return baseVersion, revisionVersion, baseVersion != "" && revisionVersion != "" && baseVersion != revisionVersion

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also what OK mean in this context? Let's rename it

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants