Files
Jyotisha/deploy/run-staging-migration.sh
T
jesse-ux 69ede4367f
Independent Staging Quality Gate / validate (push) Successful in 10m6s
Independent Staging Quality Gate / publish (push) Successful in 4m18s
fix(ci): migrate staging automatically before deploy
Quality gate now dispatches Migrate Staging Database, waits for success, then dispatches Deploy staging. Automatic migrate attests the in-progress gate run so the two jobs cannot deadlock. Manual migrate is unchanged. Staging schema changes must stay backward-compatible with the currently deployed app.
2026-09-15 23:36:09 +08:00

108 lines
3.8 KiB
Bash
Executable File

#!/usr/bin/env bash
set -euo pipefail
set +x
required=(
INCOMING_PATH DEPLOY_PATH WEB_IMAGE DEPLOY_SHA EXPECTED_PREVIOUS_SHA
DOCKER_CONFIG
)
case "${DOCKER_BIN:-docker}" in
docker) docker_command=(docker) ;;
"sudo -n docker") docker_command=(sudo -n docker --config "$DOCKER_CONFIG") ;;
*) echo "unsafe staging Docker command" >&2; exit 1 ;;
esac
for key in "${required[@]}"; do
if [ -z "${!key:-}" ]; then
echo "required staging migration input is missing: $key" >&2
exit 1
fi
done
[[ "$DEPLOY_SHA" =~ ^[0-9a-f]{40}$ ]] || {
echo "unsafe staging migration revision" >&2
exit 1
}
image_pattern='^crpi-d1feco6itet73spp\.cn-hongkong\.personal\.cr\.aliyuncs\.com/copse/jyotisha@sha256:[0-9a-f]{64}$'
[[ "$WEB_IMAGE" =~ $image_pattern ]] || {
echo "unsafe staging migration image" >&2
exit 1
}
case "$INCOMING_PATH" in
/tmp/jyotisha-staging.*) ;;
*) echo "unsafe incoming staging path" >&2; exit 1 ;;
esac
state_directory="$DEPLOY_PATH/.state"
install -d -m 700 "$state_directory"
exec 9>"$state_directory/mutation.lock"
flock -n 9 || {
echo "another staging mutation holds the host lock" >&2
exit 75
}
current_sha="not-deployed"
if [ -r "$state_directory/deployed-revision" ]; then
current_sha="$(<"$state_directory/deployed-revision")"
else
existing_web="$("${docker_command[@]}" ps -aq \
--filter 'label=com.docker.compose.project=jyotisha-staging' \
--filter 'label=com.docker.compose.service=web' | head -n 1)"
if [ -n "$existing_web" ]; then
discovered_sha="$("${docker_command[@]}" inspect --format '{{range .Config.Env}}{{println .}}{{end}}' \
"$existing_web" | sed -n 's/^GITHUB_SHA=//p' | head -n 1)"
if [ -n "$discovered_sha" ]; then current_sha="$discovered_sha"; fi
fi
fi
if [ "$current_sha" != "not-deployed" ] && [[ ! "$current_sha" =~ ^[0-9a-f]{40}$ ]]; then
echo "invalid deployed staging revision state" >&2
exit 1
fi
[ "$current_sha" = "$EXPECTED_PREVIOUS_SHA" ] || {
echo "staging revision changed while this migration was waiting" >&2
exit 1
}
[ "$current_sha" = "not-deployed" ] ||
[ "$current_sha" = "$DEPLOY_SHA" ] ||
[ "${FORWARD_REVISION_VERIFIED:-false}" = "true" ] || {
echo "forward staging revision was not verified" >&2
exit 1
}
bash "$INCOMING_PATH/deploy/sync-staging-tree.sh" \
"$INCOMING_PATH" "$DEPLOY_PATH"
cd "$DEPLOY_PATH"
EXPECTED_STAGING_ENV_OWNER_UID="$(stat -c '%u' "$DEPLOY_PATH" 2>/dev/null || stat -f '%u' "$DEPLOY_PATH")"
[[ "$EXPECTED_STAGING_ENV_OWNER_UID" =~ ^[0-9]+$ ]] || {
echo "staging deployment owner is invalid" >&2
exit 1
}
export EXPECTED_STAGING_ENV_OWNER_UID
bash deploy/prepare-staging-model-provider-env.sh .env.staging
bash deploy/validate-staging-env.sh \
.env.staging staging.jyotisha.chat deploy/Caddyfile.staging
bash deploy/validate-staging-database-env.sh .env.staging.database
export DATABASE_ENV_FILE='../.env.staging.database'
compose=("${docker_command[@]}" compose -p jyotisha-staging -f deploy/docker-compose.postgres.yml)
"${docker_command[@]}" pull "$WEB_IMAGE"
"${compose[@]}" up -d --no-build --pull never --wait postgres
"${compose[@]}" exec -T postgres psql -v ON_ERROR_STOP=1 -U postgres -d jyotisha \
-f /dev/stdin < deploy/postgres/002-ensure-business-compatibility-roles.sql
set +e
pending_output="$("${compose[@]}" --profile migration-check run --rm migration-checker 2>&1)"
pending_status=$?
set -e
if [ "$pending_status" -eq 0 ]; then
echo "无待应用迁移"
elif [ "$pending_status" -eq 3 ]; then
echo "待应用迁移:"
printf '%s\n' "$pending_output"
else
echo "待应用迁移检查退出码 $pending_status;继续交给 migrator"
printf '%s\n' "$pending_output"
fi
"${compose[@]}" --profile migration run --rm migrator
"${compose[@]}" exec -T postgres psql -U postgres -d jyotisha -Atc \
'select filename from migration.schema_migrations order by filename'