5. CI/CD & Build Pipeline

CI/CD Pipeline Health

Build Automation80 / 100
Automated Testing in CI/CD0 / 100
Code Signing Security50 / 100
Deployment Process40 / 100

5.1 Build Configuration

Status: Pass

Findings:

  • Flutter version pinned: .fvmrc file exists and pins Flutter to version 3.24.3
  • FVM used for version management: codemagic.yaml uses flutter: fvm which respects .fvmrc
  • CocoaPods version: Set to default (not pinned to specific version)
  • SDK constraint: Allows Flutter 3.x (>=3.0.0 <4.0.0) compatible with 3.24.3
  • Build number calculation: Uses PROJECT_BUILD_NUMBER directly (no offset calculation)
  • Xcode version: Uses latest for production, pinned to 16.1 for staging
  • Workflow structure: Well-organized with reusable script anchors (&set_local_properties, &setup_pods, etc.)

Evidence:

  • .fvmrc: Defines Flutter 3.24.3 for the project
  • codemagic.yaml line 19, 137: flutter: fvm (uses FVM which respects .fvmrc)
  • codemagic.yaml line 21, 139: cocoapods: default (not pinned to specific version)
  • codemagic.yaml line 20: xcode: latest (production), line 138: xcode: 16.1 (staging)
  • pubspec.yaml SDK constraint: >=3.0.0 <4.0.0 (compatible with 3.24.3)
  • Build scripts use $(($PROJECT_BUILD_NUMBER)) for version code calculation (no offset)
  • codemagic.yaml: Uses YAML anchors for reusable scripts (lines 29, 33, 40, 54, 58, 62, 79, 147, 162, 164)

Risk Level: Low Risk

Recommendation:

  • Immediate actions:
  • Pin CocoaPods version explicitly (e.g., 1.15.2) for reproducibility
  • Pin Xcode version for production (use specific version instead of latest for consistency)
  • Document Flutter version in README or build documentation
  • Short-term:
  • Plan incremental upgrades (e.g., current → latest 3.x → 4.0 when stable) aligned with dependency migrations
  • Test Flutter upgrades in staging environment before production
  • Resolve deprecated dependencies before major Flutter upgrades
  • Use automatic build number from store APIs instead of manual PROJECT_BUILD_NUMBER

5.2 Code Signing & Certificates

Status: Fail

Findings:

  • Critical: Sensitive signing credentials stored directly in repository
  • Android keystore file: android-assets/gulfforyou.keystore.jks stored in repository
  • Android signing configuration:
  • Uses Codemagic secrets management (gulf-android-keystore) in CI/CD (good practice)
  • build.gradle uses environment variables (CM_KEYSTORE_PATH, CM_KEYSTORE_PASSWORD, CM_KEY_PASSWORD, CM_KEY_ALIAS) when CI is set
  • No key.properties file found in repository (good - not hardcoded)
  • Keystore file should not be in repository, even if password-protected
  • iOS signing configuration:
  • Uses App Store Connect credentials from environment variables
  • Credentials: APP_STORE_CONNECT_PRIVATE_KEY, APP_STORE_CONNECT_KEY_IDENTIFIER, APP_STORE_CONNECT_ISSUER_ID
  • Uses xcode-project use-profiles for provisioning profiles
  • Build number management:
  • Uses PROJECT_BUILD_NUMBER directly (no offset calculation)
  • Version name extracted from Git tag (e.g., prod_1.20.01.20.0)
  • Staging builds append -staging suffix to version name
  • Certificate management issues:
  • Keystore file stored in repository despite encrypted variables in CI/CD
  • No centralized certificate management
  • Risk of credentials being exposed in version control history

Evidence:

  • android-assets/gulfforyou.keystore.jks: Android signing keystore in repository
  • android-assets/keystore-readme.txt: Documentation for keystore usage
  • codemagic.yaml:
  • Android signing: Uses Codemagic secrets management (gulf-android-keystore) (lines 6-7, 127-128)
  • iOS signing: Environment variables for App Store Connect credentials (lines 116-119, 202-205)
  • Scripts: setup_profiles for provisioning profiles (line 54)
  • Build number: $(($PROJECT_BUILD_NUMBER)) (lines 47, 53, 155, 161, 172, 179)
  • Version extraction from tag: IN="$FCI_TAG"; arrIN=(${IN//_/ }); VERSION_NAME=${arrIN[1]} (lines 43-45, 82-84, 151-153, 167-169)
  • android/app/build.gradle lines 64-68: Uses environment variables for signing when CI is set
  • android/app/build.gradle lines 70-73: Falls back to key.properties for local builds (but file doesn't exist)

Risk Level: Critical Risk

Recommendation:

  • Immediate actions:
  • Remove android-assets/gulfforyou.keystore.jks from repository (after backing up to secure location)
  • Add sensitive directories to .gitignore: android-assets/
  • Rotate keystore if it has been exposed in version control history
  • Ensure keystore is only accessed via Codemagic secrets management
  • Short-term:
  • Consider using Codemagic's App Store Connect integration instead of environment variables
  • Implement centralized certificate management
  • Document proper keystore management procedures

5.3 Automated Testing in CI/CD

Status: Fail

Findings:

  • No automated tests in CI/CD pipeline: codemagic.yaml does not include test execution steps
  • No test files found: No test files (*test*.dart) found in repository
  • Builds can succeed even with broken code
  • Missing quality gates in build process
  • No linting or static analysis steps in CI/CD

Evidence:

  • codemagic.yaml:
  • Workflow scripts (gulf-prod, gulf-staging) do not include flutter test commands
  • No test execution steps in build scripts
  • No test failure gates in build pipeline
  • Scripts focus on dependency setup, signing, and building only
  • No linting or static analysis steps visible
  • No test files found in repository (glob search for *test*.dart returned 0 results)

Risk Level: High Risk

Recommendation:

  • Immediate actions:
  • Add test execution steps to Codemagic workflows
  • Create initial test suite (unit tests, widget tests)
  • Fail builds if critical tests fail
  • Short-term:
  • Run integration tests against staging environment in CI/CD
  • Add linting and static analysis steps (flutter analyze)
  • Add code coverage reporting
  • Set up test coverage thresholds

5.4 Deployment Process

Status: ⚠️Warning

Findings:

  • Separate tag-based deployment: Uses different Git tag patterns for staging and production (staging_* vs prod_*)
  • Suboptimal tag management: Separate tags make it difficult to track which version is in staging vs production
  • No clear promotion path: No mechanism to promote same artifact from staging to production
  • Tag-based versioning: Version name extracted from tag (e.g., prod_1.20.0 → version 1.20.0)
  • Build number: Uses PROJECT_BUILD_NUMBER directly (no offset calculation, unlike SuperTank)
  • GitFlow workflow: Makes it hard to track pending changes and current state (see section 2.1)
  • Multiple long-lived branches: Create complexity and merge conflicts
  • Difficult to identify deployed code: Hard to determine what code is currently deployed in each environment
  • Workflow naming: Uses gulf-prod and gulf-staging workflow names
  • SQLite header clearing: Custom script to clear SQLite headers (lines 62-78) - workaround for build issues
  • Staging version suffix: Staging builds append -staging suffix to version name (line 160)

Evidence:

  • codemagic.yaml:
  • Separate workflows: gulf-prod and gulf-staging
  • Tag patterns: prod_* for production (line 26), staging_* for staging (line 144)
  • Version extraction from tag: IN="$FCI_TAG"; arrIN=(${IN//_/ }); VERSION_NAME=${arrIN[1]} (lines 43-45, 82-84, 151-153, 167-169)
  • Build number: $(($PROJECT_BUILD_NUMBER)) (lines 47, 53, 155, 161, 172, 179) - no offset
  • Separate bundle IDs for staging (nl.gulf.gulfforyou.staging) and production (nl.gulf.gulfforyou)
  • Different entry points: lib/main_staging.dart (staging) vs lib/main_prod.dart (production)
  • Custom SQLite header clearing script (lines 62-78) - workaround for build issues
  • Staging version name includes -staging suffix (line 160)
  • GitFlow branching model in use (based on section 2.1 findings)

Risk Level: Medium Risk

Recommendation:

  • Immediate actions:
  • Migrate from GitFlow to trunk-based development workflow
  • Implement single tag approach: tag once, deploy to staging, then promote same tag to production after approval
  • Finish and merge all hanging changes from feature branches
  • Short-term:
  • Implement deployment promotion pipeline: staging → production using same build artifact
  • Simplify branching: main/master as primary branch, short-lived feature branches
  • Add deployment notifications
  • Investigate and fix SQLite header issue to remove workaround script
  • Consider removing -staging suffix from version name (use same version for both environments)