@gpc-cli/core 0.9.55 → 0.9.56
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +57 -1
- package/dist/index.js +325 -0
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/dist/index.d.ts
CHANGED
|
@@ -1472,4 +1472,60 @@ declare function decodeNotification(base64Payload: string): DecodedNotification;
|
|
|
1472
1472
|
*/
|
|
1473
1473
|
declare function formatNotification(notification: DecodedNotification): Record<string, unknown>;
|
|
1474
1474
|
|
|
1475
|
-
|
|
1475
|
+
interface KeystoreFingerprint {
|
|
1476
|
+
sha256: string;
|
|
1477
|
+
alias: string;
|
|
1478
|
+
keystorePath: string;
|
|
1479
|
+
}
|
|
1480
|
+
interface ApiSigningFingerprint {
|
|
1481
|
+
sha256: string;
|
|
1482
|
+
versionCode: number;
|
|
1483
|
+
}
|
|
1484
|
+
interface SigningKeyComparison {
|
|
1485
|
+
local?: KeystoreFingerprint;
|
|
1486
|
+
api?: ApiSigningFingerprint;
|
|
1487
|
+
match: boolean | null;
|
|
1488
|
+
}
|
|
1489
|
+
declare function normalizeFingerprint(fp: string): string;
|
|
1490
|
+
declare function parseKeytoolOutput(stdout: string): {
|
|
1491
|
+
sha256: string;
|
|
1492
|
+
alias: string;
|
|
1493
|
+
};
|
|
1494
|
+
declare function getKeystoreFingerprint(keystorePath: string, storePassword: string, keyAlias?: string): Promise<KeystoreFingerprint>;
|
|
1495
|
+
declare function getApiSigningFingerprint(accessToken: string, packageName: string, apiHost?: string): Promise<ApiSigningFingerprint | null>;
|
|
1496
|
+
declare function compareFingerprints(a: string, b: string): boolean;
|
|
1497
|
+
|
|
1498
|
+
interface SigningConsistencyResult {
|
|
1499
|
+
currentVersionCode: number;
|
|
1500
|
+
currentFingerprint: string;
|
|
1501
|
+
previousVersionCode?: number;
|
|
1502
|
+
previousFingerprint?: string;
|
|
1503
|
+
consistent: boolean;
|
|
1504
|
+
firstRelease: boolean;
|
|
1505
|
+
}
|
|
1506
|
+
declare function checkSigningConsistency(accessToken: string, packageName: string, apiHost?: string): Promise<SigningConsistencyResult>;
|
|
1507
|
+
|
|
1508
|
+
interface ChecklistItem {
|
|
1509
|
+
id: string;
|
|
1510
|
+
title: string;
|
|
1511
|
+
status: "done" | "action-needed" | "cannot-detect";
|
|
1512
|
+
detail?: string;
|
|
1513
|
+
actionUrl?: string;
|
|
1514
|
+
}
|
|
1515
|
+
interface ChecklistResult {
|
|
1516
|
+
items: ChecklistItem[];
|
|
1517
|
+
completed: number;
|
|
1518
|
+
total: number;
|
|
1519
|
+
}
|
|
1520
|
+
interface ChecklistInput {
|
|
1521
|
+
authenticated: boolean;
|
|
1522
|
+
accountEmail?: string;
|
|
1523
|
+
appAccessible?: boolean;
|
|
1524
|
+
bundleCount?: number;
|
|
1525
|
+
hasGeneratedApks?: boolean;
|
|
1526
|
+
interactiveAnswers?: Record<string, boolean>;
|
|
1527
|
+
}
|
|
1528
|
+
declare function buildChecklist(input: ChecklistInput): ChecklistResult;
|
|
1529
|
+
declare function renderChecklistMarkdown(result: ChecklistResult, accountEmail: string): string;
|
|
1530
|
+
|
|
1531
|
+
export { ApiError, type ApiSigningFingerprint, type AppInfo, type AppStatus, type ApplyReleaseNotesResult, type AuditEntry, type BatchSyncResult, type BundleAnalysis, type BundleComparison, type BundleEntry, type BundleSizeCheckResult, type BundleSizeConfig, type ChangelogEntry, type ChecklistInput, type ChecklistItem, type ChecklistResult, type CommandContext, type CommitCluster, ConfigError, type CreateEnterpriseAppParams, DEFAULT_LIMITS, DEFAULT_MODELS, DEFAULT_PREFLIGHT_CONFIG, type DecodedNotification, type DiffToken, type DiscoverPluginsOptions, type DryRunPublishResult, type DryRunResult, type DryRunUploadResult, type ErrorReason, type ExportImagesOptions, type ExportImagesSummary, type FastlaneDetection, type FastlaneLane, type FetchChangelogOptions, type FieldLintResult, type FileValidationResult, type FindingSeverity, GOOGLE_PLAY_LANGUAGES, type GenerateOptions, type GeneratedChangelog, type GetAppStatusOptions, type GitNotesOptions, type GitReleaseNotes, type GitRunner, GpcError, type ImageValidationResult, type InitOptions, type InitResult, type InternalSharingUploadResult, type KeystoreFingerprint, type ListIapOptions, type ListSubscriptionsOptions, type ListUsersOptions, type ListVoidedOptions, type ListingDiff, type ListingFieldLimits, type ListingLintResult, type ListingsResult, type LoadedPlugin, type LocaleBundle, type LocaleEntry, type MigrationResult, NetworkError, type OneTimeProductDiff, type OutputMode, PERMISSION_PROPAGATION_WARNING, PLACEHOLDER_TEXT, PLAY_STORE_LIMIT, PROVIDER_WHITELIST, type ParsedCommit, type ParsedManifest, type ParsedMonth, type PlayStoreFormat, type PlayStoreRenderOptions, PluginManager, type PreflightConfig, type PreflightFinding, type PreflightOptions, type PreflightResult, type PreflightScanner, type Provider, type PublishOptions, type PublishResult, type PushResult, type QuotaUsage, RENDERERS, type RawCommit, type ReleaseDiff, type ReleaseNotesValidation, type ReleaseStatusResult, type Renderer, type ResolveAiConfigOptions, type ResolveLocalesOptions, type ReviewAnalysis, type ReviewExportOptions, type ReviewsFilterOptions, type RtdnStatus, SECTION_ORDER, SENSITIVE_ARG_KEYS, SENSITIVE_KEYS, SEVERITY_ORDER, type ScaffoldOptions, type ScaffoldResult, type SigningConsistencyResult, type SigningKeyComparison, type Spinner, type StatusDiff, type StatusRelease, type StatusReviews, type StatusVitalMetric, type SubscriptionAnalytics, type SubscriptionDiff, type SyncResult, type ThresholdResult, type TrainConfig, type TrainState, type TranslateBundleOptions, type TranslatedBundle, type TranslationFailure, type TranslationPath, type TranslationResult, type Translator, type TranslatorConfig, type UploadResult, type ValidateCheck, type ValidateOptions, type ValidateResult, type VersionVitalsComparison, type VersionVitalsRow, type VitalsOverview, type VitalsQueryOptions, type VitalsTrendComparison, type WatchOptions, type WatchVitalsOptions, type WebhookPayload, abortTrain, acknowledgeProductPurchase, activateBasePlan, activateOffer, addRecoveryTargeting, addTesters, advanceTrain, analyzeBundle, analyzeRemoteListings, analyzeReviews, applyReleaseNotes, batchGetOrders, batchSyncInAppProducts, buildChecklist, buildLocaleBundle, bundleToReleaseNotes, cancelRecoveryAction, cancelSubscriptionPurchase, cancelSubscriptionV2, checkBundleSize, checkSigningConsistency, checkThreshold, classifyError, clearAuditLog, compareBundles, compareFingerprints, compareVersionVitals, compareVitalsTrend, computeStatusDiff, consumeProductPurchase, convertRegionPrices, createAuditEntry, createDeviceTier, createEnterpriseApp, createExternalTransaction, createGrant, createInAppProduct, createOffer, createOneTimeOffer, createOneTimeProduct, createRecoveryAction, createSpinner, createSubscription, createTrack, createTranslator, deactivateBasePlan, deactivateOffer, decodeNotification, defaultGitRunner, deferSubscriptionPurchase, deferSubscriptionV2, deleteBasePlan, deleteGrant, deleteImage, deleteInAppProduct, deleteListing, deleteOffer, deleteOneTimeOffer, deleteOneTimeProduct, deleteSubscription, deployRecoveryAction, detectFastlane, detectOutputFormat, diffListings, diffListingsCommand, diffListingsEnhanced, diffOneTimeProduct, diffReleases, diffSubscription, discoverPlugins, downloadGeneratedApk, downloadReport, exportImages, exportReviews, fetchAggregateCost, fetchChangelog, fetchReleaseNotes, formatChangelogEntry, formatCustomPayload, formatDiscordPayload, formatJunit, formatNotification, formatOutput, formatPathLabel, formatSlackPayload, formatStatusDiff, formatStatusSummary, formatStatusTable, formatWordDiff, generateChangelog, generateMigrationPlan, generateNotesFromGit, getAllScannerNames, getApiSigningFingerprint, getAppInfo, getAppStatus, getCountryAvailability, getDeviceTier, getExternalTransaction, getInAppProduct, getKeystoreFingerprint, getListings, getOffer, getOneTimeOffer, getOneTimeProduct, getOrderDetails, getProductPurchase, getProductPurchaseV2, getQuotaUsage, getReleasesStatus, getReview, getRtdnStatus, getSubscription, getSubscriptionAnalytics, getSubscriptionPurchase, getTrainStatus, getUser, getVitalsAnomalies, getVitalsAnr, getVitalsBattery, getVitalsCrashes, getVitalsErrorCount, getVitalsLmk, getVitalsMemory, getVitalsOverview, getVitalsRendering, getVitalsStartup, importDataSafety, importTestersFromCsv, initAudit, initProject, inviteUser, isFinancialReportType, isStatsReportType, isValidBcp47, isValidReportType, isValidStatsDimension, lintListing, lintListings, lintLocalListings, listAchievements, listAuditEvents, listDeviceTiers, listEvents, listGeneratedApks, listGrants, listImages, listInAppProducts, listLeaderboards, listOffers, listOneTimeOffers, listOneTimeProducts, listRecoveryActions, listReports, listReviews, listSubscriptions, listTesters, listTracks, listUsers, listVoidedPurchases, loadPreflightConfig, loadStatusCache, maybePaginate, migratePrices, normalizeFingerprint, parseAppfile, parseCommit, parseFastfile, parseGrantArg, parseKeytoolOutput, parseMonth, parseRemoteUrl, pauseTrain, promoteRelease, publish, publishEnterpriseApp, pullListings, pushListings, readListingsFromDir, readReleaseNotesFromDir, redactAuditArgs, redactSensitive, refundExternalTransaction, refundOrder, relativeTime, removeTesters, removeUser, renderChecklistMarkdown, renderJson, renderMarkdown, renderPlayStore, renderPlayStoreJson, renderPlayStoreMd, renderPlayStorePrompt, renderPrompt, replyToReview, resolveAiConfig, resolveLocales, revokeSubscriptionPurchase, runPreflight, runWatchLoop, safePath, safePathWithin, saveStatusCache, scaffoldPlugin, searchAuditEvents, searchVitalsErrors, sendNotification, sendWebhook, sortResults, startTrain, statusHasBreach, syncInAppProducts, topFiles, trackBreachState, translateBundle, updateAppDetails, updateDataSafety, updateGrant, updateInAppProduct, updateListing, updateOffer, updateOneTimeOffer, updateOneTimeProduct, updateRollout, updateSubscription, updateTrackConfig, updateUser, uploadExternallyHosted, uploadImage, uploadInternalSharing, uploadRelease, validateBundleForApply, validateImage, validateLanguageCode, validatePackageName, validatePreSubmission, validateReleaseNotes, validateSku, validateTrackName, validateUploadFile, validateVersionCode, waitForBundleProcessing, watchVitalsWithAutoHalt, wordDiff, writeAuditLog, writeListingsToDir, writeMigrationOutput };
|
package/dist/index.js
CHANGED
|
@@ -8580,6 +8580,323 @@ function formatNotification(notification) {
|
|
|
8580
8580
|
}
|
|
8581
8581
|
return { ...base, type: "unknown" };
|
|
8582
8582
|
}
|
|
8583
|
+
|
|
8584
|
+
// src/signing.ts
|
|
8585
|
+
import { execFile as execFile3 } from "child_process";
|
|
8586
|
+
import { access as access2, constants } from "fs/promises";
|
|
8587
|
+
var SHA256_RE = /SHA-?256\s*:\s*([A-Fa-f0-9]{2}(?::[A-Fa-f0-9]{2}){31})/;
|
|
8588
|
+
var ALIAS_RE = /Alias name:\s*(.+)/i;
|
|
8589
|
+
function normalizeFingerprint(fp) {
|
|
8590
|
+
return fp.replace(/[:\s]/g, "").toLowerCase();
|
|
8591
|
+
}
|
|
8592
|
+
function parseKeytoolOutput(stdout) {
|
|
8593
|
+
const sha256Match = SHA256_RE.exec(stdout);
|
|
8594
|
+
if (!sha256Match?.[1]) {
|
|
8595
|
+
throw new GpcError(
|
|
8596
|
+
"Could not find SHA-256 fingerprint in keytool output",
|
|
8597
|
+
"KEYTOOL_PARSE_ERROR",
|
|
8598
|
+
1,
|
|
8599
|
+
"Ensure the keystore contains a valid certificate entry"
|
|
8600
|
+
);
|
|
8601
|
+
}
|
|
8602
|
+
const aliasMatch = ALIAS_RE.exec(stdout);
|
|
8603
|
+
return {
|
|
8604
|
+
sha256: sha256Match[1].toUpperCase(),
|
|
8605
|
+
alias: aliasMatch?.[1]?.trim() ?? "unknown"
|
|
8606
|
+
};
|
|
8607
|
+
}
|
|
8608
|
+
async function getKeystoreFingerprint(keystorePath, storePassword, keyAlias) {
|
|
8609
|
+
await access2(keystorePath, constants.R_OK).catch(() => {
|
|
8610
|
+
throw new GpcError(
|
|
8611
|
+
`Keystore not found or not readable: ${keystorePath}`,
|
|
8612
|
+
"KEYSTORE_READ_ERROR",
|
|
8613
|
+
1,
|
|
8614
|
+
"Check the path and file permissions"
|
|
8615
|
+
);
|
|
8616
|
+
});
|
|
8617
|
+
const args = ["-list", "-v", "-keystore", keystorePath, "-storepass", storePassword];
|
|
8618
|
+
if (keyAlias) args.push("-alias", keyAlias);
|
|
8619
|
+
const stdout = await new Promise((resolve2, reject) => {
|
|
8620
|
+
execFile3("keytool", args, { timeout: 1e4 }, (err, stdout2, stderr) => {
|
|
8621
|
+
if (err) {
|
|
8622
|
+
const msg = stderr || err.message;
|
|
8623
|
+
if (msg.includes("not found") || err.code === "ENOENT") {
|
|
8624
|
+
reject(
|
|
8625
|
+
new GpcError(
|
|
8626
|
+
"keytool not found",
|
|
8627
|
+
"KEYTOOL_NOT_FOUND",
|
|
8628
|
+
1,
|
|
8629
|
+
"Install a JDK (keytool ships with it) or add it to your PATH"
|
|
8630
|
+
)
|
|
8631
|
+
);
|
|
8632
|
+
return;
|
|
8633
|
+
}
|
|
8634
|
+
if (msg.includes("password was incorrect") || msg.includes("Keystore was tampered")) {
|
|
8635
|
+
reject(
|
|
8636
|
+
new GpcError(
|
|
8637
|
+
"Keystore password is incorrect or the keystore is corrupted",
|
|
8638
|
+
"KEYSTORE_PASSWORD_ERROR",
|
|
8639
|
+
1,
|
|
8640
|
+
"Check --store-pass or GPC_STORE_PASSWORD"
|
|
8641
|
+
)
|
|
8642
|
+
);
|
|
8643
|
+
return;
|
|
8644
|
+
}
|
|
8645
|
+
reject(
|
|
8646
|
+
new GpcError(
|
|
8647
|
+
`keytool failed: ${msg}`,
|
|
8648
|
+
"KEYTOOL_ERROR",
|
|
8649
|
+
1,
|
|
8650
|
+
"Check the keystore path and password"
|
|
8651
|
+
)
|
|
8652
|
+
);
|
|
8653
|
+
return;
|
|
8654
|
+
}
|
|
8655
|
+
resolve2(stdout2);
|
|
8656
|
+
});
|
|
8657
|
+
});
|
|
8658
|
+
const { sha256, alias } = parseKeytoolOutput(stdout);
|
|
8659
|
+
return { sha256, alias, keystorePath };
|
|
8660
|
+
}
|
|
8661
|
+
async function getApiSigningFingerprint(accessToken, packageName, apiHost = "androidpublisher.googleapis.com") {
|
|
8662
|
+
const baseUrl = `https://${apiHost}/androidpublisher/v3/applications/${encodeURIComponent(packageName)}`;
|
|
8663
|
+
const editResp = await fetch(`${baseUrl}/edits`, {
|
|
8664
|
+
method: "POST",
|
|
8665
|
+
headers: { Authorization: `Bearer ${accessToken}`, "Content-Type": "application/json" },
|
|
8666
|
+
body: JSON.stringify({}),
|
|
8667
|
+
signal: AbortSignal.timeout(1e4)
|
|
8668
|
+
});
|
|
8669
|
+
if (!editResp.ok) return null;
|
|
8670
|
+
const edit = await editResp.json();
|
|
8671
|
+
try {
|
|
8672
|
+
const bundlesResp = await fetch(`${baseUrl}/edits/${edit.id}/bundles`, {
|
|
8673
|
+
headers: { Authorization: `Bearer ${accessToken}` },
|
|
8674
|
+
signal: AbortSignal.timeout(1e4)
|
|
8675
|
+
});
|
|
8676
|
+
if (!bundlesResp.ok) return null;
|
|
8677
|
+
const bundlesData = await bundlesResp.json();
|
|
8678
|
+
const bundles = bundlesData.bundles ?? [];
|
|
8679
|
+
if (bundles.length === 0) return null;
|
|
8680
|
+
const latest = bundles.reduce((max, b) => b.versionCode > max.versionCode ? b : max);
|
|
8681
|
+
const apksResp = await fetch(`${baseUrl}/generatedApks/${latest.versionCode}`, {
|
|
8682
|
+
headers: { Authorization: `Bearer ${accessToken}` },
|
|
8683
|
+
signal: AbortSignal.timeout(1e4)
|
|
8684
|
+
});
|
|
8685
|
+
if (!apksResp.ok) return null;
|
|
8686
|
+
const apksData = await apksResp.json();
|
|
8687
|
+
const fp = apksData.generatedApks?.[0]?.certificateSha256Fingerprint;
|
|
8688
|
+
if (!fp) return null;
|
|
8689
|
+
return { sha256: fp.toUpperCase(), versionCode: latest.versionCode };
|
|
8690
|
+
} finally {
|
|
8691
|
+
await fetch(`${baseUrl}/edits/${edit.id}`, {
|
|
8692
|
+
method: "DELETE",
|
|
8693
|
+
headers: { Authorization: `Bearer ${accessToken}` },
|
|
8694
|
+
signal: AbortSignal.timeout(5e3)
|
|
8695
|
+
}).catch(() => {
|
|
8696
|
+
});
|
|
8697
|
+
}
|
|
8698
|
+
}
|
|
8699
|
+
function compareFingerprints(a, b) {
|
|
8700
|
+
return normalizeFingerprint(a) === normalizeFingerprint(b);
|
|
8701
|
+
}
|
|
8702
|
+
|
|
8703
|
+
// src/signing-consistency.ts
|
|
8704
|
+
async function checkSigningConsistency(accessToken, packageName, apiHost = "androidpublisher.googleapis.com") {
|
|
8705
|
+
const baseUrl = `https://${apiHost}/androidpublisher/v3/applications/${encodeURIComponent(packageName)}`;
|
|
8706
|
+
const editResp = await fetch(`${baseUrl}/edits`, {
|
|
8707
|
+
method: "POST",
|
|
8708
|
+
headers: { Authorization: `Bearer ${accessToken}`, "Content-Type": "application/json" },
|
|
8709
|
+
body: JSON.stringify({}),
|
|
8710
|
+
signal: AbortSignal.timeout(1e4)
|
|
8711
|
+
});
|
|
8712
|
+
if (!editResp.ok) {
|
|
8713
|
+
const body = await editResp.json().catch(() => ({}));
|
|
8714
|
+
throw new GpcError(
|
|
8715
|
+
`Failed to create edit: ${body.error?.message ?? `HTTP ${editResp.status}`}`,
|
|
8716
|
+
"EDIT_CREATE_FAILED",
|
|
8717
|
+
4,
|
|
8718
|
+
"Check your credentials and app access permissions"
|
|
8719
|
+
);
|
|
8720
|
+
}
|
|
8721
|
+
const edit = await editResp.json();
|
|
8722
|
+
try {
|
|
8723
|
+
const bundlesResp = await fetch(`${baseUrl}/edits/${edit.id}/bundles`, {
|
|
8724
|
+
headers: { Authorization: `Bearer ${accessToken}` },
|
|
8725
|
+
signal: AbortSignal.timeout(1e4)
|
|
8726
|
+
});
|
|
8727
|
+
if (!bundlesResp.ok) {
|
|
8728
|
+
throw new GpcError(
|
|
8729
|
+
`Failed to list bundles: HTTP ${bundlesResp.status}`,
|
|
8730
|
+
"BUNDLES_LIST_FAILED",
|
|
8731
|
+
4,
|
|
8732
|
+
"Check your API permissions for this app"
|
|
8733
|
+
);
|
|
8734
|
+
}
|
|
8735
|
+
const bundlesData = await bundlesResp.json();
|
|
8736
|
+
const bundles = (bundlesData.bundles ?? []).sort((a, b) => b.versionCode - a.versionCode);
|
|
8737
|
+
if (bundles.length === 0) {
|
|
8738
|
+
throw new GpcError(
|
|
8739
|
+
"No bundles found for this app",
|
|
8740
|
+
"NO_BUNDLES",
|
|
8741
|
+
4,
|
|
8742
|
+
"Upload at least one AAB with 'gpc publish' before checking signing consistency"
|
|
8743
|
+
);
|
|
8744
|
+
}
|
|
8745
|
+
const currentVc = bundles[0].versionCode;
|
|
8746
|
+
const currentFp = await fetchFingerprint(baseUrl, accessToken, currentVc);
|
|
8747
|
+
if (bundles.length === 1) {
|
|
8748
|
+
return {
|
|
8749
|
+
currentVersionCode: currentVc,
|
|
8750
|
+
currentFingerprint: currentFp,
|
|
8751
|
+
consistent: true,
|
|
8752
|
+
firstRelease: true
|
|
8753
|
+
};
|
|
8754
|
+
}
|
|
8755
|
+
const previousVc = bundles[1].versionCode;
|
|
8756
|
+
const previousFp = await fetchFingerprint(baseUrl, accessToken, previousVc);
|
|
8757
|
+
const consistent = normalizeFingerprint(currentFp) === normalizeFingerprint(previousFp);
|
|
8758
|
+
return {
|
|
8759
|
+
currentVersionCode: currentVc,
|
|
8760
|
+
currentFingerprint: currentFp,
|
|
8761
|
+
previousVersionCode: previousVc,
|
|
8762
|
+
previousFingerprint: previousFp,
|
|
8763
|
+
consistent,
|
|
8764
|
+
firstRelease: false
|
|
8765
|
+
};
|
|
8766
|
+
} finally {
|
|
8767
|
+
await fetch(`${baseUrl}/edits/${edit.id}`, {
|
|
8768
|
+
method: "DELETE",
|
|
8769
|
+
headers: { Authorization: `Bearer ${accessToken}` },
|
|
8770
|
+
signal: AbortSignal.timeout(5e3)
|
|
8771
|
+
}).catch(() => {
|
|
8772
|
+
});
|
|
8773
|
+
}
|
|
8774
|
+
}
|
|
8775
|
+
async function fetchFingerprint(baseUrl, accessToken, versionCode) {
|
|
8776
|
+
const resp = await fetch(`${baseUrl}/generatedApks/${versionCode}`, {
|
|
8777
|
+
headers: { Authorization: `Bearer ${accessToken}` },
|
|
8778
|
+
signal: AbortSignal.timeout(1e4)
|
|
8779
|
+
});
|
|
8780
|
+
if (!resp.ok) {
|
|
8781
|
+
throw new GpcError(
|
|
8782
|
+
`Failed to get generated APKs for versionCode ${versionCode}: HTTP ${resp.status}`,
|
|
8783
|
+
"GENERATED_APKS_FAILED",
|
|
8784
|
+
4,
|
|
8785
|
+
"Check your API permissions for this app"
|
|
8786
|
+
);
|
|
8787
|
+
}
|
|
8788
|
+
const data = await resp.json();
|
|
8789
|
+
const fp = data.generatedApks?.[0]?.certificateSha256Fingerprint;
|
|
8790
|
+
if (!fp) {
|
|
8791
|
+
throw new GpcError(
|
|
8792
|
+
`No signing certificate found for versionCode ${versionCode}`,
|
|
8793
|
+
"NO_SIGNING_CERT",
|
|
8794
|
+
4,
|
|
8795
|
+
"The app may not be enrolled in Play App Signing"
|
|
8796
|
+
);
|
|
8797
|
+
}
|
|
8798
|
+
return fp.toUpperCase();
|
|
8799
|
+
}
|
|
8800
|
+
|
|
8801
|
+
// src/verify-checklist.ts
|
|
8802
|
+
var PLAY_CONSOLE_SETTINGS = "https://play.google.com/console/developers/settings";
|
|
8803
|
+
var VERIFICATION_PAGE = "https://developer.android.com/developer-verification";
|
|
8804
|
+
var PLAY_APP_SIGNING = "https://support.google.com/googleplay/android-developer/answer/9842756";
|
|
8805
|
+
function buildChecklist(input) {
|
|
8806
|
+
const items = [];
|
|
8807
|
+
const answers = input.interactiveAnswers ?? {};
|
|
8808
|
+
items.push({
|
|
8809
|
+
id: "account-active",
|
|
8810
|
+
title: "Play Console account active",
|
|
8811
|
+
status: input.authenticated ? "done" : "action-needed",
|
|
8812
|
+
detail: input.authenticated ? `Authenticated as ${input.accountEmail ?? "unknown"}` : "Could not authenticate with Google Play",
|
|
8813
|
+
actionUrl: input.authenticated ? void 0 : "https://play.google.com/console"
|
|
8814
|
+
});
|
|
8815
|
+
items.push(
|
|
8816
|
+
resolveManualStep(
|
|
8817
|
+
"identity-verified",
|
|
8818
|
+
"Identity verification complete",
|
|
8819
|
+
"Confirm your identity is verified under Developer Account in Play Console Settings",
|
|
8820
|
+
PLAY_CONSOLE_SETTINGS,
|
|
8821
|
+
answers
|
|
8822
|
+
)
|
|
8823
|
+
);
|
|
8824
|
+
items.push(
|
|
8825
|
+
resolveManualStep(
|
|
8826
|
+
"auto-registration-reviewed",
|
|
8827
|
+
"Auto-registration results reviewed",
|
|
8828
|
+
"Check the registration status banner above your app list in Play Console",
|
|
8829
|
+
PLAY_CONSOLE_SETTINGS,
|
|
8830
|
+
answers
|
|
8831
|
+
)
|
|
8832
|
+
);
|
|
8833
|
+
if (input.appAccessible !== void 0) {
|
|
8834
|
+
items.push({
|
|
8835
|
+
id: "app-accessible",
|
|
8836
|
+
title: "App accessible via API",
|
|
8837
|
+
status: input.appAccessible ? "done" : "action-needed",
|
|
8838
|
+
detail: input.appAccessible ? "App is reachable through the Play Developer API" : "Could not access app via API"
|
|
8839
|
+
});
|
|
8840
|
+
}
|
|
8841
|
+
if (input.bundleCount !== void 0) {
|
|
8842
|
+
items.push({
|
|
8843
|
+
id: "bundle-uploaded",
|
|
8844
|
+
title: "At least one bundle uploaded",
|
|
8845
|
+
status: input.bundleCount > 0 ? "done" : "action-needed",
|
|
8846
|
+
detail: input.bundleCount > 0 ? `${input.bundleCount} bundle${input.bundleCount !== 1 ? "s" : ""} on file` : "No bundles found. Upload an AAB with: gpc publish"
|
|
8847
|
+
});
|
|
8848
|
+
}
|
|
8849
|
+
if (input.hasGeneratedApks !== void 0) {
|
|
8850
|
+
items.push({
|
|
8851
|
+
id: "play-app-signing",
|
|
8852
|
+
title: "Play App Signing enrolled",
|
|
8853
|
+
status: input.hasGeneratedApks ? "done" : "action-needed",
|
|
8854
|
+
detail: input.hasGeneratedApks ? "Google manages your app signing key" : "App may not be enrolled in Play App Signing",
|
|
8855
|
+
actionUrl: input.hasGeneratedApks ? void 0 : PLAY_APP_SIGNING
|
|
8856
|
+
});
|
|
8857
|
+
}
|
|
8858
|
+
items.push(
|
|
8859
|
+
resolveManualStep(
|
|
8860
|
+
"additional-keys",
|
|
8861
|
+
"Additional signing keys registered",
|
|
8862
|
+
"If you distribute outside Play with a different key, register it in Play Console",
|
|
8863
|
+
VERIFICATION_PAGE,
|
|
8864
|
+
answers
|
|
8865
|
+
)
|
|
8866
|
+
);
|
|
8867
|
+
const completed = items.filter((i) => i.status === "done").length;
|
|
8868
|
+
return { items, completed, total: items.length };
|
|
8869
|
+
}
|
|
8870
|
+
function resolveManualStep(id, title, detail, actionUrl, answers) {
|
|
8871
|
+
if (id in answers) {
|
|
8872
|
+
return {
|
|
8873
|
+
id,
|
|
8874
|
+
title,
|
|
8875
|
+
status: answers[id] ? "done" : "action-needed",
|
|
8876
|
+
detail,
|
|
8877
|
+
actionUrl: answers[id] ? void 0 : actionUrl
|
|
8878
|
+
};
|
|
8879
|
+
}
|
|
8880
|
+
return { id, title, status: "cannot-detect", detail, actionUrl };
|
|
8881
|
+
}
|
|
8882
|
+
function renderChecklistMarkdown(result, accountEmail) {
|
|
8883
|
+
const lines = [
|
|
8884
|
+
"# Developer Verification Checklist",
|
|
8885
|
+
"",
|
|
8886
|
+
`Account: ${accountEmail}`,
|
|
8887
|
+
`Date: ${(/* @__PURE__ */ new Date()).toISOString().slice(0, 10)}`,
|
|
8888
|
+
`Progress: ${result.completed}/${result.total}`,
|
|
8889
|
+
""
|
|
8890
|
+
];
|
|
8891
|
+
for (const item of result.items) {
|
|
8892
|
+
const check = item.status === "done" ? "x" : " ";
|
|
8893
|
+
lines.push(`- [${check}] ${item.title}`);
|
|
8894
|
+
if (item.detail) lines.push(` ${item.detail}`);
|
|
8895
|
+
if (item.actionUrl && item.status !== "done") lines.push(` ${item.actionUrl}`);
|
|
8896
|
+
}
|
|
8897
|
+
lines.push("");
|
|
8898
|
+
return lines.join("\n");
|
|
8899
|
+
}
|
|
8583
8900
|
export {
|
|
8584
8901
|
ApiError,
|
|
8585
8902
|
ConfigError,
|
|
@@ -8612,16 +8929,19 @@ export {
|
|
|
8612
8929
|
applyReleaseNotes,
|
|
8613
8930
|
batchGetOrders,
|
|
8614
8931
|
batchSyncInAppProducts,
|
|
8932
|
+
buildChecklist,
|
|
8615
8933
|
buildLocaleBundle,
|
|
8616
8934
|
bundleToReleaseNotes,
|
|
8617
8935
|
cancelRecoveryAction,
|
|
8618
8936
|
cancelSubscriptionPurchase,
|
|
8619
8937
|
cancelSubscriptionV2,
|
|
8620
8938
|
checkBundleSize,
|
|
8939
|
+
checkSigningConsistency,
|
|
8621
8940
|
checkThreshold,
|
|
8622
8941
|
classifyError,
|
|
8623
8942
|
clearAuditLog,
|
|
8624
8943
|
compareBundles,
|
|
8944
|
+
compareFingerprints,
|
|
8625
8945
|
compareVersionVitals,
|
|
8626
8946
|
compareVitalsTrend,
|
|
8627
8947
|
computeStatusDiff,
|
|
@@ -8689,12 +9009,14 @@ export {
|
|
|
8689
9009
|
generateMigrationPlan,
|
|
8690
9010
|
generateNotesFromGit,
|
|
8691
9011
|
getAllScannerNames,
|
|
9012
|
+
getApiSigningFingerprint,
|
|
8692
9013
|
getAppInfo,
|
|
8693
9014
|
getAppStatus,
|
|
8694
9015
|
getCountryAvailability,
|
|
8695
9016
|
getDeviceTier,
|
|
8696
9017
|
getExternalTransaction,
|
|
8697
9018
|
getInAppProduct,
|
|
9019
|
+
getKeystoreFingerprint,
|
|
8698
9020
|
getListings,
|
|
8699
9021
|
getOffer,
|
|
8700
9022
|
getOneTimeOffer,
|
|
@@ -8758,10 +9080,12 @@ export {
|
|
|
8758
9080
|
loadStatusCache,
|
|
8759
9081
|
maybePaginate,
|
|
8760
9082
|
migratePrices,
|
|
9083
|
+
normalizeFingerprint,
|
|
8761
9084
|
parseAppfile,
|
|
8762
9085
|
parseCommit,
|
|
8763
9086
|
parseFastfile,
|
|
8764
9087
|
parseGrantArg,
|
|
9088
|
+
parseKeytoolOutput,
|
|
8765
9089
|
parseMonth,
|
|
8766
9090
|
parseRemoteUrl,
|
|
8767
9091
|
pauseTrain,
|
|
@@ -8779,6 +9103,7 @@ export {
|
|
|
8779
9103
|
relativeTime,
|
|
8780
9104
|
removeTesters,
|
|
8781
9105
|
removeUser,
|
|
9106
|
+
renderChecklistMarkdown,
|
|
8782
9107
|
renderJson,
|
|
8783
9108
|
renderMarkdown,
|
|
8784
9109
|
renderPlayStore,
|