From 310ee94b0d85e74f16d19ed034b69ce42d875dca Mon Sep 17 00:00:00 2001 From: rcourtman Date: Mon, 13 Jul 2026 19:41:41 +0100 Subject: [PATCH] Refresh Pulse 6.1 RC1 mobile evidence --- .../subsystems/deployment-installability.md | 4 +- docs/releases/RELEASE_NOTES_v6.1.0-rc.1.md | 2 +- docs/releases/V6_CHANGELOG_v6.1.0-rc.1.md | 2 +- .../installtests/build_release_assets_test.go | 6 +- .../installtests/docker_entrypoint_test.go | 7 +- .../installtests/install_docker_sh_test.go | 81 ++++++++++++++++++- 6 files changed, 92 insertions(+), 10 deletions(-) diff --git a/docs/release-control/v6/internal/subsystems/deployment-installability.md b/docs/release-control/v6/internal/subsystems/deployment-installability.md index b76344624..8c649c58f 100644 --- a/docs/release-control/v6/internal/subsystems/deployment-installability.md +++ b/docs/release-control/v6/internal/subsystems/deployment-installability.md @@ -1010,8 +1010,8 @@ operator-facing Actions inbox, monitor-first product workflows, governed host and storage operations, native-agent update safety, Windows logged-readiness and recovery proof, OIDC callback recovery, and fail-closed security hardening behind RC validation before the next stable minor release. -The companion evidence for this cut is Pulse Mobile iOS build 8 and Android -versionCode 7 on TestFlight and Google Play internal testing only. The release +The companion evidence for this cut is Pulse Mobile iOS build 9 and Android +versionCode 8 on TestFlight and Google Play internal testing only. The release packet must not describe either candidate as a public store rollout. The same release boundary now provides one canonical in-app release-note experience. Update checks can preview a curated `Highlights` section, and an diff --git a/docs/releases/RELEASE_NOTES_v6.1.0-rc.1.md b/docs/releases/RELEASE_NOTES_v6.1.0-rc.1.md index fc414bb17..93017bafa 100644 --- a/docs/releases/RELEASE_NOTES_v6.1.0-rc.1.md +++ b/docs/releases/RELEASE_NOTES_v6.1.0-rc.1.md @@ -117,7 +117,7 @@ This candidate changes authentication and native installer/updater boundaries, so it is intentionally using the governed RC path rather than the direct stable-patch path. -Pulse Mobile iOS candidate build 8 and Android candidate versionCode 7 carry +Pulse Mobile iOS candidate build 9 and Android candidate versionCode 8 carry the matching plan-bound action review and approval client. They remain on the TestFlight and Google Play internal-testing tracks; no public store rollout is part of this RC. diff --git a/docs/releases/V6_CHANGELOG_v6.1.0-rc.1.md b/docs/releases/V6_CHANGELOG_v6.1.0-rc.1.md index cb5f42028..84e1ff034 100644 --- a/docs/releases/V6_CHANGELOG_v6.1.0-rc.1.md +++ b/docs/releases/V6_CHANGELOG_v6.1.0-rc.1.md @@ -111,5 +111,5 @@ stable `v6.0.5`._ - Rollback target: `v6.0.5` - Rollback command: `./scripts/install.sh --version v6.0.5` - Promotion path: release candidate from `main` -- Mobile companion candidates: iOS build 8 and Android versionCode 7 on the +- Mobile companion candidates: iOS build 9 and Android versionCode 8 on the TestFlight and Google Play internal-testing tracks diff --git a/scripts/installtests/build_release_assets_test.go b/scripts/installtests/build_release_assets_test.go index 4f2637ffe..27725ea50 100644 --- a/scripts/installtests/build_release_assets_test.go +++ b/scripts/installtests/build_release_assets_test.go @@ -346,7 +346,7 @@ func TestCurrentSupportPrereleasePacketTracksInstallMetadata(t *testing.T) { "Legacy OIDC callbacks", "Windows service recovery", "fail closed", - "Pulse Mobile iOS candidate build 8 and Android candidate versionCode 7", + "Pulse Mobile iOS candidate build 9 and Android candidate versionCode 8", "TestFlight and Google Play internal-testing tracks", "rollback target for this release candidate is `v"+previous+"`", ) @@ -370,7 +370,7 @@ func TestCurrentSupportPrereleasePacketTracksInstallMetadata(t *testing.T) { "Windows service installation fails closed", "First-run security boundaries now fail closed", "one signed exact-SHA candidate", - "Mobile companion candidates: iOS build 8 and Android versionCode 7", + "Mobile companion candidates: iOS build 9 and Android versionCode 8", "TestFlight and Google Play internal-testing tracks", ) assertFileContainsAll(t, repoFile("docs", "RELEASE_NOTES.md"), @@ -404,7 +404,7 @@ func TestCurrentSupportPrereleasePacketTracksInstallMetadata(t *testing.T) { "The active support prerelease `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 support prerelease keeps `rollback_version=v"+previous+"`, publishes a versioned public GitHub prerelease plus versioned Docker and Helm artifacts, and does not move stable/latest install pointers or stable semver aliases.", "For the active support prerelease `v"+version+"` cut, the repo-root compose default and `scripts/install-docker.sh` fallback must both pin `"+version+"` until the next governed stable cut moves them forward.", - "The companion evidence for this cut is Pulse Mobile iOS build 8 and Android versionCode 7 on TestFlight and Google Play internal testing only. The release packet must not describe either candidate as a public store rollout.", + "The companion evidence for this cut is Pulse Mobile iOS build 9 and Android versionCode 8 on TestFlight and Google Play internal testing only. The release packet must not describe either candidate as a public store rollout.", ) } diff --git a/scripts/installtests/docker_entrypoint_test.go b/scripts/installtests/docker_entrypoint_test.go index fb8599139..1cb44fff8 100644 --- a/scripts/installtests/docker_entrypoint_test.go +++ b/scripts/installtests/docker_entrypoint_test.go @@ -132,12 +132,17 @@ exit 0 if err := os.WriteFile(chownStub, []byte(chownScript), 0o755); err != nil { t.Fatalf("write chown stub: %v", err) } + systemChown, err := exec.LookPath("chown") + if err != nil { + t.Fatalf("find system chown: %v", err) + } shell := prefix + ` set -eu root="` + root + `" uid="$(id -u)" gid="$(id -g)" +"` + systemChown + `" "$uid:$gid" "$root" export CHOWN_LOG="` + chownLog + `" export PATH="` + binDir + `:$PATH" chown_tree_if_owner_mismatch pulse:pulse "$uid" "$gid" "$root" @@ -150,7 +155,7 @@ chown_tree_if_owner_mismatch pulse:pulse "$uid" "$gid" "$root" } if logData, err := os.ReadFile(chownLog); err == nil && len(logData) > 0 { - t.Fatalf("already-owned tree was chowned:\n%s", logData) + t.Fatalf("already-owned tree was chowned:\n%s\nhelper output:\n%s", logData, out) } else if err != nil && !os.IsNotExist(err) { t.Fatalf("read chown log: %v", err) } diff --git a/scripts/installtests/install_docker_sh_test.go b/scripts/installtests/install_docker_sh_test.go index 12808dd2f..489308163 100644 --- a/scripts/installtests/install_docker_sh_test.go +++ b/scripts/installtests/install_docker_sh_test.go @@ -64,7 +64,84 @@ func previousStableForPrereleaseVersion(version string) (string, bool) { if !ok { return "", false } - return previousStablePatchVersion(base) + baseParts, ok := parseStableVersion(base) + if !ok { + return "", false + } + + releaseNotes, err := filepath.Glob(repoFile("docs", "releases", "RELEASE_NOTES_v*.md")) + if err != nil { + return "", false + } + + best := [3]int{-1, -1, -1} + found := false + for _, releaseNote := range releaseNotes { + filename := filepath.Base(releaseNote) + candidate := strings.TrimSuffix(strings.TrimPrefix(filename, "RELEASE_NOTES_v"), ".md") + parts, valid := parseStableVersion(candidate) + if !valid || compareStableVersions(parts, baseParts) >= 0 { + continue + } + if !found || compareStableVersions(parts, best) > 0 { + best = parts + found = true + } + } + if !found { + return "", false + } + return fmt.Sprintf("%d.%d.%d", best[0], best[1], best[2]), true +} + +func parseStableVersion(version string) ([3]int, bool) { + parts := strings.Split(version, ".") + if len(parts) != 3 { + return [3]int{}, false + } + parsed := [3]int{} + for index, part := range parts { + value, err := strconv.Atoi(part) + if err != nil || value < 0 { + return [3]int{}, false + } + parsed[index] = value + } + return parsed, true +} + +func compareStableVersions(left, right [3]int) int { + for index := range left { + if left[index] < right[index] { + return -1 + } + if left[index] > right[index] { + return 1 + } + } + return 0 +} + +func TestPreviousStableForPrereleaseVersionCrossesMinorBoundaries(t *testing.T) { + tests := []struct { + version string + want string + }{ + {version: "6.0.5-rc.4", want: "6.0.4"}, + {version: "6.1.0-rc.1", want: "6.0.5"}, + } + + for _, test := range tests { + t.Run(test.version, func(t *testing.T) { + got, ok := previousStableForPrereleaseVersion(test.version) + if !ok { + t.Fatalf("previousStableForPrereleaseVersion(%q) did not find a stable release", test.version) + } + if got != test.want { + t.Fatalf("previousStableForPrereleaseVersion(%q) = %q, want %q", test.version, got, test.want) + } + }) + } } func previousPrereleaseVersion(version string) (string, bool) { @@ -232,7 +309,7 @@ func TestInstallDockerProofTracksSupportPrereleaseContract(t *testing.T) { "The active support prerelease `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 support prerelease keeps `rollback_version=v"+previous+"`, publishes a versioned public GitHub prerelease plus versioned Docker and Helm artifacts, and does not move stable/latest install pointers or stable semver aliases.", "the expanded Pulse Intelligence action and verification lifecycle, the operator-facing Actions inbox, monitor-first product workflows, governed host and storage operations, native-agent update safety, Windows logged-readiness and recovery proof, OIDC callback recovery, and fail-closed security hardening behind RC validation", - "The companion evidence for this cut is Pulse Mobile iOS build 8 and Android versionCode 7 on TestFlight and Google Play internal testing only. The release packet must not describe either candidate as a public store rollout.", + "The companion evidence for this cut is Pulse Mobile iOS build 9 and Android versionCode 8 on TestFlight and Google Play internal testing only. The release packet must not describe either candidate as a public store rollout.", "For the active support prerelease `v"+version+"` cut, the repo-root compose default and `scripts/install-docker.sh` fallback must both pin `"+version+"` until the next governed stable cut moves them forward.", ) }