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.
Files changed (67) hide show
  1. package/README.md +894 -0
  2. package/bin/alias.sh +10 -0
  3. package/bin/dev.cmd +3 -0
  4. package/bin/dev.js +7 -0
  5. package/bin/dev.tsc.js +15 -0
  6. package/bin/loader.js +20 -0
  7. package/bin/run.cmd +3 -0
  8. package/bin/run.js +5 -0
  9. package/bin/run.tsc.js +15 -0
  10. package/dist/App-DFPMSEZF.js +21 -0
  11. package/dist/AppleBundleIdDetails-CuhAbVEp.js +61 -0
  12. package/dist/NextSteps-CK9zHOCt.js +18 -0
  13. package/dist/RunWithSpinner-BVXNWGD3.js +27 -0
  14. package/dist/StatusTable-CxuX_R1D.js +38 -0
  15. package/dist/Table-CvM6pccN.js +101 -0
  16. package/dist/Title-BCQtayg6.js +6 -0
  17. package/dist/UserCredentialsTable-DHeRI4h6.js +76 -0
  18. package/dist/baseAppleCommand-BXUu-026.js +10 -0
  19. package/dist/baseGameCommand-xrD2WDDN.js +599 -0
  20. package/dist/cacheKeys-CShA-ZjE.js +9 -0
  21. package/dist/commands/apple/apiKey/create.js +88 -0
  22. package/dist/commands/apple/apiKey/export.js +66 -0
  23. package/dist/commands/apple/apiKey/import.js +70 -0
  24. package/dist/commands/apple/apiKey/status.js +108 -0
  25. package/dist/commands/apple/certificate/create.js +118 -0
  26. package/dist/commands/apple/certificate/export.js +66 -0
  27. package/dist/commands/apple/certificate/import.js +70 -0
  28. package/dist/commands/apple/certificate/status.js +116 -0
  29. package/dist/commands/apple/login.js +74 -0
  30. package/dist/commands/apple/status.js +50 -0
  31. package/dist/commands/dashboard.js +32 -0
  32. package/dist/commands/game/build/download.js +78 -0
  33. package/dist/commands/game/build/list.js +83 -0
  34. package/dist/commands/game/create.js +59 -0
  35. package/dist/commands/game/details.js +89 -0
  36. package/dist/commands/game/export.js +52 -0
  37. package/dist/commands/game/ios/app/addTester.js +111 -0
  38. package/dist/commands/game/ios/app/create.js +104 -0
  39. package/dist/commands/game/ios/app/status.js +52 -0
  40. package/dist/commands/game/ios/app/sync.js +81 -0
  41. package/dist/commands/game/ios/profile/create.js +110 -0
  42. package/dist/commands/game/ios/profile/export.js +68 -0
  43. package/dist/commands/game/ios/profile/import.js +77 -0
  44. package/dist/commands/game/ios/profile/status.js +183 -0
  45. package/dist/commands/game/ios/status.js +79 -0
  46. package/dist/commands/game/job/list.js +80 -0
  47. package/dist/commands/game/job/status.js +297 -0
  48. package/dist/commands/game/list.js +69 -0
  49. package/dist/commands/game/ship.js +67 -0
  50. package/dist/commands/game/status.js +76 -0
  51. package/dist/commands/game/wizard.js +124 -0
  52. package/dist/commands/login.js +63 -0
  53. package/dist/commands/status.js +80 -0
  54. package/dist/export-CujqsTR_.js +36 -0
  55. package/dist/git-DREGq-jc.js +32 -0
  56. package/dist/import-Q-KO61ll.js +38 -0
  57. package/dist/index-DKsVctbw.js +125 -0
  58. package/dist/index.d.ts +1 -0
  59. package/dist/index.js +1 -0
  60. package/dist/run.js +3 -0
  61. package/dist/upload-CUlWmNbS.js +60 -0
  62. package/dist/useAppleApp-BmwYu7qG.js +32 -0
  63. package/dist/useAppleBundleId-DCJnfNWr.js +64 -0
  64. package/dist/useBuilds-Dh_PWwCf.js +41 -0
  65. package/dist/useJob-CCkqCMvF.js +34 -0
  66. package/oclif.manifest.json +1510 -0
  67. package/package.json +167 -0
@@ -0,0 +1,79 @@
1
+ import { jsxs, jsx } from 'react/jsx-runtime';
2
+ import { render } from 'ink';
3
+ import { Flags } from '@oclif/core';
4
+ import { A as App } from '../../../App-DFPMSEZF.js';
5
+ import 'ink-spinner';
6
+ import { d as BaseGameCommand, e as getProjectPlatformProgress, P as Platform, f as getShortDate } from '../../../baseGameCommand-xrD2WDDN.js';
7
+ import { g as getShortUUID, m as makeHumanReadable } from '../../../index-DKsVctbw.js';
8
+ import 'react';
9
+ import { A as AppleAppDetails, a as AppleBundleIdDetails } from '../../../AppleBundleIdDetails-CuhAbVEp.js';
10
+ import { S as StatusTable } from '../../../StatusTable-CxuX_R1D.js';
11
+ import 'axios';
12
+ import '@tanstack/react-query';
13
+ import 'socket.io-client';
14
+ import 'string-length';
15
+ import 'strip-ansi';
16
+ import 'luxon';
17
+ import { N as NextSteps } from '../../../NextSteps-CK9zHOCt.js';
18
+ import 'path';
19
+ import 'fs';
20
+ import '@expo/apple-utils/build/index.js';
21
+ import 'crypto';
22
+ import 'readline-sync';
23
+ import 'node:readline';
24
+ import 'isomorphic-git';
25
+ import 'ini';
26
+ import 'deepmerge';
27
+ import '../../../useAppleApp-BmwYu7qG.js';
28
+ import '../../../Title-BCQtayg6.js';
29
+ import '../../../Table-CvM6pccN.js';
30
+ import '../../../useAppleBundleId-DCJnfNWr.js';
31
+
32
+ class GameIosStatus extends BaseGameCommand {
33
+ static args = {};
34
+ static description = "Shows the Game iOS Platform status. If --gameId is not provided it will look in the current directory.";
35
+ static examples = [
36
+ "<%= config.bin %> <%= command.id %>",
37
+ "<%= config.bin %> <%= command.id %> --gameId 0c179fc4"
38
+ ];
39
+ static flags = {
40
+ gameId: Flags.string({ char: "g", description: "The ID of the game" })
41
+ };
42
+ async run() {
43
+ const game = await this.getGame();
44
+ const iosPlatformStatus = await getProjectPlatformProgress(game.id, Platform.IOS);
45
+ const gameStatuses = {
46
+ name: game.name,
47
+ id: getShortUUID(game.id),
48
+ createdAt: getShortDate(game.createdAt),
49
+ engine: "Godot"
50
+ };
51
+ const steps = [iosPlatformStatus.hasBundleSet == false && "$ shipthis game ios app create"].filter(
52
+ Boolean
53
+ );
54
+ const progressToStatuses = (progress) => {
55
+ const { platform, ...rest } = progress;
56
+ return makeHumanReadable(rest);
57
+ };
58
+ const authState = await this.refreshAppleAuthState();
59
+ const ctx = authState.context;
60
+ render(
61
+ /* @__PURE__ */ jsxs(App, { children: [
62
+ /* @__PURE__ */ jsx(StatusTable, { marginBottom: 1, title: "ShipThis game status", statuses: gameStatuses }),
63
+ /* @__PURE__ */ jsx(
64
+ StatusTable,
65
+ {
66
+ marginBottom: 1,
67
+ title: "Overall iOS status for game",
68
+ statuses: progressToStatuses(iosPlatformStatus)
69
+ }
70
+ ),
71
+ /* @__PURE__ */ jsx(AppleAppDetails, { iosBundleId: game.details?.iosBundleId, ctx }),
72
+ /* @__PURE__ */ jsx(AppleBundleIdDetails, { iosBundleId: game.details?.iosBundleId, ctx }),
73
+ /* @__PURE__ */ jsx(NextSteps, { steps })
74
+ ] })
75
+ );
76
+ }
77
+ }
78
+
79
+ export { GameIosStatus as default };
@@ -0,0 +1,80 @@
1
+ import { jsxs, jsx } from 'react/jsx-runtime';
2
+ import { render, Box, Text } from 'ink';
3
+ import { Flags } from '@oclif/core';
4
+ import 'path';
5
+ import 'fs';
6
+ import '@expo/apple-utils/build/index.js';
7
+ import { d as BaseGameCommand, J as getProjectJobs } from '../../../baseGameCommand-xrD2WDDN.js';
8
+ import { d as getJobStatusColor } from '../../../index-DKsVctbw.js';
9
+ import { A as App } from '../../../App-DFPMSEZF.js';
10
+ import 'ink-spinner';
11
+ import { T as Table } from '../../../Table-CvM6pccN.js';
12
+ import { DateTime } from 'luxon';
13
+ import '@tanstack/react-query';
14
+ import 'axios';
15
+ import { a as getJobSummary } from '../../../useJob-CCkqCMvF.js';
16
+ import 'react';
17
+ import 'socket.io-client';
18
+ import 'string-length';
19
+ import 'strip-ansi';
20
+ import 'crypto';
21
+ import 'readline-sync';
22
+ import 'node:readline';
23
+ import 'ini';
24
+ import 'deepmerge';
25
+ import 'isomorphic-git';
26
+ import '../../../cacheKeys-CShA-ZjE.js';
27
+
28
+ class GameJobList extends BaseGameCommand {
29
+ static args = {};
30
+ static description = "Lists the jobs for a game. If --gameId is not provided it will look in the current directory.";
31
+ static examples = [
32
+ "<%= config.bin %> <%= command.id %>",
33
+ "<%= config.bin %> <%= command.id %> --gameId 0c179fc4"
34
+ ];
35
+ static flags = {
36
+ ...super.flags,
37
+ pageNumber: Flags.integer({ char: "p", description: "The page number to show (starts at 0)", default: 0 }),
38
+ pageSize: Flags.integer({ char: "s", description: "The number of items to show per page", default: 10 }),
39
+ orderBy: Flags.string({
40
+ char: "o",
41
+ description: "The field to order by",
42
+ default: "createdAt",
43
+ options: ["createdAt", "updatedAt"]
44
+ }),
45
+ order: Flags.string({
46
+ char: "r",
47
+ description: "The order to sort by",
48
+ default: "desc",
49
+ options: ["asc", "desc"]
50
+ })
51
+ };
52
+ async run() {
53
+ const game = await this.getGame();
54
+ const { flags } = this;
55
+ const { gameId, ...otherFlags } = flags;
56
+ const params = otherFlags;
57
+ const jobListResponse = await getProjectJobs(game.id, params);
58
+ const data = jobListResponse.data.map((j) => getJobSummary(j, DateTime.now()));
59
+ render(
60
+ /* @__PURE__ */ jsxs(App, { children: [
61
+ /* @__PURE__ */ jsx(
62
+ Table,
63
+ {
64
+ data,
65
+ getTextProps: (col, val) => {
66
+ if (col.key !== "status") return {};
67
+ return { color: getJobStatusColor(val) };
68
+ }
69
+ }
70
+ ),
71
+ jobListResponse.pageCount > 1 && /* @__PURE__ */ jsxs(Box, { marginTop: 1, flexDirection: "column", children: [
72
+ /* @__PURE__ */ jsx(Text, { children: `Showing page ${flags.pageNumber + 1} of ${jobListResponse.pageCount}.` }),
73
+ /* @__PURE__ */ jsx(Text, { children: "Use the --pageNumber parameter to see other pages." })
74
+ ] })
75
+ ] })
76
+ );
77
+ }
78
+ }
79
+
80
+ export { GameJobList as default };
@@ -0,0 +1,297 @@
1
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
2
+ import { Args, Flags } from '@oclif/core';
3
+ import { measureElement, Box, Text, render } from 'ink';
4
+ import 'path';
5
+ import 'fs';
6
+ import '@expo/apple-utils/build/index.js';
7
+ import { o as getAuthedHeaders, p as API_URL, I as castArrayObjectDates, _ as getAuthToken, Y as WS_URL, $ as castObjectDates, a0 as castJobDates, a1 as getShortTime, K as JobStatus, d as BaseGameCommand, M as getJob } from '../../../baseGameCommand-xrD2WDDN.js';
8
+ import 'crypto';
9
+ import 'readline-sync';
10
+ import 'node:readline';
11
+ import { useEffect, useState, useRef } from 'react';
12
+ import axios from 'axios';
13
+ import { useInfiniteQuery } from '@tanstack/react-query';
14
+ import { io } from 'socket.io-client';
15
+ import { DateTime } from 'luxon';
16
+ import 'isomorphic-git';
17
+ import { A as App } from '../../../App-DFPMSEZF.js';
18
+ import Spinner from 'ink-spinner';
19
+ import { h as getStageColor, j as getMessageColor, d as getJobStatusColor } from '../../../index-DKsVctbw.js';
20
+ import { c as cacheKeys } from '../../../cacheKeys-CShA-ZjE.js';
21
+ import { T as Title } from '../../../Title-BCQtayg6.js';
22
+ import stringLength from 'string-length';
23
+ import stripAnsi from 'strip-ansi';
24
+ import { u as useJob, a as getJobSummary } from '../../../useJob-CCkqCMvF.js';
25
+ import { a as StatusRow, b as StatusRowLabel } from '../../../StatusTable-CxuX_R1D.js';
26
+ import { g as getBuildSummary } from '../../../useBuilds-Dh_PWwCf.js';
27
+ import { N as NextSteps } from '../../../NextSteps-CK9zHOCt.js';
28
+ import 'ini';
29
+ import 'deepmerge';
30
+
31
+ async function queryJobLogs({
32
+ projectId,
33
+ jobId,
34
+ cursor,
35
+ pageSize = 10
36
+ }) {
37
+ try {
38
+ const headers = getAuthedHeaders();
39
+ const base = `${API_URL}/projects/${projectId}/jobs/${jobId}/logs/?pageSize=${pageSize}`;
40
+ const url = base + (cursor ? `&cursor=${cursor}` : "");
41
+ const response = await axios.get(url, { headers });
42
+ return {
43
+ ...response.data,
44
+ data: castArrayObjectDates(response.data.data, ["sentAt", "createdAt"])
45
+ };
46
+ } catch (error) {
47
+ console.warn("queryJobLogs Error", error);
48
+ throw error;
49
+ }
50
+ }
51
+ const useJobLogs = (props) => {
52
+ const queryResult = useInfiniteQuery({
53
+ queryKey: cacheKeys.jobLogs(props),
54
+ queryFn: async ({ pageParam }) => {
55
+ return queryJobLogs({
56
+ ...props,
57
+ cursor: pageParam
58
+ });
59
+ },
60
+ getNextPageParam: (lastPage) => lastPage.nextCursor,
61
+ initialPageParam: props.cursor
62
+ });
63
+ return queryResult;
64
+ };
65
+
66
+ function useWebSocket(listeners = []) {
67
+ const log = () => {
68
+ };
69
+ useEffect(() => {
70
+ if (listeners.length === 0) {
71
+ return;
72
+ }
73
+ const token = getAuthToken();
74
+ const socket = io(WS_URL, {
75
+ auth: { token },
76
+ forceNew: true
77
+ });
78
+ socket.on("connect", () => log());
79
+ for (const listener of listeners) {
80
+ const pattern = listener.getPattern();
81
+ const bindSocket = (pattern2) => {
82
+ const boundListener = listener.eventHandler.bind(listener, pattern2);
83
+ socket.on(pattern2, boundListener);
84
+ };
85
+ if (Array.isArray(pattern)) return pattern.forEach(bindSocket);
86
+ bindSocket(pattern);
87
+ }
88
+ return () => {
89
+ socket.disconnect();
90
+ };
91
+ }, []);
92
+ }
93
+
94
+ function arrayToDictionary(array, key = "id") {
95
+ return array.reduce((a, i) => {
96
+ a[i[key]] = i;
97
+ return a;
98
+ }, {});
99
+ }
100
+ function dictionaryToArray(dictionary) {
101
+ return Object.keys(dictionary).map((key) => dictionary[key]);
102
+ }
103
+
104
+ function getSortedJobLogs(logs) {
105
+ return logs.sort((a, b) => a.sentAt.toMillis() - b.sentAt.toMillis());
106
+ }
107
+ function useJobLogTail(props) {
108
+ const [websocketLogs, setWebsocketLogs] = useState([]);
109
+ const listener = {
110
+ getPattern: () => `project.${props.projectId}:job.${props.jobId}:log`,
111
+ eventHandler: async (pattern, rawLogEntry) => {
112
+ setWebsocketLogs((prevLogs) => {
113
+ const logEntry = castObjectDates(rawLogEntry, ["sentAt", "createdAt"]);
114
+ return [...prevLogs, logEntry];
115
+ });
116
+ }
117
+ };
118
+ useWebSocket(props.isWatching ? [listener] : []);
119
+ const { isLoading, data: fetchedJobLogs } = useJobLogs({
120
+ projectId: props.projectId,
121
+ jobId: props.jobId,
122
+ pageSize: props.length
123
+ });
124
+ useEffect(() => {
125
+ setWebsocketLogs([]);
126
+ }, [props.jobId, props.projectId, props.length, props.isWatching, fetchedJobLogs]);
127
+ const firstPage = fetchedJobLogs ? fetchedJobLogs?.pages[0].data : [];
128
+ const allLogs = [...firstPage, ...websocketLogs];
129
+ const allLogsById = arrayToDictionary(allLogs);
130
+ const uniqueLogs = dictionaryToArray(allLogsById);
131
+ const data = getSortedJobLogs(uniqueLogs).slice(-props.length);
132
+ return {
133
+ isLoading,
134
+ data
135
+ };
136
+ }
137
+
138
+ function useJobWatching({ projectId, jobId, isWatching, onJobUpdate }) {
139
+ const [websocketJob, setWebsocketJob] = useState(null);
140
+ const listener = {
141
+ getPattern: () => [`project.${projectId}:job:created`, `project.${projectId}:job:updated`],
142
+ eventHandler: async (pattern, rawJob) => {
143
+ if (rawJob.id !== jobId) return;
144
+ const job2 = castJobDates(rawJob);
145
+ setWebsocketJob(job2);
146
+ if (onJobUpdate) onJobUpdate(job2);
147
+ }
148
+ };
149
+ useWebSocket(isWatching ? [listener] : []);
150
+ const { isLoading, data: job } = useJob({
151
+ projectId,
152
+ jobId
153
+ });
154
+ useEffect(() => {
155
+ setWebsocketJob(null);
156
+ }, [jobId, projectId, isWatching, job]);
157
+ const fetchedJob = job ? job : null;
158
+ const data = websocketJob ? websocketJob : fetchedJob;
159
+ return {
160
+ isLoading,
161
+ data
162
+ };
163
+ }
164
+
165
+ const TruncatedText = ({ children, wrap, ...textPropsWithoutWrap }) => {
166
+ const ref = useRef();
167
+ const [width, setWidth] = useState(null);
168
+ useEffect(() => {
169
+ if (!ref.current) return;
170
+ const { width: measuredWidth } = measureElement(ref.current);
171
+ setWidth(measuredWidth);
172
+ }, []);
173
+ const getTruncated = (input) => {
174
+ const withoutCrlf = input.replaceAll(/[\r\n]/g, "");
175
+ if (width === null) return withoutCrlf;
176
+ const withoutAnsi = stripAnsi(withoutCrlf);
177
+ const textLength = stringLength(withoutAnsi);
178
+ return textLength > width ? withoutCrlf.substring(0, width) : withoutCrlf;
179
+ };
180
+ return /* @__PURE__ */ jsx(Box, { ref, children: /* @__PURE__ */ jsx(Text, { ...textPropsWithoutWrap, children: getTruncated(children) + "\x1B[0m" }) });
181
+ };
182
+
183
+ const JobLogTail = (props) => {
184
+ const { isLoading, data } = useJobLogTail(props);
185
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
186
+ /* @__PURE__ */ jsx(Title, { children: "Job Logs" }),
187
+ isLoading && /* @__PURE__ */ jsx(Spinner, { type: "dots" }),
188
+ /* @__PURE__ */ jsx(Box, { flexDirection: "column", children: data.map((log) => {
189
+ const stageColor = getStageColor(log.stage);
190
+ const messageColor = getMessageColor(log.level);
191
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "row", overflow: "hidden", height: 1, children: [
192
+ /* @__PURE__ */ jsx(Box, { children: /* @__PURE__ */ jsx(Text, { children: getShortTime(log.sentAt) }) }),
193
+ /* @__PURE__ */ jsx(Box, { marginLeft: 1, width: 9, justifyContent: "flex-start", children: /* @__PURE__ */ jsx(Text, { color: stageColor, children: log.stage }) }),
194
+ /* @__PURE__ */ jsx(Box, { marginLeft: 1, overflow: "hidden", height: 1, marginRight: 2, children: /* @__PURE__ */ jsx(TruncatedText, { color: messageColor, children: log.message }) })
195
+ ] }, log.id);
196
+ }) })
197
+ ] });
198
+ };
199
+
200
+ const JobStatusSpinner = ({ status, showSpinner }) => /* @__PURE__ */ jsxs(Fragment, { children: [
201
+ /* @__PURE__ */ jsx(Text, { color: getJobStatusColor(status), children: `${status}` }),
202
+ showSpinner && /* @__PURE__ */ jsxs(Fragment, { children: [
203
+ /* @__PURE__ */ jsx(Text, { children: " " }),
204
+ /* @__PURE__ */ jsx(Spinner, { type: "dots" })
205
+ ] })
206
+ ] });
207
+ const JobStatusTable = ({ jobId, projectId, isWatching, onJobUpdate }) => {
208
+ const { data: job, isLoading } = useJobWatching({ projectId, jobId, isWatching, onJobUpdate });
209
+ const [time, setTime] = useState(DateTime.now());
210
+ useEffect(() => {
211
+ if (!isWatching) return;
212
+ const interval = setInterval(() => setTime(DateTime.now()), 1e3);
213
+ return () => {
214
+ clearInterval(interval);
215
+ };
216
+ }, []);
217
+ const isJobInProgress = job && ![JobStatus.COMPLETED, JobStatus.FAILED].includes(job.status);
218
+ const summary = job ? getJobSummary(job, time) : null;
219
+ const buildSummary = job && job.build ? getBuildSummary(job.build) : null;
220
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "row", children: [
221
+ /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [
222
+ /* @__PURE__ */ jsx(Title, { children: "Job Details" }),
223
+ isLoading && /* @__PURE__ */ jsx(Spinner, { type: "dots" }),
224
+ summary && job && /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginLeft: 2, children: [
225
+ /* @__PURE__ */ jsx(StatusRow, { label: "ID", value: summary.id }),
226
+ /* @__PURE__ */ jsx(StatusRow, { label: "Name", value: job.project.name }),
227
+ /* @__PURE__ */ jsxs(Box, { flexDirection: "row", children: [
228
+ /* @__PURE__ */ jsx(StatusRowLabel, { label: "Status" }),
229
+ /* @__PURE__ */ jsx(JobStatusSpinner, { status: job.status, showSpinner: isWatching && !!isJobInProgress })
230
+ ] }),
231
+ /* @__PURE__ */ jsx(StatusRow, { label: "Version", value: summary.version }),
232
+ /* @__PURE__ */ jsx(StatusRow, { label: "Git Info", value: summary.gitInfo }),
233
+ /* @__PURE__ */ jsx(StatusRow, { label: "Platform", value: summary.platform }),
234
+ /* @__PURE__ */ jsx(StatusRow, { label: "Started At", value: summary.createdAt }),
235
+ /* @__PURE__ */ jsx(StatusRow, { label: "Runtime", value: summary.runtime })
236
+ ] })
237
+ ] }),
238
+ buildSummary && /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginBottom: 1, marginLeft: 3, borderStyle: "single", padding: 1, children: [
239
+ /* @__PURE__ */ jsx(Title, { children: "Build Details" }),
240
+ /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginLeft: 2, children: [
241
+ /* @__PURE__ */ jsx(StatusRow, { label: "ID", value: buildSummary.id }),
242
+ /* @__PURE__ */ jsx(StatusRow, { label: "Platform", value: buildSummary.platform }),
243
+ /* @__PURE__ */ jsx(StatusRow, { label: "Type", value: buildSummary.type }),
244
+ /* @__PURE__ */ jsx(StatusRow, { label: "CMD", value: buildSummary.cmd })
245
+ ] })
246
+ ] })
247
+ ] });
248
+ };
249
+
250
+ class GameJobStatus extends BaseGameCommand {
251
+ static args = {
252
+ job_id: Args.string({ description: "The id of the job to get the status of", required: true })
253
+ };
254
+ static description = "Shows the real-time status of a job.";
255
+ static examples = [
256
+ "<%= config.bin %> <%= command.id %> 4d32239e",
257
+ "<%= config.bin %> <%= command.id %> --gameId 0c179fc4 4d32239e",
258
+ "<%= config.bin %> <%= command.id %> --gameId 0c179fc4 --lines 20 --follow 4d32239e"
259
+ ];
260
+ static flags = {
261
+ ...super.flags,
262
+ lines: Flags.integer({ char: "n", description: "The number of lines to show", default: 10 }),
263
+ follow: Flags.boolean({ char: "f", description: "Follow the log in real-time", default: false })
264
+ };
265
+ async getJob() {
266
+ try {
267
+ const game = await this.getGame();
268
+ const job = await getJob(this.args.job_id, game.id);
269
+ return job;
270
+ } catch (e) {
271
+ if (e?.response?.status === 404) {
272
+ this.error("Job not found - please check you have access", { exit: 1 });
273
+ }
274
+ throw e;
275
+ }
276
+ }
277
+ async run() {
278
+ const job = await this.getJob();
279
+ const { lines, follow } = this.flags;
280
+ const handleJobUpdate = (job2) => {
281
+ if (!follow) return;
282
+ if ([JobStatus.COMPLETED, JobStatus.FAILED].includes(job2.status)) {
283
+ const exitCode = job2.status == JobStatus.FAILED ? 1 : 0;
284
+ setTimeout(() => process.exit(exitCode), 5e3);
285
+ }
286
+ };
287
+ render(
288
+ /* @__PURE__ */ jsxs(App, { children: [
289
+ /* @__PURE__ */ jsx(JobStatusTable, { jobId: job.id, projectId: job.project.id, isWatching: follow, onJobUpdate: handleJobUpdate }),
290
+ /* @__PURE__ */ jsx(JobLogTail, { jobId: job.id, projectId: job.project.id, isWatching: follow, length: lines }),
291
+ /* @__PURE__ */ jsx(NextSteps, { steps: [] })
292
+ ] })
293
+ );
294
+ }
295
+ }
296
+
297
+ export { GameJobStatus as default };
@@ -0,0 +1,69 @@
1
+ import { jsxs, jsx } from 'react/jsx-runtime';
2
+ import { render, Box, Text } from 'ink';
3
+ import { Flags } from '@oclif/core';
4
+ import { B as BaseAuthenticatedCommand, w as getProjects, f as getShortDate } from '../../baseGameCommand-xrD2WDDN.js';
5
+ import 'path';
6
+ import 'fs';
7
+ import '@expo/apple-utils/build/index.js';
8
+ import { g as getShortUUID } from '../../index-DKsVctbw.js';
9
+ import { A as App } from '../../App-DFPMSEZF.js';
10
+ import 'ink-spinner';
11
+ import { T as Table } from '../../Table-CvM6pccN.js';
12
+ import 'luxon';
13
+ import '@tanstack/react-query';
14
+ import 'axios';
15
+ import 'react';
16
+ import 'socket.io-client';
17
+ import 'string-length';
18
+ import 'strip-ansi';
19
+ import 'crypto';
20
+ import 'readline-sync';
21
+ import 'node:readline';
22
+ import 'ini';
23
+ import 'deepmerge';
24
+ import 'isomorphic-git';
25
+
26
+ class GameList extends BaseAuthenticatedCommand {
27
+ static args = {};
28
+ static description = "Shows a list of all your games";
29
+ static examples = ["<%= config.bin %> <%= command.id %>"];
30
+ static flags = {
31
+ pageNumber: Flags.integer({ char: "p", description: "The page number to show (starts at 0)", default: 0 }),
32
+ pageSize: Flags.integer({ char: "s", description: "The number of items to show per page", default: 10 }),
33
+ orderBy: Flags.string({
34
+ char: "o",
35
+ description: "The field to order by",
36
+ default: "createdAt",
37
+ options: ["createdAt", "updatedAt", "name"]
38
+ }),
39
+ order: Flags.string({
40
+ char: "r",
41
+ description: "The order to sort by",
42
+ default: "desc",
43
+ options: ["asc", "desc"]
44
+ })
45
+ };
46
+ async run() {
47
+ const { flags } = this;
48
+ const params = flags;
49
+ const gameListResponse = await getProjects(params);
50
+ const data = gameListResponse.data.map((game) => {
51
+ return {
52
+ id: getShortUUID(game.id),
53
+ name: game.name,
54
+ createdAt: getShortDate(game.createdAt)
55
+ };
56
+ });
57
+ render(
58
+ /* @__PURE__ */ jsxs(App, { children: [
59
+ /* @__PURE__ */ jsx(Table, { data }),
60
+ gameListResponse.pageCount > 1 && /* @__PURE__ */ jsxs(Box, { marginTop: 1, flexDirection: "column", children: [
61
+ /* @__PURE__ */ jsx(Text, { children: `Showing page ${flags.pageNumber + 1} of ${gameListResponse.pageCount}.` }),
62
+ /* @__PURE__ */ jsx(Text, { children: "Use the --pageNumber parameter to see other pages." })
63
+ ] })
64
+ ] })
65
+ );
66
+ }
67
+ }
68
+
69
+ export { GameList as default };
@@ -0,0 +1,67 @@
1
+ import { v4 } from 'uuid';
2
+ import axios from 'axios';
3
+ import fg from 'fast-glob';
4
+ import fs__default from 'fs';
5
+ import yazl from 'yazl';
6
+ import { d as BaseGameCommand, D as DEFAULT_SHIPPED_FILES_GLOBS, c as DEFAULT_IGNORED_FILES_GLOBS, N as getNewUploadTicket, O as startJobsFromUpload } from '../../baseGameCommand-xrD2WDDN.js';
7
+ import { e as getFileHash } from '../../index-DKsVctbw.js';
8
+ import { g as getCWDGitInfo } from '../../git-DREGq-jc.js';
9
+ import '@oclif/core';
10
+ import 'path';
11
+ import '@expo/apple-utils/build/index.js';
12
+ import 'luxon';
13
+ import 'crypto';
14
+ import 'readline-sync';
15
+ import 'node:readline';
16
+ import 'react';
17
+ import '@tanstack/react-query';
18
+ import 'socket.io-client';
19
+ import 'ini';
20
+ import 'deepmerge';
21
+ import 'isomorphic-git';
22
+
23
+ class GameShip extends BaseGameCommand {
24
+ static args = {};
25
+ static description = "Builds the app (for all platforms with valid credentials) and ships it to the stores";
26
+ static examples = ["<%= config.bin %> <%= command.id %>"];
27
+ async run() {
28
+ await this.ensureWeAreInAProjectDir();
29
+ const projectConfig = await this.getProjectConfig();
30
+ if (!projectConfig) throw new Error("No project config found");
31
+ if (!projectConfig.project) throw new Error("No project found in project config");
32
+ const shippedFilesGlobs = projectConfig.shippedFilesGlobs || DEFAULT_SHIPPED_FILES_GLOBS;
33
+ const ignoredFilesGlobs = projectConfig.ignoredFilesGlobs || DEFAULT_IGNORED_FILES_GLOBS;
34
+ const files = await fg(shippedFilesGlobs, { dot: true, ignore: ignoredFilesGlobs });
35
+ const zipFile = new yazl.ZipFile();
36
+ for (const file of files) {
37
+ zipFile.addFile(file, file);
38
+ }
39
+ const outputZipToFile = (zip, fileName) => new Promise((resolve) => {
40
+ const outputStream = fs__default.createWriteStream(fileName);
41
+ zip.outputStream.pipe(outputStream).on("close", () => resolve());
42
+ zip.end();
43
+ });
44
+ const tmpZipFile = `${process.cwd()}/shipthis-${v4()}.zip`;
45
+ await outputZipToFile(zipFile, tmpZipFile);
46
+ const zipBuffer = fs__default.readFileSync(tmpZipFile);
47
+ const { size } = fs__default.statSync(tmpZipFile);
48
+ const uploadTicket = await getNewUploadTicket(projectConfig.project.id);
49
+ await axios.put(uploadTicket.url, zipBuffer, {
50
+ headers: {
51
+ "Content-length": size,
52
+ "Content-Type": "application/zip"
53
+ }
54
+ });
55
+ const gitInfo = await getCWDGitInfo();
56
+ const zipFileMd5 = await getFileHash(tmpZipFile);
57
+ const uploadDetails = {
58
+ ...gitInfo,
59
+ zipFileMd5
60
+ };
61
+ const [firstJob] = await startJobsFromUpload(uploadTicket.id, uploadDetails);
62
+ fs__default.unlinkSync(tmpZipFile);
63
+ await this.config.runCommand(`game:job:status`, [firstJob.id, "--follow"]);
64
+ }
65
+ }
66
+
67
+ export { GameShip as default };
@@ -0,0 +1,76 @@
1
+ import { jsxs, jsx } from 'react/jsx-runtime';
2
+ import { render } from 'ink';
3
+ import { Flags } from '@oclif/core';
4
+ import { A as App } from '../../App-DFPMSEZF.js';
5
+ import 'ink-spinner';
6
+ import { d as BaseGameCommand, e as getProjectPlatformProgress, P as Platform, f as getShortDate } from '../../baseGameCommand-xrD2WDDN.js';
7
+ import { g as getShortUUID, m as makeHumanReadable } from '../../index-DKsVctbw.js';
8
+ import 'react';
9
+ import 'luxon';
10
+ import '@tanstack/react-query';
11
+ import '@expo/apple-utils/build/index.js';
12
+ import 'axios';
13
+ import { S as StatusTable } from '../../StatusTable-CxuX_R1D.js';
14
+ import 'socket.io-client';
15
+ import 'string-length';
16
+ import 'strip-ansi';
17
+ import { N as NextSteps } from '../../NextSteps-CK9zHOCt.js';
18
+ import 'path';
19
+ import 'fs';
20
+ import 'crypto';
21
+ import 'readline-sync';
22
+ import 'node:readline';
23
+ import 'isomorphic-git';
24
+ import 'ini';
25
+ import 'deepmerge';
26
+ import '../../Title-BCQtayg6.js';
27
+
28
+ class GameStatus extends BaseGameCommand {
29
+ static args = {};
30
+ static description = "Shows the Game status. If --gameId is not provided it will look in the current directory.";
31
+ static examples = [
32
+ "<%= config.bin %> <%= command.id %>",
33
+ "<%= config.bin %> <%= command.id %> --gameId 0c179fc4"
34
+ ];
35
+ static flags = {
36
+ gameId: Flags.string({ char: "g", description: "The ID of the game" })
37
+ };
38
+ async run() {
39
+ const game = await this.getGame();
40
+ const iosPlatformStatus = await getProjectPlatformProgress(game.id, Platform.IOS);
41
+ const { hasBundleSet, hasApiKeyForPlatform, hasCredentialsForPlatform } = iosPlatformStatus;
42
+ const steps = [
43
+ hasBundleSet == false && "$ shipthis game ios app create",
44
+ hasApiKeyForPlatform == false && "$ shipthis apple apiKey create",
45
+ hasCredentialsForPlatform == false && "$ shipthis game ios profile create",
46
+ hasBundleSet && hasApiKeyForPlatform && hasCredentialsForPlatform && "$ shipthis game ship"
47
+ ].filter(Boolean);
48
+ const progressToStatuses = (progress) => {
49
+ const { platform, ...rest } = progress;
50
+ return makeHumanReadable(rest);
51
+ };
52
+ render(
53
+ /* @__PURE__ */ jsxs(App, { children: [
54
+ /* @__PURE__ */ jsx(
55
+ StatusTable,
56
+ {
57
+ marginBottom: 1,
58
+ title: "Game Details",
59
+ statuses: {
60
+ "Game ID": getShortUUID(game.id),
61
+ Name: game.name,
62
+ Version: `${game.details?.semanticVersion || "0.0.1"}`,
63
+ "Build Number": `${game.details?.buildNumber || 1}`,
64
+ "Created At": getShortDate(game.createdAt),
65
+ "Game Engine": `${game.details?.gameEngine || "godot"} ${game.details?.gameEngineVersion || "4.3"}`
66
+ }
67
+ }
68
+ ),
69
+ /* @__PURE__ */ jsx(StatusTable, { title: "iOS Status", statuses: progressToStatuses(iosPlatformStatus) }),
70
+ /* @__PURE__ */ jsx(NextSteps, { steps })
71
+ ] })
72
+ );
73
+ }
74
+ }
75
+
76
+ export { GameStatus as default };