CLI: use configured hook directory

This enables support for hooks outside the database directory.
It relies strongly on configuration information being usable between
closing the database and destroying it.
This commit is contained in:
David Bremner 2021-01-10 07:50:14 -04:00
parent 0345bc57a0
commit f61d88c6f4
5 changed files with 109 additions and 92 deletions

View file

@ -24,14 +24,15 @@
#include <sys/wait.h> #include <sys/wait.h>
int int
notmuch_run_hook (const char *db_path, const char *hook) notmuch_run_hook (notmuch_database_t *notmuch, const char *hook)
{ {
char *hook_path; char *hook_path;
int status = 0; int status = 0;
pid_t pid; pid_t pid;
hook_path = talloc_asprintf (NULL, "%s/%s/%s/%s", db_path, ".notmuch", hook_path = talloc_asprintf (notmuch, "%s/%s",
"hooks", hook); notmuch_config_get (notmuch, NOTMUCH_CONFIG_HOOK_DIR),
hook);
if (hook_path == NULL) { if (hook_path == NULL) {
fprintf (stderr, "Out of memory\n"); fprintf (stderr, "Out of memory\n");
return 1; return 1;

View file

@ -339,7 +339,7 @@ const char *
_notmuch_config_get_path (notmuch_config_t *config); _notmuch_config_get_path (notmuch_config_t *config);
int int
notmuch_run_hook (const char *db_path, const char *hook); notmuch_run_hook (notmuch_database_t *notmuch, const char *hook);
bool bool
debugger_is_active (void); debugger_is_active (void);

View file

@ -481,7 +481,6 @@ notmuch_insert_command (unused(notmuch_config_t *config),notmuch_database_t *not
notmuch_process_shared_options (argv[0]); notmuch_process_shared_options (argv[0]);
/* XXX TODO replace this use of DATABASE_PATH with something specific to hooks */
db_path = notmuch_config_get (notmuch, NOTMUCH_CONFIG_DATABASE_PATH); db_path = notmuch_config_get (notmuch, NOTMUCH_CONFIG_DATABASE_PATH);
if (! db_path) if (! db_path)
@ -570,7 +569,7 @@ notmuch_insert_command (unused(notmuch_config_t *config),notmuch_database_t *not
status = add_file (notmuch, newpath, tag_ops, synchronize_flags, keep, indexing_cli_choices.opts); status = add_file (notmuch, newpath, tag_ops, synchronize_flags, keep, indexing_cli_choices.opts);
/* Commit changes. */ /* Commit changes. */
close_status = notmuch_database_destroy (notmuch); close_status = notmuch_database_close (notmuch);
if (close_status) { if (close_status) {
/* Hold on to the first error, if any. */ /* Hold on to the first error, if any. */
if (! status) if (! status)
@ -595,9 +594,11 @@ notmuch_insert_command (unused(notmuch_config_t *config),notmuch_database_t *not
if (hooks && status == NOTMUCH_STATUS_SUCCESS) { if (hooks && status == NOTMUCH_STATUS_SUCCESS) {
/* Ignore hook failures. */ /* Ignore hook failures. */
notmuch_run_hook (db_path, "post-insert"); notmuch_run_hook (notmuch, "post-insert");
} }
notmuch_database_destroy (notmuch);
talloc_free (local); talloc_free (local);
return status_to_exit (status); return status_to_exit (status);

View file

@ -1105,7 +1105,6 @@ notmuch_new_command (unused(notmuch_config_t *config), notmuch_database_t *notmu
struct timeval tv_start; struct timeval tv_start;
int ret = 0; int ret = 0;
const char *db_path; const char *db_path;
char *dot_notmuch_path;
struct sigaction action; struct sigaction action;
_filename_node_t *f; _filename_node_t *f;
int opt_index; int opt_index;
@ -1167,13 +1166,11 @@ notmuch_new_command (unused(notmuch_config_t *config), notmuch_database_t *notmu
} }
if (hooks) { if (hooks) {
ret = notmuch_run_hook (db_path, "pre-new"); ret = notmuch_run_hook (notmuch, "pre-new");
if (ret) if (ret)
return EXIT_FAILURE; return EXIT_FAILURE;
} }
dot_notmuch_path = talloc_asprintf (notmuch, "%s/%s", db_path, ".notmuch");
notmuch_exit_if_unmatched_db_uuid (notmuch); notmuch_exit_if_unmatched_db_uuid (notmuch);
if (notmuch_database_get_revision (notmuch, NULL) == 0) { if (notmuch_database_get_revision (notmuch, NULL) == 0) {
@ -1212,9 +1209,6 @@ notmuch_new_command (unused(notmuch_config_t *config), notmuch_database_t *notmu
action.sa_flags = SA_RESTART; action.sa_flags = SA_RESTART;
sigaction (SIGINT, &action, NULL); sigaction (SIGINT, &action, NULL);
talloc_free (dot_notmuch_path);
dot_notmuch_path = NULL;
gettimeofday (&add_files_state.tv_start, NULL); gettimeofday (&add_files_state.tv_start, NULL);
add_files_state.removed_files = _filename_list_create (notmuch); add_files_state.removed_files = _filename_list_create (notmuch);
@ -1284,7 +1278,7 @@ notmuch_new_command (unused(notmuch_config_t *config), notmuch_database_t *notmu
notmuch_database_close (notmuch); notmuch_database_close (notmuch);
if (hooks && ! ret && ! interrupted) if (hooks && ! ret && ! interrupted)
ret = notmuch_run_hook (db_path, "post-new"); ret = notmuch_run_hook (notmuch, "post-new");
notmuch_database_destroy (notmuch); notmuch_database_destroy (notmuch);

View file

@ -2,8 +2,6 @@
test_description='hooks' test_description='hooks'
. $(dirname "$0")/test-lib.sh || exit 1 . $(dirname "$0")/test-lib.sh || exit 1
HOOK_DIR=${MAIL_DIR}/.notmuch/hooks
create_echo_hook () { create_echo_hook () {
local TOKEN="${RANDOM}" local TOKEN="${RANDOM}"
mkdir -p ${HOOK_DIR} mkdir -p ${HOOK_DIR}
@ -16,6 +14,7 @@ EOF
} }
create_failing_hook () { create_failing_hook () {
local HOOK_DIR=${2}
mkdir -p ${HOOK_DIR} mkdir -p ${HOOK_DIR}
cat <<EOF >"${HOOK_DIR}/${1}" cat <<EOF >"${HOOK_DIR}/${1}"
#!/bin/sh #!/bin/sh
@ -24,98 +23,120 @@ EOF
chmod +x "${HOOK_DIR}/${1}" chmod +x "${HOOK_DIR}/${1}"
} }
rm_hooks () {
rm -rf ${HOOK_DIR}
}
# add a message to generate mail dir and database # add a message to generate mail dir and database
add_message add_message
# create maildir structure for notmuch-insert # create maildir structure for notmuch-insert
mkdir -p "$MAIL_DIR"/{cur,new,tmp} mkdir -p "$MAIL_DIR"/{cur,new,tmp}
test_begin_subtest "pre-new is run" for config in traditional profile explicit XDG; do
rm_hooks unset NOTMUCH_PROFILE
generate_message notmuch config set database.hook_dir
create_echo_hook "pre-new" expected output case $config in
notmuch new > /dev/null traditional)
test_expect_equal_file expected output HOOK_DIR=${MAIL_DIR}/.notmuch/hooks
;;
profile)
dir=${HOME}/.config/notmuch/other
mkdir -p ${dir}
HOOK_DIR=${dir}/hooks
cp ${NOTMUCH_CONFIG} ${dir}/config
export NOTMUCH_PROFILE=other
;;
explicit)
HOOK_DIR=${HOME}/.notmuch-hooks
mkdir -p $HOOK_DIR
notmuch config set database.hook_dir $HOOK_DIR
;;
XDG)
HOOK_DIR=${HOME}/.config/notmuch/default/hooks
;;
esac
test_begin_subtest "post-new is run" test_begin_subtest "pre-new is run [${config}]"
rm_hooks rm -rf ${HOOK_DIR}
generate_message generate_message
create_echo_hook "post-new" expected output create_echo_hook "pre-new" expected output $HOOK_DIR
notmuch new > /dev/null notmuch new > /dev/null
test_expect_equal_file expected output test_expect_equal_file expected output
test_begin_subtest "post-insert hook is run" test_begin_subtest "post-new is run [${config}]"
rm_hooks rm -rf ${HOOK_DIR}
generate_message generate_message
create_echo_hook "post-insert" expected output create_echo_hook "post-new" expected output $HOOK_DIR
notmuch insert < "$gen_msg_filename" notmuch new > /dev/null
test_expect_equal_file expected output test_expect_equal_file expected output
test_begin_subtest "pre-new is run before post-new" test_begin_subtest "post-insert hook is run [${config}]"
rm_hooks rm -rf ${HOOK_DIR}
generate_message generate_message
create_echo_hook "pre-new" pre-new.expected pre-new.output create_echo_hook "post-insert" expected output $HOOK_DIR
create_echo_hook "post-new" post-new.expected post-new.output notmuch insert < "$gen_msg_filename"
notmuch new > /dev/null test_expect_equal_file expected output
test_expect_equal_file post-new.expected post-new.output
test_begin_subtest "pre-new non-zero exit status (hook status)" test_begin_subtest "pre-new is run before post-new [${config}]"
rm_hooks rm -rf ${HOOK_DIR}
generate_message generate_message
create_failing_hook "pre-new" create_echo_hook "pre-new" pre-new.expected pre-new.output $HOOK_DIR
output=`notmuch new 2>&1` create_echo_hook "post-new" post-new.expected post-new.output $HOOK_DIR
test_expect_equal "$output" "Error: pre-new hook failed with status 13" notmuch new > /dev/null
test_expect_equal_file post-new.expected post-new.output
# depends on the previous subtest leaving broken hook behind test_begin_subtest "pre-new non-zero exit status (hook status) [${config}]"
test_begin_subtest "pre-new non-zero exit status (notmuch status)" rm -rf ${HOOK_DIR}
test_expect_code 1 "notmuch new" generate_message
create_failing_hook "pre-new" $HOOK_DIR
output=`notmuch new 2>&1`
test_expect_equal "$output" "Error: pre-new hook failed with status 13"
# depends on the previous subtests leaving 1 new message behind # depends on the previous subtest leaving broken hook behind
test_begin_subtest "pre-new non-zero exit status aborts new" test_begin_subtest "pre-new non-zero exit status (notmuch status) [${config}]"
rm_hooks test_expect_code 1 "notmuch new"
output=$(NOTMUCH_NEW)
test_expect_equal "$output" "Added 1 new message to the database."
test_begin_subtest "post-new non-zero exit status (hook status)" # depends on the previous subtests leaving 1 new message behind
rm_hooks test_begin_subtest "pre-new non-zero exit status aborts new [${config}]"
generate_message rm -rf ${HOOK_DIR}
create_failing_hook "post-new" output=$(NOTMUCH_NEW)
NOTMUCH_NEW 2>output.stderr >output test_expect_equal "$output" "Added 1 new message to the database."
cat output.stderr >> output
echo "Added 1 new message to the database." > expected
echo "Error: post-new hook failed with status 13" >> expected
test_expect_equal_file expected output
# depends on the previous subtest leaving broken hook behind test_begin_subtest "post-new non-zero exit status (hook status) [${config}]"
test_begin_subtest "post-new non-zero exit status (notmuch status)" rm -rf ${HOOK_DIR}
test_expect_code 1 "notmuch new" generate_message
create_failing_hook "post-new" $HOOK_DIR
NOTMUCH_NEW 2>output.stderr >output
cat output.stderr >> output
echo "Added 1 new message to the database." > expected
echo "Error: post-new hook failed with status 13" >> expected
test_expect_equal_file expected output
test_begin_subtest "post-insert hook does not affect insert status" # depends on the previous subtest leaving broken hook behind
rm_hooks test_begin_subtest "post-new non-zero exit status (notmuch status) [${config}]"
generate_message test_expect_code 1 "notmuch new"
create_failing_hook "post-insert"
test_expect_success "notmuch insert < \"$gen_msg_filename\" > /dev/null"
test_begin_subtest "hook without executable permissions" test_begin_subtest "post-insert hook does not affect insert status [${config}]"
rm_hooks rm -rf ${HOOK_DIR}
mkdir -p ${HOOK_DIR} generate_message
cat <<EOF >"${HOOK_DIR}/pre-new" create_failing_hook "post-insert" $HOOK_DIR
#!/bin/sh test_expect_success "notmuch insert < \"$gen_msg_filename\" > /dev/null"
echo foo
test_begin_subtest "hook without executable permissions [${config}]"
rm -rf ${HOOK_DIR}
mkdir -p ${HOOK_DIR}
cat <<EOF >"${HOOK_DIR}/pre-new"
#!/bin/sh
echo foo
EOF EOF
output=`notmuch new 2>&1` output=`notmuch new 2>&1`
test_expect_code 1 "notmuch new" test_expect_code 1 "notmuch new"
test_begin_subtest "hook execution failure" test_begin_subtest "hook execution failure [${config}]"
rm_hooks rm -rf ${HOOK_DIR}
mkdir -p ${HOOK_DIR} mkdir -p ${HOOK_DIR}
cat <<EOF >"${HOOK_DIR}/pre-new" cat <<EOF >"${HOOK_DIR}/pre-new"
no hashbang, execl fails no hashbang, execl fails
EOF EOF
chmod +x "${HOOK_DIR}/pre-new" chmod +x "${HOOK_DIR}/pre-new"
test_expect_code 1 "notmuch new" test_expect_code 1 "notmuch new"
rm -rf ${HOOK_DIR}
done
test_done test_done