first commit
This commit is contained in:
Executable
+209
@@ -0,0 +1,209 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -Eeuo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
|
||||
ROOT_DIR="$(cd -- "${SCRIPT_DIR}/.." && pwd)"
|
||||
ENV_FILE="${ENV_FILE:-${ROOT_DIR}/.env}"
|
||||
COMPOSE_FILE="${COMPOSE_FILE:-${ROOT_DIR}/compose.yaml}"
|
||||
APP_SERVICE="${APP_SERVICE:-laravel.test}"
|
||||
DB_SERVICE="${DB_SERVICE:-mysql}"
|
||||
COMPOSER_IMAGE="${COMPOSER_IMAGE:-composer:2}"
|
||||
BUILD_IMAGES=1
|
||||
WARM_CACHE=1
|
||||
|
||||
usage() {
|
||||
cat <<'EOF'
|
||||
Использование:
|
||||
./scripts/deploy-ubuntu.sh [--skip-build] [--skip-cache]
|
||||
|
||||
Что делает скрипт:
|
||||
1. Создаёт .env из .env.example, если файла ещё нет
|
||||
2. Подготавливает storage/bootstrap cache директории
|
||||
3. Собирает docker-образ приложения
|
||||
4. Устанавливает production-зависимости Composer внутри контейнера
|
||||
5. Генерирует APP_KEY, если он пустой
|
||||
6. Поднимает контейнеры
|
||||
7. Ждёт готовности MySQL
|
||||
8. Запускает миграции и прогревает Laravel-кэш
|
||||
|
||||
Требования:
|
||||
- Ubuntu с установленными Docker Engine и Docker Compose Plugin
|
||||
- настроенный .env с корректными значениями APP_URL и AZEROTHCORE_AUTH_DB_*
|
||||
EOF
|
||||
}
|
||||
|
||||
log() {
|
||||
printf '[%s] %s\n' "$(date '+%Y-%m-%d %H:%M:%S')" "$*"
|
||||
}
|
||||
|
||||
fail() {
|
||||
printf '[error] %s\n' "$*" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
require_command() {
|
||||
command -v "$1" >/dev/null 2>&1 || fail "Не найдена команда: $1"
|
||||
}
|
||||
|
||||
env_value() {
|
||||
local key="$1"
|
||||
|
||||
[[ -f "$ENV_FILE" ]] || return 0
|
||||
|
||||
grep -E "^${key}=" "$ENV_FILE" | tail -n 1 | cut -d '=' -f 2- | sed -e 's/^"//' -e 's/"$//'
|
||||
}
|
||||
|
||||
set_env_value() {
|
||||
local key="$1"
|
||||
local value="$2"
|
||||
|
||||
if grep -qE "^${key}=" "$ENV_FILE"; then
|
||||
sed -i "s#^${key}=.*#${key}=${value}#" "$ENV_FILE"
|
||||
else
|
||||
printf '%s=%s\n' "$key" "$value" >> "$ENV_FILE"
|
||||
fi
|
||||
}
|
||||
|
||||
compose() {
|
||||
docker compose -f "$COMPOSE_FILE" "$@"
|
||||
}
|
||||
|
||||
composer_install() {
|
||||
docker run --rm \
|
||||
-u "${WWWUSER}:${WWWGROUP}" \
|
||||
-v "${ROOT_DIR}:/app" \
|
||||
-w /app \
|
||||
"$COMPOSER_IMAGE" \
|
||||
install --no-dev --prefer-dist --optimize-autoloader --no-interaction
|
||||
}
|
||||
|
||||
app_exec() {
|
||||
compose exec -T --user "${WWWUSER}:${WWWGROUP}" "$APP_SERVICE" "$@"
|
||||
}
|
||||
|
||||
wait_for_mysql() {
|
||||
local container_id
|
||||
local status
|
||||
local attempts=0
|
||||
local max_attempts=60
|
||||
|
||||
container_id="$(compose ps -q "$DB_SERVICE")"
|
||||
[[ -n "$container_id" ]] || fail "Не удалось определить контейнер MySQL"
|
||||
|
||||
while (( attempts < max_attempts )); do
|
||||
status="$(docker inspect -f '{{if .State.Health}}{{.State.Health.Status}}{{else}}{{.State.Status}}{{end}}' "$container_id" 2>/dev/null || true)"
|
||||
|
||||
if [[ "$status" == "healthy" || "$status" == "running" ]]; then
|
||||
log "MySQL готов"
|
||||
return 0
|
||||
fi
|
||||
|
||||
attempts=$((attempts + 1))
|
||||
sleep 2
|
||||
done
|
||||
|
||||
fail "MySQL не перешёл в готовое состояние"
|
||||
}
|
||||
|
||||
prepare_env() {
|
||||
if [[ -f "$ENV_FILE" ]]; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
[[ -f "${ROOT_DIR}/.env.example" ]] || fail "Не найден .env.example"
|
||||
|
||||
cp "${ROOT_DIR}/.env.example" "$ENV_FILE"
|
||||
set_env_value "APP_ENV" "production"
|
||||
set_env_value "APP_DEBUG" "false"
|
||||
|
||||
log "Создан ${ENV_FILE} из .env.example"
|
||||
log "Проверь APP_URL и AZEROTHCORE_AUTH_DB_* перед публичным запуском"
|
||||
}
|
||||
|
||||
prepare_directories() {
|
||||
mkdir -p \
|
||||
"${ROOT_DIR}/bootstrap/cache" \
|
||||
"${ROOT_DIR}/storage/app" \
|
||||
"${ROOT_DIR}/storage/framework/cache/data" \
|
||||
"${ROOT_DIR}/storage/framework/sessions" \
|
||||
"${ROOT_DIR}/storage/framework/testing" \
|
||||
"${ROOT_DIR}/storage/framework/views" \
|
||||
"${ROOT_DIR}/storage/logs"
|
||||
|
||||
chmod -R ug+rwx "${ROOT_DIR}/bootstrap/cache" "${ROOT_DIR}/storage"
|
||||
}
|
||||
|
||||
parse_args() {
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--skip-build)
|
||||
BUILD_IMAGES=0
|
||||
;;
|
||||
--skip-cache)
|
||||
WARM_CACHE=0
|
||||
;;
|
||||
-h|--help)
|
||||
usage
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
fail "Неизвестный аргумент: $1"
|
||||
;;
|
||||
esac
|
||||
|
||||
shift
|
||||
done
|
||||
}
|
||||
|
||||
main() {
|
||||
parse_args "$@"
|
||||
|
||||
require_command docker
|
||||
docker compose version >/dev/null 2>&1 || fail "Не найден Docker Compose Plugin"
|
||||
|
||||
cd "$ROOT_DIR"
|
||||
|
||||
prepare_env
|
||||
prepare_directories
|
||||
|
||||
WWWUSER="$(env_value WWWUSER)"
|
||||
WWWGROUP="$(env_value WWWGROUP)"
|
||||
WWWUSER="${WWWUSER:-1000}"
|
||||
WWWGROUP="${WWWGROUP:-1000}"
|
||||
|
||||
log "Устанавливаю production-зависимости Composer"
|
||||
composer_install
|
||||
|
||||
if (( BUILD_IMAGES )); then
|
||||
log "Собираю образ приложения"
|
||||
compose build "$APP_SERVICE"
|
||||
fi
|
||||
|
||||
if [[ -z "$(env_value APP_KEY)" ]]; then
|
||||
log "Генерирую APP_KEY"
|
||||
compose run --rm --no-deps --user "${WWWUSER}:${WWWGROUP}" --entrypoint php "$APP_SERVICE" artisan key:generate --force
|
||||
fi
|
||||
|
||||
log "Поднимаю контейнеры"
|
||||
compose up -d
|
||||
|
||||
wait_for_mysql
|
||||
|
||||
log "Выполняю миграции"
|
||||
app_exec php artisan migrate --force
|
||||
|
||||
if (( WARM_CACHE )); then
|
||||
log "Обновляю Laravel-кэш"
|
||||
app_exec php artisan optimize:clear
|
||||
app_exec php artisan config:cache
|
||||
app_exec php artisan route:cache
|
||||
app_exec php artisan view:cache
|
||||
fi
|
||||
|
||||
log "Деплой завершён"
|
||||
log "Сайт доступен по адресу: $(env_value APP_URL)"
|
||||
log "Проверка контейнеров: docker compose -f ${COMPOSE_FILE} ps"
|
||||
}
|
||||
|
||||
main "$@"
|
||||
Reference in New Issue
Block a user