-
Notifications
You must be signed in to change notification settings - Fork 52
179 lines (152 loc) · 6.28 KB
/
deploy.yml
File metadata and controls
179 lines (152 loc) · 6.28 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
# Docker-based deployment to VPS
#
# Required GitHub Secrets:
# - VPS_HOST: VPS IP address or hostname
# - VPS_USER: SSH username
# - SSH_PRIVATE_KEY: SSH private key for VPS access
# - GHCR_PAT: Personal Access Token with read:packages, write:packages scope
# - DOCKER_ENV: Full .env file content for docker-compose (includes all config)
# - FIREBASE_CREDENTIALS_JSON: Firebase Admin SDK service account JSON (for push notifications)
#
# Setup instructions:
# 1. Create PAT: GitHub Settings -> Developer settings -> Personal access tokens with read:packages, write:packages scope
# 2. On VPS: mkdir -p /deploy/logistics
# 3. Add DOCKER_ENV secret with all environment variables
# 4. Add FIREBASE_CREDENTIALS_JSON secret with the contents of firebase-adminsdk-key.json
name: Build and Deploy
on:
push:
branches: [prod]
workflow_dispatch:
env:
REGISTRY: ghcr.io
APP_DIR: ~/deploy/logistics
jobs:
build:
name: Build ${{ matrix.image }}
runs-on: ubuntu-latest
concurrency:
group: build-${{ matrix.image }}
cancel-in-progress: true
permissions:
contents: read
packages: write
strategy:
fail-fast: false
matrix:
include:
- image: api
context: .
file: ./src/Presentation/Logistics.API/Dockerfile
- image: identity
context: .
file: ./src/Presentation/Logistics.IdentityServer/Dockerfile
- image: admin-portal
context: ./src/Client/Logistics.Angular
file: ./src/Client/Logistics.Angular/Dockerfile.admin
- image: tms-portal
context: ./src/Client/Logistics.Angular
file: ./src/Client/Logistics.Angular/Dockerfile.tms
- image: customer-portal
context: ./src/Client/Logistics.Angular
file: ./src/Client/Logistics.Angular/Dockerfile.customer
- image: website
context: ./src/Client/Logistics.Angular
file: ./src/Client/Logistics.Angular/Dockerfile.website
steps:
- name: Checkout code
uses: actions/checkout@v6
- name: Set lowercase repository name
id: repo
run: echo "name=${GITHUB_REPOSITORY,,}" >> $GITHUB_OUTPUT
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GHCR_PAT }}
- name: Write Firebase credentials
if: matrix.image == 'api'
run: echo '${{ secrets.FIREBASE_CREDENTIALS_JSON }}' > ./src/Presentation/Logistics.API/firebase-adminsdk-key.json
- name: Build and push ${{ matrix.image }}
uses: docker/build-push-action@v6
with:
context: ${{ matrix.context }}
file: ${{ matrix.file }}
push: true
tags: ${{ env.REGISTRY }}/${{ steps.repo.outputs.name }}/${{ matrix.image }}:latest
cache-from: type=registry,ref=${{ env.REGISTRY }}/${{ steps.repo.outputs.name }}/${{ matrix.image }}:buildcache
cache-to: type=registry,ref=${{ env.REGISTRY }}/${{ steps.repo.outputs.name }}/${{ matrix.image }}:buildcache,mode=max
deploy:
name: Deploy to VPS
runs-on: ubuntu-latest
needs: build
concurrency:
group: deploy-prod
cancel-in-progress: false
steps:
- name: Checkout code
uses: actions/checkout@v6
- name: Copy docker-compose to VPS
uses: appleboy/scp-action@v1
with:
host: ${{ secrets.VPS_HOST }}
username: ${{ secrets.VPS_USER }}
key: ${{ secrets.SSH_PRIVATE_KEY }}
source: "src/Aspire/Logistics.Aspire.AppHost/aspire-output/docker-compose.yaml"
target: ${{ env.APP_DIR }}
strip_components: 4
- name: Deploy with Docker Compose
uses: appleboy/ssh-action@v1.2.4
env:
DOCKER_ENV: ${{ secrets.DOCKER_ENV }}
GHCR_PAT: ${{ secrets.GHCR_PAT }}
GITHUB_REPOSITORY: ${{ github.repository }}
IMAGE_TAG: latest
with:
host: ${{ secrets.VPS_HOST }}
username: ${{ secrets.VPS_USER }}
key: ${{ secrets.SSH_PRIVATE_KEY }}
envs: DOCKER_ENV,GHCR_PAT,GITHUB_REPOSITORY,IMAGE_TAG
script: |
# Navigate to app directory
cd ${{ env.APP_DIR }}
# Create .env file from secret
echo "$DOCKER_ENV" > .env
# Add required variables for docker-compose (lowercase repo name)
REPO_LOWER=$(echo "$GITHUB_REPOSITORY" | tr '[:upper:]' '[:lower:]')
echo "GITHUB_REPOSITORY=$REPO_LOWER" >> .env
echo "IMAGE_TAG=$IMAGE_TAG" >> .env
# Login to GitHub Container Registry
echo "$GHCR_PAT" | docker login ghcr.io -u ${{ github.actor }} --password-stdin
# Create proxy network if it doesn't exist
docker network create proxy 2>/dev/null || true
# Pull latest images BEFORE stopping anything (minimizes downtime)
docker compose pull
# Recreate containers in-place with new images (no down/up cycle)
docker compose up -d --force-recreate --remove-orphans
# Clean up old images after new containers are running
docker image prune -f
# Show running containers
echo "=== Running Containers ==="
docker compose ps
# Show container health
echo "=== Container Health ==="
docker compose ps --format "table {{.Name}}\t{{.Status}}"
- name: Health check
uses: appleboy/ssh-action@v1.2.4
with:
host: ${{ secrets.VPS_HOST }}
username: ${{ secrets.VPS_USER }}
key: ${{ secrets.SSH_PRIVATE_KEY }}
script: |
echo "Waiting for services to start..."
sleep 30
echo "=== API Health Check ==="
curl -sf http://localhost:7000/health || echo "API not responding yet"
echo "=== Container Status ==="
docker compose -f ${{ env.APP_DIR }}/docker-compose.yaml ps --format "table {{.Name}}\t{{.Status}}"
echo "=== Recent Logs ==="
docker compose -f ${{ env.APP_DIR }}/docker-compose.yaml logs --tail=10 2>/dev/null || true