shipthis 0.1.9 → 0.1.10
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/AppleBundleIdDetails-CjP5y0cj.js +74 -0
- package/dist/AppleBundleIdDetails-CztWY_mT.js +73 -0
- package/dist/AppleBundleIdDetails-DKeSAte9.js +73 -0
- package/dist/Command-D6rWEZRM.js +204 -0
- package/dist/Command-Dn0P_KOV.js +205 -0
- package/dist/Command-YJJp90ru.js +204 -0
- package/dist/CommandGame-cPaTk1wh.js +8 -0
- package/dist/CommandGame-k04mHiDl.js +8 -0
- package/dist/CommandGame-pI9VXKxi.js +8 -0
- package/dist/CreateKeystore-BnNdeDUa.js +57 -0
- package/dist/CreateKeystore-Cf-nsK_M.js +56 -0
- package/dist/CreateKeystore-g1z6DsU5.js +56 -0
- package/dist/JobProgress-CPkWVSlH.js +108 -0
- package/dist/JobProgress-biiLhigg.js +109 -0
- package/dist/JobProgress-jmGAzvxS.js +108 -0
- package/dist/JobStatusTable-BA_q-kgE.js +75 -0
- package/dist/JobStatusTable-ByW0bN6c.js +75 -0
- package/dist/JobStatusTable-CgaTS7jS.js +75 -0
- package/dist/ProjectCredentialsTable-8kXt7sTS.js +37 -0
- package/dist/ProjectCredentialsTable-DRMK_SNw.js +37 -0
- package/dist/ProjectCredentialsTable-DfkpvTf8.js +37 -0
- package/dist/UserCredentialsTable-DMh8Wpy7.js +82 -0
- package/dist/UserCredentialsTable-DgHZJSHG.js +82 -0
- package/dist/UserCredentialsTable-TflQvTEP.js +82 -0
- package/dist/baseAppleCommand-1isaFxCQ.js +10 -0
- package/dist/baseAppleCommand-B9wyIqdL.js +10 -0
- package/dist/baseAppleCommand-DpasCbje.js +10 -0
- package/dist/baseGameAndroidCommand-6vsMpE7a.js +43 -0
- package/dist/baseGameAndroidCommand-B3LRG701.js +43 -0
- package/dist/baseGameAndroidCommand-C9BuS2Sr.js +43 -0
- package/dist/commands/apple/apiKey/create.js +7 -7
- package/dist/commands/apple/apiKey/export.js +6 -6
- package/dist/commands/apple/apiKey/import.js +6 -6
- package/dist/commands/apple/apiKey/status.js +5 -5
- package/dist/commands/apple/certificate/create.js +7 -7
- package/dist/commands/apple/certificate/export.js +6 -6
- package/dist/commands/apple/certificate/import.js +6 -6
- package/dist/commands/apple/certificate/status.js +5 -5
- package/dist/commands/apple/login.js +2 -2
- package/dist/commands/apple/status.js +4 -4
- package/dist/commands/dashboard.js +1 -1
- package/dist/commands/game/android/apiKey/connect.js +8 -8
- package/dist/commands/game/android/apiKey/create.js +10 -10
- package/dist/commands/game/android/apiKey/export.js +7 -7
- package/dist/commands/game/android/apiKey/import.js +7 -7
- package/dist/commands/game/android/apiKey/invite.js +4 -4
- package/dist/commands/game/android/apiKey/status.js +7 -7
- package/dist/commands/game/android/keyStore/create.js +8 -8
- package/dist/commands/game/android/keyStore/export.js +6 -6
- package/dist/commands/game/android/keyStore/import.js +6 -6
- package/dist/commands/game/android/keyStore/status.js +6 -6
- package/dist/commands/game/android/status.js +4 -4
- package/dist/commands/game/build/download.js +4 -4
- package/dist/commands/game/build/list.js +5 -5
- package/dist/commands/game/create.js +2 -2
- package/dist/commands/game/details.js +21 -7
- package/dist/commands/game/export.js +1 -1
- package/dist/commands/game/ios/app/addTester.js +5 -5
- package/dist/commands/game/ios/app/create.js +4 -4
- package/dist/commands/game/ios/app/status.js +7 -7
- package/dist/commands/game/ios/app/sync.js +5 -5
- package/dist/commands/game/ios/profile/create.js +7 -7
- package/dist/commands/game/ios/profile/export.js +6 -6
- package/dist/commands/game/ios/profile/import.js +6 -6
- package/dist/commands/game/ios/profile/status.js +6 -6
- package/dist/commands/game/ios/status.js +7 -7
- package/dist/commands/game/ios/wizard.js +2 -2
- package/dist/commands/game/job/list.js +4 -4
- package/dist/commands/game/job/status.js +7 -7
- package/dist/commands/game/list.js +4 -4
- package/dist/commands/game/ship.js +9 -9
- package/dist/commands/game/status.js +4 -4
- package/dist/commands/game/wizard.js +490 -34
- package/dist/commands/internal/fastlane.js +1 -1
- package/dist/commands/internal/readme.js +255 -770
- package/dist/commands/login.js +2 -2
- package/dist/commands/status.js +4 -4
- package/dist/export-B_F6vXIK.js +36 -0
- package/dist/export-C_lu1FD9.js +36 -0
- package/dist/export-rS9o87LD.js +36 -0
- package/dist/import-7T3J63j2.js +38 -0
- package/dist/import-Brg3zYmT.js +38 -0
- package/dist/import-DPbg8WvS.js +38 -0
- package/dist/index-BB7X1Pqp.js +695 -0
- package/dist/index-BQnkBr1Q.js +136 -0
- package/dist/index-BX9h2vPV.js +123 -0
- package/dist/index-BjGSEqP5.js +135 -0
- package/dist/index-Bv3x8c78.js +693 -0
- package/dist/index-ByOvCs4O.js +122 -0
- package/dist/index-BzffuqPa.js +24 -0
- package/dist/index-C9734Cj2.js +693 -0
- package/dist/index-CKYExssR.js +136 -0
- package/dist/index-Cz_KLwWf.js +136 -0
- package/dist/index-DOgF4dFK.js +24 -0
- package/dist/index-DgbQBx6x.js +122 -0
- package/dist/index-DxHPjZav.js +136 -0
- package/dist/index-m4HTrJ5J.js +135 -0
- package/dist/upload-2naN75Zu.js +60 -0
- package/dist/upload-Cx71802W.js +60 -0
- package/dist/upload-DvAE1vDq.js +60 -0
- package/dist/useAndroidServiceAccountTestResult-BDc7XpKE.js +52 -0
- package/dist/useAndroidServiceAccountTestResult-B_ekRewZ.js +52 -0
- package/dist/useAndroidServiceAccountTestResult-Ce1x0Eh8.js +52 -0
- package/dist/useAppleApp-BEtLQa7n.js +32 -0
- package/dist/useAppleApp-CApC0-4Q.js +32 -0
- package/dist/useAppleApp-CETIcsJS.js +32 -0
- package/dist/useAppleBundleId-DXbMDRLd.js +64 -0
- package/dist/useAppleBundleId-SmzY6rkm.js +64 -0
- package/dist/useAppleBundleId-gPBzJwQg.js +64 -0
- package/dist/useJobWatching-BIG6fnTN.js +45 -0
- package/dist/useJobWatching-Cp-CYd90.js +45 -0
- package/dist/useJobWatching-D-YzSlK8.js +45 -0
- package/dist/useProjectCredentials-CKngz2rd.js +54 -0
- package/dist/useProjectCredentials-EapDge1I.js +54 -0
- package/dist/useProjectCredentials-vjedBbKl.js +54 -0
- package/dist/useWebSocket-Bep1zAVG.js +36 -0
- package/dist/useWebSocket-CBqsjHbt.js +36 -0
- package/dist/useWebSocket-rBLiZsKb.js +36 -0
- package/dist/wizard-ChPegnMW.js +133 -0
- package/npm-shrinkwrap.json +251 -223
- package/oclif.manifest.json +256 -220
- package/package.json +2 -4
|
@@ -1,32 +1,506 @@
|
|
|
1
|
+
import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
|
|
1
2
|
import { Args } from '@oclif/core';
|
|
2
|
-
import
|
|
3
|
-
import {
|
|
4
|
-
import '
|
|
5
|
-
import '
|
|
6
|
-
import '
|
|
7
|
-
import '
|
|
3
|
+
import { Text, Box, render } from 'ink';
|
|
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
|
+
import React, { useState, useContext, useEffect, useRef } from 'react';
|
|
6
|
+
import { c as CommandContext, b as GameContext, d as useBuilds, M as Markdown, q as queryBuilds, G as GameProvider, e as CreateGooglePlayGame, C as Command } from '../../Command-D6rWEZRM.js';
|
|
7
|
+
import Spinner from 'ink-spinner';
|
|
8
|
+
import { Alert, TextInput } from '@inkjs/ui';
|
|
9
|
+
import axios from 'axios';
|
|
8
10
|
import 'crypto-js';
|
|
9
11
|
import 'uuid';
|
|
10
12
|
import 'luxon';
|
|
13
|
+
import 'fs';
|
|
14
|
+
import { useQuery, useMutation } from '@tanstack/react-query';
|
|
15
|
+
import 'open';
|
|
11
16
|
import 'crypto';
|
|
12
17
|
import 'readline-sync';
|
|
13
18
|
import 'node:readline';
|
|
14
19
|
import 'node:path';
|
|
15
20
|
import 'node:url';
|
|
16
|
-
import 'react';
|
|
17
|
-
import '@tanstack/react-query';
|
|
18
|
-
import 'ini';
|
|
19
|
-
import 'deepmerge';
|
|
20
21
|
import 'fast-glob';
|
|
21
22
|
import 'yazl';
|
|
22
23
|
import 'socket.io-client';
|
|
23
24
|
import 'isomorphic-git';
|
|
25
|
+
import 'qrcode';
|
|
26
|
+
import 'string-length';
|
|
27
|
+
import 'strip-ansi';
|
|
28
|
+
import { C as CreateKeystore } from '../../CreateKeystore-g1z6DsU5.js';
|
|
29
|
+
import { C as ConnectGoogle } from '../../index-DgbQBx6x.js';
|
|
30
|
+
import { C as CreateServiceAccountKey } from '../../index-m4HTrJ5J.js';
|
|
31
|
+
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-jmGAzvxS.js';
|
|
33
|
+
import { a as getProjectCredentials } from '../../index-DOgF4dFK.js';
|
|
34
|
+
import { T as Title } from '../../Title-BCQtayg6.js';
|
|
35
|
+
import 'path';
|
|
36
|
+
import '@expo/apple-utils/build/index.js';
|
|
37
|
+
import 'ini';
|
|
38
|
+
import 'deepmerge';
|
|
39
|
+
import '../../index-Cz_KLwWf.js';
|
|
40
|
+
import 'marked';
|
|
41
|
+
import 'marked-terminal';
|
|
42
|
+
import '../../RunWithSpinner-BVXNWGD3.js';
|
|
43
|
+
import '../../useWebSocket-CBqsjHbt.js';
|
|
44
|
+
import '../../useProjectCredentials-vjedBbKl.js';
|
|
45
|
+
import '../../ProgressSpinner-6pw1T8Iw.js';
|
|
46
|
+
import '../../git-DREGq-jc.js';
|
|
47
|
+
import '../../useJobWatching-D-YzSlK8.js';
|
|
48
|
+
|
|
49
|
+
async function queryJobs({ projectId, ...pageAndSortParams }) {
|
|
50
|
+
try {
|
|
51
|
+
const headers = getAuthedHeaders();
|
|
52
|
+
const url = `${API_URL}/projects/${projectId}/jobs`;
|
|
53
|
+
const response = await axios.get(url, { headers, params: pageAndSortParams });
|
|
54
|
+
return {
|
|
55
|
+
...response.data,
|
|
56
|
+
data: castArrayObjectDates(response.data.data)
|
|
57
|
+
};
|
|
58
|
+
} catch (error) {
|
|
59
|
+
console.warn("queryJobs Error", error);
|
|
60
|
+
throw error;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
const useJobs = (props) => {
|
|
64
|
+
const queryResult = useQuery({
|
|
65
|
+
queryKey: cacheKeys.jobs(props),
|
|
66
|
+
queryFn: async () => queryJobs(props)
|
|
67
|
+
});
|
|
68
|
+
return queryResult;
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
const useInviteServiceAccount = () => {
|
|
72
|
+
return useMutation({
|
|
73
|
+
mutationFn: async ({ projectId, developerId }) => {
|
|
74
|
+
try {
|
|
75
|
+
const headers = getAuthedHeaders();
|
|
76
|
+
const { data } = await axios.post(
|
|
77
|
+
`${API_URL}/projects/${projectId}/credentials/android/key/invite/`,
|
|
78
|
+
{ developerId },
|
|
79
|
+
{
|
|
80
|
+
headers
|
|
81
|
+
}
|
|
82
|
+
);
|
|
83
|
+
return data;
|
|
84
|
+
} catch (error) {
|
|
85
|
+
console.error("useInviteMutation Error", error);
|
|
86
|
+
throw error;
|
|
87
|
+
}
|
|
88
|
+
},
|
|
89
|
+
onSuccess: async (data) => {
|
|
90
|
+
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
91
|
+
await sleep(1e3);
|
|
92
|
+
queryClient.invalidateQueries({
|
|
93
|
+
queryKey: cacheKeys.androidKeyTestResult({ projectId: data.projectId })
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
const GameInfoForm = ({ gameInfo, onSubmit }) => {
|
|
100
|
+
const [activeInput, setActiveInput] = useState("name");
|
|
101
|
+
const [error, setError] = useState(null);
|
|
102
|
+
const [name, setName] = useState(gameInfo.name);
|
|
103
|
+
const [androidPackageName, setAndroidPackageName] = useState(gameInfo?.details?.androidPackageName);
|
|
104
|
+
const handleSubmitName = () => {
|
|
105
|
+
setError(null);
|
|
106
|
+
if (name.length === 0) {
|
|
107
|
+
setError("Please enter a name for your game");
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
setActiveInput("androidPackageName");
|
|
111
|
+
};
|
|
112
|
+
const handleSubmitPackageName = () => {
|
|
113
|
+
setError(null);
|
|
114
|
+
const packageRegex = /^[a-zA-Z][a-zA-Z0-9_]*(\.[a-zA-Z][a-zA-Z0-9_]*)+$/;
|
|
115
|
+
if (!packageRegex.test(`${androidPackageName}`)) {
|
|
116
|
+
setError("Please enter a valid package name e.g. com.flappy.souls");
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
onSubmit({
|
|
120
|
+
...gameInfo,
|
|
121
|
+
name,
|
|
122
|
+
details: {
|
|
123
|
+
...gameInfo.details,
|
|
124
|
+
androidPackageName
|
|
125
|
+
}
|
|
126
|
+
});
|
|
127
|
+
};
|
|
128
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
129
|
+
/* @__PURE__ */ jsx(Text, { bold: true, children: "Please confirm the following information about your game" }),
|
|
130
|
+
error && /* @__PURE__ */ jsx(Alert, { variant: "error", children: error }),
|
|
131
|
+
/* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginLeft: 1, children: [
|
|
132
|
+
/* @__PURE__ */ jsx(
|
|
133
|
+
FormTextInput,
|
|
134
|
+
{
|
|
135
|
+
label: "Game name:",
|
|
136
|
+
isDisabled: activeInput !== "name",
|
|
137
|
+
defaultValue: name,
|
|
138
|
+
placeholder: "Enter the name of your game...",
|
|
139
|
+
onChange: setName,
|
|
140
|
+
onSubmit: handleSubmitName
|
|
141
|
+
}
|
|
142
|
+
),
|
|
143
|
+
/* @__PURE__ */ jsx(
|
|
144
|
+
FormTextInput,
|
|
145
|
+
{
|
|
146
|
+
label: "Android package name :",
|
|
147
|
+
isDisabled: activeInput !== "androidPackageName",
|
|
148
|
+
defaultValue: androidPackageName,
|
|
149
|
+
placeholder: "e.g. com.flappy.souls",
|
|
150
|
+
onChange: setAndroidPackageName,
|
|
151
|
+
onSubmit: handleSubmitPackageName
|
|
152
|
+
}
|
|
153
|
+
)
|
|
154
|
+
] })
|
|
155
|
+
] });
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
const getGameInfo = (flagValues, project) => {
|
|
159
|
+
const androidPackageName = flagValues.androidPackageName || project?.details?.androidPackageName || "";
|
|
160
|
+
const gameInfo = {
|
|
161
|
+
name: project?.name || flagValues.name || "",
|
|
162
|
+
details: {
|
|
163
|
+
...project?.details,
|
|
164
|
+
androidPackageName
|
|
165
|
+
}
|
|
166
|
+
};
|
|
167
|
+
return gameInfo;
|
|
168
|
+
};
|
|
169
|
+
const CreateGame = (props) => {
|
|
170
|
+
const [isLoading, setIsLoading] = useState(true);
|
|
171
|
+
const [gameInfo, setGameInfo] = useState(null);
|
|
172
|
+
const [showForm, setShowForm] = useState(false);
|
|
173
|
+
const { command } = useContext(CommandContext);
|
|
174
|
+
const { setGameId, game } = useContext(GameContext);
|
|
175
|
+
const handleLoad = async () => {
|
|
176
|
+
if (!command) throw new Error("No command");
|
|
177
|
+
const flags = command.getDetailsFlagsValues();
|
|
178
|
+
const config = await command.getProjectConfigSafe();
|
|
179
|
+
setShowForm(true);
|
|
180
|
+
setIsLoading(false);
|
|
181
|
+
const info = getGameInfo(flags, config.project);
|
|
182
|
+
setGameInfo(info);
|
|
183
|
+
};
|
|
184
|
+
useEffect(() => {
|
|
185
|
+
handleLoad().catch(props.onError);
|
|
186
|
+
}, []);
|
|
187
|
+
const handleSubmitForm = async (gameInfo2) => {
|
|
188
|
+
try {
|
|
189
|
+
setShowForm(false);
|
|
190
|
+
setIsLoading(true);
|
|
191
|
+
if (!command) throw new Error("No command");
|
|
192
|
+
const projectConfig = await command.getProjectConfigSafe();
|
|
193
|
+
const existingGame = projectConfig.project;
|
|
194
|
+
const isNew = !existingGame;
|
|
195
|
+
if (!isNew) {
|
|
196
|
+
const project2 = await updateProject(existingGame.id, gameInfo2);
|
|
197
|
+
const updatedConfig = {
|
|
198
|
+
...projectConfig,
|
|
199
|
+
project: project2
|
|
200
|
+
};
|
|
201
|
+
await command.setProjectConfig(updatedConfig);
|
|
202
|
+
return props.onComplete();
|
|
203
|
+
}
|
|
204
|
+
const { name, details } = gameInfo2;
|
|
205
|
+
const projectDetails = {
|
|
206
|
+
...details,
|
|
207
|
+
gameEngine: GameEngine.GODOT,
|
|
208
|
+
gameEngineVersion: getGodotVersion()
|
|
209
|
+
};
|
|
210
|
+
const project = await createProject({ name, details: projectDetails });
|
|
211
|
+
await command.setProjectConfig({
|
|
212
|
+
project,
|
|
213
|
+
shippedFilesGlobs: DEFAULT_SHIPPED_FILES_GLOBS,
|
|
214
|
+
ignoredFilesGlobs: DEFAULT_IGNORED_FILES_GLOBS
|
|
215
|
+
});
|
|
216
|
+
setGameId(project.id);
|
|
217
|
+
props.onComplete();
|
|
218
|
+
} catch (e) {
|
|
219
|
+
props.onError(e);
|
|
220
|
+
}
|
|
221
|
+
};
|
|
222
|
+
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", gap: 1, borderStyle: "single", margin: 1, children: [
|
|
223
|
+
isLoading && /* @__PURE__ */ jsx(Spinner, {}),
|
|
224
|
+
showForm && gameInfo && /* @__PURE__ */ jsx(GameInfoForm, { gameInfo, onSubmit: handleSubmitForm })
|
|
225
|
+
] });
|
|
226
|
+
};
|
|
227
|
+
|
|
228
|
+
const FormTextInput = ({ label, labelProps, ...rest }) => /* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 1, children: [
|
|
229
|
+
/* @__PURE__ */ jsx(Text, { ...labelProps, children: label }),
|
|
230
|
+
/* @__PURE__ */ jsx(TextInput, { ...rest })
|
|
231
|
+
] });
|
|
232
|
+
|
|
233
|
+
const CreateInitialBuild = (props) => {
|
|
234
|
+
const { gameId } = useContext(GameContext);
|
|
235
|
+
return /* @__PURE__ */ jsx(Fragment, { children: gameId && /* @__PURE__ */ jsx(CreateForGame, { gameId, ...props }) });
|
|
236
|
+
};
|
|
237
|
+
const CreateForGame = ({ onComplete, onError, gameId, ...boxProps }) => {
|
|
238
|
+
const { command } = useContext(CommandContext);
|
|
239
|
+
const { data: buildData, isLoading: isLoadingBuilds } = useBuilds({ projectId: gameId, pageNumber: 0 });
|
|
240
|
+
const { data: jobData, isLoading: isLoadingJobs } = useJobs({
|
|
241
|
+
projectId: gameId,
|
|
242
|
+
pageNumber: 0
|
|
243
|
+
});
|
|
244
|
+
const prevHasBuild = useRef(false);
|
|
245
|
+
const shipMutation = useShip();
|
|
246
|
+
const [shipLog, setShipLog] = useState("");
|
|
247
|
+
useEffect(() => {
|
|
248
|
+
if (isLoadingBuilds || isLoadingJobs) return;
|
|
249
|
+
if (!buildData) return;
|
|
250
|
+
if (!jobData) return;
|
|
251
|
+
if (!command) return;
|
|
252
|
+
const hasAndroidBuild = buildData.data.some((build) => build.platform === Platform.ANDROID);
|
|
253
|
+
if (!prevHasBuild.current && hasAndroidBuild) return onComplete();
|
|
254
|
+
prevHasBuild.current = hasAndroidBuild;
|
|
255
|
+
const hasAndroidJob = jobData.data.some((job) => job.type === Platform.ANDROID);
|
|
256
|
+
const shouldRun = !hasAndroidBuild && !hasAndroidJob;
|
|
257
|
+
if (shouldRun)
|
|
258
|
+
shipMutation.mutateAsync({
|
|
259
|
+
command,
|
|
260
|
+
log: setShipLog
|
|
261
|
+
}).catch(onError);
|
|
262
|
+
}, [buildData, jobData, command]);
|
|
263
|
+
const androidJob = jobData?.data.find(
|
|
264
|
+
(job) => job.type === Platform.ANDROID && [JobStatus.PENDING, JobStatus.PROCESSING].includes(job.status)
|
|
265
|
+
);
|
|
266
|
+
return /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsxs(Box, { flexDirection: "column", gap: 1, ...boxProps, children: [
|
|
267
|
+
/* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 1, children: [
|
|
268
|
+
/* @__PURE__ */ jsx(Text, { children: "Create an initial build..." }),
|
|
269
|
+
(isLoadingBuilds || isLoadingJobs || shipMutation.isPending) && /* @__PURE__ */ jsx(Spinner, { type: "dots" })
|
|
270
|
+
] }),
|
|
271
|
+
androidJob == null && /* @__PURE__ */ jsx(Text, { children: shipLog }),
|
|
272
|
+
androidJob && /* @__PURE__ */ jsx(JobProgress, { job: androidJob, onComplete })
|
|
273
|
+
] }) });
|
|
274
|
+
};
|
|
275
|
+
|
|
276
|
+
const InviteForm = ({ onSubmit }) => {
|
|
277
|
+
const [error, setError] = useState(null);
|
|
278
|
+
const [accountId, setAccountId] = useState("");
|
|
279
|
+
const handleSubmitAccountId = () => {
|
|
280
|
+
setError(null);
|
|
281
|
+
const idRegEx = /^\d{10,20}$/;
|
|
282
|
+
if (!idRegEx.test(`${accountId}`)) {
|
|
283
|
+
setError("Please enter a valid Google Play Account ID (10-20 digits)");
|
|
284
|
+
return;
|
|
285
|
+
}
|
|
286
|
+
return onSubmit(accountId);
|
|
287
|
+
};
|
|
288
|
+
return /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginLeft: 1, children: [
|
|
289
|
+
/* @__PURE__ */ jsx(
|
|
290
|
+
FormTextInput,
|
|
291
|
+
{
|
|
292
|
+
label: "Please enter your Google Play Account ID:",
|
|
293
|
+
labelProps: { bold: true },
|
|
294
|
+
defaultValue: accountId,
|
|
295
|
+
placeholder: "e.g. 8110853839480950872",
|
|
296
|
+
onChange: setAccountId,
|
|
297
|
+
onSubmit: handleSubmitAccountId
|
|
298
|
+
}
|
|
299
|
+
),
|
|
300
|
+
error && /* @__PURE__ */ jsx(Alert, { variant: "error", children: error })
|
|
301
|
+
] }) });
|
|
302
|
+
};
|
|
303
|
+
|
|
304
|
+
const InviteServiceAccount = ({ onComplete, onError, ...boxProps }) => {
|
|
305
|
+
const { gameId } = useContext(GameContext);
|
|
306
|
+
const inviteMutation = useInviteServiceAccount();
|
|
307
|
+
const handleSubmit = async (developerId) => {
|
|
308
|
+
try {
|
|
309
|
+
if (!gameId) return;
|
|
310
|
+
await inviteMutation.mutateAsync({ projectId: gameId, developerId });
|
|
311
|
+
onComplete();
|
|
312
|
+
} catch (error) {
|
|
313
|
+
onError(error);
|
|
314
|
+
}
|
|
315
|
+
};
|
|
316
|
+
const templateVars = {
|
|
317
|
+
guideURL: new URL("/docs/guides/google-play-account-id", WEB_URL).toString()
|
|
318
|
+
};
|
|
319
|
+
return /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsxs(Box, { flexDirection: "column", gap: 1, ...boxProps, children: [
|
|
320
|
+
/* @__PURE__ */ jsx(Markdown, { filename: "invite-service-account.md", templateVars }),
|
|
321
|
+
/* @__PURE__ */ jsxs(Box, { children: [
|
|
322
|
+
inviteMutation.isPending && /* @__PURE__ */ jsx(Spinner, { type: "dots" }),
|
|
323
|
+
!inviteMutation.isPending && /* @__PURE__ */ jsx(InviteForm, { onSubmit: handleSubmit })
|
|
324
|
+
] })
|
|
325
|
+
] }) });
|
|
326
|
+
};
|
|
327
|
+
|
|
328
|
+
var StepStatus = /* @__PURE__ */ ((StepStatus2) => {
|
|
329
|
+
StepStatus2["PENDING"] = "PENDING";
|
|
330
|
+
StepStatus2["RUNNING"] = "RUNNING";
|
|
331
|
+
StepStatus2["SUCCESS"] = "SUCCESS";
|
|
332
|
+
StepStatus2["FAILURE"] = "FAILURE";
|
|
333
|
+
StepStatus2["WARN"] = "WARN";
|
|
334
|
+
return StepStatus2;
|
|
335
|
+
})(StepStatus || {});
|
|
336
|
+
const Steps = [
|
|
337
|
+
"createGame",
|
|
338
|
+
"createKeystore",
|
|
339
|
+
"connectGoogle",
|
|
340
|
+
"createServiceAccount",
|
|
341
|
+
"createInitialBuild",
|
|
342
|
+
"createGooglePlayGame",
|
|
343
|
+
"inviteServiceAccount"
|
|
344
|
+
];
|
|
345
|
+
const getStepInitialStatus = (step, statusFlags) => {
|
|
346
|
+
if (step === "connectGoogle") {
|
|
347
|
+
if (!statusFlags.hasGoogleConnection && statusFlags.hasServiceAccountKey && statusFlags.hasInvitedServiceAccount)
|
|
348
|
+
return "WARN" /* WARN */;
|
|
349
|
+
return statusFlags.hasGoogleConnection ? "SUCCESS" /* SUCCESS */ : "PENDING" /* PENDING */;
|
|
350
|
+
}
|
|
351
|
+
if (step === "createInitialBuild") {
|
|
352
|
+
if (!statusFlags.hasInitialBuild && statusFlags.hasGooglePlayGame) return "WARN" /* WARN */;
|
|
353
|
+
return statusFlags.hasInitialBuild ? "SUCCESS" /* SUCCESS */ : "PENDING" /* PENDING */;
|
|
354
|
+
}
|
|
355
|
+
const base = {
|
|
356
|
+
createGame: statusFlags.hasGameName && statusFlags.hasAndroidPackageName,
|
|
357
|
+
createKeystore: statusFlags.hasAndroidKeystore,
|
|
358
|
+
createServiceAccount: statusFlags.hasServiceAccountKey,
|
|
359
|
+
createGooglePlayGame: statusFlags.hasGooglePlayGame,
|
|
360
|
+
inviteServiceAccount: statusFlags.hasInvitedServiceAccount,
|
|
361
|
+
connectGoogle: false,
|
|
362
|
+
createInitialBuild: false
|
|
363
|
+
};
|
|
364
|
+
return base[step] ? "SUCCESS" /* SUCCESS */ : "PENDING" /* PENDING */;
|
|
365
|
+
};
|
|
366
|
+
const getStatusFlags = async (cmd) => {
|
|
367
|
+
const projectConfig = await cmd.getProjectConfigSafe();
|
|
368
|
+
const projectId = projectConfig.project?.id;
|
|
369
|
+
const project = !!projectId && await getProject(projectId);
|
|
370
|
+
const hasShipThisProject = !!project;
|
|
371
|
+
const hasGameName = project && !!project?.name;
|
|
372
|
+
const hasAndroidPackageName = project && !!project?.details?.androidPackageName;
|
|
373
|
+
const projectCredentials = hasShipThisProject ? await getProjectCredentials(project.id) : [];
|
|
374
|
+
const hasAndroidKeystore = projectCredentials.some(
|
|
375
|
+
(cred) => cred.isActive && cred.platform === Platform.ANDROID && cred.type == CredentialsType.CERTIFICATE
|
|
376
|
+
);
|
|
377
|
+
const googleStatus = await getGoogleStatus();
|
|
378
|
+
const hasGoogleConnection = googleStatus.isAuthenticated;
|
|
379
|
+
const hasServiceAccountKey = projectCredentials.some(
|
|
380
|
+
(cred) => cred.isActive && cred.platform == Platform.ANDROID && cred.type == CredentialsType.KEY
|
|
381
|
+
);
|
|
382
|
+
const buildsResponse = !!projectId && hasShipThisProject && await queryBuilds({ projectId, pageNumber: 0 });
|
|
383
|
+
const hasInitialBuild = !!buildsResponse && buildsResponse.data.some((build) => build.platform === Platform.ANDROID);
|
|
384
|
+
const testResult = projectId ? await fetchKeyTestResult({ projectId }) : null;
|
|
385
|
+
const hasGooglePlayGame = testResult && testResult?.status === KeyTestStatus.SUCCESS || testResult?.status === KeyTestStatus.ERROR && testResult?.error === KeyTestError.NOT_INVITED;
|
|
386
|
+
const hasInvitedServiceAccount = testResult ? testResult?.status === KeyTestStatus.SUCCESS : false;
|
|
387
|
+
return {
|
|
388
|
+
hasShipThisProject,
|
|
389
|
+
hasGameName,
|
|
390
|
+
hasAndroidPackageName,
|
|
391
|
+
hasAndroidKeystore,
|
|
392
|
+
hasGoogleConnection,
|
|
393
|
+
hasServiceAccountKey,
|
|
394
|
+
hasInitialBuild,
|
|
395
|
+
hasGooglePlayGame,
|
|
396
|
+
hasInvitedServiceAccount
|
|
397
|
+
};
|
|
398
|
+
};
|
|
399
|
+
|
|
400
|
+
const StepLabels = {
|
|
401
|
+
createGame: "Create game in ShipThis",
|
|
402
|
+
createKeystore: "Create an Android Keystore",
|
|
403
|
+
connectGoogle: "Connect ShipThis with Google",
|
|
404
|
+
createServiceAccount: "Create a Service Account & API Key",
|
|
405
|
+
createInitialBuild: "Create an initial build",
|
|
406
|
+
createGooglePlayGame: "Create the game in Google Play",
|
|
407
|
+
inviteServiceAccount: "Invite the Service Account"
|
|
408
|
+
};
|
|
409
|
+
const StepWithStatus = ({ position, title, status }) => {
|
|
410
|
+
const indicator = {
|
|
411
|
+
[StepStatus.PENDING]: " ",
|
|
412
|
+
// double space
|
|
413
|
+
[StepStatus.RUNNING]: /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
414
|
+
/* @__PURE__ */ jsx(Spinner, { type: "dots" }),
|
|
415
|
+
" "
|
|
416
|
+
] }),
|
|
417
|
+
[StepStatus.SUCCESS]: "\u2705",
|
|
418
|
+
// this is 2 wide?
|
|
419
|
+
[StepStatus.FAILURE]: "\u274C",
|
|
420
|
+
// this is 2 wide?
|
|
421
|
+
[StepStatus.WARN]: "\u26A0\uFE0F "
|
|
422
|
+
// double
|
|
423
|
+
}[status];
|
|
424
|
+
const isBold = status !== StepStatus.PENDING;
|
|
425
|
+
return /* @__PURE__ */ jsxs(Text, { bold: isBold, children: [
|
|
426
|
+
/* @__PURE__ */ jsx(Fragment, { children: indicator }),
|
|
427
|
+
" ",
|
|
428
|
+
position,
|
|
429
|
+
". ",
|
|
430
|
+
title
|
|
431
|
+
] });
|
|
432
|
+
};
|
|
433
|
+
const StepStatusTable = ({ stepStatuses }) => {
|
|
434
|
+
return /* @__PURE__ */ jsx(Box, { flexDirection: "column", marginLeft: 1, children: Steps.map((step, index) => {
|
|
435
|
+
return /* @__PURE__ */ jsx(StepWithStatus, { position: index + 1, title: StepLabels[step], status: stepStatuses[index] }, step);
|
|
436
|
+
}) });
|
|
437
|
+
};
|
|
438
|
+
|
|
439
|
+
const stepComponentMap = {
|
|
440
|
+
createGame: CreateGame,
|
|
441
|
+
createKeystore: CreateKeystore,
|
|
442
|
+
connectGoogle: ConnectGoogle,
|
|
443
|
+
createServiceAccount: CreateServiceAccountKey,
|
|
444
|
+
createInitialBuild: CreateInitialBuild,
|
|
445
|
+
createGooglePlayGame: CreateGooglePlayGame,
|
|
446
|
+
inviteServiceAccount: InviteServiceAccount
|
|
447
|
+
};
|
|
448
|
+
const ON_COMPLETE_DELAY_MS = 500;
|
|
449
|
+
const AndroidWizard = (props) => {
|
|
450
|
+
const { command } = React.useContext(CommandContext);
|
|
451
|
+
const [currentStep, setCurrentStep] = useState(null);
|
|
452
|
+
const [stepStatuses, setStepStatuses] = useState(null);
|
|
453
|
+
const [showSuccess, setShowSuccess] = useState(false);
|
|
454
|
+
const determineStep = async () => {
|
|
455
|
+
if (!command) return;
|
|
456
|
+
const statusFlags = await getStatusFlags(command);
|
|
457
|
+
const initStatuses = Steps.map((step) => getStepInitialStatus(step, statusFlags));
|
|
458
|
+
const firstPending = initStatuses.findIndex((status) => status === StepStatus.PENDING);
|
|
459
|
+
const pendingStep = firstPending === -1 ? null : Steps[firstPending];
|
|
460
|
+
const withPending = initStatuses.map((status, index) => {
|
|
461
|
+
if (index === firstPending) return StepStatus.RUNNING;
|
|
462
|
+
return status;
|
|
463
|
+
});
|
|
464
|
+
setCurrentStep(pendingStep);
|
|
465
|
+
setStepStatuses(withPending);
|
|
466
|
+
const isAllDone = firstPending === -1;
|
|
467
|
+
setShowSuccess(isAllDone);
|
|
468
|
+
if (isAllDone) setTimeout(props.onComplete, ON_COMPLETE_DELAY_MS);
|
|
469
|
+
};
|
|
470
|
+
useEffect(() => {
|
|
471
|
+
determineStep().catch(props.onError);
|
|
472
|
+
}, [command]);
|
|
473
|
+
const handleStepComplete = () => determineStep().catch(props.onError);
|
|
474
|
+
const StepInterface = currentStep ? stepComponentMap[currentStep] : null;
|
|
475
|
+
const templateVars = {
|
|
476
|
+
iosSetupURL: new URL("/docs/ios", WEB_URL).toString(),
|
|
477
|
+
docsURL: new URL("/docs", WEB_URL).toString()
|
|
478
|
+
};
|
|
479
|
+
return /* @__PURE__ */ jsxs(GameProvider, { children: [
|
|
480
|
+
/* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
|
|
481
|
+
/* @__PURE__ */ jsx(Box, { marginBottom: 1, children: /* @__PURE__ */ jsx(Title, { children: "ShipThis Android Wizard" }) }),
|
|
482
|
+
stepStatuses && /* @__PURE__ */ jsx(StepStatusTable, { stepStatuses })
|
|
483
|
+
] }),
|
|
484
|
+
StepInterface && /* @__PURE__ */ jsx(
|
|
485
|
+
StepInterface,
|
|
486
|
+
{
|
|
487
|
+
onComplete: handleStepComplete,
|
|
488
|
+
onError: props.onError,
|
|
489
|
+
margin: 1,
|
|
490
|
+
borderStyle: "single",
|
|
491
|
+
padding: 1
|
|
492
|
+
}
|
|
493
|
+
),
|
|
494
|
+
showSuccess && /* @__PURE__ */ jsx(Box, { marginTop: 1, children: /* @__PURE__ */ jsx(Markdown, { filename: "android-success.md", templateVars }) })
|
|
495
|
+
] });
|
|
496
|
+
};
|
|
24
497
|
|
|
25
498
|
class GameWizard extends BaseAuthenticatedCommand {
|
|
26
499
|
static args = {
|
|
27
500
|
platform: Args.string({
|
|
28
501
|
description: 'The platform to run the wizard for. This can be "android" or "ios"',
|
|
29
|
-
required:
|
|
502
|
+
required: true,
|
|
503
|
+
options: ["android", "ios"]
|
|
30
504
|
})
|
|
31
505
|
};
|
|
32
506
|
static description = "Runs all the steps for the specific platform";
|
|
@@ -37,30 +511,12 @@ class GameWizard extends BaseAuthenticatedCommand {
|
|
|
37
511
|
if (!isCWDGodotGame()) {
|
|
38
512
|
this.error("No Godot project detected. Please run this from a godot project directory.", { exit: 1 });
|
|
39
513
|
}
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
if (platform === Platform.ANDROID) {
|
|
43
|
-
return this.config.runCommand("game:android:wizard");
|
|
44
|
-
} else if (platform === Platform.IOS) {
|
|
45
|
-
return this.config.runCommand("game:ios:wizard");
|
|
46
|
-
}
|
|
47
|
-
process.exit(0);
|
|
48
|
-
};
|
|
49
|
-
if (args.platform) {
|
|
50
|
-
await runForPlatform(args.platform);
|
|
51
|
-
return;
|
|
514
|
+
if (args.platform === "ios") {
|
|
515
|
+
return this.config.runCommand("game:ios:wizard");
|
|
52
516
|
}
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
message: "Please select a platform",
|
|
57
|
-
choices: [
|
|
58
|
-
{ title: "Android", value: Platform.ANDROID },
|
|
59
|
-
{ title: "iOS", value: Platform.IOS }
|
|
60
|
-
],
|
|
61
|
-
initial: 0
|
|
62
|
-
});
|
|
63
|
-
await runForPlatform(response.platform);
|
|
517
|
+
render(
|
|
518
|
+
/* @__PURE__ */ jsx(Command, { command: this, children: /* @__PURE__ */ jsx(AndroidWizard, { onComplete: () => process.exit(0), onError: (e) => this.error(e) }) })
|
|
519
|
+
);
|
|
64
520
|
}
|
|
65
521
|
}
|
|
66
522
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import fs__default, { promises } from 'fs';
|
|
2
2
|
import path from 'path';
|
|
3
3
|
import { Args, Flags } from '@oclif/core';
|
|
4
|
-
import { T as BaseCommand } from '../../index-
|
|
4
|
+
import { T as BaseCommand } from '../../index-BB7X1Pqp.js';
|
|
5
5
|
import '@expo/apple-utils/build/index.js';
|
|
6
6
|
import 'axios';
|
|
7
7
|
import 'crypto-js';
|