mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-10-25 21:20:00 +00:00
fix(release): support RC-derived stable patch install metadata
The install-metadata contract encoded only the emergency no-RC stable-patch shape. A stable patch with a same-version RC promotes the exercised candidate (promotion_mode stable-rc-promotion), so the contract must assert that shape and derive the rollback target from the last published stable. previousStablePatchVersion hardcoded only v6.4.2 as unpublished, so v6.4.5 rolled back to the never-published v6.4.4. Add v6.4.3 and v6.4.4, which only ever shipped as prereleases, so v6.4.5 resolves to the published v6.4.1 that the promotion resolver also derives. Add stablePatchPromotedPrerelease to detect the same-version candidate from the retained release notes, and branch the stable-patch install-metadata tests between the emergency and RC-derived shapes. Unit tests pin the rollback derivation and the candidate detection. Change-source: pulse-maintainer
This commit is contained in:
@@ -1142,6 +1142,7 @@ func TestCurrentStablePatchReleasePacketTracksInstallMetadata(t *testing.T) {
|
||||
t.Skip("current release is not a stable patch release")
|
||||
}
|
||||
releaseBranch := requiredReleaseBranchForVersion(t, version)
|
||||
promotedTag, rcDerived := currentStablePatchPromotedPrerelease(t, version)
|
||||
|
||||
releaseNotesPath := repoFile("docs", "releases", "RELEASE_NOTES_v"+version+".md")
|
||||
changelogPath := repoFile("docs", "releases", "V6_CHANGELOG_v"+version+".md")
|
||||
@@ -1152,17 +1153,30 @@ func TestCurrentStablePatchReleasePacketTracksInstallMetadata(t *testing.T) {
|
||||
"## What's improved",
|
||||
"not Authenticode-signed",
|
||||
"Unknown Publisher warning",
|
||||
"This patch does not require a companion mobile release",
|
||||
"does not require a companion mobile release",
|
||||
"rollback target is stable `v"+previous+"`",
|
||||
)
|
||||
assertFileContainsAllNormalized(t, changelogPath,
|
||||
changelogRequired := make([]string, 0, 7)
|
||||
changelogRequired = append(changelogRequired,
|
||||
"Version: `v"+version+"`",
|
||||
"Rollback target: `v"+previous+"`",
|
||||
"Promotion path: emergency stable patch from `"+releaseBranch+"`",
|
||||
"Windows signing decision: the standing SignPath-unavailable policy publishes unsigned Windows Unified Agent binaries",
|
||||
"Unknown Publisher warning",
|
||||
"Mobile decision: `no-mobile-impact`",
|
||||
)
|
||||
if rcDerived {
|
||||
// A stable patch with a same-version RC promotes the exercised
|
||||
// candidate; the promotion resolver refuses the emergency no-RC path.
|
||||
changelogRequired = append(changelogRequired,
|
||||
"Promoted prerelease: `"+promotedTag+"`",
|
||||
"Promotion path: exact-SHA single-build release candidate from `"+releaseBranch+"`",
|
||||
)
|
||||
} else {
|
||||
changelogRequired = append(changelogRequired,
|
||||
"Promotion path: emergency stable patch from `"+releaseBranch+"`",
|
||||
)
|
||||
}
|
||||
assertFileContainsAllNormalized(t, changelogPath, changelogRequired...)
|
||||
assertFileContainsAll(t, repoFile("docs", "RELEASE_NOTES.md"),
|
||||
"docs/releases/RELEASE_NOTES_v"+version+".md",
|
||||
"docs/releases/V6_CHANGELOG_v"+version+".md",
|
||||
@@ -1188,11 +1202,20 @@ func TestCurrentStablePatchReleasePacketTracksInstallMetadata(t *testing.T) {
|
||||
assertFileContainsAll(t, repoFile("scripts", "install-docker.sh"),
|
||||
`CANONICAL_DEFAULT_PULSE_VERSION="`+version+`"`,
|
||||
)
|
||||
assertFileContainsAllNormalized(t, repoFile("docs", "release-control", "v6", "internal", "subsystems", "deployment-installability.md"),
|
||||
installabilityPath := repoFile("docs", "release-control", "v6", "internal", "subsystems", "deployment-installability.md")
|
||||
installabilityRequired := make([]string, 0, 3)
|
||||
installabilityRequired = append(installabilityRequired,
|
||||
"The active stable `v"+version+"` cut sets the repo-root `VERSION`, repo-root `docker-compose.yml` image default, `scripts/install-docker.sh` fallback, and Helm chart release metadata to the same `"+version+"` release version.",
|
||||
"This patch release uses the stable hotfix path with `rollback_version=v"+previous+"`, `hotfix_exception=true`, a release-owner reason, and no fabricated same-version RC tag.",
|
||||
"For the active stable `v"+version+"` cut, the repo-root compose default and `scripts/install-docker.sh` fallback must both pin `"+version+"`",
|
||||
)
|
||||
if rcDerived {
|
||||
installabilityRequired = append(installabilityRequired, "promoted_from_tag="+promotedTag)
|
||||
} else {
|
||||
installabilityRequired = append(installabilityRequired,
|
||||
"This patch release uses the stable hotfix path with `rollback_version=v"+previous+"`, `hotfix_exception=true`, a release-owner reason, and no fabricated same-version RC tag.",
|
||||
)
|
||||
}
|
||||
assertFileContainsAllNormalized(t, installabilityPath, installabilityRequired...)
|
||||
}
|
||||
|
||||
func TestCurrentStableMinorReleasePacketTracksInstallMetadata(t *testing.T) {
|
||||
|
||||
@@ -41,13 +41,20 @@ func isPrereleaseVersion(version string) bool {
|
||||
return strings.Contains(version, "-")
|
||||
}
|
||||
|
||||
// unpublishedStableVersions lists stable versions whose tag and packet exist in
|
||||
// the repository but which never activated as a public GitHub release. They are
|
||||
// not valid rollback targets and must not be treated as the previous stable.
|
||||
// unpublishedStableVersions lists stable versions that never activated as a
|
||||
// public GitHub release, whether or not a same-version tag or packet was
|
||||
// staged. They are not valid rollback targets and must not be treated as the
|
||||
// previous stable.
|
||||
var unpublishedStableVersions = map[string]bool{
|
||||
// Tagged 2026-08-31; the private Pro build failed its memory gate and the
|
||||
// release commit verdict failed, so the packet shipped through v6.4.3 instead.
|
||||
"6.4.2": true,
|
||||
// v6.4.3 and v6.4.4 were only ever published as prereleases
|
||||
// (v6.4.3-rc.1, v6.4.4-beta.N). The v6.4.5 stable patch promotes the
|
||||
// exercised v6.4.5-rc.N candidate, so the last published stable remains
|
||||
// v6.4.1 and is the rollback target.
|
||||
"6.4.3": true,
|
||||
"6.4.4": true,
|
||||
}
|
||||
|
||||
func previousStablePatchVersion(version string) (string, bool) {
|
||||
@@ -72,6 +79,50 @@ func previousStablePatchVersion(version string) (string, bool) {
|
||||
return "", false
|
||||
}
|
||||
|
||||
// stablePatchPromotedPrerelease reports the same-version release candidate a
|
||||
// stable patch promotes, if one exists. A stable patch with a same-version RC
|
||||
// is RC-derived: the promotion resolver selects promotion_mode
|
||||
// "stable-rc-promotion" and refuses the emergency no-RC hotfix path, so the
|
||||
// install-metadata contract must assert that shape instead of the emergency
|
||||
// one. Detection reads the published candidate notes so it stays offline and
|
||||
// matches the release line's retained metadata.
|
||||
func stablePatchPromotedPrerelease(docsReleasesDir, version string) (string, bool) {
|
||||
if isPrereleaseVersion(version) {
|
||||
return "", false
|
||||
}
|
||||
matches, err := filepath.Glob(filepath.Join(docsReleasesDir, "RELEASE_NOTES_v"+version+"-rc.*.md"))
|
||||
if err != nil {
|
||||
return "", false
|
||||
}
|
||||
best := ""
|
||||
bestNumber := -1
|
||||
for _, match := range matches {
|
||||
filename := filepath.Base(match)
|
||||
tag := strings.TrimSuffix(strings.TrimPrefix(filename, "RELEASE_NOTES_v"), ".md")
|
||||
marker := strings.LastIndex(tag, "-rc.")
|
||||
if marker < 0 {
|
||||
continue
|
||||
}
|
||||
number, err := strconv.Atoi(tag[marker+len("-rc."):])
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if number > bestNumber {
|
||||
bestNumber = number
|
||||
best = "v" + tag
|
||||
}
|
||||
}
|
||||
if best == "" {
|
||||
return "", false
|
||||
}
|
||||
return best, true
|
||||
}
|
||||
|
||||
func currentStablePatchPromotedPrerelease(t *testing.T, version string) (string, bool) {
|
||||
t.Helper()
|
||||
return stablePatchPromotedPrerelease(repoFile("docs", "releases"), version)
|
||||
}
|
||||
|
||||
func previousStableForPrereleaseVersion(version string) (string, bool) {
|
||||
if !isPrereleaseVersion(version) {
|
||||
return "", false
|
||||
@@ -185,6 +236,53 @@ func TestPreviousStableForPrereleaseVersionCrossesMinorBoundaries(t *testing.T)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPreviousStablePatchVersionSkipsUnpublishedStables(t *testing.T) {
|
||||
tests := []struct {
|
||||
version string
|
||||
want string
|
||||
ok bool
|
||||
}{
|
||||
{version: "6.4.1", want: "6.4.0", ok: true},
|
||||
{version: "6.4.2", want: "6.4.1", ok: true},
|
||||
// v6.4.2 was tagged but never published; v6.4.3 and v6.4.4 only ever
|
||||
// shipped as prereleases, so v6.4.5 rolls back to the published v6.4.1.
|
||||
{version: "6.4.5", want: "6.4.1", ok: true},
|
||||
{version: "6.4.0", ok: false},
|
||||
{version: "6.4.5-rc.2", ok: false},
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Run(test.version, func(t *testing.T) {
|
||||
got, ok := previousStablePatchVersion(test.version)
|
||||
if ok != test.ok || got != test.want {
|
||||
t.Fatalf("previousStablePatchVersion(%q) = %q, %v; want %q, %v", test.version, got, ok, test.want, test.ok)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestStablePatchPromotedPrereleaseDetectsSameVersionCandidate(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
for _, name := range []string{
|
||||
"RELEASE_NOTES_v6.4.5-rc.1.md",
|
||||
"RELEASE_NOTES_v6.4.5-rc.2.md",
|
||||
"RELEASE_NOTES_v6.4.4-beta.4.md",
|
||||
"RELEASE_NOTES_v6.4.5-beta.1.md",
|
||||
} {
|
||||
if err := os.WriteFile(filepath.Join(dir, name), []byte("x"), 0o600); err != nil {
|
||||
t.Fatalf("write %s: %v", name, err)
|
||||
}
|
||||
}
|
||||
if got, ok := stablePatchPromotedPrerelease(dir, "6.4.5"); !ok || got != "v6.4.5-rc.2" {
|
||||
t.Fatalf("stablePatchPromotedPrerelease(6.4.5) = %q, %v; want v6.4.5-rc.2, true", got, ok)
|
||||
}
|
||||
if got, ok := stablePatchPromotedPrerelease(dir, "6.4.6"); ok {
|
||||
t.Fatalf("stablePatchPromotedPrerelease(6.4.6) = %q, true; want no same-version candidate", got)
|
||||
}
|
||||
if got, ok := stablePatchPromotedPrerelease(dir, "6.4.5-rc.2"); ok {
|
||||
t.Fatalf("stablePatchPromotedPrerelease(6.4.5-rc.2) = %q, true; want prerelease ignored", got)
|
||||
}
|
||||
}
|
||||
|
||||
func previousPrereleaseVersion(version string) (string, bool) {
|
||||
base, suffix, ok := strings.Cut(version, "-rc.")
|
||||
if !ok {
|
||||
@@ -325,28 +423,42 @@ func TestInstallDockerProofTracksStablePatchReleaseContract(t *testing.T) {
|
||||
if !ok {
|
||||
t.Skip("current release is not a stable patch release")
|
||||
}
|
||||
promotedTag, rcDerived := currentStablePatchPromotedPrerelease(t, version)
|
||||
installabilityPath := repoFile("docs", "release-control", "v6", "internal", "subsystems", "deployment-installability.md")
|
||||
|
||||
assertFileContainsAllNormalized(t, repoFile("docs", "release-control", "v6", "internal", "subsystems", "deployment-installability.md"),
|
||||
required := make([]string, 0, 5)
|
||||
required = append(required,
|
||||
"The active stable `v"+version+"` cut sets the repo-root `VERSION`, repo-root `docker-compose.yml` image default, `scripts/install-docker.sh` fallback, and Helm chart release metadata to the same `"+version+"` release version.",
|
||||
"This patch release uses the stable hotfix path with `rollback_version=v"+previous+"`, `hotfix_exception=true`, a release-owner reason, and no fabricated same-version RC tag.",
|
||||
"active customer harm",
|
||||
"`no-mobile-impact`",
|
||||
"For the active stable `v"+version+"` cut, the repo-root compose default and `scripts/install-docker.sh` fallback must both pin `"+version+"`",
|
||||
)
|
||||
if rcDerived {
|
||||
// An RC-derived stable patch promotes the exercised same-version
|
||||
// candidate, so the active-stable paragraph names the promoted tag
|
||||
// instead of the emergency no-RC hotfix path.
|
||||
required = append(required, "promoted_from_tag="+promotedTag)
|
||||
} else {
|
||||
required = append(required,
|
||||
"This patch release uses the stable hotfix path with `rollback_version=v"+previous+"`, `hotfix_exception=true`, a release-owner reason, and no fabricated same-version RC tag.",
|
||||
"active customer harm",
|
||||
)
|
||||
}
|
||||
assertFileContainsAllNormalized(t, installabilityPath, required...)
|
||||
|
||||
if version == "6.3.1" {
|
||||
assertFileContainsAllNormalized(t, repoFile("docs", "release-control", "v6", "internal", "subsystems", "deployment-installability.md"),
|
||||
assertFileContainsAllNormalized(t, installabilityPath,
|
||||
"the prior `v"+previous+"` decision could not be reused for this patch",
|
||||
"the release owner recorded that separate `v6.3.1` exception",
|
||||
"public Unknown Publisher disclosure",
|
||||
)
|
||||
} else {
|
||||
assertFileContainsAllNormalized(t, repoFile("docs", "release-control", "v6", "internal", "subsystems", "deployment-installability.md"),
|
||||
} else if !rcDerived {
|
||||
assertFileContainsAllNormalized(t, installabilityPath,
|
||||
"standing SignPath-unavailable policy from `v6.3.2` onward",
|
||||
"Public Unknown Publisher disclosure",
|
||||
)
|
||||
}
|
||||
if version == "6.4.2" {
|
||||
assertFileContainsAllNormalized(t, repoFile("docs", "release-control", "v6", "internal", "subsystems", "deployment-installability.md"),
|
||||
assertFileContainsAllNormalized(t, installabilityPath,
|
||||
"The governed branch is `main`",
|
||||
"authenticated non-administrator organization members can reach infrastructure action control",
|
||||
"SSO-only deployments can treat every authenticated IdP user as an instance administrator without an explicit grant",
|
||||
|
||||
Reference in New Issue
Block a user