Show More
@@ -168,34 +168,44 b' default_encoding = utf8' | |||||
168 | ## issue tracker for Kallithea (leave blank to disable, absent for default) |
|
168 | ## issue tracker for Kallithea (leave blank to disable, absent for default) | |
169 | #bugtracker = https://bitbucket.org/conservancy/kallithea/issues |
|
169 | #bugtracker = https://bitbucket.org/conservancy/kallithea/issues | |
170 |
|
170 | |||
171 |
## issue tracking mapping for commit |
|
171 | ## issue tracking mapping for commit messages, comments, PR descriptions, ... | |
172 | ## comment out issue_pat, issue_server, issue_prefix to enable |
|
172 | ## Refer to the documentation ("Integration with issue trackers") for more details. | |
173 |
|
173 | |||
174 | ## pattern to get the issues from commit messages |
|
174 | ## regular expression to match issue references | |
175 | ## default one used here is #<numbers> with a regex passive group for `#` |
|
175 | ## This pattern may/should contain parenthesized groups, that can | |
176 | ## {id} will be all groups matched from this pattern |
|
176 | ## be referred to in issue_server_link or issue_sub using Python backreferences | |
|
177 | ## (e.g. \1, \2, ...). You can also create named groups with '(?P<groupname>)'. | |||
|
178 | ## To require mandatory whitespace before the issue pattern, use: | |||
|
179 | ## (?:^|(?<=\s)) before the actual pattern, and for mandatory whitespace | |||
|
180 | ## behind the issue pattern, use (?:$|(?=\s)) after the actual pattern. | |||
177 |
|
181 | |||
178 | issue_pat = #(\d+) |
|
182 | issue_pat = #(\d+) | |
179 |
|
183 | |||
180 | ## server url to the issue, each {id} will be replaced with match |
|
184 | ## server url to the issue | |
181 | ## fetched from the regex and {repo} is replaced with full repository name |
|
185 | ## This pattern may/should contain backreferences to parenthesized groups in issue_pat. | |
182 | ## including groups {repo_name} is replaced with just name of repo |
|
186 | ## A backreference can be \1, \2, ... or \g<groupname> if you specified a named group | |
183 |
|
187 | ## called 'groupname' in issue_pat. | ||
184 | issue_server_link = https://issues.example.com/{repo}/issue/{id} |
|
188 | ## The special token {repo} is replaced with the full repository name | |
|
189 | ## including repository groups, while {repo_name} is replaced with just | |||
|
190 | ## the name of the repository. | |||
185 |
|
191 | |||
186 | ## prefix to add to link to indicate it's an url |
|
192 | issue_server_link = https://issues.example.com/{repo}/issue/\1 | |
187 | ## #314 will be replaced by <issue_prefix><id> |
|
|||
188 |
|
193 | |||
189 | issue_prefix = # |
|
194 | ## substitution pattern to use as the link text | |
|
195 | ## If issue_sub is empty, the text matched by issue_pat is retained verbatim | |||
|
196 | ## for the link text. Otherwise, the link text is that of issue_sub, with any | |||
|
197 | ## backreferences to groups in issue_pat replaced. | |||
190 |
|
198 | |||
191 | ## issue_pat, issue_server_link, issue_prefix can have suffixes to specify |
|
199 | issue_sub = | |
|
200 | ||||
|
201 | ## issue_pat, issue_server_link and issue_sub can have suffixes to specify | |||
192 | ## multiple patterns, to other issues server, wiki or others |
|
202 | ## multiple patterns, to other issues server, wiki or others | |
193 | ## below an example how to create a wiki pattern |
|
203 | ## below an example how to create a wiki pattern | |
194 | # wiki-some-id -> https://wiki.example.com/some-id |
|
204 | # wiki-some-id -> https://wiki.example.com/some-id | |
195 |
|
205 | |||
196 |
#issue_pat_wiki = |
|
206 | #issue_pat_wiki = wiki-(\S+) | |
197 |
#issue_server_link_wiki = https://wiki.example.com/ |
|
207 | #issue_server_link_wiki = https://wiki.example.com/\1 | |
198 |
#issue_ |
|
208 | #issue_sub_wiki = WIKI-\1 | |
199 |
|
209 | |||
200 | ## alternative return HTTP header for failed authentication. Default HTTP |
|
210 | ## alternative return HTTP header for failed authentication. Default HTTP | |
201 | ## response is 401 HTTPUnauthorized. Currently Mercurial clients have trouble with |
|
211 | ## response is 401 HTTPUnauthorized. Currently Mercurial clients have trouble with |
@@ -535,36 +535,69 b' Integration with issue trackers' | |||||
535 |
|
535 | |||
536 | Kallithea provides a simple integration with issue trackers. It's possible |
|
536 | Kallithea provides a simple integration with issue trackers. It's possible | |
537 | to define a regular expression that will match an issue ID in commit messages, |
|
537 | to define a regular expression that will match an issue ID in commit messages, | |
538 |
and have that replaced with a URL to the issue. |
|
538 | and have that replaced with a URL to the issue. | |
539 | uncomment the following variables in the ini file:: |
|
539 | ||
|
540 | This is achieved with following three variables in the ini file:: | |||
540 |
|
541 | |||
541 |
issue_pat = |
|
542 | issue_pat = #(\d+) | |
542 |
issue_server_link = https://issues.example.com/{repo}/issue/ |
|
543 | issue_server_link = https://issues.example.com/{repo}/issue/\1 | |
543 |
issue_ |
|
544 | issue_sub = | |
544 |
|
545 | |||
545 | ``issue_pat`` is the regular expression describing which strings in |
|
546 | ``issue_pat`` is the regular expression describing which strings in | |
546 |
commit messages will be treated as issue references. |
|
547 | commit messages will be treated as issue references. The expression can/should | |
547 | parentheses should be used to specify the actual issue id. |
|
548 | have one or more parenthesized groups that can later be referred to in | |
|
549 | ``issue_server_link`` and ``issue_sub`` (see below). If you prefer, named groups | |||
|
550 | can be used instead of simple parenthesized groups. | |||
548 |
|
551 | |||
549 | The default expression matches issues in the format ``#<number>``, e.g., ``#300``. |
|
552 | If the pattern should only match if it is preceded by whitespace, add the | |
|
553 | following string before the actual pattern: ``(?:^|(?<=\s))``. | |||
|
554 | If the pattern should only match if it is followed by whitespace, add the | |||
|
555 | following string after the actual pattern: ``(?:$|(?=\s))``. | |||
|
556 | These expressions use lookbehind and lookahead assertions of the Python regular | |||
|
557 | expression module to avoid the whitespace to be part of the actual pattern, | |||
|
558 | otherwise the link text will also contain that whitespace. | |||
550 |
|
559 | |||
551 | Matched issue references are replaced with the link specified in |
|
560 | Matched issue references are replaced with the link specified in | |
552 | ``issue_server_link``. ``{id}`` is replaced with the issue ID, and |
|
561 | ``issue_server_link``, in which any backreferences are resolved. Backreferences | |
553 | ``{repo}`` with the repository name. Since the # is stripped away, |
|
562 | can be ``\1``, ``\2``, ... or for named groups ``\g<groupname>``. | |
554 | ``issue_prefix`` is prepended to the link text. ``issue_prefix`` doesn't |
|
563 | The special token ``{repo}`` is replaced with the full repository path | |
555 | necessarily need to be ``#``: if you set issue prefix to ``ISSUE-`` this will |
|
564 | (including repository groups), while token ``{repo_name}`` is replaced with the | |
556 | generate a URL in the format: |
|
565 | repository name (without repository groups). | |
|
566 | ||||
|
567 | The link text is determined by ``issue_sub``, which can be a string containing | |||
|
568 | backreferences to the groups specified in ``issue_pat``. If ``issue_sub`` is | |||
|
569 | empty, then the text matched by ``issue_pat`` is used verbatim. | |||
|
570 | ||||
|
571 | The example settings shown above match issues in the format ``#<number>``. | |||
|
572 | This will cause the text ``#300`` to be transformed into a link: | |||
557 |
|
573 | |||
558 | .. code-block:: html |
|
574 | .. code-block:: html | |
559 |
|
575 | |||
560 |
<a href="https://issues.example.com/example_repo/issue/300"> |
|
576 | <a href="https://issues.example.com/example_repo/issue/300">#300</a> | |
|
577 | ||||
|
578 | The following example transforms a text starting with either of 'pullrequest', | |||
|
579 | 'pull request' or 'PR', followed by an optional space, then a pound character | |||
|
580 | (#) and one or more digits, into a link with the text 'PR #' followed by the | |||
|
581 | digits:: | |||
|
582 | ||||
|
583 | issue_pat = (pullrequest|pull request|PR) ?#(\d+) | |||
|
584 | issue_server_link = https://issues.example.com/\2 | |||
|
585 | issue_sub = PR #\2 | |||
|
586 | ||||
|
587 | The following example demonstrates how to require whitespace before the issue | |||
|
588 | reference in order for it to be recognized, such that the text ``issue#123`` will | |||
|
589 | not cause a match, but ``issue #123`` will:: | |||
|
590 | ||||
|
591 | issue_pat = (?:^|(?<=\s))#(\d+) | |||
|
592 | issue_server_link = https://issues.example.com/\1 | |||
|
593 | issue_sub = | |||
561 |
|
594 | |||
562 | If needed, more than one pattern can be specified by appending a unique suffix to |
|
595 | If needed, more than one pattern can be specified by appending a unique suffix to | |
563 | the variables. For example:: |
|
596 | the variables. For example, also demonstrating the use of named groups:: | |
564 |
|
597 | |||
565 |
issue_pat_wiki = |
|
598 | issue_pat_wiki = wiki-(?P<pagename>\S+) | |
566 |
issue_server_link_wiki = https://wiki.example.com/ |
|
599 | issue_server_link_wiki = https://wiki.example.com/\g<pagename> | |
567 |
issue_ |
|
600 | issue_sub_wiki = WIKI-\g<pagename> | |
568 |
|
601 | |||
569 | With these settings, wiki pages can be referenced as wiki-some-id, and every |
|
602 | With these settings, wiki pages can be referenced as wiki-some-id, and every | |
570 | such reference will be transformed into: |
|
603 | such reference will be transformed into: | |
@@ -573,6 +606,9 b' such reference will be transformed into:' | |||||
573 |
|
606 | |||
574 | <a href="https://wiki.example.com/some-id">WIKI-some-id</a> |
|
607 | <a href="https://wiki.example.com/some-id">WIKI-some-id</a> | |
575 |
|
608 | |||
|
609 | Refer to the `Python regular expression documentation`_ for more details about | |||
|
610 | the supported syntax in ``issue_pat``, ``issue_server_link`` and ``issue_sub``. | |||
|
611 | ||||
576 |
|
612 | |||
577 | Hook management |
|
613 | Hook management | |
578 | --------------- |
|
614 | --------------- | |
@@ -901,6 +937,7 b' the ``init.d`` directory of the Kallithe' | |||||
901 |
|
937 | |||
902 | .. _virtualenv: http://pypi.python.org/pypi/virtualenv |
|
938 | .. _virtualenv: http://pypi.python.org/pypi/virtualenv | |
903 | .. _python: http://www.python.org/ |
|
939 | .. _python: http://www.python.org/ | |
|
940 | .. _Python regular expression documentation: https://docs.python.org/2/library/re.html | |||
904 | .. _Mercurial: https://www.mercurial-scm.org/ |
|
941 | .. _Mercurial: https://www.mercurial-scm.org/ | |
905 | .. _Celery: http://celeryproject.org/ |
|
942 | .. _Celery: http://celeryproject.org/ | |
906 | .. _Celery documentation: http://docs.celeryproject.org/en/latest/getting-started/index.html |
|
943 | .. _Celery documentation: http://docs.celeryproject.org/en/latest/getting-started/index.html |
@@ -1140,35 +1140,46 b' def urlify_issues(newtext, repo_name):' | |||||
1140 | suffix = m.group(1) |
|
1140 | suffix = m.group(1) | |
1141 | issue_pat = CONFIG.get(k) |
|
1141 | issue_pat = CONFIG.get(k) | |
1142 | issue_server_link = CONFIG.get('issue_server_link%s' % suffix) |
|
1142 | issue_server_link = CONFIG.get('issue_server_link%s' % suffix) | |
1143 |
issue_ |
|
1143 | issue_sub = CONFIG.get('issue_sub%s' % suffix) | |
1144 |
if not issue_pat or not issue_server_link or issue_ |
|
1144 | if not issue_pat or not issue_server_link or issue_sub is None: # issue_sub can be empty but should be present | |
1145 |
log.error('skipping incomplete issue pattern %r: %r -> %r %r', suffix, issue_pat, issue_server_link, issue_ |
|
1145 | log.error('skipping incomplete issue pattern %r: %r -> %r %r', suffix, issue_pat, issue_server_link, issue_sub) | |
1146 | continue |
|
1146 | continue | |
1147 |
|
1147 | |||
1148 | # Wrap tmp_urlify_issues_f with substitution of this pattern, while making sure all loop variables (and compiled regexpes) are bound |
|
1148 | # Wrap tmp_urlify_issues_f with substitution of this pattern, while making sure all loop variables (and compiled regexpes) are bound | |
1149 | try: |
|
1149 | try: | |
1150 | issue_re = re.compile(issue_pat) |
|
1150 | issue_re = re.compile(issue_pat) | |
1151 | except re.error as e: |
|
1151 | except re.error as e: | |
1152 |
log.error('skipping invalid issue pattern %r: %r -> %r %r. Error: %s', suffix, issue_pat, issue_server_link, issue_ |
|
1152 | log.error('skipping invalid issue pattern %r: %r -> %r %r. Error: %s', suffix, issue_pat, issue_server_link, issue_sub, str(e)) | |
1153 | continue |
|
1153 | continue | |
1154 |
|
1154 | |||
1155 |
log.debug('issue pattern %r: %r -> %r %r', suffix, issue_pat, issue_server_link, issue_ |
|
1155 | log.debug('issue pattern %r: %r -> %r %r', suffix, issue_pat, issue_server_link, issue_sub) | |
1156 |
|
1156 | |||
1157 | def issues_replace(match_obj, |
|
1157 | def issues_replace(match_obj, | |
1158 |
issue_server_link=issue_server_link, issue_ |
|
1158 | issue_server_link=issue_server_link, issue_sub=issue_sub): | |
1159 | issue_id = ''.join(match_obj.groups()) |
|
1159 | try: | |
1160 |
issue_url = issue_server_link |
|
1160 | issue_url = match_obj.expand(issue_server_link) | |
|
1161 | except (IndexError, re.error) as e: | |||
|
1162 | log.error('invalid issue_url setting %r -> %r %r. Error: %s', issue_pat, issue_server_link, issue_sub, str(e)) | |||
|
1163 | issue_url = issue_server_link | |||
1161 | issue_url = issue_url.replace('{repo}', repo_name) |
|
1164 | issue_url = issue_url.replace('{repo}', repo_name) | |
1162 | issue_url = issue_url.replace('{repo_name}', repo_name.split(URL_SEP)[-1]) |
|
1165 | issue_url = issue_url.replace('{repo_name}', repo_name.split(URL_SEP)[-1]) | |
|
1166 | # if issue_sub is empty use the matched issue reference verbatim | |||
|
1167 | if not issue_sub: | |||
|
1168 | issue_text = match_obj.group() | |||
|
1169 | else: | |||
|
1170 | try: | |||
|
1171 | issue_text = match_obj.expand(issue_sub) | |||
|
1172 | except (IndexError, re.error) as e: | |||
|
1173 | log.error('invalid issue_sub setting %r -> %r %r. Error: %s', issue_pat, issue_server_link, issue_sub, str(e)) | |||
|
1174 | issue_text = match_obj.group() | |||
|
1175 | ||||
1163 | return ( |
|
1176 | return ( | |
1164 | '<a class="issue-tracker-link" href="%(url)s">' |
|
1177 | '<a class="issue-tracker-link" href="%(url)s">' | |
1165 |
'%( |
|
1178 | '%(text)s' | |
1166 | '</a>' |
|
1179 | '</a>' | |
1167 | ) % { |
|
1180 | ) % { | |
1168 | 'url': issue_url, |
|
1181 | 'url': issue_url, | |
1169 |
' |
|
1182 | 'text': issue_text, | |
1170 | 'issue-prefix': issue_prefix, |
|
|||
1171 | 'serv': issue_server_link, |
|
|||
1172 | } |
|
1183 | } | |
1173 | tmp_urlify_issues_f = (lambda s, |
|
1184 | tmp_urlify_issues_f = (lambda s, | |
1174 | issue_re=issue_re, issues_replace=issues_replace, chain_f=tmp_urlify_issues_f: |
|
1185 | issue_re=issue_re, issues_replace=issues_replace, chain_f=tmp_urlify_issues_f: |
@@ -261,37 +261,44 b' default_encoding = utf8' | |||||
261 | <%text>## issue tracker for Kallithea (leave blank to disable, absent for default)</%text> |
|
261 | <%text>## issue tracker for Kallithea (leave blank to disable, absent for default)</%text> | |
262 | #bugtracker = https://bitbucket.org/conservancy/kallithea/issues |
|
262 | #bugtracker = https://bitbucket.org/conservancy/kallithea/issues | |
263 |
|
263 | |||
264 |
<%text>## issue tracking mapping for commit |
|
264 | <%text>## issue tracking mapping for commit messages, comments, PR descriptions, ...</%text> | |
265 | <%text>## comment out issue_pat, issue_server, issue_prefix to enable</%text> |
|
265 | <%text>## Refer to the documentation ("Integration with issue trackers") for more details.</%text> | |
266 |
|
266 | |||
267 |
<%text>## |
|
267 | <%text>## regular expression to match issue references</%text> | |
268 | <%text>## default one used here is #<numbers> with a regex passive group for `#`</%text> |
|
268 | <%text>## This pattern may/should contain parenthesized groups, that can</%text> | |
269 | <%text>## {id} will be all groups matched from this pattern</%text> |
|
269 | <%text>## be referred to in issue_server_link or issue_sub using Python backreferences</%text> | |
|
270 | <%text>## (e.g. \1, \2, ...). You can also create named groups with '(?P<groupname>)'.</%text> | |||
270 | <%text>## To require mandatory whitespace before the issue pattern, use:</%text> |
|
271 | <%text>## To require mandatory whitespace before the issue pattern, use:</%text> | |
271 | <%text>## (?:^|(?<=\s)) before the actual pattern, and for mandatory whitespace</%text> |
|
272 | <%text>## (?:^|(?<=\s)) before the actual pattern, and for mandatory whitespace</%text> | |
272 | <%text>## behind the issue pattern, use (?:$|(?=\s)) after the actual pattern</%text> |
|
273 | <%text>## behind the issue pattern, use (?:$|(?=\s)) after the actual pattern.</%text> | |
273 |
|
274 | |||
274 | issue_pat = #(\d+) |
|
275 | issue_pat = #(\d+) | |
275 |
|
276 | |||
276 |
<%text>## server url to the issue |
|
277 | <%text>## server url to the issue</%text> | |
277 | <%text>## fetched from the regex and {repo} is replaced with full repository name</%text> |
|
278 | <%text>## This pattern may/should contain backreferences to parenthesized groups in issue_pat.</%text> | |
278 | <%text>## including groups {repo_name} is replaced with just name of repo</%text> |
|
279 | <%text>## A backreference can be \1, \2, ... or \g<groupname> if you specified a named group</%text> | |
279 |
|
280 | <%text>## called 'groupname' in issue_pat.</%text> | ||
280 | issue_server_link = https://issues.example.com/{repo}/issue/{id} |
|
281 | <%text>## The special token {repo} is replaced with the full repository name</%text> | |
|
282 | <%text>## including repository groups, while {repo_name} is replaced with just</%text> | |||
|
283 | <%text>## the name of the repository.</%text> | |||
281 |
|
284 | |||
282 | <%text>## prefix to add to link to indicate it's an url</%text> |
|
285 | issue_server_link = https://issues.example.com/{repo}/issue/\1 | |
283 | <%text>## #314 will be replaced by <issue_prefix><id></%text> |
|
|||
284 |
|
|
286 | ||
285 | issue_prefix = # |
|
287 | <%text>## substitution pattern to use as the link text</%text> | |
|
288 | <%text>## If issue_sub is empty, the text matched by issue_pat is retained verbatim</%text> | |||
|
289 | <%text>## for the link text. Otherwise, the link text is that of issue_sub, with any</%text> | |||
|
290 | <%text>## backreferences to groups in issue_pat replaced.</%text> | |||
286 |
|
291 | |||
287 | <%text>## issue_pat, issue_server_link, issue_prefix can have suffixes to specify</%text> |
|
292 | issue_sub = | |
|
293 | ||||
|
294 | <%text>## issue_pat, issue_server_link and issue_sub can have suffixes to specify</%text> | |||
288 | <%text>## multiple patterns, to other issues server, wiki or others</%text> |
|
295 | <%text>## multiple patterns, to other issues server, wiki or others</%text> | |
289 | <%text>## below an example how to create a wiki pattern</%text> |
|
296 | <%text>## below an example how to create a wiki pattern</%text> | |
290 | # wiki-some-id -> https://wiki.example.com/some-id |
|
297 | # wiki-some-id -> https://wiki.example.com/some-id | |
291 |
|
298 | |||
292 |
#issue_pat_wiki = |
|
299 | #issue_pat_wiki = wiki-(\S+) | |
293 |
#issue_server_link_wiki = https://wiki.example.com/ |
|
300 | #issue_server_link_wiki = https://wiki.example.com/\1 | |
294 |
#issue_ |
|
301 | #issue_sub_wiki = WIKI-\1 | |
295 |
|
302 | |||
296 | <%text>## alternative return HTTP header for failed authentication. Default HTTP</%text> |
|
303 | <%text>## alternative return HTTP header for failed authentication. Default HTTP</%text> | |
297 | <%text>## response is 401 HTTPUnauthorized. Currently Mercurial clients have trouble with</%text> |
|
304 | <%text>## response is 401 HTTPUnauthorized. Currently Mercurial clients have trouble with</%text> |
@@ -406,84 +406,91 b' class TestLibs(TestController):' | |||||
406 | from kallithea.lib.helpers import urlify_text |
|
406 | from kallithea.lib.helpers import urlify_text | |
407 | assert urlify_text(sample, 'repo_name', link_='#the-link') == expected |
|
407 | assert urlify_text(sample, 'repo_name', link_='#the-link') == expected | |
408 |
|
408 | |||
409 |
@parametrize('issue_pat,issue_server,issue_ |
|
409 | @parametrize('issue_pat,issue_server,issue_sub,sample,expected', [ | |
410 |
(r'#(\d+)', 'http://foo/{repo}/issue/ |
|
410 | (r'#(\d+)', 'http://foo/{repo}/issue/\\1', '#\\1', | |
411 | 'issue #123 and issue#456', |
|
411 | 'issue #123 and issue#456', | |
412 | """issue <a class="issue-tracker-link" href="http://foo/repo_name/issue/123">#123</a> and """ |
|
412 | """issue <a class="issue-tracker-link" href="http://foo/repo_name/issue/123">#123</a> and """ | |
413 | """issue<a class="issue-tracker-link" href="http://foo/repo_name/issue/456">#456</a>"""), |
|
413 | """issue<a class="issue-tracker-link" href="http://foo/repo_name/issue/456">#456</a>"""), | |
414 | # following test case shows the result of a backward incompatible change that was made: the |
|
414 | (r'(?:\s*#)(\d+)', 'http://foo/{repo}/issue/\\1', '#\\1', | |
415 | # space between 'issue' and '#123' is removed, because the space is part of the pattern. |
|
|||
416 | (r'(?:\s*#)(\d+)', 'http://foo/{repo}/issue/{id}', '#', |
|
|||
417 | 'issue #123 and issue#456', |
|
415 | 'issue #123 and issue#456', | |
418 | """issue<a class="issue-tracker-link" href="http://foo/repo_name/issue/123">#123</a> and """ |
|
416 | """issue<a class="issue-tracker-link" href="http://foo/repo_name/issue/123">#123</a> and """ | |
419 | """issue<a class="issue-tracker-link" href="http://foo/repo_name/issue/456">#456</a>"""), |
|
417 | """issue<a class="issue-tracker-link" href="http://foo/repo_name/issue/456">#456</a>"""), | |
420 | # to require whitespace before the issue reference, one may be tempted to use \b... |
|
418 | # to require whitespace before the issue reference, one may be tempted to use \b... | |
421 |
(r'\bPR(\d+)', 'http://foo/{repo}/issue/ |
|
419 | (r'\bPR(\d+)', 'http://foo/{repo}/issue/\\1', '#\\1', | |
422 | 'issue PR123 and issuePR456', |
|
420 | 'issue PR123 and issuePR456', | |
423 | """issue <a class="issue-tracker-link" href="http://foo/repo_name/issue/123">#123</a> and """ |
|
421 | """issue <a class="issue-tracker-link" href="http://foo/repo_name/issue/123">#123</a> and """ | |
424 | """issuePR456"""), |
|
422 | """issuePR456"""), | |
425 | # ... but it turns out that \b does not work well in combination with '#': the expectations |
|
423 | # ... but it turns out that \b does not work well in combination with '#': the expectations | |
426 | # are reversed from what is actually happening. |
|
424 | # are reversed from what is actually happening. | |
427 |
(r'\b#(\d+)', 'http://foo/{repo}/issue/ |
|
425 | (r'\b#(\d+)', 'http://foo/{repo}/issue/\\1', '#\\1', | |
428 | 'issue #123 and issue#456', |
|
426 | 'issue #123 and issue#456', | |
429 | """issue #123 and """ |
|
427 | """issue #123 and """ | |
430 | """issue<a class="issue-tracker-link" href="http://foo/repo_name/issue/456">#456</a>"""), |
|
428 | """issue<a class="issue-tracker-link" href="http://foo/repo_name/issue/456">#456</a>"""), | |
431 | # ... so maybe try to be explicit? Unfortunately the whitespace before the issue |
|
429 | # ... so maybe try to be explicit? Unfortunately the whitespace before the issue | |
432 | # reference is not retained, again, because it is part of the pattern. |
|
430 | # reference is not retained, again, because it is part of the pattern. | |
433 |
(r'(?:^|\s)#(\d+)', 'http://foo/{repo}/issue/ |
|
431 | (r'(?:^|\s)#(\d+)', 'http://foo/{repo}/issue/\\1', '#\\1', | |
434 | '#15 and issue #123 and issue#456', |
|
432 | '#15 and issue #123 and issue#456', | |
435 | """<a class="issue-tracker-link" href="http://foo/repo_name/issue/15">#15</a> and """ |
|
433 | """<a class="issue-tracker-link" href="http://foo/repo_name/issue/15">#15</a> and """ | |
436 | """issue<a class="issue-tracker-link" href="http://foo/repo_name/issue/123">#123</a> and """ |
|
434 | """issue<a class="issue-tracker-link" href="http://foo/repo_name/issue/123">#123</a> and """ | |
437 | """issue#456"""), |
|
435 | """issue#456"""), | |
438 | # ... instead, use lookbehind assertions. |
|
436 | # ... instead, use lookbehind assertions. | |
439 |
(r'(?:^|(?<=\s))#(\d+)', 'http://foo/{repo}/issue/ |
|
437 | (r'(?:^|(?<=\s))#(\d+)', 'http://foo/{repo}/issue/\\1', '#\\1', | |
440 | '#15 and issue #123 and issue#456', |
|
438 | '#15 and issue #123 and issue#456', | |
441 | """<a class="issue-tracker-link" href="http://foo/repo_name/issue/15">#15</a> and """ |
|
439 | """<a class="issue-tracker-link" href="http://foo/repo_name/issue/15">#15</a> and """ | |
442 | """issue <a class="issue-tracker-link" href="http://foo/repo_name/issue/123">#123</a> and """ |
|
440 | """issue <a class="issue-tracker-link" href="http://foo/repo_name/issue/123">#123</a> and """ | |
443 | """issue#456"""), |
|
441 | """issue#456"""), | |
444 |
(r'(?:pullrequest|pull request|PR|pr) ?#?(\d+)', 'http://foo/{repo}/issue/ |
|
442 | (r'(?:pullrequest|pull request|PR|pr) ?#?(\d+)', 'http://foo/{repo}/issue/\\1', 'PR#\\1', | |
445 | 'fixed with pullrequest #1, pull request#2, PR 3, pr4', |
|
443 | 'fixed with pullrequest #1, pull request#2, PR 3, pr4', | |
446 | """fixed with <a class="issue-tracker-link" href="http://foo/repo_name/issue/1">PR#1</a>, """ |
|
444 | """fixed with <a class="issue-tracker-link" href="http://foo/repo_name/issue/1">PR#1</a>, """ | |
447 | """<a class="issue-tracker-link" href="http://foo/repo_name/issue/2">PR#2</a>, """ |
|
445 | """<a class="issue-tracker-link" href="http://foo/repo_name/issue/2">PR#2</a>, """ | |
448 | """<a class="issue-tracker-link" href="http://foo/repo_name/issue/3">PR#3</a>, """ |
|
446 | """<a class="issue-tracker-link" href="http://foo/repo_name/issue/3">PR#3</a>, """ | |
449 | """<a class="issue-tracker-link" href="http://foo/repo_name/issue/4">PR#4</a>"""), |
|
447 | """<a class="issue-tracker-link" href="http://foo/repo_name/issue/4">PR#4</a>"""), | |
450 |
(r'#(\d+)', 'http://foo/{repo}/issue/ |
|
448 | (r'#(\d+)', 'http://foo/{repo}/issue/\\1', 'PR\\1', | |
451 | 'interesting issue #123', |
|
449 | 'interesting issue #123', | |
452 | """interesting issue <a class="issue-tracker-link" href="http://foo/repo_name/issue/123">PR123</a>"""), |
|
450 | """interesting issue <a class="issue-tracker-link" href="http://foo/repo_name/issue/123">PR123</a>"""), | |
453 |
(r'BUG\d{5}', 'https://bar/{repo}/ |
|
451 | (r'BUG\d{5}', 'https://bar/{repo}/\\1', '\\1', | |
454 |
'silly me, I did not parenthesize the |
|
452 | 'silly me, I did not parenthesize the id, BUG12345.', | |
455 |
"""silly me, I did not parenthesize the |
|
453 | """silly me, I did not parenthesize the id, <a class="issue-tracker-link" href="https://bar/repo_name/\\1">BUG12345</a>."""), | |
456 | (r'BUG(\d{5})', 'https://bar/{repo}/', 'BUG', |
|
454 | (r'BUG(\d{5})', 'https://bar/{repo}/', 'BUG\\1', | |
457 |
'silly me, the URL does not contain |
|
455 | 'silly me, the URL does not contain id, BUG12345.', | |
458 |
"""silly me, the URL does not contain |
|
456 | """silly me, the URL does not contain id, <a class="issue-tracker-link" href="https://bar/repo_name/">BUG12345</a>."""), | |
459 |
(r'(PR-\d+)', 'http://foo/{repo}/issue/ |
|
457 | (r'(PR-\d+)', 'http://foo/{repo}/issue/\\1', '', | |
460 | 'interesting issue #123, err PR-56', |
|
458 | 'interesting issue #123, err PR-56', | |
461 | """interesting issue #123, err <a class="issue-tracker-link" href="http://foo/repo_name/issue/PR-56">PR-56</a>"""), |
|
459 | """interesting issue #123, err <a class="issue-tracker-link" href="http://foo/repo_name/issue/PR-56">PR-56</a>"""), | |
462 |
(r'#(\d+)', 'http://foo/{repo}/issue/ |
|
460 | (r'#(\d+)', 'http://foo/{repo}/issue/\\1', '#\\1', | |
463 | "some 'standard' text with apostrophes", |
|
461 | "some 'standard' text with apostrophes", | |
464 | """some 'standard' text with apostrophes"""), |
|
462 | """some 'standard' text with apostrophes"""), | |
465 |
(r'#(\d+)', 'http://foo/{repo}/issue/ |
|
463 | (r'#(\d+)', 'http://foo/{repo}/issue/\\1', '#\\1', | |
466 | "some 'standard' issue #123", |
|
464 | "some 'standard' issue #123", | |
467 | """some 'standard' issue <a class="issue-tracker-link" href="http://foo/repo_name/issue/123">#123</a>"""), |
|
465 | """some 'standard' issue <a class="issue-tracker-link" href="http://foo/repo_name/issue/123">#123</a>"""), | |
468 |
(r'#(\d+)', 'http://foo/{repo}/issue/ |
|
466 | (r'#(\d+)', 'http://foo/{repo}/issue/\\1', '#\\1', | |
469 | 'an issue #123 with extra whitespace', |
|
467 | 'an issue #123 with extra whitespace', | |
470 | """an issue <a class="issue-tracker-link" href="http://foo/repo_name/issue/123">#123</a> with extra whitespace"""), |
|
468 | """an issue <a class="issue-tracker-link" href="http://foo/repo_name/issue/123">#123</a> with extra whitespace"""), | |
471 | # Note: whitespace is squashed |
|
469 | (r'(?:\s*#)(\d+)', 'http://foo/{repo}/issue/\\1', '#\\1', | |
472 | (r'(?:\s*#)(\d+)', 'http://foo/{repo}/issue/{id}', '#', |
|
|||
473 | 'an issue #123 with extra whitespace', |
|
470 | 'an issue #123 with extra whitespace', | |
474 |
"""an issue |
|
471 | """an issue<a class="issue-tracker-link" href="http://foo/repo_name/issue/123">#123</a> with extra whitespace"""), | |
475 | # invalid issue pattern |
|
472 | # invalid issue pattern | |
476 | (r'(PR\d+', 'http://foo/{repo}/issue/{id}', '', |
|
473 | (r'(PR\d+', 'http://foo/{repo}/issue/{id}', '', | |
477 | 'PR135', |
|
474 | 'PR135', | |
478 | """PR135"""), |
|
475 | """PR135"""), | |
|
476 | # other character than # | |||
|
477 | (r'(?:^|(?<=\s))\$(\d+)', 'http://foo/{repo}/issue/\\1', '', | |||
|
478 | 'empty issue_sub $123 and issue$456', | |||
|
479 | """empty issue_sub <a class="issue-tracker-link" href="http://foo/repo_name/issue/123">$123</a> and """ | |||
|
480 | """issue$456"""), | |||
|
481 | # named groups | |||
|
482 | (r'(PR|pullrequest|pull request) ?(?P<sitecode>BRU|CPH|BER)-(?P<id>\d+)', 'http://foo/\g<sitecode>/pullrequest/\g<id>/', 'PR-\g<sitecode>-\g<id>', | |||
|
483 | 'pullrequest CPH-789 is similar to PRBRU-747', | |||
|
484 | """<a class="issue-tracker-link" href="http://foo/CPH/pullrequest/789/">PR-CPH-789</a> is similar to """ | |||
|
485 | """<a class="issue-tracker-link" href="http://foo/BRU/pullrequest/747/">PR-BRU-747</a>"""), | |||
479 | ]) |
|
486 | ]) | |
480 |
def test_urlify_issues(self, issue_pat, issue_server, issue_ |
|
487 | def test_urlify_issues(self, issue_pat, issue_server, issue_sub, sample, expected): | |
481 | from kallithea.lib.helpers import urlify_text |
|
488 | from kallithea.lib.helpers import urlify_text | |
482 | config_stub = { |
|
489 | config_stub = { | |
483 | 'sqlalchemy.url': 'foo', |
|
490 | 'sqlalchemy.url': 'foo', | |
484 | 'issue_pat': issue_pat, |
|
491 | 'issue_pat': issue_pat, | |
485 | 'issue_server_link': issue_server, |
|
492 | 'issue_server_link': issue_server, | |
486 |
'issue_ |
|
493 | 'issue_sub': issue_sub, | |
487 | } |
|
494 | } | |
488 | # force recreation of lazy function |
|
495 | # force recreation of lazy function | |
489 | with mock.patch('kallithea.lib.helpers._urlify_issues_f', None): |
|
496 | with mock.patch('kallithea.lib.helpers._urlify_issues_f', None): | |
@@ -496,7 +503,7 b' class TestLibs(TestController):' | |||||
496 | ('pull request7 #', '<a class="issue-tracker-link" href="http://pr/repo_name/pr/7">PR#7</a> #'), |
|
503 | ('pull request7 #', '<a class="issue-tracker-link" href="http://pr/repo_name/pr/7">PR#7</a> #'), | |
497 | ('look PR9 and pr #11', 'look <a class="issue-tracker-link" href="http://pr/repo_name/pr/9">PR#9</a> and <a class="issue-tracker-link" href="http://pr/repo_name/pr/11">PR#11</a>'), |
|
504 | ('look PR9 and pr #11', 'look <a class="issue-tracker-link" href="http://pr/repo_name/pr/9">PR#9</a> and <a class="issue-tracker-link" href="http://pr/repo_name/pr/11">PR#11</a>'), | |
498 | ('pullrequest#10 solves issue 9', '<a class="issue-tracker-link" href="http://pr/repo_name/pr/10">PR#10</a> solves <a class="issue-tracker-link" href="http://bug/repo_name/bug/9">bug#9</a>'), |
|
505 | ('pullrequest#10 solves issue 9', '<a class="issue-tracker-link" href="http://pr/repo_name/pr/10">PR#10</a> solves <a class="issue-tracker-link" href="http://bug/repo_name/bug/9">bug#9</a>'), | |
499 | ('issue FAIL67', 'issue <a class="issue-tracker-link" href="http://fail/repo_name/67">67</a>'), |
|
506 | ('issue FAIL67', 'issue <a class="issue-tracker-link" href="http://fail/repo_name/67">FAIL67</a>'), | |
500 | ('issue FAILMORE89', 'issue FAILMORE89'), # no match because absent prefix |
|
507 | ('issue FAILMORE89', 'issue FAILMORE89'), # no match because absent prefix | |
501 | ]) |
|
508 | ]) | |
502 | def test_urlify_issues_multiple_issue_patterns(self, sample, expected): |
|
509 | def test_urlify_issues_multiple_issue_patterns(self, sample, expected): | |
@@ -504,19 +511,19 b' class TestLibs(TestController):' | |||||
504 | config_stub = { |
|
511 | config_stub = { | |
505 | 'sqlalchemy.url': 'foo', |
|
512 | 'sqlalchemy.url': 'foo', | |
506 | 'issue_pat': 'X(\d+)', |
|
513 | 'issue_pat': 'X(\d+)', | |
507 |
'issue_server_link': 'http://main/{repo}/main/ |
|
514 | 'issue_server_link': 'http://main/{repo}/main/\\1/', | |
508 |
'issue_ |
|
515 | 'issue_sub': '#\\1', | |
509 | 'issue_pat_pr': '(?:pullrequest|pull request|PR|pr) ?#?(\d+)', |
|
516 | 'issue_pat_pr': '(?:pullrequest|pull request|PR|pr) ?#?(\d+)', | |
510 |
'issue_server_link_pr': 'http://pr/{repo}/pr/ |
|
517 | 'issue_server_link_pr': 'http://pr/{repo}/pr/\\1', | |
511 |
'issue_ |
|
518 | 'issue_sub_pr': 'PR#\\1', | |
512 | 'issue_pat_bug': '(?:BUG|bug|issue) ?#?(\d+)', |
|
519 | 'issue_pat_bug': '(?:BUG|bug|issue) ?#?(\d+)', | |
513 |
'issue_server_link_bug': 'http://bug/{repo}/bug/ |
|
520 | 'issue_server_link_bug': 'http://bug/{repo}/bug/\\1', | |
514 |
'issue_ |
|
521 | 'issue_sub_bug': 'bug#\\1', | |
515 | 'issue_pat_empty_prefix': 'FAIL(\d+)', |
|
522 | 'issue_pat_empty_prefix': 'FAIL(\d+)', | |
516 |
'issue_server_link_empty_prefix': 'http://fail/{repo}/ |
|
523 | 'issue_server_link_empty_prefix': 'http://fail/{repo}/\\1', | |
517 |
'issue_ |
|
524 | 'issue_sub_empty_prefix': '', | |
518 | 'issue_pat_absent_prefix': 'FAILMORE(\d+)', |
|
525 | 'issue_pat_absent_prefix': 'FAILMORE(\d+)', | |
519 |
'issue_server_link_absent_prefix': 'http://failmore/{repo}/ |
|
526 | 'issue_server_link_absent_prefix': 'http://failmore/{repo}/\\1', | |
520 | } |
|
527 | } | |
521 | # force recreation of lazy function |
|
528 | # force recreation of lazy function | |
522 | with mock.patch('kallithea.lib.helpers._urlify_issues_f', None): |
|
529 | with mock.patch('kallithea.lib.helpers._urlify_issues_f', None): |
General Comments 0
You need to be logged in to leave comments.
Login now