eletrotupi / tcc/ .github/workflows/deploy.ymlfb32fbf5090f5a80cadcf65e58ac16ae9a97c29b
3.6 KB | 144 lines Raw
1
name: Test & Deploy
2

3
on:
4
  push:
5
    branches:
6
      - master
7
      - production
8
    paths:
9
      - 'api/**'
10
      - '.github/workflows/deploy.yml'
11
  pull_request:
12
    paths:
13
      - 'api/**'
14
      - '.github/workflows/deploy.yml'
15

16
env:
17
  REGISTRY: ghcr.io
18
  IMAGE_NAME: ${{ github.repository }}
19

20
jobs:
21
  test:
22
    name: Run test suite
23
    runs-on: ubuntu-latest
24

25
    services:
26
      postgres:
27
        image: postgres:17-alpine
28
        env:
29
          POSTGRES_USER: user
30
          POSTGRES_PASSWORD: password
31
          POSTGRES_DB: postgres
32
        options: >-
33
          --health-cmd "pg_isready -U user"
34
          --health-interval 10s
35
          --health-timeout 5s
36
          --health-retries 5
37
        ports:
38
          - 5432:5432
39

40
    defaults:
41
      run:
42
        working-directory: api
43

44
    env:
45
      DATABASE_URL: postgres://user:password@localhost:5432/orbit_test
46
      NODE_ENV: test
47
      JWT_SECRET: test-secret-key
48
      FRONTEND_URL: http://localhost:8081
49

50
    steps:
51
      - uses: actions/checkout@v4
52

53
      - uses: actions/setup-node@v4
54
        with:
55
          node-version: '24'
56
          cache: 'npm'
57
          cache-dependency-path: api/package-lock.json
58

59
      - name: Install dependencies
60
        run: npm ci
61

62
      - name: Set up test database
63
        run: |
64
          node scripts/create-test-db.mjs
65
          npx prisma generate
66
          npx prisma db push
67

68
      - name: Run tests
69
        run: npx vitest run
70

71
  build-and-push:
72
    name: Build & push image
73
    needs: test
74
    if: github.event_name == 'push'
75
    runs-on: ubuntu-latest
76
    permissions:
77
      contents: read
78
      packages: write
79

80
    outputs:
81
      image_tag: ${{ steps.meta.outputs.version }}
82

83
    steps:
84
      - name: Checkout
85
        uses: actions/checkout@v4
86

87
      - name: Log in to GHCR
88
        uses: docker/login-action@v3
89
        with:
90
          registry: ${{ env.REGISTRY }}
91
          username: ${{ github.actor }}
92
          password: ${{ secrets.GHCR_TOKEN }}
93

94
      - name: Docker metadata
95
        id: meta
96
        uses: docker/metadata-action@v5
97
        with:
98
          images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
99
          tags: |
100
            # main branch => tag "latest"
101
            type=raw,value=latest,enable=${{ github.ref == 'refs/heads/master' }}
102
            # production branch => tag "production"
103
            # type=raw,value=production,enable=${{ github.ref == 'refs/heads/production' }}
104
            # always tag with short SHA for traceability
105
            type=sha,prefix=,format=short
106

107
      - name: Build and push
108
        uses: docker/build-push-action@v5
109
        with:
110
          context: ./api
111
          file: ./api/Dockerfile.production
112
          push: true
113
          tags: ${{ steps.meta.outputs.tags }}
114
          labels: ${{ steps.meta.outputs.labels }}
115

116
  deploy-production:
117
    name: Deploy to production
118
    needs: build-and-push
119
    runs-on: ubuntu-latest
120
    if: github.ref == 'refs/heads/master'
121
    environment: production
122

123
    steps:
124
      - name: Deploy via SSH
125
        uses: appleboy/ssh-action@v1
126
        with:
127
          host: ${{ secrets.VPS_HOST }}
128
          username: ${{ secrets.VPS_USER }}
129
          key: ${{ secrets.VPS_SSH_KEY }}
130
          script: |
131
            set -e
132
            cd /srv/app/production
133

134
            docker pull ghcr.io/${{ github.repository }}:latest
135

136
            sed -i "s/^IMAGE_TAG=.*/IMAGE_TAG=latest/" .env
137

138
            docker compose up -d --no-deps --force-recreate api
139

140
            # old image layers pile up across deploys; keep roughly the last week
141
            docker image prune -af --filter "until=168h" >/dev/null 2>&1 || true
142

143
            sleep 5
144
            docker compose logs --tail=50 api