shipthis
Version:
ShipThis manages building and uploading your Godot games to the App Store and Google Play.
179 lines (173 loc) • 7.47 kB
JavaScript
import { jsx, Fragment, jsxs } from 'react/jsx-runtime';
import { Box, Text } from 'ink';
import Spinner from 'ink-spinner';
import { useState, useRef, useEffect, useContext } from 'react';
import 'node:crypto';
import 'node:fs';
import 'node:path';
import 'node:readline';
import 'node:url';
import 'readline-sync';
import { p as getAuthedHeaders, o as API_URL, a4 as castObjectDates, H as queryClient, Q as revokePolicy, O as enforcePolicy, P as Platform, C as CredentialsType, M as getGoogleStatus, c as getShortDate } from './baseCommand-CTn3KGH3.js';
import axios from 'axios';
import 'isomorphic-git';
import '@oclif/core';
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import 'luxon';
import { k as cacheKeys, G as GameContext, w as useSafeInput, M as Markdown } from './baseGameCommand-8VL7xe-O.js';
import 'fast-glob';
import 'uuid';
import 'yazl';
import 'socket.io-client';
import 'fullscreen-ink';
import { u as useWebSocket } from './useWebSocket-MXDbQHcu.js';
import { u as useProjectCredentials } from './useProjectCredentials-TvlolkId.js';
import { u as useGoogleStatus } from './useGoogleStatus-WqPgHteE.js';
import 'crypto-js';
import 'string-length';
import 'strip-ansi';
import 'open';
import '@inkjs/ui';
import { P as ProgressSpinner } from './ProgressSpinner-Um6ARKlk.js';
import 'qrcode';
async function fetchStatus({ projectId }) {
try {
if (!projectId) throw new Error("projectId is required");
const headers = getAuthedHeaders();
const url = `${API_URL}/projects/${projectId}/credentials/android/key/status/`;
const response = await axios.get(url, { headers });
return castObjectDates(response.data);
} catch (error) {
console.warn("fetchStatus Error", error);
throw error;
}
}
const useAndroidServiceAccountSetupStatus = (props) => useQuery({
queryFn: () => fetchStatus(props),
queryKey: cacheKeys.androidSetupStatus(props),
// Status changes frequently, so we want to keep it fresh
refetchInterval: 1e3 * 5,
staleTime: 1e3 * 5
});
const useUpdateGoogleOrgPolicy = () => useMutation({
async mutationFn(props) {
if (props.action === "revoke") {
return await revokePolicy();
}
return await enforcePolicy();
},
async onSuccess(data) {
queryClient.invalidateQueries({
queryKey: cacheKeys.googleStatus()
});
}
});
const ERR_NOT_AUTHENTICATED = "You must be connected to Google to create a Service Account Key";
const useHasServiceAccountKey = (projectId) => {
const { data, isSuccess } = useProjectCredentials({ platform: Platform.ANDROID, projectId });
return isSuccess && data.data.some((cred) => cred.isActive && cred.platform === Platform.ANDROID && cred.type === CredentialsType.KEY);
};
const useAndroidServiceAccount = ({ onComplete, onError, projectId }) => {
const queryClient = useQueryClient();
const [isStarting, setIsStarting] = useState(false);
const hasServiceAccountKey = useHasServiceAccountKey(projectId);
const listener = {
async eventHandler(pattern, data) {
const key = cacheKeys.androidSetupStatus({ projectId });
queryClient.setQueryData(key, () => data);
},
getPattern: () => `project.${projectId}:android-setup-status`
};
useWebSocket([listener]);
const { data: setupStatus } = useAndroidServiceAccountSetupStatus({ projectId });
const prevSetupStatusRef = useRef("unknown");
useEffect(() => {
if (["queued", "running"].includes(prevSetupStatusRef.current)) {
if (setupStatus?.status === "complete") onComplete();
if (setupStatus?.status === "error") onError(new Error(setupStatus.errorMessage));
}
prevSetupStatusRef.current = setupStatus?.status || "unknown";
}, [setupStatus]);
const handleStart = async () => {
try {
setIsStarting(true);
const currentStatus = await getGoogleStatus();
if (!currentStatus.isAuthenticated) throw new Error(ERR_NOT_AUTHENTICATED);
const headers = getAuthedHeaders();
const androidKeyApiBase = `${API_URL}/projects/${projectId}/credentials/android/key`;
const startUrl = `${androidKeyApiBase}/setup/`;
const { data: updatedStatus } = await axios.post(startUrl, {}, { headers });
queryClient.invalidateQueries({
queryKey: cacheKeys.projectCredentials({ pageNumber: 0, projectId })
});
await queryClient.setQueryData(cacheKeys.androidSetupStatus({ projectId }), (_) => updatedStatus);
setIsStarting(false);
return true;
} catch (error) {
setIsStarting(false);
console.warn("useAndroidServiceAccount.handleStart Error", error);
onError(error);
return false;
}
};
const isCreating = isStarting || setupStatus?.status === "queued" || setupStatus?.status === "running";
return {
handleStart,
hasServiceAccountKey,
isCreating,
setupStatus
};
};
const CreateServiceAccountKey = (props) => {
const { gameId } = useContext(GameContext);
return /* @__PURE__ */ jsx(Fragment, { children: gameId && /* @__PURE__ */ jsx(CreateForGame, { gameId, ...props }) });
};
const CreateForGame = ({ gameId, onComplete, onError, ...boxProps }) => {
const [didStart, setDidStart] = useState(false);
const startedRef = useRef(false);
const { data: googleStatus } = useGoogleStatus();
const updatePolicyMutation = useUpdateGoogleOrgPolicy();
const { handleStart, isCreating, setupStatus } = useAndroidServiceAccount({
onComplete,
onError,
projectId: gameId
});
useEffect(() => {
if (startedRef.current) return;
if (!googleStatus) return;
if (!googleStatus.needsPolicyChange) {
startedRef.current = true;
handleStart().then(() => setDidStart(true)).catch((error) => onError(error));
}
}, [googleStatus]);
useSafeInput((input) => {
if (input === "p" && googleStatus?.needsPolicyChange && !updatePolicyMutation.isPending) {
updatePolicyMutation.mutate({ action: "revoke" });
}
});
const needsPolicy = Boolean(googleStatus?.needsPolicyChange);
const policyChanging = updatePolicyMutation.isPending || updatePolicyMutation.isSuccess && needsPolicy;
const Header = () => /* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 1, children: [
/* @__PURE__ */ jsx(Text, { children: "Creating a Service Account and API Key..." }),
isCreating && /* @__PURE__ */ jsx(Spinner, { type: "dots" })
] });
return /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsxs(Box, { flexDirection: "column", gap: 1, ...boxProps, children: [
needsPolicy ? policyChanging ? /* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 1, children: [
/* @__PURE__ */ jsx(Text, { children: "Updating organization policy..." }),
/* @__PURE__ */ jsx(Spinner, { type: "dots" })
] }) : googleStatus && /* @__PURE__ */ jsx(
Markdown,
{
filename: "service-account-policy-wizard.md.ejs",
templateVars: {
needsPolicyChange: Boolean(googleStatus.needsPolicyChange),
orgCreatedAt: googleStatus.orgCreatedAt ? getShortDate(googleStatus.orgCreatedAt) : "Unknown",
orgName: `${googleStatus.orgName}`,
orgResourceName: `${googleStatus.orgResourceName}`
}
}
) : /* @__PURE__ */ jsx(Header, {}),
didStart && /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsx(ProgressSpinner, { progress: (setupStatus?.progress || 0) * 100, spinnerType: "dots" }) })
] }) });
};
export { CreateServiceAccountKey as C };