2016-09-13 22:40:13 +02:00
|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
import os
|
|
|
|
import re
|
2016-10-19 11:37:32 +02:00
|
|
|
import hashlib
|
2016-09-13 22:40:13 +02:00
|
|
|
from typing import Any, Optional
|
|
|
|
from importlib import import_module
|
|
|
|
from six import text_type
|
|
|
|
from six.moves import cStringIO as StringIO
|
|
|
|
|
|
|
|
from django.db import connections, DEFAULT_DB_ALIAS
|
|
|
|
from django.apps import apps
|
|
|
|
from django.core.management import call_command
|
|
|
|
from django.utils.module_loading import module_has_submodule
|
|
|
|
|
2016-10-21 12:48:15 +02:00
|
|
|
FILENAME_SPLITTER = re.compile('[\W\-_]')
|
|
|
|
TEST_DB_STATUS_DIR = 'var/test_db_status'
|
|
|
|
|
2016-09-13 22:40:13 +02:00
|
|
|
def database_exists(database_name, **options):
|
|
|
|
# type: (text_type, **Any) -> bool
|
|
|
|
db = options.get('database', DEFAULT_DB_ALIAS)
|
|
|
|
connection = connections[db]
|
|
|
|
|
|
|
|
with connection.cursor() as cursor:
|
|
|
|
cursor.execute("SELECT 1 from pg_database WHERE datname='{}';".format(database_name))
|
|
|
|
return_value = bool(cursor.fetchone())
|
|
|
|
connections.close_all()
|
|
|
|
return return_value
|
|
|
|
|
|
|
|
def get_migration_status(**options):
|
|
|
|
# type: (**Any) -> str
|
|
|
|
verbosity = options.get('verbosity', 1)
|
|
|
|
|
|
|
|
for app_config in apps.get_app_configs():
|
|
|
|
if module_has_submodule(app_config.module, "management"):
|
|
|
|
import_module('.management', app_config.name)
|
|
|
|
|
|
|
|
app_labels = [options['app_label']] if options.get('app_label') else None
|
|
|
|
db = options.get('database', DEFAULT_DB_ALIAS)
|
|
|
|
out = StringIO()
|
|
|
|
call_command(
|
|
|
|
'showmigrations',
|
|
|
|
'--list',
|
|
|
|
app_labels=app_labels,
|
|
|
|
database=db,
|
|
|
|
no_color=options.get('no_color', False),
|
|
|
|
settings=options.get('settings', os.environ['DJANGO_SETTINGS_MODULE']),
|
|
|
|
stdout=out,
|
|
|
|
traceback=options.get('traceback', True),
|
|
|
|
verbosity=verbosity,
|
|
|
|
)
|
|
|
|
connections.close_all()
|
|
|
|
out.seek(0)
|
|
|
|
output = out.read()
|
|
|
|
return re.sub('\x1b\[(1|0)m', '', output)
|
|
|
|
|
|
|
|
def are_migrations_the_same(migration_file, **options):
|
|
|
|
# type: (text_type, **Any) -> bool
|
|
|
|
if not os.path.exists(migration_file):
|
|
|
|
return False
|
|
|
|
|
|
|
|
with open(migration_file) as f:
|
|
|
|
migration_content = f.read()
|
|
|
|
return migration_content == get_migration_status(**options)
|
|
|
|
|
2016-10-21 12:48:15 +02:00
|
|
|
def _get_hash_file_path(source_file_path):
|
|
|
|
# type: (str) -> str
|
|
|
|
basename = os.path.basename(source_file_path)
|
|
|
|
filename = '_'.join(FILENAME_SPLITTER.split(basename)).lower()
|
|
|
|
return os.path.join(TEST_DB_STATUS_DIR, filename)
|
|
|
|
|
2016-10-21 12:49:14 +02:00
|
|
|
def _check_hash(target_hash_file, **options):
|
|
|
|
# type: (str, **Any) -> bool
|
2016-10-19 11:37:32 +02:00
|
|
|
"""
|
|
|
|
This function has a side effect of creating a new hash file or
|
|
|
|
updating the old hash file.
|
|
|
|
"""
|
2016-10-21 12:48:15 +02:00
|
|
|
source_hash_file = _get_hash_file_path(target_hash_file)
|
|
|
|
|
2016-10-19 11:37:32 +02:00
|
|
|
with open(target_hash_file) as f:
|
|
|
|
target_hash_content = hashlib.sha1(f.read().encode('utf8')).hexdigest()
|
|
|
|
|
2016-10-21 07:21:39 +02:00
|
|
|
if os.path.exists(source_hash_file):
|
|
|
|
with open(source_hash_file) as f:
|
|
|
|
source_hash_content = f.read().strip()
|
|
|
|
else:
|
|
|
|
source_hash_content = None
|
2016-10-19 11:37:32 +02:00
|
|
|
|
2016-10-21 13:37:42 +02:00
|
|
|
with open(source_hash_file, 'w') as f:
|
2016-10-19 11:37:32 +02:00
|
|
|
f.write(target_hash_content)
|
2016-10-21 13:37:42 +02:00
|
|
|
|
|
|
|
return source_hash_content == target_hash_content
|
2016-10-19 11:37:32 +02:00
|
|
|
|
2016-09-13 22:40:13 +02:00
|
|
|
def is_template_database_current(
|
|
|
|
database_name='zulip_test_template',
|
|
|
|
migration_status='var/migration-status',
|
2016-10-19 11:37:32 +02:00
|
|
|
settings='zproject.test_settings',
|
2016-10-21 12:49:14 +02:00
|
|
|
check_files=None
|
2016-09-13 22:40:13 +02:00
|
|
|
):
|
2016-10-21 12:49:14 +02:00
|
|
|
# type: (Optional[text_type], Optional[text_type], Optional[text_type], Optional[List[str]]) -> bool
|
|
|
|
# Using str type for check_files because re.split doesn't accept unicode
|
|
|
|
if check_files is None:
|
|
|
|
check_files = [
|
|
|
|
'zilencer/management/commands/populate_db.py',
|
|
|
|
'tools/setup/postgres-init-test-db',
|
2016-10-21 12:05:18 +02:00
|
|
|
'tools/setup/postgres-init-dev-db',
|
2016-10-21 12:49:14 +02:00
|
|
|
]
|
2016-10-21 12:48:15 +02:00
|
|
|
|
|
|
|
if not os.path.exists(TEST_DB_STATUS_DIR):
|
|
|
|
os.mkdir(TEST_DB_STATUS_DIR)
|
|
|
|
|
2016-09-13 22:40:13 +02:00
|
|
|
if database_exists(database_name):
|
2016-10-21 12:49:14 +02:00
|
|
|
# To ensure Python evaluates all the hash tests (and thus creates the
|
2016-10-19 11:37:32 +02:00
|
|
|
# hash files about the current state), we evaluate them in a
|
2016-10-21 12:49:14 +02:00
|
|
|
# list and then process the result
|
|
|
|
hash_status = all([_check_hash(fn) for fn in check_files])
|
|
|
|
return are_migrations_the_same(migration_status, settings=settings) and hash_status
|
2016-10-19 11:37:32 +02:00
|
|
|
|
2016-09-13 22:40:13 +02:00
|
|
|
return False
|