From 1a3ba305a7679f2740a72b5a16ac36c60a07403b Mon Sep 17 00:00:00 2001 From: Neo Date: Tue, 18 Aug 2026 02:54:42 -0500 Subject: [PATCH] feat(deploy): production always runs a numbered version MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Policy set by _null. The stack pins :vX.Y.Z — never :dev, never :latest. The reason is what a pointer cannot do. A stack file naming :dev says "dev" today and will still say "dev" after the image underneath it changes, so the file records a preference rather than a fact and "what is running?" has no answer that survives the next push. Rollback is worse: there is nothing to roll back TO, because a pointer only ever names now. deploy.sh: with no --tag it now resolves the newest published NUMBERED version from the registry and says which it picked, instead of defaulting to :dev. A floating tag is refused outright, with --allow-floating as the loud escape hatch for the one case that is not a mistake — reproducing a fault on whatever a pointer currently is. release.sh: no longer moves :dev. It published both tags until now because the stack followed the pointer; under this policy nothing deploys :dev, so moving it would only publish something that misrepresents what is running. scripts/docker-push.sh deleted, with its npm script. Its entire job was building and pushing an unnumbered :dev with no version bump and no guards, which is now the exact thing the policy exists to prevent. Keeping a command that quietly violates a policy is how the policy stops being true. :dev and :latest stay in the registry, frozen. Not deleted: the running container was created from :dev, and removing the tag an existing deployment names is how a recreate fails to pull. Stack 58 still pins :dev as this lands. Correcting that is the first deploy made under the policy, and it is why the default resolves a version rather than reusing whatever the stack already names. Guards proven: default resolves v0.8.3; --tag dev refused; --tag latest refused; --allow-floating warns and proceeds; unpublished tag still refused. Co-Authored-By: Claude Opus 5 (1M context) --- docs/OPERATIONS.md | 46 ++++++++++++---- docs/TOOLS.md | 6 +-- docs/WORK_CYCLE.md | 6 +-- docs/architecture/README.md | 12 ++--- docs/planning/PROJECT_PLAN.md | 7 +-- package.json | 1 - scripts/deploy.sh | 99 ++++++++++++++++++++++++++++------- scripts/docker-push.sh | 22 -------- scripts/release.sh | 49 +++++++---------- 9 files changed, 152 insertions(+), 96 deletions(-) delete mode 100755 scripts/docker-push.sh diff --git a/docs/OPERATIONS.md b/docs/OPERATIONS.md index ee5b2cd..c362301 100644 --- a/docs/OPERATIONS.md +++ b/docs/OPERATIONS.md @@ -249,20 +249,46 @@ There is no non-production environment to rehearse against. `deploy.sh` takes a verified backup before the irreversible part for that reason, and refuses to continue if the backup fails. -**Deploy a version, not the pointer, when it matters.** `npm run deploy` follows -`:dev`, which is a moving tag: afterwards nothing in the stack file says which -image that was. `npm run deploy -- --tag v0.9.4` pins an immutable version, which -is what you want when somebody asks what is running, and what you need to roll -back: +### Production always runs a numbered version + +**Policy, set 2026-08-18.** The stack pins `:vX.Y.Z`. Never `:dev`, never +`:latest`, never any other pointer. + +The reason is what a pointer cannot do. A stack file naming `:dev` says `dev` +today and will still say `dev` after the image underneath it changes — so the +file records a preference rather than a fact, and "what is running?" has no +answer that survives the next push. Rollback is worse: there is nothing to roll +back *to*, because a pointer only ever names now. + +`deploy.sh` enforces it. With no `--tag` it resolves the **newest published +numbered version** from the registry and says which it picked; given a floating +tag it refuses: ```bash -bash scripts/deploy.sh --tag v0.8.3 --dry-run # see the change first -bash scripts/deploy.sh --tag v0.8.3 # roll back to a known image +npm run deploy # newest published vX.Y.Z +npm run deploy -- --tag v0.9.4 --dry-run # a specific version, previewed +npm run deploy -- --tag v0.9.4 # deploy it +npm run deploy -- --tag v0.8.3 # roll back to a known image ``` -It refuses a tag the registry does not hold — a missing tag would otherwise -become an outage, since Portainer accepts the file, fails to pull, and leaves -the container stopped. +`--allow-floating` exists for the one case that is not a mistake — reproducing a +fault on whatever a pointer currently is — and warns every time. + +It also refuses a tag the registry does not hold. A missing tag would otherwise +become an outage: Portainer accepts the file, fails to pull, and leaves the +container stopped. + +**Stack 58 pins `:dev` as this is written**, which is the state the policy +corrects. The first deploy made under it repoints the stack at a version, and +after that the stack file answers the question on its own. + +`:dev` and `:latest` remain in the registry, frozen. They are not deleted +because the running container was created from `:dev`, and removing the tag an +existing deployment names is how a recreate fails to pull. Nothing publishes to +them any more — `release.sh` stopped moving `:dev` when this policy was set, and +scripts/docker-push.sh — named without backticks because it is gone — was +deleted, its only job having been to push an unnumbered `:dev` with no version +bump and no guards. **What `deploy.sh` preserves, and why that is the dangerous part.** Portainer treats a stack `PUT` as the whole desired state. Sending it without the stack's diff --git a/docs/TOOLS.md b/docs/TOOLS.md index 2f19081..6136f76 100644 --- a/docs/TOOLS.md +++ b/docs/TOOLS.md @@ -127,9 +127,9 @@ Run from the repository root. | `npm start` / `npm run server` | the Express server alone, serving `dist/` | | `npm run docker:build` / `docker:run` | build and run the image locally | | `npm run docker:compose:up` / `:down` / `:logs` | the compose stack | -| `npm run release` | **publish an image.** Bump, guards, build, verify its version label, push `:vX.Y.Z` and move `:dev`, commit last, tag. `-- --dry-run` first | -| `npm run deploy` | **move the running stack** to a published image. Backs up first. `-- --dry-run` first, `-- --tag vX.Y.Z` to pin or roll back | -| `npm run docker:push` | build, tag and push `queue-north-website:dev` with no bump and no guards. Superseded by `npm run release` | +| `npm run release` | **publish an image.** Bump, guards, build, verify its version label, push `:vX.Y.Z`, commit last, tag. `-- --dry-run` first | +| `npm run deploy` | **move the running stack** to a published **numbered** version — the newest by default, `-- --tag vX.Y.Z` to pin or roll back. Backs up first, and refuses a floating tag. `-- --dry-run` first | + | `npm run docker:test` | build the image and smoke-test it on 3001 | **There is no `npm test`, and that is not an omission in this table.** There is diff --git a/docs/WORK_CYCLE.md b/docs/WORK_CYCLE.md index aaf2ee9..43c6e89 100644 --- a/docs/WORK_CYCLE.md +++ b/docs/WORK_CYCLE.md @@ -304,9 +304,9 @@ one, and never report progress the API did not return. - **Not the work list.** That is the tracker: milestones are batches, issues are deliverables. -- **Not the release procedure.** This project has no `release.sh` — publishing - is `npm run docker:push`, and how nebula moves to a new image is not yet - written down. `docs/OPERATIONS.md` records that gap; +- **Not the release procedure.** That is `npm run release` to publish and + `npm run deploy` to move the stack — two commands on purpose. `scripts/release.sh` + and `docs/OPERATIONS.md` carry the detail, and `docs/security/SECURITY_CHECKLIST.md` is the list to work through first. - **Not the QA procedure.** That is `docs/qa/ClaudeQAPlan.md`, which ends in its own version of step 7. diff --git a/docs/architecture/README.md b/docs/architecture/README.md index a0a02b0..30f72ac 100644 --- a/docs/architecture/README.md +++ b/docs/architecture/README.md @@ -150,9 +150,9 @@ each row says what it does *here*. | `scripts/preflight.sh` | headers and TLS against the live origin. No `--auth` checks: there are no accounts | | `scripts/backup.sh` | a verified SQLite dump. Its ENGINE block was rewritten for better-sqlite3's online `.backup()` — see below | | `scripts/restore-check.sh` | restores the newest dump into a scratch file, runs `PRAGMA integrity_check`, counts tables, and **times it**. A backup nobody has restored is a guess | -| `scripts/release.sh` | **publishes.** Bump, guard, build, verify the image's own version label, push `:vX.Y.Z` **and** move `:dev`, commit last, tag. Refuses to overwrite a published tag, to build on a dirty tree, or to build when the three hard-coded copies of the public origin disagree. Adapted from PrivacyLLC-Web's — see below | -| `scripts/deploy.sh` | **deploys, and does not build.** Points Portainer stack 58 at an already-published tag, takes a verified backup first, preserves the stack's twelve environment variables, then waits for health and checks both public origins. Reports the digest before and after | -| `scripts/docker-push.sh` | builds and pushes `queue-north-website:dev` only, with no version bump and no guards. Predates the template and is **superseded by `release.sh`** — kept for now because it is what the muscle memory reaches for | +| `scripts/release.sh` | **publishes.** Bump, guard, build, verify the image's own version label, push `:vX.Y.Z`, commit last, tag. Refuses to overwrite a published tag, to build on a dirty tree, or to build when the three hard-coded copies of the public origin disagree. Adapted from PrivacyLLC-Web's — see below | +| `scripts/deploy.sh` | **deploys, and does not build.** Points Portainer stack 58 at an already-published **numbered** version — it refuses a floating tag — taking a verified backup first and preserving the stack's twelve environment variables, then waits for health and checks both public origins. Reports the digest before and after | + | `scripts/docker-test.sh` | builds the image and runs it locally on 3001. Predates the template | | `scripts/prerender.js` | the build step that emits static HTML for every route. Predates the template | @@ -171,9 +171,9 @@ each is a fact about this project rather than a preference: The original refuses to release on a half-run 1,600-test run; this one says out loud that a build, a secret scan and a doc-header check are not tests and that nothing in the gate exercised a route, a form or an API response. -- **It moves `:dev` as well as publishing `:vX.Y.Z`,** because stack 58 follows - `:dev`. That makes `:dev` a pointer and never evidence — `status.sh` reads the - digest and the version label for that. +- **It publishes one tag and does not move `:dev`.** Both scripts follow the + policy below: production always runs a numbered version, so nothing deploys a + pointer and publishing one would only misrepresent what is running. - **It checks the public origin in three files rather than one.** The original passes its origin in as a build arg, so it has one copy to validate. Here `https://queuenorth.com` is written out in `src/lib/seo.js`, diff --git a/docs/planning/PROJECT_PLAN.md b/docs/planning/PROJECT_PLAN.md index b195f42..325ab68 100644 --- a/docs/planning/PROJECT_PLAN.md +++ b/docs/planning/PROJECT_PLAN.md @@ -113,6 +113,7 @@ Observable, in this order: - **No automated tests at all.** No test runner, no typecheck. `npm run build` is the only gate, and it catches syntax and imports rather than behaviour. Recorded as a standing gap in `docs/qa/ClaudeQACoverage.md`. -- **The roll-forward procedure is not written down.** Publishing is - `scripts/docker-push.sh`; how nebula moves to a new image is currently in - somebody's head. Noted in `docs/OPERATIONS.md`. +- **Deployment is scripted but has never been run end to end.** `npm run + release` publishes and `npm run deploy` moves Portainer stack 58, both added + 2026-08-18 and both exercised only in `--dry-run`. The first real run of either + is still ahead. diff --git a/package.json b/package.json index 5b62fae..6e94b1c 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,6 @@ "docker:compose:up": "docker-compose up -d", "docker:compose:down": "docker-compose down", "docker:compose:logs": "docker-compose logs -f", - "docker:push": "bash scripts/docker-push.sh", "docker:test": "bash scripts/docker-test.sh" }, "dependencies": { diff --git a/scripts/deploy.sh b/scripts/deploy.sh index c8ab638..912da59 100755 --- a/scripts/deploy.sh +++ b/scripts/deploy.sh @@ -2,8 +2,9 @@ # # Move the running stack to an image that is already published. # -# bash scripts/deploy.sh # deploy whatever :dev points at -# bash scripts/deploy.sh --tag v0.9.4 # pin a specific published version +# bash scripts/deploy.sh # newest published vX.Y.Z +# bash scripts/deploy.sh --tag v0.9.4 # a specific published version +# bash scripts/deploy.sh --allow-floating --tag dev # chase a pointer, loudly # bash scripts/deploy.sh --dry-run # show the change, touch nothing # bash scripts/deploy.sh --no-backup # skip the pre-deploy dump (say why) # @@ -36,15 +37,25 @@ # stack update does not remove one, but "does not normally" is not a reason to # skip the cheap thing before the irreversible one. # -# ## Why :dev is the default and a version tag is better +# ## THE POLICY: production always runs a numbered version # -# Stack 58 pins `:dev`, which is a moving pointer. Deploying it means "whatever -# was published most recently", and after the fact nothing in the stack file -# says which image that was. Passing --tag pins an immutable version instead, -# which is what you want the day somebody asks what is running — and the day you -# need to roll back to something specific. +# Set 2026-08-18. The stack pins `:vX.Y.Z` and never a floating tag. # -# Either way the digest is reported before and after, because that is the only +# `:dev` and `:latest` are pointers. A stack that follows one has no answer to +# "what is running" that survives the next push — the tag in the stack file says +# `dev` today and will still say `dev` after the image underneath it changes, so +# the file records a preference rather than a fact. Rollback is worse: there is +# nothing to roll back *to*, because the pointer only ever names now. +# +# So this refuses a floating tag. `--allow-floating` exists for the one case +# that is not a mistake — reproducing a fault on whatever `:dev` happens to be — +# and it says so loudly every time. +# +# **Stack 58 pins `:dev` as this is written.** Correcting that is the first +# deploy made under this policy, and it is why the default below resolves a +# version rather than reusing whatever the stack already names. +# +# The digest is reported before and after either way, because that is the only # thing that cannot be moved underneath you. set -uo pipefail @@ -64,9 +75,10 @@ say() { printf '\033[1mdeploy:\033[0m %s\n' "$*" >&2; } die() { printf '\033[1mdeploy:\033[0m %s\n' "$*" >&2; exit 1; } stop() { printf '\033[1mdeploy:\033[0m %s\n' "$*" >&2; exit 2; } -TAG="${DEPLOY_DEFAULT_TAG:-dev}" +TAG="" DRY_RUN="" NO_BACKUP="" +ALLOW_FLOATING="" while [ "$#" -gt 0 ]; do case "$1" in @@ -74,7 +86,8 @@ while [ "$#" -gt 0 ]; do --tag=*) TAG="${1#--tag=}" ;; --dry-run) DRY_RUN="yes" ;; --no-backup) NO_BACKUP="yes" ;; - -h|--help) say "usage: bash scripts/deploy.sh [--tag ] [--dry-run] [--no-backup]"; exit 0 ;; + --allow-floating) ALLOW_FLOATING="yes" ;; + -h|--help) say "usage: bash scripts/deploy.sh [--tag vX.Y.Z] [--dry-run] [--no-backup] [--allow-floating]"; exit 0 ;; *) stop "unknown argument '$1'. Run --help." ;; esac shift @@ -102,18 +115,66 @@ api() { curl -sk --max-time 30 -H "X-API-Key: $KEY" "$@"; } REG_ENV="${RELEASE_REGISTRY_ENV:-$HOME/.openclaw/docker-registry.env}" if [ -r "$REG_ENV" ]; then set -a; . "$REG_ENV"; set +a; fi +tags_json="" if [ -n "${FORGEJO_REGISTRY:-}" ] && [ -n "${FORGEJO_REGISTRY_TOKEN:-}" ]; then repo="${IMAGE#*/}" - tags=$(curl -sS --max-time 20 -u "${FORGEJO_REGISTRY_USER}:${FORGEJO_REGISTRY_TOKEN}" \ + tags_json=$(curl -sS --max-time 20 -u "${FORGEJO_REGISTRY_USER}:${FORGEJO_REGISTRY_TOKEN}" \ "https://${FORGEJO_REGISTRY}/v2/${repo}/tags/list" 2>/dev/null) - if [ -n "$tags" ]; then - printf '%s' "$tags" | grep -q "\"${TAG}\"" \ - || die "${IMAGE}:${TAG} is not published. Nothing was changed. - Published tags: $(printf '%s' "$tags" | python3 -c 'import sys,json;print(", ".join(json.load(sys.stdin).get("tags") or []))' 2>/dev/null)" - else - say "WARNING: could not read the registry tag list, so it is not known whether" - say " ${TAG} is published. A missing tag becomes an outage here." +fi + +# --------------------------------------------------------------------------- +# Resolve the tag. With none named, this is the newest published NUMBERED +# version — never `:dev`, and never "whatever the stack already says", because +# both of those are how a deployment stops being able to name itself. +# --------------------------------------------------------------------------- +if [ -z "$TAG" ]; then + [ -n "$tags_json" ] || stop "no tag given and the registry tag list could not be read, so + the newest published version is unknown. Name one explicitly: + bash scripts/deploy.sh --tag vX.Y.Z + Nothing was attempted." + + TAG=$(printf '%s' "$tags_json" | python3 -c " +import sys, json, re +tags = json.load(sys.stdin).get('tags') or [] +def key(t): + m = re.fullmatch(r'v(\d+)\.(\d+)\.(\d+)', t) + return tuple(int(x) for x in m.groups()) if m else None +vers = sorted([t for t in tags if key(t)], key=key) +print(vers[-1] if vers else '') +") + [ -n "$TAG" ] || stop "the registry holds no numbered vX.Y.Z tag to deploy — only + floating ones. Publish a version first: npm run release. Nothing was attempted." + say "no tag given; newest published version is ${TAG}" +fi + +# --------------------------------------------------------------------------- +# Refuse a floating tag. See THE POLICY at the top. +# --------------------------------------------------------------------------- +if ! printf '%s' "$TAG" | grep -Eq '^v[0-9]+\.[0-9]+\.[0-9]+$'; then + if [ -z "$ALLOW_FLOATING" ]; then + die "'${TAG}' is not a numbered version, and production always runs one. + A stack pinned to a moving tag cannot say what it is running and cannot be + rolled back to anything, because the pointer only ever names now. + Deploy a version: bash scripts/deploy.sh --tag vX.Y.Z + Publish one first: npm run release + If you genuinely mean to chase a pointer, --allow-floating." fi + say "WARNING: --allow-floating. Deploying the moving tag '${TAG}'." + say " After this, the stack file will not record what is running." +fi + +# --------------------------------------------------------------------------- +# Refuse to deploy a tag that is not published. Portainer would accept the +# stack file, fail to pull, and leave the container stopped — an outage caused +# by a typo, discovered by the site going down. +# --------------------------------------------------------------------------- +if [ -n "$tags_json" ]; then + printf '%s' "$tags_json" | grep -q "\"${TAG}\"" \ + || die "${IMAGE}:${TAG} is not published. Nothing was changed. + Published tags: $(printf '%s' "$tags_json" | python3 -c 'import sys,json;print(", ".join(json.load(sys.stdin).get("tags") or []))' 2>/dev/null)" +else + say "WARNING: could not read the registry tag list, so it is not known whether" + say " ${TAG} is published. A missing tag becomes an outage here." fi # --------------------------------------------------------------------------- diff --git a/scripts/docker-push.sh b/scripts/docker-push.sh deleted file mode 100755 index eb3c753..0000000 --- a/scripts/docker-push.sh +++ /dev/null @@ -1,22 +0,0 @@ -#!/usr/bin/env bash -# docker-push.sh — Tag and push dev image to Forgejo registry -# Usage: ./scripts/docker-push.sh -# Requires: ~/.openclaw/docker-registry.env (chmod 600) - -set -euo pipefail -cd "$(dirname "$0")/.." - -source ~/.openclaw/docker-registry.env - -# Build image via docker compose -DOCKER_API_VERSION=1.44 docker compose build - -# Tag and push dev -IMAGE_NAME="queue-north-website-queuenorth" -docker tag "${IMAGE_NAME}:latest" "${FORGEJO_REGISTRY}/null/queue-north-website:dev" - -echo "$FORGEJO_REGISTRY_TOKEN" | docker login "$FORGEJO_REGISTRY" -u "$FORGEJO_REGISTRY_USER" --password-stdin -docker push "${FORGEJO_REGISTRY}/null/queue-north-website:dev" - -docker logout "$FORGEJO_REGISTRY" -echo "✓ Pushed dev image" \ No newline at end of file diff --git a/scripts/release.sh b/scripts/release.sh index f58d7b0..3dc6cad 100755 --- a/scripts/release.sh +++ b/scripts/release.sh @@ -53,16 +53,21 @@ # would leave `dev` carrying a commit announcing a release that was never # published, and `.githooks/post-commit` would already have pushed it. # -# ## Two tags, and only one of them is a record +# ## One tag, and it is a record # -# `:vX.Y.Z` is immutable. Publishing over one is refused, because a running -# stack would silently pull different code on its next recreate while the tag -# said nothing had changed. +# `:vX.Y.Z`, immutable. Publishing over one is refused, because a running stack +# would silently pull different code on its next recreate while the tag said +# nothing had changed. # -# `:dev` is a **moving pointer**, and Portainer stack 58 follows it. Moving it -# is what makes a release deployable here at all, so this script moves it on -# purpose — but that also means `:dev` is never evidence of what is running. -# `scripts/status.sh` reads the digest, and now the version label, for that. +# **It does not move `:dev`, deliberately.** An earlier version of this script +# did, because stack 58 followed that pointer. The policy changed on 2026-08-18: +# production always runs a numbered version, so nothing deploys `:dev` any more +# and moving it would publish a pointer that misrepresents what is running to +# anybody who reads it. `scripts/deploy.sh` refuses a floating tag outright. +# +# The `:dev` and `:latest` tags already in this registry are frozen where they +# are rather than deleted — the running container was created from `:dev`, and +# deleting the tag an existing deployment names is how a recreate fails to pull. # # ## What it deliberately does not do # @@ -85,7 +90,6 @@ cd "$(git rev-parse --show-toplevel)" || exit 1 IMAGE="${RELEASE_IMAGE:-dream.scheller.ltd/null/queue-north-website}" SITE_URL="${RELEASE_SITE_URL:-https://queuenorth.com}" -DEV_TAG="${RELEASE_DEV_TAG:-dev}" REGISTRY_ENV="${RELEASE_REGISTRY_ENV:-$HOME/.openclaw/docker-registry.env}" # Files carrying a version string. The Dockerfile is here because its @@ -178,7 +182,7 @@ next=$(cd "$work" && npm version --no-git-tag-version "$BUMP" 2>/dev/null | tr - [ -n "$next" ] || die "npm rejected '$BUMP' as a version bump." TAG="v${next}" -say "$current -> $next (publishing ${IMAGE}:${TAG}, and moving :${DEV_TAG})" +say "$current -> $next (publishing ${IMAGE}:${TAG})" # --------------------------------------------------------------------------- # Refuse to move a tag that is already published. @@ -217,13 +221,12 @@ if [ -n "$DRY_RUN" ]; then say " bash scripts/verify.sh" say " docker build --build-arg APP_VERSION=${next} -t ${IMAGE}:${TAG} ." say " verify the image's org.opencontainers.image.version label reads ${next}" - say " docker tag ${IMAGE}:${TAG} ${IMAGE}:${DEV_TAG}" - say " docker push ${IMAGE}:${TAG} and ${IMAGE}:${DEV_TAG}" + say " docker push ${IMAGE}:${TAG}" say " git commit -m 'chore(release): ${TAG}' (post-commit then pushes)" say " git tag ${TAG} && git push origin ${TAG}" say "" - say "It would NOT deploy. Portainer stack 58 keeps running the image it has" - say "until scripts/deploy.py is run." + say "It would NOT deploy, and it would NOT move :dev — production runs a" + say "numbered version. Afterwards: npm run deploy -- --tag ${TAG}" exit 0 fi @@ -304,11 +307,6 @@ if [ "$baked" != "$next" ]; then exit 1 fi -# The pointer stack 58 follows. Tagged after the version tag is verified, so -# :dev can never point at an image that failed its own check. -docker tag "${IMAGE}:${TAG}" "${IMAGE}:${DEV_TAG}" \ - || die "could not tag ${IMAGE}:${DEV_TAG}; nothing was pushed." - say "pushing ${IMAGE}:${TAG}…" if ! docker push "${IMAGE}:${TAG}"; then say "push failed. The image exists locally and the bump is in your working" @@ -317,14 +315,6 @@ if ! docker push "${IMAGE}:${TAG}"; then exit 1 fi -say "moving ${IMAGE}:${DEV_TAG}…" -if ! docker push "${IMAGE}:${DEV_TAG}"; then - say "WARNING: ${TAG} IS published, but :${DEV_TAG} was not moved. Stack 58" - say " follows :${DEV_TAG}, so a deploy right now would redeploy the" - say " PREVIOUS image. Push it by hand before deploying:" - say " docker push ${IMAGE}:${DEV_TAG}" -fi - # --------------------------------------------------------------------------- # Commit last, by explicit path. # --------------------------------------------------------------------------- @@ -350,9 +340,10 @@ fi say "released ${TAG}." say "" say "This published an image. It did NOT deploy it — Portainer stack 58 is still" -say "running whatever it was running before. To move it:" +say "running whatever it was running before. To move it to this version:" say "" -say " python3 scripts/deploy.py" +say " npm run deploy -- --tag ${TAG} --dry-run" +say " npm run deploy -- --tag ${TAG}" say "" say "and see docs/OPERATIONS.md, which covers what a redeploy costs: it recreates" say "the container and takes both public front doors down together."