5. CI/CD & Build Pipeline

CI/CD Pipeline Health

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

5.1 Build Configuration

Status: Pass

Findings:

  • Flutter version pinned: .fvmrc file exists and pins Flutter to version 3.24.5
  • 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.5
  • Build number calculation: Uses PROJECT_BUILD_NUMBER + 100 offset (not automatic store API lookup)
  • Xcode version: Pinned to 16.1
  • Workflow structure: Well-organized with reusable script anchors (&set_key_properties, &setup_pods, etc.)

Evidence:

  • .fvmrc: Defines Flutter 3.24.5 for the project
  • codemagic.yaml line 16, 149: flutter: fvm (uses FVM which respects .fvmrc)
  • codemagic.yaml line 18, 151: cocoapods: default (not pinned to specific version)
  • codemagic.yaml line 17, 150: xcode: 16.1 (pinned)
  • pubspec.yaml SDK constraint: >=3.0.0 <4.0.0 (compatible with 3.24.5)
  • Build scripts use $(($PROJECT_BUILD_NUMBER + 100)) for version code calculation
  • codemagic.yaml: Uses YAML anchors for reusable scripts (lines 26, 37, 41, 45, 49, 63, 67, 71, 75, 79, 83, 94)

Risk Level: Low Risk

Recommendation:

  • Immediate actions:
  • Pin CocoaPods version explicitly (e.g., 1.15.2) for reproducibility
  • 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 offset calculation

5.2 Code Signing & Certificates

Status: Fail

Findings:

  • Critical: Sensitive signing credentials stored directly in repository
  • Critical: android/key.properties contains hardcoded passwords and keystore path
    • storePassword=kZeLOMmMZN2o7EqTGfwA (hardcoded in repository)
    • keyPassword=kZeLOMmMZN2o7EqTGfwA (hardcoded in repository)
    • storeFile=/Users/thomascornet/Studioprojects/supertank/android-assets/supertank.keystore.jks (hardcoded path)
  • Android keystore file (supertank.keystore.jks) in android-assets/ directory
  • Apple Sign In private key (AuthKey_9ZW5QT9Y35 (1).p8) in ios-assets/ directory
  • Push certificate passwords in ios-assets/ios_push_certificate_pass.rtf and .txt
  • Android signing configuration:
  • Uses encrypted environment variables (FCI_KEYSTORE, FCI_KEYSTORE_PASSWORD, FCI_KEY_PASSWORD, FCI_KEY_ALIAS) in CI/CD
  • Creates key.properties file dynamically from environment variables during build (good practice)
  • Both staging and production workflows include Android signing setup
  • 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 app-store-connect fetch-signing-files to fetch certificates and profiles
  • Keychain setup and certificate management in build scripts
  • Build number management:
  • Uses PROJECT_BUILD_NUMBER + 100 offset calculation (not automatic store API lookup)
  • Version name extracted from Git tag (e.g., prod_1.20.01.20.0)
  • Potential for build number conflicts if not managed carefully
  • Certificate management issues:
  • No centralized certificate management
  • Certificates and keys stored in multiple locations
  • Sensitive files in repository despite encrypted variables in CI/CD
  • Hardcoded passwords in key.properties file (critical security risk)

Evidence:

  • android/key.properties: Contains hardcoded passwords and keystore path (lines 1-4)
    • storePassword=kZeLOMmMZN2o7EqTGfwA
    • keyPassword=kZeLOMmMZN2o7EqTGfwA
    • keyAlias=key0
    • storeFile=/Users/thomascornet/Studioprojects/supertank/android-assets/supertank.keystore.jks
  • codemagic.yaml:
  • Android signing: Encrypted environment variables for keystore credentials (lines 26-36)
  • Script set_key_properties: Creates key.properties from encrypted variables (good practice)
  • iOS signing: Environment variables for App Store Connect credentials (lines 131-134, 231-234)
  • Scripts: setup_keychain, setup_certificates, add_certificates, setup_provisioning_profiles
  • Build number: $(($PROJECT_BUILD_NUMBER + 100)) (lines 56, 62, 101, 108, 169, 175, 199, 206)
  • android-assets/supertank.keystore.jks: Android signing keystore in repository
  • ios-assets/AuthKey_9ZW5QT9Y35 (1).p8: Apple Sign In private key in repository
  • ios-assets/ios_push_certificate_pass.rtf and .txt: Push certificate passwords in repository
  • android/app/build.gradle lines 19-23, 69-74: References key.properties for signing config

Risk Level: Critical Risk

Recommendation:

  • Immediate actions:
  • Remove android/key.properties from repository (contains hardcoded passwords)
  • Remove all sensitive signing files from repository (after backing up to secure location)
  • Move signing credentials to Codemagic secrets management or secure cloud storage
  • Add sensitive directories to .gitignore: android-assets/, ios-assets/, android/key.properties
  • Rotate all exposed credentials (keystores, passwords, private keys)
  • Short-term:
  • Ensure key.properties is only created from environment variables in CI/CD
  • Consider using Codemagic's App Store Connect integration instead of environment variables
  • Implement centralized certificate management

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 (supertank-prod, supertank-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 offset: Uses PROJECT_BUILD_NUMBER + 100 calculation instead of automatic store API lookup
  • 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 supertank-prod and supertank-staging workflow names
  • Staging release notes: Hardcoded release notes in staging workflow (lines 182-191)
  • SQLite header clearing: Custom script to clear SQLite headers (lines 83-93) - workaround for build issues

Evidence:

  • codemagic.yaml:
  • Separate workflows: supertank-prod and supertank-staging
  • Tag patterns: prod_* for production (line 23), staging_* for staging (line 156)
  • Version extraction from tag: IN="$FCI_TAG"; arrIN=(${IN//_/ }); VERSION_NAME=${arrIN[1]} (lines 52-54, 97-99, 165-167, 194-196)
  • Build number: $(($PROJECT_BUILD_NUMBER + 100)) (lines 56, 62, 101, 108, 169, 175, 199, 206)
  • Separate bundle IDs for staging (com.supertank.supertank.staging) and production (com.supertank.supertank)
  • Different entry points: lib/main_staging.dart (staging) vs lib/main_prod.dart (production)
  • Staging workflow includes hardcoded release notes (lines 182-191)
  • Custom SQLite header clearing script (lines 83-93) - workaround for build issues
  • 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
  • Remove hardcoded release notes, use dynamic release notes from Git commits or changelog
  • Investigate and fix SQLite header issue to remove workaround script