# Changelog Notable changes to the Evomedia.net Token Savers. ## Unreleased ### Fixed - **Deploys can no longer hang forever on an ssh prompt** — every deploy-path `ssh`/`scp` now carries `BatchMode=yes` plus connect and keepalive timeouts (`Get-Ec2SshOpts` in `ZHelpers.ps1`). Without `BatchMode`, ssh prompts for a passphrase or password and waits indefinitely; because the deploy pipes stderr through the pipeline, the prompt never reaches the screen and the run just stops under whatever step label printed last, with no explanation. Now it fails immediately — there is no prompt on this path worth answering. `ServerAlive*` bounds a session that dies mid-command (dropped VPN, sleeping laptop, rebooting host) to about a minute instead of hanging. - **Vendored archives survive the archive filter** — files under a `vendor/` directory are exempt from the "no archives in the zip" rule. A project that vendors a dependency as `vendor/*.tgz` needs it in the deploy zip; dropping it makes a Dockerfile's `COPY vendor ./vendor` fail at image build, a confusing way to learn the filter ate a build input. - **`unzip` install is idempotent** — the remote step ran `apt-get update && apt-get install -y unzip` on every deploy; it now checks `command -v unzip` first and skips the apt round-trip when the binary is already there. - **`zdeploy` edge kind now ships asset subdirectories** (#42) — the edge deploy uploaded top-level files only, so a project self-hosting assets in folders (`fonts/`, `vendor/`) lost them on every deploy: docker created empty root-owned mount points and nginx served 404s from them, which shows up as fonts silently falling back and vendored JS never loading. Every subdirectory except `nginx-logs/` and `.git/` now ships recursively, and the `ensure edge dir` chown is recursive so scp into docker-created root-owned dirs cannot fail. - **`zdeploy` no longer deletes operator-managed files on deploy** (#2) — the project-directory replacement preserved only `./.env`, silently destroying every other server-side file (`.env.db`, staged signing keys, certs) on every deploy. All `.env*` files at the project root are now preserved by default, plus anything listed in the new `deploy.preserve` array (files or directories); the vite kind, which previously preserved nothing, gets the same protection. Found the hard way: a first production deploy of an auth service wiped its staged DB credentials and RSA signing keys. ### Added - **Versioned releases: `zversion`, `zrelease`, `releases/`** — the toolkit now carries one version in the SmartPlant 5-segment scheme, `v{major}.{rc}.{beta}.{alpha}.{build}`. `zversion bump` (one per PR / defect fix) and `zversion bump-stage release|rc|beta|alpha` (zeroes every lower segment) rewrite `build-version.json`, stamp `# Version:` into every script header — so a lone copied script still says which release it came from — and regenerate `CHECKSUMS.txt` in the same step. `zrelease` packages the current version as `releases/zscripts-.zip` with a `.sha256` beside it: one hash verifies the download, the bundled `CHECKSUMS.txt` verifies the extracted contents, so nobody needs to clone the repo to get a verifiable copy. Released zips are immutable — `zrelease` refuses to overwrite one. - **`zchecksums` + `CHECKSUMS.txt`** — a SHA-256 manifest covering every `.ps1` and `.cmd`, so a download can be verified before anything is run. `zchecksums` checks them; `zchecksums -Update` regenerates after an intentional edit. The manifest is `sha256sum` format, so `sha256sum -c CHECKSUMS.txt` works on Linux/macOS/WSL too, and the hashes match on every platform because `.gitattributes` pins these files to CRLF everywhere. Flags changed files, missing files, **and scripts present on disk but absent from the manifest**. It's an integrity check, not a signature — the manifest sits in the same repo as the code, so it catches corruption and accidental drift, not a compromised repo. A Pester test fails if the manifest ever goes stale. - **Test suite (Pester)** — the toolkit now has automated coverage of its own pure logic: `Get-ArchiveExcludes` (including the deploy-vs-backup rule that keeps `.env`/`uploads` out of deploys but *in* backups), config and project lookups, `remote.composeDir` fallback, EC2 target composition, and build-label formatting. Run with `Invoke-Pester .\tests` (Pester 5+). Verified by mutation testing — reintroducing each historical bug turns the suite red. - **`ZCONFIG` environment variable** — overrides the path to `zconfig.json`, so a run can target an alternate config. Also gives the test suite a seam for injecting a fixture. - **`zec2_rotatekeys` — safely rotate/reset server-side secrets** — a new tool for when a secret leaks or a deploy overwrites a production `.env` with dev values. `-Rotate KEY` regenerates a key **on the server** (`openssl rand -hex 32`) so the new value never leaves the box; `-Set KEY` takes an operator-known value (e.g. `DATABASE_URL`, `ADMIN_EMAIL`) from a masked prompt and streams it over SSH stdin — never a command argument, never echoed. Backs the server `.env` up to a timestamped `.bak` first, updates the key atomically (matches or appends), auto-detects `backend/.env` from `deploy.preserve`, and with `-Restart` **recreates** the container (`up -d --force-recreate`, so the new values actually load — a plain restart keeps the old environment). `-WhatIf` previews the plan without touching anything. - **`zkill all`** — `zkill` now accepts `all`, stopping the dev server of every project that has a `ports.dev` (edge/docker stacks with no local dev server are skipped). Brings it in line with `zdeploy all` / `zbackup all`; the one-shot "stop everything I've got running locally". - **`zdeploy` server-side health verification (`verify` block)** — projects not published through the edge proxy can declare `"verify": { "port": ..., "path": "/health", "expect": "..." }` and the deploy is checked from the server itself (`curl localhost:` over SSH) instead of hitting the public IP. Fixes a false PASS where the proxy's default vhost answered for apps that never started; projects with neither `domain` nor `verify` are now reported as NOT verified. - **`zdeploy` optional `deploy.gitPull`** — `git pull --ff-only` in the project root before zipping. `zdeploy` zips the working tree and doesn't otherwise pull, so a checkout left behind `origin` after a merged PR would deploy stale code while still bumping the build number — success that changes nothing. A failed pull aborts the deploy instead. - **Per-project `start` config block** — `zstart` honors optional pre-start steps from `zconfig.json`: `"gitPull": true` runs `git pull --ff-only` in the project root before starting (never boot a stale checkout), and `"env": { ... }` sets environment variables for the dev-server process. Example added to `zconfig.example.json`. - **Switch-style argument tolerance** — a leading dash on a project key is ignored everywhere (`zdeploy -myapp` == `zdeploy myapp`), for hands that grew up on per-project switches. ### Changed - **`zbackup` / `zbackup_and_sync` require an explicit target** — running them bare now shows usage instead of quietly backing up every project; `all` does what bare invocation used to (matching `zdeploy`). The scheduled task created by `setup_backup_schedule.ps1` passes `all` — re-run it if your task was registered before this change. - **`zbackup` parses more `DATABASE_URL` styles** — double/single-quoted values (Prisma convention), `postgres://` and `postgresql+driver://` schemes, and URLs without an explicit port (defaults to 5432) all work; previously these skipped the Postgres dump with "Could not parse DATABASE_URL". - **`zkill` / port cleanup kills the whole process tree** — listeners on a project's port are now terminated children-first. Auto-reloading servers (uvicorn/watchfiles, nodemon) spawn workers that inherit the listening socket; killing only the parent left orphans serving stale code. - **`zbackup` finds `DATABASE_URL` in `backend\.env` too** — projects with a frontend/backend split get their Postgres dump bundled without needing a root-level `.env`. ## 1.0.0 Initial public release: `zstart` / `zkill` / `zrestart` (local dev servers), `zdeploy` (zip → upload → compose build → live build-version verification, with handlers for python / vite / nextjs / edge / docker project kinds), `zec2` / `zec2online` / `zrepair` (health checks and recovery), `zbackup` / `zbackup_ec2` / `zsync` (local, server-side, and offsite backups), all driven by a single gitignored `zconfig.json`.