Allow installing plugins declaratively
This commit is contained in:
parent
1e3a5b25c3
commit
97b06f2455
|
@ -20,6 +20,7 @@ import { decachePlugin } from '@server/helpers/decache.js'
|
|||
import { ApplicationModel } from '@server/models/application/application.js'
|
||||
import { MOAuthTokenUser, MUser } from '@server/types/models/index.js'
|
||||
import { isLibraryCodeValid, isPackageJSONValid } from '../../helpers/custom-validators/plugins.js'
|
||||
import { execShell } from '../../helpers/core-utils.js'
|
||||
import { logger } from '../../helpers/logger.js'
|
||||
import { CONFIG } from '../../initializers/config.js'
|
||||
import { PLUGIN_GLOBAL_CSS_PATH } from '../../initializers/constants.js'
|
||||
|
@ -334,13 +335,93 @@ export class PluginManager implements ServerHook {
|
|||
|
||||
// ###################### Installation ######################
|
||||
|
||||
async installDeclarativePlugins () {
|
||||
const pluginDirectory = CONFIG.STORAGE.PLUGINS_DIR
|
||||
let declaredPlugins
|
||||
try {
|
||||
declaredPlugins = await readJSON(join(pluginDirectory, "declarative_plugins.json"));
|
||||
}
|
||||
catch (err) {
|
||||
logger.info("No declarative plugins file found.")
|
||||
return
|
||||
}
|
||||
|
||||
const existingDeclaredPlugins: PluginModel[] = await PluginModel.listDeclarativePluginsAndThemes()
|
||||
|
||||
for (const plugin of existingDeclaredPlugins) {
|
||||
const npmName = PluginModel.buildNpmName(plugin.name, plugin.type)
|
||||
if (!(npmName in declaredPlugins)) {
|
||||
logger.info("Removing previously declared plugin %s", npmName)
|
||||
await this.uninstall({npmName: npmName, unregister: false})
|
||||
}
|
||||
else {
|
||||
logger.info("Keeping still-declared plugin %s.", npmName)
|
||||
}
|
||||
}
|
||||
|
||||
for (const npmName of Object.keys(declaredPlugins)) {
|
||||
const {
|
||||
pluginPath = null,
|
||||
version = null,
|
||||
extraArgs = null,
|
||||
preInstall = null,
|
||||
postInstall = null
|
||||
} = declaredPlugins[npmName];
|
||||
|
||||
if (preInstall != null) {
|
||||
logger.info("Running pre-install command for plugin %s.", npmName)
|
||||
try {
|
||||
await execShell(preInstall, { cwd: pluginDirectory })
|
||||
} catch (result) {
|
||||
logger.error("Cannot exec pre-install command.", { preInstall, err: result.err, stderr: result.stderr })
|
||||
|
||||
throw result.err
|
||||
}
|
||||
}
|
||||
|
||||
if (pluginPath != null) {
|
||||
logger.info("Installing declared plugin %s from disk (path %s).", npmName, pluginPath)
|
||||
await this.install({
|
||||
toInstall: pluginPath,
|
||||
fromDisk: true,
|
||||
declarative: true,
|
||||
extraArgs: extraArgs,
|
||||
})
|
||||
}
|
||||
else if (version != null) {
|
||||
logger.info("Installing declared plugin %s (version %s) from npm.", npmName, version)
|
||||
await this.install({
|
||||
toInstall: npmName,
|
||||
version: version,
|
||||
declarative: true,
|
||||
extraArgs: extraArgs,
|
||||
})
|
||||
}
|
||||
|
||||
if (postInstall != null) {
|
||||
logger.info("Running post-install command for plugin %s.", npmName)
|
||||
try {
|
||||
await execShell(postInstall, { cwd: pluginDirectory })
|
||||
} catch (result) {
|
||||
logger.error("Cannot exec post-install command.", { postInstall, err: result.err, stderr: result.stderr })
|
||||
|
||||
throw result.err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.sortHooksByPriority()
|
||||
}
|
||||
|
||||
async install (options: {
|
||||
toInstall: string
|
||||
version?: string
|
||||
fromDisk?: boolean // default false
|
||||
register?: boolean // default true
|
||||
declarative?: boolean // default false
|
||||
extraArgs?: string // default empty
|
||||
}) {
|
||||
const { toInstall, version, fromDisk = false, register = true } = options
|
||||
const { toInstall, version, fromDisk = false, register = true, declarative = false, extraArgs = null } = options
|
||||
|
||||
let plugin: PluginModel
|
||||
let npmName: string
|
||||
|
@ -349,8 +430,8 @@ export class PluginManager implements ServerHook {
|
|||
|
||||
try {
|
||||
fromDisk
|
||||
? await installNpmPluginFromDisk(toInstall)
|
||||
: await installNpmPlugin(toInstall, version)
|
||||
? await installNpmPluginFromDisk(toInstall, extraArgs)
|
||||
: await installNpmPlugin(toInstall, version, extraArgs)
|
||||
|
||||
npmName = fromDisk ? basename(toInstall) : toInstall
|
||||
const pluginType = PluginModel.getTypeFromNpmName(npmName)
|
||||
|
@ -368,6 +449,7 @@ export class PluginManager implements ServerHook {
|
|||
version: packageJSON.version,
|
||||
enabled: true,
|
||||
uninstalled: false,
|
||||
declarative: declarative,
|
||||
peertubeEngine: packageJSON.engine.peertube
|
||||
}, { returning: true })
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ import { logger } from '../../helpers/logger.js'
|
|||
import { CONFIG } from '../../initializers/config.js'
|
||||
import { getLatestPluginVersion } from './plugin-index.js'
|
||||
|
||||
async function installNpmPlugin (npmName: string, versionArg?: string) {
|
||||
async function installNpmPlugin (npmName: string, versionArg?: string, extraArgs?: string) {
|
||||
// Security check
|
||||
checkNpmPluginNameOrThrow(npmName)
|
||||
if (versionArg) checkPluginVersionOrThrow(versionArg)
|
||||
|
@ -15,13 +15,15 @@ async function installNpmPlugin (npmName: string, versionArg?: string) {
|
|||
|
||||
let toInstall = npmName
|
||||
if (version) toInstall += `@${version}`
|
||||
if (extraArgs) toInstall += ` ${extraArgs}`
|
||||
|
||||
const { stdout } = await execYarn('add ' + toInstall)
|
||||
|
||||
logger.debug('Added a yarn package.', { yarnStdout: stdout })
|
||||
}
|
||||
|
||||
async function installNpmPluginFromDisk (path: string) {
|
||||
async function installNpmPluginFromDisk (path: string, extraArgs?: string) {
|
||||
if (extraArgs) path += ` ${extraArgs}`
|
||||
await execYarn('add file:' + path)
|
||||
}
|
||||
|
||||
|
|
|
@ -86,6 +86,10 @@ export class PluginModel extends SequelizeModel<PluginModel> {
|
|||
@Column(DataType.JSONB)
|
||||
storage: any
|
||||
|
||||
@AllowNull(true)
|
||||
@Column
|
||||
declarative: boolean
|
||||
|
||||
@CreatedAt
|
||||
createdAt: Date
|
||||
|
||||
|
@ -103,6 +107,17 @@ export class PluginModel extends SequelizeModel<PluginModel> {
|
|||
return PluginModel.findAll(query)
|
||||
}
|
||||
|
||||
static listDeclarativePluginsAndThemes (): Promise<MPlugin[]> {
|
||||
const query = {
|
||||
where: {
|
||||
declarative: true,
|
||||
uninstalled: false
|
||||
}
|
||||
}
|
||||
|
||||
return PluginModel.findAll(query)
|
||||
}
|
||||
|
||||
static loadByNpmName (npmName: string): Promise<MPlugin> {
|
||||
const name = this.normalizePluginName(npmName)
|
||||
const type = this.getTypeFromNpmName(npmName)
|
||||
|
|
|
@ -352,6 +352,7 @@ async function startApplication () {
|
|||
server.listen(port, hostname, async () => {
|
||||
if (cliOptions.plugins) {
|
||||
try {
|
||||
await PluginManager.Instance.installDeclarativePlugins()
|
||||
await PluginManager.Instance.rebuildNativePluginsIfNeeded()
|
||||
|
||||
await PluginManager.Instance.registerPluginsAndThemes()
|
||||
|
|
Loading…
Reference in New Issue