Skip to content

Commit 27618ac

Browse files
Maffoochclaude
andcommitted
Consolidate GitHub integration into self-contained dojo/github/ package
Collapse dojo/github.py and dojo/github_issue_link/ into a single dojo/github/ package matching the canonical dojo/url/ reference layout described in CLAUDE.md (models, admin, services, ui/forms, ui/views, ui/urls, templates). Backward-compat re-exports left in dojo/models.py and dojo/forms.py. No behavior change, no migrations. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 3adf4cd commit 27618ac

16 files changed

Lines changed: 151 additions & 118 deletions

File tree

dojo/forms.py

Lines changed: 8 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,14 @@
3636
from dojo.endpoint.utils import endpoint_filter, endpoint_get_or_create, validate_endpoints_to_add
3737
from dojo.engagement.queries import get_authorized_engagements
3838
from dojo.finding.queries import get_authorized_findings
39+
from dojo.github.ui.forms import ( # noqa: F401 -- backward compat
40+
DeleteGITHUBConfForm,
41+
ExpressGITHUBForm,
42+
GITHUB_IssueForm,
43+
GITHUB_Product_Form,
44+
GITHUBFindingForm,
45+
GITHUBForm,
46+
)
3947
from dojo.group.queries import get_authorized_groups, get_group_member_roles
4048
from dojo.labels import get_labels
4149
from dojo.location.models import Location
@@ -69,9 +77,6 @@
6977
Finding_Group,
7078
Finding_Template,
7179
General_Survey,
72-
GITHUB_Conf,
73-
GITHUB_Issue,
74-
GITHUB_PKey,
7580
Global_Role,
7681
JIRA_Instance,
7782
JIRA_Issue,
@@ -2794,42 +2799,6 @@ class Meta:
27942799
fields = ["id"]
27952800

27962801

2797-
class GITHUB_IssueForm(forms.ModelForm):
2798-
2799-
class Meta:
2800-
model = GITHUB_Issue
2801-
exclude = ["product"]
2802-
2803-
2804-
class GITHUBForm(forms.ModelForm):
2805-
api_key = forms.CharField(widget=forms.PasswordInput, required=True)
2806-
2807-
class Meta:
2808-
model = GITHUB_Conf
2809-
exclude = ["product"]
2810-
2811-
2812-
class DeleteGITHUBConfForm(forms.ModelForm):
2813-
id = forms.IntegerField(required=True,
2814-
widget=forms.widgets.HiddenInput())
2815-
2816-
class Meta:
2817-
model = GITHUB_Conf
2818-
fields = ["id"]
2819-
2820-
2821-
class ExpressGITHUBForm(forms.ModelForm):
2822-
password = forms.CharField(widget=forms.PasswordInput, required=True)
2823-
issue_key = forms.CharField(required=True, help_text="A valid issue ID is required to gather the necessary information.")
2824-
2825-
class Meta:
2826-
model = GITHUB_Conf
2827-
exclude = ["product", "epic_name_id", "open_status_key",
2828-
"close_status_key", "info_mapping_severity",
2829-
"low_mapping_severity", "medium_mapping_severity",
2830-
"high_mapping_severity", "critical_mapping_severity", "finding_text"]
2831-
2832-
28332802
def get_jira_issue_template_dir_choices():
28342803
template_root = settings.JIRA_TEMPLATE_ROOT
28352804
template_dir_list = [("", "---")]
@@ -3307,14 +3276,6 @@ class Meta:
33073276
# fields = ['selenium_script']
33083277

33093278

3310-
class GITHUB_Product_Form(forms.ModelForm):
3311-
git_conf = forms.ModelChoiceField(queryset=GITHUB_Conf.objects.all(), label="GITHUB Configuration", required=False)
3312-
3313-
class Meta:
3314-
model = GITHUB_PKey
3315-
exclude = ["product"]
3316-
3317-
33183279
class JIRAProjectForm(forms.ModelForm):
33193280
inherit_from_product = forms.BooleanField(label="inherit JIRA settings from product", required=False)
33203281
jira_instance = forms.ModelChoiceField(queryset=JIRA_Instance.objects.all(), label="JIRA Instance", required=False)
@@ -3447,17 +3408,6 @@ def clean(self):
34473408
return None
34483409

34493410

3450-
class GITHUBFindingForm(forms.Form):
3451-
def __init__(self, *args, **kwargs):
3452-
self.enabled = kwargs.pop("enabled")
3453-
super().__init__(*args, **kwargs)
3454-
self.fields["push_to_github"] = forms.BooleanField()
3455-
self.fields["push_to_github"].required = False
3456-
self.fields["push_to_github"].help_text = "Checking this will overwrite content of your Github issue, or create one."
3457-
3458-
push_to_github = forms.BooleanField(required=False)
3459-
3460-
34613411
class JIRAFindingForm(forms.Form):
34623412
def __init__(self, *args, **kwargs):
34633413
self.push_all = kwargs.pop("push_all", False)

dojo/github/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
import dojo.github.admin # noqa: F401

dojo/github/admin.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
from django.contrib import admin
2+
3+
from dojo.github.models import (
4+
GITHUB_Clone,
5+
GITHUB_Conf,
6+
GITHUB_Details_Cache,
7+
GITHUB_Issue,
8+
GITHUB_PKey,
9+
)
10+
11+
admin.site.register(GITHUB_Conf)
12+
admin.site.register(GITHUB_Issue)
13+
admin.site.register(GITHUB_Clone)
14+
admin.site.register(GITHUB_Details_Cache)
15+
admin.site.register(GITHUB_PKey)

dojo/github/models.py

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
from django.db import models
2+
from django.utils.translation import gettext as _
3+
4+
from dojo.models import Finding, Product
5+
6+
7+
class GITHUB_Conf(models.Model):
8+
configuration_name = models.CharField(max_length=2000, help_text=_("Enter a name to give to this configuration"), default="")
9+
api_key = models.CharField(max_length=2000, help_text=_("Enter your Github API Key"), default="")
10+
11+
def __str__(self):
12+
return self.configuration_name
13+
14+
15+
class GITHUB_Issue(models.Model):
16+
issue_id = models.CharField(max_length=200)
17+
issue_url = models.URLField(max_length=2000, verbose_name=_("GitHub issue URL"))
18+
finding = models.OneToOneField(Finding, null=True, blank=True, on_delete=models.CASCADE)
19+
20+
def __str__(self):
21+
return str(self.issue_id) + "| GitHub Issue URL: " + str(self.issue_url)
22+
23+
24+
class GITHUB_Clone(models.Model):
25+
github_id = models.CharField(max_length=200)
26+
github_clone_id = models.CharField(max_length=200)
27+
28+
29+
class GITHUB_Details_Cache(models.Model):
30+
github_id = models.CharField(max_length=200)
31+
github_key = models.CharField(max_length=200)
32+
github_status = models.CharField(max_length=200)
33+
github_resolution = models.CharField(max_length=200)
34+
35+
36+
class GITHUB_PKey(models.Model):
37+
product = models.ForeignKey(Product, on_delete=models.CASCADE)
38+
39+
git_project = models.CharField(max_length=200, blank=True, verbose_name=_("Github project"), help_text=_("Specify your project location. (:user/:repo)"))
40+
git_conf = models.ForeignKey(GITHUB_Conf, verbose_name=_("Github Configuration"),
41+
null=True, blank=True, on_delete=models.CASCADE)
42+
git_push_notes = models.BooleanField(default=False, blank=True, help_text=_("Notes added to findings will be automatically added to the corresponding github issue"))
43+
44+
def __str__(self):
45+
return self.product.name + " | " + self.git_project

dojo/github.py renamed to dojo/github/services.py

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,23 @@
1-
# python
21
import logging
32
import sys
43

54
from django.template.loader import render_to_string
6-
7-
# External libs
85
from github import Auth, Github
96

10-
# Dojo related imports
11-
from dojo.models import Engagement, GITHUB_Issue, GITHUB_PKey, Product
7+
from dojo.github.models import GITHUB_Issue, GITHUB_PKey
8+
from dojo.models import Engagement, Product
129

13-
# Create global
1410
logger = logging.getLogger(__name__)
1511

1612

13+
def validate_github_credentials(api_key):
14+
"""Verify a GitHub API key by fetching the authenticated user. Raises on failure."""
15+
g = Github(api_key)
16+
user = g.get_user()
17+
logger.debug("Using user " + user.login)
18+
return user.login
19+
20+
1721
def reopen_external_issue_github(find, note, prod, eng):
1822
# Ensure the system setting for GitHub integration is enabled
1923
from dojo.utils import get_system_setting # noqa: PLC0415 circular import
File renamed without changes.
File renamed without changes.

dojo/github/ui/forms.py

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
from django import forms
2+
3+
from dojo.github.models import GITHUB_Conf, GITHUB_Issue, GITHUB_PKey
4+
5+
6+
class GITHUB_IssueForm(forms.ModelForm):
7+
8+
class Meta:
9+
model = GITHUB_Issue
10+
exclude = ["product"]
11+
12+
13+
class GITHUBForm(forms.ModelForm):
14+
api_key = forms.CharField(widget=forms.PasswordInput, required=True)
15+
16+
class Meta:
17+
model = GITHUB_Conf
18+
exclude = ["product"]
19+
20+
21+
class DeleteGITHUBConfForm(forms.ModelForm):
22+
id = forms.IntegerField(required=True,
23+
widget=forms.widgets.HiddenInput())
24+
25+
class Meta:
26+
model = GITHUB_Conf
27+
fields = ["id"]
28+
29+
30+
class ExpressGITHUBForm(forms.ModelForm):
31+
password = forms.CharField(widget=forms.PasswordInput, required=True)
32+
issue_key = forms.CharField(required=True, help_text="A valid issue ID is required to gather the necessary information.")
33+
34+
class Meta:
35+
model = GITHUB_Conf
36+
exclude = ["product", "epic_name_id", "open_status_key",
37+
"close_status_key", "info_mapping_severity",
38+
"low_mapping_severity", "medium_mapping_severity",
39+
"high_mapping_severity", "critical_mapping_severity", "finding_text"]
40+
41+
42+
class GITHUB_Product_Form(forms.ModelForm):
43+
git_conf = forms.ModelChoiceField(queryset=GITHUB_Conf.objects.all(), label="GITHUB Configuration", required=False)
44+
45+
class Meta:
46+
model = GITHUB_PKey
47+
exclude = ["product"]
48+
49+
50+
class GITHUBFindingForm(forms.Form):
51+
def __init__(self, *args, **kwargs):
52+
self.enabled = kwargs.pop("enabled")
53+
super().__init__(*args, **kwargs)
54+
self.fields["push_to_github"] = forms.BooleanField()
55+
self.fields["push_to_github"].required = False
56+
self.fields["push_to_github"].help_text = "Checking this will overwrite content of your Github issue, or create one."
57+
58+
push_to_github = forms.BooleanField(required=False)

0 commit comments

Comments
 (0)