serverless-python-requirements
Version:
Serverless Python Requirements Plugin
144 lines (126 loc) • 3.98 kB
JavaScript
const fs = require('fs');
const fse = require('fs-extra');
const path = require('path');
const spawn = require('child-process-ext/spawn');
const tomlParse = require('@iarna/toml/parse-string');
/**
* poetry install
*/
async function pyprojectTomlToRequirements(modulePath, pluginInstance) {
const { serverless, servicePath, options, log, progress } = pluginInstance;
const moduleProjectPath = path.join(servicePath, modulePath);
if (!options.usePoetry || !isPoetryProject(moduleProjectPath)) {
return;
}
let generateRequirementsProgress;
if (progress && log) {
generateRequirementsProgress = progress.get(
'python-generate-requirements-toml'
);
}
const emitMsg = (msg) => {
if (generateRequirementsProgress) {
generateRequirementsProgress.update(msg);
log.info(msg);
} else {
serverless.cli.log(msg);
}
};
if (fs.existsSync('poetry.lock')) {
emitMsg('Generating requirements.txt from poetry.lock');
} else {
if (options.requirePoetryLockFile) {
throw new serverless.classes.Error(
'poetry.lock file not found - set requirePoetryLockFile to false to ' +
'disable this error',
'MISSING_REQUIRED_POETRY_LOCK'
);
}
emitMsg('Generating poetry.lock and requirements.txt from pyproject.toml');
}
try {
try {
await spawn(
'poetry',
[
'export',
'--without-hashes',
'-f',
'requirements.txt',
'-o',
'requirements.txt',
'--with-credentials',
...(options.poetryWithGroups.length
? [`--with=${options.poetryWithGroups.join(',')}`]
: []),
...(options.poetryWithoutGroups.length
? [`--without=${options.poetryWithoutGroups.join(',')}`]
: []),
...(options.poetryOnlyGroups.length
? [`--only=${options.poetryOnlyGroups.join(',')}`]
: []),
],
{
cwd: moduleProjectPath,
}
);
} catch (e) {
if (
e.stderrBuffer &&
e.stderrBuffer.toString().includes('command not found')
) {
throw new serverless.classes.Error(
`poetry not found! Install it according to the poetry docs.`,
'PYTHON_REQUIREMENTS_POETRY_NOT_FOUND'
);
}
throw e;
}
const editableFlag = new RegExp(/^-e /gm);
const sourceRequirements = path.join(moduleProjectPath, 'requirements.txt');
const requirementsContents = fse.readFileSync(sourceRequirements, {
encoding: 'utf-8',
});
if (requirementsContents.match(editableFlag)) {
if (log) {
log.info('The generated file contains -e flags, removing them');
} else {
serverless.cli.log(
'The generated file contains -e flags, removing them...'
);
}
fse.writeFileSync(
sourceRequirements,
requirementsContents.replace(editableFlag, '')
);
}
fse.ensureDirSync(path.join(servicePath, '.serverless'));
fse.moveSync(
sourceRequirements,
path.join(servicePath, '.serverless', modulePath, 'requirements.txt'),
{ overwrite: true }
);
} finally {
generateRequirementsProgress && generateRequirementsProgress.remove();
}
}
/**
* Check if pyproject.toml file exists and is a poetry project.
*/
function isPoetryProject(servicePath) {
const pyprojectPath = path.join(servicePath, 'pyproject.toml');
if (!fse.existsSync(pyprojectPath)) {
return false;
}
const pyprojectToml = fs.readFileSync(pyprojectPath);
const pyproject = tomlParse(pyprojectToml);
const buildSystemReqs =
(pyproject['build-system'] && pyproject['build-system']['requires']) || [];
for (var i = 0; i < buildSystemReqs.length; i++) {
if (buildSystemReqs[i].startsWith('poetry')) {
return true;
}
}
return false;
}
module.exports = { pyprojectTomlToRequirements, isPoetryProject };