shipthis 0.1.34 → 0.1.35

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 (100) hide show
  1. package/assets/markdown/{android-success.md → android-success.md.ejs} +2 -2
  2. package/assets/markdown/{apikey-create.md → apikey-create.md.ejs} +4 -4
  3. package/assets/markdown/{create-google-play-game.md → create-google-play-game.md.ejs} +2 -2
  4. package/assets/markdown/{invite-service-account.md → invite-service-account.md.ejs} +1 -1
  5. package/assets/markdown/{ios-success.md → ios-success.md.ejs} +2 -2
  6. package/assets/markdown/{privacy-notification.md → privacy-notification.md.ejs} +1 -1
  7. package/assets/markdown/service-account-policy-wizard.md.ejs +26 -0
  8. package/assets/markdown/service-account-policy.md.ejs +30 -0
  9. package/assets/markdown/{ship-failure.md → ship-failure.md.ejs} +1 -1
  10. package/assets/markdown/{ship-success.md → ship-success.md.ejs} +2 -2
  11. package/dist/{AppleBundleIdDetails-By-sSGNl.js → AppleBundleIdDetails-N_j1wv6f.js} +5 -4
  12. package/dist/{Command-VWMnGslo.js → Command-DN1j3tjt.js} +2 -2
  13. package/dist/{CommandGame-BSVPZzNl.js → CommandGame-D2NqytWc.js} +2 -2
  14. package/dist/{Create-SR1Mq7BY.js → Create-BRdv5jXQ.js} +4 -3
  15. package/dist/{GameStatus-1ntFyxEx.js → GameStatus-joyHPy0f.js} +12 -17
  16. package/dist/{Import-cedOWPDz.js → Import-BNMHsJoM.js} +5 -4
  17. package/dist/{JobLogTail-C_W8UAMg.js → JobLogTail-D35FO5v-.js} +5 -5
  18. package/dist/{JobProgress-CI385G53.js → JobProgress-D-9KESaA.js} +6 -5
  19. package/dist/{JobStatusTable-CdbKtwJE.js → JobStatusTable-DIJ_h-gi.js} +4 -4
  20. package/dist/{ProjectCredentialsTable-ZnuIfcDB.js → ProjectCredentialsTable-BMKgv99h.js} +2 -2
  21. package/dist/{UserCredentialsTable-DRkAYgEA.js → UserCredentialsTable-CUrTDzTK.js} +10 -10
  22. package/dist/{baseAppleCommand-CsO-_Yzn.js → baseAppleCommand-Cs9a52e5.js} +1 -1
  23. package/dist/{baseGameAndroidCommand-CdjaAbNX.js → baseGameAndroidCommand-BCB1SMCf.js} +2 -2
  24. package/dist/commands/apiKey/create.js +19 -16
  25. package/dist/commands/apiKey/list.js +10 -7
  26. package/dist/commands/apiKey/revoke.js +11 -8
  27. package/dist/commands/apple/apiKey/create.js +11 -8
  28. package/dist/commands/apple/apiKey/export.js +10 -7
  29. package/dist/commands/apple/apiKey/import.js +10 -7
  30. package/dist/commands/apple/apiKey/status.js +16 -13
  31. package/dist/commands/apple/certificate/create.js +11 -8
  32. package/dist/commands/apple/certificate/export.js +10 -7
  33. package/dist/commands/apple/certificate/import.js +10 -7
  34. package/dist/commands/apple/certificate/status.js +16 -13
  35. package/dist/commands/apple/login.js +2 -2
  36. package/dist/commands/apple/status.js +8 -5
  37. package/dist/commands/dashboard.js +1 -1
  38. package/dist/commands/game/android/apiKey/connect.js +13 -9
  39. package/dist/commands/game/android/apiKey/create.js +15 -11
  40. package/dist/commands/game/android/apiKey/export.js +11 -8
  41. package/dist/commands/game/android/apiKey/import.js +11 -8
  42. package/dist/commands/game/android/apiKey/invite.js +4 -4
  43. package/dist/commands/game/android/apiKey/policy.js +87 -0
  44. package/dist/commands/game/android/apiKey/status.js +11 -8
  45. package/dist/commands/game/android/keyStore/create.js +12 -9
  46. package/dist/commands/game/android/keyStore/export.js +10 -7
  47. package/dist/commands/game/android/keyStore/import.js +13 -10
  48. package/dist/commands/game/android/keyStore/status.js +10 -7
  49. package/dist/commands/game/android/status.js +1 -1
  50. package/dist/commands/game/build/download.js +8 -5
  51. package/dist/commands/game/build/list.js +9 -6
  52. package/dist/commands/game/create.js +2 -2
  53. package/dist/commands/game/details.js +8 -5
  54. package/dist/commands/game/export.js +1 -1
  55. package/dist/commands/game/ios/app/addTester.js +15 -12
  56. package/dist/commands/game/ios/app/create.js +8 -5
  57. package/dist/commands/game/ios/app/status.js +11 -8
  58. package/dist/commands/game/ios/app/sync.js +9 -6
  59. package/dist/commands/game/ios/profile/create.js +11 -8
  60. package/dist/commands/game/ios/profile/export.js +10 -7
  61. package/dist/commands/game/ios/profile/import.js +10 -7
  62. package/dist/commands/game/ios/profile/status.js +10 -7
  63. package/dist/commands/game/ios/status.js +13 -10
  64. package/dist/commands/game/ios/wizard.js +9 -6
  65. package/dist/commands/game/job/list.js +8 -5
  66. package/dist/commands/game/job/status.js +11 -8
  67. package/dist/commands/game/list.js +15 -10
  68. package/dist/commands/game/ship.js +15 -12
  69. package/dist/commands/game/status.js +10 -7
  70. package/dist/commands/game/wizard.js +23 -19
  71. package/dist/commands/internal/fastlane.js +1 -1
  72. package/dist/commands/internal/readme.js +4 -715
  73. package/dist/commands/login.js +2 -2
  74. package/dist/commands/status.js +14 -14
  75. package/dist/ejs-DirFZbza.js +716 -0
  76. package/dist/{export-B5Yfd9vw.js → export-DFCZKNQk.js} +1 -1
  77. package/dist/{import-A2WcStHl.js → import-BpGyif-m.js} +1 -1
  78. package/dist/{index-BBLtvl1Y.js → index-BTXEUd8W.js} +59 -16
  79. package/dist/{index-CO_ssVFA.js → index-BwnzoldS.js} +14 -2
  80. package/dist/{index-CYjZ26If.js → index-CJWMt1s-.js} +1 -1
  81. package/dist/{index-Bz1qt_8T.js → index-DlE_SPt3.js} +7 -11
  82. package/dist/{index-CA6-uLMn.js → index-hoHfGrjg.js} +33 -33
  83. package/dist/{index-CgzANgJt.js → index-izrACZbC.js} +1 -1
  84. package/dist/{upload-BIsFZzBO.js → upload-D19OQsbn.js} +1 -1
  85. package/dist/{useAndroidServiceAccountTestResult-DueKynFy.js → useAndroidServiceAccountTestResult-CwKeW0ED.js} +1 -1
  86. package/dist/{useAppleApp-BZR94exU.js → useAppleApp-BZc_cNa-.js} +1 -1
  87. package/dist/{useAppleBundleId-Dg5DsItN.js → useAppleBundleId-DvMXAvWD.js} +1 -1
  88. package/dist/useGoogleStatus-Cx_QIsXa.js +10 -0
  89. package/dist/{useProjectCredentials-jQYGcDhT.js → useProjectCredentials-DxdwJCfU.js} +14 -11
  90. package/dist/{useWebSocket-CIxkPaYi.js → useWebSocket-cM5yOcDv.js} +1 -1
  91. package/docs/README.md +3 -2
  92. package/docs/apiKey.md +12 -2
  93. package/docs/game/android/apiKey/policy.md +28 -0
  94. package/docs/game/android/keyStore.md +14 -4
  95. package/docs/game/android/status.md +4 -0
  96. package/docs/game/android.md +9 -305
  97. package/docs/game/ship.md +43 -11
  98. package/docs/game.md +7 -1
  99. package/package.json +3 -1
  100. /package/assets/markdown/{create-or-import-keystore.md → create-or-import-keystore.md.ejs} +0 -0
@@ -1,6 +1,6 @@
1
1
  import * as fs from 'node:fs';
2
2
  import axios from 'axios';
3
- import { o as API_URL, p as getAuthedHeaders } from './index-CO_ssVFA.js';
3
+ import { o as API_URL, p as getAuthedHeaders } from './index-BwnzoldS.js';
4
4
 
5
5
  async function exportCredential({ credentialId, projectId, zipPath }) {
6
6
  const headers = getAuthedHeaders();
@@ -1,6 +1,6 @@
1
1
  import { promises } from 'node:fs';
2
2
  import axios from 'axios';
3
- import { o as API_URL, p as getAuthedHeaders } from './index-CO_ssVFA.js';
3
+ import { o as API_URL, p as getAuthedHeaders } from './index-BwnzoldS.js';
4
4
 
5
5
  async function getNewImportTicket(projectId) {
6
6
  const url = projectId ? `${API_URL}/projects/${projectId}/credentials/import/url` : `${API_URL}/credentials/import/url`;
@@ -8,30 +8,29 @@ import 'node:path';
8
8
  import 'node:readline';
9
9
  import 'node:url';
10
10
  import 'readline-sync';
11
- import 'luxon';
11
+ import { p as getAuthedHeaders, o as API_URL, a7 as castObjectDates, K as queryClient, T as revokePolicy, S as enforcePolicy, P as Platform, C as CredentialsType, Q as getGoogleStatus, b as getShortDate } from './index-BwnzoldS.js';
12
12
  import axios from 'axios';
13
13
  import 'isomorphic-git';
14
- import { p as getAuthedHeaders, o as API_URL, a4 as castObjectDates, P as Platform, C as CredentialsType, Q as getGoogleStatus } from './index-CO_ssVFA.js';
15
14
  import '@oclif/core';
16
- import { useQuery, useQueryClient } from '@tanstack/react-query';
17
- import { c as cacheKeys } from './useAndroidServiceAccountTestResult-DueKynFy.js';
15
+ import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
16
+ import { c as cacheKeys } from './useAndroidServiceAccountTestResult-CwKeW0ED.js';
17
+ import 'luxon';
18
18
  import 'fast-glob';
19
19
  import 'uuid';
20
20
  import 'yazl';
21
21
  import 'socket.io-client';
22
22
  import 'fullscreen-ink';
23
- import { u as useWebSocket } from './useWebSocket-CIxkPaYi.js';
24
- import { u as useProjectCredentials } from './useProjectCredentials-jQYGcDhT.js';
23
+ import { u as useWebSocket } from './useWebSocket-cM5yOcDv.js';
24
+ import { u as useProjectCredentials } from './useProjectCredentials-DxdwJCfU.js';
25
+ import { u as useGoogleStatus } from './useGoogleStatus-Cx_QIsXa.js';
25
26
  import 'crypto-js';
27
+ import { G as GameContext, e as useSafeInput, M as Markdown } from './index-hoHfGrjg.js';
26
28
  import 'string-length';
27
29
  import 'strip-ansi';
28
30
  import 'open';
29
31
  import '@inkjs/ui';
30
- import 'marked';
31
- import 'marked-terminal';
32
32
  import { P as ProgressSpinner } from './ProgressSpinner-Um6ARKlk.js';
33
33
  import 'qrcode';
34
- import { G as GameContext } from './index-CA6-uLMn.js';
35
34
 
36
35
  async function fetchStatus({ projectId }) {
37
36
  try {
@@ -53,6 +52,20 @@ const useAndroidServiceAccountSetupStatus = (props) => useQuery({
53
52
  staleTime: 1e3 * 5
54
53
  });
55
54
 
55
+ const useUpdateGoogleOrgPolicy = () => useMutation({
56
+ async mutationFn(props) {
57
+ if (props.action === "revoke") {
58
+ return await revokePolicy();
59
+ }
60
+ return await enforcePolicy();
61
+ },
62
+ async onSuccess(data) {
63
+ queryClient.invalidateQueries({
64
+ queryKey: cacheKeys.googleStatus()
65
+ });
66
+ }
67
+ });
68
+
56
69
  const ERR_NOT_AUTHENTICATED = "You must be connected to Google to create a Service Account Key";
57
70
  const useHasServiceAccountKey = (projectId) => {
58
71
  const { data, isSuccess } = useProjectCredentials({ platform: Platform.ANDROID, projectId });
@@ -116,20 +129,50 @@ const CreateServiceAccountKey = (props) => {
116
129
  };
117
130
  const CreateForGame = ({ gameId, onComplete, onError, ...boxProps }) => {
118
131
  const [didStart, setDidStart] = useState(false);
132
+ const startedRef = useRef(false);
133
+ const { data: googleStatus } = useGoogleStatus();
134
+ const updatePolicyMutation = useUpdateGoogleOrgPolicy();
119
135
  const { handleStart, isCreating, setupStatus } = useAndroidServiceAccount({
120
136
  onComplete,
121
137
  onError,
122
138
  projectId: gameId
123
139
  });
124
140
  useEffect(() => {
125
- handleStart().then(() => setDidStart(true));
126
- }, [gameId]);
141
+ if (startedRef.current) return;
142
+ if (!googleStatus) return;
143
+ if (!googleStatus.needsPolicyChange) {
144
+ startedRef.current = true;
145
+ handleStart().then(() => setDidStart(true)).catch((error) => onError(error));
146
+ }
147
+ }, [googleStatus]);
148
+ useSafeInput((input) => {
149
+ if (input === "p" && googleStatus?.needsPolicyChange && !updatePolicyMutation.isPending) {
150
+ updatePolicyMutation.mutate({ action: "revoke" });
151
+ }
152
+ });
153
+ const needsPolicy = Boolean(googleStatus?.needsPolicyChange);
154
+ const policyChanging = updatePolicyMutation.isPending || updatePolicyMutation.isSuccess && needsPolicy;
155
+ const Header = () => /* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 1, children: [
156
+ /* @__PURE__ */ jsx(Text, { children: "Creating a Service Account and API Key..." }),
157
+ isCreating && /* @__PURE__ */ jsx(Spinner, { type: "dots" })
158
+ ] });
127
159
  return /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsxs(Box, { flexDirection: "column", gap: 1, ...boxProps, children: [
128
- /* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 1, children: [
129
- /* @__PURE__ */ jsx(Text, { children: "Creating a Service Account and API Key..." }),
130
- isCreating && /* @__PURE__ */ jsx(Spinner, { type: "dots" })
131
- ] }),
132
- didStart && /* @__PURE__ */ jsx(ProgressSpinner, { progress: (setupStatus?.progress || 0) * 100, spinnerType: "dots" })
160
+ needsPolicy ? policyChanging ? /* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 1, children: [
161
+ /* @__PURE__ */ jsx(Text, { children: "Updating organization policy..." }),
162
+ /* @__PURE__ */ jsx(Spinner, { type: "dots" })
163
+ ] }) : googleStatus && /* @__PURE__ */ jsx(
164
+ Markdown,
165
+ {
166
+ filename: "service-account-policy-wizard.md.ejs",
167
+ templateVars: {
168
+ needsPolicyChange: Boolean(googleStatus.needsPolicyChange),
169
+ orgCreatedAt: googleStatus.orgCreatedAt ? getShortDate(googleStatus.orgCreatedAt) : "Unknown",
170
+ orgName: `${googleStatus.orgName}`,
171
+ orgResourceName: `${googleStatus.orgResourceName}`
172
+ }
173
+ }
174
+ ) : /* @__PURE__ */ jsx(Header, {}),
175
+ didStart && /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsx(ProgressSpinner, { progress: (setupStatus?.progress || 0) * 100, spinnerType: "dots" }) })
133
176
  ] }) });
134
177
  };
135
178
 
@@ -251,7 +251,19 @@ async function getGoogleStatus() {
251
251
  const headers = getAuthedHeaders();
252
252
  const opt = { headers };
253
253
  const { data } = await axios.get(`${API_URL}/me/google/status`, opt);
254
- return data;
254
+ return castObjectDates(data, ["orgCreatedAt"]);
255
+ }
256
+ async function enforcePolicy() {
257
+ const headers = getAuthedHeaders();
258
+ const opt = { headers };
259
+ const { data } = await axios.post(`${API_URL}/me/google/policy`, null, opt);
260
+ return castObjectDates(data, ["orgCreatedAt"]);
261
+ }
262
+ async function revokePolicy() {
263
+ const headers = getAuthedHeaders();
264
+ const opt = { headers };
265
+ const { data } = await axios.delete(`${API_URL}/me/google/policy`, opt);
266
+ return castObjectDates(data, ["orgCreatedAt"]);
255
267
  }
256
268
  async function inviteServiceAccount(projectId, developerId) {
257
269
  try {
@@ -769,4 +781,4 @@ const DetailsFlags = {
769
781
  semanticVersion: Flags.string({ char: "s", description: "Set the semantic version" })
770
782
  };
771
783
 
772
- export { setAuthToken as $, ApiKey as A, BaseAuthenticatedCommand as B, CredentialsType as C, DetailsFlags as D, getProject as E, getProjectPlatformProgress as F, GODOT_CAPABILITIES as G, downloadBuildById as H, castArrayObjectDates as I, JobStatus as J, queryClient as K, JobStage as L, LogLevel as M, WS_URL as N, getAuthToken as O, Platform as P, getGoogleStatus as Q, getGodotAndroidPackageName as R, inviteServiceAccount as S, disconnectGoogle as T, UserRole as U, BaseCommand as V, WEB_URL as W, getAPIKeys as X, createAPIKey as Y, revokeAPIKey as Z, getSingleUseUrl as _, ApiKeyType as a, acceptTerms as a0, Auth as a1, getNewUploadTicket as a2, startJobsFromUpload as a3, castObjectDates as a4, getShortAuthRequiredUrl as a5, getGoogleAuthUrl as a6, castJobDates as a7, getShortTime as a8, getShortDateTime as a9, getShortTimeDelta as aa, BuildType as ab, updateProject as ac, getShortDate as b, BaseGameCommand as c, getGodotAppleBundleIdentifier as d, BundleId as e, App as f, getProjects as g, CapabilityTypeOption as h, BetaGroup as i, isCWDGodotGame as j, Certificate as k, CertificateType as l, Profile as m, ProfileType as n, API_URL as o, getAuthedHeaders as p, getGodotProjectCapabilities as q, CapabilityType as r, GameEngine as s, getGodotVersion as t, createProject as u, DEFAULT_SHIPPED_FILES_GLOBS as v, DEFAULT_IGNORED_FILES_GLOBS as w, getGodotProjectName as x, getProjectJobs as y, getJob as z };
784
+ export { revokeAPIKey as $, ApiKey as A, BaseAuthenticatedCommand as B, CredentialsType as C, DetailsFlags as D, getProject as E, getProjectPlatformProgress as F, GODOT_CAPABILITIES as G, downloadBuildById as H, castArrayObjectDates as I, JobStatus as J, queryClient as K, JobStage as L, LogLevel as M, WS_URL as N, getAuthToken as O, Platform as P, getGoogleStatus as Q, getGodotAndroidPackageName as R, enforcePolicy as S, revokePolicy as T, UserRole as U, inviteServiceAccount as V, WEB_URL as W, disconnectGoogle as X, BaseCommand as Y, getAPIKeys as Z, createAPIKey as _, ApiKeyType as a, getSingleUseUrl as a0, setAuthToken as a1, acceptTerms as a2, Auth as a3, getNewUploadTicket as a4, startJobsFromUpload as a5, getShortAuthRequiredUrl as a6, castObjectDates as a7, getGoogleAuthUrl as a8, castJobDates as a9, getShortTime as aa, getShortDateTime as ab, getShortTimeDelta as ac, BuildType as ad, updateProject as ae, getShortDate as b, BaseGameCommand as c, getGodotAppleBundleIdentifier as d, BundleId as e, App as f, getProjects as g, CapabilityTypeOption as h, BetaGroup as i, isCWDGodotGame as j, Certificate as k, CertificateType as l, Profile as m, ProfileType as n, API_URL as o, getAuthedHeaders as p, getGodotProjectCapabilities as q, CapabilityType as r, GameEngine as s, getGodotVersion as t, createProject as u, DEFAULT_SHIPPED_FILES_GLOBS as v, DEFAULT_IGNORED_FILES_GLOBS as w, getGodotProjectName as x, getProjectJobs as y, getJob as z };
@@ -4,7 +4,7 @@ import path from 'node:path';
4
4
  import { promises } from 'node:readline';
5
5
  import { fileURLToPath } from 'node:url';
6
6
  import readlineSync from 'readline-sync';
7
- import { L as JobStage, P as Platform, M as LogLevel, J as JobStatus } from './index-CO_ssVFA.js';
7
+ import { L as JobStage, P as Platform, M as LogLevel, J as JobStatus } from './index-BwnzoldS.js';
8
8
  import 'luxon';
9
9
  import 'axios';
10
10
  import 'isomorphic-git';
@@ -12,30 +12,26 @@ import 'readline-sync';
12
12
  import 'luxon';
13
13
  import 'axios';
14
14
  import 'isomorphic-git';
15
- import { Q as getGoogleStatus, a5 as getShortAuthRequiredUrl, a6 as getGoogleAuthUrl, W as WEB_URL } from './index-CO_ssVFA.js';
15
+ import { a6 as getShortAuthRequiredUrl, a8 as getGoogleAuthUrl, W as WEB_URL } from './index-BwnzoldS.js';
16
16
  import '@oclif/core';
17
- import { useQuery } from '@tanstack/react-query';
17
+ import '@tanstack/react-query';
18
18
  import 'crypto-js';
19
19
  import 'uuid';
20
20
  import 'fast-glob';
21
21
  import 'yazl';
22
22
  import 'socket.io-client';
23
- import { c as cacheKeys } from './useAndroidServiceAccountTestResult-DueKynFy.js';
24
- import { u as useWebSocket } from './useWebSocket-CIxkPaYi.js';
23
+ import { u as useGoogleStatus } from './useGoogleStatus-Cx_QIsXa.js';
24
+ import { u as useWebSocket } from './useWebSocket-cM5yOcDv.js';
25
25
  import 'fullscreen-ink';
26
- import { G as GameContext, e as useSafeInput, M as Markdown } from './index-CA6-uLMn.js';
26
+ import { G as GameContext, e as useSafeInput, M as Markdown } from './index-hoHfGrjg.js';
27
27
  import 'string-length';
28
28
  import 'strip-ansi';
29
29
  import '@inkjs/ui';
30
+ import './ejs-DirFZbza.js';
30
31
  import 'marked';
31
32
  import 'marked-terminal';
32
33
  import qrcode from 'qrcode';
33
34
 
34
- const useGoogleStatus = () => useQuery({
35
- queryFn: getGoogleStatus,
36
- queryKey: cacheKeys.googleStatus()
37
- });
38
-
39
35
  function useGoogleStatusWatching({
40
36
  isWatching,
41
37
  onGoogleStatusUpdate,
@@ -134,7 +130,7 @@ const ConnectForGame = ({ gameId, helpPage, onComplete, onError, ...boxProps })
134
130
  };
135
131
  return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", gap: 1, ...boxProps, children: [
136
132
  !showQRCode && /* @__PURE__ */ jsxs(Box, { flexDirection: "column", gap: 1, children: [
137
- /* @__PURE__ */ jsx(Markdown, { filename: "privacy-notification.md", templateVars }),
133
+ /* @__PURE__ */ jsx(Markdown, { filename: "privacy-notification.md.ejs", templateVars }),
138
134
  connectUrl && /* @__PURE__ */ jsx(Text, { bold: true, color: "#4CE64C", children: `Press B to open ${connectUrl} in your browser and connect your Google account to ShipThis` }),
139
135
  /* @__PURE__ */ jsx(Text, { bold: true, color: "#4CE64C", children: "Press Q to show a QR-code to connect using your mobile phone" })
140
136
  ] }),
@@ -3,11 +3,11 @@ import { useStdin, useInput, Text, Box } from 'ink';
3
3
  import Spinner from 'ink-spinner';
4
4
  import open from 'open';
5
5
  import React, { useState, useEffect, useContext, useRef } from 'react';
6
- import { P as Platform, a9 as getShortDateTime, p as getAuthedHeaders, o as API_URL, I as castArrayObjectDates, J as JobStatus, aa as getShortTimeDelta, z as getJob, E as getProject, a5 as getShortAuthRequiredUrl, K as queryClient, ab as BuildType, W as WEB_URL } from './index-CO_ssVFA.js';
7
- import { h as getPlatformName, g as getShortUUID, s as scriptDir } from './index-CYjZ26If.js';
6
+ import { P as Platform, ab as getShortDateTime, p as getAuthedHeaders, o as API_URL, I as castArrayObjectDates, J as JobStatus, ac as getShortTimeDelta, z as getJob, E as getProject, a6 as getShortAuthRequiredUrl, K as queryClient, ad as BuildType, W as WEB_URL } from './index-BwnzoldS.js';
7
+ import { g as getShortUUID, h as getPlatformName, s as scriptDir } from './index-CJWMt1s-.js';
8
8
  import { useQuery } from '@tanstack/react-query';
9
9
  import axios from 'axios';
10
- import { c as cacheKeys, u as useAndroidServiceAccountTestResult, K as KeyTestStatus, a as KeyTestError } from './useAndroidServiceAccountTestResult-DueKynFy.js';
10
+ import { c as cacheKeys, u as useAndroidServiceAccountTestResult, K as KeyTestStatus, a as KeyTestError } from './useAndroidServiceAccountTestResult-CwKeW0ED.js';
11
11
  import 'luxon';
12
12
  import fs__default from 'node:fs';
13
13
  import 'fast-glob';
@@ -19,6 +19,7 @@ import 'string-length';
19
19
  import 'strip-ansi';
20
20
  import '@inkjs/ui';
21
21
  import path from 'node:path';
22
+ import { e as ejs } from './ejs-DirFZbza.js';
22
23
  import { setOptions, parse } from 'marked';
23
24
  import TerminalRenderer from 'marked-terminal';
24
25
  import 'qrcode';
@@ -40,14 +41,16 @@ async function queryBuilds({ projectId, ...pageAndSortParams }) {
40
41
  function getBuildSummary(build) {
41
42
  const ext = build.buildType || (build.platform === Platform.IOS ? "IPA" : "AAB");
42
43
  const filename = `game.${ext.toLowerCase()}`;
43
- return {
44
- id: getShortUUID(build.id),
45
- jobId: getShortUUID(build.jobId),
46
- ...getJobDetailsSummary(build.jobDetails),
47
- cmd: `shipthis game build download ${getShortUUID(build.id)} ${filename}`,
48
- createdAt: getShortDateTime(build.createdAt),
49
- type: `${getPlatformName(build.platform)} ${build.buildType || ""}`.trim()
50
- };
44
+ const details = getJobDetailsSummary(build.jobDetails);
45
+ const summary = {};
46
+ summary.id = getShortUUID(build.id);
47
+ summary.version = details.version;
48
+ summary.gitInfo = details.gitInfo;
49
+ summary.platform = getPlatformName(build.platform);
50
+ summary.jobId = getShortUUID(build.jobId);
51
+ summary.createdAt = getShortDateTime(build.createdAt);
52
+ summary.cmd = `shipthis game build download ${getShortUUID(build.id)} ${filename}`;
53
+ return summary;
51
54
  }
52
55
  const useBuilds = (props) => {
53
56
  const queryResult = useQuery({
@@ -60,23 +63,25 @@ const useBuilds = (props) => {
60
63
  function getJobDetailsSummary(jobDetails) {
61
64
  const semanticVersion = jobDetails?.semanticVersion || "N/A";
62
65
  const buildNumber = jobDetails?.buildNumber || "N/A";
63
- const gitCommit = jobDetails?.gitCommitHash ? getShortUUID(jobDetails?.gitCommitHash) : "";
66
+ const gitCommit = jobDetails?.gitCommitHash ? getShortUUID(jobDetails.gitCommitHash) : "";
64
67
  const gitBranch = jobDetails?.gitBranch || "";
65
- return {
66
- gitInfo: gitCommit ? `${gitCommit} (${gitBranch})` : "",
67
- version: `${semanticVersion} (${buildNumber})`
68
- };
68
+ const details = {};
69
+ details.version = `${semanticVersion} (${buildNumber})`;
70
+ details.gitInfo = gitCommit ? `${gitCommit} (${gitBranch})` : "";
71
+ return details;
69
72
  }
70
73
  function getJobSummary(job, timeNow) {
71
74
  const inProgress = ![JobStatus.COMPLETED, JobStatus.FAILED].includes(job.status);
72
- return {
73
- id: getShortUUID(job.id),
74
- ...getJobDetailsSummary(job.details),
75
- createdAt: getShortDateTime(job.createdAt),
76
- platform: getPlatformName(job.type),
77
- runtime: getShortTimeDelta(job.createdAt, inProgress ? timeNow : job.updatedAt),
78
- status: job.status
79
- };
75
+ const details = getJobDetailsSummary(job.details);
76
+ const summary = {};
77
+ summary.id = getShortUUID(job.id);
78
+ summary.version = details.version;
79
+ summary.gitInfo = details.gitInfo;
80
+ summary.platform = getPlatformName(job.type);
81
+ summary.status = job.status;
82
+ summary.createdAt = getShortDateTime(job.createdAt);
83
+ summary.runtime = getShortTimeDelta(job.createdAt, inProgress ? timeNow : job.updatedAt);
84
+ return summary;
80
85
  }
81
86
  const useJob = (props) => useQuery({
82
87
  queryFn: () => getJob(props.jobId, props.projectId),
@@ -110,14 +115,9 @@ const getRenderedMarkdown = ({ filename, templateVars, ...options }) => {
110
115
  const root = path.dirname(entrypointPath);
111
116
  const mdPath = path.join(root, "..", "assets", "markdown", filename);
112
117
  const mdTemplate = fs__default.readFileSync(mdPath, "utf8").trim();
113
- let markdown = mdTemplate;
114
- if (templateVars) {
115
- markdown = markdown.replaceAll(/\${if (.*?)}([\S\s]*?)\${endif}/g, (_, key, content) => templateVars[key.trim()] ? content : "");
116
- markdown = markdown.replaceAll(/\${(.*?)}/g, (_, key) => {
117
- const trimmed = key.trim();
118
- return templateVars[trimmed] ? String(templateVars[trimmed]) : "";
119
- });
120
- }
118
+ const markdown = ejs.render(mdTemplate, templateVars ?? {}, {
119
+ filename: mdPath
120
+ });
121
121
  const rendered = parse(markdown).trim();
122
122
  const cleaned = cleanHyperlinks(rendered);
123
123
  return cleaned;
@@ -214,7 +214,7 @@ const Create = ({ gameId, onComplete, onError, ...boxProps }) => {
214
214
  /* @__PURE__ */ jsx(Text, { bold: true, children: isFetching ? "Checking..." : "ShipThis has not detected your game in Google Play. Press R to test again." }),
215
215
  isFetching && /* @__PURE__ */ jsx(Spinner, { type: "dots" })
216
216
  ] }),
217
- /* @__PURE__ */ jsx(Markdown, { filename: "create-google-play-game.md", templateVars })
217
+ /* @__PURE__ */ jsx(Markdown, { filename: "create-google-play-game.md.ejs", templateVars })
218
218
  ] }) });
219
219
  };
220
220
 
@@ -1,5 +1,5 @@
1
1
  import axios from 'axios';
2
- import { o as API_URL, p as getAuthedHeaders, I as castArrayObjectDates } from './index-CO_ssVFA.js';
2
+ import { o as API_URL, p as getAuthedHeaders, I as castArrayObjectDates } from './index-BwnzoldS.js';
3
3
  import 'node:fs';
4
4
 
5
5
  async function getUserCredentials(pageSize = 100) {
@@ -1,5 +1,5 @@
1
1
  import axios from 'axios';
2
- import { o as API_URL, p as getAuthedHeaders } from './index-CO_ssVFA.js';
2
+ import { o as API_URL, p as getAuthedHeaders } from './index-BwnzoldS.js';
3
3
 
4
4
  async function getNewUploadTicket(projectId = null) {
5
5
  const url = projectId ? `${API_URL}/projects/${projectId}/credentials/url` : `${API_URL}/credentials/url`;
@@ -1,6 +1,6 @@
1
1
  import { useQuery } from '@tanstack/react-query';
2
2
  import axios from 'axios';
3
- import { o as API_URL, p as getAuthedHeaders } from './index-CO_ssVFA.js';
3
+ import { o as API_URL, p as getAuthedHeaders } from './index-BwnzoldS.js';
4
4
 
5
5
  const cacheKeys = {
6
6
  androidKeyTestResult: (props) => ["androidKeyTestResult", ...Object.values(props)],
@@ -1,5 +1,5 @@
1
1
  import { useQuery } from '@tanstack/react-query';
2
- import { f as App } from './index-CO_ssVFA.js';
2
+ import { f as App } from './index-BwnzoldS.js';
3
3
 
4
4
  const queryAppleApp = async ({ ctx, iosBundleId }) => {
5
5
  if (!iosBundleId) {
@@ -1,5 +1,5 @@
1
1
  import { useQuery } from '@tanstack/react-query';
2
- import { e as BundleId, q as getGodotProjectCapabilities, P as Platform, G as GODOT_CAPABILITIES, r as CapabilityType } from './index-CO_ssVFA.js';
2
+ import { e as BundleId, q as getGodotProjectCapabilities, P as Platform, G as GODOT_CAPABILITIES, r as CapabilityType } from './index-BwnzoldS.js';
3
3
 
4
4
  async function getBundleIdCapabilities(bundleId) {
5
5
  const current = await bundleId.getBundleIdCapabilitiesAsync();
@@ -0,0 +1,10 @@
1
+ import { useQuery } from '@tanstack/react-query';
2
+ import { Q as getGoogleStatus } from './index-BwnzoldS.js';
3
+ import { c as cacheKeys } from './useAndroidServiceAccountTestResult-CwKeW0ED.js';
4
+
5
+ const useGoogleStatus = () => useQuery({
6
+ queryFn: getGoogleStatus,
7
+ queryKey: cacheKeys.googleStatus()
8
+ });
9
+
10
+ export { useGoogleStatus as u };
@@ -1,8 +1,8 @@
1
1
  import { useQuery } from '@tanstack/react-query';
2
2
  import axios from 'axios';
3
- import { b as getShortDate, p as getAuthedHeaders, o as API_URL, I as castArrayObjectDates } from './index-CO_ssVFA.js';
4
- import { c as cacheKeys } from './useAndroidServiceAccountTestResult-DueKynFy.js';
5
- import { g as getShortUUID } from './index-CYjZ26If.js';
3
+ import { b as getShortDate, p as getAuthedHeaders, o as API_URL, I as castArrayObjectDates } from './index-BwnzoldS.js';
4
+ import { c as cacheKeys } from './useAndroidServiceAccountTestResult-CwKeW0ED.js';
5
+ import { g as getShortUUID } from './index-CJWMt1s-.js';
6
6
 
7
7
  async function queryProjectCredentials({
8
8
  projectId,
@@ -22,13 +22,14 @@ async function queryProjectCredentials({
22
22
  }
23
23
  }
24
24
  function getProjectCredentialSummary(credential) {
25
- return {
26
- createdAt: getShortDate(credential.createdAt),
27
- id: getShortUUID(credential.id),
28
- isActive: credential.isActive,
29
- serial: credential.serialNumber.slice(0, 30) + (credential.serialNumber.length > 30 ? "\u2026" : ""),
30
- type: credential.type
31
- };
25
+ const trimLength = 25;
26
+ const summary = {};
27
+ summary.id = getShortUUID(credential.id);
28
+ summary.type = credential.type;
29
+ summary.serial = credential.serialNumber.slice(0, trimLength) + (credential.serialNumber.length > trimLength ? "\u2026" : "");
30
+ summary.isActive = credential.isActive;
31
+ summary.createdAt = getShortDate(credential.createdAt);
32
+ return summary;
32
33
  }
33
34
  const useProjectCredentials = ({
34
35
  platform,
@@ -42,7 +43,9 @@ const useProjectCredentials = ({
42
43
  if (!(platform || type)) return data;
43
44
  return {
44
45
  ...data,
45
- data: data.data.filter((credential) => (!platform || credential.platform === platform) && (!type || credential.type === type))
46
+ data: data.data.filter(
47
+ (credential) => (!platform || credential.platform === platform) && (!type || credential.type === type)
48
+ )
46
49
  };
47
50
  }
48
51
  });
@@ -1,6 +1,6 @@
1
1
  import { useEffect } from 'react';
2
2
  import { io } from 'socket.io-client';
3
- import { N as WS_URL, O as getAuthToken } from './index-CO_ssVFA.js';
3
+ import { N as WS_URL, O as getAuthToken } from './index-BwnzoldS.js';
4
4
 
5
5
  function useWebSocket(listeners = []) {
6
6
  const log = () => {
package/docs/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # ShipThis CLI Reference
2
2
 
3
- **ShipThis** is a **command-line tool** which manages releasing your [Godot](https://godotengine.org/) games to the **iOS App Store**.
3
+ **ShipThis** is a **command-line tool** which manages releasing your [Godot](https://godotengine.org/) games to the **iOS App Store** and **Google Play**.
4
4
 
5
5
  :::tip New to ShipThis?
6
6
  Begin with our [Quickstart Guide](https://shipth.is/docs/guides/quick-start).
@@ -8,8 +8,9 @@ Begin with our [Quickstart Guide](https://shipth.is/docs/guides/quick-start).
8
8
 
9
9
  ## Topics
10
10
 
11
+ - [apiKey](https://shipth.is/docs/reference/apiKey) - Commands that relate to ShipThis API keys
11
12
  - [apple](https://shipth.is/docs/reference/apple) - Commands that relate to linking your ShipThis account with your Apple Developer Account
12
- - [game](https://shipth.is/docs/reference/game) - Commands that relate to configuring the specific game in the current working directory.
13
+ - [game](https://shipth.is/docs/reference/game) - Commands that relate to configuring the specific game in the current working directory
13
14
 
14
15
  ## Commands
15
16
 
package/docs/apiKey.md CHANGED
@@ -1,10 +1,20 @@
1
1
  # Topic: `apiKey`
2
2
 
3
- Commands related to ShipThis API Keys
3
+ ## Description
4
4
 
5
+ Commands in the **apiKey** topic are prefixed with `shipthis apiKey`. These commands let you create new keys, list existing ones, and revoke keys you no longer need.
5
6
 
6
- ## Commands
7
+ :::info
8
+ ShipThis API keys are securely generated, random tokens used to authenticate requests to the ShipThis API. They are intended for automated environments, like CI pipelines, where manual login is not practical.
9
+ The raw key is only shown once when created, so make sure to copy and store it securely.
10
+ :::
11
+
12
+
13
+ ## Example
7
14
 
15
+ [![asciicast](https://asciinema.org/a/bfCPQIvDNt5zlgcEQhcs7o6Fr.svg)](https://asciinema.org/a/bfCPQIvDNt5zlgcEQhcs7o6Fr#shipthis-col80row24)
16
+
17
+ ## Commands
8
18
 
9
19
  ### `apiKey create`
10
20
 
@@ -0,0 +1,28 @@
1
+ # Command: `game android apiKey policy`
2
+
3
+ ## Description
4
+
5
+ Gets and sets the iam.disableServiceAccountKeyCreation policy for your Google Organization
6
+
7
+ ## Help Output
8
+
9
+ ```help
10
+ USAGE
11
+ $ shipthis game android apiKey policy [-g <value>] [-e | -r] [-w]
12
+
13
+ FLAGS
14
+ -e, --enforce Enforces the policy
15
+ -g, --gameId=<value> The ID of the game
16
+ -r, --revoke Revokes the policy
17
+ -w, --waitForAuth Wait for Google Authentication (10 mins).
18
+
19
+ DESCRIPTION
20
+ Gets and sets the iam.disableServiceAccountKeyCreation policy for your Google Organization
21
+
22
+ EXAMPLES
23
+ $ shipthis game android apiKey policy
24
+
25
+ $ shipthis game android apiKey policy --enforce
26
+
27
+ $ shipthis game android apiKey policy --revoke
28
+ ```
@@ -1,10 +1,20 @@
1
1
  # Topic: `game android keyStore`
2
2
 
3
+ ## Description
4
+
3
5
  Commands related to the Android KeyStore for a specific game
4
6
 
7
+ :::info
8
+ An Android **keystore** is a secure file where you keep the private key that signs your app. When you build and publish an APK or AAB, this key proves to Google Play (and your users) that the app really comes from you.
5
9
 
6
- ## Commands
10
+ If you have already published your game on Google Play, you will need to [import](#game-android-keystore-import) the same keystore you used before.
11
+ :::
12
+
13
+ ## Example
7
14
 
15
+ [![asciicast](https://asciinema.org/a/S3R73FuaGMCwu2C0dHSC0BrJB.svg)](https://asciinema.org/a/S3R73FuaGMCwu2C0dHSC0BrJB#shipthis-col80row24)
16
+
17
+ ## Commands
8
18
 
9
19
  ### `game android keyStore create`
10
20
 
@@ -61,14 +71,14 @@ EXAMPLES
61
71
 
62
72
  #### Description
63
73
 
64
- Imports an Android Keystore to your ShipThis account for the specified game.
74
+ Imports an Android Keystore to your ShipThis account for the specified game. You can import as JKS and password parameters or as a ZIP file.
65
75
 
66
76
  #### Help Output
67
77
 
68
78
  ```help
69
79
  USAGE
70
- $ shipthis game android keyStore import [FILE] [-g <value>] [--jksFile <value>] [--keystorePassword <value>] [--keyPassword
71
- <value>] [-f]
80
+ $ shipthis game android keyStore import [FILE] [-g <value>] [-f] [--jksFile <value>] [--keyPassword <value>] [--keystorePassword <value>]
81
+
72
82
 
73
83
  ARGUMENTS
74
84
  FILE Path to the ZIP file to import (must be in the same format as the export)
@@ -4,6 +4,10 @@
4
4
 
5
5
  Shows the status of the setup for the Android platform for a specific game.
6
6
 
7
+ ## Example
8
+
9
+ [![asciicast](https://asciinema.org/a/SiTSwpQgQtPIcd56v8hYA7zo8.svg)](https://asciinema.org/a/SiTSwpQgQtPIcd56v8hYA7zo8#shipthis-col80row24)
10
+
7
11
  ## Help Output
8
12
 
9
13
  ```help