laconic-deployer-frontend/apps/backend/src/utils.ts

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}`
)
}
}
}