@hot-updater/android-helper 1.2.0-rc.6 → 1.2.0-rc.7
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.cjs +45 -18
- package/dist/index.d.cts +14 -2
- package/dist/index.d.mts +14 -2
- package/dist/index.mjs +45 -18
- package/package.json +4 -4
package/dist/index.cjs
CHANGED
|
@@ -1233,13 +1233,28 @@ const isConflict = (error) => error instanceof GoogleApiError && error.status ==
|
|
|
1233
1233
|
/** The API is disabled in another project than the one set up: waiting does not help. */
|
|
1234
1234
|
const consumerApiDisabled = (error) => new PlayServiceAccountError("api-not-enabled", `${error.apiMessage}\nEnable it in ${error.consumer ?? "that project"}, then run this again.`, { cause: error });
|
|
1235
1235
|
const isDenied = (error) => error instanceof GoogleApiError && (error.status === 403 || error.apiStatus === "PERMISSION_DENIED");
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
"
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1236
|
+
const lines = (output) => output.split("\n").map((line) => line.trim()).filter(Boolean);
|
|
1237
|
+
/** The Google Accounts gcloud holds, the active one, if any, first. */
|
|
1238
|
+
const credentialedAccounts = async (run) => {
|
|
1239
|
+
const accounts = lines(await run("gcloud", [
|
|
1240
|
+
"auth",
|
|
1241
|
+
"list",
|
|
1242
|
+
"--format=value(account)"
|
|
1243
|
+
]));
|
|
1244
|
+
const [active] = lines(await run("gcloud", [
|
|
1245
|
+
"auth",
|
|
1246
|
+
"list",
|
|
1247
|
+
"--filter=status:ACTIVE",
|
|
1248
|
+
"--format=value(account)"
|
|
1249
|
+
]));
|
|
1250
|
+
return active !== void 0 && accounts.includes(active) ? [active, ...accounts.filter((account) => account !== active)].map((email) => ({
|
|
1251
|
+
email,
|
|
1252
|
+
active: email === active
|
|
1253
|
+
})) : accounts.map((email) => ({
|
|
1254
|
+
email,
|
|
1255
|
+
active: false
|
|
1256
|
+
}));
|
|
1257
|
+
};
|
|
1243
1258
|
/** Stops before anything else, a prompt included, when gcloud is not installed. */
|
|
1244
1259
|
const requireGcloud = async (run) => {
|
|
1245
1260
|
try {
|
|
@@ -1252,16 +1267,26 @@ const requireGcloud = async (run) => {
|
|
|
1252
1267
|
].join("\n"), { cause: error });
|
|
1253
1268
|
}
|
|
1254
1269
|
};
|
|
1255
|
-
/**
|
|
1256
|
-
|
|
1257
|
-
|
|
1270
|
+
/**
|
|
1271
|
+
* The access token of the Google Account the developer picks among those
|
|
1272
|
+
* gcloud holds, or of the one `gcloud auth login` adds, when they pick
|
|
1273
|
+
* another or gcloud holds none. gcloud's active account stays as it was.
|
|
1274
|
+
*/
|
|
1275
|
+
const signInWithGcloud = async ({ run, request, onStep }) => {
|
|
1276
|
+
const before = await credentialedAccounts(run);
|
|
1277
|
+
let account = before.length > 0 ? await request.chooseGoogleAccount(before) ?? void 0 : void 0;
|
|
1258
1278
|
if (account === void 0) {
|
|
1259
|
-
await run("gcloud", ["auth", "login"], { interactive: true }).
|
|
1260
|
-
|
|
1261
|
-
|
|
1279
|
+
const signedIn = await run("gcloud", ["auth", "login"], { interactive: true }).then(() => true, () => false);
|
|
1280
|
+
const after = await credentialedAccounts(run);
|
|
1281
|
+
account = after.find(({ email }) => !before.some((known) => known.email === email))?.email ?? (signedIn ? after.find(({ active }) => active)?.email : void 0);
|
|
1282
|
+
if (account === void 0) throw new PlayServiceAccountError("gcloud-not-signed-in", before.length === 0 ? "gcloud has no active Google Account.\nRun gcloud auth login, then run this again." : "gcloud auth login didn't finish.\nRun this again to choose a Google Account.");
|
|
1262
1283
|
}
|
|
1263
1284
|
onStep(`Signed in to gcloud as ${account}`);
|
|
1264
|
-
const accessToken = (await run("gcloud", [
|
|
1285
|
+
const accessToken = (await run("gcloud", [
|
|
1286
|
+
"auth",
|
|
1287
|
+
"print-access-token",
|
|
1288
|
+
account
|
|
1289
|
+
])).trim();
|
|
1265
1290
|
(0, _hot_updater_cli_tools.registerSecretValue)(accessToken);
|
|
1266
1291
|
return accessToken;
|
|
1267
1292
|
};
|
|
@@ -1269,9 +1294,9 @@ const signInWithGcloud = async ({ run, onStep }) => {
|
|
|
1269
1294
|
* `IssuePlayServiceAccount` with the gcloud CLI's sign-in and Google's own
|
|
1270
1295
|
* APIs:
|
|
1271
1296
|
*
|
|
1272
|
-
* 1. once gcloud is found, `askPackageName` names the app, and
|
|
1273
|
-
*
|
|
1274
|
-
*
|
|
1297
|
+
* 1. once gcloud is found, `askPackageName` names the app, and the Google
|
|
1298
|
+
* Account `chooseGoogleAccount` picks among gcloud's, or the one
|
|
1299
|
+
* `gcloud auth login` adds, the access token the Google Cloud steps use;
|
|
1275
1300
|
* 2. the account's only project with the Play Developer API, else the one
|
|
1276
1301
|
* `chooseProject` picks, else a new one, gets the IAM and Play Developer
|
|
1277
1302
|
* APIs enabled, and the service account `hot-updater-play` its JSON key;
|
|
@@ -1289,6 +1314,7 @@ const createGoogleKeyIssuer = ({ fetch = globalThis.fetch, sleep = sleepFor, run
|
|
|
1289
1314
|
const packageName = await request.askPackageName();
|
|
1290
1315
|
const accessToken = await signInWithGcloud({
|
|
1291
1316
|
run,
|
|
1317
|
+
request,
|
|
1292
1318
|
onStep
|
|
1293
1319
|
});
|
|
1294
1320
|
const google = createGoogleRequest({
|
|
@@ -1343,8 +1369,9 @@ const createGoogleKeyIssuer = ({ fetch = globalThis.fetch, sleep = sleepFor, run
|
|
|
1343
1369
|
const deleted = await revoke();
|
|
1344
1370
|
throw new PlayServiceAccountError("play-access-pending", [
|
|
1345
1371
|
`The key can't release ${packageName} yet.`,
|
|
1346
|
-
"Invite the service account in Play Console
|
|
1372
|
+
"Invite the service account in Play Console:",
|
|
1347
1373
|
email,
|
|
1374
|
+
"App permission: Release apps to testing tracks",
|
|
1348
1375
|
inviteUrl,
|
|
1349
1376
|
"A new app needs its first .aab uploaded in Play Console.",
|
|
1350
1377
|
...deleted ? [] : [`Delete its unused key ${key.keyId} in Google Cloud:`, `https://console.cloud.google.com/iam-admin/serviceaccounts?project=${encodeURIComponent(projectId)}`],
|
package/dist/index.d.cts
CHANGED
|
@@ -158,7 +158,7 @@ declare const readBuiltVersionCode: ({ artifactPath, androidProjectPath }: {
|
|
|
158
158
|
type PlayServiceAccountErrorCode =
|
|
159
159
|
/** The gcloud CLI, which signs in to Google, is not installed. */
|
|
160
160
|
"gcloud-not-installed" |
|
|
161
|
-
/**
|
|
161
|
+
/** gcloud ended with no Google Account to use: none signed in, or the sign-in was cancelled. */
|
|
162
162
|
"gcloud-not-signed-in" |
|
|
163
163
|
/** The Google Account may not create a Google Cloud project. */
|
|
164
164
|
"project-create-denied" |
|
|
@@ -190,6 +190,12 @@ interface GoogleCloudProject {
|
|
|
190
190
|
/** Whether the Google Play Android Developer API is enabled in it. */
|
|
191
191
|
readonly playApiEnabled: boolean;
|
|
192
192
|
}
|
|
193
|
+
/** A Google Account the gcloud CLI holds credentials for. */
|
|
194
|
+
interface GoogleAccount {
|
|
195
|
+
readonly email: string;
|
|
196
|
+
/** Whether it is gcloud's active account. */
|
|
197
|
+
readonly active: boolean;
|
|
198
|
+
}
|
|
193
199
|
interface IssuePlayServiceAccountRequest {
|
|
194
200
|
/**
|
|
195
201
|
* Asks for the app's package name in Google Play, which the service
|
|
@@ -197,6 +203,12 @@ interface IssuePlayServiceAccountRequest {
|
|
|
197
203
|
* anything else.
|
|
198
204
|
*/
|
|
199
205
|
readonly askPackageName: () => Promise<string>;
|
|
206
|
+
/**
|
|
207
|
+
* Sees the Google Accounts gcloud already holds, the active one first,
|
|
208
|
+
* after the package name, and returns the one to use, or null to sign in
|
|
209
|
+
* with another. Never called when gcloud holds none: it signs in.
|
|
210
|
+
*/
|
|
211
|
+
readonly chooseGoogleAccount: (accounts: readonly GoogleAccount[]) => Promise<string | null>;
|
|
200
212
|
/**
|
|
201
213
|
* Sees Play Console's page that invites the service account `email`, to
|
|
202
214
|
* print it and open it in a browser; the issuer then waits for the invite.
|
|
@@ -243,4 +255,4 @@ type IssuePlayServiceAccount = (request: IssuePlayServiceAccountRequest) => Prom
|
|
|
243
255
|
*/
|
|
244
256
|
declare const issuePlayServiceAccount: IssuePlayServiceAccount;
|
|
245
257
|
//#endregion
|
|
246
|
-
export { type EnrichedNativeBuildAndroidScheme, type GoogleCloudProject, type IssuePlayServiceAccount, type IssuePlayServiceAccountRequest, type IssuedPlayServiceAccount, PLAY_SERVICE_ACCOUNT_ID, PlayServiceAccountError, type PlayServiceAccountErrorCode, PlayVersionCodeUsedError, type UploadToPlayResult, assertPlaySigned, buildAndroid, enrichNativeBuildAndroidScheme, extractBuiltInBundle, findPlayInternalRelease, hasPlayReleases, issuePlayServiceAccount, readBuiltApp, readBuiltPackageName, readBuiltVersionCode, resolvePlayUploadServiceAccount, uploadToPlay };
|
|
258
|
+
export { type EnrichedNativeBuildAndroidScheme, type GoogleAccount, type GoogleCloudProject, type IssuePlayServiceAccount, type IssuePlayServiceAccountRequest, type IssuedPlayServiceAccount, PLAY_SERVICE_ACCOUNT_ID, PlayServiceAccountError, type PlayServiceAccountErrorCode, PlayVersionCodeUsedError, type UploadToPlayResult, assertPlaySigned, buildAndroid, enrichNativeBuildAndroidScheme, extractBuiltInBundle, findPlayInternalRelease, hasPlayReleases, issuePlayServiceAccount, readBuiltApp, readBuiltPackageName, readBuiltVersionCode, resolvePlayUploadServiceAccount, uploadToPlay };
|
package/dist/index.d.mts
CHANGED
|
@@ -159,7 +159,7 @@ declare const readBuiltVersionCode: ({ artifactPath, androidProjectPath }: {
|
|
|
159
159
|
type PlayServiceAccountErrorCode =
|
|
160
160
|
/** The gcloud CLI, which signs in to Google, is not installed. */
|
|
161
161
|
"gcloud-not-installed" |
|
|
162
|
-
/**
|
|
162
|
+
/** gcloud ended with no Google Account to use: none signed in, or the sign-in was cancelled. */
|
|
163
163
|
"gcloud-not-signed-in" |
|
|
164
164
|
/** The Google Account may not create a Google Cloud project. */
|
|
165
165
|
"project-create-denied" |
|
|
@@ -191,6 +191,12 @@ interface GoogleCloudProject {
|
|
|
191
191
|
/** Whether the Google Play Android Developer API is enabled in it. */
|
|
192
192
|
readonly playApiEnabled: boolean;
|
|
193
193
|
}
|
|
194
|
+
/** A Google Account the gcloud CLI holds credentials for. */
|
|
195
|
+
interface GoogleAccount {
|
|
196
|
+
readonly email: string;
|
|
197
|
+
/** Whether it is gcloud's active account. */
|
|
198
|
+
readonly active: boolean;
|
|
199
|
+
}
|
|
194
200
|
interface IssuePlayServiceAccountRequest {
|
|
195
201
|
/**
|
|
196
202
|
* Asks for the app's package name in Google Play, which the service
|
|
@@ -198,6 +204,12 @@ interface IssuePlayServiceAccountRequest {
|
|
|
198
204
|
* anything else.
|
|
199
205
|
*/
|
|
200
206
|
readonly askPackageName: () => Promise<string>;
|
|
207
|
+
/**
|
|
208
|
+
* Sees the Google Accounts gcloud already holds, the active one first,
|
|
209
|
+
* after the package name, and returns the one to use, or null to sign in
|
|
210
|
+
* with another. Never called when gcloud holds none: it signs in.
|
|
211
|
+
*/
|
|
212
|
+
readonly chooseGoogleAccount: (accounts: readonly GoogleAccount[]) => Promise<string | null>;
|
|
201
213
|
/**
|
|
202
214
|
* Sees Play Console's page that invites the service account `email`, to
|
|
203
215
|
* print it and open it in a browser; the issuer then waits for the invite.
|
|
@@ -244,4 +256,4 @@ type IssuePlayServiceAccount = (request: IssuePlayServiceAccountRequest) => Prom
|
|
|
244
256
|
*/
|
|
245
257
|
declare const issuePlayServiceAccount: IssuePlayServiceAccount;
|
|
246
258
|
//#endregion
|
|
247
|
-
export { type EnrichedNativeBuildAndroidScheme, type GoogleCloudProject, type IssuePlayServiceAccount, type IssuePlayServiceAccountRequest, type IssuedPlayServiceAccount, PLAY_SERVICE_ACCOUNT_ID, PlayServiceAccountError, type PlayServiceAccountErrorCode, PlayVersionCodeUsedError, type UploadToPlayResult, assertPlaySigned, buildAndroid, enrichNativeBuildAndroidScheme, extractBuiltInBundle, findPlayInternalRelease, hasPlayReleases, issuePlayServiceAccount, readBuiltApp, readBuiltPackageName, readBuiltVersionCode, resolvePlayUploadServiceAccount, uploadToPlay };
|
|
259
|
+
export { type EnrichedNativeBuildAndroidScheme, type GoogleAccount, type GoogleCloudProject, type IssuePlayServiceAccount, type IssuePlayServiceAccountRequest, type IssuedPlayServiceAccount, PLAY_SERVICE_ACCOUNT_ID, PlayServiceAccountError, type PlayServiceAccountErrorCode, PlayVersionCodeUsedError, type UploadToPlayResult, assertPlaySigned, buildAndroid, enrichNativeBuildAndroidScheme, extractBuiltInBundle, findPlayInternalRelease, hasPlayReleases, issuePlayServiceAccount, readBuiltApp, readBuiltPackageName, readBuiltVersionCode, resolvePlayUploadServiceAccount, uploadToPlay };
|
package/dist/index.mjs
CHANGED
|
@@ -1204,13 +1204,28 @@ const isConflict = (error) => error instanceof GoogleApiError && error.status ==
|
|
|
1204
1204
|
/** The API is disabled in another project than the one set up: waiting does not help. */
|
|
1205
1205
|
const consumerApiDisabled = (error) => new PlayServiceAccountError("api-not-enabled", `${error.apiMessage}\nEnable it in ${error.consumer ?? "that project"}, then run this again.`, { cause: error });
|
|
1206
1206
|
const isDenied = (error) => error instanceof GoogleApiError && (error.status === 403 || error.apiStatus === "PERMISSION_DENIED");
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
"
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1207
|
+
const lines = (output) => output.split("\n").map((line) => line.trim()).filter(Boolean);
|
|
1208
|
+
/** The Google Accounts gcloud holds, the active one, if any, first. */
|
|
1209
|
+
const credentialedAccounts = async (run) => {
|
|
1210
|
+
const accounts = lines(await run("gcloud", [
|
|
1211
|
+
"auth",
|
|
1212
|
+
"list",
|
|
1213
|
+
"--format=value(account)"
|
|
1214
|
+
]));
|
|
1215
|
+
const [active] = lines(await run("gcloud", [
|
|
1216
|
+
"auth",
|
|
1217
|
+
"list",
|
|
1218
|
+
"--filter=status:ACTIVE",
|
|
1219
|
+
"--format=value(account)"
|
|
1220
|
+
]));
|
|
1221
|
+
return active !== void 0 && accounts.includes(active) ? [active, ...accounts.filter((account) => account !== active)].map((email) => ({
|
|
1222
|
+
email,
|
|
1223
|
+
active: email === active
|
|
1224
|
+
})) : accounts.map((email) => ({
|
|
1225
|
+
email,
|
|
1226
|
+
active: false
|
|
1227
|
+
}));
|
|
1228
|
+
};
|
|
1214
1229
|
/** Stops before anything else, a prompt included, when gcloud is not installed. */
|
|
1215
1230
|
const requireGcloud = async (run) => {
|
|
1216
1231
|
try {
|
|
@@ -1223,16 +1238,26 @@ const requireGcloud = async (run) => {
|
|
|
1223
1238
|
].join("\n"), { cause: error });
|
|
1224
1239
|
}
|
|
1225
1240
|
};
|
|
1226
|
-
/**
|
|
1227
|
-
|
|
1228
|
-
|
|
1241
|
+
/**
|
|
1242
|
+
* The access token of the Google Account the developer picks among those
|
|
1243
|
+
* gcloud holds, or of the one `gcloud auth login` adds, when they pick
|
|
1244
|
+
* another or gcloud holds none. gcloud's active account stays as it was.
|
|
1245
|
+
*/
|
|
1246
|
+
const signInWithGcloud = async ({ run, request, onStep }) => {
|
|
1247
|
+
const before = await credentialedAccounts(run);
|
|
1248
|
+
let account = before.length > 0 ? await request.chooseGoogleAccount(before) ?? void 0 : void 0;
|
|
1229
1249
|
if (account === void 0) {
|
|
1230
|
-
await run("gcloud", ["auth", "login"], { interactive: true }).
|
|
1231
|
-
|
|
1232
|
-
|
|
1250
|
+
const signedIn = await run("gcloud", ["auth", "login"], { interactive: true }).then(() => true, () => false);
|
|
1251
|
+
const after = await credentialedAccounts(run);
|
|
1252
|
+
account = after.find(({ email }) => !before.some((known) => known.email === email))?.email ?? (signedIn ? after.find(({ active }) => active)?.email : void 0);
|
|
1253
|
+
if (account === void 0) throw new PlayServiceAccountError("gcloud-not-signed-in", before.length === 0 ? "gcloud has no active Google Account.\nRun gcloud auth login, then run this again." : "gcloud auth login didn't finish.\nRun this again to choose a Google Account.");
|
|
1233
1254
|
}
|
|
1234
1255
|
onStep(`Signed in to gcloud as ${account}`);
|
|
1235
|
-
const accessToken = (await run("gcloud", [
|
|
1256
|
+
const accessToken = (await run("gcloud", [
|
|
1257
|
+
"auth",
|
|
1258
|
+
"print-access-token",
|
|
1259
|
+
account
|
|
1260
|
+
])).trim();
|
|
1236
1261
|
registerSecretValue(accessToken);
|
|
1237
1262
|
return accessToken;
|
|
1238
1263
|
};
|
|
@@ -1240,9 +1265,9 @@ const signInWithGcloud = async ({ run, onStep }) => {
|
|
|
1240
1265
|
* `IssuePlayServiceAccount` with the gcloud CLI's sign-in and Google's own
|
|
1241
1266
|
* APIs:
|
|
1242
1267
|
*
|
|
1243
|
-
* 1. once gcloud is found, `askPackageName` names the app, and
|
|
1244
|
-
*
|
|
1245
|
-
*
|
|
1268
|
+
* 1. once gcloud is found, `askPackageName` names the app, and the Google
|
|
1269
|
+
* Account `chooseGoogleAccount` picks among gcloud's, or the one
|
|
1270
|
+
* `gcloud auth login` adds, the access token the Google Cloud steps use;
|
|
1246
1271
|
* 2. the account's only project with the Play Developer API, else the one
|
|
1247
1272
|
* `chooseProject` picks, else a new one, gets the IAM and Play Developer
|
|
1248
1273
|
* APIs enabled, and the service account `hot-updater-play` its JSON key;
|
|
@@ -1260,6 +1285,7 @@ const createGoogleKeyIssuer = ({ fetch = globalThis.fetch, sleep = sleepFor, run
|
|
|
1260
1285
|
const packageName = await request.askPackageName();
|
|
1261
1286
|
const accessToken = await signInWithGcloud({
|
|
1262
1287
|
run,
|
|
1288
|
+
request,
|
|
1263
1289
|
onStep
|
|
1264
1290
|
});
|
|
1265
1291
|
const google = createGoogleRequest({
|
|
@@ -1314,8 +1340,9 @@ const createGoogleKeyIssuer = ({ fetch = globalThis.fetch, sleep = sleepFor, run
|
|
|
1314
1340
|
const deleted = await revoke();
|
|
1315
1341
|
throw new PlayServiceAccountError("play-access-pending", [
|
|
1316
1342
|
`The key can't release ${packageName} yet.`,
|
|
1317
|
-
"Invite the service account in Play Console
|
|
1343
|
+
"Invite the service account in Play Console:",
|
|
1318
1344
|
email,
|
|
1345
|
+
"App permission: Release apps to testing tracks",
|
|
1319
1346
|
inviteUrl,
|
|
1320
1347
|
"A new app needs its first .aab uploaded in Play Console.",
|
|
1321
1348
|
...deleted ? [] : [`Delete its unused key ${key.keyId} in Google Cloud:`, `https://console.cloud.google.com/iam-admin/serviceaccounts?project=${encodeURIComponent(projectId)}`],
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hot-updater/android-helper",
|
|
3
|
-
"version": "1.2.0-rc.
|
|
3
|
+
"version": "1.2.0-rc.7",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "dist/index.cjs",
|
|
6
6
|
"module": "dist/index.mjs",
|
|
@@ -29,9 +29,9 @@
|
|
|
29
29
|
"dependencies": {
|
|
30
30
|
"execa": "9.5.2",
|
|
31
31
|
"jszip": "^3.10.1",
|
|
32
|
-
"@hot-updater/cli-tools": "1.2.0-rc.
|
|
33
|
-
"@hot-updater/
|
|
34
|
-
"@hot-updater/
|
|
32
|
+
"@hot-updater/cli-tools": "1.2.0-rc.7",
|
|
33
|
+
"@hot-updater/protocol": "1.2.0-rc.7",
|
|
34
|
+
"@hot-updater/plugin-core": "1.2.0-rc.7"
|
|
35
35
|
},
|
|
36
36
|
"devDependencies": {
|
|
37
37
|
"@types/node": "^20"
|