From d59aa979e3ce800f3303a2e6bb1cce4bc67cc222 Mon Sep 17 00:00:00 2001 From: reehassan Date: Fri, 4 Sep 2026 16:55:46 +0500 Subject: [PATCH] Fix IntegrityError on rerun of collect_and_create_codebase_resources Rerunning a pipeline that calls collect_and_create_codebase_resources() on a project that already has resources raises a raw IntegrityError. Catch it and re-raise with a message pointing to the Reset feature. Fixes #1712 Signed-off-by: reehassan --- scanpipe/pipes/__init__.py | 18 ++++++++++++------ scanpipe/tests/pipes/test_pipes.py | 18 ++++++++++++++++++ 2 files changed, 30 insertions(+), 6 deletions(-) diff --git a/scanpipe/pipes/__init__.py b/scanpipe/pipes/__init__.py index 0951bd0000..9872541c2a 100644 --- a/scanpipe/pipes/__init__.py +++ b/scanpipe/pipes/__init__.py @@ -31,6 +31,7 @@ from itertools import islice from pathlib import Path +from django.db import IntegrityError from django.db.models import Count from scanpipe.models import AbstractTaskFieldsModel @@ -139,12 +140,17 @@ def collect_and_create_codebase_resources(project, batch_size=5000): """ model_class = CodebaseResource objs = yield_resources_from_codebase(project) - - while True: - batch = list(islice(objs, batch_size)) - if not batch: - break - model_class.objects.bulk_create(batch, batch_size) + try: + while True: + batch = list(islice(objs, batch_size)) + if not batch: + break + model_class.objects.bulk_create(batch, batch_size) + except IntegrityError as e: + raise IntegrityError( + "Codebase resources already exist for this project. " + "Reset the project before re-running." + ) from e def update_or_create_resource(project, resource_data): diff --git a/scanpipe/tests/pipes/test_pipes.py b/scanpipe/tests/pipes/test_pipes.py index ee0ab3a88f..fa425e5983 100644 --- a/scanpipe/tests/pipes/test_pipes.py +++ b/scanpipe/tests/pipes/test_pipes.py @@ -25,6 +25,7 @@ from pathlib import Path from unittest import mock +from django.db import IntegrityError from django.test import TestCase from django.test import TransactionTestCase @@ -448,3 +449,20 @@ def test_scanpipe_pipes_collect_and_create_codebase_resources(self): self.assertEqual("from", from_resource.tag) to_resource = p1.codebaseresources.get(path="to/a.txt") self.assertEqual("to", to_resource.tag) + + def test_scanpipe_pipes_collect_and_create_codebase_resources_duplicate_run(self): + p1 = Project.objects.create(name="Analysis") + input_location = self.data / "codebase" / "a.txt" + to_dir = p1.codebase_path / "to" + to_dir.mkdir() + from_dir = p1.codebase_path / "from" + from_dir.mkdir() + copy_input(input_location, to_dir) + copy_input(input_location, from_dir) + + pipes.collect_and_create_codebase_resources(p1) + self.assertEqual(4, p1.codebaseresources.count()) + + with self.assertRaises(IntegrityError) as ctx: + pipes.collect_and_create_codebase_resources(p1) + self.assertIn("Reset", str(ctx.exception))