summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Silverstone <dsilvers@digital-scurf.org>2014-03-11 17:39:54 +0000
committerDaniel Silverstone <dsilvers@digital-scurf.org>2014-03-11 17:39:54 +0000
commitbac13b388c04a7af9c049c75534bc83c931e2611 (patch)
treedb05c42c006afd373ef5bcf2f1ebc7d13abd4b58
parent2a078069dcfd01c7bf573d53340c8264c3dc4263 (diff)
parent115c62b1d3e0b98020ec4493ce1d9de512a4995a (diff)
downloadgitano-bac13b388c04a7af9c049c75534bc83c931e2611.tar.bz2
Merge in rsync work from Codethink
This adds support for Codethink's Trove server's rsync command and also adds a generic plugin interface and repository detection support. Signed-off-by: Daniel Silverstone <dsilvers@digital-scurf.org>
-rw-r--r--Makefile20
-rw-r--r--bin/gitano-auth.in17
-rwxr-xr-xbin/gitano-command.cgi.in1
-rw-r--r--bin/gitano-post-receive-hook.in1
-rw-r--r--bin/gitano-pre-receive-hook.in1
-rw-r--r--bin/gitano-setup.in1
-rwxr-xr-xbin/gitano-smart-http.cgi.in1
-rw-r--r--bin/gitano-update-hook.in1
-rw-r--r--bin/gitano-update-ssh.in1
-rw-r--r--lib/gitano.lua4
-rw-r--r--lib/gitano/admincommand.lua16
-rw-r--r--lib/gitano/auth.lua11
-rw-r--r--lib/gitano/command.lua24
-rw-r--r--lib/gitano/plugins.lua63
-rw-r--r--plugins/demo.lua46
-rw-r--r--plugins/rsync.lua108
-rw-r--r--utils/install-lua-bin14
17 files changed, 291 insertions, 39 deletions
diff --git a/Makefile b/Makefile
index 7fa7d11..cab0162 100644
--- a/Makefile
+++ b/Makefile
@@ -6,6 +6,7 @@ LUA_VER := 5.1
LUA := /usr/bin/lua$(LUA_VER)
PREFIX ?= /usr/local
INST_ROOT := $(PREFIX)
+SYSCONF_DIR ?= /etc
# Internal stuff
LUA_MOD_PATH := $(INST_ROOT)/share/lua/$(LUA_VER)
@@ -19,6 +20,7 @@ SHARE_INST_PATH := $(DESTDIR)$(SHARE_PATH)
SKEL_INST_PATH := $(SHARE_INST_PATH)/skel
MAN_PATH := $(INST_ROOT)/share/man
MAN_INST_PATH := $(DESTDIR)$(MAN_PATH)
+PLUGIN_PATH := $(SYSCONF_DIR)/gitano/plugins:$(INST_ROOT)/lib/gitano/plugins
LIB_BINS := gitano-auth gitano-post-receive-hook gitano-update-hook \
gitano-update-ssh gitano-pre-receive-hook gitano-smart-http.cgi \
@@ -43,7 +45,7 @@ MODS := gitano \
gitano.actions gitano.config gitano.lace gitano.log \
gitano.markdown gitano.repository gitano.supple \
gitano.command gitano.admincommand gitano.usercommand \
- gitano.repocommand gitano.copycommand gitano.auth
+ gitano.repocommand gitano.copycommand gitano.auth gitano.plugins
SKEL_FILES := gitano-admin/rules/selfchecks.lace \
gitano-admin/rules/aschecks.lace \
@@ -59,6 +61,9 @@ SKEL_FILES := gitano-admin/rules/selfchecks.lace \
MAN1S := gitano-setup.1
+
+PLUGINS := rsync.lua
+
MOD_DIRS := gitano
MOD_FILES := $(patsubst %,%.lua,$(subst .,/,$(MODS)))
SRC_MOD_FILES := $(patsubst %,lib/%,$(MOD_FILES))
@@ -77,14 +82,14 @@ GEN_BIN := utils/install-lua-bin
RUN_GEN_BIN := $(LUA) $(GEN_BIN) $(LUA)
define GEN_LOCAL_BIN
-$(RUN_GEN_BIN) $(shell pwd) $(shell pwd)/bin $(shell pwd)/lib $1 $2
+$(RUN_GEN_BIN) $(shell pwd) $(shell pwd)/bin $(shell pwd)/lib $(shell pwd)/plugins $1 $2
chmod 755 $2
endef
define GEN_INSTALL_BIN
-$(RUN_GEN_BIN) $(SHARE_PATH) $(LIB_BIN_PATH) $(LUA_MOD_PATH) $1 $2
+$(RUN_GEN_BIN) $(SHARE_PATH) $(LIB_BIN_PATH) $(LUA_MOD_PATH) $(PLUGIN_PATH) $1 $2
chmod 755 $2
endef
@@ -119,7 +124,7 @@ bin/%: bin/%.in $(GEN_BIN)
testing/%: testing/%.in $(GEN_BIN)
$(call GEN_LOCAL_BIN,$<,$@)
-install: install-bins install-lib-bins install-mods install-skel install-man
+install: install-bins install-lib-bins install-mods install-skel install-man install-plugins
install-man:
mkdir -p $(MAN_INST_PATH)/man1
@@ -143,6 +148,13 @@ install-skel:
install -m 644 skel/$$SKELFILE $(SKEL_INST_PATH)/$$SKELFILE; \
done
+install-plugins:
+ mkdir -p $(DESTDIR)$(INST_ROOT)/lib/gitano/plugins
+ mkdir -p $(DESTDIR)$(SYSCONF_DIR)/gitano/plugins
+ for PLUGIN in $(PLUGINS); do \
+ install -m 644 plugins/$$PLUGIN $(DESTDIR)$(INST_ROOT)/lib/gitano/plugins; \
+ done
+
test: local $(TEST_BINS)
@$(YARN) --env GTT="$$(pwd)/testing/gitano-test-tool" \
--env LUA_PATH="$(LUA_PATH)" --env LUA_CPATH="$(LUA_CPATH)" \
diff --git a/bin/gitano-auth.in b/bin/gitano-auth.in
index 8a91ae3..3901166 100644
--- a/bin/gitano-auth.in
+++ b/bin/gitano-auth.in
@@ -18,6 +18,7 @@ local sp = require "luxio.subprocess"
-- @@GITANO_BIN_PATH
-- @@GITANO_SHARE_PATH
+-- @@GITANO_PLUGIN_PATH
local repo_root, username, keytag = ...
@@ -104,18 +105,10 @@ if not cmd then
gitano.log.fatal("Unknown command: " .. parsed_cmdline[1])
end
-if cmd.takes_repo and #parsed_cmdline > 1 then
- -- Acquire the repository object for the target repo
- local msg
- repo, msg = gitano.repository.find(config, parsed_cmdline[2])
- if not repo then
- gitano.log.critical("Unable to locate repository.")
- gitano.log.critical(" * " .. (tostring(msg)))
- gitano.log.fatal("Cannot continue")
- end
-
- if repo.is_nascent then
- gitano.log.info("Repository " .. repo.name .. " is nascent")
+if cmd.takes_repo then
+ repo, parsed_cmdline = cmd.detect_repo(config, parsed_cmdline)
+ if not repo and not parsed_cmdline then
+ gitano.log.fatal("Failed to acquire repository object")
end
end
diff --git a/bin/gitano-command.cgi.in b/bin/gitano-command.cgi.in
index bc280b2..ba16538 100755
--- a/bin/gitano-command.cgi.in
+++ b/bin/gitano-command.cgi.in
@@ -17,6 +17,7 @@ local sio = require "luxio.simple"
-- @@GITANO_BIN_PATH
-- @@GITANO_SHARE_PATH
+-- @@GITANO_PLUGIN_PATH
local stdout = sio.stdout
diff --git a/bin/gitano-post-receive-hook.in b/bin/gitano-post-receive-hook.in
index f495d89..3dccfee 100644
--- a/bin/gitano-post-receive-hook.in
+++ b/bin/gitano-post-receive-hook.in
@@ -18,6 +18,7 @@ local sp = require "luxio.subprocess"
-- @@GITANO_BIN_PATH
-- @@GITANO_SHARE_PATH
+-- @@GITANO_PLUGIN_PATH
local start_log_level = gitano.log.get_level()
-- Clamp level at info until we have checked if the caller
diff --git a/bin/gitano-pre-receive-hook.in b/bin/gitano-pre-receive-hook.in
index c25418b..182554b 100644
--- a/bin/gitano-pre-receive-hook.in
+++ b/bin/gitano-pre-receive-hook.in
@@ -18,6 +18,7 @@ local sp = require "luxio.subprocess"
-- @@GITANO_BIN_PATH
-- @@GITANO_SHARE_PATH
+-- @@GITANO_PLUGIN_PATH
local start_log_level = gitano.log.get_level()
-- Clamp level at info until we have checked if the caller
diff --git a/bin/gitano-setup.in b/bin/gitano-setup.in
index 61a3246..f31c8f0 100644
--- a/bin/gitano-setup.in
+++ b/bin/gitano-setup.in
@@ -18,6 +18,7 @@ local clod = require "clod"
-- @@GITANO_BIN_PATH
-- @@GITANO_SHARE_PATH
+-- @@GITANO_PLUGIN_PATH
local possible_answers = {...}
diff --git a/bin/gitano-smart-http.cgi.in b/bin/gitano-smart-http.cgi.in
index 8fb0240..017c4e7 100755
--- a/bin/gitano-smart-http.cgi.in
+++ b/bin/gitano-smart-http.cgi.in
@@ -18,6 +18,7 @@ local sio = require "luxio.simple"
-- @@GITANO_BIN_PATH
-- @@GITANO_SHARE_PATH
+-- @@GITANO_PLUGIN_PATH
local stdout = sio.stdout
diff --git a/bin/gitano-update-hook.in b/bin/gitano-update-hook.in
index e338ba2..34acbf9 100644
--- a/bin/gitano-update-hook.in
+++ b/bin/gitano-update-hook.in
@@ -18,6 +18,7 @@ local sp = require "luxio.subprocess"
-- @@GITANO_BIN_PATH
-- @@GITANO_SHARE_PATH
+-- @@GITANO_PLUGIN_PATH
local refname, oldsha, newsha = ...
diff --git a/bin/gitano-update-ssh.in b/bin/gitano-update-ssh.in
index 798296f..136d6df 100644
--- a/bin/gitano-update-ssh.in
+++ b/bin/gitano-update-ssh.in
@@ -18,6 +18,7 @@ local sp = require "luxio.subprocess"
-- @@GITANO_BIN_PATH
-- @@GITANO_SHARE_PATH
+-- @@GITANO_PLUGIN_PATH
local repo_root = ...
diff --git a/lib/gitano.lua b/lib/gitano.lua
index b57bd71..31e62d5 100644
--- a/lib/gitano.lua
+++ b/lib/gitano.lua
@@ -15,6 +15,7 @@ local lace = require 'gitano.lace'
local markdown = require 'gitano.markdown'
local supple = require 'gitano.supple'
local auth = require 'gitano.auth'
+local plugins = require 'gitano.plugins'
return {
util = util,
@@ -26,5 +27,6 @@ return {
lace = lace,
markdown = markdown,
supple = supple,
- auth = auth
+ auth = auth,
+ plugins = plugins,
}
diff --git a/lib/gitano/admincommand.lua b/lib/gitano/admincommand.lua
index f565e96..d0d13ce 100644
--- a/lib/gitano/admincommand.lua
+++ b/lib/gitano/admincommand.lua
@@ -47,18 +47,12 @@ local function builtin_as_validate(config, _, cmdline)
cmdline.cmd = cmd
-- If the returned command needs a repo, find it (and save it for later)
local repo
- if cmd.takes_repo and #cmdline > 3 then
+ if cmd.takes_repo then
-- Acquire the repository object for the target repo
- local msg
- repo, msg = repository.find(config, cmdline[4])
- if not repo then
- log.critical("Unable to locate repository.")
- log.critical(" * " .. (tostring(msg)))
- log.fatal("Cannot continue")
- end
-
- if repo.is_nascent then
- log.info("Repository " .. repo.name .. " is nascent")
+ repo, cmdline.copy = cmd.detect_repo(config, cmdline.copy)
+ if not repo and not cmdline.copy then
+ log.error("Unable to continue")
+ return false
end
cmdline.repo = repo
end
diff --git a/lib/gitano/auth.lua b/lib/gitano/auth.lua
index 8cdd8ec..8f288e6 100644
--- a/lib/gitano/auth.lua
+++ b/lib/gitano/auth.lua
@@ -97,14 +97,9 @@ local function is_authorized(user, source, cmdline)
local repo
if cmd.takes_repo and #parsed_cmdline > 1 then
- -- Acquire the repository object for the target repo
- local msg
- repo, msg = repository.find(admin_conf, parsed_cmdline[2])
-
- if not repo then
- log.critical("Unable to locate repository.")
- log.critical(" * " .. (tostring(msg) or "No error"))
- return nil
+ repo, parsed_cmdline = cmd.detect_repo(admin_conf, parsed_cmdline)
+ if not repo and not parsed_cmdline then
+ return nil
end
end
diff --git a/lib/gitano/command.lua b/lib/gitano/command.lua
index e55fe33..468b34b 100644
--- a/lib/gitano/command.lua
+++ b/lib/gitano/command.lua
@@ -13,9 +13,27 @@ local sio = require "luxio.simple"
local cmds = {}
+local function default_detect_repo(config, parsed_cmdline)
+ local repo, msg
+ if #parsed_cmdline > 1 then
+ -- Acquire the repository object for the target repo from arg 2
+ repo, msg = repository.find(config, parsed_cmdline[2])
+ if not repo then
+ log.critical("Unable to locate repository.")
+ log.critical(" * " .. (tostring(msg)))
+ return nil, nil
+ end
+
+ if repo.is_nascent then
+ log.info("Repository " .. repo.name .. " is nascent")
+ end
+ end
+ return repo, parsed_cmdline
+end
+
local function register_cmd(cmdname, short, helptext,
validate_fn, prep_fn, run_fn,
- takes_repo, hidden, is_admin)
+ takes_repo, hidden, is_admin, detect_repo)
--[[
log.ddebug("Register command", cmdname)
if takes_repo then
@@ -35,7 +53,8 @@ local function register_cmd(cmdname, short, helptext,
hidden = hidden,
admin = is_admin,
short = short,
- helptext = helptext
+ helptext = helptext,
+ detect_repo = detect_repo or default_detect_repo
}
cmds[#cmds+1] = cmdname
table.sort(cmds)
@@ -52,6 +71,7 @@ local function get_cmd(cmdname)
prep = cmd.prep,
run = cmd.run,
takes_repo = cmd.takes_repo,
+ detect_repo = cmd.detect_repo
}
end
diff --git a/lib/gitano/plugins.lua b/lib/gitano/plugins.lua
new file mode 100644
index 0000000..bdc6d1e
--- /dev/null
+++ b/lib/gitano/plugins.lua
@@ -0,0 +1,63 @@
+-- gitano.plugins
+--
+-- Plugin loading support for Gitano
+--
+-- Copyright 2014 Daniel Silverstone <daniel.silverstone@codethink.co.uk>
+
+local util = require "gitano.util"
+local log = require "gitano.log"
+
+local luxio = require "luxio"
+local sio = require "luxio.simple"
+
+local gfind = string.gfind
+
+local plugin_name_pattern = "^(.+)%.lua$"
+
+local function find_plugins(path)
+ local ret = {}
+ for _, entry in ipairs(path) do
+ local dirp, err = sio.opendir(entry)
+ if not dirp then
+ log.warning(("Unable to scan plugin directory '%s': %s")
+ :format(entry, err))
+ else
+ for filename, fileinfo in dirp:iterate() do
+ local plugin_name = filename:match(plugin_name_pattern)
+ if plugin_name and fileinfo.d_type == luxio.DT_REG then
+ if not ret[plugin_name] then
+ ret[plugin_name] = entry
+ ret[#ret + 1] = plugin_name
+ end
+ end
+ end
+ end
+ end
+ table.sort(ret)
+ return ret
+end
+
+local function load_plugins(path)
+ local to_load = find_plugins(path)
+ for _, plugin_name in ipairs(to_load) do
+ local filepath = util.path_join(to_load[plugin_name],
+ plugin_name .. ".lua")
+ local chunk, err = loadfile(filepath)
+ if not chunk then
+ log.warning(("Failure loading plugin '%s' from '%s': %s")
+ :format(plugin_name, to_load[plugin_name],
+ err))
+ else
+ local ok, err = pcall(chunk)
+ if not ok then
+ log.warning(("Failure running plugin '%s' from '%s': %s")
+ :format(plugin_name, to_load[plugin_name],
+ err))
+ end
+ end
+ end
+end
+
+return {
+ load_plugins = load_plugins,
+}
diff --git a/plugins/demo.lua b/plugins/demo.lua
new file mode 100644
index 0000000..efac4c7
--- /dev/null
+++ b/plugins/demo.lua
@@ -0,0 +1,46 @@
+-- Demo Plugin
+--
+-- This is a demonstration plugin which will not be installed as part of
+-- Gitano. Its purpose is to show the way that a plugin can add commands
+-- to Gitano if it so desires.
+--
+-- Copyright 2014 Daniel Silverstone <daniel.silverstone@codethink.co.uk>
+
+local gitano = require "gitano"
+
+local demo_short_help = "Simple demo command"
+local demo_helptext = [[
+This is the long help text for the demonstration plugin 'demo' command.
+
+Enjoy.
+]]
+
+local function demo_validate(config, repo, cmdline)
+ if #cmdline ~= 2 then
+ gitano.log.error("usage: demo <reponame>")
+ return false
+ end
+ return true
+end
+
+local function demo_prep(config, repo, cmdline, context)
+ context.operation = "read"
+ return config.repo:run_lace(context)
+end
+
+local function demo_run(config, repo, cmdline, env)
+ local p = gitano.log.stdout
+ p(("Repo is: %s"):format(tostring(repo)))
+ for i, n in ipairs(cmdline) do
+ p(("cmdline[%d] is: %s"):format(i, tostring(n)))
+ end
+ for k, v in pairs(env) do
+ p(("env[%s] is: %s"):format(k, tostring(v)))
+ end
+ return "exit", 0
+end
+
+assert(gitano.command.register("demo",
+ demo_short_help, demo_helptext,
+ demo_validate, demo_prep, demo_run,
+ true, false, false))
diff --git a/plugins/rsync.lua b/plugins/rsync.lua
new file mode 100644
index 0000000..da4bdb7
--- /dev/null
+++ b/plugins/rsync.lua
@@ -0,0 +1,108 @@
+-- rsync Plugin
+--
+-- This plugin is part of Trove. Trove is Codethink's Baserock central server
+-- and uses Gitano as the Git service. This plugin adds support to Trove for
+-- supporting 'rsync' as a command in Gitano. This means that every repository
+-- has an rsync tree attached to it which can be used by remote ends sshing
+-- into the Trove.
+--
+-- Since the functionality is generically useful however, it is supported here
+-- in Gitano upstream.
+--
+-- Copyright 2014 Daniel Silverstone <daniel.silverstone@codethink.co.uk>
+
+local gitano = require "gitano"
+
+local sp = require "luxio.subprocess"
+
+local rsync_short_help = "An rsync endpoint within Gitano"
+local rsync_helptext = [[
+Users are not expected to use this command directly, but instead to
+instruct their local rsync to use a remote of user@gitanohost:reponame
+when pushing or pulling rsync content.
+]]
+
+local function rsync_detect_repo(config, cmdline)
+ local repo, msg
+ local repopath = cmdline[#cmdline]
+
+ if #cmdline < 4 then
+ -- Validate will fail with a nice error, give up now
+ return nil, cmdline
+ end
+
+ if repopath:match("^/") then
+ repopath = repopath:sub(2)
+ end
+
+ local origpath = repopath
+
+ -- Basically, while there's still something to the repopath
+ -- and we've not yet found a repo, strip an element and try again...
+ while not repo and repopath ~= ""do
+ repo, msg = gitano.repository.find(config, repopath)
+ if not repo then
+ repopath = repopath:match("^(.*)/[^/]*$") or ""
+ end
+ end
+
+ if not repo then
+ gitano.log.error("Unable to find a repository for " .. cmdline[#cmdline])
+ return nil, nil
+ end
+
+ if repo.is_nascent then
+ gitano.log.error("Repository " .. repo.name .. " is nascent")
+ gitano.log.error("Cannot use rsync command with nascent repositories")
+ return nil, nil
+ end
+
+ -- Okay, so repopath represented a repository, let's convert the path
+ -- into something which we can work with...
+ cmdline[#cmdline] = repo:fs_path() .. "/rsync" .. origpath:sub(#repopath+1)
+ if origpath:match("/$") and not (cmdline[#cmdline]):match("/$") then
+ cmdline[#cmdline] = cmdline[#cmdline] .. "/"
+ end
+
+ gitano.util.mkdir_p(repo:fs_path() .. "/rsync")
+
+ -- And give back the repo for ruleset running and the cmdline for the rsync
+
+ gitano.log.error(cmdline[#cmdline])
+
+ return repo, cmdline
+end
+
+local function rsync_validate(config, repo, cmdline)
+ if #cmdline < 4 then
+ gitano.log.error("usage: rsync --server <rsync arguments> . <server-side-path>")
+ return false
+ end
+ if cmdline[2] ~= "--server" then
+ gitano.log.error("Second cmdline element must always be --server")
+ return false
+ end
+ return true
+end
+
+local function rsync_prep(config, repo, cmdline, context)
+ if cmdline[3] == "--sender" then
+ context.operation = "read"
+ else
+ context.operation = "write"
+ end
+ return config.repo:run_lace(context)
+end
+
+local function rsync_run(config, repo, cmdline, env)
+ local cmdcopy = {env=env}
+ for i = 1, #cmdline do cmdcopy[i] = cmdline[i] end
+ local proc = sp.spawn(cmdcopy)
+ return proc:wait()
+end
+
+assert(gitano.command.register("rsync",
+ rsync_short_help, rsync_helptext,
+ rsync_validate, rsync_prep, rsync_run,
+ true, false, false,
+ rsync_detect_repo))
diff --git a/utils/install-lua-bin b/utils/install-lua-bin
index 4d7322c..3353a34 100644
--- a/utils/install-lua-bin
+++ b/utils/install-lua-bin
@@ -1,6 +1,6 @@
-- Run this explicitly through -*- Lua -*-
-local lua_bin, inst_share_path, inst_bin_path, inst_mod_path, input_name, output_name = ...
+local lua_bin, inst_share_path, inst_bin_path, inst_mod_path, inst_plugin_path, input_name, output_name = ...
local input_fh = assert(io.open(input_name, "r"))
local output_fh = assert(io.open(output_name, "w"))
@@ -20,6 +20,16 @@ for path_elem in package.path:gmatch("([^;]+)") do
end
end
+do
+ -- transform the plugin path from colon separated to a table
+ -- for interpolation
+ local path = {}
+ for entry in string.gfind(inst_plugin_path, "([^:]+)") do
+ path[#path+1] = ("%q"):format(entry)
+ end
+ inst_plugin_path = ("{%s}"):format(table.concat(path, ", "))
+end
+
while line do
local token = line:match("^%-%- @@(.+)$")
if token then
@@ -38,6 +48,8 @@ while line do
output_fh:write(("gitano.config.lib_bin_path(%q)\n"):format(inst_bin_path))
elseif token == "GITANO_SHARE_PATH" then
output_fh:write(("gitano.config.share_path(%q)\n"):format(inst_share_path))
+ elseif token == "GITANO_PLUGIN_PATH" then
+ output_fh:write(("gitano.plugins.load_plugins %s\n"):format(inst_plugin_path))
else
output_fh:write("-- Unknown token: " .. token .. "\n")
end