From patchwork Wed Mar 4 11:33:06 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jiang Xin X-Patchwork-Id: 11419921 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 1A5791395 for ; Wed, 4 Mar 2020 11:34:14 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id D424920848 for ; Wed, 4 Mar 2020 11:34:13 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="HSJQQw3D" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2387860AbgCDLeN (ORCPT ); Wed, 4 Mar 2020 06:34:13 -0500 Received: from mail-pf1-f174.google.com ([209.85.210.174]:34527 "EHLO mail-pf1-f174.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S2387799AbgCDLeL (ORCPT ); Wed, 4 Mar 2020 06:34:11 -0500 Received: by mail-pf1-f174.google.com with SMTP id y21so846456pfp.1 for ; Wed, 04 Mar 2020 03:34:08 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=+pGsHVHvzZ4Xzsp15UJfcJV/KQQThRfqCP1bSx5XGk4=; b=HSJQQw3DkhNl/wllrUihawz/CVmWdK8raARoMgo7JgckYRmtyvBq78n5DvIoggrhjm J3qrpz10PsncW3DbvcV+OHN1mmfBlT5O20p1ESSyN08ZVfrahFpISrc5GU8RUJccFv8g O2x9B2i15rldDga9UPYMbN3eH6SQ7ZEythJcMFRzUl3GAN8GGzacVOMuBEeCypvi2vka FbhZ4FlxOQ1/nSOE+UuUbnjtoI+1XQebcyGoA71IiWtvxL3ZK3tgvhUE6Qe9zdAJuTJw n8eEhScgBvkF13zq9gbbPQSHS3wf3ocsMhMXUVmNWEhwqofEkhSGiTiVCnhPIiboiQl7 6vSA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=+pGsHVHvzZ4Xzsp15UJfcJV/KQQThRfqCP1bSx5XGk4=; b=ivQpobsJTHw33Dke1HXdqOMyP4/YxrPH2tEKD7sD8zeZ+4eMJ8fjGQZVHqkwaApcRU 6eiEPOgldrkDhhmAx5PBgk/o+N8nD1wr5nqLGXfN7a69marVIzSaG+vvhdkbwjoHSzg4 Mci6SNGrJUuzKjfxokDNzP5uWgpnVSMbZ//+hZUuXWP0dFEXZCrCox7ARiOB47mUaGvo Baq5d25rVZPPRaiBiIyb3jCjar8B6ncjHgHIDUAjPQsk+tA+p2iU297h7vdxoryxy8/G LKtxN0dn2Dw7O3/kaMKFhkyb7ozEqqvfOLqTNvRnxnuHD/aF5VAolba7910g7nEq+02g AAAQ== X-Gm-Message-State: ANhLgQ1lHWQYktHE6QmmNwtIfMJPqL8sFPSgpaZ0uLuy8y3u7Bw65GNL PTEnhgLAxnYTGgjR1+DoobxePOXCbUEhIA== X-Google-Smtp-Source: ADFU+vupzhFASNZmDbFmbHqWlmgDw4CME5o3YrTTW1pT6kqhYLIVz3HXfsJ6+Ugrb+8FwaSh8fwHRQ== X-Received: by 2002:a65:488d:: with SMTP id n13mr2176098pgs.91.1583321646749; Wed, 04 Mar 2020 03:34:06 -0800 (PST) Received: from localhost.localdomain ([47.89.83.4]) by smtp.gmail.com with ESMTPSA id d77sm15350050pfd.109.2020.03.04.03.34.04 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Wed, 04 Mar 2020 03:34:06 -0800 (PST) From: Jiang Xin X-Google-Original-From: Jiang Xin To: Git List Cc: Jiang Xin , Junio C Hamano Subject: [PATCH 1/7] receive-pack: new external execute-commands hook Date: Wed, 4 Mar 2020 19:33:06 +0800 Message-Id: <20200304113312.34229-2-zhiyou.jx@alibaba-inc.com> X-Mailer: git-send-email 2.25.1.362.g51ebf55b93 In-Reply-To: <20200304113312.34229-1-zhiyou.jx@alibaba-inc.com> References: <20200304113312.34229-1-zhiyou.jx@alibaba-inc.com> MIME-Version: 1.0 Sender: git-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: git@vger.kernel.org Git calls an internal `execute_commands` function to handle commands sent from client to `git-receive-pack`. Regardless of what references the user pushes, git creates or updates the corresponding references if the user has write permission. A contributor who has no write permission, cannot push to the the repository directly. So, the contributor has to write commits to an alternate location, and sends pull request by emails or by other ways. We call this distributed workflow. It would be more convenient to work in a centralized workflow like what Gerrit provided for some cases. For example, a read-only user may run the following `git push` command to push commits to a special reference to create a code review, instead of updating a reference directly. git push -o reviewers=user1,user2 \ -o oldoid=89c082363ac950d224a7259bfba3ccfbf4c560c4 \ origin \ HEAD:refs/for// The `` in the above example can be as simple as "master", or a more complicated branch name like "foo/bar". The `` in the above example command can be the local branch name of the client side, such as "my/topic". In order to support this kind of workflow in CGit, add a filter and a new handler. The filter will check the prefix of the reference name, and if the command has a special reference name, the filter will add a specific tag (`exec_by_hook`) to the command. Commands with this specific tag will be executed by a new handler (an exeternal hook named "execute-commands") instead of the internal `execute_commands` function. There are two phases involved to run "execute-commands" hook: * In order to check permissions for special `git-push` command, run a new "execute-commands--pre-receive" hook (or `execute-commands --pre-receive`, implemented in latter commit) instead of the "pre-receive" hook, because adding a new hook won't break the old implementation of the "pre-receive" hook. * Then will call the "execute-commands" hook (without any parameter). This hook may call an external API to create a code review or send emails. Signed-off-by: Jiang Xin --- builtin/receive-pack.c | 135 ++++++++-- t/t5411-execute-commands-hook.sh | 427 +++++++++++++++++++++++++++++++ 2 files changed, 540 insertions(+), 22 deletions(-) create mode 100755 t/t5411-execute-commands-hook.sh diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c index 411e0b4d99..24eb999ed4 100644 --- a/builtin/receive-pack.c +++ b/builtin/receive-pack.c @@ -311,7 +311,8 @@ struct command { struct command *next; const char *error_string; unsigned int skip_update:1, - did_not_exist:1; + did_not_exist:1, + exec_by_hook:1; int index; struct object_id old_oid; struct object_id new_oid; @@ -668,6 +669,8 @@ static void prepare_push_cert_sha1(struct child_process *proc) struct receive_hook_feed_state { struct command *cmd; + int exec_by_hook; + int hook_must_exist; int skip_broken; struct strbuf buf; const struct string_list *push_options; @@ -683,8 +686,13 @@ static int run_and_feed_hook(const char *hook_name, feed_fn feed, int code; argv[0] = find_hook(hook_name); - if (!argv[0]) - return 0; + if (!argv[0]) { + if (feed_state->hook_must_exist) { + rp_error("cannot to find hook '%s'", hook_name); + return 1; + } else + return 0; + } argv[1] = NULL; @@ -750,9 +758,15 @@ static int feed_receive_hook(void *state_, const char **bufp, size_t *sizep) struct receive_hook_feed_state *state = state_; struct command *cmd = state->cmd; - while (cmd && - state->skip_broken && (cmd->error_string || cmd->did_not_exist)) - cmd = cmd->next; + while (cmd) + if (state->skip_broken && (cmd->error_string || cmd->did_not_exist)) + cmd = cmd->next; + else if (state->exec_by_hook && !cmd->exec_by_hook) + cmd = cmd->next; + else if (!state->exec_by_hook && cmd->exec_by_hook) + cmd = cmd->next; + else + break; if (!cmd) return -1; /* EOF */ strbuf_reset(&state->buf); @@ -777,6 +791,8 @@ static int run_receive_hook(struct command *commands, strbuf_init(&state.buf, 0); state.cmd = commands; + state.exec_by_hook = 0; + state.hook_must_exist = 0; state.skip_broken = skip_broken; if (feed_receive_hook(&state, NULL, NULL)) return 0; @@ -816,14 +832,45 @@ static int run_update_hook(struct command *cmd) return finish_command(&proc); } -static int is_ref_checked_out(const char *ref) +static int run_execute_commands_pre_receive_hook(struct command *commands, + const struct string_list *push_options) { - if (is_bare_repository()) + struct receive_hook_feed_state state; + int status; + + strbuf_init(&state.buf, 0); + state.cmd = commands; + state.exec_by_hook = 1; + state.hook_must_exist = 0; + state.skip_broken = 0; + if (feed_receive_hook(&state, NULL, NULL)) return 0; + state.cmd = commands; + state.push_options = push_options; + status = run_and_feed_hook("execute-commands--pre-receive", + feed_receive_hook, &state); + strbuf_release(&state.buf); + return status; +} + +static int run_execute_commands_hook(struct command *commands, + const struct string_list *push_options) +{ + struct receive_hook_feed_state state; + int status; - if (!head_name) + strbuf_init(&state.buf, 0); + state.cmd = commands; + state.exec_by_hook = 1; + state.hook_must_exist = 1; + state.skip_broken = 1; + if (feed_receive_hook(&state, NULL, NULL)) return 0; - return !strcmp(head_name, ref); + state.cmd = commands; + state.push_options = push_options; + status = run_and_feed_hook("execute-commands", feed_receive_hook, &state); + strbuf_release(&state.buf); + return status; } static char *refuse_unconfigured_deny_msg = @@ -1373,7 +1420,7 @@ static void warn_if_skipped_connectivity_check(struct command *commands, int checked_connectivity = 1; for (cmd = commands; cmd; cmd = cmd->next) { - if (should_process_cmd(cmd) && si->shallow_ref[cmd->index]) { + if (should_process_cmd(cmd) && !cmd->exec_by_hook && si->shallow_ref[cmd->index]) { error("BUG: connectivity check has not been run on ref %s", cmd->ref_name); checked_connectivity = 0; @@ -1390,7 +1437,7 @@ static void execute_commands_non_atomic(struct command *commands, struct strbuf err = STRBUF_INIT; for (cmd = commands; cmd; cmd = cmd->next) { - if (!should_process_cmd(cmd)) + if (!should_process_cmd(cmd) || cmd->exec_by_hook) continue; transaction = ref_transaction_begin(&err); @@ -1430,7 +1477,7 @@ static void execute_commands_atomic(struct command *commands, } for (cmd = commands; cmd; cmd = cmd->next) { - if (!should_process_cmd(cmd)) + if (!should_process_cmd(cmd) || cmd->exec_by_hook) continue; cmd->error_string = update(cmd, si); @@ -1466,6 +1513,8 @@ static void execute_commands(struct command *commands, struct iterate_data data; struct async muxer; int err_fd = 0; + int seen_exec_by_hook = 0; + int seen_internal_exec = 0; if (unpacker_error) { for (cmd = commands; cmd; cmd = cmd->next) @@ -1495,14 +1544,45 @@ static void execute_commands(struct command *commands, reject_updates_to_hidden(commands); - if (run_receive_hook(commands, "pre-receive", 0, push_options)) { - for (cmd = commands; cmd; cmd = cmd->next) { - if (!cmd->error_string) - cmd->error_string = "pre-receive hook declined"; + /* Try to find commands that have special prefix, and will run these + * commands using an external "execute-commands" hook. + */ + for (cmd = commands; cmd; cmd = cmd->next) { + if (!should_process_cmd(cmd)) + continue; + + /* TODO: replace the fixed prefix by looking up git config variables. */ + if (!strncmp(cmd->ref_name, "refs/for/", 9)) { + cmd->exec_by_hook = 1; + seen_exec_by_hook = 1; + } else + seen_internal_exec = 1; + } + + if (seen_exec_by_hook) { + /* Try to find and run the `execute-commands--pre-receive` hook to check + * permissions on the special commands. + * + * If it does not exists, try to run `execute-commands --pre-receive`. + */ + if (run_execute_commands_pre_receive_hook(commands, push_options)) { + for (cmd = commands; cmd; cmd = cmd->next) { + if (!cmd->error_string) + cmd->error_string = "execute-commands hook declined"; + } + return; } - return; } + if (seen_internal_exec) + if (run_receive_hook(commands, "pre-receive", 0, push_options)) { + for (cmd = commands; cmd; cmd = cmd->next) { + if (!cmd->error_string) + cmd->error_string = "pre-receive hook declined"; + } + return; + } + /* * Now we'll start writing out refs, which means the objects need * to be in their final positions so that other processes can see them. @@ -1521,10 +1601,21 @@ static void execute_commands(struct command *commands, free(head_name_to_free); head_name = head_name_to_free = resolve_refdup("HEAD", 0, NULL, NULL); - if (use_atomic) - execute_commands_atomic(commands, si); - else - execute_commands_non_atomic(commands, si); + if (seen_exec_by_hook) { + if (run_execute_commands_hook(commands, push_options)) { + for (cmd = commands; cmd; cmd = cmd->next) { + if (!cmd->error_string && (cmd->exec_by_hook || use_atomic)) + cmd->error_string = "fail to run execute-commands hook"; + } + } + } + + if (seen_internal_exec) { + if (use_atomic) + execute_commands_atomic(commands, si); + else + execute_commands_non_atomic(commands, si); + } if (shallow_update) warn_if_skipped_connectivity_check(commands, si); diff --git a/t/t5411-execute-commands-hook.sh b/t/t5411-execute-commands-hook.sh new file mode 100755 index 0000000000..2ff0d5cbcd --- /dev/null +++ b/t/t5411-execute-commands-hook.sh @@ -0,0 +1,427 @@ +#!/bin/sh +# +# Copyright (c) 2018-2020 Jiang Xin +# + +test_description='Test execute-commands hook on special git-push refspec' + +. ./test-lib.sh + +bare=bare.git + +create_commits_in () { + repo="$1" && + if ! parent=$(git -C "$repo" rev-parse HEAD^{} 2>/dev/null) + then + parent= + fi && + T=$(git -C "$repo" write-tree) && + shift && + while test $# -gt 0 + do + name=$1 && + test_tick && + if test -z "$parent" + then + oid=$(echo $name | git -C "$repo" commit-tree $T) + else + oid=$(echo $name | git -C "$repo" commit-tree -p $parent $T) + fi && + eval $name=$oid && + parent=$oid && + shift || + return 1 + done && + git -C "$repo" update-ref refs/heads/master $oid +} + +test_expect_success setup ' + git init --bare $bare && + + # Enable push options for bare.git. + git -C $bare config receive.advertisePushOptions true && + + git clone --no-local $bare work && + create_commits_in work A B +' + +test_expect_success "setup hooks" ' + ## execute-commands--pre-receive hook + cat >$bare/hooks/execute-commands--pre-receive <<-EOF && + #!/bin/sh + + printf >&2 "execute: execute-commands--pre-receive\n" + + while read old new ref + do + printf >&2 ">> old: \$old, new: \$new, ref: \$ref.\n" + done + EOF + + ## execute-commands hook + cat >$bare/hooks/execute-commands <<-EOF && + #!/bin/sh + + printf >&2 "execute: execute-commands\n" + + while read old new ref + do + printf >&2 ">> old: \$old, new: \$new, ref: \$ref.\n" + done + EOF + + ## pre-receive hook + cat >$bare/hooks/pre-receive <<-EOF && + #!/bin/sh + + printf >&2 "execute: pre-receive hook\n" + + while read old new ref + do + printf >&2 ">> old: \$old, new: \$new, ref: \$ref.\n" + done + EOF + + ## post-receive hook + cat >$bare/hooks/post-receive <<-EOF && + #!/bin/sh + + printf >&2 "execute: post-receive hook\n" + + while read old new ref + do + printf >&2 ">> old: \$old, new: \$new, ref: \$ref.\n" + done + EOF + chmod a+x \ + $bare/hooks/pre-receive \ + $bare/hooks/post-receive \ + $bare/hooks/execute-commands \ + $bare/hooks/execute-commands--pre-receive +' + +test_expect_success "push normal branches and execute pre-receive and post-receive hooks" ' + ( + cd work && + git update-ref HEAD $A && + git push origin HEAD HEAD:maint 2>&1 + ) >out && + grep "^remote:" out | sed -e "s/ *\$//g" >actual && + cat >expect <<-EOF && + remote: execute: pre-receive hook + remote: >> old: 0000000000000000000000000000000000000000, new: 102939797ab91a4f201d131418d2c9d919dcdd2c, ref: refs/heads/master. + remote: >> old: 0000000000000000000000000000000000000000, new: 102939797ab91a4f201d131418d2c9d919dcdd2c, ref: refs/heads/maint. + remote: execute: post-receive hook + remote: >> old: 0000000000000000000000000000000000000000, new: 102939797ab91a4f201d131418d2c9d919dcdd2c, ref: refs/heads/master. + remote: >> old: 0000000000000000000000000000000000000000, new: 102939797ab91a4f201d131418d2c9d919dcdd2c, ref: refs/heads/maint. + EOF + test_cmp expect actual +' + +test_expect_success "create local topic branch" ' + ( + cd work && + git checkout -b my/topic origin/master + ) +' + +test_expect_failure "push one special ref: refs/for/master" ' + ( + cd work && + git update-ref HEAD $B && + git push origin HEAD:refs/for/master/my/topic + ) >out 2>&1 && + grep "^remote:" out | sed -e "s/ *\$//g" >actual && + cat >expect <<-EOF && + remote: execute: execute-commands--pre-receive + remote: >> old: 0000000000000000000000000000000000000000, new: ce858e653cdbf70f9955a39d73a44219e4b92e9e, ref: refs/for/master/my/topic. + remote: execute: execute-commands + remote: >> old: 0000000000000000000000000000000000000000, new: ce858e653cdbf70f9955a39d73a44219e4b92e9e, ref: refs/for/master/my/topic. + remote: execute: post-receive hook + remote: >> old: 0000000000000000000000000000000000000000, new: ce858e653cdbf70f9955a39d73a44219e4b92e9e, ref: refs/for/master/my/topic. + EOF + test_cmp expect actual +' + +test_expect_success "remove execute-commands hook" ' + mv $bare/hooks/execute-commands $bare/hooks/execute-commands.ok +' + +test_expect_success "push branch: refs/heads/a/b/c" ' + ( + cd work && + git update-ref HEAD $A && + git push origin HEAD:a/b/c 2>&1 + ) >out && + grep "^remote:" out | sed -e "s/ *\$//g" >actual && + cat >expect <<-EOF && + remote: execute: pre-receive hook + remote: >> old: 0000000000000000000000000000000000000000, new: 102939797ab91a4f201d131418d2c9d919dcdd2c, ref: refs/heads/a/b/c. + remote: execute: post-receive hook + remote: >> old: 0000000000000000000000000000000000000000, new: 102939797ab91a4f201d131418d2c9d919dcdd2c, ref: refs/heads/a/b/c. + EOF + test_cmp expect actual +' + +test_expect_success "fail to push special ref: refs/for/master" ' + ( + cd work && + git update-ref HEAD $B && + test_must_fail git push origin HEAD:refs/for/master/my/topic + ) >out 2>&1 && + grep "^remote:" out | sed -e "s/ *\$//g" >actual && + cat >expect <<-EOF && + remote: execute: execute-commands--pre-receive + remote: >> old: 0000000000000000000000000000000000000000, new: ce858e653cdbf70f9955a39d73a44219e4b92e9e, ref: refs/for/master/my/topic. + remote: error: cannot to find hook '"'"'execute-commands'"'"' + EOF + test_cmp expect actual +' + +test_expect_success "add back the execute-commands hook" ' + mv $bare/hooks/execute-commands.ok $bare/hooks/execute-commands +' + +test_expect_failure "push one special ref: refs/for/a/b/c" ' + ( + cd work && + git push origin HEAD:refs/for/a/b/c/my/topic + ) >out 2>&1 && + grep "^remote:" out | sed -e "s/ *\$//g" >actual && + cat >expect <<-EOF && + remote: execute: execute-commands--pre-receive + remote: >> old: 0000000000000000000000000000000000000000, new: ce858e653cdbf70f9955a39d73a44219e4b92e9e, ref: refs/for/a/b/c/my/topic. + remote: execute: execute-commands + remote: >> old: 0000000000000000000000000000000000000000, new: ce858e653cdbf70f9955a39d73a44219e4b92e9e, ref: refs/for/a/b/c/my/topic. + remote: execute: post-receive hook + remote: >> old: 0000000000000000000000000000000000000000, new: ce858e653cdbf70f9955a39d73a44219e4b92e9e, ref: refs/for/a/b/c/my/topic. + EOF + test_cmp expect actual +' + +test_expect_failure "push two special references" ' + ( + cd work && + git push origin \ + HEAD:refs/for/maint/my/topic \ + HEAD:refs/for/a/b/c/my/topic + ) >out 2>&1 && + grep "^remote:" out | sed -e "s/ *\$//g" >actual && + cat >expect <<-EOF && + remote: execute: execute-commands--pre-receive + remote: >> old: 0000000000000000000000000000000000000000, new: ce858e653cdbf70f9955a39d73a44219e4b92e9e, ref: refs/for/maint/my/topic. + remote: >> old: 0000000000000000000000000000000000000000, new: ce858e653cdbf70f9955a39d73a44219e4b92e9e, ref: refs/for/a/b/c/my/topic. + remote: execute: execute-commands + remote: >> old: 0000000000000000000000000000000000000000, new: ce858e653cdbf70f9955a39d73a44219e4b92e9e, ref: refs/for/maint/my/topic. + remote: >> old: 0000000000000000000000000000000000000000, new: ce858e653cdbf70f9955a39d73a44219e4b92e9e, ref: refs/for/a/b/c/my/topic. + remote: execute: post-receive hook + remote: >> old: 0000000000000000000000000000000000000000, new: ce858e653cdbf70f9955a39d73a44219e4b92e9e, ref: refs/for/maint/my/topic. + remote: >> old: 0000000000000000000000000000000000000000, new: ce858e653cdbf70f9955a39d73a44219e4b92e9e, ref: refs/for/a/b/c/my/topic. + EOF + test_cmp expect actual +' + +test_expect_success "new execute-commands hook (fail with error)" ' + mv $bare/hooks/execute-commands $bare/hooks/execute-commands.ok && + cat >$bare/hooks/execute-commands <<-EOF && + #!/bin/sh + + printf >&2 "execute: execute-commands\n" + + while read old new ref + do + printf >&2 ">> old: \$old, new: \$new, ref: \$ref.\n" + done + + printf >&2 "fail to run execute-commands\n" + exit 1 + EOF + chmod a+x $bare/hooks/execute-commands +' + +test_expect_success "successfully push normal ref, and fail to push special reference" ' + ( + cd work && + test_must_fail git push origin \ + HEAD:refs/for/maint/my/topic \ + HEAD:refs/heads/master + ) >out 2>&1 && + grep "^remote:" out | sed -e "s/ *\$//g" >actual && + cat >expect <<-EOF && + remote: execute: execute-commands--pre-receive + remote: >> old: 0000000000000000000000000000000000000000, new: ce858e653cdbf70f9955a39d73a44219e4b92e9e, ref: refs/for/maint/my/topic. + remote: execute: pre-receive hook + remote: >> old: 102939797ab91a4f201d131418d2c9d919dcdd2c, new: ce858e653cdbf70f9955a39d73a44219e4b92e9e, ref: refs/heads/master. + remote: execute: execute-commands + remote: >> old: 0000000000000000000000000000000000000000, new: ce858e653cdbf70f9955a39d73a44219e4b92e9e, ref: refs/for/maint/my/topic. + remote: fail to run execute-commands + remote: execute: post-receive hook + remote: >> old: 102939797ab91a4f201d131418d2c9d919dcdd2c, new: ce858e653cdbf70f9955a39d73a44219e4b92e9e, ref: refs/heads/master. + EOF + test_cmp expect actual +' + +test_expect_success "restore remote master branch" ' + ( + cd $bare && + git update-ref refs/heads/master $A $B && + git show-ref + ) >actual && + cat >expect <<-eof && + 102939797ab91a4f201d131418d2c9d919dcdd2c refs/heads/a/b/c + 102939797ab91a4f201d131418d2c9d919dcdd2c refs/heads/maint + 102939797ab91a4f201d131418d2c9d919dcdd2c refs/heads/master + eof + test_cmp expect actual +' + +test_expect_success "all mixed refs are failed to push in atomic mode" ' + ( + cd work && + test_must_fail git push --atomic origin \ + HEAD:refs/for/maint/my/topic \ + HEAD:refs/heads/master + ) >out 2>&1 && + grep "^remote:" out | sed -e "s/ *\$//g" >actual && + cat >expect <<-EOF && + remote: execute: execute-commands--pre-receive + remote: >> old: 0000000000000000000000000000000000000000, new: ce858e653cdbf70f9955a39d73a44219e4b92e9e, ref: refs/for/maint/my/topic. + remote: execute: pre-receive hook + remote: >> old: 102939797ab91a4f201d131418d2c9d919dcdd2c, new: ce858e653cdbf70f9955a39d73a44219e4b92e9e, ref: refs/heads/master. + remote: execute: execute-commands + remote: >> old: 0000000000000000000000000000000000000000, new: ce858e653cdbf70f9955a39d73a44219e4b92e9e, ref: refs/for/maint/my/topic. + remote: fail to run execute-commands + EOF + test_cmp expect actual +' + +test_expect_success "restore execute-commands hook" ' + mv $bare/hooks/execute-commands $bare/hooks/execute-commands.fail && + mv $bare/hooks/execute-commands.ok $bare/hooks/execute-commands +' + +test_expect_failure "push mixed references successfully" ' + ( + cd work && + git push origin \ + HEAD:refs/for/maint/my/topic \ + HEAD:refs/heads/master + ) >out 2>&1 && + grep "^remote:" out | sed -e "s/ *\$//g" >actual && + cat >expect <<-EOF && + remote: execute: execute-commands--pre-receive + remote: >> old: 0000000000000000000000000000000000000000, new: ce858e653cdbf70f9955a39d73a44219e4b92e9e, ref: refs/for/maint/my/topic. + remote: execute: pre-receive hook + remote: >> old: 102939797ab91a4f201d131418d2c9d919dcdd2c, new: ce858e653cdbf70f9955a39d73a44219e4b92e9e, ref: refs/heads/master. + remote: execute: execute-commands + remote: >> old: 0000000000000000000000000000000000000000, new: ce858e653cdbf70f9955a39d73a44219e4b92e9e, ref: refs/for/maint/my/topic. + remote: execute: post-receive hook + remote: >> old: 102939797ab91a4f201d131418d2c9d919dcdd2c, new: ce858e653cdbf70f9955a39d73a44219e4b92e9e, ref: refs/heads/master. + remote: >> old: 0000000000000000000000000000000000000000, new: ce858e653cdbf70f9955a39d73a44219e4b92e9e, ref: refs/for/maint/my/topic. + EOF + test_cmp expect actual +' + +test_expect_success "restore remote master branch" ' + ( + cd $bare && + git update-ref refs/heads/master $A $B && + git show-ref + ) >actual && + cat >expect <<-EOF && + 102939797ab91a4f201d131418d2c9d919dcdd2c refs/heads/a/b/c + 102939797ab91a4f201d131418d2c9d919dcdd2c refs/heads/maint + 102939797ab91a4f201d131418d2c9d919dcdd2c refs/heads/master + EOF + test_cmp expect actual +' + +test_expect_success "new execute-commands--pre-receive hook (declined version)" ' + mv $bare/hooks/execute-commands--pre-receive $bare/hooks/execute-commands--pre-receive.ok && + cat >$bare/hooks/execute-commands--pre-receive <<-EOF && + #!/bin/sh + + printf >&2 "execute: execute-commands--pre-receive\n" + + while read old new ref + do + printf >&2 ">> old: \$old, new: \$new, ref: \$ref.\n" + done + + printf >&2 ">> ERROR: declined in execute-commands--pre-receive\n" + exit 1 + EOF + chmod a+x $bare/hooks/execute-commands--pre-receive +' + +test_expect_success "cannot push two special references (declined)" ' + ( + cd work && + test_must_fail git push origin \ + HEAD:refs/for/master/my/topic \ + HEAD:refs/for/maint/my/topic + ) >out 2>&1 && + grep "^remote:" out | sed -e "s/ *\$//g" >actual && + cat >expect <<-EOF && + remote: execute: execute-commands--pre-receive + remote: >> old: 0000000000000000000000000000000000000000, new: ce858e653cdbf70f9955a39d73a44219e4b92e9e, ref: refs/for/master/my/topic. + remote: >> old: 0000000000000000000000000000000000000000, new: ce858e653cdbf70f9955a39d73a44219e4b92e9e, ref: refs/for/maint/my/topic. + remote: >> ERROR: declined in execute-commands--pre-receive + EOF + test_cmp expect actual +' + +test_expect_success "cannot push mixed references (declined)" ' + ( + cd work && + test_must_fail git push origin \ + HEAD:refs/for/master/my/topic \ + HEAD:refs/heads/master + ) >out 2>&1 && + grep "^remote:" out | sed -e "s/ *\$//g" >actual && + cat >expect <<-EOF && + remote: execute: execute-commands--pre-receive + remote: >> old: 0000000000000000000000000000000000000000, new: ce858e653cdbf70f9955a39d73a44219e4b92e9e, ref: refs/for/master/my/topic. + remote: >> ERROR: declined in execute-commands--pre-receive + EOF + test_cmp expect actual +' + +test_expect_success "new pre-receive hook (declined version)" ' + mv $bare/hooks/execute-commands--pre-receive $bare/hooks/execute-commands--pre-receive.fail && + mv $bare/hooks/execute-commands--pre-receive.ok $bare/hooks/execute-commands--pre-receive && + mv $bare/hooks/pre-receive $bare/hooks/pre-receive.ok && + cat >$bare/hooks/pre-receive <<-EOF && + #!/bin/sh + + printf >&2 "execute: pre-receive hook\n" + + while read old new ref + do + printf >&2 ">> old: \$old, new: \$new, ref: \$ref.\n" + done + printf >&2 ">> ERROR: declined in pre-receive hook\n" + exit 1 + EOF + chmod a+x $bare/hooks/pre-receive +' + +test_expect_success "cannot push mixed references (declined)" ' + ( + cd work && + test_must_fail git push origin \ + HEAD:refs/for/master/my/topic \ + HEAD:refs/heads/master + ) >out 2>&1 && + grep "^remote:" out | sed -e "s/ *\$//g" >actual && + cat >expect <<-EOF && + remote: execute: execute-commands--pre-receive + remote: >> old: 0000000000000000000000000000000000000000, new: ce858e653cdbf70f9955a39d73a44219e4b92e9e, ref: refs/for/master/my/topic. + remote: execute: pre-receive hook + remote: >> old: 102939797ab91a4f201d131418d2c9d919dcdd2c, new: ce858e653cdbf70f9955a39d73a44219e4b92e9e, ref: refs/heads/master. + remote: >> ERROR: declined in pre-receive hook + EOF + test_cmp expect actual +' + +test_done From patchwork Wed Mar 4 11:33:07 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jiang Xin X-Patchwork-Id: 11419919 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 8C31A14B4 for ; Wed, 4 Mar 2020 11:34:11 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 6D9732166E for ; Wed, 4 Mar 2020 11:34:11 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="Ly03JnSg" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2387851AbgCDLeK (ORCPT ); Wed, 4 Mar 2020 06:34:10 -0500 Received: from mail-pj1-f67.google.com ([209.85.216.67]:33773 "EHLO mail-pj1-f67.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S2387776AbgCDLeK (ORCPT ); Wed, 4 Mar 2020 06:34:10 -0500 Received: by mail-pj1-f67.google.com with SMTP id o21so961693pjs.0 for ; Wed, 04 Mar 2020 03:34:09 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=OhjHc34/ATq6WbzJJIUAc6MuNm7WpSCV14ih6g2SQYo=; b=Ly03JnSg0g8Jetc2hiQLyyi0oxqvu3P99VUVz/Tla561kst+XxCSq1N9lLUS5DMTFw nU0tqfdigpzras94lypNKvoBkqdH6Drk2n7EwXZzvvHE58KVcT+AeOWRIg665WJyF638 qnACJeP3B3cFYXllw/b8N+0jOrXbzBKc2S9Tt4CKlfEIkK4xJCx8+yUYk1FYnZuTjHnH UcIiIkP9W8pRkYp619Rqy5Mx3q0Czc0AAWcJrxDrbz22bAxRdSxMY6JqfHL0hIirtFWV uoILstOnX7TADQxKJ0ki1fYDGuZkIwmJ4eqOTQYlfBWQROhq07U2qFE6oCaNgBPRtwgD FC+A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=OhjHc34/ATq6WbzJJIUAc6MuNm7WpSCV14ih6g2SQYo=; b=aW9A+13OO4kVIotcnZIOtzuH7iDvV0/M3NV4eEtTF9SwH7/JehGdjrr9IO5WN8/CcD A7dkHeniKCaGNNdvV1AwEgI9/BsxFpLBE7tvo20/OTc1b185GI9PKpAHV0eLmPdkz4UR iGZHT/KfkO9lUjx0kH6E6pBKQrbTZrK9vF4OX7VFvfiS2S4xVNE1SE+kjz14simgmmpC 8lgP7aq3nWDxQCwFpES63fgcmbLm+Lrr68wCO4JlX4ZYRRMgAlsLueeDngP5rCF3cBXT rqjZvVkTGvgKYUGeNGcaiiqm/AUoFNFcsGapCJSE3ez5KPWnE6XIaUAiRaL8nGcWuHLz UfLg== X-Gm-Message-State: ANhLgQ3hbw/0WQUfOtleWlFacVXK8eFiHHAHxGRAgNUcOzSUQewOHRP1 3bs5WLrpkblExbrGjinujkB5ZGp63P44rQ== X-Google-Smtp-Source: ADFU+vs84UWUvWlIZSS9TcpEmebDkvtZp4XS6gZlR2KrliovBjvnoaKOLDLxwdAnhPk7ti9miWfusw== X-Received: by 2002:a17:90a:608:: with SMTP id j8mr2592732pjj.85.1583321648741; Wed, 04 Mar 2020 03:34:08 -0800 (PST) Received: from localhost.localdomain ([47.89.83.4]) by smtp.gmail.com with ESMTPSA id d77sm15350050pfd.109.2020.03.04.03.34.07 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Wed, 04 Mar 2020 03:34:08 -0800 (PST) From: Jiang Xin X-Google-Original-From: Jiang Xin To: Git List Cc: Jiang Xin , Junio C Hamano Subject: [PATCH 2/7] receive-pack: feed all commands to post-receive Date: Wed, 4 Mar 2020 19:33:07 +0800 Message-Id: <20200304113312.34229-3-zhiyou.jx@alibaba-inc.com> X-Mailer: git-send-email 2.25.1.362.g51ebf55b93 In-Reply-To: <20200304113312.34229-1-zhiyou.jx@alibaba-inc.com> References: <20200304113312.34229-1-zhiyou.jx@alibaba-inc.com> MIME-Version: 1.0 Sender: git-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: git@vger.kernel.org Feed all commands to "post-receive" hook, not only normal commands, but also commands with "exec_by_hook" flag. Signed-off-by: Jiang Xin --- builtin/receive-pack.c | 11 +++++++++-- t/t5411-execute-commands-hook.sh | 8 ++++---- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c index 24eb999ed4..c97abfbcd3 100644 --- a/builtin/receive-pack.c +++ b/builtin/receive-pack.c @@ -669,6 +669,7 @@ static void prepare_push_cert_sha1(struct child_process *proc) struct receive_hook_feed_state { struct command *cmd; + int exec_all; int exec_by_hook; int hook_must_exist; int skip_broken; @@ -761,6 +762,8 @@ static int feed_receive_hook(void *state_, const char **bufp, size_t *sizep) while (cmd) if (state->skip_broken && (cmd->error_string || cmd->did_not_exist)) cmd = cmd->next; + else if (state->exec_all) + break; else if (state->exec_by_hook && !cmd->exec_by_hook) cmd = cmd->next; else if (!state->exec_by_hook && cmd->exec_by_hook) @@ -784,6 +787,7 @@ static int feed_receive_hook(void *state_, const char **bufp, size_t *sizep) static int run_receive_hook(struct command *commands, const char *hook_name, int skip_broken, + int exec_all, const struct string_list *push_options) { struct receive_hook_feed_state state; @@ -791,6 +795,7 @@ static int run_receive_hook(struct command *commands, strbuf_init(&state.buf, 0); state.cmd = commands; + state.exec_all = exec_all; state.exec_by_hook = 0; state.hook_must_exist = 0; state.skip_broken = skip_broken; @@ -840,6 +845,7 @@ static int run_execute_commands_pre_receive_hook(struct command *commands, strbuf_init(&state.buf, 0); state.cmd = commands; + state.exec_all = 0; state.exec_by_hook = 1; state.hook_must_exist = 0; state.skip_broken = 0; @@ -861,6 +867,7 @@ static int run_execute_commands_hook(struct command *commands, strbuf_init(&state.buf, 0); state.cmd = commands; + state.exec_all = 0; state.exec_by_hook = 1; state.hook_must_exist = 1; state.skip_broken = 1; @@ -1575,7 +1582,7 @@ static void execute_commands(struct command *commands, } if (seen_internal_exec) - if (run_receive_hook(commands, "pre-receive", 0, push_options)) { + if (run_receive_hook(commands, "pre-receive", 0, 0, push_options)) { for (cmd = commands; cmd; cmd = cmd->next) { if (!cmd->error_string) cmd->error_string = "pre-receive hook declined"; @@ -2114,7 +2121,7 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix) unlink_or_warn(pack_lockfile); if (report_status) report(commands, unpack_status); - run_receive_hook(commands, "post-receive", 1, + run_receive_hook(commands, "post-receive", 1, 1, &push_options); run_update_post_hook(commands); string_list_clear(&push_options, 0); diff --git a/t/t5411-execute-commands-hook.sh b/t/t5411-execute-commands-hook.sh index 2ff0d5cbcd..b6444ca047 100755 --- a/t/t5411-execute-commands-hook.sh +++ b/t/t5411-execute-commands-hook.sh @@ -125,7 +125,7 @@ test_expect_success "create local topic branch" ' ) ' -test_expect_failure "push one special ref: refs/for/master" ' +test_expect_success "push one special ref: refs/for/master" ' ( cd work && git update-ref HEAD $B && @@ -182,7 +182,7 @@ test_expect_success "add back the execute-commands hook" ' mv $bare/hooks/execute-commands.ok $bare/hooks/execute-commands ' -test_expect_failure "push one special ref: refs/for/a/b/c" ' +test_expect_success "push one special ref: refs/for/a/b/c" ' ( cd work && git push origin HEAD:refs/for/a/b/c/my/topic @@ -199,7 +199,7 @@ test_expect_failure "push one special ref: refs/for/a/b/c" ' test_cmp expect actual ' -test_expect_failure "push two special references" ' +test_expect_success "push two special references" ' ( cd work && git push origin \ @@ -300,7 +300,7 @@ test_expect_success "restore execute-commands hook" ' mv $bare/hooks/execute-commands.ok $bare/hooks/execute-commands ' -test_expect_failure "push mixed references successfully" ' +test_expect_success "push mixed references successfully" ' ( cd work && git push origin \ From patchwork Wed Mar 4 11:33:08 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jiang Xin X-Patchwork-Id: 11419923 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 926B21395 for ; Wed, 4 Mar 2020 11:34:15 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 6A4A1214D8 for ; Wed, 4 Mar 2020 11:34:15 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="nVFURce7" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2387879AbgCDLeO (ORCPT ); Wed, 4 Mar 2020 06:34:14 -0500 Received: from mail-pj1-f52.google.com ([209.85.216.52]:53558 "EHLO mail-pj1-f52.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S2387799AbgCDLeO (ORCPT ); Wed, 4 Mar 2020 06:34:14 -0500 Received: by mail-pj1-f52.google.com with SMTP id cx7so798318pjb.3 for ; Wed, 04 Mar 2020 03:34:11 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=Z75zcX+dqdDEaAW4aSZfQzpTDkbSJMbravljvhZkQYw=; b=nVFURce7j2tKSn/4n9fQDb5GuvocPdpCvxzDmA+rPGzxDFWXunlgLdgn8ePDzsdUR/ qLYI5JNHVtln8o78FA7jMPKYYeGJ+bYJIkVoWENiHDVxQRUmcvR4bJh/c5HfRQkaG4F1 p2mSnzt1LMP4JckZYlEW+YEQKmZ+slo0ohLdDLJNnbn0blgvfp49M395GtrTFeedTTH2 KFite4ubg8yVT/OkgnP/sOWy7A4DYiGvDfy+b/tyM/ySXGAHYoy+ML5jAXZ2YQQG+IB0 lBpoZ8CJG+uTnWDwW5klYvm31hdX7YJEQyJVvcmfftJzt1MI3V/Ixm4gKpM2tISAWmAP WhxQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=Z75zcX+dqdDEaAW4aSZfQzpTDkbSJMbravljvhZkQYw=; b=JILPx4IJ/2+rxGvqT4Pf5clRnEvvvZUVVhh1HvT8DrRC0IvzyLinmJ7xnMKsszjjTR ++Oy4HSIn9D2l0I5SJf+bydvfCIo1j3ikpAanWUfLlrobd4DNFS50sAscDiPsRTCoGZW Ukjo5D9de+4guZ6keDyZSUT833qNdxfI/5l3F6vGmiA23+Ef9bwEkvfDlh56GP3QpAOJ rubuUygmNifrFxi/rvZzFzeDzFi9Ofh2f5TG9d5a0AyFQjAcU/FlQezWjDcUmBBfUxEu nwHFKgnYkCwmgxRuxDf9w5GWDp0as/4IFvdFDL/YfpW+vaicRQsRvAlEzOhqXLEfcuIX wL6g== X-Gm-Message-State: ANhLgQ0FFgF0uwtBwg3S7PqQ6ZcO6RbYxG/KoNarUHcq6V7plw1DRYXs r6Gk5c04WhfkYyPHthClJ66ifoMHxF7bTQ== X-Google-Smtp-Source: ADFU+vu/ETGr9QZ8j7VJJa8PPHZECoulO0X2pZz9sejtuivb1e29ekmzOUiWlIGM1TUzD/ozlCrpgQ== X-Received: by 2002:a17:902:8492:: with SMTP id c18mr2794751plo.147.1583321650767; Wed, 04 Mar 2020 03:34:10 -0800 (PST) Received: from localhost.localdomain ([47.89.83.4]) by smtp.gmail.com with ESMTPSA id d77sm15350050pfd.109.2020.03.04.03.34.09 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Wed, 04 Mar 2020 03:34:10 -0800 (PST) From: Jiang Xin X-Google-Original-From: Jiang Xin To: Git List Cc: Jiang Xin , Junio C Hamano Subject: [PATCH 3/7] receive-pack: try `execute-commands --pre-receive` Date: Wed, 4 Mar 2020 19:33:08 +0800 Message-Id: <20200304113312.34229-4-zhiyou.jx@alibaba-inc.com> X-Mailer: git-send-email 2.25.1.362.g51ebf55b93 In-Reply-To: <20200304113312.34229-1-zhiyou.jx@alibaba-inc.com> References: <20200304113312.34229-1-zhiyou.jx@alibaba-inc.com> MIME-Version: 1.0 Sender: git-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: git@vger.kernel.org First we try to find the hook "execute-command--pre-receive" to check permissions for special commands. If the hook does not exist, will try to find the hook "execute-commands" and run command `execute-commands --pre-receive` instead. Signed-off-by: Jiang Xin --- builtin/receive-pack.c | 30 ++++++++++---- t/t5411-execute-commands-hook.sh | 68 +++++++++++++++++++++++--------- 2 files changed, 71 insertions(+), 27 deletions(-) diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c index c97abfbcd3..241b1d4cfc 100644 --- a/builtin/receive-pack.c +++ b/builtin/receive-pack.c @@ -683,20 +683,34 @@ static int run_and_feed_hook(const char *hook_name, feed_fn feed, { struct child_process proc = CHILD_PROCESS_INIT; struct async muxer; - const char *argv[2]; + const char *argv[3]; int code; argv[0] = find_hook(hook_name); if (!argv[0]) { - if (feed_state->hook_must_exist) { - rp_error("cannot to find hook '%s'", hook_name); - return 1; - } else - return 0; + char *hook_helper; + char *opt; + + opt = strstr(hook_name, "--"); + if (opt) { + hook_helper = xstrdup(hook_name); + hook_helper[opt - hook_name] = '\0'; + argv[0] = find_hook(hook_helper); + free(hook_helper); + } + if (!argv[0]) { + if (feed_state->hook_must_exist) { + rp_error("cannot to find hook '%s'", hook_name); + return 1; + } else + return 0; + } + argv[1] = opt; + argv[2] = NULL; + } else { + argv[1] = NULL; } - argv[1] = NULL; - proc.argv = argv; proc.in = -1; proc.stdout_to_stderr = 1; diff --git a/t/t5411-execute-commands-hook.sh b/t/t5411-execute-commands-hook.sh index b6444ca047..0bf14e702d 100755 --- a/t/t5411-execute-commands-hook.sh +++ b/t/t5411-execute-commands-hook.sh @@ -64,6 +64,11 @@ test_expect_success "setup hooks" ' printf >&2 "execute: execute-commands\n" + if test \$# -gt 0 && test "\$1" = "--pre-receive" + then + printf >&2 ">> pre-receive mode\n" + fi + while read old new ref do printf >&2 ">> old: \$old, new: \$new, ref: \$ref.\n" @@ -183,13 +188,15 @@ test_expect_success "add back the execute-commands hook" ' ' test_expect_success "push one special ref: refs/for/a/b/c" ' + mv $bare/hooks/execute-commands--pre-receive $bare/hooks/execute-commands--pre-receive.ok && ( cd work && git push origin HEAD:refs/for/a/b/c/my/topic ) >out 2>&1 && grep "^remote:" out | sed -e "s/ *\$//g" >actual && cat >expect <<-EOF && - remote: execute: execute-commands--pre-receive + remote: execute: execute-commands + remote: >> pre-receive mode remote: >> old: 0000000000000000000000000000000000000000, new: ce858e653cdbf70f9955a39d73a44219e4b92e9e, ref: refs/for/a/b/c/my/topic. remote: execute: execute-commands remote: >> old: 0000000000000000000000000000000000000000, new: ce858e653cdbf70f9955a39d73a44219e4b92e9e, ref: refs/for/a/b/c/my/topic. @@ -208,7 +215,8 @@ test_expect_success "push two special references" ' ) >out 2>&1 && grep "^remote:" out | sed -e "s/ *\$//g" >actual && cat >expect <<-EOF && - remote: execute: execute-commands--pre-receive + remote: execute: execute-commands + remote: >> pre-receive mode remote: >> old: 0000000000000000000000000000000000000000, new: ce858e653cdbf70f9955a39d73a44219e4b92e9e, ref: refs/for/maint/my/topic. remote: >> old: 0000000000000000000000000000000000000000, new: ce858e653cdbf70f9955a39d73a44219e4b92e9e, ref: refs/for/a/b/c/my/topic. remote: execute: execute-commands @@ -228,13 +236,21 @@ test_expect_success "new execute-commands hook (fail with error)" ' printf >&2 "execute: execute-commands\n" + if test \$# -gt 0 && test "\$1" = "--pre-receive" + then + printf >&2 ">> pre-receive mode\n" + fi + while read old new ref do printf >&2 ">> old: \$old, new: \$new, ref: \$ref.\n" done - printf >&2 "fail to run execute-commands\n" - exit 1 + if test \$# -eq 0 + then + printf >&2 "fail to run execute-commands\n" + exit 1 + fi EOF chmod a+x $bare/hooks/execute-commands ' @@ -248,7 +264,8 @@ test_expect_success "successfully push normal ref, and fail to push special refe ) >out 2>&1 && grep "^remote:" out | sed -e "s/ *\$//g" >actual && cat >expect <<-EOF && - remote: execute: execute-commands--pre-receive + remote: execute: execute-commands + remote: >> pre-receive mode remote: >> old: 0000000000000000000000000000000000000000, new: ce858e653cdbf70f9955a39d73a44219e4b92e9e, ref: refs/for/maint/my/topic. remote: execute: pre-receive hook remote: >> old: 102939797ab91a4f201d131418d2c9d919dcdd2c, new: ce858e653cdbf70f9955a39d73a44219e4b92e9e, ref: refs/heads/master. @@ -284,7 +301,8 @@ test_expect_success "all mixed refs are failed to push in atomic mode" ' ) >out 2>&1 && grep "^remote:" out | sed -e "s/ *\$//g" >actual && cat >expect <<-EOF && - remote: execute: execute-commands--pre-receive + remote: execute: execute-commands + remote: >> pre-receive mode remote: >> old: 0000000000000000000000000000000000000000, new: ce858e653cdbf70f9955a39d73a44219e4b92e9e, ref: refs/for/maint/my/topic. remote: execute: pre-receive hook remote: >> old: 102939797ab91a4f201d131418d2c9d919dcdd2c, new: ce858e653cdbf70f9955a39d73a44219e4b92e9e, ref: refs/heads/master. @@ -309,7 +327,8 @@ test_expect_success "push mixed references successfully" ' ) >out 2>&1 && grep "^remote:" out | sed -e "s/ *\$//g" >actual && cat >expect <<-EOF && - remote: execute: execute-commands--pre-receive + remote: execute: execute-commands + remote: >> pre-receive mode remote: >> old: 0000000000000000000000000000000000000000, new: ce858e653cdbf70f9955a39d73a44219e4b92e9e, ref: refs/for/maint/my/topic. remote: execute: pre-receive hook remote: >> old: 102939797ab91a4f201d131418d2c9d919dcdd2c, new: ce858e653cdbf70f9955a39d73a44219e4b92e9e, ref: refs/heads/master. @@ -336,22 +355,30 @@ test_expect_success "restore remote master branch" ' test_cmp expect actual ' -test_expect_success "new execute-commands--pre-receive hook (declined version)" ' - mv $bare/hooks/execute-commands--pre-receive $bare/hooks/execute-commands--pre-receive.ok && - cat >$bare/hooks/execute-commands--pre-receive <<-EOF && +test_expect_success "new execute-commands hook (pre-receive declined)" ' + mv $bare/hooks/execute-commands $bare/hooks/execute-commands.ok && + cat >$bare/hooks/execute-commands <<-EOF && #!/bin/sh - printf >&2 "execute: execute-commands--pre-receive\n" + printf >&2 "execute: execute-commands\n" + + if test \$# -gt 0 && test "\$1" = "--pre-receive" + then + printf >&2 ">> pre-receive mode\n" + fi while read old new ref do printf >&2 ">> old: \$old, new: \$new, ref: \$ref.\n" done - printf >&2 ">> ERROR: declined in execute-commands--pre-receive\n" - exit 1 + if test \$# -gt 0 && test "\$1" = "--pre-receive" + then + printf >&2 ">> ERROR: declined in execute-commands--pre-receive\n" + exit 1 + fi EOF - chmod a+x $bare/hooks/execute-commands--pre-receive + chmod a+x $bare/hooks/execute-commands ' test_expect_success "cannot push two special references (declined)" ' @@ -363,7 +390,8 @@ test_expect_success "cannot push two special references (declined)" ' ) >out 2>&1 && grep "^remote:" out | sed -e "s/ *\$//g" >actual && cat >expect <<-EOF && - remote: execute: execute-commands--pre-receive + remote: execute: execute-commands + remote: >> pre-receive mode remote: >> old: 0000000000000000000000000000000000000000, new: ce858e653cdbf70f9955a39d73a44219e4b92e9e, ref: refs/for/master/my/topic. remote: >> old: 0000000000000000000000000000000000000000, new: ce858e653cdbf70f9955a39d73a44219e4b92e9e, ref: refs/for/maint/my/topic. remote: >> ERROR: declined in execute-commands--pre-receive @@ -380,7 +408,8 @@ test_expect_success "cannot push mixed references (declined)" ' ) >out 2>&1 && grep "^remote:" out | sed -e "s/ *\$//g" >actual && cat >expect <<-EOF && - remote: execute: execute-commands--pre-receive + remote: execute: execute-commands + remote: >> pre-receive mode remote: >> old: 0000000000000000000000000000000000000000, new: ce858e653cdbf70f9955a39d73a44219e4b92e9e, ref: refs/for/master/my/topic. remote: >> ERROR: declined in execute-commands--pre-receive EOF @@ -388,8 +417,8 @@ test_expect_success "cannot push mixed references (declined)" ' ' test_expect_success "new pre-receive hook (declined version)" ' - mv $bare/hooks/execute-commands--pre-receive $bare/hooks/execute-commands--pre-receive.fail && - mv $bare/hooks/execute-commands--pre-receive.ok $bare/hooks/execute-commands--pre-receive && + mv $bare/hooks/execute-commands $bare/hooks/execute-commands.fail && + mv $bare/hooks/execute-commands.ok $bare/hooks/execute-commands && mv $bare/hooks/pre-receive $bare/hooks/pre-receive.ok && cat >$bare/hooks/pre-receive <<-EOF && #!/bin/sh @@ -415,7 +444,8 @@ test_expect_success "cannot push mixed references (declined)" ' ) >out 2>&1 && grep "^remote:" out | sed -e "s/ *\$//g" >actual && cat >expect <<-EOF && - remote: execute: execute-commands--pre-receive + remote: execute: execute-commands + remote: >> pre-receive mode remote: >> old: 0000000000000000000000000000000000000000, new: ce858e653cdbf70f9955a39d73a44219e4b92e9e, ref: refs/for/master/my/topic. remote: execute: pre-receive hook remote: >> old: 102939797ab91a4f201d131418d2c9d919dcdd2c, new: ce858e653cdbf70f9955a39d73a44219e4b92e9e, ref: refs/heads/master. From patchwork Wed Mar 4 11:33:09 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Jiang Xin X-Patchwork-Id: 11419925 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 564DB14B4 for ; Wed, 4 Mar 2020 11:34:17 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 22C6F20848 for ; Wed, 4 Mar 2020 11:34:17 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="S/qwBpmU" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2387881AbgCDLeQ (ORCPT ); Wed, 4 Mar 2020 06:34:16 -0500 Received: from mail-pl1-f173.google.com ([209.85.214.173]:33032 "EHLO mail-pl1-f173.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S2387863AbgCDLeP (ORCPT ); Wed, 4 Mar 2020 06:34:15 -0500 Received: by mail-pl1-f173.google.com with SMTP id ay11so919323plb.0 for ; Wed, 04 Mar 2020 03:34:14 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=a7C2Cyz9XOA6zcAEFLp48kmqzquplJwiHk3Qlq1IOig=; b=S/qwBpmU/g8aq0hR6tqqYAyn8bKzBb7ZU0m6t+gLSw1KgYte/2+G8HNFadLECFMC6T +qtD1uxtH9BAYzFmpOr5zRCM5SYqhvJEePgXFqHqENA/krQwtp2loggxWp1Fq7T6fntJ t2V7d2LScmBob4o8TZlx2eKJTh5wuomK10Lowhemz6lv6ZdiS/TCYUJCDqTUQfuCmqrw ZNZA1ojLR1USpJKpQxzqIuDEzCJEveNoyktA2yke4l/EBvGoILgfjiKNOjcasi+9tha4 Py5JWOw473ScFtBUGZ4vvZrLiAxB3AmQOpZjmTqjHfl38EcUvR9uDBaSwda6fEv2dqD5 BmgA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=a7C2Cyz9XOA6zcAEFLp48kmqzquplJwiHk3Qlq1IOig=; b=PKGapLc9inR/a4ht33q25ZyxKoXar/ej3+HF9DL1FyBzjNauevXYITvXOBblRFpu1P U53zOcf3NDQOmLN435100e0i2ZkF7iUXTBV+BzhC4rSriC38FQZ9ZrYIomVoq8bnxWa6 jtQyZAWOf9V7U1uq7jnaU/s6WYdxZilBgoqAjDQCI3t/3Y24lOMfaX1rz89grVdD5jN1 //j3CqYLCmUaIB/0bW6j3pwJ0PlnOMXEXp1SH+MVssZAF5hiYlv/SBIaSst0AuWmLfOj VosbNft6RxOOsYFtGiR7hQdj5jG7Ey/Ts2s+c81k4+Jj5NZ/cD834RmTXrLg0L+EkEbT pFCQ== X-Gm-Message-State: ANhLgQ38BkRD82e5/VWXkplDqaEIuDdEmG8nsXPuAqWRheNXMyVSUsqs 4wevzleCvu8JjSjXGbHTF/wc8qnVr3Y3hg== X-Google-Smtp-Source: ADFU+vvzAlbz1EdE168SnY9vqk88/3AYAhXoYfBnwJLQolouSbHQbKsNd3bxHDo4OpiWPHJRNuJEEA== X-Received: by 2002:a17:902:444:: with SMTP id 62mr2451544ple.209.1583321652829; Wed, 04 Mar 2020 03:34:12 -0800 (PST) Received: from localhost.localdomain ([47.89.83.4]) by smtp.gmail.com with ESMTPSA id d77sm15350050pfd.109.2020.03.04.03.34.11 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Wed, 04 Mar 2020 03:34:12 -0800 (PST) From: Jiang Xin X-Google-Original-From: Jiang Xin To: Git List Cc: Jiang Xin , Junio C Hamano Subject: [PATCH 4/7] receive-pack: read env from execute-commands output Date: Wed, 4 Mar 2020 19:33:09 +0800 Message-Id: <20200304113312.34229-5-zhiyou.jx@alibaba-inc.com> X-Mailer: git-send-email 2.25.1.362.g51ebf55b93 In-Reply-To: <20200304113312.34229-1-zhiyou.jx@alibaba-inc.com> References: <20200304113312.34229-1-zhiyou.jx@alibaba-inc.com> MIME-Version: 1.0 Sender: git-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: git@vger.kernel.org The “post-receive” hook may need the pull request ID generated by the “execute-commands” hook. The results can be passed between hooks by environment variables. Each line of the message received from the standard output of the “execute-commands” in the key=value format is parsed as environment and these variables will be sent to environment of the “post-receive” hook. Signed-off-by: Jiang Xin --- builtin/receive-pack.c | 42 +++++++++++++++-- t/t5411-execute-commands-hook.sh | 79 ++++++++++++++++++++++++++++++++ 2 files changed, 117 insertions(+), 4 deletions(-) diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c index 241b1d4cfc..fd2f3ba80a 100644 --- a/builtin/receive-pack.c +++ b/builtin/receive-pack.c @@ -85,6 +85,7 @@ static const char *nonce_status; static long nonce_stamp_slop; static timestamp_t nonce_stamp_slop_limit; static struct ref_transaction *transaction; +struct argv_array post_receive_env_array; static enum { KEEPALIVE_NEVER = 0, @@ -678,7 +679,9 @@ struct receive_hook_feed_state { }; typedef int (*feed_fn)(void *, const char **, size_t *); +typedef void (*stdout_handler_fn)(int out); static int run_and_feed_hook(const char *hook_name, feed_fn feed, + stdout_handler_fn stdout_handler, struct receive_hook_feed_state *feed_state) { struct child_process proc = CHILD_PROCESS_INIT; @@ -713,9 +716,15 @@ static int run_and_feed_hook(const char *hook_name, feed_fn feed, proc.argv = argv; proc.in = -1; - proc.stdout_to_stderr = 1; + if (stdout_handler) + proc.out = -1; + else + proc.stdout_to_stderr = 1; proc.trace2_hook_name = hook_name; + if (!strcmp(hook_name, "post-receive") && post_receive_env_array.argc > 0) + argv_array_pushv(&proc.env_array, post_receive_env_array.argv); + if (feed_state->push_options) { int i; for (i = 0; i < feed_state->push_options->nr; i++) @@ -760,6 +769,10 @@ static int run_and_feed_hook(const char *hook_name, feed_fn feed, break; } close(proc.in); + + if (stdout_handler) + stdout_handler(proc.out); + if (use_sideband) finish_async(&muxer); @@ -817,7 +830,7 @@ static int run_receive_hook(struct command *commands, return 0; state.cmd = commands; state.push_options = push_options; - status = run_and_feed_hook(hook_name, feed_receive_hook, &state); + status = run_and_feed_hook(hook_name, feed_receive_hook, NULL, &state); strbuf_release(&state.buf); return status; } @@ -868,11 +881,29 @@ static int run_execute_commands_pre_receive_hook(struct command *commands, state.cmd = commands; state.push_options = push_options; status = run_and_feed_hook("execute-commands--pre-receive", - feed_receive_hook, &state); + feed_receive_hook, NULL, &state); strbuf_release(&state.buf); return status; } + +static void prepare_post_receive_env(int in) +{ + struct strbuf stdout_buf = STRBUF_INIT; + + while (strbuf_getwholeline_fd(&stdout_buf, in, '\n') != EOF) { + char *p = stdout_buf.buf + stdout_buf.len -1; + if (*p =='\n') + *p = '\0'; + p = strchr(stdout_buf.buf, '='); + if (p == NULL) + continue; + argv_array_push(&post_receive_env_array, stdout_buf.buf); + strbuf_reset(&stdout_buf); + } + strbuf_release(&stdout_buf); +} + static int run_execute_commands_hook(struct command *commands, const struct string_list *push_options) { @@ -889,7 +920,8 @@ static int run_execute_commands_hook(struct command *commands, return 0; state.cmd = commands; state.push_options = push_options; - status = run_and_feed_hook("execute-commands", feed_receive_hook, &state); + status = run_and_feed_hook("execute-commands", + feed_receive_hook, prepare_post_receive_env, &state); strbuf_release(&state.buf); return status; } @@ -2052,6 +2084,8 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix) OPT_END() }; + argv_array_init(&post_receive_env_array); + packet_trace_identity("receive-pack"); argc = parse_options(argc, argv, prefix, options, receive_pack_usage, 0); diff --git a/t/t5411-execute-commands-hook.sh b/t/t5411-execute-commands-hook.sh index 0bf14e702d..1907d0619d 100755 --- a/t/t5411-execute-commands-hook.sh +++ b/t/t5411-execute-commands-hook.sh @@ -454,4 +454,83 @@ test_expect_success "cannot push mixed references (declined)" ' test_cmp expect actual ' +test_expect_success "new execute-commands and post-receive hooks (environments in output)" ' + ## execute-commands hook + mv $bare/hooks/execute-commands $bare/hooks/execute-commands.ok && + cat >$bare/hooks/execute-commands <<-EOF && + #!/bin/sh + + printf >&2 "execute: execute-commands\n" + + if test \$# -gt 0 && test "\$1" = "--pre-receive" + then + printf >&2 ">> pre-receive mode\n" + else + printf "GIT_VAR1=var1\n" + printf "GIT_VAR2=var2\n" + printf "AGIT_VAR1=foo\n" + printf "AGIT_VAR2=bar\n" + fi + + while read old new ref + do + printf >&2 ">> old: \$old, new: \$new, ref: \$ref.\n" + done + + for k in GIT_VAR1 GIT_VAR2 AGIT_VAR1 AGIT_VAR2 + do + if test -n "\$(eval echo \\"\\\$\$k\")" + then + printf >&2 ">> has env: \$k=\$(eval echo \\"\\\$\$k\").\n" + fi + done + EOF + chmod a+x $bare/hooks/execute-commands && + + ## post-receive hook + mv $bare/hooks/post-receive $bare/hooks/post-receive.ok && + cat >$bare/hooks/post-receive <<-EOF && + #!/bin/sh + + printf >&2 "execute: post-receive hook\n" + + while read old new ref + do + printf >&2 ">> old: \$old, new: \$new, ref: \$ref.\n" + done + + for k in GIT_VAR1 GIT_VAR2 AGIT_VAR1 AGIT_VAR2 + do + if test -n "\$(eval echo \\"\\\$\$k\")" + then + printf >&2 ">> has env: \$k=\$(eval echo \\"\\\$\$k\").\n" + fi + done + EOF + chmod a+x $bare/hooks/post-receive +' + +test_expect_success "push and show environments" ' + ( + cd work && + git push origin \ + HEAD:refs/for/master/my/topic + ) >out 2>&1 && + grep "^remote:" out | sed -e "s/ *\$//g" >actual && + cat >expect <<-EOF && + remote: execute: execute-commands + remote: >> pre-receive mode + remote: >> old: 0000000000000000000000000000000000000000, new: ce858e653cdbf70f9955a39d73a44219e4b92e9e, ref: refs/for/master/my/topic. + remote: execute: execute-commands + remote: >> old: 0000000000000000000000000000000000000000, new: ce858e653cdbf70f9955a39d73a44219e4b92e9e, ref: refs/for/master/my/topic. + remote: execute: post-receive hook + remote: >> old: 0000000000000000000000000000000000000000, new: ce858e653cdbf70f9955a39d73a44219e4b92e9e, ref: refs/for/master/my/topic. + remote: >> has env: GIT_VAR1=var1. + remote: >> has env: GIT_VAR2=var2. + remote: >> has env: AGIT_VAR1=foo. + remote: >> has env: AGIT_VAR2=bar. + EOF + test_cmp expect actual +' + test_done From patchwork Wed Mar 4 11:33:10 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jiang Xin X-Patchwork-Id: 11419927 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id D5E4914B4 for ; Wed, 4 Mar 2020 11:34:18 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id B73A32166E for ; Wed, 4 Mar 2020 11:34:18 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="R6JyQeDU" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2387897AbgCDLeS (ORCPT ); Wed, 4 Mar 2020 06:34:18 -0500 Received: from mail-pg1-f193.google.com ([209.85.215.193]:33404 "EHLO mail-pg1-f193.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S2387774AbgCDLeR (ORCPT ); Wed, 4 Mar 2020 06:34:17 -0500 Received: by mail-pg1-f193.google.com with SMTP id m5so884036pgg.0 for ; Wed, 04 Mar 2020 03:34:15 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=JggJxmPP5gHy87da6j0Fnf92VHvN+o3kRi3I6Nj2eWI=; b=R6JyQeDUQBfx5IQzbgT8pBdm/2+DvL/SLyVJZa4Ty1QlFo+yfGOHy5w3KZ5mZCZhcX lMlYOZ9KPhSmg8Deh41usGK2W4w4PQU7rJ6EonFQUupM3mMPlYlxYnKX8a2EoFGbCDDd u1Iw/TTrupOLfceItgcEJwsM7wC+gDzSofghoRB45ls9eEQiDG28I8DpuTVmDIzuNS0L 0ZLyPThM7Y/0c6XsEoV6vqlfo/DvfwOV6cwH9u0Lb++SmEPRMDr+frkbpbDZil5KLJIz PGos9gdw8/ff/oi7cjUYISkYqh02rXcHK8Fs5I7Te4rIef/dnYKA2Jcye8jVDqnyU3ue YaOg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=JggJxmPP5gHy87da6j0Fnf92VHvN+o3kRi3I6Nj2eWI=; b=kZF1El97ZwIsl8ddQ7VYoCcR+M9Wfn51eP8cAfjTwQ8/5lmEjSUPtCesCkuTfQxf7f 4ZJytif4s1dPBReArjQ2sBB+Qt3zsnSQyVBulF76a3lk7SY/hbqeaPwNz6QBln5WDsAX oURa7GfruSMA1c1VCgmgzIr0mHlR/T+pqVwatLcIA7IrZAE733u5dV7UCU8PfOASojSB yem2gKd+ChEp/0fLidTlChBJg+BvXFCruMIUm9KdpGeMGG5pHl2lCG9E3vTqEtFKKQ6k m1tK7MnJnzXn/IajXYx4iXVGoSJmeZzpjy59AJrKKuTqrQ5TVQLNBgTm2UpmpV/bMNeg 62sw== X-Gm-Message-State: ANhLgQ0e7DOkrPQdmYtGeW1wKyH147FKqA8LhlmFqzPMZjj2H+N5hiY7 BKsXpw6wU+euRb2PsBTjXSuQoE25/OXaNQ== X-Google-Smtp-Source: ADFU+vsPXek8bBjbI0aReR4kstMwlUzPkqvxxDYT4Y7+KvMZRWRuUrGTSMOHbeob3Hus8pX3ucIbWQ== X-Received: by 2002:a63:c20e:: with SMTP id b14mr2118000pgd.394.1583321654976; Wed, 04 Mar 2020 03:34:14 -0800 (PST) Received: from localhost.localdomain ([47.89.83.4]) by smtp.gmail.com with ESMTPSA id d77sm15350050pfd.109.2020.03.04.03.34.13 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Wed, 04 Mar 2020 03:34:14 -0800 (PST) From: Jiang Xin X-Google-Original-From: Jiang Xin To: Git List Cc: Jiang Xin , Junio C Hamano Subject: [PATCH 5/7] refs.c: refactor to reuse ref_is_hidden() Date: Wed, 4 Mar 2020 19:33:10 +0800 Message-Id: <20200304113312.34229-6-zhiyou.jx@alibaba-inc.com> X-Mailer: git-send-email 2.25.1.362.g51ebf55b93 In-Reply-To: <20200304113312.34229-1-zhiyou.jx@alibaba-inc.com> References: <20200304113312.34229-1-zhiyou.jx@alibaba-inc.com> MIME-Version: 1.0 Sender: git-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: git@vger.kernel.org Add new function `ref_is_matched()` to reuse `ref_is_hidden()`. Test case t5512 covered this change. Signed-off-by: Jiang Xin --- refs.c | 11 ++++++++--- refs.h | 1 + 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/refs.c b/refs.c index 1ab0bb54d3..229159ea1a 100644 --- a/refs.c +++ b/refs.c @@ -1389,13 +1389,18 @@ int parse_hide_refs_config(const char *var, const char *value, const char *secti } int ref_is_hidden(const char *refname, const char *refname_full) +{ + return ref_is_matched(hide_refs, refname, refname_full); +} + +int ref_is_matched(struct string_list *match_refs, const char *refname, const char *refname_full) { int i; - if (!hide_refs) + if (!match_refs) return 0; - for (i = hide_refs->nr - 1; i >= 0; i--) { - const char *match = hide_refs->items[i].string; + for (i = match_refs->nr - 1; i >= 0; i--) { + const char *match = match_refs->items[i].string; const char *subject; int neg = 0; const char *p; diff --git a/refs.h b/refs.h index 545029c6d8..a2ea043f7f 100644 --- a/refs.h +++ b/refs.h @@ -739,6 +739,7 @@ int parse_hide_refs_config(const char *var, const char *value, const char *); * parameter always points to the full ref name. */ int ref_is_hidden(const char *, const char *); +int ref_is_matched(struct string_list *, const char *, const char *); enum ref_type { REF_TYPE_PER_WORKTREE, /* refs inside refs/ but not shared */ From patchwork Wed Mar 4 11:33:11 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jiang Xin X-Patchwork-Id: 11419929 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 996511395 for ; Wed, 4 Mar 2020 11:34:20 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 71B17214D8 for ; Wed, 4 Mar 2020 11:34:20 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="RTICJZ+G" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2387916AbgCDLeT (ORCPT ); Wed, 4 Mar 2020 06:34:19 -0500 Received: from mail-pf1-f195.google.com ([209.85.210.195]:45635 "EHLO mail-pf1-f195.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S2387774AbgCDLeS (ORCPT ); Wed, 4 Mar 2020 06:34:18 -0500 Received: by mail-pf1-f195.google.com with SMTP id 2so822909pfg.12 for ; Wed, 04 Mar 2020 03:34:18 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=ne5F8ZRMvh556lbsimJKy9wo8h0Xh4HmkQ76OU4R18Y=; b=RTICJZ+GC3bjLwOyHYGBrqOjb9dWkFvrtj2IABL9JMbGDjvgGmoPbz8/dtGr6yuqL7 nQsLT99btDZbkb3vAYrKhBTq+6w9VGKA3pB4x55eVfAhH9m9X29nVGPscKBr5KwH6m3l dkTyd/AcD/rOGxl0KOMEMyRak86pWEbyUMuTkUu80j2Rrmf8FVmfVnbxmehyV18PjJ2m syvJOK4Q+oug3P6BKjdADS+qk3aG+8CC6pTzIM5SQ5PMUemYtXOOWcfOCRU6eRDXhszS RqdfmevresDC2rNL5JsoXaR9OR51rgUPZ6e7hleHAa12g/ucpf8o9ld/6kNEqALgBCVJ EYvg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=ne5F8ZRMvh556lbsimJKy9wo8h0Xh4HmkQ76OU4R18Y=; b=db63P2WrMVab9A9kJR4rs1H0AjpHdfkb1eol5g59bp36Q3+j4egAwQZihEWO65hYJn MLe2T64TSgj05SeqQ/LdzRy7UQJoKhJKI9e2kH0DIwb3vMkjoD3c7EAvG7uu2UnFjC46 EIJFrqed7w78PP1PEb/HaNSFpW3HnQK2Xq6N9pgw/tAyNTVKQa103p012EZGfMpxuBUR 0fF7+cbOd6PqQWlAPa+/VE4Xss+h9E8/cfay2ihJfLa+5w/WeS/pRlDi09LoBzPb+/iZ 9VtcXzLs4k3utRwr5Rj3kS/9TsWK1Qfzadjk207tYMVwFvqIIFzT355ATQO1SKm//f1M 9yig== X-Gm-Message-State: ANhLgQ2M+f9VRH1WUFyTQreHtBHjA+BrhWVUZiYBBPpCIjm+Ytf+NOhF 0kW6Q9yd9Uap6gaGOgBfqCRNL6Tt4V2/QA== X-Google-Smtp-Source: ADFU+vvRjBz8VCemwQMGFK946JTtFVI4sm+WNX4l1z1pSgkK+9ZCeJweU3ru8esVCqFE1WH0LFF2fg== X-Received: by 2002:a63:5251:: with SMTP id s17mr2247011pgl.160.1583321657233; Wed, 04 Mar 2020 03:34:17 -0800 (PST) Received: from localhost.localdomain ([47.89.83.4]) by smtp.gmail.com with ESMTPSA id d77sm15350050pfd.109.2020.03.04.03.34.15 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Wed, 04 Mar 2020 03:34:16 -0800 (PST) From: Jiang Xin X-Google-Original-From: Jiang Xin To: Git List Cc: Jiang Xin , Junio C Hamano Subject: [PATCH 6/7] receive-pack: new config receive.executeCommandsHookRefs Date: Wed, 4 Mar 2020 19:33:11 +0800 Message-Id: <20200304113312.34229-7-zhiyou.jx@alibaba-inc.com> X-Mailer: git-send-email 2.25.1.362.g51ebf55b93 In-Reply-To: <20200304113312.34229-1-zhiyou.jx@alibaba-inc.com> References: <20200304113312.34229-1-zhiyou.jx@alibaba-inc.com> MIME-Version: 1.0 Sender: git-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: git@vger.kernel.org Add a new multi-valued config variable "receive.executeCommandsHookRefs" for `receive-pack` command, like the follows: git config --system --add receive.executeCommandsHookRefs refs/for/ git config --system --add receive.executeCommandsHookRefs refs/drafts/ If the specific prefix strings match the reference names of the commands which are sent by git client to `receive-pack`, these commands will be executed by an external hook (named "execute-commands"), instead of the internal `execute_commands` function. For example, if it is set to "refs/for/", pushing to a reference such as "refs/for/master" will not create or update reference "refs/for/master", but may create or update a pull request directly by running the external hook. Use more than one definition to specify multiple prefix strings. Signed-off-by: Jiang Xin --- Documentation/config/receive.txt | 11 +++++ builtin/receive-pack.c | 48 ++++++++++++++++----- t/t5411-execute-commands-hook.sh | 72 ++++++++++++++++++++++++++++++-- 3 files changed, 117 insertions(+), 14 deletions(-) diff --git a/Documentation/config/receive.txt b/Documentation/config/receive.txt index 65f78aac37..a8e2125e62 100644 --- a/Documentation/config/receive.txt +++ b/Documentation/config/receive.txt @@ -108,6 +108,17 @@ receive.denyNonFastForwards:: even if that push is forced. This configuration variable is set when initializing a shared repository. +receive.executeCommandsHookRefs:: + If the specific prefix matches the reference name of the command + which is sent by the client to `receive-pack`, the command will be + executed by an external hook (named "execute-commands"), instead of + the internal `execute_commands` function. For example, if it is + set to "refs/for/", pushing to reference such as "refs/for/master" + will not create or update a reference named "refs/for/master", but + may create or update a pull request directly by running the external + hook. Use more than one definition to specify multiple prefix + strings. + receive.hideRefs:: This variable is the same as `transfer.hideRefs`, but applies only to `receive-pack` (and so affects pushes, but not fetches). diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c index fd2f3ba80a..f78adeffd4 100644 --- a/builtin/receive-pack.c +++ b/builtin/receive-pack.c @@ -75,6 +75,7 @@ static struct object_id push_cert_oid; static struct signature_check sigcheck; static const char *push_cert_nonce; static const char *cert_nonce_seed; +static struct string_list execute_commands_hook_refs; static const char *NONCE_UNSOLICITED = "UNSOLICITED"; static const char *NONCE_BAD = "BAD"; @@ -228,6 +229,20 @@ static int receive_pack_config(const char *var, const char *value, void *cb) return 0; } + if (strcmp(var, "receive.executecommandshookrefs") == 0) { + char *prefix; + int len; + + if (!value) + return config_error_nonbool(var); + prefix = xstrdup(value); + len = strlen(prefix); + while (len && prefix[len - 1] == '/') + prefix[--len] = '\0'; + string_list_insert(&execute_commands_hook_refs, prefix); + return 0; + } + return git_default_config(var, value, cb); } @@ -1600,17 +1615,29 @@ static void execute_commands(struct command *commands, /* Try to find commands that have special prefix, and will run these * commands using an external "execute-commands" hook. */ - for (cmd = commands; cmd; cmd = cmd->next) { - if (!should_process_cmd(cmd)) - continue; + if (execute_commands_hook_refs.nr > 0) { + struct strbuf refname_full = STRBUF_INIT; + size_t prefix_len; - /* TODO: replace the fixed prefix by looking up git config variables. */ - if (!strncmp(cmd->ref_name, "refs/for/", 9)) { - cmd->exec_by_hook = 1; - seen_exec_by_hook = 1; - } else - seen_internal_exec = 1; - } + strbuf_addstr(&refname_full, get_git_namespace()); + prefix_len = refname_full.len; + + for (cmd = commands; cmd; cmd = cmd->next) { + if (!should_process_cmd(cmd)) + continue; + + strbuf_setlen(&refname_full, prefix_len); + strbuf_addstr(&refname_full, cmd->ref_name); + if (ref_is_matched(&execute_commands_hook_refs, cmd->ref_name, refname_full.buf)) { + cmd->exec_by_hook = 1; + seen_exec_by_hook = 1; + } else + seen_internal_exec = 1; + } + + strbuf_release(&refname_full); + } else + seen_internal_exec = 1; if (seen_exec_by_hook) { /* Try to find and run the `execute-commands--pre-receive` hook to check @@ -2085,6 +2112,7 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix) }; argv_array_init(&post_receive_env_array); + string_list_init(&execute_commands_hook_refs, 0); packet_trace_identity("receive-pack"); diff --git a/t/t5411-execute-commands-hook.sh b/t/t5411-execute-commands-hook.sh index 1907d0619d..c8ee699773 100755 --- a/t/t5411-execute-commands-hook.sh +++ b/t/t5411-execute-commands-hook.sh @@ -41,6 +41,9 @@ test_expect_success setup ' # Enable push options for bare.git. git -C $bare config receive.advertisePushOptions true && + # Register ref prefix for execute-commands hook. + git -C $bare config --add receive.executeCommandsHookRefs refs/for/ && + git clone --no-local $bare work && create_commits_in work A B ' @@ -229,6 +232,67 @@ test_expect_success "push two special references" ' test_cmp expect actual ' +test_expect_success "push two special references, but one is not registered" ' + ( + cd work && + git push origin \ + HEAD:refs/for/maint/my/topic \ + HEAD:refs/drafts/maint/my/topic + ) >out 2>&1 && + grep "^remote:" out | sed -e "s/ *\$//g" >actual && + cat >expect <<-EOF && + remote: execute: execute-commands + remote: >> pre-receive mode + remote: >> old: 0000000000000000000000000000000000000000, new: ce858e653cdbf70f9955a39d73a44219e4b92e9e, ref: refs/for/maint/my/topic. + remote: execute: pre-receive hook + remote: >> old: 0000000000000000000000000000000000000000, new: ce858e653cdbf70f9955a39d73a44219e4b92e9e, ref: refs/drafts/maint/my/topic. + remote: execute: execute-commands + remote: >> old: 0000000000000000000000000000000000000000, new: ce858e653cdbf70f9955a39d73a44219e4b92e9e, ref: refs/for/maint/my/topic. + remote: execute: post-receive hook + remote: >> old: 0000000000000000000000000000000000000000, new: ce858e653cdbf70f9955a39d73a44219e4b92e9e, ref: refs/for/maint/my/topic. + remote: >> old: 0000000000000000000000000000000000000000, new: ce858e653cdbf70f9955a39d73a44219e4b92e9e, ref: refs/drafts/maint/my/topic. + EOF + test_cmp expect actual +' + +test_expect_success "restore bare.git, and register new ref prefix" ' + ( + cd $bare && + git config --add receive.executeCommandsHookRefs refs/drafts/ && + git update-ref -d refs/drafts/maint/my/topic && + git show-ref + ) >actual && + cat >expect <<-EOF && + 102939797ab91a4f201d131418d2c9d919dcdd2c refs/heads/a/b/c + 102939797ab91a4f201d131418d2c9d919dcdd2c refs/heads/maint + 102939797ab91a4f201d131418d2c9d919dcdd2c refs/heads/master + EOF + test_cmp expect actual +' + +test_expect_success "push to two special references (all registered)" ' + ( + cd work && + git push origin \ + HEAD:refs/for/master/my/topic \ + HEAD:refs/drafts/maint/my/topic + ) >out 2>&1 && + grep "^remote:" out | sed -e "s/ *\$//g" >actual && + cat >expect <<-EOF && + remote: execute: execute-commands + remote: >> pre-receive mode + remote: >> old: 0000000000000000000000000000000000000000, new: ce858e653cdbf70f9955a39d73a44219e4b92e9e, ref: refs/for/master/my/topic. + remote: >> old: 0000000000000000000000000000000000000000, new: ce858e653cdbf70f9955a39d73a44219e4b92e9e, ref: refs/drafts/maint/my/topic. + remote: execute: execute-commands + remote: >> old: 0000000000000000000000000000000000000000, new: ce858e653cdbf70f9955a39d73a44219e4b92e9e, ref: refs/for/master/my/topic. + remote: >> old: 0000000000000000000000000000000000000000, new: ce858e653cdbf70f9955a39d73a44219e4b92e9e, ref: refs/drafts/maint/my/topic. + remote: execute: post-receive hook + remote: >> old: 0000000000000000000000000000000000000000, new: ce858e653cdbf70f9955a39d73a44219e4b92e9e, ref: refs/for/master/my/topic. + remote: >> old: 0000000000000000000000000000000000000000, new: ce858e653cdbf70f9955a39d73a44219e4b92e9e, ref: refs/drafts/maint/my/topic. + EOF + test_cmp expect actual +' + test_expect_success "new execute-commands hook (fail with error)" ' mv $bare/hooks/execute-commands $bare/hooks/execute-commands.ok && cat >$bare/hooks/execute-commands <<-EOF && @@ -322,21 +386,21 @@ test_expect_success "push mixed references successfully" ' ( cd work && git push origin \ - HEAD:refs/for/maint/my/topic \ + HEAD:refs/drafts/maint/my/topic \ HEAD:refs/heads/master ) >out 2>&1 && grep "^remote:" out | sed -e "s/ *\$//g" >actual && cat >expect <<-EOF && remote: execute: execute-commands remote: >> pre-receive mode - remote: >> old: 0000000000000000000000000000000000000000, new: ce858e653cdbf70f9955a39d73a44219e4b92e9e, ref: refs/for/maint/my/topic. + remote: >> old: 0000000000000000000000000000000000000000, new: ce858e653cdbf70f9955a39d73a44219e4b92e9e, ref: refs/drafts/maint/my/topic. remote: execute: pre-receive hook remote: >> old: 102939797ab91a4f201d131418d2c9d919dcdd2c, new: ce858e653cdbf70f9955a39d73a44219e4b92e9e, ref: refs/heads/master. remote: execute: execute-commands - remote: >> old: 0000000000000000000000000000000000000000, new: ce858e653cdbf70f9955a39d73a44219e4b92e9e, ref: refs/for/maint/my/topic. + remote: >> old: 0000000000000000000000000000000000000000, new: ce858e653cdbf70f9955a39d73a44219e4b92e9e, ref: refs/drafts/maint/my/topic. remote: execute: post-receive hook remote: >> old: 102939797ab91a4f201d131418d2c9d919dcdd2c, new: ce858e653cdbf70f9955a39d73a44219e4b92e9e, ref: refs/heads/master. - remote: >> old: 0000000000000000000000000000000000000000, new: ce858e653cdbf70f9955a39d73a44219e4b92e9e, ref: refs/for/maint/my/topic. + remote: >> old: 0000000000000000000000000000000000000000, new: ce858e653cdbf70f9955a39d73a44219e4b92e9e, ref: refs/drafts/maint/my/topic. EOF test_cmp expect actual ' From patchwork Wed Mar 4 11:33:12 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jiang Xin X-Patchwork-Id: 11419931 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 476EB14B4 for ; Wed, 4 Mar 2020 11:34:23 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 1F8D9214D8 for ; Wed, 4 Mar 2020 11:34:23 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="ioHTwuOE" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2387921AbgCDLeW (ORCPT ); Wed, 4 Mar 2020 06:34:22 -0500 Received: from mail-pj1-f65.google.com ([209.85.216.65]:40081 "EHLO mail-pj1-f65.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S2387774AbgCDLeV (ORCPT ); Wed, 4 Mar 2020 06:34:21 -0500 Received: by mail-pj1-f65.google.com with SMTP id k36so817500pje.5 for ; Wed, 04 Mar 2020 03:34:20 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=lM1ts5ETOcePpWiL4XDa+kz0IbYiuE07/kACZPWPmoM=; b=ioHTwuOEjyQQ4LAZdnQCQAZeHOSTHYcoxBynsjKi3dUjjSqrKrZkhF5O5KOJiKRw+U hdrgNHoyw45kmRmXKJzJxKalpjYdKCoy6r0Fbm5S/nu006ScPnfiNWvKE4iE2p5sJiJW SVjFYrwQSf3Wo+q96M1rz80HepfBQuwqX9+jGq2oQf20tBlLO9QLcDZywu2BIrKRdP4W gbOZjCEM3o1Pzu+02Qi9YGJG9LbXeCSYaXkxUd8UA7CxyouZ1ZWbb24uerZNaUt5Aku0 tV59LHJf9Qc5wFBrEACMHN6cdItfo2mDwdgrE4fKX9QSXXGhP2R/m7/tRxfkTL8vcfTG +JmA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=lM1ts5ETOcePpWiL4XDa+kz0IbYiuE07/kACZPWPmoM=; b=Mjv64MEA8wZp9O9ykglxD5nB0zb3N1TCxw2o6g+tsZYJkq9aCoIRmyDLJskR3PXgSL 9ZQSOlTxUAsHYsXqkL1p8KP4Reco30PyUkSD/OOoks7ioiWGn/sjCtiHmgn3qDWVQso5 Jy8AhdBrMLdLJF2NqwG7rJnJvA1j6ylA75C6xFcvzWAoyAl9joXxKJ9fj/SfZjgqgGWF oF/rqURrACmrOT6XHbtHG+3KafzUO7Hp1e6iltu4GEUxfFraT9JbYlksxXsrcIhylNdS RDoknlNWn2Wwv24PpOP5byn9Du9PzuHeXeaYvMjSEeKarkmjNP0tV3uIeCBuMIQZ6KHN uI6g== X-Gm-Message-State: ANhLgQ2re5Drv8ypipSwWbLhqWnZOZ3ccsC1NXngS4GpCgDa+GykiOr0 Lkbwzmr/QtcBmc/sMgTS401ef8hJyma2eg== X-Google-Smtp-Source: ADFU+vtdeOU9YPtCIPVIGNSXdTxffNngJwV4HuW57ovzv+3B19amUBwNFFqOWqvrPQBLBCiXZrCwVQ== X-Received: by 2002:a17:902:76cc:: with SMTP id j12mr2773246plt.129.1583321659458; Wed, 04 Mar 2020 03:34:19 -0800 (PST) Received: from localhost.localdomain ([47.89.83.4]) by smtp.gmail.com with ESMTPSA id d77sm15350050pfd.109.2020.03.04.03.34.17 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Wed, 04 Mar 2020 03:34:18 -0800 (PST) From: Jiang Xin X-Google-Original-From: Jiang Xin To: Git List Cc: Jiang Xin , Junio C Hamano Subject: [PATCH 7/7] hook: add document and example for "execute-commands" hook Date: Wed, 4 Mar 2020 19:33:12 +0800 Message-Id: <20200304113312.34229-8-zhiyou.jx@alibaba-inc.com> X-Mailer: git-send-email 2.25.1.362.g51ebf55b93 In-Reply-To: <20200304113312.34229-1-zhiyou.jx@alibaba-inc.com> References: <20200304113312.34229-1-zhiyou.jx@alibaba-inc.com> MIME-Version: 1.0 Sender: git-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: git@vger.kernel.org Signed-off-by: Jiang Xin --- Documentation/githooks.txt | 43 ++++++++ t/t5411-execute-commands-hook.sh | 98 +++++++++++++++++ templates/hooks--execute-commands.sample | 131 +++++++++++++++++++++++ 3 files changed, 272 insertions(+) create mode 100755 templates/hooks--execute-commands.sample diff --git a/Documentation/githooks.txt b/Documentation/githooks.txt index 3dccab5375..6c21ab6db2 100644 --- a/Documentation/githooks.txt +++ b/Documentation/githooks.txt @@ -58,6 +58,49 @@ the message file. The default 'applypatch-msg' hook, when enabled, runs the 'commit-msg' hook, if the latter is enabled. +execute-commands--pre-receive +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +This hook is invoked by linkgit:git-receive-pack[1] when it reacts to +special `git push` command. Just before starting to execute the +external 'execute-commands' hook to make changes to the repository, +the 'execute-commands--pre-receive' hook is invoked. This hook has +the same functionality as 'pre-receive' hook, while it only acts on +special commands which 'git-push' sends to 'git-receive-pack'. +The refnames of these spacial commands have special prefix (such as +"refs/for/") which matches what the `receive.executeCommandsHookRefs` +configuration variable defines. + +If this hook does not exist, will try to find the 'execute-commands' +hook, and run `execute-command --pre-receive` command to do some +checks. Its exit status determines the success or failure of the +update. If the hook exits with non-zero status, won't execute any of +the commands, no metter calling the internal `execute_commands` +function or calling the external "execute-commands" hook. + +This hook executes once for the receive operation, and gets the +information from its standard input. +See <> for details. + +execute-commands +~~~~~~~~~~~~~~~~ +This hook is invoked by linkgit:git-receive-pack[1] when it reacts to +special `git push` command. According to refnames of the commands which +`git push` sends to 'git-receive-pack', the commands will be devided +into two groups by matching what the `receive.executeCommandsHookRefs` +configuration variable defines. One group of the commands will execute +the internal `execute_commands` function to update the corresponding +refnames, and the other group of commands which have matching refnames +will execute this external 'execute-commands' hook to create pull +requests, etc. + +Its exit status only determines the success or failure of the group of +commands with special refnames, unless atomic push is in use. + +This hook executes once if there are any special commands with special +refnames. There is no argument taken by this hook, and the push options +(if any) and command(s) will be fed to this book by its standard input. +See the <> hook for the format of the input. + pre-applypatch ~~~~~~~~~~~~~~ diff --git a/t/t5411-execute-commands-hook.sh b/t/t5411-execute-commands-hook.sh index c8ee699773..6dd1560f9d 100755 --- a/t/t5411-execute-commands-hook.sh +++ b/t/t5411-execute-commands-hook.sh @@ -597,4 +597,102 @@ test_expect_success "push and show environments" ' test_cmp expect actual ' +test_expect_success "execute-commands.sample: new execute-commands hook from templates/execute-commands.sample" ' + mv $bare/hooks/pre-receive $bare/hooks/pre-receive.fail && + mv $bare/hooks/pre-receive.ok $bare/hooks/pre-receive && + mv $bare/hooks/post-receive $bare/hooks/post-receive.env && + mv $bare/hooks/post-receive.ok $bare/hooks/post-receive && + mv $bare/hooks/execute-commands $bare/hooks/execute-commands.env && + cp ../../templates/hooks--execute-commands.sample $bare/hooks/execute-commands && + chmod a+x $bare/hooks/execute-commands +' + +test_expect_success "execute-commands.sample: show push result" ' + ( + cd work && + git push origin \ + HEAD:refs/for/a/b/c/my/topic + ) >out 2>&1 && + grep "^remote:" out | sed -e "s/ *\$//g" >actual && + cat >expect <<-EOF && + remote: 102939797ab91a4f201d131418d2c9d919dcdd2c + remote: [execute-commands] ******************************************************* + remote: [execute-commands] * Pull request #12345678901 created/updated * + remote: [execute-commands] * URL: https://... ... * + remote: [execute-commands] ******************************************************* + remote: execute: post-receive hook + remote: >> old: 0000000000000000000000000000000000000000, new: ce858e653cdbf70f9955a39d73a44219e4b92e9e, ref: refs/for/a/b/c/my/topic. + EOF + test_cmp expect actual +' + +test_expect_success "execute-commands.sample: show debug info" ' + ( + cd work && + git push -o debug=1 -o reviewers=user1,user2 \ + origin \ + HEAD:refs/for/a/b/c/my/topic + ) >out 2>&1 && + grep "^remote:" out | sed -e "s/ *\$//g" >actual && + cat >expect <<-EOF && + remote: [DEBUG] [execute-commands] push-option: AGIT_DEBUG=1 + remote: [DEBUG] [execute-commands] push-option: AGIT_REVIEWERS=user1,user2 + remote: [DEBUG] [execute-commands] command from stdin: 0000000000000000000000000000000000000000 ce858e653cdbf70f9955a39d73a44219e4b92e9e refs/for/a/b/c/my/topic + remote: 102939797ab91a4f201d131418d2c9d919dcdd2c + remote: [DEBUG] [execute-commands: pre-receive] check permissions... + remote: [DEBUG] [execute-commands] push-option: AGIT_DEBUG=1 + remote: [DEBUG] [execute-commands] push-option: AGIT_REVIEWERS=user1,user2 + remote: [DEBUG] [execute-commands] command from stdin: 0000000000000000000000000000000000000000 ce858e653cdbf70f9955a39d73a44219e4b92e9e refs/for/a/b/c/my/topic + remote: [DEBUG] [execute-commands] call API (AGIT_PR_TARGET=a/b/c, AGIT_PR_TOPIC=)... + remote: [DEBUG] [execute-commands] parse API result, and get AGIT_PR_ID, etc. + remote: [execute-commands] ******************************************************* + remote: [execute-commands] * Pull request #12345678901 created/updated * + remote: [execute-commands] * URL: https://... ... * + remote: [execute-commands] ******************************************************* + remote: [DEBUG] [execute-commands] output kv pairs to stdout for git to parse. + remote: execute: post-receive hook + remote: >> old: 0000000000000000000000000000000000000000, new: ce858e653cdbf70f9955a39d73a44219e4b92e9e, ref: refs/for/a/b/c/my/topic. + EOF + test_cmp expect actual +' + +test_expect_success "execute-commands.sample: fail to push refs/for/maint" ' + ( + cd work && + test_must_fail git push -o reviewers=user1,user2 \ + origin \ + HEAD:refs/for/maint/my/topic + ) >out 2>&1 && + grep "^remote:" out | sed -e "s/ *\$//g" >actual && + cat >expect <<-EOF && + remote: 102939797ab91a4f201d131418d2c9d919dcdd2c + remote: [execute-commands: pre-receive] send pull request to maint branch is not allowed + EOF + test_cmp expect actual +' + +test_expect_success "execute-commands.sample: fail to push non-exist branch" ' + ( + cd work && + test_must_fail git push -o reviewers=user1,user2 \ + origin \ + HEAD:refs/for/a/b/x/my/topic + ) >out 2>&1 && + grep "^remote:" out | sed -e "s/ *\$//g" >actual && + cat >expect <<-EOF && + remote: [execute-commands] cannot find target branch from ref: refs/for/a/b/x/my/topic + EOF + test_cmp expect actual +' + +test_expect_success "show refs of the repository using git-show-ref" ' + git -C $bare show-ref >actual && + cat >expect <<-EOF && + 102939797ab91a4f201d131418d2c9d919dcdd2c refs/heads/a/b/c + 102939797ab91a4f201d131418d2c9d919dcdd2c refs/heads/maint + 102939797ab91a4f201d131418d2c9d919dcdd2c refs/heads/master + EOF + test_cmp expect actual +' + test_done diff --git a/templates/hooks--execute-commands.sample b/templates/hooks--execute-commands.sample new file mode 100755 index 0000000000..d061984bca --- /dev/null +++ b/templates/hooks--execute-commands.sample @@ -0,0 +1,131 @@ +#!/bin/sh +# +# This is an example hook script, DO NOT use it on production service. + +debug() { + case "$AGIT_DEBUG" in + "yes" | "true" | "1") + ;; + *) + return + esac + + echo >&2 "[DEBUG] $@" +} + +# Parse push options +if test -n "$GIT_PUSH_OPTION_COUNT" +then + i=0 + while test "$i" -lt "$GIT_PUSH_OPTION_COUNT" + do + eval "value=\$GIT_PUSH_OPTION_$i" + i=$((i + 1)) + + k=$(echo ${value%=*} | tr [a-z] [A-Z]) + v=${value#*=} + if test -n "$v" && test -n "$k" + then + k="AGIT_$k" + else + continue + fi + eval "$k=$v" + debug "[execute-commands] push-option: $k=$v" + done +fi + +# Read push commands. +count=0 +while read old new refname +do + debug "[execute-commands] command from stdin: $old $new $refname" + count=$(( count + 1 )) + # Only one special refname is allowed for each push + if test $count -gt 1 + then + echo >&2 "[execute-commands]: cannot handle more than one push commands" + exit 1 + fi + + # Parse refname, and set envrionment + remains= + if test "${refname#refs/for/}" != "$refname" + then + AGIT_PR_IS_DRAFT=false + remains=${refname#refs/for/} + elif test "${refname#refs/drafts/}" != "$refname" + then + AGIT_PR_IS_DRAFT=true + remains=${refname#refs/drafts/} + else + echo >&2 "[execute-commands] unknown refname: $refname" + exit 1 + fi + + ref= + found_ref= + for i in $(echo $remains | tr "/" "\n") + do + if test -z "$ref" + then + ref=$i + else + ref=$ref/$i + fi + if git rev-parse --verify $ref -- 2>/dev/null + then + found_ref=yes + break + fi + done + if test -z "$found_ref" + then + echo >&2 "[execute-commands] cannot find target branch from ref: $refname" + exit 1 + fi + AGIT_PR_TARGET=$ref + AGIT_PR_SOURCE=${remains#$ref/} +done + +if test -z "$AGIT_PR_TARGET" +then + echo >&2 "[execute-commands] fail to parse refname, no target found" + exit 1 +fi + +# pre-receive mode, used to check permissions. +if test "$1" = "--pre-receive" +then + debug "[execute-commands: pre-receive] check permissions..." + if test "$AGIT_PR_TARGET" = "maint" + then + echo >&2 "[execute-commands: pre-receive] send pull request to maint branch is not allowed" + exit 1 + fi + exit 0 +fi + +# Call API to generate code review. +debug "[execute-commands] call API (AGIT_PR_TARGET=$AGIT_PR_TARGET, AGIT_PR_TOPIC=$AGIT_PR_TOPIC)..." + +# Parse result of API. +debug "[execute-commands] parse API result, and get AGIT_PR_ID, etc." +AGIT_PR_ID="12345678901" +AGIT_PR_LOCAL_ID="23" + +# Show message. +if test -n "$AGIT_PR_ID" +then + echo >&2 "[execute-commands] *******************************************************" + echo >&2 "[execute-commands] * Pull request #$AGIT_PR_ID created/updated *" + echo >&2 "[execute-commands] * URL: https://... ... *" + echo >&2 "[execute-commands] *******************************************************" +fi + +# Show envs to stdout, and will be exported as envs for "post-receive" hook. +debug "[execute-commands] output kv pairs to stdout for git to parse." +echo "AGIT_PR_ID=$AGIT_PR_ID" +echo "AGIT_PR_LOCAL_ID=$AGIT_PR_LOCAL_ID" + +exit 0