-
Notifications
You must be signed in to change notification settings - Fork 1
500 lines (422 loc) · 20 KB
/
examples-ci.yml
File metadata and controls
500 lines (422 loc) · 20 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
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
name: Examples CI
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
paths:
- 'examples/**'
- '.github/workflows/examples-ci.yml'
# Allow manual trigger
workflow_dispatch:
env:
GO_VERSION: '^1.26'
jobs:
validate-examples:
name: Validate Examples
runs-on: ubuntu-latest
strategy:
matrix:
example:
- basic-app
- reverse-proxy
- http-client
- advanced-logging
- multi-tenant-app
- instance-aware-db
- verbose-debug
- feature-flag-proxy
- testing-scenarios
- observer-pattern
- health-aware-reverse-proxy
- multi-engine-eventbus
- nats-eventbus
- logmasker-example
- logger-reconfiguration
steps:
- name: Checkout code
uses: actions/checkout@v6
- name: Set up Go
uses: actions/setup-go@v6
with:
go-version: ${{ env.GO_VERSION }}
check-latest: true
cache: true
- name: Validate example structure
run: |
cd examples/${{ matrix.example }}
# Check required files exist
if [ ! -f "go.mod" ]; then
echo "❌ Missing go.mod in ${{ matrix.example }}"
exit 1
fi
if [ ! -f "config.yaml" ]; then
echo "❌ Missing config.yaml in ${{ matrix.example }}"
exit 1
fi
if [ ! -f "main.go" ] && [ ! -f "$(basename $(pwd)).go" ]; then
echo "❌ Missing main Go file in ${{ matrix.example }}"
exit 1
fi
echo "✅ Required files found in ${{ matrix.example }}"
- name: Build example
run: |
cd examples/${{ matrix.example }}
# Use GOWORK=off to treat each example as independent
echo "🔨 Building ${{ matrix.example }}..."
GOWORK=off go mod download
GOWORK=off go mod verify
GOWORK=off go build -v .
echo "✅ ${{ matrix.example }} builds successfully"
- name: Test example startup
run: |
cd examples/${{ matrix.example }}
echo "🚀 Testing ${{ matrix.example }} startup..."
# Build the example first
GOWORK=off go build -o example .
# Start the example in background and test it can start
if [ "${{ matrix.example }}" = "basic-app" ]; then
# Basic app just needs to start and respond to health check
timeout 10s ./example &
PID=$!
sleep 3
# Test health endpoint
if curl -f http://localhost:8080/health; then
echo "✅ basic-app health check passed"
else
echo "❌ basic-app health check failed"
kill $PID 2>/dev/null || true
exit 1
fi
kill $PID 2>/dev/null || true
elif [ "${{ matrix.example }}" = "multi-tenant-app" ]; then
# Multi-tenant app needs special validation to ensure tenants are loaded
echo "🏢 Testing multi-tenant app with tenant validation..."
# Run the app and capture logs
timeout 10s ./example > app.log 2>&1 &
PID=$!
sleep 5
# Check if process is still running
if ! kill -0 $PID 2>/dev/null; then
echo "❌ multi-tenant-app crashed during startup"
cat app.log
exit 1
fi
# Validate that tenants were loaded successfully
if grep -q "Successfully loaded tenant configurations" app.log; then
echo "✅ multi-tenant-app successfully loaded tenant configurations"
else
echo "❌ multi-tenant-app failed to load tenant configurations"
echo "📋 Application logs:"
cat app.log
kill $PID 2>/dev/null || true
exit 1
fi
# Check for any tenant loading errors
if grep -q "Failed to load tenant config" app.log; then
echo "❌ multi-tenant-app encountered tenant loading errors"
echo "📋 Application logs:"
cat app.log
kill $PID 2>/dev/null || true
exit 1
fi
# Validate that expected tenants were registered
if grep -q "tenantCount=2" app.log; then
echo "✅ multi-tenant-app loaded expected number of tenants"
else
echo "❌ multi-tenant-app did not load expected number of tenants"
echo "📋 Application logs:"
cat app.log
kill $PID 2>/dev/null || true
exit 1
fi
kill $PID 2>/dev/null || true
elif [ "${{ matrix.example }}" = "testing-scenarios" ]; then
# Testing scenarios example has comprehensive validation scripts
echo "🧪 Testing testing-scenarios with validation scripts..."
# Make scripts executable
chmod +x *.sh
# Run the demo script (includes comprehensive testing)
echo "Running demo.sh for rapid validation..."
if timeout 60s ./demo.sh; then
echo "✅ testing-scenarios demo script passed"
else
echo "❌ testing-scenarios demo script failed"
exit 1
fi
# Run health check validation
echo "Running health check validation..."
if timeout 30s ./test-health-checks.sh; then
echo "✅ testing-scenarios health check validation passed"
else
echo "❌ testing-scenarios health check validation failed"
exit 1
fi
# Run feature flag testing
echo "Running feature flag validation..."
if timeout 30s ./test-feature-flags.sh; then
echo "✅ testing-scenarios feature flag validation passed"
else
echo "❌ testing-scenarios feature flag validation failed"
exit 1
fi
elif [ "${{ matrix.example }}" = "health-aware-reverse-proxy" ]; then
# Health-aware reverse proxy needs comprehensive circuit breaker testing
echo "🔄 Testing health-aware-reverse-proxy with circuit breaker validation..."
# Make test script executable
chmod +x test-circuit-breakers.sh
# Start the application in background
timeout 60s ./example > app.log 2>&1 &
PID=$!
sleep 8 # Allow time for mock backends to start
# Check if process is still running
if ! kill -0 $PID 2>/dev/null; then
echo "❌ health-aware-reverse-proxy crashed during startup"
cat app.log
exit 1
fi
# Test basic health endpoint (accepts both 200 and 503 status codes)
echo "Testing basic health endpoint..."
health_response=$(curl -s -w "HTTP_CODE:%{http_code}" http://localhost:8080/health)
http_code=$(echo "$health_response" | grep -o "HTTP_CODE:[0-9]*" | cut -d: -f2)
if [ "$http_code" = "200" ] || [ "$http_code" = "503" ]; then
echo "✅ health-aware-reverse-proxy health endpoint responding (HTTP $http_code)"
else
echo "❌ health-aware-reverse-proxy health endpoint returned unexpected status: HTTP $http_code"
echo "Response: $health_response"
kill $PID 2>/dev/null || true
exit 1
fi
# Test that unreachable backend triggers circuit breaker (simplified test)
echo "Testing circuit breaker functionality..."
# Make 3 requests to unreachable API to trigger circuit breaker
for i in {1..3}; do
curl -s http://localhost:8080/api/unreachable > /dev/null || true
done
# Wait a moment for circuit breaker to update
sleep 2
# Check that health status reflects circuit breaker state
health_response=$(curl -s http://localhost:8080/health)
if echo "$health_response" | grep -q '"circuit_open_count":[1-9]'; then
echo "✅ health-aware-reverse-proxy circuit breaker properly triggered"
else
echo "⚠️ Circuit breaker may not have triggered as expected (this could be timing-related)"
echo "Health response: $health_response"
# Don't fail here as this could be timing-sensitive in CI
fi
kill $PID 2>/dev/null || true
elif [ "${{ matrix.example }}" = "observer-pattern" ]; then
# Observer pattern example needs to complete its demo and show success message
echo "🔍 Testing observer-pattern example completion..."
# Run the observer pattern demo and capture output
timeout 30s ./example > app.log 2>&1
EXIT_CODE=$?
# Check if the demo completed successfully
if [ $EXIT_CODE -eq 0 ] && grep -q "Observer Pattern Demo completed successfully" app.log; then
echo "✅ observer-pattern demo completed successfully"
# Verify key lifecycle signals were logged (updated patterns)
# We now log structured messages like "Registered service" and "Initialized module".
if grep -q "Registered service" app.log && grep -q "Initialized module" app.log; then
echo "✅ observer-pattern logged expected lifecycle events"
else
echo "❌ observer-pattern missing expected lifecycle events (looking for 'Registered service' and 'Initialized module')"
echo "📋 Application logs:"
cat app.log
exit 1
fi
# Verify CloudEvents functionality was tested
if grep -q "CloudEvent emitted successfully" app.log; then
echo "✅ observer-pattern CloudEvents functionality verified"
else
echo "❌ observer-pattern CloudEvents functionality not verified"
echo "📋 Application logs:"
cat app.log
exit 1
fi
else
echo "❌ observer-pattern demo failed to complete successfully"
echo "📋 Application logs:"
cat app.log
exit 1
fi
elif [ "${{ matrix.example }}" = "logger-reconfiguration" ]; then
# Logger reconfiguration example demonstrates OnConfigLoaded hook
echo "📝 Testing logger-reconfiguration example completion..."
# Run the logger reconfiguration demo and capture output
timeout 30s ./example > app.log 2>&1
EXIT_CODE=$?
# Check if the demo completed successfully
if [ $EXIT_CODE -eq 0 ] && grep -q "Logger Reconfiguration Example Complete" app.log; then
echo "✅ logger-reconfiguration demo completed successfully"
# Verify logger was reconfigured from configuration
if grep -q "Logger reconfigured from configuration" app.log; then
echo "✅ logger-reconfiguration logger was reconfigured based on config"
else
echo "❌ logger-reconfiguration logger was not reconfigured"
echo "📋 Application logs:"
cat app.log
exit 1
fi
# Verify modules initialized with the reconfigured logger
if grep -q "LoggingModule initialized" app.log && grep -q "ServiceModule initialized" app.log; then
echo "✅ logger-reconfiguration modules initialized successfully"
else
echo "❌ logger-reconfiguration modules did not initialize correctly"
echo "📋 Application logs:"
cat app.log
exit 1
fi
# Verify application completed successfully
if grep -q "Application initialized successfully" app.log; then
echo "✅ logger-reconfiguration application completed successfully"
else
echo "❌ logger-reconfiguration application did not complete successfully"
echo "📋 Application logs:"
cat app.log
exit 1
fi
else
echo "❌ logger-reconfiguration demo failed to complete successfully"
echo "📋 Application logs:"
cat app.log
exit 1
fi
elif [ "${{ matrix.example }}" = "multi-engine-eventbus" ]; then
# Multi-engine eventbus example - use Redis for external service demo
echo "🔄 Testing multi-engine-eventbus with Redis service..."
# Make run-demo.sh executable
chmod +x run-demo.sh
# Check if Docker is available for Redis
if command -v docker &> /dev/null; then
echo "Docker available, testing with Redis service"
# Try to start Redis and run the demo
if timeout 60s ./run-demo.sh run-redis; then
echo "✅ multi-engine-eventbus demo completed successfully with Redis"
# Clean up Redis container
docker-compose -f docker-compose.yml down -v 2>/dev/null || true
else
echo "⚠️ Redis demo failed, testing graceful degradation mode"
# Clean up any containers
docker-compose -f docker-compose.yml down -v 2>/dev/null || true
# Test graceful degradation (this should always work)
timeout 10s ./example &
PID=$!
sleep 5
if kill -0 $PID 2>/dev/null; then
echo "✅ multi-engine-eventbus handles missing services gracefully"
kill $PID 2>/dev/null || true
else
echo "❌ multi-engine-eventbus failed to handle missing services"
exit 1
fi
fi
else
echo "Docker not available, testing graceful degradation mode only"
# Test without external services (graceful degradation)
timeout 10s ./example &
PID=$!
sleep 5
if kill -0 $PID 2>/dev/null; then
echo "✅ multi-engine-eventbus handles missing services gracefully"
kill $PID 2>/dev/null || true
else
echo "❌ multi-engine-eventbus failed to handle missing services"
exit 1
fi
fi
elif [ "${{ matrix.example }}" = "nats-eventbus" ]; then
# NATS eventbus example - test with NATS service
echo "🔄 Testing nats-eventbus with NATS service..."
# Make run-demo.sh executable
chmod +x run-demo.sh
# Check if Docker is available for NATS
if command -v docker &> /dev/null; then
echo "Docker available, testing with NATS service"
# Run the demo using the script which displays output
# Increased timeout to 120s to account for Docker image pull time in CI
if timeout 120s ./run-demo.sh run; then
echo "✅ nats-eventbus demo completed successfully with NATS"
# Clean up NATS container
./run-demo.sh cleanup 2>/dev/null || true
else
echo "❌ nats-eventbus demo failed"
# Clean up NATS container on failure
./run-demo.sh cleanup 2>/dev/null || true
exit 1
fi
else
echo "Docker not available, testing graceful degradation"
# Test without NATS (should handle gracefully)
timeout 5s ./example &
PID=$!
sleep 3
if kill -0 $PID 2>/dev/null; then
echo "✅ nats-eventbus handles missing NATS service gracefully"
kill $PID 2>/dev/null || true
else
echo "❌ nats-eventbus failed to handle missing NATS service"
exit 1
fi
fi
elif [ "${{ matrix.example }}" = "reverse-proxy" ] || [ "${{ matrix.example }}" = "http-client" ] || [ "${{ matrix.example }}" = "advanced-logging" ] || [ "${{ matrix.example }}" = "verbose-debug" ] || [ "${{ matrix.example }}" = "instance-aware-db" ] || [ "${{ matrix.example }}" = "feature-flag-proxy" ]; then
# These apps just need to start without immediate errors
timeout 5s ./example &
PID=$!
sleep 3
# Check if process is still running (no immediate crash)
if kill -0 $PID 2>/dev/null; then
echo "✅ ${{ matrix.example }} started successfully"
kill $PID 2>/dev/null || true
else
echo "❌ ${{ matrix.example }} failed to start or crashed immediately"
exit 1
fi
fi
echo "✅ ${{ matrix.example }} startup test passed"
- name: Verify go.mod configuration
run: |
cd examples/${{ matrix.example }}
echo "🔍 Verifying go.mod configuration for ${{ matrix.example }}..."
# Check that replace directives point to correct local paths
if ! grep -q "replace.*=> \.\./\.\." go.mod; then
echo "❌ Missing or incorrect replace directive in ${{ matrix.example }}/go.mod"
echo "Expected: replace github.com/GoCodeAlone/modular => ../.."
cat go.mod
exit 1
fi
# Verify module name matches directory
MODULE_NAME=$(grep "^module " go.mod | awk '{print $2}')
EXPECTED_NAME="${{ matrix.example }}"
if [ "$MODULE_NAME" != "$EXPECTED_NAME" ]; then
echo "❌ Module name mismatch in ${{ matrix.example }}"
echo "Expected: $EXPECTED_NAME"
echo "Found: $MODULE_NAME"
exit 1
fi
echo "✅ go.mod configuration verified for ${{ matrix.example }}"
examples-overview:
name: Examples Overview
runs-on: ubuntu-latest
needs: validate-examples
steps:
- name: Checkout code
uses: actions/checkout@v6
- name: Generate examples summary
run: |
echo "# 📋 Examples Validation Summary" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "All examples have been validated successfully!" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "## 🎯 Validated Examples" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
cd examples
for example in */; do
example=${example%/}
echo "- **$example**: ✅ Build and startup tests passed" >> $GITHUB_STEP_SUMMARY
done
echo "" >> $GITHUB_STEP_SUMMARY
echo "## 🧪 Test Coverage" >> $GITHUB_STEP_SUMMARY
echo "- Structure validation: ✅" >> $GITHUB_STEP_SUMMARY
echo "- Build verification: ✅" >> $GITHUB_STEP_SUMMARY
echo "- Startup testing: ✅" >> $GITHUB_STEP_SUMMARY
echo "- Configuration validation: ✅" >> $GITHUB_STEP_SUMMARY