Pulse/scripts/installtests/build_release_assets_test.go
pulse-triage[bot] eb7e70135d Preserve auto-update consent during manual version changes
An update, rollback or reinstall must not silently enable unattended updates. Share the existing-install opt-in prompt and retain affirmative CLI and interactive choices without changing helper refresh or fresh-install defaults.

Exercise all five existing main flows and add bounded configuration/timer intent observations to the signed published lifecycle rehearsal, including changed and unavailable negative controls.

Change-source: pulse-maintainer
2026-10-01 11:50:33 +01:00

4911 lines
208 KiB
Go

package installtests
import (
"archive/tar"
"compress/gzip"
"crypto/ed25519"
"crypto/rand"
"crypto/sha256"
"encoding/base64"
"encoding/json"
"os"
"os/exec"
"path/filepath"
"regexp"
"strconv"
"strings"
"testing"
"golang.org/x/crypto/ssh"
)
func TestBuildReleaseUsesV6InstallScripts(t *testing.T) {
content, err := os.ReadFile(repoFile("scripts", "build-release.sh"))
if err != nil {
t.Fatalf("read build-release.sh: %v", err)
}
script := string(content)
compileContent, err := os.ReadFile(repoFile("scripts", "build-release-binaries.sh"))
if err != nil {
t.Fatalf("read build-release-binaries.sh: %v", err)
}
compileScript := string(compileContent)
required := []string{
`SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"`,
`PULSE_REPO_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"`,
`cd "${PULSE_REPO_ROOT}"`,
`source "${SCRIPT_DIR}/release_asset_common.sh"`,
`RENDERED_INSTALLERS_DIR="${BUILD_DIR}/rendered-installers"`,
`go run ./scripts/render_installers.go \`,
// The published install.sh asset is the server installer (root install.sh).
// The rendered AGENT installer is shipped inside tarballs and Docker images
// at ./scripts/install.sh and served at the running server's /install.sh
// endpoint, but is intentionally not a top-level GitHub Releases asset:
// pulse-auto-update.sh, the root install.sh's own --rc/--version flows, and
// the README quickstart all expect releases/<tag>/install.sh
// to be the server installer that accepts --version vX.Y.Z.
`cp install.sh "$RELEASE_DIR/install.sh"`,
`[ -f "${RENDERED_INSTALLERS_DIR}/install.ps1" ] && cp "${RENDERED_INSTALLERS_DIR}/install.ps1" "$RELEASE_DIR/install.ps1"`,
`cp "$BUILD_DIR/pulse-agent-linux-amd64" "$RELEASE_DIR/"`,
`cp "$BUILD_DIR/pulse-agent-linux-arm64" "$RELEASE_DIR/"`,
`cp "$BUILD_DIR/pulse-agent-linux-armv7" "$RELEASE_DIR/"`,
`cp "$BUILD_DIR/pulse-agent-linux-armv6" "$RELEASE_DIR/"`,
`cp "$BUILD_DIR/pulse-agent-linux-386" "$RELEASE_DIR/"`,
`provider_msp_bundle_root="pulse-provider-msp-v${VERSION}"`,
`cp -a deploy/provider-msp/. "${provider_msp_bundle_dir}/"`,
`CONTROL_PLANE_IMAGE=ghcr.io/rcourtman/pulse-control-plane:v${VERSION}`,
`CP_PULSE_IMAGE=ghcr.io/rcourtman/pulse:v${VERSION}`,
`tar -czf "${provider_msp_bundle_asset}" -C "${BUILD_DIR}" "${provider_msp_bundle_root}"`,
}
for _, needle := range required {
if !strings.Contains(script, needle) {
t.Fatalf("build-release.sh missing required release asset copy: %s", needle)
}
}
// Sanity-check the opposite drift: the rendered AGENT installer must NOT be
// the published install.sh asset. Publishing it there shipped a broken LXC
// install + auto-update path across every v6 RC (rc.1 → rc.5).
if strings.Contains(script, `cp "${RENDERED_INSTALLERS_DIR}/install.sh" "$RELEASE_DIR/install.sh"`) {
t.Fatal("build-release.sh must not publish the rendered agent install.sh as the top-level release asset")
}
requiredScriptWiring := []string{
`agent_ldflags="$(./scripts/release_ldflags.sh agent --version "v${VERSION}" "${update_ldflags_args[@]}")"`,
`server_ldflags="$(./scripts/release_ldflags.sh server --version "v${VERSION}" --build-time "${build_time}" --git-commit "${git_commit}" "${license_ldflags_args[@]}" "${update_ldflags_args[@]}")"`,
`release_go_build_args=(-buildvcs=false -trimpath)`,
`"${release_go_build_args[@]}"`,
`RELEASE_PACKET_SBOM="pulse-v${VERSION}-release.sbom.spdx.json"`,
`pulse_release_prepare_signing_state "pulse-installer" "pulse-install"`,
`trap 'pulse_release_cleanup_signing_state' EXIT`,
`--installer-ssh-public-key "${PULSE_RELEASE_UPDATE_SSH_PUBLIC_KEY}"`,
`pulse_release_generate_packet_sbom "${RELEASE_DIR}" "${RELEASE_PACKET_SBOM}"`,
`mapfile -t checksum_files < <(pulse_release_collect_checksum_files "${RELEASE_DIR}")`,
`pulse_release_write_checksums_and_signatures "${RELEASE_DIR}" "${checksum_files[@]}"`,
}
for _, needle := range requiredScriptWiring {
if !strings.Contains(script, needle) {
t.Fatalf("build-release.sh missing canonical ldflags wiring: %s", needle)
}
}
if builds, cleanBuilds := strings.Count(script, "go build \\\n"), strings.Count(script, `"${release_go_build_args[@]}"`); builds != cleanBuilds {
t.Fatalf("build-release.sh must disable automatic VCS stamping on every release go build: builds=%d clean_builds=%d", builds, cleanBuilds)
}
for _, needle := range []string{
`release_go_build_args=(-buildvcs=false -trimpath)`,
`command=(go build "${release_go_build_args[@]}")`,
`package=./cmd/pulse-control-plane`,
`task_components+=(control-plane)`,
} {
if !strings.Contains(compileScript, needle) {
t.Fatalf("build-release-binaries.sh missing clean compilation contract: %s", needle)
}
}
helperBytes, err := os.ReadFile(repoFile("scripts", "release_asset_common.sh"))
if err != nil {
t.Fatalf("read release_asset_common.sh: %v", err)
}
helper := string(helperBytes)
helperRequired := []string{
`: "${PULSE_SCRIPTS_DIR:=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)}"`,
`: "${PULSE_REPO_ROOT:=$(cd "${PULSE_SCRIPTS_DIR}/.." && pwd)}"`,
`go -C "${PULSE_REPO_ROOT}" run ./scripts/release_update_key.go "$@"`,
`pulse_release_go_run_update_key public-key --private-key "${PULSE_UPDATE_SIGNING_KEY}"`,
`pulse_release_go_run_update_key fingerprint --public-key "${PULSE_RELEASE_UPDATE_PUBLIC_KEY}"`,
`pulse_release_go_run_update_key public-key-ssh --private-key "${PULSE_UPDATE_SIGNING_KEY}"`,
`pulse_release_go_run_update_key openssh-private-key --private-key "${PULSE_UPDATE_SIGNING_KEY}"`,
`pulse_release_go_run_update_key sign --private-key "${PULSE_UPDATE_SIGNING_KEY}" --file "${absolute_file}"`,
`PULSE_UPDATE_SIGNING_PUBLIC_KEY`,
`PULSE_UPDATE_SIGNING_PUBLIC_KEY_FINGERPRINT`,
`Verified update signing public key fingerprint: ${PULSE_RELEASE_UPDATE_PUBLIC_KEY_FINGERPRINT}`,
`ssh-keygen -q -Y sign`,
`"${resolved_tool}" "dir:${release_dir}" -o "spdx-json=${tmp_sbom}"`,
`if compgen -G "pulse-*.sbom.spdx.json" > /dev/null; then`,
`find . -maxdepth 1 -type f \( -name '*.sig' -o -name '*.sshsig' \) -delete`,
`pulse_release_stage_server_archive()`,
`for target in "${PULSE_RELEASE_AGENT_TARGETS[@]}"; do`,
`install -m 0755 "${server_binary}" "${staging_dir}/bin/pulse"`,
}
for _, needle := range helperRequired {
if !strings.Contains(helper, needle) {
t.Fatalf("release_asset_common.sh missing canonical release asset wiring: %s", needle)
}
}
for _, needle := range []string{
`package_workers="${PULSE_RELEASE_PACKAGE_WORKERS:-4}"`,
`package_server_target "${build_name}" &`,
`pulse_release_stage_server_archive \`,
} {
if !strings.Contains(script, needle) {
t.Fatalf("build-release.sh missing bounded parallel archive assembly: %s", needle)
}
}
}
// A no-flag, signed published-installer rehearsal must catch re-enabling after
// a version change. The install smoke's explicit disable flag alone cannot.
func TestPublishedLifecycleRehearsalPreservesAutoUpdateChoice(t *testing.T) {
content, err := os.ReadFile(repoFile("scripts", "release_lifecycle_rehearsal.sh"))
if err != nil {
t.Fatal(err)
}
for _, required := range []string{
`auto_update_snapshot > "${WORK_DIR}/state/auto-updates.baseline.tsv"`,
`check_auto_update_intent upgrade || true`,
`check_auto_update_intent rollback || true`,
`cexec '/bin/update --version "$TARGET"'`,
} {
if !strings.Contains(string(content), required) {
t.Fatalf("published installer lifecycle proof missing %s", required)
}
}
for _, line := range strings.Split(string(content), "\n") {
if strings.Contains(line, "/bin/update --version") && strings.Contains(line, "--disable-auto-updates") {
t.Fatal("lifecycle proof must observe installer consent, not bypass it")
}
}
}
func TestSecurityScanRevalidatesLatestStableDelivery(t *testing.T) {
content, err := os.ReadFile(repoFile(".github", "workflows", "security-scan.yml"))
if err != nil {
t.Fatalf("read security scan workflow: %v", err)
}
workflow := string(content)
required := []string{
`cron: '17 */6 * * *'`,
"workflow_run:",
"workflows: [Release Convergence]",
`github.event_name != 'workflow_run' || !contains(github.event.workflow_run.display_title, '-')`,
"release-continuity:",
"Latest stable release continuity",
"docker/setup-buildx-action@37fe631027851001ddb9b187196cc803df7f5f0e",
`"repos/${REPOSITORY}/releases/latest"`,
`scripts/release_control/release_continuity.py release`,
"release-diagnostic.json",
"Reject orphaned stable publication tags",
`"repos/${REPOSITORY}/git/matching-refs/tags/v?per_page=100"`,
`"repos/${REPOSITORY}/releases?per_page=100"`,
"https://ghcr.io/token?service=ghcr.io&scope=repository:",
"https://auth.docker.io/token?service=registry.docker.io&scope=repository:",
`"${registry_url}/v2/${image}/tags/list?n=10000"`,
`--registry-tags-json "${evidence}/ghcr-pulse-tags.json"`,
`--registry-tags-json "${evidence}/docker-pulse-tags.json"`,
`scripts/release_control/release_continuity.py frontier`,
"frontier-diagnostic.json",
"Bind the release activation marker",
`!cancelled()`,
`steps.release.outputs.referenceable == 'true'`,
`scripts/release_control/release_continuity.py activation`,
"activation-diagnostic.json",
`steps.activation.outcome == 'success'`,
`steps.frontier.outcome == 'success'`,
`./scripts/verify-github-release-integrity.sh`,
`./scripts/validate-published-release.sh`,
`PULSE_UPDATE_SIGNING_PUBLIC_KEY: ${{ vars.PULSE_UPDATE_SIGNING_PUBLIC_KEY }}`,
`./scripts/verify-release-container-images.sh`,
`EXPECTED_SERVER_DIGEST: ${{ steps.activation.outputs.server_image_digest }}`,
`EXPECTED_CONTROL_PLANE_DIGEST: ${{ steps.activation.outputs.control_plane_image_digest }}`,
`./scripts/verify-stable-container-aliases.sh`,
`stable_container_aliases: $alias_result`,
`activation_binding: $activation_result`,
`release_identity: $release_diagnostic[0]`,
`stable_publication_frontier: $frontier_diagnostic[0]`,
`CONVERGENCE_RUN_ID: ${{ github.event.workflow_run.id }}`,
`TRIGGER_SCHEDULE: ${{ github.event.schedule }}`,
`mode=release_lock`,
`mode: $mode`,
`release_convergence_run: {`,
`./scripts/verify-release-helm-chart.sh`,
`EXPECTED_HELM_DIGEST: ${{ steps.activation.outputs.helm_chart_digest }}`,
"continuity-evidence.json",
"retention-days: 90",
}
for _, needle := range required {
if !strings.Contains(workflow, needle) {
t.Fatalf("scheduled release continuity check missing contract: %s", needle)
}
}
if strings.Contains(workflowJobBlock(t, workflow, "release-continuity"), "contents: write") {
t.Fatal("scheduled release continuity check must remain read-only")
}
for _, jobName := range []string{"container-lifecycle", "govulncheck", "npm-audit"} {
block := workflowJobBlock(t, workflow, jobName)
if !strings.Contains(block, `github.event_name != 'workflow_run'`) {
t.Fatalf("%s must not run for the post-convergence continuity trigger", jobName)
}
if !strings.Contains(block, `github.event.schedule != '17 */6 * * *'`) {
t.Fatalf("%s must not run for the six-hour release-lock trigger", jobName)
}
}
for _, stepName := range []string{
"Set up Go",
"Set up Helm",
"Set up Docker Buildx",
"Verify immutable release and build provenance",
"Authenticate every published release asset",
"Verify exact-version container identities",
"Verify stable container discovery aliases",
"Verify exact-version Helm identity",
} {
step := workflowStepBlock(t, workflowJobBlock(t, workflow, "release-continuity"), stepName)
if !strings.Contains(step, `github.event.schedule != '17 */6 * * *'`) {
t.Fatalf("%s must not run for the six-hour release-lock trigger", stepName)
}
}
for _, stepName := range []string{
"Verify immutable release and build provenance",
"Authenticate every published release asset",
"Verify exact-version container identities",
"Verify stable container discovery aliases",
"Verify exact-version Helm identity",
} {
step := workflowStepBlock(t, workflowJobBlock(t, workflow, "release-continuity"), stepName)
for _, admission := range []string{
`steps.release.outcome == 'success'`,
`steps.frontier.outcome == 'success'`,
`steps.activation.outcome == 'success'`,
} {
if !strings.Contains(step, admission) {
t.Fatalf("%s must remain behind continuity admission: %s", stepName, admission)
}
}
}
}
func TestProPackagingBuildsFrontendEmbedWithoutTransferringBundle(t *testing.T) {
content, err := os.ReadFile(repoFile("scripts", "build-release-binaries.sh"))
if err != nil {
t.Fatalf("read build-release-binaries.sh: %v", err)
}
script := string(content)
for _, needle := range []string{
`build_frontend >"${frontend_log}" 2>&1 &`,
`npm --prefix frontend-modern ci`,
`npm --prefix frontend-modern run build`,
`if [[ "${PROFILE}" == "full" ]]; then`,
`cp -a frontend-modern/dist/. "${FRONTEND_DIR}/"`,
`if [[ "${component}" == server || "${component}" == control-plane ]]; then`,
`finish_frontend`,
`wait -n -p completed_pid "${active_pids[@]}"`,
`completed release compilation child is not in the active task set`,
`transfer public agent-side binaries only`,
} {
if !strings.Contains(script, needle) {
t.Fatalf("build-release-binaries.sh missing Pro frontend embed contract: %s", needle)
}
}
if strings.Contains(script, `if [[ "${PROFILE}" == "full" ]]; then
mkdir -p "${FRONTEND_DIR}"
echo "Building exact-SHA frontend bundle..."
npm --prefix frontend-modern ci`) {
t.Fatal("Pro packaging must build the frontend embed prerequisite")
}
if strings.Contains(script, `wait -n -p completed_pid;`) {
t.Fatal("release compilation must not let wait -n consume the independent frontend child")
}
serverGate := strings.Index(script, `if [[ "${component}" == server || "${component}" == control-plane ]]; then`)
serverLaunch := strings.Index(script, `build_one "${component}" "${target}"`)
if serverGate < 0 || serverLaunch < 0 || serverGate > serverLaunch ||
!strings.Contains(script[serverGate:serverLaunch], "finish_frontend") {
t.Fatal("server and control-plane tasks must join the frontend build before launch")
}
}
func TestBuildReleasePackagesPulseAgentHelperForLinux(t *testing.T) {
targetScriptPath := repoFile("scripts", "release_build_targets.sh")
targetCmd := exec.Command("bash", "-c", `
source "$1"
for target in "${PULSE_RELEASE_AGENT_TARGETS[@]}"; do
if [[ "${target}" == linux-* ]]; then
printf 'agent:%s\n' "${target}"
fi
done
for target in "${PULSE_RELEASE_AGENT_HELPER_TARGETS[@]}"; do
printf 'helper:%s:%s\n' "${target}" "$(pulse_release_binary_filename agent-helper "${target}")"
done
for target in "${PULSE_RELEASE_AGENT_RUNNER_TARGETS[@]}"; do
printf 'runner:%s:%s\n' "${target}" "$(pulse_release_binary_filename agent-runner "${target}")"
done
`, "pulse-agent-helper-target-test", targetScriptPath)
targetOutput, err := targetCmd.CombinedOutput()
if err != nil {
t.Fatalf("inspect release helper target matrix: %v\n%s", err, targetOutput)
}
var linuxAgentTargets []string
var helperTargets []string
var runnerTargets []string
for _, line := range strings.Split(strings.TrimSpace(string(targetOutput)), "\n") {
switch {
case strings.HasPrefix(line, "agent:"):
linuxAgentTargets = append(linuxAgentTargets, strings.TrimPrefix(line, "agent:"))
case strings.HasPrefix(line, "helper:"):
parts := strings.Split(line, ":")
if len(parts) != 3 {
t.Fatalf("unexpected helper target output %q", line)
}
helperTargets = append(helperTargets, parts[1])
wantFilename := "pulse-agent-helper-" + parts[1]
if parts[2] != wantFilename {
t.Fatalf("helper target %s filename = %s, want %s", parts[1], parts[2], wantFilename)
}
case strings.HasPrefix(line, "runner:"):
parts := strings.Split(line, ":")
if len(parts) != 3 {
t.Fatalf("unexpected runner target output %q", line)
}
runnerTargets = append(runnerTargets, parts[1])
wantFilename := "pulse-agent-runner-" + parts[1]
if parts[2] != wantFilename {
t.Fatalf("runner target %s filename = %s, want %s", parts[1], parts[2], wantFilename)
}
}
}
if got, want := strings.Join(helperTargets, ","), strings.Join(linuxAgentTargets, ","); got != want {
t.Fatalf("helper target matrix = %s, want Linux Unified Agent matrix %s", got, want)
}
if got, want := strings.Join(runnerTargets, ","), strings.Join(linuxAgentTargets, ","); got != want {
t.Fatalf("runner target matrix = %s, want Linux Unified Agent matrix %s", got, want)
}
buildBytes, err := os.ReadFile(repoFile("scripts", "build-release.sh"))
if err != nil {
t.Fatalf("read build-release.sh: %v", err)
}
compileBytes, err := os.ReadFile(repoFile("scripts", "build-release-binaries.sh"))
if err != nil {
t.Fatalf("read build-release-binaries.sh: %v", err)
}
commonBytes, err := os.ReadFile(repoFile("scripts", "release_asset_common.sh"))
if err != nil {
t.Fatalf("read release_asset_common.sh: %v", err)
}
workflowBytes, err := os.ReadFile(repoFile(".github", "workflows", "create-release.yml"))
if err != nil {
t.Fatalf("read create-release.yml: %v", err)
}
buildScript := string(buildBytes)
compileScript := string(compileBytes)
commonScript := string(commonBytes)
workflow := string(workflowBytes)
for _, needle := range []string{
`agent_helper_build_order=("${PULSE_RELEASE_AGENT_HELPER_TARGETS[@]}")`,
`output_path="${BUILD_DIR}/$(pulse_release_binary_filename agent-helper "${target}")"`,
`./cmd/pulse-agent-helper`,
`-ldflags="${agent_ldflags}"`,
`cp "$BUILD_DIR/pulse-agent-helper-${target}" "$universal_dir/bin/pulse-agent-helper-${target}"`,
`tar -czf "$RELEASE_DIR/pulse-agent-helper-v${VERSION}-${target}.tar.gz" -C "$BUILD_DIR" "pulse-agent-helper-${target}"`,
`cp "$BUILD_DIR/pulse-agent-helper-${target}" "$RELEASE_DIR/"`,
} {
if !strings.Contains(buildScript, needle) {
t.Fatalf("build-release.sh missing agent helper release wiring: %s", needle)
}
}
for _, needle := range []string{
`agent_runner_build_order=("${PULSE_RELEASE_AGENT_RUNNER_TARGETS[@]}")`,
`output_path="${BUILD_DIR}/$(pulse_release_binary_filename agent-runner "${target}")"`,
`./cmd/pulse-agent-runner`,
`cp "$BUILD_DIR/pulse-agent-runner-${target}" "$universal_dir/bin/pulse-agent-runner-${target}"`,
`tar -czf "$RELEASE_DIR/pulse-agent-runner-v${VERSION}-${target}.tar.gz" -C "$BUILD_DIR" "pulse-agent-runner-${target}"`,
`cp "$BUILD_DIR/pulse-agent-runner-${target}" "$RELEASE_DIR/"`,
} {
if !strings.Contains(buildScript, needle) {
t.Fatalf("build-release.sh missing agent runner release wiring: %s", needle)
}
}
for _, needle := range []string{
`for target in "${PULSE_RELEASE_AGENT_HELPER_TARGETS[@]}"; do`,
`task_components+=(agent-helper)`,
`package=./cmd/pulse-agent-helper`,
} {
if !strings.Contains(compileScript, needle) {
t.Fatalf("build-release-binaries.sh missing agent helper compilation wiring: %s", needle)
}
}
for _, needle := range []string{
`for target in "${PULSE_RELEASE_AGENT_RUNNER_TARGETS[@]}"; do`,
`task_components+=(agent-runner)`,
`package=./cmd/pulse-agent-runner`,
} {
if !strings.Contains(compileScript, needle) {
t.Fatalf("build-release-binaries.sh missing action runner compilation wiring: %s", needle)
}
}
for _, needle := range []string{
`if [[ ${#PULSE_RELEASE_AGENT_HELPER_TARGETS[@]} -eq 0 ]]; then`,
`src="${agent_binary_dir}/pulse-agent-helper-${target}"`,
`dest="${staging_dir}/bin/pulse-agent-helper-${target}"`,
`if compgen -G "pulse-agent-helper-linux-*" > /dev/null; then`,
} {
if !strings.Contains(commonScript, needle) {
t.Fatalf("release_asset_common.sh missing agent helper packaging wiring: %s", needle)
}
}
for _, needle := range []string{
`if [[ ${#PULSE_RELEASE_AGENT_RUNNER_TARGETS[@]} -eq 0 ]]; then`,
`src="${agent_binary_dir}/pulse-agent-runner-${target}"`,
`dest="${staging_dir}/bin/pulse-agent-runner-${target}"`,
} {
if !strings.Contains(commonScript, needle) {
t.Fatalf("release_asset_common.sh missing action runner packaging wiring: %s", needle)
}
}
helperStage := strings.Index(commonScript, `src="${agent_binary_dir}/pulse-agent-helper-${target}"`)
binSigning := strings.Index(commonScript, `pulse_release_sign_directory_assets "${staging_dir}/bin"`)
if helperStage < 0 || binSigning < 0 || helperStage > binSigning {
t.Fatal("server archives must stage helper binaries before signing their bin payload")
}
releaseDir := t.TempDir()
helperAsset := "pulse-agent-helper-linux-amd64"
if err := os.WriteFile(filepath.Join(releaseDir, helperAsset), []byte("helper"), 0o755); err != nil {
t.Fatalf("write helper checksum fixture: %v", err)
}
checksumCmd := exec.Command("bash", "-c", `source "$1"; pulse_release_collect_checksum_files "$2"`, "pulse-agent-helper-checksum-test", repoFile("scripts", "release_asset_common.sh"), releaseDir)
checksumOutput, err := checksumCmd.CombinedOutput()
if err != nil {
t.Fatalf("collect helper release checksum files: %v\n%s", err, checksumOutput)
}
if !strings.Contains(string(checksumOutput), helperAsset) {
t.Fatalf("helper release asset missing from checksum/signature input:\n%s", checksumOutput)
}
for _, target := range helperTargets {
asset := "release/pulse-agent-helper-" + target
if !strings.Contains(workflow, asset) {
t.Fatalf("create-release.yml missing bare helper upload: %s", asset)
}
}
for _, target := range runnerTargets {
asset := "release/pulse-agent-runner-" + target
if !strings.Contains(workflow, asset) {
t.Fatalf("create-release.yml missing bare action runner upload: %s", asset)
}
}
for _, signatureGlob := range []string{
`release_upload_with_retry "${TAG}" release/*.sig --clobber`,
`release_upload_with_retry "${TAG}" release/*.sshsig --clobber`,
} {
if !strings.Contains(workflow, signatureGlob) {
t.Fatalf("create-release.yml missing helper-compatible signature upload: %s", signatureGlob)
}
}
}
func TestReleaseContainerTargetsConsumeImmutableCandidate(t *testing.T) {
dockerfileBytes, err := os.ReadFile(repoFile("Dockerfile"))
if err != nil {
t.Fatalf("read Dockerfile: %v", err)
}
prepareBytes, err := os.ReadFile(repoFile("scripts", "prepare-release-container-context.sh"))
if err != nil {
t.Fatalf("read prepare-release-container-context.sh: %v", err)
}
qualifierBytes, err := os.ReadFile(repoFile(".github", "workflows", "qualify-release-containers.yml"))
if err != nil {
t.Fatalf("read qualify-release-containers.yml: %v", err)
}
dockerfile := string(dockerfileBytes)
prepareScript := string(prepareBytes)
qualifier := string(qualifierBytes)
for _, needle := range []string{
"FROM pulse-runtime-foundation AS prebuilt-runtime-base",
"COPY --from=release_payload /${TARGETARCH:-amd64}/bin/pulse ./pulse",
"FROM prebuilt-runtime-base AS runtime_prebuilt",
"COPY --from=release_payload /amd64/bin/pulse /opt/pulse/bin/pulse-linux-amd64",
"COPY --from=release_payload /arm64/bin/pulse /opt/pulse/bin/pulse-linux-arm64",
"! -name '*.sig' ! -name '*.sshsig' -exec chmod 755 {} +",
"FROM alpine:3.24@sha256:",
"AS agent_runtime_prebuilt",
} {
if !strings.Contains(dockerfile, needle) {
t.Fatalf("Dockerfile missing immutable-candidate container target: %s", needle)
}
}
for _, needle := range []string{
`pulse-v${version}-linux-${arch}.tar.gz`,
`validate_archive_entries "${archive}"`,
`tar --no-same-owner --no-same-permissions -xzf`,
`bin/pulse.sig`,
`bin/pulse.sshsig`,
`--exclude=pulse.sig`,
`--exclude=pulse.sshsig`,
`find "${output_dir}/arm64" -depth -mindepth 1`,
} {
if !strings.Contains(prepareScript, needle) {
t.Fatalf("prepare-release-container-context.sh missing candidate guard: %s", needle)
}
}
for _, needle := range []string{
`actual_embedded_agent="$(docker run --rm --entrypoint /bin/sh`,
`test "${actual_embedded_agent}" = "${expected_agent}"`,
`test "$(readlink /usr/local/bin/pulse-agent)" = "/opt/pulse/bin/pulse-agent-linux-amd64"`,
`test -x /usr/local/bin/pulse-agent`,
`for arch in amd64 arm64 386; do`,
`target="pulse-agent-windows-${arch}.exe${suffix}"`,
`test -s "${alias}"`,
`test ! -x "$sidecar"`,
} {
if !strings.Contains(qualifier, needle) {
t.Fatalf("exact-candidate container qualification missing embedded agent mode guard: %s", needle)
}
}
}
func TestWindowsAgentAliasesCarryDetachedSignatureSidecars(t *testing.T) {
commonPath := repoFile("scripts", "release_asset_common.sh")
tempDir := t.TempDir()
for _, arch := range []string{"amd64", "arm64", "386"} {
base := filepath.Join(tempDir, "pulse-agent-windows-"+arch+".exe")
for _, suffix := range []string{"", ".sig", ".sshsig"} {
if err := os.WriteFile(base+suffix, []byte("packet-"+arch+suffix), 0o644); err != nil {
t.Fatalf("write Windows packet fixture: %v", err)
}
}
}
cmd := exec.Command("bash", "-c", `source "$1"; pulse_release_link_windows_agent_aliases "$2"`, "windows-agent-alias-test", commonPath, tempDir)
if output, err := cmd.CombinedOutput(); err != nil {
t.Fatalf("create Windows agent packet aliases: %v\n%s", err, output)
}
for _, arch := range []string{"amd64", "arm64", "386"} {
for _, suffix := range []string{"", ".sig", ".sshsig"} {
alias := filepath.Join(tempDir, "pulse-agent-windows-"+arch+suffix)
want := "pulse-agent-windows-" + arch + ".exe" + suffix
got, err := os.Readlink(alias)
if err != nil {
t.Fatalf("read Windows agent alias %s: %v", alias, err)
}
if got != want {
t.Fatalf("Windows agent alias %s targets %q, want %q", alias, got, want)
}
if info, err := os.Stat(alias); err != nil || info.Size() == 0 {
t.Fatalf("Windows agent alias %s does not resolve to a non-empty packet member: info=%v err=%v", alias, info, err)
}
}
}
assertFileContainsAll(t, repoFile("Dockerfile"),
`ln -s pulse-agent-windows-amd64.exe.sig /opt/pulse/bin/pulse-agent-windows-amd64.sig`,
`ln -s pulse-agent-windows-amd64.exe.sshsig /opt/pulse/bin/pulse-agent-windows-amd64.sshsig`,
`ln -sf pulse-agent-windows-amd64.exe.sig /opt/pulse/bin/pulse-agent-windows-amd64.sig`,
`ln -sf pulse-agent-windows-amd64.exe.sshsig /opt/pulse/bin/pulse-agent-windows-amd64.sshsig`,
)
assertFileContainsAll(t, repoFile("scripts", "build-release.sh"),
`pulse_release_sign_directory_assets "$universal_dir/bin"`,
`pulse_release_link_windows_agent_aliases "$universal_dir/bin"`,
)
assertFileContainsAll(t, repoFile("scripts", "prepare-release-container-context.sh"),
`for suffix in "" .sig .sshsig; do`,
`expected_target="pulse-agent-windows-${windows_arch}.exe${suffix}"`,
)
}
func TestReleaseContainerContextTreatsServerSignaturesAsArchitectureBound(t *testing.T) {
version := "6.3.0-rc.test"
releaseDir := t.TempDir()
outputDir := filepath.Join(t.TempDir(), "container-context")
writeArchive := func(arch string, driftUniversalPayload bool) {
t.Helper()
archivePath := filepath.Join(releaseDir, "pulse-v"+version+"-linux-"+arch+".tar.gz")
archiveFile, err := os.Create(archivePath)
if err != nil {
t.Fatalf("create %s archive: %v", arch, err)
}
gzipWriter := gzip.NewWriter(archiveFile)
tarWriter := tar.NewWriter(gzipWriter)
files := map[string]string{
"bin/pulse": "server-" + arch,
"bin/pulse.sig": "minisign-" + arch,
"bin/pulse.sshsig": "sshsig-" + arch,
"bin/pulse-agent-linux-amd64": "shared-agent",
"bin/pulse-agent-linux-amd64.sig": "shared-agent-minisign",
"bin/pulse-agent-linux-amd64.sshsig": "shared-agent-sshsig",
"bin/pulse-agent-windows-amd64.exe": "shared-windows-amd64-agent",
"bin/pulse-agent-windows-arm64.exe": "shared-windows-arm64-agent",
"bin/pulse-agent-windows-386.exe": "shared-windows-386-agent",
"scripts/install-container-agent.sh": "shared-container-installer",
"scripts/install-docker.sh": "shared-docker-installer",
"scripts/install.sh": "shared-agent-installer",
"scripts/install.sh.sig": "shared-installer-minisign",
"scripts/install.sh.sshsig": "shared-installer-sshsig",
"VERSION": version,
}
for _, windowsArch := range []string{"amd64", "arm64", "386"} {
name := "bin/pulse-agent-windows-" + windowsArch + ".exe"
files[name+".sig"] = "shared-windows-" + windowsArch + "-signature"
files[name+".sshsig"] = "shared-windows-" + windowsArch + "-ssh-signature"
}
for _, helperTarget := range []string{"linux-amd64", "linux-arm64", "linux-armv7", "linux-armv6", "linux-386"} {
name := "bin/pulse-agent-helper-" + helperTarget
files[name] = "shared-helper-" + helperTarget
files[name+".sig"] = "shared-helper-signature-" + helperTarget
files[name+".sshsig"] = "shared-helper-ssh-signature-" + helperTarget
}
for _, runnerTarget := range []string{"linux-amd64", "linux-arm64", "linux-armv7", "linux-armv6", "linux-386"} {
name := "bin/pulse-agent-runner-" + runnerTarget
files[name] = "shared-runner-" + runnerTarget
files[name+".sig"] = "shared-runner-signature-" + runnerTarget
files[name+".sshsig"] = "shared-runner-ssh-signature-" + runnerTarget
}
if driftUniversalPayload {
files["scripts/install.sh"] = "drifted-agent-installer"
}
for name, content := range files {
header := &tar.Header{Name: name, Mode: 0o644, Size: int64(len(content))}
if strings.HasPrefix(name, "bin/") || strings.HasSuffix(name, ".sh") {
header.Mode = 0o755
}
if err := tarWriter.WriteHeader(header); err != nil {
t.Fatalf("write %s header to %s archive: %v", name, arch, err)
}
if _, err := tarWriter.Write([]byte(content)); err != nil {
t.Fatalf("write %s to %s archive: %v", name, arch, err)
}
}
for _, windowsArch := range []string{"amd64", "arm64", "386"} {
for _, suffix := range []string{"", ".sig", ".sshsig"} {
name := "bin/pulse-agent-windows-" + windowsArch + suffix
target := "pulse-agent-windows-" + windowsArch + ".exe" + suffix
header := &tar.Header{Name: name, Mode: 0o777, Typeflag: tar.TypeSymlink, Linkname: target}
if err := tarWriter.WriteHeader(header); err != nil {
t.Fatalf("write %s alias to %s archive: %v", name, arch, err)
}
}
}
if err := tarWriter.Close(); err != nil {
t.Fatalf("close %s tar stream: %v", arch, err)
}
if err := gzipWriter.Close(); err != nil {
t.Fatalf("close %s gzip stream: %v", arch, err)
}
if err := archiveFile.Close(); err != nil {
t.Fatalf("close %s archive: %v", arch, err)
}
}
writeArchive("amd64", false)
writeArchive("arm64", false)
cmd := exec.Command(repoFile("scripts", "prepare-release-container-context.sh"), releaseDir, version, outputDir)
if output, err := cmd.CombinedOutput(); err != nil {
t.Fatalf("prepare context with architecture-bound server signatures: %v\n%s", err, output)
}
for _, relativePath := range []string{
"amd64/bin/pulse",
"amd64/bin/pulse.sig",
"amd64/bin/pulse.sshsig",
"arm64/bin/pulse",
} {
if _, err := os.Stat(filepath.Join(outputDir, filepath.FromSlash(relativePath))); err != nil {
t.Fatalf("prepared context missing %s: %v", relativePath, err)
}
}
if _, err := os.Stat(filepath.Join(outputDir, "arm64", "scripts", "install.sh")); !os.IsNotExist(err) {
t.Fatalf("prepared context retained duplicate universal payload: %v", err)
}
for _, windowsArch := range []string{"amd64", "arm64", "386"} {
for _, suffix := range []string{"", ".sig", ".sshsig"} {
aliasPath := filepath.Join(outputDir, "amd64", "bin", "pulse-agent-windows-"+windowsArch+suffix)
if _, err := os.Lstat(aliasPath); !os.IsNotExist(err) {
t.Fatalf("prepared context retained recreated Windows alias %s: %v", aliasPath, err)
}
}
}
writeArchive("arm64", true)
cmd = exec.Command(repoFile("scripts", "prepare-release-container-context.sh"), releaseDir, version, outputDir)
if output, err := cmd.CombinedOutput(); err == nil || !strings.Contains(string(output), "differ outside the target server binary and its signatures") {
t.Fatalf("prepare context accepted drifted universal payload: err=%v\n%s", err, output)
}
}
func TestValidateReleaseScansArchivesOnceInParallel(t *testing.T) {
content, err := os.ReadFile(repoFile("scripts", "validate-release.sh"))
if err != nil {
t.Fatalf("read validate-release.sh: %v", err)
}
script := string(content)
for _, needle := range []string{
`platform_tar_entries=(`,
`check_tar_entries_nonempty "$tarball" "${platform_tar_entries[@]}"`,
`validate_platform_tarball "${arch}" >"${log_path}" 2>&1 &`,
`validate_universal_tarball >"${archive_validation_logs}/universal.log" 2>&1 &`,
`wait "${archive_validation_pids[$index]}"`,
} {
if !strings.Contains(script, needle) {
t.Fatalf("validate-release.sh missing single-pass parallel archive validation: %s", needle)
}
}
if strings.Contains(script, `tar -tzf "$tarball"`) {
t.Fatal("validate-release.sh must not rescan every platform archive with tar -t")
}
}
func TestProviderMSPReleaseBundleIsRequiredAndValidated(t *testing.T) {
validateBytes, err := os.ReadFile(repoFile("scripts", "validate-release.sh"))
if err != nil {
t.Fatalf("read validate-release.sh: %v", err)
}
validate := string(validateBytes)
for _, needle := range []string{
`"pulse-provider-msp-v${PULSE_VERSION}.tar.gz"`,
`section "Validating provider MSP bundle"`,
`provider_msp_root="pulse-provider-msp-v${PULSE_VERSION}"`,
`CONTROL_PLANE_IMAGE=ghcr.io/rcourtman/pulse-control-plane:${PULSE_TAG}`,
`CP_PULSE_IMAGE=ghcr.io/rcourtman/pulse:${PULSE_TAG}`,
} {
if !strings.Contains(validate, needle) {
t.Fatalf("validate-release.sh missing provider MSP bundle guard: %s", needle)
}
}
workflowBytes, err := os.ReadFile(repoFile(".github", "workflows", "create-release.yml"))
if err != nil {
t.Fatalf("read create-release.yml: %v", err)
}
workflow := string(workflowBytes)
if !strings.Contains(workflow, `"pulse-provider-msp-${TAG}.tar.gz"`) {
t.Fatal("create-release.yml must read the exact provider MSP asset before customer activation")
}
}
func TestHelmChartShipsOpenShiftProfile(t *testing.T) {
read := func(parts ...string) string {
t.Helper()
content, err := os.ReadFile(repoFile(parts...))
if err != nil {
t.Fatalf("read %s: %v", filepath.Join(parts...), err)
}
return string(content)
}
values := read("deploy", "helm", "pulse", "values.yaml")
agent := read("deploy", "helm", "pulse", "templates", "agent.yaml")
deployment := read("deploy", "helm", "pulse", "templates", "deployment.yaml")
rbac := read("deploy", "helm", "pulse", "templates", "agent-rbac.yaml")
helmCI := read(".github", "workflows", "helm-ci.yml")
docs := read("docs", "KUBERNETES.md")
for _, required := range []string{
"openShift:",
"kubernetesAgent:",
"clusterID:",
"rbac:",
} {
if !strings.Contains(values, required) {
t.Fatalf("values.yaml missing OpenShift chart value %q", required)
}
}
for _, required := range []string{
`$openShiftAgent := and .Values.openShift.enabled .Values.openShift.kubernetesAgent.enabled`,
`- --enable-kubernetes`,
`- --enable-host=false`,
`"name" "PULSE_AGENT_ID"`,
`$dockerSocketEnabled := and .Values.agent.dockerSocket.enabled (not $openShiftAgent)`,
`"runAsNonRoot" true`,
`omit $openShiftSecurityContext "runAsUser" "runAsGroup"`,
} {
if !strings.Contains(agent, required) {
t.Fatalf("agent template missing OpenShift contract %q", required)
}
}
for _, required := range []string{
`.Values.openShift.enabled`,
`omit $openShiftContainerSecurityContext "runAsUser" "runAsGroup"`,
`"allowPrivilegeEscalation" false`,
} {
if !strings.Contains(deployment, required) {
t.Fatalf("server deployment missing OpenShift SCC contract %q", required)
}
}
for _, required := range []string{
"kind: ClusterRole",
"kind: ClusterRoleBinding",
`apiGroups: ["metrics.k8s.io"]`,
`resources: ["nodes", "pods"]`,
`apiGroups: ["discovery.k8s.io"]`,
`resources: ["endpointslices"]`,
`apiGroups: ["rbac.authorization.k8s.io"]`,
} {
if !strings.Contains(rbac, required) {
t.Fatalf("agent RBAC template missing read-only collector rule %q", required)
}
}
if strings.Contains(rbac, "- secrets") || strings.Contains(rbac, "- nodes/proxy") {
t.Fatal("OpenShift default role must not grant Secrets or direct kubelet proxy access")
}
for _, required := range []string{
"Render and verify the OpenShift profile",
"--show-only templates/agent-rbac.yaml",
`grep -Eq "runAs(User|Group):|fsGroup:"`,
`grep -q "/var/run/docker.sock"`,
} {
if !strings.Contains(helmCI, required) {
t.Fatalf("Helm CI missing OpenShift render assertion %q", required)
}
}
if !strings.Contains(docs, "--set openShift.enabled=true") ||
!strings.Contains(docs, "--set openShift.kubernetesAgent.enabled=true") ||
!strings.Contains(docs, "create secret generic pulse-server-env") ||
!strings.Contains(docs, "create secret generic pulse-agent-env") {
t.Fatal("Kubernetes guide must document the shipped OpenShift profile")
}
if strings.Contains(docs, "--set-string agent.secretEnv.data.PULSE_TOKEN") {
t.Fatal("OpenShift guide must not persist the agent token in Helm release values")
}
}
func shieldsBadgeMessage(value string) string {
return strings.ReplaceAll(value, "-", "--")
}
// TestAgentBuildCacheDoesNotResurrectPulseAgentPackage guards the repository's
// GHCR package list, which is a user-facing surface. A registry cache ref
// creates the package it points at, so pointing the agent_runtime build cache
// at ghcr.io/<owner>/pulse-agent recreated an empty package on every release.
// It then sat in the repo's Packages sidebar beside pulse, pulse-control-plane
// and pulse-chart/pulse, reading like a pullable agent image even though no
// workflow publishes it. Only images a release workflow actually pushes may
// own a package; the unified agent ships inside the main pulse image.
func TestAgentBuildCacheDoesNotResurrectPulseAgentPackage(t *testing.T) {
workflowDir := repoFile(".github", "workflows")
entries, err := os.ReadDir(workflowDir)
if err != nil {
t.Fatalf("read workflow dir: %v", err)
}
// Registry-qualified refs only: the release binaries are legitimately named
// pulse-agent-<os>-<arch> and must keep matching nothing here. Workflow
// expressions are collapsed first so an owner interpolated as
// ${{ github.repository_owner }} cannot hide the ref behind its spaces.
workflowExpr := regexp.MustCompile(`\$\{\{[^}]*\}\}`)
packageRef := regexp.MustCompile(`(?:ghcr\.io|docker\.io)/[^\s"']*/pulse-agent\b|(?:^|\s)rcourtman/pulse-agent\b`)
for _, entry := range entries {
name := entry.Name()
if entry.IsDir() || (!strings.HasSuffix(name, ".yml") && !strings.HasSuffix(name, ".yaml")) {
continue
}
content, err := os.ReadFile(filepath.Join(workflowDir, name))
if err != nil {
t.Fatalf("read %s: %v", name, err)
}
for i, line := range strings.Split(string(content), "\n") {
if strings.HasPrefix(strings.TrimSpace(line), "#") {
continue
}
if match := packageRef.FindString(workflowExpr.ReplaceAllString(line, "EXPR")); match != "" {
t.Fatalf("%s:%d targets the unpublished pulse-agent package (%q); no workflow may reference it, buildcache refs included", name, i+1, strings.TrimSpace(match))
}
}
}
release, err := os.ReadFile(repoFile(".github", "workflows", "qualify-release-containers.yml"))
if err != nil {
t.Fatalf("read qualify-release-containers.yml: %v", err)
}
releaseText := string(release)
if !strings.Contains(releaseText, `--target agent_runtime_prebuilt`) {
t.Fatal("qualify-release-containers.yml must assemble the candidate agent image without targeting an unpublished package")
}
if strings.Contains(releaseText, "agent-buildcache") {
t.Fatal("exact-candidate release qualification must not create a remote agent image cache")
}
values, err := os.ReadFile(repoFile("deploy", "helm", "pulse", "values.yaml"))
if err != nil {
t.Fatalf("read values.yaml: %v", err)
}
agentBlock := topLevelYAMLBlock(t, string(values), "agent")
if !strings.Contains(agentBlock, "repository: rcourtman/pulse\n") {
t.Fatal("chart agent.image.repository must default to the published rcourtman/pulse image")
}
}
// topLevelYAMLBlock returns the lines of a top-level mapping key, from the key
// itself up to the next unindented key.
func topLevelYAMLBlock(t *testing.T, doc string, key string) string {
t.Helper()
lines := strings.Split(doc, "\n")
start := -1
for i, line := range lines {
if line == key+":" {
start = i
break
}
}
if start < 0 {
t.Fatalf("values.yaml missing top-level %q key", key)
}
for i := start + 1; i < len(lines); i++ {
line := lines[i]
if line == "" || strings.HasPrefix(line, " ") || strings.HasPrefix(line, "#") {
continue
}
return strings.Join(lines[start:i], "\n")
}
return strings.Join(lines[start:], "\n")
}
func TestCreateReleaseUploadsPowerShellInstaller(t *testing.T) {
content, err := os.ReadFile(repoFile(".github", "workflows", "create-release.yml"))
if err != nil {
t.Fatalf("read create-release.yml: %v", err)
}
validationContent, err := os.ReadFile(repoFile(".github", "workflows", "validate-release-assets.yml"))
if err != nil {
t.Fatalf("read validate-release-assets.yml: %v", err)
}
convergenceContent, err := os.ReadFile(repoFile(".github", "workflows", "release-convergence.yml"))
if err != nil {
t.Fatalf("read release-convergence.yml: %v", err)
}
workflow := string(content)
validationWorkflow := string(validationContent)
convergenceWorkflow := string(convergenceContent)
required := []string{
`historical_asset_backfill_only:`,
`expected_source_sha:`,
`EXPECTED_SOURCE_SHA: ${{ inputs.expected_source_sha }}`,
`"${GITHUB_SHA}" != "${EXPECTED_SOURCE_SHA}"`,
`"${GITHUB_WORKFLOW_SHA}" != "${EXPECTED_SOURCE_SHA}"`,
`description: 'Repair an already-published release packet in place without rebuilding binaries'`,
`SYFT_VERSION="1.42.4"`,
`SYFT_ARCHIVE="syft_${SYFT_VERSION}_linux_amd64.tar.gz"`,
`SYFT_SHA256="590650c2743b83f327d1bf9bec64f6f83b7fec504187bb84f500c862bf8f2a0f"`,
`install -m 0755 "${TMP_DIR}/syft" /usr/local/bin/syft`,
`release_upload_with_retry "${TAG}" release/*.sbom.spdx.json --clobber`,
`release/pulse-agent-linux-amd64`,
`release/pulse-agent-linux-arm64`,
`release/pulse-agent-linux-armv7`,
`release/pulse-agent-linux-armv6`,
`release/pulse-agent-linux-386`,
`release/pulse-agent-helper-linux-amd64`,
`release/pulse-agent-helper-linux-arm64`,
`release/pulse-agent-helper-linux-armv7`,
`release/pulse-agent-helper-linux-armv6`,
`release/pulse-agent-helper-linux-386`,
`release/pulse-agent-runner-linux-amd64`,
`release/pulse-agent-runner-linux-arm64`,
`release/pulse-agent-runner-linux-armv7`,
`release/pulse-agent-runner-linux-armv6`,
`release/pulse-agent-runner-linux-386`,
`release/pulse-agent-freebsd-amd64`,
`release/pulse-agent-freebsd-arm64`,
`release/pulse-agent-windows-amd64.exe`,
`release/pulse-agent-windows-arm64.exe`,
`release/pulse-agent-windows-386.exe`,
`release_upload_with_retry "${TAG}" release/install.sh --clobber`,
`if [ -f release/install.ps1 ]; then`,
`release_upload_with_retry "${TAG}" release/install.ps1 --clobber`,
`release_upload_with_retry "${TAG}" release/*.sig --clobber`,
`release_upload_with_retry "${TAG}" release/*.sshsig --clobber`,
`gh release upload "$@"`,
`gh release upload failed on attempt ${attempt}/${max_attempts}; retrying in ${wait_seconds}s`,
`gh release upload failed after ${max_attempts} attempts`,
`release/release-build-provenance.sigstore.json`,
`gh api "repos/${{ github.repository }}/releases?per_page=100" --paginate`,
`git push origin "refs/tags/${TAG}"`,
`--rawfile body "$NOTES_FILE"`,
`--input "$RELEASE_PAYLOAD"`,
`--expected-body-file "$NOTES_FILE"`,
`write_github_output.py historical_asset_backfill_only "${HISTORICAL_ASSET_BACKFILL_ONLY}"`,
`if: ${{ always() && needs.prepare.result == 'success' && needs.build_release_candidate.result == 'success' && needs.create_release.result == 'success' && needs.prepare.outputs.historical_asset_backfill_only != 'true' }}`,
`candidate_manifest_artifact: ${{ needs.build_release_candidate.outputs.manifest_artifact_name }}`,
`if: ${{ needs.prepare.outputs.historical_asset_backfill_only == 'true' }}`,
`permissions:`,
`issues: write`,
`statuses: write`,
`ACTUAL_RELEASE_TAG=$(jq -r '.tag_name // empty' "$RELEASE_JSON_FILE")`,
`ACTUAL_TARGET_COMMITISH=$(jq -r '.target_commitish // empty' "$RELEASE_JSON_FILE")`,
`Draft release ${RELEASE_ID} is bound to tag ${ACTUAL_RELEASE_TAG}, expected ${TAG}.`,
`Draft release ${RELEASE_ID} target_commitish is ${ACTUAL_TARGET_COMMITISH}, expected ${HEAD_SHA}.`,
`WORKFLOW_OUTPUT_1: ${{ needs.prepare.outputs.tag }}`,
`./scripts/backfill-release-assets.sh --tag "${WORKFLOW_OUTPUT_1}" --repo "${{ github.repository }}"`,
`./scripts/validate-published-release.sh "${WORKFLOW_OUTPUT_1}" "${{ github.repository }}"`,
// End-to-end install.sh smoke must run downstream of
// validate_release_assets on every release that is not a
// historical asset backfill. Without this wiring the smoke
// workflow exists but never actually protects a release —
// exactly the regression class that let rc.1 → rc.5 ship with
// broken install.sh.
`uses: ./.github/workflows/install-sh-smoke.yml`,
`install_sh_smoke:`,
`needs.validate_release_assets.result == 'success'`,
`needs.prepare.outputs.historical_asset_backfill_only != 'true'`,
`repository: ${{ github.repository }}`,
`asset_source: staged`,
`release_id: ${{ needs.create_release.outputs.release_id }}`,
// Helm chart publish must be called explicitly from create-release
// because the draft→PATCH(draft=false) publish path does NOT fire
// the `release: published` webhook (GitHub-documented quirk). v6
// rc.1 → rc.5 published successfully but never produced a Helm
// chart on the GitHub Pages index, breaking
// `helm install pulse pulse/pulse --version 6.0.0-rc.X`.
`uses: ./.github/workflows/publish-helm-chart.yml`,
`publish_helm_chart:`,
`chart_version: ${{ needs.prepare.outputs.version }}`,
`app_version: ${{ needs.prepare.outputs.version }}`,
// Draft-only mode stops after staged validation and skips the
// customer activation sequence.
`needs.prepare.outputs.historical_asset_backfill_only != 'true' && github.event.inputs.draft_only != 'true'`,
`dispatch_release_convergence:`,
`release-convergence.yml/dispatches`,
`return_run_details: true`,
`activate_release:`,
`continue-on-error: true`,
`Publish the fully staged release`,
`'{draft: false, make_latest: $make_latest}'`,
`returning ${TAG} to draft quarantine`,
`release-activation.json`,
`release_commit_verdict:`,
`Release Activation Commit Verdict`,
}
for _, needle := range required {
if !strings.Contains(workflow, needle) {
t.Fatalf("create-release.yml missing required installer upload step: %s", needle)
}
}
publishedReleaseGuard := `needs.prepare.outputs.historical_asset_backfill_only != 'true' && github.event.inputs.draft_only != 'true'`
for _, job := range []string{"install_sh_smoke", "publish_helm_chart"} {
block := workflowJobBlock(t, workflow, job)
if !strings.Contains(block, publishedReleaseGuard) {
t.Fatalf("create-release.yml job %s must skip historical backfill and draft-only runs before invoking downstream workflow_call", job)
}
}
installSmokeJob := workflowJobBlock(t, workflow, "install_sh_smoke")
if !strings.Contains(installSmokeJob, "contents: write") {
t.Fatal("create-release.yml install_sh_smoke must grant contents: write so the called workflow can read unpublished draft assets")
}
qualificationJob := workflowJobBlock(t, workflow, "candidate_qualification")
if !strings.Contains(qualificationJob, publishedReleaseGuard) {
t.Fatal("candidate qualification must skip historical backfill and draft-only runs")
}
for _, job := range []string{"promote_floating_tags", "publish_helm_pages", "promote_private_pro_runtime", "update_stable_demo"} {
if strings.Contains(workflow, "\n "+job+":\n") {
t.Fatalf("create-release.yml must not mutate customer surface %s inline", job)
}
}
for _, needle := range []string{
`await_activation_commit:`,
`release-activation.json`,
`acquire_customer_promotion_lease:`,
`uses: ./.github/workflows/promote-floating-tags.yml`,
`uses: ./.github/workflows/helm-pages.yml`,
`uses: ./.github/workflows/promote-private-pro-runtime.yml`,
`uses: ./.github/workflows/update-demo-server.yml`,
} {
if !strings.Contains(convergenceWorkflow, needle) {
t.Fatalf("release-convergence.yml missing durable customer-promotion contract: %s", needle)
}
}
if !strings.Contains(workflow, `draft: true`) {
t.Fatal("create-release.yml must validate the release while it remains staged as a draft")
}
createJob := workflowJobBlock(t, workflow, "create_release")
if strings.Contains(createJob, `draft=false`) || strings.Contains(createJob, `Publish release`) {
t.Fatal("create_release must stage assets without crossing the customer publication boundary")
}
if strings.Contains(workflow, `provenance: false`) {
t.Fatal("create-release.yml must not disable release-image provenance")
}
validationRequired := []string{
`statuses: write`,
`curl --fail-with-body --silent --show-error -X POST`,
`"context": "Release Asset Validation"`,
`WORKFLOW_OUTPUT_4: ${{ steps.context.outputs.tag }}`,
`WORKFLOW_OUTPUT_5: ${{ steps.context.outputs.target_commitish }}`,
`--arg tag "${WORKFLOW_OUTPUT_4}"`,
`--arg target_commitish "${WORKFLOW_OUTPUT_5}"`,
`{body: $body, tag_name: $tag, target_commitish: $target_commitish}`,
`{draft: true, tag_name: $tag, target_commitish: $target_commitish}`,
`Validation release body update detached release tag`,
`Validation release body update changed target_commitish`,
`Validate release body integrity`,
`--validate-body-file "$RELEASE_BODY_FILE"`,
`--expected-body-file "$CLEAN_BODY_FILE"`,
`Quarantine malformed release body`,
`Draft releases are quarantined; published releases remain immutable for explicit remediation.`,
`name: Update release body - Success
if: steps.context.outputs.should_run == 'true' && steps.context.outputs.draft == 'true'`,
`name: Delete all release assets on failure
if: steps.context.outputs.should_run == 'true' && steps.context.outputs.draft == 'true'`,
`name: Update release body - Failure
if: steps.context.outputs.should_run == 'true' && steps.context.outputs.draft == 'true'`,
}
for _, needle := range validationRequired {
if !strings.Contains(validationWorkflow, needle) {
t.Fatalf("validate-release-assets.yml missing required status publication contract: %s", needle)
}
}
for _, forbidden := range []string{
"Release was published; reverting to draft before deleting assets",
"A published release edit introduced invalid assets",
} {
if strings.Contains(validationWorkflow, forbidden) {
t.Fatalf("published release validation must not retain mutation path %q", forbidden)
}
}
}
func TestCurrentStablePatchReleasePacketTracksInstallMetadata(t *testing.T) {
version := currentReleaseVersion(t)
if isPrereleaseVersion(version) {
t.Skip("current release is a prerelease")
}
previous, ok := previousStablePatchVersion(version)
if !ok {
t.Skip("current release is not a stable patch release")
}
releaseBranch := requiredReleaseBranchForVersion(t, version)
promotedTag, rcDerived := currentStablePatchPromotedPrerelease(t, version)
releaseNotesPath := repoFile("docs", "releases", "RELEASE_NOTES_v"+version+".md")
changelogPath := repoFile("docs", "releases", "V6_CHANGELOG_v"+version+".md")
// The changelog below binds stable maturity and promotion lineage. Public
// notes bind the version and operator safety, not one author's boilerplate.
notes, err := os.ReadFile(releaseNotesPath)
if err != nil {
t.Fatal(err)
}
for _, issue := range stablePatchReleaseNotesIssues(string(notes), version, previous) {
t.Errorf("%s: %s", releaseNotesPath, issue)
}
changelogRequired := make([]string, 0, 7)
changelogRequired = append(changelogRequired,
"Version: `v"+version+"`",
"Rollback target: `v"+previous+"`",
"Windows signing decision: the standing SignPath-unavailable policy publishes unsigned Windows Unified Agent binaries",
"Unknown Publisher warning",
"Mobile decision: `no-mobile-impact`",
)
if rcDerived {
// A stable patch with a same-version RC promotes the exercised
// candidate; the promotion resolver refuses the emergency no-RC path.
changelogRequired = append(changelogRequired,
"Promoted prerelease: `"+promotedTag+"`",
"Promotion path: exact-SHA single-build release candidate from `"+releaseBranch+"`",
)
} else {
changelogRequired = append(changelogRequired,
"Promotion path: emergency stable patch from `"+releaseBranch+"`",
)
}
assertFileContainsAllNormalized(t, changelogPath, changelogRequired...)
assertFileContainsAll(t, repoFile("docs", "RELEASE_NOTES.md"),
"docs/releases/RELEASE_NOTES_v"+version+".md",
"docs/releases/V6_CHANGELOG_v"+version+".md",
)
assertFileContainsAll(t, repoFile("docs", "UPGRADE_v6.md"),
"docs/releases/RELEASE_NOTES_v"+version+".md",
"docs/releases/V6_CHANGELOG_v"+version+".md",
)
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-"+version+"-informational",
"AppVersion-"+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+`"`,
)
installabilityPath := repoFile("docs", "release-control", "v6", "internal", "subsystems", "deployment-installability.md")
installabilityRequired := make([]string, 0, 3)
installabilityRequired = append(installabilityRequired,
"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.",
"For the active stable `v"+version+"` cut, the repo-root compose default and `scripts/install-docker.sh` fallback must both pin `"+version+"`",
)
if rcDerived {
installabilityRequired = append(installabilityRequired, "promoted_from_tag="+promotedTag)
} else {
installabilityRequired = append(installabilityRequired,
"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.",
)
}
assertFileContainsAllNormalized(t, installabilityPath, installabilityRequired...)
}
func TestCurrentStableMinorReleasePacketTracksInstallMetadata(t *testing.T) {
version := currentReleaseVersion(t)
if isPrereleaseVersion(version) {
t.Skip("current release is a prerelease")
}
parts, valid := parseStableVersion(version)
if !valid || parts[1] == 0 || parts[2] != 0 {
t.Skip("current release is not a stable minor release")
}
previous, ok := previousStableForPrereleaseVersion(version + "-rc.1")
if !ok {
t.Fatal("stable minor release has no earlier stable rollback packet")
}
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 stable minor release",
"stable `v"+previous+"`",
"## What's improved",
"Alerts that survive restarts",
"More control over notifications",
"Earlier resource warnings",
"More accurate Proxmox and PBS coverage",
"Pulse Mobile iOS build 12 and Android versionCode 9 remain compatible",
"not Authenticode-signed",
"Unknown Publisher warning",
"The rollback target is stable `v"+previous+"`",
)
assertFileContainsAllNormalized(t, changelogPath,
"Version: `v"+version+"`",
"Previous stable: `v"+previous+"`",
"Rollback target: `v"+previous+"`",
"Promotion path: owner-approved expedited exact-SHA stable cutoff from `main`",
"Mobile decision: `existing-mobile-build-compatible`",
"standing SignPath-unavailable policy applies",
)
assertFileContainsAll(t, repoFile("docs", "RELEASE_NOTES.md"),
"docs/releases/RELEASE_NOTES_v"+version+".md",
"docs/releases/V6_CHANGELOG_v"+version+".md",
)
assertFileContainsAll(t, repoFile("docs", "UPGRADE_v6.md"),
"docs/releases/RELEASE_NOTES_v"+version+".md",
"docs/releases/V6_CHANGELOG_v"+version+".md",
)
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-"+version+"-informational",
"AppVersion-"+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 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.",
"`rollback_version=v"+previous+"`",
"The integrated single-build workflow must pass its exact-SHA preflight and immutable readiness gates before publication.",
"The stable server cut is classified `existing-mobile-build-compatible`.",
"explicit version-bound owner decision",
"standing unavailable policy",
"For the active stable `v"+version+"` cut, the repo-root compose default and `scripts/install-docker.sh` fallback must both pin `"+version+"`",
)
}
func TestCurrentPrereleasePacketTracksInstallMetadata(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")
}
stableTarget, _, ok := strings.Cut(version, "-")
if !ok {
t.Fatalf("current prerelease %q has no stable target", version)
}
comparisonVersion, ok := previousPrereleaseVersion(version)
if !ok {
comparisonVersion = previous
}
releaseNotesPath := repoFile("docs", "releases", "RELEASE_NOTES_v"+version+".md")
changelogPath := repoFile("docs", "releases", "V6_CHANGELOG_v"+version+".md")
assertFileContainsAllNormalized(t, releaseNotesPath,
"# Pulse v"+version+" Release Notes",
"## What's improved",
"## Before you upgrade",
"Same-name systems stay separate",
"Windows agent delivery is restored",
"Large Availability estates scan faster",
"Slow starts are recoverable",
"Disk I/O totals are more accurate",
"carries every change from the `v6.4.2` packet",
"map at least one trusted IdP group to the built-in `admin` role",
"not Authenticode-signed",
"Unknown Publisher warning",
"does not require a companion mobile release",
"The rollback target is stable `v"+previous+"`",
)
assertFileDoesNotContain(t, releaseNotesPath, "## Fixes")
comparisonSummary := "This changelog describes the changes since `v" + comparisonVersion + "`"
if version == "6.4.0-rc.10" {
comparisonSummary = "The `v6.4.0-rc.9` release staged an immutable draft, tag, and exact-version artifacts but did not activate publicly."
}
assertFileContainsAllNormalized(t, changelogPath,
"Version: `v"+version+"`",
"Previous stable: `v"+previous+"`",
"Rollback target: `v"+previous+"`",
"Promotion path: exact-SHA single-build release candidate from `main`",
comparisonSummary,
"carries the complete `v6.4.2` change set",
"no longer pin a guest in Backup Running",
"(#1815)",
"no longer collapse into a single host or Docker record",
"(#1753)",
"Windows Unified Agent auto-update no longer fails with HTTP 404",
"(#1820)",
"Windows signing decision: prereleases publish checksum- and detached-signature-verified Windows agents without Authenticode",
"Mobile decision: `no-mobile-impact`",
"no companion mobile build or store rollout is required",
)
if version == "6.3.0-rc.6" {
assertFileContainsAllNormalized(t, releaseNotesPath,
"Chart and resource-query services now qualify independently from the residual API router, shrinking the root test critical path.",
"Public server and provider control-plane images publish and attest in parallel from one verified exact-candidate payload.",
"PVE compilation remains credential-free. GitHub-hosted jobs retain signing, release mutation, and publication credentials.",
)
assertFileContainsAllNormalized(t, changelogPath,
"Chart handling and resource queries are production packages with independent test scheduling",
"Exact-version public Docker staging overlaps qualification, and server and provider control-plane products publish as parallel matrix legs.",
"Publication still requires exact-source identity, immutable manifests, signatures, public/private artifact integrity, installer smoke, and final convergence verification.",
)
}
if version == "6.4.0-rc.13" {
assertFileContainsAllNormalized(t, releaseNotesPath,
"Unchanged stopped-container details are refreshed every 15 minutes instead of being re-inspected every 30 seconds",
"Separate standalone sites that reuse a short node name can link to their own host agents through unique provider-observed addresses",
)
assertFileContainsAllNormalized(t, changelogPath,
"Docker hosts with many stopped containers no longer re-inspect every historical container on each 30-second agent report",
"Separate standalone Proxmox sites that reuse a short node name no longer lose correct agent links when their provider-observed addresses uniquely disambiguate them",
)
}
assertFileContainsAll(t, repoFile("docs", "RELEASE_NOTES.md"),
"docs/releases/RELEASE_NOTES_v"+version+".md",
"docs/releases/V6_CHANGELOG_v"+version+".md",
"current v6 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 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 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 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 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.",
"No governed mobile-facing path changed from `v"+previous+"`, so the release decision is `no-mobile-impact`",
"no companion upload or public mobile-store rollout is part of this candidate.",
"The prerelease Windows path retains exact-SHA, checksum, and detached-signature verification without Authenticode. Stable `v"+stableTarget+"` also skips SignPath under the standing unavailable policy",
)
}
func TestBackfillReleaseWorkflowRepairsPublishedAssetsWithoutRebuilds(t *testing.T) {
scriptBytes, err := os.ReadFile(repoFile("scripts", "backfill-release-assets.sh"))
if err != nil {
t.Fatalf("read backfill-release-assets.sh: %v", err)
}
script := string(scriptBytes)
scriptRequired := []string{
`SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"`,
`PULSE_REPO_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"`,
`cd "${PULSE_REPO_ROOT}"`,
`source "${SCRIPT_DIR}/release_asset_common.sh"`,
`gh release view "${TAG}" -R "${REPO}" --json isDraft,tagName`,
`Error: ${TAG} is still a draft release; use the normal release pipeline instead of historical backfill.`,
`gh release download "${TAG}" -R "${REPO}" --dir "${RELEASE_DIR}" --clobber`,
`pulse_release_prepare_signing_state "pulse-installer" "pulse-install"`,
`pulse_release_generate_packet_sbom "${PAYLOAD_DIR}" "${RELEASE_PACKET_SBOM}"`,
`pulse_release_write_checksums_and_signatures "${RELEASE_DIR}" "${checksum_files[@]}"`,
`gh release upload "${TAG}" "${RELEASE_DIR}/checksums.txt" --clobber`,
`gh release upload "${TAG}" "${RELEASE_DIR}"/*.sha256 --clobber`,
`gh release upload "${TAG}" "${RELEASE_DIR}"/*.sig --clobber`,
`gh release upload "${TAG}" "${RELEASE_DIR}"/*.sshsig --clobber`,
`gh release upload "${TAG}" "${RELEASE_DIR}/${RELEASE_PACKET_SBOM}" --clobber`,
}
for _, needle := range scriptRequired {
if !strings.Contains(script, needle) {
t.Fatalf("backfill-release-assets.sh missing required historical backfill step: %s", needle)
}
}
workflowBytes, err := os.ReadFile(repoFile(".github", "workflows", "backfill-release-assets.yml"))
if err != nil {
t.Fatalf("read backfill-release-assets.yml: %v", err)
}
workflow := string(workflowBytes)
workflowRequired := []string{
`name: Backfill Release Assets`,
`workflow_dispatch:`,
`contents: write`,
`runs-on: ubuntu-24.04`,
`uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1`,
`uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0`,
`SYFT_VERSION="1.42.4"`,
`SYFT_ARCHIVE="syft_${SYFT_VERSION}_linux_amd64.tar.gz"`,
`SYFT_SHA256="590650c2743b83f327d1bf9bec64f6f83b7fec504187bb84f500c862bf8f2a0f"`,
`TAG: ${{ inputs.tag }}`,
`REPOSITORY: ${{ github.repository }}`,
`./scripts/backfill-release-assets.sh --tag "${TAG}" --repo "${REPOSITORY}"`,
`PULSE_UPDATE_SIGNING_KEY: ${{ secrets.PULSE_UPDATE_SIGNING_KEY }}`,
`PULSE_UPDATE_SIGNING_PUBLIC_KEY: ${{ vars.PULSE_UPDATE_SIGNING_PUBLIC_KEY }}`,
`./scripts/validate-published-release.sh "${TAG}" "${REPOSITORY}"`,
}
for _, needle := range workflowRequired {
if !strings.Contains(workflow, needle) {
t.Fatalf("backfill-release-assets.yml missing required release-repair step: %s", needle)
}
}
}
func TestReleaseValidationRequiresSignedSidecars(t *testing.T) {
localValidatorBytes, err := os.ReadFile(repoFile("scripts", "validate-release.sh"))
if err != nil {
t.Fatalf("read validate-release.sh: %v", err)
}
localValidator := string(localValidatorBytes)
localRequired := []string{
`"install-mcp.sh"`,
`for installer in install.sh install-docker.sh install-mcp.sh install-mcp.ps1 install.ps1 pulse-auto-update.sh; do`,
`checksums.txt must contain exactly one entry for release installer ${installer}`,
`"pulse-v${PULSE_VERSION}-release.sbom.spdx.json"`,
`release_sbom="pulse-${PULSE_TAG}-release.sbom.spdx.json"`,
`error "checksums.txt is missing ${release_sbom}"`,
`success "Release SBOM is listed in checksums.txt"`,
`info "Validating SSH signature sidecars..."`,
`if [ ! -s "checksums.txt.sshsig" ]; then`,
`error "Missing or empty checksums.txt.sshsig"`,
`if [ ! -s "${filename}.sshsig" ]; then`,
`error "Missing or empty ${filename}.sshsig"`,
`success "SSH signature sidecars validated"`,
`validate_download_binary_headers() {`,
`http_header_value "X-Checksum-Sha256"`,
`http_header_value "X-Signature-Ed25519"`,
`http_header_value "X-Signature-SSHSIG"`,
`url="http://127.0.0.1:${HOST_PORT}/${script_name}"`,
`^# Pulse Unified Agent Installer`,
`--token-file`,
`TokenFile`,
`Install script endpoints returned required signature headers`,
`Download endpoints returned binaries with checksum and signature headers for all platforms/architectures`,
`Offline self-heal: download endpoint works with checksum and signature headers without outbound network`,
// Server installer identity guard — see the rc.1 → rc.5 regression where
// the rendered agent installer shipped as the top-level install.sh asset
// for 30 days before anyone noticed. Removing any of these unpins the asset.
`Validating install.sh is the Pulse server installer`,
`grep -qE '^# Pulse Installer Script'`,
`grep -qE '^[[:space:]]*--version\)'`,
`Pulse Unified Agent Installer`,
`bash "$install_sh_path" --help`,
`Install specific version (e.g.`,
// README key drift guard — across v6 rc.2 → rc.5 the README pinned a
// stale ed25519 key that did not verify install.sh.sshsig, so anyone
// following the secure-install path saw "Could not verify signature".
// validate-release.sh must extract the README's pinned key and actually
// run ssh-keygen -Y verify against the signed installer.
`Validating README pinned signature key matches install.sh.sshsig`,
`grep -oE "ssh-ed25519 [A-Za-z0-9+/=]+ pulse-installer" "$readme_path"`,
`ssh-keygen -Y verify \`,
`README's pinned signature key does not verify install.sh.sshsig`,
}
readmeBytes, err := os.ReadFile(repoFile("README.md"))
if err != nil {
t.Fatalf("read README.md: %v", err)
}
readme := string(readmeBytes)
// Lock in the actual signing key documented to customers. This is the public
// counterpart of PULSE_UPDATE_SIGNING_KEY and matches what install.sh and
// scripts/pulse-auto-update.sh have embedded. A future edit cannot silently
// regress to the stale Ds21c5 key without tripping this assertion.
const correctReadmeKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMZd/DaH+BldzOkq1A8KVTcFk73nAyrE8aJOyf7i00jm pulse-installer"
if !strings.Contains(readme, correctReadmeKey) {
t.Fatalf("README.md must pin the correct pulse-installer ed25519 key for install.sh signature verification")
}
const staleReadmeKey = "Ds21c5oPk2khrdHlsw1aZ9EJKoTsyalGzhb0hdwJrkV"
if strings.Contains(readme, staleReadmeKey) {
t.Fatalf("README.md still references the stale pulse-installer key Ds21c5...; rc.2 → rc.5 shipped this drift")
}
// Format drift guard — ssh-keygen -Y verify -f expects an allowed_signers
// file whose FIRST field is the principal. The docs shipped the key in
// authorized_keys order (principal last, parsed as a comment), so the
// documented verification failed against a perfectly good signature.
// Reported by a customer against v6.0.5 on 2026-07-13.
const allowedSignersLine = `pulse-installer namespaces="pulse-install" ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMZd/DaH+BldzOkq1A8KVTcFk73nAyrE8aJOyf7i00jm pulse-installer`
if !strings.Contains(readme, allowedSignersLine) {
t.Fatalf("README.md verification snippet must publish the key as an allowed_signers line (principal first), not authorized_keys order")
}
installDocsBytes, err := os.ReadFile(repoFile("docs", "INSTALL.md"))
if err != nil {
t.Fatalf("read docs/INSTALL.md: %v", err)
}
installDocs := string(installDocsBytes)
if !strings.Contains(installDocs, correctReadmeKey) {
t.Fatalf("docs/INSTALL.md must pin the correct pulse-installer ed25519 key")
}
if strings.Contains(installDocs, staleReadmeKey) {
t.Fatalf("docs/INSTALL.md still references the stale pulse-installer key Ds21c5...")
}
if !strings.Contains(installDocs, allowedSignersLine) {
t.Fatalf("docs/INSTALL.md verification snippet must publish the key as an allowed_signers line (principal first), not authorized_keys order")
}
for _, needle := range localRequired {
if !strings.Contains(localValidator, needle) {
t.Fatalf("validate-release.sh missing signed sidecar validation: %s", needle)
}
}
if strings.Contains(localValidator, `url="http://127.0.0.1:${HOST_PORT}/download/${script_name}"`) {
t.Fatal("validate-release.sh must smoke-test /install.sh and /install.ps1, not non-existent /download/install.* routes")
}
publishedValidatorBytes, err := os.ReadFile(repoFile("scripts", "validate-published-release.sh"))
if err != nil {
t.Fatalf("read validate-published-release.sh: %v", err)
}
publishedValidator := string(publishedValidatorBytes)
publishedRequired := []string{
`REQUIRED_SIGNED_INSTALLERS=(`,
`install-mcp.sh`,
`Authenticated checksums.txt must contain exactly one valid entry for published installer ${installer}.`,
`RELEASE_SBOM="pulse-${TAG}-release.sbom.spdx.json"`,
`PULSE_UPDATE_SIGNING_PUBLIC_KEY is required to authenticate published release assets.`,
`go -C "$REPO_ROOT" run ./scripts/release_update_key.go public-key-ssh`,
`ssh-keygen -Y verify`,
`-I pulse-installer`,
`-n pulse-install`,
`verify_signature "$CHECKSUMS_PATH" "$CHECKSUMS_SIG_PATH"`,
`echo "Failed to download ${RELEASE_SBOM} for ${TAG}" >&2`,
`echo "${RELEASE_SBOM} is empty for ${TAG}" >&2`,
`CHECKSUMS_SIG_PATH="${TMP_DIR}/checksums.txt.sshsig"`,
`"${BASE_URL}/checksums.txt.sshsig"`,
`echo "Failed to download checksums.txt.sshsig for ${TAG}" >&2`,
`sshsig_path="${TMP_DIR}/${filename}.sshsig"`,
`"${artifact_url}.sshsig"`,
`echo "Failed to download ${filename}.sshsig" >&2`,
`verify_signature "$artifact_path" "$sshsig_path" "$filename"`,
`Published release assets for ${TAG} match authenticated checksums.txt, *.sha256 files, and verified *.sshsig sidecars.`,
}
for _, needle := range publishedRequired {
if !strings.Contains(publishedValidator, needle) {
t.Fatalf("validate-published-release.sh missing signed sidecar validation: %s", needle)
}
}
contractBytes, err := os.ReadFile(repoFile("docs", "release-control", "v6", "internal", "subsystems", "deployment-installability.md"))
if err != nil {
t.Fatalf("read deployment-installability contract: %v", err)
}
contract := string(contractBytes)
contractRequired := []string{
"`scripts/validate-release.sh`",
"`scripts/validate-published-release.sh`",
"`scripts/backfill-release-assets.sh`",
"`.github/workflows/backfill-release-assets.yml`",
"`scripts/validate-release.sh`, and",
"`scripts/release_asset_common.sh`",
"must derive the embedded update trust root",
"standalone SPDX JSON SBOM",
"already-published packet",
"derived integrity assets",
"make post-publication validation authenticate",
"every listed artifact's `.sshsig` against the configured",
"Validation must fail if the trust root is unavailable",
"any published installer is absent from the authenticated checksum",
"release-packet SBOM is absent",
"download endpoints must return checksum and signature headers",
"must disable Go's automatic VCS stamping",
"`-buildvcs=false`",
}
for _, needle := range contractRequired {
if !strings.Contains(contract, needle) {
t.Fatalf("deployment-installability contract missing signed sidecar validation requirement: %s", needle)
}
}
}
func TestDockerBuildUsesCanonicalReleaseLdflags(t *testing.T) {
dockerfileBytes, err := os.ReadFile(repoFile("Dockerfile"))
if err != nil {
t.Fatalf("read Dockerfile: %v", err)
}
dockerfile := string(dockerfileBytes)
dockerRequired := []string{
"FROM --platform=linux/amd64 node:24-alpine@sha256:" + node24Amd64FrontendDigest + " AS frontend-builder",
`FROM --platform=linux/amd64 golang:1.26.8-alpine@sha256:`,
`FROM backend-builder AS release-assets-builder`,
`AS agent_runtime`,
`AS pulse-runtime-foundation`,
`FROM pulse-runtime-foundation AS pulse-runtime-base`,
`FROM pulse-runtime-foundation AS prebuilt-runtime-base`,
`FROM pulse-runtime-base AS hosted_runtime`,
`FROM pulse-runtime-base AS runtime`,
`COPY scripts/release_ldflags.sh ./scripts/release_ldflags.sh`,
`COPY scripts/release_update_key.go ./scripts/release_update_key.go`,
`COPY scripts/render_installers.go ./scripts/render_installers.go`,
`ARG PULSE_LICENSE_PUBLIC_KEY_SHA256`,
`--mount=type=secret,id=pulse_license_public_key,required=false`,
`--mount=type=secret,id=pulse_update_signing_key,required=false`,
`ARG PULSE_UPDATE_SIGNING_PUBLIC_KEY`,
`LICENSE_PUBLIC_KEY="$(tr -d '\r\n' < /run/secrets/pulse_license_public_key)"`,
`EXPECTED_LICENSE_PUBLIC_KEY_SHA256="${PULSE_LICENSE_PUBLIC_KEY_SHA256#SHA256:}"`,
`mounted license public key does not match PULSE_LICENSE_PUBLIC_KEY_SHA256.`,
`UPDATE_PUBLIC_KEYS="$(go run ./scripts/release_update_key.go public-key --private-key "${UPDATE_SIGNING_KEY}")"`,
`mounted update signing key does not match PULSE_UPDATE_SIGNING_PUBLIC_KEY.`,
`./scripts/release_ldflags.sh server --version "${VERSION}" --build-time "${BUILD_TIME}" --git-commit "${GIT_COMMIT}"`,
`./scripts/release_ldflags.sh agent --version "${VERSION}"`,
`-buildvcs=false`,
`go run ./scripts/render_installers.go --source-dir ./scripts --output-dir /app/rendered-installers`,
`--allow-empty-installer-ssh-public-key`,
`ssh-keygen -q -Y sign -f "${OPENSSH_SIGNING_KEY}" -n pulse-install`,
`COPY --from=release-assets-builder /app/rendered-installers/install.sh /opt/pulse/scripts/install.sh`,
`COPY --from=release-assets-builder /app/pulse-agent-* /opt/pulse/bin/`,
}
for _, needle := range dockerRequired {
if !strings.Contains(dockerfile, needle) {
t.Fatalf("Dockerfile missing canonical release ldflags usage: %s", needle)
}
}
assertDigestPinnedDockerStage(t, dockerfile, `FROM --platform=linux/amd64 node:24-alpine@sha256:`, ` AS frontend-builder`)
assertDigestPinnedDockerStage(t, dockerfile, `FROM --platform=linux/amd64 golang:1.26.8-alpine@sha256:`, ` AS backend-builder`)
assertDigestPinnedDockerStage(t, dockerfile, `FROM alpine:3.24@sha256:`, ` AS agent_runtime`)
assertDigestPinnedDockerStage(t, dockerfile, `FROM alpine:3.24@sha256:`, ` AS pulse-runtime-foundation`)
hostedStart := strings.Index(dockerfile, `FROM pulse-runtime-base AS hosted_runtime`)
runtimeStart := strings.Index(dockerfile, `FROM pulse-runtime-base AS runtime`)
if hostedStart == -1 || runtimeStart == -1 || hostedStart > runtimeStart {
t.Fatal("Dockerfile must define hosted_runtime from pulse-runtime-base before the full runtime stage")
}
hostedStage := dockerfile[hostedStart:runtimeStart]
if strings.Contains(hostedStage, "rendered-installers") || strings.Contains(hostedStage, "/opt/pulse/bin") {
t.Fatalf("hosted_runtime target must not depend on installer rendering or embedded agent artifacts:\n%s", hostedStage)
}
if strings.Contains(dockerfile, `FROM --platform=linux/amd64 node:24-alpine AS frontend-builder`) ||
strings.Contains(dockerfile, `FROM --platform=linux/amd64 golang:1.26.8-alpine AS backend-builder`) ||
strings.Contains(dockerfile, `FROM alpine:3.24 AS agent_runtime`) ||
strings.Contains(dockerfile, `FROM alpine:3.24 AS pulse-runtime-base`) {
t.Fatal("Dockerfile base images must be pinned by immutable @sha256 digests")
}
if builds, cleanBuilds := strings.Count(dockerfile, " go build \\"), strings.Count(dockerfile, "-buildvcs=false"); builds != cleanBuilds {
t.Fatalf("Dockerfile release go builds must all disable automatic VCS stamping: builds=%d clean_builds=%d", builds, cleanBuilds)
}
}
func TestDockerRuntimeShipsPinnedAppriseCLI(t *testing.T) {
dockerfileBytes, err := os.ReadFile(repoFile("Dockerfile"))
if err != nil {
t.Fatalf("read Dockerfile: %v", err)
}
dockerfile := string(dockerfileBytes)
required := []string{
`ARG APPRISE_VERSION=1.12.0`,
`AS apprise-builder`,
`python3 -m venv /opt/apprise`,
`/opt/apprise/bin/pip install --no-cache-dir "apprise==${APPRISE_VERSION}"`,
`COPY --from=apprise-builder /opt/apprise /opt/apprise`,
`ln -s /opt/apprise/bin/apprise /usr/local/bin/apprise`,
`apprise --version | grep -F "Apprise v${APPRISE_VERSION}"`,
}
for _, needle := range required {
if !strings.Contains(dockerfile, needle) {
t.Fatalf("Dockerfile missing pinned Apprise runtime contract: %s", needle)
}
}
if strings.Count(dockerfile, `apprise --version | grep -F "Apprise v${APPRISE_VERSION}"`) < 2 {
t.Fatal("Dockerfile must verify the pinned Apprise CLI in both its build and runtime stages")
}
}
func TestAgentRuntimeImageDefaultsToUnifiedHostAndDockerMonitoring(t *testing.T) {
dockerfileBytes, err := os.ReadFile(repoFile("Dockerfile"))
if err != nil {
t.Fatalf("read Dockerfile: %v", err)
}
dockerfile := string(dockerfileBytes)
required := []string{
`mkdir -p /var/lib/pulse-agent`,
`PULSE_DISABLE_AUTO_UPDATE=true`,
`PULSE_ENABLE_HOST=true`,
`PULSE_ENABLE_DOCKER=true`,
`PULSE_AGENT_ID_FILE=/var/lib/pulse-agent/agent-id`,
`PULSE_STATE_DIR=/var/lib/pulse-agent`,
`VOLUME ["/var/lib/pulse-agent"]`,
`ENTRYPOINT ["/usr/local/bin/pulse-agent"]`,
}
for _, needle := range required {
if !strings.Contains(dockerfile, needle) {
t.Fatalf("Dockerfile agent_runtime missing unified host and Docker contract: %s", needle)
}
}
if strings.Contains(dockerfile, `PULSE_ENABLE_HOST=false`) {
t.Fatal("agent_runtime must not silently force every deployment into workload-only mode")
}
if strings.Contains(dockerfile, `ENTRYPOINT ["/usr/local/bin/pulse-agent", "--enable-docker", "--enable-host=false"]`) {
t.Fatal("agent_runtime must not hard-code module flags in ENTRYPOINT; env defaults keep user args overridable")
}
}
func TestReleaseCandidateRequiresPlatformNativeAgentSigning(t *testing.T) {
candidateWorkflowPath := repoFile(".github", "workflows", "build-release-candidate.yml")
assertFileContainsAll(t, candidateWorkflowPath,
`require_macos_signing:`,
`require_windows_signing:`,
`sign-macos-agent:`,
`codesign --force --timestamp --options runtime`,
`xcrun notarytool submit`,
`--output-format json > notarization-result.json`,
`result.get('status') != 'Accepted'`,
`codesign --verify --deep --strict --verbose=2`,
`sign-windows-agent:`,
`collect-windows-signing:`,
`windows_signing_backend:`,
`signpath/github-action-submit-signing-request@f6d04783b4569d051e0c80105fe66e82819d0092 # v3.0`,
`github-artifact-id: ${{ steps.upload-unsigned-windows.outputs.artifact-id }}`,
`wait-for-completion: false`,
`windows-signing-request.json`,
`Re-run failed jobs`,
`SIGNPATH_API_TOKEN`,
`SIGNPATH_ORGANIZATION_ID`,
`SIGNPATH_PROJECT_SLUG`,
`SIGNPATH_SIGNING_POLICY_SLUG`,
`SIGNPATH_ARTIFACT_CONFIGURATION_SLUG`,
`SIGNPATH_EXPECTED_CERTIFICATE_SUBJECT`,
`signtool sign`,
`signtool verify /pa /v`,
`windows-signing-evidence.json`,
`signerThumbprint`,
`PULSE_AGENT_NATIVE_BINARIES_DIR:`,
)
candidateWorkflow, err := os.ReadFile(candidateWorkflowPath)
if err != nil {
t.Fatalf("read build-release-candidate.yml: %v", err)
}
if strings.Contains(string(candidateWorkflow), `spctl --assess --type execute`) {
t.Fatal("bare command-line Mach-O binaries must not use Gatekeeper app assessment after notarization")
}
if strings.Contains(string(candidateWorkflow), `wait-for-completion: true`) {
t.Fatal("SignPath submission must not block the Windows build job on manual approval; collection is a separate resumable job")
}
assertFileContainsAll(t, repoFile(".github", "workflows", "create-release.yml"),
`require_macos_signing: true`,
`require_windows_signing: ${{ needs.prepare.outputs.require_windows_signing == 'true' }}`,
`runs-on: ubuntu-24.04`,
`unsigned_windows_exception:`,
`unsigned_windows_reason:`,
`windows_signing_backend: signpath`,
)
assertFileContainsAll(t, repoFile(".github", "workflows", "release-dry-run.yml"),
`Definitive Dry-Run Verdict`,
`require_windows_signing: false`,
`WINDOWS_AUTHENTICODE_AVAILABLE`,
`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"),
`WINDOWS_AUTHENTICODE_AVAILABLE = False`,
`WINDOWS_AUTHENTICODE_STANDING_UNSIGNED_MIN_VERSION = (6, 3, 2)`,
`version not in {"6.1.0", "6.1.1", "6.1.2", "6.2.0", "6.2.1", "6.3.0", "6.3.1", "6.3.2"}`,
`unsigned_windows_reason is required`,
`not Authenticode-signed`,
`require_windows_signing = not is_prerelease and not effective_unsigned_windows_exception`,
)
assertFileContainsAll(t, repoFile("scripts", "build-release.sh"),
`PULSE_AGENT_NATIVE_BINARIES_DIR`,
`native_targets=()`,
`PULSE_REQUIRE_MACOS_SIGNING:-false`,
`native_targets+=(darwin-amd64 darwin-arm64)`,
`PULSE_REQUIRE_WINDOWS_SIGNING:-false`,
`native_targets+=(windows-amd64 windows-arm64 windows-386)`,
`Applied required platform-native signed Unified Agent binaries.`,
`required native signing is enabled but PULSE_AGENT_NATIVE_BINARIES_DIR is empty.`,
)
}
func TestReleaseWorkflowsUseSecretSafeAttestedImageBuilds(t *testing.T) {
createReleaseBytes, err := os.ReadFile(repoFile(".github", "workflows", "create-release.yml"))
if err != nil {
t.Fatalf("read create-release.yml: %v", err)
}
candidateWorkflowBytes, err := os.ReadFile(repoFile(".github", "workflows", "build-release-candidate.yml"))
if err != nil {
t.Fatalf("read build-release-candidate.yml: %v", err)
}
qualifierWorkflowBytes, err := os.ReadFile(repoFile(".github", "workflows", "qualify-release-containers.yml"))
if err != nil {
t.Fatalf("read qualify-release-containers.yml: %v", err)
}
createRelease := string(createReleaseBytes) + "\n" + string(candidateWorkflowBytes) + "\n" + string(qualifierWorkflowBytes)
createReleaseRequired := []string{
`Exact-Candidate Container and Helm Smoke`,
`prepare_cluster_tool`,
`https://kind.sigs.k8s.io/dl/v0.20.0/kind-linux-amd64`,
`513a7213d6d3332dd9ef27c24dab35e5ef10a04fa27274fe1c14d8a246493ded`,
`https://dl.k8s.io/release/v1.27.3/bin/linux/amd64/kubectl`,
`fba6c062e754a120bc8105cde1344de200452fe014a8759e06e4eec7ed258a09`,
`printf '%s %s\n' "$expected_sha" "$destination" | sha256sum --check`,
`test -x "$destination"`,
`test "$(command -v kind)" = "$RUNNER_TEMP/kind"`,
`test "$(command -v kubectl)" = "$RUNNER_TEMP/kubectl"`,
`kind version`,
`kubectl version --client=true`,
`./scripts/prepare-release-container-context.sh`,
`container_artifact_name`,
`container_artifact: ${{ needs.build_release_candidate.outputs.container_artifact_name }}`,
`source_sha: ${{ github.sha }}`,
`release-container-payload.json`,
`--target runtime_prebuilt`,
`--target agent_runtime_prebuilt`,
`--target control_plane_prebuilt`,
`Verify container binaries match immutable candidate`,
`PULSE_UPDATE_SIGNING_PUBLIC_KEY: ${{ vars.PULSE_UPDATE_SIGNING_PUBLIC_KEY }}`,
`Validate installer signing key pins`,
`go run ./scripts/release_update_key.go public-key-ssh`,
`install.sh scripts/pulse-auto-update.sh release/pulse-auto-update.sh`,
`does not trust the configured release signing key.`,
`id-token: write`,
`attestations: write`,
`uses: actions/attest@1e69f48acb82d1966a394da916b4c1698aa569d6 # v4.2.2`,
}
containerJob := workflowJobBlock(t, string(qualifierWorkflowBytes), "qualify")
if !strings.Contains(containerJob, "runs-on: ubuntu-24.04") || strings.Contains(containerJob, "self-hosted") {
t.Fatal("container qualification must use a fresh hosted VM for every channel")
}
if !strings.Contains(string(candidateWorkflowBytes), "always() && inputs.qualify_containers && needs.build.result == 'success'") {
t.Fatal("standalone exact-candidate qualification must not inherit skipped native-signing dependencies")
}
for _, forbidden := range []string{
"PULSE_UPDATE_SIGNING_KEY",
"PULSE_LICENSE_PUBLIC_KEY",
"packages: write",
"docker/login-action",
} {
if strings.Contains(containerJob, forbidden) {
t.Fatalf("exact-candidate container qualification must not receive release authority: %s", forbidden)
}
}
controlPlaneDockerfileBytes, err := os.ReadFile(repoFile("deploy", "provider-msp", "Dockerfile.control-plane"))
if err != nil {
t.Fatalf("read Dockerfile.control-plane: %v", err)
}
controlPlaneDockerfile := string(controlPlaneDockerfileBytes)
for _, needle := range []string{
"FROM control-plane-runtime-foundation AS control_plane_prebuilt",
"COPY --from=compiled_payload /binaries/pulse-control-plane-linux-${TARGETARCH:-amd64}",
"FROM control-plane-runtime-foundation AS runtime",
} {
if !strings.Contains(controlPlaneDockerfile, needle) {
t.Fatalf("Dockerfile.control-plane missing exact-candidate target: %s", needle)
}
}
for _, needle := range createReleaseRequired {
if !strings.Contains(createRelease, needle) {
t.Fatalf("create-release.yml missing attested secret-safe release build contract: %s", needle)
}
}
if strings.Contains(createRelease, `PULSE_LICENSE_PUBLIC_KEY=${{ secrets.PULSE_LICENSE_PUBLIC_KEY }}`) {
t.Fatal("create-release.yml must not pass the license public key through docker build args")
}
publishBytes, err := os.ReadFile(repoFile(".github", "workflows", "publish-docker.yml"))
if err != nil {
t.Fatalf("read publish-docker.yml: %v", err)
}
publish := string(publishBytes)
publishRequired := []string{
`name: Publish ${{ matrix.image }} image`,
`fail-fast: false`,
`- server`,
`- control-plane`,
`provenance: mode=max`,
`sbom: true`,
`container_artifact:`,
`source_sha:`,
`Download exact-candidate container payload`,
`Verify exact-candidate container payload`,
`target: runtime_prebuilt`,
`release_payload=${{ runner.temp }}/release-container-payload/payload/release`,
`id: build_control_plane_image`,
`if: matrix.image == 'server'`,
`if: matrix.image == 'control-plane'`,
`file: deploy/provider-msp/Dockerfile.control-plane`,
`target: control_plane_prebuilt`,
`compiled_payload=${{ runner.temp }}/release-container-payload/payload/compiled`,
`subject-name: docker.io/rcourtman/pulse`,
`subject-name: ghcr.io/${{ github.repository_owner }}/pulse`,
`subject-name: docker.io/rcourtman/pulse-control-plane`,
`subject-name: ghcr.io/${{ github.repository_owner }}/pulse-control-plane`,
`rcourtman/pulse-control-plane:${{ steps.version.outputs.tag }}`,
`ghcr.io/${{ github.repository_owner }}/pulse-control-plane:${{ steps.version.outputs.tag }}`,
// pulse-agent ships as release-asset binaries, not as a Docker
// image (see commit dropping the agent image publish steps).
// The agent attestation subject-names intentionally do not
// appear in publish-docker.yml.
`push-to-registry: true`,
`create-storage-record: false`,
`id-token: write`,
`attestations: write`,
}
for _, needle := range publishRequired {
if !strings.Contains(publish, needle) {
t.Fatalf("publish-docker.yml missing attested secret-safe publish contract: %s", needle)
}
}
for _, forbidden := range []string{
"PULSE_LICENSE_PUBLIC_KEY",
"PULSE_UPDATE_SIGNING_KEY",
"pulse_license_public_key",
"pulse_update_signing_key",
} {
if strings.Contains(publish, forbidden) {
t.Fatalf("publish-docker.yml must assemble the verified candidate without release build secrets: %s", forbidden)
}
}
}
func TestReleaseContainerQualificationBindsCheckoutToCallerCommit(t *testing.T) {
qualifierBytes, err := os.ReadFile(repoFile(".github", "workflows", "qualify-release-containers.yml"))
if err != nil {
t.Fatalf("read qualify-release-containers.yml: %v", err)
}
candidateBytes, err := os.ReadFile(repoFile(".github", "workflows", "build-release-candidate.yml"))
if err != nil {
t.Fatalf("read build-release-candidate.yml: %v", err)
}
releaseBytes, err := os.ReadFile(repoFile(".github", "workflows", "create-release.yml"))
if err != nil {
t.Fatalf("read create-release.yml: %v", err)
}
contractBytes, err := os.ReadFile(repoFile("docs", "release-control", "v6", "internal", "subsystems", "deployment-installability.md"))
if err != nil {
t.Fatalf("read deployment-installability contract: %v", err)
}
contract := strings.Join(strings.Fields(string(contractBytes)), " ")
qualifier := string(qualifierBytes)
qualifyJob := workflowJobBlock(t, qualifier, "qualify")
for _, required := range []string{
`ref: ${{ github.sha }}`,
`persist-credentials: false`,
`EXPECTED_SOURCE_SHA: ${{ github.sha }}`,
`test "$(git rev-parse HEAD)" = "${EXPECTED_SOURCE_SHA}"`,
`--source-sha "${{ github.sha }}"`,
} {
if !strings.Contains(qualifyJob, required) {
t.Fatalf("release container qualification does not fail closed on the exact caller commit: %s", required)
}
}
for _, forbidden := range []string{
"inputs.source_sha",
"source_sha:",
} {
if strings.Contains(qualifier, forbidden) {
t.Fatalf("release container qualification must not accept an arbitrary source ref: %s", forbidden)
}
}
for _, required := range []string{
"must not accept a caller-selected source revision",
"checks out that commit without persisting credentials",
"binds candidate-manifest verification to the same SHA",
} {
if !strings.Contains(contract, required) {
t.Fatalf("deployment installability contract is missing the release source-trust boundary: %s", required)
}
}
callerJobs := map[string]string{
"build-release-candidate.yml": workflowJobBlock(t, string(candidateBytes), "qualify-release-containers"),
"create-release.yml": workflowJobBlock(t, string(releaseBytes), "qualify_release_containers"),
}
for caller, job := range callerJobs {
if strings.Contains(job, "source_sha:") {
t.Fatalf("%s must not forward a caller-selectable source ref to container qualification", caller)
}
if !strings.Contains(job, "uses: ./.github/workflows/qualify-release-containers.yml") {
t.Fatalf("%s no longer calls the trusted container qualifier", caller)
}
}
}
func TestDeploymentDefaultsPinVersionedImagesAndHelmDocsChecksum(t *testing.T) {
versionBytes, err := os.ReadFile(repoFile("VERSION"))
if err != nil {
t.Fatalf("read VERSION: %v", err)
}
version := strings.TrimSpace(string(versionBytes))
if version == "" {
t.Fatal("VERSION is empty")
}
composeBytes, err := os.ReadFile(repoFile("docker-compose.yml"))
if err != nil {
t.Fatalf("read docker-compose.yml: %v", err)
}
compose := string(composeBytes)
if !strings.Contains(compose, "image: ${PULSE_IMAGE:-rcourtman/pulse:"+version+"}") {
t.Fatalf("docker-compose.yml must pin the governed release version:\n%s", compose)
}
if strings.Contains(compose, ":latest") {
t.Fatalf("docker-compose.yml must not default to a floating latest tag:\n%s", compose)
}
installDockerBytes, err := os.ReadFile(repoFile("scripts", "install-docker.sh"))
if err != nil {
t.Fatalf("read install-docker.sh: %v", err)
}
installDocker := string(installDockerBytes)
if !strings.Contains(installDocker, `CANONICAL_DEFAULT_PULSE_VERSION="`+version+`"`) {
t.Fatalf("install-docker.sh must pin the governed release version:\n%s", installDocker)
}
if strings.Contains(installDocker, ":latest") {
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)
}
}
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)
}
if previous, ok := previousPrereleaseVersion(version); ok && strings.Contains(chart, "v"+previous) {
t.Fatalf("Helm Chart.yaml must not retain the previous prerelease tag v%s:\n%s", previous, 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)
badgeVersion := shieldsBadgeMessage(version)
chartReadmeRequired := []string{
"![Version: " + version + "](https://img.shields.io/badge/Version-" + badgeVersion + "-informational?style=flat-square)",
"![AppVersion: " + version + "](https://img.shields.io/badge/AppVersion-" + badgeVersion + "-informational?style=flat-square)",
}
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)
}
}
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)
}
if previous, ok := previousPrereleaseVersion(version); ok && strings.Contains(chartReadme, previous) {
t.Fatalf("Helm README.md must not retain the previous prerelease version %s:\n%s", previous, chartReadme)
}
helmPagesBytes, err := os.ReadFile(repoFile(".github", "workflows", "helm-pages.yml"))
if err != nil {
t.Fatalf("read helm-pages.yml: %v", err)
}
helmPages := string(helmPagesBytes)
required := []string{
`workflow_call:`,
`chart_version:`,
`source_release_run_id:`,
`target_commitish:`,
`Require activated GitHub release and source run`,
`release-activation.json`,
`.github/workflows/create-release.yml`,
`"${GITHUB_REPOSITORY}" "${CHART_DIGEST}" "${CHART_PATH}"`,
`qualified chart metadata does not match the activated release`,
`name: Publish chart release and merge Pages index`,
`gh release create "${chart_release}" "${chart_path}"`,
`--repo "${GITHUB_REPOSITORY}"`,
`helm repo index "${index_work}"`,
`git -C gh-pages push origin HEAD:gh-pages`,
`grep -q "version: ${VERSION}"`,
`helm pull pulse-public/pulse --version "${VERSION}" --destination "${public_work}"`,
`cmp -s "${qualified_chart}" "${public_work}/pulse-${VERSION}.tgz"`,
}
for _, needle := range required {
if !strings.Contains(helmPages, needle) {
t.Fatalf("helm-pages.yml missing immutable chart promotion step: %s", needle)
}
}
for _, forbidden := range []string{
"workflow_run:",
`gh run download "${SOURCE_RELEASE_RUN_ID}"`,
`Smoke test with kind`,
`Install helm-docs`,
`helm package deploy/helm/pulse`,
`git checkout -B "$REQUIRED_BRANCH"`,
`git push origin HEAD:"$REQUIRED_BRANCH"`,
} {
if strings.Contains(helmPages, forbidden) {
t.Fatalf("helm-pages.yml must be an awaited exact-tag staging job; found forbidden %q", forbidden)
}
}
}
func TestV642SecurityPacketCoversBothAdministratorBoundaryFixes(t *testing.T) {
notesBytes, err := os.ReadFile(repoFile("docs", "releases", "RELEASE_NOTES_v6.4.2.md"))
if err != nil {
t.Fatalf("read v6.4.2 release notes: %v", err)
}
changelogBytes, err := os.ReadFile(repoFile("docs", "releases", "V6_CHANGELOG_v6.4.2.md"))
if err != nil {
t.Fatalf("read v6.4.2 changelog: %v", err)
}
notes := string(notesBytes)
changelog := string(changelogBytes)
for _, required := range []string{
"Infrastructure actions honor role boundaries",
"SSO access no longer implies administrator access",
"SAML allowlists fail closed",
"Security-sensitive setup requests are bounded",
"PBS backup state returns to idle reliably",
"Delivery warnings can be resolved from Overview",
"Assistant command help behaves as a complete dialog",
"Agent URL migration guidance is now included",
"Preview releases now distinguish beta and RC maturity",
"Systemd journal severity is preserved",
"Same-name Proxmox estates stay distinct after restart",
"map at least one trusted IdP group to the built-in `admin` role before upgrading",
"The rollback target is stable `v6.4.1`",
} {
if !strings.Contains(notes, required) {
t.Fatalf("v6.4.2 release notes missing %q", required)
}
}
for _, required := range []string{
"effective RBAC `admin` grant on `*`",
"SSO-only installation must map at least one trusted IdP group",
"request bodies now\n have explicit size limits",
"completed PBS-to-PBS sync copies no longer pin a\n guest in Backup Running",
"Alerts overview now exposes the same retry and dismiss actions",
"Assistant command help now uses the canonical responsive dialog boundary",
"migration guide now documents rerunning the agent installer",
"label the prerelease channel as Preview",
"Generated systemd services now preserve Pulse log severity",
"Durable Proxmox identity recovery now scopes pins",
"authenticates every Unified Agent download",
"release candidate verifier now binds the requested version explicitly",
"Helm OCI publication now authenticates both Helm",
"Promotion path: emergency stable patch from `main`",
"Mobile decision: `no-mobile-impact`",
} {
if !strings.Contains(changelog, required) {
t.Fatalf("v6.4.2 changelog missing %q", required)
}
}
}
func TestHelmChartDoesNotPublishRetiredExplorePrepassMonitoring(t *testing.T) {
chartDir := repoFile("deploy", "helm", "pulse")
err := filepath.WalkDir(chartDir, func(path string, d os.DirEntry, walkErr error) error {
if walkErr != nil {
return walkErr
}
if d.IsDir() {
return nil
}
switch filepath.Ext(path) {
case ".yaml", ".json", ".md":
default:
return nil
}
content, readErr := os.ReadFile(path)
if readErr != nil {
return readErr
}
text := string(content)
for _, forbidden := range []string{
"prometheusRule",
"pulse_ai_explore",
"Explore pre-pass",
"explore_runs_total",
} {
if strings.Contains(text, forbidden) {
t.Fatalf("helm chart file %s must not publish retired Assistant explore-prepass monitoring %q", path, forbidden)
}
}
return nil
})
if err != nil {
t.Fatalf("walk helm chart: %v", err)
}
}
func TestDeployDemoWorkflowIsRetiredToNonMutatingVerification(t *testing.T) {
workflowBytes, err := os.ReadFile(repoFile(".github", "workflows", "deploy-demo-server.yml"))
if err != nil {
t.Fatalf("read deploy-demo-server workflow: %v", err)
}
workflow := string(workflowBytes)
required := []string{
`name: Verify Demo Server`,
`workflow_dispatch:`,
`Verify Current Committed Stable Demo (No Mutation)`,
`uses: ./.github/workflows/update-demo-server.yml`,
`tag: latest`,
`target: stable`,
`verify_only: true`,
}
for _, needle := range required {
if !strings.Contains(workflow, needle) {
t.Fatalf("retired deploy-demo-server workflow missing non-mutating verification contract: %s", needle)
}
}
for _, forbidden := range []string{`go build`, `scp `, `docker compose`, `verify_only: false`} {
if strings.Contains(workflow, forbidden) {
t.Fatalf("retired deploy-demo-server workflow must not mutate the stable demo: %s", forbidden)
}
}
}
func TestUpdateDemoWorkflowUsesGovernedNetworkPath(t *testing.T) {
workflowBytes, err := os.ReadFile(repoFile(".github", "workflows", "update-demo-server.yml"))
if err != nil {
t.Fatalf("read update-demo-server workflow: %v", err)
}
profileBytes, err := os.ReadFile(repoFile(".github", "scripts", "resolve-demo-runtime-profile.sh"))
if err != nil {
t.Fatalf("read demo runtime profile resolver: %v", err)
}
workflow := string(workflowBytes) + "\n" + string(profileBytes)
required := []string{
`- name: Tailscale`,
`uses: tailscale/github-action@780049a30b6ff5c378a9e7b389d15ece7a204888 # v4.1.3`,
`oauth-client-id: ${{ secrets.TS_OAUTH_CLIENT_ID }}`,
`oauth-secret: ${{ secrets.TS_OAUTH_SECRET }}`,
`tags: tag:infra`,
`version: '1.94.2'`,
`ping: ${{ secrets.DEMO_SERVER_HOST }}`,
`bash .github/scripts/check-demo-reachability.sh`,
`workflow_call:`,
`verify_only:`,
`Waiting for activated release assets to be available`,
`bash /tmp/pulse-install.sh --version "$TAG"`,
`Refuse mutation during verification-only checks`,
`uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0`,
`go run ./scripts/release_update_key.go public-key-ssh`,
`sed -i "s|^PINNED_RELEASE_SSH_PUBLIC_KEY=.*|PINNED_RELEASE_SSH_PUBLIC_KEY=\"${TRUSTED_SSH_PUBLIC_KEY}\"|" /tmp/pulse-install.sh`,
`Verify target host identity`,
`bash .github/scripts/setup-demo-ssh.sh`,
`Demo environment points at host $REMOTE_HOSTNAME but expected $DEMO_EXPECTED_HOSTNAME.`,
`Prepare demo host storage`,
`KEEP_BACKUPS=2`,
`Removing demo backup to restore install headroom: %s`,
`Pruning demo volatile runtime stores to restore install headroom.`,
`sudo find "$CONFIG_DIR" -xdev -type f`,
`-name "metrics.db"`,
`Removing demo volatile store: %s`,
`Demo host does not have enough free space to back up $CONFIG_DIR before install.`,
`Restore demo runtime configuration`,
`Resolve target-compatible demo runtime profile`,
`git grep -q 'mockEagerHistoryPVEGuestLimit'`,
`git grep -q 'UpdateMetricCohort'`,
`git grep -q 'mockLargeEstateStartupReady'`,
`PROFILE="large-estate"`,
`MOCK_NODES=50`,
`MOCK_VMS_PER_NODE=10`,
`MOCK_K8S_PODS=40`,
`MOCK_SEED_DURATION=48h`,
`MOCK_SAMPLE_INTERVAL=1m`,
`MOCK_UPDATE_INTERVAL=2s`,
`PROFILE="legacy-bounded"`,
`MOCK_NODES=8`,
`MOCK_VMS_PER_NODE=6`,
`MOCK_LXCS_PER_NODE=4`,
`MOCK_DOCKER_HOSTS=2`,
`MOCK_DOCKER_CONTAINERS=8`,
`MOCK_GENERIC_HOSTS=2`,
`MOCK_K8S_CLUSTERS=1`,
`MOCK_K8S_NODES=3`,
`MOCK_K8S_PODS=12`,
`MOCK_K8S_DEPLOYMENTS=4`,
`MOCK_SEED_DURATION=2h`,
`MOCK_SAMPLE_INTERVAL=5m`,
`MOCK_UPDATE_INTERVAL=15s`,
`resolve_config_dir`,
`set_env_value DEMO_MODE true`,
`set_env_value PULSE_MOCK_MODE true`,
`set_env_value PULSE_MOCK_NODES "$MOCK_NODES"`,
`set_env_value PULSE_MOCK_VMS_PER_NODE "$MOCK_VMS_PER_NODE"`,
`set_env_value PULSE_MOCK_LXCS_PER_NODE "$MOCK_LXCS_PER_NODE"`,
`set_env_value PULSE_MOCK_DOCKER_HOSTS "$MOCK_DOCKER_HOSTS"`,
`set_env_value PULSE_MOCK_K8S_PODS "$MOCK_K8S_PODS"`,
`set_env_value PULSE_MOCK_SEED_METRICS_STORE false`,
`set_env_value PULSE_MOCK_TRENDS_SEED_DURATION "$MOCK_SEED_DURATION"`,
`set_env_value PULSE_MOCK_TRENDS_SAMPLE_INTERVAL "$MOCK_SAMPLE_INTERVAL"`,
`set_env_value PULSE_MOCK_UPDATE_INTERVAL "$MOCK_UPDATE_INTERVAL"`,
`ensure_demo_fixture_entitlement`,
`"demo_fixtures"`,
`del(.integrity)`,
`Demo fixture entitlement ensured in governed demo billing state.`,
`/api/license/runtime-capabilities`,
`Mock mode enabled`,
`Demo server mock mode did not enable after entitlement sync`,
`Require exact committed activation marker for mutation`,
`activation_convergence_run_id:`,
`release-activation.json`,
`.convergence_run_id == $convergence_run_id`,
`gh api "repos/${GITHUB_REPOSITORY}/releases/tags/${TAG}"`,
`.immutable // false`,
`https://raw.githubusercontent.com/${{ github.repository }}/${LEASE_SHA}/${OWNER_ASSET_NAME}`,
`.schema_version == 2`,
`Stable demo mutation refuses mutable, inactive, or prerelease tag`,
`Verify public browser smoke`,
`PULSE_DEMO_AUTH_USER`,
`PULSE_DEMO_AUTH_PASS`,
`./scripts/run_demo_public_browser_smoke.sh`,
}
for _, needle := range required {
if !strings.Contains(workflow, needle) {
t.Fatalf("update-demo-server workflow missing governed network path: %s", needle)
}
}
for _, forbidden := range []string{
`release_id:`,
`RELEASE_ID: ${{ inputs.release_id }}`,
`--archive "/tmp/${tarball}"`,
`unpublished draft`,
} {
if strings.Contains(workflow, forbidden) {
t.Fatalf("update-demo-server.yml must not deploy draft release assets; found %q", forbidden)
}
}
}
func TestUpdateDemoResolverChecksOutOutputHelperBeforeUse(t *testing.T) {
workflowBytes, err := os.ReadFile(repoFile(".github", "workflows", "update-demo-server.yml"))
if err != nil {
t.Fatalf("read update-demo-server workflow: %v", err)
}
workflow := string(workflowBytes)
resolveStart := strings.Index(workflow, "\n resolve:\n")
updateStart := strings.Index(workflow, "\n update-demo:\n")
if resolveStart < 0 || updateStart <= resolveStart {
t.Fatal("update-demo-server workflow must retain separate resolve and update-demo jobs")
}
resolve := workflow[resolveStart:updateStart]
checkout := strings.Index(resolve, "- name: Checkout repository")
target := strings.Index(resolve, "- name: Resolve target tag and demo environment")
helper := strings.Index(resolve, "python3 scripts/write_github_output.py tag \"$TAG\"")
if checkout < 0 || target <= checkout || helper <= target {
t.Fatal("demo resolver must check out its source before using scripts/write_github_output.py")
}
if !strings.Contains(resolve[checkout:target], "persist-credentials: false") {
t.Fatal("demo resolver checkout must not persist GitHub credentials")
}
}
func TestReleaseWatchdogIsControlBoundAndCannotBuildCandidate(t *testing.T) {
workflow, err := os.ReadFile(repoFile(".github", "workflows", "release-dry-run.yml"))
if err != nil {
t.Fatal(err)
}
text := string(workflow)
for _, required := range []string{
"Release Watchdog at {0}",
"if: ${{ inputs.watchdog != true && inputs.version != '' }}",
`[ "${EXPECTED_WORKFLOW_SHA_INPUT}" != "${GITHUB_SHA}" ]`,
`[ "${GITHUB_REF}" != "refs/heads/main" ]`,
`Candidate rehearsal requires explicit rollback and no watchdog SHA.`,
`Watchdog refuses candidate input ${name}.`,
`Watchdog refuses exception input ${name}.`,
"WATCHDOG_MODE: ${{ steps.mode.outputs.watchdog }}",
"verify_only: true",
`require_result "stable demo no-mutation verification" "$DEMO_RESULT" success`,
} {
if !strings.Contains(text, required) {
t.Fatalf("fixed release watchdog missing safety contract: %s", required)
}
}
guard := strings.Index(text, "- name: Validate release ref")
checkout := strings.Index(text, "- name: Checkout repository")
if guard < 0 || checkout <= guard {
t.Fatal("watchdog control identity and envelope must be checked before checkout")
}
}
func TestReleaseWatchdogDoesNotManufacturePromotionReadiness(t *testing.T) {
workflow, err := os.ReadFile(repoFile(".github", "workflows", "release-dry-run.yml"))
if err != nil {
t.Fatal(err)
}
text := string(workflow)
for _, required := range []string{
`print("metadata_mode=watchdog")`,
`echo "metadata_mode=promotion"`,
`derive_latest_stable_rollback_tag(version, list_stable_tags())`,
`ARTIFACT_NAME="release-watchdog-summary"`,
`SUMMARY_FILE="release-dry-run/watchdog-summary.md"`,
`not a candidate promotion`,
`does not establish promotion readiness, soak, qualification or installed recovery`,
`name: ${{ steps.summary.outputs.artifact_name }}`,
`path: ${{ steps.summary.outputs.summary_file }}`,
} {
if !strings.Contains(text, required) {
t.Fatalf("watchdog/promotion evidence separation missing: %s", required)
}
}
if strings.Contains(text, "--derive-rollback-latest-stable") {
t.Fatal("watchdog must not manufacture a stable-promotion envelope from the branch VERSION")
}
assertFileContainsAll(t, repoFile("scripts", "release_control", "rehearsal_source_test.py"),
`test_postpublication_watchdog_does_not_pretend_to_promote_stable`,
`test_identical_candidate_rehearsal_still_refuses_new_stable_promotion`,
`test_watchdog_summary_cannot_be_recorded_as_promotion_readiness`,
)
}
func TestDemoMutationAndRecoverySharePhysicalTargetLock(t *testing.T) {
updateBytes, err := os.ReadFile(repoFile(".github", "workflows", "update-demo-server.yml"))
if err != nil {
t.Fatalf("read update-demo-server workflow: %v", err)
}
recoveryBytes, err := os.ReadFile(repoFile(".github", "workflows", "recover-demo-server.yml"))
if err != nil {
t.Fatalf("read recover-demo-server workflow: %v", err)
}
for name, workflow := range map[string]string{
"update": string(updateBytes),
"recovery": string(recoveryBytes),
} {
for _, required := range []string{
"concurrency:\n",
"group: stable-demo-runtime",
"queue: max",
"cancel-in-progress: false",
} {
if !strings.Contains(workflow, required) {
t.Fatalf("%s demo workflow must share the non-cancelling physical-target lock: missing %q", name, required)
}
}
}
if !strings.Contains(string(updateBytes), "environment: ${{ needs.resolve.outputs.environment_name }}") {
t.Fatal("update demo workflow must apply the shared lock to its resolved environment target")
}
if !strings.Contains(string(recoveryBytes), "environment: demo-stable") {
t.Fatal("demo recovery workflow must apply the shared lock to the stable target")
}
}
func TestDemoSshSetupHelperHandlesIpLiteralTargets(t *testing.T) {
helperBytes, err := os.ReadFile(repoFile(".github", "scripts", "setup-demo-ssh.sh"))
if err != nil {
t.Fatalf("read demo ssh setup helper: %v", err)
}
helper := string(helperBytes)
required := []string{
`is_ip_literal()`,
`ipaddress.ip_address(sys.argv[1])`,
`host_needs_dns=false`,
`Demo SSH host is an IP literal; skipping DNS resolution wait.`,
`[ "$host_needs_dns" = "true" ] && ! getent hosts "$DEMO_SERVER_HOST"`,
`ssh-keyscan -T 10 -H "$DEMO_SERVER_HOST"`,
`MAX_SSH_SETUP_ATTEMPTS="${DEMO_SSH_SETUP_ATTEMPTS:-3}"`,
`Demo network preflight passed, but ssh-keyscan did not return host keys.`,
}
for _, needle := range required {
if !strings.Contains(helper, needle) {
t.Fatalf("demo ssh setup helper missing guarded IP/hostname behavior: %s", needle)
}
}
tmpDir := t.TempDir()
fakeBin := filepath.Join(tmpDir, "bin")
if err := os.MkdirAll(fakeBin, 0o755); err != nil {
t.Fatalf("create fake bin: %v", err)
}
getentMarker := filepath.Join(tmpDir, "getent-called")
if err := os.WriteFile(filepath.Join(fakeBin, "getent"), []byte("#!/bin/sh\n: > \"$GETENT_MARKER\"\nexit 1\n"), 0o755); err != nil {
t.Fatalf("write fake getent: %v", err)
}
if err := os.WriteFile(filepath.Join(fakeBin, "ssh-keyscan"), []byte("#!/bin/sh\nprintf '100.109.163.95 ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDemo\\n'\n"), 0o755); err != nil {
t.Fatalf("write fake ssh-keyscan: %v", err)
}
homeDir := filepath.Join(tmpDir, "home")
cmd := exec.Command("bash", repoFile(".github", "scripts", "setup-demo-ssh.sh"))
cmd.Env = append(os.Environ(),
"DEMO_SERVER_HOST=100.109.163.95",
"DEMO_SERVER_SSH_KEY=fake-private-key",
"GETENT_MARKER="+getentMarker,
"HOME="+homeDir,
"PATH="+fakeBin+string(os.PathListSeparator)+os.Getenv("PATH"),
)
output, err := cmd.CombinedOutput()
if err != nil {
t.Fatalf("demo ssh setup helper failed for IP literal: %v\n%s", err, output)
}
if _, err := os.Stat(getentMarker); !os.IsNotExist(err) {
t.Fatalf("demo ssh setup helper must not require getent hosts for IP literals; stat err=%v", err)
}
knownHosts, err := os.ReadFile(filepath.Join(homeDir, ".ssh", "known_hosts"))
if err != nil {
t.Fatalf("read generated known_hosts: %v", err)
}
if !strings.Contains(string(knownHosts), "ssh-ed25519") {
t.Fatalf("known_hosts missing captured key: %s", knownHosts)
}
if !strings.Contains(string(output), "Demo SSH host is an IP literal; skipping DNS resolution wait.") {
t.Fatalf("helper output did not report IP literal path: %s", output)
}
}
func TestDemoReachabilityHelperSeparatesTailnetAndSshTransportProof(t *testing.T) {
helperBytes, err := os.ReadFile(repoFile(".github", "scripts", "check-demo-reachability.sh"))
if err != nil {
t.Fatalf("read demo reachability helper: %v", err)
}
helper := string(helperBytes)
for _, needle := range []string{
`tailscale status --json`,
`tailscale ping --c 3 --timeout 10s "$DEMO_SERVER_HOST"`,
`nc -z -w 5 "$DEMO_SERVER_HOST" "$TCP_PORT"`,
`Runner Tailscale DNS:`,
`Runner Tailscale tags:`,
`Demo peer is not present in the runner peer map yet.`,
`Verify sshd and the host firewall on tailscale0.`,
} {
if !strings.Contains(helper, needle) {
t.Fatalf("demo reachability helper missing diagnostic contract: %s", needle)
}
}
tmpDir := t.TempDir()
fakeBin := filepath.Join(tmpDir, "bin")
if err := os.MkdirAll(fakeBin, 0o755); err != nil {
t.Fatalf("create fake bin: %v", err)
}
tailscaleScript := `#!/bin/sh
if [ "$1" = "status" ]; then
printf '%s\n' '{"BackendState":"Running","Self":{"TailscaleIPs":["100.100.100.1"]},"Peer":{"demo":{"TailscaleIPs":["100.109.163.95"],"Online":true,"Active":true,"Relay":"lhr"}}}'
exit 0
fi
if [ "$1" = "ping" ]; then
echo 'pong from demo'
exit 0
fi
exit 1
`
if err := os.WriteFile(filepath.Join(fakeBin, "tailscale"), []byte(tailscaleScript), 0o755); err != nil {
t.Fatalf("write fake tailscale: %v", err)
}
if err := os.WriteFile(filepath.Join(fakeBin, "nc"), []byte("#!/bin/sh\nexit 0\n"), 0o755); err != nil {
t.Fatalf("write fake nc: %v", err)
}
cmd := exec.Command("bash", repoFile(".github", "scripts", "check-demo-reachability.sh"))
cmd.Env = append(os.Environ(),
"DEMO_SERVER_HOST=100.109.163.95",
"PATH="+fakeBin+string(os.PathListSeparator)+os.Getenv("PATH"),
)
output, err := cmd.CombinedOutput()
if err != nil {
t.Fatalf("demo reachability helper failed: %v\n%s", err, output)
}
for _, needle := range []string{"Tailscale backend: Running", "Demo peer state: online=True active=True relay=lhr", "Demo SSH transport is reachable over Tailscale."} {
if !strings.Contains(string(output), needle) {
t.Fatalf("demo reachability output missing %q: %s", needle, output)
}
}
}
func TestDemoPublicBrowserSmokeWaitsForVisibleLoginUI(t *testing.T) {
scriptBytes, err := os.ReadFile(repoFile("scripts", "demo_public_browser_smoke.cjs"))
if err != nil {
t.Fatalf("read demo public browser smoke script: %v", err)
}
script := string(scriptBytes)
required := []string{
`waitUntil: 'domcontentloaded'`,
`getByLabel('Username').waitFor({ state: 'visible', timeout: 120000 })`,
`getByLabel('Password').waitFor({ state: 'visible', timeout: 120000 })`,
`getByRole('button', { name: 'Sign in to Pulse' }).waitFor({ state: 'visible', timeout: 120000 })`,
`getByRole('status', { name: 'Backend and live data stream are connected.' })`,
}
for _, needle := range required {
if !strings.Contains(script, needle) {
t.Fatalf("demo public browser smoke missing visible-login readiness proof: %s", needle)
}
}
if strings.Contains(script, `waitUntil: 'networkidle'`) {
t.Fatal("demo public browser smoke still depends on networkidle instead of visible login readiness")
}
}
func TestDockerfileStagesShippedDocsForEmbeddedFrontendBuild(t *testing.T) {
dockerfileBytes, err := os.ReadFile(repoFile("Dockerfile"))
if err != nil {
t.Fatalf("read Dockerfile: %v", err)
}
dockerfile := string(dockerfileBytes)
required := []string{
`COPY docs/ /app/docs/`,
`COPY SECURITY.md TERMS.md /app/`,
}
for _, needle := range required {
if !strings.Contains(dockerfile, needle) {
t.Fatalf("Dockerfile missing shipped-doc build input: %s", needle)
}
}
dockerignoreBytes, err := os.ReadFile(repoFile(".dockerignore"))
if err != nil {
t.Fatalf("read .dockerignore: %v", err)
}
dockerignore := string(dockerignoreBytes)
requiredAllowlist := []string{
`!docs/`,
`!docs/**`,
`!SECURITY.md`,
`!TERMS.md`,
}
for _, needle := range requiredAllowlist {
if !strings.Contains(dockerignore, needle) {
t.Fatalf(".dockerignore missing shipped-doc allowlist entry: %s", needle)
}
}
}
func TestDockerfileStampsTelemetryDeploymentMethod(t *testing.T) {
dockerfileBytes, err := os.ReadFile(repoFile("Dockerfile"))
if err != nil {
t.Fatalf("read Dockerfile: %v", err)
}
if !strings.Contains(string(dockerfileBytes), `ENV PULSE_DEPLOYMENT_METHOD=container_other`) {
t.Fatal("Dockerfile must stamp the closed fallback deployment method for container images")
}
}
func TestReleaseUpdateKeyFingerprintUsesCanonicalRawPublicKeyHash(t *testing.T) {
publicKey, privateKey, err := ed25519.GenerateKey(rand.Reader)
if err != nil {
t.Fatalf("generate signing key: %v", err)
}
cmd := exec.Command("go", "run", "./scripts/release_update_key.go", "fingerprint", "--private-key", base64.StdEncoding.EncodeToString(privateKey))
cmd.Dir = repoFile()
output, err := cmd.CombinedOutput()
if err != nil {
t.Fatalf("release_update_key.go fingerprint failed: %v\n%s", err, output)
}
sum := sha256.Sum256(publicKey)
expected := "SHA256:" + base64.StdEncoding.EncodeToString(sum[:])
if got := strings.TrimSpace(string(output)); got != expected {
t.Fatalf("fingerprint mismatch: got %q want %q", got, expected)
}
}
func TestReleaseUpdateKeyPublicKeySSHAcceptsPublicKey(t *testing.T) {
publicKey, _, err := ed25519.GenerateKey(rand.Reader)
if err != nil {
t.Fatalf("generate signing key: %v", err)
}
cmd := exec.Command("go", "run", "./scripts/release_update_key.go", "public-key-ssh", "--public-key", base64.StdEncoding.EncodeToString(publicKey), "--comment", "pulse-installer")
cmd.Dir = repoFile()
output, err := cmd.CombinedOutput()
if err != nil {
t.Fatalf("release_update_key.go public-key-ssh failed: %v\n%s", err, output)
}
sshPublicKey, err := ssh.NewPublicKey(publicKey)
if err != nil {
t.Fatalf("derive SSH public key: %v", err)
}
expected := strings.TrimSpace(string(ssh.MarshalAuthorizedKey(sshPublicKey))) + " pulse-installer"
if got := strings.TrimSpace(string(output)); got != expected {
t.Fatalf("SSH public key mismatch: got %q want %q", got, expected)
}
}
func TestReleaseUpdateKeyVerifiesDetachedUpdateSignature(t *testing.T) {
publicKey, privateKey, err := ed25519.GenerateKey(rand.Reader)
if err != nil {
t.Fatalf("generate signing key: %v", err)
}
root := t.TempDir()
artifact := filepath.Join(root, "artifact")
signature := filepath.Join(root, "artifact.sig")
if err := os.WriteFile(artifact, []byte("release bytes"), 0o600); err != nil {
t.Fatalf("write artifact: %v", err)
}
sign := exec.Command("go", "run", "./scripts/release_update_key.go", "sign", "--private-key", base64.StdEncoding.EncodeToString(privateKey), "--file", artifact)
sign.Dir = repoFile()
signatureBytes, err := sign.CombinedOutput()
if err != nil {
t.Fatalf("sign release artifact: %v\n%s", err, signatureBytes)
}
if err := os.WriteFile(signature, signatureBytes, 0o600); err != nil {
t.Fatalf("write detached signature: %v", err)
}
verify := exec.Command("go", "run", "./scripts/release_update_key.go", "verify", "--public-key", base64.StdEncoding.EncodeToString(publicKey), "--file", artifact, "--signature-file", signature)
verify.Dir = repoFile()
if output, err := verify.CombinedOutput(); err != nil {
t.Fatalf("verify release artifact: %v\n%s", err, output)
}
if err := os.WriteFile(artifact, []byte("tampered bytes"), 0o600); err != nil {
t.Fatalf("tamper artifact: %v", err)
}
verify = exec.Command("go", "run", "./scripts/release_update_key.go", "verify", "--public-key", base64.StdEncoding.EncodeToString(publicKey), "--file", artifact, "--signature-file", signature)
verify.Dir = repoFile()
if output, err := verify.CombinedOutput(); err == nil || !strings.Contains(string(output), "signature verification failed") {
t.Fatalf("tampered release artifact passed verification: err=%v output=%s", err, output)
}
}
func TestSecureRuntimeQualificationPacketIsHostedAndReleaseBound(t *testing.T) {
read := func(parts ...string) string {
t.Helper()
content, err := os.ReadFile(repoFile(parts...))
if err != nil {
t.Fatalf("read %s: %v", strings.Join(parts, "/"), err)
}
return string(content)
}
compilerWorkflow := read(".github", "workflows", "compile-release-payload.yml")
hostedJob := workflowJobBlock(t, compilerWorkflow, "compile-secure-runtime-qualification")
for _, required := range []string{
"runs-on: ubuntu-24.04",
"attestations: write",
"id-token: write",
`EXPECTED_SOURCE_SHA: ${{ inputs.source_sha }}`,
`test "$(git rev-parse HEAD)" = "${EXPECTED_SOURCE_SHA}"`,
"./scripts/build-secure-runtime-qualification.sh",
"secure-runtime-compiler-subjects.sha256",
"secure-runtime-compiler-provenance.sigstore.json",
} {
if !strings.Contains(hostedJob, required) {
t.Fatalf("hosted secure-runtime compiler job missing %q", required)
}
}
if strings.Contains(hostedJob, "PULSE_UPDATE_SIGNING_KEY") || strings.Contains(hostedJob, "PULSE_LICENSE_PUBLIC_KEY") {
t.Fatal("hosted secure-runtime compiler must not receive private signing or license material")
}
if strings.Contains(hostedJob, `test "$(git rev-parse HEAD)" = "${{ inputs.source_sha }}"`) {
t.Fatal("hosted secure-runtime compiler must pass the requested source SHA through env instead of generating shell source")
}
builder := read("scripts", "build-secure-runtime-qualification.sh")
for _, required := range []string{
"go build -buildvcs=false -trimpath",
"collector_v1_version=\"${predecessor_base}-0.secure.v6.1\"",
"collector_v3_version=\"${predecessor_base}-0.secure.v6.3\"",
"compiler_runner_trust\": \"github-hosted-deny-self-hosted\"",
"secure-runtime-build-contract-v1.json",
} {
if !strings.Contains(builder, required) {
t.Fatalf("secure-runtime qualification builder missing %q", required)
}
}
candidateWorkflow := read(".github", "workflows", "build-release-candidate.yml")
for _, required := range []string{
"secure_runtime_artifact_digest",
"Verify hosted secure-runtime compiler packet",
"--deny-self-hosted-runners",
"secure-runtime-compiler-provenance.sigstore.json",
"cmp secure-runtime-qualification/pulse-agent-linux-amd64 release-compiled/payload/binaries/pulse-agent-linux-amd64",
"PULSE_REQUIRE_SECURE_RUNTIME_QUALIFICATION: \"true\"",
} {
if !strings.Contains(candidateWorkflow, required) {
t.Fatalf("candidate workflow missing secure-runtime packet binding %q", required)
}
}
buildRelease := read("scripts", "build-release.sh")
if strings.Index(buildRelease, "Imported hosted secure-runtime qualification packet") > strings.Index(buildRelease, "pulse_release_generate_packet_sbom") {
t.Fatal("secure-runtime packet must be imported before SBOM and checksum generation")
}
for _, required := range []string{
"hosted secure-runtime collector-v4 does not reproduce the release collector",
"secure-runtime-build-contract-v1.json",
"secure-runtime-compiler-provenance.sigstore.json",
} {
if !strings.Contains(buildRelease, required) {
t.Fatalf("build-release.sh missing secure-runtime import guard %q", required)
}
}
assetHelper := read("scripts", "release_asset_common.sh")
for _, required := range []string{
`pulse-agent-runner-linux-*`,
`pulse-secure-runtime-collector-v*-linux-*`,
`secure-runtime-build-contract-v1.json`,
`secure-runtime-compiler-provenance.sigstore.json`,
} {
if !strings.Contains(assetHelper, required) {
t.Fatalf("release checksum inventory missing %q", required)
}
}
publicationWorkflow := read(".github", "workflows", "create-release.yml")
for _, required := range []string{
"release/secure-runtime-build-contract-v1.json",
"release/secure-runtime-compiler-provenance.sigstore.json",
"release/pulse-secure-runtime-collector-v1-linux-amd64",
"release/pulse-secure-runtime-collector-v3-linux-amd64",
"qualify-secure-runtime-release.yml/dispatches",
`{ref: "main", return_run_details: true, inputs: {tag: $tag}}`,
"Secure-runtime qualification dispatch did not return an exact workflow run.",
"Immutable RC publication did not retain an exact secure-runtime qualification run identity.",
} {
if !strings.Contains(publicationWorkflow, required) {
t.Fatalf("release publication missing secure-runtime asset %q", required)
}
}
qualificationWorkflow := read(".github", "workflows", "qualify-secure-runtime-release.yml")
if strings.Contains(qualificationWorkflow, "release:\n types: [published]") {
t.Fatal("secure-runtime qualification must be explicitly dispatched after immutable publication, not rely on suppressed release events")
}
originIndex := strings.Index(qualificationWorkflow, "Bind canonical Pulse origin")
sourceIndex := strings.Index(qualificationWorkflow, "Verify detached release source")
preauthenticationIndex := strings.Index(qualificationWorkflow, "Pre-authenticate exact qualification packet")
privilegedExecutionIndex := strings.Index(qualificationWorkflow, "docker run")
if originIndex < 0 || sourceIndex < originIndex || preauthenticationIndex < sourceIndex || privilegedExecutionIndex < preauthenticationIndex {
t.Fatal("secure-runtime release packet must be authenticated before any privileged Docker execution")
}
if strings.Contains(qualificationWorkflow, `$RUNNER_TEMP/secure-runtime-downloads:/release:ro`) {
t.Fatal("privileged qualification must never mount caller-owned downloaded binaries as the executable packet")
}
for _, required := range []string{
".immutable == true",
`test "${GITHUB_REF}" = "refs/heads/main"`,
`test "${GITHUB_WORKFLOW_SHA}" = "${GITHUB_SHA}"`,
`test "${GITHUB_REPOSITORY}" = "rcourtman/Pulse"`,
`https://github.com/rcourtman/Pulse|https://github.com/rcourtman/Pulse.git)`,
`git remote set-url origin https://github.com/rcourtman/Pulse.git`,
"ca-certificates curl dbus systemd systemd-sysv util-linux",
`docker:27.5.1-dind@sha256:f649ef046008ca7f926a2571c32b0ac22e5c59eb61b959617f9acc2a4c638cf5`,
`for command in curl docker dockerd id ip nsenter runuser systemctl`,
`--host=unix:///var/run/docker.sock`,
`--storage-driver=vfs`,
`--bridge=none`,
`--iptables=false`,
`--network none`,
`ip link add pulse-can0 type veth peer name pulse-can1`,
`ip address add 192.0.2.1/32 dev pulse-can0`,
`test -z "$(ip -4 route show default)"`,
`docker exec "${container}" chown -R "$(id -u):$(id -g)" /evidence`,
`pulse-secure-runtime-fixture:v7`,
"--verify-release-packet-only",
"--verified-packet-dir",
"$RUNNER_TEMP/secure-runtime-verified:/release:ro",
"$RUNNER_TEMP/secure-runtime-harness:/harness:ro",
"PULSE_SECURE_RUNTIME_SYSTEMD_LAB=disposable-v1",
"^TestSecureRuntimeSystemdDockerV7Lab$",
"--release-candidate-tag",
"--collector-v4-signature",
"secure_runtime_attestation_v7.py",
"secure-agent-runtime-systemd-receipt-v7-",
"secure-agent-runtime-systemd-transcript-v7-",
} {
if !strings.Contains(qualificationWorkflow, required) {
t.Fatalf("post-publication secure-runtime qualification missing %q", required)
}
}
canaryIndex := strings.Index(qualificationWorkflow, `ip link add pulse-can0 type veth peer name pulse-can1`)
labIndex := strings.Index(qualificationWorkflow, `--env PULSE_SECURE_RUNTIME_SYSTEMD_LAB=1`)
if canaryIndex <= privilegedExecutionIndex || labIndex <= canaryIndex {
t.Fatal("the isolated host-interface canary must be configured before running the immutable RC lab")
}
ownershipIndex := strings.Index(qualificationWorkflow, `docker exec "${container}" chown -R "$(id -u):$(id -g)" /evidence`)
attestIndex := strings.Index(qualificationWorkflow, `--output "${evidence_dir}/attestation.json"`)
if ownershipIndex <= labIndex || attestIndex <= ownershipIndex {
t.Fatal("the lab evidence must be handed to the runner user after the lab and before attestation")
}
if !strings.Contains(qualificationWorkflow, `--privileged \
--network none \
--cgroupns=host`) {
t.Fatal("the outer systemd lab must retain its no-network namespace")
}
for _, forbidden := range []string{
`/var/run/docker.sock:/var/run/docker.sock`,
`--host=tcp://`,
`docker pull`,
} {
if strings.Contains(qualificationWorkflow, forbidden) {
t.Fatalf("post-publication secure-runtime qualification contains forbidden Docker boundary %q", forbidden)
}
}
}
func TestReleaseAssetCommonRunsUpdateKeyThroughModulePath(t *testing.T) {
if _, err := exec.LookPath("bash"); err != nil {
t.Skip("bash not installed")
}
if _, err := exec.LookPath("go"); err != nil {
t.Skip("go not installed")
}
// Keep the toolchain selected by the test runner. A login shell may source a
// developer's stale mise/asdf profile and replace setup-go's release
// toolchain while leaving its GOROOT behind.
cmd := exec.Command("bash", "-c", "source ./scripts/release_asset_common.sh; pulse_release_go_run_update_key")
cmd.Dir = repoFile()
output, err := cmd.CombinedOutput()
if err == nil {
t.Fatalf("expected release_update_key.go usage failure, got success:\n%s", output)
}
text := string(output)
if !strings.Contains(text, "release_update_key.go public-key") {
t.Fatalf("expected release_update_key.go usage output, got:\n%s", output)
}
if strings.Contains(text, "use of internal package") {
t.Fatalf("release helper invoked update key outside module import boundary:\n%s", output)
}
}
func TestReleaseAssetCommonRejectsUnexpectedUpdateSigningPublicKey(t *testing.T) {
if _, err := exec.LookPath("bash"); err != nil {
t.Skip("bash not installed")
}
if _, err := exec.LookPath("go"); err != nil {
t.Skip("go not installed")
}
_, privateKey, err := ed25519.GenerateKey(rand.Reader)
if err != nil {
t.Fatalf("generate signing key: %v", err)
}
unexpectedPublicKey, _, err := ed25519.GenerateKey(rand.Reader)
if err != nil {
t.Fatalf("generate unexpected public key: %v", err)
}
cmd := exec.Command("bash", "-c", "source ./scripts/release_asset_common.sh; pulse_release_prepare_signing_state pulse-installer pulse-install")
cmd.Dir = repoFile()
cmd.Env = append(os.Environ(),
"PULSE_UPDATE_SIGNING_KEY="+base64.StdEncoding.EncodeToString(privateKey),
"PULSE_UPDATE_SIGNING_PUBLIC_KEY="+base64.StdEncoding.EncodeToString(unexpectedPublicKey),
)
output, err := cmd.CombinedOutput()
if err == nil {
t.Fatalf("expected release_asset_common.sh to reject a mismatched signing public key:\n%s", output)
}
if !strings.Contains(string(output), "does not match PULSE_UPDATE_SIGNING_PUBLIC_KEY") {
t.Fatalf("expected mismatched signing public key error, got:\n%s", output)
}
}
// TestBuildReleasePackagesPulseMcpForAllPlatforms pins the
// distribution path for pulse-mcp: each Pulse release must build
// the MCP adapter for the same multi-OS matrix as the unified
// agent and emit per-platform tarballs/zips, bare binaries (for
// /releases/latest/download/ redirect compatibility), and the
// install-mcp.sh script into RELEASE_DIR. Drift in any of those
// strings means an integrator following the published install
// path hits a 404 on the release endpoint instead of a working
// binary.
func TestBuildReleasePackagesPulseMcpForAllPlatforms(t *testing.T) {
content, err := os.ReadFile(repoFile("scripts", "build-release.sh"))
if err != nil {
t.Fatalf("read build-release.sh: %v", err)
}
script := string(content)
compileContent, err := os.ReadFile(repoFile("scripts", "build-release-binaries.sh"))
if err != nil {
t.Fatalf("read build-release-binaries.sh: %v", err)
}
compileScript := string(compileContent)
required := []string{
// Per-platform packaging follows the pulse-agent shape
// exactly so the upload step's glob does not need
// special cases.
`tar -czf "$RELEASE_DIR/pulse-mcp-v${VERSION}-linux-amd64.tar.gz" -C "$BUILD_DIR" pulse-mcp-linux-amd64`,
`tar -czf "$RELEASE_DIR/pulse-mcp-v${VERSION}-darwin-arm64.tar.gz" -C "$BUILD_DIR" pulse-mcp-darwin-arm64`,
`zip -j "$RELEASE_DIR/pulse-mcp-v${VERSION}-windows-amd64.zip" "$BUILD_DIR/pulse-mcp-windows-amd64.exe"`,
// Bare-binary copies for the /releases/latest/download/
// redirect that install-mcp.sh fetches by default.
`cp "$BUILD_DIR/pulse-mcp-linux-amd64" "$RELEASE_DIR/"`,
`cp "$BUILD_DIR/pulse-mcp-darwin-amd64" "$RELEASE_DIR/"`,
`cp "$BUILD_DIR/pulse-mcp-darwin-arm64" "$RELEASE_DIR/"`,
`cp "$BUILD_DIR/pulse-mcp-windows-amd64.exe" "$RELEASE_DIR/"`,
// The installer scripts themselves must reach
// RELEASE_DIR so the GitHub Releases asset upload can
// publish them as the canonical curl-pipe-bash entry
// point.
`cp scripts/install-mcp.sh "$RELEASE_DIR/install-mcp.sh"`,
`[ -f scripts/install-mcp.ps1 ] && cp scripts/install-mcp.ps1 "$RELEASE_DIR/install-mcp.ps1"`,
}
for _, needle := range required {
if !strings.Contains(script, needle) {
t.Fatalf("build-release.sh missing pulse-mcp distribution wiring: %s", needle)
}
}
for _, needle := range []string{
`package=./cmd/pulse-mcp`,
`pulse_release_binary_filename "${component}" "${target}"`,
} {
if !strings.Contains(compileScript, needle) {
t.Fatalf("build-release-binaries.sh missing pulse-mcp compilation wiring: %s", needle)
}
}
// install-mcp.sh and install-mcp.ps1 must both exist as
// shipped scripts; the build pipeline references them, so
// missing-file drift breaks release builds rather than
// quietly ships an installer that 404s.
if _, err := os.Stat(repoFile("scripts", "install-mcp.sh")); err != nil {
t.Fatalf("scripts/install-mcp.sh missing: %v", err)
}
if _, err := os.Stat(repoFile("scripts", "install-mcp.ps1")); err != nil {
t.Fatalf("scripts/install-mcp.ps1 missing: %v", err)
}
// install-mcp.sh's install-dir resolution and SHA256
// verification are load-bearing: dropping either silently
// turns the installer into "curl | bash with no integrity
// check," which is the failure mode the hook is here to
// prevent. Pin the touchstones.
mcpScript, err := os.ReadFile(repoFile("scripts", "install-mcp.sh"))
if err != nil {
t.Fatalf("read install-mcp.sh: %v", err)
}
for _, needle := range []string{
`detect_platform()`,
`choose_install_dir()`,
`PINNED_RELEASE_SSH_PUBLIC_KEY`,
`checksums.txt`,
`checksums.txt.sshsig`,
`ssh-keygen -Y verify`,
`sha256 mismatch`,
} {
if !strings.Contains(string(mcpScript), needle) {
t.Fatalf("install-mcp.sh missing required helper or guard: %s", needle)
}
}
// Unix installers consume bare binaries, not the versioned archives. Keep
// those exact assets in the signed manifest; the broad pulse-*.tar.gz and
// pulse-*.exe patterns otherwise leave only Unix bare MCP binaries out.
commonContent, err := os.ReadFile(repoFile("scripts", "release_asset_common.sh"))
if err != nil {
t.Fatalf("read release_asset_common.sh: %v", err)
}
for _, needle := range []string{
`checksum_files+=( pulse-mcp-linux-* )`,
`checksum_files+=( pulse-mcp-darwin-* )`,
`checksum_files+=( pulse-mcp-freebsd-* )`,
`checksum_files+=( install-mcp.sh )`,
} {
if !strings.Contains(string(commonContent), needle) {
t.Fatalf("release checksum collection missing bare MCP assets: %s", needle)
}
}
releaseDir := t.TempDir()
for _, asset := range []string{
"pulse-mcp-linux-amd64",
"pulse-mcp-darwin-arm64",
"pulse-mcp-freebsd-amd64",
"install-mcp.sh",
} {
if err := os.WriteFile(filepath.Join(releaseDir, asset), []byte(asset), 0o755); err != nil {
t.Fatalf("write MCP checksum fixture: %v", err)
}
}
checksumCmd := exec.Command("bash", "-c", `source "$1"; pulse_release_collect_checksum_files "$2"`, "pulse-mcp-checksum-test", repoFile("scripts", "release_asset_common.sh"), releaseDir)
checksumOutput, err := checksumCmd.CombinedOutput()
if err != nil {
t.Fatalf("collect MCP release checksum files: %v\n%s", err, checksumOutput)
}
for _, asset := range []string{"pulse-mcp-linux-amd64", "pulse-mcp-darwin-arm64", "pulse-mcp-freebsd-amd64", "install-mcp.sh"} {
if !strings.Contains(string(checksumOutput), asset) {
t.Fatalf("bare MCP release asset %s missing from checksum/signature input:\n%s", asset, checksumOutput)
}
}
mcpPowerShell, err := os.ReadFile(repoFile("scripts", "install-mcp.ps1"))
if err != nil {
t.Fatalf("read install-mcp.ps1: %v", err)
}
for _, needle := range []string{
`function Resolve-Architecture`,
`$PinnedReleaseSshPublicKey`,
`checksums.txt`,
`checksums.txt.sshsig`,
`Assert-ChecksumManifestSignature`,
`Get-FileHash -Path $tmp -Algorithm SHA256`,
`sha256 mismatch`,
} {
if !strings.Contains(string(mcpPowerShell), needle) {
t.Fatalf("install-mcp.ps1 missing required helper or guard: %s", needle)
}
}
}
// The release-pipeline downstream workflows and private Pro publication path
// share one customer boundary. Exact-version artifacts are staged behind a
// draft, verified, and only then activated; GitHub publication is the final
// notification rather than the trigger for a long tail of publication work.
// The tests below pin that barrier so the staggered-release regression class
// cannot return.
func TestInstallShSmokeWorkflowPresent(t *testing.T) {
workflowPath := repoFile(".github", "workflows", "install-sh-smoke-body.yml")
assertFileContainsAll(t, workflowPath,
// Inputs and triggers.
`name: install.sh Smoke Body (Caller Permissions)`,
`workflow_call:`,
`asset_source:`,
`release_id:`,
// Staged cuts use authenticated draft assets; manual verification can
// still pull from the public release URL.
`repos/${REPO}/releases/${RELEASE_ID}/assets?per_page=100`,
`repos/${REPO}/releases/assets/${asset_id}`,
`Accept: application/octet-stream`,
`releases/download/${TAG}`,
`install.sh.sshsig`,
`pulse-${TAG}-linux-amd64.tar.gz`,
// README key extraction + ssh-keygen verify against the asset.
`grep -oE 'ssh-ed25519 [A-Za-z0-9+/=]+ pulse-installer' README.md`,
`ssh-keygen -Y verify \`,
`-I pulse-installer \`,
`-n pulse-install \`,
`-s install.sh.sshsig < install.sh`,
// Server-installer identity assertions, mirroring validate-release.sh.
`grep -qE '^# Pulse Installer Script' install.sh`,
`grep -q 'Pulse Unified Agent Installer' install.sh`,
`grep -qE '^[[:space:]]*--version\)' install.sh`,
// End-to-end install in a privileged systemd container.
`jrei/systemd-debian:12@sha256:61d70dc3e574337bd9df794674a60ae73113460fff16ab41a2d234b4a11dcd98`,
`bash install.sh --archive /smoke/${tarball} --disable-auto-updates`,
`systemctl is-active pulse`,
// curl --retry handles its own poll loop instead of a bash for-loop.
`--retry 30 --retry-delay 2 --retry-connrefused --retry-all-errors http://127.0.0.1:7655/api/health`,
// Authoritative version check via /api/version (not /api/health).
`curl -fsS http://127.0.0.1:7655/api/version`,
`Installed version mismatch. Expected`,
)
workflowBytes, err := os.ReadFile(workflowPath)
if err != nil {
t.Fatal(err)
}
if strings.Contains(string(workflowBytes), "permissions:") {
t.Fatal("shared smoke body must inherit its caller budget, not request elevated permissions")
}
assertFileContainsAll(t, repoFile(".github", "workflows", "install-sh-smoke.yml"),
`workflow_dispatch:`,
`workflow_call:`,
`contents: write`,
`uses: ./.github/workflows/install-sh-smoke-body.yml`,
`release_id: ${{ inputs.release_id }}`,
)
}
func TestStableInstallContinuityReinstallsLatestReleaseReadOnly(t *testing.T) {
workflowPath := repoFile(".github", "workflows", "stable-install-continuity.yml")
assertFileContainsAll(t, workflowPath,
`name: Stable Install Continuity`,
`schedule:`,
`cron: '47 4 * * 3'`,
`workflow_dispatch:`,
`contents: read`,
`"repos/${REPOSITORY}/releases/latest"`,
`scripts/release_control/release_continuity.py release`,
`version=${tag#v}`,
`uses: ./.github/workflows/install-sh-smoke-body.yml`,
`tag: ${{ needs.resolve.outputs.tag }}`,
`version: ${{ needs.resolve.outputs.version }}`,
`asset_source: published`,
)
workflowBytes, err := os.ReadFile(workflowPath)
if err != nil {
t.Fatalf("read stable install continuity workflow: %v", err)
}
workflow := string(workflowBytes)
if strings.Contains(workflow, "contents: write") {
t.Fatal("stable install continuity must remain read-only")
}
assertFileContainsAll(t, repoFile(".github", "workflows", "README.md"),
`stable-install-continuity.yml`,
`weekly reinstall of the advertised stable release`,
`read-only token`,
`digest-pinned`,
)
}
func TestPromoteFloatingTagsReachableViaWorkflowCall(t *testing.T) {
workflowPath := repoFile(".github", "workflows", "promote-floating-tags.yml")
assertFileContainsAll(t, workflowPath,
`workflow_call:`,
`tag:`,
`description: "Release tag (e.g., v6.0.0). Required for workflow_call."`,
`prerelease:`,
`type: boolean`,
`TAG="${INPUT_TAG}"`,
`Require activated GitHub release`,
`gh release view "${TAG}" --json isDraft,publishedAt,tagName`,
`Floating-tag promotion refuses inactive release ${TAG}.`,
`for image in pulse pulse-control-plane; do`,
`"rcourtman/${image}:rc"`,
`"ghcr.io/${OWNER}/${image}:latest"`,
)
content, err := os.ReadFile(workflowPath)
if err != nil {
t.Fatalf("read promote-floating-tags.yml: %v", err)
}
if strings.Contains(string(content), "workflow_run:") {
t.Fatal("floating aliases must have one explicit activation owner, not an implicit workflow_run trigger")
}
publishBytes, err := os.ReadFile(repoFile(".github", "workflows", "publish-docker.yml"))
if err != nil {
t.Fatalf("read publish-docker.yml: %v", err)
}
publishWorkflow := string(publishBytes)
for _, mutableTag := range []string{
`rcourtman/pulse:latest`,
`ghcr.io/{0}/pulse:latest`,
`rcourtman/pulse-control-plane:latest`,
`ghcr.io/{0}/pulse-control-plane:latest`,
} {
if strings.Contains(publishWorkflow, mutableTag) {
t.Fatalf("publish-docker.yml must stage exact-version images without moving mutable alias %q", mutableTag)
}
}
}
func TestPublishHelmChartReachableViaWorkflowCall(t *testing.T) {
workflowPath := repoFile(".github", "workflows", "publish-helm-chart.yml")
assertFileContainsAll(t, workflowPath,
`workflow_call:`,
`chart_version:`,
`description: "Chart version (e.g., 6.0.0-rc.5). Required for workflow_call."`,
`required: true`,
`type: string`,
`app_version:`,
// Chart-version resolver prefers inputs over release-event tag.
`if [ -n "${INPUT_CHART_VERSION}" ]; then`,
`RELEASE_TAG="${RELEASE_TAG_NAME}"`,
`name: Verify public GHCR chart identity and provenance`,
`helm registry logout ghcr.io || true`,
`name: Authenticate OCI attestation client with GHCR`,
`uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0`,
`registry: ghcr.io`,
`username: ${{ github.actor }}`,
`password: ${{ github.token }}`,
`uses: actions/attest@`,
`subject-digest: ${{ steps.push.outputs.chart_digest }}`,
`./scripts/verify-release-helm-chart.sh`,
`value: ${{ jobs.publish.outputs.chart_digest }}`,
`chart_digest: ${{ steps.proof.outputs.chart_digest }}`,
)
content, err := os.ReadFile(workflowPath)
if err != nil {
t.Fatalf("read publish-helm-chart.yml: %v", err)
}
workflow := string(content)
attestationLogin := strings.Index(workflow, "- name: Authenticate OCI attestation client with GHCR")
chartPush := strings.Index(workflow, "- name: Push exact-version chart to GHCR")
attestation := strings.Index(workflow, "- name: Attest exact-version OCI chart")
if !(attestationLogin < chartPush && chartPush < attestation) {
t.Fatal("publish-helm-chart.yml must authenticate the OCI attestation client before pushing and attesting the chart")
}
// The chart-version resolver writes its outputs through
// scripts/write_github_output.py, so the repository must already be
// checked out when it runs (de41ea1883 broke every chart publish this way).
checkout := strings.Index(workflow, "- name: Checkout repository")
chartVersion := strings.Index(workflow, "- name: Determine chart version")
if !(checkout >= 0 && chartVersion > checkout) {
t.Fatal("publish-helm-chart.yml must check out the repository before the chart-version resolver runs scripts/write_github_output.py")
}
for _, forbidden := range []string{
`versions/latest/restore`,
`-f visibility=public`,
`Package visibility configuration attempted`,
} {
if strings.Contains(workflow, forbidden) {
t.Fatalf("publish-helm-chart.yml must verify chart readability instead of masking GHCR visibility API failures; found %q", forbidden)
}
}
}
func TestReleasePipelinePromotesOneImmutableCandidate(t *testing.T) {
createBytes, err := os.ReadFile(repoFile(".github", "workflows", "create-release.yml"))
if err != nil {
t.Fatalf("read create-release.yml: %v", err)
}
candidateBytes, err := os.ReadFile(repoFile(".github", "workflows", "build-release-candidate.yml"))
if err != nil {
t.Fatalf("read build-release-candidate.yml: %v", err)
}
compilerBytes, err := os.ReadFile(repoFile(".github", "workflows", "compile-release-payload.yml"))
if err != nil {
t.Fatalf("read compile-release-payload.yml: %v", err)
}
validationBytes, err := os.ReadFile(repoFile(".github", "workflows", "validate-release-assets.yml"))
if err != nil {
t.Fatalf("read validate-release-assets.yml: %v", err)
}
convergenceBytes, err := os.ReadFile(repoFile(".github", "workflows", "release-convergence.yml"))
if err != nil {
t.Fatalf("read release-convergence.yml: %v", err)
}
recoveryBytes, err := os.ReadFile(repoFile(".github", "workflows", "recover-release-activation.yml"))
if err != nil {
t.Fatalf("read recover-release-activation.yml: %v", err)
}
leaseScriptBytes, err := os.ReadFile(repoFile("scripts", "release_control", "customer_promotion_lease.sh"))
if err != nil {
t.Fatalf("read customer_promotion_lease.sh: %v", err)
}
createWorkflow := string(createBytes)
candidateWorkflow := string(candidateBytes)
compilerWorkflow := string(compilerBytes)
compileScriptBytes, err := os.ReadFile(repoFile("scripts", "build-release-binaries.sh"))
if err != nil {
t.Fatalf("read build-release-binaries.sh: %v", err)
}
compileScript := string(compileScriptBytes)
validationWorkflow := string(validationBytes)
convergenceWorkflow := string(convergenceBytes)
recoveryWorkflow := string(recoveryBytes)
leaseScript := string(leaseScriptBytes)
for _, needle := range []string{
`"repos/${GITHUB_REPOSITORY}/git/refs"`,
`Bootstrapped absent customer-promotion lease ref`,
`git push --atomic origin`,
`"${lock_commit}:${owner_ref}"`,
} {
if !strings.Contains(leaseScript, needle) {
t.Fatalf("customer-promotion lease missing absent-ref bootstrap contract: %s", needle)
}
}
createJob := workflowJobBlock(t, createWorkflow, "create_release")
prepareJob := workflowJobBlock(t, createWorkflow, "prepare")
publicationPreflightJob := workflowJobBlock(t, createWorkflow, "publication_trust_preflight")
frontendBundleJob := workflowJobBlock(t, createWorkflow, "frontend_bundle")
backendJob := workflowJobBlock(t, createWorkflow, "backend_tests")
integrationJob := workflowJobBlock(t, createWorkflow, "integration_tests")
validationJob := workflowJobBlock(t, createWorkflow, "validate_release_assets")
privateStageJob := workflowJobBlock(t, createWorkflow, "stage_private_pro_runtime")
qualificationJob := workflowJobBlock(t, createWorkflow, "candidate_qualification")
readinessJob := workflowJobBlock(t, createWorkflow, "release_readiness")
dispatchJob := workflowJobBlock(t, createWorkflow, "dispatch_release_convergence")
activationJob := workflowJobBlock(t, createWorkflow, "activate_release")
commitVerdictJob := workflowJobBlock(t, createWorkflow, "release_commit_verdict")
leaseJob := workflowJobBlock(t, convergenceWorkflow, "acquire_customer_promotion_lease")
privatePromotionJob := workflowJobBlock(t, convergenceWorkflow, "promote_private_pro_runtime")
floatingJob := workflowJobBlock(t, convergenceWorkflow, "promote_floating_tags")
helmPagesJob := workflowJobBlock(t, convergenceWorkflow, "publish_helm_pages")
demoJob := workflowJobBlock(t, convergenceWorkflow, "update_stable_demo")
compileJob := workflowJobBlock(t, compilerWorkflow, "compile-release-payload")
obtainPayloadJob := workflowJobBlock(t, candidateWorkflow, "obtain-release-payload")
candidateBuildJob := workflowJobBlock(t, candidateWorkflow, "build")
compiledPayloadVerificationStep := workflowStepBlock(t, candidateBuildJob, "Verify exact-SHA compiled payload")
if !strings.Contains(prepareJob, "runs-on: ubuntu-24.04") ||
strings.Contains(prepareJob, "self-hosted") ||
strings.Contains(prepareJob, "pulse-pve-compile") ||
strings.Contains(prepareJob, "contains(inputs.version") {
t.Fatal("release preparation must use a fresh hosted VM for every channel")
}
if strings.Contains(prepareJob, "sparse-checkout") {
t.Fatal("release preparation must use the complete admitted source")
}
for _, needle := range []string{
`runs-on: ubuntu-24.04`,
`GH_TOKEN: ${{ secrets.WORKFLOW_PAT }}`,
`./scripts/check-github-release-immutability.sh "${GITHUB_REPOSITORY}"`,
`github.event.inputs.draft_only != 'true'`,
`historical_asset_backfill_only != 'true'`,
} {
if !strings.Contains(publicationPreflightJob, needle) {
t.Fatalf("publication trust preflight missing early immutable-setting contract: %s", needle)
}
}
for label, job := range map[string]string{
"release candidate": workflowJobBlock(t, createWorkflow, "build_release_candidate"),
"frontend bundle": frontendBundleJob,
"frontend checks": workflowJobBlock(t, createWorkflow, "frontend_checks"),
"Windows smoke": workflowJobBlock(t, createWorkflow, "windows_install_command_smoke"),
"release-note visuals": workflowJobBlock(t, createWorkflow, "release_note_visuals"),
"private Pro staging": privateStageJob,
} {
if !strings.Contains(job, "- publication_trust_preflight") {
t.Fatalf("%s must wait for publication trust preflight", label)
}
}
for _, needle := range []string{
`runs-on: ubuntu-24.04`,
`Compile Exact-SHA Release Payload on Ephemeral VM`,
`GITHUB_WORKFLOW_SHA`,
`ref: ${{ inputs.source_sha }}`,
`PULSE_RELEASE_BUILD_JOBS: "2"`,
`VERSION: ${{ inputs.version }}`,
`./scripts/build-release-binaries.sh "${VERSION}" "$RUNNER_TEMP/release-compiled"`,
`release-compiled-${{ inputs.source_sha }}-${{ inputs.version }}-${{ inputs.request_id }}`,
} {
if !strings.Contains(compileJob, needle) {
t.Fatalf("compiled release payload job missing exact-SHA contract: %s", needle)
}
}
if strings.Contains(compileJob, "self-hosted") || strings.Contains(compileJob, "pulse-pve-") {
t.Fatal("release payload compilation must stay on an ephemeral GitHub-hosted runner")
}
if strings.Contains(compileJob, "PULSE_UPDATE_SIGNING_KEY") {
t.Fatal("release compilation job must not receive private update-signing material")
}
if !strings.Contains(compiledPayloadVerificationStep, `VERSION: ${{ inputs.version }}`) {
t.Fatal("exact-SHA compiled payload verification must bind the requested release version")
}
if strings.Contains(candidateWorkflow, `runs-on: ${{ fromJSON('["self-hosted"`) {
t.Fatal("SignPath release workflow must not contain a self-hosted runner job")
}
for _, needle := range []string{
`runs-on: ubuntu-24.04`,
`actions: write`,
`return_run_details: true`,
`actions/workflows/compile-release-payload.yml/dispatches`,
`X-GitHub-Api-Version: 2026-03-10`,
`compiler_run_id: ${{ steps.dispatch.outputs.compiler_run_id }}`,
`.path == ".github/workflows/compile-release-payload.yml"`,
} {
if !strings.Contains(obtainPayloadJob, needle) {
t.Fatalf("hosted compiler handoff job missing isolated-workflow contract: %s", needle)
}
}
for label, job := range map[string]string{
"frontend bundle": frontendBundleJob,
"backend tests": backendJob,
} {
if !strings.Contains(job, "runs-on: ubuntu-24.04") || strings.Contains(job, "self-hosted") {
t.Fatalf("%s must use a fresh hosted VM for every channel", label)
}
if strings.Contains(job, "require_windows_signing") || strings.Contains(job, "unsigned_windows_exception") {
t.Fatalf("%s runner selection must not depend on the Windows-signing decision", label)
}
}
if !strings.Contains(compileJob, "cache: false") || strings.Contains(compileJob, "cache: 'npm'") {
t.Fatal("release compilation must avoid Actions cache archival")
}
if strings.Contains(frontendBundleJob, "cache: 'npm'") {
t.Fatal("frontend bundle must avoid restoring an Actions npm cache")
}
if !strings.Contains(backendJob, "cache: false") {
t.Fatal("backend qualification must keep Actions Go caching disabled")
}
for _, needle := range []string{
`scripts/release_candidate_manifest.py create`,
`--source-sha "${SOURCE_SHA}"`,
`--release-dir "${PAYLOAD_DIR}"`,
} {
if !strings.Contains(compileScript, needle) {
t.Fatalf("compiled release payload script missing manifest binding: %s", needle)
}
}
for _, needle := range []string{
`needs.obtain-release-payload.result == 'success'`,
`actions: read`,
`EXPECTED_ARTIFACT_ID: ${{ needs.obtain-release-payload.outputs.artifact_id }}`,
`EXPECTED_ARTIFACT_DIGEST: ${{ needs.obtain-release-payload.outputs.artifact_digest }}`,
`EXPECTED_COMPILER_RUN_ID: ${{ needs.obtain-release-payload.outputs.compiler_run_id }}`,
`actions/artifacts/${EXPECTED_ARTIFACT_ID}`,
`.workflow_run.head_sha == $source_sha`,
`sha256sum --check --`,
`scripts/release_candidate_manifest.py verify-local`,
`compiled-payload-verification.json`,
`separate-ephemeral-github-hosted-compiler-workflow`,
`PULSE_RELEASE_COMPILED_PAYLOAD_DIR`,
} {
if !strings.Contains(candidateBuildJob, needle) {
t.Fatalf("hosted candidate build missing compiled-payload verification: %s", needle)
}
}
for _, needle := range []string{
`VERSION: ${{ inputs.version }}`,
`./scripts/build-release.sh "${VERSION}"`,
`scripts/validate-release.sh "${VERSION}" --skip-docker`,
`scripts/release_candidate_manifest.py create`,
`compression-level: 0`,
`retention-days: 1`,
} {
if !strings.Contains(candidateWorkflow, needle) {
t.Fatalf("build-release-candidate.yml missing single-build contract: %s", needle)
}
}
for _, jobName := range []string{"publish_release_tag", "publish_docker", "publish_helm_chart"} {
job := workflowJobBlock(t, createWorkflow, jobName)
if !strings.Contains(job, "- candidate_qualification") ||
!strings.Contains(job, "needs.candidate_qualification.result == 'success'") {
t.Fatalf("public writer %s must require successful candidate qualification", jobName)
}
}
publishDockerJob := workflowJobBlock(t, createWorkflow, "publish_docker")
for _, needle := range []string{
"- build_release_candidate",
"- create_release",
"- publish_release_tag",
"needs.create_release.result == 'success'",
"needs.publish_release_tag.result == 'success'",
`source_sha: ${{ github.sha }}`,
} {
if !strings.Contains(publishDockerJob, needle) {
t.Fatalf("qualified Docker publication missing exact-source dependency contract: %s", needle)
}
}
if strings.Contains(createJob, "git push") || strings.Contains(createWorkflow, `git push origin "refs/tags/${TAG}" --force`) {
t.Fatal("restricted draft staging must not publish or rewrite a public tag")
}
for _, needle := range []string{
`Download immutable release candidate`,
`scripts/release_candidate_manifest.py verify-local`,
`needs.build_release_candidate.outputs.artifact_name`,
} {
if !strings.Contains(createJob, needle) {
t.Fatalf("create_release missing candidate promotion contract: %s", needle)
}
}
if strings.Contains(createJob, "scripts/build-release.sh") {
t.Fatal("create_release must promote the verified candidate instead of rebuilding release assets")
}
if !strings.Contains(backendJob, "- frontend_bundle") || !strings.Contains(integrationJob, "- frontend_bundle") {
t.Fatal("backend and integration jobs must consume the shared verified frontend bundle")
}
if strings.Contains(integrationJob, "- backend_tests") {
t.Fatal("integration tests must run in parallel with backend tests")
}
if !strings.Contains(integrationJob, `tests/66-organization-sharing-approval-ui.spec.ts`) {
t.Fatal("integration release gate missing current organization-sharing coverage")
}
if strings.Contains(integrationJob, `tests/03-multi-tenant.spec.ts`) {
t.Fatal("integration release gate must not target the quarantined multi-tenant spec")
}
if strings.Contains(validationJob, "- publish_docker") {
t.Fatal("release asset digest validation must not depend on public Docker publication")
}
if !strings.Contains(privateStageJob, "- prepare") ||
strings.Contains(privateStageJob, "- create_release") ||
strings.Contains(privateStageJob, "- validate_release_assets") {
t.Fatal("inert private Pro staging must start after preparation without waiting for public qualification or draft creation")
}
for _, needle := range []string{
`--arg pulse_checkout_ref "${GITHUB_SHA}"`,
`pulse_checkout_ref: $pulse_checkout_ref`,
`allow_pre_activation_staging: "true"`,
} {
if !strings.Contains(privateStageJob, needle) {
t.Fatalf("private Pro pre-activation staging missing exact-SHA contract: %s", needle)
}
}
for _, dependency := range []string{
"publication_trust_preflight", "create_release", "validate_release_assets",
"install_sh_smoke", "stage_private_pro_runtime",
} {
if !strings.Contains(qualificationJob, "- "+dependency) ||
!strings.Contains(qualificationJob, "needs."+dependency+".result == 'success'") {
t.Fatalf("candidate qualification must require successful %s", dependency)
}
}
for _, dependency := range []string{
"candidate_qualification", "publish_release_tag", "publish_docker", "publish_helm_chart",
} {
if !strings.Contains(readinessJob, "- "+dependency) ||
!strings.Contains(readinessJob, "needs."+dependency+".result == 'success'") {
t.Fatalf("release readiness must require successful %s", dependency)
}
}
if !strings.Contains(commitVerdictJob, "- publication_trust_preflight") ||
!strings.Contains(commitVerdictJob, `require_result "publication trust preflight"`) {
t.Fatal("release commit verdict must surface publication trust preflight failure")
}
for _, forbiddenDependency := range []string{
"- publish_helm_pages",
"- promote_floating_tags",
"- promote_private_pro_runtime",
"- update_stable_demo",
} {
if strings.Contains(readinessJob, forbiddenDependency) {
t.Fatalf("immutable readiness must exclude mutable customer state: %s", forbiddenDependency)
}
}
for _, dependency := range []string{"- create_release", "- stage_private_pro_runtime"} {
if !strings.Contains(dispatchJob, dependency) {
t.Fatalf("durable convergence dispatch missing staged dependency: %s", dependency)
}
}
if strings.Contains(dispatchJob, "- release_readiness") {
t.Fatal("durable convergence dispatch must prewarm before the readiness join")
}
if !strings.Contains(dispatchJob, "github.event.inputs.draft_only != 'true'") ||
!strings.Contains(dispatchJob, "historical_asset_backfill_only != 'true'") {
t.Fatal("early convergence dispatch must remain disabled for inert release modes")
}
if !strings.Contains(convergenceWorkflow, `gh run view "${EXPECTED_SOURCE_RUN_ID}"`) ||
!strings.Contains(convergenceWorkflow, "completed without the exact activation marker") ||
!strings.Contains(convergenceWorkflow, `select(.name == "release-activation.json") | .state`) ||
!strings.Contains(convergenceWorkflow, "uploaded but not publicly readable yet") {
t.Fatal("prewarmed convergence must terminate when its source run ends without activation")
}
if !strings.Contains(activationJob, "- dispatch_release_convergence") {
t.Fatal("release activation must depend on the exact durable convergence dispatch")
}
for _, forbiddenDependency := range []string{
"- update_stable_demo",
"- promote_floating_tags",
"- promote_private_pro_runtime",
} {
if strings.Contains(activationJob, forbiddenDependency) {
t.Fatalf("release activation must precede mutable customer state: %s", forbiddenDependency)
}
}
if !strings.Contains(activationJob, `'{draft: false, make_latest: $make_latest}'`) {
t.Fatal("release activation must be the job that crosses the draft publication boundary")
}
for _, needle := range []string{
`release-activation.json`,
`require_viable_convergence_owner`,
`validate_existing_activation_commit`,
`Recover release activation ${TAG} source ${GITHUB_RUN_ID}`,
`.path == ".github/workflows/recover-release-activation.yml"`,
`.path == ".github/workflows/release-convergence.yml"`,
`.status == "completed" and .conclusion == "success"`,
`continue-on-error: true`,
`Resuming quarantined activation for ${TAG}`,
`[ "$activation_committed" = "true" ] ||`,
`--repo "${GITHUB_REPOSITORY}"`,
} {
if !strings.Contains(activationJob, needle) {
t.Fatalf("release activation missing irreversible handoff contract: %s", needle)
}
}
if strings.Contains(activationJob, `[ -n "$published_at" ] ||`) {
t.Fatal("release activation must allow retrying a current draft when GitHub retains historical published_at metadata")
}
if strings.Contains(activationJob, `--clobber`) &&
!strings.Contains(activationJob, `already committed by successful recovery run`) {
t.Fatal("release activation reruns must recognize a qualified recovery before considering marker replacement")
}
if !strings.Contains(createJob, `Resuming quarantined draft release for ${TAG}`) {
t.Fatal("release creation must explicitly support resuming a quarantined draft")
}
if !strings.Contains(createJob, `write_github_output.py release_activation_committed "${RELEASE_ACTIVATION_COMMITTED}"`) ||
!strings.Contains(createJob, `[ "$ACTIVATION_COMMITTED" != "true" ]`) {
t.Fatal("release creation must refuse to retarget a draft whose irreversible activation marker exists")
}
if strings.Contains(createJob, `[ -z "$EXISTING_RELEASE_PUBLISHED_AT" ]`) ||
strings.Contains(createJob, `[ -z "$PUBLISHED_AT" ]`) {
t.Fatal("release creation must not mistake historical published_at metadata for current public visibility")
}
for _, needle := range []string{
`.path == ".github/workflows/create-release.yml"`,
`release-candidate-manifest-${source_sha}-${version}`,
`scripts/release_candidate_manifest.py verify-release`,
`--release-body-file "${release_body}"`,
`failure outside the recoverable activation boundary`,
`release-convergence.yml/dispatches`,
`activation_recovery_run_id`,
`for attempt in $(seq 1 12)`,
`waiting for GitHub indexing`,
`--repo "${GITHUB_REPOSITORY}"`,
} {
if !strings.Contains(recoveryWorkflow, needle) {
t.Fatalf("activation-only recovery missing qualified reuse contract: %s", needle)
}
}
if strings.Contains(recoveryWorkflow, `scripts/build-release.sh`) ||
strings.Contains(recoveryWorkflow, `build-release-candidate.yml`) {
t.Fatal("activation-only recovery must never rebuild the qualified candidate")
}
for _, needle := range []string{`sort -Vr`, `superseded=true`} {
if !strings.Contains(leaseJob, needle) {
t.Fatalf("global customer-promotion lease missing monotonicity contract: %s", needle)
}
}
for _, needle := range []string{`refs/heads/release-customer-promotion-lock`, `--force-with-lease="${LOCK_REF}:${observed_sha}"`} {
if !strings.Contains(leaseScript, needle) {
t.Fatalf("shared global customer-promotion lease helper missing contract: %s", needle)
}
}
for jobName, jobBlock := range map[string]string{
"floating-tag promotion": floatingJob,
"private Pro live promotion": privatePromotionJob,
"stable demo deployment": demoJob,
} {
if !strings.Contains(jobBlock, `needs: acquire_customer_promotion_lease`) ||
!strings.Contains(jobBlock, `needs.acquire_customer_promotion_lease.outputs.superseded != 'true'`) {
t.Fatalf("%s must run under the monotonic global promotion lease", jobName)
}
}
if !strings.Contains(helmPagesJob, `needs: acquire_customer_promotion_lease`) ||
strings.Contains(helmPagesJob, `superseded != 'true'`) {
t.Fatal("additive Helm Pages indexing must run under the lease for every committed release")
}
if strings.Contains(demoJob, "release_id:") {
t.Fatal("stable demo must use activated public assets, not an unpublished release id")
}
for _, needle := range []string{
`inputs.candidate_manifest_artifact != ''`,
`scripts/release_candidate_manifest.py verify-release`,
`--release-body-file "$RUNNER_TEMP/release-body.md"`,
`VALIDATION_EXIT_CODE=${PIPESTATUS[0]}`,
`inputs.candidate_manifest_artifact == ''`,
} {
if !strings.Contains(validationWorkflow, needle) {
t.Fatalf("validate-release-assets.yml missing fast digest contract: %s", needle)
}
}
}
func TestReleaseTrainCITriggersIncludeBuildAndE2E(t *testing.T) {
for _, workflow := range []string{"build-and-test.yml", "test-e2e.yml"} {
content, err := os.ReadFile(repoFile(".github", "workflows", workflow))
if err != nil {
t.Fatal(err)
}
for _, event := range []string{"push", "pull_request"} {
t.Run(workflow+"/"+event, func(t *testing.T) {
// Limit the assertion to this event's branch list, not another
// trigger or a job comment mentioning the same pattern.
expression := regexp.MustCompile(`(?m)^ ` + event + `:\n branches:\n((?: - [^\n]+\n)+)`)
match := expression.FindStringSubmatch(string(content))
if len(match) != 2 || !strings.Contains(match[1], " - 'release/v*'\n") || !strings.Contains(match[1], " - main\n") {
t.Fatal("CI must admit main and release/v* branches for this event")
}
})
}
}
}
func TestProviderPairDockerProofRunsBeforeFreezeAndOnExactCandidate(t *testing.T) {
const testName = "TestIntegrationProviderPairNetworkIsolation"
const helperImage = "alpine:3.24@sha256:294b683cb724975bec92580e1e685676bd4b50bda910ddb8c51d4cabeaec77e6"
const liveFlag = "PULSE_RUN_PROVIDER_PAIR_DOCKER_INTEGRATION: '1'"
const requiredPass = "grep -q '^--- PASS: " + testName + " '"
ciBytes, err := os.ReadFile(repoFile(".github", "workflows", "build-and-test.yml"))
if err != nil {
t.Fatal(err)
}
branchJob := workflowJobBlock(t, string(ciBytes), "provider-pair-docker")
for _, required := range []string{
"github.event_name == 'push' && startsWith(github.ref, 'refs/heads/release/')",
"needs.changes.outputs.code == 'true'",
"runs-on: ubuntu-24.04",
"ref: ${{ github.sha }}",
"persist-credentials: false",
"EXPECTED_SOURCE_SHA: ${{ github.sha }}",
`test "$(git rev-parse HEAD)" = "$EXPECTED_SOURCE_SHA"`,
"docker pull '" + helperImage + "'",
liveFlag,
"PULSE_DOCKER_INTEGRATION_IMAGE: " + helperImage,
"-run '^" + testName + "$' -v",
requiredPass,
} {
if !strings.Contains(branchJob, required) {
t.Fatalf("release-line provider pair job missing %q", required)
}
}
if strings.Index(branchJob, "Verify exact release-line source") > strings.Index(branchJob, "Prove provider pair provisioning and cleanup") {
t.Fatal("provider pair check ran before exact release-line source verification")
}
qualifiedBytes, err := os.ReadFile(repoFile(".github", "workflows", "qualify-release-containers.yml"))
if err != nil {
t.Fatal(err)
}
qualifiedJob := workflowJobBlock(t, string(qualifiedBytes), "qualify")
for _, required := range []string{
"ref: ${{ github.sha }}",
"persist-credentials: false",
"--source-sha \"${{ github.sha }}\"",
"Verify container binaries match immutable candidate",
liveFlag,
"PULSE_DOCKER_INTEGRATION_IMAGE: pulse-control-plane-candidate:${{ inputs.version }}",
"-run '^" + testName + "$' -v",
requiredPass,
} {
if !strings.Contains(qualifiedJob, required) {
t.Fatalf("exact-candidate provider pair job missing %q", required)
}
}
verify := strings.Index(qualifiedJob, "Verify container binaries match immutable candidate")
pair := strings.Index(qualifiedJob, "Verify two-provider network isolation on live Docker")
helm := strings.Index(qualifiedJob, "Helm smoke test with local release-line image")
if verify < 0 || pair <= verify || helm <= pair {
t.Fatal("live provider pair check must follow payload digest verification and precede Helm smoke")
}
assertFileContainsAll(t, repoFile("internal", "cloudcp", "docker", "manager_integration_test.go"),
"func "+testName+"(t *testing.T)",
`os.Getenv("PULSE_RUN_PROVIDER_PAIR_DOCKER_INTEGRATION") != "1"`,
"provider A adopted an unowned same-name network",
"A cleanup removed B tenant network",
)
}
func TestBenchmarkQualificationRetainsProvenance(t *testing.T) {
content, err := os.ReadFile(repoFile(".github", "workflows", "build-and-test.yml"))
if err != nil {
t.Fatal(err)
}
job := workflowJobBlock(t, string(content), "benchmarks")
start := strings.Index(job, "- name: Upload benchmark evidence")
if start < 0 {
t.Fatal("missing benchmark evidence upload")
}
upload := job[start:]
for _, required := range []string{
"if: always()", "bench-baseline.txt", "bench-results.txt",
"bench-comparison.txt", "bench-metadata.txt",
} {
if !strings.Contains(upload, required) {
t.Fatalf("benchmark upload must retain %q even on failure", required)
}
}
if !strings.Contains(job, "bash scripts/check-bench-regression.sh bench-comparison.txt") {
t.Fatal("provenance must not replace the benchmark regression gate")
}
}
func TestBackendAPIShardsKeepRequiredCheckExhaustive(t *testing.T) {
content, err := os.ReadFile(repoFile(".github", "workflows", "build-and-test.yml"))
if err != nil {
t.Fatal(err)
}
workflow := string(content)
// Branch protection requires these exact check names. The rest shards stay
// matrix entries and internal/api keeps its name on the verdict job.
backend := workflowJobBlock(t, workflow, "backend")
for _, required := range []string{
"name: Backend tests (${{ matrix.shard }})",
"shard: [rest-0, rest-1]",
"grep -v '/internal/api$'",
"go test -race -timeout 50m $pkgs",
} {
if !strings.Contains(backend, required) {
t.Fatalf("backend rest shards missing %q", required)
}
}
shards := workflowJobBlock(t, workflow, "backend-api")
indexes := regexp.MustCompile(`(?m)^ index: \[([0-9, ]+)\]$`).FindStringSubmatch(shards)
count := regexp.MustCompile(`(?m)^ API_SHARD_COUNT: ([0-9]+)$`).FindStringSubmatch(shards)
if len(indexes) != 2 || len(count) != 2 {
t.Fatal("internal/api shard job must declare its index matrix and API_SHARD_COUNT")
}
want, _ := strconv.Atoi(count[1])
listed := strings.Split(indexes[1], ", ")
if want < 2 || len(listed) != want {
t.Fatalf("internal/api shard matrix %v must list exactly API_SHARD_COUNT=%d indexes", listed, want)
}
for i, value := range listed {
if value != strconv.Itoa(i) {
t.Fatalf("internal/api shard indexes must be 0..%d in order, got %v", want-1, listed)
}
}
for _, required := range []string{
"needs: changes",
"fail-fast: false",
// The list comes from the commit under test, so a new test cannot be
// missed, and contiguous slices of go test's own order put it in
// exactly one shard while keeping order-coupled neighbours together.
// The checked-in weights only choose where those slices are cut.
"go test -race -list . ./internal/api",
"bash .github/scripts/select-internal-api-shard.sh \\\n .github/scripts/internal-api-test-seconds.txt \"$API_SHARD_COUNT\" \"$API_SHARD_INDEX\")",
"resolved to an empty test list",
"go test -list found no tests in ./internal/api",
// A shard holding most tests is named by skipping every other
// shard's tests, which keeps its argument under the exec limit.
`others=$(printf '%s\n' "$tests" | grep -vxF -f <(printf '%s\n' "$selected") || true)`,
`filter=(-skip "$pattern")`,
`if [ "${#pattern}" -gt 120000 ]; then`,
// Every shard records per-test seconds on the runner through -json
// so the weights can be refreshed from CI, while pipefail keeps a
// go test failure fatal behind the recorder.
"set -euo pipefail",
`go test -race -timeout 50m -json "${filter[@]}" ./internal/api \
| python3 .github/scripts/record-internal-api-test-seconds.py "$timings/api-${API_SHARD_INDEX}.txt"`,
"if: always() && needs.changes.outputs.code == 'true'",
"name: internal-api-test-seconds-${{ matrix.index }}",
"path: ${{ runner.temp }}/internal-api-test-seconds/",
"PULSE_DATA_DIR: /tmp/pulse-test-data",
} {
if !strings.Contains(shards, required) {
t.Fatalf("internal/api shard job missing %q", required)
}
}
// The test binary caches one compiled pattern, so -run next to -skip
// recompiles the long skip pattern for every test and subtest.
if strings.Contains(shards, "-run . -skip") {
t.Fatal("internal/api shards must pass -skip alone; pairing it with -run recompiles the skip pattern per test")
}
if strings.Contains(shards, "sort") {
t.Fatal("internal/api shards must keep go test's run order; sorting splits order-coupled tests")
}
if strings.Contains(shards, "\n if:") {
t.Fatal("internal/api shards must expand for every change so the verdict never sees skipped shards")
}
verdict := workflowJobBlock(t, workflow, "backend-api-verdict")
for _, required := range []string{
"name: Backend tests (api)\n",
"needs: backend-api",
"if: always()",
"API_SHARDS_RESULT: ${{ needs.backend-api.result }}",
`if [ "$API_SHARDS_RESULT" != success ]; then`,
} {
if !strings.Contains(verdict, required) {
t.Fatalf("Backend tests (api) verdict missing %q", required)
}
}
if got := strings.Count(workflow, "name: Backend tests (api)\n"); got != 1 {
t.Fatalf("exactly one job may carry the required Backend tests (api) name, found %d", got)
}
assertInternalAPIShardSelectionExhaustive(t, want)
assertInternalAPITimingRecorderKeepsFailuresVisible(t)
}
// assertInternalAPITimingRecorderKeepsFailuresVisible feeds the -json
// recorder a passing, a failing and an unfinished test. Passing output must
// stay hidden like plain go test, failing and unfinished output must print,
// any failure must exit non-zero, and every finished top-level test must be
// written with its seconds.
func assertInternalAPITimingRecorderKeepsFailuresVisible(t *testing.T) {
t.Helper()
recorder := repoFile(".github", "scripts", "record-internal-api-test-seconds.py")
record := func(events string) (string, string, error) {
out := filepath.Join(t.TempDir(), "seconds.txt")
cmd := exec.Command("python3", recorder, out)
cmd.Stdin = strings.NewReader(events)
printed, err := cmd.Output()
written, readErr := os.ReadFile(out)
if readErr != nil {
t.Fatalf("recorder wrote no seconds file: %v", readErr)
}
return string(printed), string(written), err
}
const pkg = `"Package":"example/internal/api"`
passing := strings.Join([]string{
`{"Action":"run",` + pkg + `,"Test":"TestQuiet"}`,
`{"Action":"output",` + pkg + `,"Test":"TestQuiet","Output":"quiet log line\n"}`,
`{"Action":"pass",` + pkg + `,"Test":"TestQuiet","Elapsed":1.25}`,
`{"Action":"output",` + pkg + `,"Output":"ok \texample/internal/api\t2.000s\n"}`,
`{"Action":"pass",` + pkg + `,"Elapsed":2}`,
}, "\n") + "\n"
printed, written, err := record(passing)
if err != nil {
t.Fatalf("recorder must pass a passing run: %v", err)
}
if strings.Contains(printed, "quiet log line") || !strings.Contains(printed, "ok \texample/internal/api") {
t.Fatalf("recorder must print package lines and hide passing test output, printed %q", printed)
}
if written != "# package-seconds 2.00\nTestQuiet 1.25\n" {
t.Fatalf("recorder seconds file = %q", written)
}
failing := strings.Join([]string{
`{"Action":"run",` + pkg + `,"Test":"TestBroken"}`,
`{"Action":"output",` + pkg + `,"Test":"TestBroken/case","Output":"broken detail\n"}`,
`{"Action":"fail",` + pkg + `,"Test":"TestBroken/case","Elapsed":0.5}`,
`{"Action":"fail",` + pkg + `,"Test":"TestBroken","Elapsed":0.75}`,
`{"Action":"run",` + pkg + `,"Test":"TestHung"}`,
`{"Action":"output",` + pkg + `,"Test":"TestHung","Output":"panic: test timed out\n"}`,
}, "\n") + "\n"
printed, written, err = record(failing)
if err == nil {
t.Fatal("recorder must exit non-zero when a test fails or never finishes")
}
for _, want := range []string{"broken detail", "TestHung did not finish", "panic: test timed out"} {
if !strings.Contains(printed, want) {
t.Fatalf("recorder must print %q for failing or unfinished tests, printed %q", want, printed)
}
}
if written != "TestBroken 0.75\n" {
t.Fatalf("recorder must record only finished top-level tests, wrote %q", written)
}
}
// assertInternalAPIShardSelectionExhaustive runs the shard selector the way
// the workflow does and proves that, whatever the weights say, the shards are
// non-empty contiguous slices that together cover the list exactly once in
// order.
func assertInternalAPIShardSelectionExhaustive(t *testing.T, workflowShards int) {
t.Helper()
selector := repoFile(".github", "scripts", "select-internal-api-shard.sh")
weightsPath := repoFile(".github", "scripts", "internal-api-test-seconds.txt")
weightsContent, err := os.ReadFile(weightsPath)
if err != nil {
t.Fatal(err)
}
// Weighted names from the checked-in file interleaved with unknown ones,
// which stand in for tests added after the weights were measured.
var weighted []string
for _, line := range strings.Split(string(weightsContent), "\n") {
fields := strings.Fields(line)
if len(fields) == 0 || strings.HasPrefix(fields[0], "#") {
continue
}
if len(fields) != 2 || !(strings.HasPrefix(fields[0], "Test") || fields[0] == "DEFAULT_WEIGHT") {
t.Fatalf("internal/api weights line must be `<TestName> <seconds>` or `DEFAULT_WEIGHT <seconds>`, got %q", line)
}
if seconds, err := strconv.ParseFloat(fields[1], 64); err != nil || seconds <= 0 {
t.Fatalf("internal/api weight for %s must be positive seconds, got %q", fields[0], fields[1])
}
if fields[0] == "DEFAULT_WEIGHT" {
continue
}
weighted = append(weighted, fields[0])
}
if len(weighted) == 0 {
t.Fatal("internal/api weights file lists no tests")
}
var tests []string
for i, name := range weighted {
tests = append(tests, name)
for j := 0; j < 1+i%7; j++ {
tests = append(tests, "TestUnweightedShardProbe"+strconv.Itoa(i)+"x"+strconv.Itoa(j))
}
}
emptyWeights := filepath.Join(t.TempDir(), "empty.txt")
if err := os.WriteFile(emptyWeights, nil, 0o644); err != nil {
t.Fatal(err)
}
skewedWeights := filepath.Join(t.TempDir(), "skewed.txt")
skewed := "DEFAULT_WEIGHT 7.5\n" + tests[len(tests)/3] + " 900\n" + tests[len(tests)-1] + " 450\n"
if err := os.WriteFile(skewedWeights, []byte(skewed), 0o644); err != nil {
t.Fatal(err)
}
selectShard := func(weights string, count, index int, input []string) ([]string, error) {
cmd := exec.Command("bash", selector, weights, strconv.Itoa(count), strconv.Itoa(index))
cmd.Stdin = strings.NewReader(strings.Join(input, "\n") + "\n")
out, err := cmd.Output()
if err != nil {
return nil, err
}
return strings.Fields(string(out)), nil
}
for _, weights := range []string{weightsPath, emptyWeights, skewedWeights} {
for _, count := range []int{1, 2, workflowShards, workflowShards + 1, 9} {
var combined []string
for index := 0; index < count; index++ {
selected, err := selectShard(weights, count, index, tests)
if err != nil {
t.Fatalf("select shard %d of %d with %s: %v", index, count, filepath.Base(weights), err)
}
if len(selected) == 0 {
t.Fatalf("shard %d of %d with %s selected no tests", index, count, filepath.Base(weights))
}
combined = append(combined, selected...)
}
if strings.Join(combined, "\n") != strings.Join(tests, "\n") {
t.Fatalf("%d shards with %s do not cover the test list exactly once in order", count, filepath.Base(weights))
}
}
}
if _, err := selectShard(weightsPath, workflowShards, workflowShards, tests); err == nil {
t.Fatal("shard selector must reject an index outside the shard count")
}
if _, err := selectShard(weightsPath, 3, 0, tests[:2]); err == nil {
t.Fatal("shard selector must fail when there are fewer tests than shards")
}
}
func TestFrontendDependencySecurityAuditsAreRequired(t *testing.T) {
workflowPath := repoFile(".github", "workflows", "build-and-test.yml")
assertFileContainsAll(t, workflowPath,
`run: npm ci --no-audit`,
`- name: Audit complete frontend dependency graph`,
`npm-audit-retry.sh" all`,
// The runner may retry an unreachable advisory endpoint, but only a
// change that leaves the dependency graph untouched may proceed
// without a fresh result.
`NPM_AUDIT_REQUIRE_RESULT: ${{ needs.changes.outputs.frontend_deps }}`,
`frontend_deps: ${{ steps.filter.outputs.frontend_deps }}`,
`continue-on-error: true`,
`- name: Require frontend dependency audit`,
`if: ${{ !cancelled() }}`,
`COMPLETE_AUDIT_RESULT: ${{ steps.audit-complete.outcome }}`,
)
// The production-only audit reports a subset of the complete audit's
// advisories and cannot gate anything the complete audit did not already
// fail on, so it is off the per-pull-request path. It must still run
// somewhere: the scheduled scan owns the dev-versus-production split.
assertFileContainsAll(t, repoFile(".github", "workflows", "security-scan.yml"),
`- name: Audit production dependencies`,
`npm-audit-retry.sh" production --package-lock-only`,
`- name: Require dependency audits`,
`COMPLETE_AUDIT_RESULT: ${{ steps.audit-complete.outcome }}`,
`PRODUCTION_AUDIT_RESULT: ${{ steps.audit-production.outcome }}`,
)
// The gate itself must stay strict. An unreachable endpoint may be
// retried, but no severity threshold may be introduced that lets a real
// advisory through. Any positive package, severity or total evidence
// must fail, even when the summary is missing or contradictory.
runnerPath := repoFile("scripts", "npm-audit-retry.sh")
runner, err := os.ReadFile(runnerPath)
if err != nil {
t.Fatalf("read %s: %v", runnerPath, err)
}
if strings.Contains(string(runner), "--audit-level") {
t.Fatalf("%s must not weaken the audit with a severity threshold", runnerPath)
}
assertFileContainsAll(t, runnerPath,
`NPM_AUDIT_REQUIRE_RESULT:-true`,
`AUDIT_ARGS=("$@")`,
`if type(value) is int and value >= 0:`,
`has_findings = isinstance(findings, dict) and bool(findings)`,
`if has_findings or any(count > 0 for count in counts.values()):`,
`print("vulnerable")`,
`isinstance(report, dict) and not report.get("error")`,
`len(counts) == len(count_names) and all(count == 0 for count in counts.values())`,
`and ("vulnerabilities" not in report or isinstance(findings, dict))`,
)
// Retrying must be bounded by wall clock, not by attempt count alone.
// npm's own fetch-timeout defaults to five minutes and it retries
// internally, so three unbounded attempts once ran for 10m56s and
// cancelled the Frontend job with every test already passing.
assertFileContainsAll(t, runnerPath,
`NPM_AUDIT_MAX_SECONDS`,
`NPM_AUDIT_ATTEMPT_TIMEOUT`,
`export npm_config_fetch_retries=0`,
`DEADLINE=`,
)
// The job budget has to stay above the audit budget by a wide margin, or
// a stalled endpoint reappears as a cancelled job rather than a warning.
workflow, err := os.ReadFile(workflowPath)
if err != nil {
t.Fatalf("read %s: %v", workflowPath, err)
}
frontendJob := workflowJobBlock(t, string(workflow), "frontend")
if !strings.Contains(frontendJob, "timeout-minutes: 30") {
t.Fatal("frontend job must keep a bounded timeout above the audit budget")
}
}
func TestReleaseCutGatesCriticalFrontendAndWindowsRuntimeProof(t *testing.T) {
content, err := os.ReadFile(repoFile(".github", "workflows", "create-release.yml"))
if err != nil {
t.Fatalf("read create-release.yml: %v", err)
}
workflow := string(content)
frontendJob := workflowJobBlock(t, workflow, "frontend_checks")
windowsJob := workflowJobBlock(t, workflow, "windows_install_command_smoke")
smokeJob := workflowJobBlock(t, workflow, "release_smoke")
createJob := workflowJobBlock(t, workflow, "create_release")
qualificationJob := workflowJobBlock(t, workflow, "candidate_qualification")
verdictJob := workflowJobBlock(t, workflow, "release_commit_verdict")
for _, needle := range []string{
`npm --prefix frontend-modern run type-check`,
`npm --prefix frontend-modern test`,
} {
if !strings.Contains(frontendJob, needle) {
t.Fatalf("frontend release gate missing %s", needle)
}
}
for _, needle := range []string{
`runs-on: windows-2025`,
`agentInstallCommand.windows.test.ts`,
} {
if !strings.Contains(windowsJob, needle) {
t.Fatalf("Windows install-command release gate missing %s", needle)
}
}
for _, needle := range []string{
`tests/95-release-smoke.spec.ts`,
`release-smoke-failures-${{ github.sha }}`,
`tests/integration/test-results/`,
} {
if !strings.Contains(smokeJob, needle) {
t.Fatalf("release render smoke missing %s", needle)
}
}
if !strings.Contains(qualificationJob, `needs.windows_install_command_smoke.result == 'success'`) {
t.Fatal("candidate qualification must fail closed on the Windows install-command smoke")
}
if strings.Contains(createJob, `needs.windows_install_command_smoke.result`) {
t.Fatal("inert draft staging must overlap independent Windows qualification")
}
for _, result := range []string{
"needs.qualify_release_containers.result == 'success'",
"needs.frontend_checks.result == 'success'",
"needs.backend_tests.result == 'success'",
"needs.release_smoke.result == 'success'",
} {
if !strings.Contains(qualificationJob, result) {
t.Fatalf("candidate qualification missing required proof: %s", result)
}
if strings.Contains(createJob, result) {
t.Fatalf("inert draft staging must not serialize on deferred qualification: %s", result)
}
}
if !strings.Contains(workflow, "qualify_containers: false") ||
!strings.Contains(workflow, "uses: ./.github/workflows/qualify-release-containers.yml") {
t.Fatal("publishing release must qualify the candidate beside inert draft staging")
}
if !strings.Contains(verdictJob, `require_result "Windows install command smoke" "$WINDOWS_INSTALL_COMMAND_RESULT" success`) {
t.Fatal("release activation commit verdict must report the Windows install-command smoke")
}
}
func TestCreateReleasePublishesPrivateProRuntime(t *testing.T) {
content, err := os.ReadFile(repoFile(".github", "workflows", "create-release.yml"))
if err != nil {
t.Fatalf("read create-release.yml: %v", err)
}
workflow := string(content)
stageJob := workflowJobBlock(t, workflow, "stage_private_pro_runtime")
promotionContent, err := os.ReadFile(repoFile(".github", "workflows", "promote-private-pro-runtime.yml"))
if err != nil {
t.Fatalf("read promote-private-pro-runtime.yml: %v", err)
}
convergenceContent, err := os.ReadFile(repoFile(".github", "workflows", "release-convergence.yml"))
if err != nil {
t.Fatalf("read release-convergence.yml: %v", err)
}
promotionJob := workflowJobBlock(t, string(promotionContent), "promote")
convergencePromotionJob := workflowJobBlock(t, string(convergenceContent), "promote_private_pro_runtime")
for _, needle := range []string{
`needs.prepare.result == 'success'`,
`github.event.inputs.draft_only != 'true'`,
`startsWith(needs.prepare.outputs.version, '6.')`,
`GH_TOKEN: ${{ secrets.WORKFLOW_PAT }}`,
`--json createdAt`,
`r2_prefix="${TAG}-pro-${run_created_date}-${GITHUB_RUN_ID}"`,
`return_run_details: true`,
`pulse_ref: $pulse_ref`,
`pulse_checkout_ref: $pulse_checkout_ref`,
`version: $version`,
`upload_actions_artifact: "false"`,
`upload_to_r2: "true"`,
`publish_docker_image: "true"`,
`docker_image: "license.pulserelay.pro/pulse-pro"`,
`r2_prefix: $r2_prefix`,
`reuse_existing_packet: "true"`,
`allow_pre_activation_staging: "true"`,
`allow_stable_ga_publish: $allow_stable_ga_publish`,
`repos/rcourtman/pulse-enterprise/actions/workflows/build-pro-release.yml/dispatches`,
`build_run_id="$(jq -r '.workflow_run_id // empty' <<<"${build_dispatch}")"`,
`wait_for_workflow rcourtman/pulse-enterprise "${build_run_id}" "private Pro build"`,
`write_github_output.py r2_prefix "${r2_prefix}"`,
} {
if !strings.Contains(stageJob, needle) {
t.Fatalf("stage_private_pro_runtime missing required contract: %s", needle)
}
}
for _, needle := range []string{
`R2_PREFIX: ${{ inputs.r2_prefix }}`,
`PULSE_LEASE_SHA: ${{ inputs.pulse_lease_sha }}`,
`PULSE_CONVERGENCE_RUN_ID: ${{ inputs.pulse_convergence_run_id }}`,
`return_run_details: true`,
`r2_prefix: $r2_prefix`,
`allow_ga_prefix: $allow_ga_prefix`,
`pulse_lease_sha: $pulse_lease_sha`,
`pulse_convergence_run_id: $pulse_convergence_run_id`,
`repos/rcourtman/pulse-pro/actions/workflows/promote-paid-runtime-release.yml/dispatches`,
`promote_run_id="$(jq -r '.workflow_run_id // empty' <<<"${promote_dispatch}")"`,
`wait_for_workflow rcourtman/pulse-pro "${promote_run_id}" "private Pro live promotion"`,
`echo "::error::${label} failed with conclusion=${conclusion}: ${url}"`,
} {
if !strings.Contains(promotionJob, needle) {
t.Fatalf("promote_private_pro_runtime missing required contract: %s", needle)
}
}
for _, needle := range []string{
`uses: ./.github/workflows/promote-private-pro-runtime.yml`,
`r2_prefix: ${{ inputs.r2_prefix }}`,
`pulse_lease_sha: ${{ needs.acquire_customer_promotion_lease.outputs.lock_sha }}`,
`pulse_convergence_run_id: ${{ github.run_id }}`,
`needs: acquire_customer_promotion_lease`,
} {
if !strings.Contains(convergencePromotionJob, needle) {
t.Fatalf("release convergence private Pro promotion missing required contract: %s", needle)
}
}
if strings.Contains(stageJob, "continue-on-error: true") || strings.Contains(promotionJob, "continue-on-error: true") {
t.Fatal("private Pro staging and promotion must fail the release pipeline on error")
}
for label, job := range map[string]string{
"private Pro staging": stageJob,
"private Pro promotion": promotionJob,
} {
if strings.Contains(job, "gh run list") || strings.Contains(job, "started_at") {
t.Fatalf("%s must not infer a downstream run from time-ordered workflow listings", label)
}
for _, needle := range []string{
`-H "X-GitHub-Api-Version: 2026-03-10"`,
`local run_id="$2"`,
`gh run view "${run_id}"`,
`did not return an exact workflow run ID`,
} {
if !strings.Contains(job, needle) {
t.Fatalf("%s missing exact downstream-run correlation contract: %s", label, needle)
}
}
}
}
func TestReleaseBackendRaceGateUsesCompleteWorkerPartition(t *testing.T) {
makefileBytes, err := os.ReadFile(repoFile("Makefile"))
if err != nil {
t.Fatalf("read Makefile: %v", err)
}
if !strings.Contains(string(makefileBytes), "GO_TEST_TIMEOUT ?= 30m") {
t.Fatal("backend race suite must keep a 30-minute per-package timeout for hosted-runner variance")
}
if !strings.Contains(string(makefileBytes), "go test -race -timeout $(GO_TEST_TIMEOUT)") {
t.Fatal("make test must apply the governed backend race-suite timeout")
}
workflowBytes, err := os.ReadFile(repoFile(".github", "workflows", "create-release.yml"))
if err != nil {
t.Fatalf("read create-release.yml: %v", err)
}
backendJob := workflowJobBlock(t, string(workflowBytes), "backend_tests")
if !strings.Contains(backendJob, "timeout-minutes: 70") {
t.Fatal("release backend job must retain the measured stable execution ceiling with setup and cleanup headroom")
}
for _, invalidCeiling := range []string{"timeout-minutes: 20", "timeout-minutes: 30", "timeout-minutes: 40"} {
if strings.Contains(backendJob, invalidCeiling) {
t.Fatalf("release backend job must not restore a ceiling that can pre-empt the 45-minute API watchdog: %s", invalidCeiling)
}
}
if !strings.Contains(backendJob, "runs-on: ubuntu-24.04") || strings.Contains(backendJob, "self-hosted") || !strings.Contains(backendJob, "run-release-backend-tests.sh") {
t.Fatal("release backend job must use the canonical partition runner on a fresh hosted VM")
}
backendScriptBytes, err := os.ReadFile(repoFile("scripts", "run-release-backend-tests.sh"))
if err != nil {
t.Fatalf("read run-release-backend-tests.sh: %v", err)
}
backendScript := string(backendScriptBytes)
for _, needle := range []string{
"go test -c -race",
"python3 scripts/shard_go_tests.py",
`--max-regex-bytes "$MAX_REGEX_BYTES"`,
`MEMORY_WAIT_SECONDS="${PULSE_BACKEND_TEST_MEMORY_WAIT_SECONDS:-120}"`,
// Both the four-vCPU stable worker and eight-vCPU prerelease worker
// need the measured three-way partition. Two equal-count shards left
// the stable suffix over its 45-minute watchdog while still progressing.
`if [ "$VCPUS" -ge 4 ]; then`,
"cpu_shards=3",
// Admission thresholds are grounded in the 2026-08-21 direct probe on
// the PVE worker (~7.5 GiB measured gate footprint); auto mode must
// degrade the shard count when headroom is missing, never fail the
// release at admission.
`2) echo $((8 * 1024 * 1024))`,
`*) echo $((10 * 1024 * 1024))`,
"Degrading to $cpu_shards API shard(s)",
// Shard CPU is weighted by planned test volume while two single-CPU
// package workers are reserved for the non-API graph. The canonical
// 8-vCPU plan is therefore 4/1/1 plus two, never oversubscribed.
"SHARD_GOMAXPROCS",
`GOMAXPROCS="$shard_procs"`,
"RESERVED_OTHER_PACKAGE_PROCS",
`GOMAXPROCS=1 PULSE_DATA_DIR="$RUN_ROOT/data/other"`,
`go test -race -p "$OTHER_PACKAGE_PROCS" -timeout 30m`,
`API_SHARD_TIMEOUT="${PULSE_BACKEND_API_SHARD_TIMEOUT:-45m}"`,
"--shard-boundaries",
"TestWebSocketOriginAllowsTrustedForwardedHostedOriginIPv6Loopback",
"TestServerInfoEndpointMethodNotAllowed",
`-test.timeout "$API_SHARD_TIMEOUT"`,
} {
if !strings.Contains(backendScript, needle) {
t.Fatalf("release backend partition missing coverage contract: %s", needle)
}
}
}
func TestHelmAgentRuntimePointsAtRealImage(t *testing.T) {
// The helm chart's agent.enabled=true workload used to default to
// ghcr.io/rcourtman/pulse-agent — an image that was never published.
// The chart now points at the main rcourtman/pulse image and uses an
// arch-resolved /usr/local/bin/pulse-agent symlink baked into the
// runtime stage. This test pins:
// 1. values.yaml uses the main image
// 2. values.yaml has the command override
// 3. the agent template renders the command
// 4. the Dockerfile creates the symlink for every supported arch
// 5. validate-release.sh asserts the symlink exists in the published image
// Reverting any one of these unwires the chart back to ImagePullBackOff.
valuesBytes, err := os.ReadFile(repoFile("deploy", "helm", "pulse", "values.yaml"))
if err != nil {
t.Fatalf("read values.yaml: %v", err)
}
values := string(valuesBytes)
if !strings.Contains(values, "repository: rcourtman/pulse\n") {
t.Fatal("agent.image.repository must default to rcourtman/pulse (single-image agent + server)")
}
// Match the actual config value, not casual mentions in surrounding
// comments that explain why the default changed.
if strings.Contains(values, "repository: ghcr.io/rcourtman/pulse-agent") {
t.Fatal("agent.image.repository must not reference the never-published ghcr.io/rcourtman/pulse-agent image")
}
if !strings.Contains(values, "- /usr/local/bin/pulse-agent") {
t.Fatal("agent.command must default to /usr/local/bin/pulse-agent so the main image's server ENTRYPOINT is overridden")
}
agentTemplate, err := os.ReadFile(repoFile("deploy", "helm", "pulse", "templates", "agent.yaml"))
if err != nil {
t.Fatalf("read agent.yaml: %v", err)
}
tmpl := string(agentTemplate)
if !strings.Contains(tmpl, "{{- if .Values.agent.command }}") {
t.Fatal("agent.yaml template must conditionally render command from .Values.agent.command")
}
if !strings.Contains(tmpl, "command:\n {{- toYaml .Values.agent.command | nindent 12 }}") {
t.Fatal("agent.yaml template must render command via toYaml so list values pass through correctly")
}
assertFileContainsAll(t, repoFile("Dockerfile"),
`ln -s /opt/pulse/bin/pulse-agent-linux-arm64 /usr/local/bin/pulse-agent`,
`ln -s /opt/pulse/bin/pulse-agent-linux-armv7 /usr/local/bin/pulse-agent`,
`ln -s /opt/pulse/bin/pulse-agent-linux-amd64 /usr/local/bin/pulse-agent`,
)
assertFileContainsAll(t, repoFile("scripts", "validate-release.sh"),
`Validating /usr/local/bin/pulse-agent arch-resolved symlink`,
`[ -L /usr/local/bin/pulse-agent ]`,
`/usr/local/bin/pulse-agent target is not executable`,
)
}
func repoFile(parts ...string) string {
root := filepath.Join("..", "..")
segments := append([]string{root}, parts...)
return filepath.Join(segments...)
}
// assertFileContainsAll reads the file at path and fails the test if any of
// the required substrings is missing. The standard pinning-test shape in
// this package.
func assertFileContainsAll(t *testing.T, path string, required ...string) {
t.Helper()
content, err := os.ReadFile(path)
if err != nil {
t.Fatalf("read %s: %v", path, err)
}
s := string(content)
for _, needle := range required {
if !strings.Contains(s, needle) {
t.Fatalf("%s missing required substring: %s", path, needle)
}
}
}
func TestReleaseNotesGeneratorResolvesChannelSpecificComparisonRanges(t *testing.T) {
repo := t.TempDir()
runGit := func(args ...string) string {
t.Helper()
cmd := exec.Command("git", args...)
cmd.Dir = repo
output, err := cmd.CombinedOutput()
if err != nil {
t.Fatalf("git %s: %v\n%s", strings.Join(args, " "), err, output)
}
return strings.TrimSpace(string(output))
}
commit := func(message string) {
t.Helper()
runGit("commit", "--allow-empty", "--no-gpg-sign", "-m", message)
}
runGit("init", "-b", "main")
runGit("config", "user.name", "Pulse Release Test")
runGit("config", "user.email", "release-test@example.invalid")
commit("stable 6.3.1")
runGit("tag", "v6.3.1")
runGit("checkout", "-b", "release-v6.3.2")
commit("stable 6.3.2 hotfix")
runGit("tag", "v6.3.2")
runGit("checkout", "main")
commit("alpha 1")
runGit("tag", "v6.4.0-alpha.1")
commit("alpha 2")
runGit("tag", "v6.4.0-alpha.2")
commit("beta 1")
runGit("tag", "v6.4.0-beta.1")
commit("beta 2")
runGit("tag", "v6.4.0-beta.2")
for rc := 1; rc <= 10; rc++ {
commit("release candidate " + strconv.Itoa(rc))
runGit("tag", "v6.4.0-rc."+strconv.Itoa(rc))
}
commit("release candidate 11 changes")
generator, err := filepath.Abs(repoFile("scripts", "generate-release-notes.sh"))
if err != nil {
t.Fatalf("resolve release-note generator path: %v", err)
}
generatorContent, err := os.ReadFile(generator)
if err != nil {
t.Fatalf("read release-note generator: %v", err)
}
for _, required := range []string{
"Researching the complete release range",
"It has no public length or item",
"Drafting an independent customer release story",
"Running an independent improvement pass",
"Auditing the customer story for material omissions",
"Look for distinct user-observable changes that are absent or materially",
"Researching visual release-note evidence",
"RELEASE_NOTES_REASONING_EFFORT",
"--ephemeral",
"--output-schema",
"empty prior response is not evidence that screenshots add no value",
"Locator names and values are literal accessible",
"make any changes you judge warranted",
"RELEASE_NOTES_TRACE_DIR",
"validate-notes-file /dev/stdin",
"260 characters or fewer",
"Use no semicolon or em dash characters",
"- **[Short outcome]** - [Where users notice it and why it matters.]",
"requesting one constrained revision",
} {
if !strings.Contains(string(generatorContent), required) {
t.Fatalf("release-note generator missing stable synthesis contract %q", required)
}
}
for _, forbidden := range []string{"claude", "anthropic", "RELEASE_NOTES_ENGINE"} {
if strings.Contains(strings.ToLower(string(generatorContent)), strings.ToLower(forbidden)) {
t.Fatalf("release-note generator must remain Codex-only, found %q", forbidden)
}
}
resolve := func(version string) string {
t.Helper()
cmd := exec.Command("bash", generator, "--resolve-base", version)
cmd.Dir = repo
output, err := cmd.CombinedOutput()
if err != nil {
t.Fatalf("resolve comparison base for %s: %v\n%s", version, err, output)
}
return strings.TrimSpace(string(output))
}
if got := resolve("6.4.0-rc.11"); got != "v6.4.0-rc.10" {
t.Fatalf("RC comparison base = %q, want v6.4.0-rc.10", got)
}
if got := resolve("6.4.0-alpha.1"); got != "v6.3.2" {
t.Fatalf("alpha.1 comparison base = %q, want v6.3.2", got)
}
if got := resolve("6.4.0-alpha.2"); got != "v6.4.0-alpha.1" {
t.Fatalf("alpha.2 comparison base = %q, want v6.4.0-alpha.1", got)
}
if got := resolve("6.4.0-beta.1"); got != "v6.4.0-alpha.2" {
t.Fatalf("beta.1 comparison base = %q, want v6.4.0-alpha.2", got)
}
if got := resolve("6.4.0-beta.2"); got != "v6.4.0-beta.1" {
t.Fatalf("beta.2 comparison base = %q, want v6.4.0-beta.1", got)
}
if got := resolve("6.4.0-rc.1"); got != "v6.4.0-beta.2" {
t.Fatalf("rc.1 comparison base = %q, want v6.4.0-beta.2", got)
}
if got := resolve("6.4.0"); got != "v6.3.2" {
t.Fatalf("GA comparison base = %q, want v6.3.2", got)
}
cmd := exec.Command("bash", generator, "6.4.0-rc.11", "v6.4.0-rc.9")
cmd.Dir = repo
output, err := cmd.CombinedOutput()
if err == nil {
t.Fatal("generator accepted a comparison tag older than the immediately preceding RC")
}
if !strings.Contains(string(output), "expected 'v6.4.0-rc.10'") {
t.Fatalf("unexpected comparison-range rejection:\n%s", output)
}
}
func TestReleaseTriggersReevaluateVisualsInsteadOfTrustingSidecars(t *testing.T) {
for _, path := range []string{
repoFile("scripts", "trigger-release.sh"),
repoFile("scripts", "trigger-stable-patch.sh"),
} {
content, err := os.ReadFile(path)
if err != nil {
t.Fatalf("read %s: %v", path, err)
}
text := string(content)
if strings.Contains(text, "VISUAL_PLAN_SIDECAR") {
t.Fatalf("%s must not treat a committed visual sidecar as dispatch evidence", path)
}
for _, required := range []string{
"A committed sidecar is review material, not proof",
"generate-release-notes.sh --visual-plan",
} {
if !strings.Contains(text, required) {
t.Fatalf("%s missing visual reevaluation contract %q", path, required)
}
}
}
}
func TestReleaseRollbackGuidanceUsesServerUpdateHelper(t *testing.T) {
for _, path := range []string{
repoFile("scripts", "trigger-release.sh"),
repoFile("scripts", "release_control", "resolve_release_promotion.py"),
repoFile("scripts", "release_control", "render_release_body.py"),
repoFile("docs", "UPGRADE_v6.md"),
repoFile("frontend-modern", "public", "docs", "UPGRADE_v6.md"),
repoFile("docs", "releases", "RELEASE_NOTES_v6.4.0.md"),
repoFile("docs", "releases", "V6_CHANGELOG_v6.4.0.md"),
} {
content, err := os.ReadFile(path)
if err != nil {
t.Fatalf("read %s: %v", path, err)
}
text := string(content)
if !strings.Contains(text, "/bin/update --version") {
t.Fatalf("%s must route systemd/LXC exact-version changes through the server update helper", path)
}
if strings.Contains(text, "./scripts/install.sh --version") {
t.Fatalf("%s routes server rollback through the Unified Agent installer", path)
}
}
renderer, err := os.ReadFile(repoFile("scripts", "release_control", "render_release_body.py"))
if err != nil {
t.Fatalf("read release body renderer: %v", err)
}
if !strings.Contains(string(renderer), "For Docker Compose, set the Pulse image to the rollback target") {
t.Fatal("release body renderer must retain deployment-specific Docker rollback guidance")
}
}
func TestCommittedReleaseVisualSidecarsCarrySelectionEvidence(t *testing.T) {
paths, err := filepath.Glob(repoFile("docs", "releases", "*.visuals.json"))
if err != nil {
t.Fatalf("glob release visual sidecars: %v", err)
}
if len(paths) == 0 {
t.Fatal("no committed release visual sidecars found")
}
for _, path := range paths {
content, err := os.ReadFile(path)
if err != nil {
t.Fatalf("read %s: %v", path, err)
}
var plan struct {
SchemaVersion int `json:"schema_version"`
Decision string `json:"decision"`
Captures json.RawMessage `json:"captures"`
}
if err := json.Unmarshal(content, &plan); err != nil {
t.Fatalf("parse %s: %v", path, err)
}
if plan.SchemaVersion != 1 || strings.TrimSpace(plan.Decision) == "" || len(plan.Captures) == 0 {
t.Fatalf("%s must carry schema version, visual selection evidence, and a captures decision", path)
}
}
}
func assertFileContainsAllNormalized(t *testing.T, path string, required ...string) {
t.Helper()
content, err := os.ReadFile(path)
if err != nil {
t.Fatalf("read %s: %v", path, err)
}
s := normalizedInstallTestWhitespace(string(content))
for _, needle := range required {
if !strings.Contains(s, normalizedInstallTestWhitespace(needle)) {
t.Fatalf("%s missing required normalized substring: %s", path, needle)
}
}
}
func assertFileDoesNotContain(t *testing.T, path string, forbidden ...string) {
t.Helper()
content, err := os.ReadFile(path)
if err != nil {
t.Fatalf("read %s: %v", path, err)
}
s := string(content)
for _, needle := range forbidden {
if strings.Contains(s, needle) {
t.Fatalf("%s contains forbidden substring: %s", path, needle)
}
}
}
func assertFileContainsExactlyOnce(t *testing.T, path string, required ...string) {
t.Helper()
content, err := os.ReadFile(path)
if err != nil {
t.Fatalf("read %s: %v", path, err)
}
s := string(content)
for _, needle := range required {
if count := strings.Count(s, needle); count != 1 {
t.Fatalf("%s contains %q %d times, want exactly once", path, needle, count)
}
}
}
func normalizedInstallTestWhitespace(text string) string {
return strings.Join(strings.Fields(text), " ")
}
func workflowJobBlock(t *testing.T, workflow, job string) string {
t.Helper()
startMarker := "\n " + job + ":\n"
start := strings.Index(workflow, startMarker)
if start == -1 {
t.Fatalf("workflow missing job %s", job)
}
start += 1
rest := workflow[start+len(" "+job+":\n"):]
end := len(rest)
for _, line := range strings.Split(rest, "\n") {
if strings.HasPrefix(line, " ") && !strings.HasPrefix(line, " ") {
candidate := strings.Index(rest, "\n"+line)
if candidate >= 0 {
end = candidate
break
}
}
}
return workflow[start : start+len(" "+job+":\n")+end]
}
func workflowStepBlock(t *testing.T, jobBlock, step string) string {
t.Helper()
startMarker := "\n - name: " + step + "\n"
start := strings.Index(jobBlock, startMarker)
if start == -1 {
t.Fatalf("workflow job missing step %s", step)
}
start += 1
rest := jobBlock[start+len(" - name: "+step+"\n"):]
end := len(rest)
if candidate := strings.Index(rest, "\n - name: "); candidate >= 0 {
end = candidate
}
return jobBlock[start : start+len(" - name: "+step+"\n")+end]
}
// The action's ESM migration must not turn privileged release jobs into cache
// writers or change the application toolchain requested by release consumers.
func TestReleaseNodeSetupKeepsExplicitCacheIsolation(t *testing.T) {
for _, name := range []string{"build-release-candidate.yml", "compile-release-payload.yml", "create-release.yml", "release-dry-run.yml"} {
t.Run(name, func(t *testing.T) {
content, err := os.ReadFile(repoFile(".github", "workflows", name))
if err != nil {
t.Fatal(err)
}
blocks := regexp.MustCompile(`(?m)^ uses: actions/setup-node@[^\n]+\n(?: [^\n]*\n| with:\n)*`).FindAllString(string(content), -1)
if len(blocks) == 0 {
t.Fatal("missing Node setup")
}
if name == "create-release.yml" {
// First four jobs build/test; only the fifth is the privileged
// release consumer. Preserve the three explicit test caches.
if len(blocks) != 5 {
t.Fatalf("review changed release job layout: %d", len(blocks))
}
for i, block := range blocks[:4] {
if i > 0 && (!strings.Contains(block, "cache: 'npm'") || !strings.Contains(block, "cache-dependency-path: 'frontend-modern/package-lock.json'")) {
t.Fatalf("test cache lost its lockfile: %s", block)
}
}
blocks = blocks[4:]
}
for _, block := range blocks {
for _, want := range []string{"actions/setup-node@820762786026740c76f36085b0efc47a31fe5020", "node-version: '24'", "package-manager-cache: false"} {
if !strings.Contains(block, want) {
t.Fatalf("Node setup lost %s: %s", want, block)
}
}
if strings.Contains(block, "registry-url:") || strings.Contains(block, " cache:") {
t.Fatalf("unexpected authentication or explicit cache: %s", block)
}
}
})
}
}
func TestInstallMCPFreeBSDSHA256Fallback(t *testing.T) {
content, err := os.ReadFile(repoFile("scripts", "install-mcp.sh"))
if err != nil {
t.Fatalf("read install-mcp.sh: %v", err)
}
script := string(content)
// FreeBSD base provides sha256(1) but neither GNU sha256sum nor Perl's
// shasum, so the MCP installer must select the available digest tool.
for _, want := range []string{
"command -v sha256sum",
"command -v sha256 >/dev/null 2>&1",
`sha_cmd="sha256 -q"`,
"command -v shasum",
} {
if !strings.Contains(script, want) {
t.Fatalf("install-mcp.sh lost FreeBSD sha256 fallback %q", want)
}
}
}