"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.updateArtifacts = void 0;
const tslib_1 = require("tslib");
const is_1 = tslib_1.__importDefault(require("@sindresorhus/is"));
const shlex_1 = require("shlex");
const zod_1 = require("zod");
const error_messages_1 = require("../../../constants/error-messages");
const logger_1 = require("../../../logger");
const check_token_1 = require("../../../util/check-token");
const exec_1 = require("../../../util/exec");
const fs_1 = require("../../../util/fs");
const git_1 = require("../../../util/git");
const hostRules = tslib_1.__importStar(require("../../../util/host-rules"));
const regex_1 = require("../../../util/regex");
const schema_utils_1 = require("../../../util/schema-utils");
const git_tags_1 = require("../../datasource/git-tags");
const packagist_1 = require("../../datasource/packagist");
const schema_1 = require("./schema");
const utils_1 = require("./utils");
function getAuthJson() {
    const authJson = {};
    const githubHostRule = hostRules.find({
        hostType: 'github',
        url: 'https://api.github.com/',
    });
    const gitTagsHostRule = hostRules.find({
        hostType: git_tags_1.GitTagsDatasource.id,
        url: 'https://github.com',
    });
    const selectedGithubToken = (0, check_token_1.takePersonalAccessTokenIfPossible)((0, utils_1.isArtifactAuthEnabled)(githubHostRule)
        ? (0, check_token_1.findGithubToken)(githubHostRule)
        : undefined, (0, utils_1.isArtifactAuthEnabled)(gitTagsHostRule)
        ? (0, check_token_1.findGithubToken)(gitTagsHostRule)
        : undefined);
    if (selectedGithubToken) {
        authJson['github-oauth'] = {
            'github.com': selectedGithubToken,
        };
    }
    for (const gitlabHostRule of hostRules.findAll({ hostType: 'gitlab' })) {
        if (!(0, utils_1.isArtifactAuthEnabled)(gitlabHostRule)) {
            continue;
        }
        if (gitlabHostRule?.token) {
            const host = gitlabHostRule.resolvedHost ?? 'gitlab.com';
            authJson['gitlab-token'] = authJson['gitlab-token'] ?? {};
            authJson['gitlab-token'][host] = gitlabHostRule.token;
            // https://getcomposer.org/doc/articles/authentication-for-private-packages.md#gitlab-token
            authJson['gitlab-domains'] = [
                host,
                ...(authJson['gitlab-domains'] ?? []),
            ];
        }
    }
    for (const packagistHostRule of hostRules.findAll({
        hostType: packagist_1.PackagistDatasource.id,
    })) {
        if (!(0, utils_1.isArtifactAuthEnabled)(packagistHostRule)) {
            continue;
        }
        const { resolvedHost, username, password, token } = packagistHostRule;
        if (resolvedHost && username && password) {
            authJson['http-basic'] = authJson['http-basic'] ?? {};
            authJson['http-basic'][resolvedHost] = { username, password };
        }
        else if (resolvedHost && token) {
            authJson.bearer = authJson.bearer ?? {};
            authJson.bearer[resolvedHost] = token;
        }
    }
    return is_1.default.emptyObject(authJson) ? null : JSON.stringify(authJson);
}
async function updateArtifacts({ packageFileName, updatedDeps, newPackageFileContent, config, }) {
    logger_1.logger.debug(`composer.updateArtifacts(${packageFileName})`);
    const file = schema_utils_1.Json.pipe(schema_1.PackageFile).parse(newPackageFileContent);
    const lockFileName = packageFileName.replace((0, regex_1.regEx)(/\.json$/), '.lock');
    const lockfile = await zod_1.z
        .string()
        .transform((f) => (0, fs_1.readLocalFile)(f, 'utf8'))
        .pipe(schema_utils_1.Json)
        .pipe(schema_1.Lockfile)
        .nullable()
        .catch(null)
        .parseAsync(lockFileName);
    if (!lockfile) {
        logger_1.logger.debug('Composer: unable to read lockfile');
        return null;
    }
    const vendorDir = (0, fs_1.getSiblingFileName)(packageFileName, 'vendor');
    const commitVendorFiles = await (0, fs_1.localPathExists)(vendorDir);
    await (0, fs_1.ensureLocalDir)(vendorDir);
    try {
        await (0, fs_1.writeLocalFile)(packageFileName, newPackageFileContent);
        const constraints = {
            ...(0, utils_1.extractConstraints)(file, lockfile),
            ...config.constraints,
        };
        const composerToolConstraint = {
            toolName: 'composer',
            constraint: constraints.composer,
        };
        const phpToolConstraint = {
            toolName: 'php',
            constraint: (0, utils_1.getPhpConstraint)(constraints),
        };
        const execOptions = {
            cwdFile: packageFileName,
            extraEnv: {
                COMPOSER_CACHE_DIR: await (0, fs_1.ensureCacheDir)('composer'),
                COMPOSER_AUTH: getAuthJson(),
            },
            toolConstraints: [phpToolConstraint, composerToolConstraint],
            docker: {},
        };
        const commands = [];
        // Determine whether install is required before update
        if ((0, utils_1.requireComposerDependencyInstallation)(lockfile)) {
            const preCmd = 'composer';
            const preArgs = 'install' + (0, utils_1.getComposerArguments)(config, composerToolConstraint);
            logger_1.logger.trace({ preCmd, preArgs }, 'composer pre-update command');
            commands.push('git stash -- composer.json');
            commands.push(`${preCmd} ${preArgs}`);
            commands.push('git stash pop || true');
        }
        const cmd = 'composer';
        let args;
        if (config.isLockFileMaintenance) {
            args = 'update';
        }
        else {
            args =
                ('update ' +
                    updatedDeps
                        .map((dep) => dep.depName)
                        .filter(is_1.default.string)
                        .map((dep) => (0, shlex_1.quote)(dep))
                        .join(' ')).trim() + ' --with-dependencies';
        }
        args += (0, utils_1.getComposerArguments)(config, composerToolConstraint);
        logger_1.logger.trace({ cmd, args }, 'composer command');
        commands.push(`${cmd} ${args}`);
        await (0, exec_1.exec)(commands, execOptions);
        const status = await (0, git_1.getRepoStatus)();
        if (!status.modified.includes(lockFileName)) {
            return null;
        }
        logger_1.logger.debug('Returning updated composer.lock');
        const res = [
            {
                file: {
                    type: 'addition',
                    path: lockFileName,
                    contents: await (0, fs_1.readLocalFile)(lockFileName),
                },
            },
        ];
        if (!commitVendorFiles) {
            return res;
        }
        logger_1.logger.debug(`Committing vendor files in ${vendorDir}`);
        for (const f of [...status.modified, ...status.not_added]) {
            if (f.startsWith(vendorDir)) {
                res.push({
                    file: {
                        type: 'addition',
                        path: f,
                        contents: await (0, fs_1.readLocalFile)(f),
                    },
                });
            }
        }
        for (const f of status.deleted) {
            res.push({
                file: {
                    type: 'deletion',
                    path: f,
                },
            });
        }
        return res;
    }
    catch (err) {
        // istanbul ignore if
        if (err.message === error_messages_1.TEMPORARY_ERROR) {
            throw err;
        }
        if (err.message?.includes('Your requirements could not be resolved to an installable set of packages.')) {
            logger_1.logger.info('Composer requirements cannot be resolved');
        }
        else if (err.message?.includes('write error (disk full?)')) {
            throw new Error(error_messages_1.SYSTEM_INSUFFICIENT_DISK_SPACE);
        }
        else {
            logger_1.logger.debug({ err }, 'Failed to generate composer.lock');
        }
        return [
            {
                artifactError: {
                    lockFile: lockFileName,
                    stderr: err.message,
                },
            },
        ];
    }
}
exports.updateArtifacts = updateArtifacts;
//# sourceMappingURL=artifacts.js.map