Menu
Public repository / e55b0a09fcde561fec2f32c80daf62b0a80389b2
AkurAI-Mail / deploy.sh
Latest change 08dd5db33217bf8fcf9f3f0bcf7ca2f409e8c1a4 - docs: add deploy conduct rules by Ólafur Búi Ólafsson
#!/usr/bin/env bash
# Deploy rules of conduct:
# - Read AGENTS.md and canonical Notes/docs before changing or deploying.
# - Never print, commit, or copy secrets; use passvault/env files only.
# - Preserve env files, service users, user data, and databases; snapshot state before risky swaps.
# - Verify ports, DNS, TLS, and systemd unit names before changing routes or services.
# - Use managed services only (systemd/pm2); no nohup, background shells, or ad hoc daemons.
# - Run gates and health checks; if deploy fails, stop and roll back rather than improvising.
# - Keep deploy behavior unchanged unless the task explicitly asks for deploy logic changes.
set -euo pipefail
# ---------------------------------------------------------------------------
# AkurAI Mail UI — build locally, deploy artifacts to AWS VM
# ---------------------------------------------------------------------------
APP_NAME="akurai-mail-ui"
SSH_HOST="${AKURAI_MAIL_SSH:-akurai-mail}"
DEPLOY_DIR="/opt/${APP_NAME}"
SERVICE="${APP_NAME}"
VERSION="$(cat VERSION | tr -d '\n')"
RELEASE_ID="$(date -u +%Y%m%dT%H%M%SZ)-$(git rev-parse --short HEAD 2>/dev/null || echo nogit)"
STAGING_DIR="${PWD}/.deploy-staging/${RELEASE_ID}"
log() { echo "[deploy] $*"; }
step() { echo; echo "── $* ──"; }
remote() { ssh "${SSH_HOST}" "$@"; }
# ---------------------------------------------------------------------------
# Parse
# ---------------------------------------------------------------------------
COMMAND="${1:-prod}"
case "${COMMAND}" in
status)
git status --short --branch
remote "sudo systemctl status ${SERVICE} --no-pager" || true
exit 0
;;
--help|-h|help)
echo "Usage: deploy.sh [prod|status|help]"
exit 0
;;
esac
# ---------------------------------------------------------------------------
# Step 1: Install + build locally
# ---------------------------------------------------------------------------
step "1/5 Local build"
bun install --frozen-lockfile 2>&1 || bun install
bun run build
log "Build complete"
# ---------------------------------------------------------------------------
# Step 2: Package artifacts
# ---------------------------------------------------------------------------
step "2/5 Package"
mkdir -p "${STAGING_DIR}"
TARBALL="${STAGING_DIR}/${APP_NAME}-${RELEASE_ID}.tar.gz"
tar czf "${TARBALL}" \
build/ \
package.json \
bun.lock \
scripts/ \
backup.sh \
rollback.sh \
VERSION \
CHANGELOG.md
log "Tarball: $(du -sh "${TARBALL}" | cut -f1)"
# ---------------------------------------------------------------------------
# Step 3: Upload
# ---------------------------------------------------------------------------
step "3/5 Upload to ${SSH_HOST}"
remote "mkdir -p /tmp/akurai-mail-deploy"
scp -q "${TARBALL}" "${SSH_HOST}:/tmp/akurai-mail-deploy/"
# ---------------------------------------------------------------------------
# Step 4: Remote install
# ---------------------------------------------------------------------------
step "4/5 Remote install"
ARCHIVE_BASENAME="$(basename "${TARBALL}")"
remote "sudo bash -s" <<INSTALL
set -euo pipefail
# Backup current
if [ -d ${DEPLOY_DIR}/build ]; then
BACKUP_DIR="/opt/${APP_NAME}-backups/${RELEASE_ID}"
mkdir -p "\${BACKUP_DIR}"
tar czf "\${BACKUP_DIR}/app.tar.gz" -C ${DEPLOY_DIR} build/ package.json VERSION 2>/dev/null || true
echo " [backup] \${BACKUP_DIR}"
fi
# Extract new release (overwrite build/ and support files)
tar xzf /tmp/akurai-mail-deploy/${ARCHIVE_BASENAME} -C ${DEPLOY_DIR}
# Install production runtime deps
cd ${DEPLOY_DIR}
/home/ubuntu/.bun/bin/bun install --frozen-lockfile --production 2>/dev/null || /home/ubuntu/.bun/bin/bun install --production
# Install helper scripts
install -m 755 scripts/akurai-mail-server /usr/local/sbin/akurai-mail-server
install -m 755 backup.sh /usr/local/sbin/akurai-mail-backup
install -m 755 rollback.sh /usr/local/sbin/akurai-mail-rollback
# Ensure state file
install -d -o ubuntu -g ubuntu /var/lib/akurai-mail
if [ ! -s /var/lib/akurai-mail/state.json ]; then
printf '{"domain":"olibuijr.com","hostname":"mail.olibuijr.com","publicIp":"3.94.46.219","users":[],"aliases":[],"events":[],"metricsHistory":[]}\n' > /var/lib/akurai-mail/state.json
fi
chown ubuntu:ubuntu /var/lib/akurai-mail/state.json
# Restart
systemctl daemon-reload
systemctl restart ${SERVICE}
# Cleanup
rm -rf /tmp/akurai-mail-deploy
# Prune old backups (keep 10)
ls -dt /opt/${APP_NAME}-backups/*/ 2>/dev/null | tail -n +11 | xargs -r rm -rf
echo " [done] ${APP_NAME} ${VERSION} installed"
INSTALL
# ---------------------------------------------------------------------------
# Step 5: Healthcheck
# ---------------------------------------------------------------------------
step "5/5 Healthcheck"
sleep 2
if remote "curl -fsS -o /dev/null -w '%{http_code}' http://127.0.0.1:3000/login" | grep -q 200; then
log "Healthcheck passed"
else
echo "ERROR: healthcheck failed"
echo "Check: ssh ${SSH_HOST} 'sudo journalctl -u ${SERVICE} -n 50'"
exit 1
fi
rm -rf "${STAGING_DIR}"
echo
echo "════════════════════════════════════════════════════"
echo " AkurAI Mail UI deployed"
echo "════════════════════════════════════════════════════"
echo " Version : ${VERSION}"
echo " Release : ${RELEASE_ID}"
echo " Host : ${SSH_HOST}"
echo " Public : https://mail.olibuijr.com"
echo "════════════════════════════════════════════════════"