+
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -47,12 +47,21 @@ Release notes
https://github.com/aboutcode-org/dejacode/issues/349

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

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

- Add Forgejo workflow Request integration.
Documentation: https://dejacode.readthedocs.io/en/latest/integrations-forgejo.html
https://github.com/aboutcode-org/dejacode/issues/347

- Add SourceHut workflow Request integration.
Documentation: https://dejacode.readthedocs.io/en/latest/integrations-sourcehut.html
https://github.com/aboutcode-org/dejacode/issues/348

### Version 5.3.0

- Rename ProductDependency is_resolved to is_pinned.
Expand Down
15 changes: 12 additions & 3 deletions dje/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -1057,10 +1057,11 @@ class DataspaceConfigurationForm(forms.ModelForm):
"scancodeio_api_key",
"vulnerablecode_api_key",
"purldb_api_key",
"forgejo_token",
"github_token",
"gitlab_token",
"jira_token",
"forgejo_token",
"sourcehut_token",
]

def __init__(self, *args, **kwargs):
Expand Down Expand Up @@ -1101,6 +1102,14 @@ class DataspaceConfigurationInline(DataspacedFKMixin, admin.StackedInline):
)
},
),
(
"Forgejo Integration",
{
"fields": [
"forgejo_token",
]
},
),
(
"GitHub Integration",
{
Expand All @@ -1127,10 +1136,10 @@ class DataspaceConfigurationInline(DataspacedFKMixin, admin.StackedInline):
},
),
(
"Forgejo Integration",
"SourceHut Integration",
{
"fields": [
"forgejo_token",
"sourcehut_token",
]
},
),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Generated by Django 5.2.4 on 2025-08-11 14:51

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('dje', '0011_dataspaceconfiguration_forgejo_token_and_more'),
]

operations = [
migrations.AlterField(
model_name='dataspaceconfiguration',
name='sourcehut_token',
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'),
),
]
2 changes: 1 addition & 1 deletion dje/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -572,7 +572,7 @@ class DataspaceConfiguration(models.Model):

sourcehut_token = models.CharField(
_("SourceHut token"),
max_length=255,
max_length=2048, # The length of this token depends on the selected permissions
blank=True,
help_text=_(
"Access token used to authenticate API requests for the SourceHut integration. "
Expand Down
1 change: 1 addition & 0 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ Welcome to the very start of your DejaCode journey!
integrations-github
integrations-gitlab
integrations-jira
integrations-sourcehut

.. toctree::
:maxdepth: 1
Expand Down
63 changes: 63 additions & 0 deletions docs/integrations-sourcehut.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
.. _integrations_sourcehut:

SourceHut Integration
=====================

DejaCode's integration with SourceHut allows you to automatically forward
**Workflow Requests** to SourceHut **tickets**.
This behavior can be selectively applied to any **Request Template** of your choice.

Prerequisites
-------------

- A **SourceHut project** hosted on https://todo.sr.ht that you want to integrate with
DejaCode.
- A **SourceHut account** with API access and permission to create/edit tickets.

SourceHut API Token
-------------------

To enable integration, you need a SourceHut **API token**.

1. **Create a Token**:

- Go to https://meta.sr.ht/oauth2
- Under **Personal Access Tokens**, click **"Generate new token"**
- Set a clear description like ``DejaCode Integration`` in the "Comment" field
- Select **both** the ``todo.sr.ht`` > ``TRACKERS`` AND ``TICKETS`` scopes
- **Generate token** and copy the **entire token string**

.. note::

It is recommended to **create a dedicated SourceHut user** with a clear, descriptive
name such as ``dejacode-integration``. This ensures that all SourceHut issues
managed by integration are clearly attributed to that user, improving traceability
and auditability.

DejaCode Dataspace Configuration
--------------------------------

To use your SourceHut token in DejaCode:

1. Go to the **Administration dashboard**
2. Navigate to **Dataspaces**, and select your Dataspace
3. Scroll to the **SourceHut Integration** section under **Configuration**
4. Paste your SourceHut token in the **SourceHut token** field
5. Save the form

Activate SourceHut Integration on Request Templates
---------------------------------------------------

1. Go to the **Administration dashboard**
2. Navigate to **Workflow** > **Request templates**
3. Create or edit a Request Template in your Dataspace
4. Set the **Issue Tracker ID** field to your SourceHut project URL, e.g.::

https://todo.sr.ht/~USERNAME/PROJECT_NAME

Once the integration is configured:

- New **Requests** using this template will be automatically pushed to SourceHut
- Field updates (like title or priority) and **status changes** (e.g. closed) will be
synced
- New **Comments** on a DejaCode Request will be propagated to the SourceHut ticket
1 change: 1 addition & 0 deletions workflow/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ def clean_issue_tracker_id(self):
"• GitHub: https://github.com/ORG/REPO_NAME",
"• GitLab: https://gitlab.com/GROUP/PROJECT_NAME",
"• Jira: https://YOUR_DOMAIN.atlassian.net/projects/PROJECTKEY",
"• SourceHut: https://todo.sr.ht/~USERNAME/PROJECT_NAME",
]
)
return issue_tracker_id
Expand Down
8 changes: 8 additions & 0 deletions workflow/integrations/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,15 @@
from workflow.integrations.github import GitHubIntegration
from workflow.integrations.gitlab import GitLabIntegration
from workflow.integrations.jira import JiraIntegration
from workflow.integrations.sourcehut import SourceHutIntegration

__all__ = [
"BaseIntegration",
"ForgejoIntegration",
"GitHubIntegration",
"GitLabIntegration",
"JiraIntegration",
"SourceHutIntegration",
"is_valid_issue_tracker_id",
"get_class_for_tracker",
"get_class_for_platform",
Expand All @@ -36,11 +38,14 @@
r"/(?:projects|browse)/[A-Z][A-Z0-9]+(?:/[^/]*)*/*$"
)

SOURCEHUT_PATTERN = re.compile(r"^https://todo\.sr\.ht/~[^/]+/[^/]+/?$")

ISSUE_TRACKER_PATTERNS = [
FORGEJO_PATTERN,
GITHUB_PATTERN,
GITLAB_PATTERN,
JIRA_PATTERN,
SOURCEHUT_PATTERN,
]


Expand All @@ -57,6 +62,8 @@ def get_class_for_tracker(issue_tracker_id):
return JiraIntegration
elif "forgejo" in issue_tracker_id:
return ForgejoIntegration
elif "todo.sr.ht" in issue_tracker_id:
return SourceHutIntegration


def get_class_for_platform(platform):
Expand All @@ -65,4 +72,5 @@ def get_class_for_platform(platform):
"github": GitHubIntegration,
"gitlab": GitLabIntegration,
"jira": JiraIntegration,
"sourcehut": SourceHutIntegration,
}.get(platform)
7 changes: 7 additions & 0 deletions workflow/integrations/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ class BaseIntegration:
"""Base class for managing issue tracker integrations from DejaCode requests."""

default_timeout = 10
open_status = "open"
closed_status = "closed"

def __init__(self, dataspace, timeout=None):
if not dataspace:
Expand Down Expand Up @@ -95,6 +97,11 @@ def patch(self, url, json=None):
def post_comment(self, repo_id, issue_id, comment_body, base_url=None):
raise NotImplementedError

def get_status(self, request):
if request.is_closed:
return self.closed_status
return self.open_status

@staticmethod
def make_issue_title(request):
return f"[DEJACODE] {request.title}"
Expand Down
5 changes: 1 addition & 4 deletions workflow/integrations/forgejo.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,6 @@ class ForgejoIntegration(BaseIntegration):
from DejaCode requests.
"""

open_status = "open"
closed_status = "closed"

def get_headers(self):
forgejo_token = self.dataspace.get_configuration("forgejo_token")
if not forgejo_token:
Expand All @@ -46,7 +43,7 @@ def sync(self, request):
issue_id=external_issue.issue_id,
title=self.make_issue_title(request),
body=self.make_issue_body(request),
state=self.closed_status if request.is_closed else self.open_status,
state=self.get_status(request),
)
else:
issue = self.create_issue(
Expand Down
4 changes: 1 addition & 3 deletions workflow/integrations/github.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,6 @@ class GitHubIntegration(BaseIntegration):
"""

api_url = GITHUB_API_URL
open_status = "open"
closed_status = "closed"

def get_headers(self):
github_token = self.dataspace.get_configuration(field_name="github_token")
Expand All @@ -47,7 +45,7 @@ def sync(self, request):
issue_id=external_issue.issue_id,
title=self.make_issue_title(request),
body=self.make_issue_body(request),
state=self.closed_status if request.is_closed else self.open_status,
state=self.get_status(request),
labels=labels,
)
else:
Expand Down
4 changes: 3 additions & 1 deletion workflow/integrations/gitlab.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ class GitLabIntegration(BaseIntegration):
"""

api_url = GITLAB_API_URL
open_status = "reopen"
closed_status = "close"

def get_headers(self):
gitlab_token = self.dataspace.get_configuration(field_name="gitlab_token")
Expand Down Expand Up @@ -48,7 +50,7 @@ def sync(self, request):
issue_id=external_issue.issue_id,
title=self.make_issue_title(request),
body=self.make_issue_body(request),
state_event="close" if request.is_closed else "reopen",
state_event=self.get_status(request),
labels=labels,
)
else:
Expand Down
3 changes: 2 additions & 1 deletion workflow/integrations/jira.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ class JiraIntegration(BaseIntegration):
"""

issuetype = "DejaCode Request"
open_status = None
closed_status = "Closed"

def get_headers(self):
Expand Down Expand Up @@ -55,7 +56,7 @@ def sync(self, request):
issue_id=external_issue.issue_id,
title=self.make_issue_title(request),
body=self.make_issue_body(request),
status=self.closed_status if request.is_closed else None,
status=self.get_status(request),
)
else:
issue = self.create_issue(
Expand Down
Loading
点击 这是indexloc提供的php浏览器服务,不要输入任何密码和下载