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
13 changes: 11 additions & 2 deletions nix/overlay.nix
Original file line number Diff line number Diff line change
@@ -1,9 +1,18 @@
final: _prev:
final: prev:
let
sources = import ../npins;
meta = with builtins; fromTOML (readFile ../src/pyproject.toml);
in
{
/*
XXX(@fricklerhandwerk): At the time of writing, Nixpkgs has Django 4 as default.
Some packages that depend on Django use that default implicitly, so we override it for everything.
*/
python3 = prev.python3.override {
packageOverrides = pyfinal: _pyprev: {
django = pyfinal.django_5;
};
};
# go through the motions to make a flake-incompat project use the build
# inputs we want
pre-commit-hooks = final.callPackage "${sources.pre-commit-hooks}/nix/run.nix" {
Expand All @@ -30,7 +39,7 @@ in
django-debug-toolbar
django-filter
django-types
django_4
django
djangorestframework
httpretty
ipython
Expand Down
29 changes: 26 additions & 3 deletions nix/web-security-tracker.nix
Original file line number Diff line number Diff line change
Expand Up @@ -266,8 +266,8 @@ in
'';
};

web-security-tracker-worker = {
description = "Web security tracker - background job processor";
web-security-tracker-evaluator = {
description = "Web security tracker - Nixpkgs evaluation worker";
after = [
"network.target"
"postgresql.service"
Expand All @@ -280,7 +280,30 @@ in
# Before starting, crash all the in-progress evaluations.
# This will prevent them from being stalled forever, since workers would not pick up evaluations marked as in-progress.
wst-manage crash_all_evaluations
wst-manage listen --recover --processes ${toString cfg.maxJobProcessors}
wst-manage listen --recover \
--processes ${toString cfg.maxJobProcessors} \
--channels \
shared.channels.NixEvaluationChannel
'';
};

web-security-tracker-worker = {
description = "Web security tracker - background job processor";
after = [
"network.target"
"postgresql.service"
"web-security-tracker-server.service"
];
requires = [ "postgresql.service" ];
wantedBy = [ "multi-user.target" ];

script = ''
wst-manage listen --recover \
--channels \
shared.channels.NixChannelChannel \
shared.channels.ContainerChannel \
shared.channels.CVEDerivationClusterProposalCacheChannel \
shared.channels.CVEDerivationClusterProposalNotificationChannel \
'';
};

Expand Down
8 changes: 6 additions & 2 deletions src/shared/auth/github_state.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,12 @@ def sync_groups_with_github_teams(self) -> None:
member.id for member in self.committers_team.get_members()
}

users = User.objects.prefetch_related("socialaccount_set").iterator()
for user in users:
for user in User.objects.prefetch_related("socialaccount_set").iterator(
# XXX(@fricklerhandwerk): `chunk_size` must be set in presence of `prefetch_related`
# https://docs.djangoproject.com/en/5.2/ref/models/querysets/#iterator
# This is probably totally okay, but consider choosing a non-arbitrary value.
chunk_size=2000
):
social = user.socialaccount_set.filter(provider="github").first() # type: ignore
if not social:
# Superusers are the only possible users with no social account.
Expand Down
10 changes: 8 additions & 2 deletions src/shared/logs/fetchers.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,10 @@ def fetch_suggestion_events(suggestion_id: int) -> list[RawEventType]:
.filter(pgh_obj_id=suggestion_id)
)

for status_event in status_qs.all().iterator():
# XXX(@fricklerhandwerk): `chunk_size` must be set in presence of `prefetch_related`
# https://docs.djangoproject.com/en/5.2/ref/models/querysets/#iterator
# This is probably okay, but consider choosing a non-arbitrary value.
for status_event in status_qs.all().iterator(chunk_size=2000):
all_events.append(
RawStatusEvent(
suggestion_id=status_event.pgh_obj_id,
Expand Down Expand Up @@ -102,7 +105,10 @@ def fetch_suggestion_events(suggestion_id: int) -> list[RawEventType]:
).filter(suggestion_id=suggestion_id)
)

for maintainer_event in maintainer_qs.all().iterator():
# XXX(@fricklerhandwerk): `chunk_size` must be set in presence of `prefetch_related`
# https://docs.djangoproject.com/en/5.2/ref/models/querysets/#iterator
# This is probably okay, but consider choosing a non-arbitrary value.
for maintainer_event in maintainer_qs.all().iterator(chunk_size=2000):
all_events.append(
RawMaintainerEvent(
suggestion_id=maintainer_event.suggestion.id,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# Generated by Django 5.2.6 on 2026-01-05 10:28

import django.db.models.deletion
from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('shared', '0061_nixpkgs_issue_relate_to_suggestion'),
]

operations = [
migrations.SeparateDatabaseAndState(
state_operations=[
migrations.CreateModel(
name='NixDerivationDependencyThrough',
fields=[
('pk', models.CompositePrimaryKey('nixderivation_id', 'nixderivationoutput_id', blank=True, editable=False, primary_key=True, serialize=False)),
('nixderivation', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='shared.nixderivation')),
('nixderivationoutput', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='shared.nixderivationoutput')),
],
options={
'db_table': 'shared_nixderivation_dependencies',
},
),
migrations.AlterField(
model_name='nixderivation',
name='dependencies',
field=models.ManyToManyField(through='shared.NixDerivationDependencyThrough', to='shared.nixderivationoutput'),
),
],
database_operations=[
migrations.RunSQL(
sql="""
ALTER TABLE shared_nixderivation_dependencies
DROP CONSTRAINT shared_nixderivation_dependencies_pkey,
DROP COLUMN id;

-- Redundant, since it's caputed by the CompositePrimaryKey's first element
DROP INDEX shared_nixderivation_dependencies_nixderivation_id_1e830ca9;

-- Run this manually with `CONCURRENTLY` before applying the migration in order to avoid stopping the world:
CREATE UNIQUE INDEX IF NOT EXISTS shared_nixderivation_dependencies_pkey_composite
ON shared_nixderivation_dependencies (nixderivation_id, nixderivationoutput_id);

ALTER TABLE shared_nixderivation_dependencies
-- Unfortunately we can't reuse the underlying index because it's bound to the uniqueness constraint
DROP CONSTRAINT shared_nixderivation_dep_nixderivation_id_nixderi_7ba267e1_uniq,
ADD CONSTRAINT shared_nixderivation_dependencies_pkey_composite
PRIMARY KEY USING INDEX shared_nixderivation_dependencies_pkey_composite;
""",
reverse_sql="""
ALTER TABLE shared_nixderivation_dependencies
DROP CONSTRAINT shared_nixderivation_dependencies_pkey,
-- This will take time!
ADD COLUMN id bigint GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY;

-- Run the following two statements manually with `CONCURRENTLY` before applying the rollback in order to avoid stopping the world:
CREATE INDEX IF NOT EXISTS shared_nixderivation_dependencies_nixderivation_id_1e830ca9
ON shared_nixderivation_dependencies (nixderivation_id);
CREATE INDEX IF NOT EXISTS shared_nixderivation_dep_nixderivation_id_nixderi_7ba267e1_uniq
ON shared_nixderivation_dependencies (nixderivation_id, nixderivationoutput_id);

ALTER TABLE shared_nixderivation_dependencies
ADD CONSTRAINT shared_nixderivation_dep_nixderivation_id_nixderi_7ba267e1_uniq
UNIQUE (nixderivation_id, nixderivationoutput_id);
""",
),
],
),
]
21 changes: 20 additions & 1 deletion src/shared/models/nix_evaluation.py
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,22 @@ class Meta: # type: ignore[override]
unique_together = ("channel", "commit_sha1")


class NixDerivationDependencyThrough(models.Model):
pk = models.CompositePrimaryKey("nixderivation_id", "nixderivationoutput_id")

nixderivation = models.ForeignKey(
"NixDerivation",
on_delete=models.CASCADE,
)
nixderivationoutput = models.ForeignKey(
NixDerivationOutput,
on_delete=models.CASCADE,
)

class Meta:
db_table = "shared_nixderivation_dependencies"


class NixDerivation(models.Model):
"""
This represents a Nix derivation "evaluated",
Expand All @@ -281,7 +297,10 @@ class NixDerivation(models.Model):

attribute = models.CharField(max_length=255)
derivation_path = models.CharField(max_length=255)
dependencies = models.ManyToManyField(NixDerivationOutput)
dependencies = models.ManyToManyField(
NixDerivationOutput,
through=NixDerivationDependencyThrough,
)
name = models.CharField(max_length=255)
metadata = models.OneToOneField(
NixDerivationMeta,
Expand Down
17 changes: 9 additions & 8 deletions src/webview/templates/components/nixpkgs_package.html
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,17 @@ <h3 class="bold"><span class="dimmed">pkgs.</span>{{ attribute_name }}</h3>
<summary>
<div class="inline-row gap-small">
<span class="w-12">{{ major_channel }}</span>
{% if major_version %}
<a class="{% if major_version.status == 'affected' %}hl-red{% elif major_version.status == 'unaffected' %}hl-green{% endif %}" target="_blank" href="{{ major_version.src_position }}">
{% if major_version.major_version %}
{{ major_version.major_version }}
{% else %}
???
{% endif %}
{% if major_version.major_version %}
<a
class="{% if major_version.status == 'affected' %}hl-red{% elif major_version.status == 'unaffected' %}hl-green{% endif %}"
target="_blank"
href="{{ major_version.src_position }}"
title="evaluated {{ major_version.updated|iso }}"
>
{{ major_version.major_version }}
</a>
{% else %}
???
-
{% endif %}
</div>
</summary>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{% load humanize %}
{% load viewutils %}

{% if not activity_log|length_is:0 %}
{% if activity_log %}
<details class="column align-end"
id="suggestion-activity-log-{{suggestion.id}}"
{% if oob_update %}hx-swap-oob="true"{% endif %}
Expand Down
Loading