def test_evolve_with_hinted(self):
        """Testing Evolver.evolve with hinting"""
        model_sig = ModelSignature.from_model(EvolverTestModel)
        model_sig.get_field_sig('value').field_attrs['max_length'] = 50

        app_sig = AppSignature(app_id='tests')
        app_sig.add_model_sig(model_sig)

        orig_version = Version.objects.current_version()
        orig_version.signature.add_app_sig(app_sig)
        orig_version.save()

        with ensure_test_db(model_entries=[('TestModel', EvolverTestModel)]):
            evolver = Evolver(hinted=True)
            evolver.queue_evolve_app(evo_test)
            evolver.evolve()

        self.assertTrue(evolver.evolved)

        version = Version.objects.current_version()
        self.assertNotEqual(version, orig_version)
        self.assertTrue(version.is_hinted())

        model_sig = (
            version.signature.get_app_sig('tests').get_model_sig('TestModel'))
        self.assertEqual(
            model_sig.get_field_sig('value').field_attrs['max_length'], 100)
예제 #2
0
    def test_signature_save(self):
        """Testing Version.signature field serializes JSON-encoded v2
        signatures
        """
        project_sig = ProjectSignature()
        project_sig.add_app_sig(AppSignature('app1'))
        project_sig.add_app_sig(AppSignature('app2'))

        version = Version.objects.create(signature=project_sig)

        raw_signature = (Version.objects.filter(
            pk=version.pk).values_list('signature'))[0][0]

        self.assertTrue(raw_signature.startswith('json!'))
        sig_data = json.loads(raw_signature[len('json!'):])

        self.assertEqual(
            sig_data, {
                '__version__': 2,
                'apps': {
                    'app1': {
                        'legacy_app_label': 'app1',
                        'models': {},
                    },
                    'app2': {
                        'legacy_app_label': 'app2',
                        'models': {},
                    },
                },
            })
예제 #3
0
    def test_evolve_with_hinted(self):
        """Testing Evolver.evolve with hinting"""
        model_sig = ModelSignature.from_model(EvolverTestModel)
        model_sig.get_field_sig('value').field_attrs['max_length'] = 50

        app_sig = AppSignature(app_id='tests')
        app_sig.add_model_sig(model_sig)

        orig_version = Version.objects.current_version()
        orig_version.signature.add_app_sig(app_sig)
        orig_version.save()

        with ensure_test_db(model_entries=[('TestModel', EvolverTestModel)]):
            evolver = Evolver(hinted=True)
            evolver.queue_evolve_app(evo_test)
            evolver.evolve()

        self.assertTrue(evolver.evolved)

        version = Version.objects.current_version()
        self.assertNotEqual(version, orig_version)
        self.assertTrue(version.is_hinted())

        model_sig = (
            version.signature
            .get_app_sig('tests')
            .get_model_sig('TestModel')
        )
        self.assertEqual(
            model_sig.get_field_sig('value').field_attrs['max_length'],
            100)
    def test_evolve(self):
        """Testing Evolver.evolve"""
        model_sig = ModelSignature.from_model(EvolverTestModel)
        model_sig.get_field_sig('value').field_attrs['max_length'] = 50

        app_sig = AppSignature(app_id='tests')
        app_sig.add_model_sig(model_sig)

        orig_version = Version.objects.current_version()
        orig_version.signature.add_app_sig(app_sig)
        orig_version.save()

        with ensure_test_db(model_entries=[('TestModel', EvolverTestModel)]):
            evolver = Evolver()
            evolver.queue_task(
                EvolveAppTask(evolver=evolver,
                              app=evo_test,
                              evolutions=[
                                  {
                                      'label':
                                      'my_evolution1',
                                      'mutations': [
                                          ChangeField('TestModel',
                                                      'value',
                                                      max_length=200),
                                      ],
                                  },
                                  {
                                      'label':
                                      'my_evolution2',
                                      'mutations': [
                                          AddField('TestModel',
                                                   'new_field',
                                                   models.BooleanField,
                                                   null=True),
                                      ],
                                  },
                              ]))
            evolver.evolve()

        self.assertTrue(evolver.evolved)

        version = Version.objects.current_version()
        self.assertNotEqual(version, orig_version)
        self.assertFalse(version.is_hinted())

        evolutions = list(version.evolutions.all())
        self.assertEqual(len(evolutions), 2)
        self.assertEqual(evolutions[0].app_label, 'tests')
        self.assertEqual(evolutions[0].label, 'my_evolution1')
        self.assertEqual(evolutions[1].app_label, 'tests')
        self.assertEqual(evolutions[1].label, 'my_evolution2')

        model_sig = (
            version.signature.get_app_sig('tests').get_model_sig('TestModel'))
        self.assertEqual(
            model_sig.get_field_sig('value').field_attrs['max_length'], 200)
        self.assertIsNotNone(model_sig.get_field_sig('new_field'))
예제 #5
0
    def test_enable_extension_evolve_with_pending_evolutions(self):
        """Testing ExtensionManager.enable_extension evolves database models
        when pending evolutions found
        """
        from django_evolution.models import Version
        from django_evolution.signature import AppSignature, ModelSignature

        self.spy_on(Evolver.evolve,
                    owner=Evolver)

        class TestExtensionWithApps(Extension):
            apps = [
                'djblets.extensions.tests.apps.evolve_tests',
            ]

        # We need to set some initial state in the database for the model and
        # for the evolution history.
        connection.cursor().execute(
            'CREATE TABLE evolve_tests_testevolveextensionmodel ('
            '    id INTEGER PRIMARY KEY AUTOINCREMENT,'
            '    test_field VARCHAR(16) NOT NULL'
            ')')

        from djblets.extensions.tests.apps.evolve_tests.models import \
            TestEvolveExtensionModel

        latest_version = Version.objects.current_version()

        model_sig = ModelSignature.from_model(TestEvolveExtensionModel)
        model_sig.remove_field_sig('new_field')

        app_sig = AppSignature(app_id='evolve_tests')
        app_sig.add_model_sig(model_sig)

        latest_version.signature.add_app_sig(app_sig)
        latest_version.save()

        # We can now enable the extension, which will perform an evolution.
        extension = self.setup_extension(TestExtensionWithApps)

        self.assertTrue(Evolver.evolve.called)

        # We should be able to create entries and query them.
        TestEvolveExtensionModel.objects.create(test_field='test')
        self.assertEqual(TestEvolveExtensionModel.objects.count(), 1)

        # We're now going to shut down and re-enable, but with a different
        # version of the model. This should trigger an evolution sequence.
        self.manager.disable_extension(extension.id)

        self.manager.enable_extension(extension.id)

        TestEvolveExtensionModel.objects.create(test_field='test',
                                                new_field=100)
        self.assertEqual(TestEvolveExtensionModel.objects.count(), 2)

        obj = TestEvolveExtensionModel.objects.get(pk=2)
        self.assertEqual(obj.new_field, 100)
예제 #6
0
    def test_evolve(self):
        """Testing Evolver.evolve"""
        model_sig = ModelSignature.from_model(EvolverTestModel)
        model_sig.get_field_sig('value').field_attrs['max_length'] = 50

        app_sig = AppSignature(app_id='tests')
        app_sig.add_model_sig(model_sig)

        orig_version = Version.objects.current_version()
        orig_version.signature.add_app_sig(app_sig)
        orig_version.save()

        with ensure_test_db(model_entries=[('TestModel', EvolverTestModel)]):
            evolver = Evolver()
            evolver.queue_task(EvolveAppTask(
                evolver=evolver,
                app=evo_test,
                evolutions=[
                    {
                        'label': 'my_evolution1',
                        'mutations': [
                            ChangeField('TestModel', 'value', max_length=200),
                        ],
                    },
                    {
                        'label': 'my_evolution2',
                        'mutations': [
                            AddField('TestModel', 'new_field',
                                     models.BooleanField, null=True),
                        ],
                    },
                ]))
            evolver.evolve()

        self.assertTrue(evolver.evolved)

        version = Version.objects.current_version()
        self.assertNotEqual(version, orig_version)
        self.assertFalse(version.is_hinted())

        evolutions = list(version.evolutions.all())
        self.assertEqual(len(evolutions), 2)
        self.assertEqual(evolutions[0].app_label, 'tests')
        self.assertEqual(evolutions[0].label, 'my_evolution1')
        self.assertEqual(evolutions[1].app_label, 'tests')
        self.assertEqual(evolutions[1].label, 'my_evolution2')

        model_sig = (
            version.signature
            .get_app_sig('tests')
            .get_model_sig('TestModel')
        )
        self.assertEqual(
            model_sig.get_field_sig('value').field_attrs['max_length'],
            200)
        self.assertIsNotNone(model_sig.get_field_sig('new_field'))
    def setUp(self):
        super(PurgeAppTaskTests, self).setUp()

        app_sig = AppSignature(app_id='tests')
        app_sig.add_model_sig(
            ModelSignature(model_name='TestModel',
                           table_name='tests_testmodel'))

        version = Version.objects.current_version()
        version.signature.add_app_sig(app_sig)
        version.save()
예제 #8
0
    def setUp(self):
        super(PurgeAppTaskTests, self).setUp()

        app_sig = AppSignature(app_id='tests')
        app_sig.add_model_sig(ModelSignature(
            model_name='TestModel',
            table_name='tests_testmodel'))

        version = Version.objects.current_version()
        version.signature.add_app_sig(app_sig)
        version.save()
    def setUp(self):
        super(EvolveAppTaskTests, self).setUp()

        model_sig = ModelSignature.from_model(EvolverTestModel)
        model_sig.get_field_sig('value').field_attrs['max_length'] = 50

        app_sig = AppSignature(app_id='tests')
        app_sig.add_model_sig(model_sig)

        version = Version.objects.current_version()
        version.signature.add_app_sig(app_sig)
        version.save()
예제 #10
0
    def setUp(self):
        super(EvolveAppTaskTests, self).setUp()

        model_sig = ModelSignature.from_model(EvolverTestModel)
        model_sig.get_field_sig('value').field_attrs['max_length'] = 50

        app_sig = AppSignature(app_id='tests')
        app_sig.add_model_sig(model_sig)

        version = Version.objects.current_version()
        version.signature.add_app_sig(app_sig)
        version.save()
    def test_queue_purge_old_apps(self):
        """Testing Evolver.queue_purge_old_apps"""
        version = Version.objects.current_version()
        version.signature.add_app_sig(AppSignature(app_id='old_app1'))
        version.signature.add_app_sig(AppSignature(app_id='old_app2'))
        version.save()

        evolver = Evolver()
        evolver.queue_purge_old_apps()

        tasks = list(evolver.tasks)
        self.assertEqual(len(tasks), 2)
        self.assertIsInstance(tasks[0], PurgeAppTask)
        self.assertIsInstance(tasks[1], PurgeAppTask)
        self.assertEqual(tasks[0].app_label, 'old_app1')
        self.assertEqual(tasks[1].app_label, 'old_app2')
예제 #12
0
    def test_enable_extension_evolve_with_applied_evolutions(self):
        """Testing ExtensionManager.enable_extension evolves database models
        when all evolutions are already applied
        """
        from django_evolution.models import Evolution, Version
        from django_evolution.signature import AppSignature, ModelSignature

        self.spy_on(Evolver.evolve,
                    owner=Evolver)

        class TestExtensionWithApps(Extension):
            apps = [
                'djblets.extensions.tests.apps.evolve_tests',
            ]

        # We need to set some initial state in the database for the model and
        # for the evolution history.
        connection.cursor().execute(
            'CREATE TABLE evolve_tests_testevolveextensionmodel ('
            '    id INTEGER PRIMARY KEY AUTOINCREMENT,'
            '    test_field VARCHAR(16) NOT NULL'
            ')')

        from djblets.extensions.tests.apps.model_tests.models import \
            TestExtensionModel

        latest_version = Version.objects.current_version()
        signature = latest_version.signature.clone()

        model_sig = ModelSignature.from_model(TestExtensionModel)
        model_sig.model_name = 'TestEvolveExtensionModel'
        model_sig.table_name = 'evolve_tests_testevolveextensionmodel'

        app_sig = AppSignature(app_id='evolve_tests')
        app_sig.add_model_sig(model_sig)
        signature.add_app_sig(app_sig)

        version = Version.objects.create(signature=signature)
        Evolution.objects.create(version=version,
                                 app_label='evolve_tests',
                                 label='add_new_field')

        # We can now enable the extension, which will perform an evolution.
        self.setup_extension(TestExtensionWithApps)

        self.assertFalse(Evolver.evolve.called)
예제 #13
0
    def test_from_app_sig(self):
        """Testing MigrationList.from_app_sig"""
        app_sig = AppSignature(
            app_id='tests',
            applied_migrations=['0001_initial', '0002_stuff'])

        migration_list = MigrationList.from_app_sig(app_sig)
        self.assertTrue(migration_list.has_migration_info(app_label='tests',
                                                          name='0001_initial'))
        self.assertTrue(migration_list.has_migration_info(app_label='tests',
                                                          name='0002_stuff'))
예제 #14
0
    def test_with_bad_field(self):
        """Testing ChangeField with field not in signature"""
        mutation = ChangeField('TestModel', 'char_field1')

        model_sig = ModelSignature(model_name='TestModel',
                                   table_name='tests_testmodel')

        app_sig = AppSignature(app_id='tests')
        app_sig.add_model_sig(model_sig)

        project_sig = ProjectSignature()
        project_sig.add_app_sig(app_sig)

        message = ('Cannot change the field "char_field1" on model '
                   '"tests.TestModel". The field could not be found in the '
                   'signature.')

        with self.assertRaisesMessage(SimulationFailure, message):
            mutation.run_simulation(app_label='tests',
                                    project_sig=project_sig,
                                    database_state=None)
    def test_with_bad_field(self):
        """Testing RenameField with field not in signature"""
        mutation = RenameField('TestModel', 'char_field1', 'char_field2')

        model_sig = ModelSignature(model_name='TestModel',
                                   table_name='tests_testmodel')

        app_sig = AppSignature(app_id='tests')
        app_sig.add_model_sig(model_sig)

        project_sig = ProjectSignature()
        project_sig.add_app_sig(app_sig)

        message = (
            'Cannot rename the field "char_field1" on model '
            '"tests.TestModel". The field could not be found in the '
            'signature.'
        )

        with self.assertRaisesMessage(SimulationFailure, message):
            mutation.run_simulation(app_label='tests',
                                    project_sig=project_sig,
                                    database_state=None)
    def test_queue_purge_app_with_already_queued(self):
        """Testing Evolver.queue_purge_app with app purge already queued"""
        version = Version.objects.current_version()
        version.signature.add_app_sig(AppSignature(app_id='old_app'))
        version.save()

        evolver = Evolver()
        evolver.queue_purge_app('old_app')

        message = '"old_app" is already being tracked for purging'

        with self.assertRaisesMessage(EvolutionTaskAlreadyQueuedError,
                                      message):
            evolver.queue_purge_app('old_app')
    def test_with_bad_model(self):
        """Testing DeleteModel with model not in signature"""
        mutation = DeleteModel('TestModel')

        project_sig = ProjectSignature()
        project_sig.add_app_sig(AppSignature(app_id='tests'))

        message = (
            'Cannot delete the model "tests.TestModel". The model could '
            'not be found in the signature.')

        with self.assertRaisesMessage(SimulationFailure, message):
            mutation.run_simulation(app_label='tests',
                                    project_sig=project_sig,
                                    database_state=None)
예제 #18
0
    def test_with_bad_model(self):
        """Testing AddField with model not in signature"""
        mutation = AddField('TestModel', 'char_field1', models.CharField)

        project_sig = ProjectSignature()
        project_sig.add_app_sig(AppSignature(app_id='tests'))

        message = (
            'Cannot add the field "char_field1" to model "tests.TestModel". '
            'The model could not be found in the signature.')

        with self.assertRaisesMessage(SimulationFailure, message):
            mutation.run_simulation(app_label='tests',
                                    project_sig=project_sig,
                                    database_state=None)
예제 #19
0
    def test_with_bad_field(self):
        """Testing AddField with field already in signature"""
        mutation = AddField('TestModel', 'char_field1', models.CharField)

        model_sig = ModelSignature(model_name='TestModel',
                                   table_name='tests_testmodel')
        model_sig.add_field_sig(
            FieldSignature(field_name='char_field1',
                           field_type=models.CharField))

        app_sig = AppSignature(app_id='tests')
        app_sig.add_model_sig(model_sig)

        project_sig = ProjectSignature()
        project_sig.add_app_sig(app_sig)

        message = (
            'Cannot add the field "char_field1" to model "tests.TestModel". '
            'A field with this name already exists.')

        with self.assertRaisesMessage(SimulationFailure, message):
            mutation.run_simulation(app_label='tests',
                                    project_sig=project_sig,
                                    database_state=None)
예제 #20
0
    def test_with_bad_field(self):
        """Testing AddField with field already in signature"""
        mutation = AddField('TestModel', 'char_field1', models.CharField)

        model_sig = ModelSignature(model_name='TestModel',
                                   table_name='tests_testmodel')
        model_sig.add_field_sig(FieldSignature(field_name='char_field1',
                                               field_type=models.CharField))

        app_sig = AppSignature(app_id='tests')
        app_sig.add_model_sig(model_sig)

        project_sig = ProjectSignature()
        project_sig.add_app_sig(app_sig)

        message = (
            'Cannot add the field "char_field1" to model "tests.TestModel". '
            'A field with this name already exists.'
        )

        with self.assertRaisesMessage(SimulationFailure, message):
            mutation.run_simulation(app_label='tests',
                                    project_sig=project_sig,
                                    database_state=None)
    def test_queue_purge_app_after_prepared(self):
        """Testing Evolver.queue_purge_app after tasks were already prepared"""
        version = Version.objects.current_version()
        version.signature.add_app_sig(AppSignature(app_id='old_app'))
        version.save()

        evolver = Evolver()

        # Force preparation of tasks.
        list(evolver.tasks)

        message = ('Evolution tasks have already been prepared. New tasks '
                   'cannot be added.')

        with self.assertRaisesMessage(QueueEvolverTaskError, message):
            evolver.queue_purge_app('old_app')
예제 #22
0
def create_test_project_sig(models, app_label='tests', version=1):
    """Return a dummy project signature for the given models.

    Args:
        models (list of django.db.models.Model):
            The list of models for the project signature.

        app_label (unicode, optional):
            The application label that will contain the models.

        version (int, optional):
            The signature version to use for the project signature.

    Returns:
        dict:
        The new project signature.
    """
    app_sig = AppSignature(app_id=app_label)

    project_sig = ProjectSignature()
    project_sig.add_app_sig(app_sig)

    for full_name, model in models:
        parts = full_name.split('.')

        if len(parts) == 1:
            app_sig.add_model_sig(ModelSignature.from_model(model))
        else:
            model_app_label, model_name = parts
            model_app_sig = project_sig.get_app_sig(model_app_label)

            if model_app_sig is None:
                model_app_sig = AppSignature(app_id=model_app_label)
                project_sig.add_app_sig(model_app_sig)

            model_app_sig.add_model_sig(ModelSignature.from_model(model))

    return project_sig
예제 #23
0
def create_test_project_sig(models, app_label='tests', version=1):
    """Return a dummy project signature for the given models.

    Args:
        models (list of django.db.models.Model):
            The list of models for the project signature.

        app_label (unicode, optional):
            The application label that will contain the models.

        version (int, optional):
            The signature version to use for the project signature.

    Returns:
        dict:
        The new project signature.
    """
    app_sig = AppSignature(app_id=app_label)

    project_sig = ProjectSignature()
    project_sig.add_app_sig(app_sig)

    for full_name, model in models:
        parts = full_name.split('.')

        if len(parts) == 1:
            app_sig.add_model_sig(ModelSignature.from_model(model))
        else:
            model_app_label, model_name = parts
            model_app_sig = project_sig.get_app_sig(model_app_label)

            if model_app_sig is None:
                model_app_sig = AppSignature(app_id=model_app_label)
                project_sig.add_app_sig(model_app_sig)

            model_app_sig.add_model_sig(ModelSignature.from_model(model))

    return project_sig
예제 #24
0
    def simulate(self, simulation):
        """Simulate the mutation.

        This will alter the signature to make any changes needed for the
        application's evolution storage.
        """
        old_app_label = self.old_app_label
        new_app_label = self.new_app_label
        model_names = self.model_names

        project_sig = simulation.project_sig
        old_app_sig = project_sig.get_app_sig(old_app_label, required=True)

        # Begin building the new AppSignature. For at least a short time, both
        # the old and new will exist, as we begin moving some or all of the old
        # to the new. The old will only be removed if it's empty after the
        # rename (so that we don't get rid of anything if there's two apps
        # sharing the same old app ID in the signature).
        new_app_sig = AppSignature(app_id=new_app_label,
                                   legacy_app_label=self.legacy_app_label,
                                   upgrade_method=UpgradeMethod.EVOLUTIONS)
        project_sig.add_app_sig(new_app_sig)

        if model_names is None:
            # Move over every single model listed under the app's signature.
            model_sigs = [
                model_sig
                for model_sig in old_app_sig.model_sigs
            ]
        else:
            # Move over only the requested models, in case the app signature
            # has the contents of two separate apps merged. Each will be
            # validated by way of simulation.get_model_sig.
            model_sigs = [
                simulation.get_model_sig(model_name)
                for model_name in model_names
            ]

        # Copy over the models.
        for model_sig in model_sigs:
            old_app_sig.remove_model_sig(model_sig.model_name)
            new_app_sig.add_model_sig(model_sig)

        if old_app_sig.is_empty():
            # The old app is now empty. We can remove the signature.
            project_sig.remove_app_sig(old_app_sig.app_id)

        # Update the simulation to refer to the new label.
        simulation.app_label = new_app_label

        # Go through the model signatures and update any that have a
        # related_model property referencing the old app label.
        for cur_app_sig in project_sig.app_sigs:
            for cur_model_sig in cur_app_sig.model_sigs:
                for cur_field_sig in cur_model_sig.field_sigs:
                    if cur_field_sig.related_model:
                        parts = cur_field_sig.related_model.split('.', 1)[1]

                        if parts[0] == old_app_label:
                            cur_field_sig.related_model = \
                                '%s.%s' % (new_app_label, parts[1])
예제 #25
0
    def __init__(self,
                 hinted=False,
                 verbosity=0,
                 interactive=False,
                 database_name=DEFAULT_DB_ALIAS):
        """Initialize the evolver.

        Args:
            hinted (bool, optional):
                Whether to operate against hinted evolutions. This may
                result in changes to the database without there being any
                accompanying evolution files backing those changes.

            verbosity (int, optional):
                The verbosity level for any output. This is passed along to
                signal emissions.

            interactive (bool, optional):
                Whether the evolution operations are being performed in a
                way that allows interactivity on the command line. This is
                passed along to signal emissions.

            database_name (unicode, optional):
                The name of the database to evolve.

        Raises:
            django_evolution.errors.EvolutionBaselineMissingError:
                An initial baseline for the project was not yet installed.
                This is due to ``syncdb``/``migrate`` not having been run.
        """
        self.database_name = database_name
        self.hinted = hinted
        self.verbosity = verbosity
        self.interactive = interactive

        self.evolved = False
        self.initial_diff = None
        self.project_sig = None
        self.version = None
        self.installed_new_database = False

        self.connection = connections[database_name]

        if hasattr(self.connection, 'prepare_database'):
            # Django >= 1.8
            self.connection.prepare_database()

        self.database_state = DatabaseState(self.database_name)
        self.target_project_sig = \
            ProjectSignature.from_database(database_name)

        self._tasks_by_class = OrderedDict()
        self._tasks_by_id = OrderedDict()
        self._tasks_prepared = False

        latest_version = None

        if self.database_state.has_model(Version):
            try:
                latest_version = \
                    Version.objects.current_version(using=database_name)
            except Version.DoesNotExist:
                # We'll populate this next.
                pass

        if latest_version is None:
            # Either the models aren't yet synced to the database, or we
            # don't have a saved project signature, so let's set these up.
            self.installed_new_database = True

            self.project_sig = ProjectSignature()
            app = get_app('django_evolution')

            task = EvolveAppTask(evolver=self, app=app)
            task.prepare(hinted=False)

            with self.sql_executor() as sql_executor:
                task.execute(sql_executor=sql_executor, create_models_now=True)

            self.database_state.rescan_tables()

            app_sig = AppSignature.from_app(app=app, database=database_name)
            self.project_sig.add_app_sig(app_sig)

            # Let's make completely sure that we've only found the models
            # we expect. This is mostly for the benefit of unit tests.
            model_names = set(model_sig.model_name
                              for model_sig in app_sig.model_sigs)
            expected_model_names = set(['Evolution', 'Version'])

            assert model_names == expected_model_names, (
                'Unexpected models found for django_evolution app: %s' %
                ', '.join(model_names - expected_model_names))

            self._save_project_sig(new_evolutions=task.new_evolutions)
            latest_version = self.version

        self.project_sig = latest_version.signature
        self.initial_diff = Diff(self.project_sig, self.target_project_sig)