shipthis 0.1.10 → 0.1.11
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/assets/markdown/create-or-import-keystore.md +7 -0
- package/dist/AppleBundleIdDetails-DymjrhOZ.js +73 -0
- package/dist/Command-BiB9MqbJ.js +204 -0
- package/dist/CommandGame-C1oTlfdb.js +8 -0
- package/dist/Create-DIaSKUpl.js +56 -0
- package/dist/Import-BIOsf8AA.js +107 -0
- package/dist/JobProgress-W0QQR49T.js +108 -0
- package/dist/JobStatusTable-DqoppRro.js +75 -0
- package/dist/commands/apple/apiKey/create.js +4 -4
- package/dist/commands/apple/apiKey/export.js +3 -3
- package/dist/commands/apple/apiKey/import.js +4 -4
- package/dist/commands/apple/apiKey/status.js +3 -3
- package/dist/commands/apple/certificate/create.js +4 -4
- package/dist/commands/apple/certificate/export.js +3 -3
- package/dist/commands/apple/certificate/import.js +4 -4
- package/dist/commands/apple/certificate/status.js +3 -3
- package/dist/commands/apple/status.js +3 -3
- package/dist/commands/game/android/apiKey/connect.js +6 -6
- package/dist/commands/game/android/apiKey/create.js +6 -6
- package/dist/commands/game/android/apiKey/export.js +3 -3
- package/dist/commands/game/android/apiKey/import.js +4 -4
- package/dist/commands/game/android/apiKey/status.js +3 -3
- package/dist/commands/game/android/keyStore/create.js +7 -7
- package/dist/commands/game/android/keyStore/export.js +3 -3
- package/dist/commands/game/android/keyStore/import.js +45 -25
- package/dist/commands/game/android/keyStore/status.js +3 -3
- package/dist/commands/game/android/status.js +5 -5
- package/dist/commands/game/build/download.js +4 -4
- package/dist/commands/game/build/list.js +4 -4
- package/dist/commands/game/details.js +3 -3
- package/dist/commands/game/ios/app/addTester.js +3 -3
- package/dist/commands/game/ios/app/create.js +3 -3
- package/dist/commands/game/ios/app/status.js +4 -4
- package/dist/commands/game/ios/app/sync.js +3 -3
- package/dist/commands/game/ios/profile/create.js +4 -4
- package/dist/commands/game/ios/profile/export.js +3 -3
- package/dist/commands/game/ios/profile/import.js +4 -4
- package/dist/commands/game/ios/profile/status.js +3 -3
- package/dist/commands/game/ios/status.js +6 -6
- package/dist/commands/game/job/list.js +5 -5
- package/dist/commands/game/job/status.js +7 -7
- package/dist/commands/game/list.js +5 -5
- package/dist/commands/game/ship.js +7 -7
- package/dist/commands/game/status.js +5 -5
- package/dist/commands/game/wizard.js +116 -17
- package/dist/commands/status.js +3 -3
- package/dist/import-v54PM_Qg.js +47 -0
- package/dist/index-D6BH5UAM.js +135 -0
- package/dist/index-o9Y-84Rj.js +122 -0
- package/dist/useJobWatching-I_A3b36f.js +45 -0
- package/npm-shrinkwrap.json +2 -2
- package/oclif.manifest.json +27 -4
- package/package.json +1 -1
|
@@ -1,35 +1,36 @@
|
|
|
1
1
|
import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
|
|
2
2
|
import { Args } from '@oclif/core';
|
|
3
|
-
import { Text, Box, render } from 'ink';
|
|
3
|
+
import { Text, Box, useInput, render } from 'ink';
|
|
4
4
|
import { p as getAuthedHeaders, q as API_URL, K as castArrayObjectDates, $ as queryClient, a8 as updateProject, u as getGodotVersion, t as GameEngine, v as createProject, w as DEFAULT_IGNORED_FILES_GLOBS, x as DEFAULT_SHIPPED_FILES_GLOBS, P as Platform, J as JobStatus, X as WEB_URL, H as getProject, C as CredentialsType, Q as getGoogleStatus, B as BaseAuthenticatedCommand, j as isCWDGodotGame } from '../../index-BB7X1Pqp.js';
|
|
5
5
|
import React, { useState, useContext, useEffect, useRef } from 'react';
|
|
6
|
-
import { c as CommandContext, b as GameContext,
|
|
6
|
+
import { c as CommandContext, b as GameContext, M as Markdown, d as useBuilds, q as queryBuilds, G as GameProvider, e as CreateGooglePlayGame, C as Command } from '../../Command-BiB9MqbJ.js';
|
|
7
7
|
import Spinner from 'ink-spinner';
|
|
8
8
|
import { Alert, TextInput } from '@inkjs/ui';
|
|
9
9
|
import axios from 'axios';
|
|
10
10
|
import 'crypto-js';
|
|
11
11
|
import 'uuid';
|
|
12
12
|
import 'luxon';
|
|
13
|
-
import 'fs';
|
|
13
|
+
import fs__default from 'fs';
|
|
14
14
|
import { useQuery, useMutation } from '@tanstack/react-query';
|
|
15
|
-
import '
|
|
15
|
+
import 'yazl';
|
|
16
16
|
import 'crypto';
|
|
17
17
|
import 'readline-sync';
|
|
18
18
|
import 'node:readline';
|
|
19
19
|
import 'node:path';
|
|
20
20
|
import 'node:url';
|
|
21
21
|
import 'fast-glob';
|
|
22
|
-
import 'yazl';
|
|
23
22
|
import 'socket.io-client';
|
|
24
23
|
import 'isomorphic-git';
|
|
24
|
+
import 'open';
|
|
25
25
|
import 'qrcode';
|
|
26
26
|
import 'string-length';
|
|
27
27
|
import 'strip-ansi';
|
|
28
|
-
import { C as CreateKeystore } from '../../
|
|
29
|
-
import {
|
|
30
|
-
import { C as
|
|
28
|
+
import { C as CreateKeystore } from '../../Create-DIaSKUpl.js';
|
|
29
|
+
import { I as ImportKeystore } from '../../Import-BIOsf8AA.js';
|
|
30
|
+
import { C as ConnectGoogle } from '../../index-o9Y-84Rj.js';
|
|
31
|
+
import { C as CreateServiceAccountKey } from '../../index-D6BH5UAM.js';
|
|
31
32
|
import { c as cacheKeys, f as fetchKeyTestResult, K as KeyTestStatus, a as KeyTestError } from '../../useAndroidServiceAccountTestResult-Ce1x0Eh8.js';
|
|
32
|
-
import { u as useShip, J as JobProgress } from '../../JobProgress-
|
|
33
|
+
import { u as useShip, J as JobProgress } from '../../JobProgress-W0QQR49T.js';
|
|
33
34
|
import { a as getProjectCredentials } from '../../index-DOgF4dFK.js';
|
|
34
35
|
import { T as Title } from '../../Title-BCQtayg6.js';
|
|
35
36
|
import 'path';
|
|
@@ -40,11 +41,12 @@ import '../../index-Cz_KLwWf.js';
|
|
|
40
41
|
import 'marked';
|
|
41
42
|
import 'marked-terminal';
|
|
42
43
|
import '../../RunWithSpinner-BVXNWGD3.js';
|
|
44
|
+
import '../../import-v54PM_Qg.js';
|
|
43
45
|
import '../../useWebSocket-CBqsjHbt.js';
|
|
44
46
|
import '../../useProjectCredentials-vjedBbKl.js';
|
|
45
47
|
import '../../ProgressSpinner-6pw1T8Iw.js';
|
|
46
48
|
import '../../git-DREGq-jc.js';
|
|
47
|
-
import '../../useJobWatching-
|
|
49
|
+
import '../../useJobWatching-I_A3b36f.js';
|
|
48
50
|
|
|
49
51
|
async function queryJobs({ projectId, ...pageAndSortParams }) {
|
|
50
52
|
try {
|
|
@@ -230,6 +232,103 @@ const FormTextInput = ({ label, labelProps, ...rest }) => /* @__PURE__ */ jsxs(B
|
|
|
230
232
|
/* @__PURE__ */ jsx(TextInput, { ...rest })
|
|
231
233
|
] });
|
|
232
234
|
|
|
235
|
+
const ImportForm = ({ importKeystoreProps, onSubmit }) => {
|
|
236
|
+
const [activeInput, setActiveInput] = useState("jksFilePath" /* jksFilePath */);
|
|
237
|
+
const [error, setError] = useState(null);
|
|
238
|
+
const [jksFilePath, setJksFilePath] = useState(importKeystoreProps.jksFilePath);
|
|
239
|
+
const [password, setPassword] = useState(importKeystoreProps.keyPassword);
|
|
240
|
+
const handleSubmitJksFilePath = () => {
|
|
241
|
+
setError(null);
|
|
242
|
+
if (!jksFilePath || jksFilePath.length === 0) {
|
|
243
|
+
setError("Please enter a path to your jks file");
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
246
|
+
if (!fs__default.existsSync(jksFilePath)) {
|
|
247
|
+
setError("The file does not exist");
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
250
|
+
setActiveInput("password" /* password */);
|
|
251
|
+
};
|
|
252
|
+
const handleSubmitPassword = () => {
|
|
253
|
+
setError(null);
|
|
254
|
+
if (!password || password.length === 0) {
|
|
255
|
+
setError("Please enter a password");
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
258
|
+
onSubmit({
|
|
259
|
+
...importKeystoreProps,
|
|
260
|
+
jksFilePath,
|
|
261
|
+
keyPassword: password,
|
|
262
|
+
keystorePassword: password
|
|
263
|
+
});
|
|
264
|
+
};
|
|
265
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
266
|
+
error && /* @__PURE__ */ jsx(Alert, { variant: "error", children: error }),
|
|
267
|
+
/* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginLeft: 1, children: [
|
|
268
|
+
/* @__PURE__ */ jsx(
|
|
269
|
+
FormTextInput,
|
|
270
|
+
{
|
|
271
|
+
label: "Path to your jks file:",
|
|
272
|
+
isDisabled: activeInput !== "jksFilePath" /* jksFilePath */,
|
|
273
|
+
defaultValue: jksFilePath,
|
|
274
|
+
placeholder: "Enter the path to your jks file...",
|
|
275
|
+
onChange: setJksFilePath,
|
|
276
|
+
onSubmit: handleSubmitJksFilePath
|
|
277
|
+
}
|
|
278
|
+
),
|
|
279
|
+
/* @__PURE__ */ jsx(
|
|
280
|
+
FormTextInput,
|
|
281
|
+
{
|
|
282
|
+
label: "Password:",
|
|
283
|
+
isDisabled: activeInput !== "password" /* password */,
|
|
284
|
+
defaultValue: password,
|
|
285
|
+
placeholder: "Enter the password for your jks file...",
|
|
286
|
+
onChange: setPassword,
|
|
287
|
+
onSubmit: handleSubmitPassword
|
|
288
|
+
}
|
|
289
|
+
)
|
|
290
|
+
] })
|
|
291
|
+
] });
|
|
292
|
+
};
|
|
293
|
+
|
|
294
|
+
const CreateOrImport = ({ onComplete, onError, ...boxProps }) => {
|
|
295
|
+
const [stage, setStage] = useState(0 /* Choose */);
|
|
296
|
+
const [importKeystoreProps, setImportKeystoreProps] = useState({
|
|
297
|
+
jksFilePath: "",
|
|
298
|
+
keyPassword: "",
|
|
299
|
+
keystorePassword: ""
|
|
300
|
+
});
|
|
301
|
+
useInput(async (input) => {
|
|
302
|
+
if (stage !== 0 /* Choose */) return;
|
|
303
|
+
if (input === "c") return setStage(1 /* Create */);
|
|
304
|
+
if (input === "i") return setStage(2 /* ImportForm */);
|
|
305
|
+
});
|
|
306
|
+
const handleImportFormSubmit = (newImportProps) => {
|
|
307
|
+
setImportKeystoreProps(newImportProps);
|
|
308
|
+
setStage(3 /* ImportKeystore */);
|
|
309
|
+
};
|
|
310
|
+
const renderStage = () => {
|
|
311
|
+
switch (stage) {
|
|
312
|
+
case 0 /* Choose */:
|
|
313
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
314
|
+
/* @__PURE__ */ jsx(Text, { children: "Would you like to create a new keystore or import an existing one?" }),
|
|
315
|
+
/* @__PURE__ */ jsx(Text, { bold: true, children: "Press C to create a new keystore" }),
|
|
316
|
+
/* @__PURE__ */ jsx(Text, { bold: true, children: "Press I to import an existing keystore" })
|
|
317
|
+
] });
|
|
318
|
+
case 1 /* Create */:
|
|
319
|
+
return /* @__PURE__ */ jsx(CreateKeystore, { onComplete, onError });
|
|
320
|
+
case 2 /* ImportForm */:
|
|
321
|
+
return /* @__PURE__ */ jsx(ImportForm, { onSubmit: handleImportFormSubmit, importKeystoreProps });
|
|
322
|
+
case 3 /* ImportKeystore */:
|
|
323
|
+
return /* @__PURE__ */ jsx(ImportKeystore, { onComplete, onError, importKeystoreProps });
|
|
324
|
+
}
|
|
325
|
+
};
|
|
326
|
+
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", gap: 1, ...boxProps, children: [
|
|
327
|
+
/* @__PURE__ */ jsx(Markdown, { filename: "create-or-import-keystore.md", templateVars: {} }),
|
|
328
|
+
renderStage()
|
|
329
|
+
] });
|
|
330
|
+
};
|
|
331
|
+
|
|
233
332
|
const CreateInitialBuild = (props) => {
|
|
234
333
|
const { gameId } = useContext(GameContext);
|
|
235
334
|
return /* @__PURE__ */ jsx(Fragment, { children: gameId && /* @__PURE__ */ jsx(CreateForGame, { gameId, ...props }) });
|
|
@@ -252,8 +351,10 @@ const CreateForGame = ({ onComplete, onError, gameId, ...boxProps }) => {
|
|
|
252
351
|
const hasAndroidBuild = buildData.data.some((build) => build.platform === Platform.ANDROID);
|
|
253
352
|
if (!prevHasBuild.current && hasAndroidBuild) return onComplete();
|
|
254
353
|
prevHasBuild.current = hasAndroidBuild;
|
|
255
|
-
const
|
|
256
|
-
|
|
354
|
+
const hasRunningAndroidJob = jobData.data.some(
|
|
355
|
+
(job) => job.type === Platform.ANDROID && [JobStatus.PENDING, JobStatus.PROCESSING].includes(job.status)
|
|
356
|
+
);
|
|
357
|
+
const shouldRun = !hasAndroidBuild && !hasRunningAndroidJob;
|
|
257
358
|
if (shouldRun)
|
|
258
359
|
shipMutation.mutateAsync({
|
|
259
360
|
command,
|
|
@@ -357,9 +458,7 @@ const getStepInitialStatus = (step, statusFlags) => {
|
|
|
357
458
|
createKeystore: statusFlags.hasAndroidKeystore,
|
|
358
459
|
createServiceAccount: statusFlags.hasServiceAccountKey,
|
|
359
460
|
createGooglePlayGame: statusFlags.hasGooglePlayGame,
|
|
360
|
-
inviteServiceAccount: statusFlags.hasInvitedServiceAccount
|
|
361
|
-
connectGoogle: false,
|
|
362
|
-
createInitialBuild: false
|
|
461
|
+
inviteServiceAccount: statusFlags.hasInvitedServiceAccount
|
|
363
462
|
};
|
|
364
463
|
return base[step] ? "SUCCESS" /* SUCCESS */ : "PENDING" /* PENDING */;
|
|
365
464
|
};
|
|
@@ -399,7 +498,7 @@ const getStatusFlags = async (cmd) => {
|
|
|
399
498
|
|
|
400
499
|
const StepLabels = {
|
|
401
500
|
createGame: "Create game in ShipThis",
|
|
402
|
-
createKeystore: "Create an Android Keystore",
|
|
501
|
+
createKeystore: "Create or import an Android Keystore",
|
|
403
502
|
connectGoogle: "Connect ShipThis with Google",
|
|
404
503
|
createServiceAccount: "Create a Service Account & API Key",
|
|
405
504
|
createInitialBuild: "Create an initial build",
|
|
@@ -438,7 +537,7 @@ const StepStatusTable = ({ stepStatuses }) => {
|
|
|
438
537
|
|
|
439
538
|
const stepComponentMap = {
|
|
440
539
|
createGame: CreateGame,
|
|
441
|
-
createKeystore:
|
|
540
|
+
createKeystore: CreateOrImport,
|
|
442
541
|
connectGoogle: ConnectGoogle,
|
|
443
542
|
createServiceAccount: CreateServiceAccountKey,
|
|
444
543
|
createInitialBuild: CreateInitialBuild,
|
package/dist/commands/status.js
CHANGED
|
@@ -2,7 +2,7 @@ import { jsxs, jsx } from 'react/jsx-runtime';
|
|
|
2
2
|
import { render } from 'ink';
|
|
3
3
|
import { T as BaseCommand, j as isCWDGodotGame } from '../index-BB7X1Pqp.js';
|
|
4
4
|
import 'react';
|
|
5
|
-
import { C as Command } from '../Command-
|
|
5
|
+
import { C as Command } from '../Command-BiB9MqbJ.js';
|
|
6
6
|
import 'ink-spinner';
|
|
7
7
|
import 'axios';
|
|
8
8
|
import 'crypto-js';
|
|
@@ -11,17 +11,17 @@ import 'luxon';
|
|
|
11
11
|
import 'fs';
|
|
12
12
|
import '@inkjs/ui';
|
|
13
13
|
import '@tanstack/react-query';
|
|
14
|
-
import '
|
|
14
|
+
import 'yazl';
|
|
15
15
|
import 'crypto';
|
|
16
16
|
import 'readline-sync';
|
|
17
17
|
import 'node:readline';
|
|
18
18
|
import 'node:path';
|
|
19
19
|
import 'node:url';
|
|
20
20
|
import 'fast-glob';
|
|
21
|
-
import 'yazl';
|
|
22
21
|
import 'socket.io-client';
|
|
23
22
|
import { i as isCWDGitRepo } from '../git-DREGq-jc.js';
|
|
24
23
|
import '@oclif/core';
|
|
24
|
+
import 'open';
|
|
25
25
|
import { N as NextSteps } from '../NextSteps-CK9zHOCt.js';
|
|
26
26
|
import 'qrcode';
|
|
27
27
|
import { S as StatusTable } from '../StatusTable-Dm5St4g-.js';
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import axios from 'axios';
|
|
2
|
+
import { promises } from 'fs';
|
|
3
|
+
import { p as getAuthedHeaders, q as API_URL } from './index-BB7X1Pqp.js';
|
|
4
|
+
|
|
5
|
+
async function getNewImportTicket(projectId) {
|
|
6
|
+
const url = projectId ? `${API_URL}/projects/${projectId}/credentials/import/url` : `${API_URL}/credentials/import/url`;
|
|
7
|
+
const headers = getAuthedHeaders();
|
|
8
|
+
const { data: importInfo } = await axios({
|
|
9
|
+
method: "post",
|
|
10
|
+
url,
|
|
11
|
+
headers
|
|
12
|
+
});
|
|
13
|
+
return importInfo;
|
|
14
|
+
}
|
|
15
|
+
async function importCredential({
|
|
16
|
+
projectId,
|
|
17
|
+
zipPath,
|
|
18
|
+
type,
|
|
19
|
+
platform
|
|
20
|
+
}) {
|
|
21
|
+
const importTicket = await getNewImportTicket(projectId);
|
|
22
|
+
const zipBuffer = await promises.readFile(zipPath);
|
|
23
|
+
await axios.put(importTicket.url, zipBuffer, {
|
|
24
|
+
headers: {
|
|
25
|
+
"Content-length": zipBuffer.length,
|
|
26
|
+
"Content-Type": "application/zip"
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
const headers = getAuthedHeaders();
|
|
30
|
+
const url = projectId ? `${API_URL}/projects/${projectId}/credentials/import` : `${API_URL}/credentials/import`;
|
|
31
|
+
const { data: publicCredential } = await axios({
|
|
32
|
+
method: "post",
|
|
33
|
+
url,
|
|
34
|
+
headers,
|
|
35
|
+
data: {
|
|
36
|
+
uuid: importTicket.uuid,
|
|
37
|
+
type,
|
|
38
|
+
platform
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
if (projectId) {
|
|
42
|
+
return publicCredential;
|
|
43
|
+
}
|
|
44
|
+
return publicCredential;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export { importCredential as i };
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { jsx, Fragment, jsxs } from 'react/jsx-runtime';
|
|
2
|
+
import { Box, Text } from 'ink';
|
|
3
|
+
import { useState, useRef, useEffect, useContext } from 'react';
|
|
4
|
+
import Spinner from 'ink-spinner';
|
|
5
|
+
import { b as GameContext } from './Command-BiB9MqbJ.js';
|
|
6
|
+
import { p as getAuthedHeaders, q as API_URL, F as castObjectDates, P as Platform, C as CredentialsType, Q as getGoogleStatus } from './index-BB7X1Pqp.js';
|
|
7
|
+
import axios from 'axios';
|
|
8
|
+
import 'crypto-js';
|
|
9
|
+
import 'uuid';
|
|
10
|
+
import 'luxon';
|
|
11
|
+
import 'fs';
|
|
12
|
+
import '@inkjs/ui';
|
|
13
|
+
import { useQuery, useQueryClient } from '@tanstack/react-query';
|
|
14
|
+
import 'yazl';
|
|
15
|
+
import 'crypto';
|
|
16
|
+
import 'readline-sync';
|
|
17
|
+
import 'node:readline';
|
|
18
|
+
import 'node:path';
|
|
19
|
+
import 'node:url';
|
|
20
|
+
import { c as cacheKeys } from './useAndroidServiceAccountTestResult-Ce1x0Eh8.js';
|
|
21
|
+
import 'fast-glob';
|
|
22
|
+
import 'socket.io-client';
|
|
23
|
+
import 'isomorphic-git';
|
|
24
|
+
import '@oclif/core';
|
|
25
|
+
import { u as useWebSocket } from './useWebSocket-CBqsjHbt.js';
|
|
26
|
+
import { u as useProjectCredentials } from './useProjectCredentials-vjedBbKl.js';
|
|
27
|
+
import 'open';
|
|
28
|
+
import { P as ProgressSpinner } from './ProgressSpinner-6pw1T8Iw.js';
|
|
29
|
+
import 'qrcode';
|
|
30
|
+
import 'string-length';
|
|
31
|
+
import 'strip-ansi';
|
|
32
|
+
|
|
33
|
+
async function fetchStatus({ projectId }) {
|
|
34
|
+
try {
|
|
35
|
+
if (!projectId) throw new Error("projectId is required");
|
|
36
|
+
const headers = getAuthedHeaders();
|
|
37
|
+
const url = `${API_URL}/projects/${projectId}/credentials/android/key/status/`;
|
|
38
|
+
const response = await axios.get(url, { headers });
|
|
39
|
+
return castObjectDates(response.data);
|
|
40
|
+
} catch (error) {
|
|
41
|
+
console.warn("fetchStatus Error", error);
|
|
42
|
+
throw error;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
const useAndroidServiceAccountSetupStatus = (props) => {
|
|
46
|
+
return useQuery({
|
|
47
|
+
queryKey: cacheKeys.androidSetupStatus(props),
|
|
48
|
+
queryFn: () => fetchStatus(props),
|
|
49
|
+
// Status changes frequently, so we want to keep it fresh
|
|
50
|
+
refetchInterval: 1e3 * 5,
|
|
51
|
+
staleTime: 1e3 * 5
|
|
52
|
+
});
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
const ERR_NOT_AUTHENTICATED = "You must be connected to Google to create a Service Account Key";
|
|
56
|
+
const useHasServiceAccountKey = (projectId) => {
|
|
57
|
+
const { data, isSuccess } = useProjectCredentials({ projectId, platform: Platform.ANDROID });
|
|
58
|
+
return isSuccess && data.data.some((cred) => cred.isActive && cred.platform === Platform.ANDROID && cred.type == CredentialsType.KEY);
|
|
59
|
+
};
|
|
60
|
+
const useAndroidServiceAccount = ({ projectId, onError, onComplete }) => {
|
|
61
|
+
const queryClient = useQueryClient();
|
|
62
|
+
const [isStarting, setIsStarting] = useState(false);
|
|
63
|
+
const hasServiceAccountKey = useHasServiceAccountKey(projectId);
|
|
64
|
+
const listener = {
|
|
65
|
+
getPattern: () => `project.${projectId}:android-setup-status`,
|
|
66
|
+
eventHandler: async (pattern, data) => {
|
|
67
|
+
const key = cacheKeys.androidSetupStatus({ projectId });
|
|
68
|
+
queryClient.setQueryData(key, () => data);
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
useWebSocket([listener]);
|
|
72
|
+
const { data: setupStatus } = useAndroidServiceAccountSetupStatus({ projectId });
|
|
73
|
+
const prevSetupStatusRef = useRef("unknown");
|
|
74
|
+
useEffect(() => {
|
|
75
|
+
if (["running", "queued"].includes(prevSetupStatusRef.current)) {
|
|
76
|
+
if (setupStatus?.status === "complete") onComplete();
|
|
77
|
+
if (setupStatus?.status === "error") onError(new Error(setupStatus.errorMessage));
|
|
78
|
+
}
|
|
79
|
+
prevSetupStatusRef.current = setupStatus?.status || "unknown";
|
|
80
|
+
}, [setupStatus]);
|
|
81
|
+
const handleStart = async () => {
|
|
82
|
+
try {
|
|
83
|
+
setIsStarting(true);
|
|
84
|
+
const currentStatus = await getGoogleStatus();
|
|
85
|
+
if (!currentStatus.isAuthenticated) throw new Error(ERR_NOT_AUTHENTICATED);
|
|
86
|
+
const headers = getAuthedHeaders();
|
|
87
|
+
const androidKeyApiBase = `${API_URL}/projects/${projectId}/credentials/android/key`;
|
|
88
|
+
const startUrl = `${androidKeyApiBase}/setup/`;
|
|
89
|
+
const { data: updatedStatus } = await axios.post(startUrl, {}, { headers });
|
|
90
|
+
queryClient.invalidateQueries({
|
|
91
|
+
queryKey: cacheKeys.projectCredentials({ projectId, pageNumber: 0 })
|
|
92
|
+
});
|
|
93
|
+
await queryClient.setQueryData(cacheKeys.androidSetupStatus({ projectId }), (_) => updatedStatus);
|
|
94
|
+
setIsStarting(false);
|
|
95
|
+
return true;
|
|
96
|
+
} catch (error) {
|
|
97
|
+
setIsStarting(false);
|
|
98
|
+
console.warn("useAndroidServiceAccount.handleStart Error", error);
|
|
99
|
+
onError(error);
|
|
100
|
+
return false;
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
const isCreating = isStarting || setupStatus?.status === "queued" || setupStatus?.status === "running";
|
|
104
|
+
return {
|
|
105
|
+
handleStart,
|
|
106
|
+
setupStatus,
|
|
107
|
+
isCreating,
|
|
108
|
+
hasServiceAccountKey
|
|
109
|
+
};
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
const CreateServiceAccountKey = (props) => {
|
|
113
|
+
const { gameId } = useContext(GameContext);
|
|
114
|
+
return /* @__PURE__ */ jsx(Fragment, { children: gameId && /* @__PURE__ */ jsx(CreateForGame, { gameId, ...props }) });
|
|
115
|
+
};
|
|
116
|
+
const CreateForGame = ({ onComplete, onError, gameId, ...boxProps }) => {
|
|
117
|
+
const [didStart, setDidStart] = useState(false);
|
|
118
|
+
const { handleStart, setupStatus, isCreating } = useAndroidServiceAccount({
|
|
119
|
+
projectId: gameId,
|
|
120
|
+
onError,
|
|
121
|
+
onComplete
|
|
122
|
+
});
|
|
123
|
+
useEffect(() => {
|
|
124
|
+
handleStart().then(() => setDidStart(true));
|
|
125
|
+
}, [gameId]);
|
|
126
|
+
return /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsxs(Box, { flexDirection: "column", gap: 1, ...boxProps, children: [
|
|
127
|
+
/* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 1, children: [
|
|
128
|
+
/* @__PURE__ */ jsx(Text, { children: "Creating a Service Account and API Key..." }),
|
|
129
|
+
isCreating && /* @__PURE__ */ jsx(Spinner, { type: "dots" })
|
|
130
|
+
] }),
|
|
131
|
+
didStart && /* @__PURE__ */ jsx(ProgressSpinner, { progress: (setupStatus?.progress || 0) * 100, spinnerType: "dots" })
|
|
132
|
+
] }) });
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
export { CreateServiceAccountKey as C };
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { jsx, Fragment, jsxs } from 'react/jsx-runtime';
|
|
2
|
+
import { Text, useInput, Box } from 'ink';
|
|
3
|
+
import open from 'open';
|
|
4
|
+
import { useState, useEffect, useContext } from 'react';
|
|
5
|
+
import { Q as getGoogleStatus, a2 as getShortAuthRequiredUrl, a3 as getGoogleAuthUrl, X as WEB_URL } from './index-BB7X1Pqp.js';
|
|
6
|
+
import 'crypto';
|
|
7
|
+
import 'fs';
|
|
8
|
+
import 'readline-sync';
|
|
9
|
+
import 'node:readline';
|
|
10
|
+
import 'node:path';
|
|
11
|
+
import 'node:url';
|
|
12
|
+
import 'axios';
|
|
13
|
+
import { useQuery } from '@tanstack/react-query';
|
|
14
|
+
import 'crypto-js';
|
|
15
|
+
import 'uuid';
|
|
16
|
+
import 'luxon';
|
|
17
|
+
import 'fast-glob';
|
|
18
|
+
import 'yazl';
|
|
19
|
+
import 'socket.io-client';
|
|
20
|
+
import { u as useWebSocket } from './useWebSocket-CBqsjHbt.js';
|
|
21
|
+
import { c as cacheKeys } from './useAndroidServiceAccountTestResult-Ce1x0Eh8.js';
|
|
22
|
+
import 'isomorphic-git';
|
|
23
|
+
import '@oclif/core';
|
|
24
|
+
import { b as GameContext, M as Markdown } from './Command-BiB9MqbJ.js';
|
|
25
|
+
import 'ink-spinner';
|
|
26
|
+
import '@inkjs/ui';
|
|
27
|
+
import 'string-length';
|
|
28
|
+
import 'strip-ansi';
|
|
29
|
+
import qrcode from 'qrcode';
|
|
30
|
+
|
|
31
|
+
const useGoogleStatus = () => {
|
|
32
|
+
return useQuery({
|
|
33
|
+
queryKey: cacheKeys.googleStatus(),
|
|
34
|
+
queryFn: getGoogleStatus
|
|
35
|
+
});
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
function useGoogleStatusWatching({
|
|
39
|
+
projectId,
|
|
40
|
+
isWatching,
|
|
41
|
+
onGoogleStatusUpdate
|
|
42
|
+
}) {
|
|
43
|
+
const [wsGoogleStatus, setWsGoogleStatus] = useState(null);
|
|
44
|
+
const listener = {
|
|
45
|
+
getPattern: () => `project.${projectId}:google-status`,
|
|
46
|
+
eventHandler: async (pattern, data2) => {
|
|
47
|
+
setWsGoogleStatus(data2);
|
|
48
|
+
if (onGoogleStatusUpdate) onGoogleStatusUpdate(data2);
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
useWebSocket([listener] );
|
|
52
|
+
const { isLoading, data: googleStatus } = useGoogleStatus();
|
|
53
|
+
useEffect(() => {
|
|
54
|
+
setWsGoogleStatus(null);
|
|
55
|
+
}, [projectId, isWatching, googleStatus]);
|
|
56
|
+
const fetchedGoogleStatus = googleStatus ? googleStatus : null;
|
|
57
|
+
const data = wsGoogleStatus ? wsGoogleStatus : fetchedGoogleStatus;
|
|
58
|
+
return {
|
|
59
|
+
isLoading,
|
|
60
|
+
data
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const QRCodeTerminal = ({ url }) => {
|
|
65
|
+
const [code, setCode] = useState(null);
|
|
66
|
+
const handleLoad = async () => {
|
|
67
|
+
const codeString = await qrcode.toString(url, { type: "terminal", errorCorrectionLevel: "L", small: true });
|
|
68
|
+
setCode(codeString);
|
|
69
|
+
};
|
|
70
|
+
useEffect(() => {
|
|
71
|
+
handleLoad();
|
|
72
|
+
}, []);
|
|
73
|
+
return /* @__PURE__ */ jsx(Fragment, { children: code && /* @__PURE__ */ jsx(Text, { children: code }) });
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
async function getConnectUrl(gameId, helpPage) {
|
|
77
|
+
const helpPagePath = `/docs/android?gameId=${gameId}#2-connect-shipthis-with-google`;
|
|
78
|
+
const url = helpPage ? await getShortAuthRequiredUrl(helpPagePath) : await getGoogleAuthUrl(gameId);
|
|
79
|
+
return url;
|
|
80
|
+
}
|
|
81
|
+
const GoogleAuthQRCode = ({ gameId, helpPage }) => {
|
|
82
|
+
const [url, setUrl] = useState(null);
|
|
83
|
+
const handleLoad = async () => {
|
|
84
|
+
const url2 = await getConnectUrl(gameId, helpPage);
|
|
85
|
+
setUrl(url2);
|
|
86
|
+
};
|
|
87
|
+
useEffect(() => {
|
|
88
|
+
handleLoad();
|
|
89
|
+
}, []);
|
|
90
|
+
return /* @__PURE__ */ jsx(Fragment, { children: url && /* @__PURE__ */ jsx(QRCodeTerminal, { url }) });
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
const ConnectGoogle = (props) => {
|
|
94
|
+
const { gameId } = useContext(GameContext);
|
|
95
|
+
return /* @__PURE__ */ jsx(Fragment, { children: gameId && /* @__PURE__ */ jsx(ConnectForGame, { gameId, ...props }) });
|
|
96
|
+
};
|
|
97
|
+
const ConnectForGame = ({ onComplete, onError, helpPage, gameId, ...boxProps }) => {
|
|
98
|
+
useGoogleStatusWatching({
|
|
99
|
+
projectId: gameId,
|
|
100
|
+
isWatching: true,
|
|
101
|
+
onGoogleStatusUpdate: (status) => {
|
|
102
|
+
if (status.isAuthenticated) return onComplete();
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
useInput(async (input) => {
|
|
106
|
+
if (!gameId) return;
|
|
107
|
+
if (input !== "d") return;
|
|
108
|
+
const url = await getConnectUrl(gameId, true);
|
|
109
|
+
await open(url);
|
|
110
|
+
});
|
|
111
|
+
const templateVars = {
|
|
112
|
+
privacyURL: new URL("/privacy", WEB_URL).toString()
|
|
113
|
+
};
|
|
114
|
+
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", gap: 1, ...boxProps, children: [
|
|
115
|
+
/* @__PURE__ */ jsx(Markdown, { filename: "privacy-notification.md", templateVars }),
|
|
116
|
+
/* @__PURE__ */ jsx(Text, { children: "Scan the QR code below to connect your Google account to ShipThis:" }),
|
|
117
|
+
gameId && /* @__PURE__ */ jsx(GoogleAuthQRCode, { gameId, helpPage: !!helpPage }),
|
|
118
|
+
/* @__PURE__ */ jsx(Text, { children: "Or press D to sign-in using your browser" })
|
|
119
|
+
] });
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
export { ConnectGoogle as C };
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { u as useJob } from './Command-BiB9MqbJ.js';
|
|
2
|
+
import { useState, useEffect } from 'react';
|
|
3
|
+
import { u as useWebSocket } from './useWebSocket-CBqsjHbt.js';
|
|
4
|
+
import { E as castJobDates, F as castObjectDates } from './index-BB7X1Pqp.js';
|
|
5
|
+
|
|
6
|
+
function useJobWatching({ projectId, jobId, isWatching, onJobUpdate }) {
|
|
7
|
+
const [websocketJob, setWebsocketJob] = useState(null);
|
|
8
|
+
const [mostRecentLog, setMostRecentLog] = useState(null);
|
|
9
|
+
const jobStatusListener = {
|
|
10
|
+
getPattern: () => [`project.${projectId}:job:created`, `project.${projectId}:job:updated`],
|
|
11
|
+
eventHandler: async (pattern, rawJob) => {
|
|
12
|
+
if (rawJob.id !== jobId) return;
|
|
13
|
+
const job2 = castJobDates(rawJob);
|
|
14
|
+
setWebsocketJob(job2);
|
|
15
|
+
if (onJobUpdate) onJobUpdate(job2);
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
const jobProgressListener = {
|
|
19
|
+
getPattern: () => `project.${projectId}:job.${jobId}:log`,
|
|
20
|
+
eventHandler: async (pattern, rawLogEntry) => {
|
|
21
|
+
const logEntry = castObjectDates(rawLogEntry, ["sentAt", "createdAt"]);
|
|
22
|
+
setMostRecentLog(logEntry);
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
useWebSocket(isWatching ? [jobStatusListener, jobProgressListener] : []);
|
|
26
|
+
const { isLoading, data: job } = useJob({
|
|
27
|
+
projectId,
|
|
28
|
+
jobId
|
|
29
|
+
});
|
|
30
|
+
useEffect(() => {
|
|
31
|
+
setWebsocketJob(null);
|
|
32
|
+
}, [jobId, projectId, isWatching, job]);
|
|
33
|
+
const fetchedJob = job ? job : null;
|
|
34
|
+
const data = websocketJob ? websocketJob : fetchedJob;
|
|
35
|
+
const progress = mostRecentLog?.progress || null;
|
|
36
|
+
const stage = mostRecentLog?.stage || null;
|
|
37
|
+
return {
|
|
38
|
+
isLoading,
|
|
39
|
+
data,
|
|
40
|
+
progress,
|
|
41
|
+
stage
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export { useJobWatching as u };
|
package/npm-shrinkwrap.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "shipthis",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.11",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "shipthis",
|
|
9
|
-
"version": "0.1.
|
|
9
|
+
"version": "0.1.11",
|
|
10
10
|
"license": "MIT",
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"@expo/apple-utils": "2.0.3",
|
package/oclif.manifest.json
CHANGED
|
@@ -1827,14 +1827,15 @@
|
|
|
1827
1827
|
"aliases": [],
|
|
1828
1828
|
"args": {
|
|
1829
1829
|
"file": {
|
|
1830
|
-
"description": "
|
|
1830
|
+
"description": "Path to the ZIP file to import (must be in the same format as the export)",
|
|
1831
1831
|
"name": "file",
|
|
1832
|
-
"required":
|
|
1832
|
+
"required": false
|
|
1833
1833
|
}
|
|
1834
1834
|
},
|
|
1835
1835
|
"description": "Imports an Android Keystore to your ShipThis account for the specified game.",
|
|
1836
1836
|
"examples": [
|
|
1837
|
-
"<%= config.bin %> <%= command.id %>"
|
|
1837
|
+
"<%= config.bin %> <%= command.id %> path/to/import.zip -g abfd5b00",
|
|
1838
|
+
"<%= config.bin %> <%= command.id %> --jksFile path/to/file.jks --keystorePassword yourpass --keyPassword yourkeypass"
|
|
1838
1839
|
],
|
|
1839
1840
|
"flags": {
|
|
1840
1841
|
"gameId": {
|
|
@@ -1845,8 +1846,30 @@
|
|
|
1845
1846
|
"multiple": false,
|
|
1846
1847
|
"type": "option"
|
|
1847
1848
|
},
|
|
1849
|
+
"jksFile": {
|
|
1850
|
+
"description": "Path to the JKS file to import (requires passwords)",
|
|
1851
|
+
"name": "jksFile",
|
|
1852
|
+
"hasDynamicHelp": false,
|
|
1853
|
+
"multiple": false,
|
|
1854
|
+
"type": "option"
|
|
1855
|
+
},
|
|
1856
|
+
"keystorePassword": {
|
|
1857
|
+
"description": "Keystore password (required when using --jksFile)",
|
|
1858
|
+
"name": "keystorePassword",
|
|
1859
|
+
"hasDynamicHelp": false,
|
|
1860
|
+
"multiple": false,
|
|
1861
|
+
"type": "option"
|
|
1862
|
+
},
|
|
1863
|
+
"keyPassword": {
|
|
1864
|
+
"description": "Key alias password (required when using --jksFile)",
|
|
1865
|
+
"name": "keyPassword",
|
|
1866
|
+
"hasDynamicHelp": false,
|
|
1867
|
+
"multiple": false,
|
|
1868
|
+
"type": "option"
|
|
1869
|
+
},
|
|
1848
1870
|
"force": {
|
|
1849
1871
|
"char": "f",
|
|
1872
|
+
"description": "Overwrite any existing keystore without confirmation",
|
|
1850
1873
|
"name": "force",
|
|
1851
1874
|
"allowNo": false,
|
|
1852
1875
|
"type": "boolean"
|
|
@@ -2297,5 +2320,5 @@
|
|
|
2297
2320
|
]
|
|
2298
2321
|
}
|
|
2299
2322
|
},
|
|
2300
|
-
"version": "0.1.
|
|
2323
|
+
"version": "0.1.11"
|
|
2301
2324
|
}
|
package/package.json
CHANGED