1
1
mirror of https://github.com/leon-ai/leon.git synced 2024-12-22 08:11:34 +03:00
leon/scripts/setup/setup-python-dev-env.js

206 lines
6.0 KiB
JavaScript

import fs from 'node:fs'
import path from 'node:path'
import { command } from 'execa'
import { PYTHON_BRIDGE_SRC_PATH, TCP_SERVER_SRC_PATH } from '@/constants'
import { LogHelper } from '@/helpers/log-helper'
import { LoaderHelper } from '@/helpers/loader-helper'
import { OSHelper, OSTypes } from '@/helpers/os-helper'
/**
* Set up development environment according to the given setup target
* 1. Verify Python environment
* 2. Verify if the targeted development environment is up-to-date
* 3. If up-to-date, exit
* 4. If not up-to-date, delete the outdated development environment and install the new one
* 5. Install spaCy models if the targeted development environment is the TCP server
*/
const SETUP_TARGETS = new Map()
// Find new spaCy models: https://github.com/explosion/spacy-models/releases
const SPACY_MODELS = ['en_core_web_trf-3.4.0', 'fr_core_news_md-3.4.0']
SETUP_TARGETS.set('python-bridge', {
name: 'Python bridge',
pipfilePath: path.join(PYTHON_BRIDGE_SRC_PATH, 'Pipfile'),
dotVenvPath: path.join(PYTHON_BRIDGE_SRC_PATH, '.venv'),
dotProjectPath: path.join(PYTHON_BRIDGE_SRC_PATH, '.venv', '.project')
})
SETUP_TARGETS.set('tcp-server', {
name: 'TCP server',
pipfilePath: path.join(TCP_SERVER_SRC_PATH, 'Pipfile'),
dotVenvPath: path.join(TCP_SERVER_SRC_PATH, '.venv'),
dotProjectPath: path.join(TCP_SERVER_SRC_PATH, '.venv', '.project')
})
;(async () => {
LoaderHelper.start()
const { argv } = process
const givenSetupTarget = argv[2].toLowerCase()
if (!SETUP_TARGETS.has(givenSetupTarget)) {
LogHelper.error(
`Invalid setup target: ${givenSetupTarget}. Valid targets are: ${Array.from(
SETUP_TARGETS.keys()
).join(', ')}`
)
process.exit(1)
}
const {
name: setupTarget,
pipfilePath,
dotVenvPath,
dotProjectPath
} = SETUP_TARGETS.get(givenSetupTarget)
LogHelper.info('Checking Python environment...')
/**
* Verify Python environment
*/
// Check if the Pipfile exists
if (fs.existsSync(pipfilePath)) {
LogHelper.success(`${pipfilePath} found`)
try {
// Check if Pipenv is installed
const pipenvVersionChild = await command('pipenv --version', {
shell: true
})
let pipenvVersion = String(pipenvVersionChild.stdout)
if (pipenvVersion.includes('version')) {
pipenvVersion = pipenvVersion.split('version')[1].trim()
pipenvVersion = `${pipenvVersion} version`
}
LogHelper.success(`Pipenv ${pipenvVersion} found`)
} catch (e) {
LogHelper.error(
`${e}\nPlease install Pipenv: "pip install pipenv" or read the documentation https://docs.pipenv.org`
)
process.exit(1)
}
}
/**
* Install Python packages
*/
LogHelper.info(`Setting up ${setupTarget} development environment...`)
const pipfileMtime = fs.statSync(pipfilePath).mtime
const hasDotVenv = fs.existsSync(dotVenvPath)
const installPythonPackages = async () => {
LogHelper.info(`Installing Python packages from ${pipfilePath}.lock...`)
const { type: osType } = OSHelper.getInformation()
// Delete .venv directory to reset the development environment
if (hasDotVenv) {
LogHelper.info(`Deleting ${dotVenvPath}...`)
fs.rmSync(dotVenvPath, { recursive: true, force: true })
LogHelper.success(`${dotVenvPath} deleted`)
}
try {
await command(`pipenv install --site-packages`, {
shell: true,
stdio: 'inherit'
})
LogHelper.success('Python packages installed')
} catch (e) {
LogHelper.error(`Failed to install Python packages: ${e}`)
if (osType === OSTypes.Windows) {
LogHelper.error(
'Please check the error above. It might be related to Microsoft C++ Build Tools. If it is, you can check here: "https://stackoverflow.com/a/64262038/1768162" then restart your machine and retry'
)
}
process.exit(1)
}
}
/**
* Verify if a fresh development environment installation is necessary
*/
// Required environment variables to set up
process.env.PIPENV_PIPFILE = pipfilePath
process.env.PIPENV_VENV_IN_PROJECT = true
if (givenSetupTarget === 'python-bridge') {
// As per: https://github.com/marcelotduarte/cx_Freeze/issues/1548
process.env.PIP_NO_BINARY = 'cx_Freeze'
}
try {
if (!hasDotVenv) {
await installPythonPackages()
} else {
if (fs.existsSync(dotProjectPath)) {
const dotProjectMtime = fs.statSync(dotProjectPath).mtime
// Check if Python deps tree has been modified since the initial setup
if (pipfileMtime > dotProjectMtime) {
LogHelper.info('The development environment is not up-to-date')
await installPythonPackages()
} else {
LogHelper.success('Python packages are up-to-date')
}
} else {
await installPythonPackages()
}
}
} catch (e) {
LogHelper.error(
`Failed to set up the ${setupTarget} development environment: ${e}`
)
} finally {
LoaderHelper.stop()
}
if (givenSetupTarget === 'tcp-server') {
const installSpacyModels = async () => {
try {
LogHelper.info('Installing spaCy models...')
// Install models one by one to avoid network throttling
for (const model of SPACY_MODELS) {
await command(`pipenv run spacy download ${model} --direct`, {
shell: true,
stdio: 'inherit'
})
}
LogHelper.success('spaCy models installed')
} catch (e) {
LogHelper.error(`Failed to install spaCy models: ${e}`)
process.exit(1)
}
}
LogHelper.info('Checking whether all spaCy models are installed...')
try {
for (let model of SPACY_MODELS) {
;[model] = model.split('-')
await command(`pipenv run python -c "import ${model}"`, { shell: true })
}
LogHelper.success('All spaCy models are already installed')
} catch (e) {
LogHelper.info('Not all spaCy models are installed')
await installSpacyModels()
}
}
LogHelper.success(`${setupTarget} development environment ready`)
})()