diff --git a/VERSION b/VERSION index 288b2cd9a..eb3a7fc0e 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -6.0.5 +6.0.5-rc.1 diff --git a/deploy/helm/pulse/Chart.yaml b/deploy/helm/pulse/Chart.yaml index a3c7d0c70..1977dc9be 100644 --- a/deploy/helm/pulse/Chart.yaml +++ b/deploy/helm/pulse/Chart.yaml @@ -2,9 +2,9 @@ apiVersion: v2 name: pulse description: Helm chart for deploying the Pulse hub and optional Docker monitoring agent. type: application -version: 6.0.5 -appVersion: "6.0.5" -icon: https://raw.githubusercontent.com/rcourtman/Pulse/v6.0.5/docs/images/pulse-logo.svg +version: 6.0.5-rc.1 +appVersion: "6.0.5-rc.1" +icon: https://raw.githubusercontent.com/rcourtman/Pulse/v6.0.5-rc.1/docs/images/pulse-logo.svg keywords: - monitoring - proxmox @@ -32,7 +32,7 @@ annotations: description: Smoke tests with kind cluster deployment artifacthub.io/links: | - name: Documentation - url: https://github.com/rcourtman/Pulse/blob/v6.0.5/docs/KUBERNETES.md + url: https://github.com/rcourtman/Pulse/blob/v6.0.5-rc.1/docs/KUBERNETES.md - name: Support url: https://github.com/rcourtman/Pulse/discussions artifacthub.io/maintainers: | diff --git a/deploy/helm/pulse/README.md b/deploy/helm/pulse/README.md index ab7591a90..8b3549139 100644 --- a/deploy/helm/pulse/README.md +++ b/deploy/helm/pulse/README.md @@ -1,6 +1,6 @@ # pulse -![Version: 6.0.5](https://img.shields.io/badge/Version-6.0.5-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 6.0.5](https://img.shields.io/badge/AppVersion-6.0.5-informational?style=flat-square) +![Version: 6.0.5-rc.1](https://img.shields.io/badge/Version-6.0.5--rc.1-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 6.0.5-rc.1](https://img.shields.io/badge/AppVersion-6.0.5--rc.1-informational?style=flat-square) Helm chart for deploying the Pulse hub and optional Docker monitoring agent. diff --git a/docker-compose.yml b/docker-compose.yml index 4bf91e7b9..174d00a29 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -2,7 +2,7 @@ version: '3.8' services: pulse: - image: ${PULSE_IMAGE:-rcourtman/pulse:6.0.5} + image: ${PULSE_IMAGE:-rcourtman/pulse:6.0.5-rc.1} container_name: pulse restart: unless-stopped logging: diff --git a/docs/RELEASE_NOTES.md b/docs/RELEASE_NOTES.md index 4fc87641b..a7032705a 100644 --- a/docs/RELEASE_NOTES.md +++ b/docs/RELEASE_NOTES.md @@ -6,9 +6,11 @@ Pulse release notes live on GitHub: For historical v4 notes that previously lived in this repo, see: `docs/releases/RELEASE_NOTES_v4.md` +For the current v6 support release candidate packet, see: +- `docs/releases/RELEASE_NOTES_v6.0.5-rc.1.md` +- `docs/releases/V6_CHANGELOG_v6.0.5-rc.1.md` + For the current stable v6 patch packet, see: -- `docs/releases/RELEASE_NOTES_v6.0.5.md` -- `docs/releases/V6_CHANGELOG_v6.0.5.md` - `docs/releases/RELEASE_NOTES_v6.0.4.md` - `docs/releases/V6_CHANGELOG_v6.0.4.md` - `docs/releases/RELEASE_NOTES_v6.0.3.md` diff --git a/docs/UPGRADE_v6.md b/docs/UPGRADE_v6.md index 8528bc6ad..9fdd7b962 100644 --- a/docs/UPGRADE_v6.md +++ b/docs/UPGRADE_v6.md @@ -2,10 +2,13 @@ This guide covers practical upgrade steps for existing Pulse installs moving to v6. +For the current v6 support release candidate packet, see: + +- `docs/releases/RELEASE_NOTES_v6.0.5-rc.1.md` +- `docs/releases/V6_CHANGELOG_v6.0.5-rc.1.md` + For the current stable v6 packet and rollout references, see: -- `docs/releases/RELEASE_NOTES_v6.0.5.md` -- `docs/releases/V6_CHANGELOG_v6.0.5.md` - `docs/releases/RELEASE_NOTES_v6.0.4.md` - `docs/releases/V6_CHANGELOG_v6.0.4.md` - `docs/releases/RELEASE_NOTES_v6.0.3.md` diff --git a/docs/release-control/v6/internal/subsystems/deployment-installability.md b/docs/release-control/v6/internal/subsystems/deployment-installability.md index 0a81f0ce7..a5ad78fa7 100644 --- a/docs/release-control/v6/internal/subsystems/deployment-installability.md +++ b/docs/release-control/v6/internal/subsystems/deployment-installability.md @@ -811,13 +811,16 @@ TLS floor in the dynamic config. ## Current State -The active stable `v6.0.5` cut sets the repo-root `VERSION`, +The active support prerelease `v6.0.5-rc.1` 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 `6.0.5` release version. -This patch release uses the stable hotfix path with `rollback_version=v6.0.4`, -`hotfix_exception=true`, a release-owner reason, and no fabricated same-version -RC tag. It advances the v6.0.4 stable line with customer-support fixes for -Gemini Patrol tool-call readiness and remembered-login submit persistence. +fallback, and Helm chart release metadata to the same `6.0.5-rc.1` release +version. This support prerelease keeps `rollback_version=v6.0.4`, publishes a +versioned public GitHub prerelease plus versioned Docker and Helm artifacts, +and does not move stable/latest install pointers or stable semver aliases. It +queues the v6.0.4 stable line's customer-support fixes for Gemini Patrol +tool-call readiness, remembered-login submit persistence, and Proxmox +SATA/SAT SMART temperature fallback behind RC validation instead of publishing +another same-day stable patch. The initial GA promotion metadata remains `promoted_from_tag=v6.0.0-rc.7`, `rollback_version=v5.1.35`, @@ -871,10 +874,11 @@ compose image default, standalone installer fallback constant, and packaged Helm metadata. A draft release workflow failure caused by stale image or chart pins is a release-packet blocker until the defaults, tests, and evidence record are refreshed from the new branch head. -For the active stable `v6.0.5` cut, the repo-root compose default and -`scripts/install-docker.sh` fallback must both pin `6.0.5`. The stable -promotion guard remains in force by rejecting leftover `-rc.` defaults when -the governed `VERSION` is a stable release. +For the active support prerelease `v6.0.5-rc.1` cut, the repo-root compose +default and `scripts/install-docker.sh` fallback must both pin `6.0.5-rc.1` +until the next governed stable cut moves them forward. The stable promotion +guard remains in force by rejecting leftover `-rc.` defaults when the governed +`VERSION` is a stable release. The RC7 packet refresh records `fc10de9b5477613316473267b72b05b6b2b7aaff` as the current validation-risk commit. That head includes the earlier Docker-default correction plus the follow-on capacity-forecast and Patrol diff --git a/docs/releases/RELEASE_NOTES_v6.0.5.md b/docs/releases/RELEASE_NOTES_v6.0.5-rc.1.md similarity index 58% rename from docs/releases/RELEASE_NOTES_v6.0.5.md rename to docs/releases/RELEASE_NOTES_v6.0.5-rc.1.md index 5553f4238..dc9c044ab 100644 --- a/docs/releases/RELEASE_NOTES_v6.0.5.md +++ b/docs/releases/RELEASE_NOTES_v6.0.5-rc.1.md @@ -1,9 +1,9 @@ -# Pulse v6.0.5 Release Notes +# Pulse v6.0.5-rc.1 Release Notes -`v6.0.5` is a stable patch release for the Pulse v6 line. It follows -`v6.0.4` and bundles support fixes for Patrol Gemini model readiness and -remembered-login submit persistence, plus a Proxmox SMART temperature fallback -for direct SATA/SAT disks. +`v6.0.5-rc.1` is a release candidate for the next Pulse v6 patch line. It +follows stable `v6.0.4` and packages support fixes for Patrol Gemini model +readiness and remembered-login submit persistence, plus a Proxmox SMART +temperature fallback for direct SATA/SAT disks. ## Fixes @@ -14,13 +14,14 @@ for direct SATA/SAT disks. - Fixed Proxmox SMART temperature collection for direct SATA/SAT disks where smartctl auto-detection returned disk health but no temperature until retried with an explicit SAT probe. -- Refreshed Docker, Helm, installer, and release-helper metadata for the stable - patch line. +- Refreshed Docker, Helm, installer, and release-helper metadata for the + release candidate. ## Upgrade Notes -Use the normal v6 install or update flow for `v6.0.5`. The rollback target for -this patch release is `v6.0.4`. +Use the normal v6 install or update flow for `v6.0.5-rc.1` only when you are +comfortable testing an RC. The rollback target for this release candidate is +`v6.0.4`. Paid Pulse Pro, Relay, and eligible legacy customers should continue to use the private download page and private runtime image for paid runtime features. diff --git a/docs/releases/V6_CHANGELOG_v6.0.5.md b/docs/releases/V6_CHANGELOG_v6.0.5-rc.1.md similarity index 71% rename from docs/releases/V6_CHANGELOG_v6.0.5.md rename to docs/releases/V6_CHANGELOG_v6.0.5-rc.1.md index 3ed418256..2fb5c238b 100644 --- a/docs/releases/V6_CHANGELOG_v6.0.5.md +++ b/docs/releases/V6_CHANGELOG_v6.0.5-rc.1.md @@ -1,7 +1,7 @@ -# Pulse v6.0.5 +# Pulse v6.0.5-rc.1 -_This changelog describes the stable `v6.0.5` patch release compared with -`v6.0.4`._ +_This changelog describes the `v6.0.5-rc.1` release candidate compared with +stable `v6.0.4`._ ## Fixed @@ -13,10 +13,10 @@ _This changelog describes the stable `v6.0.5` patch release compared with explicit SAT probe when smartctl auto-detection returns health but no temperature. - Docker, Helm, installer, and release-helper metadata now track the active - stable patch version. + release candidate version. ## Release Metadata -- Version: `v6.0.5` +- Version: `v6.0.5-rc.1` - Rollback target: `v6.0.4` -- Promotion path: stable patch hotfix from `main` +- Promotion path: release candidate from `main` diff --git a/scripts/install-docker.sh b/scripts/install-docker.sh index 16735b0c5..9a336de0c 100755 --- a/scripts/install-docker.sh +++ b/scripts/install-docker.sh @@ -6,7 +6,7 @@ set -euo pipefail SCRIPT_DIR="$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)" DOCKER_IMAGE_REPO="${DOCKER_IMAGE_REPO:-rcourtman/pulse}" -CANONICAL_DEFAULT_PULSE_VERSION="6.0.5" +CANONICAL_DEFAULT_PULSE_VERSION="6.0.5-rc.1" resolve_default_pulse_version() { if [ -n "${PULSE_IMAGE_VERSION:-}" ]; then diff --git a/scripts/installtests/build_release_assets_test.go b/scripts/installtests/build_release_assets_test.go index 17393a075..5ce71582f 100644 --- a/scripts/installtests/build_release_assets_test.go +++ b/scripts/installtests/build_release_assets_test.go @@ -308,6 +308,71 @@ func TestCurrentStablePatchReleasePacketTracksInstallMetadata(t *testing.T) { ) } +func TestCurrentSupportPrereleasePacketTracksInstallMetadata(t *testing.T) { + version := currentReleaseVersion(t) + if !isPrereleaseVersion(version) { + t.Skip("current release is stable") + } + previous, ok := previousStableForPrereleaseVersion(version) + if !ok { + t.Skip("current prerelease does not have a previous stable patch") + } + + releaseNotesPath := repoFile("docs", "releases", "RELEASE_NOTES_v"+version+".md") + changelogPath := repoFile("docs", "releases", "V6_CHANGELOG_v"+version+".md") + + assertFileContainsAllNormalized(t, releaseNotesPath, + "`v"+version+"` is a release candidate", + "stable `v"+previous+"`", + "Patrol Gemini model readiness", + "remembered-login submit persistence", + "Proxmox SMART temperature fallback", + "direct SATA/SAT disks", + "rollback target for this release candidate is `v"+previous+"`", + ) + assertFileContainsAll(t, changelogPath, + "Version: `v"+version+"`", + "Rollback target: `v"+previous+"`", + "Promotion path: release candidate from `main`", + "Gemini tool-call capability", + "Remembered-login state now persists", + "Proxmox SMART temperature collection", + "explicit SAT probe", + ) + assertFileContainsAll(t, repoFile("docs", "RELEASE_NOTES.md"), + "docs/releases/RELEASE_NOTES_v"+version+".md", + "docs/releases/V6_CHANGELOG_v"+version+".md", + "current v6 support release candidate packet", + ) + assertFileContainsAll(t, repoFile("docs", "UPGRADE_v6.md"), + "docs/releases/RELEASE_NOTES_v"+version+".md", + "docs/releases/V6_CHANGELOG_v"+version+".md", + "current v6 support release candidate packet", + ) + assertFileContainsAll(t, repoFile("deploy", "helm", "pulse", "Chart.yaml"), + "version: "+version, + `appVersion: "`+version+`"`, + "raw.githubusercontent.com/rcourtman/Pulse/v"+version+"/docs/images/pulse-logo.svg", + "blob/v"+version+"/docs/KUBERNETES.md", + ) + assertFileContainsAll(t, repoFile("deploy", "helm", "pulse", "README.md"), + "Version-"+shieldsBadgeMessage(version)+"-informational", + "AppVersion-"+shieldsBadgeMessage(version)+"-informational", + "Autogenerated from chart metadata using [helm-docs v1.14.2]", + ) + assertFileContainsAll(t, repoFile("docker-compose.yml"), + "image: ${PULSE_IMAGE:-rcourtman/pulse:"+version+"}", + ) + assertFileContainsAll(t, repoFile("scripts", "install-docker.sh"), + `CANONICAL_DEFAULT_PULSE_VERSION="`+version+`"`, + ) + assertFileContainsAllNormalized(t, repoFile("docs", "release-control", "v6", "internal", "subsystems", "deployment-installability.md"), + "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.", + ) +} + func TestBackfillReleaseWorkflowRepairsPublishedAssetsWithoutRebuilds(t *testing.T) { scriptBytes, err := os.ReadFile(repoFile("scripts", "backfill-release-assets.sh")) if err != nil { diff --git a/scripts/installtests/install_docker_sh_test.go b/scripts/installtests/install_docker_sh_test.go index 7ffd416c5..8b8b3782b 100644 --- a/scripts/installtests/install_docker_sh_test.go +++ b/scripts/installtests/install_docker_sh_test.go @@ -228,7 +228,7 @@ func TestInstallDockerProofTracksSupportPrereleaseContract(t *testing.T) { assertFileContainsAllNormalized(t, repoFile("docs", "release-control", "v6", "internal", "subsystems", "deployment-installability.md"), "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 uses the private Pro support image path with a stable rollback reference `v"+previous+"`, no `latest` movement, no stable semver tag movement, no R2 manifest promotion, no broker download metadata promotion, and no public `rcourtman/pulse` image publication.", + "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.", ) } diff --git a/scripts/release_control/release_promotion_policy_test.py b/scripts/release_control/release_promotion_policy_test.py index cb4abb61a..7bdddf7b4 100644 --- a/scripts/release_control/release_promotion_policy_test.py +++ b/scripts/release_control/release_promotion_policy_test.py @@ -78,6 +78,18 @@ def rc_packet_paths_for_version(version: str) -> tuple[str, str, str] | None: ) +def support_prerelease_packet_paths_for_version(version: str) -> tuple[str, str] | None: + """Return release-notes and changelog paths for post-GA support RC versions.""" + if not re.match(r"^6\.\d+\.\d+-rc\.\d+$", version): + return None + if version.startswith("6.0.0-rc."): + return None + return ( + f"docs/releases/RELEASE_NOTES_v{version}.md", + f"docs/releases/V6_CHANGELOG_v{version}.md", + ) + + def stable_packet_paths_for_version(version: str) -> tuple[str, str] | None: """Return the stable release-notes and changelog packet paths for a v6 stable VERSION.""" if not re.match(r"^6\.\d+\.\d+$", version): @@ -350,25 +362,40 @@ class ReleasePromotionPolicyTest(unittest.TestCase): self.assertIn(f"Pulse v{current_version}", changelog) else: packet_paths = rc_packet_paths_for_version(current_version) - self.assertIsNotNone( - packet_paths, - f"VERSION={current_version} does not match the 6.0.0-rc.N pattern", + support_packet_paths = support_prerelease_packet_paths_for_version(current_version) + self.assertTrue( + packet_paths is not None or support_packet_paths is not None, + f"VERSION={current_version} does not match a governed v6 prerelease packet pattern", ) - release_notes_path, changelog_path, operator_pack_path = packet_paths + if packet_paths is not None: + release_notes_path, changelog_path, operator_pack_path = packet_paths - release_notes = read(release_notes_path) - changelog = read(changelog_path) - operator_pack = read(operator_pack_path) + release_notes = read(release_notes_path) + changelog = read(changelog_path) + operator_pack = read(operator_pack_path) - self.assertIn(f"current in-repo v6 `rc.{current_version.rsplit('.', 1)[1]}` draft packet", release_index) - self.assertIn(release_notes_path, release_index) - self.assertIn(changelog_path, release_index) - self.assertIn(operator_pack_path, release_index) - self.assertNotIn("current stable v6 release packet", release_index) - self.assertIn(f"Pulse v{current_version} Draft Release Notes", release_notes) - self.assertIn(f"`v{current_version}`", release_notes) - self.assertIn(f"Pulse v{current_version} Draft Changelog", changelog) - self.assertIn(f"`v{current_version}`", operator_pack) + self.assertIn(f"current in-repo v6 `rc.{current_version.rsplit('.', 1)[1]}` draft packet", release_index) + self.assertIn(release_notes_path, release_index) + self.assertIn(changelog_path, release_index) + self.assertIn(operator_pack_path, release_index) + self.assertNotIn("current stable v6 release packet", release_index) + self.assertIn(f"Pulse v{current_version} Draft Release Notes", release_notes) + self.assertIn(f"`v{current_version}`", release_notes) + self.assertIn(f"Pulse v{current_version} Draft Changelog", changelog) + self.assertIn(f"`v{current_version}`", operator_pack) + else: + assert support_packet_paths is not None + release_notes_path, changelog_path = support_packet_paths + + release_notes = read(release_notes_path) + changelog = read(changelog_path) + + self.assertIn("current v6 support release candidate packet", release_index) + self.assertIn(release_notes_path, release_index) + self.assertIn(changelog_path, release_index) + self.assertIn(f"Pulse v{current_version} Release Notes", release_notes) + self.assertIn(f"`v{current_version}`", release_notes) + self.assertIn(f"Pulse v{current_version}", changelog) def test_upgrade_guide_points_at_current_rc_support_pack(self) -> None: upgrade_guide = read("docs/UPGRADE_v6.md") @@ -400,17 +427,27 @@ class ReleasePromotionPolicyTest(unittest.TestCase): self.assertNotIn("docs/releases/V6_RC_OPERATOR_SUPPORT_PACK.md", upgrade_guide) else: packet_paths = rc_packet_paths_for_version(current_version) - self.assertIsNotNone( - packet_paths, - f"VERSION={current_version} does not match the 6.0.0-rc.N pattern", + support_packet_paths = support_prerelease_packet_paths_for_version(current_version) + self.assertTrue( + packet_paths is not None or support_packet_paths is not None, + f"VERSION={current_version} does not match a governed v6 prerelease packet pattern", ) - current_support_pack = packet_paths[2] - self.assertIn(current_support_pack, upgrade_guide) - for _, _, _, support_pack in discover_rc_draft_packets(): - if support_pack == current_support_pack: - continue - self.assertNotIn(support_pack, upgrade_guide) - self.assertNotIn("docs/releases/V6_RC_OPERATOR_SUPPORT_PACK.md", upgrade_guide) + if packet_paths is not None: + current_support_pack = packet_paths[2] + self.assertIn(current_support_pack, upgrade_guide) + for _, _, _, support_pack in discover_rc_draft_packets(): + if support_pack == current_support_pack: + continue + self.assertNotIn(support_pack, upgrade_guide) + self.assertNotIn("docs/releases/V6_RC_OPERATOR_SUPPORT_PACK.md", upgrade_guide) + else: + assert support_packet_paths is not None + release_notes_path, changelog_path = support_packet_paths + self.assertIn(release_notes_path, upgrade_guide) + self.assertIn(changelog_path, upgrade_guide) + for _, _, _, support_pack in discover_rc_draft_packets(): + self.assertNotIn(support_pack, upgrade_guide) + self.assertNotIn("docs/releases/V6_RC_OPERATOR_SUPPORT_PACK.md", upgrade_guide) def test_prerelease_feedback_template_uses_generic_current_rc_wording(self) -> None: template = read(".github/ISSUE_TEMPLATE/v6_rc_feedback.yml") @@ -855,9 +892,10 @@ class ReleasePromotionPolicyTest(unittest.TestCase): if current_version != "6.0.0": self.assertNotIn(f"VERSION={current_version}", blocked) else: - self.assertIsNotNone( - rc_packet_paths_for_version(current_version), - f"VERSION={current_version} does not match the 6.0.0-rc.N pattern", + self.assertTrue( + rc_packet_paths_for_version(current_version) is not None + or support_prerelease_packet_paths_for_version(current_version) is not None, + f"VERSION={current_version} does not match a governed v6 prerelease packet pattern", ) self.assertIn("VERSION=6.0.0", blocked) self.assertNotIn(f"VERSION={current_version}", blocked)