UNPKG

genezio

Version:

Command line utility to interact with Genezio infrastructure.

145 lines (141 loc) 7.25 kB
import { DEFAULT_ARCHITECTURE, DEFAULT_PYTHON_RUNTIME, SSRFrameworkComponentType, } from "../../../models/projectOptions.js"; import { PackageManagerType, PYTHON_DEFAULT_PACKAGE_MANAGER, } from "../../../packageManagers/packageManager.js"; import { PYTHON_DEFAULT_ENTRY_FILE } from "../../analyze/command.js"; import { STREAMLIT_PATTERN } from "../../analyze/constants.js"; import { findEntryFile } from "../../analyze/frameworks.js"; import { addSSRComponentToConfig } from "../../analyze/utils.js"; import { actionDetectedEnvFile, prepareServicesPostBackendDeployment, prepareServicesPreBackendDeployment, readOrAskConfig, createBackendEnvVarList, uploadUserCode, } from "../utils.js"; import path from "path"; import fs from "fs"; import git from "isomorphic-git"; import { debugLogger, log } from "../../../utils/logging.js"; import colors from "colors"; import { Language } from "../../../projectConfiguration/yaml/models.js"; import { DASHBOARD_URL } from "../../../constants.js"; import { getCloudProvider } from "../../../requests/getCloudProvider.js"; import { functionToCloudInput, getCloudAdapter } from "../genezio.js"; import { FunctionType } from "../../../projectConfiguration/yaml/models.js"; import { ProjectConfiguration } from "../../../models/projectConfiguration.js"; import { createTemporaryFolder } from "../../../utils/file.js"; import { warningMissingEnvironmentVariables } from "../../../utils/environmentVariables.js"; import { isCI } from "../../../utils/process.js"; export async function streamlitDeploy(options) { const genezioConfig = await readOrAskConfig(options.config); const packageManagerType = genezioConfig.streamlit?.packageManager || PYTHON_DEFAULT_PACKAGE_MANAGER; const cwd = process.cwd(); const componentPath = genezioConfig.streamlit?.path ? path.resolve(cwd, genezioConfig.streamlit.path) : cwd; // Give the user another chance if he forgot to add `--env` flag if (!isCI() && !options.env) { options.env = await actionDetectedEnvFile(componentPath, genezioConfig.name, options.stage); } // Prepare services before deploying (database, authentication, etc) await prepareServicesPreBackendDeployment(genezioConfig, genezioConfig.name, options.stage, options.env); const entryFile = await findEntryFile(componentPath, getFilesContents(componentPath), STREAMLIT_PATTERN, PYTHON_DEFAULT_ENTRY_FILE); // Add streamlit component to config await addSSRComponentToConfig(options.config, { path: componentPath, packageManager: packageManagerType, runtime: DEFAULT_PYTHON_RUNTIME, entryFile: entryFile, }, SSRFrameworkComponentType.streamlit); // Copy to tmp folder const tempCwd = await createTemporaryFolder(); debugLogger.debug(`Copying project files to ${tempCwd}`); await fs.promises.cp(componentPath, tempCwd, { recursive: true, force: true, dereference: true, }); const randomId = Math.random().toString(36).substring(2, 6); const startFileName = `start-${randomId}.py`; const startFile = path.join(tempCwd, startFileName); if (!fs.existsSync(startFile)) { fs.writeFileSync(startFile, getStartFileContent(entryFile)); } const updatedGenezioConfig = await readOrAskConfig(options.config); const environmentVariables = await createBackendEnvVarList(options.env, options.stage, updatedGenezioConfig, SSRFrameworkComponentType.streamlit); // Deploy the component const result = await deployFunction(updatedGenezioConfig, options, tempCwd, startFileName, environmentVariables); await uploadUserCode(updatedGenezioConfig.name, updatedGenezioConfig.region, options.stage, componentPath); const functionUrl = result.functions.find((f) => f.name === "function-streamlit")?.cloudUrl; await warningMissingEnvironmentVariables(genezioConfig.streamlit?.path || "./", result.projectId, result.projectEnvId); await prepareServicesPostBackendDeployment(updatedGenezioConfig, updatedGenezioConfig.name, options.stage); if (functionUrl) { log.info(`The app is being deployed at ${colors.cyan(functionUrl)}. It might take a few moments to be available worldwide.`); log.info(`\nApp Dashboard URL: ${colors.cyan(`${DASHBOARD_URL}/project/${result.projectId}/${result.projectEnvId}`)}\n` + `${colors.dim("Here you can monitor logs, set up a custom domain, and more.")}\n`); } else { log.warn("No deployment URL was returned."); } } async function deployFunction(config, options, cwd, startFileName, environmentVariables) { const cloudProvider = await getCloudProvider(config.name); const cloudAdapter = getCloudAdapter(cloudProvider); const serverFunction = { path: ".", name: "streamlit", entry: startFileName, type: config.streamlit?.type === FunctionType.persistent ? FunctionType.persistent : FunctionType.httpServer, timeout: config.streamlit?.timeout, storageSize: config.streamlit?.storageSize, instanceSize: config.streamlit?.instanceSize, vcpuCount: config.streamlit?.vcpuCount, memoryMb: config.streamlit?.memoryMb, maxConcurrentRequestsPerInstance: config.streamlit?.maxConcurrentRequestsPerInstance, maxConcurrentInstances: config.streamlit?.maxConcurrentInstances, cooldownTime: config.streamlit?.cooldownTime, }; const runtime = config.streamlit?.runtime || DEFAULT_PYTHON_RUNTIME; const deployConfig = { ...config, backend: { path: ".", language: { name: Language.python, runtime: runtime, architecture: DEFAULT_ARCHITECTURE, packageManager: PackageManagerType.pip, }, functions: [serverFunction], }, }; const projectConfiguration = new ProjectConfiguration(deployConfig, await getCloudProvider(deployConfig.name), { generatorResponses: [], classesInfo: [], }); const cloudInputs = await Promise.all(projectConfiguration.functions.map((f) => functionToCloudInput(f, cwd, undefined, runtime))); const projectGitRepositoryUrl = (await git.listRemotes({ fs, dir: process.cwd() })).find((r) => r.remote === "origin")?.url; const result = await cloudAdapter.deploy(cloudInputs, projectConfiguration, { stage: options.stage }, ["streamlit"], projectGitRepositoryUrl, environmentVariables); return result; } function getStartFileContent(entryFile) { const startFileContent = ` import streamlit.web.bootstrap as bootstrap import asyncio flags = { 'server_port': 8080, 'global_developmentMode': False } loop = asyncio.new_event_loop() asyncio.set_event_loop(loop) bootstrap.load_config_options(flag_options=flags) bootstrap.run('${entryFile}', False, [], flags) `; return startFileContent; } function getFilesContents(dir) { const contents = {}; const files = fs.readdirSync(dir); for (const file of files) { if (file.endsWith(".py")) { const filePath = path.join(dir, file); contents[file] = fs.readFileSync(filePath, "utf8"); } } return contents; }