161 lines
3.7 KiB
TypeScript
161 lines
3.7 KiB
TypeScript
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<Config>(
|
|
path.join(__dirname, '../environments/local.toml')
|
|
)
|
|
}
|
|
|
|
const _getConfig = async <ConfigType>(
|
|
configFile: string
|
|
): Promise<ConfigType> => {
|
|
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<boolean> => {
|
|
try {
|
|
await fs.access(filePath, fs.constants.F_OK)
|
|
return true
|
|
} catch (err) {
|
|
log(err)
|
|
return false
|
|
}
|
|
}
|
|
|
|
export const getEntities = async (filePath: string): Promise<any> => {
|
|
const entitiesData = await fs.readFile(filePath, 'utf-8')
|
|
const entities = JSON.parse(entitiesData)
|
|
return entities
|
|
}
|
|
|
|
export const loadAndSaveData = async <Entity extends ObjectLiteral>(
|
|
entityType: EntityTarget<Entity>,
|
|
dataSource: DataSource,
|
|
entities: any,
|
|
relations?: any | undefined
|
|
): Promise<Entity[]> => {
|
|
const entityRepository = dataSource.getRepository(entityType)
|
|
|
|
const savedEntity: Entity[] = []
|
|
|
|
for (const entityData of entities) {
|
|
let entity = entityRepository.create(entityData as DeepPartial<Entity>)
|
|
|
|
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<void> =>
|
|
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<any>
|
|
): Promise<any> => {
|
|
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}`
|
|
)
|
|
}
|
|
}
|
|
}
|