"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getPythonConstraint = getPythonConstraint;
exports.getPoetryRequirement = getPoetryRequirement;
exports.updateArtifacts = updateArtifacts;
const tslib_1 = require("tslib");
const is_1 = tslib_1.__importDefault(require("@sindresorhus/is"));
const shlex_1 = require("shlex");
const error_messages_1 = require("../../../constants/error-messages");
const logger_1 = require("../../../logger");
const exec_1 = require("../../../util/exec");
const fs_1 = require("../../../util/fs");
const auth_1 = require("../../../util/git/auth");
const host_rules_1 = require("../../../util/host-rules");
const regex_1 = require("../../../util/regex");
const result_1 = require("../../../util/result");
const toml_1 = require("../../../util/toml");
const pypi_1 = require("../../datasource/pypi");
const schema_1 = require("./schema");
function getPythonConstraint(pyProjectContent, existingLockFileContent) {
    // Read Python version from `pyproject.toml` first as it could have been updated
    const pyprojectPythonConstraint = result_1.Result.parse(pyProjectContent, schema_1.PoetrySchemaToml.transform(({ packageFileContent }) => packageFileContent.deps.find((dep) => dep.depName === 'python')
        ?.currentValue)).unwrapOrNull();
    if (pyprojectPythonConstraint) {
        logger_1.logger.debug('Using python version from pyproject.toml');
        return pyprojectPythonConstraint;
    }
    const lockfilePythonConstraint = result_1.Result.parse(existingLockFileContent, schema_1.Lockfile.transform(({ pythonVersions }) => pythonVersions)).unwrapOrNull();
    if (lockfilePythonConstraint) {
        logger_1.logger.debug('Using python version from poetry.lock');
        return lockfilePythonConstraint;
    }
    return null;
}
function getPoetryRequirement(pyProjectContent, existingLockFileContent) {
    // Read Poetry version from first line of poetry.lock
    const firstLine = existingLockFileContent.split('\n')[0];
    const poetryVersionMatch = firstLine.match(/by Poetry ([\d\\.]+)/);
    if (poetryVersionMatch?.[1]) {
        const poetryVersion = poetryVersionMatch[1];
        logger_1.logger.debug(`Using poetry version ${poetryVersion} from poetry.lock header`);
        return poetryVersion;
    }
    const { val: lockfilePoetryConstraint } = result_1.Result.parse(existingLockFileContent, schema_1.Lockfile.transform(({ poetryConstraint }) => poetryConstraint)).unwrap();
    if (lockfilePoetryConstraint) {
        logger_1.logger.debug(`Using poetry version ${lockfilePoetryConstraint} from poetry.lock metadata`);
        return lockfilePoetryConstraint;
    }
    const { val: pyprojectPoetryConstraint } = result_1.Result.parse(pyProjectContent, schema_1.PoetrySchemaToml.transform(({ poetryRequirement }) => poetryRequirement)).unwrap();
    if (pyprojectPoetryConstraint) {
        logger_1.logger.debug(`Using poetry version ${pyprojectPoetryConstraint} from pyproject.toml`);
        return pyprojectPoetryConstraint;
    }
    return null;
}
function getPoetrySources(content, fileName) {
    let pyprojectFile;
    try {
        pyprojectFile = (0, toml_1.parse)(content);
    }
    catch (err) {
        logger_1.logger.debug({ err }, 'Error parsing pyproject.toml file');
        return [];
    }
    if (!pyprojectFile.tool?.poetry) {
        logger_1.logger.debug(`{$fileName} contains no poetry section`);
        return [];
    }
    const sources = pyprojectFile.tool?.poetry?.source ?? [];
    const sourceArray = [];
    for (const source of sources) {
        if (source.name && source.url) {
            sourceArray.push({ name: source.name, url: source.url });
        }
    }
    return sourceArray;
}
function getMatchingHostRule(url) {
    const scopedMatch = (0, host_rules_1.find)({ hostType: pypi_1.PypiDatasource.id, url });
    return is_1.default.nonEmptyObject(scopedMatch) ? scopedMatch : (0, host_rules_1.find)({ url });
}
function getSourceCredentialVars(pyprojectContent, packageFileName) {
    const poetrySources = getPoetrySources(pyprojectContent, packageFileName);
    const envVars = {};
    for (const source of poetrySources) {
        const matchingHostRule = getMatchingHostRule(source.url);
        const formattedSourceName = source.name
            .replace((0, regex_1.regEx)(/(\.|-)+/g), '_')
            .toUpperCase();
        if (matchingHostRule.username) {
            envVars[`POETRY_HTTP_BASIC_${formattedSourceName}_USERNAME`] =
                matchingHostRule.username;
        }
        if (matchingHostRule.password) {
            envVars[`POETRY_HTTP_BASIC_${formattedSourceName}_PASSWORD`] =
                matchingHostRule.password;
        }
    }
    return envVars;
}
async function updateArtifacts({ packageFileName, updatedDeps, newPackageFileContent, config, }) {
    logger_1.logger.debug(`poetry.updateArtifacts(${packageFileName})`);
    const isLockFileMaintenance = config.updateType === 'lockFileMaintenance';
    if (!is_1.default.nonEmptyArray(updatedDeps) && !isLockFileMaintenance) {
        logger_1.logger.debug('No updated poetry deps - returning null');
        return null;
    }
    // Try poetry.lock first
    let lockFileName = (0, fs_1.getSiblingFileName)(packageFileName, 'poetry.lock');
    let existingLockFileContent = await (0, fs_1.readLocalFile)(lockFileName, 'utf8');
    if (!existingLockFileContent) {
        // Try pyproject.lock next
        lockFileName = (0, fs_1.getSiblingFileName)(packageFileName, 'pyproject.lock');
        existingLockFileContent = await (0, fs_1.readLocalFile)(lockFileName, 'utf8');
        if (!existingLockFileContent) {
            logger_1.logger.debug(`No lock file found`);
            return null;
        }
    }
    logger_1.logger.debug(`Updating ${lockFileName}`);
    try {
        await (0, fs_1.writeLocalFile)(packageFileName, newPackageFileContent);
        const cmd = [];
        if (isLockFileMaintenance) {
            await (0, fs_1.deleteLocalFile)(lockFileName);
            cmd.push('poetry update --lock --no-interaction');
        }
        else {
            cmd.push(`poetry update --lock --no-interaction ${updatedDeps
                .map((dep) => dep.depName)
                .filter(is_1.default.string)
                .map((dep) => (0, shlex_1.quote)(dep))
                .join(' ')}`);
        }
        const pythonConstraint = config?.constraints?.python ??
            getPythonConstraint(newPackageFileContent, existingLockFileContent);
        const poetryConstraint = config.constraints?.poetry ??
            getPoetryRequirement(newPackageFileContent, existingLockFileContent);
        const extraEnv = {
            ...getSourceCredentialVars(newPackageFileContent, packageFileName),
            ...(0, auth_1.getGitEnvironmentVariables)(['poetry']),
            PIP_CACHE_DIR: await (0, fs_1.ensureCacheDir)('pip'),
        };
        const execOptions = {
            cwdFile: packageFileName,
            extraEnv,
            docker: {},
            userConfiguredEnv: config.env,
            toolConstraints: [
                { toolName: 'python', constraint: pythonConstraint },
                { toolName: 'poetry', constraint: poetryConstraint },
            ],
        };
        await (0, exec_1.exec)(cmd, execOptions);
        const newPoetryLockContent = await (0, fs_1.readLocalFile)(lockFileName, 'utf8');
        if (existingLockFileContent === newPoetryLockContent) {
            logger_1.logger.debug(`${lockFileName} is unchanged`);
            return null;
        }
        logger_1.logger.debug(`Returning updated ${lockFileName}`);
        return [
            {
                file: {
                    type: 'addition',
                    path: lockFileName,
                    contents: newPoetryLockContent,
                },
            },
        ];
    }
    catch (err) {
        // istanbul ignore if
        if (err.message === error_messages_1.TEMPORARY_ERROR) {
            throw err;
        }
        logger_1.logger.debug({ err }, `Failed to update ${lockFileName} file`);
        return [
            {
                artifactError: {
                    lockFile: lockFileName,
                    stderr: `${String(err.stdout)}\n${String(err.stderr)}`,
                },
            },
        ];
    }
}
//# sourceMappingURL=artifacts.js.map