Plugins: Add auto-update functionality (#3487)

* Plugins: Add auto-update functionality

* Check if directory is a git repo

* Display message if any plugins were loaded
This commit is contained in:
Cohee
2025-02-16 20:55:53 +02:00
committed by GitHub
parent 4d323ec76f
commit 362470da18
3 changed files with 83 additions and 2 deletions

View File

@ -210,3 +210,5 @@ claude:
cachingAtDepth: -1
# -- SERVER PLUGIN CONFIGURATION --
enableServerPlugins: false
# Attempt to automatically update server plugins on startup
enableServerPluginsAutoUpdate: true

View File

@ -48,6 +48,13 @@ async function updatePlugins() {
console.log(`Updating plugin ${color.green(directory)}...`);
const pluginPath = path.join(pluginsPath, directory);
const pluginRepo = git(pluginPath);
const isRepo = await pluginRepo.checkIsRepo();
if (!isRepo) {
console.log(`Directory ${color.yellow(directory)} is not a Git repository`);
continue;
}
await pluginRepo.fetch();
const commitHash = await pluginRepo.revparse(['HEAD']);
const trackingBranch = await pluginRepo.revparse(['--abbrev-ref', '@{u}']);

View File

@ -3,8 +3,12 @@ import path from 'node:path';
import url from 'node:url';
import express from 'express';
import { getConfigValue } from './util.js';
const enableServerPlugins = getConfigValue('enableServerPlugins', false);
import { default as git } from 'simple-git';
import { sync as commandExistsSync } from 'command-exists';
import { getConfigValue, color } from './util.js';
const enableServerPlugins = !!getConfigValue('enableServerPlugins', false);
const enableServerPluginsAutoUpdate = !!getConfigValue('enableServerPluginsAutoUpdate', true);
/**
* Map of loaded plugins.
@ -54,6 +58,8 @@ export async function loadPlugins(app, pluginsPath) {
return emptyFn;
}
await updatePlugins(pluginsPath);
for (const file of files) {
const pluginFilePath = path.join(pluginsPath, file);
@ -70,6 +76,10 @@ export async function loadPlugins(app, pluginsPath) {
await loadFromFile(app, pluginFilePath, exitHooks);
}
if (loadedPlugins.size > 0) {
console.log(`${loadedPlugins.size} server plugin(s) are currently loaded. Make sure you know exactly what they do, and only install plugins from trusted sources!`);
}
// Call all plugin "exit" functions at once and wait for them to finish
return () => Promise.all(exitHooks.map(exitFn => exitFn()));
}
@ -214,3 +224,65 @@ async function initPlugin(app, plugin, exitHooks) {
return true;
}
/**
* Automatically update all git plugins in the ./plugins directory
* @param {string} pluginsPath Path to plugins directory
*/
async function updatePlugins(pluginsPath) {
if (!enableServerPluginsAutoUpdate) {
return;
}
const directories = fs.readdirSync(pluginsPath)
.filter(file => !file.startsWith('.'))
.filter(file => fs.statSync(path.join(pluginsPath, file)).isDirectory());
if (directories.length === 0) {
return;
}
console.log(color.blue('Auto-updating server plugins... Set'), color.yellow('enableServerPluginsAutoUpdate: false'), color.blue('in config.yaml to disable this feature.'));
if (!commandExistsSync('git')) {
console.error(color.red('Git is not installed. Please install Git to enable auto-updating of server plugins.'));
return;
}
let pluginsToUpdate = 0;
for (const directory of directories) {
try {
const pluginPath = path.join(pluginsPath, directory);
const pluginRepo = git(pluginPath);
const isRepo = await pluginRepo.checkIsRepo();
if (!isRepo) {
continue;
}
await pluginRepo.fetch();
const commitHash = await pluginRepo.revparse(['HEAD']);
const trackingBranch = await pluginRepo.revparse(['--abbrev-ref', '@{u}']);
const log = await pluginRepo.log({
from: commitHash,
to: trackingBranch,
});
if (log.total === 0) {
continue;
}
pluginsToUpdate++;
await pluginRepo.pull();
const latestCommit = await pluginRepo.revparse(['HEAD']);
console.log(`Plugin ${color.green(directory)} updated to commit ${color.cyan(latestCommit)}`);
} catch (error) {
console.error(color.red(`Failed to update plugin ${directory}: ${error.message}`));
}
}
if (pluginsToUpdate === 0) {
console.log('All plugins are up to date.');
}
}