"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.extractFromImage = extractFromImage;
exports.extractFromServices = extractFromServices;
exports.extractFromJob = extractFromJob;
exports.extractPackageFile = extractPackageFile;
exports.extractAllPackageFiles = extractAllPackageFiles;
const tslib_1 = require("tslib");
const is_1 = tslib_1.__importDefault(require("@sindresorhus/is"));
const logger_1 = require("../../../logger");
const fs_1 = require("../../../util/fs");
const regex_1 = require("../../../util/regex");
const url_1 = require("../../../util/url");
const yaml_1 = require("../../../util/yaml");
const gitlab_tags_1 = require("../../datasource/gitlab-tags");
const common_1 = require("./common");
const utils_1 = require("./utils");
// See https://docs.gitlab.com/ee/ci/components/index.html#use-a-component
const componentReferenceRegex = (0, regex_1.regEx)(/(?<fqdn>[^/]+)\/(?<projectPath>.+)\/(?:.+)@(?<specificVersion>.+)/);
const componentReferenceLatestVersion = '~latest';
function extractFromImage(image, registryAliases) {
    if (is_1.default.undefined(image)) {
        return null;
    }
    let dep = null;
    if (is_1.default.string(image)) {
        dep = (0, utils_1.getGitlabDep)(image, registryAliases);
        dep.depType = 'image';
    }
    else if (is_1.default.string(image?.name)) {
        dep = (0, utils_1.getGitlabDep)(image.name, registryAliases);
        dep.depType = 'image-name';
    }
    return dep;
}
function extractFromServices(services, registryAliases) {
    if (is_1.default.undefined(services)) {
        return [];
    }
    const deps = [];
    for (const service of services) {
        if (is_1.default.string(service)) {
            const dep = (0, utils_1.getGitlabDep)(service, registryAliases);
            dep.depType = 'service-image';
            deps.push(dep);
        }
        else if (is_1.default.string(service?.name)) {
            const dep = (0, utils_1.getGitlabDep)(service.name, registryAliases);
            dep.depType = 'service-image';
            deps.push(dep);
        }
    }
    return deps;
}
function extractFromJob(job, registryAliases) {
    if (is_1.default.undefined(job)) {
        return [];
    }
    const deps = [];
    if (is_1.default.object(job)) {
        const { image, services } = { ...job };
        if (is_1.default.object(image) || is_1.default.string(image)) {
            const dep = extractFromImage(image, registryAliases);
            if (dep) {
                deps.push(dep);
            }
        }
        if (is_1.default.array(services)) {
            deps.push(...extractFromServices(services, registryAliases));
        }
    }
    return deps;
}
function getIncludeComponentsFromInclude(includeValue) {
    const includes = is_1.default.array(includeValue) ? includeValue : [includeValue];
    return includes.filter(common_1.isGitlabIncludeComponent);
}
function getAllIncludeComponents(data) {
    const childrenData = Object.values((0, common_1.filterIncludeFromGitlabPipeline)(data))
        .filter(common_1.isNonEmptyObject)
        .map(getAllIncludeComponents)
        .flat();
    // Process include key.
    if (data.include) {
        childrenData.push(...getIncludeComponentsFromInclude(data.include));
    }
    return childrenData;
}
function extractDepFromIncludeComponent(includeComponent, registryAliases) {
    const componentReference = componentReferenceRegex.exec(includeComponent.component)?.groups;
    if (!componentReference) {
        logger_1.logger.debug({ componentReference: includeComponent.component }, 'Ignoring malformed component reference');
        return null;
    }
    const projectPathParts = componentReference.projectPath.split('/');
    if (projectPathParts.length < 2) {
        logger_1.logger.debug({ componentReference: includeComponent.component }, 'Ignoring component reference with incomplete project path');
        return null;
    }
    const aliasValue = registryAliases?.[componentReference.fqdn];
    if (aliasValue) {
        componentReference.fqdn = aliasValue;
    }
    const dep = {
        datasource: gitlab_tags_1.GitlabTagsDatasource.id,
        depName: componentReference.projectPath,
        depType: 'repository',
        currentValue: componentReference.specificVersion,
        registryUrls: [`https://${componentReference.fqdn}`],
    };
    if (dep.currentValue === componentReferenceLatestVersion) {
        logger_1.logger.debug({ componentVersion: dep.currentValue }, 'Ignoring component version');
        dep.skipReason = 'unsupported-version';
    }
    return dep;
}
function extractPackageFile(content, packageFile, config) {
    let deps = [];
    try {
        // TODO: use schema (#9610)
        const docs = (0, yaml_1.parseYaml)((0, utils_1.replaceReferenceTags)(content), null, {
            json: true,
        });
        for (const doc of docs) {
            if (is_1.default.object(doc)) {
                for (const [property, value] of Object.entries(doc)) {
                    switch (property) {
                        case 'image':
                            {
                                const dep = extractFromImage(value, config.registryAliases);
                                if (dep) {
                                    deps.push(dep);
                                }
                            }
                            break;
                        case 'services':
                            deps.push(...extractFromServices(value, config.registryAliases));
                            break;
                        default:
                            deps.push(...extractFromJob(value, config.registryAliases));
                            break;
                    }
                }
                deps = deps.filter(is_1.default.truthy);
            }
            const includedComponents = getAllIncludeComponents(doc);
            for (const includedComponent of includedComponents) {
                const dep = extractDepFromIncludeComponent(includedComponent, config.registryAliases);
                if (dep) {
                    deps.push(dep);
                }
            }
        }
    }
    catch (err) /* istanbul ignore next */ {
        if (err.stack?.startsWith('YAMLException:')) {
            logger_1.logger.debug({ err, packageFile }, 'YAML exception extracting GitLab CI includes');
        }
        else {
            logger_1.logger.debug({ err, packageFile }, 'Error extracting GitLab CI dependencies');
        }
    }
    return deps.length ? { deps } : null;
}
async function extractAllPackageFiles(config, packageFiles) {
    const filesToExamine = [...packageFiles];
    const seen = new Set(packageFiles);
    const results = [];
    // extract all includes from the files
    while (filesToExamine.length > 0) {
        const file = filesToExamine.pop();
        const content = await (0, fs_1.readLocalFile)(file, 'utf8');
        if (!content) {
            logger_1.logger.debug({ packageFile: file }, `Empty or non existent gitlabci file`);
            continue;
        }
        let docs;
        try {
            // TODO: use schema (#9610)
            docs = (0, yaml_1.parseYaml)((0, utils_1.replaceReferenceTags)(content), null, {
                json: true,
            });
        }
        catch (err) {
            logger_1.logger.debug({ err, packageFile: file }, 'Error extracting GitLab CI dependencies');
            continue;
        }
        for (const doc of docs) {
            if (is_1.default.array(doc?.include)) {
                for (const includeObj of doc.include.filter(common_1.isGitlabIncludeLocal)) {
                    const fileObj = (0, url_1.trimLeadingSlash)(includeObj.local);
                    if (!seen.has(fileObj)) {
                        seen.add(fileObj);
                        filesToExamine.push(fileObj);
                    }
                }
            }
            else if (is_1.default.string(doc?.include)) {
                const fileObj = (0, url_1.trimLeadingSlash)(doc.include);
                if (!seen.has(fileObj)) {
                    seen.add(fileObj);
                    filesToExamine.push(fileObj);
                }
            }
        }
        const result = extractPackageFile(content, file, config);
        if (result !== null) {
            results.push({
                packageFile: file,
                deps: result.deps,
            });
        }
    }
    logger_1.logger.trace({ packageFiles, files: filesToExamine.entries() }, 'extracted all GitLab CI files');
    if (!results.length) {
        return null;
    }
    return results;
}
//# sourceMappingURL=extract.js.map