diff mbox series

[1/9] hook: run a list of hooks instead

Message ID 20210715232603.3415111-2-emilyshaffer@google.com (mailing list archive)
State Superseded
Headers show
Series config-based hooks restarted | expand

Commit Message

Emily Shaffer July 15, 2021, 11:25 p.m. UTC
To prepare for multihook support, teach hook.[hc] to take a list of
hooks at run_hooks and run_found_hooks. Right now the list is always one
entry, but in the future we will allow users to supply more than one
executable for a single hook event.

Signed-off-by: Emily Shaffer <emilyshaffer@google.com>
---
 builtin/hook.c |  9 +++---
 hook.c         | 85 ++++++++++++++++++++++++++++++++++++++++----------
 hook.h         | 15 ++++++++-
 3 files changed, 87 insertions(+), 22 deletions(-)
diff mbox series

Patch

diff --git a/builtin/hook.c b/builtin/hook.c
index 169a8dd08f..a41ff36da9 100644
--- a/builtin/hook.c
+++ b/builtin/hook.c
@@ -25,7 +25,7 @@  static int run(int argc, const char **argv, const char *prefix)
 	int rc = 0;
 	int ignore_missing = 0;
 	const char *hook_name;
-	const char *hook_path;
+	struct list_head *hooks;
 
 	struct option run_options[] = {
 		OPT_BOOL(0, "ignore-missing", &ignore_missing,
@@ -58,15 +58,16 @@  static int run(int argc, const char **argv, const char *prefix)
 	 * run_found_hooks() instead...
 	 */
 	hook_name = argv[0];
-	hook_path = find_hook(hook_name);
-	if (!hook_path) {
+	hooks = hook_list(hook_name);
+	if (list_empty(hooks)) {
 		/* ... act like run_hooks() under --ignore-missing */
 		if (ignore_missing)
 			return 0;
 		error("cannot find a hook named %s", hook_name);
 		return 1;
 	}
-	rc = run_found_hooks(hook_name, hook_path, &opt);
+
+	rc = run_found_hooks(hook_name, hooks, &opt);
 
 	run_hooks_opt_clear(&opt);
 
diff --git a/hook.c b/hook.c
index 31e822bf51..c1dac6982f 100644
--- a/hook.c
+++ b/hook.c
@@ -4,6 +4,28 @@ 
 #include "hook-list.h"
 #include "config.h"
 
+static void free_hook(struct hook *ptr)
+{
+	if (ptr) {
+		free(ptr->feed_pipe_cb_data);
+	}
+	free(ptr);
+}
+
+static void remove_hook(struct list_head *to_remove)
+{
+	struct hook *hook_to_remove = list_entry(to_remove, struct hook, list);
+	list_del(to_remove);
+	free_hook(hook_to_remove);
+}
+
+void clear_hook_list(struct list_head *head)
+{
+	struct list_head *pos, *tmp;
+	list_for_each_safe(pos, tmp, head)
+		remove_hook(pos);
+}
+
 static int known_hook(const char *name)
 {
 	const char **p;
@@ -71,6 +93,30 @@  int hook_exists(const char *name)
 	return !!find_hook(name);
 }
 
+struct list_head* hook_list(const char* hookname)
+{
+	struct list_head *hook_head = xmalloc(sizeof(struct list_head));
+
+	INIT_LIST_HEAD(hook_head);
+
+	if (!hookname)
+		return NULL;
+
+	if (have_git_dir()) {
+		const char *hook_path = find_hook(hookname);
+
+		/* Add the hook from the hookdir */
+		if (hook_path) {
+			struct hook *to_add = xmalloc(sizeof(*to_add));
+			to_add->hook_path = hook_path;
+			to_add->feed_pipe_cb_data = NULL;
+			list_add_tail(&to_add->list, hook_head);
+		}
+	}
+
+	return hook_head;
+}
+
 void run_hooks_opt_clear(struct run_hooks_opt *o)
 {
 	strvec_clear(&o->env);
@@ -108,6 +154,8 @@  static int pick_next_hook(struct child_process *cp,
 	struct hook_cb_data *hook_cb = pp_cb;
 	struct hook *run_me = hook_cb->run_me;
 
+	if (!run_me)
+		return 0;
 
 	/* reopen the file for stdin; run_command closes it. */
 	if (hook_cb->options->path_to_stdin) {
@@ -126,7 +174,10 @@  static int pick_next_hook(struct child_process *cp,
 	cp->dir = hook_cb->options->dir;
 
 	/* add command */
-	strvec_push(&cp->args, run_me->hook_path);
+	if (hook_cb->options->absolute_path)
+		strvec_push(&cp->args, absolute_path(run_me->hook_path));
+	else
+		strvec_push(&cp->args, run_me->hook_path);
 
 	/*
 	 * add passed-in argv, without expanding - let the user get back
@@ -137,6 +188,13 @@  static int pick_next_hook(struct child_process *cp,
 	/* Provide context for errors if necessary */
 	*pp_task_cb = run_me;
 
+	/* Get the next entry ready */
+	if (hook_cb->run_me->list.next == hook_cb->head)
+		hook_cb->run_me = NULL;
+	else
+		hook_cb->run_me = list_entry(hook_cb->run_me->list.next,
+					     struct hook, list);
+
 	return 1;
 }
 
@@ -170,24 +228,17 @@  static int notify_hook_finished(int result,
 	return 1;
 }
 
-int run_found_hooks(const char *hook_name, const char *hook_path,
+int run_found_hooks(const char *hook_name, struct list_head *hooks,
 		    struct run_hooks_opt *options)
 {
-	struct strbuf abs_path = STRBUF_INIT;
-	struct hook my_hook = {
-		.hook_path = hook_path,
-	};
 	struct hook_cb_data cb_data = {
 		.rc = 0,
 		.hook_name = hook_name,
 		.options = options,
 		.invoked_hook = options->invoked_hook,
 	};
-	if (options->absolute_path) {
-		strbuf_add_absolute_path(&abs_path, hook_path);
-		my_hook.hook_path = abs_path.buf;
-	}
-	cb_data.run_me = &my_hook;
+
+	cb_data.run_me = list_first_entry(hooks, struct hook, list);
 
 	if (options->jobs != 1)
 		BUG("we do not handle %d or any other != 1 job number yet", options->jobs);
@@ -201,15 +252,13 @@  int run_found_hooks(const char *hook_name, const char *hook_path,
 				   &cb_data,
 				   "hook",
 				   hook_name);
-	if (options->absolute_path)
-		strbuf_release(&abs_path);
 
 	return cb_data.rc;
 }
 
 int run_hooks(const char *hook_name, struct run_hooks_opt *options)
 {
-	const char *hook_path;
+	struct list_head *hooks;
 	int ret;
 	if (!options)
 		BUG("a struct run_hooks_opt must be provided to run_hooks");
@@ -217,15 +266,17 @@  int run_hooks(const char *hook_name, struct run_hooks_opt *options)
 	if (options->path_to_stdin && options->feed_pipe)
 		BUG("choose only one method to populate stdin");
 
-	hook_path = find_hook(hook_name);
+	hooks = hook_list(hook_name);
 
 	/*
 	 * If you need to act on a missing hook, use run_found_hooks()
 	 * instead
 	 */
-	if (!hook_path)
+	if (list_empty(hooks))
 		return 0;
 
-	ret = run_found_hooks(hook_name, hook_path, options);
+	ret = run_found_hooks(hook_name, hooks, options);
+
+	clear_hook_list(hooks);
 	return ret;
 }
diff --git a/hook.h b/hook.h
index 9d9171672d..b97237931b 100644
--- a/hook.h
+++ b/hook.h
@@ -3,6 +3,7 @@ 
 #include "strbuf.h"
 #include "strvec.h"
 #include "run-command.h"
+#include "list.h"
 
 /*
  * Returns the path to the hook file, or NULL if the hook is missing
@@ -17,6 +18,7 @@  const char *find_hook(const char *name);
 int hook_exists(const char *hookname);
 
 struct hook {
+	struct list_head list;
 	/* The path to the hook */
 	const char *hook_path;
 
@@ -27,6 +29,12 @@  struct hook {
 	void *feed_pipe_cb_data;
 };
 
+/*
+ * Provides a linked list of 'struct hook' detailing commands which should run
+ * in response to the 'hookname' event, in execution order.
+ */
+struct list_head* hook_list(const char *hookname);
+
 struct run_hooks_opt
 {
 	/* Environment vars to be set for each hook */
@@ -103,6 +111,7 @@  struct hook_cb_data {
 	/* rc reflects the cumulative failure state */
 	int rc;
 	const char *hook_name;
+	struct list_head *head;
 	struct hook *run_me;
 	struct run_hooks_opt *options;
 	int *invoked_hook;
@@ -120,6 +129,10 @@  int run_hooks(const char *hook_name, struct run_hooks_opt *options);
  * Takes an already resolved hook and runs it. Internally the simpler
  * run_hooks() will call this.
  */
-int run_found_hooks(const char *hookname, const char *hook_path,
+int run_found_hooks(const char *hookname, struct list_head *hooks,
 		    struct run_hooks_opt *options);
+
+/* Empties the list at 'head', calling 'free_hook()' on each entry */
+void clear_hook_list(struct list_head *head);
+
 #endif