"use strict";
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
// See LICENSE in the project root for license information.
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.BuildCacheConfiguration = void 0;
const node_crypto_1 = require("node:crypto");
const node_core_library_1 = require("@rushstack/node-core-library");
const FileSystemBuildCacheProvider_1 = require("../logic/buildCache/FileSystemBuildCacheProvider");
const RushConstants_1 = require("../logic/RushConstants");
const RushUserConfiguration_1 = require("./RushUserConfiguration");
const EnvironmentConfiguration_1 = require("./EnvironmentConfiguration");
const CacheEntryId_1 = require("../logic/buildCache/CacheEntryId");
const build_cache_schema_json_1 = __importDefault(require("../schemas/build-cache.schema.json"));
const BUILD_CACHE_JSON_SCHEMA = node_core_library_1.JsonSchema.fromLoadedObject(build_cache_schema_json_1.default);
/**
 * Use this class to load and save the "common/config/rush/build-cache.json" config file.
 * This file provides configuration options for cached project build output.
 * @beta
 */
class BuildCacheConfiguration {
    constructor({ getCacheEntryId, buildCacheJson, rushUserConfiguration, rushConfiguration, cloudCacheProvider }) {
        var _a;
        this.buildCacheEnabled = (_a = EnvironmentConfiguration_1.EnvironmentConfiguration.buildCacheEnabled) !== null && _a !== void 0 ? _a : buildCacheJson.buildCacheEnabled;
        this.cacheWriteEnabled =
            !!this.buildCacheEnabled && EnvironmentConfiguration_1.EnvironmentConfiguration.buildCacheWriteAllowed !== false;
        this.getCacheEntryId = getCacheEntryId;
        this.localCacheProvider = new FileSystemBuildCacheProvider_1.FileSystemBuildCacheProvider({
            rushUserConfiguration: rushUserConfiguration,
            rushConfiguration: rushConfiguration
        });
        this.cloudCacheProvider = cloudCacheProvider;
        this.cacheHashSalt = buildCacheJson.cacheHashSalt;
    }
    /**
     * Attempts to load the build-cache.json data from the standard file path `common/config/rush/build-cache.json`.
     * If the file has not been created yet, then undefined is returned.
     */
    static async tryLoadAsync(terminal, rushConfiguration, rushSession) {
        const { buildCacheConfiguration } = await BuildCacheConfiguration._tryLoadInternalAsync(terminal, rushConfiguration, rushSession);
        return buildCacheConfiguration;
    }
    /**
     * Loads the build-cache.json data from the standard file path `common/config/rush/build-cache.json`.
     * If the file has not been created yet, or if the feature is not enabled, then an error is reported.
     */
    static async loadAndRequireEnabledAsync(terminal, rushConfiguration, rushSession) {
        const { buildCacheConfiguration, jsonFilePath } = await BuildCacheConfiguration._tryLoadInternalAsync(terminal, rushConfiguration, rushSession);
        if (!buildCacheConfiguration) {
            terminal.writeErrorLine(`The build cache feature is not enabled. This config file is missing:\n` + jsonFilePath);
            terminal.writeLine(`\nThe Rush website documentation has instructions for enabling the build cache.`);
            throw new node_core_library_1.AlreadyReportedError();
        }
        if (!buildCacheConfiguration.buildCacheEnabled) {
            terminal.writeErrorLine(`The build cache feature is not enabled. You can enable it by editing this config file:\n` +
                jsonFilePath);
            throw new node_core_library_1.AlreadyReportedError();
        }
        return buildCacheConfiguration;
    }
    /**
     * Gets the absolute path to the build-cache.json file in the specified rush workspace.
     */
    static getBuildCacheConfigFilePath(rushConfiguration) {
        return `${rushConfiguration.commonRushConfigFolder}/${RushConstants_1.RushConstants.buildCacheFilename}`;
    }
    static async _tryLoadInternalAsync(terminal, rushConfiguration, rushSession) {
        const jsonFilePath = BuildCacheConfiguration.getBuildCacheConfigFilePath(rushConfiguration);
        const buildCacheConfiguration = await BuildCacheConfiguration._tryLoadAsync(jsonFilePath, terminal, rushConfiguration, rushSession);
        return { buildCacheConfiguration, jsonFilePath };
    }
    static async _tryLoadAsync(jsonFilePath, terminal, rushConfiguration, rushSession) {
        let buildCacheJson;
        const buildCacheOverrideJson = EnvironmentConfiguration_1.EnvironmentConfiguration.buildCacheOverrideJson;
        if (buildCacheOverrideJson) {
            buildCacheJson = node_core_library_1.JsonFile.parseString(buildCacheOverrideJson);
            BUILD_CACHE_JSON_SCHEMA.validateObject(buildCacheJson, `${EnvironmentConfiguration_1.EnvironmentVariableNames.RUSH_BUILD_CACHE_OVERRIDE_JSON} environment variable`);
        }
        else {
            const buildCacheOverrideJsonFilePath = EnvironmentConfiguration_1.EnvironmentConfiguration.buildCacheOverrideJsonFilePath;
            if (buildCacheOverrideJsonFilePath) {
                buildCacheJson = await node_core_library_1.JsonFile.loadAndValidateAsync(buildCacheOverrideJsonFilePath, BUILD_CACHE_JSON_SCHEMA);
            }
            else {
                try {
                    buildCacheJson = await node_core_library_1.JsonFile.loadAndValidateAsync(jsonFilePath, BUILD_CACHE_JSON_SCHEMA);
                }
                catch (e) {
                    if (!node_core_library_1.FileSystem.isNotExistError(e)) {
                        throw e;
                    }
                    else {
                        return undefined;
                    }
                }
            }
        }
        const rushUserConfiguration = await RushUserConfiguration_1.RushUserConfiguration.initializeAsync();
        let innerGetCacheEntryId;
        try {
            innerGetCacheEntryId = CacheEntryId_1.CacheEntryId.parsePattern(buildCacheJson.cacheEntryNamePattern);
        }
        catch (e) {
            terminal.writeErrorLine(`Error parsing cache entry name pattern "${buildCacheJson.cacheEntryNamePattern}": ${e}`);
            throw new node_core_library_1.AlreadyReportedError();
        }
        const { cacheHashSalt = '', cacheProvider } = buildCacheJson;
        const salt = `${RushConstants_1.RushConstants.buildCacheVersion}${cacheHashSalt ? `${RushConstants_1.RushConstants.hashDelimiter}${cacheHashSalt}` : ''}`;
        // Extend the cache entry id with to salt the hash
        // This facilitates forcing cache invalidation either when the build cache version changes (new version of Rush)
        // or when the user-side salt changes (need to purge bad cache entries, plugins including additional files)
        const getCacheEntryId = (options) => {
            const saltedHash = (0, node_crypto_1.createHash)('sha1')
                .update(salt)
                .update(options.projectStateHash)
                .digest('hex');
            return innerGetCacheEntryId({
                phaseName: options.phaseName,
                projectName: options.projectName,
                projectStateHash: saltedHash
            });
        };
        let cloudCacheProvider;
        // Don't configure a cloud cache provider if local-only
        if (cacheProvider !== 'local-only') {
            const cloudCacheProviderFactory = rushSession.getCloudBuildCacheProviderFactory(cacheProvider);
            if (!cloudCacheProviderFactory) {
                throw new Error(`Unexpected cache provider: ${cacheProvider}`);
            }
            cloudCacheProvider = await cloudCacheProviderFactory(buildCacheJson);
        }
        return new BuildCacheConfiguration({
            buildCacheJson,
            getCacheEntryId,
            rushConfiguration,
            rushUserConfiguration,
            rushSession,
            cloudCacheProvider
        });
    }
}
exports.BuildCacheConfiguration = BuildCacheConfiguration;
//# sourceMappingURL=BuildCacheConfiguration.js.map