Skip to content

feat(cli): Android onboarding - verify app exists in Play Store (apps:search + Trapeze rename)#2443

Draft
WcaleNieWolny wants to merge 17 commits into
mainfrom
wolny/android-onboarding-app-listing
Draft

feat(cli): Android onboarding - verify app exists in Play Store (apps:search + Trapeze rename)#2443
WcaleNieWolny wants to merge 17 commits into
mainfrom
wolny/android-onboarding-app-listing

Conversation

@WcaleNieWolny

Copy link
Copy Markdown
Contributor

Summary

Adds an Android app-existence check to the CLI onboarding flow. When a user selects an Android package, the CLI now verifies whether the app already exists in the Play Store and reconciles the local config against what Google reports.

  • Queries the Play Developer Reporting API (apps:search, OAuth-only) to list the developer's apps.
  • Reconciles the selected package name against the returned apps, with a picker when multiple candidates exist and graceful degradation when the required scope is missing.
  • Path A (app exists): offers an explicit, opt-in Trapeze rename of PRODUCT_BUNDLE_IDENTIFIER / package id (never a silent build.gradle rewrite).
  • Path B (app does not exist): guides create-app and surfaces telemetry; app-record creation remains the one UI-only step.
  • Pure modules (reporting apps:search, reconcile, rename helpers) are extracted with unit tests.

Docs

Verification status

ALL GREEN (typecheck + lint + build + tests pass).

Deferred / follow-ups

  • D1 — PREPROD scope wiring: add the playdeveloperreporting scope to the PREPROD /private/config/builder scopes[] and test via CAPGO_BUILDER_CONFIG_URL. Prod degrades gracefully until the scope is added.
  • D2 — Draft-app visibility probe: verify whether apps:search returns Draft (not-yet-published) apps; this is currently UNVERIFIED.

There are 6 remaining non-blocking review findings (none blocking merge).

…loper Reporting API (apps:search, OAuth-only)
…pload is documented; apps:search Draft-app visibility is UNVERIFIED
…s (fastlane/terraform/gradle-play-publisher); Path B is inform-only
…HIGH); first upload likely API-able as draft (fastlane #18293, MEDIUM)
…e_status to draft (fastlaneTemplateAndroid.ts:447)
@coderabbitai

coderabbitai Bot commented Jun 6, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 091537b2-c977-487d-933c-732170709f66

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Comment @coderabbitai help to get the list of available commands and usage tips.

@socket-security

socket-security Bot commented Jun 6, 2026

Copy link
Copy Markdown

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Addedmicromatch@​4.0.810010010083100

View full report

@socket-security

socket-security Bot commented Jun 6, 2026

Copy link
Copy Markdown

All alerts resolved. Learn more about Socket for GitHub.

This PR previously contained dependency changes with security issues that have been resolved, removed, or ignored.

View full report

assert.equal(calls.length, 1)
// Bearer auth + the pageSize query are wired correctly.
assert.equal(calls[0].init.headers.Authorization, 'Bearer tok')
assert.ok(calls[0].url.includes('playdeveloperreporting.googleapis.com'))
@codspeed-hq

codspeed-hq Bot commented Jun 6, 2026

Copy link
Copy Markdown
Contributor

Merging this PR will not alter performance

✅ 43 untouched benchmarks
⏩ 2 skipped benchmarks1


Comparing wolny/android-onboarding-app-listing (0a875c6) with main (b9b7aaf)

Open in CodSpeed

Footnotes

  1. 2 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

- parseAppsSearchResponse: drop rows missing packageName (the reconcile join
  key) so a malformed Play row can never spuriously exact-match an empty
  Gradle id; reconcileAndroidApp gets a matching empty-id guard
- add verifyAsyncEpochRef generation counter: render-scope async ops
  (startRename/doRename/recheckBuildId/recheckCreateApp/openCreatePlayConsole)
  bail before state writes when the step leaves the verify family or the
  component unmounts (they can't see the step effect's cancelled flag)
- await-studio-confirm: fail honestly if the rename workspace ref was lost
  instead of running node rename.mjs against ''
- reset packageLoadedRef on step leave so Path A Back / Path B routes
  re-detect Gradle ids + re-fetch Play apps (a failed rename may have
  changed build.gradle); 'Continue anyway' pins the plain picker so it
  can't bounce back to create-app
- doRename/recheckBuildId read Play apps via verifyPlayAppsRef (stale
  closure snapshot during long npm-install/cap-sync)
- tests: parser drop rule + empty-id reconcile edges
Google's consent page words playdeveloperreporting as 'see metrics and
data about the apps in your Google Play Developer account' — unexplained,
that reads like analytics snooping. Add a third bullet to the sign-in
screen (it lists 'the two access requests' no more) and a matching
learn-more Q&A; tighten the other learn-more answers to stay within the
80x49 Android min-size floor (was already at exactly 49 rows).
…list scope was skipped

The loopback success page now lists declined OPTIONAL scopes with a human
blurb (decoding Google's 'see metrics and data' wording) and what degrades
— right where the decline just happened. Required-scope misses keep the
existing blocking scopeMissingHtml/MissingScopesError path. New exported
splitMissingScopes(granted, scopes, requiredScopes) does the split (+
tests); the CLI status stream also gets a one-line note so the later
'verification skipped' warning isn't a surprise.
…package that isn't a real Play app

The SA invite grants per-package (developers/{id}/users grants[].packageName)
and Google 400s INVALID_ARGUMENT on a package that doesn't exist, so picking a
build id that apps:search doesn't list was a guaranteed failure. Close every
leak on the verified generate path:
- enriched picker: gradle-id options annotated with Play status (✓ in Play /
  ⚠ not on Play yet); picking one not in apps:search routes to create-app
  instead of advancing into a doomed provision
- manual entry: a typed package not in apps:search routes to create-app
  (import/degraded path unchanged — it has no apps list + already warns)
- Path B create-app: removed 'Continue anyway' (both variants) — on the
  generate path you genuinely cannot proceed until the app exists; it's now a
  real gate (create → re-check → auto-advance), with Back to pick a different app
- safety net: the gcp-setup SA-invite catch turns Google's raw 'packages are
  not available' 400 into an actionable 'create the app first' message for the
  import/degraded path

result: picking a non-existent build id can no longer 400 the SA invite.
@sonarqubecloud

sonarqubecloud Bot commented Jun 8, 2026

Copy link
Copy Markdown

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants