From estetus at gmail.com Mon Oct 4 10:22:34 2021 From: estetus at gmail.com (Sergey Bronnikov) Date: Mon, 4 Oct 2021 13:22:34 +0300 Subject: [PATCH 0/2] Support of test results Message-ID: From: Sergey Bronnikov Git SCM allows to store information in so called notes [1] and cgit allows to show notes that placed to default reference ('refs/notes/commits') and attached to certain commits. It is convenient to add various useful information related to commit in Git notes. Often tests results stored for future analysis in different systems (aka 'test report systems'). But one is the logic places to store test results is a Git SCM, test report automatically connected to commits and easily available for everyone. It even can be automated using git-test [2]. How-to use: $ cat report.tap 1..2 ok 1 - test_0.py::test_list_once ok 2 - test_0.py::test_list_twice $ GIT_NOTES_REF=refs/notes/testres git notes add -F report.tap 95aa7d200ee10580c472a1156a11c726046b110f $ git notes add -m 'Tested-by: Sergey Bronnikov ' \ 2a3a0d1a62de2ae5ab4511c15d82a6a0f2c2a930 $ GIT_NOTES_REF=refs/notes/testres git show 95aa7d200ee10580c472a1156a11c726046b110f commit 95aa7d200ee10580c472a1156a11c726046b110f Author: Sergey Bronnikov Date: Tue Dec 22 10:06:10 2020 +0300 ... Notes (testres): 1..2 ok 1 - test_0.py::test_list_once ok 2 - test_0.py::test_list_twice Test report become available in cgit by clicking to href 'tests' for that commit. 1. https://git-scm.com/docs/git-notes 2. https://github.com/ligurio/git-test 3. https://github.com/ligurio/testres/wiki/Using-GIT-as-a-storage Sergey Bronnikov (2): ui-commit: add support of test results ui-commit: add testres filter cgit.c | 6 +++ cgit.h | 4 +- cgitrc.5.txt | 17 +++++++++ cmd.c | 8 +++- filter.c | 6 +++ filters/testres-example.lua | 21 +++++++++++ shared.c | 1 + tests/setup.sh | 2 + tests/t0105-commit.sh | 7 ++++ tests/t0111-filter.sh | 8 ++++ ui-commit.c | 73 ++++++++++++++++++++++++++++++++++++- ui-commit.h | 1 + ui-shared.c | 8 +++- ui-shared.h | 3 ++ 14 files changed, 161 insertions(+), 4 deletions(-) create mode 100755 filters/testres-example.lua -- 2.25.1 From estetus at gmail.com Mon Oct 4 10:22:38 2021 From: estetus at gmail.com (Sergey Bronnikov) Date: Mon, 4 Oct 2021 13:22:38 +0300 Subject: [PATCH 2/2] ui-commit: add testres filter In-Reply-To: References: Message-ID: <249f4e44cc96942fff1c5c9f4ea832d35365627e.1633340983.git.sergeyb@tarantool.org> From: Sergey Bronnikov This allows to process test results before printing. Signed-off-by: Sergey Bronnikov --- cgit.c | 6 ++++++ cgit.h | 3 ++- cgitrc.5.txt | 17 +++++++++++++++++ filter.c | 6 ++++++ filters/testres-example.lua | 21 +++++++++++++++++++++ shared.c | 1 + tests/setup.sh | 1 + tests/t0111-filter.sh | 8 ++++++++ ui-commit.c | 2 ++ 9 files changed, 64 insertions(+), 1 deletion(-) create mode 100755 filters/testres-example.lua diff --git a/cgit.c b/cgit.c index 08d81a1..6a1224c 100644 --- a/cgit.c +++ b/cgit.c @@ -114,6 +114,8 @@ static void repo_config(struct cgit_repo *repo, const char *name, const char *va repo->commit_filter = cgit_new_filter(value, COMMIT); else if (!strcmp(name, "source-filter")) repo->source_filter = cgit_new_filter(value, SOURCE); + else if (!strcmp(name, "testres-filter")) + repo->testres_filter = cgit_new_filter(value, TESTRES); else if (!strcmp(name, "email-filter")) repo->email_filter = cgit_new_filter(value, EMAIL); else if (!strcmp(name, "owner-filter")) @@ -221,6 +223,8 @@ static void config_cb(const char *name, const char *value) ctx.cfg.about_filter = cgit_new_filter(value, ABOUT); else if (!strcmp(name, "commit-filter")) ctx.cfg.commit_filter = cgit_new_filter(value, COMMIT); + else if (!strcmp(name, "testres-filter")) + ctx.cfg.commit_filter = cgit_new_filter(value, TESTRES); else if (!strcmp(name, "email-filter")) ctx.cfg.email_filter = cgit_new_filter(value, EMAIL); else if (!strcmp(name, "owner-filter")) @@ -830,6 +834,8 @@ static void print_repo(FILE *f, struct cgit_repo *repo) cgit_fprintf_filter(repo->commit_filter, f, "repo.commit-filter="); if (repo->source_filter && repo->source_filter != ctx.cfg.source_filter) cgit_fprintf_filter(repo->source_filter, f, "repo.source-filter="); + if (repo->testres_filter && repo->testres_filter != ctx.cfg.testres_filter) + cgit_fprintf_filter(repo->testres_filter, f, "repo.testres_filter="); if (repo->email_filter && repo->email_filter != ctx.cfg.email_filter) cgit_fprintf_filter(repo->email_filter, f, "repo.email-filter="); if (repo->owner_filter && repo->owner_filter != ctx.cfg.owner_filter) diff --git a/cgit.h b/cgit.h index b504168..da30079 100644 --- a/cgit.h +++ b/cgit.h @@ -58,7 +58,7 @@ typedef enum { } diff_type; typedef enum { - ABOUT, COMMIT, SOURCE, EMAIL, AUTH, OWNER + ABOUT, COMMIT, SOURCE, TESTRES, EMAIL, AUTH, OWNER } filter_type; struct cgit_filter { @@ -268,6 +268,7 @@ struct cgit_config { struct cgit_filter *about_filter; struct cgit_filter *commit_filter; struct cgit_filter *source_filter; + struct cgit_filter *testres_filter; struct cgit_filter *email_filter; struct cgit_filter *owner_filter; struct cgit_filter *auth_filter; diff --git a/cgitrc.5.txt b/cgitrc.5.txt index 33a6a8c..77386fb 100644 --- a/cgitrc.5.txt +++ b/cgitrc.5.txt @@ -441,6 +441,14 @@ strict-export:: repositories to match those exported by git-daemon. This option must be defined prior to scan-path. +testres-filter:: + Specifies a command which will be invoked to format testing results + report that stored in Git notes by reference 'refs/notes/testres'. + The command will get the report's content on its STDIN, and the STDOUT from the + command will be included verbatim as the formatted report, i.e. this can + be used to parse report and visualize it. Default value: none. + See also: "FILTER API". + virtual-root:: Url which, if specified, will be used as root for all cgit links. It will also cause cgit to generate 'virtual urls', i.e. urls like @@ -605,6 +613,10 @@ repo.source-filter:: Override the default source-filter. Default value: none. See also: "enable-filter-overrides". See also: "FILTER API". +repo.testres-filter:: + Override the default testres-filter. Default value: none. See also: + "enable-filter-overrides". See also: "FILTER API". + repo.url:: The relative url used to access the repository. This must be the first setting specified for each repo. Default value: none. @@ -723,6 +735,11 @@ source filter:: file that is to be filtered is available on standard input and the filtered contents is expected on standard output. +testres filter:: + This filter is given a single parameter: the buffer with raw test + report contents to filter. The contents of the buffer that is to be + filtered is available on standard input and the filtered contents is + expected on standard output. All filters are handed the following environment variables: diff --git a/filter.c b/filter.c index 70f5b74..52f1232 100644 --- a/filter.c +++ b/filter.c @@ -27,6 +27,7 @@ void cgit_cleanup_filters(void) reap_filter(ctx.cfg.about_filter); reap_filter(ctx.cfg.commit_filter); reap_filter(ctx.cfg.source_filter); + reap_filter(ctx.cfg.testres_filter); reap_filter(ctx.cfg.email_filter); reap_filter(ctx.cfg.owner_filter); reap_filter(ctx.cfg.auth_filter); @@ -34,6 +35,7 @@ void cgit_cleanup_filters(void) reap_filter(cgit_repolist.repos[i].about_filter); reap_filter(cgit_repolist.repos[i].commit_filter); reap_filter(cgit_repolist.repos[i].source_filter); + reap_filter(cgit_repolist.repos[i].testres_filter); reap_filter(cgit_repolist.repos[i].email_filter); reap_filter(cgit_repolist.repos[i].owner_filter); } @@ -433,6 +435,10 @@ struct cgit_filter *cgit_new_filter(const char *cmd, filter_type filtertype) break; case SOURCE: + case TESTRES: + argument_count = 1; + break; + case ABOUT: argument_count = 1; break; diff --git a/filters/testres-example.lua b/filters/testres-example.lua new file mode 100755 index 0000000..fae45e5 --- /dev/null +++ b/filters/testres-example.lua @@ -0,0 +1,21 @@ +-- This script is an example of an testres-filter. It replaces the +-- test statuses used in Test Anything Protocol format to PASS and FAIL. +-- This script may be used with the testres-filter or repo.testres-filter +-- settings in cgitrc with the `lua:` prefix. + +function filter_open() + buffer = "" +end + +function filter_close() + html("
")
+	buffer = string.gsub(buffer, "not ok", "FAIL")
+	buffer = string.gsub(buffer, "ok", "PASS")
+	html(buffer)
+	html("
") + return 0 +end + +function filter_write(str) + buffer = buffer .. str +end diff --git a/shared.c b/shared.c index 8115469..6fd4b26 100644 --- a/shared.c +++ b/shared.c @@ -75,6 +75,7 @@ struct cgit_repo *cgit_add_repo(const char *url) ret->commit_filter = ctx.cfg.commit_filter; ret->source_filter = ctx.cfg.source_filter; ret->email_filter = ctx.cfg.email_filter; + ret->testres_filter = ctx.cfg.testres_filter; ret->owner_filter = ctx.cfg.owner_filter; ret->clone_url = ctx.cfg.clone_url; ret->submodules.strdup_strings = 1; diff --git a/tests/setup.sh b/tests/setup.sh index 7a83c9f..b338fcf 100755 --- a/tests/setup.sh +++ b/tests/setup.sh @@ -155,6 +155,7 @@ repo.about-filter=lua:$FILTER_DIRECTORY/dump.lua repo.commit-filter=lua:$FILTER_DIRECTORY/dump.lua repo.email-filter=lua:$FILTER_DIRECTORY/dump.lua repo.source-filter=lua:$FILTER_DIRECTORY/dump.lua +repo.testres-filter=lua:$FILTER_DIRECTORY/dump.lua repo.readme=master:a+b EOF fi diff --git a/tests/t0111-filter.sh b/tests/t0111-filter.sh index 2fdc366..173f20c 100755 --- a/tests/t0111-filter.sh +++ b/tests/t0111-filter.sh @@ -41,6 +41,14 @@ do test_expect_success "check whether the $prefix email filter works for committers" ' grep " commit C O MITTER <COMMITTER at EXAMPLE.COM>" tmp ' + + test_expect_success "generate filter-$prefix/testres/" " + cgit_url 'filter-$prefix/testres/' >tmp + " + + test_expect_success "check whether the $prefix testres filter works" ' + grep "AABBCC" tmp + ' done test_done diff --git a/ui-commit.c b/ui-commit.c index 7824f93..62cd860 100644 --- a/ui-commit.c +++ b/ui-commit.c @@ -66,7 +66,9 @@ void cgit_print_testres(char *hex) cgit_print_layout_start(); if (ctx.repo->testres_filter) { + cgit_open_filter(ctx.repo->testres_filter); html_raw(buf, size); + cgit_close_filter(ctx.repo->testres_filter); } else { html("
");
 		html_txt(buf);
-- 
2.25.1


From s at brnkv.ru  Tue Oct  5 09:56:30 2021
From: s at brnkv.ru (Sergey Bronnikov)
Date: Tue, 5 Oct 2021 12:56:30 +0300
Subject: [PATCH 0/2] Support of test results
In-Reply-To: 
References: 
Message-ID: <584ccba6-db33-e6ce-17a4-7e069a6547c2@brnkv.ru>

First patch was not delivered because patch itself contains HTML tags and
mails with HTML are forbidden in a mail server.
The patch series is also available on Github [1].
And also there are screenshots that demonstrate changes in cgit web ui: 
[2] and [3].

1. https://github.com/ligurio/cgit/tree/support_test_results
2. https://bronevichok.ru/static/cgit-1.png
3. https://bronevichok.ru/static/cgit-2.png

On 04.10.2021 13:22, Sergey Bronnikov wrote:
> From: Sergey Bronnikov 
>
> Git SCM allows to store information in so called notes [1] and cgit
> allows to show notes that placed to default reference
> ('refs/notes/commits') and attached to certain commits. It is convenient
> to add various useful information related to commit in Git notes.
>
> Often tests results stored for future analysis in different systems (aka
> 'test report systems'). But one is the logic places to store test
> results is a Git SCM, test report automatically connected to commits and
> easily available for everyone. It even can be automated using git-test [2].
>
> How-to use:
>
> $ cat report.tap
> 1..2
> ok 1 - test_0.py::test_list_once
> ok 2 - test_0.py::test_list_twice
> $ GIT_NOTES_REF=refs/notes/testres git notes add -F report.tap 95aa7d200ee10580c472a1156a11c726046b110f
> $ git notes add -m 'Tested-by: Sergey Bronnikov ' \
> 	    2a3a0d1a62de2ae5ab4511c15d82a6a0f2c2a930
> $ GIT_NOTES_REF=refs/notes/testres git show 95aa7d200ee10580c472a1156a11c726046b110f
>
> commit 95aa7d200ee10580c472a1156a11c726046b110f
> Author: Sergey Bronnikov 
> Date:   Tue Dec 22 10:06:10 2020 +0300
>
> ...
>
> Notes (testres):
>      1..2
>      ok 1 - test_0.py::test_list_once
>      ok 2 - test_0.py::test_list_twice
>
> Test report become available in cgit by clicking to href 'tests' for
> that commit.
>
> 1. https://git-scm.com/docs/git-notes
> 2. https://github.com/ligurio/git-test
> 3. https://github.com/ligurio/testres/wiki/Using-GIT-as-a-storage
>
> Sergey Bronnikov (2):
>    ui-commit: add support of test results
>    ui-commit: add testres filter
>
>   cgit.c                      |  6 +++
>   cgit.h                      |  4 +-
>   cgitrc.5.txt                | 17 +++++++++
>   cmd.c                       |  8 +++-
>   filter.c                    |  6 +++
>   filters/testres-example.lua | 21 +++++++++++
>   shared.c                    |  1 +
>   tests/setup.sh              |  2 +
>   tests/t0105-commit.sh       |  7 ++++
>   tests/t0111-filter.sh       |  8 ++++
>   ui-commit.c                 | 73 ++++++++++++++++++++++++++++++++++++-
>   ui-commit.h                 |  1 +
>   ui-shared.c                 |  8 +++-
>   ui-shared.h                 |  3 ++
>   14 files changed, 161 insertions(+), 4 deletions(-)
>   create mode 100755 filters/testres-example.lua
>

From john at keeping.me.uk  Thu Oct  7 09:35:49 2021
From: john at keeping.me.uk (John Keeping)
Date: Thu, 7 Oct 2021 10:35:49 +0100
Subject: [PATCH] cache: Tolerate short writes in print_slot
In-Reply-To: <20210910141841.2092532-1-hristo@venev.name>
References: <20210910141841.2092532-1-hristo@venev.name>
Message-ID: 

On Fri, Sep 10, 2021 at 05:18:41PM +0300, Hristo Venev wrote:
> sendfile() can return after a short read/write, so we may need to call
> it more than once. Furthermore, not all files support sendfile(), so we
> may need to fall back to read/write.

Have you seen these errors in practice, or is this just theoretical?

In recent (since v2.6.33) versions of Linux, all files should support
sendfile(), especially since we expect out_fd to be a socket or pipe.

> On the read/write path, use write_in_full which deals with short writes.
> 
> Signed-off-by: Hristo Venev 
> ---
>  cache.c | 46 ++++++++++++++++++++++++----------------------
>  1 file changed, 24 insertions(+), 22 deletions(-)
> 
> diff --git a/cache.c b/cache.c
> index 55199e8..85cfbd9 100644
> --- a/cache.c
> +++ b/cache.c
> @@ -85,40 +85,42 @@ static int close_slot(struct cache_slot *slot)
>  /* Print the content of the active cache slot (but skip the key). */
>  static int print_slot(struct cache_slot *slot)
>  {
> -#ifdef HAVE_LINUX_SENDFILE
> -	off_t start_off;
> -	int ret;
> +	off_t off;
> +	ssize_t i;
> +
> +	off = slot->keylen + 1;
>  
> -	start_off = slot->keylen + 1;
> +#ifdef HAVE_LINUX_SENDFILE
> +	off_t size;

decl-after-stmt if HAVE_LINUX_SENDFILE is set.

> +	size = slot->cache_st.st_size;
>  
>  	do {
> -		ret = sendfile(STDOUT_FILENO, slot->cache_fd, &start_off,
> -				slot->cache_st.st_size - start_off);
> -		if (ret < 0) {
> +		i = sendfile(STDOUT_FILENO, slot->cache_fd, &off, size - off);

Why is ret renamed?  i is normally a loop index variable, using it for
the return value here is strange, please stick with "ret".

> +		if (i < 0) {
>  			if (errno == EAGAIN || errno == EINTR)
>  				continue;
> +			/* Fall back to read/write on EINVAL */
> +			if (errno == EINVAL)
> +				break;
>  			return errno;
>  		}
> -		return 0;
> +		if (off == size)
> +			return 0;
>  	} while (1);
> -#else
> -	ssize_t i, j;
> +#endif
>  
> -	i = lseek(slot->cache_fd, slot->keylen + 1, SEEK_SET);
> -	if (i != slot->keylen + 1)
> +	if (lseek(slot->cache_fd, off, SEEK_SET) != off)
>  		return errno;
>  
>  	do {
> -		i = j = xread(slot->cache_fd, slot->buf, sizeof(slot->buf));
> -		if (i > 0)
> -			j = xwrite(STDOUT_FILENO, slot->buf, i);
> -	} while (i > 0 && j == i);
> -
> -	if (i < 0 || j != i)
> -		return errno;
> -	else
> -		return 0;
> -#endif
> +		i = xread(slot->cache_fd, slot->buf, sizeof(slot->buf));
> +		if (i < 0)
> +			return errno;
> +		if (i == 0)
> +			return 0;
> +		if (write_in_full(STDOUT_FILENO, slot->buf, i) < 0)
> +			return errno;
> +	} while (1);
>  }
>  
>  /* Check if the slot has expired */
> -- 
> 2.31.1
> 

From john at keeping.me.uk  Thu Oct  7 09:42:09 2021
From: john at keeping.me.uk (John Keeping)
Date: Thu, 7 Oct 2021 10:42:09 +0100
Subject: [PATCH 0/2] Support of test results
In-Reply-To: 
References: 
Message-ID: 

On Mon, Oct 04, 2021 at 01:22:34PM +0300, Sergey Bronnikov wrote:
> From: Sergey Bronnikov 
> 
> Git SCM allows to store information in so called notes [1] and cgit
> allows to show notes that placed to default reference
> ('refs/notes/commits') and attached to certain commits. It is convenient
> to add various useful information related to commit in Git notes.
> 
> Often tests results stored for future analysis in different systems (aka
> 'test report systems'). But one is the logic places to store test
> results is a Git SCM, test report automatically connected to commits and
> easily available for everyone. It even can be automated using git-test [2].

This feels very specifc to one use case that isn't particularly general.
Have you considered any way to make this more generic for displaying
different notes for different purposes?

Did you experiment with changing notes.displayRef in gitconfing and
using the existing commit filter to display those inline in the commit
page?

> How-to use:
> 
> $ cat report.tap
> 1..2
> ok 1 - test_0.py::test_list_once
> ok 2 - test_0.py::test_list_twice
> $ GIT_NOTES_REF=refs/notes/testres git notes add -F report.tap 95aa7d200ee10580c472a1156a11c726046b110f
> $ git notes add -m 'Tested-by: Sergey Bronnikov ' \
> 	    2a3a0d1a62de2ae5ab4511c15d82a6a0f2c2a930
> $ GIT_NOTES_REF=refs/notes/testres git show 95aa7d200ee10580c472a1156a11c726046b110f
> 
> commit 95aa7d200ee10580c472a1156a11c726046b110f
> Author: Sergey Bronnikov 
> Date:   Tue Dec 22 10:06:10 2020 +0300
> 
> ...
> 
> Notes (testres):
>     1..2
>     ok 1 - test_0.py::test_list_once
>     ok 2 - test_0.py::test_list_twice
> 
> Test report become available in cgit by clicking to href 'tests' for
> that commit.
> 
> 1. https://git-scm.com/docs/git-notes
> 2. https://github.com/ligurio/git-test
> 3. https://github.com/ligurio/testres/wiki/Using-GIT-as-a-storage
> 
> Sergey Bronnikov (2):
>   ui-commit: add support of test results
>   ui-commit: add testres filter
> 
>  cgit.c                      |  6 +++
>  cgit.h                      |  4 +-
>  cgitrc.5.txt                | 17 +++++++++
>  cmd.c                       |  8 +++-
>  filter.c                    |  6 +++
>  filters/testres-example.lua | 21 +++++++++++
>  shared.c                    |  1 +
>  tests/setup.sh              |  2 +
>  tests/t0105-commit.sh       |  7 ++++
>  tests/t0111-filter.sh       |  8 ++++
>  ui-commit.c                 | 73 ++++++++++++++++++++++++++++++++++++-
>  ui-commit.h                 |  1 +
>  ui-shared.c                 |  8 +++-
>  ui-shared.h                 |  3 ++
>  14 files changed, 161 insertions(+), 4 deletions(-)
>  create mode 100755 filters/testres-example.lua
> 
> -- 
> 2.25.1
> 

From hristo at venev.name  Thu Oct  7 15:44:35 2021
From: hristo at venev.name (Hristo Venev)
Date: Thu, 07 Oct 2021 18:44:35 +0300
Subject: [PATCH] cache: Tolerate short writes in print_slot
In-Reply-To: 
References: <20210910141841.2092532-1-hristo@venev.name>
 
Message-ID: <50038a48ec126969331acfa1df13b4ae6bdd3814.camel@venev.name>

On Thu, 2021-10-07 at 10:35 +0100, John Keeping wrote:
> Have you seen these errors in practice, or is this just theoretical?
>
> In recent (since v2.6.33) versions of Linux, all files should support
> sendfile(), especially since we expect out_fd to be a socket or pipe.

Even though I haven't seen any errors from sendfile, I'm not sure if it
always works with fuse filesystems and with third-party filesystems
like zfs. The sendfile(2) man page says this:

"Applications may wish to fall back to read(2)/write(2) in the case
where sendfile() fails with EINVAL or ENOSYS."

> > On the read/write path, use write_in_full which deals with short
> > writes.
> > 
> > Signed-off-by: Hristo Venev 
> > ---
> > ?cache.c | 46 ++++++++++++++++++++++++----------------------
> > ?1 file changed, 24 insertions(+), 22 deletions(-)
> > 
> > diff --git a/cache.c b/cache.c
> > index 55199e8..85cfbd9 100644
> > --- a/cache.c
> > +++ b/cache.c
> > @@ -85,40 +85,42 @@ static int close_slot(struct cache_slot *slot)
> > ?/* Print the content of the active cache slot (but skip the key). */
> > ?static int print_slot(struct cache_slot *slot)
> > ?{
> > -#ifdef HAVE_LINUX_SENDFILE
> > -???????off_t start_off;
> > -???????int ret;
> > +???????off_t off;
> > +???????ssize_t i;
> > +
> > +???????off = slot->keylen + 1;
> > ?
> > -???????start_off = slot->keylen + 1;
> > +#ifdef HAVE_LINUX_SENDFILE
> > +???????off_t size;
> 
> decl-after-stmt if HAVE_LINUX_SENDFILE is set.

I didn't know that compilers that don't support that exist outside
museums. I will fix it in v2.

> > +???????size = slot->cache_st.st_size;
> > ?
> > ????????do {
> > -???????????????ret = sendfile(STDOUT_FILENO, slot->cache_fd,
> > &start_off,
> > -???????????????????????????????slot->cache_st.st_size - start_off);
> > -???????????????if (ret < 0) {
> > +???????????????i = sendfile(STDOUT_FILENO, slot->cache_fd, &off,
> > size - off);
> 
> Why is ret renamed?? i is normally a loop index variable, using it for
> the return value here is strange, please stick with "ret".

I will fix this in v2.

> > +???????????????if (i < 0) {
> > ????????????????????????if (errno == EAGAIN || errno == EINTR)
> > ????????????????????????????????continue;
> > +???????????????????????/* Fall back to read/write on EINVAL */
> > +???????????????????????if (errno == EINVAL)
> > +???????????????????????????????break;
> > ????????????????????????return errno;
> > ????????????????}
> > -???????????????return 0;
> > +???????????????if (off == size)
> > +???????????????????????return 0;
> > ????????} while (1);
> > -#else
> > -???????ssize_t i, j;
> > +#endif
> > ?
> > -???????i = lseek(slot->cache_fd, slot->keylen + 1, SEEK_SET);
> > -???????if (i != slot->keylen + 1)
> > +???????if (lseek(slot->cache_fd, off, SEEK_SET) != off)
> > ????????????????return errno;
> > ?
> > ????????do {
> > -???????????????i = j = xread(slot->cache_fd, slot->buf, sizeof(slot-
> > >buf));
> > -???????????????if (i > 0)
> > -???????????????????????j = xwrite(STDOUT_FILENO, slot->buf, i);
> > -???????} while (i > 0 && j == i);
> > -
> > -???????if (i < 0 || j != i)
> > -???????????????return errno;
> > -???????else
> > -???????????????return 0;
> > -#endif
> > +???????????????i = xread(slot->cache_fd, slot->buf, sizeof(slot-
> > >buf));
> > +???????????????if (i < 0)
> > +???????????????????????return errno;
> > +???????????????if (i == 0)
> > +???????????????????????return 0;
> > +???????????????if (write_in_full(STDOUT_FILENO, slot->buf, i) < 0)
> > +???????????????????????return errno;
> > +???????} while (1);
> > ?}
> > ?
> > ?/* Check if the slot has expired */
> > -- 
> > 2.31.1
> > 

-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 858 bytes
Desc: This is a digitally signed message part
URL: 

From s at brnkv.ru  Sun Oct 10 17:51:33 2021
From: s at brnkv.ru (Sergey Bronnikov)
Date: Sun, 10 Oct 2021 20:51:33 +0300
Subject: [PATCH 0/2] Support of test results
In-Reply-To: 
References: 
 
Message-ID: <0bce2d8f-19b0-51fb-1240-5993c2fb6d10@brnkv.ru>


On 07.10.2021 12:42, John Keeping wrote:
> On Mon, Oct 04, 2021 at 01:22:34PM +0300, Sergey Bronnikov wrote:
>> From: Sergey Bronnikov 
>>
>> Git SCM allows to store information in so called notes [1] and cgit
>> allows to show notes that placed to default reference
>> ('refs/notes/commits') and attached to certain commits. It is convenient
>> to add various useful information related to commit in Git notes.
>>
>> Often tests results stored for future analysis in different systems (aka
>> 'test report systems'). But one is the logic places to store test
>> results is a Git SCM, test report automatically connected to commits and
>> easily available for everyone. It even can be automated using git-test [2].
> This feels very specifc to one use case that isn't particularly general.
> Have you considered any way to make this more generic for displaying
> different notes for different purposes?

In my experience different tools uses different git notes ref and

it cannot be changed. For example: "git appraise" uses

refs/notes/devtools/reviews, gerrit uses refs/notes/review

and google-git-notes-publisher-plugin uses refs/notes/devtools/ci.

I can make git note ref configurable. Will make it more general feature?

> Did you experiment with changing notes.displayRef in gitconfing and
> using the existing commit filter to display those inline in the commit
> page?

Sure, I know about default notes and experimented with commit filters a bit.

I don't like to store test results in default ref because:

- often test report has a long list of tests and it is inconvenient to 
scroll down it on cgit commit page.

- it is inconvenient to parse test report format when it is mixed with 
commit message,

>
>> How-to use:
>>
>> $ cat report.tap
>> 1..2
>> ok 1 - test_0.py::test_list_once
>> ok 2 - test_0.py::test_list_twice
>> $ GIT_NOTES_REF=refs/notes/testres git notes add -F report.tap 95aa7d200ee10580c472a1156a11c726046b110f
>> $ git notes add -m 'Tested-by: Sergey Bronnikov ' \
>> 	    2a3a0d1a62de2ae5ab4511c15d82a6a0f2c2a930
>> $ GIT_NOTES_REF=refs/notes/testres git show 95aa7d200ee10580c472a1156a11c726046b110f
>>
>> commit 95aa7d200ee10580c472a1156a11c726046b110f
>> Author: Sergey Bronnikov 
>> Date:   Tue Dec 22 10:06:10 2020 +0300
>>
>> ...
>>
>> Notes (testres):
>>      1..2
>>      ok 1 - test_0.py::test_list_once
>>      ok 2 - test_0.py::test_list_twice
>>
>> Test report become available in cgit by clicking to href 'tests' for
>> that commit.
>>
>> 1. https://git-scm.com/docs/git-notes
>> 2. https://github.com/ligurio/git-test
>> 3. https://github.com/ligurio/testres/wiki/Using-GIT-as-a-storage
>>
>> Sergey Bronnikov (2):
>>    ui-commit: add support of test results
>>    ui-commit: add testres filter
>>
>>   cgit.c                      |  6 +++
>>   cgit.h                      |  4 +-
>>   cgitrc.5.txt                | 17 +++++++++
>>   cmd.c                       |  8 +++-
>>   filter.c                    |  6 +++
>>   filters/testres-example.lua | 21 +++++++++++
>>   shared.c                    |  1 +
>>   tests/setup.sh              |  2 +
>>   tests/t0105-commit.sh       |  7 ++++
>>   tests/t0111-filter.sh       |  8 ++++
>>   ui-commit.c                 | 73 ++++++++++++++++++++++++++++++++++++-
>>   ui-commit.h                 |  1 +
>>   ui-shared.c                 |  8 +++-
>>   ui-shared.h                 |  3 ++
>>   14 files changed, 161 insertions(+), 4 deletions(-)
>>   create mode 100755 filters/testres-example.lua
>>
>> -- 
>> 2.25.1
>>

From wu.tommy at gmail.com  Thu Oct 21 08:42:44 2021
From: wu.tommy at gmail.com (Tommy Wu)
Date: Thu, 21 Oct 2021 16:42:44 +0800
Subject: [PATCH] avoid to break UTF-8 character when wrap message
Message-ID: 

Wrap message with specified length might break the UTF-8 character.
We need to calculate the proper length when wrap message.
---
 ui-log.c    |  2 +-
 ui-shared.c | 17 ++++++++++++++++-
 ui-shared.h |  2 ++
 3 files changed, 19 insertions(+), 2 deletions(-)

diff --git a/ui-log.c b/ui-log.c
index 20774bf..cf66a84 100644
--- a/ui-log.c
+++ b/ui-log.c
@@ -227,7 +227,7 @@ static void print_commit(struct commit *commit,
struct rev_info *revs)
  while (i > 0 && !isspace(info->subject[i]))
  --i;
  if (!i) /* Oops, zero spaces. Reset i */
- i = ctx.cfg.max_msg_len - strlen(wrap_symbol);
+ i = cgit_utf8_wrap_len(info->subject, ctx.cfg.max_msg_len -
strlen(wrap_symbol));

  /* add remainder starting at i to msgbuf */
  strbuf_add(&msgbuf, info->subject + i, subject_len - i);
diff --git a/ui-shared.c b/ui-shared.c
index acd8ab5..ba0a968 100644
--- a/ui-shared.c
+++ b/ui-shared.c
@@ -29,6 +29,20 @@ static char *http_date(time_t t)
     tm.tm_hour, tm.tm_min, tm.tm_sec);
 }

+int cgit_utf8_wrap_len(const char *s, int maxlen)
+{
+ int i = 0, pos = 0;
+
+ while (s[i]) {
+ if ((s[i] & 0xC0) != 0x80)
+ pos = i;
+ i++;
+ if (i > maxlen)
+ break;
+ }
+ return pos;
+}
+
 void cgit_print_error(const char *fmt, ...)
 {
  va_list ap;
@@ -436,7 +450,8 @@ void cgit_commit_link(const char *name, const char
*title, const char *class,
  html("'>");
  if (name[0] != '\0') {
  if (strlen(name) > ctx.cfg.max_msg_len && ctx.cfg.max_msg_len >= 15) {
- html_ntxt(name, ctx.cfg.max_msg_len - 3);
+ int maxlen = cgit_utf8_wrap_len(name, ctx.cfg.max_msg_len - 3);
+ html_ntxt(name, maxlen);
  html("...");
  } else
  html_txt(name);
diff --git a/ui-shared.h b/ui-shared.h
index 6964873..28ec841 100644
--- a/ui-shared.h
+++ b/ui-shared.h
@@ -1,6 +1,8 @@
 #ifndef UI_SHARED_H
 #define UI_SHARED_H

+extern int cgit_utf8_wrap_len(const char *s, int maxlen);
+
 extern const char *cgit_httpscheme(void);
 extern char *cgit_hosturl(void);
 extern const char *cgit_rooturl(void);
-- 
2.30.2

From wu.tommy at gmail.com  Thu Oct 21 08:45:44 2021
From: wu.tommy at gmail.com (Tommy Wu)
Date: Thu, 21 Oct 2021 16:45:44 +0800
Subject: [PATCH] add diff-filter option
Message-ID: 

This patch add diff-filter option. This new option is used to
specify an external command which will be executed when displaying diff
content. Diff content will be written to STDIN of the filter and STDOUT from
the filter will be included verbatim in the html output from cgit.
---
 cgit.c       | 6 ++++++
 cgit.h       | 4 +++-
 cgitrc.5.txt | 7 +++++++
 filter.c     | 3 +++
 shared.c     | 5 +++++
 5 files changed, 24 insertions(+), 1 deletion(-)

diff --git a/cgit.c b/cgit.c
index 08d81a1..039f98b 100644
--- a/cgit.c
+++ b/cgit.c
@@ -112,6 +112,8 @@ static void repo_config(struct cgit_repo *repo,
const char *name, const char *va
  repo->about_filter = cgit_new_filter(value, ABOUT);
  else if (!strcmp(name, "commit-filter"))
  repo->commit_filter = cgit_new_filter(value, COMMIT);
+ else if (!strcmp(name, "diff-filter"))
+ repo->diff_filter = cgit_new_filter(value, DIFF);
  else if (!strcmp(name, "source-filter"))
  repo->source_filter = cgit_new_filter(value, SOURCE);
  else if (!strcmp(name, "email-filter"))
@@ -221,6 +223,8 @@ static void config_cb(const char *name, const char *value)
  ctx.cfg.about_filter = cgit_new_filter(value, ABOUT);
  else if (!strcmp(name, "commit-filter"))
  ctx.cfg.commit_filter = cgit_new_filter(value, COMMIT);
+ else if (!strcmp(name, "diff-filter"))
+ ctx.cfg.diff_filter = cgit_new_filter(value, DIFF);
  else if (!strcmp(name, "email-filter"))
  ctx.cfg.email_filter = cgit_new_filter(value, EMAIL);
  else if (!strcmp(name, "owner-filter"))
@@ -828,6 +832,8 @@ static void print_repo(FILE *f, struct cgit_repo *repo)
  cgit_fprintf_filter(repo->about_filter, f, "repo.about-filter=");
  if (repo->commit_filter && repo->commit_filter != ctx.cfg.commit_filter)
  cgit_fprintf_filter(repo->commit_filter, f, "repo.commit-filter=");
+ if (repo->diff_filter && repo->diff_filter != ctx.cfg.diff_filter)
+ cgit_fprintf_filter(repo->diff_filter, f, "repo.diff-filter=");
  if (repo->source_filter && repo->source_filter != ctx.cfg.source_filter)
  cgit_fprintf_filter(repo->source_filter, f, "repo.source-filter=");
  if (repo->email_filter && repo->email_filter != ctx.cfg.email_filter)
diff --git a/cgit.h b/cgit.h
index 69b5c13..5b03c0d 100644
--- a/cgit.h
+++ b/cgit.h
@@ -58,7 +58,7 @@ typedef enum {
 } diff_type;

 typedef enum {
- ABOUT, COMMIT, SOURCE, EMAIL, AUTH, OWNER
+ ABOUT, COMMIT, DIFF, SOURCE, EMAIL, AUTH, OWNER
 } filter_type;

 struct cgit_filter {
@@ -107,6 +107,7 @@ struct cgit_repo {
  time_t mtime;
  struct cgit_filter *about_filter;
  struct cgit_filter *commit_filter;
+ struct cgit_filter *diff_filter;
  struct cgit_filter *source_filter;
  struct cgit_filter *email_filter;
  struct cgit_filter *owner_filter;
@@ -266,6 +267,7 @@ struct cgit_config {
  struct string_list mimetypes;
  struct cgit_filter *about_filter;
  struct cgit_filter *commit_filter;
+ struct cgit_filter *diff_filter;
  struct cgit_filter *source_filter;
  struct cgit_filter *email_filter;
  struct cgit_filter *owner_filter;
diff --git a/cgitrc.5.txt b/cgitrc.5.txt
index 33a6a8c..8b0fd96 100644
--- a/cgitrc.5.txt
+++ b/cgitrc.5.txt
@@ -128,6 +128,13 @@ css::
  Url which specifies the css document to include in all cgit pages.
  Default value: "/cgit.css".

+diff-filter::
+ Specifies a command which will be invoked to format diff content.
+ The command will get the diff content on its STDIN and the STDOUT
+ from the command will be included verbatim as the diff contents, i.e.
+ this can be used to implement e.g. fallback encoding like gitweb.
+ Default value: none. See also: "FILTER API".
+
 email-filter::
  Specifies a command which will be invoked to format names and email
  address of committers, authors, and taggers, as represented in various
diff --git a/filter.c b/filter.c
index 70f5b74..71c8828 100644
--- a/filter.c
+++ b/filter.c
@@ -26,6 +26,7 @@ void cgit_cleanup_filters(void)
  int i;
  reap_filter(ctx.cfg.about_filter);
  reap_filter(ctx.cfg.commit_filter);
+ reap_filter(ctx.cfg.diff_filter);
  reap_filter(ctx.cfg.source_filter);
  reap_filter(ctx.cfg.email_filter);
  reap_filter(ctx.cfg.owner_filter);
@@ -33,6 +34,7 @@ void cgit_cleanup_filters(void)
  for (i = 0; i < cgit_repolist.count; ++i) {
  reap_filter(cgit_repolist.repos[i].about_filter);
  reap_filter(cgit_repolist.repos[i].commit_filter);
+ reap_filter(cgit_repolist.repos[i].diff_filter);
  reap_filter(cgit_repolist.repos[i].source_filter);
  reap_filter(cgit_repolist.repos[i].email_filter);
  reap_filter(cgit_repolist.repos[i].owner_filter);
@@ -438,6 +440,7 @@ struct cgit_filter *cgit_new_filter(const char
*cmd, filter_type filtertype)
  break;

  case COMMIT:
+ case DIFF:
  default:
  argument_count = 0;
  break;
diff --git a/shared.c b/shared.c
index 8115469..4c0e144 100644
--- a/shared.c
+++ b/shared.c
@@ -73,6 +73,7 @@ struct cgit_repo *cgit_add_repo(const char *url)
  ret->mtime = -1;
  ret->about_filter = ctx.cfg.about_filter;
  ret->commit_filter = ctx.cfg.commit_filter;
+ ret->diff_filter = ctx.cfg.diff_filter;
  ret->source_filter = ctx.cfg.source_filter;
  ret->email_filter = ctx.cfg.email_filter;
  ret->owner_filter = ctx.cfg.owner_filter;
@@ -328,7 +329,11 @@ int cgit_diff_files(const struct object_id *old_oid,
  emit_params.flags = XDL_EMIT_FUNCNAMES;
  emit_cb.out_line = filediff_cb;
  emit_cb.priv = fn;
+ if (ctx.repo->diff_filter)
+ cgit_open_filter(ctx.repo->diff_filter);
  xdl_diff(&file1, &file2, &diff_params, &emit_params, &emit_cb);
+ if (ctx.repo->diff_filter)
+ cgit_close_filter(ctx.repo->diff_filter);
  if (file1.size)
  free(file1.ptr);
  if (file2.size)
-- 
2.30.2