Skip to Content
3️⃣ День 3Автодеплой

Автодеплой

Эта методичка поможет выстроить современный CI/CD процесс деплоя Node.js/React-приложения через GitHub Actions и Docker. Пайплайн будет расти по мере добавления новых задач — от тестов до деплоя.


🔹 Этап 1. Проверка кода: линт и тесты

Создаём отдельный workflow checks.yaml, который принимает context (frontend или backend) и выполняет npm-скрипты.

.github/workflows/checks.yaml
name: Lint & Tests on: workflow_call: inputs: context: required: true type: string jobs: check: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Установка зависимостей working-directory: ${{ inputs.context }} run: npm ci - name: Линтинг working-directory: ${{ inputs.context }} run: npm run lint - name: Тестирование working-directory: ${{ inputs.context }} run: npm run test

🔹 Этап 2. Определение, что изменилось

Создаём джобу detect-changes, которая определяет, были ли изменения во frontend/** или backend/**, и передаёт это дальше.

name: Deploy on: push: branches: [ main ] jobs: detect-changes: runs-on: ubuntu-latest outputs: changed_context: ${{ steps.set-context.outputs.context }} steps: - uses: actions/checkout@v3 - name: Detect changed services id: filter uses: dorny/paths-filter@v3 with: filters: | frontend: - 'frontend/**' backend: - 'backend/**' - name: Set changed context id: set-context run: | if [[ "${{ steps.filter.outputs.frontend }}" == "true" ]]; then echo "context=frontend" >> $GITHUB_OUTPUT elif [[ "${{ steps.filter.outputs.backend }}" == "true" ]]; then echo "context=backend" >> $GITHUB_OUTPUT else echo "context=none" >> $GITHUB_OUTPUT fi

🔹 Этап 3. Запуск проверок

Вызываем checks.yaml, если frontend или backend был изменён:

checks: needs: detect-changes if: needs.detect-changes.outputs.changed_context != 'none' uses: ./.github/workflows/checks.yaml with: context: ${{ needs.detect-changes.outputs.changed_context }}

🔹 Этап 4. Сборка Docker-образа

Билдим и пушим Docker-образ только для изменившегося сервиса:

build: needs: - detect-changes - checks if: needs.detect-changes.outputs.changed_context != 'none' runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Log in to Docker Hub uses: docker/login-action@v3 with: username: ${{ secrets.DOCKER_USER }} password: ${{ secrets.DOCKER_PASS }} - name: Build and push run: | docker build -t ${{ secrets.DOCKER_USER }}/devops-${{ needs.detect-changes.outputs.changed_context }}:${{ github.sha }} ./${{ needs.detect-changes.outputs.changed_context }} docker push ${{ secrets.DOCKER_USER }}/devops-${{ needs.detect-changes.outputs.changed_context }}:${{ github.sha }}

🔹 Этап 5. Деплой на VPS

Деплой выполняется только при наличии изменений. Мы подставляем нужный тег образа в .env и перезапускаем docker compose.

deploy: runs-on: ubuntu-latest needs: - detect-changes - build if: needs.detect-changes.outputs.changed_context != 'none' steps: - name: SSH deploy uses: appleboy/ssh-action@v0.1.6 with: host: ${{ secrets.SSH_HOST }} username: ${{ secrets.SSH_USER }} key: ${{ secrets.SSH_KEY }} script: | IMAGE=${{ secrets.DOCKER_USER }}/devops-${{ needs.detect-changes.outputs.changed_context }}:${{ github.sha }} docker pull $IMAGE cd /var/www/devops-intensive.it-incubator.io if [ "${{ needs.detect-changes.outputs.changed_context }}" = "frontend" ]; then sed -i "s|^FRONTEND_IMAGE=.*|FRONTEND_IMAGE=$IMAGE|" .env elif [ "${{ needs.detect-changes.outputs.changed_context }}" = "backend" ]; then sed -i "s|^BACKEND_IMAGE=.*|BACKEND_IMAGE=$IMAGE|" .env fi docker compose up -d

Добавляем в секцию script

echo ${{ secrets.DOCKER_PASS }} | docker login -u ${{ secrets.DOCKER_USER }} --password-stdin

Добавляем в секцию build

docker build \ -t ${{ secrets.DOCKER_USER }}/devops-${{ needs.detect-changes.outputs.changed_context }}:${{ github.sha }} \ --build-arg VITE_BACKEND_URL=https://devops-intensive.it-incubator.io/api \ ./${{ needs.detect-changes.outputs.changed_context }}

Добавим изменим команду запуска для Dockerfile на backend

CMD ["sh", "-c", "node scripts/setup-database.js && npm start"]

✅ Финальный файл .github/workflows/deploy.yml

name: Deploy on: push: branches: [main] jobs: detect-changes: runs-on: ubuntu-latest outputs: changed_context: ${{ steps.set-context.outputs.context }} steps: - uses: actions/checkout@v3 - name: Detect changed services id: filter uses: dorny/paths-filter@v3 with: filters: | frontend: - 'frontend/**' backend: - 'backend/**' - name: Set changed context id: set-context run: | if [[ "${{ steps.filter.outputs.frontend }}" == "true" ]]; then echo "context=frontend" >> $GITHUB_OUTPUT elif [[ "${{ steps.filter.outputs.backend }}" == "true" ]]; then echo "context=backend" >> $GITHUB_OUTPUT else echo "context=none" >> $GITHUB_OUTPUT fi checks: needs: detect-changes if: needs.detect-changes.outputs.changed_context != 'none' uses: ./.github/workflows/checks.yaml with: context: ${{ needs.detect-changes.outputs.changed_context }} build: needs: - detect-changes - checks if: needs.detect-changes.outputs.changed_context != 'none' runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Log in to Docker Hub uses: docker/login-action@v3 with: username: ${{ secrets.DOCKER_USER }} password: ${{ secrets.DOCKER_PASS }} - name: Build and push run: | docker build \ -t ${{ secrets.DOCKER_USER }}/devops-${{ needs.detect-changes.outputs.changed_context }}:${{ github.sha }} \ --build-arg VITE_BACKEND_URL=https://devops-intensive.it-incubator.io/api \ ./${{ needs.detect-changes.outputs.changed_context }} docker push ${{ secrets.DOCKER_USER }}/devops-${{ needs.detect-changes.outputs.changed_context }}:${{ github.sha }} deploy: runs-on: ubuntu-latest needs: - detect-changes - build if: needs.detect-changes.outputs.changed_context != 'none' steps: - name: SSH deploy uses: appleboy/ssh-action@v0.1.6 with: host: ${{ secrets.SSH_HOST }} username: ${{ secrets.SSH_USER }} key: ${{ secrets.SSH_KEY }} script: | echo ${{ secrets.DOCKER_PASS }} | docker login -u ${{ secrets.DOCKER_USER }} --password-stdin IMAGE=${{ secrets.DOCKER_USER }}/devops-${{ needs.detect-changes.outputs.changed_context }}:${{ github.sha }} docker pull $IMAGE cd /var/www/devops-intensive.it-incubator.io if [ "${{ needs.detect-changes.outputs.changed_context }}" = "frontend" ]; then sed -i "s|^FRONTEND_IMAGE=.*|FRONTEND_IMAGE=$IMAGE|" .env elif [ "${{ needs.detect-changes.outputs.changed_context }}" = "backend" ]; then sed -i "s|^BACKEND_IMAGE=.*|BACKEND_IMAGE=$IMAGE|" .env fi docker compose up -d

Теперь у тебя минималистичный и масштабируемый пайплайн: всё собирается и деплоится только при реальных изменениях.

Last updated on