Make deploy.sh data-source agnostic with --scripts-only flag

- Add --scripts-only flag for quick script/docs deployment without restart
- Replace hardcoded Keboola env vars with generic loop over all known vars
  (supports Keboola, BigQuery, OpenMetadata, and optional services)
- Make data directories conditional (Jira, notifications, corporate memory
  created only when relevant code/config exists)
- Enable timers only when their .timer files exist on disk
- Use root:data-ops ownership (works without deploy user)
This commit is contained in:
Petr 2026-03-14 20:38:43 +01:00
parent c2681ccc86
commit 4206b06d92

View file

@ -1,6 +1,13 @@
#!/bin/bash #!/bin/bash
# Deploy script for Data Analyst application # Deploy script for Data Analyst application
# This script is called by GitHub Actions or manually to deploy updates # This script is called by GitHub Actions or manually to deploy updates.
#
# Works with any data source (Keboola, BigQuery, etc.) — instance-specific
# configuration comes from instance.yaml and GHA secrets, not from this script.
#
# Usage:
# bash server/deploy.sh # Full deploy (from GHA or manually)
# bash server/deploy.sh --scripts-only # Only update /data/scripts and /data/docs
set -euo pipefail set -euo pipefail
@ -10,6 +17,14 @@ VENV_DIR="${APP_DIR}/.venv"
LOG_DIR="${APP_DIR}/logs" LOG_DIR="${APP_DIR}/logs"
DEPLOY_LOG="${LOG_DIR}/deploy.log" DEPLOY_LOG="${LOG_DIR}/deploy.log"
# Parse arguments
SCRIPTS_ONLY=false
for arg in "$@"; do
case "$arg" in
--scripts-only) SCRIPTS_ONLY=true ;;
esac
done
log() { log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" | tee -a "$DEPLOY_LOG" echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" | tee -a "$DEPLOY_LOG"
} }
@ -31,36 +46,41 @@ cd "$REPO_DIR" || error "Cannot cd to $REPO_DIR"
# Ensure git trusts this directory # Ensure git trusts this directory
git config --global --add safe.directory "$REPO_DIR" 2>/dev/null || true git config --global --add safe.directory "$REPO_DIR" 2>/dev/null || true
# Pull latest changes if [[ "$SCRIPTS_ONLY" == false ]]; then
log "Pulling latest changes from origin/main..." # Pull latest changes
git fetch origin log "Pulling latest changes from origin/main..."
git reset --hard origin/main git fetch origin
git reset --hard origin/main
# Update Python dependencies if requirements.txt changed # Update Python dependencies if requirements.txt changed
if git diff HEAD@{1} --name-only 2>/dev/null | grep -q "requirements.txt"; then if git diff HEAD@{1} --name-only 2>/dev/null | grep -q "requirements.txt"; then
log "requirements.txt changed, updating dependencies..." log "requirements.txt changed, updating dependencies..."
source "${VENV_DIR}/bin/activate" source "${VENV_DIR}/bin/activate"
pip install -q -r requirements.txt pip install -q -r requirements.txt
deactivate deactivate
fi
fi fi
# Update server management scripts # --- Core: scripts and docs (always runs) ---
# Update server management scripts (add-analyst, list-analysts, etc.)
log "Updating server management scripts..." log "Updating server management scripts..."
for script in "${REPO_DIR}"/server/bin/*; do if compgen -G "${REPO_DIR}/server/bin/*" > /dev/null 2>&1; then
if [[ -f "$script" ]]; then for script in "${REPO_DIR}"/server/bin/*; do
script_name=$(basename "$script") if [[ -f "$script" ]]; then
sudo /usr/bin/cp "$script" "/usr/local/bin/${script_name}" script_name=$(basename "$script")
sudo /usr/bin/chmod 755 "/usr/local/bin/${script_name}" sudo /usr/bin/cp "$script" "/usr/local/bin/${script_name}"
log " Updated /usr/local/bin/${script_name}" sudo /usr/bin/chmod 755 "/usr/local/bin/${script_name}"
fi log " Updated /usr/local/bin/${script_name}"
done fi
done
fi
# Update sudoers configurations # Update sudoers configurations
log "Updating sudoers configurations..." log "Updating sudoers configurations..."
for sudoers_file in "${REPO_DIR}"/server/sudoers-*; do for sudoers_file in "${REPO_DIR}"/server/sudoers-*; do
if [[ -f "$sudoers_file" ]]; then if [[ -f "$sudoers_file" ]]; then
sudoers_name=$(basename "$sudoers_file" | sed 's/sudoers-//') sudoers_name=$(basename "$sudoers_file" | sed 's/sudoers-//')
# Validate before installing
if sudo /usr/sbin/visudo -cf "$sudoers_file" 2>/dev/null; then if sudo /usr/sbin/visudo -cf "$sudoers_file" 2>/dev/null; then
sudo /usr/bin/cp "$sudoers_file" "/etc/sudoers.d/${sudoers_name}" sudo /usr/bin/cp "$sudoers_file" "/etc/sudoers.d/${sudoers_name}"
sudo /usr/bin/chmod 440 "/etc/sudoers.d/${sudoers_name}" sudo /usr/bin/chmod 440 "/etc/sudoers.d/${sudoers_name}"
@ -71,123 +91,132 @@ for sudoers_file in "${REPO_DIR}"/server/sudoers-*; do
fi fi
done done
# Update user scripts in /data/scripts # Update user-facing scripts in /data/scripts
# These are synced to analyst machines via sync_data.sh
log "Updating scripts in /data/scripts/..." log "Updating scripts in /data/scripts/..."
sudo /usr/bin/mkdir -p /data/scripts sudo /usr/bin/mkdir -p /data/scripts
sudo /usr/bin/cp "${REPO_DIR}"/scripts/setup_views.sh /data/scripts/ for script_file in setup_views.sh duckdb_manager.py sync_data.sh activate_venv.sh \
sudo /usr/bin/cp "${REPO_DIR}"/scripts/duckdb_manager.py /data/scripts/ README.md generate_user_sync_configs.py collect_session.py; do
sudo /usr/bin/cp "${REPO_DIR}"/scripts/sync_data.sh /data/scripts/ if [[ -f "${REPO_DIR}/scripts/${script_file}" ]]; then
sudo /usr/bin/cp "${REPO_DIR}"/scripts/activate_venv.sh /data/scripts/ sudo /usr/bin/cp "${REPO_DIR}/scripts/${script_file}" /data/scripts/
sudo /usr/bin/cp "${REPO_DIR}"/scripts/README.md /data/scripts/ fi
sudo /usr/bin/cp "${REPO_DIR}"/scripts/sync_jira.sh /data/scripts/ done
sudo /usr/bin/cp "${REPO_DIR}"/scripts/generate_user_sync_configs.py /data/scripts/ # Copy connector-specific sync scripts (e.g. sync_jira.sh) if they exist
sudo /usr/bin/cp "${REPO_DIR}"/scripts/collect_session.py /data/scripts/ for sync_script in "${REPO_DIR}"/connectors/*/scripts/sync_*.sh "${REPO_DIR}"/scripts/sync_*.sh; do
if [[ -f "$sync_script" ]]; then
sudo /usr/bin/cp "$sync_script" /data/scripts/
fi
done
sudo /usr/bin/chmod -R 755 /data/scripts sudo /usr/bin/chmod -R 755 /data/scripts
sudo /usr/bin/chown -R deploy:data-ops /data/scripts sudo /usr/bin/chown -R root:data-ops /data/scripts
log " Scripts updated in /data/scripts/" log " Scripts updated in /data/scripts/"
# Update documentation in /data/docs # Update documentation in /data/docs
log "Updating documentation..." log "Updating documentation..."
sudo /usr/bin/mkdir -p /data/docs/setup sudo /usr/bin/mkdir -p /data/docs/setup
if [[ -f "${REPO_DIR}/docs/data_description.md" ]]; then # Core docs (copy if they exist)
sudo /usr/bin/cp "${REPO_DIR}"/docs/data_description.md /data/docs/ for doc_file in data_description.md GETTING_STARTED.md notifications.md jira_schema.md schema.yml; do
fi if [[ -f "${REPO_DIR}/docs/${doc_file}" ]]; then
sudo /usr/bin/cp "${REPO_DIR}"/docs/GETTING_STARTED.md /data/docs/ sudo /usr/bin/cp "${REPO_DIR}/docs/${doc_file}" /data/docs/
if [[ -f "${REPO_DIR}/docs/notifications.md" ]]; then fi
sudo /usr/bin/cp "${REPO_DIR}"/docs/notifications.md /data/docs/ done
fi # Setup docs
if [[ -f "${REPO_DIR}/docs/jira_schema.md" ]]; then for setup_file in bootstrap.yaml claude_md_template.txt claude_settings.json; do
sudo /usr/bin/cp "${REPO_DIR}"/docs/jira_schema.md /data/docs/ if [[ -f "${REPO_DIR}/docs/setup/${setup_file}" ]]; then
fi sudo /usr/bin/cp "${REPO_DIR}/docs/setup/${setup_file}" /data/docs/setup/
sudo /usr/bin/cp "${REPO_DIR}"/docs/setup/bootstrap.yaml /data/docs/setup/ fi
sudo /usr/bin/cp "${REPO_DIR}"/docs/setup/claude_md_template.txt /data/docs/setup/ done
sudo /usr/bin/cp "${REPO_DIR}"/docs/setup/claude_settings.json /data/docs/setup/ # Metrics definitions
if [[ -d "${REPO_DIR}/docs/metrics" ]]; then if [[ -d "${REPO_DIR}/docs/metrics" ]]; then
sudo /usr/bin/cp -r "${REPO_DIR}"/docs/metrics /data/docs/ sudo /usr/bin/cp -r "${REPO_DIR}"/docs/metrics /data/docs/
fi fi
# Note: schema.yml files are generated directly to DOCS_OUTPUT_DIR by data_sync.py # Dataset documentation
# Here we only copy static *.md files from datasets/
if [[ -d "${REPO_DIR}/docs/datasets" ]]; then if [[ -d "${REPO_DIR}/docs/datasets" ]]; then
sudo /usr/bin/mkdir -p /data/docs/datasets sudo /usr/bin/mkdir -p /data/docs/datasets
# Copy only .md files (glob expands before sudo)
if compgen -G "${REPO_DIR}/docs/datasets/*.md" > /dev/null; then if compgen -G "${REPO_DIR}/docs/datasets/*.md" > /dev/null; then
sudo /usr/bin/cp "${REPO_DIR}"/docs/datasets/*.md /data/docs/datasets/ sudo /usr/bin/cp "${REPO_DIR}"/docs/datasets/*.md /data/docs/datasets/
fi fi
log " Dataset docs (*.md) copied to /data/docs/datasets/" log " Dataset docs copied to /data/docs/datasets/"
fi fi
sudo /usr/bin/chmod -R 775 /data/docs sudo /usr/bin/chmod -R 775 /data/docs
sudo /usr/bin/chown -R deploy:data-ops /data/docs sudo /usr/bin/chown -R root:data-ops /data/docs
log " Documentation updated in /data/docs/" log " Documentation updated in /data/docs/"
# Deploy notify-runner to /usr/local/bin # Deploy examples (notifications, queries, etc.)
log "Deploying notify-runner..." log "Deploying examples..."
if [[ -f "${REPO_DIR}/server/bin/notify-runner" ]]; then if [[ -d "${REPO_DIR}/examples" ]]; then
sudo /usr/bin/cp "${REPO_DIR}/server/bin/notify-runner" /usr/local/bin/notify-runner sudo /usr/bin/mkdir -p /data/examples
sudo /usr/bin/chmod 755 /usr/local/bin/notify-runner sudo /usr/bin/cp -r "${REPO_DIR}"/examples/* /data/examples/ 2>/dev/null || true
log " Updated /usr/local/bin/notify-runner" sudo /usr/bin/chmod -R 755 /data/examples
sudo /usr/bin/chown -R root:data-ops /data/examples
fi fi
# Deploy notify-scripts helper to /usr/local/bin if [[ "$SCRIPTS_ONLY" == true ]]; then
log "Deploying notify-scripts..." log "Scripts-only deployment completed successfully!"
if [[ -f "${REPO_DIR}/server/bin/notify-scripts" ]]; then exit 0
sudo /usr/bin/cp "${REPO_DIR}/server/bin/notify-scripts" /usr/local/bin/notify-scripts
sudo /usr/bin/chmod 755 /usr/local/bin/notify-scripts
log " Updated /usr/local/bin/notify-scripts"
fi fi
# Create notifications data directory # --- Optional: server management scripts ---
log "Setting up notifications directory..."
sudo /usr/bin/mkdir -p /data/notifications
sudo /usr/bin/chown deploy:data-ops /data/notifications
sudo /usr/bin/chmod 2770 /data/notifications # setgid, no others access (socket is in /run/notify-bot/)
# Ensure deploy user is in dataread group (needed for notify-bot socket group ownership) # Deploy helper binaries to /usr/local/bin (notify-runner, notify-scripts, etc.)
if ! id -nG deploy | grep -qw dataread; then for bin_file in "${REPO_DIR}"/server/bin/*; do
sudo /usr/sbin/usermod -a -G dataread deploy if [[ -f "$bin_file" ]]; then
log " Added deploy user to dataread group" bin_name=$(basename "$bin_file")
sudo /usr/bin/cp "$bin_file" "/usr/local/bin/${bin_name}"
sudo /usr/bin/chmod 755 "/usr/local/bin/${bin_name}"
log " Updated /usr/local/bin/${bin_name}"
fi
done
# --- Optional: data directories (created only if relevant features exist) ---
# Notifications directory
if [[ -f "${REPO_DIR}/server/bin/notify-runner" ]] || [[ -n "${TELEGRAM_BOT_TOKEN:-}" ]]; then
log "Setting up notifications directory..."
sudo /usr/bin/mkdir -p /data/notifications
sudo /usr/bin/chown root:data-ops /data/notifications
sudo /usr/bin/chmod 2770 /data/notifications
fi fi
# Create Jira webhook data directory (raw data, will be processed to parquet later) # Jira data directory (only if Jira connector exists)
log "Setting up Jira data directory..." if [[ -d "${REPO_DIR}/connectors/jira" ]]; then
sudo /usr/bin/mkdir -p /data/src_data/raw/jira/issues log "Setting up Jira data directory..."
sudo /usr/bin/mkdir -p /data/src_data/raw/jira/webhook_events sudo /usr/bin/mkdir -p /data/src_data/raw/jira/issues
sudo /usr/bin/mkdir -p /data/src_data/raw/jira/attachments sudo /usr/bin/mkdir -p /data/src_data/raw/jira/webhook_events
sudo /usr/bin/chown -R root:data-ops /data/src_data/raw/jira sudo /usr/bin/mkdir -p /data/src_data/raw/jira/attachments
sudo /usr/bin/chmod -R 2770 /data/src_data/raw/jira # setgid, www-data (data-ops member) can write sudo /usr/bin/chown -R root:data-ops /data/src_data/raw/jira
sudo /usr/bin/chmod -R 2770 /data/src_data/raw/jira
# ACL for read access by analysts
if command -v setfacl &>/dev/null; then
sudo /usr/bin/setfacl -R -m g:dataread:rx /data/src_data/raw/jira/attachments 2>/dev/null || true
sudo /usr/bin/setfacl -R -d -m g:dataread:rx /data/src_data/raw/jira/attachments 2>/dev/null || true
fi
fi
# Create password auth data directory # Password auth directory (only if password auth module exists)
log "Setting up password auth directory..." if [[ -f "${REPO_DIR}/auth/password.py" ]]; then
sudo /usr/bin/mkdir -p /data/auth log "Setting up password auth directory..."
sudo /usr/bin/chown www-data:data-ops /data/auth sudo /usr/bin/mkdir -p /data/auth
sudo /usr/bin/chmod 2770 /data/auth # setgid, www-data can write, no others access sudo /usr/bin/chown www-data:data-ops /data/auth
sudo /usr/bin/chmod 2770 /data/auth
fi
# Create corporate memory data directory # Corporate memory directory
log "Setting up corporate memory directory..." if [[ -d "${REPO_DIR}/services/corporate-memory" ]]; then
sudo /usr/bin/mkdir -p /data/corporate-memory log "Setting up corporate memory directory..."
sudo /usr/bin/chown deploy:data-ops /data/corporate-memory sudo /usr/bin/mkdir -p /data/corporate-memory
sudo /usr/bin/chmod 2770 /data/corporate-memory # setgid, deploy can write sudo /usr/bin/chown root:data-ops /data/corporate-memory
sudo /usr/bin/chmod 2770 /data/corporate-memory
fi
# Create user sessions data directory # User sessions directory
log "Setting up user sessions directory..." log "Setting up user sessions directory..."
sudo /usr/bin/mkdir -p /data/user_sessions sudo /usr/bin/mkdir -p /data/user_sessions
sudo /usr/bin/chown root:data-ops /data/user_sessions sudo /usr/bin/chown root:data-ops /data/user_sessions
sudo /usr/bin/chmod 2770 /data/user_sessions # setgid, root writes, admins only sudo /usr/bin/chmod 2770 /data/user_sessions
# Create staging directory for data sync (uses /tmp for faster I/O) # Private data ACL (only if private directory exists)
log "Setting up staging directory..." if [[ -d /data/src_data/parquet/private ]] && command -v setfacl &>/dev/null; then
sudo /usr/bin/mkdir -p /tmp/data_analyst_staging
sudo /usr/bin/chown root:data-ops /tmp/data_analyst_staging
sudo /usr/bin/chmod 2770 /tmp/data_analyst_staging # setgid, data-ops can write
# Add read access to Jira attachments for analysts (dataread group)
if command -v setfacl &>/dev/null; then
sudo /usr/bin/setfacl -R -m g:dataread:rx /data/src_data/raw/jira/attachments 2>/dev/null || true
sudo /usr/bin/setfacl -R -d -m g:dataread:rx /data/src_data/raw/jira/attachments 2>/dev/null || true
log " ACL set for dataread group on Jira attachments"
fi
# Set ACL for private data directory (data-private group only, remove dataread)
if command -v setfacl &>/dev/null; then
sudo /usr/bin/setfacl -R -m g:data-private:rx /data/src_data/parquet/private/ 2>/dev/null || true sudo /usr/bin/setfacl -R -m g:data-private:rx /data/src_data/parquet/private/ 2>/dev/null || true
sudo /usr/bin/setfacl -R -d -m g:data-private:rx /data/src_data/parquet/private/ 2>/dev/null || true sudo /usr/bin/setfacl -R -d -m g:data-private:rx /data/src_data/parquet/private/ 2>/dev/null || true
sudo /usr/bin/setfacl -R -x g:dataread /data/src_data/parquet/private/ 2>/dev/null || true sudo /usr/bin/setfacl -R -x g:dataread /data/src_data/parquet/private/ 2>/dev/null || true
@ -195,7 +224,8 @@ if command -v setfacl &>/dev/null; then
log " ACL set for data-private group on private parquet directory" log " ACL set for data-private group on private parquet directory"
fi fi
# Deploy systemd service and timer files from services/ and connectors/ # --- Deploy systemd services and timers ---
log "Deploying systemd service and timer files..." log "Deploying systemd service and timer files..."
SYSTEMD_CHANGED=false SYSTEMD_CHANGED=false
for unit_file in "${REPO_DIR}"/services/*/systemd/*.service "${REPO_DIR}"/services/*/systemd/*.timer \ for unit_file in "${REPO_DIR}"/services/*/systemd/*.service "${REPO_DIR}"/services/*/systemd/*.timer \
@ -219,154 +249,128 @@ if [[ -f "/etc/systemd/system/jira-consistency.service" ]]; then
sudo /usr/bin/chmod 664 /opt/data-analyst/logs/jira-consistency.log sudo /usr/bin/chmod 664 /opt/data-analyst/logs/jira-consistency.log
fi fi
# Deploy example notification scripts to /data/examples
log "Deploying example notification scripts..."
sudo /usr/bin/mkdir -p /data/examples/notifications
for example in "${REPO_DIR}"/examples/notifications/*.py; do
if [[ -f "$example" ]]; then
sudo /usr/bin/cp "$example" /data/examples/notifications/
fi
done
sudo /usr/bin/chmod -R 755 /data/examples
sudo /usr/bin/chown -R deploy:data-ops /data/examples
# Update resource limits configuration # Update resource limits configuration
log "Updating resource limits..."
if [[ -f "${REPO_DIR}/server/limits-users.conf" ]]; then if [[ -f "${REPO_DIR}/server/limits-users.conf" ]]; then
log "Updating resource limits..."
sudo /usr/bin/cp "${REPO_DIR}/server/limits-users.conf" /etc/security/limits.d/99-users.conf sudo /usr/bin/cp "${REPO_DIR}/server/limits-users.conf" /etc/security/limits.d/99-users.conf
sudo /usr/bin/chmod 644 /etc/security/limits.d/99-users.conf sudo /usr/bin/chmod 644 /etc/security/limits.d/99-users.conf
log " Updated /etc/security/limits.d/99-users.conf"
fi fi
# Create data sync .env file from environment variables (passed from GitHub Actions) # --- Create .env for data sync (data-source agnostic) ---
SYNC_ENV_FILE="${REPO_DIR}/.env" SYNC_ENV_FILE="${REPO_DIR}/.env"
if [[ -n "${KEBOOLA_STORAGE_TOKEN:-}" ]]; then
log "Creating data sync .env file..." # Write all known env vars that are set (works for any data source)
{ log "Creating data sync .env file..."
echo "KEBOOLA_STORAGE_TOKEN=${KEBOOLA_STORAGE_TOKEN}" {
echo "KEBOOLA_STACK_URL=${KEBOOLA_STACK_URL}" # Core settings (always written if set)
echo "KEBOOLA_PROJECT_ID=${KEBOOLA_PROJECT_ID}" for var in DATA_DIR DATA_SOURCE DOCS_OUTPUT_DIR LOG_LEVEL; do
echo "DATA_DIR=${DATA_DIR}" if [[ -n "${!var:-}" ]]; then
echo "DATA_SOURCE=${DATA_SOURCE}" echo "${var}=${!var}"
echo "LOG_LEVEL=${LOG_LEVEL}"
if [[ -n "${DOCS_OUTPUT_DIR:-}" ]]; then
echo "DOCS_OUTPUT_DIR=${DOCS_OUTPUT_DIR}"
fi fi
if [[ -n "${TELEGRAM_BOT_TOKEN:-}" ]]; then done
echo "TELEGRAM_BOT_TOKEN=${TELEGRAM_BOT_TOKEN}"
# Keboola data source
for var in KEBOOLA_STORAGE_TOKEN KEBOOLA_STACK_URL KEBOOLA_PROJECT_ID; do
if [[ -n "${!var:-}" ]]; then
echo "${var}=${!var}"
fi fi
if [[ -n "${DESKTOP_JWT_SECRET:-}" ]]; then done
echo "DESKTOP_JWT_SECRET=${DESKTOP_JWT_SECRET}"
# BigQuery data source
for var in BIGQUERY_PROJECT BIGQUERY_LOCATION; do
if [[ -n "${!var:-}" ]]; then
echo "${var}=${!var}"
fi fi
if [[ -n "${SENDGRID_API_KEY:-}" ]]; then done
echo "SENDGRID_API_KEY=${SENDGRID_API_KEY}"
# OpenMetadata catalog
for var in OPENMETADATA_TOKEN; do
if [[ -n "${!var:-}" ]]; then
echo "${var}=${!var}"
fi fi
if [[ -n "${JIRA_SLA_EMAIL:-}" ]]; then done
echo "JIRA_SLA_EMAIL=${JIRA_SLA_EMAIL}"
# Optional services (written only if set)
for var in TELEGRAM_BOT_TOKEN DESKTOP_JWT_SECRET SENDGRID_API_KEY \
JIRA_SLA_EMAIL JIRA_SLA_API_TOKEN JIRA_CLOUD_ID \
EMAIL_FROM_ADDRESS EMAIL_FROM_NAME ALLOWED_EMAILS \
ANTHROPIC_API_KEY; do
if [[ -n "${!var:-}" ]]; then
echo "${var}=${!var}"
fi fi
if [[ -n "${JIRA_SLA_API_TOKEN:-}" ]]; then done
echo "JIRA_SLA_API_TOKEN=${JIRA_SLA_API_TOKEN}" } | sudo /usr/bin/tee "$SYNC_ENV_FILE" > /dev/null
fi
if [[ -n "${JIRA_CLOUD_ID:-}" ]]; then # Only set permissions if file has content
echo "JIRA_CLOUD_ID=${JIRA_CLOUD_ID}" if [[ -s "$SYNC_ENV_FILE" ]]; then
fi
if [[ -n "${EMAIL_FROM_ADDRESS:-}" ]]; then
echo "EMAIL_FROM_ADDRESS=${EMAIL_FROM_ADDRESS}"
fi
if [[ -n "${EMAIL_FROM_NAME:-}" ]]; then
echo "EMAIL_FROM_NAME=${EMAIL_FROM_NAME}"
fi
if [[ -n "${ALLOWED_EMAILS:-}" ]]; then
echo "ALLOWED_EMAILS=${ALLOWED_EMAILS}"
fi
if [[ -n "${ANTHROPIC_API_KEY:-}" ]]; then
echo "ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY}"
fi
} | sudo /usr/bin/tee "$SYNC_ENV_FILE" > /dev/null
sudo /usr/bin/chown root:data-ops "$SYNC_ENV_FILE" sudo /usr/bin/chown root:data-ops "$SYNC_ENV_FILE"
sudo /usr/bin/chmod 640 "$SYNC_ENV_FILE" sudo /usr/bin/chmod 640 "$SYNC_ENV_FILE"
log " Data sync .env created with secure permissions (640)" log " Data sync .env created with secure permissions (640)"
else else
log " Skipping data sync .env creation (no sync credentials provided)" log " No environment variables provided, .env is empty"
fi fi
# Set correct permissions # --- Set correct permissions ---
log "Setting permissions..." log "Setting permissions..."
sudo /usr/bin/chown -R root:data-ops "$APP_DIR" sudo /usr/bin/chown -R root:data-ops "$APP_DIR"
sudo /usr/bin/chmod -R 770 "$APP_DIR" # owner+group rwx, others none sudo /usr/bin/chmod -R 770 "$APP_DIR"
sudo /usr/bin/chmod -R g+s "$APP_DIR" # setgid for new files sudo /usr/bin/chmod -R g+s "$APP_DIR"
# Restore .env permissions (may have been overwritten by chmod -R) # Restore .env permissions (may have been overwritten by chmod -R)
if [[ -f "$SYNC_ENV_FILE" ]]; then if [[ -f "$SYNC_ENV_FILE" ]]; then
sudo /usr/bin/chmod 640 "$SYNC_ENV_FILE" sudo /usr/bin/chmod 640 "$SYNC_ENV_FILE"
fi fi
# Update and restart webapp if running # --- Restart services ---
# Webapp (always restart if running)
if systemctl is-active --quiet webapp 2>/dev/null || systemctl is-enabled --quiet webapp 2>/dev/null; then if systemctl is-active --quiet webapp 2>/dev/null || systemctl is-enabled --quiet webapp 2>/dev/null; then
log "Updating webapp service..." log "Updating webapp service..."
sudo /usr/bin/cp "${REPO_DIR}/server/webapp.service" /etc/systemd/system/webapp.service if [[ -f "${REPO_DIR}/server/webapp.service" ]]; then
sudo /usr/bin/systemctl daemon-reload sudo /usr/bin/cp "${REPO_DIR}/server/webapp.service" /etc/systemd/system/webapp.service
sudo /usr/bin/systemctl daemon-reload
fi
log "Restarting webapp..." log "Restarting webapp..."
sudo /usr/bin/systemctl restart webapp sudo /usr/bin/systemctl restart webapp
fi fi
# Restart notify-bot if running # Optional services (restart only if already running or newly configured)
if systemctl is-active --quiet notify-bot 2>/dev/null; then for svc in notify-bot ws-gateway; do
log "Restarting notify-bot..." if systemctl is-active --quiet "$svc" 2>/dev/null; then
sudo /usr/bin/systemctl restart notify-bot log "Restarting ${svc}..."
elif [[ -n "${TELEGRAM_BOT_TOKEN:-}" ]]; then sudo /usr/bin/systemctl restart "$svc"
log "Starting notify-bot service..." fi
sudo /usr/bin/systemctl enable notify-bot done
sudo /usr/bin/systemctl start notify-bot
fi
# Restart ws-gateway if running # Enable notify-bot if Telegram token is newly provided
if systemctl is-active --quiet ws-gateway 2>/dev/null; then if [[ -n "${TELEGRAM_BOT_TOKEN:-}" ]] && ! systemctl is-active --quiet notify-bot 2>/dev/null; then
log "Restarting ws-gateway..." if [[ -f "/etc/systemd/system/notify-bot.service" ]]; then
sudo /usr/bin/systemctl restart ws-gateway log "Starting notify-bot service..."
elif [[ -n "${DESKTOP_JWT_SECRET:-}" ]]; then sudo /usr/bin/systemctl enable notify-bot
log "Starting ws-gateway service..." sudo /usr/bin/systemctl start notify-bot
sudo /usr/bin/systemctl enable ws-gateway
sudo /usr/bin/systemctl start ws-gateway
fi
# Enable corporate-memory timer if ANTHROPIC_API_KEY is set
if [[ -n "${ANTHROPIC_API_KEY:-}" ]]; then
if ! systemctl is-enabled --quiet corporate-memory.timer 2>/dev/null; then
log "Enabling corporate-memory timer..."
sudo /usr/bin/systemctl enable corporate-memory.timer
sudo /usr/bin/systemctl start corporate-memory.timer
fi fi
fi fi
# Enable jira-sla-poll timer if JIRA_SLA_API_TOKEN is set # Enable ws-gateway if JWT secret is newly provided
if [[ -n "${JIRA_SLA_API_TOKEN:-}" ]]; then if [[ -n "${DESKTOP_JWT_SECRET:-}" ]] && ! systemctl is-active --quiet ws-gateway 2>/dev/null; then
if ! systemctl is-enabled --quiet jira-sla-poll.timer 2>/dev/null; then if [[ -f "/etc/systemd/system/ws-gateway.service" ]]; then
log "Enabling jira-sla-poll timer..." log "Starting ws-gateway service..."
sudo /usr/bin/systemctl enable jira-sla-poll.timer sudo /usr/bin/systemctl enable ws-gateway
sudo /usr/bin/systemctl start jira-sla-poll.timer sudo /usr/bin/systemctl start ws-gateway
fi fi
fi fi
# Enable jira-consistency timers (always enabled if Jira credentials are configured) # Enable timers (only if service files exist)
if [[ -f "/opt/data-analyst/.env" ]] && grep -q "JIRA_API_TOKEN" /opt/data-analyst/.env 2>/dev/null; then for timer in corporate-memory session-collector jira-sla-poll jira-consistency jira-consistency-deep; do
if ! systemctl is-enabled --quiet jira-consistency.timer 2>/dev/null; then if [[ -f "/etc/systemd/system/${timer}.timer" ]]; then
log "Enabling jira-consistency timer..." if ! systemctl is-enabled --quiet "${timer}.timer" 2>/dev/null; then
sudo /usr/bin/systemctl enable jira-consistency.timer log "Enabling ${timer} timer..."
sudo /usr/bin/systemctl start jira-consistency.timer sudo /usr/bin/systemctl enable "${timer}.timer"
sudo /usr/bin/systemctl start "${timer}.timer"
fi
fi fi
if ! systemctl is-enabled --quiet jira-consistency-deep.timer 2>/dev/null; then done
log "Enabling jira-consistency-deep timer..."
sudo /usr/bin/systemctl enable jira-consistency-deep.timer
sudo /usr/bin/systemctl start jira-consistency-deep.timer
fi
fi
# Enable session-collector timer log "Deployment completed successfully! (v5)"
if ! systemctl is-enabled --quiet session-collector.timer 2>/dev/null; then
log "Enabling session-collector timer..."
sudo /usr/bin/systemctl enable session-collector.timer
sudo /usr/bin/systemctl start session-collector.timer
fi
log "Deployment completed successfully! (v4)"