Skip to content
14 changes: 7 additions & 7 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
PyYAML>=3.11
SQLAlchemy>=0.9.6
humanize>=0.5.1
schema>=0.3.1
psutil>=2.1.1
click>=3.1
SQLAlchemy-Utils>=0.26.11
PyYAML>=5.3.1
SQLAlchemy>=1.3.22
humanize>=2.6.0
schema>=0.7.2
psutil>=5.8.0
click>=7.1.2
SQLAlchemy-Utils>=0.36.8
18 changes: 9 additions & 9 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@
from setuptools import setup, find_packages


# https://bitbucket.org/zzzeek/alembic/raw/f38eaad4a80d7e3d893c3044162971971ae0
# 09bf/setup.py
with open(
os.path.join(os.path.dirname(__file__), 'stellar', 'app.py')
) as app_file:
Expand Down Expand Up @@ -48,12 +46,14 @@
'Topic :: Software Development :: Version Control',
],
install_requires = [
'PyYAML>=3.11',
'SQLAlchemy>=0.9.6',
'humanize>=0.5.1',
'schema>=0.3.1',
'click>=3.1',
'SQLAlchemy-Utils>=0.26.11',
'psutil>=2.1.1',
'PyYAML>=5.3.1',
'SQLAlchemy>=1.3.22',
'humanize>=2.6.0',
'schema>=0.7.2',
'click>=7.1.2',
'SQLAlchemy-Utils>=0.36.8',
'psutil>=5.8.0',
'wheel>=0.36.2',
'psycopg2-binary>=2.8.6'
]
)
10 changes: 5 additions & 5 deletions stellar/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
from psutil import pid_exists


__version__ = '0.4.5'
__version__ = '0.5.0'
logger = logging.getLogger(__name__)


Expand Down Expand Up @@ -49,7 +49,7 @@ def load_config(self):
logging.basicConfig(level=self.config['logging'])

def init_database(self):
self.raw_db = create_engine(self.config['url'], echo=False)
self.raw_db = create_engine(self.config['url'])
self.raw_conn = self.raw_db.connect()
self.operations = Operations(self.raw_conn, self.config)

Expand All @@ -58,11 +58,11 @@ def init_database(self):
except AttributeError:
logger.info('Could not set isolation level to 0')

self.db = create_engine(self.config['stellar_url'], echo=False)
self.db = create_engine(self.config['stellar_url'])
self.db.session = sessionmaker(bind=self.db)()
self.raw_db.session = sessionmaker(bind=self.raw_db)()
tables_missing = self.create_stellar_database()


self.create_stellar_database()
self.create_stellar_tables()

# logger.getLogger('sqlalchemy.engine').setLevel(logger.WARN)
Expand Down
105 changes: 53 additions & 52 deletions stellar/command.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import textwrap
import sys
from datetime import datetime
from time import sleep
Expand All @@ -6,31 +7,19 @@
import click
import logging
from sqlalchemy import create_engine
from sqlalchemy.exc import OperationalError
from sqlalchemy.exc import ArgumentError, OperationalError

from .app import Stellar, __version__
from .config import InvalidConfig, MissingConfig, load_config, save_config
from .operations import database_exists, list_of_databases, SUPPORTED_DIALECTS


def upgrade_from_old_version(app):
if app.config['migrate_from_0_3_2']:
if app.is_old_database():
click.echo('Upgrading from old Stellar version...')
def after_rename(old_name, new_name):
click.echo('* Renamed %s to %s' % (old_name, new_name))
app.update_database_names_to_new_version(after_rename=after_rename)

app.config['migrate_from_0_3_2'] = False
save_config(app.config)

def get_app():
app = Stellar()
upgrade_from_old_version(app)
return app


@click.group()
@click.group(context_settings={"help_option_names": ["-h", "--help"]})
def stellar():
"""Fast database snapshots for development. It's like Git for databases."""
pass
Expand All @@ -49,7 +38,6 @@ def after_delete(database):
click.echo("Deleted table %s" % database)

app = get_app()
upgrade_from_old_version(app)
app.delete_orphan_snapshots(after_delete)


Expand All @@ -58,7 +46,6 @@ def after_delete(database):
def snapshot(name):
"""Takes a snapshot of the database"""
app = get_app()
upgrade_from_old_version(app)
name = name or app.default_snapshot_name

if app.get_snapshot(name):
Expand Down Expand Up @@ -183,37 +170,56 @@ def replace(name):


@stellar.command()
def init():
@click.argument('url', required=False)
@click.argument('project', required=False)
def init(url, project):
"""Initializes Stellar configuration."""

def prompt_url():
msg = textwrap.dedent("""\
Please enter the url for your database.

For example:
PostgreSQL: postgresql://localhost:5432/
MySQL: mysql+pymysql://root@localhost/
""")
return click.prompt(msg)

while True:
url = click.prompt(
"Please enter the url for your database.\n\n"
"For example:\n"
"PostgreSQL: postgresql://localhost:5432/\n"
"MySQL: mysql+pymysql://root@localhost/"
)
if not url:
url = prompt_url()

if url.count('/') == 2 and not url.endswith('/'):
url = url + '/'

if (
url.count('/') == 3 and
url.endswith('/') and
url.startswith('postgresql://')
):
connection_url = url + 'template1'
else:
connection_url = url
# if (
# url.count('/') == 3 and
# url.endswith('/') and
# url.startswith('postgresql://')
# ):
# connection_url = url + 'template1'
# else:
# connection_url = url
connection_url = url

try:
engine = create_engine(connection_url, echo=False)
except ArgumentError as err:
click.echo("Error: %s" % err)
url = None
continue

engine = create_engine(connection_url, echo=False)
try:
conn = engine.connect()
except OperationalError as err:
click.echo("Could not connect to database: %s" % url)
click.echo("Error message: %s" % err.message)
click.echo("Error message: %s" % err)
click.echo('')
else:
break

url = None

if engine.dialect.name not in SUPPORTED_DIALECTS:
click.echo("Your engine dialect %s is not supported." % (
engine.dialect.name
Expand Down Expand Up @@ -241,31 +247,26 @@ def init():
db_name = url.rsplit('/', 1)[-1]
url = url.rsplit('/', 1)[0] + '/'

name = click.prompt(
'Please enter your project name (used internally, eg. %s)' % db_name,
default=db_name
)
if project is None:
project = click.prompt(
'Please enter project name (used internally, eg. %s)' % db_name,
default=db_name
)

raw_url = url

if engine.dialect.name == 'postgresql':
raw_url = raw_url + 'template1'
raw_url = raw_url + db_name

with open('stellar.yaml', 'w') as project_file:
project_file.write(
"""
project_name: '%(name)s'
tracked_databases: ['%(db_name)s']
url: '%(raw_url)s'
stellar_url: '%(url)sstellar_data'
""".strip() %
{
'name': name,
'raw_url': raw_url,
'url': url,
'db_name': db_name
}
)
textwrap.dedent("""\
project_name: {name}
tracked_databases: ['{db_name}']
url: '{raw_url}'
stellar_url: '{url}stellar_data'
""")
.format(name=project, db_name=db_name, raw_url=raw_url, url=url))

click.echo("Wrote stellar.yaml")
click.echo('')
Expand All @@ -286,7 +287,7 @@ def main():
sys.exit(1)
except ImportError as e:
libraries = {
'psycopg2': 'PostreSQL',
'psycopg2': 'PostgreSQL',
'pymysql': 'MySQL',
}
for library, name in libraries.items():
Expand Down
6 changes: 2 additions & 4 deletions stellar/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,14 @@ class MissingConfig(Exception):


default_config = {
'logging': 30,
'migrate_from_0_3_2': True
'logging': 30
}
schema = Schema({
'stellar_url': Use(str),
'url': Use(str),
'project_name': Use(str),
'tracked_databases': [Use(str)],
Optional('logging'): int,
Optional('migrate_from_0_3_2'): bool
Optional('logging'): int
})


Expand Down