Raw
1 #include "git-compat-util.h"
2 #include "abspath.h"
3 #include "advice.h"
4 #include "config.h"
5 #include "environment.h"
6 #include "gettext.h"
7 #include "hook.h"
8 #include "hook-list.h"
9 #include "parse.h"
10 #include "path.h"
11 #include "run-command.h"
12 #include "setup.h"
13 #include "strbuf.h"
14 #include "strmap.h"
15 #include "thread-utils.h"
16
17 bool is_known_hook(const char *name)
18 {
19 const char **h;
20 for (h = hook_name_list; *h; h++)
21 if (!strcmp(*h, name))
22 return true;
23 return false;
24 }
25
26 const char *find_hook(struct repository *r, const char *name)
27 {
28 static struct strbuf path = STRBUF_INIT;
29
30 int found_hook;
31
32 if (!r || !r->gitdir)
33 return NULL;
34
35 repo_git_path_replace(r, &path, "hooks/%s", name);
36 found_hook = access(path.buf, X_OK) >= 0;
37 #ifdef STRIP_EXTENSION
38 if (!found_hook) {
39 int err = errno;
40
41 strbuf_addstr(&path, STRIP_EXTENSION);
42 found_hook = access(path.buf, X_OK) >= 0;
43 if (!found_hook)
44 errno = err;
45 }
46 #endif
47
48 if (!found_hook) {
49 if (errno == EACCES && advice_enabled(ADVICE_IGNORED_HOOK)) {
50 static struct string_list advise_given = STRING_LIST_INIT_DUP;
51
52 if (!string_list_lookup(&advise_given, name)) {
53 string_list_insert(&advise_given, name);
54 advise(_("The '%s' hook was ignored because "
55 "it's not set as executable.\n"
56 "You can disable this warning with "
57 "`git config set advice.ignoredHook false`."),
58 path.buf);
59 }
60 }
61 return NULL;
62 }
63 return path.buf;
64 }
65
66 void hook_free(void *p, const char *str UNUSED)
67 {
68 struct hook *h = p;
69
70 if (!h)
71 return;
72
73 if (h->kind == HOOK_TRADITIONAL) {
74 free((void *)h->u.traditional.path);
75 } else if (h->kind == HOOK_CONFIGURED) {
76 free((void *)h->u.configured.friendly_name);
77 free((void *)h->u.configured.command);
78 }
79
80 if (h->data_free && h->feed_pipe_cb_data)
81 h->data_free(h->feed_pipe_cb_data);
82
83 free(h);
84 }
85
86 /* Helper to detect and add default "traditional" hooks from the hookdir. */
87 static void list_hooks_add_default(struct repository *r, const char *hookname,
88 struct string_list *hook_list,
89 struct run_hooks_opt *options)
90 {
91 const char *hook_path = find_hook(r, hookname);
92 struct hook *h;
93
94 if (!hook_path)
95 return;
96
97 CALLOC_ARRAY(h, 1);
98
99 /*
100 * If the hook is to run in a specific dir, a relative path can
101 * become invalid in that dir, so convert to an absolute path.
102 */
103 if (options && options->dir)
104 hook_path = absolute_path(hook_path);
105
106 /*
107 * Setup per-hook internal state callback data.
108 * When provided, the alloc/free callbacks are always provided
109 * together, so use them to alloc/free the internal hook state.
110 */
111 if (options && options->feed_pipe_cb_data_alloc) {
112 h->feed_pipe_cb_data = options->feed_pipe_cb_data_alloc(options->feed_pipe_ctx);
113 h->data_free = options->feed_pipe_cb_data_free;
114 }
115
116 h->kind = HOOK_TRADITIONAL;
117 h->u.traditional.path = xstrdup(hook_path);
118
119 string_list_append(hook_list, hook_path)->util = h;
120 }
121
122 /*
123 * Cache entry stored as the .util pointer of string_list items inside the
124 * hook config cache.
125 */
126 struct hook_config_cache_entry {
127 char *command;
128 enum config_scope scope;
129 bool disabled;
130 bool parallel;
131 };
132
133 /*
134 * Callback struct to collect all hook.* keys in a single config pass.
135 * commands: friendly-name to command map.
136 * event_hooks: event-name to list of friendly-names map.
137 * disabled_hooks: set of all names with hook.<name>.enabled = false; after
138 * parsing, names that are not friendly-names become event-level
139 * disables stored in r->disabled_events. This collects all.
140 * parallel_hooks: friendly-name to parallel flag.
141 * event_jobs: event-name to per-event jobs count (stored as uintptr_t, NULL == unset).
142 * jobs: value of the global hook.jobs key. Defaults to 0 if unset (stored in r->hook_jobs).
143 */
144 struct hook_all_config_cb {
145 struct strmap commands;
146 struct strmap event_hooks;
147 struct string_list disabled_hooks;
148 struct strmap parallel_hooks;
149 struct strmap event_jobs;
150 unsigned int jobs;
151 };
152
153 /* repo_config() callback that collects all hook.* configuration in one pass. */
154 static int hook_config_lookup_all(const char *key, const char *value,
155 const struct config_context *ctx,
156 void *cb_data)
157 {
158 struct hook_all_config_cb *data = cb_data;
159 const char *name, *subkey;
160 char *hook_name;
161 size_t name_len = 0;
162
163 if (parse_config_key(key, "hook", &name, &name_len, &subkey))
164 return 0;
165
166 /* Handle plain hook.<key> entries that have no hook name component. */
167 if (!name) {
168 if (!strcmp(subkey, "jobs") && value) {
169 int v;
170 if (!git_parse_int(value, &v))
171 warning(_("hook.jobs must be an integer, ignoring: '%s'"), value);
172 else if (v == -1)
173 data->jobs = online_cpus();
174 else if (v > 0)
175 data->jobs = v;
176 else
177 warning(_("hook.jobs must be a positive integer"
178 " or -1, ignoring: '%s'"),
179 value);
180 }
181 return 0;
182 }
183
184 if (!value)
185 return config_error_nonbool(key);
186
187 /* Extract name, ensuring it is null-terminated. */
188 hook_name = xmemdupz(name, name_len);
189
190 if (!strcmp(subkey, "event")) {
191 if (!*value) {
192 /* Empty values reset previous events for this hook. */
193 struct hashmap_iter iter;
194 struct strmap_entry *e;
195
196 strmap_for_each_entry(&data->event_hooks, &iter, e)
197 unsorted_string_list_remove(e->value, hook_name, 0);
198 } else {
199 struct string_list *hooks;
200
201 if (is_known_hook(hook_name))
202 die(_("hook friendly-name '%s' collides with "
203 "a known event name; please choose a "
204 "different friendly-name"),
205 hook_name);
206
207 if (!strcmp(hook_name, value))
208 warning(_("hook friendly-name '%s' is the "
209 "same as its event; this may cause "
210 "ambiguity with hook.%s.enabled"),
211 hook_name, hook_name);
212
213 hooks = strmap_get(&data->event_hooks, value);
214
215 if (!hooks) {
216 CALLOC_ARRAY(hooks, 1);
217 string_list_init_dup(hooks);
218 strmap_put(&data->event_hooks, value, hooks);
219 }
220
221 /* Re-insert if necessary to preserve last-seen order. */
222 unsorted_string_list_remove(hooks, hook_name, 0);
223
224 if (!ctx->kvi)
225 BUG("hook config callback called without key-value info");
226
227 /*
228 * Stash the config scope in the util pointer for
229 * later retrieval in build_hook_config_map(). This
230 * intermediate struct is transient and never leaves
231 * that function, so we pack the enum value into the
232 * pointer rather than heap-allocating a wrapper.
233 */
234 string_list_append(hooks, hook_name)->util =
235 (void *)(uintptr_t)ctx->kvi->scope;
236 }
237 } else if (!strcmp(subkey, "command")) {
238 /* Store command overwriting the old value */
239 char *old = strmap_put(&data->commands, hook_name,
240 xstrdup(value));
241 free(old);
242 } else if (!strcmp(subkey, "enabled")) {
243 switch (git_parse_maybe_bool(value)) {
244 case 0: /* disabled */
245 if (!unsorted_string_list_lookup(&data->disabled_hooks,
246 hook_name))
247 string_list_append(&data->disabled_hooks,
248 hook_name);
249 break;
250 case 1: /* enabled: undo a prior disabled entry */
251 unsorted_string_list_remove(&data->disabled_hooks,
252 hook_name, 0);
253 break;
254 default:
255 break; /* ignore unrecognised values */
256 }
257 } else if (!strcmp(subkey, "parallel")) {
258 int v = git_parse_maybe_bool(value);
259 if (v >= 0)
260 strmap_put(&data->parallel_hooks, hook_name,
261 (void *)(uintptr_t)v);
262 else
263 warning(_("hook.%s.parallel must be a boolean,"
264 " ignoring: '%s'"),
265 hook_name, value);
266 } else if (!strcmp(subkey, "jobs")) {
267 int v;
268 if (!git_parse_int(value, &v))
269 warning(_("hook.%s.jobs must be an integer,"
270 " ignoring: '%s'"),
271 hook_name, value);
272 else if (v == -1)
273 strmap_put(&data->event_jobs, hook_name,
274 (void *)(uintptr_t)online_cpus());
275 else if (v > 0)
276 strmap_put(&data->event_jobs, hook_name,
277 (void *)(uintptr_t)v);
278 else
279 warning(_("hook.%s.jobs must be a positive"
280 " integer or -1, ignoring: '%s'"),
281 hook_name, value);
282 }
283
284 free(hook_name);
285 return 0;
286 }
287
288 /*
289 * The hook config cache maps each hook event name to a string_list where
290 * every item's string is the hook's friendly-name and its util pointer is
291 * the corresponding command string. Both strings are owned by the map.
292 *
293 * Disabled hooks are kept in the cache with entry->disabled set, so that
294 * "git hook list" can display them. A non-disabled hook missing a command
295 * is fatal; a disabled hook missing a command emits a warning and is kept
296 * in the cache with entry->command = NULL.
297 */
298 void hook_cache_clear(struct strmap *cache)
299 {
300 struct hashmap_iter iter;
301 struct strmap_entry *e;
302
303 strmap_for_each_entry(cache, &iter, e) {
304 struct string_list *hooks = e->value;
305 for (size_t i = 0; i < hooks->nr; i++) {
306 struct hook_config_cache_entry *entry = hooks->items[i].util;
307 free(entry->command);
308 free(entry);
309 }
310 string_list_clear(hooks, 0);
311 free(hooks);
312 }
313 strmap_clear(cache, 0);
314 }
315
316 /*
317 * Return true if `name` is a hook friendly-name, i.e. it has at least one of
318 * .command, .event, or .parallel configured. These are the reliable clues
319 * that distinguish a friendly-name from an event name. Note: .enabled is
320 * deliberately excluded because it can appear under both namespaces.
321 */
322 static int is_friendly_name(struct hook_all_config_cb *cb, const char *name)
323 {
324 struct hashmap_iter iter;
325 struct strmap_entry *e;
326
327 if (strmap_get(&cb->commands, name) || strmap_get(&cb->parallel_hooks, name))
328 return 1;
329
330 strmap_for_each_entry(&cb->event_hooks, &iter, e) {
331 if (unsorted_string_list_lookup(e->value, name))
332 return 1;
333 }
334
335 return 0;
336 }
337
338 /* Warn if any name in event_jobs is also a hook friendly-name. */
339 static void warn_jobs_on_friendly_names(struct hook_all_config_cb *cb_data)
340 {
341 struct hashmap_iter iter;
342 struct strmap_entry *e;
343
344 strmap_for_each_entry(&cb_data->event_jobs, &iter, e) {
345 if (is_friendly_name(cb_data, e->key))
346 warning(_("hook.%s.jobs is set but '%s' looks like a "
347 "hook friendly-name, not an event name; "
348 "hook.<event>.jobs uses the event name "
349 "(e.g. hook.post-receive.jobs), so this "
350 "setting will be ignored"), e->key, e->key);
351 }
352 }
353
354 /* Populate `cache` with the complete hook configuration */
355 static void build_hook_config_map(struct repository *r, struct strmap *cache)
356 {
357 struct hook_all_config_cb cb_data = { 0 };
358 struct hashmap_iter iter;
359 struct strmap_entry *e;
360
361 strmap_init(&cb_data.commands);
362 strmap_init(&cb_data.event_hooks);
363 string_list_init_dup(&cb_data.disabled_hooks);
364 strmap_init(&cb_data.parallel_hooks);
365 strmap_init(&cb_data.event_jobs);
366
367 /* Parse all configs in one run, capturing hook.* including hook.jobs. */
368 repo_config(r, hook_config_lookup_all, &cb_data);
369
370 warn_jobs_on_friendly_names(&cb_data);
371
372 /*
373 * Populate disabled_events: names in disabled_hooks that are not
374 * friendly-names are event-level switches (hook.<event>.enabled = false).
375 * Names that are friendly-names are already handled per-hook via the
376 * hook_config_cache_entry.disabled flag below.
377 */
378 if (r) {
379 string_list_clear(&r->disabled_events, 0);
380 string_list_init_dup(&r->disabled_events);
381 for (size_t i = 0; i < cb_data.disabled_hooks.nr; i++) {
382 const char *n = cb_data.disabled_hooks.items[i].string;
383 if (!is_friendly_name(&cb_data, n))
384 string_list_append(&r->disabled_events, n);
385 }
386 }
387
388 /* Construct the cache from parsed configs. */
389 strmap_for_each_entry(&cb_data.event_hooks, &iter, e) {
390 struct string_list *hook_names = e->value;
391 struct string_list *hooks;
392
393 CALLOC_ARRAY(hooks, 1);
394 string_list_init_dup(hooks);
395
396 for (size_t i = 0; i < hook_names->nr; i++) {
397 const char *hname = hook_names->items[i].string;
398 enum config_scope scope =
399 (enum config_scope)(uintptr_t)hook_names->items[i].util;
400 struct hook_config_cache_entry *entry;
401 char *command;
402
403 bool is_par = !!strmap_get(&cb_data.parallel_hooks, hname);
404 bool is_disabled =
405 !!unsorted_string_list_lookup(
406 &cb_data.disabled_hooks, hname);
407
408 command = strmap_get(&cb_data.commands, hname);
409 if (!command) {
410 if (is_disabled)
411 warning(_("disabled hook '%s' has no "
412 "command configured"), hname);
413 else
414 die(_("'hook.%s.command' must be configured or "
415 "'hook.%s.event' must be removed;"
416 " aborting."), hname, hname);
417 }
418
419 /* util stores a cache entry; owned by the cache. */
420 CALLOC_ARRAY(entry, 1);
421 entry->command = xstrdup_or_null(command);
422 entry->scope = scope;
423 entry->disabled = is_disabled;
424 entry->parallel = is_par;
425 string_list_append(hooks, hname)->util = entry;
426 }
427
428 strmap_put(cache, e->key, hooks);
429 }
430
431 if (r) {
432 r->hook_jobs = cb_data.jobs;
433 r->event_jobs = cb_data.event_jobs;
434 }
435
436 strmap_clear(&cb_data.commands, 1);
437 strmap_clear(&cb_data.parallel_hooks, 0); /* values are uintptr_t, not heap ptrs */
438 string_list_clear(&cb_data.disabled_hooks, 0);
439 strmap_for_each_entry(&cb_data.event_hooks, &iter, e) {
440 string_list_clear(e->value, 0);
441 free(e->value);
442 }
443 strmap_clear(&cb_data.event_hooks, 0);
444 }
445
446 /*
447 * Return the hook config map for `r`, populating it first if needed.
448 *
449 * Out-of-repo calls (r->gitdir == NULL) allocate and return a temporary
450 * cache map; the caller is responsible for freeing it with
451 * hook_cache_clear() + free().
452 */
453 static struct strmap *get_hook_config_cache(struct repository *r)
454 {
455 struct strmap *cache = NULL;
456
457 if (r && r->gitdir) {
458 /*
459 * For in-repo calls, the map is stored in r->hook_config_cache,
460 * so repeated invocations don't parse the configs, so allocate
461 * it just once on the first call.
462 */
463 if (!r->hook_config_cache) {
464 CALLOC_ARRAY(r->hook_config_cache, 1);
465 strmap_init(r->hook_config_cache);
466 build_hook_config_map(r, r->hook_config_cache);
467 }
468 cache = r->hook_config_cache;
469 } else {
470 /*
471 * Out-of-repo calls (no gitdir) allocate and return a temporary
472 * cache which gets freed immediately by the caller.
473 */
474 CALLOC_ARRAY(cache, 1);
475 strmap_init(cache);
476 build_hook_config_map(r, cache);
477 }
478
479 return cache;
480 }
481
482 static void list_hooks_add_configured(struct repository *r,
483 const char *hookname,
484 struct string_list *list,
485 struct run_hooks_opt *options)
486 {
487 struct strmap *cache = get_hook_config_cache(r);
488 struct string_list *configured_hooks = strmap_get(cache, hookname);
489 bool event_is_disabled = r ? !!unsorted_string_list_lookup(&r->disabled_events,
490 hookname) : 0;
491
492 /* Iterate through configured hooks and initialize internal states */
493 for (size_t i = 0; configured_hooks && i < configured_hooks->nr; i++) {
494 const char *friendly_name = configured_hooks->items[i].string;
495 struct hook_config_cache_entry *entry = configured_hooks->items[i].util;
496 struct hook *hook;
497
498 CALLOC_ARRAY(hook, 1);
499
500 /*
501 * When provided, the alloc/free callbacks are always provided
502 * together, so use them to alloc/free the internal hook state.
503 */
504 if (options && options->feed_pipe_cb_data_alloc) {
505 hook->feed_pipe_cb_data =
506 options->feed_pipe_cb_data_alloc(
507 options->feed_pipe_ctx);
508 hook->data_free = options->feed_pipe_cb_data_free;
509 }
510
511 hook->kind = HOOK_CONFIGURED;
512 hook->u.configured.friendly_name = xstrdup(friendly_name);
513 hook->u.configured.command =
514 entry->command ? xstrdup(entry->command) : NULL;
515 hook->u.configured.scope = entry->scope;
516 hook->u.configured.disabled = entry->disabled;
517 hook->u.configured.event_disabled = event_is_disabled;
518 hook->parallel = entry->parallel;
519
520 string_list_append(list, friendly_name)->util = hook;
521 }
522
523 /*
524 * Cleanup temporary cache for out-of-repo calls since they can't be
525 * stored persistently. Next out-of-repo calls will have to re-parse.
526 */
527 if (!r || !r->gitdir) {
528 hook_cache_clear(cache);
529 free(cache);
530 if (r)
531 string_list_clear(&r->disabled_events, 0);
532 }
533 }
534
535 struct string_list *list_hooks(struct repository *r, const char *hookname,
536 struct run_hooks_opt *options)
537 {
538 struct string_list *hook_head;
539
540 if (!hookname)
541 BUG("null hookname was provided to hook_list()!");
542
543 CALLOC_ARRAY(hook_head, 1);
544 string_list_init_dup(hook_head);
545
546 /* Add hooks from the config, e.g. hook.myhook.event = pre-commit */
547 list_hooks_add_configured(r, hookname, hook_head, options);
548
549 /* Add the default "traditional" hooks from hookdir. */
550 list_hooks_add_default(r, hookname, hook_head, options);
551
552 return hook_head;
553 }
554
555 int hook_exists(struct repository *r, const char *name)
556 {
557 struct string_list *hooks = list_hooks(r, name, NULL);
558 int exists = 0;
559
560 for (size_t i = 0; i < hooks->nr; i++) {
561 struct hook *h = hooks->items[i].util;
562 if (h->kind == HOOK_TRADITIONAL ||
563 (!h->u.configured.disabled && !h->u.configured.event_disabled)) {
564 exists = 1;
565 break;
566 }
567 }
568 string_list_clear_func(hooks, hook_free);
569 free(hooks);
570 return exists;
571 }
572
573 static int pick_next_hook(struct child_process *cp,
574 struct strbuf *out UNUSED,
575 void *pp_cb,
576 void **pp_task_cb)
577 {
578 struct hook_cb_data *hook_cb = pp_cb;
579 struct string_list *hook_list = hook_cb->hook_command_list;
580 struct hook *h;
581
582 do {
583 if (hook_cb->hook_to_run_index >= hook_list->nr)
584 return 0;
585 h = hook_list->items[hook_cb->hook_to_run_index++].util;
586 } while (h->kind == HOOK_CONFIGURED &&
587 (h->u.configured.disabled || h->u.configured.event_disabled));
588
589 cp->no_stdin = 1;
590 strvec_pushv(&cp->env, hook_cb->options->env.v);
591
592 if (hook_cb->options->path_to_stdin && hook_cb->options->feed_pipe)
593 BUG("options path_to_stdin and feed_pipe are mutually exclusive");
594
595 /* reopen the file for stdin; run_command closes it. */
596 if (hook_cb->options->path_to_stdin) {
597 cp->no_stdin = 0;
598 cp->in = xopen(hook_cb->options->path_to_stdin, O_RDONLY);
599 }
600
601 if (hook_cb->options->feed_pipe) {
602 cp->no_stdin = 0;
603 /* start_command() will allocate a pipe / stdin fd for us */
604 cp->in = -1;
605 }
606
607 cp->stdout_to_stderr = hook_cb->options->stdout_to_stderr;
608 cp->trace2_hook_name = hook_cb->hook_name;
609 cp->dir = hook_cb->options->dir;
610
611 /* Add hook exec paths or commands */
612 if (h->kind == HOOK_TRADITIONAL) {
613 strvec_push(&cp->args, h->u.traditional.path);
614 } else if (h->kind == HOOK_CONFIGURED) {
615 /* to enable oneliners, let config-specified hooks run in shell. */
616 cp->use_shell = true;
617 if (!h->u.configured.command)
618 BUG("non-disabled HOOK_CONFIGURED hook has no command");
619 strvec_push(&cp->args, h->u.configured.command);
620 } else {
621 BUG("unknown hook kind");
622 }
623
624 if (!cp->args.nr)
625 BUG("hook must have at least one command or exec path");
626
627 strvec_pushv(&cp->args, hook_cb->options->args.v);
628
629 /*
630 * Provide per-hook internal state via task_cb for easy access, so
631 * hook callbacks don't have to go through hook_cb->options.
632 */
633 *pp_task_cb = h->feed_pipe_cb_data;
634
635 return 1;
636 }
637
638 static int notify_start_failure(struct strbuf *out UNUSED,
639 void *pp_cb,
640 void *pp_task_cp UNUSED)
641 {
642 struct hook_cb_data *hook_cb = pp_cb;
643
644 hook_cb->rc |= 1;
645
646 return 1;
647 }
648
649 static int notify_hook_finished(int result,
650 struct strbuf *out UNUSED,
651 void *pp_cb,
652 void *pp_task_cb UNUSED)
653 {
654 struct hook_cb_data *hook_cb = pp_cb;
655 struct run_hooks_opt *opt = hook_cb->options;
656
657 hook_cb->rc |= result;
658
659 if (opt->invoked_hook)
660 *opt->invoked_hook = 1;
661
662 return 0;
663 }
664
665 static void run_hooks_opt_clear(struct run_hooks_opt *options)
666 {
667 strvec_clear(&options->env);
668 strvec_clear(&options->args);
669 }
670
671 /*
672 * When running in parallel, stdout must be merged into stderr so
673 * run-command can buffer and de-interleave outputs correctly. This
674 * applies even to hooks like pre-push that normally keep stdout and
675 * stderr separate: the user has opted into parallelism, so the output
676 * stream behavior changes accordingly.
677 */
678 static void merge_output_if_parallel(struct run_hooks_opt *options)
679 {
680 if (options->jobs > 1)
681 options->stdout_to_stderr = 1;
682 }
683
684 static void warn_non_parallel_hooks_override(unsigned int jobs,
685 struct string_list *hook_list)
686 {
687 /* Don't warn for hooks running sequentially. */
688 if (jobs == 1)
689 return;
690
691 for (size_t i = 0; i < hook_list->nr; i++) {
692 struct hook *h = hook_list->items[i].util;
693 if (h->kind == HOOK_CONFIGURED && !h->parallel)
694 warning(_("hook '%s' is not marked as parallel=true, "
695 "running in parallel anyway due to -j%u"),
696 h->u.configured.friendly_name, jobs);
697 }
698 }
699
700 /* Resolve a hook.jobs config key, handling -1 as online_cpus(). */
701 static void resolve_hook_config_jobs(struct repository *r,
702 const char *key,
703 unsigned int *jobs)
704 {
705 int v;
706
707 if (repo_config_get_int(r, key, &v))
708 return;
709
710 if (v == -1)
711 *jobs = online_cpus();
712 else if (v > 0)
713 *jobs = v;
714 else
715 warning(_("%s must be a positive integer or -1,"
716 " ignoring: %d"), key, v);
717 }
718
719 /* Determine how many jobs to use for hook execution. */
720 static unsigned int get_hook_jobs(struct repository *r,
721 struct run_hooks_opt *options,
722 const char *hook_name,
723 struct string_list *hook_list)
724 {
725 /*
726 * An explicit job count overrides everything else: this covers both
727 * FORCE_SERIAL callers (for hooks that must never run in parallel)
728 * and the -j flag from the CLI. The CLI override is intentional: users
729 * may want to serialize hooks declared parallel or to parallelize more
730 * aggressively than the default.
731 */
732 if (options->jobs)
733 goto cleanup;
734
735 /*
736 * Use hook.jobs from the already-parsed config cache (in-repo), or
737 * fallback to a direct config lookup (out-of-repo).
738 * Default to 1 (serial execution) on failure.
739 */
740 options->jobs = 1;
741 if (r) {
742 if (r->gitdir && r->hook_config_cache) {
743 void *event_jobs;
744
745 if (r->hook_jobs)
746 options->jobs = r->hook_jobs;
747
748 event_jobs = strmap_get(&r->event_jobs, hook_name);
749 if (event_jobs)
750 options->jobs = (unsigned int)(uintptr_t)event_jobs;
751 } else {
752 char *key;
753
754 resolve_hook_config_jobs(r, "hook.jobs", &options->jobs);
755
756 key = xstrfmt("hook.%s.jobs", hook_name);
757 resolve_hook_config_jobs(r, key, &options->jobs);
758 free(key);
759 }
760 }
761
762 /*
763 * Cap to serial any configured hook not marked as parallel = true.
764 * This enforces the parallel = false default, even for "traditional"
765 * hooks from the hookdir which cannot be marked parallel = true.
766 * The same restriction applies whether jobs came from hook.jobs or
767 * hook.<event>.jobs.
768 */
769 for (size_t i = 0; i < hook_list->nr; i++) {
770 struct hook *h = hook_list->items[i].util;
771 if (h->kind == HOOK_CONFIGURED && !h->parallel) {
772 options->jobs = 1;
773 break;
774 }
775 }
776
777 cleanup:
778 merge_output_if_parallel(options);
779 warn_non_parallel_hooks_override(options->jobs, hook_list);
780 return options->jobs;
781 }
782
783 int run_hooks_opt(struct repository *r, const char *hook_name,
784 struct run_hooks_opt *options)
785 {
786 struct string_list *hook_list = list_hooks(r, hook_name, options);
787 struct hook_cb_data cb_data = {
788 .rc = 0,
789 .hook_name = hook_name,
790 .hook_command_list = hook_list,
791 .options = options,
792 };
793 int ret = 0;
794 unsigned int jobs = get_hook_jobs(r, options, hook_name, hook_list);
795 const struct run_process_parallel_opts opts = {
796 .tr2_category = "hook",
797 .tr2_label = hook_name,
798
799 .processes = jobs,
800 .ungroup = jobs == 1,
801
802 .get_next_task = pick_next_hook,
803 .start_failure = notify_start_failure,
804 .feed_pipe = options->feed_pipe,
805 .task_finished = notify_hook_finished,
806
807 .data = &cb_data,
808 };
809
810 if (!options)
811 BUG("a struct run_hooks_opt must be provided to run_hooks");
812
813 if (options->path_to_stdin && options->feed_pipe)
814 BUG("options path_to_stdin and feed_pipe are mutually exclusive");
815
816 /*
817 * Ensure cb_data copy and free functions are either provided together,
818 * or neither one is provided.
819 */
820 if (!options->feed_pipe_cb_data_alloc != !options->feed_pipe_cb_data_free)
821 BUG("feed_pipe_cb_data_alloc and feed_pipe_cb_data_free must be set together");
822
823 if (options->invoked_hook)
824 *options->invoked_hook = 0;
825
826 if (!cb_data.hook_command_list->nr) {
827 if (options->error_if_missing)
828 ret = error("cannot find a hook named %s", hook_name);
829 goto cleanup;
830 }
831
832 run_processes_parallel(&opts);
833 ret = cb_data.rc;
834 cleanup:
835 string_list_clear_func(cb_data.hook_command_list, hook_free);
836 free(cb_data.hook_command_list);
837 run_hooks_opt_clear(options);
838 return ret;
839 }
840
841 int run_hooks(struct repository *r, const char *hook_name)
842 {
843 struct run_hooks_opt opt = RUN_HOOKS_OPT_INIT;
844
845 return run_hooks_opt(r, hook_name, &opt);
846 }
847
848 int run_hooks_l(struct repository *r, const char *hook_name, ...)
849 {
850 struct run_hooks_opt opt = RUN_HOOKS_OPT_INIT;
851 va_list ap;
852 const char *arg;
853
854 va_start(ap, hook_name);
855 while ((arg = va_arg(ap, const char *)))
856 strvec_push(&opt.args, arg);
857 va_end(ap);
858
859 return run_hooks_opt(r, hook_name, &opt);
860 }