import debug from 'debug' import fs from 'fs-extra' import assert from 'node:assert' import path from 'node:path' import type { Octokit } from 'octokit' import toml from 'toml' import type { DataSource, DeepPartial, EntityTarget, ObjectLiteral } from 'typeorm' import type { Config } from './config' interface PackageJSON { name: string description?: string homepage?: string license?: string author?: string | { [key: string]: unknown } version?: string [key: string]: unknown } const log = debug('snowball:utils') export async function getConfig() { return await _getConfig( path.join(__dirname, '../environments/local.toml') ) } const _getConfig = async ( configFile: string ): Promise => { const fileExists = await fs.pathExists(configFile) if (!fileExists) { throw new Error(`Config file not found: ${configFile}`) } const config = toml.parse(await fs.readFile(configFile, 'utf8')) log('config', JSON.stringify(config, null, 2)) return config } export const checkFileExists = async (filePath: string): Promise => { try { await fs.access(filePath, fs.constants.F_OK) return true } catch (err) { log(err) return false } } export const getEntities = async (filePath: string): Promise => { const entitiesData = await fs.readFile(filePath, 'utf-8') const entities = JSON.parse(entitiesData) return entities } export const loadAndSaveData = async ( entityType: EntityTarget, dataSource: DataSource, entities: any, relations?: any | undefined ): Promise => { const entityRepository = dataSource.getRepository(entityType) const savedEntity: Entity[] = [] for (const entityData of entities) { let entity = entityRepository.create(entityData as DeepPartial) if (relations) { for (const field in relations) { const valueIndex = `${field}Index` entity = { ...entity, [field]: relations[field][entityData[valueIndex]] } } } const dbEntity = await entityRepository.save(entity) savedEntity.push(dbEntity) } return savedEntity } export const sleep = async (ms: number): Promise => new Promise((resolve) => setTimeout(resolve, ms)) export const getRepoDetails = async ( octokit: Octokit, repository: string, commitHash: string | undefined ): Promise<{ repo: string packageJSON: PackageJSON repoUrl: string }> => { const [owner, repo] = repository.split('/') const { data: packageJSONData } = await octokit.rest.repos.getContent({ owner, repo, path: 'package.json', ref: commitHash }) if (!packageJSONData) { throw new Error('Package.json file not found') } assert(!Array.isArray(packageJSONData) && packageJSONData.type === 'file') const packageJSON: PackageJSON = JSON.parse(atob(packageJSONData.content)) assert(packageJSON.name, "name field doesn't exist in package.json") const repoUrl = ( await octokit.rest.repos.get({ owner, repo }) ).data.html_url return { repo, packageJSON, repoUrl } } // Wrapper method for registry txs to retry once if 'account sequence mismatch' occurs export const registryTransactionWithRetry = async ( txMethod: () => Promise ): Promise => { try { return await txMethod() } catch (error: any) { if (!error.message.includes('account sequence mismatch')) { throw error } console.error( 'Transaction failed due to account sequence mismatch. Retrying...' ) try { return await txMethod() } catch (retryError: any) { throw new Error( `Transaction failed again after retry: ${retryError.message}` ) } } }