-
Notifications
You must be signed in to change notification settings - Fork 65
166 lines (149 loc) · 6.27 KB
/
Copy pathdeploy.yml
File metadata and controls
166 lines (149 loc) · 6.27 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
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
# Creation and polling are separate so both exact deployment IDs are
# retained and independently monitored. POST creation is never retried.
- 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: 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 API deployment
id: wait_api
continue-on-error: true
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: 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