Repository navigation
Expand file tree
/
Copy pathgaeld
More file actions
executable file
·328 lines (283 loc) · 10.9 KB
/
Copy pathgaeld
File metadata and controls
executable file
·328 lines (283 loc) · 10.9 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
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
#!/usr/bin/env bash
# ─────────────────────────────────────────────────────────────
# gaeld — CLI wrapper for Gäld development environment
# Usage: ./gaeld setup [--demo] Full first-time setup
# ./gaeld up Start containers
# ./gaeld down Stop containers
# ./gaeld doctor Diagnose the running installation
# ./gaeld update Apply migrations and rebuild caches
# ./gaeld artisan ... Run artisan inside the container
# ./gaeld logs [service] Tail container logs
# ./gaeld worker Tail queue worker logs
# ./gaeld status Show container status
# ─────────────────────────────────────────────────────────────
set -euo pipefail
# ── Colors ──────────────────────────────────────────────────
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
RED='\033[0;31m'
BLUE='\033[0;34m'
BOLD='\033[1m'
DIM='\033[2m'
RESET='\033[0m'
# ── Helpers ─────────────────────────────────────────────────
step() {
printf "\n${BLUE}▸${RESET} ${BOLD}%s${RESET}\n" "$1"
}
ok() {
printf " ${GREEN}✔${RESET} %s\n" "$1"
}
warn() {
printf " ${YELLOW}⚠${RESET} %s\n" "$1"
}
fail() {
local message=$1
local exit_code=${2:-1}
printf " ${RED}✖${RESET} %s\n" "$message"
exit "$exit_code"
}
run_step() {
local label=$1
shift
printf " ${BLUE}▸${RESET} ${BOLD}%s${RESET}\n" "$label"
if "$@"; then
ok "$label"
else
local rc=$?
fail "$label failed with exit code ${rc}" "$rc"
fi
}
require_docker() {
if ! command -v docker > /dev/null 2>&1; then
fail "Docker CLI is not installed. Install Docker Desktop or Docker Engine and retry."
fi
local context
local docker_error
context=$(docker context show 2>/dev/null || printf "unknown")
if docker_error=$(docker info 2>&1); then
ok "Docker Engine is ready (${context})"
return 0
fi
if [[ "$docker_error" == *"permission denied"* ]]; then
printf " ${RED}✖${RESET} Docker socket access denied for context '${context}'.\n"
printf " Add your Linux user to the docker group, then start a new session:\n"
printf " sudo usermod -aG docker \"$USER\" && newgrp docker\n"
printf " Verify with: docker info\n"
exit 1
fi
printf " ${RED}✖${RESET} Docker Engine is unavailable for context '${context}'.\n"
printf "%s\n" "$docker_error" | tail -n 3 | while IFS= read -r line; do
printf " %s\n" "$line"
done
printf " Start Docker, select the correct context, and verify with: docker info\n"
exit 1
}
wait_for() {
local label=$1
local max_retries=$2
shift 2
local retries=0
local frames='⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏'
printf " ${DIM}%s${RESET} %s" "${frames:0:1}" "$label"
while [ $retries -lt $max_retries ]; do
if "$@" > /dev/null 2>&1; then
printf "\r ${GREEN}✔${RESET} %s \n" "$label"
return 0
fi
printf "\r ${DIM}%s${RESET} %s" "${frames:retries%10:1}" "$label"
retries=$((retries + 1))
sleep 1
done
printf "\r ${RED}✖${RESET} %s\n" "$label"
fail "Timed out after ${max_retries}s"
}
elapsed() {
local start=$1
local end
end=$(date +%s)
local diff=$((end - start))
local min=$((diff / 60))
local sec=$((diff % 60))
if [ $min -gt 0 ]; then
printf "%dm%ds" $min $sec
else
printf "%ds" $sec
fi
}
# ── Commands ────────────────────────────────────────────────
cmd_setup() {
local demo_flag=""
local start_time
start_time=$(date +%s)
for arg in "$@"; do
case "$arg" in
--demo) demo_flag="--demo" ;;
esac
done
printf "\n${BOLD}🏔 Gäld — Swiss Accounting Platform${RESET}\n"
printf "${DIM} Setting up development environment...${RESET}\n"
# Fail early with an actionable message instead of hiding a Docker problem
# behind a retry loop.
require_docker
# 1. Environment file
step "Environment"
if [ -f .env ]; then
ok ".env already exists"
else
cp .env.example .env
ok "Created .env from .env.example"
fi
# 2. Build image if needed
step "Docker image"
if docker image inspect gaeld-sail/app > /dev/null 2>&1; then
ok "Image gaeld-sail/app already built"
else
run_step "Building gaeld-sail/app (first time — takes a few minutes)" \
docker compose build --progress=plain laravel.test
fi
# 3. Start containers
step "Starting services"
run_step "Starting PostgreSQL, Redis, Meilisearch, and Mailpit" \
docker compose up -d pgsql redis meilisearch mailpit
# Wait for DB + Redis health (retry loops — containers need time to initialize)
wait_for "Waiting for PostgreSQL to accept connections" 30 \
docker compose exec -T pgsql pg_isready -q -d "${DB_DATABASE:-gaeld}" -U "${DB_USERNAME:-gaeld}"
wait_for "Waiting for Redis to respond" 15 \
docker compose exec -T redis redis-cli ping
# Start app + worker (depend on healthy services)
run_step "Starting web server and queue worker" \
docker compose up -d laravel.test worker
# Wait for app health
wait_for "Waiting for application to be ready" 60 \
docker compose exec -T laravel.test curl -sf http://localhost:80/up
# 4. Install
step "Installing Gäld"
if [ -n "$demo_flag" ]; then
docker compose exec -T laravel.test php artisan gaeld:install --demo --no-interaction
else
docker compose exec laravel.test php artisan gaeld:install
fi
# 5. Summary
local duration
duration=$(elapsed "$start_time")
printf "\n${GREEN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}\n"
printf "${GREEN}${BOLD} ✔ Gäld is ready!${RESET} ${DIM}(${duration})${RESET}\n"
printf "${GREEN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}\n\n"
printf " ${BOLD}App${RESET} http://localhost:${APP_PORT:-8080}\n"
printf " ${BOLD}Mailpit${RESET} http://localhost:${FORWARD_MAILPIT_DASHBOARD_PORT:-8025}\n"
printf " ${BOLD}Search${RESET} http://localhost:${FORWARD_MEILISEARCH_PORT:-7700}\n\n"
printf " ${DIM}Run ${RESET}${BOLD}./gaeld artisan${RESET}${DIM} to execute commands inside the container${RESET}\n"
printf " ${DIM}Run ${RESET}${BOLD}./gaeld logs${RESET}${DIM} to tail application logs${RESET}\n"
printf " ${DIM}Run ${RESET}${BOLD}./gaeld down${RESET}${DIM} to stop all services${RESET}\n\n"
}
cmd_up() {
step "Starting all services"
docker compose up -d "$@"
ok "All services started"
cmd_status
}
cmd_down() {
step "Stopping all services"
docker compose down "$@"
ok "All services stopped"
}
cmd_restart() {
step "Restarting services"
if [ $# -gt 0 ]; then
docker compose restart "$@"
ok "Restarted: $*"
else
docker compose restart
ok "All services restarted"
fi
cmd_status
}
cmd_pnpm() {
docker compose exec laravel.test pnpm "$@"
}
cmd_composer() {
docker compose exec laravel.test composer "$@"
}
cmd_artisan() {
docker compose exec laravel.test php artisan "$@"
}
cmd_doctor() {
docker compose exec laravel.test php artisan gaeld:doctor "$@"
}
cmd_update() {
docker compose exec laravel.test php artisan gaeld:update "$@"
}
cmd_logs() {
docker compose logs -f "${1:-laravel.test}"
}
cmd_worker() {
docker compose logs -f worker
}
cmd_status() {
printf "\n${BOLD}Service Status${RESET}\n\n"
docker compose ps --format "table {{.Service}}\t{{.Status}}\t{{.Ports}}" 2>/dev/null \
|| docker compose ps
echo ""
}
cmd_help() {
printf '%s\n' \
'' \
'🏔 Gäld CLI' \
'' \
'Usage: ./gaeld <command> [options]' \
'' \
'Commands:' \
' setup [--demo] Full first-time setup (build, start, install)' \
' up Start all containers' \
' down Stop all containers' \
' doctor Diagnose the running installation' \
' update Apply migrations and rebuild application caches' \
' restart [service] Restart all or specific service' \
' artisan <cmd> Run artisan command in the app container' \
' pnpm <cmd> Run pnpm command in the app container' \
' logs [service] Tail logs (default: laravel.test)' \
' worker Tail queue worker logs' \
' status Show container status' \
' help Show this help message' \
'' \
'Examples:' \
' ./gaeld setup Interactive first-time setup' \
' ./gaeld setup --demo Non-interactive setup with demo data' \
' ./gaeld artisan migrate Run database migrations' \
' ./gaeld doctor Diagnose configuration and service health' \
' ./gaeld update Apply pending migrations and rebuild caches' \
' ./gaeld restart worker Restart queue worker' \
' ./gaeld logs worker Tail worker logs' \
' ./gaeld pnpm build Run pnpm build inside the container' \
''
}
# ── Main ────────────────────────────────────────────────────
# Load .env for port variables if available
if [ -f .env ]; then
set -a
# shellcheck disable=SC1091
source .env 2>/dev/null || true
set +a
fi
command="${1:-help}"
shift 2>/dev/null || true
case "$command" in
setup) cmd_setup "$@" ;;
up) cmd_up "$@" ;;
down) cmd_down "$@" ;;
restart) cmd_restart "$@" ;;
artisan) cmd_artisan "$@" ;;
doctor) cmd_doctor "$@" ;;
update) cmd_update "$@" ;;
composer) cmd_composer "$@" ;;
pnpm) cmd_pnpm "$@" ;;
logs) cmd_logs "$@" ;;
worker) cmd_worker "$@" ;;
status) cmd_status ;;
help|--help|-h) cmd_help ;;
*)
printf "${RED}Unknown command: %s${RESET}\n" "$command"
cmd_help
exit 1
;;
esac