mirror of
https://github.com/profullstack/agentbbs.git
synced 2026-08-13 22:37:28 +00:00
Autonomous deploy + free-pod/Premium-email membership (#1)
Deploy automation (idempotent, runs on every deploy): - .github/workflows/deploy.yml: push to main/master (or dispatch) SSHes to the droplet and re-runs setup.sh; deploys the pushed branch; smoke-tests :22. - scripts/self-update.sh + agentbbs-update.timer: autonomous backstop that redeploys only when origin advances. - setup.sh hardened: flock, fetch+reset (survives force-push), fixed the always-skipped arcade asset fetch path. Membership model: - Free, email-verified members get their own Docker pod (pod@ paywall removed) and a /~name homepage (seeded at join@). - join@ is now interactive: email -> emailed 6-digit code -> enter code. - Premium ($10 one-time, lifetime via CoinPay) grants a personal <name>@host email (new internal/forwardemail; forwardemail.net aliases) and custom domains (domain@ gated to Premium). - ensurePremium() silently verifies/grants/provisions on hub login, join@, and domain@. New-signup details emailed to AGENTBBS_SIGNUP_NOTIFY (subject "bbs"). Store: User.Premium + premium/premium_ref cols, ConfirmEmailCode, GrantPremium. Tests: store_premium_test.go, forwardemail_test.go. Build/vet/gofmt/test green. Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
1086d57a4d
commit
7a85f2dbbb
11 changed files with 900 additions and 101 deletions
87
.github/workflows/deploy.yml
vendored
Normal file
87
.github/workflows/deploy.yml
vendored
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
name: deploy
|
||||
|
||||
# Fully autonomous, idempotent deploy. On every push to main (or manual
|
||||
# dispatch) this SSHes to the bbs.profullstack.com droplet and re-runs the
|
||||
# idempotent provisioner (setup.sh), which pulls origin, rebuilds the Go
|
||||
# binaries, and restarts the agentbbs service that answers
|
||||
# `ssh join@bbs.profullstack.com`. Re-running is always safe.
|
||||
#
|
||||
# Required repo secrets (Settings -> Secrets and variables -> Actions):
|
||||
# DEPLOY_SSH_KEY private key whose public half is in the droplet admin
|
||||
# user's ~/.ssh/authorized_keys
|
||||
# DEPLOY_HOST bbs.profullstack.com (or the droplet's public IP)
|
||||
# Optional (have sensible defaults below):
|
||||
# DEPLOY_USER admin SSH user (default: root)
|
||||
# DEPLOY_PORT admin SSH port (default: 2202 — setup.sh moves OpenSSH here)
|
||||
#
|
||||
# The admin user needs passwordless sudo (root already does).
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main, master]
|
||||
workflow_dispatch:
|
||||
|
||||
# Never let two deploys overlap; setup.sh also self-locks, this is belt+braces.
|
||||
concurrency:
|
||||
group: deploy-production
|
||||
cancel-in-progress: false
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Configure SSH
|
||||
env:
|
||||
DEPLOY_SSH_KEY: ${{ secrets.DEPLOY_SSH_KEY }}
|
||||
DEPLOY_HOST: ${{ secrets.DEPLOY_HOST }}
|
||||
DEPLOY_PORT: ${{ secrets.DEPLOY_PORT || '2202' }}
|
||||
run: |
|
||||
test -n "$DEPLOY_SSH_KEY" || { echo "::error::DEPLOY_SSH_KEY secret is not set"; exit 1; }
|
||||
test -n "$DEPLOY_HOST" || { echo "::error::DEPLOY_HOST secret is not set"; exit 1; }
|
||||
install -d -m 700 ~/.ssh
|
||||
printf '%s\n' "$DEPLOY_SSH_KEY" > ~/.ssh/id_deploy
|
||||
chmod 600 ~/.ssh/id_deploy
|
||||
ssh-keyscan -p "$DEPLOY_PORT" -H "$DEPLOY_HOST" >> ~/.ssh/known_hosts 2>/dev/null
|
||||
|
||||
- name: Provision / redeploy (idempotent)
|
||||
env:
|
||||
DEPLOY_HOST: ${{ secrets.DEPLOY_HOST }}
|
||||
DEPLOY_USER: ${{ secrets.DEPLOY_USER || 'root' }}
|
||||
DEPLOY_PORT: ${{ secrets.DEPLOY_PORT || '2202' }}
|
||||
# Deploy whichever branch was pushed (main or master), so a rename
|
||||
# "just works". For workflow_dispatch this is the chosen branch.
|
||||
DEPLOY_BRANCH: ${{ github.ref_name }}
|
||||
run: |
|
||||
ssh -i ~/.ssh/id_deploy -p "$DEPLOY_PORT" \
|
||||
-o BatchMode=yes -o StrictHostKeyChecking=yes \
|
||||
"${DEPLOY_USER}@${DEPLOY_HOST}" \
|
||||
"sudo -n env BRANCH=$(printf %q "$DEPLOY_BRANCH") bash -s" <<'REMOTE'
|
||||
set -euo pipefail
|
||||
REPO=https://github.com/profullstack/agentbbs.git
|
||||
BRANCH="${BRANCH:-main}"
|
||||
SRC=/opt/agentbbs
|
||||
# Bootstrap on a fresh box, then always sync to origin so we run the
|
||||
# latest setup.sh (it may have changed in this very push).
|
||||
if [ ! -d "$SRC/.git" ]; then
|
||||
git clone --depth 1 -b "$BRANCH" "$REPO" "$SRC"
|
||||
fi
|
||||
git -C "$SRC" fetch --depth 1 origin "$BRANCH"
|
||||
git -C "$SRC" reset --hard "origin/$BRANCH"
|
||||
exec env BRANCH="$BRANCH" "$SRC/setup.sh"
|
||||
REMOTE
|
||||
|
||||
- name: Smoke-test that agentbbs serves :22
|
||||
if: success()
|
||||
env:
|
||||
DEPLOY_HOST: ${{ secrets.DEPLOY_HOST }}
|
||||
run: |
|
||||
# Confirm an SSH server answers on :22 WITHOUT authenticating — a real
|
||||
# join@ connection would register the connecting key as a new account,
|
||||
# so we never complete a handshake here. Admin OpenSSH lives on the
|
||||
# admin port, so anything serving :22 is agentbbs answering join@/bbs@.
|
||||
if timeout 15 ssh-keyscan -T 10 -p 22 "$DEPLOY_HOST" 2>/dev/null | grep -q .; then
|
||||
echo "::notice::agentbbs is serving SSH on ${DEPLOY_HOST}:22 (join@ is reachable)"
|
||||
else
|
||||
echo "::error::nothing is serving SSH on ${DEPLOY_HOST}:22 — agentbbs may be down"
|
||||
exit 1
|
||||
fi
|
||||
Loading…
Add table
Add a link
Reference in a new issue