Merge branch 'kn/receive-report-hook' into jch
A new hook 'report' is added to 'git receive-pack', which runs after reference updates and allows the server to filter or modify the packet-line status report sent back to the client. * kn/receive-report-hook: receive-pack: coccinelle fix hook: introduce the receive-report hook receive-pack: move message generation to separate function receive-pack: drop static variables to track report status version doc: add proc-receive hook info in 'git-receive-pack.adoc'
commit
e0cd7bd5b0
|
|
@ -236,6 +236,23 @@ if the repository is packed and is served via a dumb transport.
|
|||
exec git update-server-info
|
||||
----
|
||||
|
||||
PROC-RECEIVE HOOK
|
||||
-----------------
|
||||
This hook is invoked by linkgit:git-receive-pack[1]. If the server has
|
||||
set the multi-valued config variable `receive.procReceiveRefs`, and the
|
||||
commands sent to 'receive-pack' have matching reference names, these
|
||||
commands will be executed by this hook, instead of by the internal
|
||||
`execute_commands()` function. This hook is responsible for updating
|
||||
the relevant references and reporting the results back to 'receive-pack'.
|
||||
|
||||
RECEIVE-REPORT HOOK
|
||||
-------------------
|
||||
This hook is invoked by 'git-receive-pack' after all the ref updates
|
||||
have been applied but before the report is sent to the client. The hook
|
||||
receives the complete report in pkt-line format on stdin and its stdout
|
||||
replaces the report sent to the client, which allows the hook to rewrite
|
||||
the outcomes or abort the push completely. See linkgit:githooks[5] for
|
||||
the full protocol description.
|
||||
|
||||
QUARANTINE ENVIRONMENT
|
||||
----------------------
|
||||
|
|
|
|||
|
|
@ -527,6 +527,67 @@ The exit status of the hook is ignored for any state except for the
|
|||
status will cause the transaction to be aborted. The hook will not be
|
||||
called with "aborted" state in that case.
|
||||
|
||||
receive-report
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
This hook is invoked by linkgit:git-receive-pack[1] when it reacts to
|
||||
`git push` and updates references in its repository. It executes on
|
||||
the repository once after all refs have been updated and after all
|
||||
accepted ref changes are applied to the repository, but before the
|
||||
pkt-line encoded status report is sent back to the client.
|
||||
|
||||
The hook receives the complete pkt-line encoded status report on
|
||||
standard input, see linkgit:gitprotocol-pack[5] for details on the
|
||||
structure. The hook's standard output entirely replaces the report
|
||||
that is sent to the client. The hook must write a valid pkt-line
|
||||
encoded report in the same format it received. The hook's stdout is
|
||||
fully buffered by `receive-pack` before any data is sent to the client,
|
||||
so the hook's exit status is known before the client receives anything.
|
||||
|
||||
There are three distinct ways the hook can affect the push outcome:
|
||||
|
||||
* To reject the push, modify the unpack status from `ok` to the required
|
||||
error message. While `git-push` will fail, individual references may
|
||||
still show success messages unless modified.
|
||||
|
||||
* To reject individual ref updates while keeping `receive-pack` alive,
|
||||
rewrite the corresponding `ok <refname>` lines to
|
||||
`ng <refname>[ <reason>]` lines in the output and exit with status 0.
|
||||
The client will then mark those specific refs as rejected while
|
||||
treating any `ok` refs as successful. The push as a whole is
|
||||
considered failed if any ref is `ng`, and `git push` will exit with
|
||||
a non-zero status on the client side.
|
||||
|
||||
* To abort the entire push unconditionally, exit with a non-zero
|
||||
status. In this case the hook's stdout is discarded, `receive-pack`
|
||||
modifies all references to be rejected with a 'receive-report hook
|
||||
failed' error.
|
||||
|
||||
Any output written to standard error is forwarded to the client over
|
||||
the sideband channel and will appear as `remote:` lines on clients
|
||||
using 'git-push(1)', regardless of the hook's exit status. Writing to
|
||||
standard error alone does not affect the push outcome.
|
||||
|
||||
Note that by the time this hook runs, all ref updates have already been
|
||||
applied to the repository. Neither a non-zero exit nor rewriting refs
|
||||
to `ng` rolls back any ref changes that were already committed
|
||||
server-side. The hook can cause the client to perceive the push as
|
||||
failed, but cannot undo the server-side updates.
|
||||
|
||||
This means that reporting a reference as `ng` makes the client believe
|
||||
the update did not happen while the server has in fact applied it. The
|
||||
client leaves its remote-tracking reference at its old value, and a
|
||||
later `git fetch` may reveal the very update that the push reported as
|
||||
rejected. Neither Git nor the server can reconcile this; only the user,
|
||||
by fetching again, will find out.
|
||||
|
||||
This hook is therefore only appropriate for servers which can guarantee
|
||||
that a rejected update is not observable by any reader, for example
|
||||
because the committed transaction produces a candidate state that is
|
||||
discarded rather than published. On a repository where a committed
|
||||
reference update is immediately visible, using this hook to reject a
|
||||
push will leave the pusher with a view that does not match the server.
|
||||
|
||||
push-to-checkout
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
|
|
|
|||
|
|
@ -53,6 +53,12 @@ enum deny_action {
|
|||
DENY_UPDATE_INSTEAD
|
||||
};
|
||||
|
||||
enum report_status_version {
|
||||
REPORT_STATUS_UNKNOWN = 0,
|
||||
REPORT_STATUS_V0,
|
||||
REPORT_STATUS_V2,
|
||||
};
|
||||
|
||||
static int deny_deletes;
|
||||
static int deny_non_fast_forwards;
|
||||
static enum deny_action deny_current_branch = DENY_UNCONFIGURED;
|
||||
|
|
@ -64,8 +70,6 @@ static int advertise_atomic_push = 1;
|
|||
static int advertise_push_options;
|
||||
static int advertise_sid;
|
||||
static off_t max_input_size;
|
||||
static int report_status;
|
||||
static int report_status_v2;
|
||||
static int use_sideband;
|
||||
static int use_atomic;
|
||||
static int use_push_options;
|
||||
|
|
@ -988,6 +992,41 @@ static int run_update_hook(struct command *cmd)
|
|||
return code;
|
||||
}
|
||||
|
||||
static int run_receive_report_hook(struct strbuf *report)
|
||||
{
|
||||
struct child_process proc = CHILD_PROCESS_INIT;
|
||||
struct async sideband_async;
|
||||
int sideband_async_started = 0;
|
||||
int saved_stderr = -1;
|
||||
struct strbuf out = STRBUF_INIT;
|
||||
const char *hook_path;
|
||||
int ret;
|
||||
|
||||
hook_path = find_hook(the_repository, "receive-report");
|
||||
if (!hook_path)
|
||||
return 0;
|
||||
|
||||
strvec_push(&proc.args, hook_path);
|
||||
proc.trace2_hook_name = "receive-report";
|
||||
|
||||
prepare_sideband_async(&sideband_async, &saved_stderr,
|
||||
&sideband_async_started);
|
||||
|
||||
sigchain_push(SIGPIPE, SIG_IGN);
|
||||
ret = pipe_command(&proc, report->buf, report->len, &out,
|
||||
report->len, NULL, 0);
|
||||
sigchain_pop(SIGPIPE);
|
||||
|
||||
finish_sideband_async(&sideband_async, saved_stderr,
|
||||
sideband_async_started);
|
||||
|
||||
if (!ret)
|
||||
strbuf_swap(&out, report);
|
||||
|
||||
strbuf_release(&out);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct command *find_command_by_refname(struct command *list,
|
||||
const char *refname)
|
||||
{
|
||||
|
|
@ -2191,7 +2230,8 @@ static void queue_commands_from_cert(struct command **tail,
|
|||
}
|
||||
|
||||
static struct command *read_head_info(struct packet_reader *reader,
|
||||
struct oid_array *shallow)
|
||||
struct oid_array *shallow,
|
||||
enum report_status_version *version)
|
||||
{
|
||||
struct command *commands = NULL;
|
||||
struct command **p = &commands;
|
||||
|
|
@ -2217,9 +2257,9 @@ static struct command *read_head_info(struct packet_reader *reader,
|
|||
const char *client_sid;
|
||||
size_t len = 0;
|
||||
if (parse_feature_request(feature_list, "report-status"))
|
||||
report_status = 1;
|
||||
*version = REPORT_STATUS_V0;
|
||||
if (parse_feature_request(feature_list, "report-status-v2"))
|
||||
report_status_v2 = 1;
|
||||
*version = REPORT_STATUS_V2;
|
||||
if (parse_feature_request(feature_list, "side-band-64k"))
|
||||
use_sideband = LARGE_PACKET_MAX;
|
||||
if (parse_feature_request(feature_list, "quiet"))
|
||||
|
|
@ -2409,67 +2449,72 @@ static void update_shallow_info(struct command *commands,
|
|||
free(ref_status);
|
||||
}
|
||||
|
||||
static void report(struct command *commands, const struct strbuf *unpack_status)
|
||||
static void override_cmds_error(struct command *commands, const char *err)
|
||||
{
|
||||
struct command *cmd;
|
||||
struct strbuf buf = STRBUF_INIT;
|
||||
|
||||
packet_buf_write(&buf, "unpack %s\n",
|
||||
unpack_status->len ? unpack_status->buf : "ok");
|
||||
for (cmd = commands; cmd; cmd = cmd->next) {
|
||||
if (!cmd->error_string)
|
||||
packet_buf_write(&buf, "ok %s\n",
|
||||
cmd->ref_name);
|
||||
else
|
||||
packet_buf_write(&buf, "ng %s %s\n",
|
||||
cmd->ref_name, cmd->error_string);
|
||||
for (struct command *cmd = commands; cmd; cmd = cmd->next) {
|
||||
FREE_AND_NULL(cmd->error_string_owned);
|
||||
cmd->error_string = err;
|
||||
}
|
||||
packet_buf_flush(&buf);
|
||||
|
||||
if (use_sideband)
|
||||
send_sideband(1, 1, buf.buf, buf.len, use_sideband);
|
||||
else
|
||||
write_or_die(1, buf.buf, buf.len);
|
||||
strbuf_release(&buf);
|
||||
}
|
||||
|
||||
static void report_v2(struct command *commands, const struct strbuf *unpack_status)
|
||||
/*
|
||||
* Generate the response to be sent to the client invoking 'git-receive-pack(1)'.
|
||||
*/
|
||||
static void generate_report(struct strbuf *buf, struct command *commands,
|
||||
const struct strbuf *unpack_status,
|
||||
enum report_status_version version)
|
||||
{
|
||||
struct command *cmd;
|
||||
struct strbuf buf = STRBUF_INIT;
|
||||
struct ref_push_report *report;
|
||||
|
||||
packet_buf_write(&buf, "unpack %s\n",
|
||||
packet_buf_write(buf, "unpack %s\n",
|
||||
unpack_status->len ? unpack_status->buf : "ok");
|
||||
|
||||
for (cmd = commands; cmd; cmd = cmd->next) {
|
||||
struct ref_push_report *report;
|
||||
int count = 0;
|
||||
|
||||
if (cmd->error_string) {
|
||||
packet_buf_write(&buf, "ng %s %s\n",
|
||||
cmd->ref_name,
|
||||
cmd->error_string);
|
||||
if (cmd->error_string)
|
||||
packet_buf_write(buf, "ng %s %s\n",
|
||||
cmd->ref_name, cmd->error_string);
|
||||
else
|
||||
packet_buf_write(buf, "ok %s\n", cmd->ref_name);
|
||||
|
||||
if (version != REPORT_STATUS_V2 || cmd->error_string)
|
||||
continue;
|
||||
}
|
||||
packet_buf_write(&buf, "ok %s\n",
|
||||
cmd->ref_name);
|
||||
|
||||
for (report = cmd->report; report; report = report->next) {
|
||||
if (count++ > 0)
|
||||
packet_buf_write(&buf, "ok %s\n",
|
||||
packet_buf_write(buf, "ok %s\n",
|
||||
cmd->ref_name);
|
||||
if (report->ref_name)
|
||||
packet_buf_write(&buf, "option refname %s\n",
|
||||
packet_buf_write(buf, "option refname %s\n",
|
||||
report->ref_name);
|
||||
if (report->old_oid)
|
||||
packet_buf_write(&buf, "option old-oid %s\n",
|
||||
packet_buf_write(buf, "option old-oid %s\n",
|
||||
oid_to_hex(report->old_oid));
|
||||
if (report->new_oid)
|
||||
packet_buf_write(&buf, "option new-oid %s\n",
|
||||
packet_buf_write(buf, "option new-oid %s\n",
|
||||
oid_to_hex(report->new_oid));
|
||||
if (report->forced_update)
|
||||
packet_buf_write(&buf, "option forced-update\n");
|
||||
packet_buf_write(buf, "option forced-update\n");
|
||||
}
|
||||
}
|
||||
packet_buf_flush(&buf);
|
||||
|
||||
packet_buf_flush(buf);
|
||||
}
|
||||
|
||||
static void report(struct command *commands, const struct strbuf *unpack_status,
|
||||
enum report_status_version version)
|
||||
{
|
||||
struct strbuf buf = STRBUF_INIT;
|
||||
|
||||
generate_report(&buf, commands, unpack_status, version);
|
||||
|
||||
if (run_receive_report_hook(&buf)) {
|
||||
strbuf_reset(&buf);
|
||||
override_cmds_error(commands, "receive-report hook failed");
|
||||
generate_report(&buf, commands, unpack_status, version);
|
||||
}
|
||||
|
||||
if (use_sideband)
|
||||
send_sideband(1, 1, buf.buf, buf.len, use_sideband);
|
||||
|
|
@ -2500,6 +2545,7 @@ int cmd_receive_pack(int argc,
|
|||
struct shallow_info si;
|
||||
struct packet_reader reader;
|
||||
struct odb_transaction *transaction = NULL;
|
||||
enum report_status_version version = REPORT_STATUS_UNKNOWN;
|
||||
|
||||
struct option options[] = {
|
||||
OPT__QUIET(&quiet, N_("quiet")),
|
||||
|
|
@ -2563,7 +2609,7 @@ int cmd_receive_pack(int argc,
|
|||
PACKET_READ_CHOMP_NEWLINE |
|
||||
PACKET_READ_DIE_ON_ERR_PACKET);
|
||||
|
||||
if ((commands = read_head_info(&reader, &shallow))) {
|
||||
if ((commands = read_head_info(&reader, &shallow, &version))) {
|
||||
struct string_list push_options = STRING_LIST_INIT_DUP;
|
||||
struct strbuf unpack_status = STRBUF_INIT;
|
||||
|
||||
|
|
@ -2596,10 +2642,16 @@ int cmd_receive_pack(int argc,
|
|||
&push_options);
|
||||
odb_transaction_finalize(transaction);
|
||||
sigchain_push(SIGPIPE, SIG_IGN);
|
||||
if (report_status_v2)
|
||||
report_v2(commands, &unpack_status);
|
||||
else if (report_status)
|
||||
report(commands, &unpack_status);
|
||||
|
||||
switch (version) {
|
||||
case REPORT_STATUS_V2:
|
||||
case REPORT_STATUS_V0:
|
||||
report(commands, &unpack_status, version);
|
||||
break;
|
||||
case REPORT_STATUS_UNKNOWN:
|
||||
break;
|
||||
}
|
||||
|
||||
sigchain_pop(SIGPIPE);
|
||||
run_receive_hook(commands, "post-receive", 1, NULL,
|
||||
&push_options);
|
||||
|
|
|
|||
|
|
@ -653,6 +653,7 @@ integration_tests = [
|
|||
't5409-colorize-remote-messages.sh',
|
||||
't5410-receive-pack.sh',
|
||||
't5411-proc-receive-hook.sh',
|
||||
't5412-receive-report-hook.sh',
|
||||
't5500-fetch-pack.sh',
|
||||
't5501-fetch-push-alternates.sh',
|
||||
't5502-quickfetch.sh',
|
||||
|
|
|
|||
|
|
@ -0,0 +1,257 @@
|
|||
#!/bin/sh
|
||||
|
||||
test_description='test receive-report hook'
|
||||
|
||||
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
|
||||
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
|
||||
|
||||
. ./test-lib.sh
|
||||
|
||||
. "$TEST_DIRECTORY"/t5411/common-functions.sh
|
||||
|
||||
URL_PREFIX="\.\."
|
||||
|
||||
test_expect_success "setup workbench" '
|
||||
git init workbench &&
|
||||
create_commits_in workbench A B
|
||||
'
|
||||
|
||||
test_expect_success "no report hook, push succeeds" '
|
||||
test_when_finished "rm -rf upstream" &&
|
||||
test_when_finished "git -C workbench remote remove origin" &&
|
||||
git init --bare upstream &&
|
||||
|
||||
git -C workbench remote add origin ../upstream &&
|
||||
git -C workbench push origin $A:refs/heads/main &&
|
||||
git -C workbench push origin $B:refs/heads/main >out 2>&1 &&
|
||||
|
||||
make_user_friendly_and_stable_output <out >actual &&
|
||||
cat >expect <<-\EOF &&
|
||||
To ../upstream
|
||||
<COMMIT-A>..<COMMIT-B> <COMMIT-B> -> main
|
||||
EOF
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success "passthrough does not alter report" '
|
||||
test_when_finished "rm -rf upstream" &&
|
||||
test_when_finished "git -C workbench remote remove origin" &&
|
||||
git init --bare upstream &&
|
||||
|
||||
test_hook -C upstream --setup receive-report <<-\EOF &&
|
||||
cat
|
||||
EOF
|
||||
|
||||
git -C workbench remote add origin ../upstream &&
|
||||
git -C workbench push origin $A:refs/heads/main &&
|
||||
git -C workbench push origin $B:refs/heads/main >out 2>&1 &&
|
||||
|
||||
make_user_friendly_and_stable_output <out >actual &&
|
||||
cat >expect <<-\EOF &&
|
||||
To ../upstream
|
||||
<COMMIT-A>..<COMMIT-B> <COMMIT-B> -> main
|
||||
EOF
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success "non-zero exit reports as hook failed" '
|
||||
test_when_finished "rm -rf upstream" &&
|
||||
test_when_finished "git -C workbench remote remove origin" &&
|
||||
|
||||
git init --bare upstream &&
|
||||
git -C workbench remote add origin ../upstream &&
|
||||
git -C workbench push origin $A:refs/heads/main &&
|
||||
|
||||
test_hook -C upstream --setup receive-report <<-\EOF &&
|
||||
exit 1
|
||||
EOF
|
||||
|
||||
test_must_fail git -C workbench push origin $B:refs/heads/main >out 2>&1 &&
|
||||
make_user_friendly_and_stable_output <out >actual &&
|
||||
cat >expect <<-\EOF &&
|
||||
To ../upstream
|
||||
! [remote rejected] <COMMIT-B> -> main (receive-report hook failed)
|
||||
EOF
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success "hook is invoked and receives report on stdin" '
|
||||
test_when_finished "rm -rf upstream" &&
|
||||
test_when_finished "git -C workbench remote remove origin" &&
|
||||
|
||||
git init --bare upstream &&
|
||||
test_hook -C upstream --setup receive-report <<-EOF &&
|
||||
tee raw
|
||||
EOF
|
||||
|
||||
git -C workbench remote add origin ../upstream &&
|
||||
git -C workbench push origin $A:refs/heads/main &&
|
||||
git -C workbench push origin $B:refs/heads/main >out 2>&1 &&
|
||||
|
||||
make_user_friendly_and_stable_output <out >actual &&
|
||||
cat >expect <<-EOF &&
|
||||
To ../upstream
|
||||
<COMMIT-A>..<COMMIT-B> <COMMIT-B> -> main
|
||||
EOF
|
||||
test_cmp expect actual &&
|
||||
|
||||
test-tool pkt-line unpack <upstream/raw >actual-report &&
|
||||
cat >expect-report <<-EOF &&
|
||||
unpack ok
|
||||
ok refs/heads/main
|
||||
0000
|
||||
EOF
|
||||
test_cmp expect-report actual-report
|
||||
'
|
||||
|
||||
test_expect_success "hook can modify the report sent to client" '
|
||||
test_when_finished "rm -rf upstream" &&
|
||||
test_when_finished "git -C workbench remote remove origin" &&
|
||||
|
||||
git init --bare upstream &&
|
||||
git -C workbench remote add origin ../upstream &&
|
||||
git -C workbench push origin $A:refs/heads/main &&
|
||||
|
||||
test_hook -C upstream --setup receive-report <<-\EOF &&
|
||||
test-tool pkt-line unpack |
|
||||
sed "s/^ok /ng /" |
|
||||
test-tool pkt-line pack
|
||||
EOF
|
||||
|
||||
test_must_fail git -C workbench push origin $B:refs/heads/main >out 2>&1 &&
|
||||
make_user_friendly_and_stable_output <out >actual &&
|
||||
cat >expect <<-\EOF &&
|
||||
To ../upstream
|
||||
! [remote rejected] <COMMIT-B> -> main (failed)
|
||||
EOF
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success "hook can modify the unpack status" '
|
||||
test_when_finished "rm -rf upstream" &&
|
||||
test_when_finished "git -C workbench remote remove origin" &&
|
||||
|
||||
git init --bare upstream &&
|
||||
git -C workbench remote add origin ../upstream &&
|
||||
git -C workbench push origin $A:refs/heads/main &&
|
||||
|
||||
test_hook -C upstream --setup receive-report <<-\EOF &&
|
||||
test-tool pkt-line unpack |
|
||||
sed "s/^unpack ok$/unpack push failed due to server error/" |
|
||||
test-tool pkt-line pack
|
||||
EOF
|
||||
|
||||
test_must_fail git -C workbench push origin $B:refs/heads/main >out 2>&1 &&
|
||||
test_grep "error: remote unpack failed: push failed due to server error" out &&
|
||||
make_user_friendly_and_stable_output <out >actual &&
|
||||
cat >expect <<-\EOF &&
|
||||
To ../upstream
|
||||
<COMMIT-A>..<COMMIT-B> <COMMIT-B> -> main
|
||||
EOF
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success "hook can report a custom failure message" '
|
||||
test_when_finished "rm -rf upstream" &&
|
||||
test_when_finished "git -C workbench remote remove origin" &&
|
||||
|
||||
git init --bare upstream &&
|
||||
git -C workbench remote add origin ../upstream &&
|
||||
git -C workbench push origin $A:refs/heads/main &&
|
||||
|
||||
test_hook -C upstream --setup receive-report <<-\EOF &&
|
||||
echo "push rejected: service X is down" >&2
|
||||
test-tool pkt-line unpack |
|
||||
sed "s/^ok \(.*\)/ng \1 service-x-is-down/" |
|
||||
test-tool pkt-line pack |
|
||||
tee raw
|
||||
EOF
|
||||
|
||||
test_must_fail git -C workbench push origin $B:refs/heads/main >out 2>&1 &&
|
||||
test_grep "push rejected: service X is down" out &&
|
||||
|
||||
test-tool pkt-line unpack <upstream/raw >actual-report &&
|
||||
cat >expect-report <<-\EOF &&
|
||||
unpack ok
|
||||
ng refs/heads/main service-x-is-down
|
||||
0000
|
||||
EOF
|
||||
test_cmp expect-report actual-report
|
||||
'
|
||||
|
||||
test_expect_success "hook stderr with zero exit status code" '
|
||||
test_when_finished "rm -rf upstream" &&
|
||||
test_when_finished "git -C workbench remote remove origin" &&
|
||||
|
||||
git init --bare upstream &&
|
||||
git -C workbench remote add origin ../upstream &&
|
||||
git -C workbench push origin $A:refs/heads/main &&
|
||||
|
||||
test_hook -C upstream --setup receive-report <<-\EOF &&
|
||||
echo "push rejected: service X is down" >&2
|
||||
tee raw
|
||||
EOF
|
||||
|
||||
git -C workbench push origin $B:refs/heads/main >out 2>&1 &&
|
||||
test_grep "push rejected: service X is down" out &&
|
||||
|
||||
test-tool pkt-line unpack <upstream/raw >actual-report &&
|
||||
cat >expect-report <<-\EOF &&
|
||||
unpack ok
|
||||
ok refs/heads/main
|
||||
0000
|
||||
EOF
|
||||
test_cmp expect-report actual-report
|
||||
'
|
||||
|
||||
test_expect_success "non-zero exit with pre-existing ng from proc-receive" '
|
||||
test_when_finished "rm -rf upstream" &&
|
||||
test_when_finished "git -C workbench remote remove origin" &&
|
||||
|
||||
git init --bare upstream &&
|
||||
git -C upstream config receive.procReceiveRefs refs/for &&
|
||||
git -C workbench remote add origin ../upstream &&
|
||||
git -C workbench push origin $A:refs/heads/main &&
|
||||
|
||||
# Use a proc-receive hook to generate a dynamic error string.
|
||||
# This is used to capture any leaks stemming from overriding the
|
||||
# error message via the receive-report.
|
||||
test_hook -C upstream --setup proc-receive <<-\EOF &&
|
||||
test-tool proc-receive -r "ng refs/for/main/topic push-rejected-by-service-x"
|
||||
EOF
|
||||
|
||||
test_hook -C upstream --setup receive-report <<-\EOF &&
|
||||
tee raw
|
||||
exit 1
|
||||
EOF
|
||||
|
||||
test_must_fail git -C workbench push origin HEAD:refs/for/main/topic >out 2>&1 &&
|
||||
test_grep "receive-report hook failed" out &&
|
||||
|
||||
test-tool pkt-line unpack <upstream/raw >actual-report &&
|
||||
cat >expect-report <<-\EOF &&
|
||||
unpack ok
|
||||
ng refs/for/main/topic push-rejected-by-service-x
|
||||
0000
|
||||
EOF
|
||||
test_cmp expect-report actual-report
|
||||
'
|
||||
|
||||
test_expect_success "hook stderr is relayed to client via sideband" '
|
||||
test_when_finished "rm -rf upstream" &&
|
||||
test_when_finished "git -C workbench remote remove origin" &&
|
||||
|
||||
git init --bare upstream &&
|
||||
git -C workbench remote add origin ../upstream &&
|
||||
git -C workbench push origin $A:refs/heads/main &&
|
||||
|
||||
test_hook -C upstream --setup receive-report <<-\EOF &&
|
||||
echo "hook-stderr-message" >&2
|
||||
exit 1
|
||||
EOF
|
||||
|
||||
test_must_fail git -C workbench push origin $B:refs/heads/main >out 2>&1 &&
|
||||
test_grep "remote: hook-stderr-message" out
|
||||
'
|
||||
|
||||
test_done
|
||||
Loading…
Reference in New Issue