Support a persistent, private plugins directory alongside the built-in ones
CI / check (push) Successful in 41s
CI / deploy (push) Successful in 13s

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:
randy committed 2026-09-09 17:24:52 +02:00
1 parent 1733fc7c1b
commit 6e0b67f2a0
5 files changed
+82 -19

No files matched your search

+15 -5
View File
@@ -67,11 +67,21 @@ module Maptool
# Not under DATA_DIR: unlike everything else there (uploads, PMTiles,
# the kiwix ZIM), a plugin is code, not local data — plugins/overpass-pois
# ships checked into this repo as the reference example of the
# contract. A private/scraper plugin just lives in its own directory
# here too, simply never `git add`ed, no special-casing needed.
# Overridable so tests can point it at a throwaway fixture directory.
# contract. Overridable so tests can point it at a throwaway fixture
# directory. In production this whole directory is baked into the
# image (see Dockerfile's `COPY . .`) and wiped/replaced on every
# deploy along with the rest of the checkout (docs/deployment.adoc) —
# fine for a plugin checked into this repo, not for a private one.
PLUGINS_DIR = ENV.fetch("MAPTOOL_PLUGINS_DIR") { File.join(__dir__, "plugins") }
# A second, optional plugin directory under DATA_DIR — the same
# persistent bind mount as gpx/kiwix (docs/deployment.adoc), so a
# private/scraper plugin (one whose ToS forbids living in this public
# repo) dropped in data/plugins/ survives a redeploy the way PLUGINS_DIR
# above can't. Checked after PLUGINS_DIR (see Plugins.list/run), so a
# same-named private plugin never shadows the built-in one.
PLUGINS_DIRS = [PLUGINS_DIR, File.join(DATA_DIR, "plugins")].freeze
# Shared secret for the Garmin LiveTrack webhook (a Google Apps Script
# watching Gmail for LiveTrack emails, see docs/deployment.adoc) — not a
# Gitea-authenticated user, so it can't use the OAuth2 login flow above.
@@ -342,7 +352,7 @@ module Maptool
# for the contract) — read-only, doesn't need require_login! the way
# actually running one does below.
get "/api/plugins" do
json Plugins.list(PLUGINS_DIR)
json Plugins.list(PLUGINS_DIRS)
end
# Runs one plugin for the given bbox (the current map viewport, from
@@ -358,7 +368,7 @@ module Maptool
south, west, north, east = bbox.map(&:to_f)
begin
geojson = Plugins.run(PLUGINS_DIR, params[:name], [south, west, north, east])
geojson = Plugins.run(PLUGINS_DIRS, params[:name], [south, west, north, east])
rescue Plugins::RunError => e
halt 502, json(error: e.message)
end