@hashgraph/solo
Version:
An opinionated CLI tool to deploy and manage private Hedera Networks.
188 lines • 9.77 kB
JavaScript
// 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