diff options
-rw-r--r-- | Makefile | 20 | ||||
-rw-r--r-- | bin/gitano-auth.in | 17 | ||||
-rwxr-xr-x | bin/gitano-command.cgi.in | 1 | ||||
-rw-r--r-- | bin/gitano-post-receive-hook.in | 1 | ||||
-rw-r--r-- | bin/gitano-pre-receive-hook.in | 1 | ||||
-rw-r--r-- | bin/gitano-setup.in | 1 | ||||
-rwxr-xr-x | bin/gitano-smart-http.cgi.in | 1 | ||||
-rw-r--r-- | bin/gitano-update-hook.in | 1 | ||||
-rw-r--r-- | bin/gitano-update-ssh.in | 1 | ||||
-rw-r--r-- | lib/gitano.lua | 4 | ||||
-rw-r--r-- | lib/gitano/admincommand.lua | 16 | ||||
-rw-r--r-- | lib/gitano/auth.lua | 11 | ||||
-rw-r--r-- | lib/gitano/command.lua | 24 | ||||
-rw-r--r-- | lib/gitano/plugins.lua | 63 | ||||
-rw-r--r-- | plugins/demo.lua | 46 | ||||
-rw-r--r-- | plugins/rsync.lua | 108 | ||||
-rw-r--r-- | utils/install-lua-bin | 14 |
17 files changed, 291 insertions, 39 deletions
@@ -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 |