UNPKG

alwaysai

Version:

The alwaysAI command-line interface (CLI)

155 lines (138 loc) 4.46 kB
import { CliUsageError, CliTerseError } from '@alwaysai/alwayscli'; import * as crypto from 'crypto'; import { ALWAYSAI_CLI_EXECUTABLE_NAME } from '../../constants'; import { checkUserIsLoggedInComponent } from '../user'; import { runWithSpinner, JsSpawner, tarFiles, streamToBuffer } from '../../util'; import { appCheckComponent } from './app-check-component'; import { appConfigureComponent } from './app-configure-component'; import { getCurrentProjectId, ProjectJsonFile } from '../../core/project'; import { insertAppRecord, getPresignedUrlAppUpload, usePresignedUrlAppUpload } from '../../infrastructure'; import { APP_IGNORE_FILES } from './app-install-component'; import { checkForInvalidModelsComponent } from './models/app-invalid-models-check-component'; type ApplicationData = { filename: string; tarBuffer: Buffer; releaseManifest: string; projectId: string; releaseHash: string; }; export async function appPublishComponent(props: { yes: boolean; name?: string; excludes?: string[]; }) { const { yes, name, excludes } = props; await checkUserIsLoggedInComponent({ yes }); try { await appCheckComponent({ ignoreTargetJsonFile: true }); } catch (err) { if (yes) { throw new CliUsageError( `App is not properly configured. Did you run \`${ALWAYSAI_CLI_EXECUTABLE_NAME} app configure\`?` ); } else { await appConfigureComponent({ yes }); } } const invalidModels = await checkForInvalidModelsComponent(); if (Object.keys(invalidModels).length > 0) { throw new CliTerseError( `You do not have permission to use the following models, or the model version does not exist:\n\n` + `${Object.entries(invalidModels) .map(([model, version]) => `- ${model}: version ${version}`) .join('\n')}\n\n` + `Please remove these models before publishing the application again.` ); } const applicationPackage = await runWithSpinner( createApplicationFiles, [{ excludes: excludes || [] }], 'Create application package' ); await runWithSpinner( publishApplicationPackage, [applicationPackage, name], 'Publish application package' ); return applicationPackage.releaseHash; } async function createApplicationFiles(params: { excludes: string[] }) { const { excludes } = params; // Get project ID const projectJsonFile = ProjectJsonFile().read(); const projectId = projectJsonFile.project ? projectJsonFile.project.id : undefined; if (projectId === undefined) { throw new CliTerseError('Please set up a project for this app'); } // Put src in tarball const spawner = JsSpawner(); const ignore = APP_IGNORE_FILES.concat(['alwaysai.target.json'], excludes); const tarfile = await tarFiles(spawner, ignore); const tarBuffer = await streamToBuffer(tarfile); // Generate release hash const hashSum = crypto.createHash('sha256'); hashSum.update(tarBuffer); const releaseHash = hashSum.digest('hex'); const releaseDate = new Date(); const filename = `${projectId}/${releaseHash}.tgz`; // Generate release manifest const releaseData = { releaseHash, releaseDate, filename }; const releaseManifest = JSON.stringify(releaseData, null, 2); const applicationPackage: ApplicationData = { filename, tarBuffer, releaseManifest, projectId, releaseHash }; return applicationPackage; } async function publishApplicationPackage( applicationPackage: ApplicationData, name?: string ) { const fileData = { tarFileName: applicationPackage.filename, tarFile: applicationPackage.tarBuffer, releaseManifestName: `${applicationPackage.projectId}/release.json`, releaseManifestFile: applicationPackage.releaseManifest }; const presignedUrlTarFile = await getPresignedUrlAppUpload( fileData.tarFileName, 'application/octet-stream' ); const presignedUrlManifestFile = await getPresignedUrlAppUpload( fileData.releaseManifestName, 'application/octet-stream' ); await usePresignedUrlAppUpload( presignedUrlTarFile.uploadURL, fileData.tarFile ); await usePresignedUrlAppUpload( presignedUrlManifestFile.uploadURL, fileData.releaseManifestFile ); const record = { hash: applicationPackage.releaseHash, s3Path: applicationPackage.filename, projectId: await getCurrentProjectId(), name: name ?? '' }; await insertAppRecord(record); }