Skip to content

Commit 82adc8e

Browse files
committed
ENH - link shortening support for codeberg, forgejo, gitea
Signed-off-by: Martin Matous <[email protected]>
1 parent b2c02e3 commit 82adc8e

File tree

10 files changed

+463
-6
lines changed

10 files changed

+463
-6
lines changed

docs/user_guide/theme-elements.md

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,37 @@ For example:
238238

239239
There are a variety of link targets supported, here's a table for reference:
240240

241+
**Codeberg**
242+
243+
- `https://codeberg.org`: https://codeberg.org
244+
- `https://codeberg.org/c-org`: https://codeberg.org/c-org
245+
- `https://codeberg.org/c-org/c-repo`: https://codeberg.org/c-org/c-repo
246+
- `https://codeberg.org/c-org/c-repo/issues/375583`: https://codeberg.org/c-org/c-repo/issues/375583
247+
- `https://codeberg.org/c-org/c-repo/pulls/1012`: https://codeberg.org/c-org/c-repo/pulls/1012
248+
249+
**Forgejo**
250+
251+
Since self-hosted Forgejo instances can't be easily identified, only links specified
252+
in `forgejo_url` (links to project's own forge) or domains containing "forgejo"
253+
leading to issues, pull requests or projects are shortened.
254+
255+
- `https://my-forgejo.org`: https://my-forgejo.org
256+
- `https://forgejo.my.org/forgejo-org/forgejo`: https://my-forgejo.org/forgejo-org/forgejo/pulls
257+
- `https://my-forgejo.com/forgejo-org/forgejo/issues/375583`: https://my-forgejo.org/forgejo-org/forgejo/issues/375583
258+
- `https://my.forgejo.org/forgejo-org/forgejo/pulls/1012`: https://my.forgejo.org/forgejo-org/forgejo/pulls/1012
259+
260+
**Gitea**
261+
262+
`https://gitea.com` is always identified, while the same rules in place for Forgejo
263+
apply to self-hosted instances.
264+
265+
- `https://gitea.com`: https://gitea.com
266+
- `https://my-gitea.com`: https://my-gitea.com
267+
- `https://gitea.com/gitea-org`: https://gitea.com/gitea-org
268+
- `https://gitea.com/gitea-org/gitea`: https://gitea.com/gitea-org/gitea
269+
- `https://my-gitea.com/gitea-org/gitea/issues/375583`: https://my-gitea.com/gitea-org/gitea/issues/375583
270+
- `https://gitea.my.com/gitea-org/gitea/pulls/1012`: https://gitea.my.com/gitea-org/gitea/pulls/1012
271+
241272
**GitHub**
242273

243274
- `https://github.com`: https://github.com

src/pydata_sphinx_theme/assets/styles/base/_base.scss

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,16 +46,48 @@ a {
4646
user-select: none;
4747
}
4848

49-
// set up a icon next to the shorten links from github and gitlab
49+
// set up an icon next to the shortened links from forges
50+
&.codeberg,
51+
&.forgejo,
52+
&.gitea,
5053
&.github,
5154
&.gitlab {
5255
&::before {
5356
color: var(--pst-color-text-muted);
54-
font: var(--fa-font-brands);
5557
margin-right: 0.25rem;
5658
}
5759
}
5860

61+
&.codeberg,
62+
&.forgejo,
63+
&.gitea {
64+
&::before {
65+
display: inline-block;
66+
vertical-align: -15%;
67+
width: calc(var(--pst-font-size-icon) - 0.5rem);
68+
height: calc(var(--pst-font-size-icon) - 0.5rem);
69+
}
70+
}
71+
72+
&.github,
73+
&.gitlab {
74+
&::before {
75+
font: var(--fa-font-brands);
76+
}
77+
}
78+
79+
&.codeberg::before {
80+
content: var(--pst-icon-codeberg);
81+
}
82+
83+
&.forgejo::before {
84+
content: var(--pst-icon-forgejo);
85+
}
86+
87+
&.gitea::before {
88+
content: var(--pst-icon-gitea);
89+
}
90+
5991
&.github::before {
6092
content: var(--pst-icon-github);
6193
}

src/pydata_sphinx_theme/assets/styles/variables/_icons.scss

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,17 @@ html {
2424
--pst-icon-bell: "\f0f3"; // fa-solid fa-bell
2525
--pst-icon-pencil: "\f303"; // fa-solid fa-pencil
2626

27+
// SVG icons
28+
// minified & encoded by SVGOMG 3.0.0 + https://yoksel.github.io/url-encoder/
29+
// https://codeberg.org/Codeberg/Design/src/commit/ac514aa9aaa2457d4af3c3e13df3ab136d22a49a/logo/icon/svg/codeberg-logo_icon_blue.svg
30+
--pst-icon-codeberg: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' viewBox='0 0 4.233 4.233'%3E%3Cdefs%3E%3ClinearGradient xlink:href='%23a' id='b' x1='42519.285' x2='42575.336' y1='-7078.789' y2='-6966.931' gradientUnits='userSpaceOnUse'/%3E%3ClinearGradient id='a'%3E%3Cstop offset='0' style='stop-color:%232185d0;stop-opacity:0'/%3E%3Cstop offset='.495' style='stop-color:%232185d0;stop-opacity:.30000001'/%3E%3Cstop offset='1' style='stop-color:%232185d0;stop-opacity:.30000001'/%3E%3C/linearGradient%3E%3C/defs%3E%3Cpath d='M42519.285-7078.79a.76.568 0 0 0-.738.675l33.586 125.888a87.182 87.182 0 0 0 39.381-33.763l-71.565-92.52a.76.568 0 0 0-.664-.28z' style='font-variation-settings:normal;opacity:1;vector-effect:none;fill:url(%23b);fill-opacity:1;stroke:none;stroke-width:3.67846;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:2;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:stroke markers fill;stop-color:%23000;stop-opacity:1' transform='translate(-1030.156 172.97) scale(.02428)'/%3E%3Cpath d='M11249.461-1883.696c-12.74 0-23.067 10.327-23.067 23.067 0 4.333 1.22 8.58 3.522 12.251l19.232-24.863c.138-.18.486-.18.624 0l19.233 24.864a23.068 23.068 0 0 0 3.523-12.252c0-12.74-10.327-23.067-23.067-23.067z' style='opacity:1;fill:%232185d0;fill-opacity:1;stroke-width:17.0055;paint-order:markers fill stroke;stop-color:%23000' transform='translate(-1030.156 172.97) scale(.09176)'/%3E%3C/svg%3E");
31+
32+
// https://codeberg.org/forgejo/forgejo/src/commit/0ce1f708202c966ea190dc1f91b0d527ceb09c11/assets/logo.svg
33+
--pst-icon-forgejo: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 212 212'%3E%3Cstyle%3Ecircle,path%7Bfill:none;stroke:%23000;stroke-width:15%7Dpath%7Bstroke-width:25%7D.orange%7Bstroke:%23f60%7D.red%7Bstroke:%23d40000%7D%3C/style%3E%3Cg transform='translate(6 6)'%3E%3Cpath d='M58 168V70a50 50 0 0 1 50-50h20' class='orange'/%3E%3Cpath d='M58 168v-30a50 50 0 0 1 50-50h20' class='red'/%3E%3Ccircle cx='142' cy='20' r='18' class='orange'/%3E%3Ccircle cx='142' cy='88' r='18' class='red'/%3E%3Ccircle cx='58' cy='180' r='18' class='red'/%3E%3C/g%3E%3C/svg%3E");
34+
35+
// https://gitea.com/gitea/design/src/commit/4f571b3c617d2330dc066c5f076f62ac84723dd2/logo/logo.svg
36+
--pst-icon-gitea: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 640 640'%3E%3Cpath fill='%23fff' d='m395.9 484.2-126.9-61c-12.5-6-17.9-21.2-11.8-33.8l61-126.9c6-12.5 21.2-17.9 33.8-11.8 17.2 8.3 27.1 13 27.1 13l-.1-109.2 16.7-.1.1 117.1s57.4 24.2 83.1 40.1c3.7 2.3 10.2 6.8 12.9 14.4 2.1 6.1 2 13.1-1 19.3l-61 126.9c-6.2 12.7-21.4 18.1-33.9 12z'/%3E%3Cg fill='%23609926'%3E%3Cpath d='M622.7 149.8c-4.1-4.1-9.6-4-9.6-4s-117.2 6.6-177.9 8c-13.3.3-26.5.6-39.6.7v117.2c-5.5-2.6-11.1-5.3-16.6-7.9 0-36.4-.1-109.2-.1-109.2-29 .4-89.2-2.2-89.2-2.2s-141.4-7.1-156.8-8.5c-9.8-.6-22.5-2.1-39 1.5-8.7 1.8-33.5 7.4-53.8 26.9C-4.9 212.4 6.6 276.2 8 285.8c1.7 11.7 6.9 44.2 31.7 72.5 45.8 56.1 144.4 54.8 144.4 54.8s12.1 28.9 30.6 55.5c25 33.1 50.7 58.9 75.7 62 63 0 188.9-.1 188.9-.1s12 .1 28.3-10.3c14-8.5 26.5-23.4 26.5-23.4S547 483 565 451.5c5.5-9.7 10.1-19.1 14.1-28 0 0 55.2-117.1 55.2-231.1-1.1-34.5-9.6-40.6-11.6-42.6zM125.6 353.9c-25.9-8.5-36.9-18.7-36.9-18.7S69.6 321.8 60 295.4c-16.5-44.2-1.4-71.2-1.4-71.2s8.4-22.5 38.5-30c13.8-3.7 31-3.1 31-3.1s7.1 59.4 15.7 94.2c7.2 29.2 24.8 77.7 24.8 77.7s-26.1-3.1-43-9.1zm300.3 107.6s-6.1 14.5-19.6 15.4c-5.8.4-10.3-1.2-10.3-1.2s-.3-.1-5.3-2.1l-112.9-55s-10.9-5.7-12.8-15.6c-2.2-8.1 2.7-18.1 2.7-18.1L322 273s4.8-9.7 12.2-13c.6-.3 2.3-1 4.5-1.5 8.1-2.1 18 2.8 18 2.8L467.4 315s12.6 5.7 15.3 16.2c1.9 7.4-.5 14-1.8 17.2-6.3 15.4-55 113.1-55 113.1z'/%3E%3Cpath d='M326.8 380.1c-8.2.1-15.4 5.8-17.3 13.8-1.9 8 2 16.3 9.1 20 7.7 4 17.5 1.8 22.7-5.4 5.1-7.1 4.3-16.9-1.8-23.1l24-49.1c1.5.1 3.7.2 6.2-.5 4.1-.9 7.1-3.6 7.1-3.6 4.2 1.8 8.6 3.8 13.2 6.1 4.8 2.4 9.3 4.9 13.4 7.3.9.5 1.8 1.1 2.8 1.9 1.6 1.3 3.4 3.1 4.7 5.5 1.9 5.5-1.9 14.9-1.9 14.9-2.3 7.6-18.4 40.6-18.4 40.6-8.1-.2-15.3 5-17.7 12.5-2.6 8.1 1.1 17.3 8.9 21.3 7.8 4 17.4 1.7 22.5-5.3 5-6.8 4.6-16.3-1.1-22.6 1.9-3.7 3.7-7.4 5.6-11.3 5-10.4 13.5-30.4 13.5-30.4.9-1.7 5.7-10.3 2.7-21.3-2.5-11.4-12.6-16.7-12.6-16.7-12.2-7.9-29.2-15.2-29.2-15.2s0-4.1-1.1-7.1c-1.1-3.1-2.8-5.1-3.9-6.3 4.7-9.7 9.4-19.3 14.1-29-4.1-2-8.1-4-12.2-6.1-4.8 9.8-9.7 19.7-14.5 29.5-6.7-.1-12.9 3.5-16.1 9.4-3.4 6.3-2.7 14.1 1.9 19.8l-24.6 50.4z'/%3E%3C/g%3E%3C/svg%3E");
37+
2738
// Bootstrap icons
2839
--pst-breadcrumb-divider: "\f105";
2940
}

src/pydata_sphinx_theme/short_link.py

Lines changed: 60 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,20 @@
11
"""A custom Transform object to shorten github and gitlab links."""
22

3-
from typing import ClassVar
3+
from typing import ClassVar, Literal
44
from urllib.parse import ParseResult, urlparse, urlunparse
55

66
from docutils import nodes
77
from sphinx.transforms.post_transforms import SphinxPostTransform
88
from sphinx.util.nodes import NodeMatcher
99

10-
from .utils import traverse_or_findall
10+
from .utils import get_theme_options_dict, traverse_or_findall
1111

1212

1313
class ShortenLinkTransform(SphinxPostTransform):
1414
"""
15-
Shorten link when they are coming from github or gitlab and add an extra class to
16-
the tag for further styling.
15+
Shorten links leading to supported forges.
16+
Also attempt to identify self-hosted forge instances.
17+
Add an extra class to the tag for further styling.
1718
1819
Before:
1920
.. code-block:: html
@@ -35,6 +36,8 @@ class ShortenLinkTransform(SphinxPostTransform):
3536
default_priority = 400
3637
formats = ("html",)
3738
supported_platform: ClassVar[dict[str, str]] = {
39+
"codeberg.org": "codeberg",
40+
"gitea.com": "gitea",
3841
"github.com": "github",
3942
"gitlab.com": "gitlab",
4043
}
@@ -53,10 +56,54 @@ def run(self, **kwargs):
5356
uri = urlparse(uri)
5457
# only do something if the platform is identified
5558
self.platform = self.supported_platform.get(uri.netloc)
59+
# or we can make a reasonable guess about self-hosted forges
60+
if self.platform is None:
61+
html_theme_options = get_theme_options_dict(self.app)
62+
self.platform = self.identify_selfhosted(uri, html_theme_options)
5663
if self.platform is not None:
5764
node.attributes["classes"].append(self.platform)
5865
node.children[0] = nodes.Text(self.parse_url(uri))
5966

67+
def identify_selfhosted(
68+
self, uri: ParseResult, html_theme_options: dict[str, str]
69+
) -> Literal["forgejo", "gitea", "gitlab"] | None:
70+
"""Try to identify what self-hosted forge uri leads to (if any).
71+
72+
Args:
73+
uri: the link to the platform content
74+
html_theme_options: varia
75+
76+
Returns:
77+
likely platform if one matches, None otherwise
78+
"""
79+
# forge name in authority and known url part in the right place
80+
# unreliable but may catch any number of hosts
81+
path_parts = uri.path.strip("/").split("/")
82+
if len(path_parts) > 2 and path_parts[2] in ("pulls", "issues", "projects"):
83+
if "forgejo" in uri.netloc:
84+
return "forgejo"
85+
elif "gitea" in uri.netloc:
86+
return "gitea"
87+
if (
88+
len(path_parts) > 3
89+
and path_parts[2] == "-"
90+
and path_parts[3] in ("issues", "merge_requests")
91+
):
92+
if "gitlab" in uri.netloc:
93+
return "gitlab"
94+
95+
# url passed in *_url option
96+
# will only match project's own forge but that's
97+
# likely where most doc links will lead anyway
98+
str_url = f"{uri.scheme}://{uri.netloc}"
99+
selfhosted = ("forgejo", "gitea", "gitlab")
100+
for forge in selfhosted:
101+
known_url = html_theme_options.get(f"{forge}_url")
102+
if known_url and known_url.startswith(str_url):
103+
return forge
104+
105+
return None
106+
60107
def parse_url(self, uri: ParseResult) -> str:
61108
"""Parse the content of the url with respect to the selected platform.
62109
@@ -119,5 +166,14 @@ def parse_url(self, uri: ParseResult) -> str:
119166
# for example "<group>/<subgroup1>/<subgroup2>/<repository>"
120167
text = uri._replace(netloc="", scheme="") # remove platform
121168
text = urlunparse(text)[1:] # combine to string and strip leading "/"
169+
elif self.platform in ("codeberg", "forgejo", "gitea"):
170+
parts = path.rstrip("/").split("/")
171+
if len(parts) == 4 and parts[2] in ("issues", "pulls"):
172+
text = f"{parts[0]}/{parts[1]}#{parts[3]}" # element number
173+
elif parts == [""]:
174+
text = self.platform
175+
else:
176+
text = uri._replace(netloc="", scheme="") # remove platform
177+
text = urlunparse(text)[1:] # combine to string and strip leading "/"
122178

123179
return text

tests/sites/base/page1.rst

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,64 @@ Page 1
55

66
- https://pydata-sphinx-theme.readthedocs.io/en/latest/
77

8+
**Codeberg**
9+
10+
.. container:: codeberg-container
11+
12+
https://codeberg.org
13+
https://codeberg.org/codeberg-org
14+
https://codeberg.org/codeberg-org/f-repo
15+
https://codeberg.org/codeberg-org/f-repo/issues
16+
https://codeberg.org/codeberg-org/f-repo/issues/
17+
https://codeberg.org/codeberg-org/f-repo/issues/42
18+
https://codeberg.org/codeberg-org/f-repo/pulls/
19+
https://codeberg.org/codeberg-org/f-repo/pulls/1012
20+
https://codeberg.org/codeberg-org/-/projects
21+
https://codeberg.org/codeberg-org/-/projects/2
22+
https://codeberg.org/codeberg-org/f-repo/projects
23+
https://codeberg.org/codeberg-org/f-repo/projects/1
24+
https://codeberg.org/codeberg-org/f-repo/link/to/unknown/feature
25+
https://codeberg.org/explore/repos
26+
27+
**Forgejo**
28+
29+
.. container:: forgejo-container
30+
31+
https://my-forgejo.org
32+
https://my-forgejo.org/forgejo-org
33+
https://my-forgejo.org/forgejo-org/f-repo
34+
https://my-forgejo.org/forgejo-org/f-repo/issues
35+
https://my-forgejo.org/forgejo-org/f-repo/issues/
36+
https://my-forgejo.org/forgejo-org/f-repo/issues/42
37+
https://my-forgejo.org/forgejo-org/f-repo/pulls/
38+
https://forgejo.my.org/forgejo-org/f-repo/pulls/1012
39+
https://forgejo.my.org/forgejo-org/-/projects
40+
https://forgejo.my.org/forgejo-org/-/projects/2
41+
https://forgejo.my.org/forgejo-org/f-repo/projects
42+
https://forgejo.my.org/forgejo-org/f-repo/projects/1
43+
https://forgejo.my.org/forgejo-org/f-repo/link/to/unknown/feature
44+
https://forgejo.my.org/explore/repos
45+
46+
47+
**Gitea**
48+
49+
.. container:: gitea-container
50+
51+
https://gitea.com
52+
https://gitea.com/gitea-org
53+
https://gitea.com/gitea-org/g-repo
54+
https://gitea.com/gitea-org/g-repo/issues
55+
https://gitea.com/gitea-org/g-repo/issues/
56+
https://gitea.com/gitea-org/g-repo/issues/42
57+
https://gitea.com/gitea-org/g-repo/pulls/
58+
https://gitea.com/gitea-org/g-repo/pulls/1012
59+
https://gitea.com/gitea-org/-/projects
60+
https://gitea.com/gitea-org/-/projects/2
61+
https://gitea.com/gitea-org/g-repo/projects
62+
https://gitea.com/gitea-org/g-repo/projects/1
63+
https://gitea.com/gitea-org/g-repo/link/to/unknown/feature
64+
https://gitea.com/explore/repos
65+
866
**GitHub**
967

1068
.. container:: github-container

tests/test_build.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -894,6 +894,19 @@ def test_shorten_link(sphinx_build_factory, file_regression) -> None:
894894
"""Regression test for "edit on <provider>" link shortening."""
895895
sphinx_build = sphinx_build_factory("base").build()
896896

897+
codeberg = sphinx_build.html_tree("page1.html").select(".codeberg-container")[0]
898+
file_regression.check(
899+
codeberg.prettify(), basename="codeberg_links", extension=".html"
900+
)
901+
902+
forgejo = sphinx_build.html_tree("page1.html").select(".forgejo-container")[0]
903+
file_regression.check(
904+
forgejo.prettify(), basename="forgejo_links", extension=".html"
905+
)
906+
907+
gitea = sphinx_build.html_tree("page1.html").select(".gitea-container")[0]
908+
file_regression.check(gitea.prettify(), basename="gitea_links", extension=".html")
909+
897910
github = sphinx_build.html_tree("page1.html").select(".github-container")[0]
898911
file_regression.check(github.prettify(), basename="github_links", extension=".html")
899912

tests/test_build/codeberg_links.html

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
<div class="codeberg-container docutils container">
2+
<p>
3+
<a class="codeberg reference external" href="https://codeberg.org">
4+
codeberg
5+
</a>
6+
<a class="codeberg reference external" href="https://codeberg.org/codeberg-org">
7+
codeberg-org
8+
</a>
9+
<a class="codeberg reference external" href="https://codeberg.org/codeberg-org/f-repo">
10+
codeberg-org/f-repo
11+
</a>
12+
<a class="codeberg reference external" href="https://codeberg.org/codeberg-org/f-repo/issues">
13+
codeberg-org/f-repo/issues
14+
</a>
15+
<a class="codeberg reference external" href="https://codeberg.org/codeberg-org/f-repo/issues/">
16+
codeberg-org/f-repo/issues/
17+
</a>
18+
<a class="codeberg reference external" href="https://codeberg.org/codeberg-org/f-repo/issues/42">
19+
codeberg-org/f-repo#42
20+
</a>
21+
<a class="codeberg reference external" href="https://codeberg.org/codeberg-org/f-repo/pulls/">
22+
codeberg-org/f-repo/pulls/
23+
</a>
24+
<a class="codeberg reference external" href="https://codeberg.org/codeberg-org/f-repo/pulls/1012">
25+
codeberg-org/f-repo#1012
26+
</a>
27+
<a class="codeberg reference external" href="https://codeberg.org/codeberg-org/-/projects">
28+
codeberg-org/-/projects
29+
</a>
30+
<a class="codeberg reference external" href="https://codeberg.org/codeberg-org/-/projects/2">
31+
codeberg-org/-/projects/2
32+
</a>
33+
<a class="codeberg reference external" href="https://codeberg.org/codeberg-org/f-repo/projects">
34+
codeberg-org/f-repo/projects
35+
</a>
36+
<a class="codeberg reference external" href="https://codeberg.org/codeberg-org/f-repo/projects/1">
37+
codeberg-org/f-repo/projects/1
38+
</a>
39+
<a class="codeberg reference external" href="https://codeberg.org/codeberg-org/f-repo/link/to/unknown/feature">
40+
codeberg-org/f-repo/link/to/unknown/feature
41+
</a>
42+
<a class="codeberg reference external" href="https://codeberg.org/explore/repos">
43+
explore/repos
44+
</a>
45+
</p>
46+
</div>

tests/test_build/forgejo_links.html

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
<div class="forgejo-container docutils container">
2+
<p>
3+
<a class="reference external" href="https://my-forgejo.org">
4+
https://my-forgejo.org
5+
</a>
6+
<a class="reference external" href="https://my-forgejo.org/forgejo-org">
7+
https://my-forgejo.org/forgejo-org
8+
</a>
9+
<a class="reference external" href="https://my-forgejo.org/forgejo-org/f-repo">
10+
https://my-forgejo.org/forgejo-org/f-repo
11+
</a>
12+
<a class="forgejo reference external" href="https://my-forgejo.org/forgejo-org/f-repo/issues">
13+
forgejo-org/f-repo/issues
14+
</a>
15+
<a class="forgejo reference external" href="https://my-forgejo.org/forgejo-org/f-repo/issues/">
16+
forgejo-org/f-repo/issues/
17+
</a>
18+
<a class="forgejo reference external" href="https://my-forgejo.org/forgejo-org/f-repo/issues/42">
19+
forgejo-org/f-repo#42
20+
</a>
21+
<a class="forgejo reference external" href="https://my-forgejo.org/forgejo-org/f-repo/pulls/">
22+
forgejo-org/f-repo/pulls/
23+
</a>
24+
<a class="forgejo reference external" href="https://forgejo.my.org/forgejo-org/f-repo/pulls/1012">
25+
forgejo-org/f-repo#1012
26+
</a>
27+
<a class="forgejo reference external" href="https://forgejo.my.org/forgejo-org/-/projects">
28+
forgejo-org/-/projects
29+
</a>
30+
<a class="forgejo reference external" href="https://forgejo.my.org/forgejo-org/-/projects/2">
31+
forgejo-org/-/projects/2
32+
</a>
33+
<a class="forgejo reference external" href="https://forgejo.my.org/forgejo-org/f-repo/projects">
34+
forgejo-org/f-repo/projects
35+
</a>
36+
<a class="forgejo reference external" href="https://forgejo.my.org/forgejo-org/f-repo/projects/1">
37+
forgejo-org/f-repo/projects/1
38+
</a>
39+
<a class="reference external" href="https://forgejo.my.org/forgejo-org/f-repo/link/to/unknown/feature">
40+
https://forgejo.my.org/forgejo-org/f-repo/link/to/unknown/feature
41+
</a>
42+
<a class="reference external" href="https://forgejo.my.org/explore/repos">
43+
https://forgejo.my.org/explore/repos
44+
</a>
45+
</p>
46+
</div>

0 commit comments

Comments
 (0)