Prepare v6.1.1 emergency patch release

This commit is contained in:
rcourtman
2026-07-23 13:28:17 +01:00
parent d2c268c0e5
commit 781e168e34
22 changed files with 335 additions and 126 deletions
+2 -2
View File
@@ -38,12 +38,12 @@ on:
required: false
type: string
unsigned_windows_exception:
description: 'v6.1.0 only: publish Windows agents without Authenticode under the recorded owner exception'
description: 'v6.1.0 or v6.1.1 only: publish Windows agents without Authenticode under a recorded owner exception'
required: false
type: boolean
default: false
unsigned_windows_reason:
description: 'v6.1.0 only: owner reason for the unsigned Windows exception'
description: 'v6.1.0 or v6.1.1 only: owner reason for the unsigned Windows exception'
required: false
type: string
historical_asset_backfill_only:
+3 -3
View File
@@ -43,12 +43,12 @@ on:
required: false
type: string
unsigned_windows_exception:
description: 'v6.1.0 only: rehearse Windows agents without Authenticode under the recorded owner exception'
description: 'v6.1.0 or v6.1.1 only: rehearse Windows agents without Authenticode under a recorded owner exception'
required: false
type: boolean
default: false
unsigned_windows_reason:
description: 'v6.1.0 only: owner reason for the unsigned Windows exception'
description: 'v6.1.0 or v6.1.1 only: owner reason for the unsigned Windows exception'
required: false
type: string
note:
@@ -79,7 +79,7 @@ jobs:
with:
version: ${{ inputs.version }}
require_macos_signing: true
require_windows_signing: ${{ !contains(inputs.version, '-') && !(inputs.version == '6.1.0' && inputs.unsigned_windows_exception) }}
require_windows_signing: ${{ !contains(inputs.version, '-') && !((inputs.version == '6.1.0' || inputs.version == '6.1.1') && inputs.unsigned_windows_exception) }}
windows_signing_backend: signpath
dry-run:
+1 -1
View File
@@ -1 +1 @@
6.1.0
6.1.1
+4 -4
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.1.0
appVersion: "6.1.0"
icon: https://raw.githubusercontent.com/rcourtman/Pulse/v6.1.0/docs/images/pulse-logo.svg
version: 6.1.1
appVersion: "6.1.1"
icon: https://raw.githubusercontent.com/rcourtman/Pulse/v6.1.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.1.0/docs/KUBERNETES.md
url: https://github.com/rcourtman/Pulse/blob/v6.1.1/docs/KUBERNETES.md
- name: Support
url: https://github.com/rcourtman/Pulse/discussions
artifacthub.io/maintainers: |
+1 -1
View File
@@ -1,6 +1,6 @@
# pulse
![Version: 6.1.0](https://img.shields.io/badge/Version-6.1.0-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 6.1.0](https://img.shields.io/badge/AppVersion-6.1.0-informational?style=flat-square)
![Version: 6.1.1](https://img.shields.io/badge/Version-6.1.1-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 6.1.1](https://img.shields.io/badge/AppVersion-6.1.1-informational?style=flat-square)
Helm chart for deploying the Pulse hub and optional Docker monitoring agent.
+1 -1
View File
@@ -2,7 +2,7 @@ version: '3.8'
services:
pulse:
image: ${PULSE_IMAGE:-rcourtman/pulse:6.1.0}
image: ${PULSE_IMAGE:-rcourtman/pulse:6.1.1}
container_name: pulse
restart: unless-stopped
logging:
+4 -3
View File
@@ -3,13 +3,14 @@
Pulse release notes live on GitHub:
<https://github.com/rcourtman/Pulse/releases>
For the next release disclosure draft (version assigned at release cut), see:
- `docs/releases/RELEASE_NOTES_NEXT_DRAFT.md`
For historical v4 notes that previously lived in this repo, see:
`docs/releases/RELEASE_NOTES_v4.md`
For the current stable v6 packet, see:
- `docs/releases/RELEASE_NOTES_v6.1.1.md`
- `docs/releases/V6_CHANGELOG_v6.1.1.md`
For earlier stable v6.1 packets, see:
- `docs/releases/RELEASE_NOTES_v6.1.0.md`
- `docs/releases/V6_CHANGELOG_v6.1.0.md`
+4 -2
View File
@@ -4,11 +4,13 @@ This guide covers practical upgrade steps for existing Pulse installs moving to
For the current stable v6 packet, see:
- `docs/releases/RELEASE_NOTES_v6.1.0.md`
- `docs/releases/V6_CHANGELOG_v6.1.0.md`
- `docs/releases/RELEASE_NOTES_v6.1.1.md`
- `docs/releases/V6_CHANGELOG_v6.1.1.md`
For earlier stable v6 packets and rollout references, see:
- `docs/releases/RELEASE_NOTES_v6.1.0.md`
- `docs/releases/V6_CHANGELOG_v6.1.0.md`
- `docs/releases/RELEASE_NOTES_v6.0.5.md`
- `docs/releases/V6_CHANGELOG_v6.0.5.md`
- `docs/releases/RELEASE_NOTES_v6.0.4.md`
@@ -1022,9 +1022,10 @@ Companion drill:
the public demo remains healthy on stable `6.0.5`.
- Pass when:
The external rehearsal proves the canonical exact-SHA candidate builder and
its version-specific native-signing policy. If the `v6.1.0` Windows exception
is used, the rehearsal must preserve checksum, detached-signature, manifest,
and digest controls and record the owner reason and public disclosure. Local
its version-specific native-signing policy. If a recorded `v6.1.0` or
`v6.1.1` Windows exception is used, the rehearsal must preserve checksum,
detached-signature, manifest, and digest controls and record the
version-bound owner reason and public disclosure. Local
tests pin candidate-only publication and GitHub digest validation, and the
static release DAG contains no duplicate release build,
backend-to-integration serialization, full-download standard validator, or
@@ -1037,7 +1038,8 @@ Companion drill:
release packet, independent post-release jobs are serialized, or the
definitive verdict can pass without all applicable downstream results. Also
block if an unsigned stable Windows exception can apply to any version other
than `v6.1.0` or can omit its owner reason and release-note disclosure.
than the recorded `v6.1.0` and `v6.1.1` decisions or can omit its owner
reason and release-note disclosure.
## Gate: `operational-trust-canonical-lifecycle`
@@ -179,8 +179,15 @@ Cloud, and self-hosted production users.
Windows Authenticode exception. The Windows artifacts must remain bound
by the exact-SHA candidate manifest, checksums, detached `.sig`/`.sshsig`
signatures, and published digests, and the public notes must disclose the
Unknown Publisher state. Later stable releases restore the Authenticode
requirement; this flag cannot be reused for another version.
Unknown Publisher state.
- On 2026-07-23, the release owner separately approved a `v6.1.1`-only
Windows Authenticode exception for the emergency patch addressing active
customer update harm. This is a new, version-bound decision rather than a
reuse of the `v6.1.0` exception. The same exact-SHA candidate, checksum,
detached-signature, manifest, published-digest, owner-reason, and public
Unknown Publisher disclosure controls remain mandatory. Stable `v6.1.2`
and later restore the Authenticode requirement unless another explicit
version-bound owner decision is recorded in policy.
- Unproved self-service commercial transitions remain unavailable and
unadvertised under the exposure-safety gate. This exception does not
authorize enabling that feature or running a production billing proof.
@@ -237,8 +244,11 @@ Cloud, and self-hosted production users.
onto the RC promotion path. The resolver enforces that boundary. Do not use
the routine helper to relabel a risky patch as routine.
4. `--emergency-hotfix-reason` is the narrow escape hatch for active customer
harm. It does not remove the exact-SHA dry-run requirement, and the reason is
recorded in the release metadata.
harm. It does not remove the integrated exact-SHA candidate and release
checks, and the reason is recorded in the release metadata.
A version-bound unsigned Windows decision must additionally be supplied
through `--unsigned-windows-exception-reason`; it never follows implicitly
from emergency patch status.
5. The release workflow must await Docker publication, stable demo deployment,
public health/browser verification, install smoke, Helm publication,
floating-tag promotion, and private Pro promotion where applicable. The
+15 -1
View File
@@ -9176,7 +9176,21 @@
]
}
],
"work_claims": [],
"work_claims": [
{
"id": "codex-release-gate-stable-patch-unattended-release-path",
"agent_id": "codex",
"summary": "Prepare and publish v6.1.1 emergency stable patch with owner-approved unsigned Windows exception",
"target_id": "v6-product-lane-expansion",
"claimed_at": "2026-07-23T12:19:02Z",
"heartbeat_at": "2026-07-23T12:19:02Z",
"expires_at": "2026-07-23T16:19:02Z",
"work_item": {
"kind": "release-gate",
"id": "stable-patch-unattended-release-path"
}
}
],
"open_decisions": [],
"source_of_truth_file": "docs/release-control/v6/internal/SOURCE_OF_TRUTH.md",
"resolved_decisions": [
@@ -379,12 +379,14 @@ TLS floor in the dynamic config.
apply the same channel-specific native-signing policy as a publish run.
macOS notarization remains mandatory for both prerelease and stable
candidates. Windows Authenticode remains mandatory for stable candidates
except for the explicitly version-bound `v6.1.0` owner exception;
prerelease candidates and that one stable exception may retain checksum and
detached-signature verification without Authenticode while the release
packet explicitly discloses the unknown-publisher warning. Prerelease promotion remains
blocked on the normal stable signing requirement, and every stable version
after `v6.1.0` restores it automatically. A cheap signing-configuration job
except for the explicitly version-bound `v6.1.0` and `v6.1.1` owner
exceptions; prerelease candidates and those two stable exceptions may
retain checksum and detached-signature verification without Authenticode
while the release packet explicitly discloses the unknown-publisher warning.
Prerelease promotion remains blocked on the normal stable signing
requirement, and stable `v6.1.2` and later restore it automatically unless
policy records a new version-bound owner decision. A cheap
signing-configuration job
must report every missing secret for the platforms required by that
candidate before either platform runner is allocated. Stable Windows signing must use SignPath's GitHub
trusted-build-system action by default, submit an immutable GitHub artifact
@@ -1055,9 +1057,12 @@ evidence beside the candidate manifest. Release Dry Run now has a terminal
verdict covering the exact-SHA candidate and no-mutation demo lane. Stable
rehearsal `29927692302` confirmed that the external SignPath project was not
configured and stopped without creating a public release. The release owner
subsequently approved a `v6.1.0`-only unsigned-Windows exception; the gate stays
blocked until a new exact-`main` rehearsal proves that exception and the
remaining candidate controls.
subsequently approved and exercised a `v6.1.0`-only unsigned-Windows exception.
On 2026-07-23 the owner separately approved a `v6.1.1`-only exception for the
emergency patch addressing active customer update harm. The new decision must
flow through the normal exact-SHA candidate, checksum, detached-signature,
manifest, published-digest, and definitive-verdict controls with an explicit
owner reason and public Unknown Publisher disclosure.
Every caller of the reusable release-candidate builder must delegate
`actions: read` alongside `contents: read`; the Windows signing job reads the
exact uploaded artifact through the GitHub Actions API, and GitHub validates
@@ -1068,11 +1073,35 @@ host-local redirect contract as runtime token minting and exchange. Proof input
must reject absolute, scheme-relative, backslash-authority, encoded-separator,
and control-character targets before constructing the handoff request.
The active stable `v6.1.0` 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.1.0` release version.
This stable minor release uses `promoted_from_tag=v6.1.0-rc.4`,
`rollback_version=v6.0.5`, and the one-version release-owner cutoff exception
The active stable `v6.1.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.1.1` release version. This patch
release uses the stable hotfix path with `rollback_version=v6.1.0`,
`hotfix_exception=true`, a release-owner reason, and no fabricated
same-version RC tag. It fixes manual Unified Agent updates that received a
distribution identifier instead of the canonical Linux platform family and
durable Docker update actions that could remain stuck after a terminal
digest-drift preflight refusal. The exact stable `main` SHA must pass the
integrated release checks and immutable-candidate build before the single-build
workflow crosses its public mutation boundary. The same workflow must finish
Docker, Helm, stable demo, install-smoke, public-health, floating-tag,
paid-runtime, and definitive-verdict lanes before the cut is complete.
The `v6.1.1` release is `existing-mobile-build-compatible`: no production
relay or mobile trust contract changed from `v6.1.0`, the matched mobile-facing
path is test-only expiry branch coverage, and Pulse Mobile `1.0.0` iOS build
`11` and Android versionCode `9` remain the compatible candidate builds without
a companion upload or public store rollout.
The release owner separately approved a `v6.1.1`-only unsigned-Windows
exception because SignPath configuration remains unavailable. Windows assets
must remain exact-SHA and manifest-bound with checksums, detached
`.sig`/`.sshsig` signatures, and published-digest verification; the release
notes must disclose the Unknown Publisher state. Stable `v6.1.2` and later
restore Authenticode unless policy records another explicit version-bound
decision.
The preceding stable `v6.1.0` cut used
`promoted_from_tag=v6.1.0-rc.4`, `rollback_version=v6.0.5`, and the
one-version release-owner cutoff exception
recorded on 2026-07-22. The workflow input `hotfix_exception=true` carries that
approved soak bypass through the existing promotion resolver; it does not
reclassify the release as a patch hotfix. The exact stable `main` SHA must pass
@@ -1112,8 +1141,10 @@ explicitly waived Authenticode after the first stable rehearsal exposed
unavailable external SignPath configuration. The unsigned Windows binaries
remain exact-SHA and manifest-bound with checksum, detached `.sig`/`.sshsig`,
and published-digest verification, and the release notes disclose the Unknown
Publisher state. This exception cannot apply to a later stable version.
The stable server cut is classified `existing-mobile-build-compatible`. Pulse
Publisher state. That `v6.1.0` decision does not itself authorize another
version.
The `v6.1.0` stable server cut was classified
`existing-mobile-build-compatible`. Pulse
Mobile `1.0.0` iOS build `11` and Android versionCode `9` remain the existing
candidate builds; the canonical core/mobile contract proves that `v6.1.0`
serves their route, scope, payload, pairing, and push requirements, including
@@ -1180,8 +1211,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.1.0` cut, the repo-root compose default and
`scripts/install-docker.sh` fallback must both pin `6.1.0` until the next
For the active stable `v6.1.1` cut, the repo-root compose default and
`scripts/install-docker.sh` fallback must both pin `6.1.1` until the next
governed stable cut moves them forward. The stable promotion guard remains in
force and rejects leftover `-rc.` defaults.
The RC7 packet refresh records `fc10de9b5477613316473267b72b05b6b2b7aaff`
@@ -2496,8 +2527,9 @@ discloses the unsigned Windows publisher state and the Windows binaries retain
the exact-SHA candidate, checksum, detached-signature, and post-publication
digest controls. Stable publication and the stable-path dry-run must continue
to require both native signing lanes except for the recorded, version-bound
`v6.1.0` Windows exception; every subsequent stable release restores both
requirements. `scripts/build-release.sh` must replace
`v6.1.0` and `v6.1.1` Windows exceptions; stable `v6.1.2` and later restore
both requirements unless policy records a new explicit version-bound owner
decision. `scripts/build-release.sh` must replace
only the native targets required by those independent inputs and must fail
closed when a required native-binary directory or target is absent.
Historical published-release repair must flow through
-51
View File
@@ -1,51 +0,0 @@
# Next Pulse Release — Disclosure Draft
This draft records customer-visible disclosures that must be carried into the
next Pulse release packet. The release version is assigned only when that
packet is cut.
## Outbound usage telemetry schema v2
This release updates Pulse's legacy unversioned outbound usage telemetry
payload to schema v2. Telemetry remains enabled by default unless an operator
has disabled it; an existing enabled or disabled choice is preserved on
upgrade. The purpose remains aggregate product and release understanding, and
the payload remains pseudonymous rather than tied to a Pulse account or
person.
Schema v2 adds these deliberately coarse signal categories:
- closed deployment-method, known-install-age, activation-stage,
time-to-first-monitored-resource, and estate-size buckets;
- authentication-configured and monitoring-active booleans, plus an aggregate
configured-connection count;
- aggregate alert fired, acknowledged, and resolved counts from the existing
30-day local window;
- aggregate notification attempt, delivery, and failure counts from the
existing seven-day local window; and
- a boolean indicating whether an operational outcome was observed in the
existing 30-day local window.
The payload does not include names, email addresses, account IDs, hostnames,
credentials, infrastructure or resource identifiers, IP addresses, URLs,
paths, locale, recipients, notification endpoints, alert or notification
content, prompts, chat messages, command text, action output, token values,
browser events, or an event-level journey or clickstream. The rotating
pseudonymous installation ID continues to rotate every 30 days. Telemetry rows
are retained server-side for up to 90 days; request IP addresses are used only
transiently for rate limiting and are not stored in telemetry rows.
Existing installations receive a one-time, non-blocking
**Telemetry payload updated** notice after upgrade. It links directly to the
exact payload preview, the disable action, and the full privacy disclosure.
Fresh installations do not receive a duplicate banner because the current
payload and controls are already disclosed during setup.
## Terminology correction
Earlier public website copy used an anonymity label that was too strong for a
payload containing a rotating installation identifier. The accurate term is
**pseudonymous**. The website wording was corrected in July 2026, before
schema v2 reached a public Pulse release; the shipped privacy documentation
and in-product control identify the rotating pseudonymous ID and the concrete
data categories excluded from the payload.
+97
View File
@@ -0,0 +1,97 @@
# Pulse v6.1.1 Release Notes
`v6.1.1` is a stable patch release following `v6.1.0`. It takes the governed
emergency stable-patch path to resolve active customer harm in the manual
Unified Agent update flow and durable Docker update recovery. It also carries
the reliability, infrastructure, and privacy-disclosure improvements already
completed on `main` after the `v6.1.0` cutoff.
## Highlights
- Manual Unified Agent updates now use the canonical operating-system family,
so Mageia and other supported Linux distributions receive the Linux update
path instead of being rejected by a distribution-specific platform value.
- Docker update actions recover their durable terminal receipt by immutable
action and operation identity. A failed or no-effect digest-drift preflight
no longer leaves an update stuck after the live capability disappears.
- Outbound usage telemetry moves to a documented schema v2 with coarse,
pseudonymous operational signals, an exact payload preview, and a one-time
non-blocking upgrade notice.
- Infrastructure pages retain navigation through stream reconnects, proxy and
SSO bootstrap is more reliable, node edits use the correct update endpoint,
and PBS datastore alert overrides appear on the thresholds page.
## Changed
- Agent runtime platform reporting is normalized to the canonical Go operating
system family while preserving the original operating-system identity for
diagnostics. Unsupported platforms still fail closed.
- Outbound usage telemetry remains enabled by default unless an operator has
disabled it, and an existing enabled or disabled preference is preserved on
upgrade. The rotating pseudonymous installation ID continues to rotate every
30 days.
- Telemetry schema v2 adds deliberately coarse deployment-method, install-age,
activation-stage, time-to-first-monitored-resource, and estate-size buckets;
authentication-configured and monitoring-active booleans; configured
connection count; aggregate alert outcome counts; aggregate notification
attempt, delivery, and failure counts; and an operational-outcome boolean.
- Existing installations receive one non-blocking **Telemetry payload
updated** notice with direct links to the exact payload preview, disable
action, and privacy disclosure. Fresh installations do not receive a
duplicate notice.
- Public privacy terminology now describes this rotating-identifier payload as
**pseudonymous**, not anonymous.
Telemetry does not include identities, account details, hostnames, credentials,
resource identifiers, IP addresses, URLs, paths, locale, recipients,
notification endpoints, alert or notification content, prompts, chat
messages, command text or output, token values, browser events, or an
event-level journey or clickstream. Telemetry rows are retained server-side for
up to 90 days; request IP addresses are used transiently for rate limiting and
are not stored in telemetry rows.
## Fixed
- Manual Unified Agent updates on Mageia and other supported Linux
distributions no longer fail because the update planner receives a distro
identifier instead of the Linux platform family (#1607).
- Docker update actions that reach a terminal digest-drift preflight refusal
now recover as failed or no-effect without redispatch, even when the live
update capability is no longer advertised (#1608).
- Node state aggregation keeps clusters separate when they reuse the same node
names.
- Missing Patrol verdicts are swept with bounded follow-up instead of remaining
indefinitely unresolved.
- Platform navigation survives stream reconnects, and authenticated bootstrap
works correctly through proxy and SSO configurations.
- Infrastructure node edits route to the update endpoint.
- PBS datastore alert overrides are projected onto the thresholds page.
- The Proxmox VE setup script avoids an `awk` variable name that conflicts with
implementations where `exp` is reserved.
## Upgrade Notes
Use the normal v6 install or update flow for `v6.1.1`.
Windows Unified Agent binaries in `v6.1.1` are not Authenticode-signed and may
show an Unknown Publisher warning. Verify published checksums and detached
`.sig` or `.sshsig` signatures before installation. This is a `v6.1.1`-only
release-owner exception; later stable releases restore the Windows
Authenticode requirement.
The rollback target for this patch release is `v6.1.0`. The exact rollback
reinstall command is:
```bash
./scripts/install.sh --version v6.1.0
```
The server/mobile decision is `existing-mobile-build-compatible`. Pulse Mobile
`1.0.0` iOS build `11` and Android versionCode `9` remain the compatible
candidate builds. No production relay or mobile trust contract changed after
`v6.1.0`; the matched mobile-facing path is test-only branch coverage. This
release does not upload a companion build or start a public mobile-store
rollout.
Paid Pulse Pro, Relay, and eligible legacy customers should continue to use the
private download page and private runtime image for paid runtime features.
+45
View File
@@ -0,0 +1,45 @@
# Pulse v6.1.1
_This changelog describes stable `v6.1.1` compared with stable `v6.1.0`._
## Changed
- Unified Agent operating-system reporting now provides the canonical runtime
platform family to the update planner while retaining detailed OS identity
for diagnostics.
- Outbound usage telemetry uses schema v2 with rotating pseudonymous identity,
bounded aggregate operational signals, preserved operator preference, exact
payload preview, and a one-time non-blocking upgrade disclosure.
- Privacy copy uses the accurate term **pseudonymous** and explicitly lists the
identity, infrastructure, content, command, and clickstream categories
excluded from telemetry.
- Infrastructure navigation, authenticated proxy/SSO bootstrap, node-edit
routing, and PBS alert-threshold projection are more consistent.
## Fixed
- Linux manual agent updates no longer misclassify Mageia or other supported
distributions as an unsupported update platform (#1607).
- Durable Docker update receipt recovery uses immutable action and operation
binding, terminalizes digest-drift preflight refusals after capability loss,
and never redispatches the rejected operation (#1608).
- Cluster node aggregation no longer conflates separate clusters that reuse a
node name.
- Missing Patrol verdicts retain bounded follow-up.
- The Proxmox VE setup script avoids an `awk` identifier collision.
## Release Metadata
- Version: `v6.1.1`
- Previous stable: `v6.1.0`
- Rollback target: `v6.1.0`
- Rollback command: `./scripts/install.sh --version v6.1.0`
- Promotion path: stable patch hotfix from `main`, with an owner-recorded reason
for active customer update harm and no fabricated same-version RC tag
- Windows signing decision: `v6.1.1`-only release-owner exception; Windows
Unified Agent binaries are not Authenticode-signed and may show an Unknown
Publisher warning, while exact-SHA candidate binding, checksums, detached
`.sig`/`.sshsig` signatures, manifests, and published digests remain required
- Mobile decision: `existing-mobile-build-compatible`; Pulse Mobile `1.0.0`
iOS build `11` and Android versionCode `9` require no companion upload, and
no public store rollout is part of this server release
+1 -1
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.1.0"
CANONICAL_DEFAULT_PULSE_VERSION="6.1.1"
resolve_default_pulse_version() {
if [ -n "${PULSE_IMAGE_VERSION:-}" ]; then
@@ -269,25 +269,23 @@ func TestCurrentStablePatchReleasePacketTracksInstallMetadata(t *testing.T) {
assertFileContainsAllNormalized(t, releaseNotesPath,
"`v"+version+"` is a stable patch release",
"`v"+previous+"`",
"Patrol Gemini model readiness",
"remembered-login submit persistence",
"Proxmox SMART temperature fallback",
"direct SATA/SAT disks",
"paid-runtime activation continuity",
"license-period status fixes",
"Manual Unified Agent updates now use the canonical operating-system family",
"Docker update actions recover their durable terminal receipt",
"Outbound usage telemetry moves to a documented schema v2",
"clusters separate when they reuse the same node names",
"not Authenticode-signed",
"`existing-mobile-build-compatible`",
"rollback target for this patch release is `v"+previous+"`",
)
assertFileContainsAll(t, changelogPath,
"Version: `v"+version+"`",
"Rollback target: `v"+previous+"`",
"Promotion path: stable patch hotfix from `"+releaseBranch+"`",
"Gemini tool-call capability",
"Remembered-login state now persists",
"Proxmox SMART temperature collection",
"explicit SAT probe",
"Paid runtime activation now writes and reuses a durable installation fingerprint",
"License grants now include the billing `current_period_end`",
"Server installer execution now rejects unsafe piped invocation",
"canonical runtime",
"telemetry uses schema v2",
"Durable Docker update receipt recovery",
"`v6.1.1`-only release-owner exception",
"Mobile decision: `existing-mobile-build-compatible`",
)
assertFileContainsAll(t, repoFile("docs", "RELEASE_NOTES.md"),
"docs/releases/RELEASE_NOTES_v"+version+".md",
@@ -385,7 +383,7 @@ func TestCurrentStableMinorReleasePacketTracksInstallMetadata(t *testing.T) {
"`rollback_version=v"+previous+"`",
"The exact stable `main` SHA must pass the no-publication dry run before the same SHA is dispatched through the single-build publish workflow.",
"The stable server cut is classified `existing-mobile-build-compatible`.",
"This exception cannot apply to a later stable version",
"explicit version-bound decision",
"For the active stable `v"+version+"` cut, the repo-root compose default and `scripts/install-docker.sh` fallback must both pin `"+version+"`",
)
}
@@ -849,12 +847,12 @@ func TestReleaseCandidateRequiresPlatformNativeAgentSigning(t *testing.T) {
)
assertFileContainsAll(t, repoFile(".github", "workflows", "release-dry-run.yml"),
`Definitive Dry-Run Verdict`,
`require_windows_signing: ${{ !contains(inputs.version, '-') && !(inputs.version == '6.1.0' && inputs.unsigned_windows_exception) }}`,
`require_windows_signing: ${{ !contains(inputs.version, '-') && !((inputs.version == '6.1.0' || inputs.version == '6.1.1') && inputs.unsigned_windows_exception) }}`,
`require_result "exact-SHA release candidate" "$CANDIDATE_RESULT" success`,
`require_result "stable demo no-mutation verification" "$DEMO_RESULT" success`,
)
assertFileContainsAll(t, repoFile("scripts", "release_control", "resolve_release_promotion.py"),
`version != "6.1.0"`,
`version not in {"6.1.0", "6.1.1"}`,
`unsigned_windows_reason is required`,
`not Authenticode-signed`,
`require_windows_signing = not is_prerelease and not unsigned_windows_exception`,
@@ -288,9 +288,10 @@ func TestInstallDockerProofTracksStablePatchReleaseContract(t *testing.T) {
assertFileContainsAllNormalized(t, repoFile("docs", "release-control", "v6", "internal", "subsystems", "deployment-installability.md"),
"The active stable `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 patch release uses the stable hotfix path with `rollback_version=v"+previous+"`, `hotfix_exception=true`, a release-owner reason, and no fabricated same-version RC tag.",
"It advances the v"+previous+" stable line with customer-support fixes",
"paid-runtime activation fingerprint reuse",
"pinned installer execution hardening",
"manual Unified Agent updates",
"durable Docker update actions",
"`existing-mobile-build-compatible`",
"`v"+version+"`-only unsigned-Windows exception",
"For the active stable `v"+version+"` cut, the repo-root compose default and `scripts/install-docker.sh` fallback must both pin `"+version+"`",
)
}
@@ -488,7 +488,7 @@ class ReleasePromotionPolicyTest(unittest.TestCase):
self.assertIn("if: ${{ inputs.version != '' }}", workflow)
self.assertIn("require_macos_signing: true", workflow)
self.assertIn(
"require_windows_signing: ${{ !contains(inputs.version, '-') && !(inputs.version == '6.1.0' && inputs.unsigned_windows_exception) }}",
"require_windows_signing: ${{ !contains(inputs.version, '-') && !((inputs.version == '6.1.0' || inputs.version == '6.1.1') && inputs.unsigned_windows_exception) }}",
workflow,
)
self.assertIn("unsigned_windows_exception:", workflow)
@@ -669,7 +669,7 @@ class ReleasePromotionPolicyTest(unittest.TestCase):
self.assertIn("require_windows_signing: ${{ needs.prepare.outputs.require_windows_signing == 'true' }}", content)
self.assertIn("unsigned_windows_exception:", content)
self.assertIn("unsigned_windows_reason:", content)
self.assertIn('version != "6.1.0"', resolver)
self.assertIn('version not in {"6.1.0", "6.1.1"}', resolver)
self.assertIn("not Authenticode-signed", resolver)
self.assertIn("windows_signing_backend: signpath", content)
self.assertIn('if [[ "$REQUIRE_WINDOWS_SIGNING" == "true" ]]', candidate_workflow)
@@ -1124,6 +1124,10 @@ class ReleasePromotionPolicyTest(unittest.TestCase):
self.assertNotIn(".createdAt >= $cutoff", helper)
self.assertIn("gh workflow run create-release.yml", helper)
self.assertIn("gh workflow run \"$WORKFLOW\"", helper)
self.assertIn("--unsigned-windows-exception-reason", helper)
self.assertIn("--unsigned-windows-exception", helper)
self.assertIn("unsigned_windows_exception", helper)
self.assertIn("unsigned_windows_reason", helper)
self.assertIn("Single-Build Release Path", policy)
self.assertIn("Routine Stable Patch Path", policy)
self.assertIn("single publish workflow performs the exact-SHA preflight", normalize_ws(policy))
@@ -245,10 +245,10 @@ def resolve_metadata(
promotion_mode = "prerelease" if is_prerelease else "stable-rc-promotion"
if unsigned_windows_exception:
if version != "6.1.0":
if version not in {"6.1.0", "6.1.1"}:
raise ValueError(
"unsigned_windows_exception is approved only for stable v6.1.0. "
"Later stable releases must restore Windows Authenticode signing."
"unsigned_windows_exception is approved only for stable v6.1.0 or v6.1.1. "
"Stable v6.1.2 and later must restore Windows Authenticode signing."
)
if not unsigned_windows_reason:
raise ValueError(
@@ -256,7 +256,7 @@ def resolve_metadata(
)
if release_notes and "not authenticode-signed" not in release_notes.lower():
raise ValueError(
"Stable v6.1.0 release_notes must disclose that Windows binaries are not Authenticode-signed."
f"Stable v{version} release_notes must disclose that Windows binaries are not Authenticode-signed."
)
elif unsigned_windows_reason:
raise ValueError(
@@ -171,12 +171,41 @@ class ResolveReleasePromotionTest(unittest.TestCase):
"Release owner accepted the Windows unknown-publisher warning for v6.1.0.",
)
def test_v611_owner_exception_allows_disclosed_emergency_patch(self) -> None:
metadata = resolver.resolve_metadata(
version="6.1.1",
promoted_from_tag_input="",
rollback_version_input="v6.1.0",
ga_date_input="",
v5_eos_date_input="",
hotfix_exception=True,
hotfix_reason_input="Active customer update harm.",
release_notes_input=(
"Windows Unified Agent binaries are not Authenticode-signed for v6.1.1."
),
unsigned_windows_exception=True,
unsigned_windows_reason_input=(
"Release owner accepted the Windows unknown-publisher warning for v6.1.1."
),
list_stable_tags_fn=lambda: ["v6.1.0", "v6.0.5"],
list_same_version_rc_tags_fn=lambda version: [],
changed_paths_fn=lambda tag: ["install.sh"],
tag_exists_fn=lambda tag: tag == "v6.1.0",
tag_commit_fn=lambda tag: "v610-commit",
head_descends_from_fn=lambda commit: commit == "v610-commit",
)
self.assertEqual(metadata["promotion_mode"], "emergency-stable-patch")
self.assertEqual(metadata["rollback_tag"], "v6.1.0")
self.assertEqual(metadata["require_windows_signing"], "false")
self.assertEqual(metadata["unsigned_windows_exception"], "true")
def test_unsigned_windows_exception_is_rejected_for_other_stable_versions(self) -> None:
with self.assertRaisesRegex(ValueError, "approved only for stable v6.1.0"):
with self.assertRaisesRegex(ValueError, "approved only for stable v6.1.0 or v6.1.1"):
resolver.resolve_metadata(
version="6.1.1",
version="6.1.2",
promoted_from_tag_input="",
rollback_version_input="v6.1.0",
rollback_version_input="v6.1.1",
ga_date_input="",
v5_eos_date_input="",
hotfix_exception=True,
+25
View File
@@ -6,6 +6,7 @@ VERSION=""
MOBILE_RELEASE_DECISION=""
MOBILE_RELEASE_EVIDENCE=""
HOTFIX_REASON=""
UNSIGNED_WINDOWS_REASON=""
usage() {
cat <<'EOF'
@@ -20,6 +21,8 @@ Options:
--mobile-release-decision VALUE Override the inferred mobile decision.
--mobile-release-evidence VALUE Evidence for a mobile compatibility decision.
--emergency-hotfix-reason VALUE Bypass an RC-required risk with an explicit reason.
--unsigned-windows-exception-reason VALUE
Use an approved version-bound unsigned Windows exception.
-h, --help Show this help.
EOF
}
@@ -42,6 +45,10 @@ while [ "$#" -gt 0 ]; do
HOTFIX_REASON="${2:?--emergency-hotfix-reason requires a value}"
shift 2
;;
--unsigned-windows-exception-reason)
UNSIGNED_WINDOWS_REASON="${2:?--unsigned-windows-exception-reason requires a value}"
shift 2
;;
-h|--help)
usage
exit 0
@@ -111,6 +118,14 @@ if [ -n "$HOTFIX_REASON" ]; then
HOTFIX_EXCEPTION="true"
RESOLVER_ARGS+=(--hotfix-exception --hotfix-reason "$HOTFIX_REASON")
fi
UNSIGNED_WINDOWS_EXCEPTION="false"
if [ -n "$UNSIGNED_WINDOWS_REASON" ]; then
UNSIGNED_WINDOWS_EXCEPTION="true"
RESOLVER_ARGS+=(
--unsigned-windows-exception
--unsigned-windows-reason "$UNSIGNED_WINDOWS_REASON"
)
fi
PROMOTION_METADATA="$(python3 scripts/release_control/resolve_release_promotion.py "${RESOLVER_ARGS[@]}")"
ROLLBACK_TAG="$(awk -F= '$1 == "rollback_tag" {print $2}' <<<"$PROMOTION_METADATA")"
@@ -150,6 +165,8 @@ if [ "$MODE" = "dry-run" ]; then
--require v5_eos_date \
--require hotfix_exception \
--require hotfix_reason \
--require unsigned_windows_exception \
--require unsigned_windows_reason \
--require note \
--require mobile_release_decision \
--require mobile_release_evidence
@@ -163,6 +180,8 @@ if [ "$MODE" = "dry-run" ]; then
-f v5_eos_date="" \
-f hotfix_exception="$HOTFIX_EXCEPTION" \
-f hotfix_reason="$HOTFIX_REASON" \
-f unsigned_windows_exception="$UNSIGNED_WINDOWS_EXCEPTION" \
-f unsigned_windows_reason="$UNSIGNED_WINDOWS_REASON" \
-f note="Stable patch preflight for ${VERSION} at ${LOCAL_SHA}" \
-f mobile_release_decision="$MOBILE_RELEASE_DECISION" \
-f mobile_release_evidence="$MOBILE_RELEASE_EVIDENCE"
@@ -182,6 +201,8 @@ else
--require v5_eos_date \
--require hotfix_exception \
--require hotfix_reason \
--require unsigned_windows_exception \
--require unsigned_windows_reason \
--require draft_only \
--require mobile_release_decision \
--require mobile_release_evidence
@@ -195,6 +216,8 @@ else
--arg v5_eos_date "" \
--argjson hotfix_exception "$HOTFIX_EXCEPTION" \
--arg hotfix_reason "$HOTFIX_REASON" \
--argjson unsigned_windows_exception "$UNSIGNED_WINDOWS_EXCEPTION" \
--arg unsigned_windows_reason "$UNSIGNED_WINDOWS_REASON" \
--argjson draft_only false \
--arg mobile_release_decision "$MOBILE_RELEASE_DECISION" \
--arg mobile_release_evidence "$MOBILE_RELEASE_EVIDENCE" \
@@ -207,6 +230,8 @@ else
v5_eos_date: $v5_eos_date,
hotfix_exception: $hotfix_exception,
hotfix_reason: $hotfix_reason,
unsigned_windows_exception: $unsigned_windows_exception,
unsigned_windows_reason: $unsigned_windows_reason,
draft_only: $draft_only,
mobile_release_decision: $mobile_release_decision,
mobile_release_evidence: $mobile_release_evidence