Support private Pro archive installs

This commit is contained in:
rcourtman 2026-05-07 09:28:38 +01:00
parent 1f5098f89e
commit 0f747781fb
6 changed files with 62 additions and 10 deletions

View file

@ -56,6 +56,21 @@ Cloud, and self-hosted production users.
3. Failed prereleases are fixed forward and replaced with a new prerelease. They are never
promoted as-is to `stable`.
## Paid Pro Artifact Lineage
1. Customer-facing private Pulse Pro archives and private Pulse Pro Docker images
must track the same immutable release checkpoint as the public Pulse release
they support.
2. During the v6 RC phase, private Pro artifacts must be built from the exact
public RC tag, use the same RC version, and publish under RC-shaped artifact
names, R2 prefixes, and Docker tags such as `6.0.0-rc.5`.
3. Do not build or advertise `license.pulserelay.pro/pulse-pro:6.0.0`, a
`pulse-pro-v6.0.0-...` private archive, or a GA-shaped private R2 prefix
until the intentional v6 GA publish.
4. A private Pro build from a moving branch is valid only as an internal proof
artifact. It is not valid customer guidance and must not update the live
paid-download manifest or private Docker customer tag.
## v5 Maintenance Policy
1. When Pulse v6 reaches `stable`, Pulse v5 immediately enters

View file

@ -218,19 +218,26 @@ server-side update execution surfaces.
declared feature-complete.
Paid-user GA is part of that same release boundary: the public Pulse release
workflow builds OSS `pulse-v...` artifacts only, so release docs and runbooks
must require a same-ref/same-version `pulse-enterprise` Pro package, verify
`pulse-pro-v...` archives identify `Pulse Pro`, and keep the paid
install/upgrade path pointed at Pro artifacts or a verified paid image before
any paid-user Pro runtime claim is made. Public GitHub release assets and the
must require a same-tag/same-version `pulse-enterprise` Pro package for
customer-facing publication, verify `pulse-pro-v...` archives identify
`Pulse Pro`, and keep the paid install/upgrade path pointed at Pro artifacts
or a verified paid image before any paid-user Pro runtime claim is made.
During the v6 RC phase, private Pulse Pro archive prefixes and Docker tags
must retain the RC suffix from the exact public Pulse RC tag; GA-shaped
`6.0.0` Pro archive names, R2 prefixes, and Docker tags are reserved for the
intentional v6 GA publish. Public GitHub release assets and the
public `rcourtman/pulse` Docker image must be described as community builds
where paid customers are likely to install or upgrade, and generated public
release bodies must send Relay, Pulse Pro, and eligible legacy customers to
`https://pulserelay.pro/download.html` for the private Pulse Pro Docker image
or Linux archive. Public Docker and install docs must also preserve a
or Linux/LXC archive. Public Docker and install docs must also preserve a
`PULSE_IMAGE`-aware compose image line and warn that any hardcoded
`image: rcourtman/pulse:...` line must be replaced before the private
Pulse Pro compose commands can move an existing Docker install off the
community image.
community image. The root installer must accept private `pulse-pro-v...`
archive filenames through `--archive` so direct Linux and Proxmox LXC users
can keep the normal service setup while installing the private Pulse Pro
runtime.
The repo-root VERSION file is part of the same governed boundary and must
not drift as an
unowned release-cut switch: changing the version string for a new RC or

View file

@ -2544,8 +2544,8 @@ infer_release_from_archive_name() {
local archive_name
archive_name=$(basename "$1")
if [[ "$archive_name" =~ ^pulse-(v[0-9]+\.[0-9]+\.[0-9]+([-.][0-9A-Za-z.]+)*)-linux-(amd64|arm64|armv7)\.tar\.gz$ ]]; then
printf '%s\n' "${BASH_REMATCH[1]}"
if [[ "$archive_name" =~ ^pulse(-pro)?-(v[0-9]+\.[0-9]+\.[0-9]+([-.][0-9A-Za-z.]+)*)-linux-(amd64|arm64|armv7|armv6|386)\.tar\.gz$ ]]; then
printf '%s\n' "${BASH_REMATCH[2]}"
return 0
fi

View file

@ -88,6 +88,32 @@ func TestRootInstallScriptArchiveSupportContract(t *testing.T) {
}
}
func TestRootInstallScriptInfersPrivateProArchiveVersion(t *testing.T) {
script := `
set -euo pipefail
` + extractRootInstallShellFunction(t, "infer_release_from_archive_name") + `
infer_release_from_archive_name /tmp/pulse-v6.0.0-rc.5-linux-amd64.tar.gz
infer_release_from_archive_name /tmp/pulse-pro-v6.0.0-rc.5-linux-amd64.tar.gz
infer_release_from_archive_name /tmp/pulse-pro-v6.0.0-linux-arm64.tar.gz
`
out, err := exec.Command("bash", "-c", script).CombinedOutput()
if err != nil {
t.Fatalf("bash: %v\n%s", err, out)
}
got := strings.Fields(string(out))
want := []string{"v6.0.0-rc.5", "v6.0.0-rc.5", "v6.0.0"}
if len(got) != len(want) {
t.Fatalf("versions = %#v, want %#v", got, want)
}
for i := range want {
if got[i] != want[i] {
t.Fatalf("versions = %#v, want %#v", got, want)
}
}
}
func TestRootInstallScriptUpdateDiskHeadroomRejectsSharedLowSpaceFilesystem(t *testing.T) {
script := `
set -euo pipefail

View file

@ -88,6 +88,10 @@ class ReleasePromotionPolicyTest(unittest.TestCase):
self.assertIn("release notes may keep placeholder", content)
self.assertIn("Exact v6 GA and v5 end-of-support dates locked before GA publish", content)
self.assertIn("governed prerelease and stable release branches", content)
self.assertIn("Customer-facing private Pulse Pro archives", content)
self.assertIn("public RC tag", content)
self.assertIn("license.pulserelay.pro/pulse-pro:6.0.0", content)
self.assertIn("moving branch", content)
def test_pre_release_checklist_tracks_rc_to_ga_gate_inputs(self) -> None:
content = read("docs/release-control/v6/internal/PRE_RELEASE_CHECKLIST.md")
@ -355,7 +359,7 @@ class ReleasePromotionPolicyTest(unittest.TestCase):
"the public Pulse release workflow builds OSS `pulse-v...` artifacts only",
normalize_ws(contract),
)
self.assertIn("`pulse-pro-v...` archives identify `Pulse Pro`", contract)
self.assertIn("`pulse-pro-v...` archives identify `Pulse Pro`", normalize_ws(contract))
self.assertIn("https://pulserelay.pro/download.html", contract)
self.assertIn("PULSE_IMAGE`-aware compose image line", normalize_ws(contract))
self.assertIn("hardcoded `image: rcourtman/pulse:...`", normalize_ws(contract))

View file

@ -81,7 +81,7 @@ def build_installation_section(version: str) -> str:
"",
"See the [Installation Guide](https://github.com/rcourtman/Pulse#installation) for complete setup instructions.",
"",
"Paid Pulse Pro, Relay, and eligible legacy customers: public GitHub release assets and the public `rcourtman/pulse` Docker image are community builds. They do not include the private Pulse Pro runtime hooks. Use https://pulserelay.pro/download.html with your activation key to get the private Pulse Pro Docker image or Linux archive.",
"Paid Pulse Pro, Relay, and eligible legacy customers: public GitHub release assets and the public `rcourtman/pulse` Docker image are community builds. They do not include the private Pulse Pro runtime hooks. Use https://pulserelay.pro/download.html with your activation key to get the private Pulse Pro Docker image or Linux/LXC archive.",
]
)