Coverage Report

Created: 2024-09-16 06:10

/src/git/hook.c
Line
Count
Source (jump to first uncovered line)
1
#include "git-compat-util.h"
2
#include "abspath.h"
3
#include "advice.h"
4
#include "gettext.h"
5
#include "hook.h"
6
#include "path.h"
7
#include "run-command.h"
8
#include "config.h"
9
#include "strbuf.h"
10
#include "environment.h"
11
#include "setup.h"
12
13
const char *find_hook(struct repository *r, const char *name)
14
0
{
15
0
  static struct strbuf path = STRBUF_INIT;
16
17
0
  int found_hook;
18
19
0
  strbuf_reset(&path);
20
0
  strbuf_repo_git_path(&path, r, "hooks/%s", name);
21
0
  found_hook = access(path.buf, X_OK) >= 0;
22
#ifdef STRIP_EXTENSION
23
  if (!found_hook) {
24
    int err = errno;
25
26
    strbuf_addstr(&path, STRIP_EXTENSION);
27
    found_hook = access(path.buf, X_OK) >= 0;
28
    if (!found_hook)
29
      errno = err;
30
  }
31
#endif
32
33
0
  if (!found_hook) {
34
0
    if (errno == EACCES && advice_enabled(ADVICE_IGNORED_HOOK)) {
35
0
      static struct string_list advise_given = STRING_LIST_INIT_DUP;
36
37
0
      if (!string_list_lookup(&advise_given, name)) {
38
0
        string_list_insert(&advise_given, name);
39
0
        advise(_("The '%s' hook was ignored because "
40
0
           "it's not set as executable.\n"
41
0
           "You can disable this warning with "
42
0
           "`git config advice.ignoredHook false`."),
43
0
               path.buf);
44
0
      }
45
0
    }
46
0
    return NULL;
47
0
  }
48
0
  return path.buf;
49
0
}
50
51
int hook_exists(struct repository *r, const char *name)
52
0
{
53
0
  return !!find_hook(r, name);
54
0
}
55
56
static int pick_next_hook(struct child_process *cp,
57
        struct strbuf *out UNUSED,
58
        void *pp_cb,
59
        void **pp_task_cb UNUSED)
60
0
{
61
0
  struct hook_cb_data *hook_cb = pp_cb;
62
0
  const char *hook_path = hook_cb->hook_path;
63
64
0
  if (!hook_path)
65
0
    return 0;
66
67
0
  cp->no_stdin = 1;
68
0
  strvec_pushv(&cp->env, hook_cb->options->env.v);
69
  /* reopen the file for stdin; run_command closes it. */
70
0
  if (hook_cb->options->path_to_stdin) {
71
0
    cp->no_stdin = 0;
72
0
    cp->in = xopen(hook_cb->options->path_to_stdin, O_RDONLY);
73
0
  }
74
0
  cp->stdout_to_stderr = 1;
75
0
  cp->trace2_hook_name = hook_cb->hook_name;
76
0
  cp->dir = hook_cb->options->dir;
77
78
0
  strvec_push(&cp->args, hook_path);
79
0
  strvec_pushv(&cp->args, hook_cb->options->args.v);
80
81
  /*
82
   * This pick_next_hook() will be called again, we're only
83
   * running one hook, so indicate that no more work will be
84
   * done.
85
   */
86
0
  hook_cb->hook_path = NULL;
87
88
0
  return 1;
89
0
}
90
91
static int notify_start_failure(struct strbuf *out UNUSED,
92
        void *pp_cb,
93
        void *pp_task_cp UNUSED)
94
0
{
95
0
  struct hook_cb_data *hook_cb = pp_cb;
96
97
0
  hook_cb->rc |= 1;
98
99
0
  return 1;
100
0
}
101
102
static int notify_hook_finished(int result,
103
        struct strbuf *out UNUSED,
104
        void *pp_cb,
105
        void *pp_task_cb UNUSED)
106
0
{
107
0
  struct hook_cb_data *hook_cb = pp_cb;
108
0
  struct run_hooks_opt *opt = hook_cb->options;
109
110
0
  hook_cb->rc |= result;
111
112
0
  if (opt->invoked_hook)
113
0
    *opt->invoked_hook = 1;
114
115
0
  return 0;
116
0
}
117
118
static void run_hooks_opt_clear(struct run_hooks_opt *options)
119
0
{
120
0
  strvec_clear(&options->env);
121
0
  strvec_clear(&options->args);
122
0
}
123
124
int run_hooks_opt(struct repository *r, const char *hook_name,
125
      struct run_hooks_opt *options)
126
0
{
127
0
  struct strbuf abs_path = STRBUF_INIT;
128
0
  struct hook_cb_data cb_data = {
129
0
    .rc = 0,
130
0
    .hook_name = hook_name,
131
0
    .options = options,
132
0
  };
133
0
  const char *const hook_path = find_hook(r, hook_name);
134
0
  int ret = 0;
135
0
  const struct run_process_parallel_opts opts = {
136
0
    .tr2_category = "hook",
137
0
    .tr2_label = hook_name,
138
139
0
    .processes = 1,
140
0
    .ungroup = 1,
141
142
0
    .get_next_task = pick_next_hook,
143
0
    .start_failure = notify_start_failure,
144
0
    .task_finished = notify_hook_finished,
145
146
0
    .data = &cb_data,
147
0
  };
148
149
0
  if (!options)
150
0
    BUG("a struct run_hooks_opt must be provided to run_hooks");
151
152
0
  if (options->invoked_hook)
153
0
    *options->invoked_hook = 0;
154
155
0
  if (!hook_path && !options->error_if_missing)
156
0
    goto cleanup;
157
158
0
  if (!hook_path) {
159
0
    ret = error("cannot find a hook named %s", hook_name);
160
0
    goto cleanup;
161
0
  }
162
163
0
  cb_data.hook_path = hook_path;
164
0
  if (options->dir) {
165
0
    strbuf_add_absolute_path(&abs_path, hook_path);
166
0
    cb_data.hook_path = abs_path.buf;
167
0
  }
168
169
0
  run_processes_parallel(&opts);
170
0
  ret = cb_data.rc;
171
0
cleanup:
172
0
  strbuf_release(&abs_path);
173
0
  run_hooks_opt_clear(options);
174
0
  return ret;
175
0
}
176
177
int run_hooks(struct repository *r, const char *hook_name)
178
0
{
179
0
  struct run_hooks_opt opt = RUN_HOOKS_OPT_INIT;
180
181
0
  return run_hooks_opt(r, hook_name, &opt);
182
0
}
183
184
int run_hooks_l(struct repository *r, const char *hook_name, ...)
185
0
{
186
0
  struct run_hooks_opt opt = RUN_HOOKS_OPT_INIT;
187
0
  va_list ap;
188
0
  const char *arg;
189
190
0
  va_start(ap, hook_name);
191
0
  while ((arg = va_arg(ap, const char *)))
192
0
    strvec_push(&opt.args, arg);
193
0
  va_end(ap);
194
195
0
  return run_hooks_opt(r, hook_name, &opt);
196
0
}