1+ # GitHub Actions Workflow: Continuous Integration
2+ # Validates pull requests with formatting, linting, tests, and build verification
3+ # Runs on: PR to main, push to any branch, manual trigger
4+
5+ name : CI
6+
7+ on :
8+ pull_request :
9+ branches :
10+ - main
11+ types :
12+ - opened
13+ - synchronize
14+ - reopened
15+ push :
16+ branches :
17+ - ' **'
18+ workflow_dispatch :
19+ inputs :
20+ debug_enabled :
21+ type : boolean
22+ description : ' Run with debug logging'
23+ required : false
24+ default : false
25+
26+ # Cancel outdated runs for the same PR/branch
27+ concurrency :
28+ group : ci-${{ github.event.pull_request.number || github.ref }}
29+ cancel-in-progress : true
30+
31+ env :
32+ FLUTTER_CHANNEL : stable
33+
34+ jobs :
35+ # ============================================
36+ # FORMAT CHECK
37+ # ============================================
38+ format :
39+ name : 📝 Format Check
40+ runs-on : ubuntu-latest
41+
42+ steps :
43+ - name : Checkout repository
44+ uses : actions/checkout@v4
45+
46+ - name : Set up Flutter SDK
47+ uses : subosito/flutter-action@v2
48+ with :
49+ channel : ${{ env.FLUTTER_CHANNEL }}
50+ cache : true
51+
52+ - name : Get Flutter dependencies
53+ run : flutter pub get
54+
55+ - name : Check code formatting
56+ id : format_check
57+ run : |
58+ echo "## 📝 Format Check Results" >> $GITHUB_STEP_SUMMARY
59+ echo "" >> $GITHUB_STEP_SUMMARY
60+
61+ # Run dart format and capture output
62+ if dart format --set-exit-if-changed --output=none . 2>&1 | tee format_output.txt; then
63+ echo "✅ All files are properly formatted!" >> $GITHUB_STEP_SUMMARY
64+ echo "format_status=success" >> $GITHUB_OUTPUT
65+ else
66+ echo "❌ **Formatting issues found!**" >> $GITHUB_STEP_SUMMARY
67+ echo "" >> $GITHUB_STEP_SUMMARY
68+ echo "The following files need formatting:" >> $GITHUB_STEP_SUMMARY
69+ echo "" >> $GITHUB_STEP_SUMMARY
70+ echo '```' >> $GITHUB_STEP_SUMMARY
71+ # Find files that would be changed
72+ dart format --set-exit-if-changed . 2>&1 | grep -E "^Changed|^Would change" >> $GITHUB_STEP_SUMMARY || true
73+ echo '```' >> $GITHUB_STEP_SUMMARY
74+ echo "" >> $GITHUB_STEP_SUMMARY
75+ echo "**Fix:** Run \`dart format .\` locally and commit the changes." >> $GITHUB_STEP_SUMMARY
76+ echo "format_status=failure" >> $GITHUB_OUTPUT
77+ exit 1
78+ fi
79+
80+ # ============================================
81+ # STATIC ANALYSIS (LINTING)
82+ # ============================================
83+ analyze :
84+ name : 🔍 Static Analysis
85+ runs-on : ubuntu-latest
86+
87+ steps :
88+ - name : Checkout repository
89+ uses : actions/checkout@v4
90+
91+ - name : Set up Flutter SDK
92+ uses : subosito/flutter-action@v2
93+ with :
94+ channel : ${{ env.FLUTTER_CHANNEL }}
95+ cache : true
96+
97+ - name : Get Flutter dependencies
98+ run : flutter pub get
99+
100+ - name : Run Flutter analyze
101+ id : analyze
102+ run : |
103+ echo "## 🔍 Static Analysis Results" >> $GITHUB_STEP_SUMMARY
104+ echo "" >> $GITHUB_STEP_SUMMARY
105+
106+ # Run flutter analyze and capture output
107+ if flutter analyze --no-fatal-infos 2>&1 | tee analyze_output.txt; then
108+ echo "✅ No analysis issues found!" >> $GITHUB_STEP_SUMMARY
109+ echo "" >> $GITHUB_STEP_SUMMARY
110+ echo '```' >> $GITHUB_STEP_SUMMARY
111+ cat analyze_output.txt >> $GITHUB_STEP_SUMMARY
112+ echo '```' >> $GITHUB_STEP_SUMMARY
113+ echo "analyze_status=success" >> $GITHUB_OUTPUT
114+ else
115+ echo "❌ **Analysis issues found!**" >> $GITHUB_STEP_SUMMARY
116+ echo "" >> $GITHUB_STEP_SUMMARY
117+ echo '```' >> $GITHUB_STEP_SUMMARY
118+ cat analyze_output.txt >> $GITHUB_STEP_SUMMARY
119+ echo '```' >> $GITHUB_STEP_SUMMARY
120+ echo "" >> $GITHUB_STEP_SUMMARY
121+ echo "**Fix:** Address the issues listed above and commit the changes." >> $GITHUB_STEP_SUMMARY
122+ echo "analyze_status=failure" >> $GITHUB_OUTPUT
123+ exit 1
124+ fi
125+
126+ # ============================================
127+ # UNIT TESTS
128+ # ============================================
129+ test :
130+ name : 🧪 Unit Tests
131+ runs-on : ubuntu-latest
132+
133+ steps :
134+ - name : Checkout repository
135+ uses : actions/checkout@v4
136+
137+ - name : Set up Flutter SDK
138+ uses : subosito/flutter-action@v2
139+ with :
140+ channel : ${{ env.FLUTTER_CHANNEL }}
141+ cache : true
142+
143+ - name : Get Flutter dependencies
144+ run : flutter pub get
145+
146+ - name : Create .env file for tests
147+ run : |
148+ # Create minimal .env file required by the app
149+ echo "# Test environment" > .env
150+
151+ - name : Run Flutter tests
152+ id : test
153+ run : |
154+ echo "## 🧪 Test Results" >> $GITHUB_STEP_SUMMARY
155+ echo "" >> $GITHUB_STEP_SUMMARY
156+
157+ # Run tests with coverage
158+ if flutter test --coverage 2>&1 | tee test_output.txt; then
159+ echo "✅ All tests passed!" >> $GITHUB_STEP_SUMMARY
160+ echo "" >> $GITHUB_STEP_SUMMARY
161+ echo '```' >> $GITHUB_STEP_SUMMARY
162+ tail -20 test_output.txt >> $GITHUB_STEP_SUMMARY
163+ echo '```' >> $GITHUB_STEP_SUMMARY
164+ echo "test_status=success" >> $GITHUB_OUTPUT
165+ else
166+ echo "❌ **Some tests failed!**" >> $GITHUB_STEP_SUMMARY
167+ echo "" >> $GITHUB_STEP_SUMMARY
168+ echo '```' >> $GITHUB_STEP_SUMMARY
169+ cat test_output.txt >> $GITHUB_STEP_SUMMARY
170+ echo '```' >> $GITHUB_STEP_SUMMARY
171+ echo "" >> $GITHUB_STEP_SUMMARY
172+ echo "**Fix:** Review the failing tests and fix the issues." >> $GITHUB_STEP_SUMMARY
173+ echo "test_status=failure" >> $GITHUB_OUTPUT
174+ exit 1
175+ fi
176+
177+ - name : Upload coverage report
178+ if : always()
179+ uses : actions/upload-artifact@v4
180+ with :
181+ name : coverage-report
182+ path : coverage/lcov.info
183+ retention-days : 7
184+ if-no-files-found : ignore
185+
186+ # ============================================
187+ # BUILD VERIFICATION
188+ # ============================================
189+ build :
190+ name : 🔨 Build Verification
191+ runs-on : ubuntu-latest
192+ needs : [format, analyze, test]
193+
194+ steps :
195+ - name : Checkout repository
196+ uses : actions/checkout@v4
197+
198+ - name : Set up Java JDK 17
199+ uses : actions/setup-java@v4
200+ with :
201+ distribution : ' temurin'
202+ java-version : ' 17'
203+ cache : ' gradle'
204+
205+ - name : Set up Flutter SDK
206+ uses : subosito/flutter-action@v2
207+ with :
208+ channel : ${{ env.FLUTTER_CHANNEL }}
209+ cache : true
210+
211+ - name : Get Flutter dependencies
212+ run : flutter pub get
213+
214+ - name : Create .env file for build
215+ run : |
216+ # Create minimal .env file required by the app
217+ echo "# Build environment" > .env
218+
219+ - name : Build debug APK
220+ id : build
221+ run : |
222+ echo "## 🔨 Build Results" >> $GITHUB_STEP_SUMMARY
223+ echo "" >> $GITHUB_STEP_SUMMARY
224+
225+ if flutter build apk --debug 2>&1 | tee build_output.txt; then
226+ echo "✅ Debug APK built successfully!" >> $GITHUB_STEP_SUMMARY
227+ echo "" >> $GITHUB_STEP_SUMMARY
228+
229+ # Get APK size
230+ APK_PATH="build/app/outputs/flutter-apk/app-debug.apk"
231+ if [ -f "$APK_PATH" ]; then
232+ APK_SIZE=$(du -h "$APK_PATH" | cut -f1)
233+ echo "| Property | Value |" >> $GITHUB_STEP_SUMMARY
234+ echo "|----------|-------|" >> $GITHUB_STEP_SUMMARY
235+ echo "| **APK Size** | $APK_SIZE |" >> $GITHUB_STEP_SUMMARY
236+ echo "| **Build Type** | Debug |" >> $GITHUB_STEP_SUMMARY
237+ fi
238+
239+ echo "build_status=success" >> $GITHUB_OUTPUT
240+ else
241+ echo "❌ **Build failed!**" >> $GITHUB_STEP_SUMMARY
242+ echo "" >> $GITHUB_STEP_SUMMARY
243+ echo '```' >> $GITHUB_STEP_SUMMARY
244+ tail -50 build_output.txt >> $GITHUB_STEP_SUMMARY
245+ echo '```' >> $GITHUB_STEP_SUMMARY
246+ echo "" >> $GITHUB_STEP_SUMMARY
247+ echo "**Fix:** Review the build errors above and fix the issues." >> $GITHUB_STEP_SUMMARY
248+ echo "build_status=failure" >> $GITHUB_OUTPUT
249+ exit 1
250+ fi
251+
252+ # ============================================
253+ # CI STATUS SUMMARY
254+ # ============================================
255+ ci-status :
256+ name : ✅ CI Status
257+ runs-on : ubuntu-latest
258+ needs : [format, analyze, test, build]
259+ if : always()
260+
261+ steps :
262+ - name : Check CI status
263+ run : |
264+ echo "## 📊 CI Pipeline Summary" >> $GITHUB_STEP_SUMMARY
265+ echo "" >> $GITHUB_STEP_SUMMARY
266+ echo "| Check | Status |" >> $GITHUB_STEP_SUMMARY
267+ echo "|-------|--------|" >> $GITHUB_STEP_SUMMARY
268+
269+ # Check format job
270+ if [ "${{ needs.format.result }}" == "success" ]; then
271+ echo "| 📝 Format | ✅ Passed |" >> $GITHUB_STEP_SUMMARY
272+ else
273+ echo "| 📝 Format | ❌ Failed |" >> $GITHUB_STEP_SUMMARY
274+ fi
275+
276+ # Check analyze job
277+ if [ "${{ needs.analyze.result }}" == "success" ]; then
278+ echo "| 🔍 Analysis | ✅ Passed |" >> $GITHUB_STEP_SUMMARY
279+ else
280+ echo "| 🔍 Analysis | ❌ Failed |" >> $GITHUB_STEP_SUMMARY
281+ fi
282+
283+ # Check test job
284+ if [ "${{ needs.test.result }}" == "success" ]; then
285+ echo "| 🧪 Tests | ✅ Passed |" >> $GITHUB_STEP_SUMMARY
286+ else
287+ echo "| 🧪 Tests | ❌ Failed |" >> $GITHUB_STEP_SUMMARY
288+ fi
289+
290+ # Check build job
291+ if [ "${{ needs.build.result }}" == "success" ]; then
292+ echo "| 🔨 Build | ✅ Passed |" >> $GITHUB_STEP_SUMMARY
293+ else
294+ echo "| 🔨 Build | ❌ Failed |" >> $GITHUB_STEP_SUMMARY
295+ fi
296+
297+ echo "" >> $GITHUB_STEP_SUMMARY
298+
299+ # Overall status
300+ if [ "${{ needs.format.result }}" == "success" ] && \
301+ [ "${{ needs.analyze.result }}" == "success" ] && \
302+ [ "${{ needs.test.result }}" == "success" ] && \
303+ [ "${{ needs.build.result }}" == "success" ]; then
304+ echo "### 🎉 All CI checks passed!" >> $GITHUB_STEP_SUMMARY
305+ echo "" >> $GITHUB_STEP_SUMMARY
306+ echo "This PR is ready for review and merge." >> $GITHUB_STEP_SUMMARY
307+ else
308+ echo "### ⚠️ Some CI checks failed" >> $GITHUB_STEP_SUMMARY
309+ echo "" >> $GITHUB_STEP_SUMMARY
310+ echo "Please fix the failing checks before merging." >> $GITHUB_STEP_SUMMARY
311+ exit 1
312+ fi
0 commit comments