mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-07-09 16:00:59 +00:00
Prepare v6.0.1 patch release
- bump release, Docker, and Helm metadata to 6.0.1 - add committed v6.0.1 patch release notes and changelog - allow stable patch hotfix releases without a fabricated same-version prerelease tag
This commit is contained in:
parent
cefc032a37
commit
84ad973540
17 changed files with 334 additions and 82 deletions
|
|
@ -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.0"
|
||||
CANONICAL_DEFAULT_PULSE_VERSION="6.0.1"
|
||||
|
||||
resolve_default_pulse_version() {
|
||||
if [ -n "${PULSE_IMAGE_VERSION:-}" ]; then
|
||||
|
|
|
|||
|
|
@ -657,6 +657,38 @@ func TestDeploymentDefaultsPinVersionedImagesAndHelmDocsChecksum(t *testing.T) {
|
|||
t.Fatalf("install-docker.sh must not default to a floating latest tag:\n%s", installDocker)
|
||||
}
|
||||
|
||||
chartBytes, err := os.ReadFile(repoFile("deploy", "helm", "pulse", "Chart.yaml"))
|
||||
if err != nil {
|
||||
t.Fatalf("read Helm Chart.yaml: %v", err)
|
||||
}
|
||||
chart := string(chartBytes)
|
||||
chartRequired := []string{
|
||||
"version: " + version,
|
||||
`appVersion: "` + version + `"`,
|
||||
"https://raw.githubusercontent.com/rcourtman/Pulse/v" + version + "/docs/images/pulse-logo.svg",
|
||||
"https://github.com/rcourtman/Pulse/blob/v" + version + "/docs/KUBERNETES.md",
|
||||
}
|
||||
for _, needle := range chartRequired {
|
||||
if !strings.Contains(chart, needle) {
|
||||
t.Fatalf("Helm Chart.yaml must pin the governed release version, missing %s:\n%s", needle, chart)
|
||||
}
|
||||
}
|
||||
|
||||
chartReadmeBytes, err := os.ReadFile(repoFile("deploy", "helm", "pulse", "README.md"))
|
||||
if err != nil {
|
||||
t.Fatalf("read Helm README.md: %v", err)
|
||||
}
|
||||
chartReadme := string(chartReadmeBytes)
|
||||
chartReadmeRequired := []string{
|
||||
"",
|
||||
"",
|
||||
}
|
||||
for _, needle := range chartReadmeRequired {
|
||||
if !strings.Contains(chartReadme, needle) {
|
||||
t.Fatalf("Helm README.md must reflect the governed release version, missing %s:\n%s", needle, chartReadme)
|
||||
}
|
||||
}
|
||||
|
||||
helmPagesBytes, err := os.ReadFile(repoFile(".github", "workflows", "helm-pages.yml"))
|
||||
if err != nil {
|
||||
t.Fatalf("read helm-pages.yml: %v", err)
|
||||
|
|
|
|||
|
|
@ -102,6 +102,9 @@ func TestRepoDockerComposeDefaultPinsCurrentVersion(t *testing.T) {
|
|||
if !isPrereleaseVersion(version) && version == "6.0.0" && !strings.Contains(text, "rcourtman/pulse:6.0.0") {
|
||||
t.Fatalf("v6 GA repo docker-compose.yml must default to the stable v6 image:\n%s", text)
|
||||
}
|
||||
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 strings.Contains(text, ":latest") {
|
||||
t.Fatalf("repo docker-compose.yml must not default to a floating latest tag:\n%s", text)
|
||||
}
|
||||
|
|
@ -124,6 +127,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("v6 GA install-docker.sh fallback must default to the stable v6 image tag:\n%s", text)
|
||||
}
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
func runInstallDockerScript(t *testing.T, workDir string, envVars ...string) {
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ from typing import Callable
|
|||
from repo_file_io import REPO_ROOT, git_env
|
||||
|
||||
|
||||
SEMVER_STABLE_RE = re.compile(r"^(\d+)\.(\d+)\.(\d+)$")
|
||||
SEMVER_PRERELEASE_RE = re.compile(r"-(?:[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)(?:\+[0-9A-Za-z.-]+)?$")
|
||||
|
||||
|
||||
|
|
@ -29,6 +30,11 @@ def is_prerelease_version(version: str) -> bool:
|
|||
return bool(SEMVER_PRERELEASE_RE.search(version))
|
||||
|
||||
|
||||
def is_stable_patch_version(version: str) -> bool:
|
||||
match = SEMVER_STABLE_RE.match(version)
|
||||
return bool(match and int(match.group(3)) > 0)
|
||||
|
||||
|
||||
def tag_exists(tag: str) -> bool:
|
||||
result = subprocess.run(
|
||||
["git", "rev-parse", "-q", "--verify", f"refs/tags/{tag}"],
|
||||
|
|
@ -124,35 +130,41 @@ def resolve_metadata(
|
|||
else:
|
||||
promoted_from_tag = normalize_tag(promoted_from_tag_input)
|
||||
if not promoted_from_tag:
|
||||
raise ValueError(
|
||||
"Stable promotion requires promoted_from_tag naming the RC being promoted."
|
||||
)
|
||||
if not re.match(rf"^v{re.escape(version)}-rc\.\d+$", promoted_from_tag):
|
||||
raise ValueError(
|
||||
f"promoted_from_tag must reference an RC tag for the same stable version ({version}), got {promoted_from_tag}."
|
||||
)
|
||||
if not tag_exists_fn(promoted_from_tag):
|
||||
raise ValueError(
|
||||
f"promoted_from_tag {promoted_from_tag} does not exist as a repository tag."
|
||||
)
|
||||
if is_stable_patch_version(version) and hotfix_exception:
|
||||
if not hotfix_reason:
|
||||
raise ValueError("hotfix_reason is required when hotfix_exception is true.")
|
||||
else:
|
||||
raise ValueError(
|
||||
"Stable promotion requires promoted_from_tag naming the prerelease being promoted. "
|
||||
"Stable patch hotfix releases may omit promoted_from_tag only when hotfix_exception is true and hotfix_reason is set."
|
||||
)
|
||||
else:
|
||||
if not re.match(rf"^v{re.escape(version)}-rc\.\d+$", promoted_from_tag):
|
||||
raise ValueError(
|
||||
f"promoted_from_tag must reference a prerelease tag for the same stable version ({version}), got {promoted_from_tag}."
|
||||
)
|
||||
if not tag_exists_fn(promoted_from_tag):
|
||||
raise ValueError(
|
||||
f"promoted_from_tag {promoted_from_tag} does not exist as a repository tag."
|
||||
)
|
||||
|
||||
promoted_commit = tag_commit_fn(promoted_from_tag)
|
||||
if not head_descends_from_fn(promoted_commit):
|
||||
raise ValueError(
|
||||
f"Stable promotion {tag} must descend from promoted prerelease tag {promoted_from_tag}."
|
||||
)
|
||||
promoted_commit = tag_commit_fn(promoted_from_tag)
|
||||
if not head_descends_from_fn(promoted_commit):
|
||||
raise ValueError(
|
||||
f"Stable promotion {tag} must descend from promoted prerelease tag {promoted_from_tag}."
|
||||
)
|
||||
|
||||
promoted_tag_ts = tag_created_unix_fn(promoted_from_tag)
|
||||
soak_hours_value = int((now_unix_fn() - promoted_tag_ts) / 3600)
|
||||
soak_hours = str(soak_hours_value)
|
||||
promoted_tag_ts = tag_created_unix_fn(promoted_from_tag)
|
||||
soak_hours_value = int((now_unix_fn() - promoted_tag_ts) / 3600)
|
||||
soak_hours = str(soak_hours_value)
|
||||
|
||||
if hotfix_exception:
|
||||
if not hotfix_reason:
|
||||
raise ValueError("hotfix_reason is required when hotfix_exception is true.")
|
||||
elif soak_hours_value < 72:
|
||||
raise ValueError(
|
||||
f"Stable promotion {tag} has only {soak_hours_value} hours of prerelease soak since {promoted_from_tag}; minimum is 72 hours unless hotfix_exception is true."
|
||||
)
|
||||
if hotfix_exception:
|
||||
if not hotfix_reason:
|
||||
raise ValueError("hotfix_reason is required when hotfix_exception is true.")
|
||||
elif soak_hours_value < 72:
|
||||
raise ValueError(
|
||||
f"Stable promotion {tag} has only {soak_hours_value} hours of prerelease soak since {promoted_from_tag}; minimum is 72 hours unless hotfix_exception is true."
|
||||
)
|
||||
|
||||
if version == "6.0.0":
|
||||
if not re.match(r"^\d{4}-\d{2}-\d{2}$", ga_date):
|
||||
|
|
|
|||
|
|
@ -82,6 +82,57 @@ class ResolveReleasePromotionTest(unittest.TestCase):
|
|||
now_unix_fn=lambda: 100 + (73 * 3600),
|
||||
)
|
||||
|
||||
def test_stable_patch_hotfix_can_omit_promoted_prerelease(self) -> None:
|
||||
metadata = resolver.resolve_metadata(
|
||||
version="6.0.1",
|
||||
promoted_from_tag_input="",
|
||||
rollback_version_input="6.0.0",
|
||||
ga_date_input="",
|
||||
v5_eos_date_input="",
|
||||
hotfix_exception=True,
|
||||
hotfix_reason_input="Patch release for v6.0.0 upgrade and monitoring fixes.",
|
||||
release_notes_input="",
|
||||
tag_exists_fn=lambda tag: tag == "v6.0.0",
|
||||
)
|
||||
|
||||
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["hotfix_exception"], "true")
|
||||
self.assertEqual(
|
||||
metadata["hotfix_reason"],
|
||||
"Patch release for v6.0.0 upgrade and monitoring fixes.",
|
||||
)
|
||||
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",
|
||||
promoted_from_tag_input="",
|
||||
rollback_version_input="6.0.0",
|
||||
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",
|
||||
)
|
||||
|
||||
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",
|
||||
promoted_from_tag_input="",
|
||||
rollback_version_input="6.0.0",
|
||||
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",
|
||||
)
|
||||
|
||||
def test_stable_hotfix_requires_reason(self) -> None:
|
||||
with self.assertRaisesRegex(ValueError, "hotfix_reason is required"):
|
||||
resolver.resolve_metadata(
|
||||
|
|
|
|||
|
|
@ -78,6 +78,18 @@ def rc_packet_paths_for_version(version: str) -> tuple[str, str, str] | None:
|
|||
)
|
||||
|
||||
|
||||
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):
|
||||
return None
|
||||
if version == "6.0.0":
|
||||
return ("docs/releases/RELEASE_NOTES_v6.md", "docs/releases/V6_CHANGELOG.md")
|
||||
return (
|
||||
f"docs/releases/RELEASE_NOTES_v{version}.md",
|
||||
f"docs/releases/V6_CHANGELOG_v{version}.md",
|
||||
)
|
||||
|
||||
|
||||
def staged_files() -> tuple[str, ...]:
|
||||
result = subprocess.run(
|
||||
["git", "diff", "--cached", "--name-only"],
|
||||
|
|
@ -327,10 +339,13 @@ class ReleasePromotionPolicyTest(unittest.TestCase):
|
|||
def test_version_file_matches_current_rc_packet(self) -> None:
|
||||
current_version = read("VERSION").strip()
|
||||
release_index = read("docs/RELEASE_NOTES.md")
|
||||
if current_version == "6.0.0":
|
||||
release_notes = read("docs/releases/RELEASE_NOTES_v6.md")
|
||||
changelog = read("docs/releases/V6_CHANGELOG.md")
|
||||
self.assertIn("prepared stable v6 release packet", release_index)
|
||||
stable_packet_paths = stable_packet_paths_for_version(current_version)
|
||||
if stable_packet_paths is not None:
|
||||
release_notes_path, changelog_path = stable_packet_paths
|
||||
release_notes = read(release_notes_path)
|
||||
changelog = read(changelog_path)
|
||||
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)
|
||||
|
|
@ -376,9 +391,11 @@ class ReleasePromotionPolicyTest(unittest.TestCase):
|
|||
self.assertNotIn("`POST /api/license/trial/start`", upgrade_guide)
|
||||
self.assertNotIn("signed activation token to `/auth/trial-activate`", upgrade_guide)
|
||||
self.assertNotIn("25 hosted Patrol", upgrade_guide)
|
||||
if current_version == "6.0.0":
|
||||
self.assertIn("docs/releases/RELEASE_NOTES_v6.md", upgrade_guide)
|
||||
self.assertIn("docs/releases/V6_CHANGELOG.md", upgrade_guide)
|
||||
stable_packet_paths = stable_packet_paths_for_version(current_version)
|
||||
if stable_packet_paths is not None:
|
||||
release_notes_path, changelog_path = stable_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)
|
||||
|
|
@ -453,6 +470,7 @@ class ReleasePromotionPolicyTest(unittest.TestCase):
|
|||
self.assertIn("default_output_path", recorder)
|
||||
self.assertIn("rollback_version is required for every release rehearsal and promotion", resolver)
|
||||
self.assertIn("Stable promotion requires promoted_from_tag", resolver)
|
||||
self.assertIn("Stable patch hotfix releases may omit promoted_from_tag", resolver)
|
||||
self.assertIn("Stable v6.0.0 requires ga_date in YYYY-MM-DD form", resolver)
|
||||
self.assertIn("release_notes must include the exact ga_date", resolver)
|
||||
self.assertIn("check-workflow-dispatch-inputs.py", dry_run_trigger)
|
||||
|
|
@ -802,8 +820,10 @@ class ReleasePromotionPolicyTest(unittest.TestCase):
|
|||
blocked = read("docs/release-control/v6/internal/records/rc-to-ga-promotion-readiness-blocked-2026-04-04.md")
|
||||
current_version = read("VERSION").strip()
|
||||
active_target_id = read_json("docs/release-control/control_plane.json")["active_target_id"]
|
||||
if current_version == "6.0.0":
|
||||
self.assertIn(f"VERSION={current_version}", blocked)
|
||||
if stable_packet_paths_for_version(current_version) is not None:
|
||||
self.assertIn("VERSION=6.0.0", blocked)
|
||||
if current_version != "6.0.0":
|
||||
self.assertNotIn(f"VERSION={current_version}", blocked)
|
||||
else:
|
||||
self.assertIsNotNone(
|
||||
rc_packet_paths_for_version(current_version),
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ from typing import Callable
|
|||
from repo_file_io import REPO_ROOT, git_env
|
||||
|
||||
|
||||
SEMVER_STABLE_RE = re.compile(r"^(\d+)\.(\d+)\.(\d+)$")
|
||||
SEMVER_PRERELEASE_RE = re.compile(r"-(?:[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)(?:\+[0-9A-Za-z.-]+)?$")
|
||||
|
||||
|
||||
|
|
@ -29,6 +30,11 @@ def is_prerelease_version(version: str) -> bool:
|
|||
return bool(SEMVER_PRERELEASE_RE.search(version))
|
||||
|
||||
|
||||
def is_stable_patch_version(version: str) -> bool:
|
||||
match = SEMVER_STABLE_RE.match(version)
|
||||
return bool(match and int(match.group(3)) > 0)
|
||||
|
||||
|
||||
def tag_exists(tag: str) -> bool:
|
||||
result = subprocess.run(
|
||||
["git", "rev-parse", "-q", "--verify", f"refs/tags/{tag}"],
|
||||
|
|
@ -124,35 +130,41 @@ def resolve_metadata(
|
|||
else:
|
||||
promoted_from_tag = normalize_tag(promoted_from_tag_input)
|
||||
if not promoted_from_tag:
|
||||
raise ValueError(
|
||||
"Stable promotion requires promoted_from_tag naming the prerelease being promoted."
|
||||
)
|
||||
if not re.match(rf"^v{re.escape(version)}-rc\.\d+$", promoted_from_tag):
|
||||
raise ValueError(
|
||||
f"promoted_from_tag must reference a prerelease tag for the same stable version ({version}), got {promoted_from_tag}."
|
||||
)
|
||||
if not tag_exists_fn(promoted_from_tag):
|
||||
raise ValueError(
|
||||
f"promoted_from_tag {promoted_from_tag} does not exist as a repository tag."
|
||||
)
|
||||
if is_stable_patch_version(version) and hotfix_exception:
|
||||
if not hotfix_reason:
|
||||
raise ValueError("hotfix_reason is required when hotfix_exception is true.")
|
||||
else:
|
||||
raise ValueError(
|
||||
"Stable promotion requires promoted_from_tag naming the prerelease being promoted. "
|
||||
"Stable patch hotfix releases may omit promoted_from_tag only when hotfix_exception is true and hotfix_reason is set."
|
||||
)
|
||||
else:
|
||||
if not re.match(rf"^v{re.escape(version)}-rc\.\d+$", promoted_from_tag):
|
||||
raise ValueError(
|
||||
f"promoted_from_tag must reference a prerelease tag for the same stable version ({version}), got {promoted_from_tag}."
|
||||
)
|
||||
if not tag_exists_fn(promoted_from_tag):
|
||||
raise ValueError(
|
||||
f"promoted_from_tag {promoted_from_tag} does not exist as a repository tag."
|
||||
)
|
||||
|
||||
promoted_commit = tag_commit_fn(promoted_from_tag)
|
||||
if not head_descends_from_fn(promoted_commit):
|
||||
raise ValueError(
|
||||
f"Stable promotion {tag} must descend from promoted prerelease tag {promoted_from_tag}."
|
||||
)
|
||||
promoted_commit = tag_commit_fn(promoted_from_tag)
|
||||
if not head_descends_from_fn(promoted_commit):
|
||||
raise ValueError(
|
||||
f"Stable promotion {tag} must descend from promoted prerelease tag {promoted_from_tag}."
|
||||
)
|
||||
|
||||
promoted_tag_ts = tag_created_unix_fn(promoted_from_tag)
|
||||
soak_hours_value = int((now_unix_fn() - promoted_tag_ts) / 3600)
|
||||
soak_hours = str(soak_hours_value)
|
||||
promoted_tag_ts = tag_created_unix_fn(promoted_from_tag)
|
||||
soak_hours_value = int((now_unix_fn() - promoted_tag_ts) / 3600)
|
||||
soak_hours = str(soak_hours_value)
|
||||
|
||||
if hotfix_exception:
|
||||
if not hotfix_reason:
|
||||
raise ValueError("hotfix_reason is required when hotfix_exception is true.")
|
||||
elif soak_hours_value < 72:
|
||||
raise ValueError(
|
||||
f"Stable promotion {tag} has only {soak_hours_value} hours of prerelease soak since {promoted_from_tag}; minimum is 72 hours unless hotfix_exception is true."
|
||||
)
|
||||
if hotfix_exception:
|
||||
if not hotfix_reason:
|
||||
raise ValueError("hotfix_reason is required when hotfix_exception is true.")
|
||||
elif soak_hours_value < 72:
|
||||
raise ValueError(
|
||||
f"Stable promotion {tag} has only {soak_hours_value} hours of prerelease soak since {promoted_from_tag}; minimum is 72 hours unless hotfix_exception is true."
|
||||
)
|
||||
|
||||
if version == "6.0.0":
|
||||
if not re.match(r"^\d{4}-\d{2}-\d{2}$", ga_date):
|
||||
|
|
|
|||
|
|
@ -86,7 +86,6 @@ class ResolveReleasePromotionTest(unittest.TestCase):
|
|||
)
|
||||
|
||||
def test_current_stable_v6_packet_resolves_with_publish_dates(self) -> None:
|
||||
self.assertEqual((REPO_ROOT / "VERSION").read_text(encoding="utf-8").strip(), "6.0.0")
|
||||
release_notes = (REPO_ROOT / "docs/releases/RELEASE_NOTES_v6.md").read_text(encoding="utf-8")
|
||||
metadata = resolver.resolve_metadata(
|
||||
version="6.0.0",
|
||||
|
|
@ -110,6 +109,57 @@ class ResolveReleasePromotionTest(unittest.TestCase):
|
|||
self.assertEqual(metadata["ga_date"], "2026-07-04")
|
||||
self.assertEqual(metadata["v5_eos_date"], "2026-10-02")
|
||||
|
||||
def test_stable_patch_hotfix_can_omit_promoted_prerelease(self) -> None:
|
||||
metadata = resolver.resolve_metadata(
|
||||
version="6.0.1",
|
||||
promoted_from_tag_input="",
|
||||
rollback_version_input="6.0.0",
|
||||
ga_date_input="",
|
||||
v5_eos_date_input="",
|
||||
hotfix_exception=True,
|
||||
hotfix_reason_input="Patch release for v6.0.0 upgrade and monitoring fixes.",
|
||||
release_notes_input="",
|
||||
tag_exists_fn=lambda tag: tag == "v6.0.0",
|
||||
)
|
||||
|
||||
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["hotfix_exception"], "true")
|
||||
self.assertEqual(
|
||||
metadata["hotfix_reason"],
|
||||
"Patch release for v6.0.0 upgrade and monitoring fixes.",
|
||||
)
|
||||
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",
|
||||
promoted_from_tag_input="",
|
||||
rollback_version_input="6.0.0",
|
||||
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",
|
||||
)
|
||||
|
||||
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",
|
||||
promoted_from_tag_input="",
|
||||
rollback_version_input="6.0.0",
|
||||
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",
|
||||
)
|
||||
|
||||
def test_stable_hotfix_requires_reason(self) -> None:
|
||||
with self.assertRaisesRegex(ValueError, "hotfix_reason is required"):
|
||||
resolver.resolve_metadata(
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue