Skip to content

Commit 71bc056

Browse files
committed
ci: add azure container apps auto-deploy workflow
- New deploy-azure.yml: builds Docker image, pushes to ACR, updates Container App - Deactivated old deploy.yml (VPS no longer exists, switched to Azure) - Triggers on push to main, includes health check verification - min-replicas pinned to 1 to prevent cold starts
1 parent 68976cb commit 71bc056

2 files changed

Lines changed: 185 additions & 49 deletions

File tree

.github/workflows/deploy-azure.yml

Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
name: Deploy API to Azure
2+
3+
on:
4+
push:
5+
branches: [main]
6+
workflow_dispatch:
7+
8+
concurrency:
9+
group: deploy-azure
10+
cancel-in-progress: false
11+
12+
env:
13+
AZURE_CONTAINER_APP_NAME: openlinear-api
14+
AZURE_RESOURCE_GROUP: rg-openlinear-prod
15+
AZURE_REGISTRY: openlinearcr.azurecr.io
16+
AZURE_REGISTRY_NAME: openlinearcr
17+
IMAGE_NAME: openlinear-api
18+
19+
jobs:
20+
checks:
21+
name: Checks
22+
runs-on: ubuntu-latest
23+
services:
24+
postgres:
25+
image: postgres:16
26+
env:
27+
POSTGRES_USER: openlinear
28+
POSTGRES_PASSWORD: openlinear
29+
POSTGRES_DB: openlinear_test
30+
ports:
31+
- 5432:5432
32+
options: >-
33+
--health-cmd pg_isready
34+
--health-interval 10s
35+
--health-timeout 5s
36+
--health-retries 5
37+
env:
38+
DATABASE_URL: postgresql://openlinear:openlinear@localhost:5432/openlinear_test
39+
steps:
40+
- uses: actions/checkout@v4
41+
42+
- uses: pnpm/action-setup@v4
43+
44+
- uses: actions/setup-node@v4
45+
with:
46+
node-version: 22
47+
cache: pnpm
48+
49+
- run: pnpm install --frozen-lockfile
50+
51+
- name: Generate Prisma client
52+
run: pnpm --filter @openlinear/db db:generate
53+
54+
- name: Push schema to test database
55+
run: pnpm --filter @openlinear/db db:push
56+
57+
- name: Typecheck
58+
run: pnpm --filter @openlinear/api typecheck
59+
60+
- name: Build API
61+
run: pnpm --filter @openlinear/api build
62+
63+
- name: Build Web
64+
run: pnpm --filter @openlinear/desktop-ui build
65+
env:
66+
NEXT_PUBLIC_API_URL: https://openlinear.tech
67+
68+
- name: Build Landing
69+
run: pnpm --filter @openlinear/landing build
70+
71+
- name: Test
72+
run: pnpm --filter @openlinear/api test
73+
74+
build-and-push:
75+
name: Build and Push Docker Image
76+
needs: checks
77+
runs-on: ubuntu-latest
78+
steps:
79+
- uses: actions/checkout@v4
80+
81+
- name: Set up Docker Buildx
82+
uses: docker/setup-buildx-action@v3
83+
84+
- name: Login to Azure Container Registry
85+
uses: docker/login-action@v3
86+
with:
87+
registry: ${{ env.AZURE_REGISTRY }}
88+
username: ${{ secrets.AZURE_REGISTRY_USERNAME }}
89+
password: ${{ secrets.AZURE_REGISTRY_PASSWORD }}
90+
91+
- name: Build and push API image
92+
uses: docker/build-push-action@v5
93+
with:
94+
context: .
95+
file: ./apps/api/Dockerfile
96+
push: true
97+
tags: |
98+
${{ env.AZURE_REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }}
99+
${{ env.AZURE_REGISTRY }}/${{ env.IMAGE_NAME }}:latest
100+
cache-from: type=gha
101+
cache-to: type=gha,mode=max
102+
103+
deploy-to-azure:
104+
name: Deploy to Azure Container Apps
105+
needs: build-and-push
106+
runs-on: ubuntu-latest
107+
steps:
108+
- uses: actions/checkout@v4
109+
110+
- name: Login to Azure
111+
uses: azure/login@v2
112+
with:
113+
creds: ${{ secrets.AZURE_CREDENTIALS }}
114+
115+
- name: Update Container App
116+
run: |
117+
az containerapp update \
118+
--name ${{ env.AZURE_CONTAINER_APP_NAME }} \
119+
--resource-group ${{ env.AZURE_RESOURCE_GROUP }} \
120+
--image ${{ env.AZURE_REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }} \
121+
--min-replicas 1 \
122+
--max-replicas 3
123+
124+
- name: Wait for revision to be ready
125+
run: |
126+
echo "Waiting for Container App revision to be ready..."
127+
for i in $(seq 1 30); do
128+
STATUS=$(az containerapp show \
129+
--name ${{ env.AZURE_CONTAINER_APP_NAME }} \
130+
--resource-group ${{ env.AZURE_RESOURCE_GROUP }} \
131+
--query "properties.runningStatus" -o tsv)
132+
echo "Attempt $i: status=$STATUS"
133+
if [ "$STATUS" = "Running" ]; then
134+
echo "Container App is running!"
135+
break
136+
fi
137+
if [ "$i" -eq 30 ]; then
138+
echo "Timeout waiting for Container App"
139+
exit 1
140+
fi
141+
sleep 10
142+
done
143+
144+
- name: Verify deployment
145+
run: |
146+
echo "Testing API health endpoint..."
147+
for i in $(seq 1 20); do
148+
HTTP_STATUS=$(curl -s -o /dev/null -w '%{http_code}' \
149+
https://api.openlinear.tech/health || echo "000")
150+
echo "Attempt $i: HTTP $HTTP_STATUS"
151+
if [ "$HTTP_STATUS" = "200" ]; then
152+
echo "✓ Deployment verified — API is healthy"
153+
exit 0
154+
fi
155+
if [ "$i" -eq 20 ]; then
156+
echo "✗ Health check failed after 20 attempts"
157+
exit 1
158+
fi
159+
sleep 5
160+
done
161+
162+
- name: Run diagnostics on failure
163+
if: failure()
164+
run: |
165+
echo "=== Container App Status ==="
166+
az containerapp show \
167+
--name ${{ env.AZURE_CONTAINER_APP_NAME }} \
168+
--resource-group ${{ env.AZURE_RESOURCE_GROUP }} \
169+
--query "properties"
170+
171+
echo ""
172+
echo "=== Container Logs ==="
173+
az containerapp logs show \
174+
--name ${{ env.AZURE_CONTAINER_APP_NAME }} \
175+
--resource-group ${{ env.AZURE_RESOURCE_GROUP }} \
176+
--tail 50
177+
178+

.github/workflows/deploy.yml

Lines changed: 7 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
1-
name: Deploy to Production
1+
name: Deploy to VPS (Legacy - Disabled)
22

33
on:
4-
push:
5-
branches: [main]
64
workflow_dispatch:
75

86
concurrency:
@@ -64,53 +62,13 @@ jobs:
6462
- name: Test
6563
run: pnpm --filter @openlinear/api test
6664

67-
deploy:
68-
name: Deploy
69-
needs: checks
70-
runs-on: ubuntu-latest
71-
environment: production
72-
steps:
73-
- name: Deploy to droplet
74-
uses: appleboy/ssh-action@v1
75-
with:
76-
host: ${{ secrets.DEPLOY_HOST }}
77-
username: ${{ secrets.DEPLOY_USER }}
78-
key: ${{ secrets.DEPLOY_SSH_KEY }}
79-
command_timeout: 15m
80-
script: |
81-
set -e
82-
export CI=true
83-
cd /opt/openlinear && git reset --hard HEAD
84-
/opt/openlinear/scripts/deploy.sh
85-
86-
- name: Diagnostics (pm2 + port + local health)
87-
if: ${{ always() }}
88-
uses: appleboy/ssh-action@v1
89-
with:
90-
host: ${{ secrets.DEPLOY_HOST }}
91-
username: ${{ secrets.DEPLOY_USER }}
92-
key: ${{ secrets.DEPLOY_SSH_KEY }}
93-
command_timeout: 2m
94-
script: |
95-
set -e
96-
echo "== pm2 show =="
97-
pm2 show openlinear-api || true
98-
pm2 show openlinear-web || true
99-
echo "== port 3001 listener =="
100-
(command -v ss >/dev/null 2>&1 && ss -ltnp | grep ':3001' && exit 0) || true
101-
(command -v lsof >/dev/null 2>&1 && lsof -iTCP:3001 -sTCP:LISTEN -nP && exit 0) || true
102-
echo "== localhost health =="
103-
curl -sS -D- -o /dev/null --max-time 5 http://localhost:3001/health || true
104-
echo "== localhost web =="
105-
curl -sS -D- -o /dev/null --max-time 10 http://localhost:3000/ || true
106-
107-
- name: Verify deployment
65+
- name: Verify API health
10866
run: |
109-
sleep 30
110-
HTTP_STATUS=$(curl -s -o /dev/null -w '%{http_code}' https://openlinear.tech/health)
67+
echo "API deployed via Azure Container Apps (see deploy-azure.yml)"
68+
echo "This legacy workflow no longer deploys to VPS"
69+
HTTP_STATUS=$(curl -s -o /dev/null -w '%{http_code}' https://api.openlinear.tech/health)
11170
if [ "$HTTP_STATUS" = "200" ]; then
112-
echo "✓ Deployment verified — health check returned $HTTP_STATUS"
71+
echo "✓ API is healthy on Azure"
11372
else
114-
echo "✗ Health check failed — returned $HTTP_STATUS"
115-
exit 1
73+
echo "⚠ API health check returned $HTTP_STATUS"
11674
fi

0 commit comments

Comments
 (0)