@@ -252,6 +252,123 @@ jobs:
252252 echo "SwiftLint not available, skipping"
253253 fi
254254
255+ # ============================================
256+ # Code Quality Checks (based on CODE_REVIEW_GUIDELINES.md)
257+ # ============================================
258+ code-quality :
259+ name : Code Quality
260+ runs-on : ubuntu-latest
261+ timeout-minutes : 5
262+
263+ steps :
264+ - name : Checkout code
265+ uses : actions/checkout@v4
266+
267+ - name : Check for print() statements
268+ run : |
269+ echo "Checking for print() statements in production code..."
270+ # Exclude test files and comments
271+ PRINTS=$(grep -rn "print(" ${{ env.APP_NAME }}/ --include="*.swift" | grep -v "// " | grep -v "Test" | grep -v "#Preview" || true)
272+ if [ -n "$PRINTS" ]; then
273+ echo "::warning::Found print() statements in production code:"
274+ echo "$PRINTS"
275+ echo ""
276+ echo "Consider removing print() statements before merging."
277+ else
278+ echo "✅ No print() statements found"
279+ fi
280+
281+ - name : Check for force unwraps
282+ run : |
283+ echo "Checking for force unwraps (!)..."
284+ # Look for force unwraps, excluding comments and common safe patterns
285+ FORCE_UNWRAPS=$(grep -rn '\![^=]' ${{ env.APP_NAME }}/ --include="*.swift" | grep -v "//" | grep -v "!=" | grep -v "!important" | grep -v "@IBOutlet" | grep -v "canImport" || true)
286+ COUNT=$(echo "$FORCE_UNWRAPS" | grep -c "!" || echo "0")
287+ if [ "$COUNT" -gt "0" ] && [ -n "$FORCE_UNWRAPS" ]; then
288+ echo "::warning::Found potential force unwraps:"
289+ echo "$FORCE_UNWRAPS" | head -20
290+ echo ""
291+ echo "Review these for safety. Force unwraps should have justification."
292+ else
293+ echo "✅ No obvious force unwraps found"
294+ fi
295+
296+ - name : Check macOS 26 availability patterns
297+ run : |
298+ echo "Checking macOS 26 compatibility patterns..."
299+
300+ # Check for #unavailable (should use #available instead)
301+ UNAVAILABLE=$(grep -rn "#unavailable" ${{ env.APP_NAME }}/ --include="*.swift" || true)
302+ if [ -n "$UNAVAILABLE" ]; then
303+ echo "::error::Found #unavailable patterns (use #available with else instead):"
304+ echo "$UNAVAILABLE"
305+ exit 1
306+ fi
307+ echo "✅ No #unavailable patterns found"
308+
309+ # Check that glassEffect usage has availability checks
310+ GLASS_NO_CHECK=$(grep -rn "\.glassEffect" ${{ env.APP_NAME }}/ --include="*.swift" | grep -v "#available" | grep -v "//" || true)
311+ if [ -n "$GLASS_NO_CHECK" ]; then
312+ echo "::warning::Found .glassEffect without #available check:"
313+ echo "$GLASS_NO_CHECK"
314+ echo ""
315+ echo "Ensure all glassEffect calls are wrapped in #available(macOS 26, *)"
316+ else
317+ echo "✅ All glassEffect calls appear to have availability checks"
318+ fi
319+
320+ # Verify macOS 26 patterns exist (sanity check)
321+ MACOS26_COUNT=$(grep -rn "#available(macOS 26" ${{ env.APP_NAME }}/ --include="*.swift" | wc -l || echo "0")
322+ echo "Found $MACOS26_COUNT macOS 26 availability checks"
323+
324+ - name : Check for large files
325+ run : |
326+ echo "Checking for oversized files..."
327+ # Flag Swift files over 500 lines (per guidelines)
328+ LARGE_FILES=$(find ${{ env.APP_NAME }} -name "*.swift" -exec wc -l {} + | awk '$1 > 500 {print}' | grep -v "total" || true)
329+ if [ -n "$LARGE_FILES" ]; then
330+ echo "::warning::Found files exceeding 500 lines:"
331+ echo "$LARGE_FILES"
332+ echo ""
333+ echo "Consider splitting these files per CODE_REVIEW_GUIDELINES.md"
334+ else
335+ echo "✅ All files within size guidelines"
336+ fi
337+
338+ - name : Check for TODO/FIXME comments
339+ run : |
340+ echo "Checking for TODO/FIXME comments..."
341+ TODOS=$(grep -rn "TODO\|FIXME\|HACK\|XXX" ${{ env.APP_NAME }}/ --include="*.swift" | grep -v "// TODO: (template)" || true)
342+ COUNT=$(echo "$TODOS" | grep -c "TODO\|FIXME\|HACK\|XXX" || echo "0")
343+ if [ "$COUNT" -gt "0" ] && [ -n "$TODOS" ]; then
344+ echo "::warning::Found $COUNT TODO/FIXME comments:"
345+ echo "$TODOS"
346+ echo ""
347+ echo "Ensure these are tracked issues, not forgotten work."
348+ else
349+ echo "✅ No TODO/FIXME comments found"
350+ fi
351+
352+ - name : Check Theme usage
353+ run : |
354+ echo "Checking for hardcoded values that should use Theme..."
355+
356+ # Check for hardcoded fonts
357+ HARDCODED_FONTS=$(grep -rn "\.font(\.system(size:" ${{ env.APP_NAME }}/ --include="*.swift" | grep -v "Theme" || true)
358+ if [ -n "$HARDCODED_FONTS" ]; then
359+ echo "::warning::Found hardcoded font sizes (should use Theme.Typography):"
360+ echo "$HARDCODED_FONTS" | head -10
361+ fi
362+
363+ # Check for hardcoded cornerRadius with numbers
364+ HARDCODED_RADIUS=$(grep -rn "cornerRadius: [0-9]" ${{ env.APP_NAME }}/ --include="*.swift" | grep -v "Theme" || true)
365+ if [ -n "$HARDCODED_RADIUS" ]; then
366+ echo "::warning::Found hardcoded cornerRadius (should use Theme.CornerRadius):"
367+ echo "$HARDCODED_RADIUS" | head -10
368+ fi
369+
370+ echo "✅ Theme usage check complete"
371+
255372 # ============================================
256373 # Documentation Check
257374 # ============================================
@@ -296,15 +413,18 @@ jobs:
296413 - name : Checkout code
297414 uses : actions/checkout@v4
298415
416+ - name : Install xmllint
417+ run : sudo apt-get update && sudo apt-get install -y libxml2-utils
418+
299419 - name : Validate appcast.xml
300420 run : |
301421 if [ -f "appcast.xml" ]; then
302422 # Check if it's valid XML
303- if ! xmllint --noout appcast.xml 2>/dev/null ; then
423+ if ! xmllint --noout appcast.xml; then
304424 echo "::error::appcast.xml is not valid XML"
305425 exit 1
306426 fi
307- echo "appcast.xml is valid XML"
427+ echo "✅ appcast.xml is valid XML"
308428 else
309429 echo "No appcast.xml found (OK for PRs)"
310430 fi
@@ -315,7 +435,7 @@ jobs:
315435 pr-check-complete :
316436 name : PR Checks Complete
317437 runs-on : ubuntu-latest
318- needs : [build, test, archive, docs]
438+ needs : [build, test, archive, docs, code-quality, validate-appcast ]
319439 if : always()
320440
321441 steps :
@@ -337,4 +457,12 @@ jobs:
337457 echo "::error::Documentation check failed"
338458 exit 1
339459 fi
460+ if [ "${{ needs.code-quality.result }}" != "success" ]; then
461+ echo "::error::Code quality checks failed"
462+ exit 1
463+ fi
464+ if [ "${{ needs.validate-appcast.result }}" != "success" ]; then
465+ echo "::error::Appcast validation failed"
466+ exit 1
467+ fi
340468 echo "All required checks passed!"
0 commit comments