Prepare v6.0.2 release
Some checks are pending
Build and Test / Secret Scan (push) Waiting to run
Build and Test / Frontend & Backend (push) Waiting to run

This commit is contained in:
rcourtman 2026-07-04 23:19:42 +01:00
parent d029e4dc42
commit c0ae794dc9
13 changed files with 110 additions and 36 deletions

View file

@ -1 +1 @@
6.0.1
6.0.2

View file

@ -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.1
appVersion: "6.0.1"
icon: https://raw.githubusercontent.com/rcourtman/Pulse/v6.0.1/docs/images/pulse-logo.svg
version: 6.0.2
appVersion: "6.0.2"
icon: https://raw.githubusercontent.com/rcourtman/Pulse/v6.0.2/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.1/docs/KUBERNETES.md
url: https://github.com/rcourtman/Pulse/blob/v6.0.2/docs/KUBERNETES.md
- name: Support
url: https://github.com/rcourtman/Pulse/discussions
artifacthub.io/maintainers: |

View file

@ -1,6 +1,6 @@
# pulse
![Version: 6.0.1](https://img.shields.io/badge/Version-6.0.1-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 6.0.1](https://img.shields.io/badge/AppVersion-6.0.1-informational?style=flat-square)
![Version: 6.0.2](https://img.shields.io/badge/Version-6.0.2-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 6.0.2](https://img.shields.io/badge/AppVersion-6.0.2-informational?style=flat-square)
Helm chart for deploying the Pulse hub and optional Docker monitoring agent.

View file

@ -2,7 +2,7 @@ version: '3.8'
services:
pulse:
image: ${PULSE_IMAGE:-rcourtman/pulse:6.0.1}
image: ${PULSE_IMAGE:-rcourtman/pulse:6.0.2}
container_name: pulse
restart: unless-stopped
logging:

View file

@ -7,8 +7,8 @@ For historical v4 notes that previously lived in this repo, see:
`docs/releases/RELEASE_NOTES_v4.md`
For the current stable v6 patch packet, see:
- `docs/releases/RELEASE_NOTES_v6.0.1.md`
- `docs/releases/V6_CHANGELOG_v6.0.1.md`
- `docs/releases/RELEASE_NOTES_v6.0.2.md`
- `docs/releases/V6_CHANGELOG_v6.0.2.md`
- `docs/UPGRADE_v6.md`
For the first stable v6 release packet, see:

View file

@ -4,8 +4,8 @@ This guide covers practical upgrade steps for existing Pulse installs moving to
For the current stable v6 packet and rollout references, see:
- `docs/releases/RELEASE_NOTES_v6.0.1.md`
- `docs/releases/V6_CHANGELOG_v6.0.1.md`
- `docs/releases/RELEASE_NOTES_v6.0.2.md`
- `docs/releases/V6_CHANGELOG_v6.0.2.md`
- `docs/releases/RELEASE_NOTES_v6.md`
- `docs/releases/V6_CHANGELOG.md`

View file

@ -781,10 +781,10 @@ TLS floor in the dynamic config.
## Current State
The active stable `v6.0.1` cut sets the repo-root `VERSION`, repo-root
The active stable `v6.0.2` 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.1` release version. This patch
release uses the stable hotfix path with `rollback_version=v6.0.0`,
Helm chart release metadata to the same `6.0.2` release version. This patch
release uses the stable hotfix path with `rollback_version=v6.0.1`,
`hotfix_exception=true`, a release-owner reason, and no fabricated
same-version RC tag. The initial GA promotion metadata remains
`promoted_from_tag=v6.0.0-rc.7`, `rollback_version=v5.1.35`,
@ -838,8 +838,8 @@ 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.1` cut, the repo-root compose default and
`scripts/install-docker.sh` fallback must both pin `6.0.1`; the stable
For the active stable `v6.0.2` cut, the repo-root compose default and
`scripts/install-docker.sh` fallback must both pin `6.0.2`; 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`

View file

@ -0,0 +1,25 @@
# Pulse v6.0.2 Release Notes
`v6.0.2` is a stable patch release for the Pulse v6 line. It follows
`v6.0.1` and focuses on agent upgrade recovery, release publication, and demo
verification after the GA cutover.
## Fixes
- Fixed legacy agent update recovery when an installed agent has connection
details in its running process but no persisted v6 connection state. The
generated update command can now recover the URL and token instead of
failing with "No existing Pulse Agent connection state found."
- Restored release asset validation for stable patch hotfixes so a patch can
publish without inventing a same-version prerelease tag when the explicit
hotfix path is selected.
- Restored demo verification entitlement handling after release updates so the
public demo can recover its fixture entitlement and verify mock mode after
deployment.
- Refreshed Helm chart release metadata and documentation for the stable patch
line.
## Upgrade Notes
Use the normal v6 install or update flow for `v6.0.2`. The rollback target for
this patch release is `v6.0.1`.

View file

@ -0,0 +1,20 @@
# Pulse v6.0.2
_This changelog describes the stable `v6.0.2` patch release compared with
`v6.0.1`._
## Fixed
- Legacy agent updates now recover connection details from the running agent
process when the persisted v6 state file is missing or stale.
- Stable patch release validation now supports the governed hotfix path without
requiring a fabricated same-version RC tag.
- Demo update verification now restores the fixture entitlement before checking
mock-mode access after release deployment.
- Helm chart release metadata now tracks the active stable patch version.
## Release Metadata
- Version: `v6.0.2`
- Rollback target: `v6.0.1`
- Promotion path: stable patch hotfix from `pulse/v6-release`

View file

@ -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.1"
CANONICAL_DEFAULT_PULSE_VERSION="6.0.2"
resolve_default_pulse_version() {
if [ -n "${PULSE_IMAGE_VERSION:-}" ]; then

View file

@ -673,6 +673,9 @@ func TestDeploymentDefaultsPinVersionedImagesAndHelmDocsChecksum(t *testing.T) {
t.Fatalf("Helm Chart.yaml must pin the governed release version, missing %s:\n%s", needle, chart)
}
}
if previous, ok := previousStablePatchVersion(version); ok && strings.Contains(chart, "v"+previous) {
t.Fatalf("Helm Chart.yaml must not retain the previous stable patch tag v%s:\n%s", previous, chart)
}
chartReadmeBytes, err := os.ReadFile(repoFile("deploy", "helm", "pulse", "README.md"))
if err != nil {
@ -688,6 +691,9 @@ func TestDeploymentDefaultsPinVersionedImagesAndHelmDocsChecksum(t *testing.T) {
t.Fatalf("Helm README.md must reflect the governed release version, missing %s:\n%s", needle, chartReadme)
}
}
if previous, ok := previousStablePatchVersion(version); ok && strings.Contains(chartReadme, previous) {
t.Fatalf("Helm README.md must not retain the previous stable patch version %s:\n%s", previous, chartReadme)
}
helmPagesBytes, err := os.ReadFile(repoFile(".github", "workflows", "helm-pages.yml"))
if err != nil {

View file

@ -1,9 +1,11 @@
package installtests
import (
"fmt"
"os"
"os/exec"
"path/filepath"
"strconv"
"strings"
"testing"
)
@ -25,6 +27,21 @@ func isPrereleaseVersion(version string) bool {
return strings.Contains(version, "-")
}
func previousStablePatchVersion(version string) (string, bool) {
if isPrereleaseVersion(version) {
return "", false
}
parts := strings.Split(version, ".")
if len(parts) != 3 {
return "", false
}
patch, err := strconv.Atoi(parts[2])
if err != nil || patch <= 0 {
return "", false
}
return fmt.Sprintf("%s.%s.%d", parts[0], parts[1], patch-1), true
}
func TestInstallDockerScriptUsesConfiguredImageRepoDefault(t *testing.T) {
workDir := t.TempDir()
version := currentReleaseVersion(t)
@ -105,6 +122,9 @@ func TestRepoDockerComposeDefaultPinsCurrentVersion(t *testing.T) {
if !isPrereleaseVersion(version) && version != "6.0.0" && strings.Contains(text, "rcourtman/pulse:6.0.0") {
t.Fatalf("stable patch repo docker-compose.yml must move off the initial GA image tag:\n%s", text)
}
if previous, ok := previousStablePatchVersion(version); ok && strings.Contains(text, "rcourtman/pulse:"+previous) {
t.Fatalf("repo docker-compose.yml must not retain the previous stable patch image tag %s:\n%s", previous, text)
}
if strings.Contains(text, ":latest") {
t.Fatalf("repo docker-compose.yml must not default to a floating latest tag:\n%s", text)
}
@ -130,6 +150,9 @@ func TestInstallDockerScriptFallbackPinsCurrentVersion(t *testing.T) {
if !isPrereleaseVersion(version) && version != "6.0.0" && strings.Contains(text, `CANONICAL_DEFAULT_PULSE_VERSION="6.0.0"`) {
t.Fatalf("stable patch install-docker.sh fallback must move off the initial GA image tag:\n%s", text)
}
if previous, ok := previousStablePatchVersion(version); ok && strings.Contains(text, `CANONICAL_DEFAULT_PULSE_VERSION="`+previous+`"`) {
t.Fatalf("install-docker.sh fallback must not retain the previous stable patch version %s:\n%s", previous, text)
}
}
func runInstallDockerScript(t *testing.T, workDir string, envVars ...string) {

View file

@ -111,61 +111,61 @@ class ResolveReleasePromotionTest(unittest.TestCase):
def test_stable_patch_hotfix_can_omit_promoted_prerelease(self) -> None:
metadata = resolver.resolve_metadata(
version="6.0.1",
version="6.0.2",
promoted_from_tag_input="",
rollback_version_input="6.0.0",
rollback_version_input="6.0.1",
ga_date_input="",
v5_eos_date_input="",
hotfix_exception=True,
hotfix_reason_input="Patch release for v6.0.0 upgrade and monitoring fixes.",
hotfix_reason_input="Patch release for v6.0.1 agent upgrade recovery.",
release_notes_input="",
tag_exists_fn=lambda tag: tag == "v6.0.0",
tag_exists_fn=lambda tag: tag == "v6.0.1",
)
self.assertEqual(metadata["promoted_from_tag"], "")
self.assertEqual(metadata["rollback_tag"], "v6.0.0")
self.assertEqual(metadata["rollback_command"], "./scripts/install.sh --version v6.0.0")
self.assertEqual(metadata["rollback_tag"], "v6.0.1")
self.assertEqual(metadata["rollback_command"], "./scripts/install.sh --version v6.0.1")
self.assertEqual(metadata["hotfix_exception"], "true")
self.assertEqual(
metadata["hotfix_reason"],
"Patch release for v6.0.0 upgrade and monitoring fixes.",
"Patch release for v6.0.1 agent upgrade recovery.",
)
self.assertEqual(metadata["soak_hours"], "")
def test_stable_patch_without_promoted_tag_requires_hotfix_exception(self) -> None:
with self.assertRaisesRegex(ValueError, "Stable promotion requires promoted_from_tag"):
resolver.resolve_metadata(
version="6.0.1",
version="6.0.2",
promoted_from_tag_input="",
rollback_version_input="6.0.0",
rollback_version_input="6.0.1",
ga_date_input="",
v5_eos_date_input="",
hotfix_exception=False,
hotfix_reason_input="",
release_notes_input="",
tag_exists_fn=lambda tag: tag == "v6.0.0",
tag_exists_fn=lambda tag: tag == "v6.0.1",
)
def test_stable_patch_hotfix_without_promoted_tag_requires_reason(self) -> None:
with self.assertRaisesRegex(ValueError, "hotfix_reason is required"):
resolver.resolve_metadata(
version="6.0.1",
version="6.0.2",
promoted_from_tag_input="",
rollback_version_input="6.0.0",
rollback_version_input="6.0.1",
ga_date_input="",
v5_eos_date_input="",
hotfix_exception=True,
hotfix_reason_input="",
release_notes_input="",
tag_exists_fn=lambda tag: tag == "v6.0.0",
tag_exists_fn=lambda tag: tag == "v6.0.1",
)
def test_stable_hotfix_requires_reason(self) -> None:
with self.assertRaisesRegex(ValueError, "hotfix_reason is required"):
resolver.resolve_metadata(
version="6.0.1",
promoted_from_tag_input="6.0.1-rc.1",
rollback_version_input="6.0.0",
version="6.0.2",
promoted_from_tag_input="6.0.2-rc.1",
rollback_version_input="6.0.1",
ga_date_input="",
v5_eos_date_input="",
hotfix_exception=True,
@ -181,9 +181,9 @@ class ResolveReleasePromotionTest(unittest.TestCase):
def test_stable_rejects_short_soak_without_hotfix(self) -> None:
with self.assertRaisesRegex(ValueError, "hours of prerelease soak"):
resolver.resolve_metadata(
version="6.0.1",
promoted_from_tag_input="6.0.1-rc.1",
rollback_version_input="6.0.0",
version="6.0.2",
promoted_from_tag_input="6.0.2-rc.1",
rollback_version_input="6.0.1",
ga_date_input="",
v5_eos_date_input="",
hotfix_exception=False,