234 lines
6.6 KiB
JavaScript

import Logger from "../logger"
import fs from "node:fs"
import DB from "../db"
import ManifestReader from "../manifest/reader"
import ManifestVM from "../manifest/vm"
import GenericSteps from "../generic_steps"
import Apply from "../handlers/apply"
const BaseLog = Logger.child({ service: "INSTALLER" })
export default async function install(manifest, options = {}) {
let id = null
let abortController = new AbortController()
try {
BaseLog.info(`Invoking new installation...`)
BaseLog.info(`Fetching manifest [${manifest}]`)
const ManifestRead = await ManifestReader(manifest)
manifest = await ManifestVM(ManifestRead.code)
id = manifest.constructor.id
globalThis.relic_core.tasks.push({
type: "install",
id: id,
abortController: abortController,
})
const Log = BaseLog.child({ service: `INSTALLER|${id}` })
Log.info(`Creating install path [${manifest.install_path}]`)
if (abortController.signal.aborted) {
return false
}
if (fs.existsSync(manifest.install_path)) {
Log.info(`Package already exists, removing...`)
await fs.rmSync(manifest.install_path, { recursive: true })
}
await fs.mkdirSync(manifest.install_path, { recursive: true })
Log.info(`Initializing manifest...`)
if (abortController.signal.aborted) {
return false
}
if (typeof manifest.initialize === "function") {
await manifest.initialize()
}
Log.info(`Appending to db...`)
if (abortController.signal.aborted) {
return false
}
let pkg = DB.defaultPackageState({
...manifest.constructor,
id: id,
name: manifest.constructor.pkg_name,
version: manifest.constructor.version,
install_path: manifest.install_path,
description: manifest.constructor.description,
license: manifest.constructor.license,
last_status: "installing",
remote_manifest: ManifestRead.remote_manifest,
local_manifest: ManifestRead.local_manifest,
executable: !!manifest.execute
})
await DB.writePackage(pkg)
global._relic_eventBus.emit("pkg:new", pkg)
if (abortController.signal.aborted) {
return false
}
if (manifest.configuration) {
Log.info(`Applying default config to package...`)
pkg.config = Object.entries(manifest.configuration).reduce((acc, [key, value]) => {
acc[key] = value.default
return acc
}, {})
}
if (abortController.signal.aborted) {
return false
}
if (typeof manifest.beforeInstall === "function") {
Log.info(`Executing beforeInstall hook...`)
global._relic_eventBus.emit(`pkg:update:state`, {
id: pkg.id,
status_text: `Performing beforeInstall hook...`,
})
await manifest.beforeInstall(pkg)
}
if (abortController.signal.aborted) {
return false
}
if (Array.isArray(manifest.installSteps) && !options.noInstallSteps) {
Log.info(`Executing generic install steps...`)
global._relic_eventBus.emit(`pkg:update:state`, {
id: pkg.id,
status_text: `Performing generic install steps...`,
})
await GenericSteps(pkg, manifest.installSteps, Log, abortController)
}
if (abortController.signal.aborted) {
return false
}
if (typeof manifest.afterInstall === "function") {
Log.info(`Executing afterInstall hook...`)
global._relic_eventBus.emit(`pkg:update:state`, {
id: pkg.id,
status_text: `Performing afterInstall hook...`,
})
await manifest.afterInstall(pkg)
}
global._relic_eventBus.emit(`pkg:update:state`, {
id: pkg.id,
status_text: `Finishing up...`,
})
Log.info(`Copying manifest to the final location...`)
const finalPath = `${manifest.install_path}/.rmanifest`
if (abortController.signal.aborted) {
return false
}
if (fs.existsSync(finalPath)) {
await fs.promises.unlink(finalPath)
}
await fs.promises.copyFile(ManifestRead.local_manifest, finalPath)
if (ManifestRead.is_catched) {
Log.info(`Removing cache manifest...`)
await fs.promises.unlink(ManifestRead.local_manifest)
}
pkg.local_manifest = finalPath
pkg.last_status = "loading"
pkg.installed_at = Date.now()
await DB.writePackage(pkg)
if (abortController.signal.aborted) {
return false
}
if (manifest.patches) {
const defaultPatches = manifest.patches.filter((patch) => patch.default)
if (defaultPatches.length > 0) {
Log.info(`Applying default patches...`)
global._relic_eventBus.emit(`pkg:update:state`, {
id: pkg.id,
status_text: `Applying default patches...`,
})
pkg = await Apply(id, {
patches: Object.fromEntries(defaultPatches.map((patch) => [patch.id, true])),
})
}
}
pkg.last_status = "installed"
if (abortController.signal.aborted) {
return false
}
await DB.writePackage(pkg)
globalThis.relic_core.tasks.filter((task) => task.id !== id)
global._relic_eventBus.emit(`pkg:update:state`, {
...pkg,
id: pkg.id,
last_status: "installed",
status_text: `Installation completed successfully`,
})
global._relic_eventBus.emit(`pkg:new:done`, pkg)
Log.info(`Package installed successfully!`)
return pkg
} catch (error) {
global._relic_eventBus.emit(`pkg:error`, {
id: id ?? manifest.constructor.id,
event: "install",
error,
})
global._relic_eventBus.emit(`pkg:update:state`, {
id: id ?? manifest.constructor.id,
last_status: "failed",
status_text: `Installation failed`,
})
globalThis.relic_core.tasks.filter((task) => task.id !== id)
BaseLog.error(`Error during installation of package [${id}] >`, error)
BaseLog.error(error.stack)
return null
}
}