From 3cd3dcb5bb3b0481812ddd3ff8dc182bdb20be23 Mon Sep 17 00:00:00 2001 From: "Rafael G. Martins" Date: Mon, 10 Oct 2016 02:13:47 +0200 Subject: git-receiver: added support to get mirror url from config file this commit also includes some "integration test" in shell script --- .gitignore | 2 + Makefile.am | 44 ++++++-- build-aux/valgrind.sh | 18 ++++ man/blogc-git-receiver.1.ronn | 21 +++- src/blogc-git-receiver/post-receive.c | 136 +++++++++++++++++++++++-- src/blogc-git-receiver/post-receive.h | 4 + src/common/error.h | 3 + tests/blogc-git-receiver/check_post_receive.c | 86 ++++++++++++++++ tests/blogc-git-receiver/check_post_receive.sh | 87 ++++++++++++++++ 9 files changed, 379 insertions(+), 22 deletions(-) create mode 100755 build-aux/valgrind.sh create mode 100644 tests/blogc-git-receiver/check_post_receive.c create mode 100755 tests/blogc-git-receiver/check_post_receive.sh diff --git a/.gitignore b/.gitignore index 98aee66..32b3f51 100644 --- a/.gitignore +++ b/.gitignore @@ -35,6 +35,7 @@ Makefile.in !/build-aux/git-version-gen !/build-aux/travis-build.sh !/build-aux/travis-deploy.sh +!/build-aux/valgrind.sh # installed .m4 files /m4/*.m4 @@ -58,6 +59,7 @@ blogc*.html /tests/blogc/check_source_parser /tests/blogc/check_template_parser /tests/blogc-git-receiver/check_pre_receive_parser +/tests/blogc-git-receiver/check_post_receive /tests/blogc-git-receiver/check_shell_command_parser /tests/blogc-runserver/check_httpd_utils /tests/blogc-runserver/check_mime diff --git a/Makefile.am b/Makefile.am index 31c1dca..54c71bd 100644 --- a/Makefile.am +++ b/Makefile.am @@ -15,6 +15,7 @@ AM_DISTCHECK_CONFIGURE_FLAGS = \ EXTRA_DIST = \ build-aux/git-version-gen \ + build-aux/valgrind.sh \ $(top_srcdir)/.version \ autogen.sh \ LICENSE \ @@ -90,6 +91,9 @@ endif check_PROGRAMS = \ $(NULL) +dist_check_SCRIPTS = \ + $(NULL) + libblogc_la_SOURCES = \ src/blogc/content-parser.c \ @@ -314,6 +318,12 @@ endif ## Build rules: tests +if BUILD_GIT_RECEIVER +dist_check_SCRIPTS += \ + tests/blogc-git-receiver/check_post_receive.sh \ + $(NULL) +endif + if USE_CMOCKA check_PROGRAMS += \ @@ -573,6 +583,7 @@ endif if BUILD_GIT_RECEIVER check_PROGRAMS += \ tests/blogc-git-receiver/check_pre_receive_parser \ + tests/blogc-git-receiver/check_post_receive \ tests/blogc-git-receiver/check_shell_command_parser \ $(NULL) @@ -594,6 +605,25 @@ tests_blogc_git_receiver_check_pre_receive_parser_LDADD = \ libblogc_common.la \ $(NULL) +tests_blogc_git_receiver_check_post_receive_SOURCES = \ + tests/blogc-git-receiver/check_post_receive.c \ + $(NULL) + +tests_blogc_git_receiver_check_post_receive_CFLAGS = \ + $(CMOCKA_CFLAGS) \ + $(NULL) + +tests_blogc_git_receiver_check_post_receive_LDFLAGS = \ + -no-install \ + -Wl,--wrap=realpath \ + $(NULL) + +tests_blogc_git_receiver_check_post_receive_LDADD = \ + $(CMOCKA_LIBS) \ + libblogc_git_receiver.la \ + libblogc_common.la \ + $(NULL) + tests_blogc_git_receiver_check_shell_command_parser_SOURCES = \ tests/blogc-git-receiver/check_shell_command_parser.c \ $(NULL) @@ -616,7 +646,9 @@ endif endif TESTS = \ - $(check_PROGRAMS) + $(check_PROGRAMS) \ + $(dist_check_SCRIPTS) \ + $(NULL) ## Helpers: dist-srpm @@ -644,12 +676,6 @@ dist-hook: if USE_VALGRIND valgrind: all $(MAKE) check TESTS_ENVIRONMENT=" \ - $(VALGRIND) \ - --tool=memcheck \ - --leak-check=full \ - --leak-resolution=high \ - --num-callers=20 \ - --error-exitcode=1 \ - --show-possibly-lost=no" - + VALGRIND=$(VALGRIND) \ + $(top_srcdir)/build-aux/valgrind.sh" endif diff --git a/build-aux/valgrind.sh b/build-aux/valgrind.sh new file mode 100755 index 0000000..4db7477 --- /dev/null +++ b/build-aux/valgrind.sh @@ -0,0 +1,18 @@ +#!/bin/bash + +set -e + +export TESTS_ENVIRONMENT=" + ${VALGRIND:-valgrind} \ + --tool=memcheck \ + --leak-check=full \ + --leak-resolution=high \ + --num-callers=20 \ + --error-exitcode=1 \ + --show-possibly-lost=no" + +if [[ "${1}" == *.sh ]]; then + exec "${1}" +else + exec ${TESTS_ENVIRONMENT} "${1}" +fi diff --git a/man/blogc-git-receiver.1.ronn b/man/blogc-git-receiver.1.ronn index dc35621..806ba3d 100644 --- a/man/blogc-git-receiver.1.ronn +++ b/man/blogc-git-receiver.1.ronn @@ -63,9 +63,7 @@ After running these commands, the machine is ready to be used. ## REPOSITORY MIRRORING Users can rely on `blogc-git-receiver` to mirror repositories to a remote Git -repository (e.g. a free Bitbucket private repository). This feature just requires -adding a remote called `mirror` to the bare repository in the server. If such remote -exists, `blogc-git-receiver` will `git push --mirror` to it. +repository (e.g. a free Bitbucket private repository). Please note that the `blogc` user must be able to push to the remote repository, and that any content manually pushed to the remote repository is overwritten by @@ -83,7 +81,20 @@ means that if an error happens when mirroring the repository, the deploy will st succeed. Users should pay attention to the git hook logs, to avoid losing data due to repositories not being mirrored. -To add the `mirror` remote: +This feature just requires adding a remote called `mirror` to the bare repository +in the server, or creating a configuration file (~/blogc-git-receiver.ini), that is +a simple INI-style file where each repository is represented by a section and the +value of the `mirror` variable is the URL that should be used to push. + +To create the configuration file (recommended): + + # su -s /bin/bash - blogc + # cat > ~/blogc-git-receiver.ini < #include +#include +#include #include +#include +#include "../common/config-parser.h" +#include "../common/error.h" +#include "../common/file.h" + + +char* +bgr_post_receive_get_config_section(bc_config_t *config, const char *repo_path, + const char *home) +{ + char *rv = NULL; + char** sections = bc_config_list_sections(config); + for (size_t i = 0; sections[i] != NULL; i++) { + if (bc_str_starts_with(sections[i], "repo:")) { + char *tmp_repo = bc_strdup_printf("%s/repos/%s", home, sections[i] + 5); + char *real_tmp_repo = realpath(tmp_repo, NULL); // maybe not needed + free(tmp_repo); + if (real_tmp_repo == NULL) + continue; + if (0 == strcmp(real_tmp_repo, repo_path)) { + rv = bc_strdup(sections[i]); + free(real_tmp_repo); + break; + } + free(real_tmp_repo); + } + } + bc_strv_free(sections); + return rv; +} int bgr_post_receive_hook(int argc, char *argv[]) { - if (0 != system("git config --local remote.mirror.pushurl &> /dev/null")) { - if (0 != system("git config --local remote.mirror.url &> /dev/null")) { - fprintf(stderr, "warning: repository mirroring disabled\n"); - return 0; - } + char *mirror = NULL; + + // local repository settings should take precedence, so if the repo have + // the 'mirror' remote, just push to it. + // this will be removed at some point, but will be kept for compatibility + // with old setups. + if ((0 == system("git config --local remote.mirror.pushurl &> /dev/null")) || + (0 == system("git config --local remote.mirror.url &> /dev/null"))) + { + mirror = bc_strdup("mirror"); + goto push; + } + + char buffer[4096]; + if (NULL == getcwd(buffer, sizeof(buffer))) { + fprintf(stderr, "warning: failed to get repository remote path, " + "mirroring disabled: %s\n", strerror(errno)); + return 0; + } + + char *repo_path = realpath(buffer, NULL); + if (repo_path == NULL) { + fprintf(stderr, "warning: failed to find remote repository directory, " + "mirroring disabled: %s\n", strerror(errno)); + return 0; + } + + char *home = getenv("HOME"); + if (home == NULL) { + fprintf(stderr, "warning: failed to find user home path, " + "mirroring disabled\n"); + free(repo_path); + return 0; + } + + char *config_file = bc_strdup_printf("%s/blogc-git-receiver.ini", home); + if ((0 != access(config_file, F_OK))) { + fprintf(stderr, "warning: repository mirroring disabled\n"); + free(repo_path); + free(config_file); + return 0; + } + + size_t len; + bc_error_t *err = NULL; + char* config_content = bc_file_get_contents(config_file, true, &len, &err); + if (err != NULL) { + fprintf(stderr, "warning: failed to read configuration file (%s), " + "mirroring disabled: %s\n", config_file, err->msg); + bc_error_free(err); + free(repo_path); + free(config_file); + free(config_content); + return 0; + } + + bc_config_t *config = bc_config_parse(config_content, len, &err); + free(config_content); + if (err != NULL) { + fprintf(stderr, "warning: failed to parse configuration file (%s), " + "mirroring disabled: %s\n", config_file, err->msg); + bc_error_free(err); + free(repo_path); + free(config_file); + return 0; + } + free(config_file); + + char *config_section = bgr_post_receive_get_config_section(config, repo_path, + home); + free(repo_path); + if (config_section == NULL) { + fprintf(stderr, "warning: repository mirroring disabled\n"); + bc_config_free(config); + return 0; + } + + mirror = bc_strdup(bc_config_get(config, config_section, "mirror")); + free(config_section); + bc_config_free(config); + + if (mirror == NULL) { + fprintf(stderr, "warning: repository mirroring disabled\n"); + return 0; + } + +push: + + { + char *git_cmd = bc_strdup_printf("git push --mirror %s", mirror); + if (0 != system(git_cmd)) + fprintf(stderr, "warning: failed push to git mirror\n"); + free(git_cmd); } - // at this point we know that we have a remote called mirror, we can just - // push to it. - if (0 != system("git push --mirror mirror")) - fprintf(stderr, "warning: failed push to git mirror\n"); + free(mirror); return 0; } diff --git a/src/blogc-git-receiver/post-receive.h b/src/blogc-git-receiver/post-receive.h index a28dd5a..e17c3d4 100644 --- a/src/blogc-git-receiver/post-receive.h +++ b/src/blogc-git-receiver/post-receive.h @@ -9,6 +9,10 @@ #ifndef _POST_RECEIVE_H #define _POST_RECEIVE_H +#include "../common/config-parser.h" + +char* bgr_post_receive_get_config_section(bc_config_t *config, + const char *repo_path, const char *home); int bgr_post_receive_hook(int argc, char *argv[]); #endif /* _POST_RECEIVE_H */ diff --git a/src/common/error.h b/src/common/error.h index 5ac2b15..21b332a 100644 --- a/src/common/error.h +++ b/src/common/error.h @@ -23,6 +23,9 @@ typedef enum { BLOGC_ERROR_TEMPLATE_PARSER, BLOGC_ERROR_LOADER, BLOGC_WARNING_DATETIME_PARSER, + + // errors for src/blogc-git-receiver + BLOGC_GR_ERROR_POST_RECEIVE = 200, } bc_error_type_t; typedef struct { diff --git a/tests/blogc-git-receiver/check_post_receive.c b/tests/blogc-git-receiver/check_post_receive.c new file mode 100644 index 0000000..74a9a29 --- /dev/null +++ b/tests/blogc-git-receiver/check_post_receive.c @@ -0,0 +1,86 @@ +/* + * blogc: A blog compiler. + * Copyright (C) 2015-2016 Rafael G. Martins + * + * This program can be distributed under the terms of the BSD License. + * See the file LICENSE. + */ + +#include +#include +#include +#include +#include +#include +#include "../../src/common/config-parser.h" +#include "../../src/common/utils.h" +#include "../../src/blogc-git-receiver/post-receive.h" + + +char* +__wrap_realpath(const char *path, char *resolved_path) +{ + const char *real_path = mock_type(const char*); + if (real_path == NULL) + return NULL; + assert_string_equal(path, real_path); + return bc_strdup(real_path); +} + + +static void +test_post_receive_get_config_section(void **state) +{ + bc_config_t *config = bc_config_parse("", 0, NULL); + assert_null(bgr_post_receive_get_config_section(config, + "/home/blogc/repos/foo.git", "/home/blogc")); + bc_config_free(config); + + will_return(__wrap_realpath, NULL); + will_return(__wrap_realpath, "/home/blogc/repos/bar.git"); + const char *conf = + "[repo:foo.git]\n" + "mirror = foo\n" + "\n" + "[repo:bar.git]\n" + "mirror = bar\n" + "\n" + "[repo:baz.git]\n" + "mirror = baz\n" + "\n"; + config = bc_config_parse(conf, strlen(conf), NULL); + char *s = bgr_post_receive_get_config_section(config, + "/home/blogc/repos/bar.git", "/home/blogc"); + assert_string_equal(s, "repo:bar.git"); + free(s); + bc_config_free(config); + + will_return(__wrap_realpath, NULL); + will_return(__wrap_realpath, "/home/blogc/repos/asd/bar.git"); + conf = + "[repo:asd/foo.git]\n" + "mirror = foo\n" + "\n" + "[repo:asd/bar.git]\n" + "mirror = bar\n" + "\n" + "[repo:asd/baz.git]\n" + "mirror = baz\n" + "\n"; + config = bc_config_parse(conf, strlen(conf), NULL); + s = bgr_post_receive_get_config_section(config, + "/home/blogc/repos/asd/bar.git", "/home/blogc"); + assert_string_equal(s, "repo:asd/bar.git"); + free(s); + bc_config_free(config); +} + + +int +main(void) +{ + const UnitTest tests[] = { + unit_test(test_post_receive_get_config_section), + }; + return run_tests(tests); +} diff --git a/tests/blogc-git-receiver/check_post_receive.sh b/tests/blogc-git-receiver/check_post_receive.sh new file mode 100755 index 0000000..78b566e --- /dev/null +++ b/tests/blogc-git-receiver/check_post_receive.sh @@ -0,0 +1,87 @@ +#!/bin/bash + +set -xe -o pipefail + +TEMP="$(mktemp -d)" +[[ -n "${TEMP}" ]] + +trap_func() { + [[ -e "${TEMP}/output.txt" ]] && cat "${TEMP}/output.txt" + rm -rf "${TEMP}" +} + +trap trap_func EXIT + +mkdir -p "${TEMP}/repos" +git init --bare "${TEMP}/repos/foo.git" &> /dev/null + +ln -s "${PWD}/blogc-git-receiver" "${TEMP}/repos/foo.git/hooks/post-receive" + +cat > "${TEMP}/tmp.txt" < 1476033730 +0200 +committer Rafael G. Martins 1476033888 +0200 +data 11 +testing... +M 100644 :1 foo + +EOF + +cd "${TEMP}/repos/foo.git" +git fast-import < "${TEMP}/tmp.txt" &> /dev/null + +git init --bare "${TEMP}/repos/bar.git" &> /dev/null + +HOME="${TEMP}" ${TESTS_ENVIRONMENT} ./hooks/post-receive 2>&1 | tee "${TEMP}/output.txt" +grep "warning: repository mirroring disabled" "${TEMP}/output.txt" &> /dev/null + +git config --local remote.mirror.pushurl "${TEMP}/repos/bar.git" +HOME="${TEMP}" ${TESTS_ENVIRONMENT} ./hooks/post-receive 2>&1 | tee "${TEMP}/output.txt" +grep "[new branch] *master" "${TEMP}/output.txt" &> /dev/null + +git config --local --unset remote.mirror.pushurl +rm -rf "${TEMP}/repos/bar.git" +git init --bare "${TEMP}/repos/bar.git" &> /dev/null +git config --local remote.mirror.url "${TEMP}/repos/bar.git" +HOME="${TEMP}" ${TESTS_ENVIRONMENT} ./hooks/post-receive 2>&1 | tee "${TEMP}/output.txt" +grep "[new branch] *master" "${TEMP}/output.txt" &> /dev/null + +git config --local --unset remote.mirror.url +rm -rf "${TEMP}/repos/bar.git" +cat > "${TEMP}/blogc-git-receiver.ini" < /dev/null +HOME="${TEMP}" ${TESTS_ENVIRONMENT} ./hooks/post-receive 2>&1 | tee "${TEMP}/output.txt" +grep "[new branch] *master" "${TEMP}/output.txt" &> /dev/null + +rm -rf "${TEMP}/repos/bar.git" +cat > "${TEMP}/blogc-git-receiver.ini" < /dev/null +HOME="${TEMP}" ${TESTS_ENVIRONMENT} ./hooks/post-receive 2>&1 | tee "${TEMP}/output.txt" +grep "warning: failed to parse configuration file " "${TEMP}/output.txt" &> /dev/null + +rm "${TEMP}/output.txt" -- cgit v1.2.3-18-g5258