UNPKG

@hashgraph/solo

Version:

An opinionated CLI tool to deploy and manage private Hedera Networks.

188 lines 9.77 kB
// SPDX-License-Identifier: Apache-2.0 var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; var __metadata = (this && this.__metadata) || function (k, v) { if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); }; var __param = (this && this.__param) || function (paramIndex, decorator) { return function (target, key) { decorator(target, key, paramIndex); } }; import { inject, injectable } from 'tsyringe-neo'; import { InjectTokens } from '../../../../core/dependency-injection/inject-tokens.js'; import { LocalConfigSource } from '../../../../data/configuration/impl/local-config-source.js'; import { YamlFileStorageBackend } from '../../../../data/backend/impl/yaml-file-storage-backend.js'; import { patchInject } from '../../../../core/dependency-injection/container-helper.js'; import { ClassToObjectMapper } from '../../../../data/mapper/impl/class-to-object-mapper.js'; import { ConfigKeyFormatter } from '../../../../data/key/config-key-formatter.js'; import { LocalConfigSchemaDefinition } from '../../../../data/schema/migration/impl/local/local-config-schema-definition.js'; import { LocalConfigSchema } from '../../../../data/schema/model/local/local-config-schema.js'; import { RefreshLocalConfigSourceError } from '../../../errors/refresh-local-config-source-error.js'; import { WriteLocalConfigFileError } from '../../../errors/write-local-config-file-error.js'; import { PathEx } from '../../../utils/path-ex.js'; import fs, { existsSync, mkdirSync } from 'node:fs'; import { LocalConfig } from './local-config.js'; import path from 'node:path'; import { Templates } from '../../../../core/templates.js'; import { Flags as flags } from '../../../../commands/flags.js'; let LocalConfigRuntimeState = class LocalConfigRuntimeState { basePath; fileName; configManager; source; backend; objectMapper; isLoaded = false; _localConfig; constructor(basePath, fileName, configManager) { this.basePath = basePath; this.fileName = fileName; this.configManager = configManager; this.fileName = patchInject(fileName, InjectTokens.LocalConfigFileName, this.constructor.name); this.basePath = patchInject(basePath, InjectTokens.HomeDirectory, this.constructor.name); this.configManager = patchInject(configManager, InjectTokens.ConfigManager, this.constructor.name); this.backend = new YamlFileStorageBackend(this.basePath); this.objectMapper = new ClassToObjectMapper(ConfigKeyFormatter.instance()); this.source = new LocalConfigSource(fileName, new LocalConfigSchemaDefinition(this.objectMapper), this.objectMapper, this.backend, LocalConfigSchema.EMPTY); } get configuration() { if (!this.isLoaded) { throw new Error('configuration: Local configuration is not loaded yet. Please call load() first.'); } return this._localConfig; } // Loads the source data and writes it back in case of migrations. async load() { // TODO this needs to be a migration, not a load // check if config from an old version exists under the cache directory const oldConfigPath = PathEx.join(this.basePath, 'cache'); const oldConfigFile = PathEx.join(oldConfigPath, this.fileName); const oldConfigFileExists = existsSync(oldConfigFile); if (this.configFileExists() && oldConfigFileExists) { // if both files exist, remove the old one fs.rmSync(oldConfigFile); } else if (existsSync(oldConfigFile)) { // if only the old file exists, copy it to the new location mkdirSync(this.basePath, { recursive: true }); fs.copyFileSync(oldConfigFile, PathEx.join(this.basePath, this.fileName)); fs.rmSync(oldConfigFile); } this.refresh(); if (!this.configFileExists()) { return await this.persist(); } try { await this.source.refresh(); this.refresh(); } catch (error) { throw new RefreshLocalConfigSourceError('Failed to refresh local config source', error); } await this.persist(); await this.migrateCacheDirectories(); this.isLoaded = true; } /** * Migrates the cache directories to the new structure. * It will look for directories in the format 'v0.58/staging/v0.58.10' and move them to current staging directory. */ async migrateCacheDirectories() { if (!this.isLoaded) { throw new Error('migrateCacheDirectories: Local configuration is not loaded yet. Please call load() first.'); } const cacheDirectory = PathEx.join(this.basePath, 'cache').toString(); const releaseTag = this.configManager.getFlag(flags.releaseTag); const currentStagingDirectory = Templates.renderStagingDir(cacheDirectory, releaseTag); if (fs.existsSync(currentStagingDirectory)) { return; } // migrate the staging directory if it exists const foundStagingDirectory = await this.findMatchingSoloCacheDirectories(PathEx.join(this.basePath, 'cache').toString()); if (foundStagingDirectory && foundStagingDirectory.length > 0) { for (const stagingDirectory of foundStagingDirectory) { // Guard against accidental self-copy when the discovered path already points to // the current release staging directory. if (stagingDirectory === currentStagingDirectory) { continue; } // Keep source staging directories intact to avoid deleting another command's active staging path // when multiple commands run concurrently (for example one-shot parallel subcommands). fs.cpSync(stagingDirectory, currentStagingDirectory, { recursive: true, force: true }); } } } async findMatchingSoloCacheDirectories(baseDirectory) { if (!this.isLoaded) { throw new Error('findMatchingSoloCacheDirectories: Local configuration is not loaded yet. Please call load() first.'); } // Regex to match directory names like 'v0.58' or 'v0.60' // This will capture the version number. const versionDirectionRegex = /^v(\d+\.\d+)$/; // Regex to match the full path structure like 'v0.58/staging/v0.58.10' // This captures the major.minor version and the patch version. const fullPathRegex = /^v(\d+\.\d+)\/staging\/v(\d+\.\d+\.\d+)$/; const matchingDirectories = []; try { // 1. Read the contents of the baseCacheDir (e.g., '.solo/cache/') const versionDirectories = fs.readdirSync(baseDirectory); for (const versionDirectory of versionDirectories) { const versionMatch = versionDirectory.match(versionDirectionRegex); if (versionMatch) { // If the version directory matches (e.g., 'v0.58') const fullVersionPath = PathEx.join(baseDirectory, versionDirectory, 'staging'); // Check if 'staging' directory exists within the version directory if (fs.existsSync(fullVersionPath)) { // Read the contents of the 'staging' directory const stagingContents = fs.readdirSync(fullVersionPath); for (const stagingItem of stagingContents) { const fullItemPath = PathEx.join(fullVersionPath, stagingItem); const relativeItemPath = path.relative(baseDirectory, fullItemPath); // Get path relative to baseCacheDir // Check if the full relative path matches the desired pattern if (fullPathRegex.test(relativeItemPath) && fs.existsSync(fullItemPath)) { matchingDirectories.push(fullItemPath); } } } } } } catch { // The Directory isn't found or any other error return undefined; } return matchingDirectories; } async persist() { try { await this.source.persist(); this.isLoaded = true; } catch (error) { throw new WriteLocalConfigFileError('Failed to write local config file', error); } } refresh() { this._localConfig = new LocalConfig(this.source.modelData); } configFileExists() { try { return fs.existsSync(PathEx.join(this.basePath, this.fileName)); } catch { return false; } } }; LocalConfigRuntimeState = __decorate([ injectable(), __param(0, inject(InjectTokens.HomeDirectory)), __param(1, inject(InjectTokens.LocalConfigFileName)), __param(2, inject(InjectTokens.ConfigManager)), __metadata("design:paramtypes", [String, String, Function]) ], LocalConfigRuntimeState); export { LocalConfigRuntimeState }; //# sourceMappingURL=local-config-runtime-state.js.map