Compare commits

...
Author SHA1 Message Date
Stanisław Chmiela 20af814ec1 [build-tools] clarify Google Play app setup errors 2026-10-23 13:49:48 +02:00
3 changed files with 69 additions and 0 deletions

No files matched your search

+5
View File
@@ -0,0 +1,5 @@
---
'@expo/build-tools': patch
---
Explain Google Play failures caused by missing app declarations, a disabled API, or an upload certificate that is not yet active.
@@ -302,6 +302,28 @@ export namespace GooglePlayUtils {
code = 'EAS_GOOGLE_PLAY_DRAFT_APP';
detail =
'This app is still a draft in Play Console. Submit a draft release without a rollout, or complete the app setup in Play Console before submitting a release to testers. Use releaseStatus: "draft" in eas.json, or release_status: "draft" in a workflow.';
} else if (/must declare the use of advertising ID/i.test(message)) {
code = 'EAS_GOOGLE_PLAY_ADVERTISING_ID_DECLARATION_REQUIRED';
detail =
'Google Play requires an advertising ID declaration for this app. Complete the declaration in Play Console so it matches the app and its SDKs, then submit again.';
} else if (
/must let us know whether your app uses any Foreground Service permissions/i.test(message)
) {
code = 'EAS_GOOGLE_PLAY_FOREGROUND_SERVICE_DECLARATION_REQUIRED';
detail =
'Google Play requires a foreground service declaration for this app. Complete the declaration in Play Console for the permissions used by the app, then submit again.';
} else if (
/Google Play Android Developer API has not been used in project .* before or it is disabled/i.test(
message
)
) {
code = 'EAS_GOOGLE_PLAY_API_DISABLED';
detail =
'The Google Play Android Developer API is not enabled for the Google Cloud project used by this service account. Enable the API in that project, wait for the change to take effect, then submit again.';
} else if (/upload certificate.*not yet valid.*recently reset/i.test(message)) {
code = 'EAS_GOOGLE_PLAY_UPLOAD_CERTIFICATE_NOT_YET_VALID';
detail =
'Google Play has not activated the upload certificate after its reset. Check the activation time in Play Console or the reset confirmation, wait until the certificate is active, then submit again. Do not reset the key again to resolve this wait.';
} else if (error.status === 409 || /edit.*(?:expired|invalid|not found)/i.test(message)) {
code = 'EAS_GOOGLE_PLAY_EDIT_INVALID';
detail =
@@ -194,6 +194,48 @@ it.each([
expect((mapped as Error).message).toContain('releaseStatus: "draft" in eas.json');
}
});
it.each([
[
400,
'Your app targets Android 13 (API 33) or above. You must declare the use of advertising ID in Play Console.',
'EAS_GOOGLE_PLAY_ADVERTISING_ID_DECLARATION_REQUIRED',
'advertising ID declaration',
],
[
403,
'You must let us know whether your app uses any Foreground Service permissions.',
'EAS_GOOGLE_PLAY_FOREGROUND_SERVICE_DECLARATION_REQUIRED',
'foreground service declaration',
],
[
403,
'Google Play Android Developer API has not been used in project 123 before or it is disabled. Enable it by visiting https://console.developers.google.com/',
'EAS_GOOGLE_PLAY_API_DISABLED',
'Google Cloud project',
],
[
403,
'The upload certificate this APK is signed with is not yet valid because it has been recently reset.',
'EAS_GOOGLE_PLAY_UPLOAD_CERTIFICATE_NOT_YET_VALID',
'activation time',
],
] as const)('gives a specific app-setup remedy for %s %s', (status, message, code, remedy) => {
const original = new GooglePlayApiError(status, message, [], {
requestPath: '/example',
responseBody: 'diagnostic',
});
const mapped = GooglePlayUtils.mapGooglePlayError(original, packageName);
expect(mapped).toMatchObject({
errorCode: code,
cause: original,
metadata: { googlePlayRequestPath: '/example', googlePlayErrorResponse: 'diagnostic' },
});
expect((mapped as Error).message).toContain(remedy);
expect((mapped as Error).message).toContain(`App: ${packageName}`);
expect((mapped as Error).message).toContain('https://play.google.com/console/');
expect((mapped as Error).message).not.toContain('Check the track');
});
it('maps a rejected OAuth key to a credential error', () => {
const original = new GooglePlayAuthRequestError(400, 'invalid_grant');
expect(GooglePlayUtils.mapGooglePlayError(original, packageName)).toMatchObject({