2022-09-26 18:28:25 +03:00
|
|
|
import fs from 'node:fs'
|
|
|
|
import path from 'node:path'
|
2019-02-10 15:26:50 +03:00
|
|
|
|
2022-09-26 19:01:03 +03:00
|
|
|
import { command } from 'execa'
|
|
|
|
|
2022-10-07 10:31:24 +03:00
|
|
|
import {
|
|
|
|
EN_SPACY_MODEL_NAME,
|
|
|
|
EN_SPACY_MODEL_VERSION,
|
|
|
|
FR_SPACY_MODEL_NAME,
|
|
|
|
FR_SPACY_MODEL_VERSION,
|
|
|
|
PYTHON_BRIDGE_SRC_PATH,
|
|
|
|
TCP_SERVER_SRC_PATH
|
|
|
|
} from '@/constants'
|
2022-09-26 16:29:56 +03:00
|
|
|
import { LogHelper } from '@/helpers/log-helper'
|
2022-10-06 21:37:43 +03:00
|
|
|
import { LoaderHelper } from '@/helpers/loader-helper'
|
2022-10-07 08:24:58 +03:00
|
|
|
import { OSHelper, OSTypes } from '@/helpers/os-helper'
|
2019-02-10 15:26:50 +03:00
|
|
|
|
|
|
|
/**
|
2022-10-06 21:37:43 +03:00
|
|
|
* 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
|
2019-02-10 15:26:50 +03:00
|
|
|
*/
|
|
|
|
|
2022-10-07 10:31:24 +03:00
|
|
|
// Define mirror to download models installation file
|
|
|
|
function getModelInstallationFileUrl(model, mirror = undefined) {
|
|
|
|
const { name, version } = SPACY_MODELS.get(model)
|
|
|
|
const suffix = 'py3-none-any.whl'
|
|
|
|
let urlPrefix = 'https://github.com/explosion/spacy-models/releases/download'
|
|
|
|
|
|
|
|
if (mirror === 'cn') {
|
|
|
|
LogHelper.info(
|
|
|
|
'Using Chinese mirror to download model installation file...'
|
|
|
|
)
|
|
|
|
urlPrefix =
|
|
|
|
'https://download.fastgit.org/explosion/spacy-models/releases/download'
|
|
|
|
}
|
|
|
|
|
|
|
|
return `${urlPrefix}/${name}-${version}/${name}-${version}-${suffix}`
|
|
|
|
}
|
|
|
|
|
2022-10-06 21:37:43 +03:00
|
|
|
const SETUP_TARGETS = new Map()
|
2022-10-07 10:31:24 +03:00
|
|
|
const SPACY_MODELS = new Map()
|
2022-10-06 21:37:43 +03:00
|
|
|
|
|
|
|
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')
|
|
|
|
})
|
2022-10-07 10:31:24 +03:00
|
|
|
|
|
|
|
SPACY_MODELS.set('en', {
|
|
|
|
name: EN_SPACY_MODEL_NAME,
|
|
|
|
version: EN_SPACY_MODEL_VERSION
|
|
|
|
})
|
|
|
|
SPACY_MODELS.set('fr', {
|
|
|
|
name: FR_SPACY_MODEL_NAME,
|
|
|
|
version: FR_SPACY_MODEL_VERSION
|
|
|
|
})
|
2022-10-06 21:37:43 +03:00
|
|
|
;(async () => {
|
|
|
|
LoaderHelper.start()
|
|
|
|
|
|
|
|
const { argv } = process
|
|
|
|
const givenSetupTarget = argv[2].toLowerCase()
|
2022-10-07 10:31:24 +03:00
|
|
|
// cn
|
|
|
|
const givenMirror = argv[3]?.toLowerCase()
|
2022-10-06 21:37:43 +03:00
|
|
|
|
|
|
|
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)
|
2022-10-07 09:10:32 +03:00
|
|
|
const { type: osType } = OSHelper.getInformation()
|
2022-10-06 21:37:43 +03:00
|
|
|
const installPythonPackages = async () => {
|
|
|
|
LogHelper.info(`Installing Python packages from ${pipfilePath}.lock...`)
|
|
|
|
|
|
|
|
// 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`)
|
|
|
|
}
|
|
|
|
|
2022-10-12 18:50:46 +03:00
|
|
|
await command(`pipenv install --system --deploy`, {
|
2022-10-12 18:37:23 +03:00
|
|
|
shell: true,
|
|
|
|
stdio: 'inherit'
|
|
|
|
})
|
2022-10-07 08:24:58 +03:00
|
|
|
|
2022-10-12 18:37:23 +03:00
|
|
|
LogHelper.success('Python packages installed')
|
2022-10-06 21:37:43 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Verify if a fresh development environment installation is necessary
|
|
|
|
*/
|
|
|
|
|
2022-10-07 06:26:39 +03:00
|
|
|
// Required environment variables to set up
|
|
|
|
process.env.PIPENV_PIPFILE = pipfilePath
|
|
|
|
process.env.PIPENV_VENV_IN_PROJECT = true
|
2022-10-06 21:37:43 +03:00
|
|
|
|
2022-10-07 06:26:39 +03:00
|
|
|
if (givenSetupTarget === 'python-bridge') {
|
|
|
|
// As per: https://github.com/marcelotduarte/cx_Freeze/issues/1548
|
|
|
|
process.env.PIP_NO_BINARY = 'cx_Freeze'
|
|
|
|
}
|
2019-02-10 15:26:50 +03:00
|
|
|
|
2022-10-07 06:26:39 +03:00
|
|
|
try {
|
2022-10-06 21:37:43 +03:00
|
|
|
if (!hasDotVenv) {
|
|
|
|
await installPythonPackages()
|
2022-10-07 06:26:39 +03:00
|
|
|
} else {
|
|
|
|
if (fs.existsSync(dotProjectPath)) {
|
|
|
|
const dotProjectMtime = fs.statSync(dotProjectPath).mtime
|
2022-02-04 08:44:40 +03:00
|
|
|
|
2022-10-07 06:26:39 +03:00
|
|
|
// 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')
|
2022-09-03 14:12:41 +03:00
|
|
|
await installPythonPackages()
|
|
|
|
} else {
|
2022-10-07 06:26:39 +03:00
|
|
|
LogHelper.success('Python packages are up-to-date')
|
2022-02-04 08:44:40 +03:00
|
|
|
}
|
2022-10-06 21:37:43 +03:00
|
|
|
} else {
|
2022-10-07 06:26:39 +03:00
|
|
|
await installPythonPackages()
|
2022-09-03 14:12:41 +03:00
|
|
|
}
|
2019-02-10 15:26:50 +03:00
|
|
|
}
|
2022-10-06 21:37:43 +03:00
|
|
|
} catch (e) {
|
|
|
|
LogHelper.error(
|
|
|
|
`Failed to set up the ${setupTarget} development environment: ${e}`
|
|
|
|
)
|
|
|
|
} finally {
|
|
|
|
LoaderHelper.stop()
|
|
|
|
}
|
2022-10-07 06:26:39 +03:00
|
|
|
|
|
|
|
if (givenSetupTarget === 'tcp-server') {
|
2022-10-07 08:44:48 +03:00
|
|
|
const installSpacyModels = async () => {
|
|
|
|
try {
|
|
|
|
LogHelper.info('Installing spaCy models...')
|
|
|
|
|
|
|
|
// Install models one by one to avoid network throttling
|
2022-10-07 10:31:24 +03:00
|
|
|
for (const modelLanguage of SPACY_MODELS.keys()) {
|
|
|
|
const modelInstallationFileUrl = getModelInstallationFileUrl(
|
|
|
|
modelLanguage,
|
|
|
|
givenMirror
|
|
|
|
)
|
|
|
|
|
|
|
|
await command(`pipenv run pip install ${modelInstallationFileUrl}`, {
|
2022-10-07 08:44:48 +03:00
|
|
|
shell: true,
|
|
|
|
stdio: 'inherit'
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
LogHelper.success('spaCy models installed')
|
|
|
|
} catch (e) {
|
|
|
|
LogHelper.error(`Failed to install spaCy models: ${e}`)
|
|
|
|
process.exit(1)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-10-07 06:26:39 +03:00
|
|
|
LogHelper.info('Checking whether all spaCy models are installed...')
|
|
|
|
|
|
|
|
try {
|
2022-10-07 10:31:24 +03:00
|
|
|
for (const { name: modelName } of SPACY_MODELS.values()) {
|
2022-10-07 09:10:32 +03:00
|
|
|
const { stderr } = await command(
|
2022-10-07 10:31:24 +03:00
|
|
|
`pipenv run python -c "import ${modelName}"`,
|
2022-10-07 09:10:32 +03:00
|
|
|
{ shell: true }
|
|
|
|
)
|
|
|
|
|
|
|
|
// Check stderr output for Windows as no exception is thrown
|
|
|
|
if (osType === OSTypes.Windows) {
|
|
|
|
if (String(stderr).length > 0) {
|
|
|
|
await installSpacyModels()
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
2022-10-07 06:26:39 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
LogHelper.success('All spaCy models are already installed')
|
|
|
|
} catch (e) {
|
|
|
|
LogHelper.info('Not all spaCy models are installed')
|
2022-10-07 08:44:48 +03:00
|
|
|
await installSpacyModels()
|
2022-10-07 06:26:39 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
LogHelper.success(`${setupTarget} development environment ready`)
|
2022-10-06 21:37:43 +03:00
|
|
|
})()
|