-
Notifications
You must be signed in to change notification settings - Fork 69
165 lines (148 loc) · 6.24 KB
/
Copy pathdeploy.yml
File metadata and controls
165 lines (148 loc) · 6.24 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
name: Deploy API and worker to Render
on:
workflow_dispatch:
inputs:
environment:
description: Target Render environment
required: true
type: choice
options:
- staging
- production
run_smoke_tests:
description: Run smoke tests after deploy
required: true
default: true
type: boolean
concurrency:
group: deploy-${{ inputs.environment }}
cancel-in-progress: false
permissions:
id-token: write
contents: read
jobs:
deploy:
name: Deploy to Render (${{ inputs.environment }})
runs-on: ubuntu-latest
environment: ${{ inputs.environment }}
steps:
- name: Checkout repository
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
- name: Set up Python 3.11
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
with:
python-version: "3.11"
- name: Cache pip dependencies
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-${{ hashFiles('requirements.txt') }}
restore-keys: |
${{ runner.os }}-pip-
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
# This must remain before either create step: invalid branch/environment
# combinations and missing configuration must result in zero Render POSTs.
- name: Validate deployment preflight
env:
DEPLOY_ENVIRONMENT: ${{ inputs.environment }}
RUN_SMOKE_TESTS: ${{ inputs.run_smoke_tests }}
GITHUB_REF_NAME: ${{ github.ref_name }}
GITHUB_SHA: ${{ github.sha }}
RENDER_API_KEY: ${{ secrets.RENDER_API_KEY }}
RENDER_STAGING_SERVICE_ID: ${{ secrets.RENDER_STAGING_SERVICE_ID }}
RENDER_STAGING_WORKER_SERVICE_ID: ${{ secrets.RENDER_STAGING_WORKER_SERVICE_ID }}
RENDER_PRODUCTION_SERVICE_ID: ${{ secrets.RENDER_PRODUCTION_SERVICE_ID }}
RENDER_PRODUCTION_WORKER_SERVICE_ID: ${{ secrets.RENDER_PRODUCTION_WORKER_SERVICE_ID }}
STAGING_API_URL: ${{ secrets.STAGING_API_URL }}
PRODUCTION_API_URL: ${{ secrets.PRODUCTION_API_URL }}
JWT_SECRET: ${{ secrets.JWT_SECRET }}
AZURE_SUBSCRIPTION_ID: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }}
AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }}
AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }}
run: python scripts/render_deploy_preflight.py
# The API must become live before a new worker is created. API startup
# owns schema migration, and the worker for this SHA may require it.
- name: Create API deployment
id: create_api
env:
RENDER_API_KEY: ${{ secrets.RENDER_API_KEY }}
RENDER_SERVICE_ID: ${{ env.RENDER_API_SERVICE_ID }}
RENDER_SERVICE_NAME: API
GITHUB_SHA: ${{ github.sha }}
run: python scripts/render_deploy.py create
- name: Wait for API deployment
id: wait_api
env:
RENDER_API_KEY: ${{ secrets.RENDER_API_KEY }}
RENDER_SERVICE_ID: ${{ env.RENDER_API_SERVICE_ID }}
RENDER_SERVICE_NAME: API
RENDER_DEPLOY_ID: ${{ steps.create_api.outputs.deploy_id }}
GITHUB_SHA: ${{ github.sha }}
run: python scripts/render_deploy.py wait
- name: Create worker deployment
id: create_worker
env:
RENDER_API_KEY: ${{ secrets.RENDER_API_KEY }}
RENDER_SERVICE_ID: ${{ env.RENDER_WORKER_SERVICE_ID }}
RENDER_SERVICE_NAME: worker
GITHUB_SHA: ${{ github.sha }}
run: python scripts/render_deploy.py create
- name: Wait for worker deployment
id: wait_worker
continue-on-error: true
env:
RENDER_API_KEY: ${{ secrets.RENDER_API_KEY }}
RENDER_SERVICE_ID: ${{ env.RENDER_WORKER_SERVICE_ID }}
RENDER_SERVICE_NAME: worker
RENDER_DEPLOY_ID: ${{ steps.create_worker.outputs.deploy_id }}
GITHUB_SHA: ${{ github.sha }}
run: python scripts/render_deploy.py wait
- name: Require both deployments to be live
if: always()
env:
API_RESULT: ${{ steps.wait_api.outcome }}
WORKER_RESULT: ${{ steps.wait_worker.outcome }}
API_DEPLOY_ID: ${{ steps.create_api.outputs.deploy_id }}
WORKER_DEPLOY_ID: ${{ steps.create_worker.outputs.deploy_id }}
GITHUB_SHA: ${{ github.sha }}
run: |
if [ "$API_RESULT" != "success" ] || [ "$WORKER_RESULT" != "success" ]; then
echo "ERROR: coordinated deployment failed for SHA $GITHUB_SHA. API deploy $API_DEPLOY_ID: $API_RESULT; worker deploy $WORKER_DEPLOY_ID: $WORKER_RESULT."
exit 1
fi
echo "API deploy $API_DEPLOY_ID and worker deploy $WORKER_DEPLOY_ID are live at SHA $GITHUB_SHA."
- name: Health gate check
env:
DEPLOY_ENVIRONMENT: ${{ inputs.environment }}
run: |
MAX_RETRIES=5
RETRY_DELAY=15
URL="${API_URL}/health"
echo "Pinging health gate at: $URL"
for i in $(seq 1 $MAX_RETRIES); do
echo "Health check attempt $i of $MAX_RETRIES..."
HTTP_STATUS=$(curl -s -o /dev/null -w "%{http_code}" "$URL" --max-time 30) || true
if [ "$HTTP_STATUS" -eq 200 ]; then
echo "Health check passed (HTTP $HTTP_STATUS)"
exit 0
fi
echo "Got HTTP $HTTP_STATUS; retrying in ${RETRY_DELAY}s..."
sleep $RETRY_DELAY
done
echo "ERROR: Health gate failed after $MAX_RETRIES attempts on $DEPLOY_ENVIRONMENT."
exit 1
- name: Run smoke tests against live deployment
if: inputs.run_smoke_tests
env:
JWT_SECRET: ${{ secrets.JWT_SECRET }}
AZURE_SUBSCRIPTION_ID: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }}
AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }}
RUN_REAL_SCAN: "true"
run: |
echo "Running smoke tests against: $API_URL"
python tests/smoke_test.py