shipthis
Version:
ShipThis manages building and uploading your Godot games to the App Store and Google Play.
123 lines (119 loc) • 4.67 kB
JavaScript
import fs__default from 'node:fs';
import { useMutation } from '@tanstack/react-query';
import axios from 'axios';
import fg from 'fast-glob';
import { v4 } from 'uuid';
import { ZipFile } from 'yazl';
import { K as queryClient, v as DEFAULT_SHIPPED_FILES_GLOBS, w as DEFAULT_IGNORED_FILES_GLOBS, a4 as getNewUploadTicket, a5 as startJobsFromUpload } from './index-BwnzoldS.js';
import { c as cacheKeys } from './useAndroidServiceAccountTestResult-CwKeW0ED.js';
import { f as getFileHash, h as getPlatformName } from './index-CJWMt1s-.js';
import { g as getCWDGitInfo } from './git-BpsfNFZ_.js';
import { jsx, Fragment } from 'react/jsx-runtime';
import 'ink';
import 'ink-spinner';
import 'react';
import 'crypto-js';
import 'luxon';
import 'socket.io-client';
import { u as useJobWatching } from './JobLogTail-D35FO5v-.js';
import 'fullscreen-ink';
import 'string-length';
import 'strip-ansi';
import 'open';
import '@inkjs/ui';
import 'node:path';
import './ejs-DirFZbza.js';
import 'marked';
import 'marked-terminal';
import { P as ProgressSpinner } from './ProgressSpinner-Um6ARKlk.js';
import 'qrcode';
import './index-hoHfGrjg.js';
async function ship({ command, log = () => {
}, shipFlags }) {
log("Fetching game config...");
const projectConfig = await command.getProjectConfig();
if (!projectConfig.project) throw new Error("No project found in project config");
const hasConfiguredIos = Boolean(projectConfig.project.details?.iosBundleId);
const hasConfiguredAndroid = Boolean(projectConfig.project.details?.androidPackageName);
if (!hasConfiguredAndroid && !hasConfiguredIos) {
throw new Error(
"No Android or iOS configuration found. Please run `shipthis game wizard android` or `shipthis game wizard ios` to configure your game."
);
}
log("Retrieving file globs...");
const shippedFilesGlobs = projectConfig.shippedFilesGlobs || DEFAULT_SHIPPED_FILES_GLOBS;
const ignoredFilesGlobs = projectConfig.ignoredFilesGlobs || DEFAULT_IGNORED_FILES_GLOBS;
log("Finding files to include in zip...");
const files = await fg(shippedFilesGlobs, { dot: true, ignore: ignoredFilesGlobs });
log(`Found ${files.length} files, adding to zip...`);
const zipFile = new ZipFile();
for (const file of files) {
zipFile.addFile(file, file);
}
const outputZipToFile = (zip, fileName) => new Promise((resolve) => {
const outputStream = fs__default.createWriteStream(fileName);
zip.outputStream.pipe(outputStream).on("close", () => resolve());
zip.end();
});
const tmpZipFile = `${process.cwd()}/shipthis-${v4()}.zip`;
log(`Creating zip file: ${tmpZipFile}`);
await outputZipToFile(zipFile, tmpZipFile);
log("Reading zip file buffer...");
const zipBuffer = fs__default.readFileSync(tmpZipFile);
const { size } = fs__default.statSync(tmpZipFile);
log("Requesting upload ticket...");
const uploadTicket = await getNewUploadTicket(projectConfig.project.id);
log("Uploading zip file...");
await axios.put(uploadTicket.url, zipBuffer, {
headers: {
"Content-Type": "application/zip",
"Content-length": size
}
});
log("Fetching Git info...");
const gitInfo = await getCWDGitInfo();
log("Computing file hash...");
const zipFileMd5 = await getFileHash(tmpZipFile);
const uploadDetails = {
...gitInfo,
zipFileMd5
};
log("Starting jobs from upload...");
const finalFlags = shipFlags || command.getFlags();
const startJobsOptions = {
...uploadDetails,
platform: finalFlags.platform?.toUpperCase(),
skipPublish: finalFlags.skipPublish
};
const jobs = await startJobsFromUpload(uploadTicket.id, startJobsOptions);
log("Cleaning up temporary zip file...");
fs__default.unlinkSync(tmpZipFile);
log("Job submission complete.");
if (jobs.length === 0) {
throw new Error("No jobs were created. Please check your game configuration and try again.");
}
if (finalFlags?.follow) {
log("Waiting for job to start...");
}
return jobs;
}
const useShip = () => useMutation({
mutationFn: ship,
async onSuccess(data) {
queryClient.invalidateQueries({
queryKey: cacheKeys.jobs({ pageNumber: 0, projectId: data[0].project.id })
});
}
});
const JobProgress = (props) => {
const { progress } = useJobWatching({
isWatching: true,
jobId: props.job.id,
onComplete: props.onComplete,
onFailure: props.onFailure,
projectId: props.job.project.id
});
const label = `${getPlatformName(props.job.type)} build progress...`;
return /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsx(ProgressSpinner, { label, progress, spinnerType: "dots" }) });
};
export { JobProgress as J, useShip as u };