diff --git a/.woodpecker/db.yml b/.woodpecker/db.yml index ae2a399..f805025 100644 --- a/.woodpecker/db.yml +++ b/.woodpecker/db.yml @@ -22,12 +22,11 @@ steps: commands: - apk add --no-cache curl bash coreutils sed - mkdir -p secrets - - set -a; . ./.env.version; set +a # fetch registry creds - - ./ci/vlt kv_to_file kv registry user secrets/REGISTRY_USER 600 - - ./ci/vlt kv_to_file kv registry password secrets/REGISTRY_PASS 600 + - ./ci/vlt kv_to_file kv sendico/registry user secrets/REGISTRY_USER 600 + - ./ci/vlt kv_to_file kv sendico/registry password secrets/REGISTRY_PASS 600 # fetch SSH private key for deploy - - ./ci/vlt kv_to_file kv ops/deploy/ssh_key private secrets/SSH_KEY 600 + - ./ci/vlt kv_to_file kv sendico/ops/deploy/ssh_key private secrets/SSH_KEY 600 - name: lock-db image: quay.io/skopeo/stable:latest @@ -36,47 +35,35 @@ steps: REGISTRY_URL: registry.sendico.io MONGO_VERSION: latest commands: - - microdnf install -y bash coreutils-single sed && microdnf clean all - - mkdir -p ci/prod/env - - set -a; . ./ci/prod/.env.runtime; set +a - - set -a; . ./.env.version; set +a - - test -s secrets/REGISTRY_USER && test -s secrets/REGISTRY_PASS - - CREDS="$(cat secrets/REGISTRY_USER):$(cat secrets/REGISTRY_PASS)" - # mirror multi-arch image into registry under app version tag - - >- - skopeo copy --all docker://docker.io/library/mongo:${MONGO_VERSION} - docker://${REGISTRY_URL}/mirror/mongo:${APP_V} --dest-creds "$CREDS" - # inspect the mirrored image to capture immutable digest - - >- - INSPECT=$(skopeo inspect docker://${REGISTRY_URL}/mirror/mongo:${APP_V} - --creds "$CREDS") - - DIGEST="$(printf '%s' "$INSPECT" | tr -d '\n' | sed -n 's/.*"Digest"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p')" - - test -n "$DIGEST" - # store lock both for local deploy metadata and rsync to server - - printf 'MONGO_TAG=%s\nMONGO_DIGEST=%s\n' "$APP_V" "$DIGEST" | tee .env.lock ci/prod/env/.env.lock.db - - cat .env.lock + - | + set -euo + mkdir -p ci/prod/env + set -a + . ./ci/prod/.env.runtime + . ./.env.version + set +a + test -s secrets/REGISTRY_USER && test -s secrets/REGISTRY_PASS + CREDS="$(cat secrets/REGISTRY_USER):$(cat secrets/REGISTRY_PASS)" + skopeo copy --all \ + docker://docker.io/library/mongo:${MONGO_VERSION} \ + docker://${REGISTRY_URL}/mirror/mongo:${APP_V} \ + --dest-creds "$CREDS" + INSPECT=$(skopeo inspect docker://${REGISTRY_URL}/mirror/mongo:${APP_V} --creds "$CREDS") + DIGEST="$(printf '%s' "$INSPECT" | tr -d '\n' | sed -n 's/.*"Digest"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p')" + test -n "$DIGEST" + printf 'MONGO_TAG=%s\nMONGO_DIGEST=%s\n' "$APP_V" "$DIGEST" | tee .env.lock ci/prod/env/.env.lock.db + cat .env.lock - name: deploy image: alpine:latest depends_on: [ lock-db ] commands: - - apk add --no-cache openssh-client rsync - - set -a; . ./ci/prod/.env.runtime; set +a # SSH_HOST, SSH_USER, REMOTE_DIR берём из репо - - set -a; . ./.env.version; set +a - - install -m 600 secrets/SSH_KEY /root/.ssh/id_rsa - # ensure target dir layout - - ssh -o StrictHostKeyChecking=no ${SSH_USER}@${SSH_HOST} "mkdir -p ${REMOTE_DIR}/{ops,vault/templates,backup,.woodpecker}" - # sync non-secret runtime files from repo → server - - rsync -avz --delete ci/prod/compose/ ${SSH_USER}@${SSH_HOST}:${REMOTE_BASE}/${DB_DIR}/compose/ - - rsync -avz .woodpecker/ ${SSH_USER}@${SSH_HOST}:${REMOTE_DIR}/.woodpecker/ - - rsync -avz ci/prod/.env.runtime ${SSH_USER}@${SSH_HOST}:${REMOTE_BASE}/${DB_DIR}/env/.env.runtime - - rsync -avz ci/prod/env/.env.lock.db ${SSH_USER}@${SSH_HOST}:${REMOTE_BASE}/${DB_DIR}/env/.env.lock.db - # upload the generated lock - - scp -o StrictHostKeyChecking=no .env.lock ${SSH_USER}@${SSH_HOST}:${REMOTE_DIR}/.env.lock - # deploy - - ssh -o StrictHostKeyChecking=no ${SSH_USER}@${SSH_HOST} " - set -euo pipefail - cd ${REMOTE_DIR} - docker compose -f .woodpecker/db.yml pull - docker compose -f .woodpecker/db.yml up -d --remove-orphans - " + - | + set -euo + apk add --no-cache openssh-client rsync + set -a + . ./ci/prod/.env.runtime + . ./.env.version + set +a + install -m 600 secrets/SSH_KEY /root/.ssh/id_rsa + ssh -o StrictHostKeyChecking=no "${SSH_USER}@${SSH_HOST}" 'bash -s' < ci/prod/scripts/deploy-db.sh diff --git a/ci/prod/scripts/deploy-db.sh b/ci/prod/scripts/deploy-db.sh new file mode 100755 index 0000000..7d1886f --- /dev/null +++ b/ci/prod/scripts/deploy-db.sh @@ -0,0 +1,24 @@ +#!/usr/bin/env bash +set -euo pipefail + +: "${REMOTE_DIR:?missing REMOTE_DIR}" +: "${REMOTE_BASE:?missing REMOTE_BASE}" +: "${DB_DIR:?missing DB_DIR}" +: "${SSH_USER:?missing SSH_USER}" +: "${SSH_HOST:?missing SSH_HOST}" + +REMOTE_TARGET="${SSH_USER}@${SSH_HOST}" +REMOTE_COMPOSE_DIR="${REMOTE_BASE}/${DB_DIR}" + +ssh -o StrictHostKeyChecking=no "$REMOTE_TARGET" "mkdir -p ${REMOTE_DIR}/{ops,vault/templates,backup,.woodpecker}" +rsync -avz --delete ci/prod/compose/ "$REMOTE_TARGET:${REMOTE_COMPOSE_DIR}/compose/" +rsync -avz .woodpecker/ "$REMOTE_TARGET:${REMOTE_DIR}/.woodpecker/" +rsync -avz ci/prod/.env.runtime "$REMOTE_TARGET:${REMOTE_COMPOSE_DIR}/env/.env.runtime" +rsync -avz ci/prod/env/.env.lock.db "$REMOTE_TARGET:${REMOTE_COMPOSE_DIR}/env/.env.lock.db" +scp -o StrictHostKeyChecking=no .env.lock "$REMOTE_TARGET:${REMOTE_DIR}/.env.lock" +ssh -o StrictHostKeyChecking=no "$REMOTE_TARGET" <<'EOSSH' + set -euo pipefail + cd "${REMOTE_DIR}" + docker compose -f .woodpecker/db.yml pull + docker compose -f .woodpecker/db.yml up -d --remove-orphans +EOSSH