"GOT", but the "O" is a cute, smiling sun Index | Thread

From:
Omar Polo <op@omarpolo.com>
Subject:
got send: show failure in updating remote branches
To:
gameoftrees@openbsd.org
Date:
Mon, 14 Nov 2022 17:05:02 +0100

Download raw body.

Gonzalo hit an issue earlier this morning: `got send -f' was failing
because of server-side checks on github.  It's wasn't obvious what
failure was because to read the error message one had to pass -vvv and
read the raw remote packet line.

Diff below propagates the error read from got-send-pack where it's
parsed up to the got send pack callback.  Now, pushing results in:

% got send -f github
Connecting to "github" git@github.com
39 commits colored; 6 objects found; 4 trees scanned
packing 1 reference; 6 objects; deltify: 100%; uploading pack:  484B 100%
Server has rejected refs/heads/main: protected branch hook declined

which at least gives a clue to what's happening.

ok/thoughts?

diff /home/op/w/got
commit - 712c26063dd1a052a8c12603c51a363a14955915
path + /home/op/w/gotd
blob - c0c38ea47dfbd657ee0c14d0e8829767ad691ae6
file + got/got.c
--- got/got.c
+++ got/got.c
@@ -8749,7 +8749,8 @@ send_progress(void *arg, int ncolored, int nfound, int
 static const struct got_error *
 send_progress(void *arg, int ncolored, int nfound, int ntrees,
     off_t packfile_size, int ncommits, int nobj_total, int nobj_deltify,
-    int nobj_written, off_t bytes_sent, const char *refname, int success)
+    int nobj_written, off_t bytes_sent, const char *refname,
+    const char *errmsg, int success)
 {
 	struct got_send_progress_arg *a = arg;
 	char scaled_packsize[FMT_SCALED_STRSIZE];
@@ -8781,6 +8782,8 @@ send_progress(void *arg, int ncolored, int nfound, int
 		if (a->printed_something)
 			putchar('\n');
 		printf("Server has %s %s", status, refname);
+		if (errmsg)
+			printf(": %s", errmsg);
 		a->printed_something = 1;
 		return NULL;
 	}
blob - 19e9e4f116e70c354ec4bbff8ce7e5a1615acd1f
file + include/got_send.h
--- include/got_send.h
+++ include/got_send.h
@@ -38,7 +38,7 @@ typedef const struct got_error *(*got_send_progress_cb
 typedef const struct got_error *(*got_send_progress_cb)(void *,
     int ncolored, int nfound, int ntrees, off_t packfile_size, int ncommits,
     int nobj_total, int nobj_deltify, int nobj_written, off_t bytes_sent,
-    const char *refname, int success);
+    const char *refname, const char *, int success);
 
 /*
  * Attempt to generate a pack file and sent it to a server.
blob - f84d920be3d90fdc2c9b6db77113efd0d7d87c62
file + lib/got_lib_privsep.h
--- lib/got_lib_privsep.h
+++ lib/got_lib_privsep.h
@@ -481,7 +481,9 @@ struct got_imsg_send_ref_status {
 struct got_imsg_send_ref_status {
 	int success;
 	size_t name_len;
+	size_t errmsg_len;
 	/* Followed by name_len data bytes. */
+	/* Followed by errmsg_len data bytes. */
 } __attribute__((__packed__));
 
 /* Structure for GOT_IMSG_IDXPACK_REQUEST data. */
@@ -702,7 +704,7 @@ const struct got_error *got_privsep_recv_send_progress
     struct got_pathlist_head *, struct imsgbuf *);
 const struct got_error *got_privsep_send_packfd(struct imsgbuf *, int);
 const struct got_error *got_privsep_recv_send_progress(int *, off_t *,
-    int *, char **, struct imsgbuf *);
+    int *, char **, char **, struct imsgbuf *);
 const struct got_error *got_privsep_get_imsg_obj(struct got_object **,
     struct imsg *, struct imsgbuf *);
 const struct got_error *got_privsep_recv_obj(struct got_object **,
blob - 175c3a0c4138ebc26f9e5160af4f028ccf1f46e7
file + lib/privsep.c
--- lib/privsep.c
+++ lib/privsep.c
@@ -964,7 +964,7 @@ got_privsep_recv_send_progress(int *done, off_t *bytes
 
 const struct got_error *
 got_privsep_recv_send_progress(int *done, off_t *bytes_sent,
-    int *success, char **refname, struct imsgbuf *ibuf)
+    int *success, char **refname, char **errmsg, struct imsgbuf *ibuf)
 {
 	const struct got_error *err = NULL;
 	struct imsg imsg;
@@ -975,6 +975,7 @@ got_privsep_recv_send_progress(int *done, off_t *bytes
 	*done = 0;
 	*success = 0;
 	*refname = NULL;
+	*errmsg = NULL;
 
 	err = got_privsep_recv_imsg(&imsg, ibuf, 0);
 	if (err)
@@ -995,13 +996,18 @@ got_privsep_recv_send_progress(int *done, off_t *bytes
 			break;
 		}
 		memcpy(&iref_status, imsg.data, sizeof(iref_status));
-		if (datalen != sizeof(iref_status) + iref_status.name_len) {
+		if (datalen != sizeof(iref_status) + iref_status.name_len +
+		    iref_status.errmsg_len) {
 			err = got_error(GOT_ERR_PRIVSEP_MSG);
 			break;
 		}
 		*success = iref_status.success;
 		*refname = strndup(imsg.data + sizeof(iref_status),
 		    iref_status.name_len);
+
+		if (iref_status.errmsg_len != 0)
+			*errmsg = strndup(imsg.data + sizeof(iref_status) +
+			    iref_status.name_len, iref_status.errmsg_len);
 		break;
 	case GOT_IMSG_SEND_DONE:
 		if (datalen != 0) {
blob - f916422dab87523c5b073b161f9ba85ad2f970b2
file + lib/send.c
--- lib/send.c
+++ lib/send.c
@@ -126,7 +126,7 @@ pack_progress(void *arg, int ncolored, int nfound, int
 
 	err = a->progress_cb(a->progress_arg, ncolored, nfound, ntrees,
 	    packfile_size, ncommits, nobj_total, nobj_deltify,
-	    nobj_written, 0, NULL, 0);
+	    nobj_written, 0, NULL, NULL, 0);
 	if (err)
 		return err;
 
@@ -670,13 +670,15 @@ got_send_pack(const char *remote_name, struct got_path
 	while (!done) {
 		int success = 0;
 		char *refname = NULL;
+		char *errmsg = NULL;
+
 		if (cancel_cb) {
 			err = (*cancel_cb)(cancel_arg);
 			if (err)
 				goto done;
 		}
 		err = got_privsep_recv_send_progress(&done, &bytes_sent,
-		    &success, &refname, &sendibuf);
+		    &success, &refname, &errmsg, &sendibuf);
 		if (err)
 			goto done;
 		if (refname && got_ref_name_is_valid(refname) && success &&
@@ -700,14 +702,16 @@ got_send_pack(const char *remote_name, struct got_path
 			    ppa.nfound, ppa.ntrees, ppa.packfile_size,
 			    ppa.ncommits, ppa.nobj_total, ppa.nobj_deltify,
 			    ppa.nobj_written, bytes_sent,
-			    refname, success);
+			    refname, errmsg, success);
 			if (err) {
 				free(refname);
+				free(errmsg);
 				goto done;
 			}
 			bytes_sent_cur = bytes_sent;
 		}
 		free(refname);
+		free(errmsg);
 	}
 done:
 	if (sendpid != -1) {
blob - c773102729c2714c83c3b63b36a5ce9699aba978
file + libexec/got-send-pack/Makefile
--- libexec/got-send-pack/Makefile
+++ libexec/got-send-pack/Makefile
@@ -5,7 +5,7 @@ SRCS=		got-send-pack.c error.c inflate.c object_parse.
 PROG=		got-send-pack
 SRCS=		got-send-pack.c error.c inflate.c object_parse.c \
 		path.c privsep.c sha1.c pkt.c gitproto.c ratelimit.c \
-		pollfd.c
+		pollfd.c reference_parse.c
 
 CPPFLAGS = -I${.CURDIR}/../../include -I${.CURDIR}/../../lib
 
blob - 0e18f194406f1c8fe2f30bab9b5c8a3a3c0a6e5f
file + libexec/got-send-pack/got-send-pack.c
--- libexec/got-send-pack/got-send-pack.c
+++ libexec/got-send-pack/got-send-pack.c
@@ -223,10 +223,11 @@ send_ref_status(struct imsgbuf *ibuf, const char *refn
     struct got_pathlist_head *refs, struct got_pathlist_head *delete_refs)
 {
 	struct ibuf *wbuf;
-	size_t len, reflen = strlen(refname);
+	size_t len, reflen, errmsglen = 0;
 	struct got_pathlist_entry *pe;
 	int ref_valid = 0;
-	char *eol;
+	char *eol, *sp;
+	const char *errmsg = "";
 
 	eol = strchr(refname, '\n');
 	if (eol == NULL) {
@@ -235,6 +236,19 @@ send_ref_status(struct imsgbuf *ibuf, const char *refn
 	}
 	*eol = '\0';
 
+	sp = strchr(refname, ' ');
+	if (sp != NULL) {
+		*sp++ = '\0';
+		errmsg = sp;
+		errmsglen = strlen(errmsg);
+	}
+
+	reflen = strlen(refname);
+	if (!got_ref_name_is_valid(refname)) {
+		return got_error_msg(GOT_ERR_BAD_PACKET,
+		    "unexpected message from server");
+	}
+
 	TAILQ_FOREACH(pe, refs, entry) {
 		if (strcmp(refname, pe->path) == 0) {
 			ref_valid = 1;
@@ -254,7 +268,7 @@ send_ref_status(struct imsgbuf *ibuf, const char *refn
 		    "unexpected message from server");
 	}
 
-	len = sizeof(struct got_imsg_send_ref_status) + reflen;
+	len = sizeof(struct got_imsg_send_ref_status) + reflen + errmsglen;
 	if (len >= MAX_IMSGSIZE - IMSG_HEADER_SIZE)
 		return got_error(GOT_ERR_NO_SPACE);
 
@@ -268,8 +282,12 @@ send_ref_status(struct imsgbuf *ibuf, const char *refn
 		return got_error_from_errno("imsg_add SEND_REF_STATUS");
 	if (imsg_add(wbuf, &reflen, sizeof(reflen)) == -1)
 		return got_error_from_errno("imsg_add SEND_REF_STATUS");
+	if (imsg_add(wbuf, &errmsglen, sizeof(errmsglen)) == -1)
+		return got_error_from_errno("imsg_add SEND_REF_STATUS");
 	if (imsg_add(wbuf, refname, reflen) == -1)
 		return got_error_from_errno("imsg_add SEND_REF_STATUS");
+	if (imsg_add(wbuf, errmsg, errmsglen) == -1)
+		return got_error_from_errno("imsg_add SEND_REF_STATUS");
 
 	wbuf->fd = -1;
 	imsg_close(ibuf, wbuf);
blob - f989b0dbd6a3ca710184d881f574e55a5eb83e78
file + regress/cmdline/send.sh
--- regress/cmdline/send.sh
+++ regress/cmdline/send.sh
@@ -1513,6 +1513,69 @@ test_parseargs "$@"
 	test_done "$testroot" "$ret"
 }
 
+test_send_rejected() {
+	local testroot=`test_init send_rejected`
+	local testurl=ssh://127.0.0.1/$testroot
+	local commit_id=`git_show_head $testroot/repo`
+
+	if ! got clone -q "$testurl/repo" "$testroot/repo-clone"; then
+		echo "got clone command failed unexpectedly" >&2
+		test_done "$testroot" 1
+		return 1
+	fi
+
+	mkdir "$testroot/repo-clone/hooks"
+	cat <<'EOF' >$testroot/repo-clone/hooks/update
+case "$1" in
+	*master*)
+		echo "rejecting push on master branch"
+		exit 1
+		;;
+esac
+exit 0
+EOF
+	chmod +x "$testroot/repo-clone/hooks/update"
+
+	cat > $testroot/repo/.git/got.conf <<EOF
+remote "origin" {
+	protocol ssh
+	server 127.0.0.1
+	repository "$testroot/repo-clone"
+}
+EOF
+
+	echo "modified alpha" >$testroot/repo/alpha
+	git_commit "$testroot/repo" -m "modified alpha"
+
+	got send -q -r "$testroot/repo" >$testroot/stdout 2>$testroot/stderr
+	ret=$?
+	if [ $ret -ne 0 ]; then
+		echo "got send command failed unexpectedly" >&2
+		test_done "$testroot" $ret
+		return 1
+	fi
+
+	touch "$testroot/stdout.expected"
+	if ! cmp -s "$testroot/stdout.expected" "$testroot/stdout"; then
+		diff -u "$testroot/stdout.expected" "$testroot/stdout"
+		test_done "$testroot" 1
+		return 1
+	fi
+
+	cat <<EOF >$testroot/stderr.expected
+rejecting push on master branch
+error: hook declined to update refs/heads/master
+EOF
+
+	if ! cmp -s "$testroot/stderr.expected" "$testroot/stderr"; then
+		diff -u "$testroot/stderr.expected" "$testroot/stderr"
+		test_done "$testroot" 1
+		return 1
+	fi
+
+	test_done "$testroot" 0
+}
+
 test_parseargs "$@"
 run_test test_send_basic
 run_test test_send_rebase_required
@@ -1526,3 +1589,4 @@ run_test test_send_config
 run_test test_send_to_empty_repo
 run_test test_send_and_fetch_config
 run_test test_send_config
+run_test test_send_rejected