#!/usr/bin/env bash set -euo pipefail ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" DEPLOY_HOST="${DEPLOY_HOST:-192.168.100.50}" DEPLOY_USER="${DEPLOY_USER:-root}" DEPLOY_PATH="${DEPLOY_PATH:-/var/www/jabali}" WWW_USER="${WWW_USER:-www-data}" NPM_CACHE_DIR="${NPM_CACHE_DIR:-}" GITEA_REMOTE="${GITEA_REMOTE:-gitea}" GITEA_URL="${GITEA_URL:-}" GITHUB_REMOTE="${GITHUB_REMOTE:-origin}" GITHUB_URL="${GITHUB_URL:-}" PUSH_BRANCH="${PUSH_BRANCH:-}" SKIP_SYNC=0 SKIP_COMPOSER=0 SKIP_NPM=0 SKIP_MIGRATE=0 SKIP_CACHE=0 DELETE_REMOTE=0 DRY_RUN=0 PUSH_GITEA=0 PUSH_GITHUB=0 SET_VERSION="" usage() { cat <<'EOF' Usage: scripts/deploy.sh [options] Options: --host HOST Remote host (default: 192.168.100.50) --user USER SSH user (default: root) --path PATH Remote path (default: /var/www/jabali) --www-user USER Remote runtime user (default: www-data) --skip-sync Skip rsync sync step --skip-composer Skip composer install --skip-npm Skip npm install/build --skip-migrate Skip php artisan migrate --skip-cache Skip cache clear/rebuild --delete Pass --delete to rsync (dangerous) --dry-run Dry-run rsync only --push-gitea Push current branch to Gitea before deploy --gitea-remote NAME Gitea git remote name (default: gitea) --gitea-url URL Push to this URL instead of a named remote --push-github Push current branch to GitHub before deploy --github-remote NAME GitHub git remote name (default: origin) --github-url URL Push to this URL instead of a named remote --version VALUE Set VERSION to a specific value before push -h, --help Show this help Environment overrides: DEPLOY_HOST, DEPLOY_USER, DEPLOY_PATH, WWW_USER, NPM_CACHE_DIR, GITEA_REMOTE, GITEA_URL, GITHUB_REMOTE, GITHUB_URL, PUSH_BRANCH EOF } while [[ $# -gt 0 ]]; do case "$1" in --host) DEPLOY_HOST="$2" shift 2 ;; --user) DEPLOY_USER="$2" shift 2 ;; --path) DEPLOY_PATH="$2" shift 2 ;; --www-user) WWW_USER="$2" shift 2 ;; --skip-sync) SKIP_SYNC=1 shift ;; --skip-composer) SKIP_COMPOSER=1 shift ;; --skip-npm) SKIP_NPM=1 shift ;; --skip-migrate) SKIP_MIGRATE=1 shift ;; --skip-cache) SKIP_CACHE=1 shift ;; --delete) DELETE_REMOTE=1 shift ;; --dry-run) DRY_RUN=1 shift ;; --push-gitea) PUSH_GITEA=1 shift ;; --gitea-remote) GITEA_REMOTE="$2" shift 2 ;; --gitea-url) GITEA_URL="$2" shift 2 ;; --push-github) PUSH_GITHUB=1 shift ;; --github-remote) GITHUB_REMOTE="$2" shift 2 ;; --github-url) GITHUB_URL="$2" shift 2 ;; --version) SET_VERSION="$2" shift 2 ;; -h|--help) usage exit 0 ;; *) echo "Unknown option: $1" usage exit 1 ;; esac done REMOTE="${DEPLOY_USER}@${DEPLOY_HOST}" ensure_clean_worktree() { if ! git -C "$ROOT_DIR" diff --quiet || ! git -C "$ROOT_DIR" diff --cached --quiet; then echo "Working tree is dirty. Commit or stash changes before pushing." exit 1 fi } get_current_version() { sed -n 's/^VERSION=//p' "$ROOT_DIR/VERSION" } bump_version() { local current new base num current="$(get_current_version)" if [[ -n "$SET_VERSION" ]]; then new="$SET_VERSION" else if [[ "$current" =~ ^(.+-rc)([0-9]+)?$ ]]; then base="${BASH_REMATCH[1]}" num="${BASH_REMATCH[2]}" if [[ -z "$num" ]]; then num=1 else num=$((num + 1)) fi new="${base}${num}" elif [[ "$current" =~ ^(.+?)([0-9]+)$ ]]; then new="${BASH_REMATCH[1]}$((BASH_REMATCH[2] + 1))" else echo "Cannot auto-bump VERSION from '$current'. Use --version to set it explicitly." exit 1 fi fi if [[ "$new" == "$current" ]]; then echo "VERSION is already '$current'. Use --version to set a new value." exit 1 fi printf 'VERSION=%s\n' "$new" > "$ROOT_DIR/VERSION" perl -0pi -e "s/JABALI_VERSION=\\\"\\$\\{JABALI_VERSION:-[^\\\"]+\\}\\\"/JABALI_VERSION=\\\"\\$\\{JABALI_VERSION:-$new\\}\\\"/g" "$ROOT_DIR/install.sh" git -C "$ROOT_DIR" add VERSION install.sh if ! git -C "$ROOT_DIR" diff --cached --quiet; then git -C "$ROOT_DIR" commit -m "Bump VERSION to $new" fi } prepare_push() { ensure_clean_worktree bump_version } push_remote() { local label="$1" local remote_name="$2" local remote_url="$3" local target if [[ -n "$remote_url" ]]; then target="$remote_url" else if ! git -C "$ROOT_DIR" remote get-url "$remote_name" >/dev/null 2>&1; then echo "$label remote '$remote_name' not found. Use --${label,,}-url or --${label,,}-remote." exit 1 fi target="$remote_name" fi if [[ -z "$PUSH_BRANCH" ]]; then PUSH_BRANCH="$(git -C "$ROOT_DIR" rev-parse --abbrev-ref HEAD)" fi git -C "$ROOT_DIR" push "$target" "$PUSH_BRANCH" } rsync_project() { local -a rsync_opts rsync_opts=(-az --info=progress2) if [[ "$DELETE_REMOTE" -eq 1 ]]; then rsync_opts+=(--delete) fi if [[ "$DRY_RUN" -eq 1 ]]; then rsync_opts+=(--dry-run) fi rsync "${rsync_opts[@]}" \ --exclude ".git/" \ --exclude "node_modules/" \ --exclude "vendor/" \ --exclude "storage/" \ --exclude "bootstrap/cache/" \ --exclude "public/build/" \ --exclude ".env" \ --exclude ".env.*" \ --exclude "database/*.sqlite" \ --exclude "database/*.sqlite-wal" \ --exclude "database/*.sqlite-shm" \ "$ROOT_DIR/" \ "${REMOTE}:${DEPLOY_PATH}/" } remote_run() { ssh -o StrictHostKeyChecking=no "$REMOTE" "bash -lc '$1'" } remote_run_www() { ssh -o StrictHostKeyChecking=no "$REMOTE" "bash -lc 'cd \"$DEPLOY_PATH\" && sudo -u \"$WWW_USER\" -H bash -lc \"$1\"'" } ensure_remote_permissions() { local parent_dir parent_dir="$(dirname "$DEPLOY_PATH")" if [[ -z "$NPM_CACHE_DIR" ]]; then NPM_CACHE_DIR="${parent_dir}/.npm" fi remote_run "mkdir -p \"$DEPLOY_PATH/storage\" \"$DEPLOY_PATH/bootstrap/cache\" \"$DEPLOY_PATH/public/build\" \"$DEPLOY_PATH/node_modules\" \"$DEPLOY_PATH/database\" \"$NPM_CACHE_DIR\"" remote_run "chown -R \"$WWW_USER\":\"$WWW_USER\" \"$DEPLOY_PATH/storage\" \"$DEPLOY_PATH/bootstrap/cache\" \"$DEPLOY_PATH/public\" \"$DEPLOY_PATH/public/build\" \"$DEPLOY_PATH/node_modules\" \"$DEPLOY_PATH/database\" \"$NPM_CACHE_DIR\"" remote_run "if [[ -f \"$DEPLOY_PATH/auth.json\" ]]; then chown \"$WWW_USER\":\"$WWW_USER\" \"$DEPLOY_PATH/auth.json\" && chmod 600 \"$DEPLOY_PATH/auth.json\"; fi" } echo "Deploying to ${REMOTE}:${DEPLOY_PATH}" if [[ "$PUSH_GITEA" -eq 1 ]]; then prepare_push echo "Pushing to Gitea..." push_remote "Gitea" "$GITEA_REMOTE" "$GITEA_URL" fi if [[ "$PUSH_GITHUB" -eq 1 ]]; then if [[ "$PUSH_GITEA" -ne 1 ]]; then prepare_push fi echo "Pushing to GitHub..." push_remote "GitHub" "$GITHUB_REMOTE" "$GITHUB_URL" fi if [[ "$SKIP_SYNC" -eq 0 ]]; then echo "Syncing project files..." rsync_project fi if [[ "$DRY_RUN" -eq 1 ]]; then echo "Dry run complete. No remote commands executed." exit 0 fi echo "Ensuring remote permissions..." ensure_remote_permissions if [[ "$SKIP_COMPOSER" -eq 0 ]]; then echo "Installing composer dependencies..." remote_run_www "composer install --no-interaction --prefer-dist --optimize-autoloader" fi if [[ "$SKIP_NPM" -eq 0 ]]; then echo "Building frontend assets..." remote_run_www "npm ci" remote_run_www "npm run build" fi if [[ "$SKIP_MIGRATE" -eq 0 ]]; then echo "Running migrations..." remote_run_www "php artisan migrate --force" fi if [[ "$SKIP_CACHE" -eq 0 ]]; then echo "Refreshing caches..." remote_run_www "php artisan optimize:clear" remote_run_www "php artisan config:cache" remote_run_www "php artisan route:cache" remote_run_www "php artisan view:cache" fi echo "Deploy complete."