-
Notifications
You must be signed in to change notification settings - Fork 0
feat(firebase): Add Firebase configuration detection and validation #5
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
- Detect Firebase credential files (google-services.json, GoogleService-Info.plist) - Support XML and JSON plist formats for iOS configuration - Validate credentials against Zod schemas - Interactive wizard for Firebase configuration management - OAuth 2.0 with PKCE support for downloading configs from Firebase Console - Support React Native, Flutter, iOS, and Android platforms Key features: - Automatic detection of platform and expected credential file locations - Comprehensive validation with detailed error messages - Project ID verification across platforms - Integration with existing Clix CLI command system Co-Authored-By: Claude <noreply@anthropic.com>
|
Note Other AI code review bot(s) detectedCodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review. WalkthroughAdds a complete Firebase configuration feature set: CLI command and Ink UI for an interactive Firebase wizard, OAuth-based authentication, Firebase Management API client, credential detection/validation for Android and iOS, download utilities, error types, schemas/validators, and documentation updates. Changes
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: a0c56059c0
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| // Also search for app-specific plist locations in iOS directory | ||
| const iosDir = path.join(projectPath, 'ios'); | ||
| try { | ||
| const entries = await fs.readdir(iosDir, { withFileTypes: true }); | ||
| for (const entry of entries) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Expand iOS plist search beyond ios/ subdir
The iOS detector only looks in IOS_SEARCH_PATHS and then scans projectPath/ios/*/GoogleService-Info.plist. Native iOS projects commonly store the file inside the app target directory at the repo root (e.g., MyApp/GoogleService-Info.plist next to the .xcodeproj), so this function will report “not found” even when a valid plist exists. That means /firebase will incorrectly flag missing credentials and recommend a download for standard native iOS layouts. Consider also searching top-level app directories (e.g., siblings of .xcodeproj) or a broader root-level glob for */GoogleService-Info.plist when the detected platform is ios.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 9
🤖 Fix all issues with AI agents
In `@llms.txt`:
- Around line 229-237: The documentation added a new system command `/firebase`,
increasing the system commands from 9 to 10, so update the earlier slash-command
totals to read "3 autonomous + 5 skills + 10 system = 18 total" (or update any
"total slash-commands" summary) to avoid inconsistency; locate the earlier
totals/summary text that lists the counts (look for phrases like "autonomous",
"skills", "system", or "total slash-commands") and change the numbers and the
computed total to 18 wherever they appear.
In `@src/lib/services/firebase/detector.ts`:
- Around line 327-382: The detector currently treats any file flagged as
"inExpectedLocation" by findGoogleServicesJson but that helper marks expected
locations globally; update detectAndroidCredential to prefer and label files
using the platform-specific expectedPaths: when selecting expectedFile, find a
file whose path matches any entry in expectedPaths (compare normalized relative
paths or compare absolutePath to path.join(projectPath, expectedPath)), set
inExpectedLocation based on that match, and set expectedPath only when the
chosen file is not inExpectedLocation (use the first matching expectedPaths
value if applicable); apply the same change to the iOS detector
(detectIosCredential / findGoogleServiceInfoPlist) so validation and guidance
use the platform-specific expectedPaths.
- Around line 149-176: The function detectPlatform currently returns
'react-native' when detectNativePlatforms finds both hasIos and hasAndroid even
if isReactNativeProject/isFlutterProject were false; change the logic in
detectPlatform so that when hasIos && hasAndroid and neither
isReactNativeProject(projectPath) nor isFlutterProject(projectPath) are true, it
returns 'unknown' instead of 'react-native' to avoid misclassifying purely
native monorepos; update the hasIos && hasAndroid branch in detectPlatform (and
any related comments) to implement this conditional return and add/update tests
that assert dual-native-but-not-cross-platform repos yield 'unknown'.
In `@src/lib/services/firebase/oauth/auth-client.ts`:
- Around line 96-167: The waitForCallback Promise currently starts a server
bound to all interfaces and never clears the timeout; change server.listen(...)
to bind explicitly to '127.0.0.1' using GOOGLE_OAUTH_CONFIG.callbackPort, store
the setTimeout return value in a variable (e.g., timeoutHandle), and add a small
cleanup helper (e.g., cleanup = () => { clearTimeout(timeoutHandle); try {
server.close(); } catch {} }) that you call before every resolve and reject
(inside the OAuth success branch where resolve({ code, state }) is called, in
the error and missing-code branches before reject, in the server.on('error')
handler, and in the timeout callback) so the server is closed and the timeout
cleared on all code paths.
- Around line 34-89: generate and store a cryptographically-random OAuth state
string when building the URL in generateAuthUrl() (add a private this.state:
string | null), include that state in the client.generateAuthUrl(...)
parameters, and in waitForCallback() validate that the incoming state equals the
stored this.state before accepting the code; if missing or mismatched,
throw/reject and clear the stored state. Ensure state is cleared after
successful or failed validation to avoid reuse.
In `@src/lib/services/firebase/oauth/token-store.ts`:
- Around line 45-49: The save method writes OAuth tokens with default permissive
filesystem bits; change mkdir and file writes to enforce restrictive
permissions: create the directory with mode 0o700 (use fs.mkdir(..., {
recursive: true, mode: 0o700 })) and write the token file with mode 0o600 (use
fs.writeFile with the mode option or follow up with fs.chmod(this.tokenPath,
0o600)). Update the save function (in token-store.ts, method save) to ensure the
directory and file permissions are set atomically/explicitly so the token JSON
is only readable by the owner.
In `@src/lib/services/firebase/schemas.ts`:
- Around line 12-17: The AndroidClientInfoSchema currently allows uppercase
letters due to the case-insensitive regex flag; update the schema's regex in
AndroidClientInfoSchema by removing the `i` flag so the pattern becomes strictly
lowercase (use `/^[a-z][a-z0-9_]*(\.[a-z][a-z0-9_]*)+$/`) to enforce lowercase
package names while keeping the existing min(1) and error message intact.
In `@src/ui/components/FirebaseWizard.tsx`:
- Around line 828-833: In handleAction (the async callback handling
CredentialAction), await the async call to handleDownload so the promise is
awaited and potential rejections are propagated/handled; update the 'download'
case to use await handleDownload() (keeping the surrounding async handleAction)
and ensure any needed try/catch around the switch remains or is added to surface
errors from handleDownload.
- Around line 149-162: The openBrowser function is vulnerable to shell injection
and mishandles Windows arguments while ignoring exec errors; replace the
string-interpolated exec call with safe child_process APIs (e.g., spawn or
execFile) and pass the URL as an argument array instead of interpolating into a
shell command, handle Windows by invoking cmd /c start with an explicit empty
title (e.g., ['cmd', ['/c','start','', url]] or equivalent) or call
spawn('start', ['', url], {shell:true}) carefully, and attach an error
callback/listener to surface failures (or log/throw the error) so exec/spawn
errors are not silently ignored; update references in openBrowser and any call
sites accordingly.
🧹 Nitpick comments (13)
src/lib/services/firebase/oauth/types.ts (1)
12-18: Redundant field declarations in extended interface.
OAuthTokensextendsCredentialsfromgoogle-auth-library, which already declaresaccess_token,refresh_token,token_type, andexpiry_date. Re-declaring them here is redundant but documents the expected shape explicitly. Consider removing the duplicates or adding a comment explaining the intent.src/lib/services/firebase/api/types.ts (1)
31-35: Consider adding'STATE_UNSPECIFIED'to project state union.The Firebase Management API can return
STATE_UNSPECIFIEDin addition toACTIVEandDELETED. Adding it would make the type more robust against unexpected API responses.Suggested change
/** * Project state. */ - state: 'ACTIVE' | 'DELETED'; + state: 'STATE_UNSPECIFIED' | 'ACTIVE' | 'DELETED';src/ui/components/FirebaseStatusDisplay.tsx (3)
99-109: Potential non-unique React key if errors have duplicate path+message.The key
${error.path}-${error.message}could collide if the same error appears multiple times. Consider appending the index for guaranteed uniqueness.Suggested fix
- {credential.errors.slice(0, 3).map((error) => ( - <Text key={`${error.path}-${error.message}`} color="red"> + {credential.errors.slice(0, 3).map((error, index) => ( + <Text key={`${error.path}-${error.message}-${index}`} color="red">
154-166: Consider extracting platform-needs helpers to a shared utility.
platformNeedsAndroidandplatformNeedsIosduplicate logic fromdownloader.ts(lines 179-181). Extract to a shared utility in the firebase module to maintain consistency.
180-187:getStatusColorreturns 'red' for both missing and n/a statuses.Currently
n/aand✗both render as red. Consider using a neutral color (e.g.,dimColororgray) forn/ato differentiate "not applicable" from "missing/error".Suggested fix
function getStatusColor(status: string): string { if (status === '✓') return 'green'; if (status === '!') return 'yellow'; + if (status === 'n/a') return 'gray'; return 'red'; }src/lib/services/firebase/downloader.ts (1)
145-168: Consider adding error handling for file write operations.
downloadAndroidConfiganddownloadIosConfigdon't catch errors fromfs.mkdirorfs.writeFile. If the directory creation or file write fails (e.g., permission denied), the error propagates uncaught. Consider wrapping in try-catch with meaningful error messages.src/ui/components/FirebaseWizard.tsx (1)
656-706: Consider simplifying callback dependency management.The ref-based pattern to avoid circular dependencies works but adds complexity. Consider extracting the download flow into a custom hook or using a reducer pattern for cleaner state management.
src/lib/services/firebase/firebase-service.ts (1)
64-71: Consider extracting platform requirements logic to a shared utility.The
needsAndroid/needsIoscomputation is duplicated across multiple files (here, inFirebaseWizard.tsx, and indetector.ts). Extract this to a shared helper function to ensure consistency.♻️ Example shared utility
// In types.ts or a new utils.ts export function getPlatformRequirements(platform: Platform): { needsAndroid: boolean; needsIos: boolean } { const needsAndroid = platform === 'android' || platform === 'react-native' || platform === 'flutter'; const needsIos = platform === 'ios' || platform === 'react-native' || platform === 'flutter'; return { needsAndroid, needsIos }; }src/lib/services/firebase/api/firebase-api.ts (2)
39-56: Consider adding request timeout.The
fetchcalls have no timeout configuration. Long-running or hung requests could block the CLI indefinitely. Consider usingAbortControllerwith a timeout.♻️ Example with timeout
private async request<T>(path: string): Promise<T> { const token = await this.getAccessToken(); const url = `${BASE_URL}${path}`; + const controller = new AbortController(); + const timeoutId = setTimeout(() => controller.abort(), 30000); - const response = await fetch(url, { - headers: { - Authorization: `Bearer ${token}`, - 'Content-Type': 'application/json', - }, - }); + try { + const response = await fetch(url, { + headers: { + Authorization: `Bearer ${token}`, + 'Content-Type': 'application/json', + }, + signal: controller.signal, + }); if (!response.ok) { const error = await response.text(); throw new Error(`Firebase API error (${response.status}): ${error}`); } return response.json() as Promise<T>; + } finally { + clearTimeout(timeoutId); + } }
103-106: Consider URL-encoding path parameters defensively.While Firebase project IDs and app IDs typically don't contain special characters, encoding them with
encodeURIComponentwould be a defensive measure against edge cases.src/lib/services/firebase/types.ts (2)
86-90: Naming conflict with existingValidationErrorclass.This
ValidationErrorinterface shadows theValidationErrorclass insrc/lib/errors/types.ts. Consider renaming toFirebaseValidationErrororCredentialValidationErrorto avoid confusion when both are imported.
104-125: Consider documenting thevalid/contentinvariant.The
contentfield is optional, but whenvalidis true,contentshould always be defined. This invariant is relied upon infirebase-service.ts. Consider adding a JSDoc comment to document this relationship.📝 Documentation suggestion
export interface FirebaseCredentialFile { /** File path relative to project root */ path: string; /** Absolute file path */ absolutePath: string; /** Target platform */ platform: 'android' | 'ios'; /** Credential file type */ type: CredentialFileType; /** Whether file exists */ exists: boolean; /** Whether file is valid */ valid: boolean; /** Validation errors if any */ errors: ValidationError[]; - /** Parsed content if valid */ + /** Parsed content if valid. Guaranteed to be defined when `valid` is true. */ content?: GoogleServicesJson | GoogleServiceInfoPlist; /** Whether file is in expected location */ inExpectedLocation: boolean; /** Expected location if different from actual */ expectedPath?: string; }src/lib/services/firebase/detector.ts (1)
607-623: Reuse the existingneedsAndroidConfig/needsIosConfighelpers.Avoids duplicating platform logic and keeps behavior consistent.
♻️ Suggested refactor
- const needsAndroid = - platform === 'android' || platform === 'react-native' || platform === 'flutter'; - const needsIos = platform === 'ios' || platform === 'react-native' || platform === 'flutter'; + const needsAndroid = needsAndroidConfig(platform); + const needsIos = needsIosConfig(platform);
| function openBrowser(url: string): void { | ||
| const platform = process.platform; | ||
|
|
||
| let command: string; | ||
| if (platform === 'darwin') { | ||
| command = `open "${url}"`; | ||
| } else if (platform === 'win32') { | ||
| command = `start "${url}"`; | ||
| } else { | ||
| command = `xdg-open "${url}"`; | ||
| } | ||
|
|
||
| exec(command); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shell command injection risk and missing error handling in openBrowser.
The URL is interpolated directly into shell commands without proper escaping. On Windows, start interprets the first quoted argument as a window title, causing the URL to be mishandled. Additionally, exec errors are silently ignored.
🔒 Proposed fix
-function openBrowser(url: string): void {
- const platform = process.platform;
-
- let command: string;
- if (platform === 'darwin') {
- command = `open "${url}"`;
- } else if (platform === 'win32') {
- command = `start "${url}"`;
- } else {
- command = `xdg-open "${url}"`;
- }
-
- exec(command);
-}
+function openBrowser(url: string): void {
+ const platform = process.platform;
+
+ let command: string;
+ let args: string[];
+ if (platform === 'darwin') {
+ command = 'open';
+ args = [url];
+ } else if (platform === 'win32') {
+ command = 'cmd';
+ args = ['/c', 'start', '""', url];
+ } else {
+ command = 'xdg-open';
+ args = [url];
+ }
+
+ const { spawn } = require('node:child_process');
+ spawn(command, args, { detached: true, stdio: 'ignore' }).unref();
+}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| function openBrowser(url: string): void { | |
| const platform = process.platform; | |
| let command: string; | |
| if (platform === 'darwin') { | |
| command = `open "${url}"`; | |
| } else if (platform === 'win32') { | |
| command = `start "${url}"`; | |
| } else { | |
| command = `xdg-open "${url}"`; | |
| } | |
| exec(command); | |
| } | |
| function openBrowser(url: string): void { | |
| const platform = process.platform; | |
| let command: string; | |
| let args: string[]; | |
| if (platform === 'darwin') { | |
| command = 'open'; | |
| args = [url]; | |
| } else if (platform === 'win32') { | |
| command = 'cmd'; | |
| args = ['/c', 'start', '""', url]; | |
| } else { | |
| command = 'xdg-open'; | |
| args = [url]; | |
| } | |
| const { spawn } = require('node:child_process'); | |
| spawn(command, args, { detached: true, stdio: 'ignore' }).unref(); | |
| } |
🤖 Prompt for AI Agents
In `@src/ui/components/FirebaseWizard.tsx` around lines 149 - 162, The openBrowser
function is vulnerable to shell injection and mishandles Windows arguments while
ignoring exec errors; replace the string-interpolated exec call with safe
child_process APIs (e.g., spawn or execFile) and pass the URL as an argument
array instead of interpolating into a shell command, handle Windows by invoking
cmd /c start with an explicit empty title (e.g., ['cmd', ['/c','start','', url]]
or equivalent) or call spawn('start', ['', url], {shell:true}) carefully, and
attach an error callback/listener to surface failures (or log/throw the error)
so exec/spawn errors are not silently ignored; update references in openBrowser
and any call sites accordingly.
- Add CSRF protection via OAuth state parameter validation - Bind callback server to localhost only for security - Add proper timeout cleanup in OAuth flow - Harden token file permissions (0o600 for file, 0o700 for dir) - Use spawn instead of exec for shell injection prevention - Add missing await for handleDownload - Return 'unknown' for dual-platform detection - Expand iOS plist search to include root-level directories - Use platform-specific expectedPaths for credential detection - Enforce lowercase for Android package names in schema - Update llms.txt slash-command count (17→18) Co-Authored-By: Claude <noreply@anthropic.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In `@src/lib/services/firebase/detector.ts`:
- Around line 632-648: detectFirebaseConfig currently treats an 'unknown'
platform as not needing Android or iOS, causing configured to be true when both
configs are missing; update detectFirebaseConfig to call
detectNativePlatforms(projectPath) when detectPlatform returns 'unknown' and use
its result to set needsAndroid and needsIos (instead of defaulting to false), so
the boolean flags reflect actual native platform presence before computing
androidConfigured, iosConfigured and configured; reference detectFirebaseConfig,
detectPlatform, detectNativePlatforms, needsAndroid, needsIos and
getExpectedPaths when making the change.
♻️ Duplicate comments (2)
src/lib/services/firebase/oauth/token-store.ts (1)
45-53: Ensure existing token files are re-permissioned too.
fs.writeFile(..., { mode })only sets permissions on newly created files; if a token file already exists from an older version, it can keep permissive bits. Considerchmodafter write (and optionally on the directory) to guarantee owner-only access on upgrades.🔐 Suggested hardening
await fs.mkdir(dir, { recursive: true, mode: 0o700 }); // Write token file with restricted permissions (owner read/write only) await fs.writeFile(this.tokenPath, JSON.stringify(tokens, null, 2), { encoding: 'utf-8', mode: 0o600, }); + // Ensure existing paths are tightened as well + await fs.chmod(dir, 0o700); + await fs.chmod(this.tokenPath, 0o600);src/ui/components/FirebaseWizard.tsx (1)
150-169: Handlespawnerrors to avoid CLI crashes when browser open fails.If
open/xdg-open/cmdisn’t available,spawnemits anerrorevent; without a handler, Node can throw. Add an error listener (and ideally surface a user-facing message).🛠 Minimal safeguard
- spawn(command, args, { detached: true, stdio: 'ignore' }).unref(); + const child = spawn(command, args, { detached: true, stdio: 'ignore' }); + child.on('error', (err) => { + // eslint-disable-next-line no-console + console.error('Failed to open browser:', err.message); + }); + child.unref();
| export async function detectFirebaseConfig(projectPath: string): Promise<FirebaseDetectionResult> { | ||
| const platform = await detectPlatform(projectPath); | ||
| const expectedPaths = getExpectedPaths(platform, projectPath); | ||
|
|
||
| const android = await detectAndroidCredential(projectPath, expectedPaths.android); | ||
| const ios = await detectIosCredential(projectPath, expectedPaths.ios); | ||
|
|
||
| const issues = generateIssues(android, ios, platform, expectedPaths); | ||
|
|
||
| // Determine if Firebase is configured | ||
| const needsAndroid = | ||
| platform === 'android' || platform === 'react-native' || platform === 'flutter'; | ||
| const needsIos = platform === 'ios' || platform === 'react-native' || platform === 'flutter'; | ||
|
|
||
| const androidConfigured = !needsAndroid || (android?.valid ?? false); | ||
| const iosConfigured = !needsIos || (ios?.valid ?? false); | ||
| const configured = androidConfigured && iosConfigured; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
cat -n src/lib/services/firebase/detector.ts | head -100Repository: clix-so/clix-cli
Length of output: 3392
🏁 Script executed:
wc -l src/lib/services/firebase/detector.tsRepository: clix-so/clix-cli
Length of output: 101
🏁 Script executed:
sed -n '620,660p' src/lib/services/firebase/detector.tsRepository: clix-so/clix-cli
Length of output: 1306
🏁 Script executed:
# Search for detectPlatform implementation
rg -A 10 "function detectPlatform|const detectPlatform" src/lib/services/firebase/Repository: clix-so/clix-cli
Length of output: 778
🏁 Script executed:
# Search for detectNativePlatforms to see if it exists
rg "detectNativePlatforms" .Repository: clix-so/clix-cli
Length of output: 254
🏁 Script executed:
# Look at the platform detection logic and how 'unknown' is returned
rg -B 5 -A 5 "unknown" src/lib/services/firebase/detector.tsRepository: clix-so/clix-cli
Length of output: 1415
🏁 Script executed:
sed -n '110,160p' src/lib/services/firebase/detector.tsRepository: clix-so/clix-cli
Length of output: 1325
🏁 Script executed:
# Get the full detectNativePlatforms function
rg -A 30 "async function detectNativePlatforms" src/lib/services/firebase/detector.tsRepository: clix-so/clix-cli
Length of output: 886
🏁 Script executed:
# Get full detectPlatform function to see how detectNativePlatforms is used
rg -A 50 "export async function detectPlatform" src/lib/services/firebase/detector.tsRepository: clix-so/clix-cli
Length of output: 1469
unknown platform incorrectly reports configured: true when Firebase configs are missing.
For dual-native projects detected as 'unknown' platform, the logic treats both needsAndroid and needsIos as false, causing androidConfigured and iosConfigured to always be true regardless of whether configs exist. This means missing Firebase credentials go undetected in dual-native setups.
Since detectPlatform already calls detectNativePlatforms() to determine this case, use the same method in detectFirebaseConfig to set the actual needs when platform === 'unknown':
Suggested fix
export async function detectFirebaseConfig(projectPath: string): Promise<FirebaseDetectionResult> {
const platform = await detectPlatform(projectPath);
+ const native =
+ platform === 'unknown'
+ ? await detectNativePlatforms(projectPath)
+ : { hasIos: false, hasAndroid: false };
const expectedPaths = getExpectedPaths(platform, projectPath);
const android = await detectAndroidCredential(projectPath, expectedPaths.android);
const ios = await detectIosCredential(projectPath, expectedPaths.ios);
const issues = generateIssues(android, ios, platform, expectedPaths);
// Determine if Firebase is configured
- const needsAndroid =
- platform === 'android' || platform === 'react-native' || platform === 'flutter';
- const needsIos = platform === 'ios' || platform === 'react-native' || platform === 'flutter';
+ const needsAndroid =
+ platform === 'android' ||
+ platform === 'react-native' ||
+ platform === 'flutter' ||
+ (platform === 'unknown' && native.hasAndroid);
+ const needsIos =
+ platform === 'ios' ||
+ platform === 'react-native' ||
+ platform === 'flutter' ||
+ (platform === 'unknown' && native.hasIos);🤖 Prompt for AI Agents
In `@src/lib/services/firebase/detector.ts` around lines 632 - 648,
detectFirebaseConfig currently treats an 'unknown' platform as not needing
Android or iOS, causing configured to be true when both configs are missing;
update detectFirebaseConfig to call detectNativePlatforms(projectPath) when
detectPlatform returns 'unknown' and use its result to set needsAndroid and
needsIos (instead of defaulting to false), so the boolean flags reflect actual
native platform presence before computing androidConfigured, iosConfigured and
configured; reference detectFirebaseConfig, detectPlatform,
detectNativePlatforms, needsAndroid, needsIos and getExpectedPaths when making
the change.
Summary
Add comprehensive Firebase configuration detection and validation system. Users can now automatically detect Firebase credential files (google-services.json for Android, GoogleService-Info.plist for iOS), validate them against Firebase schemas, and optionally download configs directly from Firebase Console using OAuth 2.0 authentication.
Details
Core Components:
detector.ts: Automatically detects Firebase credential files and their locationsvalidator.ts: Validates credentials against Zod schemas with detailed error messagesfirebase-service.ts: Main service orchestrating detection, validation, and recommendationsFirebaseWizard.tsx: Interactive UI for configuration managementKey Features:
/firebasecommand for interactive configuration workflowHow to Validate
bun run devto start interactive mode/firebaseto open the Firebase configuration wizardPre-Merge Checklist
Code Quality
bun run build)bun run typecheck)bun run lint)bun test- 529 tests)Documentation
/firebasecommanddoctorandinstallCommit Standards
🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
Documentation
✏️ Tip: You can customize this high-level summary in your review settings.