diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml index 6ba2cbb..e8fd648 100644 --- a/.gitea/workflows/ci.yml +++ b/.gitea/workflows/ci.yml @@ -5,6 +5,7 @@ on: branches: [main] pull_request: branches: [main] + workflow_dispatch: jobs: test: @@ -22,3 +23,65 @@ jobs: - run: python -m pip install -r requirements.txt - run: python -m pytest -q - run: node --check app/static/admin.js + + deploy: + if: (github.event_name == 'push' && github.ref == 'refs/heads/main') || github.event_name == 'workflow_dispatch' + needs: test + runs-on: ubuntu-latest + concurrency: + group: production-deploy + cancel-in-progress: false + env: + PROD_PATH: /srv/miem_workers + RELEASE_NAME: miem-workers-release-${{ github.run_id }}.tar + steps: + - uses: actions/checkout@v4 + - name: Prepare release + shell: bash + run: | + set -euo pipefail + tar --exclude=.git --exclude=.env -cf "$RUNNER_TEMP/$RELEASE_NAME" . + printf '%s\n' "$PROD_SSH_KEY" > "$RUNNER_TEMP/deploy_key" + printf '%s\n' "$PROD_KNOWN_HOSTS" > "$RUNNER_TEMP/known_hosts" + chmod 600 "$RUNNER_TEMP/deploy_key" "$RUNNER_TEMP/known_hosts" + env: + PROD_SSH_KEY: ${{ secrets.PROD_SSH_KEY }} + PROD_KNOWN_HOSTS: ${{ secrets.PROD_KNOWN_HOSTS }} + - name: Upload release + shell: bash + run: | + set -euo pipefail + scp -i "$RUNNER_TEMP/deploy_key" \ + -o BatchMode=yes \ + -o UserKnownHostsFile="$RUNNER_TEMP/known_hosts" \ + "$RUNNER_TEMP/$RELEASE_NAME" \ + "$PROD_USER@$PROD_HOST:/tmp/$RELEASE_NAME" + env: + PROD_HOST: ${{ secrets.PROD_HOST }} + PROD_USER: ${{ secrets.PROD_USER }} + - name: Deploy and verify + shell: bash + run: | + set -euo pipefail + ssh -i "$RUNNER_TEMP/deploy_key" \ + -o BatchMode=yes \ + -o UserKnownHostsFile="$RUNNER_TEMP/known_hosts" \ + "$PROD_USER@$PROD_HOST" \ + "PROD_PATH='$PROD_PATH' RELEASE_NAME='$RELEASE_NAME' bash -s" <<'REMOTE' + set -euo pipefail + cd "$PROD_PATH" + mkdir -p "$HOME/backups/miem_workers" + backup="$HOME/backups/miem_workers/miem_workers-$(date +%Y%m%d-%H%M%S).sql" + docker compose exec -T postgres sh -c 'pg_dump -U "$POSTGRES_USER" "$POSTGRES_DB"' > "$backup" + docker run --rm \ + -v "/tmp/$RELEASE_NAME:/src/release.tar:ro" \ + -v "$PROD_PATH":/dst \ + alpine:3.20 sh -c 'tar -xf /src/release.tar -C /dst' + docker compose up -d --build + curl --fail --silent --show-error http://127.0.0.1:8000/api/health + docker compose ps + rm -f "/tmp/$RELEASE_NAME" + REMOTE + env: + PROD_HOST: ${{ secrets.PROD_HOST }} + PROD_USER: ${{ secrets.PROD_USER }} diff --git a/.gitea/workflows/deploy.yml b/.gitea/workflows/deploy.yml deleted file mode 100644 index bfc1e93..0000000 --- a/.gitea/workflows/deploy.yml +++ /dev/null @@ -1,63 +0,0 @@ -name: Deploy production - -on: - push: - branches: [main] - workflow_dispatch: - -jobs: - deploy: - runs-on: ubuntu-latest - env: - PROD_PATH: /srv/miem_workers - steps: - - uses: actions/checkout@v4 - - name: Prepare release - shell: bash - run: | - set -euo pipefail - tar --exclude=.git --exclude=.env -cf "$RUNNER_TEMP/miem-workers-release.tar" . - printf '%s\n' "$PROD_SSH_KEY" > "$RUNNER_TEMP/deploy_key" - printf '%s\n' "$PROD_KNOWN_HOSTS" > "$RUNNER_TEMP/known_hosts" - chmod 600 "$RUNNER_TEMP/deploy_key" "$RUNNER_TEMP/known_hosts" - env: - PROD_SSH_KEY: ${{ secrets.PROD_SSH_KEY }} - PROD_KNOWN_HOSTS: ${{ secrets.PROD_KNOWN_HOSTS }} - - name: Upload release - shell: bash - run: | - set -euo pipefail - scp -i "$RUNNER_TEMP/deploy_key" \ - -o BatchMode=yes \ - -o UserKnownHostsFile="$RUNNER_TEMP/known_hosts" \ - "$RUNNER_TEMP/miem-workers-release.tar" \ - "$PROD_USER@$PROD_HOST:/tmp/miem-workers-release.tar" - env: - PROD_HOST: ${{ secrets.PROD_HOST }} - PROD_USER: ${{ secrets.PROD_USER }} - - name: Deploy and verify - shell: bash - run: | - set -euo pipefail - ssh -i "$RUNNER_TEMP/deploy_key" \ - -o BatchMode=yes \ - -o UserKnownHostsFile="$RUNNER_TEMP/known_hosts" \ - "$PROD_USER@$PROD_HOST" \ - "PROD_PATH='$PROD_PATH' bash -s" <<'REMOTE' - set -euo pipefail - cd "$PROD_PATH" - mkdir -p "$HOME/backups/miem_workers" - backup="$HOME/backups/miem_workers/miem_workers-$(date +%Y%m%d-%H%M%S).sql" - docker compose exec -T postgres sh -c 'pg_dump -U "$POSTGRES_USER" "$POSTGRES_DB"' > "$backup" - docker run --rm \ - -v /tmp/miem-workers-release.tar:/src/release.tar:ro \ - -v "$PROD_PATH":/dst \ - alpine:3.20 sh -c 'tar -xf /src/release.tar -C /dst' - docker compose up -d --build - curl --fail --silent --show-error http://127.0.0.1:8000/api/health - docker compose ps - rm -f /tmp/miem-workers-release.tar - REMOTE - env: - PROD_HOST: ${{ secrets.PROD_HOST }} - PROD_USER: ${{ secrets.PROD_USER }} diff --git a/README.md b/README.md index a9eea95..2dfee27 100644 --- a/README.md +++ b/README.md @@ -122,7 +122,7 @@ docker compose exec postgres pg_dump -U miem miem_workers > backup.sql docker compose down ``` -Production deploy выполняется workflow `Deploy production` после push в `main` или вручную. Для него нужны Actions secrets `PROD_HOST`, `PROD_USER`, `PROD_SSH_KEY` и `PROD_KNOWN_HOSTS`; `.env` и ключи в репозиторий не добавляются. Перед обновлением workflow сохраняет PostgreSQL backup в домашний каталог deploy-пользователя, пересобирает `api` и `worker` и проверяет `/api/health`. +Production deploy выполняется job `deploy` workflow `CI` после успешного `test` на push в `main` или вручную. Для него нужны Actions secrets `PROD_HOST`, `PROD_USER`, `PROD_SSH_KEY` и `PROD_KNOWN_HOSTS`; `.env` и ключи в репозиторий не добавляются. Перед обновлением workflow сохраняет PostgreSQL backup в домашний каталог deploy-пользователя, пересобирает `api` и `worker` и проверяет `/api/health`. Одновременно выполняется только один production deploy. ## Проверки