Skip to content

Commit

Permalink
Allow ignoring sqlmigrate errors.
Browse files Browse the repository at this point in the history
  • Loading branch information
kekekekule authored and David-Wobrock committed Mar 30, 2024
1 parent a3acff2 commit b15924c
Show file tree
Hide file tree
Showing 7 changed files with 55 additions and 7 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

Feature:
- Support Django 5.0 `db_default` attribute (issue #275)
- Allow ignoring the failures of `sqlmigrate` commands, with `--ignore-sqlmigrate-errors` option (issue #274)

Bug:
- Don't detect 'IS NOT NULL' as backward incompatible changes (issue #263)
Expand Down
1 change: 1 addition & 0 deletions docs/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ If you are using a config file, replace any dashes (`-`) with an underscore (`_`
| `--quiet or -q {ok,ignore,warning,error}` | Suppress certain output messages, instead of writing them to stdout. |
| `--warnings-as-errors [MIGRATION_TEST_CODE [...]]` | Handle warnings as errors and therefore return an error status code if we should. Optionally specify migration test codes to handle as errors. When no test code specified, all warnings are handled as errors. |
| `--sql-analyser` | Specify the SQL analyser that should be used. Allowed values: 'sqlite', 'mysql', 'postgresql'. |
| `--ignore-sqlmigrate-errors` | Ignore failures of sqlmigrate commands. |

## Django settings configuration

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,7 @@ def handle(self, *args, **options):
all_warnings_as_errors=all_warnings_as_errors,
no_output=options["verbosity"] == 0,
analyser_string=options["sql_analyser"],
ignore_sqlmigrate_errors=options["ignore_sqlmigrate_errors"],
)
linter.lint_all_migrations(
app_label=options["app_label"],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ def handle(self, *app_labels, **options):
self.exclude_migrations_tests = options["exclude_migration_tests"]
self.warnings_as_errors = options["warnings_as_errors"]
self.sql_analyser = options["sql_analyser"]
self.ignore_sqlmigrate_errors = options["ignore_sqlmigrate_errors"]
configure_logging(options["verbosity"])
return super().handle(*app_labels, **options)

Expand Down Expand Up @@ -92,6 +93,7 @@ def write_migration_files(self, changes: dict[str, list[Migration]]) -> None:
warnings_as_errors_tests=warnings_as_errors_tests,
all_warnings_as_errors=all_warnings_as_errors,
analyser_string=self.sql_analyser,
ignore_sqlmigrate_errors=self.ignore_sqlmigrate_errors,
)

for app_label, app_migrations in changes.items():
Expand Down
6 changes: 6 additions & 0 deletions src/django_migration_linter/management/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,12 @@ def register_linting_configuration_options(parser: CommandParser) -> None:
help="select the SQL analyser",
)

parser.add_argument(
"--ignore-sqlmigrate-errors",
action="store_true",
help="ignore failures of sqlmigrate command",
)


def configure_logging(verbosity: int) -> None:
logger = logging.getLogger("django_migration_linter")
Expand Down
27 changes: 20 additions & 7 deletions src/django_migration_linter/migration_linter.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ def __init__(
all_warnings_as_errors: bool = False,
no_output: bool = False,
analyser_string: str | None = None,
ignore_sqlmigrate_errors: bool = False,
):
# Store parameters and options
self.django_path = path
Expand All @@ -86,6 +87,7 @@ def __init__(
settings.DATABASES[self.database]["ENGINE"],
analyser_string=analyser_string,
)
self.ignore_sqlmigrate_errors = ignore_sqlmigrate_errors

# Initialise counters
self.reset_counters()
Expand Down Expand Up @@ -321,13 +323,24 @@ def get_sql(self, app_label: str, migration_name: str) -> list[str]:
database=self.database,
stdout=dev_null,
)
except (ValueError, ProgrammingError):
logger.warning(
"Error while executing sqlmigrate on (%s, %s).",
app_label,
migration_name,
)
raise
except (ValueError, ProgrammingError) as err:
if self.ignore_sqlmigrate_errors:
logger.warning(
"Error while executing sqlmigrate on (%s, %s) with exception: %s. "
"Continuing execution with empty SQL.",
app_label,
migration_name,
str(err),
)
sql_statement = ""
else:
logger.warning(
"Error while executing sqlmigrate on (%s, %s) with exception: %s.",
app_label,
migration_name,
str(err),
)
raise
return sql_statement.splitlines()

@staticmethod
Expand Down
24 changes: 24 additions & 0 deletions tests/unit/test_linter.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@

import tempfile
import unittest
from unittest.mock import patch

from django.db import ProgrammingError
from django.db.migrations import Migration

from django_migration_linter import MigrationLinter
Expand Down Expand Up @@ -109,6 +111,28 @@ def test_read_migrations_unknown_file(self):
with self.assertRaises(Exception):
MigrationLinter.read_migrations_list(file_path)

@patch(
"django_migration_linter.migration_linter.call_command",
side_effect=ProgrammingError,
)
def test_raise_exception_on_sqlmigrate_error(self, call_command_mock):
linter = MigrationLinter(
exclude_migration_tests=[], database="mysql", ignore_sqlmigrate_errors=False
)
with self.assertRaises(ProgrammingError):
linter.get_sql("app_correct", "0002_foo")

@patch(
"django_migration_linter.migration_linter.call_command",
side_effect=ProgrammingError,
)
def test_ignore_exception_on_sqlmigrate_error(self, call_command_mock):
linter = MigrationLinter(
exclude_migration_tests=[], database="mysql", ignore_sqlmigrate_errors=True
)
sql_result = linter.get_sql("app_correct", "0002_foo")
self.assertEqual([], sql_result)

def test_read_migrations_no_file(self):
migration_list = MigrationLinter.read_migrations_list(None)
self.assertIsNone(migration_list)
Expand Down

0 comments on commit b15924c

Please sign in to comment.