1b8d6fcce6
Add database-backed administrators, configurable Alipay packages, idempotent payment settlement and platform reporting. Standardize Gitea workflows on the xiaoxin runner so reviewed staging commits are tested, packaged, and deployed by immutable image digest.
94 lines
5.9 KiB
YAML
94 lines
5.9 KiB
YAML
name: Migrate Staging Database (manual only)
|
|
|
|
on:
|
|
workflow_dispatch:
|
|
inputs:
|
|
deploy_sha:
|
|
description: Full current staging SHA to migrate
|
|
required: true
|
|
type: string
|
|
|
|
concurrency:
|
|
group: staging-mutation
|
|
cancel-in-progress: false
|
|
|
|
jobs:
|
|
migrate:
|
|
runs-on: xiaoxin
|
|
timeout-minutes: 20
|
|
env:
|
|
GITEA_SHA: ${{ gitea.sha }}
|
|
REGISTRY_HOST: crpi-d1feco6itet73spp.cn-hongkong.personal.cr.aliyuncs.com
|
|
IMAGE_REPOSITORY: crpi-d1feco6itet73spp.cn-hongkong.personal.cr.aliyuncs.com/copse/jyotisha
|
|
DEPLOY_HOST: ${{ vars.STAGING_HOST }}
|
|
DEPLOY_PORT: ${{ vars.STAGING_PORT }}
|
|
DEPLOY_USER: ${{ vars.STAGING_USER }}
|
|
DEPLOY_PATH: ${{ vars.STAGING_PATH }}
|
|
STAGING_KNOWN_HOSTS: ${{ vars.STAGING_KNOWN_HOSTS }}
|
|
steps:
|
|
- name: Checkout trusted main controller
|
|
run: |
|
|
set -euo pipefail
|
|
git init .
|
|
git remote remove origin 2>/dev/null || true
|
|
git remote add origin https://git.copse.top/root/Jyotisha.git
|
|
git fetch --no-tags origin main staging
|
|
git checkout --detach --force origin/main
|
|
- name: Verify runner toolchain
|
|
run: |
|
|
set -euo pipefail
|
|
python3 --version
|
|
node --version
|
|
npm --version
|
|
docker version
|
|
- name: Validate revision and resolve migration image
|
|
id: image
|
|
env:
|
|
DEPLOY_SHA: ${{ inputs.deploy_sha }}
|
|
REGISTRY_USERNAME: ${{ secrets.REGISTRY_USERNAME }}
|
|
REGISTRY_PASSWORD: ${{ secrets.REGISTRY_PASSWORD }}
|
|
run: |
|
|
set -euo pipefail
|
|
[[ "$DEPLOY_SHA" =~ ^[0-9a-f]{40}$ ]] || exit 1
|
|
[[ "$(git ls-remote origin refs/heads/staging | awk '{print $1}')" == "$DEPLOY_SHA" ]] || { echo "migration requires current staging head" >&2; exit 1; }
|
|
git cat-file -e "${DEPLOY_SHA}^{commit}"
|
|
git merge-base --is-ancestor "$DEPLOY_SHA" origin/main || { echo "revision is not in reviewed main history" >&2; exit 1; }
|
|
printf '%s' "$REGISTRY_PASSWORD" | docker login "$REGISTRY_HOST" --username "$REGISTRY_USERNAME" --password-stdin
|
|
web_digest="$(docker manifest inspect "${IMAGE_REPOSITORY}:web-${DEPLOY_SHA}" --verbose | python3 -c 'import json,sys; d=json.load(sys.stdin); print(d.get("Descriptor", d).get("digest", ""))')"
|
|
[[ "$web_digest" =~ ^sha256:[0-9a-f]{64}$ ]] || exit 1
|
|
echo "web_image=${IMAGE_REPOSITORY}@${web_digest}" >> "$GITHUB_OUTPUT"
|
|
echo "deploy_sha=$DEPLOY_SHA" >> "$GITHUB_OUTPUT"
|
|
- name: Apply digest-pinned migration under host lock
|
|
env:
|
|
SSH_PRIVATE_KEY: ${{ secrets.STAGING_SSH_PRIVATE_KEY }}
|
|
REGISTRY_USERNAME: ${{ secrets.REGISTRY_USERNAME }}
|
|
REGISTRY_PASSWORD: ${{ secrets.REGISTRY_PASSWORD }}
|
|
DEPLOY_SHA: ${{ steps.image.outputs.deploy_sha }}
|
|
WEB_IMAGE: ${{ steps.image.outputs.web_image }}
|
|
run: |
|
|
set -euo pipefail
|
|
ssh_root="${RUNNER_TEMP}/staging-migration-ssh"
|
|
key_path="$ssh_root/id_ed25519"
|
|
known_hosts_path="$ssh_root/known_hosts"
|
|
incoming="$DEPLOY_PATH/.incoming/$GITEA_RUN_NUMBER-$GITEA_RUN_ATTEMPT"
|
|
install -m 700 -d "$ssh_root"
|
|
printf '%s\n' "$SSH_PRIVATE_KEY" | tr -d '\r' > "$key_path"
|
|
printf '%s\n' "$STAGING_KNOWN_HOSTS" | tr -d '\r' > "$known_hosts_path"
|
|
chmod 600 "$key_path" "$known_hosts_path"
|
|
ssh_options=(-i "$key_path" -p "$DEPLOY_PORT" -o BatchMode=yes -o IdentitiesOnly=yes -o StrictHostKeyChecking=yes -o "UserKnownHostsFile=$known_hosts_path")
|
|
remote="$DEPLOY_USER@$DEPLOY_HOST"
|
|
cleanup() { ssh "${ssh_options[@]}" "$remote" "DOCKER_CONFIG='$incoming/.docker' docker logout '$REGISTRY_HOST' >/dev/null 2>&1 || true; rm -rf -- '$incoming'" >/dev/null 2>&1 || true; docker logout "$REGISTRY_HOST" >/dev/null 2>&1 || true; rm -rf -- "$ssh_root"; }
|
|
trap cleanup EXIT
|
|
ssh "${ssh_options[@]}" "$remote" "install -d -m 700 '$incoming/.docker'"
|
|
tar -cf "${RUNNER_TEMP}/deploy.tar" deploy
|
|
scp -i "$key_path" -P "$DEPLOY_PORT" -o BatchMode=yes -o IdentitiesOnly=yes -o StrictHostKeyChecking=yes -o "UserKnownHostsFile=$known_hosts_path" "${RUNNER_TEMP}/deploy.tar" "$remote:$incoming/deploy.tar"
|
|
ssh "${ssh_options[@]}" "$remote" "tar -xf '$incoming/deploy.tar' -C '$incoming' && rm -f -- '$incoming/deploy.tar'"
|
|
previous_sha="$(ssh "${ssh_options[@]}" "$remote" "state='$DEPLOY_PATH/.state/deployed-revision'; if [ -f \"\$state\" ]; then cat \"\$state\"; else id=\$(docker ps -aq --filter 'label=com.docker.compose.project=jyotisha-staging' --filter 'label=com.docker.compose.service=web' | head -n 1); if [ -n \"\$id\" ]; then docker inspect --format '{{range .Config.Env}}{{println .}}{{end}}' \"\$id\" | sed -n 's/^GITHUB_SHA=//p' | head -n 1; else printf not-deployed; fi; fi")"
|
|
[[ "$previous_sha" == not-deployed || "$previous_sha" =~ ^[0-9a-f]{40}$ ]] || exit 1
|
|
forward_verified=false
|
|
if [[ "$previous_sha" != not-deployed && "$previous_sha" != "$DEPLOY_SHA" ]]; then git cat-file -e "${previous_sha}^{commit}" 2>/dev/null || git fetch origin "$previous_sha"; git merge-base --is-ancestor "$previous_sha" "$DEPLOY_SHA" || { echo "migration rollback or divergence refused" >&2; exit 1; }; forward_verified=true; fi
|
|
printf '%s' "$REGISTRY_PASSWORD" | ssh "${ssh_options[@]}" "$remote" "DOCKER_CONFIG='$incoming/.docker' docker login '$REGISTRY_HOST' --username '$REGISTRY_USERNAME' --password-stdin"
|
|
ssh "${ssh_options[@]}" "$remote" "INCOMING_PATH='$incoming' DEPLOY_PATH='$DEPLOY_PATH' WEB_IMAGE='$WEB_IMAGE' DEPLOY_SHA='$DEPLOY_SHA' EXPECTED_PREVIOUS_SHA='$previous_sha' FORWARD_REVISION_VERIFIED='$forward_verified' DOCKER_CONFIG='$incoming/.docker' bash '$incoming/deploy/run-staging-migration.sh'"
|
|
- name: Operator action
|
|
run: echo 'Migration complete. Start Deploy staging manually with this exact SHA.'
|