← Back to Dashboard

Review #7WARN

📦 Seanbez/Word-Studio👤 Seanbez🔑 e6d93186🕐 5/27/2026, 1:35:38 AM
90/100
AI Summary

This is a high-quality update that significantly improves the robustness and reliability of the deployment workflows. The addition of secret validation and post-deployment smoke checks is excellent practice. The only minor issue is the duplication of logic between the two workflow files, which could be refactored to improve long-term maintainability.

Issue Breakdown
0
Critical
2
Warnings
0
Suggestions
Token Usage
3,318
Input
488
Output
3,806
Total
Issues (2)
WARNINGDuplicated workflow logic across multiple workflow files
.github/workflows/build-deploy.yml:33
The steps 'Validate required deployment secrets', 'Build web', and 'Post-deploy smoke check' are nearly identical to the ones in `.github/workflows/deploy.yml`. This duplication violates the DRY (Don't Repeat Yourself) principle and makes maintenance more difficult, as any changes must be synchronized across both files, increasing the risk of them becoming inconsistent.
💡 To improve maintainability and reduce redundancy, refactor the common steps into a reusable workflow (`workflow_call`) or a composite action. The main workflows (`build-deploy.yml` and `deploy.yml`) can then call this shared component, simplifying the workflow files and ensuring consistency.
WARNINGDuplicated workflow logic across multiple workflow files
.github/workflows/deploy.yml:38
The steps 'Validate required deployment secrets', 'Build web', and 'Post-deploy smoke check' are nearly identical to the ones in `.github/workflows/build-deploy.yml`. This duplication violates the DRY (Don't Repeat Yourself) principle and makes maintenance more difficult, as any changes must be synchronized across both files, increasing the risk of them becoming inconsistent.
💡 To improve maintainability and reduce redundancy, refactor the common steps into a reusable workflow (`workflow_call`) or a composite action. The main workflows (`build-deploy.yml` and `deploy.yml`) can then call this shared component, simplifying the workflow files and ensuring consistency.
Diff
1 diff --git a/.github/workflows/build-deploy.yml b/.github/workflows/build-deploy.yml
2 index cc2ed2c..4f78f78 100644
3 --- a/.github/workflows/build-deploy.yml
4 +++ b/.github/workflows/build-deploy.yml
5 @@ -30,17 +30,92 @@ jobs:
6 continue-on-error: true
7 run: flutter analyze --no-fatal-warnings
8
9++ - name: Validate required deployment secrets
10++ if: github.event_name == 'push' && github.ref == 'refs/heads/main'
11++ env:
12++ FIREBASE_API_KEY: ${{ secrets.FIREBASE_API_KEY }}
13++ FIREBASE_APP_ID: ${{ secrets.FIREBASE_APP_ID }}
14++ FIREBASE_MESSAGING_SENDER_ID: ${{ secrets.FIREBASE_MESSAGING_SENDER_ID }}
15++ FIREBASE_PROJECT_ID: ${{ secrets.FIREBASE_PROJECT_ID }}
16++ FIREBASE_STORAGE_BUCKET: ${{ secrets.FIREBASE_STORAGE_BUCKET }}
17++ FIREBASE_AUTH_DOMAIN: ${{ secrets.FIREBASE_AUTH_DOMAIN }}
18++ ESV_API_KEY: ${{ secrets.ESV_API_KEY }}
19++ GOOGLE_API_KEY: ${{ secrets.GOOGLE_API_KEY }}
20++ FIREBASE_SERVICE_ACCOUNT_WORD_STUDIO: ${{ secrets.FIREBASE_SERVICE_ACCOUNT_WORD_STUDIO }}
21++ run: |
22++ required=(
23++ FIREBASE_API_KEY
24++ FIREBASE_APP_ID
25++ FIREBASE_MESSAGING_SENDER_ID
26++ FIREBASE_PROJECT_ID
27++ FIREBASE_STORAGE_BUCKET
28++ FIREBASE_AUTH_DOMAIN
29++ ESV_API_KEY
30++ GOOGLE_API_KEY
31++ FIREBASE_SERVICE_ACCOUNT_WORD_STUDIO
32++ )
33++ missing=0
34++ for key in "${required[@]}"; do
35++ if [ -z "${!key}" ]; then
36++ echo "::error::Missing required secret: $key"
37++ missing=1
38++ fi
39++ done
40++ if [ "$missing" -ne 0 ]; then
41++ exit 1
42++ fi
43++
44 - name: Build web
45-- run: flutter build web --release --dart-define=GOOGLE_API_KEY=${{ secrets.GOOGLE_API_KEY }} --dart-define=ESV_API_KEY=${{ secrets.ESV_API_KEY }}
46++ run: |
47++ flutter build web --release \
48++ --dart-define=FIREBASE_API_KEY=${{ secrets.FIREBASE_API_KEY }} \
49++ --dart-define=FIREBASE_APP_ID=${{ secrets.FIREBASE_APP_ID }} \
50++ --dart-define=FIREBASE_MESSAGING_SENDER_ID=${{ secrets.FIREBASE_MESSAGING_SENDER_ID }} \
51++ --dart-define=FIREBASE_PROJECT_ID=${{ secrets.FIREBASE_PROJECT_ID }} \
52++ --dart-define=FIREBASE_STORAGE_BUCKET=${{ secrets.FIREBASE_STORAGE_BUCKET }} \
53++ --dart-define=FIREBASE_AUTH_DOMAIN=${{ secrets.FIREBASE_AUTH_DOMAIN }} \
54++ --dart-define=GOOGLE_API_KEY=${{ secrets.GOOGLE_API_KEY }} \
55++ --dart-define=ESV_API_KEY=${{ secrets.ESV_API_KEY }}
56
57 - name: Deploy to Firebase Hosting
58++ if: github.event_name == 'push' && github.ref == 'refs/heads/main'
59 uses: FirebaseExtended/action-hosting-deploy@v0
60 with:
61 repoToken: '${{ secrets.GITHUB_TOKEN }}'
62-- firebaseServiceAccount: '${{ secrets.FIREBASE_SERVICE_ACCOUNT }}'
63++ firebaseServiceAccount: '${{ secrets.FIREBASE_SERVICE_ACCOUNT_WORD_STUDIO }}'
64 projectId: word-studio-2cfe5
65 channelId: live
66
67++ - name: Post-deploy smoke check
68++ if: github.event_name == 'push' && github.ref == 'refs/heads/main'
69++ run: |
70++ set -euo pipefail
71++
72++ for url in "https://word-ministries.com" "https://word-studio-2cfe5.web.app"; do
73++ code=$(curl -sS -o /tmp/index.html -w "%{http_code}" "$url")
74++ if [ "$code" != "200" ]; then
75++ echo "::error::Smoke check failed for $url (HTTP $code)"
76++ exit 1
77++ fi
78++
79++ if ! grep -q "flutter_bootstrap.js" /tmp/index.html; then
80++ echo "::error::Smoke check failed for $url (missing flutter_bootstrap.js in HTML)"
81++ exit 1
82++ fi
83++ done
84++
85++ curl -sS "https://word-studio-2cfe5.web.app/main.dart.js" -o /tmp/main.dart.js
86++
87++ if grep -q 'case"FIREBASE_API_KEY":return""' /tmp/main.dart.js; then
88++ echo "::error::Deployed bundle appears to have empty FIREBASE_API_KEY"
89++ exit 1
90++ fi
91++
92++ if grep -q 'case"FIREBASE_PROJECT_ID":return""' /tmp/main.dart.js; then
93++ echo "::error::Deployed bundle appears to have empty FIREBASE_PROJECT_ID"
94++ exit 1
95++ fi
96++
97 - name: Build Status
98 if: always()
99 run: |
100 diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml
101 index 7689f33..2abf41e 100644
102 --- a/.github/workflows/deploy.yml
103 +++ b/.github/workflows/deploy.yml
104 @@ -32,11 +32,53 @@ jobs:
105 run: |
106 flutter test 2>/dev/null || echo "No tests found"
107
108++ - name: Validate required deployment secrets
109++ env:
110++ FIREBASE_API_KEY: ${{ secrets.FIREBASE_API_KEY }}
111++ FIREBASE_APP_ID: ${{ secrets.FIREBASE_APP_ID }}
112++ FIREBASE_MESSAGING_SENDER_ID: ${{ secrets.FIREBASE_MESSAGING_SENDER_ID }}
113++ FIREBASE_PROJECT_ID: ${{ secrets.FIREBASE_PROJECT_ID }}
114++ FIREBASE_STORAGE_BUCKET: ${{ secrets.FIREBASE_STORAGE_BUCKET }}
115++ FIREBASE_AUTH_DOMAIN: ${{ secrets.FIREBASE_AUTH_DOMAIN }}
116++ ESV_API_KEY: ${{ secrets.ESV_API_KEY }}
117++ GOOGLE_API_KEY: ${{ secrets.GOOGLE_API_KEY }}
118++ FIREBASE_SERVICE_ACCOUNT_WORD_STUDIO: ${{ secrets.FIREBASE_SERVICE_ACCOUNT_WORD_STUDIO }}
119++ run: |
120++ required=(
121++ FIREBASE_API_KEY
122++ FIREBASE_APP_ID
123++ FIREBASE_MESSAGING_SENDER_ID
124++ FIREBASE_PROJECT_ID
125++ FIREBASE_STORAGE_BUCKET
126++ FIREBASE_AUTH_DOMAIN
127++ ESV_API_KEY
128++ GOOGLE_API_KEY
129++ FIREBASE_SERVICE_ACCOUNT_WORD_STUDIO
130++ )
131++ missing=0
132++ for key in "${required[@]}"; do
133++ if [ -z "${!key}" ]; then
134++ echo "::error::Missing required secret: $key"
135++ missing=1
136++ fi
137++ done
138++ if [ "$missing" -ne 0 ]; then
139++ exit 1
140++ fi
141++
142 - name: Build web
143 run: |
144 flutter clean
145 flutter pub get
146-- flutter build web --release --dart-define=ESV_API_KEY=${{ secrets.ESV_API_KEY }} --dart-define=GOOGLE_API_KEY=${{ secrets.GOOGLE_API_KEY }}
147++ flutter build web --release \
148++ --dart-define=FIREBASE_API_KEY=${{ secrets.FIREBASE_API_KEY }} \
149++ --dart-define=FIREBASE_APP_ID=${{ secrets.FIREBASE_APP_ID }} \
150++ --dart-define=FIREBASE_MESSAGING_SENDER_ID=${{ secrets.FIREBASE_MESSAGING_SENDER_ID }} \
151++ --dart-define=FIREBASE_PROJECT_ID=${{ secrets.FIREBASE_PROJECT_ID }} \
152++ --dart-define=FIREBASE_STORAGE_BUCKET=${{ secrets.FIREBASE_STORAGE_BUCKET }} \
153++ --dart-define=FIREBASE_AUTH_DOMAIN=${{ secrets.FIREBASE_AUTH_DOMAIN }} \
154++ --dart-define=ESV_API_KEY=${{ secrets.ESV_API_KEY }} \
155++ --dart-define=GOOGLE_API_KEY=${{ secrets.GOOGLE_API_KEY }}
156
157 - name: Deploy to Firebase Hosting
158 uses: FirebaseExtended/action-hosting-deploy@v0
159 @@ -47,3 +89,32 @@ jobs:
160 projectId: word-studio-2cfe5
161 env:
162 FIREBASE_CLI_EXPERIMENTS: webframeworks
163++
164++ - name: Post-deploy smoke check
165++ run: |
166++ set -euo pipefail
167++
168++ for url in "https://word-ministries.com" "https://word-studio-2cfe5.web.app"; do
169++ code=$(curl -sS -o /tmp/index.html -w "%{http_code}" "$url")
170++ if [ "$code" != "200" ]; then
171++ echo "::error::Smoke check failed for $url (HTTP $code)"
172++ exit 1
173++ fi
174++
175++ if ! grep -q "flutter_bootstrap.js" /tmp/index.html; then
176++ echo "::error::Smoke check failed for $url (missing flutter_bootstrap.js in HTML)"
177++ exit 1
178++ fi
179++ done
180++
181++ curl -sS "https://word-studio-2cfe5.web.app/main.dart.js" -o /tmp/main.dart.js
182++
183++ if grep -q 'case"FIREBASE_API_KEY":return""' /tmp/main.dart.js; then
184++ echo "::error::Deployed bundle appears to have empty FIREBASE_API_KEY"
185++ exit 1
186++ fi
187++
188++ if grep -q 'case"FIREBASE_PROJECT_ID":return""' /tmp/main.dart.js; then
189++ echo "::error::Deployed bundle appears to have empty FIREBASE_PROJECT_ID"
190++ exit 1
191++ fi
192 diff --git a/README.md b/README.md
193 index eac3ec5..ddd3ba6 100644
194 --- a/README.md
195 +++ b/README.md
196 @@ -61,6 +61,22 @@ flutter build web --release
197 ### Deploy to Firebase Hosting
198 Preferred (production-safe): use GitHub Actions manual deploy workflow (`.github/workflows/deploy.yml`).
199
200++Required GitHub repository secrets for web deploy:
201++- `FIREBASE_API_KEY`
202++- `FIREBASE_APP_ID`
203++- `FIREBASE_MESSAGING_SENDER_ID`
204++- `FIREBASE_PROJECT_ID`
205++- `FIREBASE_STORAGE_BUCKET`
206++- `FIREBASE_AUTH_DOMAIN`
207++- `ESV_API_KEY`
208++- `GOOGLE_API_KEY`
209++- `FIREBASE_SERVICE_ACCOUNT_WORD_STUDIO`
210++
211++Deployment workflows also run a post-deploy smoke check to confirm:
212++- Site returns HTTP 200 on `word-ministries.com` and `word-studio-2cfe5.web.app`
213++- `flutter_bootstrap.js` is present in served HTML
214++- Deployed `main.dart.js` does not contain empty compiled `FIREBASE_*` defines
215++
216 Optional (advanced/manual): deploy from a trusted local machine:
217 ```bash
218 firebase deploy
219