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:
.fvmrcfile exists and pins Flutter to version 3.24.3 - FVM used for version management:
codemagic.yamlusesflutter: fvmwhich 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_NUMBERdirectly (no offset calculation) - Xcode version: Uses
latestfor production, pinned to16.1for staging - Workflow structure: Well-organized with reusable script anchors (
&set_local_properties,&setup_pods, etc.)
Evidence:
.fvmrc: Defines Flutter 3.24.3 for the projectcodemagic.yamlline 19, 137:flutter: fvm(uses FVM which respects.fvmrc)codemagic.yamlline 21, 139:cocoapods: default(not pinned to specific version)codemagic.yamlline 20:xcode: latest(production), line 138:xcode: 16.1(staging)pubspec.yamlSDK 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
latestfor 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.jksstored in repository - Android signing configuration:
- Uses Codemagic secrets management (
gulf-android-keystore) in CI/CD (good practice) build.gradleuses environment variables (CM_KEYSTORE_PATH,CM_KEYSTORE_PASSWORD,CM_KEY_PASSWORD,CM_KEY_ALIAS) whenCIis set- No
key.propertiesfile 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-profilesfor provisioning profiles - Build number management:
- Uses
PROJECT_BUILD_NUMBERdirectly (no offset calculation) - Version name extracted from Git tag (e.g.,
prod_1.20.0→1.20.0) - Staging builds append
-stagingsuffix 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 repositoryandroid-assets/keystore-readme.txt: Documentation for keystore usagecodemagic.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_profilesfor 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.gradlelines 64-68: Uses environment variables for signing whenCIis setandroid/app/build.gradlelines 70-73: Falls back tokey.propertiesfor local builds (but file doesn't exist)
Risk Level: Critical Risk
Recommendation:
- Immediate actions:
- Remove
android-assets/gulfforyou.keystore.jksfrom 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.yamldoes 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 includeflutter testcommands - 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*.dartreturned 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_*vsprod_*) - 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→ version1.20.0) - Build number: Uses
PROJECT_BUILD_NUMBERdirectly (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-prodandgulf-stagingworkflow names - SQLite header clearing: Custom script to clear SQLite headers (lines 62-78) - workaround for build issues
- Staging version suffix: Staging builds append
-stagingsuffix to version name (line 160)
Evidence:
codemagic.yaml:- Separate workflows:
gulf-prodandgulf-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) vslib/main_prod.dart(production) - Custom SQLite header clearing script (lines 62-78) - workaround for build issues
- Staging version name includes
-stagingsuffix (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
-stagingsuffix from version name (use same version for both environments)