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
30 changes: 29 additions & 1 deletion src/shared/github.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ def get_gh(per_page: int = 30) -> Github:
def create_gh_issue(
cached_suggestion: CachedSuggestions,
tracker_issue_uri: str,
comment: str | None = None,
github: Github = get_gh(),
) -> GithubIssue:
"""
Expand Down Expand Up @@ -105,6 +106,33 @@ def affected_nix_packages() -> str:
{"\n".join(packages)}
</details>"""

def additional_comment() -> str:
if comment:
# Find the maximum number of consecutive backticks in the comment
max_backticks = 0
current_backticks = 0

for char in comment:
if char == "`":
current_backticks += 1
max_backticks = max(max_backticks, current_backticks)
else:
current_backticks = 0

# Use at least 3 backticks, or one more than the maximum found in
# order to escape accidents or attempts at escaping the code block
fence_backticks = "`" * max(3, max_backticks + 1)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is fine, and I know that fixing a problem with a regex doubles the problem, but iterating over a comment in Python seems scary to me. This could also work to count backtick runs:

max(m.end() - m.start() for m in re.finditer('`+', comment))


return f"""

## Additional comment

{fence_backticks}
{comment}
{fence_backticks}"""
else:
return ""

repo = github.get_repo(f"{settings.GH_ORGANIZATION}/{settings.GH_ISSUES_REPO}")
title = cached_suggestion.payload["title"]

Expand All @@ -116,7 +144,7 @@ def affected_nix_packages() -> str:

{cached_suggestion.payload["description"]}
{cvss_details()}
{affected_nix_packages()}"""
{affected_nix_packages()}{additional_comment()}"""

return repo.create_issue(title=title, body=body, labels=settings.GH_ISSUES_LABELS)

Expand Down
18 changes: 18 additions & 0 deletions src/shared/migrations/0059_cvederivationclusterproposal_comment.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Generated by Django 4.2.24 on 2025-11-12 14:42

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('shared', '0058_remove_cvederivationclusterproposal_pgpubsub_8c7ef_and_more'),
]

operations = [
migrations.AddField(
model_name='cvederivationclusterproposal',
name='comment',
field=models.CharField(default='', help_text='Optional free text comment for additional notes, context, dismissal reason', max_length=1000),
),
]
18 changes: 18 additions & 0 deletions src/shared/migrations/0060_nixpkgsissue_comment.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Generated by Django 4.2.24 on 2025-11-14 16:08

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('shared', '0059_cvederivationclusterproposal_comment'),
]

operations = [
migrations.AddField(
model_name='nixpkgsissue',
name='comment',
field=models.CharField(default='', help_text='Optional comment from the suggestion that lead to this issue', max_length=1000),
),
]
5 changes: 5 additions & 0 deletions src/shared/models/cve.py
Original file line number Diff line number Diff line change
Expand Up @@ -531,6 +531,11 @@ class NixpkgsIssue(models.Model):

cve = models.ManyToManyField(CveRecord)
description = models.ForeignKey(Description, on_delete=models.PROTECT)
comment = models.CharField(
max_length=1000,
default="",
help_text=_("Optional comment from the suggestion that lead to this issue"),
)
status = models.CharField(
max_length=text_length(IssueStatus),
choices=IssueStatus.choices,
Expand Down
9 changes: 9 additions & 0 deletions src/shared/models/linkage.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,14 @@ class Status(models.TextChoices):
max_length=text_length(Status), choices=Status.choices, default=Status.PENDING
)

comment = models.CharField(
max_length=1000,
default="",
help_text=_(
"Optional free text comment for additional notes, context, dismissal reason"
),
)

def create_nixpkgs_issue(self) -> NixpkgsIssue:
"""
Create a NixpkgsIssue from this suggestion and save it in the database. Note
Expand All @@ -62,6 +70,7 @@ def create_nixpkgs_issue(self) -> NixpkgsIssue:
)
issue.cve.add(self.cve)
issue.derivations.set(self.derivations.all())
issue.comment = self.comment
issue.save()
return issue

Expand Down
10 changes: 10 additions & 0 deletions src/shared/templates/base.html
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,16 @@ <h1>
</div>
{% endif %}


<!-- Show Django messages, mostly for non-JS fallback -->
{% if messages %}
<div id="messages">
{% for message in messages %}
<div class="message">{{ message }}</div>
{% endfor %}
</div>
{% endif %}

{% block layout %}
<article>
{% block content %}{% endblock content %}
Expand Down
44 changes: 44 additions & 0 deletions src/webview/static/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -369,6 +369,8 @@
--issue-notaffected: #e8867d;
--issue-notforus: #e2e2e2;
--issue-wontfix: #ccccdd;
--error-color: #d31919;
--user-comment: #fcf2bd;
}

body {
Expand Down Expand Up @@ -462,6 +464,23 @@ nav.issue-pipeline ul {
margin: 0;
}

/* Error messages from Django message framework */
#messages {
margin: 2em;
display: flex;
flex-direction: column;
gap: 1em;
}

#messages .message {
background: var(--error-color);
color: white;
font-weight: bold;
padding: 0.5em;
margin: 0.5em;
border-radius: 0.2em;
}

#overview {
margin: auto;
}
Expand Down Expand Up @@ -784,6 +803,31 @@ details.description[open] > summary:after {
background: var(--affected);
}

.suggestion-comment,
.issue-comment {
margin-top: 3em;
border-radius: 0.3em;
border: 1px solid var(--grey);
font-size: 1.2em;
font-family: monospace;
padding: 0.5em;
width: calc(100% - 1em);
background: var(--user-comment);
}

textarea.suggestion-comment:placeholder-shown {
background: white;
}

.suggestion .error-message {
background: var(--error-color);
color: white;
font-weight: bold;
padding: 0.5em;
margin: 0.5em;
border-radius: 0.2em;
}

article .nixpkgs-packages {
margin-top: 0.5rem;
display: flex;
Expand Down
13 changes: 1 addition & 12 deletions src/webview/static/subscriptions.css
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
:root {
--subscription-error-color: #d31919;
--subscription-subscribe-color: #48a564;
--subscription-subscribe-background-color: #d9fce3;
--subscription-unsubscribe-color: #d31919;
Expand All @@ -15,16 +14,6 @@
background-image: url("/static/eye.svg");
}

.subscriptions-center-message,
.package-subscriptions .message {
background: var(--subscription-error-color);
color: white;
font-weight: bold;
padding: 0.5em;
margin: 0.5em;
border-radius: 0.2em;
}

.subscription-center-header > h1 {
margin: 1em 0;
font-size: 2em;
Expand Down Expand Up @@ -102,7 +91,7 @@
}

.package-subscription .package-not-found {
background: var(--subscription-error-color);
background: var(--error-color);
color: white;
padding: 2em;
margin: 2em;
Expand Down
4 changes: 4 additions & 0 deletions src/webview/templates/components/issue.html
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@
</details>
{% endif %}

{% if issue.comment %}
<p class="issue-comment">{{ issue.comment|default:"" }}</p>
{% endif %}

{% if show_permalink %}
<a href="/issues/{{ issue.code }}" class="permalink">Permalink</a>
{% endif %}
Expand Down
14 changes: 14 additions & 0 deletions src/webview/templates/components/suggestion.html
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,15 @@
{% maintainers_list cached_suggestion.maintainers %}
{% endif %}


{% if user|is_maintainer_or_admin %}
<textarea
class="suggestion-comment"
name="comment"
maxlength="1000"
placeholder="Free comment: context, additional info, dismissal reason, etc."
rows="3"
>{{ suggestion.comment|default:"" }}</textarea>
<div class="change-issue-state">
{% if status_filter == "pending" %}
<button type="submit" name="new_status" value="accepted" class="draft-color">
Expand Down Expand Up @@ -87,5 +95,11 @@
</div>
{% endif %}
</form>

{% if error_message %}
<div class="error-message">
{{ error_message }}
</div>
{% endif %}
</article>

9 changes: 0 additions & 9 deletions src/webview/templates/components/suggestion_state_error.html

This file was deleted.

This file was deleted.

7 changes: 0 additions & 7 deletions src/webview/templates/subscriptions/subscriptions_center.html
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,6 @@
<h1>Subscriptions</h1>
</header>

<!-- Show Django messages for non-JS fallback -->
{% if messages %}
{% for message in messages %}
<div class="subscriptions-center-message">{{ message }}</div>
{% endfor %}
{% endif %}

<p>When a new CVE is suspected to affect packages you have subscribed to, you will receive a notification.</p>

<div id="auto-subscribe-container">
Expand Down
Loading
Loading