summaryrefslogtreecommitdiff
path: root/lib/gitano/hooks.lua
blob: 0c3cba967f22abe1bebe7338f51ecf563a271dac (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
-- gitano.hooks
--
-- Hook management routines for Gitano
--
-- Copyright 2017 Daniel Silverstone <dsilvers@digital-scurf.org>
-- All rights reserved.
--
-- Redistribution and use in source and binary forms, with or without
-- modification, are permitted provided that the following conditions
-- are met:
-- 1. Redistributions of source code must retain the above copyright
--    notice, this list of conditions and the following disclaimer.
-- 2. Redistributions in binary form must reproduce the above copyright
--    notice, this list of conditions and the following disclaimer in the
--    documentation and/or other materials provided with the distribution.
-- 3. Neither the name of the author nor the names of their contributors
--    may be used to endorse or promote products derived from this software
--    without specific prior written permission.
--
-- THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
-- ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-- ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
-- FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-- DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-- OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-- HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-- LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-- OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-- SUCH DAMAGE.
--
--
--

local hooks = {}

-- In order to centralise and ensure hook names are good, these
-- are the names of the hooks Gitano uses internally.
-- Plugins are at liberty to add their own hooks if they want, but
-- Gitano is unlikely to call them itself.

local hook_names = {
   -- Called by gitano.auth.is_authorized to allow manipulation of the
   -- command line if wanted.  Hook functions should take the form:
   -- function preauth_cmdline_hook(cancel, config, ip, user, keytag, ...)
   --   -- Decide what to do, if we're rejecting the command then set cancel
   --   -- to something truthy such as:
   --   return "stop", true, "Some reason"
   --   -- otherwise try
   --   return "continue"
   --   -- or
   --   return "update", cancel, config, ip, user, keytag, ...
   -- end
   PREAUTH_CMDLINE = "PREAUTH_CMDLINE",
   -- Called by gitano-post-receive to allow processing to occur on the git
   -- post-receive event if needed.  The hook carries all the usual functions
   -- as well as any registered by plugins.
   -- Core admin stuff (running gitano-admin updates, update-server-info, etc)
   -- runs at -1000.  The supple hooks run at 0.  Hook functions take the form:
   -- function post_receive_hook(repo, updates)
   --   -- Decide what to do.  If we want to stop the hooks, return "stop"
   --   -- but only do that if we MUST, since it will alter expected behaviour.
   --   return "stop"
   --   -- Otherwise, normally we'd just continue
   --   return "continue"
   --   -- Finally we can update if we want to alter the updates table
   --   return "update", repo, different_updates
   -- end
   POST_RECEIVE = "POST_RECEIVE",
}

local function _get_hook(hookname)
   local ret = hooks[hookname] or {}
   hooks[hookname] = ret
   return ret
end

local function _sort(hooktab)
   table.sort(hooktab, function(a,b) return a[1] < b[1] end)
end

local function add_to_hook(hookname, priority, func)
   assert(hookname, "Cannot add to a nil hook")
   assert(type(priority) == "number", "Cannot use a non-numerical priority")
   assert(type(func) == "function", "Cannot use a non-function hook func")
   local h = _get_hook(hookname)
   h[#h+1] = {priority, func}
end

local function _allbutone(_, ...)
   return ...
end

local function run_hook(hookname, ...)
   assert(hookname, "Cannot run a nil hook")
   local h = _get_hook(hookname)
   _sort(h)
   local args = {...}
   for _, entry in ipairs(h) do
      local result = { entry[2](unpack(args)) }
      if result[1] == nil then
	 return unpack(result)
      elseif type(result[1]) ~= "string" then
	 return nil, "Bad results", unpack(result)
      elseif result[1] == "stop" then
	 return _allbutone(unpack(result))
      elseif result[1] == "update" then
	 args = {_allbutone(unpack(result))}
      elseif result[1] == "continue" then
	 -- Nothing to do
      else
	 return nil, "Bad results", unpack(result)
      end
   end
   return unpack(args)
end

-- Hook functions take the form:
-- action, ... = hookfunc(...)
-- where the ... is chained through, and returned verbatim at
-- the end.
-- action can be nil (on error) or else one of:
-- continue --> call the next hook function if there is one (not chaining ...)
-- update   --> as for 'continue' but chaining the ...
-- stop     --> stop now and return the rest of the results.

-- Wherever Gitano registers a hook for something, the hook priority
-- of zero will be Gitano's action.  So if you want to alter what is
-- passed to Gitano's default behaviour, register with a negative value
-- and if you want to just do more afterwards, register with a positive
-- value

return {
   add = add_to_hook,
   run = run_hook,
   names = hook_names,
}