inspect method
Run comprehensive CI health inspection on projectPath.
Implementation
Future<CIDoctorReport> inspect(String projectPath) async {
final checks = <CIDoctorCheck>[];
// 1. Environment Check
final ctx = _ciEnv.createContext();
checks.add(CIDoctorCheck(
category: 'Environment',
title: 'CI Environment',
status: ctx.isCi ? 'Configured' : 'Passed',
details: 'Provider: ${ctx.providerName} (isCi: ${ctx.isCi})',
));
// 2. Workflows Check
final workflowsDir = Directory('$projectPath/.github/workflows');
final hasWorkflowsDir = await workflowsDir.exists();
if (hasWorkflowsDir) {
checks.add(const CIDoctorCheck(
category: 'Workflows',
title: 'Workflows Directory',
status: 'Configured',
details: 'Found .github/workflows directory',
));
for (final name in [
'flutter_verify.yml',
'flutter_release.yml',
'flutter_patch.yml'
]) {
final file = File('${workflowsDir.path}/$name');
final exists = await file.exists();
checks.add(CIDoctorCheck(
category: 'Workflows',
title: 'Workflow: $name',
status: exists ? 'Configured' : 'Missing',
details: exists
? 'File exists'
: 'File missing (run "flutter_setup ci init")',
));
}
} else {
checks.add(const CIDoctorCheck(
category: 'Workflows',
title: 'Workflows Directory',
status: 'Missing',
details: 'No .github/workflows directory found',
));
}
// 3. Fastlane Check
final fastlaneAvailable = await _fastlaneRunner.isFastlaneAvailable();
checks.add(CIDoctorCheck(
category: 'Deployment',
title: 'Fastlane CLI Tool',
status: fastlaneAvailable ? 'Configured' : 'Warning',
details: fastlaneAvailable
? 'Fastlane executable found on PATH'
: 'Fastlane not installed on local machine (required for local store deployment)',
));
final androidFastfile = File('$projectPath/android/fastlane/Fastfile');
final iosFastfile = File('$projectPath/ios/fastlane/Fastfile');
final rootFastfile = File('$projectPath/fastlane/Fastfile');
final hasFastfile = await androidFastfile.exists() ||
await iosFastfile.exists() ||
await rootFastfile.exists();
checks.add(CIDoctorCheck(
category: 'Deployment',
title: 'Fastlane Configuration',
status: hasFastfile ? 'Configured' : 'Warning',
details:
hasFastfile ? 'Fastfile detected' : 'No Fastfile found in project',
));
// 4. Shorebird Check
final shorebirdAvailable = await _shorebirdClient.isShorebirdAvailable();
checks.add(CIDoctorCheck(
category: 'Shorebird',
title: 'Shorebird CLI Tool',
status: shorebirdAvailable ? 'Configured' : 'Warning',
details: shorebirdAvailable
? 'Shorebird executable found on PATH'
: 'Shorebird not installed (required for OTA patches)',
));
final hasShorebirdYaml = _shorebirdClient.isProjectConfigured(projectPath);
checks.add(CIDoctorCheck(
category: 'Shorebird',
title: 'Shorebird Project Configuration',
status: hasShorebirdYaml ? 'Configured' : 'Warning',
details: hasShorebirdYaml
? 'shorebird.yaml detected'
: 'shorebird.yaml not found',
));
// 5. Secret Configuration Status (Never exposes values!)
final env = ctx.environment;
for (final secretKey in [
'GOOGLE_PLAY_SERVICE_ACCOUNT',
'APP_STORE_CONNECT_API_KEY',
'SHOREBIRD_TOKEN',
'KEYSTORE_PASSWORD',
]) {
final isPresent =
env.containsKey(secretKey) && env[secretKey]!.isNotEmpty;
checks.add(CIDoctorCheck(
category: 'Secrets',
title: 'Secret: $secretKey',
status: isPresent ? 'Configured' : 'Missing',
details: isPresent
? 'Configured in environment'
: 'Not set in current environment (Required in CI GitHub Secrets)',
));
}
final isHealthy = checks.every((c) => c.status != 'Failed');
return CIDoctorReport(checks: checks, isHealthy: isHealthy);
}