rewrite to be more dnf-like, with config editing
This commit is contained in:
parent
81adb51f5b
commit
99f4220a67
@ -1,6 +1,6 @@
|
||||
# A tool to manage plugins on play.malloc.gg
|
||||
# Minecraft Plugin Sync
|
||||
|
||||
plugin-sync is a tool used to maintain the various servers and their plugins on
|
||||
mpm is a tool used to maintain the various servers and their plugins on
|
||||
play.malloc.gg.
|
||||
|
||||
Malloc runs a heterogenous environment of server versions, depending on what
|
||||
@ -12,7 +12,7 @@ tool to help out the workload.
|
||||
|
||||
## Use cases
|
||||
|
||||
plugin-sync is highly specialized for the purposes of malloc.gg, but it should
|
||||
mpm is highly specialized for the purposes of malloc.gg, but it should
|
||||
be able to work in other systems where:
|
||||
|
||||
- You have multiple servers in a network
|
||||
|
267
main.py
Executable file
267
main.py
Executable file
@ -0,0 +1,267 @@
|
||||
#!/bin/env python
|
||||
import argparse
|
||||
import pathlib
|
||||
import sys
|
||||
import yaml
|
||||
import os
|
||||
|
||||
from repo import Repo
|
||||
from server import Server
|
||||
from model import *
|
||||
|
||||
try:
|
||||
from yaml import CLoader as Loader, CDumper as Dumper
|
||||
except ImportError:
|
||||
from yaml import Loader, Dumper
|
||||
|
||||
DEFAULT_CONFIG = """
|
||||
repositories: {}
|
||||
servers: {}
|
||||
"""
|
||||
|
||||
class Config():
|
||||
|
||||
def __init__(self, path):
|
||||
if path is None:
|
||||
path = './mpm.yaml'
|
||||
self.path = path
|
||||
with open(path, 'r') as fd:
|
||||
self.yaml = yaml.load(fd, Loader=Loader)
|
||||
self.config = yaml.load(DEFAULT_CONFIG, Loader=Loader)
|
||||
if isinstance(self.yaml, dict):
|
||||
self.config.update(self.yaml)
|
||||
|
||||
def repository(self, name):
|
||||
return Repo(name, self.config['repositories'][name])
|
||||
|
||||
def repositories(self):
|
||||
return [Repo(name, c) for (name, c) in self.config['repositories'].items()]
|
||||
|
||||
def update_repository(self, name, config):
|
||||
self.config['repositories'][name] = config
|
||||
|
||||
def add_repository(self, name, path):
|
||||
if name in self.config['repositories']:
|
||||
raise ValueError('Repository already exists')
|
||||
|
||||
self.update_repository(name, {
|
||||
'path': path
|
||||
})
|
||||
|
||||
def servers(self):
|
||||
return [Server(name, c) for (name, c) in self.config['servers'].items()]
|
||||
|
||||
def server(self, name):
|
||||
return Server(name, self.config['servers'][name])
|
||||
|
||||
def update_server(self, server, config):
|
||||
self.config['servers'][server] = config
|
||||
|
||||
def add_server(self, name, path):
|
||||
if name in self.config['servers']:
|
||||
raise ValueError("Server already exists")
|
||||
|
||||
self.update_server(name, {
|
||||
'path': path,
|
||||
'plugins': [],
|
||||
'inherit': []
|
||||
})
|
||||
|
||||
def save(self):
|
||||
stream = open(self.path, 'w')
|
||||
yaml.dump(self.config, stream, Dumper=Dumper)
|
||||
stream.close()
|
||||
|
||||
def do_repo_add(args, config):
|
||||
if not os.path.exists(args.path):
|
||||
os.makedirs(args.path)
|
||||
config.add_repository(args.name, args.path)
|
||||
config.save()
|
||||
print("Added repository {}".format(args.path))
|
||||
|
||||
def do_repo_list(args, config):
|
||||
for repo in config.repositories():
|
||||
print("{} ({})".format(repo.name, repo.path))
|
||||
for plugin in sorted(repo.plugins()):
|
||||
print('\t', plugin.name, '\t', plugin.version)
|
||||
for badFile in sorted(repo.badFiles()):
|
||||
print('\tWARNING: Unknown file', badFile)
|
||||
|
||||
def do_repo_import(args, config):
|
||||
repo = config.repository(args.name)
|
||||
plugins = []
|
||||
for path in args.path:
|
||||
try:
|
||||
plugins.append(Plugin(path))
|
||||
except:
|
||||
print("Bad plugin filename {}".format(path))
|
||||
|
||||
if len(plugins) == 0:
|
||||
print("No plugins found.")
|
||||
|
||||
print('Found the following plugins:')
|
||||
for plugin in plugins:
|
||||
print("\t{} {}".format(plugin.name, plugin.version))
|
||||
print("Import plugins into {}? [y/N]".format(repo.name))
|
||||
answer = input().lower()
|
||||
if answer == "y":
|
||||
for plugin in plugins:
|
||||
repo.importPlugin(plugin)
|
||||
print("Imported!")
|
||||
else:
|
||||
print("Cancelled.")
|
||||
|
||||
def do_server_add(args, config):
|
||||
config.add_server(args.name, args.path)
|
||||
config.save()
|
||||
print("Added server {} in {}".format(args.name, args.path))
|
||||
|
||||
def do_server_list(args, config):
|
||||
for server in config.servers():
|
||||
print('{} ({}):'.format(server.name, server.path))
|
||||
outdatedLinks = []
|
||||
missing = []
|
||||
installed = []
|
||||
unmanaged = []
|
||||
conflicts = []
|
||||
for state in sorted(server.pluginStates(config.repositories())):
|
||||
if isinstance(state, OutdatedSymlink):
|
||||
outdatedLinks.append(state)
|
||||
elif isinstance(state, Installed):
|
||||
installed.append(state)
|
||||
elif isinstance(state, MissingVersions):
|
||||
missing.append(state)
|
||||
elif isinstance(state, UnmanagedFile):
|
||||
unmanaged.append(state)
|
||||
elif isinstance(state, SymlinkConflict):
|
||||
conflicts.append(state)
|
||||
|
||||
print("Installed plugins:")
|
||||
for state in sorted(installed):
|
||||
print("\t{} {}: {}".format(state.plugin.name, state.plugin.versionSpec, state.currentVersion))
|
||||
print("Oudated symlinks:")
|
||||
for state in sorted(outdatedLinks):
|
||||
print("\t{} {}: Current: {} Wanted: {}".format(state.plugin.name, state.plugin.versionSpec, state.currentVersion, state.wantedVersion))
|
||||
print("Missing plugins:")
|
||||
for state in sorted(missing):
|
||||
print("\t{}: {}".format(state.plugin.name, state.plugin.versionSpec))
|
||||
print("Unmanaged files:")
|
||||
for state in sorted(unmanaged):
|
||||
print("\t{}".format(state.filename))
|
||||
print("Symlink Conflicts:")
|
||||
for state in sorted(conflicts):
|
||||
print("\t{}.jar".format(state.plugin.name))
|
||||
|
||||
def do_server_add_plugin(args, config):
|
||||
server = config.server(args.server)
|
||||
plugins = []
|
||||
for pluginSpec in args.plugin:
|
||||
|
||||
if os.path.exists(pluginSpec):
|
||||
plugin = Plugin(pluginSpec)
|
||||
pluginSpec = PluginSpec(plugin.name, str(plugin.version))
|
||||
else:
|
||||
allVersions = []
|
||||
for repo in config.repositories():
|
||||
allVersions += repo.versionsForPlugin(pluginSpec)
|
||||
pluginSpec = PluginSpec(pluginSpec, list(reversed(sorted(allVersions)))[0])
|
||||
|
||||
plugins.append(pluginSpec)
|
||||
|
||||
print("Added {} to {}".format(pluginSpec, server.name))
|
||||
for pluginSpec in plugins:
|
||||
print("\t{} {}".format(pluginSpec.name, pluginSpec.versionSpec))
|
||||
print("Add these plugins to server {}? [y/N]".format(server.name))
|
||||
answer = input().lower()
|
||||
if answer == "y":
|
||||
for pluginSpec in plugins:
|
||||
server.add_plugin(pluginSpec)
|
||||
config.update_server(server.name, server.config)
|
||||
config.save()
|
||||
print("Added!")
|
||||
else:
|
||||
print("Cancelled.")
|
||||
|
||||
def do_server_sync(args, config):
|
||||
for server in config.servers():
|
||||
print('{} ({}):'.format(server.name, server.path))
|
||||
outdatedLinks = []
|
||||
available = []
|
||||
for state in sorted(server.pluginStates(config.repositories())):
|
||||
if isinstance(state, OutdatedSymlink):
|
||||
outdatedLinks.append(state)
|
||||
elif isinstance(state, Available):
|
||||
available.append(state)
|
||||
|
||||
print("Plugins to update:")
|
||||
for state in sorted(outdatedLinks):
|
||||
print("\t{} {}: Current: {} Wanted: {}".format(state.plugin.name, state.plugin.versionSpec, state.currentVersion, state.wantedVersion))
|
||||
print("New plugins to install:")
|
||||
for state in sorted(available):
|
||||
print("\t{}: {}".format(state.plugin.name, state.plugin.version))
|
||||
|
||||
if len(outdatedLinks) > 0 or len(available) > 0:
|
||||
print("Apply changes? [y/N]")
|
||||
answer = input().lower()
|
||||
if answer == "y":
|
||||
for state in available:
|
||||
server.installVersion(state.plugin)
|
||||
server.updateSymlinkForPlugin(state.plugin, state.plugin.version)
|
||||
print("Installed {} {}".format(state.plugin.name, state.plugin.version))
|
||||
for state in outdatedLinks:
|
||||
server.updateSymlinkForPlugin(state.plugin, state.wantedVersion)
|
||||
print("Updated {} to {}".format(state.plugin.name, state.wantedVersion))
|
||||
else:
|
||||
print("Not applying changes.")
|
||||
else:
|
||||
print("No changes to apply.")
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description='Paper Plugin Sync')
|
||||
parser.add_argument('--config', dest='config_path', type=Config)
|
||||
subparsers = parser.add_subparsers()
|
||||
repos = subparsers.add_parser('repo')
|
||||
repo_sub = repos.add_subparsers()
|
||||
|
||||
repo_add = repo_sub.add_parser('add')
|
||||
repo_add.add_argument('name', help='Name of the repository')
|
||||
repo_add.add_argument('path', help='Where to add a repository or create a new one')
|
||||
repo_add.set_defaults(func=do_repo_add)
|
||||
|
||||
repo_list = repo_sub.add_parser('list')
|
||||
repo_list.set_defaults(func=do_repo_list)
|
||||
|
||||
repo_import = repo_sub.add_parser('import')
|
||||
repo_import.add_argument('name', help='Name of the repository')
|
||||
repo_import.add_argument('path', nargs="+", help='Path of the plugin to import')
|
||||
repo_import.set_defaults(func=do_repo_import)
|
||||
|
||||
servers = subparsers.add_parser('server')
|
||||
server_sub = servers.add_subparsers()
|
||||
server_add = server_sub.add_parser('add')
|
||||
server_add.add_argument('name', help='Name for the server')
|
||||
server_add.add_argument('path', help='Path to your server\'s root directory')
|
||||
server_add.set_defaults(func=do_server_add)
|
||||
|
||||
server_list = server_sub.add_parser('list')
|
||||
server_list.set_defaults(func=do_server_list)
|
||||
|
||||
server_add_plugin = server_sub.add_parser('add-plugin')
|
||||
server_add_plugin.add_argument('server', help='Name of server to modify')
|
||||
server_add_plugin.add_argument('plugin', nargs='+', help='Plugin file or spec to install')
|
||||
server_add_plugin.set_defaults(func=do_server_add_plugin)
|
||||
|
||||
server_sync = server_sub.add_parser('sync')
|
||||
server_sync.set_defaults(func=do_server_sync)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
config = Config(args.config_path)
|
||||
|
||||
if 'func' not in args:
|
||||
parser.print_usage()
|
||||
else:
|
||||
args.func(args, config)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
99
model.py
Normal file
99
model.py
Normal file
@ -0,0 +1,99 @@
|
||||
from semantic_version import Spec, Version
|
||||
from functools import total_ordering
|
||||
import os
|
||||
import re
|
||||
|
||||
#version_pattern = re.compile('^(?P<name>.*)-(?P<version>[^-]+)(?P<extra>-[^-]+)?\.jar$')
|
||||
version_pattern = re.compile('^(?P<name>.*)-(?P<version>[^-]+(?:-[^-]+)?)\.jar$')
|
||||
version_pattern = re.compile('^(?P<name>.+)-(?P<version>(?:\.?\d+)+).+jar$')
|
||||
|
||||
@total_ordering
|
||||
class Plugin:
|
||||
def __init__(self, path):
|
||||
self.path = path
|
||||
pluginName = os.path.basename(path)
|
||||
pluginMatches = version_pattern.match(pluginName)
|
||||
|
||||
if pluginMatches is None:
|
||||
raise ValueError("Cannot derive plugin name from '{}'".format(path))
|
||||
|
||||
self.name = pluginMatches['name']
|
||||
|
||||
try:
|
||||
self.version = Version.coerce(pluginMatches['version'])
|
||||
except ValueError:
|
||||
raise ValueError("Cannot derive semver from '{}'".format(path))
|
||||
|
||||
def __eq__(self, other):
|
||||
return self.name == other.name and self.version == other.version
|
||||
|
||||
def __ne__(self, other):
|
||||
return self.name != other.name or self.version != other.version
|
||||
|
||||
def __lt__(self, other):
|
||||
if self.name == other.name:
|
||||
return self.version < other.version
|
||||
return self.name < other.name
|
||||
|
||||
@total_ordering
|
||||
class PluginSpec:
|
||||
def __init__(self, name, versionSpec):
|
||||
self.name = name
|
||||
try:
|
||||
self.versionSpec = Spec(str(versionSpec))
|
||||
except ValueError:
|
||||
raise ValueError("Invalid version spec for plugin {}: {}".format(name, versionSpec))
|
||||
|
||||
def __str__(self):
|
||||
return "{} {}".format(self.name, self.versionSpec)
|
||||
|
||||
def __eq__(self, other):
|
||||
return self.name == other.name and self.versionSpec == other.versionSpec
|
||||
|
||||
def __ne__(self, other):
|
||||
return self.name != other.name or self.versionSpec != other.versionSpec
|
||||
|
||||
def __lt__(self, other):
|
||||
return self.name < other.name
|
||||
|
||||
@total_ordering
|
||||
class PluginState:
|
||||
def __init__(self, plugin):
|
||||
self.plugin = plugin
|
||||
|
||||
def __eq__(self, other):
|
||||
return self.plugin == other.plugin
|
||||
|
||||
def __ne__(self, other):
|
||||
return self.plugin != other.plugin
|
||||
|
||||
def __lt__(self, other):
|
||||
return self.plugin.name < other.plugin.name
|
||||
|
||||
class UnmanagedFile(PluginState):
|
||||
def __init__(self, filename):
|
||||
self.filename = filename
|
||||
|
||||
def __lt__(self, other):
|
||||
return self.filename < other.filename
|
||||
|
||||
class OutdatedSymlink(PluginState):
|
||||
def __init__(self, plugin, currentVersion, wantedVersion):
|
||||
super().__init__(plugin)
|
||||
self.currentVersion = currentVersion
|
||||
self.wantedVersion = wantedVersion
|
||||
|
||||
class SymlinkConflict(PluginState):
|
||||
pass
|
||||
|
||||
class MissingVersions(PluginState):
|
||||
pass
|
||||
|
||||
class Available(PluginState):
|
||||
def __init__(self, repoPlugin):
|
||||
super().__init__(repoPlugin)
|
||||
|
||||
class Installed(PluginState):
|
||||
def __init__(self, plugin, currentVersion):
|
||||
super().__init__(plugin)
|
||||
self.currentVersion = currentVersion
|
191
plugin-sync.py
191
plugin-sync.py
@ -1,191 +0,0 @@
|
||||
#!/bin/env python
|
||||
import yaml
|
||||
from semantic_version import Spec, Version
|
||||
import os
|
||||
import sys
|
||||
from functools import total_ordering
|
||||
|
||||
try:
|
||||
from yaml import CLoader as Loader, CDumper as Dumper
|
||||
except ImportError:
|
||||
from yaml import Loader, Dumper
|
||||
|
||||
conf = yaml.load(open('plugins.yml', 'r'), Loader=Loader)
|
||||
|
||||
class PluginSpec:
|
||||
def __init__(self, name, versionSpec):
|
||||
self.name = name
|
||||
try:
|
||||
self.versionSpec = Spec(versionSpec)
|
||||
except ValueError:
|
||||
raise ValueError("Invalid version spec for plugin {}: {}".format(name, versionSpec))
|
||||
|
||||
@total_ordering
|
||||
class PluginState:
|
||||
def __init__(self, plugin):
|
||||
self.plugin = plugin
|
||||
|
||||
def __eq__(self, other):
|
||||
return self.plugin == other.plugin
|
||||
|
||||
def __ne__(self, other):
|
||||
return self.plugin != other.plugin
|
||||
|
||||
def __lt__(self, other):
|
||||
return self.plugin.name < other.plugin.name
|
||||
|
||||
class UnmanagedFile(PluginState):
|
||||
def __init__(self, filename):
|
||||
self.filename = filename
|
||||
|
||||
def __lt__(self, other):
|
||||
return self.filename < other.filename
|
||||
|
||||
class OutdatedSymlink(PluginState):
|
||||
def __init__(self, plugin, currentVersion, wantedVersion):
|
||||
super().__init__(plugin)
|
||||
self.currentVersion = currentVersion
|
||||
self.wantedVersion = wantedVersion
|
||||
|
||||
class SymlinkConflict(PluginState):
|
||||
pass
|
||||
|
||||
class MissingVersions(PluginState):
|
||||
pass
|
||||
|
||||
class Installed(PluginState):
|
||||
def __init__(self, plugin, currentVersion):
|
||||
super().__init__(plugin)
|
||||
self.currentVersion = currentVersion
|
||||
|
||||
class Repo:
|
||||
def __init__(self, path, pluginSet):
|
||||
self.path = path
|
||||
self.pluginSet = pluginSet
|
||||
|
||||
def updateSymlinkForPlugin(self, plugin, version):
|
||||
pluginFilename = os.path.join(self.path, 'versions/{}-{}.jar'.format(plugin.name, version))
|
||||
pluginSymlink = os.path.join(self.path, plugin.name + '.jar')
|
||||
linkDst = os.path.relpath(pluginFilename, self.path)
|
||||
|
||||
if os.path.lexists(pluginSymlink):
|
||||
os.unlink(pluginSymlink)
|
||||
os.symlink(linkDst, pluginSymlink)
|
||||
|
||||
def pluginStates(self):
|
||||
managedPluginFilenames = []
|
||||
for plugin in self.pluginSet:
|
||||
compatibleVersions = []
|
||||
pluginLinkName = '{}.jar'.format(plugin.name)
|
||||
managedPluginFilenames.append(pluginLinkName)
|
||||
|
||||
if os.path.exists(os.path.join(self.path, pluginLinkName)) and not os.path.islink(os.path.join(self.path, pluginLinkName)):
|
||||
yield SymlinkConflict(plugin)
|
||||
continue
|
||||
|
||||
for installedVersion in self.versionsForPlugin(plugin.name):
|
||||
if installedVersion in plugin.versionSpec:
|
||||
compatibleVersions.append(installedVersion)
|
||||
|
||||
if len(compatibleVersions) == 0:
|
||||
yield MissingVersions(plugin)
|
||||
else:
|
||||
preferredVersion = list(reversed(sorted(compatibleVersions)))[0]
|
||||
currentVersion = self.currentVersionForPlugin(plugin.name)
|
||||
|
||||
if currentVersion == preferredVersion:
|
||||
yield Installed(plugin, currentVersion)
|
||||
else:
|
||||
yield OutdatedSymlink(plugin, currentVersion, preferredVersion)
|
||||
|
||||
otherPlugins = os.listdir(self.path)
|
||||
for pluginFile in otherPlugins:
|
||||
if os.path.isfile(os.path.join(self.path, pluginFile)) and pluginFile not in managedPluginFilenames:
|
||||
yield UnmanagedFile(pluginFile)
|
||||
|
||||
def currentVersionForPlugin(self, pluginName):
|
||||
pluginSymlink = os.path.join(self.path, pluginName + '.jar')
|
||||
if not os.path.lexists(pluginSymlink):
|
||||
return None
|
||||
suffix = '.jar'
|
||||
pluginJar = os.path.basename(os.readlink(pluginSymlink))
|
||||
jarVersion = pluginJar[len(pluginName)+1:len(pluginJar)-len(suffix)]
|
||||
try:
|
||||
pluginSemver = Version.coerce(jarVersion)
|
||||
except ValueError:
|
||||
pluginSemver = jarVersion
|
||||
return pluginSemver
|
||||
|
||||
def versionsForPlugin(self, pluginName):
|
||||
plugins = os.listdir(os.path.join(self.path, 'versions'))
|
||||
for pluginJar in plugins:
|
||||
if pluginJar.startswith(pluginName):
|
||||
prefix = pluginName + '-'
|
||||
suffix = '.jar'
|
||||
jarVersion = pluginJar[len(prefix):len(pluginJar)-len(suffix)]
|
||||
try:
|
||||
pluginSemver = Version.coerce(jarVersion)
|
||||
except ValueError:
|
||||
pluginSemver = jarVersion
|
||||
yield pluginSemver
|
||||
|
||||
for (serverName,server) in conf['servers'].items():
|
||||
changeset = []
|
||||
if len(sys.argv) > 1 and serverName not in sys.argv[1:]:
|
||||
continue
|
||||
if 'pluginPath' not in server:
|
||||
continue
|
||||
if not os.path.exists(server['pluginPath']):
|
||||
print("Missing plugin path for {}: {}".format(serverName, server['pluginPath']))
|
||||
else:
|
||||
print("=== Updating server {}".format(serverName))
|
||||
pluginSpecs = {}
|
||||
for inherited in server.get('inherit', ()):
|
||||
for inheritedPlugin in conf['servers'][inherited]['plugins']:
|
||||
pluginSpecs[inheritedPlugin['name']] = PluginSpec(inheritedPlugin['name'], str(inheritedPlugin.get('version', '*')))
|
||||
|
||||
for pluginConf in server.get('plugins', ()):
|
||||
pluginSpecs[pluginConf['name']] = PluginSpec(pluginConf['name'], str(pluginConf.get('version', '*')))
|
||||
|
||||
repo = Repo(server['pluginPath'], pluginSpecs.values())
|
||||
outdatedLinks = []
|
||||
missing = []
|
||||
installed = []
|
||||
unmanaged = []
|
||||
conflicts = []
|
||||
for state in repo.pluginStates():
|
||||
if isinstance(state, OutdatedSymlink):
|
||||
outdatedLinks.append(state)
|
||||
elif isinstance(state, Installed):
|
||||
installed.append(state)
|
||||
elif isinstance(state, MissingVersions):
|
||||
missing.append(state)
|
||||
elif isinstance(state, UnmanagedFile):
|
||||
unmanaged.append(state)
|
||||
elif isinstance(state, SymlinkConflict):
|
||||
conflicts.append(state)
|
||||
|
||||
print("Installed plugins:")
|
||||
for state in sorted(installed):
|
||||
print("\t{} {}: {}".format(state.plugin.name, state.plugin.versionSpec, state.currentVersion))
|
||||
print("Oudated symlinks:")
|
||||
for state in sorted(outdatedLinks):
|
||||
print("\t{} {}: Current: {} Wanted: {}".format(state.plugin.name, state.plugin.versionSpec, state.currentVersion, state.wantedVersion))
|
||||
print("Missing plugins:")
|
||||
for state in sorted(missing):
|
||||
print("\t{}: {}".format(state.plugin.name, state.plugin.versionSpec))
|
||||
print("Unmanaged files:")
|
||||
for state in sorted(unmanaged):
|
||||
print("\t{}".format(state.filename))
|
||||
print("Symlink Conflicts:")
|
||||
for state in sorted(conflicts):
|
||||
print("\t{}.jar".format(state.plugin.name))
|
||||
|
||||
if len(outdatedLinks) > 0:
|
||||
print("Apply changes? [y/N]")
|
||||
answer = input().lower()
|
||||
if answer == "y":
|
||||
for state in outdatedLinks:
|
||||
repo.updateSymlinkForPlugin(state.plugin, state.wantedVersion)
|
||||
else:
|
||||
print("Not applying changes.")
|
183
plugins.yml
183
plugins.yml
@ -1,183 +0,0 @@
|
||||
servers:
|
||||
1.17-smp:
|
||||
plugins:
|
||||
- name: Pl3xMap
|
||||
- name: pl3xmap-signs
|
||||
1.18-common:
|
||||
plugins:
|
||||
- name: voicechat-bukkit-1.18.2
|
||||
version: 2.2.28
|
||||
1.17-common:
|
||||
plugins:
|
||||
- name: voicechat-bukkit-1.17.1
|
||||
version: 2.2.28
|
||||
smp-common:
|
||||
plugins:
|
||||
- name: ActionHealth
|
||||
creative:
|
||||
inherit:
|
||||
- common
|
||||
- 1.17-common
|
||||
pluginPath: /srv/minecraft/creative/plugins/
|
||||
plugins:
|
||||
- name: HolographicDisplays
|
||||
- name: HolographicExtension
|
||||
- name: EssentialsXSpawn
|
||||
- name: WorldSystem
|
||||
version: 2.4.12
|
||||
villager-defense:
|
||||
inherit:
|
||||
- common
|
||||
- 1.17-common
|
||||
pluginPath: /srv/minecraft/villager-defense/plugins/
|
||||
mcdev:
|
||||
inherit:
|
||||
- common
|
||||
- smp-common
|
||||
- 1.18-common
|
||||
- SMP
|
||||
pluginPath: /srv/minecraft/mcdev/plugins/
|
||||
plugins:
|
||||
- name: tabtps-spigot
|
||||
version: '*'
|
||||
- name: item-nbt-api-plugin
|
||||
version: 2.9.2
|
||||
- name: QuickShop
|
||||
version: 5
|
||||
- name: AngelChest
|
||||
version: '*'
|
||||
- name: GameModeInventories
|
||||
version: '*'
|
||||
SMP:
|
||||
inherit:
|
||||
- common
|
||||
- smp-common
|
||||
- 1.17-common
|
||||
- 1.17-smp
|
||||
pluginPath: /srv/minecraft/server/plugins/
|
||||
plugins:
|
||||
- name: ATMSigns
|
||||
- name: Harbor
|
||||
- name: BetterRTP
|
||||
- name: CoreProtect
|
||||
version: 19.5.*
|
||||
- name: Chunky
|
||||
version: 1.2.86
|
||||
- name: ChunkyBorder
|
||||
version: 1.0.38
|
||||
- name: GameModeInventories
|
||||
version: 3.3.3
|
||||
- name: HolographicDisplays
|
||||
version: 2
|
||||
- name: Regions
|
||||
version: '0.3'
|
||||
- name: Shopkeepers
|
||||
version: 2.13.2
|
||||
- name: Lands
|
||||
version: 5.11.18
|
||||
- name: OpenInv
|
||||
version: 4.1.8
|
||||
- name: AngelChest
|
||||
version: 4.2.0
|
||||
- name: PvPManager
|
||||
version: 3
|
||||
- name: EssentialsXSpawn
|
||||
version: 2
|
||||
- name: MythicMobs
|
||||
version: 5.0.0-alpha1
|
||||
- name: Model-Engine
|
||||
version: 2.*
|
||||
- name: LibsDisguises
|
||||
version: 10
|
||||
- name: QuickShop
|
||||
version: 4.0
|
||||
- name: ProtocolLib
|
||||
version: 4.8.0
|
||||
skyblock:
|
||||
inherit:
|
||||
- 1.18-common
|
||||
- common
|
||||
pluginPath: /srv/minecraft/skyblock/plugins/
|
||||
plugins:
|
||||
- name: BentoBox
|
||||
version: 1.19.0
|
||||
lobby:
|
||||
pluginPath: /srv/minecraft/lobby/plugins/
|
||||
inherit:
|
||||
- common
|
||||
plugins:
|
||||
- name: ItemJoin
|
||||
- name: Parkour
|
||||
version: 6.7.1
|
||||
- name: GadgetsMenu
|
||||
version: 5.0.2
|
||||
- name: Denizen
|
||||
version: 1.2.1-b1743-REL
|
||||
- name: JumpPads
|
||||
version: 1.25.6
|
||||
- name: VoidSpawn
|
||||
version: 1.0.1
|
||||
- name: EssentialsXSpawn
|
||||
version: 2.18.2
|
||||
- name: HolographicExtension
|
||||
version: 1.10.9
|
||||
- name: HolographicDisplays
|
||||
version: 2.4.9
|
||||
- name: Citizens
|
||||
version: 2.0.28-5
|
||||
- name: voicechat-bukkit-1.17.1
|
||||
version: 2.2.28
|
||||
common:
|
||||
plugins:
|
||||
- name: Advanced-Portals
|
||||
version: 0.7.1
|
||||
- name: DeluxeMenus
|
||||
version: 1.13.3
|
||||
- name: AsyncWorldEdit
|
||||
version: 3.9.1
|
||||
- name: CoordinatesHUD
|
||||
- name: BungeeTabListPlus_BukkitBridge
|
||||
version: ~3.4
|
||||
- name: BuycraftX
|
||||
- name: DBVerifier
|
||||
version: 1.6.5-malloc
|
||||
- name: DiscordSRV
|
||||
version: 1.24.0
|
||||
- name: EssentialsX
|
||||
version: 2.18.2
|
||||
- name: GSit
|
||||
version: 6.0.2
|
||||
- name: item-nbt-api-plugin
|
||||
version: 2.*
|
||||
- name: LuckPerm-Bukkit
|
||||
version: 5.4.9
|
||||
- name: PlaceholderAPI
|
||||
version: 2.10.9
|
||||
- name: Plan
|
||||
version: 5.4.0-build-1354
|
||||
- name: PlayerPoints
|
||||
version: 3.0.3
|
||||
- name: ProtocolLib
|
||||
version: 4.8.0
|
||||
- name: SuperVanish
|
||||
version: 6.2.6
|
||||
- name: tabtps-spigot
|
||||
version: 1.3.8
|
||||
- name: Umfrage
|
||||
version: 1.6.3-STABLE
|
||||
- name: Vault
|
||||
- name: VentureChat
|
||||
version: 3.3.2
|
||||
- name: floodgate-bukkit
|
||||
version: 53
|
||||
- name: DisableJoinMessage
|
||||
- name: worldedit-bukkit
|
||||
- name: worldguard-bukkit
|
||||
- name: SkQuery
|
||||
version: 4.1.4
|
||||
- name: Skript
|
||||
version: 2.6.0-beta2
|
||||
- name: skUtilities
|
||||
version: 0.9.2
|
||||
- name: Skungee
|
||||
- name: NametagEdit
|
110
repo.py
Normal file
110
repo.py
Normal file
@ -0,0 +1,110 @@
|
||||
import os
|
||||
from model import *
|
||||
import shutil
|
||||
|
||||
class Repo:
|
||||
def __init__(self, name, config):
|
||||
self.name = name
|
||||
self.config = config
|
||||
self.path = config['path']
|
||||
|
||||
def importPlugin(self, plugin):
|
||||
dest = "{}/{}-{}.jar".format(self.path, plugin.name, plugin.version)
|
||||
shutil.copyfile(plugin.path, dest)
|
||||
|
||||
def plugins(self):
|
||||
plugins = os.listdir(self.path)
|
||||
for pluginFile in plugins:
|
||||
fullPath = os.path.join(self.path, pluginFile)
|
||||
if os.path.isfile(fullPath):
|
||||
try:
|
||||
yield Plugin(fullPath)
|
||||
except ValueError:
|
||||
continue
|
||||
|
||||
def versionsForPlugin(self, name):
|
||||
for plugin in self.plugins():
|
||||
if plugin.name == name:
|
||||
yield plugin.version
|
||||
|
||||
def badFiles(self):
|
||||
plugins = os.listdir(self.path)
|
||||
for pluginFile in plugins:
|
||||
fullPath = os.path.join(self.path, pluginFile)
|
||||
if os.path.isfile(fullPath):
|
||||
try:
|
||||
Plugin(fullPath)
|
||||
except ValueError:
|
||||
yield fullPath
|
||||
|
||||
class Server:
|
||||
def __init__(self, path, pluginSet):
|
||||
self.path = path
|
||||
self.pluginSet = pluginSet
|
||||
|
||||
def updateSymlinkForPlugin(self, plugin, version):
|
||||
pluginFilename = os.path.join(self.path, 'plugins/versions/{}-{}.jar'.format(plugin.name, version))
|
||||
pluginSymlink = os.path.join(self.path, plugin.name + '.jar')
|
||||
linkDst = os.path.relpath(pluginFilename, self.path)
|
||||
|
||||
if os.path.lexists(pluginSymlink):
|
||||
os.unlink(pluginSymlink)
|
||||
os.symlink(linkDst, pluginSymlink)
|
||||
|
||||
def pluginStates(self):
|
||||
managedPluginFilenames = []
|
||||
for plugin in self.pluginSet:
|
||||
compatibleVersions = []
|
||||
pluginLinkName = '{}.jar'.format(plugin.name)
|
||||
managedPluginFilenames.append(pluginLinkName)
|
||||
|
||||
if os.path.exists(os.path.join(self.path, pluginLinkName)) and not os.path.islink(os.path.join(self.path, pluginLinkName)):
|
||||
yield SymlinkConflict(plugin)
|
||||
continue
|
||||
|
||||
for installedVersion in self.versionsForPlugin(plugin.name):
|
||||
if installedVersion in plugin.versionSpec:
|
||||
compatibleVersions.append(installedVersion)
|
||||
|
||||
if len(compatibleVersions) == 0:
|
||||
yield MissingVersions(plugin)
|
||||
else:
|
||||
preferredVersion = list(reversed(sorted(compatibleVersions)))[0]
|
||||
currentVersion = self.currentVersionForPlugin(plugin.name)
|
||||
|
||||
if currentVersion == preferredVersion:
|
||||
yield Installed(plugin, currentVersion)
|
||||
else:
|
||||
yield OutdatedSymlink(plugin, currentVersion, preferredVersion)
|
||||
|
||||
otherPlugins = os.listdir(self.path)
|
||||
for pluginFile in otherPlugins:
|
||||
if os.path.isfile(os.path.join(self.path, pluginFile)) and pluginFile not in managedPluginFilenames:
|
||||
yield UnmanagedFile(pluginFile)
|
||||
|
||||
def currentVersionForPlugin(self, pluginName):
|
||||
pluginSymlink = os.path.join(self.path, pluginName + '.jar')
|
||||
if not os.path.lexists(pluginSymlink):
|
||||
return None
|
||||
suffix = '.jar'
|
||||
pluginJar = os.path.basename(os.readlink(pluginSymlink))
|
||||
jarVersion = pluginJar[len(pluginName)+1:len(pluginJar)-len(suffix)]
|
||||
try:
|
||||
pluginSemver = Version.coerce(jarVersion)
|
||||
except ValueError:
|
||||
pluginSemver = jarVersion
|
||||
return pluginSemver
|
||||
|
||||
def versionsForPlugin(self, pluginName):
|
||||
plugins = os.listdir(os.path.join(self.path, 'plugins', 'versions'))
|
||||
for pluginJar in plugins:
|
||||
if pluginJar.startswith(pluginName):
|
||||
prefix = pluginName + '-'
|
||||
suffix = '.jar'
|
||||
jarVersion = pluginJar[len(prefix):len(pluginJar)-len(suffix)]
|
||||
try:
|
||||
pluginSemver = Version.coerce(jarVersion)
|
||||
except ValueError:
|
||||
pluginSemver = jarVersion
|
||||
yield pluginSemver
|
||||
|
96
server.py
Normal file
96
server.py
Normal file
@ -0,0 +1,96 @@
|
||||
from model import *
|
||||
import shutil
|
||||
|
||||
class Server:
|
||||
def __init__(self, name, config):
|
||||
self.name = name
|
||||
self.config = config
|
||||
self.path = config['path']
|
||||
self.pluginPath = self.path+'/plugins'
|
||||
|
||||
def plugins(self):
|
||||
return [PluginSpec(p['name'], p['version']) for p in self.config['plugins']]
|
||||
|
||||
def add_plugin(self, pluginSpec):
|
||||
for plugin in self.config['plugins']:
|
||||
if plugin['name'] == pluginSpec.name:
|
||||
raise KeyError("Cannot add plugin multiple times.")
|
||||
self.config['plugins'].append({'name': pluginSpec.name, 'version': str(pluginSpec.versionSpec)})
|
||||
|
||||
def pluginStates(self, repos):
|
||||
managedPluginFilenames = []
|
||||
for plugin in self.plugins():
|
||||
compatibleVersions = []
|
||||
pluginLinkName = '{}.jar'.format(plugin.name)
|
||||
managedPluginFilenames.append(pluginLinkName)
|
||||
|
||||
if os.path.exists(os.path.join(self.pluginPath, pluginLinkName)) and not os.path.islink(os.path.join(self.pluginPath, pluginLinkName)):
|
||||
yield SymlinkConflict(plugin)
|
||||
continue
|
||||
|
||||
for installedVersion in self.versionsForPlugin(plugin.name, repos):
|
||||
if installedVersion in plugin.versionSpec:
|
||||
compatibleVersions.append(installedVersion)
|
||||
|
||||
if len(compatibleVersions) == 0:
|
||||
for repo in repos:
|
||||
for repoPlugin in repo.plugins():
|
||||
if repoPlugin.name == plugin.name and repoPlugin.version in plugin.versionSpec:
|
||||
compatibleVersions.append(repoPlugin)
|
||||
if len(compatibleVersions) == 0:
|
||||
yield MissingVersions(plugin)
|
||||
else:
|
||||
preferredVersion = list(reversed(sorted(compatibleVersions)))[0]
|
||||
yield Available(preferredVersion)
|
||||
else:
|
||||
preferredVersion = list(reversed(sorted(compatibleVersions)))[0]
|
||||
currentVersion = self.currentVersionForPlugin(plugin.name)
|
||||
|
||||
if currentVersion == preferredVersion:
|
||||
yield Installed(plugin, currentVersion)
|
||||
else:
|
||||
yield OutdatedSymlink(plugin, currentVersion, preferredVersion)
|
||||
|
||||
otherPlugins = os.listdir(self.pluginPath)
|
||||
for pluginFile in otherPlugins:
|
||||
if os.path.isfile(os.path.join(self.pluginPath, pluginFile)) and pluginFile not in managedPluginFilenames:
|
||||
yield UnmanagedFile(pluginFile)
|
||||
|
||||
def currentVersionForPlugin(self, pluginName):
|
||||
pluginSymlink = os.path.join(self.pluginPath, pluginName + '.jar')
|
||||
if not os.path.lexists(pluginSymlink):
|
||||
return None
|
||||
suffix = '.jar'
|
||||
pluginJar = os.path.basename(os.readlink(pluginSymlink))
|
||||
jarVersion = pluginJar[len(pluginName)+1:len(pluginJar)-len(suffix)]
|
||||
try:
|
||||
pluginSemver = Version.coerce(jarVersion)
|
||||
except ValueError:
|
||||
pluginSemver = jarVersion
|
||||
return pluginSemver
|
||||
|
||||
def versionsForPlugin(self, pluginName, repos):
|
||||
plugins = os.listdir(os.path.join(self.pluginPath, 'versions'))
|
||||
for pluginJar in plugins:
|
||||
if pluginJar.startswith(pluginName):
|
||||
prefix = pluginName + '-'
|
||||
suffix = '.jar'
|
||||
jarVersion = pluginJar[len(prefix):len(pluginJar)-len(suffix)]
|
||||
try:
|
||||
pluginSemver = Version.coerce(jarVersion)
|
||||
except ValueError:
|
||||
pluginSemver = jarVersion
|
||||
yield pluginSemver
|
||||
|
||||
def updateSymlinkForPlugin(self, plugin, version):
|
||||
pluginFilename = os.path.join(self.pluginPath, 'versions/{}-{}.jar'.format(plugin.name, version))
|
||||
pluginSymlink = os.path.join(self.pluginPath, plugin.name + '.jar')
|
||||
linkDst = os.path.relpath(pluginFilename, self.pluginPath)
|
||||
|
||||
if os.path.lexists(pluginSymlink):
|
||||
os.unlink(pluginSymlink)
|
||||
os.symlink(linkDst, pluginSymlink)
|
||||
|
||||
def installVersion(self, plugin):
|
||||
dest = os.path.join(self.pluginPath, 'versions/{}-{}.jar'.format(plugin.name, plugin.version))
|
||||
shutil.copyfile(plugin.path, dest)
|
Loading…
Reference in New Issue
Block a user