removed git runner
This commit is contained in:
parent
d7e19bd9da
commit
8588b058c5
@ -11,7 +11,7 @@ AI_MODEL=
|
||||
# Optional private host-side JSON file binding internal hotel IDs to OHIP credentials.
|
||||
# Default example has no connections. Never commit the real configuration.
|
||||
PMS_CONFIG_FILE_HOST=./deploy/pms.example.json
|
||||
# CI produces image archives. Set these to the loaded, reviewed commit tags.
|
||||
# Ansible builds the reviewed source package. Set these to its full-commit image tags.
|
||||
GUESTOPS_API_IMAGE=guestops-api:local
|
||||
GUESTOPS_WORKER_IMAGE=guestops-worker:local
|
||||
|
||||
|
||||
98
.github/workflows/web.yml
vendored
98
.github/workflows/web.yml
vendored
@ -1,98 +0,0 @@
|
||||
name: Build and verify web migration
|
||||
on:
|
||||
push:
|
||||
branches: [main, 'codex/**']
|
||||
tags: ['[0-9]+.[0-9]+.[0-9]+']
|
||||
pull_request:
|
||||
workflow_dispatch:
|
||||
permissions:
|
||||
contents: read
|
||||
jobs:
|
||||
verify:
|
||||
runs-on: ubuntu-latest
|
||||
services:
|
||||
mongo:
|
||||
image: mongo:8.0
|
||||
ports: ['27017:27017']
|
||||
options: >-
|
||||
--health-cmd "mongosh --quiet --eval 'db.adminCommand({ping:1}).ok'"
|
||||
--health-interval 10s --health-timeout 5s --health-retries 10
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-dotnet@v4
|
||||
with: { dotnet-version: '10.0.x' }
|
||||
- uses: actions/setup-node@v4
|
||||
with: { node-version: '22', cache: npm, cache-dependency-path: web/package-lock.json }
|
||||
- name: Build services
|
||||
run: dotnet build src/GuestOps.Worker/GuestOps.Worker.csproj -c Release
|
||||
- name: Verify backup validation and failure recovery
|
||||
run: python3 -m unittest discover -s tests -p 'test_*.py'
|
||||
- name: Build interface
|
||||
working-directory: web
|
||||
run: npm ci && npm run build
|
||||
- name: Start isolated preview API
|
||||
run: |
|
||||
ASPNETCORE_ENVIRONMENT=Development Preview=true dotnet src/GuestOps.Api/bin/Release/net10.0/GuestOps.Api.dll --urls http://127.0.0.1:5180 > /tmp/guestops-api.log 2>&1 &
|
||||
for i in $(seq 1 30); do curl -fsS http://127.0.0.1:5180/health && exit 0; sleep 1; done
|
||||
cat /tmp/guestops-api.log
|
||||
exit 1
|
||||
- name: Verify MongoDB and HTTP boundaries
|
||||
env:
|
||||
MONGO_TEST_URI: mongodb://127.0.0.1:27017
|
||||
TEST_API_URL: http://127.0.0.1:5180
|
||||
run: dotnet run --project tests/GuestOps.Tests/GuestOps.Tests.csproj -c Release
|
||||
- name: Build Linux images
|
||||
run: |
|
||||
docker build --target api -t guestops-api:${{ github.sha }} .
|
||||
docker build --target worker -t guestops-worker:${{ github.sha }} .
|
||||
- name: Package reviewed images
|
||||
if: github.event_name != 'pull_request'
|
||||
run: |
|
||||
docker save guestops-api:${{ github.sha }} guestops-worker:${{ github.sha }} | gzip -n > guestops-images.tar.gz
|
||||
python3 deploy/release_record.py \
|
||||
--artifact guestops-images.tar.gz \
|
||||
--commit '${{ github.sha }}' \
|
||||
--api-image 'guestops-api:${{ github.sha }}' \
|
||||
--api-id "$(docker image inspect --format '{{.Id}}' 'guestops-api:${{ github.sha }}')" \
|
||||
--worker-image 'guestops-worker:${{ github.sha }}' \
|
||||
--worker-id "$(docker image inspect --format '{{.Id}}' 'guestops-worker:${{ github.sha }}')" \
|
||||
--output release-record.json
|
||||
sha256sum --check <(python3 -c "import json; r=json.load(open('release-record.json')); print(r['artifact']['sha256'] + ' ' + r['artifact']['name'])")
|
||||
- name: Smoke test production containers and restart persistence
|
||||
env:
|
||||
GUESTOPS_API_IMAGE: guestops-api:${{ github.sha }}
|
||||
GUESTOPS_WORKER_IMAGE: guestops-worker:${{ github.sha }}
|
||||
BOOTSTRAP_EMAIL: ci-owner@example.invalid
|
||||
BOOTSTRAP_HOTEL: CI test hotel
|
||||
run: |
|
||||
export MONGO_ROOT_PASSWORD=$(openssl rand -hex 32)
|
||||
export MONGO_APP_PASSWORD=$(openssl rand -hex 32)
|
||||
export BOOTSTRAP_PASSWORD=$(openssl rand -hex 24)
|
||||
trap 'docker compose down --volumes' EXIT
|
||||
docker compose config --quiet
|
||||
docker compose up -d --no-build
|
||||
curl --retry 30 --retry-delay 2 --retry-all-errors --fail http://127.0.0.1:8080/health
|
||||
docker compose run --rm --no-deps -e BOOTSTRAP_EMAIL -e BOOTSTRAP_HOTEL -e BOOTSTRAP_PASSWORD api --bootstrap
|
||||
python3 tests/production_smoke.py
|
||||
docker compose restart api worker
|
||||
curl --retry 30 --retry-delay 2 --retry-all-errors --fail http://127.0.0.1:8080/health
|
||||
python3 tests/production_smoke.py --read
|
||||
install -m 600 /dev/null .env
|
||||
python3 deploy/ops.py preflight --offline
|
||||
mkdir -m 700 .guestops-test-keyring
|
||||
export GNUPGHOME="$PWD/.guestops-test-keyring"
|
||||
gpg --batch --pinentry-mode loopback --passphrase '' --quick-generate-key 'GuestOps CI <ci@example.invalid>' rsa2048 encr 1d
|
||||
BACKUP_RECIPIENT=$(gpg --batch --with-colons --list-keys | awk -F: '$1=="fpr" {print $10; exit}')
|
||||
backup_dir=$(mktemp -d)
|
||||
python3 deploy/ops.py backup --recipient "$BACKUP_RECIPIENT" --output "$backup_dir/fixture.tar.gpg" --confirm-maintenance
|
||||
python3 deploy/ops.py restore-drill "$backup_dir/fixture.tar.gpg" --api-image "$GUESTOPS_API_IMAGE"
|
||||
curl --retry 30 --retry-delay 2 --retry-all-errors --fail http://127.0.0.1:8080/health/ready
|
||||
python3 tests/production_smoke.py --read
|
||||
- uses: actions/upload-artifact@v4
|
||||
if: github.event_name != 'pull_request'
|
||||
with:
|
||||
name: guestops-linux-${{ github.run_number }}
|
||||
path: |
|
||||
guestops-images.tar.gz
|
||||
release-record.json
|
||||
retention-days: 90
|
||||
@ -19,7 +19,7 @@ This summary explains what each milestone delivers and where it currently stands
|
||||
| 6 | Team onboarding and account recovery | Staff invitations, password setup/reset, access disable/restore, and administrator recovery. | **Implemented; acceptance required.** Deployed link delivery, expiry, recovery, and administrator procedures still need operational evidence. |
|
||||
| 7 | Google connection recovery | OAuth reconnect, checkpoint recovery, grant revocation handling, and worker restart safety. | **Implemented; acceptance required.** Dedicated Google-account and worker-restart exercises have not yet been accepted. |
|
||||
| 8 | Operational readiness tooling | Release verification, diagnostics, encrypted backup, restore, preflight, and persistence tools. | **Implemented; acceptance required.** The tools must still be run against the exact release on the Debian host. |
|
||||
| 9 | Gitea and reproducible releases | CI-built immutable images, checksummed release records, retained artifacts, and approval tagging. | **In progress.** The `0.2.0` candidate is frozen; successful exact-commit CI evidence and the post-Gate-B tag are still required. |
|
||||
| 9 | Gitea and reproducible releases | A checksummed source package, Ansible-controlled installation, recorded image identities, retained artifacts, and approval tagging. | **In progress.** The Gitea Action has been removed to match the CMS/CMSFront deployment model; the exact-commit package, Ansible playbook evidence, and post-Gate-B tag are still required. |
|
||||
| 10 | Debian deployment and persistence | Secure Debian/Compose deployment, HTTPS, persistent database and key volumes, and reboot/recreation proof. | **In progress.** Acceptance tooling is ready, but Docker, correct HTTPS/network exposure, exact artifacts, privileged installation, and the supervised drills remain open. |
|
||||
| 11 | Backups, monitoring, and recovery | Scheduled encrypted backups, verified off-host transfer, Zabbix monitoring, restore, and rollback rehearsal. | **In progress.** Repository tooling is ready; installation and timed operational evidence are blocked until Milestones 9 and 10 pass. |
|
||||
| 12 | Google mailbox acceptance | End-to-end Gmail consent, import, recovery, reviewed sending, reconciliation, and revocation evidence. | **In progress.** The runbook and validator exist; the live synthetic-data exercise and independent review remain outstanding. |
|
||||
@ -59,7 +59,7 @@ This summary explains what each milestone delivers and where it currently stands
|
||||
| 6 | Team onboarding and account recovery | B | Implemented / acceptance required | Invitation, password reset, and recovery flows are promoted to local `main`; verify deployed links, mail delivery, token expiry, and administrator recovery procedures. |
|
||||
| 7 | Google connection recovery | B | Implemented / acceptance required | Connection epochs, checkpoint recovery, and revocation handling are promoted to local `main`; complete real Google acceptance and worker-restart exercises. |
|
||||
| 8 | Operational readiness tooling | A | Implemented / acceptance required | Backup, restore, release, and diagnostic tooling is promoted to local `main`; execute it on the actual Debian host and retain evidence. |
|
||||
| 9 | Gitea and reproducible releases | A | In progress | The `0.2.0` candidate is versioned on `main`. CI records the full commit, matched application version, archive checksum and immutable image IDs, and the rollback procedure is documented. Retain the successful default-branch evidence off-host and create the immutable approval tag only after Gate B approval; the existing `0.1.0` tag remains attached to the foundation release. |
|
||||
| 9 | Gitea and reproducible releases | A | In progress | Package the exact versioned `main` commit as a checksummed source archive and hand it to a version-selected Ansible playbook, following the CMS/CMSFront pattern. Ansible installs the archive, builds commit-tagged images, records their immutable IDs, and deploys without using a Gitea runner. Retain the package/install evidence off-host and create the immutable approval tag only after Gate B approval; the existing `0.1.0` tag remains attached to the foundation release. |
|
||||
| 10 | Debian deployment and persistence | A | In progress | Compose uses separate named database and shared key volumes, private host configuration, loopback-only API access and bounded logs. The confirmation-gated persistence drill verifies restart and container-recreation behaviour. Run it on the provisioned Debian host, complete HTTPS and controlled-reboot acceptance, and retain the evidence. |
|
||||
| 11 | Backups, monitoring, and recovery | A | In progress | Encrypted backup and isolated restore tooling now includes opt-in systemd scheduling, checksum-verified rsync transfer, a restricted Zabbix status boundary, guarded local retention and a release-bound acceptance validator. Install and test it on Debian, configure the restricted store and alerts, name operational/review owners, and retain independently reviewed evidence from the timed restore and rollback drill. |
|
||||
| 12 | Google mailbox and reviewed-reply acceptance | B | In progress | The synthetic-data provider runbook, exact scenario set and restricted-record validator are implemented. Complete every scenario against the accepted Debian release and dedicated Google sandbox accounts, independently review the evidence, and retain the validated record. |
|
||||
@ -68,7 +68,7 @@ This summary explains what each milestone delivers and where it currently stands
|
||||
| 15 | Knowledge, AI, and FAQ activation | B | Implemented / acceptance required | Owners can run a bounded no-send batch evaluation, and a release-bound acceptance record enforces positive/negative coverage, zero FAQ errors, separate AI review, staff training, stop-control evidence and named monitoring/rollback owners. Complete the supervised evaluation and retain independent approval. |
|
||||
| 16 | Identity, preferences, and privacy | B/C | Implemented / acceptance required | Login throttling trusts the client address only after one-hop processing by the configured proxy. A release-bound review now covers owner-controlled preferences, account/session controls, data inventory, retention/deletion/legal-hold ownership, provider decisions, audit evidence and known identity limitations. Complete the legal/operational decisions and independently approve the record. |
|
||||
| 17 | Inbox usability and desktop parity | B | Implemented / acceptance required | The inbox uses tenant-scoped stable cursor pagination in pages of 50 and protects unsaved drafts during route/history navigation, reload, conversation selection, filtering and search. Operational timestamps use the saved hotel timezone, and a release-bound desktop-parity acceptance record is implemented. The implementation and preview HTTP suite pass; run the supervised exercise against the approved release and retain independent approval. |
|
||||
| 18 | Pilot, capacity, and release approval | B/C | In progress | The `0.2.0` Gate B candidate has bounded capacity, five-business-day pilot, incident and final-decision record validators with agreed targets. Push and retain CI evidence, complete Gate A and Gate B prerequisites, run the probe and supervised exercises, resolve or contain findings, and retain separate hotel-owner and technical approval. Gate C remains dependent on milestones 13 and 14. |
|
||||
| 18 | Pilot, capacity, and release approval | B/C | In progress | The `0.2.0` Gate B candidate has bounded capacity, five-business-day pilot, incident and final-decision record validators with agreed targets. Retain the exact source-package and Ansible-install evidence, complete Gate A and Gate B prerequisites, run the probe and supervised exercises, resolve or contain findings, and retain separate hotel-owner and technical approval. Gate C remains dependent on milestones 13 and 14. |
|
||||
|
||||
## Delivery sequence
|
||||
|
||||
@ -80,8 +80,9 @@ Milestones 13 (Guestline/Rezlynx) and 14 (payments) can progress as parallel pro
|
||||
|
||||
## Next actions
|
||||
|
||||
- [ ] Push the local release-candidate promotion to the intended default branch and retain its successful CI evidence.
|
||||
- [ ] Retain successful `0.2.0` default-branch CI evidence, then create and archive the immutable approval tag only after Gate B approval.
|
||||
- [ ] Merge the Action removal and release-process update to the intended default branch; that resulting commit becomes the new package candidate.
|
||||
- [ ] Create and checksum the `0.2.0` source archive, add/select it in the GuestOps Ansible playbook, and retain the package and installation evidence.
|
||||
- [ ] Create and archive the immutable `0.2.0` approval tag only after Gate B approval.
|
||||
- [ ] Deploy to the target Debian environment with persistent MongoDB and data-protection keys.
|
||||
- [ ] Run and record backup, restore, restart, monitoring, and rollback exercises.
|
||||
- [ ] Complete real Google mailbox acceptance without using production guest data.
|
||||
|
||||
@ -66,7 +66,7 @@ dotnet run --project tests/GuestOps.Tests
|
||||
cd web && npm ci && npm run build
|
||||
```
|
||||
|
||||
Set `MONGO_TEST_URI` to an isolated MongoDB server and `TEST_API_URL=http://127.0.0.1:5180` with a preview API running to enable database and HTTP integration checks. The suite creates and drops only its own randomly named `guestops_test_*` database. CI runs both integrations and builds both Linux images.
|
||||
Set `MONGO_TEST_URI` to an isolated MongoDB server and `TEST_API_URL=http://127.0.0.1:5180` with a preview API running to enable database and HTTP integration checks. The suite creates and drops only its own randomly named `guestops_test_*` database. Run these checks before creating a versioned source package. GuestOps does not require a Gitea Actions runner; deployment follows the existing Futuresens source-package and Ansible process described in the [deployment guide](docs/deployment.md).
|
||||
|
||||
See [controlled FAQ automation](docs/auto-replies.md), [NMI payment setup and recovery](docs/payments.md), [OHIP reservation setup and recovery](docs/pms.md), [AI drafts and reply delivery setup](docs/replies.md), [migration status](docs/migration.md) and [deployment guide](docs/deployment.md).
|
||||
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
|
||||
## 0.2.0 — Gate B release candidate
|
||||
|
||||
This candidate freezes the implemented Gate B scope for controlled acceptance. It is not yet approved for live hotel operations and does not become a release until the exact commit is pushed, CI and operational evidence are retained, the supervised pilot is approved and the immutable `0.2.0` tag is created.
|
||||
This candidate freezes the implemented Gate B scope for controlled acceptance. It is not yet approved for live hotel operations and does not become a release until the exact commit is pushed, a checksummed source package and operational evidence are retained, the supervised pilot is approved and the immutable `0.2.0` tag is created.
|
||||
|
||||
### Promoted scope
|
||||
|
||||
@ -19,7 +19,7 @@ These capabilities still require their separately documented provider, host and
|
||||
|
||||
### Known limitations and launch conditions
|
||||
|
||||
- Gate A still requires a successful default-branch CI run, durable off-host release archive, target-Debian deployment, persistent storage/key validation, monitoring, and a successful restore/rollback exercise.
|
||||
- Gate A still requires a checksummed source package from the exact default-branch commit, Ansible installation on the target Debian host, persistent storage/key validation, monitoring, and a successful restore/rollback exercise.
|
||||
- Gate B still requires real Google acceptance and supervised staff testing, including desktop-parity acceptance of pagination, draft protection, proxy-aware login throttling and saved-hotel-timezone rendering.
|
||||
- Gate C still requires the Rezlynx/Guestline adapter and independently accepted PMS/payment workflows, plus privacy, identity, capacity, and release approvals.
|
||||
- FAQ live mode and all external write actions must remain disabled until their corresponding acceptance gate has passed.
|
||||
|
||||
@ -11,7 +11,7 @@ import re
|
||||
|
||||
|
||||
GATE_B = {
|
||||
"release-ci", "debian-host", "persistence", "backup-restore", "google-mailbox",
|
||||
"release-package", "debian-host", "persistence", "backup-restore", "google-mailbox",
|
||||
"automation", "identity-privacy", "inbox-usability", "capacity",
|
||||
"incident-support", "pilot-findings",
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Create deterministic evidence for a reviewed GuestOps image archive."""
|
||||
"""Bind a reviewed GuestOps source package to its installed image identities."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Verify a GuestOps release archive and its immutable CI release record."""
|
||||
"""Verify a GuestOps source package and its immutable release record."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
|
||||
@ -8,19 +8,30 @@ Check the existing Nginx, Docker, MongoDB and firewall configuration before inst
|
||||
|
||||
Install Docker Engine/Compose and Nginx using their official Debian instructions. Keep SSH access unchanged. Only HTTPS/HTTP for this hostname need public access; port 8080 is loopback-only and MongoDB has no published port.
|
||||
|
||||
Clone the private repository using an authorized GitHub account. Place the checkout in a dedicated application directory. Copy `.env.example` to `.env`, set permissions to 600, and fill two different MongoDB passwords generated with `openssl rand -hex 32`. Use hex values so they are safe in the MongoDB URI. Store real values only on the server or in its secret-management system.
|
||||
Install the versioned source package through the Futuresens Ansible repository. Do not clone GuestOps from the target server and do not place Gitea credentials on it. The playbook should extract the package into a dedicated application directory. Copy `.env.example` to `.env`, set permissions to 600, and fill two different MongoDB passwords generated with `openssl rand -hex 32`. Use hex values so they are safe in the MongoDB URI. Store real values only on the server or in its secret-management system.
|
||||
|
||||
The MongoDB initialization script runs only on a new volume. Changing `.env` later does not rotate existing database users. Rotate those credentials through MongoDB administration and update application configuration together.
|
||||
|
||||
## 2. Load reviewed application images
|
||||
## 2. Package and install with Ansible
|
||||
|
||||
CI builds API and worker images and packages them in a `guestops-linux-*` artifact. Download the successful artifact for the desired commit and transfer it to the sandbox through your normal authorized deployment process.
|
||||
GuestOps follows the CMS/CMSFront deployment pattern: Gitea stores the application source, a specific committed version is compressed, and a version-selected Ansible playbook installs it. No Gitea Actions runner is required.
|
||||
|
||||
On the trusted packaging machine, run the automated checks, select the full commit SHA, and create the archive from that committed tree rather than from a working directory:
|
||||
|
||||
```sh
|
||||
docker load -i guestops-images.tar.gz
|
||||
git archive --format=tar.gz --prefix=GuestOps-0.2.0/ \
|
||||
--output GuestOps-0.2.0.tar.gz FULL_40_CHARACTER_SHA
|
||||
sha256sum GuestOps-0.2.0.tar.gz > GuestOps-0.2.0.tar.gz.sha256
|
||||
```
|
||||
|
||||
Set `GUESTOPS_API_IMAGE=guestops-api:<commit-sha>` and `GUESTOPS_WORKER_IMAGE=guestops-worker:<commit-sha>` in `.env` using that exact build's SHA. Then run:
|
||||
Store the archive and checksum under a versioned GuestOps files directory in the private Ansible repository. The GuestOps playbook and environment variables should select that version, copy and verify the archive, extract it into the application directory, preserve the private `.env` and provider configuration, and build images tagged with the full source commit:
|
||||
|
||||
```sh
|
||||
docker build --target api -t guestops-api:FULL_40_CHARACTER_SHA .
|
||||
docker build --target worker -t guestops-worker:FULL_40_CHARACTER_SHA .
|
||||
```
|
||||
|
||||
Record the resulting immutable image IDs and bind them to the source archive with `deploy/release_record.py`. Retain the source archive, checksum, release record, its checksum, build output, commit and Ansible run result in the restricted release store. Set `GUESTOPS_API_IMAGE=guestops-api:FULL_40_CHARACTER_SHA` and `GUESTOPS_WORKER_IMAGE=guestops-worker:FULL_40_CHARACTER_SHA` in `.env`. Then the playbook runs:
|
||||
|
||||
```sh
|
||||
docker compose config --quiet
|
||||
@ -28,7 +39,7 @@ docker compose up -d --no-build
|
||||
curl --fail http://127.0.0.1:8080/health
|
||||
```
|
||||
|
||||
Do not run `docker compose config` without `--quiet` in shared logs: expanded configuration contains secrets. Local image builds are available with Compose for development, but CI builds avoid consuming sandbox resources.
|
||||
Do not run `docker compose config` without `--quiet` in shared logs: expanded configuration contains secrets. Do not store `.env`, provider credentials, host inventory secrets, or restricted evidence in either application repository. Ansible must stop on a checksum, commit, version, build, or health-check mismatch.
|
||||
|
||||
## 3. Provision the first hotel owner
|
||||
|
||||
|
||||
@ -4,7 +4,7 @@ The owner-only **Workspace health** page reports database reachability, the work
|
||||
|
||||
## Release evidence and rollback
|
||||
|
||||
Every non-pull-request CI build packages the API and worker images under the full Git commit SHA. The accompanying `release-record.json` binds the archive checksum, application version, commit, image references and immutable Docker image IDs. Retain both files together in restricted off-host release storage; the CI artifact is a transfer mechanism, not the permanent archive.
|
||||
Create the release source archive from an exact committed Gitea tree with `git archive`, then verify its SHA-256 before Ansible installs it. Ansible builds the API and worker images under the full Git commit SHA. The accompanying `release-record.json` binds the source-archive checksum, application version, commit, image references and immutable Docker image IDs. Retain the archive, checksum, record and Ansible result together in restricted off-host release storage.
|
||||
|
||||
Before deployment, verify the archive against its record without loading it:
|
||||
|
||||
@ -18,29 +18,28 @@ print(r['commit'], r['version'], r['images'])
|
||||
PY
|
||||
```
|
||||
|
||||
The repository verifier performs the same checks strictly, also calculates the release-record checksum used by later acceptance records, and can compare the record with images already loaded on an isolated Docker host:
|
||||
After Ansible has built the commit-tagged images, the repository verifier performs the same checks strictly, calculates the release-record checksum used by later acceptance records, and compares the record with the installed Docker images:
|
||||
|
||||
```sh
|
||||
python3 deploy/verify_release.py \
|
||||
--archive guestops-images.tar.gz \
|
||||
--archive GuestOps-0.2.0.tar.gz \
|
||||
--record release-record.json \
|
||||
--commit a3ef408de61d895e69516fa3ba014b43621032bc \
|
||||
--commit FULL_40_CHARACTER_SHA \
|
||||
--version 0.2.0 \
|
||||
--output release-verification.json
|
||||
|
||||
gunzip -c guestops-images.tar.gz | docker load
|
||||
python3 deploy/verify_release.py \
|
||||
--archive guestops-images.tar.gz \
|
||||
--archive GuestOps-0.2.0.tar.gz \
|
||||
--record release-record.json \
|
||||
--commit a3ef408de61d895e69516fa3ba014b43621032bc \
|
||||
--commit FULL_40_CHARACTER_SHA \
|
||||
--version 0.2.0 \
|
||||
--verify-loaded-images \
|
||||
--output loaded-image-verification.json
|
||||
```
|
||||
|
||||
Retain both verification summaries with the untouched archive, release record, its SHA-256, and the exact default-branch CI metadata. A waiting, cancelled, failed, or branch-only run is not release evidence. Do not substitute a local rebuild for the archived default-branch images.
|
||||
Retain both verification summaries with the untouched source archive, its checksum, the release record, its SHA-256, and the exact Ansible run metadata. A package from an uncommitted working tree, a mismatched checksum, or a failed Ansible run is not release evidence. Do not silently replace an approved package or rebuild under the same release identity.
|
||||
|
||||
Load the archive, verify each loaded image ID matches the record, set `GUESTOPS_API_IMAGE` and `GUESTOPS_WORKER_IMAGE` to the recorded full-SHA references, and run the deployment preflight. Record the CI run, commit, checksum and operator in the change ticket. A release tag is an approval marker; do not move or reuse an existing tag. The application and web versions must match before the record can be created.
|
||||
Verify each installed image ID matches the record, set `GUESTOPS_API_IMAGE` and `GUESTOPS_WORKER_IMAGE` to the recorded full-SHA references, and run the deployment preflight. Record the Ansible run, commit, archive checksum and operator in the change ticket. A release tag is an approval marker; do not move or reuse an existing tag. The application and web versions must match before the package is accepted.
|
||||
|
||||
For rollback, first disable worker-driven external writes and reconcile any sending, payment or PMS operation that may have completed since the prior release. Confirm the previous release archive and record are retained, verify its checksum and image IDs, take an encrypted backup, then select the previous recorded image references in `.env` and recreate only the API and worker. Do not roll back MongoDB or the key volume merely to change application images. Run the online preflight, readiness check and read-only smoke test before re-enabling the worker or provider writes. If a release introduced an incompatible data change, follow its release-specific recovery plan rather than starting an older image against newer data.
|
||||
|
||||
@ -182,7 +181,7 @@ Restore into new isolated MongoDB and key volumes; preserve the damaged original
|
||||
|
||||
**A restored database can predate emails, invoices and PMS changes that providers already completed.** Review pending, sending and uncertain records against provider evidence before enabling any worker, including automatic FAQ rules. Do not replay an older approval merely because the restored record says it is pending. Reconcile external effects, validate account sessions and mailbox authorization, and explicitly approve the cutover only after these checks. Rotate credentials if compromise prompted the recovery. Keep the old deployment stopped when enabling the replacement.
|
||||
|
||||
CI exercises a synthetic encrypted backup and isolated restore drill, including actual key decryption and database comparison. A successful CI drill is separate from the required rehearsal on the Debian server with its actual deployment configuration.
|
||||
Run the synthetic encrypted backup and isolated restore drill in a controlled test environment, including actual key decryption and database comparison. This automated check is separate from the required rehearsal on the Debian server with its actual deployment configuration.
|
||||
|
||||
## Milestone 11 acceptance record
|
||||
|
||||
|
||||
@ -46,6 +46,6 @@ Only unsubmitted proposals can be cancelled in GuestOps. Invoice closure, refund
|
||||
|
||||
## Acceptance before live use
|
||||
|
||||
Automated tests use an in-process fake HTTP handler and never contact NMI. They cover tenant isolation, amount/currency/identity mismatches, partial status, concurrent approvals, lost create responses, pagination and merchant changes. CI checks the production configuration mount and disabled defaults.
|
||||
Automated tests use an in-process fake HTTP handler and never contact NMI. They cover tenant isolation, amount/currency/identity mismatches, partial status, concurrent approvals, lost create responses, pagination and merchant changes. Run the production-configuration and disabled-default checks before packaging and again during the Ansible deployment verification.
|
||||
|
||||
Use a dedicated sandbox merchant and recipient to validate authentication, supported currency, exact request/response fields, preservation of `order_details.order_id`, customer invoice email and hosted checkout, partial/full payments and interrupted-request recovery. Live acceptance remains pending. Planet, direct payment links in GuestOps replies, automatic booking after payment, automated expiry/closure and background polling are follow-on work.
|
||||
|
||||
@ -41,7 +41,7 @@ The example deliberately fails while daily reviews are `not-run`. A structurally
|
||||
|
||||
The go/no-go record must bind all evidence to the same release commit and release-record checksum. Record named owners, dates, evidence locations, findings and explicit dispositions for:
|
||||
|
||||
- default-branch CI and immutable release archive;
|
||||
- exact-commit source package, checksum, recorded image IDs, and successful Ansible installation;
|
||||
- Debian preflight, HTTPS, persistence and controlled reboot;
|
||||
- encrypted off-host backup and timed isolated restore;
|
||||
- Google mailbox and reviewed-send acceptance;
|
||||
|
||||
@ -34,6 +34,6 @@ Current limitations: messages imported before reply headers were stored must be
|
||||
|
||||
## Verification
|
||||
|
||||
The test suite covers competing workers, send identity/thread headers, invalid recipients, header injection, uncertain outcomes, restart recovery, pre-send token failure, disabling hotel sending, cross-hotel access, Gmail reconciliation mismatches, AI source isolation, invalid citations, escalations and incomplete responses. CI also runs production container login/restart-persistence smoke checks. Live acceptance must additionally verify Google consent, actual threading, grant revocation, a representative AI draft evaluation set and provider error behaviour with the configured accounts.
|
||||
The test suite covers competing workers, send identity/thread headers, invalid recipients, header injection, uncertain outcomes, restart recovery, pre-send token failure, disabling hotel sending, cross-hotel access, Gmail reconciliation mismatches, AI source isolation, invalid citations, escalations and incomplete responses. Run the production-container login and restart-persistence smoke checks before packaging and during Ansible deployment verification. Live acceptance must additionally verify Google consent, actual threading, grant revocation, a representative AI draft evaluation set and provider error behaviour with the configured accounts.
|
||||
|
||||
Implementation references: [OpenAI Structured Outputs](https://developers.openai.com/api/docs/guides/structured-outputs), [Gmail sending](https://developers.google.com/workspace/gmail/api/guides/sending), [Gmail threads](https://developers.google.com/workspace/gmail/api/guides/threads).
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
"""Exercise disposable CI containers through the host's trusted proxy address.
|
||||
"""Exercise disposable deployment containers through the host's trusted proxy address.
|
||||
|
||||
The forwarded HTTPS header simulates Nginx TLS termination; this is never run
|
||||
against an existing hotel database. Cookie values and credentials are not logged.
|
||||
@ -66,12 +66,12 @@ team = request("/api/team")
|
||||
assert all("passwordHash" not in member and "securityStamp" not in member and "accountLinkHash" not in member for member in team)
|
||||
if "--read" in sys.argv:
|
||||
assert hotel["signature"] == "Persisted across container restart"
|
||||
invited = next(member for member in team if member["email"] == "ci-staff@example.invalid")
|
||||
invited = next(member for member in team if member["email"] == "deployment-staff@example.invalid")
|
||||
assert invited["pending"] and not invited["active"] and invited["linkPurpose"] == "Invite"
|
||||
else:
|
||||
hotel["signature"] = "Persisted across container restart"
|
||||
request("/api/hotel", "PUT", hotel)
|
||||
invite = request("/api/team/invite", "POST", {"name": "CI Staff", "email": "ci-staff@example.invalid"})
|
||||
invite = request("/api/team/invite", "POST", {"name": "Deployment Staff", "email": "deployment-staff@example.invalid"})
|
||||
assert invite["link"].startswith("https://sandbox-guestops.futuresens.co.uk/account#token=")
|
||||
request("/api/auth/logout", "POST")
|
||||
request("/api/hotel", expected=401)
|
||||
|
||||
@ -13,7 +13,7 @@ ROOT = Path(__file__).resolve().parents[1]
|
||||
class ReleaseRecordTests(unittest.TestCase):
|
||||
def test_writes_versions_checksum_and_immutable_image_ids(self):
|
||||
with tempfile.TemporaryDirectory() as directory:
|
||||
artifact = Path(directory) / "guestops-images.tar.gz"
|
||||
artifact = Path(directory) / "GuestOps-0.2.0.tar.gz"
|
||||
output = Path(directory) / "release-record.json"
|
||||
artifact.write_bytes(b"reviewed image archive")
|
||||
|
||||
|
||||
@ -21,7 +21,7 @@ WORKER_ID = "sha256:" + "c" * 64
|
||||
class VerifyReleaseTests(unittest.TestCase):
|
||||
def fixture(self, directory: str):
|
||||
root = Path(directory)
|
||||
archive = root / "guestops-images.tar.gz"
|
||||
archive = root / "GuestOps-0.2.0.tar.gz"
|
||||
record = root / "release-record.json"
|
||||
archive.write_bytes(b"reviewed image archive")
|
||||
release = {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user