shipthis 0.0.1
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/README.md +894 -0
- package/bin/alias.sh +10 -0
- package/bin/dev.cmd +3 -0
- package/bin/dev.js +7 -0
- package/bin/dev.tsc.js +15 -0
- package/bin/loader.js +20 -0
- package/bin/run.cmd +3 -0
- package/bin/run.js +5 -0
- package/bin/run.tsc.js +15 -0
- package/dist/App-DFPMSEZF.js +21 -0
- package/dist/AppleBundleIdDetails-CuhAbVEp.js +61 -0
- package/dist/NextSteps-CK9zHOCt.js +18 -0
- package/dist/RunWithSpinner-BVXNWGD3.js +27 -0
- package/dist/StatusTable-CxuX_R1D.js +38 -0
- package/dist/Table-CvM6pccN.js +101 -0
- package/dist/Title-BCQtayg6.js +6 -0
- package/dist/UserCredentialsTable-DHeRI4h6.js +76 -0
- package/dist/baseAppleCommand-BXUu-026.js +10 -0
- package/dist/baseGameCommand-xrD2WDDN.js +599 -0
- package/dist/cacheKeys-CShA-ZjE.js +9 -0
- package/dist/commands/apple/apiKey/create.js +88 -0
- package/dist/commands/apple/apiKey/export.js +66 -0
- package/dist/commands/apple/apiKey/import.js +70 -0
- package/dist/commands/apple/apiKey/status.js +108 -0
- package/dist/commands/apple/certificate/create.js +118 -0
- package/dist/commands/apple/certificate/export.js +66 -0
- package/dist/commands/apple/certificate/import.js +70 -0
- package/dist/commands/apple/certificate/status.js +116 -0
- package/dist/commands/apple/login.js +74 -0
- package/dist/commands/apple/status.js +50 -0
- package/dist/commands/dashboard.js +32 -0
- package/dist/commands/game/build/download.js +78 -0
- package/dist/commands/game/build/list.js +83 -0
- package/dist/commands/game/create.js +59 -0
- package/dist/commands/game/details.js +89 -0
- package/dist/commands/game/export.js +52 -0
- package/dist/commands/game/ios/app/addTester.js +111 -0
- package/dist/commands/game/ios/app/create.js +104 -0
- package/dist/commands/game/ios/app/status.js +52 -0
- package/dist/commands/game/ios/app/sync.js +81 -0
- package/dist/commands/game/ios/profile/create.js +110 -0
- package/dist/commands/game/ios/profile/export.js +68 -0
- package/dist/commands/game/ios/profile/import.js +77 -0
- package/dist/commands/game/ios/profile/status.js +183 -0
- package/dist/commands/game/ios/status.js +79 -0
- package/dist/commands/game/job/list.js +80 -0
- package/dist/commands/game/job/status.js +297 -0
- package/dist/commands/game/list.js +69 -0
- package/dist/commands/game/ship.js +67 -0
- package/dist/commands/game/status.js +76 -0
- package/dist/commands/game/wizard.js +124 -0
- package/dist/commands/login.js +63 -0
- package/dist/commands/status.js +80 -0
- package/dist/export-CujqsTR_.js +36 -0
- package/dist/git-DREGq-jc.js +32 -0
- package/dist/import-Q-KO61ll.js +38 -0
- package/dist/index-DKsVctbw.js +125 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/run.js +3 -0
- package/dist/upload-CUlWmNbS.js +60 -0
- package/dist/useAppleApp-BmwYu7qG.js +32 -0
- package/dist/useAppleBundleId-DCJnfNWr.js +64 -0
- package/dist/useBuilds-Dh_PWwCf.js +41 -0
- package/dist/useJob-CCkqCMvF.js +34 -0
- package/oclif.manifest.json +1510 -0
- package/package.json +167 -0
|
@@ -0,0 +1,599 @@
|
|
|
1
|
+
import { Command, Flags } from '@oclif/core';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import fs__default from 'fs';
|
|
4
|
+
import * as expo from '@expo/apple-utils/build/index.js';
|
|
5
|
+
import axios from 'axios';
|
|
6
|
+
import { DateTime } from 'luxon';
|
|
7
|
+
import 'crypto';
|
|
8
|
+
import 'readline-sync';
|
|
9
|
+
import 'node:readline';
|
|
10
|
+
import 'react';
|
|
11
|
+
import { QueryClient } from '@tanstack/react-query';
|
|
12
|
+
import 'socket.io-client';
|
|
13
|
+
import { parse } from 'ini';
|
|
14
|
+
import merge from 'deepmerge';
|
|
15
|
+
import 'isomorphic-git';
|
|
16
|
+
|
|
17
|
+
const defaultExport = expo.default;
|
|
18
|
+
const {
|
|
19
|
+
ApiKey,
|
|
20
|
+
ApiKeyType,
|
|
21
|
+
App,
|
|
22
|
+
Auth,
|
|
23
|
+
BundleId,
|
|
24
|
+
CapabilityType,
|
|
25
|
+
CapabilityTypeOption,
|
|
26
|
+
Certificate,
|
|
27
|
+
CertificateType,
|
|
28
|
+
Profile,
|
|
29
|
+
ProfileType,
|
|
30
|
+
Session,
|
|
31
|
+
UserRole,
|
|
32
|
+
BetaGroup
|
|
33
|
+
} = defaultExport;
|
|
34
|
+
|
|
35
|
+
const DEFAULT_SHIPPED_FILES_GLOBS = ["**/*"];
|
|
36
|
+
const DEFAULT_IGNORED_FILES_GLOBS = [".git", ".gitignore", "shipthis.json", "shipthis-*.zip"];
|
|
37
|
+
const BACKENDS = {
|
|
38
|
+
dev: {
|
|
39
|
+
apiUrl: "https://api.develop.shipthis.cc/api/1.0.0",
|
|
40
|
+
wsUrl: "wss://ws.develop.shipthis.cc",
|
|
41
|
+
webUrl: "https://develop.shipthis.cc/"
|
|
42
|
+
},
|
|
43
|
+
local: {
|
|
44
|
+
apiUrl: "https://easy-reliably-gull.ngrok-free.app/api/1.0.0",
|
|
45
|
+
wsUrl: "wss://easy-reliably-gull.ngrok-free.app",
|
|
46
|
+
webUrl: "https://easy-reliably-gull.ngrok-free.app/"
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
const BACKEND_ENV_VAR_NAME = "SHIPTHIS_BACKEND";
|
|
50
|
+
const BACKEND_NAME = process.env[BACKEND_ENV_VAR_NAME] || "dev";
|
|
51
|
+
const API_URL = BACKENDS[BACKEND_NAME].apiUrl;
|
|
52
|
+
const WS_URL = BACKENDS[BACKEND_NAME].wsUrl;
|
|
53
|
+
const WEB_URL = BACKENDS[BACKEND_NAME].webUrl;
|
|
54
|
+
|
|
55
|
+
const DEFAULT_LOCALE = "en-US";
|
|
56
|
+
function castObjectDates(apiObject, keys = ["createdAt", "updatedAt"]) {
|
|
57
|
+
if (!apiObject) return apiObject;
|
|
58
|
+
const datesOnly = Object.keys(apiObject).filter((k) => keys.includes(k)).reduce((a, c) => {
|
|
59
|
+
a[c] = DateTime.fromISO(apiObject[c]);
|
|
60
|
+
return a;
|
|
61
|
+
}, {});
|
|
62
|
+
return {
|
|
63
|
+
...apiObject,
|
|
64
|
+
...datesOnly
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
function castArrayObjectDates(apiArray, keys = ["createdAt", "updatedAt"]) {
|
|
68
|
+
return apiArray.map((apiObject) => castObjectDates(apiObject, keys));
|
|
69
|
+
}
|
|
70
|
+
function castJobDates(jobObject) {
|
|
71
|
+
if (jobObject.build) return castObjectDates({ ...jobObject, build: castObjectDates(jobObject.build) });
|
|
72
|
+
return castObjectDates(jobObject);
|
|
73
|
+
}
|
|
74
|
+
function getDateLocale() {
|
|
75
|
+
const fallback = Intl.DateTimeFormat().resolvedOptions().locale.replace(/_/g, "-") || DEFAULT_LOCALE;
|
|
76
|
+
try {
|
|
77
|
+
const env = process.env;
|
|
78
|
+
const fullLocale = env.LC_TIME || env.LANG || env.LANGUAGE || env.LC_ALL || env.LC_MESSAGES;
|
|
79
|
+
const shortLocale = fullLocale?.split(".")[0].replace(/_/g, "-");
|
|
80
|
+
const finalLocal = shortLocale || fallback;
|
|
81
|
+
const _ = DateTime.now().toLocaleString(DateTime.DATE_SHORT, { locale: finalLocal });
|
|
82
|
+
return finalLocal;
|
|
83
|
+
} catch (e) {
|
|
84
|
+
return fallback;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
function getShortDate(inputDate) {
|
|
88
|
+
const locale = getDateLocale();
|
|
89
|
+
return inputDate.toLocaleString(DateTime.DATE_SHORT, { locale });
|
|
90
|
+
}
|
|
91
|
+
function getShortDateTime(inputDate, extraFormatOpts = {}) {
|
|
92
|
+
const locale = getDateLocale();
|
|
93
|
+
const formatOpts = { ...DateTime.DATETIME_SHORT, ...extraFormatOpts };
|
|
94
|
+
return inputDate.toLocaleString(formatOpts, { locale });
|
|
95
|
+
}
|
|
96
|
+
function getShortTime(inputDate, extraFormatOpts = { fractionalSecondDigits: 3 }) {
|
|
97
|
+
const locale = getDateLocale();
|
|
98
|
+
const formatOpts = { ...DateTime.TIME_24_WITH_SECONDS, ...extraFormatOpts };
|
|
99
|
+
return inputDate.toLocaleString(formatOpts, { locale });
|
|
100
|
+
}
|
|
101
|
+
function getShortTimeDelta(start, end) {
|
|
102
|
+
return end.diff(start).rescale().set({ milliseconds: 0 }).shiftTo("minutes", "seconds").toHuman({
|
|
103
|
+
listStyle: "short",
|
|
104
|
+
unitDisplay: "short"
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
async function getUserCredentials(pageSize = 100) {
|
|
109
|
+
const headers = getAuthedHeaders();
|
|
110
|
+
const { data } = await axios({
|
|
111
|
+
method: "get",
|
|
112
|
+
url: `${API_URL}/credentials?pageSize=${pageSize}`,
|
|
113
|
+
headers
|
|
114
|
+
});
|
|
115
|
+
return castArrayObjectDates(data.data);
|
|
116
|
+
}
|
|
117
|
+
async function getProjectCredentials(projectId, pageSize = 100) {
|
|
118
|
+
const headers = getAuthedHeaders();
|
|
119
|
+
const { data } = await axios({
|
|
120
|
+
method: "get",
|
|
121
|
+
url: `${API_URL}/projects/${projectId}/credentials?pageSize=${pageSize}`,
|
|
122
|
+
headers
|
|
123
|
+
});
|
|
124
|
+
return castArrayObjectDates(data.data);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const AUTH_ENV_VAR_NAME = "SHIPTHIS_TOKEN";
|
|
128
|
+
function setAuthToken(token) {
|
|
129
|
+
process.env[AUTH_ENV_VAR_NAME] = token;
|
|
130
|
+
}
|
|
131
|
+
function getAuthToken() {
|
|
132
|
+
return process.env[AUTH_ENV_VAR_NAME];
|
|
133
|
+
}
|
|
134
|
+
function getAuthedHeaders() {
|
|
135
|
+
return {
|
|
136
|
+
Authorization: `Bearer ${process.env[AUTH_ENV_VAR_NAME]}`
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
async function createProject(props) {
|
|
140
|
+
const headers = getAuthedHeaders();
|
|
141
|
+
const opt = { headers };
|
|
142
|
+
const { data } = await axios.post(`${API_URL}/projects`, props, opt);
|
|
143
|
+
return castObjectDates(data);
|
|
144
|
+
}
|
|
145
|
+
async function getProject(projectId) {
|
|
146
|
+
const headers = getAuthedHeaders();
|
|
147
|
+
const opt = { headers };
|
|
148
|
+
const { data } = await axios.get(`${API_URL}/projects/${projectId}`, opt);
|
|
149
|
+
return castObjectDates(data);
|
|
150
|
+
}
|
|
151
|
+
async function getProjects(params) {
|
|
152
|
+
const headers = getAuthedHeaders();
|
|
153
|
+
const opt = { headers, params };
|
|
154
|
+
const { data: rawData } = await axios.get(`${API_URL}/projects`, opt);
|
|
155
|
+
const data = castArrayObjectDates(rawData.data);
|
|
156
|
+
return {
|
|
157
|
+
data,
|
|
158
|
+
pageCount: rawData.pageCount
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
async function updateProject(projectId, edits) {
|
|
162
|
+
const headers = getAuthedHeaders();
|
|
163
|
+
const opt = { headers };
|
|
164
|
+
const { data } = await axios.put(`${API_URL}/projects/${projectId}`, edits, opt);
|
|
165
|
+
return castObjectDates(data);
|
|
166
|
+
}
|
|
167
|
+
async function getProjectPlatformProgress(projectId, platform) {
|
|
168
|
+
const headers = getAuthedHeaders();
|
|
169
|
+
const opt = { headers };
|
|
170
|
+
const { data } = await axios.get(`${API_URL}/projects/${projectId}/${platform}/progress`, opt);
|
|
171
|
+
return data;
|
|
172
|
+
}
|
|
173
|
+
async function getNewUploadTicket(projectId) {
|
|
174
|
+
const headers = getAuthedHeaders();
|
|
175
|
+
const opt = { headers };
|
|
176
|
+
const { data } = await axios.post(`${API_URL}/upload/${projectId}/url`, {}, opt);
|
|
177
|
+
return data;
|
|
178
|
+
}
|
|
179
|
+
async function startJobsFromUpload(uploadTicketId, uploadDetails) {
|
|
180
|
+
const headers = getAuthedHeaders();
|
|
181
|
+
const opt = { headers };
|
|
182
|
+
const { data } = await axios.post(`${API_URL}/upload/start/${uploadTicketId}`, uploadDetails, opt);
|
|
183
|
+
return castArrayObjectDates(data);
|
|
184
|
+
}
|
|
185
|
+
async function getProjectJobs(projectId, params) {
|
|
186
|
+
const headers = getAuthedHeaders();
|
|
187
|
+
const opt = { headers, params };
|
|
188
|
+
const { data: rawData } = await axios.get(`${API_URL}/projects/${projectId}/jobs`, opt);
|
|
189
|
+
const data = castArrayObjectDates(rawData.data);
|
|
190
|
+
return {
|
|
191
|
+
data,
|
|
192
|
+
pageCount: rawData.pageCount
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
async function getJob(jobId, projectId) {
|
|
196
|
+
const headers = getAuthedHeaders();
|
|
197
|
+
const opt = { headers };
|
|
198
|
+
const { data } = await axios.get(`${API_URL}/projects/${projectId}/jobs/${jobId}`, opt);
|
|
199
|
+
return castJobDates(data);
|
|
200
|
+
}
|
|
201
|
+
async function getSingleUseUrl(destination) {
|
|
202
|
+
const headers = await getAuthedHeaders();
|
|
203
|
+
const { data } = await axios.post(`${API_URL}/me/otp`, {}, { headers });
|
|
204
|
+
const queryString = Object.entries({ ...data, destination }).map(([key, value]) => `${key}=${value}`).join("&");
|
|
205
|
+
const url = `${WEB_URL}exchange/?${queryString}`;
|
|
206
|
+
return url;
|
|
207
|
+
}
|
|
208
|
+
async function getBuild(projectId, buildId) {
|
|
209
|
+
const headers = getAuthedHeaders();
|
|
210
|
+
const opt = { headers };
|
|
211
|
+
const { data } = await axios.get(`${API_URL}/projects/${projectId}/builds/${buildId}`, opt);
|
|
212
|
+
return castObjectDates(data);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
var Platform = /* @__PURE__ */ ((Platform2) => {
|
|
216
|
+
Platform2["IOS"] = "IOS";
|
|
217
|
+
Platform2["ANDROID"] = "ANDROID";
|
|
218
|
+
return Platform2;
|
|
219
|
+
})(Platform || {});
|
|
220
|
+
var GameEngine = /* @__PURE__ */ ((GameEngine2) => {
|
|
221
|
+
GameEngine2["GODOT"] = "godot";
|
|
222
|
+
return GameEngine2;
|
|
223
|
+
})(GameEngine || {});
|
|
224
|
+
var JobStatus = /* @__PURE__ */ ((JobStatus2) => {
|
|
225
|
+
JobStatus2["PENDING"] = "PENDING";
|
|
226
|
+
JobStatus2["PROCESSING"] = "PROCESSING";
|
|
227
|
+
JobStatus2["COMPLETED"] = "COMPLETED";
|
|
228
|
+
JobStatus2["FAILED"] = "FAILED";
|
|
229
|
+
return JobStatus2;
|
|
230
|
+
})(JobStatus || {});
|
|
231
|
+
var JobStage = /* @__PURE__ */ ((JobStage2) => {
|
|
232
|
+
JobStage2["SETUP"] = "SETUP";
|
|
233
|
+
JobStage2["EXPORT"] = "EXPORT";
|
|
234
|
+
JobStage2["CONFIGURE"] = "CONFIGURE";
|
|
235
|
+
JobStage2["BUILD"] = "BUILD";
|
|
236
|
+
JobStage2["PUBLISH"] = "PUBLISH";
|
|
237
|
+
return JobStage2;
|
|
238
|
+
})(JobStage || {});
|
|
239
|
+
var LogLevel = /* @__PURE__ */ ((LogLevel2) => {
|
|
240
|
+
LogLevel2["INFO"] = "INFO";
|
|
241
|
+
LogLevel2["WARN"] = "WARN";
|
|
242
|
+
LogLevel2["ERROR"] = "ERROR";
|
|
243
|
+
return LogLevel2;
|
|
244
|
+
})(LogLevel || {});
|
|
245
|
+
var CredentialsType = /* @__PURE__ */ ((CredentialsType2) => {
|
|
246
|
+
CredentialsType2["CERTIFICATE"] = "CERTIFICATE";
|
|
247
|
+
CredentialsType2["KEY"] = "KEY";
|
|
248
|
+
return CredentialsType2;
|
|
249
|
+
})(CredentialsType || {});
|
|
250
|
+
|
|
251
|
+
const queryClient = new QueryClient({
|
|
252
|
+
defaultOptions: {
|
|
253
|
+
queries: {
|
|
254
|
+
staleTime: 50
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
function isCWDGodotGame() {
|
|
260
|
+
const cwd = process.cwd();
|
|
261
|
+
const godotProject = path.join(cwd, "project.godot");
|
|
262
|
+
return fs__default.existsSync(godotProject);
|
|
263
|
+
}
|
|
264
|
+
const GODOT_CAPABILITIES = [
|
|
265
|
+
// TODO: how about capabilities from godot extensions
|
|
266
|
+
{ name: "Access WiFi", key: "capabilities/access_wifi", type: CapabilityType.ACCESS_WIFI },
|
|
267
|
+
{ name: "Push Notifications", key: "capabilities/push_notifications", type: CapabilityType.PUSH_NOTIFICATIONS }
|
|
268
|
+
];
|
|
269
|
+
function getGodotProjectCapabilities(platform) {
|
|
270
|
+
const exportPresets = getGodotExportPresets(platform);
|
|
271
|
+
const options = exportPresets.options;
|
|
272
|
+
const capabilities = [];
|
|
273
|
+
for (const capability of GODOT_CAPABILITIES) {
|
|
274
|
+
if (!(capability.key in options)) continue;
|
|
275
|
+
if (`${options[capability.key]}`.toLocaleLowerCase() == "true") capabilities.push(capability.type);
|
|
276
|
+
}
|
|
277
|
+
return capabilities;
|
|
278
|
+
}
|
|
279
|
+
function getGodotProjectConfig() {
|
|
280
|
+
const cwd = process.cwd();
|
|
281
|
+
const projectGodotPath = path.join(cwd, "project.godot");
|
|
282
|
+
const projectGodotContent = fs__default.readFileSync(projectGodotPath, "utf8");
|
|
283
|
+
return parse(projectGodotContent);
|
|
284
|
+
}
|
|
285
|
+
function getGodotProjectName() {
|
|
286
|
+
try {
|
|
287
|
+
const projectGodotConfig = getGodotProjectConfig();
|
|
288
|
+
return projectGodotConfig["application"]["config/name"];
|
|
289
|
+
} catch (e) {
|
|
290
|
+
return null;
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
function getGodotAppleBundleIdentifier() {
|
|
294
|
+
try {
|
|
295
|
+
const preset = getGodotExportPresets(Platform.IOS);
|
|
296
|
+
return preset.options["application/bundle_identifier"];
|
|
297
|
+
} catch (e) {
|
|
298
|
+
console.log(e);
|
|
299
|
+
return null;
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
function getGodotVersion() {
|
|
303
|
+
const projectGodotConfig = getGodotProjectConfig();
|
|
304
|
+
if ("config/features" in projectGodotConfig["application"]) {
|
|
305
|
+
const features = projectGodotConfig["application"]["config/features"];
|
|
306
|
+
const match = features.match(/"(\d+\.\d+)"/);
|
|
307
|
+
if (!match) throw new Error("Couldn't find Godot version in project.godot");
|
|
308
|
+
return match[1];
|
|
309
|
+
}
|
|
310
|
+
return "3.X";
|
|
311
|
+
}
|
|
312
|
+
function getGodotExportPresets(platform) {
|
|
313
|
+
const { warn } = console;
|
|
314
|
+
let presetConfig = platform === Platform.IOS ? getBaseExportPresets_iOS() : getBaseExportPresets_Android();
|
|
315
|
+
const cwd = process.cwd();
|
|
316
|
+
const filename = "export_presets.cfg";
|
|
317
|
+
const exportPresetsPath = path.join(cwd, filename);
|
|
318
|
+
const isFound = fs__default.existsSync(exportPresetsPath);
|
|
319
|
+
if (!isFound) {
|
|
320
|
+
warn(`${filename} not found at ${exportPresetsPath}`);
|
|
321
|
+
} else {
|
|
322
|
+
const exportPresetsContent = fs__default.readFileSync(exportPresetsPath, "utf8");
|
|
323
|
+
const exportPresetsIni = parse(exportPresetsContent);
|
|
324
|
+
const presetIndexes = Object.keys(exportPresetsIni.preset);
|
|
325
|
+
const presetIndex = presetIndexes.find((index) => {
|
|
326
|
+
const current = exportPresetsIni.preset[index];
|
|
327
|
+
return `${current.name}`.toUpperCase() === platform;
|
|
328
|
+
});
|
|
329
|
+
if (!presetIndex) {
|
|
330
|
+
warn(`Preset ${platform} not found in ${filename} - will use defaults`);
|
|
331
|
+
} else {
|
|
332
|
+
presetConfig = merge(presetConfig, exportPresetsIni.preset[presetIndex]);
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
return presetConfig;
|
|
336
|
+
}
|
|
337
|
+
function getBaseExportPresets_iOS() {
|
|
338
|
+
return {
|
|
339
|
+
name: "iOS",
|
|
340
|
+
platform: "iOS",
|
|
341
|
+
runnable: true,
|
|
342
|
+
dedicated_server: false,
|
|
343
|
+
custom_features: "",
|
|
344
|
+
export_filter: "all_resources",
|
|
345
|
+
include_filter: "",
|
|
346
|
+
exclude_filter: "",
|
|
347
|
+
export_path: "output",
|
|
348
|
+
encryption_include_filters: "",
|
|
349
|
+
encryption_exclude_filters: "",
|
|
350
|
+
encrypt_pck: false,
|
|
351
|
+
encrypt_directory: false,
|
|
352
|
+
options: {
|
|
353
|
+
"custom_template/debug": "",
|
|
354
|
+
"custom_template/release": "",
|
|
355
|
+
"architectures/arm64": true,
|
|
356
|
+
"application/signature": "",
|
|
357
|
+
"application/icon_interpolation": "4",
|
|
358
|
+
"application/launch_screens_interpolation": "4",
|
|
359
|
+
"application/export_project_only": true,
|
|
360
|
+
"capabilities/access_wifi": false,
|
|
361
|
+
"capabilities/push_notifications": false,
|
|
362
|
+
"user_data/accessible_from_files_app": false,
|
|
363
|
+
"user_data/accessible_from_itunes_sharing": false,
|
|
364
|
+
"privacy/camera_usage_description": "",
|
|
365
|
+
"privacy/camera_usage_description_localized": "{}",
|
|
366
|
+
"privacy/microphone_usage_description": "",
|
|
367
|
+
"privacy/microphone_usage_description_localized": "{}",
|
|
368
|
+
"privacy/photolibrary_usage_description": "",
|
|
369
|
+
"privacy/photolibrary_usage_description_localized": "{}",
|
|
370
|
+
"icons/iphone_120x120": "",
|
|
371
|
+
"icons/iphone_180x180": "",
|
|
372
|
+
"icons/ipad_76x76": "",
|
|
373
|
+
"icons/ipad_152x152": "",
|
|
374
|
+
"icons/ipad_167x167": "",
|
|
375
|
+
"icons/app_store_1024x1024": "",
|
|
376
|
+
"icons/spotlight_40x40": "",
|
|
377
|
+
"icons/spotlight_80x80": "",
|
|
378
|
+
"icons/settings_58x58": "",
|
|
379
|
+
"icons/settings_87x87": "",
|
|
380
|
+
"icons/notification_40x40": "",
|
|
381
|
+
"icons/notification_60x60": "",
|
|
382
|
+
"storyboard/use_launch_screen_storyboard": true,
|
|
383
|
+
"storyboard/image_scale_mode": "0",
|
|
384
|
+
"storyboard/custom_image@2x": "",
|
|
385
|
+
"storyboard/custom_image@3x": "",
|
|
386
|
+
"storyboard/use_custom_bg_color": false,
|
|
387
|
+
"storyboard/custom_bg_color": "Color(0, 0, 0, 1)",
|
|
388
|
+
"landscape_launch_screens/iphone_2436x1125": "",
|
|
389
|
+
"landscape_launch_screens/iphone_2208x1242": "",
|
|
390
|
+
"landscape_launch_screens/ipad_1024x768": "",
|
|
391
|
+
"landscape_launch_screens/ipad_2048x1536": "",
|
|
392
|
+
"portrait_launch_screens/iphone_640x960": "",
|
|
393
|
+
"portrait_launch_screens/iphone_640x1136": "",
|
|
394
|
+
"portrait_launch_screens/iphone_750x1334": "",
|
|
395
|
+
"portrait_launch_screens/iphone_1125x2436": "",
|
|
396
|
+
"portrait_launch_screens/ipad_768x1024": "",
|
|
397
|
+
"portrait_launch_screens/ipad_1536x2048": "",
|
|
398
|
+
"portrait_launch_screens/iphone_1242x2208": "",
|
|
399
|
+
"application/short_version": "1.0.0"
|
|
400
|
+
// default version number
|
|
401
|
+
}
|
|
402
|
+
};
|
|
403
|
+
}
|
|
404
|
+
function getBaseExportPresets_Android() {
|
|
405
|
+
return {
|
|
406
|
+
name: "Android",
|
|
407
|
+
platform: "Android",
|
|
408
|
+
// TODO
|
|
409
|
+
options: {
|
|
410
|
+
// TODO
|
|
411
|
+
}
|
|
412
|
+
};
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
class BaseCommand extends Command {
|
|
416
|
+
// add the --json flag
|
|
417
|
+
static enableJsonFlag = false;
|
|
418
|
+
// define flags that can be inherited by any command that extends BaseCommand
|
|
419
|
+
static baseFlags = {};
|
|
420
|
+
flags;
|
|
421
|
+
args;
|
|
422
|
+
async init() {
|
|
423
|
+
process.on("SIGINT", () => process.exit(0));
|
|
424
|
+
process.on("SIGTERM", () => process.exit(0));
|
|
425
|
+
await super.init();
|
|
426
|
+
const { args, flags } = await this.parse({
|
|
427
|
+
flags: this.ctor.flags,
|
|
428
|
+
baseFlags: super.ctor.baseFlags,
|
|
429
|
+
enableJsonFlag: this.ctor.enableJsonFlag,
|
|
430
|
+
args: this.ctor.args,
|
|
431
|
+
strict: this.ctor.strict
|
|
432
|
+
});
|
|
433
|
+
this.flags = flags;
|
|
434
|
+
this.args = args;
|
|
435
|
+
if (this.hasAuthConfig()) await this.loadAuthConfig();
|
|
436
|
+
}
|
|
437
|
+
async catch(err) {
|
|
438
|
+
return super.catch(err);
|
|
439
|
+
}
|
|
440
|
+
async finally(_) {
|
|
441
|
+
return super.finally(_);
|
|
442
|
+
}
|
|
443
|
+
getAuthConfigPath() {
|
|
444
|
+
return path.join(this.config.home, ".shipthis.auth.json");
|
|
445
|
+
}
|
|
446
|
+
hasAuthConfig() {
|
|
447
|
+
const configPath = this.getAuthConfigPath();
|
|
448
|
+
return fs__default.existsSync(configPath);
|
|
449
|
+
}
|
|
450
|
+
async getAuthConfig() {
|
|
451
|
+
const baseConfig = {};
|
|
452
|
+
const configPath = this.getAuthConfigPath();
|
|
453
|
+
if (!fs__default.existsSync(configPath)) return baseConfig;
|
|
454
|
+
const raw = await fs__default.promises.readFile(configPath, "utf8");
|
|
455
|
+
const typesConfig = JSON.parse(raw);
|
|
456
|
+
return {
|
|
457
|
+
...baseConfig,
|
|
458
|
+
...typesConfig
|
|
459
|
+
};
|
|
460
|
+
}
|
|
461
|
+
async setAuthConfig(config) {
|
|
462
|
+
const configPath = this.getAuthConfigPath();
|
|
463
|
+
await fs__default.promises.writeFile(configPath, JSON.stringify(config, null, 2));
|
|
464
|
+
}
|
|
465
|
+
async loadAuthConfig() {
|
|
466
|
+
const authConfig = await this.getAuthConfig();
|
|
467
|
+
if (!authConfig.shipThisUser) {
|
|
468
|
+
throw new Error("You must be logged in to use this command");
|
|
469
|
+
}
|
|
470
|
+
setAuthToken(authConfig.shipThisUser.jwt);
|
|
471
|
+
}
|
|
472
|
+
getProjectConfigPath() {
|
|
473
|
+
return path.join(process.cwd(), "shipthis.json");
|
|
474
|
+
}
|
|
475
|
+
hasProjectConfig() {
|
|
476
|
+
const configPath = this.getProjectConfigPath();
|
|
477
|
+
return fs__default.existsSync(configPath);
|
|
478
|
+
}
|
|
479
|
+
async getProjectConfig() {
|
|
480
|
+
if (!this.hasProjectConfig()) throw new Error("No project config found");
|
|
481
|
+
return this.getProjectConfigSafe();
|
|
482
|
+
}
|
|
483
|
+
async getProjectConfigSafe() {
|
|
484
|
+
if (!this.hasProjectConfig()) return {};
|
|
485
|
+
const configPath = this.getProjectConfigPath();
|
|
486
|
+
const raw = await fs__default.promises.readFile(configPath, "utf8");
|
|
487
|
+
return JSON.parse(raw);
|
|
488
|
+
}
|
|
489
|
+
async setProjectConfig(config) {
|
|
490
|
+
const configPath = this.getProjectConfigPath();
|
|
491
|
+
await fs__default.promises.writeFile(configPath, JSON.stringify(config, null, 2));
|
|
492
|
+
}
|
|
493
|
+
async updateProjectConfig(update) {
|
|
494
|
+
const config = await this.getProjectConfig();
|
|
495
|
+
await this.setProjectConfig({ ...config, ...update });
|
|
496
|
+
}
|
|
497
|
+
// Used in baseGameCommand and the other commands that need to ensure that the CWD is a Godot project
|
|
498
|
+
ensureWeAreInAProjectDir() {
|
|
499
|
+
if (!isCWDGodotGame()) {
|
|
500
|
+
this.error("No Godot project detected. Please run this from a godot project directory.", { exit: 1 });
|
|
501
|
+
}
|
|
502
|
+
if (!this.hasProjectConfig()) {
|
|
503
|
+
this.error(
|
|
504
|
+
'No ShipThis config found. Please run `shipthis game create --name "Space Invaders"` to create a game.',
|
|
505
|
+
{ exit: 1 }
|
|
506
|
+
);
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
async refreshAppleAuthState() {
|
|
510
|
+
const cookies = await this.getAppleCookies();
|
|
511
|
+
const rerunMessage = "Please run shipthis apple login to authenticate with Apple.";
|
|
512
|
+
if (!cookies) throw new Error(`No Apple cookies found. ${rerunMessage}`);
|
|
513
|
+
const authState = await Auth.loginWithCookiesAsync(
|
|
514
|
+
{
|
|
515
|
+
cookies
|
|
516
|
+
},
|
|
517
|
+
{}
|
|
518
|
+
);
|
|
519
|
+
if (!authState) throw new Error(`Failed to refresh Apple auth state. ${rerunMessage}`);
|
|
520
|
+
return authState;
|
|
521
|
+
}
|
|
522
|
+
// Tests the apple cookies
|
|
523
|
+
async hasValidAppleAuthState() {
|
|
524
|
+
try {
|
|
525
|
+
await this.refreshAppleAuthState();
|
|
526
|
+
return true;
|
|
527
|
+
} catch (e) {
|
|
528
|
+
return false;
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
// Used in the apple commands to get the cookies from the auth file
|
|
532
|
+
async getAppleCookies() {
|
|
533
|
+
const authConfig = await this.getAuthConfig();
|
|
534
|
+
if (!authConfig.appleCookies) return null;
|
|
535
|
+
return authConfig.appleCookies;
|
|
536
|
+
}
|
|
537
|
+
async setAppleCookies(cookies) {
|
|
538
|
+
const authConfig = await this.getAuthConfig();
|
|
539
|
+
await this.setAuthConfig({ ...authConfig, appleCookies: cookies });
|
|
540
|
+
}
|
|
541
|
+
ensureWeHaveAppleCookies() {
|
|
542
|
+
if (!this.hasAuthConfig()) {
|
|
543
|
+
this.error("You must be authenticated with Apple in to use this command. Please run shipthis apple login", {
|
|
544
|
+
exit: 1
|
|
545
|
+
});
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
class BaseAuthenticatedCommand extends BaseCommand {
|
|
551
|
+
static flags = {};
|
|
552
|
+
async init() {
|
|
553
|
+
await super.init();
|
|
554
|
+
if (!this.hasAuthConfig()) {
|
|
555
|
+
this.error("No auth config found. Please run `shipthis login` to authenticate.", { exit: 1 });
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
class BaseGameCommand extends BaseAuthenticatedCommand {
|
|
561
|
+
static flags = {
|
|
562
|
+
...BaseAuthenticatedCommand.flags,
|
|
563
|
+
gameId: Flags.string({ char: "g", description: "The ID of the game" })
|
|
564
|
+
};
|
|
565
|
+
async getGame() {
|
|
566
|
+
try {
|
|
567
|
+
const { flags } = this;
|
|
568
|
+
if (flags.gameId) {
|
|
569
|
+
return await getProject(flags.gameId);
|
|
570
|
+
}
|
|
571
|
+
this.ensureWeAreInAProjectDir();
|
|
572
|
+
const { project } = await this.getProjectConfig();
|
|
573
|
+
if (!project) throw new Error("No project");
|
|
574
|
+
return await getProject(project.id);
|
|
575
|
+
} catch (e) {
|
|
576
|
+
if (e?.response?.status === 404) {
|
|
577
|
+
this.error("Game not found - please check you have access");
|
|
578
|
+
} else throw e;
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
async getGameCredentials() {
|
|
582
|
+
const { project } = await this.getProjectConfig();
|
|
583
|
+
if (!project) throw new Error("No project");
|
|
584
|
+
const projectCredentials = await getProjectCredentials(project.id);
|
|
585
|
+
return projectCredentials;
|
|
586
|
+
}
|
|
587
|
+
async updateGame(update) {
|
|
588
|
+
const project = await this.getGame();
|
|
589
|
+
const projectUpdate = {
|
|
590
|
+
...project,
|
|
591
|
+
...update
|
|
592
|
+
};
|
|
593
|
+
const updatedProject = await updateProject(project.id, projectUpdate);
|
|
594
|
+
await this.updateProjectConfig({ project: updatedProject });
|
|
595
|
+
return updatedProject;
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
export { castObjectDates as $, ApiKey as A, BaseAuthenticatedCommand as B, CredentialsType as C, DEFAULT_SHIPPED_FILES_GLOBS as D, getGodotProjectName as E, getBuild as F, GODOT_CAPABILITIES as G, getShortDateTime as H, castArrayObjectDates as I, getProjectJobs as J, JobStatus as K, getShortTimeDelta as L, getJob as M, getNewUploadTicket as N, startJobsFromUpload as O, Platform as P, queryClient as Q, JobStage as R, LogLevel as S, getSingleUseUrl as T, UserRole as U, BaseCommand as V, Auth as W, BACKEND_NAME as X, WS_URL as Y, WEB_URL as Z, getAuthToken as _, ApiKeyType as a, castJobDates as a0, getShortTime as a1, getProject as b, DEFAULT_IGNORED_FILES_GLOBS as c, BaseGameCommand as d, getProjectPlatformProgress as e, getShortDate as f, getUserCredentials as g, CapabilityTypeOption as h, isCWDGodotGame as i, BetaGroup as j, App as k, getGodotAppleBundleIdentifier as l, BundleId as m, getProjectCredentials as n, getAuthedHeaders as o, API_URL as p, Certificate as q, CertificateType as r, Profile as s, ProfileType as t, getGodotProjectCapabilities as u, CapabilityType as v, getProjects as w, GameEngine as x, getGodotVersion as y, createProject as z };
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
const cacheKeys = {
|
|
2
|
+
job: (props) => ["job", ...Object.values(props)],
|
|
3
|
+
jobLogs: (props) => ["jobLogs", ...Object.values(props)],
|
|
4
|
+
userCredentials: (props) => ["userCredentials", ...Object.values(props)],
|
|
5
|
+
projectCredentials: (props) => ["projectCredentials", ...Object.values(props)],
|
|
6
|
+
builds: (props) => ["builds", ...Object.values(props)]
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
export { cacheKeys as c };
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { jsx } from 'react/jsx-runtime';
|
|
2
|
+
import { Flags } from '@oclif/core';
|
|
3
|
+
import { render } from 'ink';
|
|
4
|
+
import 'path';
|
|
5
|
+
import 'fs';
|
|
6
|
+
import { g as getUserCredentials, P as Platform, C as CredentialsType, A as ApiKey, U as UserRole, a as ApiKeyType } from '../../../baseGameCommand-xrD2WDDN.js';
|
|
7
|
+
import 'axios';
|
|
8
|
+
import 'luxon';
|
|
9
|
+
import 'crypto';
|
|
10
|
+
import 'readline-sync';
|
|
11
|
+
import 'node:readline';
|
|
12
|
+
import 'react';
|
|
13
|
+
import '@tanstack/react-query';
|
|
14
|
+
import 'socket.io-client';
|
|
15
|
+
import 'isomorphic-git';
|
|
16
|
+
import { B as BaseAppleCommand } from '../../../baseAppleCommand-BXUu-026.js';
|
|
17
|
+
import { A as App } from '../../../App-DFPMSEZF.js';
|
|
18
|
+
import 'ink-spinner';
|
|
19
|
+
import 'string-length';
|
|
20
|
+
import 'strip-ansi';
|
|
21
|
+
import { R as RunWithSpinner } from '../../../RunWithSpinner-BVXNWGD3.js';
|
|
22
|
+
import { u as uploadUserCredentials } from '../../../upload-CUlWmNbS.js';
|
|
23
|
+
import '@expo/apple-utils/build/index.js';
|
|
24
|
+
import 'ini';
|
|
25
|
+
import 'deepmerge';
|
|
26
|
+
|
|
27
|
+
class AppleApiKeyCreate extends BaseAppleCommand {
|
|
28
|
+
static args = {};
|
|
29
|
+
static description = "Creates an App Store Connect API Key in your Apple Developer account and saves the private key in your ShipThis account";
|
|
30
|
+
static examples = ["<%= config.bin %> <%= command.id %>", "<%= config.bin %> <%= command.id %> --force"];
|
|
31
|
+
static flags = {
|
|
32
|
+
force: Flags.boolean({ char: "f" }),
|
|
33
|
+
quiet: Flags.boolean({ char: "q", description: "Avoid output except for interactions and errors" })
|
|
34
|
+
};
|
|
35
|
+
async run() {
|
|
36
|
+
const { flags } = this;
|
|
37
|
+
const { force } = flags;
|
|
38
|
+
const userCredentials = await getUserCredentials();
|
|
39
|
+
const userAppleApiKeyCredentials = userCredentials.filter(
|
|
40
|
+
(cred) => cred.platform == Platform.IOS && cred.type == CredentialsType.KEY
|
|
41
|
+
);
|
|
42
|
+
if (userAppleApiKeyCredentials.length !== 0 && !force) {
|
|
43
|
+
this.error("An App Store Connect API already exists. Use --force to overwrite it.");
|
|
44
|
+
}
|
|
45
|
+
const authState = await this.refreshAppleAuthState();
|
|
46
|
+
const ctx = authState.context;
|
|
47
|
+
const createApiKey = async () => {
|
|
48
|
+
const userKey = await ApiKey.createAsync(ctx, {
|
|
49
|
+
nickname: `ShipThis ${Math.floor((/* @__PURE__ */ new Date()).valueOf() / 1e3)}`,
|
|
50
|
+
allAppsVisible: true,
|
|
51
|
+
roles: [UserRole.ADMIN],
|
|
52
|
+
keyType: ApiKeyType.PUBLIC_API
|
|
53
|
+
});
|
|
54
|
+
const keyContent = await userKey.downloadAsync();
|
|
55
|
+
if (!keyContent) throw new Error("Failed to download key content");
|
|
56
|
+
const reloadedKey = await ApiKey.infoAsync(ctx, { id: userKey.id });
|
|
57
|
+
const key = {
|
|
58
|
+
keyId: userKey.id,
|
|
59
|
+
issuer: `${reloadedKey.attributes.provider?.id}`,
|
|
60
|
+
p8Content: keyContent,
|
|
61
|
+
serialNumber: userKey.id
|
|
62
|
+
};
|
|
63
|
+
await uploadUserCredentials({
|
|
64
|
+
platform: Platform.IOS,
|
|
65
|
+
type: CredentialsType.KEY,
|
|
66
|
+
contents: key,
|
|
67
|
+
serialNumber: key.serialNumber
|
|
68
|
+
});
|
|
69
|
+
};
|
|
70
|
+
const handleComplete = async () => {
|
|
71
|
+
await this.config.runCommand(`apple:apiKey:status`);
|
|
72
|
+
};
|
|
73
|
+
if (this.flags.quiet) return await createApiKey();
|
|
74
|
+
render(
|
|
75
|
+
/* @__PURE__ */ jsx(App, { children: /* @__PURE__ */ jsx(
|
|
76
|
+
RunWithSpinner,
|
|
77
|
+
{
|
|
78
|
+
msgInProgress: `Creating App Store Connect API in the Apple Developer Portal...`,
|
|
79
|
+
msgComplete: `App Store Connect API created and saved to ShipThis`,
|
|
80
|
+
executeMethod: createApiKey,
|
|
81
|
+
onComplete: handleComplete
|
|
82
|
+
}
|
|
83
|
+
) })
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export { AppleApiKeyCreate as default };
|