"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.extractAllPackageFiles = exports.extractPackageFile = void 0;
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 yaml_1 = require("../../../util/yaml");
const bitbucket_tags_1 = require("../../datasource/bitbucket-tags");
const docker_1 = require("../../datasource/docker");
const git_refs_1 = require("../../datasource/git-refs");
const git_tags_1 = require("../../datasource/git-tags");
const github_releases_1 = require("../../datasource/github-releases");
const github_tags_1 = require("../../datasource/github-tags");
const gitlab_tags_1 = require("../../datasource/gitlab-tags");
const helm_1 = require("../../datasource/helm");
const extract_1 = require("../dockerfile/extract");
const common_1 = require("./common");
function readManifest(content, packageFile) {
    if ((0, common_1.isSystemManifest)(packageFile)) {
        const versionMatch = (0, regex_1.regEx)(common_1.systemManifestHeaderRegex).exec(content);
        if (!versionMatch) {
            return null;
        }
        return {
            kind: 'system',
            file: packageFile,
            version: versionMatch[1],
            components: versionMatch[2],
        };
    }
    const manifest = {
        kind: 'resource',
        file: packageFile,
        resources: [],
    };
    let resources;
    try {
        resources = (0, yaml_1.parseYaml)(content, null, { json: true });
    }
    catch (err) {
        logger_1.logger.debug({ err, packageFile }, 'Failed to parse Flux manifest');
        return null;
    }
    // It's possible there are other non-Flux HelmRelease/HelmRepository CRs out there, so we filter based on apiVersion.
    for (const resource of resources) {
        switch (resource?.kind) {
            case 'HelmRelease':
                if (resource.apiVersion?.startsWith('helm.toolkit.fluxcd.io/') &&
                    resource.spec?.chart?.spec?.chart) {
                    manifest.resources.push(resource);
                }
                break;
            case 'HelmRepository':
                if (resource.apiVersion?.startsWith('source.toolkit.fluxcd.io/') &&
                    resource.metadata?.name &&
                    resource.metadata.namespace &&
                    resource.spec?.url) {
                    manifest.resources.push(resource);
                }
                break;
            case 'GitRepository':
                if (resource.apiVersion?.startsWith('source.toolkit.fluxcd.io/') &&
                    resource.spec?.url) {
                    manifest.resources.push(resource);
                }
                break;
            case 'OCIRepository':
                if (resource.apiVersion?.startsWith('source.toolkit.fluxcd.io/') &&
                    resource.spec?.url) {
                    manifest.resources.push(resource);
                }
                break;
        }
    }
    return manifest;
}
const githubUrlRegex = (0, regex_1.regEx)(/^(?:https:\/\/|git@)github\.com[/:](?<packageName>[^/]+\/[^/]+?)(?:\.git)?$/);
const gitlabUrlRegex = (0, regex_1.regEx)(/^(?:https:\/\/|git@)gitlab\.com[/:](?<packageName>[^/]+\/[^/]+?)(?:\.git)?$/);
const bitbucketUrlRegex = (0, regex_1.regEx)(/^(?:https:\/\/|git@)bitbucket\.org[/:](?<packageName>[^/]+\/[^/]+?)(?:\.git)?$/);
function resolveGitRepositoryPerSourceTag(dep, gitUrl) {
    const githubMatchGroups = githubUrlRegex.exec(gitUrl)?.groups;
    if (githubMatchGroups) {
        dep.datasource = github_tags_1.GithubTagsDatasource.id;
        dep.packageName = githubMatchGroups.packageName;
        dep.sourceUrl = `https://github.com/${dep.packageName}`;
        return;
    }
    const gitlabMatchGroups = gitlabUrlRegex.exec(gitUrl)?.groups;
    if (gitlabMatchGroups) {
        dep.datasource = gitlab_tags_1.GitlabTagsDatasource.id;
        dep.packageName = gitlabMatchGroups.packageName;
        dep.sourceUrl = `https://gitlab.com/${dep.packageName}`;
        return;
    }
    const bitbucketMatchGroups = bitbucketUrlRegex.exec(gitUrl)?.groups;
    if (bitbucketMatchGroups) {
        dep.datasource = bitbucket_tags_1.BitbucketTagsDatasource.id;
        dep.packageName = bitbucketMatchGroups.packageName;
        dep.sourceUrl = `https://bitbucket.org/${dep.packageName}`;
        return;
    }
    dep.datasource = git_tags_1.GitTagsDatasource.id;
    dep.packageName = gitUrl;
    if (gitUrl.startsWith('https://')) {
        dep.sourceUrl = gitUrl.replace(/\.git$/, '');
    }
}
function resolveSystemManifest(manifest) {
    return [
        {
            depName: 'fluxcd/flux2',
            datasource: github_releases_1.GithubReleasesDatasource.id,
            currentValue: manifest.version,
            managerData: {
                components: manifest.components,
            },
        },
    ];
}
function resolveResourceManifest(manifest, helmRepositories) {
    const deps = [];
    for (const resource of manifest.resources) {
        switch (resource.kind) {
            case 'HelmRelease': {
                const dep = {
                    depName: resource.spec.chart.spec.chart,
                    currentValue: resource.spec.chart.spec.version,
                    datasource: helm_1.HelmDatasource.id,
                };
                const matchingRepositories = helmRepositories.filter((rep) => rep.kind === resource.spec.chart.spec.sourceRef?.kind &&
                    rep.metadata.name === resource.spec.chart.spec.sourceRef.name &&
                    rep.metadata.namespace ===
                        (resource.spec.chart.spec.sourceRef.namespace ??
                            resource.metadata?.namespace));
                if (matchingRepositories.length) {
                    dep.registryUrls = matchingRepositories
                        .map((repo) => {
                        if (repo.spec.type === 'oci' ||
                            repo.spec.url.startsWith('oci://')) {
                            // Change datasource to Docker
                            dep.datasource = docker_1.DockerDatasource.id;
                            // Ensure the URL is a valid OCI path
                            dep.packageName = `${repo.spec.url.replace('oci://', '')}/${resource.spec.chart.spec.chart}`;
                            return null;
                        }
                        else {
                            return repo.spec.url;
                        }
                    })
                        .filter(is_1.default.string);
                    // if registryUrls is empty, delete it from dep
                    if (!dep.registryUrls?.length) {
                        delete dep.registryUrls;
                    }
                }
                else {
                    dep.skipReason = 'unknown-registry';
                }
                deps.push(dep);
                break;
            }
            case 'GitRepository': {
                const dep = {
                    depName: resource.metadata.name,
                };
                if (resource.spec.ref?.commit) {
                    const gitUrl = resource.spec.url;
                    dep.currentDigest = resource.spec.ref.commit;
                    dep.datasource = git_refs_1.GitRefsDatasource.id;
                    dep.packageName = gitUrl;
                    dep.replaceString = resource.spec.ref.commit;
                    if (gitUrl.startsWith('https://')) {
                        dep.sourceUrl = gitUrl.replace(/\.git$/, '');
                    }
                }
                else if (resource.spec.ref?.tag) {
                    dep.currentValue = resource.spec.ref.tag;
                    resolveGitRepositoryPerSourceTag(dep, resource.spec.url);
                }
                else {
                    dep.skipReason = 'unversioned-reference';
                }
                deps.push(dep);
                break;
            }
            case 'OCIRepository': {
                const container = resource.spec.url?.replace('oci://', '');
                let dep = {
                    depName: container,
                };
                if (resource.spec.ref?.digest) {
                    dep = (0, extract_1.getDep)(`${container}@${resource.spec.ref.digest}`, false);
                    if (resource.spec.ref?.tag) {
                        logger_1.logger.debug('A digest and tag was found, ignoring tag');
                    }
                }
                else if (resource.spec.ref?.tag) {
                    dep = (0, extract_1.getDep)(`${container}:${resource.spec.ref.tag}`, false);
                    dep.autoReplaceStringTemplate =
                        '{{#if newValue}}{{newValue}}{{/if}}{{#if newDigest}}@{{newDigest}}{{/if}}';
                    dep.replaceString = resource.spec.ref.tag;
                }
                else {
                    dep.skipReason = 'unversioned-reference';
                }
                deps.push(dep);
                break;
            }
        }
    }
    return deps;
}
function extractPackageFile(content, packageFile) {
    const manifest = readManifest(content, packageFile);
    if (!manifest) {
        return null;
    }
    const helmRepositories = [];
    if (manifest.kind === 'resource') {
        for (const resource of manifest.resources) {
            if (resource.kind === 'HelmRepository') {
                helmRepositories.push(resource);
            }
        }
    }
    let deps = null;
    switch (manifest.kind) {
        case 'system':
            deps = resolveSystemManifest(manifest);
            break;
        case 'resource': {
            deps = resolveResourceManifest(manifest, helmRepositories);
            break;
        }
    }
    return deps?.length ? { deps } : null;
}
exports.extractPackageFile = extractPackageFile;
async function extractAllPackageFiles(_config, packageFiles) {
    const manifests = [];
    const results = [];
    for (const file of packageFiles) {
        const content = await (0, fs_1.readLocalFile)(file, 'utf8');
        // TODO #22198
        const manifest = readManifest(content, file);
        if (manifest) {
            manifests.push(manifest);
        }
    }
    const helmRepositories = [];
    for (const manifest of manifests) {
        if (manifest.kind === 'resource') {
            for (const resource of manifest.resources) {
                if (resource.kind === 'HelmRepository') {
                    helmRepositories.push(resource);
                }
            }
        }
    }
    for (const manifest of manifests) {
        let deps = null;
        switch (manifest.kind) {
            case 'system':
                deps = resolveSystemManifest(manifest);
                break;
            case 'resource': {
                deps = resolveResourceManifest(manifest, helmRepositories);
                break;
            }
        }
        if (deps?.length) {
            results.push({
                packageFile: manifest.file,
                deps,
            });
        }
    }
    return results.length ? results : null;
}
exports.extractAllPackageFiles = extractAllPackageFiles;
//# sourceMappingURL=extract.js.map