blob: f6306d72b31879705977a023a7865c3f1677404c [file] [log] [blame]
Elijah Newrenfc7bd512023-02-24 00:09:34 +00001#include "git-compat-util.h"
Calvin Wan5d1344b2023-06-06 19:48:39 +00002#include "abspath.h"
Elijah Newrenfc7bd512023-02-24 00:09:34 +00003#include "advice.h"
4#include "gettext.h"
Ævar Arnfjörð Bjarmason5e3aba32021-09-26 21:03:26 +02005#include "hook.h"
Elijah Newrend1cbe1e2023-04-22 20:17:20 +00006#include "path.h"
Ævar Arnfjörð Bjarmason5e3aba32021-09-26 21:03:26 +02007#include "run-command.h"
Emily Shaffer96e72252021-12-22 04:59:27 +01008#include "config.h"
Elijah Newrenfc7bd512023-02-24 00:09:34 +00009#include "strbuf.h"
Ævar Arnfjörð Bjarmason5e3aba32021-09-26 21:03:26 +020010
11const char *find_hook(const char *name)
12{
13 static struct strbuf path = STRBUF_INIT;
14
15 strbuf_reset(&path);
16 strbuf_git_path(&path, "hooks/%s", name);
17 if (access(path.buf, X_OK) < 0) {
18 int err = errno;
19
20#ifdef STRIP_EXTENSION
21 strbuf_addstr(&path, STRIP_EXTENSION);
22 if (access(path.buf, X_OK) >= 0)
23 return path.buf;
24 if (errno == EACCES)
25 err = errno;
26#endif
27
28 if (err == EACCES && advice_enabled(ADVICE_IGNORED_HOOK)) {
29 static struct string_list advise_given = STRING_LIST_INIT_DUP;
30
31 if (!string_list_lookup(&advise_given, name)) {
32 string_list_insert(&advise_given, name);
33 advise(_("The '%s' hook was ignored because "
34 "it's not set as executable.\n"
35 "You can disable this warning with "
36 "`git config advice.ignoredHook false`."),
37 path.buf);
38 }
39 }
40 return NULL;
41 }
42 return path.buf;
43}
Emily Shaffer330155e2021-09-26 21:03:27 +020044
45int hook_exists(const char *name)
46{
47 return !!find_hook(name);
48}
Emily Shaffer96e72252021-12-22 04:59:27 +010049
50static int pick_next_hook(struct child_process *cp,
Jeff Kinga5c76b32023-02-24 01:39:46 -050051 struct strbuf *out UNUSED,
Emily Shaffer96e72252021-12-22 04:59:27 +010052 void *pp_cb,
Jeff Kinga5c76b32023-02-24 01:39:46 -050053 void **pp_task_cb UNUSED)
Emily Shaffer96e72252021-12-22 04:59:27 +010054{
55 struct hook_cb_data *hook_cb = pp_cb;
56 const char *hook_path = hook_cb->hook_path;
57
58 if (!hook_path)
59 return 0;
60
61 cp->no_stdin = 1;
Ævar Arnfjörð Bjarmason29fda242022-06-02 11:09:50 +020062 strvec_pushv(&cp->env, hook_cb->options->env.v);
Emily Shaffer917e0802023-02-08 20:21:13 +010063 /* reopen the file for stdin; run_command closes it. */
64 if (hook_cb->options->path_to_stdin) {
65 cp->no_stdin = 0;
66 cp->in = xopen(hook_cb->options->path_to_stdin, O_RDONLY);
67 }
Emily Shaffer96e72252021-12-22 04:59:27 +010068 cp->stdout_to_stderr = 1;
69 cp->trace2_hook_name = hook_cb->hook_name;
Emily Shaffer1a3017d2021-12-22 04:59:36 +010070 cp->dir = hook_cb->options->dir;
Emily Shaffer96e72252021-12-22 04:59:27 +010071
72 strvec_push(&cp->args, hook_path);
73 strvec_pushv(&cp->args, hook_cb->options->args.v);
74
Emily Shaffer96e72252021-12-22 04:59:27 +010075 /*
76 * This pick_next_hook() will be called again, we're only
77 * running one hook, so indicate that no more work will be
78 * done.
79 */
80 hook_cb->hook_path = NULL;
81
82 return 1;
83}
84
Jeff Kinga5c76b32023-02-24 01:39:46 -050085static int notify_start_failure(struct strbuf *out UNUSED,
Emily Shaffer96e72252021-12-22 04:59:27 +010086 void *pp_cb,
Jeff Kinga5c76b32023-02-24 01:39:46 -050087 void *pp_task_cp UNUSED)
Emily Shaffer96e72252021-12-22 04:59:27 +010088{
89 struct hook_cb_data *hook_cb = pp_cb;
Emily Shaffer96e72252021-12-22 04:59:27 +010090
91 hook_cb->rc |= 1;
92
Emily Shaffer96e72252021-12-22 04:59:27 +010093 return 1;
94}
95
96static int notify_hook_finished(int result,
Jeff Kinga5c76b32023-02-24 01:39:46 -050097 struct strbuf *out UNUSED,
Emily Shaffer96e72252021-12-22 04:59:27 +010098 void *pp_cb,
Jeff Kinga5c76b32023-02-24 01:39:46 -050099 void *pp_task_cb UNUSED)
Emily Shaffer96e72252021-12-22 04:59:27 +0100100{
101 struct hook_cb_data *hook_cb = pp_cb;
Ævar Arnfjörð Bjarmasona8cc5942022-03-07 13:33:46 +0100102 struct run_hooks_opt *opt = hook_cb->options;
Emily Shaffer96e72252021-12-22 04:59:27 +0100103
104 hook_cb->rc |= result;
105
Ævar Arnfjörð Bjarmasona8cc5942022-03-07 13:33:46 +0100106 if (opt->invoked_hook)
107 *opt->invoked_hook = 1;
108
Emily Shaffer96e72252021-12-22 04:59:27 +0100109 return 0;
110}
111
112static void run_hooks_opt_clear(struct run_hooks_opt *options)
113{
114 strvec_clear(&options->env);
115 strvec_clear(&options->args);
116}
117
118int run_hooks_opt(const char *hook_name, struct run_hooks_opt *options)
119{
Emily Shaffer1a3017d2021-12-22 04:59:36 +0100120 struct strbuf abs_path = STRBUF_INIT;
Emily Shaffer96e72252021-12-22 04:59:27 +0100121 struct hook_cb_data cb_data = {
122 .rc = 0,
123 .hook_name = hook_name,
124 .options = options,
125 };
126 const char *const hook_path = find_hook(hook_name);
Emily Shaffer96e72252021-12-22 04:59:27 +0100127 int ret = 0;
Ævar Arnfjörð Bjarmason6e5ba0b2022-10-12 23:02:26 +0200128 const struct run_process_parallel_opts opts = {
129 .tr2_category = "hook",
130 .tr2_label = hook_name,
131
132 .processes = 1,
133 .ungroup = 1,
134
135 .get_next_task = pick_next_hook,
136 .start_failure = notify_start_failure,
137 .task_finished = notify_hook_finished,
138
139 .data = &cb_data,
140 };
Emily Shaffer96e72252021-12-22 04:59:27 +0100141
142 if (!options)
143 BUG("a struct run_hooks_opt must be provided to run_hooks");
144
Ævar Arnfjörð Bjarmasona8cc5942022-03-07 13:33:46 +0100145 if (options->invoked_hook)
146 *options->invoked_hook = 0;
147
Emily Shaffer96e72252021-12-22 04:59:27 +0100148 if (!hook_path && !options->error_if_missing)
149 goto cleanup;
150
151 if (!hook_path) {
152 ret = error("cannot find a hook named %s", hook_name);
153 goto cleanup;
154 }
155
156 cb_data.hook_path = hook_path;
Emily Shaffer1a3017d2021-12-22 04:59:36 +0100157 if (options->dir) {
158 strbuf_add_absolute_path(&abs_path, hook_path);
159 cb_data.hook_path = abs_path.buf;
160 }
161
Ævar Arnfjörð Bjarmason6e5ba0b2022-10-12 23:02:26 +0200162 run_processes_parallel(&opts);
Emily Shaffer96e72252021-12-22 04:59:27 +0100163 ret = cb_data.rc;
164cleanup:
Emily Shaffer1a3017d2021-12-22 04:59:36 +0100165 strbuf_release(&abs_path);
Emily Shaffer96e72252021-12-22 04:59:27 +0100166 run_hooks_opt_clear(options);
167 return ret;
168}
Ævar Arnfjörð Bjarmason474c1192021-12-22 04:59:28 +0100169
170int run_hooks(const char *hook_name)
171{
172 struct run_hooks_opt opt = RUN_HOOKS_OPT_INIT;
173
174 return run_hooks_opt(hook_name, &opt);
175}
Ævar Arnfjörð Bjarmasonab81cf22021-12-22 04:59:31 +0100176
177int run_hooks_l(const char *hook_name, ...)
178{
179 struct run_hooks_opt opt = RUN_HOOKS_OPT_INIT;
180 va_list ap;
181 const char *arg;
182
183 va_start(ap, hook_name);
184 while ((arg = va_arg(ap, const char *)))
185 strvec_push(&opt.args, arg);
186 va_end(ap);
187
188 return run_hooks_opt(hook_name, &opt);
189}