shipthis 0.1.23 → 0.1.25

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.
Files changed (89) hide show
  1. package/README.md +25 -16
  2. package/docs/assets/ship-outputx0.8.gif +0 -0
  3. package/package.json +1 -1
  4. package/bin/readme.sh +0 -15
  5. package/dist/AppleBundleIdDetails-C9C5WSPR.js +0 -76
  6. package/dist/Command-BQCJ9Wf-.js +0 -28
  7. package/dist/CommandGame-CkGqTno0.js +0 -9
  8. package/dist/Create-t_E231OA.js +0 -59
  9. package/dist/Import-Ljz1gxXd.js +0 -110
  10. package/dist/JobProgress-D9tHgBvi.js +0 -121
  11. package/dist/JobStatusTable-DmI7qCHc.js +0 -203
  12. package/dist/NextSteps-CK9zHOCt.js +0 -18
  13. package/dist/ProgressSpinner-6pw1T8Iw.js +0 -16
  14. package/dist/ProjectCredentialsTable-CTkP1mvy.js +0 -37
  15. package/dist/RunWithSpinner-BVXNWGD3.js +0 -27
  16. package/dist/StatusTable-Dm5St4g-.js +0 -33
  17. package/dist/Table-CvM6pccN.js +0 -101
  18. package/dist/Title-BCQtayg6.js +0 -6
  19. package/dist/UserCredentialsTable-DhtM_iTG.js +0 -82
  20. package/dist/baseAppleCommand-IGl6KTvv.js +0 -10
  21. package/dist/baseGameAndroidCommand-DFn4zMvq.js +0 -43
  22. package/dist/commands/apple/apiKey/create.js +0 -103
  23. package/dist/commands/apple/apiKey/export.js +0 -81
  24. package/dist/commands/apple/apiKey/import.js +0 -85
  25. package/dist/commands/apple/apiKey/status.js +0 -122
  26. package/dist/commands/apple/certificate/create.js +0 -133
  27. package/dist/commands/apple/certificate/export.js +0 -81
  28. package/dist/commands/apple/certificate/import.js +0 -85
  29. package/dist/commands/apple/certificate/status.js +0 -130
  30. package/dist/commands/apple/login.js +0 -76
  31. package/dist/commands/apple/status.js +0 -79
  32. package/dist/commands/dashboard.js +0 -38
  33. package/dist/commands/game/android/apiKey/connect.js +0 -74
  34. package/dist/commands/game/android/apiKey/create.js +0 -74
  35. package/dist/commands/game/android/apiKey/export.js +0 -84
  36. package/dist/commands/game/android/apiKey/import.js +0 -93
  37. package/dist/commands/game/android/apiKey/invite.js +0 -81
  38. package/dist/commands/game/android/apiKey/status.js +0 -87
  39. package/dist/commands/game/android/keyStore/create.js +0 -69
  40. package/dist/commands/game/android/keyStore/export.js +0 -83
  41. package/dist/commands/game/android/keyStore/import.js +0 -112
  42. package/dist/commands/game/android/keyStore/status.js +0 -70
  43. package/dist/commands/game/android/status.js +0 -84
  44. package/dist/commands/game/build/download.js +0 -80
  45. package/dist/commands/game/build/list.js +0 -94
  46. package/dist/commands/game/create.js +0 -67
  47. package/dist/commands/game/details.js +0 -113
  48. package/dist/commands/game/export.js +0 -58
  49. package/dist/commands/game/ios/app/addTester.js +0 -124
  50. package/dist/commands/game/ios/app/create.js +0 -117
  51. package/dist/commands/game/ios/app/status.js +0 -66
  52. package/dist/commands/game/ios/app/sync.js +0 -95
  53. package/dist/commands/game/ios/profile/create.js +0 -129
  54. package/dist/commands/game/ios/profile/export.js +0 -83
  55. package/dist/commands/game/ios/profile/import.js +0 -92
  56. package/dist/commands/game/ios/profile/status.js +0 -139
  57. package/dist/commands/game/ios/status.js +0 -92
  58. package/dist/commands/game/ios/wizard.js +0 -153
  59. package/dist/commands/game/job/list.js +0 -91
  60. package/dist/commands/game/job/status.js +0 -91
  61. package/dist/commands/game/list.js +0 -83
  62. package/dist/commands/game/ship.js +0 -205
  63. package/dist/commands/game/status.js +0 -114
  64. package/dist/commands/game/wizard.js +0 -686
  65. package/dist/commands/internal/fastlane.js +0 -74
  66. package/dist/commands/internal/readme.js +0 -937
  67. package/dist/commands/login.js +0 -92
  68. package/dist/commands/status.js +0 -76
  69. package/dist/export-CVs_xoDN.js +0 -36
  70. package/dist/git-DREGq-jc.js +0 -32
  71. package/dist/import-Ch5O7xfN.js +0 -47
  72. package/dist/index-BB00V5oF.js +0 -136
  73. package/dist/index-CIa2EDQ6.js +0 -24
  74. package/dist/index-DkNQs11R.js +0 -711
  75. package/dist/index-DrcGhlrc.js +0 -138
  76. package/dist/index-DyOv-ge5.js +0 -125
  77. package/dist/index-vMXsdSCy.js +0 -208
  78. package/dist/index.d.ts +0 -1
  79. package/dist/index.js +0 -1
  80. package/dist/upload-CRE2nVdd.js +0 -60
  81. package/dist/useAndroidServiceAccountTestResult-DcYDam-p.js +0 -52
  82. package/dist/useAppleApp-B16WbUxJ.js +0 -32
  83. package/dist/useAppleBundleId-DobPATan.js +0 -64
  84. package/dist/useJobWatching-BZSUa8E-.js +0 -45
  85. package/dist/useProjectCredentials-Btnr7WK3.js +0 -54
  86. package/dist/useWebSocket-ByuNoqRw.js +0 -36
  87. package/dist/utils/help.js +0 -14
  88. package/npm-shrinkwrap.json +0 -14011
  89. package/oclif.manifest.json +0 -2275
@@ -1,686 +0,0 @@
1
- import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
2
- import { Args } from '@oclif/core';
3
- import { Text, Box, useInput, useFocus, measureElement, render } from 'ink';
4
- import { p as getAuthedHeaders, q as API_URL, L as castArrayObjectDates, M 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, W as WEB_URL, I as getProject, C as CredentialsType, T as getGoogleStatus, B as BaseAuthenticatedCommand, j as isCWDGodotGame } from '../../index-DkNQs11R.js';
5
- import React, { useState, useContext, useEffect, useRef, useReducer } from 'react';
6
- import { e as CommandContext, b as GameContext, M as Markdown, c as useBuilds, q as queryBuilds, G as GameProvider, f as CreateGooglePlayGame } from '../../index-vMXsdSCy.js';
7
- import Spinner from 'ink-spinner';
8
- import { Alert, TextInput } from '@inkjs/ui';
9
- import axios from 'axios';
10
- import 'crypto-js';
11
- import 'uuid';
12
- import fs__default from 'fs';
13
- import 'luxon';
14
- import { useQuery, useMutation } from '@tanstack/react-query';
15
- import 'yazl';
16
- import 'crypto';
17
- import 'readline-sync';
18
- import 'node:readline';
19
- import 'node:path';
20
- import 'node:url';
21
- import 'isomorphic-git';
22
- import 'fast-glob';
23
- import 'socket.io-client';
24
- import 'open';
25
- import 'marked';
26
- import 'marked-terminal';
27
- import 'path';
28
- import 'qrcode';
29
- import 'string-length';
30
- import 'strip-ansi';
31
- import { C as CreateKeystore } from '../../Create-t_E231OA.js';
32
- import { I as ImportKeystore } from '../../Import-Ljz1gxXd.js';
33
- import { C as ConnectGoogle } from '../../index-DyOv-ge5.js';
34
- import { C as CreateServiceAccountKey } from '../../index-DrcGhlrc.js';
35
- import { c as cacheKeys, f as fetchKeyTestResult, K as KeyTestStatus, a as KeyTestError } from '../../useAndroidServiceAccountTestResult-DcYDam-p.js';
36
- import { u as useShip, J as JobProgress } from '../../JobProgress-D9tHgBvi.js';
37
- import { a as getProjectCredentials } from '../../index-CIa2EDQ6.js';
38
- import { T as Title } from '../../Title-BCQtayg6.js';
39
- import { C as Command } from '../../Command-BQCJ9Wf-.js';
40
- import '@expo/apple-utils/build/index.js';
41
- import 'ini';
42
- import 'deepmerge';
43
- import '../../index-BB00V5oF.js';
44
- import '../../RunWithSpinner-BVXNWGD3.js';
45
- import '../../import-Ch5O7xfN.js';
46
- import '../../useWebSocket-ByuNoqRw.js';
47
- import '../../useProjectCredentials-Btnr7WK3.js';
48
- import '../../ProgressSpinner-6pw1T8Iw.js';
49
- import '../../git-DREGq-jc.js';
50
- import '../../useJobWatching-BZSUa8E-.js';
51
-
52
- async function queryJobs({ projectId, ...pageAndSortParams }) {
53
- try {
54
- const headers = getAuthedHeaders();
55
- const url = `${API_URL}/projects/${projectId}/jobs`;
56
- const response = await axios.get(url, { headers, params: pageAndSortParams });
57
- return {
58
- ...response.data,
59
- data: castArrayObjectDates(response.data.data)
60
- };
61
- } catch (error) {
62
- console.warn("queryJobs Error", error);
63
- throw error;
64
- }
65
- }
66
- const useJobs = (props) => {
67
- const queryResult = useQuery({
68
- queryKey: cacheKeys.jobs(props),
69
- queryFn: async () => queryJobs(props)
70
- });
71
- return queryResult;
72
- };
73
-
74
- const useInviteServiceAccount = () => {
75
- return useMutation({
76
- mutationFn: async ({ projectId, developerId }) => {
77
- try {
78
- const headers = getAuthedHeaders();
79
- const { data } = await axios.post(
80
- `${API_URL}/projects/${projectId}/credentials/android/key/invite/`,
81
- { developerId },
82
- {
83
- headers
84
- }
85
- );
86
- return data;
87
- } catch (error) {
88
- console.error("useInviteMutation Error", error);
89
- throw error;
90
- }
91
- },
92
- onSuccess: async (data) => {
93
- const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
94
- await sleep(1e3);
95
- queryClient.invalidateQueries({
96
- queryKey: cacheKeys.androidKeyTestResult({ projectId: data.projectId })
97
- });
98
- }
99
- });
100
- };
101
-
102
- const GameInfoForm = ({ gameInfo, onSubmit }) => {
103
- const [activeInput, setActiveInput] = useState("name");
104
- const [error, setError] = useState(null);
105
- const [name, setName] = useState(gameInfo.name);
106
- const [androidPackageName, setAndroidPackageName] = useState(gameInfo?.details?.androidPackageName);
107
- const handleSubmitName = () => {
108
- setError(null);
109
- if (name.length === 0) {
110
- setError("Please enter a name for your game");
111
- return;
112
- }
113
- setActiveInput("androidPackageName");
114
- };
115
- const handleSubmitPackageName = () => {
116
- setError(null);
117
- const packageRegex = /^[a-zA-Z][a-zA-Z0-9_]*(\.[a-zA-Z][a-zA-Z0-9_]*)+$/;
118
- if (!packageRegex.test(`${androidPackageName}`)) {
119
- setError("Please enter a valid package name e.g. com.flappy.souls");
120
- return;
121
- }
122
- onSubmit({
123
- ...gameInfo,
124
- name,
125
- details: {
126
- ...gameInfo.details,
127
- androidPackageName
128
- }
129
- });
130
- };
131
- return /* @__PURE__ */ jsxs(Fragment, { children: [
132
- /* @__PURE__ */ jsx(Text, { bold: true, children: "Please confirm the following information about your game" }),
133
- error && /* @__PURE__ */ jsx(Alert, { variant: "error", children: error }),
134
- /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginLeft: 1, children: [
135
- /* @__PURE__ */ jsx(
136
- FormTextInput,
137
- {
138
- label: "Game name:",
139
- isDisabled: activeInput !== "name",
140
- defaultValue: name,
141
- placeholder: "Enter the name of your game...",
142
- onChange: setName,
143
- onSubmit: handleSubmitName
144
- }
145
- ),
146
- /* @__PURE__ */ jsx(
147
- FormTextInput,
148
- {
149
- label: "Android package name :",
150
- isDisabled: activeInput !== "androidPackageName",
151
- defaultValue: androidPackageName,
152
- placeholder: "e.g. com.flappy.souls",
153
- onChange: setAndroidPackageName,
154
- onSubmit: handleSubmitPackageName
155
- }
156
- )
157
- ] })
158
- ] });
159
- };
160
-
161
- const getGameInfo = (flagValues, project) => {
162
- const androidPackageName = flagValues.androidPackageName || project?.details?.androidPackageName || "";
163
- const gameInfo = {
164
- name: project?.name || flagValues.name || "",
165
- details: {
166
- ...project?.details,
167
- androidPackageName
168
- }
169
- };
170
- return gameInfo;
171
- };
172
- const CreateGame = (props) => {
173
- const [isLoading, setIsLoading] = useState(true);
174
- const [gameInfo, setGameInfo] = useState(null);
175
- const [showForm, setShowForm] = useState(false);
176
- const { command } = useContext(CommandContext);
177
- const { setGameId, game } = useContext(GameContext);
178
- const handleLoad = async () => {
179
- if (!command) throw new Error("No command");
180
- const flags = command.getDetailsFlagsValues();
181
- const config = await command.getProjectConfigSafe();
182
- setShowForm(true);
183
- setIsLoading(false);
184
- const info = getGameInfo(flags, config.project);
185
- setGameInfo(info);
186
- };
187
- useEffect(() => {
188
- handleLoad().catch(props.onError);
189
- }, []);
190
- const handleSubmitForm = async (gameInfo2) => {
191
- try {
192
- setShowForm(false);
193
- setIsLoading(true);
194
- if (!command) throw new Error("No command");
195
- const projectConfig = await command.getProjectConfigSafe();
196
- const existingGame = projectConfig.project;
197
- const isNew = !existingGame;
198
- if (!isNew) {
199
- const project2 = await updateProject(existingGame.id, gameInfo2);
200
- const updatedConfig = {
201
- ...projectConfig,
202
- project: project2
203
- };
204
- await command.setProjectConfig(updatedConfig);
205
- return props.onComplete();
206
- }
207
- const { name, details } = gameInfo2;
208
- const projectDetails = {
209
- ...details,
210
- gameEngine: GameEngine.GODOT,
211
- gameEngineVersion: getGodotVersion()
212
- };
213
- const project = await createProject({ name, details: projectDetails });
214
- await command.setProjectConfig({
215
- project,
216
- shippedFilesGlobs: DEFAULT_SHIPPED_FILES_GLOBS,
217
- ignoredFilesGlobs: DEFAULT_IGNORED_FILES_GLOBS
218
- });
219
- setGameId(project.id);
220
- props.onComplete();
221
- } catch (e) {
222
- props.onError(e);
223
- }
224
- };
225
- return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", gap: 1, borderStyle: "single", margin: 1, children: [
226
- isLoading && /* @__PURE__ */ jsx(Spinner, {}),
227
- showForm && gameInfo && /* @__PURE__ */ jsx(GameInfoForm, { gameInfo, onSubmit: handleSubmitForm })
228
- ] });
229
- };
230
-
231
- const FormTextInput = ({ label, labelProps, ...rest }) => /* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 1, children: [
232
- /* @__PURE__ */ jsx(Text, { ...labelProps, children: label }),
233
- /* @__PURE__ */ jsx(TextInput, { ...rest })
234
- ] });
235
-
236
- const ImportForm = ({ importKeystoreProps, onSubmit }) => {
237
- const [activeInput, setActiveInput] = useState("jksFilePath" /* jksFilePath */);
238
- const [error, setError] = useState(null);
239
- const [jksFilePath, setJksFilePath] = useState(importKeystoreProps.jksFilePath);
240
- const [password, setPassword] = useState(importKeystoreProps.keyPassword);
241
- const handleSubmitJksFilePath = () => {
242
- setError(null);
243
- if (!jksFilePath || jksFilePath.length === 0) {
244
- setError("Please enter a path to your jks file");
245
- return;
246
- }
247
- if (!fs__default.existsSync(jksFilePath)) {
248
- setError("The file does not exist");
249
- return;
250
- }
251
- setActiveInput("password" /* password */);
252
- };
253
- const handleSubmitPassword = () => {
254
- setError(null);
255
- if (!password || password.length === 0) {
256
- setError("Please enter a password");
257
- return;
258
- }
259
- onSubmit({
260
- ...importKeystoreProps,
261
- jksFilePath,
262
- keyPassword: password,
263
- keystorePassword: password
264
- });
265
- };
266
- return /* @__PURE__ */ jsxs(Fragment, { children: [
267
- error && /* @__PURE__ */ jsx(Alert, { variant: "error", children: error }),
268
- /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginLeft: 1, children: [
269
- /* @__PURE__ */ jsx(
270
- FormTextInput,
271
- {
272
- label: "Path to your jks file:",
273
- isDisabled: activeInput !== "jksFilePath" /* jksFilePath */,
274
- defaultValue: jksFilePath,
275
- placeholder: "Enter the path to your jks file...",
276
- onChange: setJksFilePath,
277
- onSubmit: handleSubmitJksFilePath
278
- }
279
- ),
280
- /* @__PURE__ */ jsx(
281
- FormTextInput,
282
- {
283
- label: "Password:",
284
- isDisabled: activeInput !== "password" /* password */,
285
- defaultValue: password,
286
- placeholder: "Enter the password for your jks file...",
287
- onChange: setPassword,
288
- onSubmit: handleSubmitPassword
289
- }
290
- )
291
- ] })
292
- ] });
293
- };
294
-
295
- const CreateOrImport = ({ onComplete, onError, ...boxProps }) => {
296
- const [stage, setStage] = useState(0 /* Choose */);
297
- const [importKeystoreProps, setImportKeystoreProps] = useState({
298
- jksFilePath: "",
299
- keyPassword: "",
300
- keystorePassword: ""
301
- });
302
- useInput(async (input) => {
303
- if (stage !== 0 /* Choose */) return;
304
- if (input === "c") return setStage(1 /* Create */);
305
- if (input === "i") return setStage(2 /* ImportForm */);
306
- });
307
- const handleImportFormSubmit = (newImportProps) => {
308
- setImportKeystoreProps(newImportProps);
309
- setStage(3 /* ImportKeystore */);
310
- };
311
- const renderStage = () => {
312
- switch (stage) {
313
- case 0 /* Choose */:
314
- return /* @__PURE__ */ jsxs(Fragment, { children: [
315
- /* @__PURE__ */ jsx(Text, { children: "Would you like to create a new keystore or import an existing one?" }),
316
- /* @__PURE__ */ jsx(Text, { bold: true, children: "Press C to create a new keystore" }),
317
- /* @__PURE__ */ jsx(Text, { bold: true, children: "Press I to import an existing keystore" })
318
- ] });
319
- case 1 /* Create */:
320
- return /* @__PURE__ */ jsx(CreateKeystore, { onComplete, onError });
321
- case 2 /* ImportForm */:
322
- return /* @__PURE__ */ jsx(ImportForm, { onSubmit: handleImportFormSubmit, importKeystoreProps });
323
- case 3 /* ImportKeystore */:
324
- return /* @__PURE__ */ jsx(ImportKeystore, { onComplete, onError, importKeystoreProps });
325
- }
326
- };
327
- return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", gap: 1, ...boxProps, children: [
328
- /* @__PURE__ */ jsx(Markdown, { filename: "create-or-import-keystore.md", templateVars: {} }),
329
- renderStage()
330
- ] });
331
- };
332
-
333
- const CreateInitialBuild = (props) => {
334
- const { gameId } = useContext(GameContext);
335
- return /* @__PURE__ */ jsx(Fragment, { children: gameId && /* @__PURE__ */ jsx(CreateForGame, { gameId, ...props }) });
336
- };
337
- const CreateForGame = ({ onComplete, onError, gameId, ...boxProps }) => {
338
- const { command } = useContext(CommandContext);
339
- const { data: buildData, isLoading: isLoadingBuilds } = useBuilds({ projectId: gameId, pageNumber: 0 });
340
- const { data: jobData, isLoading: isLoadingJobs } = useJobs({
341
- projectId: gameId,
342
- pageNumber: 0
343
- });
344
- const prevHasBuild = useRef(false);
345
- const shipMutation = useShip();
346
- const [shipLog, setShipLog] = useState("");
347
- useEffect(() => {
348
- if (isLoadingBuilds || isLoadingJobs) return;
349
- if (!buildData) return;
350
- if (!jobData) return;
351
- if (!command) return;
352
- const hasAndroidBuild = buildData.data.some((build) => build.platform === Platform.ANDROID);
353
- if (!prevHasBuild.current && hasAndroidBuild) return onComplete();
354
- prevHasBuild.current = hasAndroidBuild;
355
- const hasRunningAndroidJob = jobData.data.some(
356
- (job) => job.type === Platform.ANDROID && [JobStatus.PENDING, JobStatus.PROCESSING].includes(job.status)
357
- );
358
- const shouldRun = !hasAndroidBuild && !hasRunningAndroidJob;
359
- if (shouldRun)
360
- shipMutation.mutateAsync({
361
- command,
362
- log: setShipLog
363
- }).catch(onError);
364
- }, [buildData, jobData, command]);
365
- const androidJob = jobData?.data.find(
366
- (job) => job.type === Platform.ANDROID && [JobStatus.PENDING, JobStatus.PROCESSING].includes(job.status)
367
- );
368
- return /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsxs(Box, { flexDirection: "column", gap: 1, ...boxProps, children: [
369
- /* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 1, children: [
370
- /* @__PURE__ */ jsx(Text, { children: "Create an initial build..." }),
371
- (isLoadingBuilds || isLoadingJobs || shipMutation.isPending) && /* @__PURE__ */ jsx(Spinner, { type: "dots" })
372
- ] }),
373
- androidJob == null && /* @__PURE__ */ jsx(Text, { children: shipLog }),
374
- androidJob && /* @__PURE__ */ jsx(JobProgress, { job: androidJob, onComplete })
375
- ] }) });
376
- };
377
-
378
- const InviteForm = ({ onSubmit }) => {
379
- const [error, setError] = useState(null);
380
- const [accountId, setAccountId] = useState("");
381
- const handleSubmitAccountId = () => {
382
- setError(null);
383
- const idRegEx = /^\d{10,20}$/;
384
- if (!idRegEx.test(`${accountId}`)) {
385
- setError("Please enter a valid Google Play Account ID (10-20 digits)");
386
- return;
387
- }
388
- return onSubmit(accountId);
389
- };
390
- return /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginLeft: 1, children: [
391
- /* @__PURE__ */ jsx(
392
- FormTextInput,
393
- {
394
- label: "Please enter your Google Play Account ID:",
395
- labelProps: { bold: true },
396
- defaultValue: accountId,
397
- placeholder: "e.g. 8110853839480950872",
398
- onChange: setAccountId,
399
- onSubmit: handleSubmitAccountId
400
- }
401
- ),
402
- error && /* @__PURE__ */ jsx(Alert, { variant: "error", children: error })
403
- ] }) });
404
- };
405
-
406
- const InviteServiceAccount = ({ onComplete, onError, ...boxProps }) => {
407
- const { gameId } = useContext(GameContext);
408
- const inviteMutation = useInviteServiceAccount();
409
- const handleSubmit = async (developerId) => {
410
- try {
411
- if (!gameId) return;
412
- await inviteMutation.mutateAsync({ projectId: gameId, developerId });
413
- onComplete();
414
- } catch (error) {
415
- onError(error);
416
- }
417
- };
418
- const templateVars = {
419
- guideURL: new URL("/docs/guides/google-play-account-id", WEB_URL).toString()
420
- };
421
- return /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsxs(Box, { flexDirection: "column", gap: 1, ...boxProps, children: [
422
- /* @__PURE__ */ jsx(Markdown, { filename: "invite-service-account.md", templateVars }),
423
- /* @__PURE__ */ jsxs(Box, { children: [
424
- inviteMutation.isPending && /* @__PURE__ */ jsx(Spinner, { type: "dots" }),
425
- !inviteMutation.isPending && /* @__PURE__ */ jsx(InviteForm, { onSubmit: handleSubmit })
426
- ] })
427
- ] }) });
428
- };
429
-
430
- var StepStatus = /* @__PURE__ */ ((StepStatus2) => {
431
- StepStatus2["PENDING"] = "PENDING";
432
- StepStatus2["RUNNING"] = "RUNNING";
433
- StepStatus2["SUCCESS"] = "SUCCESS";
434
- StepStatus2["FAILURE"] = "FAILURE";
435
- StepStatus2["WARN"] = "WARN";
436
- return StepStatus2;
437
- })(StepStatus || {});
438
- const Steps = [
439
- "createGame",
440
- "createKeystore",
441
- "connectGoogle",
442
- "createServiceAccount",
443
- "createInitialBuild",
444
- "createGooglePlayGame",
445
- "inviteServiceAccount"
446
- ];
447
- const getStepInitialStatus = (step, statusFlags) => {
448
- if (step === "connectGoogle") {
449
- if (!statusFlags.hasGoogleConnection && statusFlags.hasServiceAccountKey && statusFlags.hasInvitedServiceAccount)
450
- return "WARN" /* WARN */;
451
- return statusFlags.hasGoogleConnection ? "SUCCESS" /* SUCCESS */ : "PENDING" /* PENDING */;
452
- }
453
- if (step === "createInitialBuild") {
454
- if (!statusFlags.hasInitialBuild && statusFlags.hasGooglePlayGame) return "WARN" /* WARN */;
455
- return statusFlags.hasInitialBuild ? "SUCCESS" /* SUCCESS */ : "PENDING" /* PENDING */;
456
- }
457
- const base = {
458
- createGame: statusFlags.hasGameName && statusFlags.hasAndroidPackageName,
459
- createKeystore: statusFlags.hasAndroidKeystore,
460
- createServiceAccount: statusFlags.hasServiceAccountKey,
461
- createGooglePlayGame: statusFlags.hasGooglePlayGame,
462
- inviteServiceAccount: statusFlags.hasInvitedServiceAccount
463
- };
464
- return base[step] ? "SUCCESS" /* SUCCESS */ : "PENDING" /* PENDING */;
465
- };
466
- const getStatusFlags = async (cmd) => {
467
- const projectConfig = await cmd.getProjectConfigSafe();
468
- const projectId = projectConfig.project?.id;
469
- const project = !!projectId && await getProject(projectId);
470
- const hasShipThisProject = !!project;
471
- const hasGameName = project && !!project?.name;
472
- const hasAndroidPackageName = project && !!project?.details?.androidPackageName;
473
- const projectCredentials = hasShipThisProject ? await getProjectCredentials(project.id) : [];
474
- const hasAndroidKeystore = projectCredentials.some(
475
- (cred) => cred.isActive && cred.platform === Platform.ANDROID && cred.type == CredentialsType.CERTIFICATE
476
- );
477
- const googleStatus = await getGoogleStatus();
478
- const hasGoogleConnection = googleStatus.isAuthenticated;
479
- const hasServiceAccountKey = projectCredentials.some(
480
- (cred) => cred.isActive && cred.platform == Platform.ANDROID && cred.type == CredentialsType.KEY
481
- );
482
- const buildsResponse = !!projectId && hasShipThisProject && await queryBuilds({ projectId, pageNumber: 0 });
483
- const hasInitialBuild = !!buildsResponse && buildsResponse.data.some((build) => build.platform === Platform.ANDROID);
484
- const testResult = projectId ? await fetchKeyTestResult({ projectId }) : null;
485
- const hasGooglePlayGame = testResult && testResult?.status === KeyTestStatus.SUCCESS || testResult?.status === KeyTestStatus.ERROR && testResult?.error === KeyTestError.NOT_INVITED;
486
- const hasInvitedServiceAccount = testResult ? testResult?.status === KeyTestStatus.SUCCESS : false;
487
- return {
488
- hasShipThisProject,
489
- hasGameName,
490
- hasAndroidPackageName,
491
- hasAndroidKeystore,
492
- hasGoogleConnection,
493
- hasServiceAccountKey,
494
- hasInitialBuild,
495
- hasGooglePlayGame,
496
- hasInvitedServiceAccount
497
- };
498
- };
499
-
500
- const StepLabels = {
501
- createGame: "Create game in ShipThis",
502
- createKeystore: "Create or import an Android Keystore",
503
- connectGoogle: "Connect ShipThis with Google",
504
- createServiceAccount: "Create a Service Account & API Key",
505
- createInitialBuild: "Create an initial build",
506
- createGooglePlayGame: "Create the game in Google Play",
507
- inviteServiceAccount: "Invite the Service Account"
508
- };
509
- const StepWithStatus = ({ position, title, status }) => {
510
- const indicator = {
511
- [StepStatus.PENDING]: " ",
512
- // double space
513
- [StepStatus.RUNNING]: /* @__PURE__ */ jsxs(Fragment, { children: [
514
- /* @__PURE__ */ jsx(Spinner, { type: "dots" }),
515
- " "
516
- ] }),
517
- [StepStatus.SUCCESS]: "\u2705",
518
- // this is 2 wide?
519
- [StepStatus.FAILURE]: "\u274C",
520
- // this is 2 wide?
521
- [StepStatus.WARN]: "\u26A0\uFE0F "
522
- // double
523
- }[status];
524
- const isBold = status !== StepStatus.PENDING;
525
- return /* @__PURE__ */ jsxs(Text, { bold: isBold, children: [
526
- /* @__PURE__ */ jsx(Fragment, { children: indicator }),
527
- " ",
528
- position,
529
- ". ",
530
- title
531
- ] });
532
- };
533
- const StepStatusTable = ({ stepStatuses }) => {
534
- return /* @__PURE__ */ jsx(Box, { flexDirection: "column", marginLeft: 1, children: Steps.map((step, index) => {
535
- return /* @__PURE__ */ jsx(StepWithStatus, { position: index + 1, title: StepLabels[step], status: stepStatuses[index] }, step);
536
- }) });
537
- };
538
-
539
- const stepComponentMap = {
540
- createGame: CreateGame,
541
- createKeystore: CreateOrImport,
542
- connectGoogle: ConnectGoogle,
543
- createServiceAccount: CreateServiceAccountKey,
544
- createInitialBuild: CreateInitialBuild,
545
- createGooglePlayGame: CreateGooglePlayGame,
546
- inviteServiceAccount: InviteServiceAccount
547
- };
548
- const ON_COMPLETE_DELAY_MS = 500;
549
- const AndroidWizard = (props) => {
550
- const { command } = React.useContext(CommandContext);
551
- const [currentStep, setCurrentStep] = useState(null);
552
- const [stepStatuses, setStepStatuses] = useState(null);
553
- const [showSuccess, setShowSuccess] = useState(false);
554
- const determineStep = async () => {
555
- if (!command) return;
556
- const statusFlags = await getStatusFlags(command);
557
- const initStatuses = Steps.map((step) => getStepInitialStatus(step, statusFlags));
558
- const firstPending = initStatuses.findIndex((status) => status === StepStatus.PENDING);
559
- const pendingStep = firstPending === -1 ? null : Steps[firstPending];
560
- const withPending = initStatuses.map((status, index) => {
561
- if (index === firstPending) return StepStatus.RUNNING;
562
- return status;
563
- });
564
- setCurrentStep(pendingStep);
565
- setStepStatuses(withPending);
566
- const isAllDone = firstPending === -1;
567
- setShowSuccess(isAllDone);
568
- if (isAllDone) setTimeout(props.onComplete, ON_COMPLETE_DELAY_MS);
569
- };
570
- useEffect(() => {
571
- determineStep().catch(props.onError);
572
- }, [command]);
573
- const handleStepComplete = () => determineStep().catch(props.onError);
574
- const StepInterface = currentStep ? stepComponentMap[currentStep] : null;
575
- const templateVars = {
576
- iosSetupURL: new URL("/docs/ios", WEB_URL).toString(),
577
- docsURL: new URL("/docs", WEB_URL).toString()
578
- };
579
- return /* @__PURE__ */ jsx(GameProvider, { children: /* @__PURE__ */ jsxs(ScrollArea, { height: process.stdout.rows || 24, children: [
580
- /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
581
- /* @__PURE__ */ jsx(Box, { marginBottom: 1, children: /* @__PURE__ */ jsx(Title, { children: "ShipThis Android Wizard" }) }),
582
- stepStatuses && /* @__PURE__ */ jsx(StepStatusTable, { stepStatuses })
583
- ] }),
584
- StepInterface && /* @__PURE__ */ jsx(
585
- StepInterface,
586
- {
587
- onComplete: handleStepComplete,
588
- onError: props.onError,
589
- margin: 1,
590
- borderStyle: "single",
591
- padding: 1
592
- }
593
- ),
594
- showSuccess && /* @__PURE__ */ jsx(Box, { marginTop: 1, children: /* @__PURE__ */ jsx(Markdown, { filename: "android-success.md", templateVars }) })
595
- ] }) });
596
- };
597
-
598
- const reducer = (state, action) => {
599
- switch (action.type) {
600
- case "SET_INNER_HEIGHT":
601
- return {
602
- ...state,
603
- innerHeight: action.innerHeight
604
- };
605
- case "SET_HEIGHT":
606
- return {
607
- ...state,
608
- height: action.height
609
- };
610
- case "SCROLL_DOWN":
611
- return {
612
- ...state,
613
- scrollTop: Math.min(
614
- state.innerHeight <= state.height ? 0 : state.innerHeight - state.height,
615
- state.scrollTop + 1
616
- )
617
- };
618
- case "SCROLL_UP":
619
- return {
620
- ...state,
621
- scrollTop: Math.max(0, state.scrollTop - 1)
622
- };
623
- default:
624
- return state;
625
- }
626
- };
627
- function ScrollArea({ height, children }) {
628
- useFocus();
629
- const [state, dispatch] = useReducer(reducer, {
630
- height,
631
- scrollTop: 0,
632
- innerHeight: 0
633
- });
634
- const innerRef = useRef(null);
635
- useEffect(() => {
636
- dispatch({ type: "SET_HEIGHT", height });
637
- }, [height]);
638
- useEffect(() => {
639
- if (!innerRef.current) return;
640
- const dimensions = measureElement(innerRef.current);
641
- dispatch({
642
- type: "SET_INNER_HEIGHT",
643
- innerHeight: dimensions.height
644
- });
645
- }, []);
646
- useInput((_input, key) => {
647
- if (key.downArrow) {
648
- dispatch({
649
- type: "SCROLL_DOWN"
650
- });
651
- }
652
- if (key.upArrow) {
653
- dispatch({
654
- type: "SCROLL_UP"
655
- });
656
- }
657
- });
658
- return /* @__PURE__ */ jsx(Box, { height, flexDirection: "column", flexGrow: 1, overflow: "hidden", children: /* @__PURE__ */ jsx(Box, { ref: innerRef, flexShrink: 0, flexDirection: "column", marginTop: -state.scrollTop, children }) });
659
- }
660
-
661
- class GameWizard extends BaseAuthenticatedCommand {
662
- static args = {
663
- platform: Args.string({
664
- description: 'The platform to run the wizard for. This can be "android" or "ios"',
665
- required: true,
666
- options: ["android", "ios"]
667
- })
668
- };
669
- static description = "Runs all the steps for the specific platform";
670
- static examples = ["<%= config.bin %> <%= command.id %> ios", "<%= config.bin %> <%= command.id %> android"];
671
- static flags = {};
672
- async run() {
673
- const { args } = this;
674
- if (!isCWDGodotGame()) {
675
- this.error("No Godot project detected. Please run this from a godot project directory.", { exit: 1 });
676
- }
677
- if (args.platform === "ios") {
678
- return this.config.runCommand("game:ios:wizard");
679
- }
680
- render(
681
- /* @__PURE__ */ jsx(Command, { command: this, children: /* @__PURE__ */ jsx(AndroidWizard, { onComplete: () => process.exit(0), onError: (e) => this.error(e) }) })
682
- );
683
- }
684
- }
685
-
686
- export { GameWizard as default };