@hashgraph/solo
Version:
An opinionated CLI tool to deploy and manage private Hedera Networks.
191 lines • 9.01 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); }
};
var CraneDependencyManager_1;
import * as constants from '../constants.js';
import * as version from '../../../version.js';
import { inject, injectable } from 'tsyringe-neo';
import { patchInject } from '../dependency-injection/container-helper.js';
import { InjectTokens } from '../dependency-injection/inject-tokens.js';
import { BaseDependencyManager } from './base-dependency-manager.js';
import { PackageDownloader } from '../package-downloader.js';
import { Zippy } from '../zippy.js';
import { PathEx } from '../../business/utils/path-ex.js';
import util from 'node:util';
import fs from 'node:fs';
import { SoloError } from '../errors/solo-error.js';
import { OperatingSystem } from '../../business/utils/operating-system.js';
const CRANE_RELEASES_LIST_URL = 'https://api.github.com/repos/google/go-containerregistry/releases';
let CraneDependencyManager = CraneDependencyManager_1 = class CraneDependencyManager extends BaseDependencyManager {
zippy;
checksum;
releaseBaseUrl;
artifactFileName;
artifactVersion;
constructor(downloader, zippy, installationDirectory, osArch, craneVersion) {
super(patchInject(downloader, InjectTokens.PackageDownloader, CraneDependencyManager_1.name), patchInject(installationDirectory, InjectTokens.CraneInstallationDirectory, CraneDependencyManager_1.name), patchInject(osArch, InjectTokens.OsArch, CraneDependencyManager_1.name), patchInject(craneVersion, InjectTokens.CraneVersion, CraneDependencyManager_1.name) || version.CRANE_VERSION, constants.CRANE, '');
this.zippy = zippy;
this.zippy = patchInject(this.zippy, InjectTokens.Zippy, CraneDependencyManager_1.name);
}
/**
* This class uses GitHub release discovery in preInstall(), so the artifact name
* is determined dynamically from the matching asset.
*/
getArtifactName() {
return util.format(this.artifactFileName, this.getRequiredVersion(), OperatingSystem.getFormattedPlatform(), this.osArch);
}
async getVersion(executableWithPath) {
const maxAttempts = 3;
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
try {
const output = await this.run(`"${executableWithPath}" version`);
if (output.length > 0) {
const joined = output.join('\n').trim();
const match = joined.match(/(\d+\.\d+\.\d+)/);
if (match && match[1]) {
return match[1];
}
}
}
catch (error) {
throw new SoloError('Failed to check crane version', error);
}
}
throw new SoloError('Failed to check crane version');
}
/**
* Match the release asset name for the current platform and architecture.
*
* Release examples observed publicly include:
* - go-containerregistry_Linux_x86_64.tar.gz
* - go-containerregistry_darwin_arm64.tar.gz
*
* We use a case-insensitive regex because historical naming appears to vary in capitalization.
*/
getAssetPattern() {
const arch = this.getArch();
let normalizedArch;
if (arch === 'amd64') {
normalizedArch = '(amd64|x86_64)';
}
else if (arch === 'arm64') {
normalizedArch = 'arm64';
}
else {
normalizedArch = arch;
}
let platformPattern;
if (OperatingSystem.isWin32()) {
platformPattern = 'windows';
}
else if (OperatingSystem.isDarwin()) {
platformPattern = 'darwin';
}
else if (OperatingSystem.isLinux()) {
platformPattern = 'linux';
}
else {
throw new SoloError(`Unsupported platform: ${OperatingSystem.getPlatform()}`);
}
// Prefer archives; support both tar.gz and zip if upstream ever varies by platform/version.
return new RegExp(String.raw `go-containerregistry_${platformPattern}_${normalizedArch}\.(tar\.gz|zip)$`, 'i');
}
async fetchReleaseInfo(tagName) {
try {
const response = await fetch(CRANE_RELEASES_LIST_URL, {
method: 'GET',
headers: {
'User-Agent': constants.SOLO_USER_AGENT_HEADER,
Accept: 'application/vnd.github.v3+json',
},
});
if (!response.ok) {
throw new SoloError(`GitHub API request failed with status ${response.status}`);
}
const releases = await response.json();
if (!releases || releases.length === 0) {
throw new SoloError('No releases found');
}
const release = releases.find((release_) => release_.tag_name === tagName);
if (!release) {
throw new SoloError(`Release not found for tag ${tagName}`);
}
const versionOnly = release.tag_name.replace(/^v/, '');
const assetPattern = this.getAssetPattern();
const matchingAsset = release.assets.find((asset) => assetPattern.test(asset.name) || assetPattern.test(asset.browser_download_url));
if (!matchingAsset) {
throw new SoloError(`No matching crane asset found for ${OperatingSystem.getFormattedPlatform()}-${this.getArch()}`);
}
const checksum = matchingAsset.digest
? matchingAsset.digest.replace('sha256:', '')
: '0000000000000000000000000000000000000000000000000000000000000000';
const downloadUrl = matchingAsset.browser_download_url.slice(0, Math.max(0, matchingAsset.browser_download_url.lastIndexOf('/')));
return {
downloadUrl,
assetName: matchingAsset.name,
checksum,
version: versionOnly,
};
}
catch (error) {
if (error instanceof SoloError) {
throw error;
}
throw new SoloError('Failed to parse GitHub API response', error);
}
}
async preInstall() {
const releaseInfo = await this.fetchReleaseInfo(version.CRANE_VERSION);
this.checksum = releaseInfo.checksum;
this.releaseBaseUrl = releaseInfo.downloadUrl;
this.artifactFileName = releaseInfo.assetName;
this.artifactVersion = releaseInfo.version;
}
getDownloadURL() {
return `${this.releaseBaseUrl}/${this.artifactFileName}`;
}
getChecksumURL() {
return this.checksum;
}
async processDownloadedPackage(packageFilePath, temporaryDirectory) {
if (packageFilePath.endsWith('.zip')) {
this.zippy.unzip(packageFilePath, temporaryDirectory);
}
else {
this.zippy.untar(packageFilePath, temporaryDirectory);
}
const executableName = OperatingSystem.isWin32() ? 'crane.exe' : 'crane';
const candidatePaths = [
PathEx.join(temporaryDirectory, executableName),
PathEx.join(temporaryDirectory, 'crane', executableName),
PathEx.join(temporaryDirectory, 'go-containerregistry', executableName),
];
const matchedPath = candidatePaths.find((candidate) => fs.existsSync(candidate));
if (!matchedPath) {
throw new SoloError(`Crane executable not found in extracted archive: ${temporaryDirectory}`);
}
return [matchedPath];
}
};
CraneDependencyManager = CraneDependencyManager_1 = __decorate([
injectable(),
__param(0, inject(InjectTokens.PackageDownloader)),
__param(1, inject(InjectTokens.Zippy)),
__param(2, inject(InjectTokens.CraneInstallationDirectory)),
__param(3, inject(InjectTokens.OsArch)),
__param(4, inject(InjectTokens.CraneVersion)),
__metadata("design:paramtypes", [PackageDownloader,
Zippy, String, String, String])
], CraneDependencyManager);
export { CraneDependencyManager };
//# sourceMappingURL=crane-dependency-manager.js.map