Support a persistent, private plugins directory alongside the built-in ones
plugins/ ships baked into the image and gets wiped/replaced by the checkout sync on every deploy (docs/deployment.adoc), so a private plugin dropped there — the exact use case the plugin contract was designed for — didn't survive a redeploy the way uploaded GPX/kiwix data under data/ does. Plugins.list/run now accept either one directory or an array, and app.rb checks the built-in plugins/ plus data/plugins/ (already covered by the existing ./data bind mount, no new volume needed).
This commit is contained in:
1 parent
1733fc7c1b
commit
6e0b67f2a0
5 files changed
+82
-19
No files matched your search
+25
-13
@@ -4,7 +4,7 @@ require "open3"
|
||||
module Maptool
|
||||
# Runs an external "importer" script that turns a bbox into a GeoJSON
|
||||
# FeatureCollection, and lists what's available. Each plugin is a
|
||||
# directory under PLUGINS_DIR with a plugin.json manifest ({"label":
|
||||
# directory under one of `dirs` with a plugin.json manifest ({"label":
|
||||
# "...", "command": "./run.rb"}) plus whatever script it names — the
|
||||
# entire interface a plugin needs to know about maptool is "read
|
||||
# --south/--west/--north/--east, print GeoJSON to stdout". Kept this thin
|
||||
@@ -12,6 +12,13 @@ module Maptool
|
||||
# forbids that live in its own private repo, dropped into plugins/
|
||||
# locally, never touching this (public) one — see plugins/overpass-pois
|
||||
# for a real, in-repo example of the contract itself.
|
||||
#
|
||||
# `dirs` takes either one directory or an array of them (wrapped with
|
||||
# Array() below) — app.rb passes both the built-in plugins/ (baked into
|
||||
# the image, wiped and replaced on every deploy along with the rest of
|
||||
# the checkout) and an optional second directory under the persistent
|
||||
# data/ bind mount, so a private plugin dropped there survives a
|
||||
# redeploy the same way an uploaded GPX file does.
|
||||
module Plugins
|
||||
class RunError < StandardError; end
|
||||
|
||||
@@ -21,27 +28,32 @@ module Maptool
|
||||
# filesystem, same spirit as GpxStore's own path-escape guard.
|
||||
NAME_PATTERN = /\A[A-Za-z0-9_-]+\z/
|
||||
|
||||
def self.list(dir)
|
||||
Dir.glob(File.join(dir, "*", "plugin.json")).filter_map do |manifest_path|
|
||||
name = File.basename(File.dirname(manifest_path))
|
||||
manifest = JSON.parse(File.read(manifest_path))
|
||||
{ "name" => name, "label" => manifest["label"] || name }
|
||||
rescue JSON::ParserError
|
||||
nil
|
||||
end.sort_by { |p| p["label"] }
|
||||
def self.list(dirs)
|
||||
Array(dirs).flat_map do |dir|
|
||||
Dir.glob(File.join(dir, "*", "plugin.json")).filter_map do |manifest_path|
|
||||
name = File.basename(File.dirname(manifest_path))
|
||||
manifest = JSON.parse(File.read(manifest_path))
|
||||
{ "name" => name, "label" => manifest["label"] || name }
|
||||
rescue JSON::ParserError
|
||||
nil
|
||||
end
|
||||
end.uniq { |plugin| plugin["name"] }.sort_by { |p| p["label"] }
|
||||
end
|
||||
|
||||
# bbox: [south, west, north, east]. Raises RunError with a short
|
||||
# message on anything that isn't a clean run — an unknown plugin, a
|
||||
# non-zero exit, or output that isn't a GeoJSON FeatureCollection — so
|
||||
# the route can turn it into a single error response without knowing
|
||||
# any of these details itself.
|
||||
def self.run(dir, name, bbox)
|
||||
# any of these details itself. When the same name exists under more
|
||||
# than one of `dirs`, the first one that has it wins — same precedence
|
||||
# as list's `uniq` above.
|
||||
def self.run(dirs, name, bbox)
|
||||
raise RunError, "unknown plugin" unless name =~ NAME_PATTERN
|
||||
|
||||
plugin_dir = File.join(dir, name)
|
||||
plugin_dir = Array(dirs).map { |dir| File.join(dir, name) }.find { |dir| File.file?(File.join(dir, "plugin.json")) }
|
||||
raise RunError, "unknown plugin" unless plugin_dir
|
||||
|
||||
manifest_path = File.join(plugin_dir, "plugin.json")
|
||||
raise RunError, "unknown plugin" unless File.file?(manifest_path)
|
||||
|
||||
command = JSON.parse(File.read(manifest_path))["command"]
|
||||
raise RunError, "plugin has no command" unless command
|
||||
|
||||
Reference in new issue
Block a user