From da435ca6aa4c3aee10c531694b7f293ae2ebb689 Mon Sep 17 00:00:00 2001 From: Cristi Miloiu Date: Wed, 29 Jan 2025 13:38:19 +0200 Subject: [PATCH] feat: Add the possibility to choose the python version --- src/commands/analyze/command.ts | 6 ++++- src/commands/deploy/genezio.ts | 45 +++++++++++++++++++++++++++++--- src/models/projectOptions.ts | 15 ++++++++--- src/utils/detectPythonCommand.ts | 14 ++++++++++ 4 files changed, 71 insertions(+), 9 deletions(-) diff --git a/src/commands/analyze/command.ts b/src/commands/analyze/command.ts index 8c5ff7c32..da9217c0e 100644 --- a/src/commands/analyze/command.ts +++ b/src/commands/analyze/command.ts @@ -32,7 +32,7 @@ import { packageManagers, PYTHON_DEFAULT_PACKAGE_MANAGER, } from "../../packageManagers/packageManager.js"; -import { SSRFrameworkComponentType } from "../../models/projectOptions.js"; +import { DEFAULT_PYTHON_RUNTIME, SSRFrameworkComponentType } from "../../models/projectOptions.js"; import { RawYamlProjectConfiguration, YAMLLanguage } from "../../projectConfiguration/yaml/v2.js"; import { addBackendComponentToConfig, @@ -415,6 +415,7 @@ export async function analyzeCommand(options: GenezioAnalyzeOptions) { language: { name: Language.python, packageManager: packageManagerType, + runtime: DEFAULT_PYTHON_RUNTIME, } as YAMLLanguage, environment: mapEnvironmentVariableToConfig( resultEnvironmentAnalysis.get(componentPath)?.environmentVariables, @@ -453,6 +454,7 @@ export async function analyzeCommand(options: GenezioAnalyzeOptions) { language: { name: Language.python, packageManager: packageManagerType, + runtime: DEFAULT_PYTHON_RUNTIME, } as YAMLLanguage, environment: mapEnvironmentVariableToConfig( resultEnvironmentAnalysis.get(componentPath)?.environmentVariables, @@ -494,6 +496,7 @@ export async function analyzeCommand(options: GenezioAnalyzeOptions) { language: { name: Language.python, packageManager: packageManagerType, + runtime: DEFAULT_PYTHON_RUNTIME, } as YAMLLanguage, environment: mapEnvironmentVariableToConfig( resultEnvironmentAnalysis.get(componentPath)?.environmentVariables, @@ -532,6 +535,7 @@ export async function analyzeCommand(options: GenezioAnalyzeOptions) { language: { name: Language.python, packageManager: packageManagerType, + runtime: DEFAULT_PYTHON_RUNTIME, } as YAMLLanguage, environment: mapEnvironmentVariableToConfig( resultEnvironmentAnalysis.get(componentPath)?.environmentVariables, diff --git a/src/commands/deploy/genezio.ts b/src/commands/deploy/genezio.ts index 99f858fed..8ff6ca0c8 100644 --- a/src/commands/deploy/genezio.ts +++ b/src/commands/deploy/genezio.ts @@ -78,7 +78,8 @@ import { packageManagers, PYTHON_DEFAULT_PACKAGE_MANAGER, } from "../../packageManagers/packageManager.js"; -import { supportedPythonDepsInstallVersion } from "../../models/projectOptions.js"; +import { DEFAULT_PYTHON_VERSION_INSTALL } from "../../models/projectOptions.js"; +import { detectPythonVersion } from "../../utils/detectPythonCommand.js"; export async function genezioDeploy(options: GenezioDeployOptions) { const configIOController = new YamlConfigurationIOController(options.config, { @@ -495,7 +496,13 @@ export async function deployClasses( ); const functionsResultArray: Promise[] = projectConfiguration.functions.map( - (f) => functionToCloudInput(f, backend.path, /* outputDir */ undefined), + (f) => + functionToCloudInput( + f, + backend.path, + /* outputDir */ undefined, + backend.language.runtime, + ), ); const cloudAdapterDeployInput = await Promise.all([ @@ -578,6 +585,7 @@ export async function functionToCloudInput( functionElement: FunctionConfiguration, backendPath: string, outputDir?: string, + runtime?: string, ): Promise { const supportedFunctionLanguages = ["js", "ts", "python"]; @@ -636,6 +644,35 @@ export async function functionToCloudInput( const pathForDependencies = path.join(tmpFolderPath, "packages"); const packageManager = packageManagers[PYTHON_DEFAULT_PACKAGE_MANAGER]; let installCommand; + const versionMatch = runtime!.match(/\d+\.\d+/); + const pythonVersion = versionMatch ? versionMatch[0] : DEFAULT_PYTHON_VERSION_INSTALL; + + // check pythonVersion matches with local python version + const localPythonVersion = + (await detectPythonVersion())?.match(/\d+\.\d+/)?.[0] || + DEFAULT_PYTHON_VERSION_INSTALL; + + if (pythonVersion !== localPythonVersion) { + const warningMessage = [ + `Python Version Mismatch`, + ``, + `We noticed you're using different Python versions locally and in production:`, + `Local Environment: Python ${localPythonVersion}`, + `Production Runtime: Python ${pythonVersion}`, + ``, + `This might lead to unexpected behavior. To ensure consistency, update your genezio.yaml configuration to match your local version:`, + ``, + `language:`, + ` name: python`, + ` packageManager: pip`, + ` runtime: python${localPythonVersion}.x`, + ``, + `This will help prevent potential compatibility issues!`, + `For complete yaml configuration, visit: https://genezio.com/docs/project-structure/genezio-configuration-file/`, + ].join("\n"); + + log.warn(colors.yellow(warningMessage)); + } if (packageManager.command === "pip" || packageManager.command === "pip3") { if (fs.existsSync(requirementsPath)) { @@ -644,10 +681,10 @@ export async function functionToCloudInput( .readFileSync(requirementsOutputPath, "utf8") .trim(); if (requirementsContent) { - installCommand = `${packageManager.command} install -r ${requirementsOutputPath} --platform manylinux2014_x86_64 --only-binary=:all: --python-version ${supportedPythonDepsInstallVersion} -t ${pathForDependencies} --no-user`; + installCommand = `${packageManager.command} install -r ${requirementsOutputPath} --platform manylinux2014_x86_64 --only-binary=:all: --python-version ${pythonVersion} -t ${pathForDependencies} --no-user`; } } else if (fs.existsSync(pyProjectTomlPath)) { - installCommand = `${packageManager.command} install . --platform manylinux2014_x86_64 --only-binary=:all: --python-version ${supportedPythonDepsInstallVersion} -t ${pathForDependencies}`; + installCommand = `${packageManager.command} install . --platform manylinux2014_x86_64 --only-binary=:all: --python-version ${pythonVersion} -t ${pathForDependencies}`; } } else if (packageManager.command === "poetry") { installCommand = `${packageManager.command} install --no-root --directory ${pathForDependencies}`; diff --git a/src/models/projectOptions.ts b/src/models/projectOptions.ts index 3347cb3d2..c239307ee 100644 --- a/src/models/projectOptions.ts +++ b/src/models/projectOptions.ts @@ -1,9 +1,10 @@ export type NodeRuntime = "nodejs20.x"; -export type PythonRuntime = "python3.9.x"; +export type PythonRuntime = "python3.11.x"; export type Architecture = "arm64" | "x86_64"; export const DEFAULT_NODE_RUNTIME: NodeRuntime = "nodejs20.x"; export const DEFAULT_ARCHITECTURE: Architecture = "arm64"; -export const DEFAULT_PYTHON_RUNTIME: PythonRuntime = "python3.9.x"; +export const DEFAULT_PYTHON_RUNTIME: PythonRuntime = "python3.11.x"; +export const DEFAULT_PYTHON_VERSION_INSTALL: string = "3.11"; export const CONTAINER_IMAGE_NODE20 = "node:20.11.1-alpine3.19"; @@ -46,7 +47,13 @@ export enum ContainerComponentType { } export const supportedNodeRuntimes = ["nodejs20.x"] as const; -export const supportedPythonRuntimes = ["python3.9.x"] as const; +export const supportedPythonRuntimes = [ + "python3.9.x", + "python3.10.x", + "python3.11.x", + "python3.12.x", + "python3.13.x", +] as const; export const supportedArchitectures = ["arm64", "x86_64"] as const; export const supportedSSRFrameworks = ["nextjs", "nitro", "nuxt"] as const; -export const supportedPythonDepsInstallVersion = "3.11" as const; +export const supportedPythonDepsInstallVersion = ["3.9", "3.10", "3.11", "3.12", "3.13"] as const; diff --git a/src/utils/detectPythonCommand.ts b/src/utils/detectPythonCommand.ts index f916da004..7a36b05d3 100644 --- a/src/utils/detectPythonCommand.ts +++ b/src/utils/detectPythonCommand.ts @@ -27,3 +27,17 @@ export async function detectPipCommand() { } } } + +export async function detectPythonVersion(): Promise { + try { + const { stdout } = await $`python3 --version`; + return stdout.replace("Python ", "").trim(); + } catch { + try { + const { stdout } = await $`python --version`; + return stdout.replace("Python ", "").trim(); + } catch { + return; + } + } +}