-
Notifications
You must be signed in to change notification settings - Fork 15
refactor: add maintainers and maintainers edits to (cached) suggestions #553
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -11,7 +11,7 @@ | |
| from shared.models import NixDerivation, NixMaintainer | ||
| from shared.models.cached import CachedSuggestions | ||
| from shared.models.cve import AffectedProduct, Metric, Version | ||
| from shared.models.linkage import CVEDerivationClusterProposal | ||
| from shared.models.linkage import CVEDerivationClusterProposal, MaintainersEdit | ||
| from shared.models.nix_evaluation import get_major_channel | ||
|
|
||
| logger = logging.getLogger(__name__) | ||
|
|
@@ -109,6 +109,11 @@ def cache_new_suggestions(suggestion: CVEDerivationClusterProposal) -> None: | |
| ) | ||
|
|
||
| prefetched_metrics = Metric.objects.filter(container__cve=suggestion.cve) | ||
| packages = channel_structure(all_versions, derivations) | ||
| maintainers_edits = list( | ||
| suggestion.maintainers_edits.select_related("maintainer").all() | ||
| ) | ||
| maintainers = maintainers_list(packages, maintainers_edits) | ||
|
|
||
| only_relevant_data = { | ||
| "pk": suggestion.pk, | ||
|
|
@@ -117,8 +122,9 @@ def cache_new_suggestions(suggestion: CVEDerivationClusterProposal) -> None: | |
| "title": relevant_piece["title"], | ||
| "description": relevant_piece["descriptions__value"], | ||
| "affected_products": affected_products, | ||
| "packages": channel_structure(all_versions, derivations), | ||
| "packages": packages, | ||
| "metrics": [to_dict(m) for m in prefetched_metrics], | ||
| "maintainers": maintainers, | ||
| } | ||
|
|
||
| # TODO: add format checking to avoid disasters in the frontend. | ||
|
|
@@ -295,3 +301,32 @@ def parse_drv_name(name: str) -> tuple[str, str]: | |
| return match.group(1), match.group(2) | ||
| else: | ||
| return name, "" | ||
|
|
||
|
|
||
| def maintainers_list(packages: dict, edits: list[MaintainersEdit]) -> list[dict]: | ||
| """ | ||
| Returns a deduplicated list (by GitHub ID) of all the maintainers of all the | ||
| affected packages linked to this suggestion, modified by potential | ||
| user-supplied edits. | ||
| """ | ||
|
|
||
| to_remove = { | ||
| m.maintainer for m in edits if m.type == MaintainersEdit.EditType.REMOVE | ||
| } | ||
| to_add = [m.maintainer for m in edits if m.type == MaintainersEdit.EditType.ADD] | ||
|
||
|
|
||
| seen_ids = set() | ||
| maintainers = list() | ||
| all_maintainers = [m for pkg in packages.values() for m in pkg["maintainers"]] | ||
|
|
||
| for m in all_maintainers: | ||
| if m["github_id"] not in seen_ids and m["github_id"] not in to_remove: | ||
| seen_ids.add(m["github_id"]) | ||
| maintainers.append(m) | ||
|
|
||
| for m in to_add: | ||
| if m.github_id not in seen_ids and m.github_id not in to_remove: | ||
| seen_ids.add(m.github_id) | ||
| maintainers.append(m) | ||
|
|
||
| return maintainers | ||
fricklerhandwerk marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,23 @@ | ||
| # Generated by Django 4.2.16 on 2025-05-14 13:58 | ||
|
|
||
| from django.db import migrations, models | ||
| import django.db.models.deletion | ||
|
|
||
|
|
||
| class Migration(migrations.Migration): | ||
|
|
||
| dependencies = [ | ||
| ('shared', '0048_remove_attribute_suffix'), | ||
| ] | ||
|
|
||
| operations = [ | ||
| migrations.CreateModel( | ||
| name='MaintainersEdit', | ||
| fields=[ | ||
| ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), | ||
| ('edit_type', models.CharField(choices=[('add', 'add'), ('remove', 'remove')], max_length=6)), | ||
| ('maintainer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='shared.nixmaintainer')), | ||
| ('suggestion', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='maintainers_edits', to='shared.cvederivationclusterproposal')), | ||
| ], | ||
| ), | ||
| ] |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,11 +1,12 @@ | ||
| from enum import STRICT, IntFlag, auto | ||
|
|
||
| import pghistory | ||
| import shared.models.cached | ||
| from django.db import models | ||
| from django.utils.translation import gettext_lazy as _ | ||
|
|
||
| import shared.models.cached | ||
| from shared.models.cve import CveRecord, Description, IssueStatus, NixpkgsIssue | ||
| from shared.models.nix_evaluation import NixDerivation, TimeStampMixin | ||
| from shared.models.nix_evaluation import NixDerivation, NixMaintainer, TimeStampMixin | ||
|
|
||
|
|
||
| def text_length(choices: type[models.TextChoices]) -> int: | ||
|
|
@@ -62,6 +63,26 @@ def create_nixpkgs_issue(self) -> NixpkgsIssue: | |
| return issue | ||
|
|
||
|
|
||
| class MaintainersEdit(models.Model): | ||
| """ | ||
| A single manual edit of the list of maintainers of a suggestion. | ||
| """ | ||
|
|
||
| class EditType(models.TextChoices): | ||
| ADD = "add", _("add") | ||
| REMOVE = "remove", _("remove") | ||
|
|
||
| edit_type = models.CharField( | ||
| max_length=text_length(EditType), choices=EditType.choices | ||
| ) | ||
| maintainer = models.ForeignKey(NixMaintainer, on_delete=models.CASCADE) | ||
| suggestion = models.ForeignKey( | ||
| CVEDerivationClusterProposal, | ||
| related_name="maintainers_edits", | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we enforce in a constraint that a maintainer edit only appears once on a suggestion? We'd still need to ensure our UI logic won't run into the constraint, but at least the data model would make invalid state impossible to represent and we won't need to do post-checks in the display logic.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do you mean that for example we don't have two edits that add the same maintainer to the same suggestion? Otherwise, reading things literally, I believe
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Yes. Using sets we wouldn't see the problem, but there'd still be potential for a small pile-up in the DB.
Well, it's a one-to-many for edits, not for maintainers in those edits. You could have multiple edits with the same maintainer, right?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Yes, but this is ok... ? You might want to independently add maintainer
which I believe is indeed enforced by the foreign key. So, if I understand correctly, the only remaining constraint that isn't enforced and that we want to enforce is the one you said, that several edits to the same package with the same maintainer shouldn't be allowed. Is that correct?
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Yes, that's what I meant. |
||
| on_delete=models.CASCADE, | ||
| ) | ||
|
|
||
|
|
||
| class ProvenanceFlags(IntFlag, boundary=STRICT): | ||
| PACKAGE_NAME_MATCH = auto() | ||
| VERSION_CONSTRAINT_INRANGE = auto() | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.