mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-07-09 16:00:59 +00:00
Retire v6 preview demo target after GA
This commit is contained in:
parent
72c8d9677a
commit
de0c5ff5c1
11 changed files with 93 additions and 166 deletions
46
.github/workflows/README.md
vendored
46
.github/workflows/README.md
vendored
|
|
@ -17,18 +17,16 @@ Issue intake is split deliberately:
|
|||
**File**: `update-demo-server.yml`
|
||||
|
||||
Automatically updates the governed demo target after a release is published.
|
||||
Stable releases update the stable public demo. Prerelease tags update the
|
||||
separate v6 preview demo.
|
||||
Stable releases update the public demo. Prerelease tags no longer update a
|
||||
separate v6 preview demo after GA.
|
||||
|
||||
### Configuration Required
|
||||
|
||||
Create two GitHub Environments:
|
||||
Create one GitHub Environment:
|
||||
|
||||
1. `demo-stable`
|
||||
2. `demo-preview-v6`
|
||||
|
||||
Each environment must define the same secret names so the workflow can select
|
||||
the target by environment instead of hardcoding separate workflows.
|
||||
The environment must define the secret names used by the governed demo target.
|
||||
|
||||
Required environment secrets:
|
||||
|
||||
|
|
@ -53,26 +51,22 @@ Required shared secret:
|
|||
Required environment variables:
|
||||
|
||||
1. **DEMO_EXPECTED_HOSTNAME**
|
||||
- The remote `hostname` value the selected environment is expected to report
|
||||
- The remote `hostname` value the stable demo environment is expected to report
|
||||
- Stable example: `pulse-relay`
|
||||
- Preview example: `pulse-v6-preview`
|
||||
- This is a host-identity guard: the workflow fails closed if the SSH secret points at the wrong machine
|
||||
|
||||
2. **DEMO_LOCAL_BASE_URL**
|
||||
- Local URL used on the target host for version and mock-mode verification
|
||||
- Example stable value: `http://localhost:7655`
|
||||
- Example preview value: `http://localhost:8665`
|
||||
|
||||
3. **DEMO_PUBLIC_HEALTH_URL**
|
||||
- Public health endpoint for the selected demo target
|
||||
- Public health endpoint for the stable demo target
|
||||
- Example stable value: `https://demo.pulserelay.pro/api/health`
|
||||
- Example preview value: `https://v6-demo.pulserelay.pro/api/health`
|
||||
|
||||
Optional environment variables:
|
||||
|
||||
1. **DEMO_SERVICE_NAME**
|
||||
- Stable default: `pulse`
|
||||
- Preview example: `pulse-v6-preview`
|
||||
- When set, the server installer derives the instance-specific install dir,
|
||||
config dir, update helper, and update timer from this service identity.
|
||||
|
||||
|
|
@ -83,10 +77,10 @@ Optional environment variables:
|
|||
### How It Works
|
||||
|
||||
1. **Trigger**: Runs automatically when a GitHub release is published
|
||||
2. **Target selection**: Stable tags deploy to `demo-stable`; prerelease tags deploy to `demo-preview-v6`
|
||||
3. **Service identity guard**: Preview runs default to `pulse-v6-preview` and refuse to target the stable `pulse` service identity
|
||||
2. **Target selection**: Stable tags deploy to `demo-stable`; prerelease tags are skipped because the public v6 preview target is retired after GA
|
||||
3. **Service identity**: Stable runs default to the `pulse` service identity
|
||||
4. **Governance check**: Validates the selected tag is reachable from the governed release branch for that version
|
||||
5. **Latest check**: Refuses to update a target unless the published tag is the latest release for that target channel
|
||||
5. **Latest check**: Refuses to update the public demo unless the published tag is the latest stable release
|
||||
6. **Network attach**: Joins Tailscale before any SSH step so governed demo targets can stay on private hostnames or Tailscale IPs
|
||||
7. **Update**: SSHs to the selected demo host and runs the tag-matched root installer from that exact git tag
|
||||
8. **Host identity check**: Verifies the SSH target reports the governed expected hostname before running installer or deploy steps
|
||||
|
|
@ -99,39 +93,29 @@ Optional environment variables:
|
|||
To test without publishing a release:
|
||||
1. Go to `Actions` tab in GitHub
|
||||
2. Select `Update Demo Server` workflow
|
||||
3. Provide a tag and choose `stable`, `preview-v6`, or `auto`
|
||||
3. Provide a stable tag and choose `stable` or `auto`
|
||||
|
||||
### Benefits
|
||||
|
||||
- ✅ Stable and preview demos stay on separate governed targets
|
||||
- ✅ Prereleases no longer require a stable demo overwrite or a manual skip
|
||||
- ✅ The public demo follows the stable v6 release line after GA
|
||||
- ✅ Prereleases no longer require a second public v6 preview surface
|
||||
- ✅ Validates the real server installer path on the selected target
|
||||
- ✅ Removes release-operator guesswork about which demo should move
|
||||
|
||||
### Preview Bootstrap Note
|
||||
|
||||
The preview environment must be bootstrapped once on the host before the update
|
||||
workflow can keep it current. The supported path is a separate service identity
|
||||
such as `pulse-v6-preview` plus a separate public route such as
|
||||
`v6-demo.pulserelay.pro`; do not reuse the stable `pulse.service` instance.
|
||||
|
||||
## Deploy Demo Server
|
||||
|
||||
**File**: `deploy-demo-server.yml`
|
||||
|
||||
Manually deploys the current branch build to either the stable or preview demo
|
||||
environment without changing the governed release workflow.
|
||||
Manually deploys the current branch build to the stable demo environment
|
||||
without changing the governed release workflow.
|
||||
|
||||
- Uses the same `demo-stable` / `demo-preview-v6` environment contract as the
|
||||
release-driven updater
|
||||
- Uses the same `demo-stable` environment contract as the release-driven updater
|
||||
- Joins Tailscale before SSH so governed demo targets can stay on private
|
||||
addresses instead of requiring public runner reachability
|
||||
- Requires `DEMO_EXPECTED_HOSTNAME`, `DEMO_LOCAL_BASE_URL`, and `DEMO_PUBLIC_HEALTH_URL`
|
||||
- Supports optional `DEMO_SERVICE_NAME`, `DEMO_INSTALL_DIR`, `DEMO_TEST_PORT`,
|
||||
`DEMO_AUTH_USER`, and `DEMO_AUTH_PASS`
|
||||
- Assumes the target service and install directory already exist on the host
|
||||
- Defaults preview runs to `pulse-v6-preview` and refuses to target the stable
|
||||
`pulse` service identity
|
||||
- Verifies the SSH target reports the governed expected hostname before deploy
|
||||
- Verifies that the public demo shell serves the same frontend entry asset that
|
||||
was built and deployed
|
||||
|
|
|
|||
9
.github/workflows/create-release.yml
vendored
9
.github/workflows/create-release.yml
vendored
|
|
@ -992,12 +992,11 @@ jobs:
|
|||
REQUIRED_BRANCH: ${{ needs.prepare.outputs.required_branch }}
|
||||
run: |
|
||||
if [ "${{ needs.prepare.outputs.is_prerelease }}" = "true" ]; then
|
||||
TARGET="preview-v6"
|
||||
else
|
||||
TARGET="stable"
|
||||
echo "[OK] Prerelease public demo update skipped; post-GA demo target is stable only."
|
||||
exit 0
|
||||
fi
|
||||
gh workflow run update-demo-server.yml --ref "${REQUIRED_BRANCH}" -f tag="${{ needs.prepare.outputs.tag }}" -f target="${TARGET}"
|
||||
echo "[OK] Demo server update dispatched for ${TARGET} from ${REQUIRED_BRANCH}"
|
||||
gh workflow run update-demo-server.yml --ref "${REQUIRED_BRANCH}" -f tag="${{ needs.prepare.outputs.tag }}" -f target="stable"
|
||||
echo "[OK] Demo server update dispatched for stable from ${REQUIRED_BRANCH}"
|
||||
|
||||
- name: Summary
|
||||
run: |
|
||||
|
|
|
|||
21
.github/workflows/deploy-demo-server.yml
vendored
21
.github/workflows/deploy-demo-server.yml
vendored
|
|
@ -10,7 +10,6 @@ on:
|
|||
type: choice
|
||||
options:
|
||||
- stable
|
||||
- preview-v6
|
||||
# schedule:
|
||||
# - cron: '0 0 * * *' # Nightly at midnight
|
||||
|
||||
|
|
@ -36,9 +35,6 @@ jobs:
|
|||
stable)
|
||||
ENVIRONMENT_NAME="demo-stable"
|
||||
;;
|
||||
preview-v6)
|
||||
ENVIRONMENT_NAME="demo-preview-v6"
|
||||
;;
|
||||
*)
|
||||
echo "::error::Unsupported demo target: ${TARGET}"
|
||||
exit 1
|
||||
|
|
@ -180,21 +176,10 @@ jobs:
|
|||
TARGET="${{ needs.resolve.outputs.target }}"
|
||||
SERVICE_NAME="${DEMO_SERVICE_NAME:-}"
|
||||
if [ -z "$SERVICE_NAME" ]; then
|
||||
case "$TARGET" in
|
||||
stable)
|
||||
SERVICE_NAME="pulse"
|
||||
;;
|
||||
preview-v6)
|
||||
SERVICE_NAME="pulse-v6-preview"
|
||||
;;
|
||||
*)
|
||||
echo "::error::Unsupported demo target: ${TARGET}"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
SERVICE_NAME="pulse"
|
||||
fi
|
||||
if [ "$TARGET" = "preview-v6" ] && [ "$SERVICE_NAME" = "pulse" ]; then
|
||||
echo "::error::Preview demo deployments must not target the stable pulse service."
|
||||
if [ "$TARGET" != "stable" ]; then
|
||||
echo "::error::Unsupported demo target: ${TARGET}"
|
||||
exit 1
|
||||
fi
|
||||
INSTALL_DIR="${DEMO_INSTALL_DIR:-}"
|
||||
|
|
|
|||
54
.github/workflows/update-demo-server.yml
vendored
54
.github/workflows/update-demo-server.yml
vendored
|
|
@ -6,7 +6,7 @@ on:
|
|||
workflow_dispatch:
|
||||
inputs:
|
||||
tag:
|
||||
description: 'Release tag to deploy (e.g., v6.0.0-rc.N or v6.0.0)'
|
||||
description: 'Stable release tag to deploy (e.g., v6.0.0)'
|
||||
required: true
|
||||
type: string
|
||||
target:
|
||||
|
|
@ -17,7 +17,6 @@ on:
|
|||
options:
|
||||
- auto
|
||||
- stable
|
||||
- preview-v6
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
|
@ -29,7 +28,7 @@ jobs:
|
|||
tag: ${{ steps.target.outputs.tag }}
|
||||
target: ${{ steps.target.outputs.target }}
|
||||
environment_name: ${{ steps.target.outputs.environment_name }}
|
||||
skip: ${{ steps.latest.outputs.skip || 'false' }}
|
||||
skip: ${{ steps.target.outputs.skip || steps.latest.outputs.skip || 'false' }}
|
||||
|
||||
steps:
|
||||
- name: Resolve target tag and demo environment
|
||||
|
|
@ -59,10 +58,14 @@ jobs:
|
|||
TARGET="${REQUESTED_TARGET:-auto}"
|
||||
if [ -z "$TARGET" ] || [ "$TARGET" = "auto" ]; then
|
||||
if [ "$IS_PRERELEASE" = "true" ]; then
|
||||
TARGET="preview-v6"
|
||||
else
|
||||
TARGET="stable"
|
||||
echo "tag=$TAG" >> "$GITHUB_OUTPUT"
|
||||
echo "target=stable" >> "$GITHUB_OUTPUT"
|
||||
echo "environment_name=demo-stable" >> "$GITHUB_OUTPUT"
|
||||
echo "skip=true" >> "$GITHUB_OUTPUT"
|
||||
echo "Prerelease demo updates are retired after v6 GA; skipping public demo update for ${TAG}."
|
||||
exit 0
|
||||
fi
|
||||
TARGET="stable"
|
||||
fi
|
||||
|
||||
case "$TARGET" in
|
||||
|
|
@ -73,13 +76,6 @@ jobs:
|
|||
fi
|
||||
ENVIRONMENT_NAME="demo-stable"
|
||||
;;
|
||||
preview-v6)
|
||||
if [ "$IS_PRERELEASE" != "true" ]; then
|
||||
echo "::error::Preview demo target only accepts prerelease tags. Refusing stable tag ${TAG}."
|
||||
exit 1
|
||||
fi
|
||||
ENVIRONMENT_NAME="demo-preview-v6"
|
||||
;;
|
||||
*)
|
||||
echo "::error::Unsupported demo target: ${TARGET}"
|
||||
exit 1
|
||||
|
|
@ -124,31 +120,26 @@ jobs:
|
|||
|
||||
- name: Skip if not latest published release for target
|
||||
id: latest
|
||||
if: github.event_name == 'release'
|
||||
if: github.event_name == 'release' && steps.target.outputs.skip != 'true'
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
TARGET="${{ steps.target.outputs.target }}"
|
||||
TAG="${{ steps.target.outputs.tag }}"
|
||||
|
||||
if [ "$TARGET" = "stable" ]; then
|
||||
LATEST=$(gh api "repos/${{ github.repository }}/releases/latest" --jq '.tag_name')
|
||||
else
|
||||
LATEST=$(gh api "repos/${{ github.repository }}/releases?per_page=100" --jq '[.[] | select(.draft == false and .prerelease == true)][0].tag_name')
|
||||
fi
|
||||
LATEST=$(gh api "repos/${{ github.repository }}/releases/latest" --jq '.tag_name')
|
||||
|
||||
echo "Target tag: $TAG"
|
||||
echo "Latest published tag for $TARGET: $LATEST"
|
||||
echo "Latest published stable release: $LATEST"
|
||||
|
||||
if [ -z "$LATEST" ] || [ "$LATEST" = "null" ]; then
|
||||
echo "::error::Could not determine the latest published release for demo target $TARGET."
|
||||
echo "::error::Could not determine the latest published stable release for demo deployment."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ "$TAG" != "$LATEST" ]; then
|
||||
echo "skip=true" >> "$GITHUB_OUTPUT"
|
||||
echo "Release is not the latest published tag for ${TARGET}; skipping demo update."
|
||||
echo "Release is not the latest published stable tag; skipping demo update."
|
||||
else
|
||||
echo "skip=false" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
|
|
@ -292,21 +283,10 @@ jobs:
|
|||
TARGET="${{ needs.resolve.outputs.target }}"
|
||||
SERVICE_NAME="${DEMO_SERVICE_NAME:-}"
|
||||
if [ -z "$SERVICE_NAME" ]; then
|
||||
case "$TARGET" in
|
||||
stable)
|
||||
SERVICE_NAME="pulse"
|
||||
;;
|
||||
preview-v6)
|
||||
SERVICE_NAME="pulse-v6-preview"
|
||||
;;
|
||||
*)
|
||||
echo "::error::Unsupported demo target: ${TARGET}"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
SERVICE_NAME="pulse"
|
||||
fi
|
||||
if [ "$TARGET" = "preview-v6" ] && [ "$SERVICE_NAME" = "pulse" ]; then
|
||||
echo "::error::Preview demo updates must not target the stable pulse service."
|
||||
if [ "$TARGET" != "stable" ]; then
|
||||
echo "::error::Unsupported demo target: ${TARGET}"
|
||||
exit 1
|
||||
fi
|
||||
echo "service_name=$SERVICE_NAME" >> "$GITHUB_OUTPUT"
|
||||
|
|
|
|||
|
|
@ -886,7 +886,7 @@ hands-on Patrol modes, issue investigation, verified fixes, and 90-day history`.
|
|||
may prewarm shared infrastructure summary caches for route-owned surfaces,
|
||||
but public-demo arrival must not front-run a broader infrastructure-summary
|
||||
fetch than the route actually renders. Commercial
|
||||
posture on `v6-demo` therefore stays governed by the route-owned
|
||||
posture on the stable public demo therefore stays governed by the route-owned
|
||||
presentation policy and summary scope rather than by app-shell-wide
|
||||
bootstrap heuristics. That same app-shell boundary must also keep the
|
||||
public login entrypoints (`/` and `/login`) quiet before a session exists:
|
||||
|
|
@ -1324,20 +1324,13 @@ The governed browser proof for that posture lives in
|
|||
`tests/integration/tests/53-demo-mode-commercial-boundary.spec.ts` and is
|
||||
expected to stay runnable through
|
||||
`tests/integration/scripts/run-tests.sh demo-contract`.
|
||||
That same public-demo boundary also owns the governed `pulse-pro` operational
|
||||
path for the live v6 preview. `pulse-pro/.github/workflows/deploy-v6-preview-demo.yml`
|
||||
is the canonical operator entrypoint, and it must drive
|
||||
`pulse-pro/scripts/bootstrap-v6-demo-preview.sh` as the canonical preview
|
||||
runtime bootstrap/update path. The cost-contained default is the
|
||||
`pulse-v6-preview.service` instance on `pulse-relay` behind the
|
||||
`v6-demo.pulserelay.pro` Caddy route; the old dedicated
|
||||
`pulse-v6-preview` Droplet remains a hibernated snapshot and is only a rebuild
|
||||
or isolation option.
|
||||
`pulse-pro/scripts/audit_v6_preview_demo.sh` is the canonical public smoke
|
||||
proof. The bootstrap must fail closed unless the target preview host is
|
||||
already the live public target and that public smoke audit passes; any
|
||||
`--skip-public-audit` escape hatch is only for pre-cutover host staging or
|
||||
deliberate Caddy/DNS maintenance, never an ordinary live refresh.
|
||||
That same public-demo boundary now treats the old `v6-demo.pulserelay.pro`
|
||||
preview route and its `pulse-v6-preview` service/bootstrap tooling as retired
|
||||
after v6 GA. The stable public demo is the canonical customer-facing v6 demo
|
||||
surface. Any future prerelease or canary demo needs a newly governed target,
|
||||
hostname, service identity, and audit path; it must not silently revive the
|
||||
retired v6-preview route or split the public demo posture back into parallel
|
||||
stable and v6 surfaces.
|
||||
That same licensing/browser boundary now also owns authenticated commercial
|
||||
posture bootstrap and the prohibition on non-billing entitlement reads.
|
||||
`frontend-modern/src/useAppRuntimeState.ts` is the canonical authenticated
|
||||
|
|
|
|||
|
|
@ -990,21 +990,20 @@ rehearsal skipped that header-composition gate.
|
|||
That same dry-run backend gate must run non-race Go package tests serially with
|
||||
`go test -p 1 ./...` so release SLO proof reflects product behavior rather
|
||||
than cross-package contention on 2-core hosted runners.
|
||||
That same governed demo-deployment boundary now owns target separation between
|
||||
the public stable demo and the opt-in v6 preview demo. `.github/workflows/create-release.yml`,
|
||||
That same governed demo-deployment boundary now owns the post-GA single-demo
|
||||
contract. `.github/workflows/create-release.yml`,
|
||||
`.github/workflows/update-demo-server.yml`, and `.github/workflows/deploy-demo-server.yml`
|
||||
must route stable tags to the stable demo environment and prerelease tags to a
|
||||
separate preview environment instead of skipping prerelease demo updates or
|
||||
reusing the stable runtime in place.
|
||||
That same preview deployment boundary also owns service-identity isolation and
|
||||
public-shell parity proof. Preview demo runs must fail closed onto the
|
||||
dedicated preview service identity instead of defaulting back to the stable
|
||||
`pulse` instance, must prove that the SSH target reports the governed expected
|
||||
hostname before any installer or binary copy runs, and demo deploy/update
|
||||
verification must prove that the public demo HTML serves the same frontend
|
||||
entry asset as the target service or freshly built preview artifact rather than
|
||||
treating a passing `/api/health` response as enough evidence that the public
|
||||
shell actually updated. That proof
|
||||
must treat `demo-stable` as the only active public demo target: stable releases
|
||||
may update it, prerelease tags must not create or update a second public v6
|
||||
preview target by default, and any future preview surface requires a new
|
||||
explicitly governed target instead of reusing the retired v6-preview path.
|
||||
That same demo deployment boundary also owns service-identity and public-shell
|
||||
parity proof. Stable demo runs default to the `pulse` service identity, must
|
||||
prove that the SSH target reports the governed expected hostname before any
|
||||
installer or binary copy runs, and demo deploy/update verification must prove
|
||||
that the public demo HTML serves the same frontend entry asset as the target
|
||||
service or freshly built artifact rather than treating a passing `/api/health`
|
||||
response as enough evidence that the public shell actually updated. That proof
|
||||
must use a deterministic HTML parser for the actual module entry script rather
|
||||
than brittle escaped shell regex or a first-match asset scrape that can fail
|
||||
differently over SSH or select the wrong preloaded chunk.
|
||||
|
|
|
|||
|
|
@ -2572,7 +2572,6 @@
|
|||
"internal/cloudcp/routes.go",
|
||||
"internal/cloudcp/stripe/provisioner.go",
|
||||
"internal/hosted/provisioner.go",
|
||||
"pulse-pro:.github/workflows/deploy-v6-preview-demo.yml",
|
||||
"pulse-pro:landing-page/thanks.html",
|
||||
"pulse-pro:license-server/grandfathering.go",
|
||||
"pulse-pro:license-server/intelligence_value.go",
|
||||
|
|
@ -2580,9 +2579,6 @@
|
|||
"pulse-pro:license-server/self_hosted_feature_catalog.generated.go",
|
||||
"pulse-pro:license-server/v6_checkout.go",
|
||||
"pulse-pro:license-server/v6_handlers.go",
|
||||
"pulse-pro:scripts/audit_v6_preview_demo.sh",
|
||||
"pulse-pro:scripts/audit_v6_preview_demo_browser.cjs",
|
||||
"pulse-pro:scripts/bootstrap-v6-demo-preview.sh",
|
||||
"pulse-pro:scripts/grandfathered_recurring_cutover_preview.py"
|
||||
],
|
||||
"verification": {
|
||||
|
|
@ -3285,23 +3281,6 @@
|
|||
"pulse-pro:license-server/main_test.go",
|
||||
"pulse-pro:scripts/tests/test_grandfathered_recurring_cutover_preview.py"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "public-demo-preview-operations",
|
||||
"label": "public demo preview operations proof",
|
||||
"match_prefixes": [],
|
||||
"match_files": [
|
||||
"pulse-pro:.github/workflows/deploy-v6-preview-demo.yml",
|
||||
"pulse-pro:scripts/audit_v6_preview_demo.sh",
|
||||
"pulse-pro:scripts/audit_v6_preview_demo_browser.cjs",
|
||||
"pulse-pro:scripts/bootstrap-v6-demo-preview.sh"
|
||||
],
|
||||
"allow_same_subsystem_tests": false,
|
||||
"test_prefixes": [],
|
||||
"exact_files": [
|
||||
"pulse-pro:scripts/audit_v6_preview_demo.sh",
|
||||
"tests/integration/tests/53-demo-mode-commercial-boundary.spec.ts"
|
||||
]
|
||||
}
|
||||
],
|
||||
"match_files": null
|
||||
|
|
|
|||
|
|
@ -32,8 +32,9 @@ Release workflows now enforce branch/tag lineage rules:
|
|||
- Validate the release tag commit is reachable from the governed branch for
|
||||
that version instead of assuming `main`.
|
||||
- `update-demo-server.yml`
|
||||
- Routes stable tags to the stable demo environment and prerelease tags to
|
||||
the separate v6 preview demo environment.
|
||||
- Routes stable tags to the stable demo environment.
|
||||
- Prerelease public demo deployment is retired after v6 GA; prerelease tags
|
||||
must not create or update a second public v6 demo surface by default.
|
||||
- The selected tag must still be reachable from the governed branch for that
|
||||
version.
|
||||
|
||||
|
|
@ -132,10 +133,11 @@ git push origin pulse/v6-release
|
|||
- Existing unpublished draft releases for the same tag are updated in place
|
||||
and their tag is retargeted to the current governed `pulse/v6-release`
|
||||
head automatically. Do not delete the tag manually just to retry publish.
|
||||
- The release workflow dispatches `update-demo-server.yml` against the
|
||||
`preview-v6` demo target automatically.
|
||||
- Keep the public stable demo on v5/stable; prereleases must land only on
|
||||
the separate preview demo runtime.
|
||||
- Historical prerelease publications used a separate preview demo runtime
|
||||
while v5 remained the public stable demo.
|
||||
- Prerelease public demo deployment is retired after v6 GA; future
|
||||
prerelease tags must not create or update a second public v6 demo surface
|
||||
unless a new governed preview target is explicitly introduced.
|
||||
|
||||
6. Canary rollout:
|
||||
- Upgrade a small user subset first.
|
||||
|
|
|
|||
|
|
@ -503,7 +503,6 @@ func TestDockerAndDemoBuildsUseCanonicalReleaseLdflags(t *testing.T) {
|
|||
workflowRequired := []string{
|
||||
`./scripts/release_ldflags.sh server --version "${VERSION}" --build-time "${BUILD_TIME}" --git-commit "${GIT_COMMIT}"`,
|
||||
`-buildvcs=false`,
|
||||
`demo-preview-v6`,
|
||||
`demo-stable`,
|
||||
`workflow_dispatch:`,
|
||||
`target:`,
|
||||
|
|
@ -513,6 +512,9 @@ func TestDockerAndDemoBuildsUseCanonicalReleaseLdflags(t *testing.T) {
|
|||
t.Fatalf("deploy-demo-server workflow missing canonical release ldflags usage: %s", needle)
|
||||
}
|
||||
}
|
||||
if strings.Contains(workflow, `preview-v6`) || strings.Contains(workflow, `demo-preview-v6`) {
|
||||
t.Fatal("deploy-demo-server workflow must not keep a separate v6 preview demo target after GA")
|
||||
}
|
||||
}
|
||||
|
||||
func TestAgentRuntimeImagePersistsAgentIdentityByDefault(t *testing.T) {
|
||||
|
|
|
|||
|
|
@ -410,9 +410,10 @@ class ReleasePromotionPolicyTest(unittest.TestCase):
|
|||
self.assertNotIn("docs/releases/RELEASE_NOTES_v6.md", demo_copy)
|
||||
self.assertNotIn("docs/releases/V6_RC_OPERATOR_SUPPORT_PACK.md", demo_copy)
|
||||
|
||||
def test_update_demo_server_workflow_uses_generic_tag_examples(self) -> None:
|
||||
def test_update_demo_server_workflow_uses_stable_tag_example(self) -> None:
|
||||
workflow = read(".github/workflows/update-demo-server.yml")
|
||||
self.assertIn("v6.0.0-rc.N or v6.0.0", workflow)
|
||||
self.assertIn("Stable release tag to deploy (e.g., v6.0.0)", workflow)
|
||||
self.assertIn("Prerelease demo updates are retired after v6 GA", workflow)
|
||||
self.assertNotIn("v6.0.0-rc.1", workflow)
|
||||
|
||||
def test_rehearsal_template_and_workflow_capture_ga_rehearsal_record(self) -> None:
|
||||
|
|
@ -701,14 +702,14 @@ class ReleasePromotionPolicyTest(unittest.TestCase):
|
|||
self.assertNotIn("provenance: false", publish)
|
||||
self.assertIn("control_plane.py --branch-for-version", promote)
|
||||
self.assertIn("control_plane.py --branch-for-version", demo)
|
||||
self.assertIn("preview-v6", demo)
|
||||
self.assertIn("demo-preview-v6", demo)
|
||||
self.assertIn("demo-stable", demo)
|
||||
self.assertIn("Refusing prerelease tag", demo)
|
||||
self.assertIn("Refusing stable tag", demo)
|
||||
self.assertIn("latest published release for target", demo)
|
||||
self.assertIn('SERVICE_NAME="pulse-v6-preview"', demo)
|
||||
self.assertIn("Preview demo updates must not target the stable pulse service.", demo)
|
||||
self.assertIn("Prerelease demo updates are retired after v6 GA", demo)
|
||||
self.assertIn("Latest published stable release", demo)
|
||||
self.assertNotIn("preview-v6", demo)
|
||||
self.assertNotIn("demo-preview-v6", demo)
|
||||
self.assertNotIn('SERVICE_NAME="pulse-v6-preview"', demo)
|
||||
self.assertNotIn("Preview demo updates must not target the stable pulse service.", demo)
|
||||
self.assertIn("tailscale/github-action@4e4c49acaa9818630ce0bd7a564372c17e33fb4d # v2", demo)
|
||||
self.assertIn("TS_AUTHKEY", demo)
|
||||
self.assertIn("DEMO_EXPECTED_HOSTNAME", demo)
|
||||
|
|
@ -724,8 +725,10 @@ class ReleasePromotionPolicyTest(unittest.TestCase):
|
|||
self.assertIn("extract_entry_asset()", demo)
|
||||
self.assertIn(r'<script\b[^>]*\bsrc=\"(/assets/index-[^\"]*\.js)\"', demo)
|
||||
self.assertIn("Public demo is serving $PUBLIC_ASSET but the target service is serving $REMOTE_ASSET.", demo)
|
||||
self.assertIn('SERVICE_NAME="pulse-v6-preview"', preview_deploy)
|
||||
self.assertIn("Preview demo deployments must not target the stable pulse service.", preview_deploy)
|
||||
self.assertNotIn("preview-v6", preview_deploy)
|
||||
self.assertNotIn("demo-preview-v6", preview_deploy)
|
||||
self.assertNotIn('SERVICE_NAME="pulse-v6-preview"', preview_deploy)
|
||||
self.assertNotIn("Preview demo deployments must not target the stable pulse service.", preview_deploy)
|
||||
self.assertIn("DEMO_EXPECTED_HOSTNAME", preview_deploy)
|
||||
self.assertIn("Verify target host identity", preview_deploy)
|
||||
self.assertIn("Demo environment points at host $REMOTE_HOSTNAME but expected $DEMO_EXPECTED_HOSTNAME.", preview_deploy)
|
||||
|
|
@ -762,9 +765,9 @@ class ReleasePromotionPolicyTest(unittest.TestCase):
|
|||
self.assertIn("helm status pulse || true", helm_pages)
|
||||
self.assertIn("kubectl describe pods -A || true", helm_pages)
|
||||
self.assertIn("kubectl get events -A --sort-by=.lastTimestamp || kubectl get events -A || true", helm_pages)
|
||||
self.assertIn('TARGET="preview-v6"', release_workflow)
|
||||
self.assertIn('TARGET="stable"', release_workflow)
|
||||
self.assertIn('gh workflow run update-demo-server.yml --ref "${REQUIRED_BRANCH}" -f tag="${{ needs.prepare.outputs.tag }}" -f target="${TARGET}"', release_workflow)
|
||||
self.assertIn("Prerelease public demo update skipped; post-GA demo target is stable only.", release_workflow)
|
||||
self.assertIn('gh workflow run update-demo-server.yml --ref "${REQUIRED_BRANCH}" -f tag="${{ needs.prepare.outputs.tag }}" -f target="stable"', release_workflow)
|
||||
self.assertNotIn('TARGET="preview-v6"', release_workflow)
|
||||
self.assertIn("sync_chart_release_metadata.py", helm)
|
||||
self.assertIn("sync_chart_release_metadata.py", helm_pages)
|
||||
self.assertIn("--chart deploy/helm/pulse/Chart.yaml", helm)
|
||||
|
|
@ -776,8 +779,9 @@ class ReleasePromotionPolicyTest(unittest.TestCase):
|
|||
self.assertIn("raw.githubusercontent.com/{repo}/{tag}/docs/images/pulse-logo.svg", chart_sync)
|
||||
self.assertIn("both stable and prerelease releases dispatch", runbook)
|
||||
self.assertIn("Release `6.0.0` from `pulse/v6-release`", runbook)
|
||||
self.assertIn("separate v6 preview demo environment", runbook)
|
||||
self.assertIn("preview-v6", runbook)
|
||||
self.assertIn("Prerelease public demo deployment is retired after v6 GA", runbook)
|
||||
self.assertNotIn("separate v6 preview demo environment", runbook)
|
||||
self.assertNotIn("preview-v6", runbook)
|
||||
self.assertIn(promotion_metadata_envelope(), normalize_ws(runbook))
|
||||
self.assertIn("waitUntil: 'domcontentloaded'", demo_smoke)
|
||||
self.assertIn("getByLabel('Username').waitFor({ state: 'visible', timeout: 120000 })", demo_smoke)
|
||||
|
|
|
|||
|
|
@ -152,14 +152,14 @@ class SubsystemLookupTest(unittest.TestCase):
|
|||
self.assertEqual(match["verification_requirement"]["id"], "mobile-relay-runtime")
|
||||
|
||||
def test_lookup_paths_normalizes_workspace_relative_cross_repo_runtime_paths(self) -> None:
|
||||
result = lookup_paths(["repos/pulse-pro/scripts/bootstrap-v6-demo-preview.sh"])
|
||||
result = lookup_paths(["repos/pulse-pro/scripts/grandfathered_recurring_cutover_preview.py"])
|
||||
self.assertEqual(result["unowned_runtime_files"], [])
|
||||
self.assertEqual(
|
||||
{item["subsystem"] for item in result["impacted_subsystems"]},
|
||||
{"cloud-paid"},
|
||||
)
|
||||
file_entry = result["files"][0]
|
||||
self.assertEqual(file_entry["path"], "pulse-pro:scripts/bootstrap-v6-demo-preview.sh")
|
||||
self.assertEqual(file_entry["path"], "pulse-pro:scripts/grandfathered_recurring_cutover_preview.py")
|
||||
self.assertEqual(file_entry["classification"], "runtime")
|
||||
self.assertEqual(
|
||||
{match["subsystem"] for match in file_entry["matches"]},
|
||||
|
|
@ -169,7 +169,7 @@ class SubsystemLookupTest(unittest.TestCase):
|
|||
self.assertEqual(match["lane_context"]["lane_id"], "L3")
|
||||
self.assertEqual(
|
||||
match["verification_requirement"]["id"],
|
||||
"public-demo-preview-operations",
|
||||
"legacy-grandfathering-cutover",
|
||||
)
|
||||
|
||||
def test_lookup_paths_assigns_shared_tag_badges_to_frontend_primitives(self) -> None:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue