-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathdeploy-prod.sh
More file actions
executable file
·136 lines (117 loc) · 5.85 KB
/
deploy-prod.sh
File metadata and controls
executable file
·136 lines (117 loc) · 5.85 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
#!/usr/bin/env bash
# deploy-prod.sh - Generic local deployment helper.
#
# This script is intentionally free of host-specific paths, domains, and
# production configuration. Provide local deployment details via environment
# variables or untracked files outside the public repository.
#
# Usage:
# PROD_DIR=/path/to/runtime ./deploy-prod.sh
# PROD_DIR=/path/to/runtime ./deploy-prod.sh 1.2.3
# ./deploy-prod.sh --help
set -euo pipefail
SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
DEV_DIR="${DEV_DIR:-$SCRIPT_DIR}"
PROD_DIR="${PROD_DIR:-}"
IMAGE_NAME="${IMAGE_NAME:-mdedit-app}"
PROD_COMPOSE_FILE="${PROD_COMPOSE_FILE:-${PROD_DIR:+$PROD_DIR/docker-compose.yml}}"
PROD_HEALTH_URL="${PROD_HEALTH_URL:-http://localhost:3211/health}"
FALLBACK_DIR="${FALLBACK_DIR:-}"
FALLBACK_USE_SUDO="${FALLBACK_USE_SUDO:-0}"
# ── Help ─────────────────────────────────────────────────────────────────────
if [[ "${1:-}" == "--help" ]]; then
echo "Usage: $0 [version]"
echo " version optional semver (e.g. 1.2.3). Defaults to current package.json version."
echo
echo "Environment variables:"
echo " PROD_DIR Required. Runtime directory containing docker-compose.yml and data/."
echo " PROD_COMPOSE_FILE Optional. Defaults to \$PROD_DIR/docker-compose.yml."
echo " PROD_HEALTH_URL Optional. Defaults to http://localhost:3211/health."
echo " IMAGE_NAME Optional. Defaults to mdedit-app."
echo " FALLBACK_DIR Optional. If set, copies entry-page fallback files for nginx/proxy use."
echo " FALLBACK_USE_SUDO Optional. Set to 1 to copy fallback files via sudo."
exit 0
fi
cd "$DEV_DIR"
run_fallback_cmd() {
if [[ "$FALLBACK_USE_SUDO" == "1" ]]; then
sudo "$@"
else
"$@"
fi
}
NODE_MAJOR=$(node -p "process.versions.node.split('.')[0]")
if [[ "$NODE_MAJOR" -lt 20 ]]; then
echo "✗ Node 20+ is required for release checks and current production dependencies. Found: $(node -v)"
exit 1
fi
if [[ -z "$PROD_DIR" ]]; then
echo "✗ PROD_DIR is required. Keep host-specific runtime paths outside the public repo."
exit 1
fi
if [[ -z "$PROD_COMPOSE_FILE" || ! -f "$PROD_COMPOSE_FILE" ]]; then
echo "✗ Production compose file not found: ${PROD_COMPOSE_FILE:-<unset>}"
exit 1
fi
# ── Determine version ────────────────────────────────────────────────────────
CURRENT_VERSION=$(node -e "console.log(require('./package.json').version)")
if [[ -n "${1:-}" ]]; then
NEW_VERSION="$1"
else
NEW_VERSION="$CURRENT_VERSION"
fi
echo "► Deploying version $NEW_VERSION"
echo " Running release checks ..."
npm run release:check
# ── Bump version in package.json if changed ───────────────────────────────────
if [[ "$NEW_VERSION" != "$CURRENT_VERSION" ]]; then
echo " Bumping version: $CURRENT_VERSION → $NEW_VERSION"
node -e "
const fs = require('fs');
const pkg = JSON.parse(fs.readFileSync('package.json','utf8'));
pkg.version = '$NEW_VERSION';
fs.writeFileSync('package.json', JSON.stringify(pkg, null, 2) + '\n');
"
fi
# ── Build Docker image ────────────────────────────────────────────────────────
echo " Building image $IMAGE_NAME:$NEW_VERSION ..."
docker build -t "$IMAGE_NAME:$NEW_VERSION" -t "$IMAGE_NAME:latest" .
# ── Optional: sync static fallback assets ─────────────────────────────────────
if [[ -n "$FALLBACK_DIR" ]]; then
echo " Syncing fallback assets to $FALLBACK_DIR ..."
run_fallback_cmd install -d "$FALLBACK_DIR"
run_fallback_cmd install -m 644 "$DEV_DIR/public/index.html" "$FALLBACK_DIR/index.html"
run_fallback_cmd install -m 644 "$DEV_DIR/public/help.html" "$FALLBACK_DIR/help.html"
run_fallback_cmd install -m 644 "$DEV_DIR/public/help-en.html" "$FALLBACK_DIR/help-en.html"
run_fallback_cmd install -m 644 "$DEV_DIR/public/brand/mdedit-icon.png" "$FALLBACK_DIR/mdedit-icon.png"
if [[ -f "$DEV_DIR/public/brand/social-card.png" ]]; then
run_fallback_cmd install -m 644 "$DEV_DIR/public/brand/social-card.png" "$FALLBACK_DIR/social-card.png"
fi
fi
# ── Restart production container ──────────────────────────────────────────────
echo " Restarting production container ..."
cd "$PROD_DIR"
docker compose pull --ignore-buildable 2>/dev/null || true
docker compose up -d --force-recreate
# ── Health check ─────────────────────────────────────────────────────────────
echo " Waiting for health check ..."
sleep 5
HTTP_STATUS=$(curl -s -o /dev/null -w "%{http_code}" "$PROD_HEALTH_URL" || echo "000")
if [[ "$HTTP_STATUS" == "200" ]]; then
echo "✓ Production is up and healthy (v$NEW_VERSION)"
else
echo "✗ Health check failed (HTTP $HTTP_STATUS) - check: docker compose -f $PROD_COMPOSE_FILE logs"
exit 1
fi
# ── Optional: commit & tag in git ─────────────────────────────────────────────
cd "$DEV_DIR"
if git diff --quiet HEAD -- package.json; then
echo " (package.json unchanged, no git tag)"
else
echo " Committing version bump and tagging v$NEW_VERSION ..."
git add package.json
git commit -m "chore: release v$NEW_VERSION"
git tag "v$NEW_VERSION"
echo " Run 'git push && git push --tags' to publish."
fi
echo "Done. Production deployment finished."