aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore2
-rw-r--r--Makefile.am44
-rwxr-xr-xbuild-aux/valgrind.sh18
-rw-r--r--man/blogc-git-receiver.1.ronn21
-rw-r--r--src/blogc-git-receiver/post-receive.c136
-rw-r--r--src/blogc-git-receiver/post-receive.h4
-rw-r--r--src/common/error.h3
-rw-r--r--tests/blogc-git-receiver/check_post_receive.c86
-rwxr-xr-xtests/blogc-git-receiver/check_post_receive.sh87
9 files changed, 379 insertions, 22 deletions
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 <<EOF
+ [repo:myblog.git]
+ mirror=$YOUR_GIT_MIRROR_URL
+ EOF
+
+Or to add the `mirror` remote:
# su -s /bin/bash - blogc
$ cd repos
@@ -91,6 +102,8 @@ To add the `mirror` remote:
$ cd myblog.git
$ git remote add --mirror=push mirror $YOUR_GIT_MIRROR_URL
+Both examples assume that your repository is named `myblog.git`.
+
### Caveats of repository mirroring with SSH
The authentication must be done with a password-less SSH key created by the `blogc`
diff --git a/src/blogc-git-receiver/post-receive.c b/src/blogc-git-receiver/post-receive.c
index 8310939..79c51ac 100644
--- a/src/blogc-git-receiver/post-receive.c
+++ b/src/blogc-git-receiver/post-receive.c
@@ -6,24 +6,142 @@
* See the file LICENSE.
*/
+#include <errno.h>
#include <stdio.h>
+#include <limits.h>
+#include <unistd.h>
#include <stdlib.h>
+#include <string.h>
+#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 <rafael@rafaelmartins.eng.br>
+ *
+ * This program can be distributed under the terms of the BSD License.
+ * See the file LICENSE.
+ */
+
+#include <stdarg.h>
+#include <stddef.h>
+#include <setjmp.h>
+#include <cmocka.h>
+#include <string.h>
+#include <stdlib.h>
+#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" <<EOF
+blob
+mark :1
+data 4
+bar
+
+reset refs/heads/master
+commit refs/heads/master
+mark :2
+author Rafael G. Martins <rafael@rafaelmartins.eng.br> 1476033730 +0200
+committer Rafael G. Martins <rafael@rafaelmartins.eng.br> 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" <<EOF
+[repo:boo.git]
+mirror = 123
+
+[repo:foo.git]
+mirror = ${TEMP}/repos/bar.git
+
+[repo:bar.git]
+mirror = lol
+EOF
+git init --bare "${TEMP}/repos/bar.git" &> /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" <<EOF
+asd[repo:boo.git]
+mirror = 123
+
+[repo:foo.git]
+mirror = ${TEMP}/repos/bar.git
+
+[repo:bar.git]
+mirror = lol
+EOF
+git init --bare "${TEMP}/repos/bar.git" &> /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"