deploy foundation: push-based deploy.sh, spec-007, v3.0.0
This commit is contained in:
@@ -54,6 +54,11 @@ Decisions inside the set architecture. D-NNN, never renumbered.
|
|||||||
fact-level erasure ships with FDB-007 structured memory — stated in
|
fact-level erasure ships with FDB-007 structured memory — stated in
|
||||||
the user-facing confirmation, not hidden. (Superseded by D-014:
|
the user-facing confirmation, not hidden. (Superseded by D-014:
|
||||||
erasure now covers facts, observations and episode traces.)
|
erasure now covers facts, observations and episode traces.)
|
||||||
|
- **D-015** — Deploys are push-based from the dev machine
|
||||||
|
(`git archive <tag> | ssh`), not pull-based: no deploy keys or git
|
||||||
|
state on the hosts, the artifact is exactly the tag tree, untracked
|
||||||
|
live config survives in-place extraction. Trade-off: deploys need
|
||||||
|
the dev machine; acceptable for a one-operator project.
|
||||||
- **D-014** — Structured memory (FDB-007): observations are the only
|
- **D-014** — Structured memory (FDB-007): observations are the only
|
||||||
consolidation feed (independent of history trimming); consolidation
|
consolidation feed (independent of history trimming); consolidation
|
||||||
returns NEW facts only (no wholesale rewrite — the lossiness of the
|
returns NEW facts only (no wholesale rewrite — the lossiness of the
|
||||||
|
|||||||
@@ -79,3 +79,7 @@ build: clean ## Build distribution packages
|
|||||||
|
|
||||||
# CI targets
|
# CI targets
|
||||||
ci: install-dev all-checks ## Full CI pipeline (install deps and run all checks)
|
ci: install-dev all-checks ## Full CI pipeline (install deps and run all checks)
|
||||||
|
|
||||||
|
# Deploy targets (SPEC-007)
|
||||||
|
deploy: ## Deploy a tag to a host: make deploy HOST=ggg TAG=v3.0.0
|
||||||
|
bash deploy/deploy.sh $(HOST) $(TAG)
|
||||||
|
|||||||
Executable
+52
@@ -0,0 +1,52 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# deploy.sh <host> <tag> — push-based deploy from the dev machine (SPEC-007).
|
||||||
|
# Rollback = run again with the previous tag (DEP-06); restore the
|
||||||
|
# bot.db.pre-<tag> backup first when the schema version moved (PER-06).
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
HOST="${1:?usage: deploy.sh <fjerkroa|ggg> <tag>}"
|
||||||
|
TAG="${2:?usage: deploy.sh <fjerkroa|ggg> <tag>}"
|
||||||
|
|
||||||
|
case "$HOST" in
|
||||||
|
fjerkroa) SERVICE=kroa CONFIG=kroa.toml ;;
|
||||||
|
ggg) SERVICE=luma CONFIG=ggg.toml ;;
|
||||||
|
*) echo "unknown host: $HOST (known: fjerkroa, ggg)" >&2; exit 1 ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# Tags only — no branch/commit deploys (DEP-01)
|
||||||
|
git rev-parse -q --verify "refs/tags/$TAG" >/dev/null || { echo "not a tag: $TAG" >&2; exit 1; }
|
||||||
|
|
||||||
|
# Restaurant service window (DEP-05)
|
||||||
|
if [ "$HOST" = fjerkroa ] && [ "${DEPLOY_FORCE:-0}" != 1 ]; then
|
||||||
|
HOUR=$(TZ=Europe/Oslo date +%H)
|
||||||
|
if [ "$HOUR" -ge 11 ] && [ "$HOUR" -lt 22 ]; then
|
||||||
|
echo "refusing kroa deploy during service hours (11-22 Europe/Oslo); DEPLOY_FORCE=1 overrides (DEP-05)" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "== deploy $TAG -> $HOST (service $SERVICE, config $CONFIG) =="
|
||||||
|
|
||||||
|
# Code push: tag tree over ~/fjerkroa_bot; untracked config/state survives (DEP-01)
|
||||||
|
git archive "$TAG" | ssh "$HOST" 'mkdir -p ~/fjerkroa_bot && tar -x -C ~/fjerkroa_bot'
|
||||||
|
|
||||||
|
ssh "$HOST" "set -e
|
||||||
|
[ -x ~/venv-bot/bin/python ] || python3.11 -m venv ~/venv-bot
|
||||||
|
~/venv-bot/bin/pip install -q --upgrade pip
|
||||||
|
~/venv-bot/bin/pip install -q ~/fjerkroa_bot
|
||||||
|
printf '#!/bin/sh\ncd %s/fjerkroa_bot || exit 1\nexec %s/venv-bot/bin/python -m fjerkroa_bot --config $CONFIG\n' \"\$HOME\" \"\$HOME\" > ~/fjerkroa_bot/start.sh
|
||||||
|
chmod +x ~/fjerkroa_bot/start.sh
|
||||||
|
~/venv-bot/bin/python -c 'import fjerkroa_bot'
|
||||||
|
find ~/fjerkroa_bot -maxdepth 3 -name bot.db | while read -r db; do cp \"\$db\" \"\$db.pre-$TAG\"; done # DEP-03
|
||||||
|
supervisorctl restart $SERVICE"
|
||||||
|
|
||||||
|
echo "== waiting for startsecs =="
|
||||||
|
sleep 35
|
||||||
|
|
||||||
|
# Smoke (DEP-04)
|
||||||
|
ssh "$HOST" "supervisorctl status $SERVICE | grep -q RUNNING" \
|
||||||
|
|| { echo "SMOKE FAIL: $SERVICE not RUNNING on $HOST — rollback: deploy.sh $HOST <previous-tag> (DEP-06)" >&2; exit 1; }
|
||||||
|
ssh "$HOST" "tail -80 ~/logs/supervisord.log | grep -q 'We have logged in as'" \
|
||||||
|
|| { echo "SMOKE FAIL: no fresh Discord login line on $HOST — check logs, consider rollback (DEP-06)" >&2; exit 1; }
|
||||||
|
|
||||||
|
echo "== OK: $HOST runs $TAG — RUNNING + logged in (DEP-04) =="
|
||||||
@@ -6,6 +6,9 @@ with date + result.
|
|||||||
|
|
||||||
| ID | Date | Result |
|
| ID | Date | Result |
|
||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
|
| DEP-01 | 2026-07-13 | Verified with the v3.0.0 ggg deploy: tag-only refusal + untracked config/state survived. fjerkroa redeploy after the service window (tree already identical to 3d22894). |
|
||||||
_No manual-coverage requirements declared yet — DEP-NN arrives with
|
| DEP-02 | 2026-07-13 | Service map exercised: luma restart via script (v3.0.0); kroa mapping code-reviewed, exercised on its next deploy. |
|
||||||
FDB-017._
|
| DEP-03 | 2026-07-13 | bot.db.pre-v3.0.0 backup confirmed on ggg after deploy. |
|
||||||
|
| DEP-04 | 2026-07-13 | Smoke gate exercised on ggg: RUNNING + fresh login line. |
|
||||||
|
| DEP-05 | 2026-07-13 | Live-verified: kroa deploy attempt ~15h Oslo refused without DEPLOY_FORCE=1. |
|
||||||
|
| DEP-06 | 2026-07-13 | Rollback documented (older tag + db backup restore); live drill pending — next release. |
|
||||||
|
|||||||
+1
-1
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|||||||
|
|
||||||
[project]
|
[project]
|
||||||
name = "fjerkroa-bot"
|
name = "fjerkroa-bot"
|
||||||
version = "2.0"
|
version = "3.0.0"
|
||||||
description = "Discord bot with OpenAI responder for Fjærkroa and GGG"
|
description = "Discord bot with OpenAI responder for Fjærkroa and GGG"
|
||||||
authors = [{ name = "Oleksandr Kozachuk", email = "ddeus.lp@mailnull.com" }]
|
authors = [{ name = "Oleksandr Kozachuk", email = "ddeus.lp@mailnull.com" }]
|
||||||
requires-python = ">=3.11"
|
requires-python = ">=3.11"
|
||||||
|
|||||||
@@ -0,0 +1,46 @@
|
|||||||
|
# SPEC-007 — Deploy + hosts
|
||||||
|
|
||||||
|
Two uberspace hosts, one release: **fjerkroa** (service `kroa`,
|
||||||
|
config `kroa.toml`) and **ggg** (service `luma`, config `ggg.toml`).
|
||||||
|
Deploys are push-based from the dev machine — `git archive <tag>`
|
||||||
|
over ssh, no repo credentials on the hosts (D-015). Coverage here is
|
||||||
|
`manual`: rows in `manual-verification.md` with date + result.
|
||||||
|
|
||||||
|
### DEP-01 — Deploys go by tag, in place, configs survive (coverage: manual)
|
||||||
|
|
||||||
|
`deploy/deploy.sh <host> <tag>` refuses unknown hosts and refs that
|
||||||
|
are not tags. The tag's tree is extracted over `~/fjerkroa_bot` —
|
||||||
|
untracked files (live TOML config, `history/`, news snapshot) are
|
||||||
|
never touched. Dev-on-host drift ends here: hosts run tag content
|
||||||
|
only.
|
||||||
|
|
||||||
|
### DEP-02 — Per-host service map (coverage: manual)
|
||||||
|
|
||||||
|
fjerkroa → supervisord program `kroa`, config `kroa.toml`; ggg →
|
||||||
|
program `luma`, config `ggg.toml`. The script owns this map; restart
|
||||||
|
via `supervisorctl restart <service>`.
|
||||||
|
|
||||||
|
### DEP-03 — Pre-deploy state backup (coverage: manual)
|
||||||
|
|
||||||
|
Before the restart, every `bot.db` under `~/fjerkroa_bot` is copied
|
||||||
|
to `bot.db.pre-<tag>` on the host. Schema migrations are
|
||||||
|
forward-only (PER-06) — rolling back past a schema bump means
|
||||||
|
restoring this backup.
|
||||||
|
|
||||||
|
### DEP-04 — Smoke test gates the deploy (coverage: manual)
|
||||||
|
|
||||||
|
After restart the script fails loudly unless the service reports
|
||||||
|
RUNNING and the log shows a fresh Discord login line. On failure the
|
||||||
|
operator instruction is printed: deploy the previous tag (DEP-06).
|
||||||
|
|
||||||
|
### DEP-05 — No kroa deploys during service hours (coverage: manual)
|
||||||
|
|
||||||
|
Deploys to fjerkroa between 11:00 and 22:00 Europe/Oslo are refused
|
||||||
|
unless `DEPLOY_FORCE=1` is set. The restaurant does not beta-test
|
||||||
|
during dinner.
|
||||||
|
|
||||||
|
### DEP-06 — Rollback is a deploy of an older tag (coverage: manual)
|
||||||
|
|
||||||
|
`deploy.sh <host> <previous-tag>` is the rollback path; when the
|
||||||
|
schema version moved, restore the DEP-03 backup first. Venv is
|
||||||
|
rebuilt from the tag's pyproject either way.
|
||||||
@@ -623,7 +623,7 @@ wheels = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "fjerkroa-bot"
|
name = "fjerkroa-bot"
|
||||||
version = "2.0"
|
version = "3.0.0"
|
||||||
source = { editable = "." }
|
source = { editable = "." }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "aiohttp" },
|
{ name = "aiohttp" },
|
||||||
|
|||||||
Reference in New Issue
Block a user