-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
This pre-commit hook checks the migration files in the current repository and ensures that they all have a `safe` property defined. While we can think of easy ways to be break this, for all the use-cases I've had this covers it quite nicely, so I think it's worth merging even if we think it's a bit rough. Tim Schilling wrote this in #39 and I'm very happy to take that almost verbatim and prove it. The only thing I changed was to make a new function to handle sys.exit(), so that I could set it as a console_script to use with pre-commit.
- Loading branch information
1 parent
e4e5300
commit 714b06e
Showing
5 changed files
with
83 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -10,3 +10,4 @@ db.sqlite3 | |
htmlcov/ | ||
dist/ | ||
.tox/ | ||
.venv/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -52,3 +52,7 @@ repos: | |
rev: v0.12.1 | ||
hooks: | ||
- id: validate-pyproject | ||
- repo: . | ||
rev: HEAD | ||
hooks: | ||
- id: check |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
- id: check | ||
name: Mark Migrations for Safety | ||
description: Ensure that all local migrations have been marked for safety. | ||
entry: safemigrate-check | ||
language: python | ||
types: [text] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
""" | ||
Search our application's migrations are using django_safemigrate. | ||
This is fairly rudimentary and won't work if the class doesn't explicitly inherit | ||
from ``Migration``. | ||
""" | ||
|
||
import os | ||
import re | ||
import sys | ||
|
||
MIGRATION_PATTERN = re.compile(r"class\s+(?P<MigrationClass>\w+)\s?\(.*Migration\):") | ||
MIGRATION_FILE_PATTERN = re.compile(r"^\d{4}_.+\.py$") | ||
|
||
MISSING_SAFE_MESSAGE = ( | ||
"{file_path}: {migration_class} is missing the 'safe' attribute.\n" | ||
) | ||
FAILURE_MESSAGE = ( | ||
"\n" | ||
"Add the following to the migration class:\n" | ||
"\n" | ||
"from django_safemigrate import Safe\n" | ||
"class Migration(migrations.Migration):\n" | ||
" safe = Safe.before_deploy\n" | ||
"\n" | ||
"You can also use the following:\n" | ||
" safe = Safe.always\n" | ||
" safe = Safe.after_deploy\n" | ||
) | ||
|
||
|
||
def find_migration_files(directory): | ||
for root, _, files in os.walk(directory): | ||
for file in files: | ||
file_is_migration = MIGRATION_FILE_PATTERN.match(file) | ||
if file_is_migration and file.endswith(".py"): | ||
yield os.path.join(root, file) | ||
|
||
|
||
def validate_migrations(): | ||
success = True | ||
for file_path in find_migration_files("."): | ||
# Strip the leading ./ it's a byproduct of os.walk on the current directory | ||
file_path = file_path[2:] if file_path.startswith("./") else file_path | ||
with open(file_path, "r") as f: | ||
content = f.read() | ||
|
||
match = MIGRATION_PATTERN.search(content) | ||
if match: | ||
migration_class = match.group("MigrationClass") | ||
if "safe = Safe." not in content: | ||
success = False | ||
sys.stdout.write( | ||
MISSING_SAFE_MESSAGE.format( | ||
file_path=file_path, migration_class=migration_class | ||
) | ||
) | ||
if not success: | ||
sys.stdout.write(FAILURE_MESSAGE) | ||
return success | ||
|
||
|
||
def check(): | ||
if not validate_migrations(): | ||
sys.exit(1) | ||
|
||
|
||
if __name__ == "__main__": | ||
check() |