Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
34d7587
feat(core): add base Clean Architecture structure
CowboyGH Oct 29, 2025
8c558e8
chore(deps): add project dependencies
CowboyGH Oct 29, 2025
aabeae4
feat(linter): add public_member_api_docs rule to analysis options
CowboyGH Oct 29, 2025
1543364
refactor(app): extract FlutterStarterTemplate widget and add runner.dart
CowboyGH Oct 29, 2025
382702c
feat(di): add GetIt service locator and setup function
CowboyGH Oct 29, 2025
90077f3
chore(ios): update generated Xcode and CocoaPods files
CowboyGH Oct 29, 2025
24b8204
fix(runner): move WidgetsFlutterBinding.ensureInitialized() inside ru…
CowboyGH Oct 29, 2025
cf98048
feat(debug): add DebugScreen widget for debugging purposes
CowboyGH Oct 29, 2025
9034180
feat(router): add router and route paths for app navigation
CowboyGH Oct 29, 2025
ff299ef
feat(logger): integrate logger with AppLogger abstraction, DI setup a…
CowboyGH Nov 12, 2025
11eab76
feat(firebase): add firebase core setup
CowboyGH Nov 13, 2025
46cfb62
feat(firebase): add Crashlytics integration for global error handling
CowboyGH Nov 15, 2025
e5d5eba
feat(core): implement AppFailure base class and UnknownFailure subclass
CowboyGH Nov 15, 2025
a6326fa
feat(core): introduce Result model with Success, Failure and AsyncRes…
CowboyGH Nov 15, 2025
de6d149
chore(linter): remove `always_put_control_body_on_new_line` rule from…
CowboyGH Nov 21, 2025
14232a7
refactor (runner): add DI-independent fallback error handler in runZo…
CowboyGH Nov 21, 2025
ee93044
fix(deps): resolve build_runner failure by updating retrofit dependen…
CowboyGH Nov 24, 2025
4629d92
chore(xcode): update project settings and remove deprecated workspace…
CowboyGH Nov 24, 2025
2e335ad
feat(analytics): integrate Firebase Analytics with a custom analytics…
CowboyGH Nov 24, 2025
aa95cf9
refactor(di): streamline logger registration in dependency injection …
CowboyGH Nov 25, 2025
fc0bc6e
feat(logger): add AppLoggerMixin for convenient logger access in Bloc…
CowboyGH Nov 25, 2025
f8da6f7
chore(auth): remove unused .gitkeep file from auth domain sources
CowboyGH Nov 25, 2025
41a5410
chore: update Dart and Flutter SDK requirements to >=3.10.0
CowboyGH Nov 26, 2025
9b9a1e3
docs: update changelog and contributing guidelines for clarity and fo…
CowboyGH Dec 9, 2025
4ca3de2
feat: implement comprehensive CI/CD pipeline (#1)
CowboyGH Dec 14, 2025
970f1be
refactor(core): use interface contracts for analytics/logger and seal…
CowboyGH Dec 14, 2025
65b0adf
feat(network): add NetworkService for network availability monitoring…
CowboyGH Dec 19, 2025
6c9e5cf
chore(deps): update dependencies
CowboyGH Dec 19, 2025
7c494af
fix(ci): update free disk space action to v3 and add additional clean…
CowboyGH Dec 20, 2025
194e8a8
fix(ci): remove push trigger from CI/CD pipeline
CowboyGH Dec 20, 2025
7f4deee
docs: update README.md
CowboyGH Dec 20, 2025
a65b1a5
chore(release): bump version to 0.2.0 in pubspec.yaml and update chan…
CowboyGH Dec 20, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
114 changes: 114 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
# Flutter/Dart related
.dart_tool/
.flutter-plugins
.flutter-plugins-dependencies
.packages
.pub-cache/
.pub/
build/
*.lock.json
.metadata

# Android related
android/.gradle/
android/app/build/
android/gradle/
android/.idea/
android/local.properties
android/key.properties
android/app/release/
android/app/debug/
android/captures/
android/*.iml
*.jks
*.keystore
*.p12
google-services.json

# iOS related
ios/Pods/
ios/.symlinks/
ios/Flutter/Flutter.framework
ios/Flutter/Flutter.podspec
ios/.generated/
ios/Runner.xcworkspace/
ios/Runner.xcodeproj/xcuserdata/
ios/Runner.xcodeproj/project.xcworkspace/xcuserdata/
*.pbxuser
*.mode1v3
*.mode2v3
*.perspectivev3
*.xcuserstate
xcuserdata/
DerivedData/
*.mobileprovision
*.cer
*.p12
GoogleService-Info.plist

# IDE related
.idea/
.vscode/
*.swp
*.swo
*~
.DS_Store
Thumbs.db
desktop.ini

# Git
.git/
.gitignore
.gitattributes
.github/

# CI/CD
.gitlab-ci.yml
.circleci/
.travis.yml
azure-pipelines.yml

# Testing and coverage
coverage/
test/
integration_test/
.test_coverage/
*.lcov
*.info

# Documentation
README.md
README*.md
LICENSE
CHANGELOG.md
docs/
CONTRIBUTING.md

# Docker files
Dockerfile
Dockerfile.*
.dockerignore
docker-compose.yml
docker-compose.*.yml

# Local configurations
.env
.env.*
.env.local
.env.*.local
*.log
*.log.*

# Temporary files
*.tmp
*.temp
*.bak
*.swp
*~

# Firebase
firebase.json
.firebaserc
firestore.rules
firestore.indexes.json
storage.rules
225 changes: 225 additions & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,225 @@
name: Flutter CI/CD Pipeline

on:
pull_request:
branches: [main, develop]
workflow_dispatch:

env:
FLUTTER_VERSION: '3.38.4'
ANDROID_SDK_VERSION: '35'

jobs:
# ==========================================
# Job 1: Code Quality & Analysis
# ==========================================
analyze:
name: Code Analysis
runs-on: ubuntu-latest

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Setup Flutter
uses: subosito/flutter-action@v2
with:
flutter-version: ${{ env.FLUTTER_VERSION }}
channel: 'stable'
cache: true

- name: Create fake firebase_options.dart
run: |
mkdir -p lib
cat <<EOF > lib/firebase_options.dart
// ignore_for_file: type=lint
import 'package:firebase_core/firebase_core.dart'; import 'package:flutter/foundation.dart';
class DefaultFirebaseOptions {
static FirebaseOptions get currentPlatform {
if (kIsWeb) throw UnsupportedError('Web not configured');
switch (defaultTargetPlatform) {
case TargetPlatform.android: return android;
case TargetPlatform.iOS: return ios;
default: throw UnsupportedError('Platform not configured');
}
}
static const FirebaseOptions android = FirebaseOptions(apiKey: 'fake', appId: 'fake', messagingSenderId: 'fake', projectId: 'fake', storageBucket: 'fake');
static const FirebaseOptions ios = FirebaseOptions(apiKey: 'fake', appId: 'fake', messagingSenderId: 'fake', projectId: 'fake', storageBucket: 'fake', iosBundleId: 'fake');
}
EOF

- name: Get dependencies
run: flutter pub get

- name: Run build_runner
run: dart run build_runner build --delete-conflicting-outputs

- name: Format generated code
run: dart format .

- name: Verify formatting
run: dart format --set-exit-if-changed lib/ test/
continue-on-error: false

- name: Analyze code
run: flutter analyze --fatal-infos

- name: Run tests
run: flutter test --coverage || echo "⚠️ No tests found"

- name: Upload coverage to artifacts
if: always()
uses: actions/upload-artifact@v4
with:
name: code-coverage
path: coverage/lcov.info
if-no-files-found: ignore
retention-days: 7

# ==========================================
# Job 2: Android Build (Docker)
# ==========================================
build-android:
name: Build Android APK
runs-on: ubuntu-latest
needs: analyze
timeout-minutes: 60

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Free disk space
uses: endersonmenezes/free-disk-space@v3
with:
remove_android: true
remove_dotnet: true
remove_haskell: true
remove_tool_cache: true
remove_swap: true

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Create mock configuration files
run: |
mkdir -p lib
# firebase_options.dart
echo "import 'package:firebase_core/firebase_core.dart'; import 'package:flutter/foundation.dart'; class DefaultFirebaseOptions { static FirebaseOptions get currentPlatform { if (kIsWeb) throw UnsupportedError('Web not configured'); switch (defaultTargetPlatform) { case TargetPlatform.android: return android; case TargetPlatform.iOS: return ios; default: throw UnsupportedError('Platform not configured'); } } static const FirebaseOptions android = FirebaseOptions(apiKey: 'fake', appId: 'fake', messagingSenderId: 'fake', projectId: 'fake', storageBucket: 'fake'); static const FirebaseOptions ios = FirebaseOptions(apiKey: 'fake', appId: 'fake', messagingSenderId: 'fake', projectId: 'fake', storageBucket: 'fake', iosBundleId: 'fake'); }" > lib/firebase_options.dart
# google-services.json
echo '{"project_info":{"project_number":"123","project_id":"fake","storage_bucket":"fake"},"client":[{"client_info":{"mobilesdk_app_id":"1:123:android:123","android_client_info":{"package_name":"com.flutter.starter.template.flutter_starter_template"}},"api_key":[{"current_key":"fake"}]}]}' > android/app/google-services.json

- name: Build Docker image
id: build
uses: docker/build-push-action@v5
with:
context: .
push: false
load: true
tags: flutter-build:latest
build-args: |
BUILD_DATE=${{ github.event.repository.updated_at }}
VCS_REF=${{ github.sha }}
FLUTTER_VERSION=${{ env.FLUTTER_VERSION }}
ANDROID_SDK_VERSION=${{ env.ANDROID_SDK_VERSION }}
cache-from: type=gha
cache-to: type=gha,mode=max

- name: Extract APK
run: |
docker create --name temp flutter-build:latest
mkdir -p apk-output
docker cp temp:/artifacts/. apk-output/
docker rm temp
ls -lh apk-output/

- name: Upload Android APK artifacts
uses: actions/upload-artifact@v4
with:
name: android-apk-${{ github.sha }}
path: apk-output/*.apk
retention-days: 30
compression-level: 0

- name: Generate Summary
run: |
echo "## πŸ€– Android APK Build Results" >> $GITHUB_STEP_SUMMARY
echo "| Architecture | File | Size |" >> $GITHUB_STEP_SUMMARY
echo "|--------------|----------|------|" >> $GITHUB_STEP_SUMMARY
for apk in apk-output/*.apk; do
name=$(basename "$apk")
size=$(du -h "$apk" | cut -f1)
arch="Universal"
if [[ "$name" == *"arm64"* ]]; then arch="ARM64"; fi
if [[ "$name" == *"armeabi"* ]]; then arch="ARMv7"; fi
if [[ "$name" == *"x86_64"* ]]; then arch="x86_64"; fi
echo "| $arch | \`$name\` | $size |" >> $GITHUB_STEP_SUMMARY
done

# ==========================================
# Job 3: iOS Build (macOS runner)
# ==========================================
build-ios:
name: Build iOS IPA
runs-on: macos-latest
needs: analyze
timeout-minutes: 45

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Setup Flutter
uses: subosito/flutter-action@v2
with:
flutter-version: ${{ env.FLUTTER_VERSION }}
channel: 'stable'
cache: true

- name: Create mock configuration files
run: |
mkdir -p lib ios/Runner
# Create firebase_options.dart
echo "import 'package:firebase_core/firebase_core.dart'; import 'package:flutter/foundation.dart'; class DefaultFirebaseOptions { static FirebaseOptions get currentPlatform { if (kIsWeb) throw UnsupportedError('Web not configured'); switch (defaultTargetPlatform) { case TargetPlatform.android: return android; case TargetPlatform.iOS: return ios; default: throw UnsupportedError('Platform not configured'); } } static const FirebaseOptions android = FirebaseOptions(apiKey: 'fake', appId: 'fake', messagingSenderId: 'fake', projectId: 'fake', storageBucket: 'fake'); static const FirebaseOptions ios = FirebaseOptions(apiKey: 'fake', appId: 'fake', messagingSenderId: 'fake', projectId: 'fake', storageBucket: 'fake', iosBundleId: 'fake'); }" > lib/firebase_options.dart
# Create GoogleService-Info.plist
echo '<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"><plist version="1.0"><dict><key>API_KEY</key><string>fake</string><key>GCM_SENDER_ID</key><string>123</string><key>PLIST_VERSION</key><string>1</string><key>BUNDLE_ID</key><string>com.flutter.starter.template.flutterStarterTemplate</string><key>PROJECT_ID</key><string>fake</string><key>STORAGE_BUCKET</key><string>fake</string><key>IS_ADS_ENABLED</key><false/><key>IS_ANALYTICS_ENABLED</key><false/><key>IS_APPINVITE_ENABLED</key><true/><key>IS_GCM_ENABLED</key><true/><key>IS_SIGNIN_ENABLED</key><true/><key>GOOGLE_APP_ID</key><string>1:123:ios:123</string></dict></plist>' > ios/Runner/GoogleService-Info.plist

- name: Get dependencies
run: flutter pub get

- name: Install Pods
run: |
cd ios
rm -rf Pods Podfile.lock
pod install --repo-update

- name: Build iOS app
run: |
flutter build ios --release --no-codesign --build-number=${{ github.run_number }}

- name: Create IPA
run: |
mkdir -p Payload
cp -R build/ios/iphoneos/Runner.app Payload/
IPA_NAME="flutter-starter-ios-${{ github.run_number }}-unsigned.ipa"
zip -r "$IPA_NAME" Payload
rm -rf Payload

- name: Upload iOS IPA artifact
uses: actions/upload-artifact@v4
with:
name: ios-ipa-${{ github.sha }}
path: "*.ipa"
retention-days: 30
compression-level: 0

- name: Generate Summary
run: |
echo "## 🍎 iOS IPA Build Results" >> $GITHUB_STEP_SUMMARY
echo "| File | Size |" >> $GITHUB_STEP_SUMMARY
echo "|------|------|" >> $GITHUB_STEP_SUMMARY
for ipa in *.ipa; do
size=$(du -h "$ipa" | cut -f1)
echo "| \`$ipa\` | $size |" >> $GITHUB_STEP_SUMMARY
done
echo "⚠️ Unsigned IPA for testing only." >> $GITHUB_STEP_SUMMARY
18 changes: 17 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,22 @@ linked_*.ds
unlinked.ds
unlinked_spec.ds

# Build Runner generated
*.g.dart
*.mocks.dart

# Firebase related
firebase_options.dart

# Docker related
*.img
*.tar
*.tar.gz

# APK related
*.apk
apk-output/

# Symbolication related
app.*.symbols

Expand Down Expand Up @@ -127,4 +143,4 @@ ios/Runner/GoogleService-Info.plist
android/app/google-services.json

# Generated localization
lib/core/localization/generated/
lib/core/localization/generated/
Loading