Skip to content

Commit eed0701

Browse files
authored
CRAVEX SourceHut workflow integration #348 (#377)
Signed-off-by: tdruez <[email protected]>
1 parent 768c609 commit eed0701

File tree

16 files changed

+479
-14
lines changed

16 files changed

+479
-14
lines changed

CHANGELOG.rst

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,12 +47,21 @@ Release notes
4747
https://github.com/aboutcode-org/dejacode/issues/349
4848

4949
- Add GitLab workflow Request integration.
50-
Documentation: https://dejacode.readthedocs.io/en/latest/integrations-github.html
50+
Documentation: https://dejacode.readthedocs.io/en/latest/integrations-gitlab.html
5151
https://github.com/aboutcode-org/dejacode/issues/346
5252

5353
- Add Jira workflow Request integration.
54+
Documentation: https://dejacode.readthedocs.io/en/latest/integrations-jira.html
5455
https://github.com/aboutcode-org/dejacode/issues/350
5556

57+
- Add Forgejo workflow Request integration.
58+
Documentation: https://dejacode.readthedocs.io/en/latest/integrations-forgejo.html
59+
https://github.com/aboutcode-org/dejacode/issues/347
60+
61+
- Add SourceHut workflow Request integration.
62+
Documentation: https://dejacode.readthedocs.io/en/latest/integrations-sourcehut.html
63+
https://github.com/aboutcode-org/dejacode/issues/348
64+
5665
### Version 5.3.0
5766

5867
- Rename ProductDependency is_resolved to is_pinned.

dje/admin.py

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1057,10 +1057,11 @@ class DataspaceConfigurationForm(forms.ModelForm):
10571057
"scancodeio_api_key",
10581058
"vulnerablecode_api_key",
10591059
"purldb_api_key",
1060+
"forgejo_token",
10601061
"github_token",
10611062
"gitlab_token",
10621063
"jira_token",
1063-
"forgejo_token",
1064+
"sourcehut_token",
10641065
]
10651066

10661067
def __init__(self, *args, **kwargs):
@@ -1101,6 +1102,14 @@ class DataspaceConfigurationInline(DataspacedFKMixin, admin.StackedInline):
11011102
)
11021103
},
11031104
),
1105+
(
1106+
"Forgejo Integration",
1107+
{
1108+
"fields": [
1109+
"forgejo_token",
1110+
]
1111+
},
1112+
),
11041113
(
11051114
"GitHub Integration",
11061115
{
@@ -1127,10 +1136,10 @@ class DataspaceConfigurationInline(DataspacedFKMixin, admin.StackedInline):
11271136
},
11281137
),
11291138
(
1130-
"Forgejo Integration",
1139+
"SourceHut Integration",
11311140
{
11321141
"fields": [
1133-
"forgejo_token",
1142+
"sourcehut_token",
11341143
]
11351144
},
11361145
),
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# Generated by Django 5.2.4 on 2025-08-11 14:51
2+
3+
from django.db import migrations, models
4+
5+
6+
class Migration(migrations.Migration):
7+
8+
dependencies = [
9+
('dje', '0011_dataspaceconfiguration_forgejo_token_and_more'),
10+
]
11+
12+
operations = [
13+
migrations.AlterField(
14+
model_name='dataspaceconfiguration',
15+
name='sourcehut_token',
16+
field=models.CharField(blank=True, help_text='Access token used to authenticate API requests for the SourceHut integration. This token must have permissions to create and update tickets. Keep this token secure.', max_length=2048, verbose_name='SourceHut token'),
17+
),
18+
]

dje/models.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -572,7 +572,7 @@ class DataspaceConfiguration(models.Model):
572572

573573
sourcehut_token = models.CharField(
574574
_("SourceHut token"),
575-
max_length=255,
575+
max_length=2048, # The length of this token depends on the selected permissions
576576
blank=True,
577577
help_text=_(
578578
"Access token used to authenticate API requests for the SourceHut integration. "

docs/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ Welcome to the very start of your DejaCode journey!
5252
integrations-github
5353
integrations-gitlab
5454
integrations-jira
55+
integrations-sourcehut
5556

5657
.. toctree::
5758
:maxdepth: 1

docs/integrations-sourcehut.rst

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
.. _integrations_sourcehut:
2+
3+
SourceHut Integration
4+
=====================
5+
6+
DejaCode's integration with SourceHut allows you to automatically forward
7+
**Workflow Requests** to SourceHut **tickets**.
8+
This behavior can be selectively applied to any **Request Template** of your choice.
9+
10+
Prerequisites
11+
-------------
12+
13+
- A **SourceHut project** hosted on https://todo.sr.ht that you want to integrate with
14+
DejaCode.
15+
- A **SourceHut account** with API access and permission to create/edit tickets.
16+
17+
SourceHut API Token
18+
-------------------
19+
20+
To enable integration, you need a SourceHut **API token**.
21+
22+
1. **Create a Token**:
23+
24+
- Go to https://meta.sr.ht/oauth2
25+
- Under **Personal Access Tokens**, click **"Generate new token"**
26+
- Set a clear description like ``DejaCode Integration`` in the "Comment" field
27+
- Select **both** the ``todo.sr.ht`` > ``TRACKERS`` AND ``TICKETS`` scopes
28+
- **Generate token** and copy the **entire token string**
29+
30+
.. note::
31+
32+
It is recommended to **create a dedicated SourceHut user** with a clear, descriptive
33+
name such as ``dejacode-integration``. This ensures that all SourceHut issues
34+
managed by integration are clearly attributed to that user, improving traceability
35+
and auditability.
36+
37+
DejaCode Dataspace Configuration
38+
--------------------------------
39+
40+
To use your SourceHut token in DejaCode:
41+
42+
1. Go to the **Administration dashboard**
43+
2. Navigate to **Dataspaces**, and select your Dataspace
44+
3. Scroll to the **SourceHut Integration** section under **Configuration**
45+
4. Paste your SourceHut token in the **SourceHut token** field
46+
5. Save the form
47+
48+
Activate SourceHut Integration on Request Templates
49+
---------------------------------------------------
50+
51+
1. Go to the **Administration dashboard**
52+
2. Navigate to **Workflow** > **Request templates**
53+
3. Create or edit a Request Template in your Dataspace
54+
4. Set the **Issue Tracker ID** field to your SourceHut project URL, e.g.::
55+
56+
https://todo.sr.ht/~USERNAME/PROJECT_NAME
57+
58+
Once the integration is configured:
59+
60+
- New **Requests** using this template will be automatically pushed to SourceHut
61+
- Field updates (like title or priority) and **status changes** (e.g. closed) will be
62+
synced
63+
- New **Comments** on a DejaCode Request will be propagated to the SourceHut ticket

workflow/admin.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ def clean_issue_tracker_id(self):
6969
"• GitHub: https://github.com/ORG/REPO_NAME",
7070
"• GitLab: https://gitlab.com/GROUP/PROJECT_NAME",
7171
"• Jira: https://YOUR_DOMAIN.atlassian.net/projects/PROJECTKEY",
72+
"• SourceHut: https://todo.sr.ht/~USERNAME/PROJECT_NAME",
7273
]
7374
)
7475
return issue_tracker_id

workflow/integrations/__init__.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,15 @@
1313
from workflow.integrations.github import GitHubIntegration
1414
from workflow.integrations.gitlab import GitLabIntegration
1515
from workflow.integrations.jira import JiraIntegration
16+
from workflow.integrations.sourcehut import SourceHutIntegration
1617

1718
__all__ = [
1819
"BaseIntegration",
1920
"ForgejoIntegration",
2021
"GitHubIntegration",
2122
"GitLabIntegration",
2223
"JiraIntegration",
24+
"SourceHutIntegration",
2325
"is_valid_issue_tracker_id",
2426
"get_class_for_tracker",
2527
"get_class_for_platform",
@@ -36,11 +38,14 @@
3638
r"/(?:projects|browse)/[A-Z][A-Z0-9]+(?:/[^/]*)*/*$"
3739
)
3840

41+
SOURCEHUT_PATTERN = re.compile(r"^https://todo\.sr\.ht/~[^/]+/[^/]+/?$")
42+
3943
ISSUE_TRACKER_PATTERNS = [
4044
FORGEJO_PATTERN,
4145
GITHUB_PATTERN,
4246
GITLAB_PATTERN,
4347
JIRA_PATTERN,
48+
SOURCEHUT_PATTERN,
4449
]
4550

4651

@@ -57,6 +62,8 @@ def get_class_for_tracker(issue_tracker_id):
5762
return JiraIntegration
5863
elif "forgejo" in issue_tracker_id:
5964
return ForgejoIntegration
65+
elif "todo.sr.ht" in issue_tracker_id:
66+
return SourceHutIntegration
6067

6168

6269
def get_class_for_platform(platform):
@@ -65,4 +72,5 @@ def get_class_for_platform(platform):
6572
"github": GitHubIntegration,
6673
"gitlab": GitLabIntegration,
6774
"jira": JiraIntegration,
75+
"sourcehut": SourceHutIntegration,
6876
}.get(platform)

workflow/integrations/base.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ class BaseIntegration:
2121
"""Base class for managing issue tracker integrations from DejaCode requests."""
2222

2323
default_timeout = 10
24+
open_status = "open"
25+
closed_status = "closed"
2426

2527
def __init__(self, dataspace, timeout=None):
2628
if not dataspace:
@@ -95,6 +97,11 @@ def patch(self, url, json=None):
9597
def post_comment(self, repo_id, issue_id, comment_body, base_url=None):
9698
raise NotImplementedError
9799

100+
def get_status(self, request):
101+
if request.is_closed:
102+
return self.closed_status
103+
return self.open_status
104+
98105
@staticmethod
99106
def make_issue_title(request):
100107
return f"[DEJACODE] {request.title}"

workflow/integrations/forgejo.py

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,6 @@ class ForgejoIntegration(BaseIntegration):
1919
from DejaCode requests.
2020
"""
2121

22-
open_status = "open"
23-
closed_status = "closed"
24-
2522
def get_headers(self):
2623
forgejo_token = self.dataspace.get_configuration("forgejo_token")
2724
if not forgejo_token:
@@ -46,7 +43,7 @@ def sync(self, request):
4643
issue_id=external_issue.issue_id,
4744
title=self.make_issue_title(request),
4845
body=self.make_issue_body(request),
49-
state=self.closed_status if request.is_closed else self.open_status,
46+
state=self.get_status(request),
5047
)
5148
else:
5249
issue = self.create_issue(

0 commit comments

Comments
 (0)