Enhance the deployment scripts for Finland and Iran by refining Docker image pruning and transfer processes. Implement conditional transfer compression using zstd or gzip based on availability, and ensure only unused admin tags are removed without affecting build cache. This improves efficiency and reduces unnecessary image deletions during deployments.
174 lines
7.3 KiB
Bash
Executable File
174 lines
7.3 KiB
Bash
Executable File
#!/usr/bin/env bash
|
||
# Build admin image on the Finland host, then load+run it on Iran.
|
||
#
|
||
# Architecture:
|
||
# - Finland (git.ghabilee.ir / act_runner): build + Telegram notify
|
||
# - Iran: runtime only (/opt/ghabilee-admin)
|
||
set -euo pipefail
|
||
|
||
: "${FINLAND_SSH_KEY:?}"
|
||
: "${FINLAND_HOST:?}"
|
||
: "${FINLAND_USER:?}"
|
||
: "${IRAN_SSH_KEY:?}"
|
||
: "${IRAN_HOST:?}"
|
||
: "${IRAN_USER:?}"
|
||
|
||
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||
cd "$ROOT"
|
||
|
||
IMAGE_TAG="${IMAGE_TAG:-ghabilee-admin:$(git rev-parse --short HEAD 2>/dev/null || date +%s)}"
|
||
FINLAND_APP="/opt/ghabilee-admin-ci"
|
||
IRAN_APP="/opt/ghabilee-admin"
|
||
|
||
install -m 700 -d "$HOME/.ssh"
|
||
printf '%s\n' "$FINLAND_SSH_KEY" > "$HOME/.ssh/finland_key"
|
||
printf '%s\n' "$IRAN_SSH_KEY" > "$HOME/.ssh/iran_key"
|
||
chmod 600 "$HOME/.ssh/finland_key" "$HOME/.ssh/iran_key"
|
||
trap 'rm -f "$HOME/.ssh/finland_key" "$HOME/.ssh/iran_key"' EXIT
|
||
|
||
FSSH=(ssh -i "$HOME/.ssh/finland_key" -o IdentitiesOnly=yes -o StrictHostKeyChecking=accept-new)
|
||
ISSH=(ssh -i "$HOME/.ssh/iran_key" -o IdentitiesOnly=yes -o StrictHostKeyChecking=accept-new)
|
||
FRSYNC=(-e "ssh -i $HOME/.ssh/finland_key -o IdentitiesOnly=yes -o StrictHostKeyChecking=accept-new")
|
||
|
||
# --- build env from Iran (runtime .env holds NEXT_PUBLIC_* bake inputs) ---
|
||
"${ISSH[@]}" "${IRAN_USER}@${IRAN_HOST}" "test -s ${IRAN_APP}/.env"
|
||
scp -i "$HOME/.ssh/iran_key" -o IdentitiesOnly=yes -o StrictHostKeyChecking=accept-new \
|
||
"${IRAN_USER}@${IRAN_HOST}:${IRAN_APP}/.env" .env.production
|
||
test -s .env.production
|
||
|
||
# --- sync sources + env to Finland build dir ---
|
||
"${FSSH[@]}" "${FINLAND_USER}@${FINLAND_HOST}" "install -d -m 0750 '${FINLAND_APP}/src'"
|
||
rsync -az --delete "${FRSYNC[@]}" \
|
||
--exclude '.git' \
|
||
--exclude 'node_modules' \
|
||
--exclude '.next' \
|
||
--exclude '.env' \
|
||
--exclude '.env.*' \
|
||
--exclude 'test-results' \
|
||
--exclude 'playwright-report' \
|
||
./ "${FINLAND_USER}@${FINLAND_HOST}:${FINLAND_APP}/src/"
|
||
|
||
scp -i "$HOME/.ssh/finland_key" -o IdentitiesOnly=yes -o StrictHostKeyChecking=accept-new \
|
||
.env.production "${FINLAND_USER}@${FINLAND_HOST}:${FINLAND_APP}/.env"
|
||
rm -f .env.production
|
||
|
||
# --- build on Finland (enough RAM; Telegram/git live here too) ---
|
||
"${FSSH[@]}" "${FINLAND_USER}@${FINLAND_HOST}" \
|
||
"IMAGE_TAG='${IMAGE_TAG}' APP_DIR='${FINLAND_APP}' SRC_DIR='${FINLAND_APP}/src' bash -s" <<'REMOTE'
|
||
set -euo pipefail
|
||
cd "$APP_DIR"
|
||
# noglob: backend-style cron values must never expand if present in .env
|
||
set -f
|
||
# shellcheck disable=SC1091
|
||
set -a
|
||
# shellcheck disable=SC1090
|
||
source "$APP_DIR/.env"
|
||
set +a
|
||
set +f
|
||
|
||
env_or_empty() { printf '%s' "${!1-}"; }
|
||
|
||
sanitize_api_proxy_target() {
|
||
local target
|
||
target="$(env_or_empty API_PROXY_TARGET)"
|
||
case "$target" in
|
||
*127.0.0.1*|*localhost*|*'::1'*) printf '' ;;
|
||
*) printf '%s' "$target" ;;
|
||
esac
|
||
}
|
||
|
||
# DOCKER_BUILDKIT keeps layer/build cache across deploys; never prune -af here.
|
||
export DOCKER_BUILDKIT=1
|
||
docker build --platform linux/amd64 -t "$IMAGE_TAG" \
|
||
--build-arg "NEXT_PUBLIC_API_URL=$(env_or_empty NEXT_PUBLIC_API_URL)" \
|
||
--build-arg "NEXT_PUBLIC_FILE_SERVER_URL=$(env_or_empty NEXT_PUBLIC_FILE_SERVER_URL)" \
|
||
--build-arg "MAP_API_KEY=$(env_or_empty MAP_API_KEY)" \
|
||
--build-arg "NEXT_PUBLIC_MAP_API_KEY=$(env_or_empty NEXT_PUBLIC_MAP_API_KEY)" \
|
||
--build-arg "NEXT_PUBLIC_VAPID_PUBLIC_KEY=$(env_or_empty NEXT_PUBLIC_VAPID_PUBLIC_KEY)" \
|
||
--build-arg "NEXT_PUBLIC_BASE_PATH=$(env_or_empty NEXT_PUBLIC_BASE_PATH)" \
|
||
--build-arg "NEXT_PUBLIC_SITE_URL=$(env_or_empty NEXT_PUBLIC_SITE_URL)" \
|
||
--build-arg "NEXT_PUBLIC_OBSERVABILITY_ENDPOINT=$(env_or_empty NEXT_PUBLIC_OBSERVABILITY_ENDPOINT)" \
|
||
--build-arg "NEXT_PUBLIC_SENTRY_DSN=$(env_or_empty NEXT_PUBLIC_SENTRY_DSN)" \
|
||
--build-arg "NEXT_PUBLIC_SENTRY_ENVIRONMENT=$(env_or_empty NEXT_PUBLIC_SENTRY_ENVIRONMENT)" \
|
||
--build-arg "NEXT_PUBLIC_SENTRY_TRACES_SAMPLE_RATE=$(env_or_empty NEXT_PUBLIC_SENTRY_TRACES_SAMPLE_RATE)" \
|
||
--build-arg "NEXT_PUBLIC_ARCAPTCHA_SITE_KEY=$(env_or_empty NEXT_PUBLIC_ARCAPTCHA_SITE_KEY)" \
|
||
--build-arg "SENTRY_AUTH_TOKEN=$(env_or_empty SENTRY_AUTH_TOKEN)" \
|
||
--build-arg "SENTRY_ORG=$(env_or_empty SENTRY_ORG)" \
|
||
--build-arg "SENTRY_PROJECT=$(env_or_empty SENTRY_PROJECT)" \
|
||
--build-arg "API_PROXY_TARGET=$(sanitize_api_proxy_target)" \
|
||
"$SRC_DIR"
|
||
|
||
# فقط dangling؛ prune -a/-af کش build را میکشد و هر deploy را cold میکند
|
||
docker image prune -f >/dev/null 2>&1 || true
|
||
echo "BUILT $IMAGE_TAG"
|
||
REMOTE
|
||
|
||
# --- stream image Finland → Iran, then compose up ---
|
||
"${ISSH[@]}" "${IRAN_USER}@${IRAN_HOST}" "install -d -m 0750 '${IRAN_APP}'"
|
||
scp -i "$HOME/.ssh/iran_key" -o IdentitiesOnly=yes -o StrictHostKeyChecking=accept-new \
|
||
deploy/docker-compose.production.yml \
|
||
"${IRAN_USER}@${IRAN_HOST}:${IRAN_APP}/docker-compose.yml"
|
||
if [[ -f deploy/nginx/backoffice.conf ]]; then
|
||
scp -i "$HOME/.ssh/iran_key" -o IdentitiesOnly=yes -o StrictHostKeyChecking=accept-new \
|
||
deploy/nginx/backoffice.conf \
|
||
"${IRAN_USER}@${IRAN_HOST}:/etc/nginx/sites-available/backoffice"
|
||
"${ISSH[@]}" "${IRAN_USER}@${IRAN_HOST}" '
|
||
ln -sfn /etc/nginx/sites-available/backoffice /etc/nginx/sites-enabled/backoffice
|
||
nginx -t && systemctl reload nginx
|
||
'
|
||
fi
|
||
|
||
# فشردهسازی transfer؛ zstd اگر روی هر دو host باشد، وگرنه gzip
|
||
TRANSFER_CODEC=gzip
|
||
if "${FSSH[@]}" "${FINLAND_USER}@${FINLAND_HOST}" 'command -v zstd >/dev/null 2>&1' \
|
||
&& "${ISSH[@]}" "${IRAN_USER}@${IRAN_HOST}" 'command -v zstd >/dev/null 2>&1'; then
|
||
TRANSFER_CODEC=zstd
|
||
fi
|
||
|
||
echo "Transferring ${IMAGE_TAG} Finland → Iran (${TRANSFER_CODEC})..."
|
||
if [[ "$TRANSFER_CODEC" == zstd ]]; then
|
||
"${FSSH[@]}" "${FINLAND_USER}@${FINLAND_HOST}" "docker save '${IMAGE_TAG}' | zstd -T0 -3" \
|
||
| "${ISSH[@]}" "${IRAN_USER}@${IRAN_HOST}" 'zstd -d | docker load'
|
||
else
|
||
"${FSSH[@]}" "${FINLAND_USER}@${FINLAND_HOST}" "docker save '${IMAGE_TAG}' | gzip -1" \
|
||
| "${ISSH[@]}" "${IRAN_USER}@${IRAN_HOST}" 'gzip -d | docker load'
|
||
fi
|
||
|
||
"${ISSH[@]}" "${IRAN_USER}@${IRAN_HOST}" \
|
||
"ADMIN_IMAGE='${IMAGE_TAG}' bash -s" <<'REMOTE'
|
||
set -euo pipefail
|
||
cd /opt/ghabilee-admin
|
||
test -s .env
|
||
ADMIN_IMAGE="$ADMIN_IMAGE" docker compose -f docker-compose.yml up -d --no-deps ghabilee-admin
|
||
for attempt in $(seq 1 36); do
|
||
health="$(docker inspect --format '{{if .State.Health}}{{.State.Health.Status}}{{else}}{{.State.Status}}{{end}}' ghabilee-admin 2>/dev/null || echo missing)"
|
||
echo "Admin health ${attempt}/36: ${health}"
|
||
if [ "$health" = healthy ]; then
|
||
# آزاد کردن دیسک بدون نابود کردن کش: فقط tagهای قدیمی admin که در حال استفاده نیستند
|
||
docker images --format '{{.Repository}}:{{.Tag}}' \
|
||
| awk -v keep="$ADMIN_IMAGE" '$0 ~ /^ghabilee-admin:/ && $0 != keep { print }' \
|
||
| while IFS= read -r old; do
|
||
docker rmi "$old" >/dev/null 2>&1 || true
|
||
done
|
||
docker image prune -f >/dev/null 2>&1 || true
|
||
echo "Deploy OK"
|
||
exit 0
|
||
fi
|
||
case "$health" in unhealthy|exited|dead|missing) exit 1 ;; esac
|
||
sleep 5
|
||
done
|
||
exit 1
|
||
REMOTE
|
||
|
||
# روی Finland هم tagهای قدیمی admin را بردار؛ BuildKit cache دستنخورده میماند
|
||
"${FSSH[@]}" "${FINLAND_USER}@${FINLAND_HOST}" \
|
||
"IMAGE_TAG='${IMAGE_TAG}' bash -s" <<'REMOTE'
|
||
set -euo pipefail
|
||
docker images --format '{{.Repository}}:{{.Tag}}' \
|
||
| awk -v keep="$IMAGE_TAG" '$0 ~ /^ghabilee-admin:/ && $0 != keep { print }' \
|
||
| while IFS= read -r old; do
|
||
docker rmi "$old" >/dev/null 2>&1 || true
|
||
done
|
||
docker image prune -f >/dev/null 2>&1 || true
|
||
REMOTE
|