206 lines
5.3 KiB
Bash
206 lines
5.3 KiB
Bash
#!/bin/bash
|
||
set -euo pipefail
|
||
umask 022
|
||
|
||
export NODE_OPTIONS="--max_old_space_size=4096"
|
||
export CI="true"
|
||
export npm_config_progress="false"
|
||
export npm_config_loglevel="warn"
|
||
export FORCE_COLOR="0"
|
||
|
||
LOG_FILE="/var/log/bssapp_deploy.log"
|
||
APP_DIR="/opt/bssapp"
|
||
LOCK_FILE="/tmp/bssapp_deploy.lock"
|
||
RUNTIME_BACKUP_DIR=""
|
||
RUNTIME_PRESERVE_FILES=(
|
||
".env"
|
||
"mail.env"
|
||
"svc/.env"
|
||
"svc/mail.env"
|
||
"svc/fonts"
|
||
"svc/public"
|
||
)
|
||
|
||
backup_runtime_files() {
|
||
RUNTIME_BACKUP_DIR="$(mktemp -d /tmp/bssapp-runtime.XXXXXX)"
|
||
|
||
for rel in "${RUNTIME_PRESERVE_FILES[@]}"; do
|
||
src="$APP_DIR/$rel"
|
||
dst="$RUNTIME_BACKUP_DIR/$rel"
|
||
|
||
if [[ -e "$src" ]]; then
|
||
mkdir -p "$(dirname "$dst")"
|
||
cp -a "$src" "$dst"
|
||
fi
|
||
done
|
||
}
|
||
|
||
restore_runtime_files() {
|
||
[[ -n "$RUNTIME_BACKUP_DIR" && -d "$RUNTIME_BACKUP_DIR" ]] || return 0
|
||
find "$RUNTIME_BACKUP_DIR" -mindepth 1 -print -quit | grep -q . || return 0
|
||
cp -a "$RUNTIME_BACKUP_DIR/." "$APP_DIR/"
|
||
}
|
||
|
||
cleanup_runtime_backup() {
|
||
if [[ -n "$RUNTIME_BACKUP_DIR" && -d "$RUNTIME_BACKUP_DIR" ]]; then
|
||
rm -rf "$RUNTIME_BACKUP_DIR"
|
||
fi
|
||
}
|
||
|
||
ensure_runtime_env_files() {
|
||
# Bazı unit dosyaları EnvironmentFile olarak bu path'leri bekliyor.
|
||
# Dosyalar yoksa systemd "Failed to load environment files" ile düşüyor.
|
||
[[ -f "$APP_DIR/.env" ]] || touch "$APP_DIR/.env"
|
||
[[ -f "$APP_DIR/mail.env" ]] || touch "$APP_DIR/mail.env"
|
||
[[ -f "$APP_DIR/svc/.env" ]] || touch "$APP_DIR/svc/.env"
|
||
[[ -f "$APP_DIR/svc/mail.env" ]] || touch "$APP_DIR/svc/mail.env"
|
||
}
|
||
|
||
ensure_pdf_fonts() {
|
||
font_dir="$APP_DIR/svc/fonts"
|
||
sys_font_dir="/usr/share/fonts/truetype/dejavu"
|
||
|
||
mkdir -p "$font_dir"
|
||
|
||
if [[ ! -f "$font_dir/DejaVuSans.ttf" && -f "$sys_font_dir/DejaVuSans.ttf" ]]; then
|
||
cp -a "$sys_font_dir/DejaVuSans.ttf" "$font_dir/DejaVuSans.ttf"
|
||
fi
|
||
if [[ ! -f "$font_dir/DejaVuSans-Bold.ttf" && -f "$sys_font_dir/DejaVuSans-Bold.ttf" ]]; then
|
||
cp -a "$sys_font_dir/DejaVuSans-Bold.ttf" "$font_dir/DejaVuSans-Bold.ttf"
|
||
fi
|
||
|
||
if [[ ! -f "$font_dir/DejaVuSans.ttf" || ! -f "$font_dir/DejaVuSans-Bold.ttf" ]]; then
|
||
echo "ERROR: Required PDF fonts missing in $font_dir (DejaVuSans.ttf / DejaVuSans-Bold.ttf)"
|
||
return 1
|
||
fi
|
||
}
|
||
|
||
ensure_ui_permissions() {
|
||
ui_root="$APP_DIR/ui/dist/spa"
|
||
|
||
if [[ ! -d "$ui_root" ]]; then
|
||
echo "ERROR: UI build output not found at $ui_root"
|
||
return 1
|
||
fi
|
||
|
||
# Nginx must be able to traverse parent directories and read built assets.
|
||
chmod 755 "$APP_DIR" "$APP_DIR/ui" "$APP_DIR/ui/dist" "$ui_root"
|
||
find "$ui_root" -type d -exec chmod 755 {} \;
|
||
find "$ui_root" -type f -exec chmod 644 {} \;
|
||
}
|
||
|
||
ensure_ui_readable_by_nginx() {
|
||
ui_index="$APP_DIR/ui/dist/spa/index.html"
|
||
|
||
if [[ ! -f "$ui_index" ]]; then
|
||
echo "ERROR: UI index not found at $ui_index"
|
||
return 1
|
||
fi
|
||
|
||
# Verify nginx user can read index.html and traverse parent directories.
|
||
if id -u www-data >/dev/null 2>&1; then
|
||
if ! su -s /bin/sh -c "test -r '$ui_index'" www-data; then
|
||
echo "ERROR: www-data cannot read $ui_index"
|
||
namei -l "$ui_index" || true
|
||
return 1
|
||
fi
|
||
fi
|
||
}
|
||
|
||
build_api_binary() {
|
||
if ! command -v go >/dev/null 2>&1; then
|
||
echo "go command not found; cannot build backend binary."
|
||
return 1
|
||
fi
|
||
|
||
export GOPATH="${GOPATH:-/var/cache/bssapp-go}"
|
||
export GOMODCACHE="${GOMODCACHE:-$GOPATH/pkg/mod}"
|
||
export GOCACHE="${GOCACHE:-/var/cache/bssapp-go-build}"
|
||
mkdir -p "$GOPATH" "$GOMODCACHE" "$GOCACHE"
|
||
|
||
cd "$APP_DIR/svc"
|
||
GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -ldflags="-w -s" -o "$APP_DIR/svc/bssapp" ./main.go
|
||
chmod +x "$APP_DIR/svc/bssapp"
|
||
}
|
||
|
||
run_deploy() {
|
||
trap cleanup_runtime_backup EXIT
|
||
|
||
exec 9>"$LOCK_FILE"
|
||
if ! flock -n 9; then
|
||
echo "[$(date '+%F %T')] Deploy already running. Skipping new request."
|
||
return 0
|
||
fi
|
||
|
||
echo "=============================="
|
||
echo "[DEPLOY START] $(date '+%F %T')"
|
||
echo "=============================="
|
||
|
||
cd "$APP_DIR"
|
||
|
||
echo "== GIT SYNC =="
|
||
backup_runtime_files
|
||
git fetch origin
|
||
git reset --hard origin/master
|
||
git clean -fdx \
|
||
-e .env \
|
||
-e mail.env \
|
||
-e svc/.env \
|
||
-e svc/mail.env \
|
||
-e svc/fonts \
|
||
-e svc/public \
|
||
-e svc/bssapp
|
||
restore_runtime_files
|
||
|
||
echo "== BUILD UI =="
|
||
cd "$APP_DIR/ui"
|
||
npm ci --no-audit --no-fund --include=optional
|
||
|
||
# Linux'ta sass --embedded hatasını engellemek için
|
||
# deploy sırasında çalışan node_modules ağacına doğrudan yazıyoruz.
|
||
npm i -D --no-audit --no-fund sass-embedded@1.93.2
|
||
|
||
npm run build
|
||
|
||
echo "== ENSURE UI PERMISSIONS =="
|
||
ensure_ui_permissions
|
||
ensure_ui_readable_by_nginx
|
||
|
||
echo "== BUILD API =="
|
||
build_api_binary
|
||
|
||
echo "== ENSURE ENV FILES =="
|
||
ensure_runtime_env_files
|
||
|
||
echo "== ENSURE PDF FONTS =="
|
||
ensure_pdf_fonts
|
||
|
||
echo "== RESTART SERVICES =="
|
||
systemctl restart bssapp
|
||
if systemctl cat nginx >/dev/null 2>&1; then
|
||
systemctl restart nginx
|
||
if ! systemctl is-active --quiet nginx; then
|
||
echo "ERROR: nginx service failed to start"
|
||
return 1
|
||
fi
|
||
else
|
||
echo "WARN: nginx service not found; frontend may be unreachable."
|
||
fi
|
||
|
||
if ! systemctl is-active --quiet bssapp; then
|
||
echo "ERROR: bssapp service failed to start"
|
||
return 1
|
||
fi
|
||
|
||
echo "[DEPLOY FINISHED] $(date '+%F %T')"
|
||
}
|
||
|
||
if [[ "${1:-}" == "--run" ]]; then
|
||
run_deploy >>"$LOG_FILE" 2>&1
|
||
exit 0
|
||
fi
|
||
|
||
# Fully detach webhook-triggered process to avoid EPIPE from closed request sockets.
|
||
nohup /bin/bash "$0" --run </dev/null >/dev/null 2>&1 &
|
||
exit 0
|