def test_empty_string_migration_id_is_error():
    class Migration_A(Migration):
        migration_id = ''

    with pytest.raises(ValueError):
        validate_migration_classes([
            Migration_A,
        ])
def test_null_migration_id_is_error():
    class Migration_A(Migration):
        migration_id = None

    with pytest.raises(ValueError):
        validate_migration_classes([
            Migration_A,
        ])
def test_self_dependency_is_error():
    """
    Test circular dependencies are handled.
    """
    class Migration_A(Migration):
        migration_id = "A"
        dependencies = ["A"]

    with pytest.raises(ValueError):
        validate_migration_classes([
            Migration_A,
        ])
def test_migration_ids_must_be_unique():
    class Migration_A(Migration):
        migration_id = 'duplicate-id'

    class Migration_B(Migration):
        migration_id = 'duplicate-id'

    with pytest.raises(ValueError):
        validate_migration_classes([
            Migration_A,
            Migration_B,
        ])
Beispiel #5
0
def migrate(ctx, chain_name):
    """
    Run project migrations
    """
    project = ctx.obj['PROJECT']

    if not project.migrations:
        raise click.ClickException((
            "The project does not appear to have any migrations.  You can use "
            "the `populus makemigration` command to generate project migrations"
        ))

    # Validate the project migrations
    validate_migration_classes(project.migrations)

    # Determine which chain should be used.
    if not chain_name:
        chain_name = select_chain(project)

    chain_config = project.config.chains[chain_name]

    # Determine if the chain is *migratable*
    if 'registrar' not in chain_config:
        # TODO: this should be a property of the chain object itself.
        # Something like `chain.is_ready_for_migrations`.
        if chain_name not in {'testrpc', 'temp'}:
            # ignore `testrpc` and `temp` because they lazily create their
            # registrar contracts.
            # TODO: We can present the use with the option to just initialize
            # the chain right here rather than throwing an error.
            initialize_chain_prompt = (
                "The chain '{0}' is not configured with a registrar contract "
                "address.  Would you like to deploy one now?".format(
                    chain_name,
                )
            )
            if click.confirm(initialize_chain_prompt, default=True):
                from .chain_cmd import chain_init
                ctx.invoke(chain_init, chain_name=chain_name)
            else:
                no_registrar_message = (
                    "In order to use the migrations functionality, a registrar "
                    "contract is required.  You can initialize this chain with a "
                    "registrar using the command `$ populus chain init "
                    "{name}`".format(name=chain_name)
                )
                click.echo(no_registrar_message, err=True)
                click.exit(1)

    with project.get_chain(chain_name) as chain:
        if chain_name in {'mainnet', 'morden'}:
            show_chain_sync_progress(chain)

        account = get_unlocked_deploy_from_address(chain)
        chain.web3.eth.defaultAccount = account

        # Wait for chain sync if this is a public network.
        if chain_name in {'mainnet', 'morden'}:
            show_chain_sync_progress(chain)

        # Determine if we have any migrations to run.
        migrations_to_execute = get_migration_classes_for_execution(
            project.migrations,
            chain,
        )

        if not migrations_to_execute:
            click.echo("All migrations have been run.")
            ctx.exit(0)

        click.echo("Migration operations to perform:")

        for migration in migrations_to_execute:
            click.echo(''.join((
                "  ",
                migration.migration_id,
                " ({0} operations)".format(len(migration.operations)),
                ":",
            )))
            for operation_index, operation in enumerate(migration.operations):
                click.echo(''.join((
                    "    ",
                    str(operation_index),
                    " - ",
                    str(operation),
                )))

        click.echo("Executing migrations:")
        for migration in migrations_to_execute:
            click.echo(''.join((
                "  ",
                migration.migration_id,
                "... ",
            )), nl=False)
            migration.execute()
            click.echo(" DONE")