From c08ae1def0ab140b4e66553c97b2149ff3751660 Mon Sep 17 00:00:00 2001 From: liangxu Date: Sat, 2 May 2026 00:32:17 +0800 Subject: [PATCH] chore(ci): remove NAS Compose deployment workflow --- .gitea/workflows/nas-compose-deploy.yml | 535 ------------------------ 1 file changed, 535 deletions(-) delete mode 100644 .gitea/workflows/nas-compose-deploy.yml diff --git a/.gitea/workflows/nas-compose-deploy.yml b/.gitea/workflows/nas-compose-deploy.yml deleted file mode 100644 index 68736f0..0000000 --- a/.gitea/workflows/nas-compose-deploy.yml +++ /dev/null @@ -1,535 +0,0 @@ -name: Deploy NAS Compose - -on: - workflow_dispatch: - inputs: - host: - description: "NAS SSH host" - required: true - default: "192.168.100.19" - port: - description: "NAS SSH port" - required: true - default: "22" - user: - description: "NAS SSH user" - required: true - default: "admin" - password_source: - description: "NAS SSH password source" - required: true - default: "repository_secret" - type: choice - options: - - repository_secret - - workflow_input - ssh_password: - description: "NAS SSH password. Used only when password_source is workflow_input." - required: false - default: "" - deploy_base: - description: "Base deployment directory on the NAS" - required: true - default: "/vol1/1000/application/geo" - registry_host: - description: "Gitea Registry host, without scheme" - required: true - default: "192.168.100.19:13000" - registry_owner: - description: "Gitea package owner" - required: true - default: "root" - registry_image_prefix: - description: "Image package prefix. Images become /." - required: true - default: "geo-rankly" - registry_username: - description: "Gitea Registry username" - required: true - default: "root" - registry_password_source: - description: "Gitea Registry password source" - required: true - default: "repository_secret" - type: choice - options: - - repository_secret - - workflow_input - registry_password: - description: "Gitea Registry password/token. Used only when registry_password_source is workflow_input." - required: false - default: "" - registry_plain_http: - description: "Registry uses plain HTTP and must be configured as an insecure registry." - required: true - default: "true" - type: choice - options: - - "true" - - "false" - image_tag: - description: "Docker image tag. Defaults to the pushed commit SHA first 8 chars." - required: false - default: "" - keep_releases: - description: "Number of old release directories to keep" - required: false - default: "1" - -permissions: - contents: read - -jobs: - deploy: - name: Deploy Docker Compose to NAS - runs-on: ubuntu-latest - timeout-minutes: 180 - env: - NAS_HOST: ${{ inputs.host }} - NAS_PORT: ${{ inputs.port }} - NAS_USER: ${{ inputs.user }} - NAS_SSH_PASSWORD_SOURCE: ${{ inputs.password_source }} - NAS_SSH_PASSWORD_INPUT: ${{ inputs.ssh_password }} - NAS_SSH_PASSWORD_SECRET: ${{ secrets.NAS_SSH_PASSWORD }} - DEPLOY_BASE: ${{ inputs.deploy_base }} - REGISTRY_HOST: ${{ inputs.registry_host }} - REGISTRY_OWNER: ${{ inputs.registry_owner }} - REGISTRY_IMAGE_PREFIX: ${{ inputs.registry_image_prefix }} - REGISTRY_USERNAME: ${{ inputs.registry_username }} - REGISTRY_PASSWORD_SOURCE: ${{ inputs.registry_password_source }} - REGISTRY_PASSWORD_INPUT: ${{ inputs.registry_password }} - REGISTRY_PASSWORD_SECRET: ${{ secrets.REGISTRY_PASSWORD }} - REGISTRY_PLAIN_HTTP: ${{ inputs.registry_plain_http }} - REQUESTED_IMAGE_TAG: ${{ inputs.image_tag }} - KEEP_RELEASES: ${{ inputs.keep_releases }} - NAS_DEPLOY_ENV: ${{ secrets.NAS_DEPLOY_ENV }} - DOCKER_BUILDKIT: "1" - IMAGE_LIST: migrate tenant-api ops-api worker-generate kol-assist-worker scheduler frontend ops-web - - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Install SSH tools - run: | - apt-get update - apt-get install -y curl openssh-client sshpass tar - - - name: Resolve deployment metadata - run: | - set -eu - - case "${NAS_SSH_PASSWORD_SOURCE:-repository_secret}" in - repository_secret) - nas_ssh_password="${NAS_SSH_PASSWORD_SECRET:-}" - ;; - workflow_input) - nas_ssh_password="${NAS_SSH_PASSWORD_INPUT:-}" - ;; - *) - echo "::error::password_source must be repository_secret or workflow_input" - exit 1 - ;; - esac - - if [ -n "${nas_ssh_password}" ]; then - echo "::add-mask::${nas_ssh_password}" - fi - - { - echo "NAS_SSH_PASSWORD<<__NAS_SSH_PASSWORD__" - printf '%s\n' "${nas_ssh_password}" - echo "__NAS_SSH_PASSWORD__" - echo "NAS_SSH_PASSWORD_INPUT=" - echo "NAS_SSH_PASSWORD_SECRET=" - } >> "${GITHUB_ENV}" - - if [ -z "${nas_ssh_password}" ]; then - if [ "${NAS_SSH_PASSWORD_SOURCE:-repository_secret}" = "workflow_input" ]; then - echo "::error::Missing workflow input ssh_password" - else - echo "::error::Missing repository secret NAS_SSH_PASSWORD" - fi - exit 1 - fi - - case "${REGISTRY_PASSWORD_SOURCE:-repository_secret}" in - repository_secret) - registry_password="${REGISTRY_PASSWORD_SECRET:-}" - ;; - workflow_input) - registry_password="${REGISTRY_PASSWORD_INPUT:-}" - ;; - *) - echo "::error::registry_password_source must be repository_secret or workflow_input" - exit 1 - ;; - esac - - if [ -n "${registry_password}" ]; then - echo "::add-mask::${registry_password}" - fi - - { - echo "REGISTRY_PASSWORD<<__REGISTRY_PASSWORD__" - printf '%s\n' "${registry_password}" - echo "__REGISTRY_PASSWORD__" - echo "REGISTRY_PASSWORD_INPUT=" - echo "REGISTRY_PASSWORD_SECRET=" - } >> "${GITHUB_ENV}" - - if [ -z "${registry_password}" ]; then - if [ "${REGISTRY_PASSWORD_SOURCE:-repository_secret}" = "workflow_input" ]; then - echo "::error::Missing workflow input registry_password" - else - echo "::error::Missing repository secret REGISTRY_PASSWORD" - fi - exit 1 - fi - - REGISTRY_HOST="${REGISTRY_HOST#http://}" - REGISTRY_HOST="${REGISTRY_HOST#https://}" - REGISTRY_HOST="${REGISTRY_HOST%/}" - - case "${NAS_PORT}" in - *[!0-9]*|"") - echo "::error::NAS SSH port must be numeric" - exit 1 - ;; - esac - - case "${DEPLOY_BASE}" in - /*) ;; - *) - echo "::error::deploy_base must be an absolute path" - exit 1 - ;; - esac - - case "${DEPLOY_BASE}" in - *[!A-Za-z0-9_./-]*) - echo "::error::deploy_base contains unsupported characters" - exit 1 - ;; - esac - - case "${KEEP_RELEASES}" in - *[!0-9]*|"") KEEP_RELEASES=5 ;; - esac - - case "${REGISTRY_HOST}" in - *[!A-Za-z0-9_.:-]*|"") - echo "::error::registry_host must be a host[:port] without scheme" - exit 1 - ;; - esac - - case "${REGISTRY_OWNER}" in - *[!A-Za-z0-9_.-]*|"") - echo "::error::registry_owner contains unsupported characters" - exit 1 - ;; - esac - - case "${REGISTRY_IMAGE_PREFIX}" in - *[!A-Za-z0-9_.-]*|"") - echo "::error::registry_image_prefix contains unsupported characters" - exit 1 - ;; - esac - - case "${REGISTRY_USERNAME}" in - *[!A-Za-z0-9_.@-]*|"") - echo "::error::registry_username contains unsupported characters" - exit 1 - ;; - esac - - case "${REGISTRY_PLAIN_HTTP}" in - true|false) ;; - *) - echo "::error::registry_plain_http must be true or false" - exit 1 - ;; - esac - - image_tag="${REQUESTED_IMAGE_TAG}" - if [ -z "${image_tag}" ]; then - image_tag="$(git rev-parse --short=8 HEAD)" - fi - - case "${image_tag}" in - *[!A-Za-z0-9_.-]*|"") - echo "::error::image_tag may only contain letters, numbers, underscore, dot, and dash" - exit 1 - ;; - esac - - release_id="$(date -u +%Y%m%d%H%M%S)-$(git rev-parse --short=12 HEAD)" - - { - echo "REGISTRY_HOST=${REGISTRY_HOST}" - echo "IMAGE_TAG=${image_tag}" - echo "RELEASE_ID=${release_id}" - echo "KEEP_RELEASES=${KEEP_RELEASES}" - } >> "${GITHUB_ENV}" - - echo "Image tag: ${image_tag}" - echo "Release ID: ${release_id}" - echo "Deploy base: ${DEPLOY_BASE}" - echo "Gitea Registry: ${REGISTRY_HOST}/${REGISTRY_OWNER}/${REGISTRY_IMAGE_PREFIX}/:${image_tag}" - - - name: Verify current images in Gitea Registry - run: | - set -eu - if ! bash deploy/scripts/gitea-registry-images.sh verify; then - echo "::error::Missing image(s) for commit ${IMAGE_TAG}. CI must publish images before CD." - exit 1 - fi - - - name: Prepare SSH known hosts - run: | - set -eu - mkdir -p ~/.ssh - chmod 700 ~/.ssh - ssh-keyscan -p "${NAS_PORT}" "${NAS_HOST}" >> ~/.ssh/known_hosts - - - name: Validate migrations on empty Postgres - run: | - set -eu - - test_name="geo-migration-preflight-${GITHUB_RUN_ID:-manual}-${GITHUB_RUN_NUMBER:-0}" - network_name="${test_name}-net" - postgres_name="${test_name}-postgres" - migrate_image="${test_name}-migrate" - - cleanup() { - docker rm -f "${postgres_name}" >/dev/null 2>&1 || true - docker network rm "${network_name}" >/dev/null 2>&1 || true - docker image rm "${migrate_image}" >/dev/null 2>&1 || true - } - trap cleanup EXIT INT TERM - - docker network create "${network_name}" >/dev/null - docker run -d \ - --name "${postgres_name}" \ - --network "${network_name}" \ - -e POSTGRES_DB=geo \ - -e POSTGRES_USER=geo \ - -e POSTGRES_PASSWORD=geo_dev \ - postgres:16-alpine >/dev/null - - echo "Waiting for empty Postgres..." - for i in $(seq 1 60); do - if docker exec "${postgres_name}" pg_isready -U geo -d geo >/dev/null 2>&1; then - break - fi - if [ "${i}" -eq 60 ]; then - docker logs "${postgres_name}" || true - echo "::error::Postgres did not become ready" - exit 1 - fi - sleep 1 - done - - docker exec "${postgres_name}" createdb -U geo geo_monitoring - - printf '%s\n' "${REGISTRY_PASSWORD}" \ - | docker login "${REGISTRY_HOST}" -u "${REGISTRY_USERNAME}" --password-stdin - - echo "Pulling migration image..." - docker pull "${REGISTRY_HOST}/${REGISTRY_OWNER}/${REGISTRY_IMAGE_PREFIX}/migrate:${IMAGE_TAG}" - docker tag "${REGISTRY_HOST}/${REGISTRY_OWNER}/${REGISTRY_IMAGE_PREFIX}/migrate:${IMAGE_TAG}" "${migrate_image}" - - database_url="postgres://geo:geo_dev@${postgres_name}:5432/geo?sslmode=disable" - monitoring_database_url="postgres://geo:geo_dev@${postgres_name}:5432/geo_monitoring?sslmode=disable" - ops_database_url="postgres://geo:geo_dev@${postgres_name}:5432/geo?sslmode=disable&x-migrations-table=schema_migrations_ops" - - echo "Running tenant migrations on an empty database..." - docker run --rm --network "${network_name}" "${migrate_image}" \ - -path /migrations \ - -database "${database_url}" \ - up - - echo "Running monitoring migrations on an empty database..." - docker run --rm --network "${network_name}" "${migrate_image}" \ - -path /migrations_monitoring \ - -database "${monitoring_database_url}" \ - up - - echo "Running ops migrations on an empty database..." - docker run --rm --network "${network_name}" "${migrate_image}" \ - -path /migrations_ops \ - -database "${ops_database_url}" \ - up - - echo "Migration preflight passed." - - - name: Upload release to NAS - run: | - set -eu - - remote="${NAS_USER}@${NAS_HOST}" - ssh_opts="-p ${NAS_PORT} -o BatchMode=no -o StrictHostKeyChecking=yes" - release_dir="${DEPLOY_BASE}/releases/${RELEASE_ID}" - - SSHPASS="${NAS_SSH_PASSWORD}" sshpass -e ssh ${ssh_opts} "${remote}" \ - "mkdir -p '${release_dir}' '${DEPLOY_BASE}/shared'" - - git archive --format=tar.gz HEAD \ - | SSHPASS="${NAS_SSH_PASSWORD}" sshpass -e ssh ${ssh_opts} "${remote}" \ - "tar -xzf - -C '${release_dir}'" - - - name: Upload deploy env when provided - run: | - set -eu - - if [ -z "${NAS_DEPLOY_ENV:-}" ]; then - echo "NAS_DEPLOY_ENV is empty; keeping existing NAS .env or creating from .env.example during deploy." - exit 0 - fi - - remote="${NAS_USER}@${NAS_HOST}" - ssh_opts="-p ${NAS_PORT} -o BatchMode=no -o StrictHostKeyChecking=yes" - - printf '%s\n' "${NAS_DEPLOY_ENV}" \ - | SSHPASS="${NAS_SSH_PASSWORD}" sshpass -e ssh ${ssh_opts} "${remote}" \ - "cat > '${DEPLOY_BASE}/shared/.env' && chmod 600 '${DEPLOY_BASE}/shared/.env'" - - - name: Deploy with Docker Compose - run: | - set -eu - - remote="${NAS_USER}@${NAS_HOST}" - ssh_opts="-p ${NAS_PORT} -o BatchMode=no -o StrictHostKeyChecking=yes" - sudo_password_b64="$(printf '%s' "${NAS_SSH_PASSWORD}" | base64 | tr -d '\n')" - registry_password_b64="$(printf '%s' "${REGISTRY_PASSWORD}" | base64 | tr -d '\n')" - - { - printf 'SUDO_PASSWORD_B64=%s\n' "${sudo_password_b64}" - printf 'REGISTRY_PASSWORD_B64=%s\n' "${registry_password_b64}" - cat <<'REMOTE_SCRIPT' - set -eu - - release_dir="${DEPLOY_BASE}/releases/${RELEASE_ID}" - shared_dir="${DEPLOY_BASE}/shared" - lock_dir="${DEPLOY_BASE}/.deploy.lock" - - if ! mkdir "${lock_dir}" 2>/dev/null; then - echo "Another deployment is already running: ${lock_dir}" - exit 1 - fi - trap 'rmdir "${lock_dir}"' EXIT INT TERM - - if [ ! -f "${shared_dir}/.env" ]; then - cp "${release_dir}/deploy/.env.example" "${shared_dir}/.env" - chmod 600 "${shared_dir}/.env" - echo "Created ${shared_dir}/.env from .env.example. Edit it on the NAS before production traffic if secrets are still placeholders." - fi - - SUDO_PASSWORD="$(printf '%s' "${SUDO_PASSWORD_B64}" | base64 -d 2>/dev/null || true)" - REGISTRY_PASSWORD="$(printf '%s' "${REGISTRY_PASSWORD_B64}" | base64 -d 2>/dev/null || true)" - unset SUDO_PASSWORD_B64 REGISTRY_PASSWORD_B64 - - docker_cmd() { - if [ "${DOCKER_MODE}" = "sudo" ]; then - printf '%s\n' "${SUDO_PASSWORD}" | sudo -S -p "" docker "$@" - else - docker "$@" - fi - } - - compose_cmd() { - docker_cmd compose "$@" - } - - docker_login() { - if [ "${DOCKER_MODE}" = "sudo" ]; then - printf '%s\n' "${SUDO_PASSWORD}" | sudo -S -p "" -v - printf '%s\n' "${REGISTRY_PASSWORD}" \ - | sudo -p "" docker login "${REGISTRY_HOST}" -u "${REGISTRY_USERNAME}" --password-stdin - else - printf '%s\n' "${REGISTRY_PASSWORD}" \ - | docker login "${REGISTRY_HOST}" -u "${REGISTRY_USERNAME}" --password-stdin - fi - } - - if docker info >/dev/null 2>&1; then - DOCKER_MODE="direct" - elif command -v sudo >/dev/null 2>&1 && [ -n "${SUDO_PASSWORD}" ] && printf '%s\n' "${SUDO_PASSWORD}" | sudo -S -p "" docker info >/dev/null 2>&1; then - DOCKER_MODE="sudo" - else - echo "Cannot access Docker daemon as $(id -un). Add the user to the docker group or allow sudo docker." - exit 1 - fi - - echo "Docker access mode: ${DOCKER_MODE}" - compose_cmd version - - if [ "${REGISTRY_PLAIN_HTTP}" = "true" ] \ - && ! docker_cmd info 2>/dev/null \ - | sed -n '/Insecure Registries:/,/Live Restore/p' \ - | grep -Fq "${REGISTRY_HOST}"; then - echo "${REGISTRY_HOST} is not listed in Docker insecure registries on the NAS." - echo "Configure Docker daemon.json with: {\"insecure-registries\":[\"${REGISTRY_HOST}\"]}" - exit 1 - fi - - docker_login - - IMAGE_REGISTRY="${REGISTRY_HOST}/${REGISTRY_OWNER}/${REGISTRY_IMAGE_PREFIX}" \ - IMAGE_TAG="${IMAGE_TAG}" compose_cmd \ - --env-file "${shared_dir}/.env" \ - -f "${release_dir}/deploy/docker-compose.yaml" \ - config >/tmp/geo-rankly-compose.yaml - - IMAGE_REGISTRY="${REGISTRY_HOST}/${REGISTRY_OWNER}/${REGISTRY_IMAGE_PREFIX}" \ - IMAGE_TAG="${IMAGE_TAG}" compose_cmd \ - --env-file "${shared_dir}/.env" \ - -f "${release_dir}/deploy/docker-compose.yaml" \ - pull migrate tenant-api ops-api worker-generate kol-assist-worker scheduler frontend ops-web - - IMAGE_REGISTRY="${REGISTRY_HOST}/${REGISTRY_OWNER}/${REGISTRY_IMAGE_PREFIX}" \ - IMAGE_TAG="${IMAGE_TAG}" compose_cmd \ - --env-file "${shared_dir}/.env" \ - -f "${release_dir}/deploy/docker-compose.yaml" \ - up -d --no-build --remove-orphans - - ln -sfn "${release_dir}" "${DEPLOY_BASE}/current" - - IMAGE_REGISTRY="${REGISTRY_HOST}/${REGISTRY_OWNER}/${REGISTRY_IMAGE_PREFIX}" \ - IMAGE_TAG="${IMAGE_TAG}" compose_cmd \ - --env-file "${shared_dir}/.env" \ - -f "${release_dir}/deploy/docker-compose.yaml" \ - ps - - if [ "${KEEP_RELEASES}" -gt 0 ] 2>/dev/null; then - find "${DEPLOY_BASE}/releases" -mindepth 1 -maxdepth 1 -type d \ - | sort -r \ - | awk "NR>${KEEP_RELEASES}" \ - | xargs -r rm -rf - fi - REMOTE_SCRIPT - } | SSHPASS="${NAS_SSH_PASSWORD}" sshpass -e ssh ${ssh_opts} "${remote}" \ - "DEPLOY_BASE='${DEPLOY_BASE}' RELEASE_ID='${RELEASE_ID}' IMAGE_TAG='${IMAGE_TAG}' KEEP_RELEASES='${KEEP_RELEASES}' REGISTRY_HOST='${REGISTRY_HOST}' REGISTRY_OWNER='${REGISTRY_OWNER}' REGISTRY_IMAGE_PREFIX='${REGISTRY_IMAGE_PREFIX}' REGISTRY_USERNAME='${REGISTRY_USERNAME}' REGISTRY_PLAIN_HTTP='${REGISTRY_PLAIN_HTTP}' sh -se" - - - name: Deployment summary - run: | - summary_file="${GITHUB_STEP_SUMMARY:-/tmp/nas-compose-deploy-summary.md}" - { - echo "### NAS Docker Compose deployment" - echo "" - echo "- Host: ${NAS_HOST}:${NAS_PORT}" - echo "- Deploy base: ${DEPLOY_BASE}" - echo "- Release: ${RELEASE_ID}" - echo "- Image tag: ${IMAGE_TAG}" - echo "- Gitea Registry: ${REGISTRY_HOST}/${REGISTRY_OWNER}/${REGISTRY_IMAGE_PREFIX}/:${IMAGE_TAG}" - echo "" - echo "Useful NAS commands:" - echo "" - echo '```sh' - echo "cd ${DEPLOY_BASE}/current" - echo "docker compose --env-file ${DEPLOY_BASE}/shared/.env -f deploy/docker-compose.yaml ps" - echo "docker compose --env-file ${DEPLOY_BASE}/shared/.env -f deploy/docker-compose.yaml logs -f tenant-api" - echo '```' - } >> "${summary_file}"