-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsetup.bat
More file actions
299 lines (257 loc) · 9.12 KB
/
Copy pathsetup.bat
File metadata and controls
299 lines (257 loc) · 9.12 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
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
@echo off
setlocal EnableDelayedExpansion
:: Run from the directory containing setup.bat
cd /d "%~dp0"
echo.
echo AWARE Dashboard Setup
echo ---------------------
echo.
:: Check Docker
where docker >nul 2>&1
if errorlevel 1 (
echo Docker is required but was not found in PATH.
echo Install Docker Desktop, then rerun setup.bat.
echo.
exit /b 1
)
:: Check Docker Compose v2
docker compose version >nul 2>&1
if errorlevel 1 (
echo Docker Compose v2 is required but is not available.
echo Update Docker Desktop, then rerun setup.bat.
echo.
exit /b 1
)
:: Detect Python command (try python3, python, py)
set PYTHON=
where python3 >nul 2>&1
if not errorlevel 1 (
set PYTHON=python3
) else (
where python >nul 2>&1
if not errorlevel 1 (
set PYTHON=python
) else (
where py >nul 2>&1
if not errorlevel 1 set PYTHON=py
)
)
if "!PYTHON!"=="" (
echo Python 3 is required but was not found in PATH.
echo.
exit /b 1
)
set HAS_ENV=0
set HAS_MICRO_CONFIG=0
if exist .env set HAS_ENV=1
if exist aware-micro-server\aware-config.json set HAS_MICRO_CONFIG=1
if "%HAS_ENV%"=="1" if "%HAS_MICRO_CONFIG%"=="1" (
echo Existing configuration found ^(.env^)
echo.
echo 1^) Deploy with current config
echo 2^) Edit configuration first
echo.
set /p CHOICE= Choose [1/2]:
if "!CHOICE!"=="1" (
echo.
echo Regenerating config and starting services...
echo.
call :deploy_stack
if errorlevel 1 (
echo.
echo Deployment failed. Check the output above.
exit /b 1
)
call :verify_ingest
echo.
echo All services are starting.
call :print_deployment_links
echo Run 'docker compose ps' to check status.
echo Run 'docker compose logs -f' to see logs.
echo.
exit /b 0
)
goto :wizard_flow
)
if "%HAS_ENV%"=="1" if "%HAS_MICRO_CONFIG%"=="0" (
echo Found .env but no aware-micro-server\aware-config.json.
echo Opening the setup wizard to finish the micro-server configuration.
echo.
)
:wizard_flow
:: Remove markers from any previous run
if exist .env.saved del /f /q .env.saved
if exist setup\.wizard_url del /f /q setup\.wizard_url
if exist setup\.ingest-check.json del /f /q setup\.ingest-check.json
if exist setup\.database-check.json del /f /q setup\.database-check.json
:: Detect public host
set SUGGESTED_PUBLIC_HOST=localhost
for /f "delims=" %%i in ('!PYTHON! setup\detect_public_host.py') do set SUGGESTED_PUBLIC_HOST=%%i
(
echo PUBLIC_HOST=!SUGGESTED_PUBLIC_HOST!
echo PUBLIC_PORT=80
echo PROTOCOL=http
)>.setup-defaults.env
call :check_ports --wizard
if errorlevel 1 exit /b 1
:: Build and start the wizard
call :compose_files
docker compose %COMPOSE_FILES% --profile setup up --build -d setup-wizard
if errorlevel 1 exit /b 1
echo.
echo Waiting for setup wizard to start...
:: Wait for server.py to write the token URL (up to 30 seconds)
set /a _wi=0
:wait_wizard_url
if exist setup\.wizard_url (
for %%f in (setup\.wizard_url) do if %%~zf gtr 0 goto :wizard_url_ready
)
set /a _wi+=1
if !_wi! geq 30 (
echo.
echo ERROR: Could not read wizard URL.
echo Check logs with: docker compose logs setup-wizard
echo.
exit /b 1
)
timeout /t 1 /nobreak >nul
goto :wait_wizard_url
:wizard_url_ready
:: for/f strips CR/LF automatically
for /f "usebackq delims=" %%i in ("setup\.wizard_url") do set TOKEN_PATH=%%i
set WIZARD_URL=http://!SUGGESTED_PUBLIC_HOST!:9999!TOKEN_PATH!
echo.
echo Setup wizard is ready. Open this URL in your browser:
echo.
echo !WIZARD_URL!
echo.
echo This token is valid for this session only.
echo.
echo Keep this URL to yourself - the page behind it holds this deployment's
echo passwords, and it is served over plain HTTP. Port 9999 closes when setup
echo finishes. On an untrusted network: put SETUP_BIND=127.0.0.1 in .env, then
echo reach the wizard through a tunnel with
echo ssh -N -L 9999:localhost:9999 %USERNAME%@!SUGGESTED_PUBLIC_HOST!
echo.
start "" "!WIZARD_URL!"
echo Fill in the form and click Save.
echo Waiting for configuration...
echo.
:: Wait for the wizard to write config
:wait_env_saved
if exist .env.saved goto :env_saved
timeout /t 2 /nobreak >nul
goto :wait_env_saved
:env_saved
del /f /q .env.saved
echo Configuration saved! Starting services...
echo.
:: Start the actual services using the config already generated by the wizard
call :check_ports
if errorlevel 1 exit /b 1
call :compose_files
docker compose %COMPOSE_FILES% up --build -d
if errorlevel 1 exit /b 1
!PYTHON! setup\init_study_tables.py
if errorlevel 1 exit /b 1
call :publish_database_authority
if errorlevel 1 exit /b 1
call :verify_database
if errorlevel 1 exit /b 1
:: The wizard serves the browser its readiness poll and the ingest self-test, so it
:: is taken down once the services report healthy and stays up otherwise.
call :wait_for_service_redirect
set SERVICES_READY=!errorlevel!
call :verify_ingest
if "!SERVICES_READY!"=="0" (
timeout /t 3 /nobreak >nul
docker compose %COMPOSE_FILES% --profile setup stop setup-wizard 2>nul
docker compose %COMPOSE_FILES% --profile setup rm -f setup-wizard 2>nul
rem The token goes with the server that honoured it.
if exist setup\.wizard_url del /f /q setup\.wizard_url
)
echo.
echo All services are starting.
call :print_deployment_links
echo Run 'docker compose ps' to check status.
echo Run 'docker compose logs -f' to see logs.
echo.
exit /b 0
:deploy_stack
if not exist studies mkdir studies
if not exist aware-micro-server\cache mkdir aware-micro-server\cache
if not exist aware-micro-server\esm mkdir aware-micro-server\esm
%PYTHON% setup\deploy_config.py
if errorlevel 1 exit /b 1
call :check_ports
if errorlevel 1 exit /b 1
call :compose_files
docker compose %COMPOSE_FILES% up --build -d
if errorlevel 1 exit /b 1
%PYTHON% setup\init_study_tables.py
if errorlevel 1 exit /b 1
call :publish_database_authority
if errorlevel 1 exit /b 1
call :verify_database
if errorlevel 1 exit /b 1
exit /b 0
:: The question a participant's phone will ask, asked before anyone enrols. A
:: failure is reported rather than exited on: the stack is up either way, and the
:: wizard shows the same result in the browser.
:verify_ingest
%PYTHON% setup\verify_ingest.py
exit /b 0
:: The compose files this deployment runs. The override is written from the database
:: placement and removed again, so its presence is the placement.
:compose_files
if exist docker-compose.external-db.yml (
set "COMPOSE_FILES=-f docker-compose.yml -f docker-compose.external-db.yml"
) else (
set "COMPOSE_FILES="
)
exit /b 0
:: The addresses this deployment publishes, asked about before the containers that
:: need them are built. A port another program holds is a container that never
:: starts, so the run stops here with the port and what holds it named.
:check_ports
%PYTHON% setup\check_ports.py %1
exit /b !errorlevel!
:: A bundled database generates the authority it signs its own certificate with on
:: first start, and that start follows the deploy which wrote the study. The stack is
:: up by the time this runs, so setup\publish_authority.py reads the authority out of
:: the container and republishes the study carrying it. setup.sh calls the same
:: script, so one deployment is asked one question on either platform.
:publish_database_authority
%PYTHON% setup\publish_authority.py
exit /b !errorlevel!
:: Does the database answer, and can this study write a row to it.
:verify_database
%PYTHON% setup\verify_database.py
exit /b 0
:print_deployment_links
if not exist deployment-urls.json exit /b 0
%PYTHON% -c "import json,pathlib; p=pathlib.Path('deployment-urls.json'); urls=json.loads(p.read_text(encoding='utf-8')) if p.exists() else {}; labels=[('App','app_url'),('Dashboard','dashboard_url'),('Configurator','configurator_url'),('Study links','studies_url')]; print(' Access links:') if urls else None; [print(f' {label}: {urls[key]}') for label,key in labels if urls.get(key)]"
exit /b 0
:wait_for_service_redirect
echo Waiting for services to become ready for browser redirect...
if exist docker-compose.external-db.yml (
set "HEALTH_SERVICES=aware_micro aware_configurator aware_dashboard_api aware_dashboard aware_nginx"
) else (
set "HEALTH_SERVICES=aware_mysql aware_micro aware_configurator aware_dashboard_api aware_dashboard aware_nginx"
)
set /a _hi=0
:_health_loop
%PYTHON% -c "import os,subprocess,sys; names=os.environ['HEALTH_SERVICES'].split(); r=subprocess.run(['docker','inspect','-f','{{if .State.Health}}{{.State.Health.Status}}{{else}}{{.State.Status}}{{end}}']+names,capture_output=True,text=True); lines=[l.strip() for l in r.stdout.strip().splitlines()]; ok=all(l in ('healthy','running') for l in lines) and len(lines)==len(names); sys.exit(0 if ok else 1)" >nul 2>&1
if not errorlevel 1 (
echo Services are ready. Redirecting browser shortly...
timeout /t 5 /nobreak >nul
exit /b 0
)
set /a _hi+=1
if !_hi! geq 180 (
echo Services are still starting. The setup wizard will remain open.
echo Run 'docker compose ps' to check status.
exit /b 1
)
timeout /t 2 /nobreak >nul
goto :_health_loop