Giter VIP home page Giter VIP logo

septentrion's Introduction

Septentrion: A CLI tool to apply PostgreSQL migrations to a database

Deployed to PyPI

Documentation Status

Continuous Integration Status

Coverage Status

MIT License

Contributor Covenant

Overview

Maybe you're looking for a tool to take care of Database migrations in your project. For Django projects, that tool used to be South and then it became Django itself.

But maybe you're looking for a tool that just focuses on running existing SQL migrations and keeping track of what was applied. Your tool of choice would not generate those migrations, because you prefer your migrations to be manually written in SQL. Then your tool would be django-north.

But maybe you're not using Django. You would like a standalone migration tool. You're looking for Septentrion. Congratulations, you've found it.

Septentrion supports PostgreSQL 9.6+ & Python 3.7+, and requires the psql executable to be present on the system.

Very quick start

  • Step 0: Install with pip install septentrion[psycopg2_binary] (or pip install septentrion[psycopg2] if you know what you're doing)
  • Step 1: Create a folder for the version, and add some migration files.
migrations/
└──  1.0
    ├── 1.0-0-version-dml.sql
    ├── 1.0-author-1-ddl.sql
    └── 1.0-author-2-dml.sql
  • Step 2: Run septentrion
$ septentrion --target-version 1.0 migrate
  • Step 3: That's it.

We're currently working on this tool, and it's been used internally since 2018, but for now, if you want to use it without a direct access to the people who wrote it, you're going to have a lot of questions. We expect a proper documentation to be ready by mid-2020. Please feel free to contact us meanwhile.

Where to go from here

The complete docs is probably the best place to learn about the project.

You can check the quickstart guide to start running your first migrations.

If you encounter a bug, or want to get in touch, you're always welcome to open a ticket.

septentrion's People

Contributors

alexdashkov avatar alyohea avatar brunobord avatar dependabot[bot] avatar ewjoachim avatar k4nar avatar mgu avatar mike-perdide avatar nadege avatar pre-commit-ci[bot] avatar samuelhamard avatar sdgjlbl avatar sheb avatar thomasecuer avatar thomasperrot avatar wo0dyn avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Forkers

ldgeo

septentrion's Issues

Proposition for a new architecture

Here's what we would do in septentrion migrate, in order.
The idea is that as much as possible, we avoid mixing decision making and I/O.

  • Create the migration table if not exsists
  • Read existing migrations in the DB
  • Read existing migration versions and filenames in the file system
  • Decide whether to initialize the DB with a schema
  • Compute the list of migrations to run
  • migrate(fixtures before schema)
  • migrate(schema)
  • migrate(fixtures after schema)
  • For each migration:
    • Fake it if boolean says to fake it or
    • migrate(migration)
    • Write the migration in the table as executed

def migrate(migration):

  • Read the migration file content
  • Parse the migration (meta, sqlparse statements, clean (\timing), etc)
  • For each statement (including meta loops):
    • Execute it

Switch to GitHub Actions

Using Github Actions will allow us to drop Travis, which has the drawback to be an third-party service, and lacks support for Windows. Besides, as Procrastinate already uses Github actions, we will soon be able to factor the two pipeline (is will soon be implemented in Github Actions).

Remove global variables

Even if the app is mainly designed as a CLI, I'm guessing it might benefit from being usable as a lib too, and it's much better if we avoid global states.

Add type annotations

The project is Py3.6+ which means we can add type annotations everywhere. Mypy is already installed, so let's do this.

Officialize use as lib (for Django-North)

  • Make it easy to be called from outside without too many idiosyncrasies
  • Give knobs to allow customizing non-critical parts of the workflow (tracking migrations, displaying, etc)

Septentrion as a library writes to stdout by default

In my opinion, when used as a library, septentrion should not write anything to stdout by default.

The current behavior is to print to stdout. This can be disabled using a quiet parameter.
I would reverse the logic and write to stdout only when a parameter is passed (e.g. enable_stdout ?)

Improve process

  • Add license whoops :/
  • Updgrade tooling (mypy black flake8 isort, the 4 horsetools of the apocalypse) (and check-manifest as a useful party)
  • Adjust CODEOWNERS
  • Add documentation
  • Write documentation -> #27
  • Add type annotations -> #28
  • Add release script
  • Complete coverage -> #29
  • Add docker compose dev env
  • Add code of conduct
  • Exclude tests from packaging
  • Review code (because our practice has evolved) -> #29

Environment elements must be strings

If the septentrion port number is an int, subprocess.run which launches psql fails with a traceback.

This can be solved by making sure the port number, as well as all other environment injected values, are strings.

@ThomasEcuer is on it :)

Don't play with query whitespaces

We currently replace all whitespace (multiple spaces, tabs and newlines) with whitespace, here:

query = " ".join(query.format(table=settings.TABLE).split())

I'm not sure there's a reason for doing this, we could stop doing that I think.

Anyone has an opinion ?

Is output clean when redirected ?

I haven't tested it, but I wonder what the output looks like when not connected to a TTY. I believe colorama behavies itself, and doesn't output colors. Though, we also do a fair share of line rewriting and we should maybe avoid this.

Note: I believe sys.stdout.isatty might be helpful, but there's maybe something more advanced in click or colorama to help us.

Todo List

To Do:

  • Add black and isort, add a CI (travis ? circle ?): #6
  • Convert csv parameters to list in click (e.g. ADDITIONAL_SCHEMA_FILES): #7
  • Fix relative import bug in python 2: #7
  • Understand why WEST_PASSWORD doesn't work: #7
  • Rework logs/print/outputs and add logging everywhere: #7
  • Embed west init into west migrate when applicable: #7
  • Make west fake as a separate command: #7
  • Unify stdout with click.style everywhere we need it (improved in #7)
  • And obviously, write (py-)tests, loads of 'em, unit, functional, acceptance tests, compute our coverage and make it a big number
  • Write documentation (see Daniele Procida's talk on how to organize a good technical documentation)
  • Contributing doc too
  • Publish on PyPI ! before the name is taken by someone else
  • Communicate around the project

Don't run migrations out of order except if we ask to

It's fairly easy to notice if there are unapplied migrations that are not at the end of the list. We should ask the user whether they want to run them or not. Also, there should be a CLI parameter indicating if we want to always do, or not do, or ask, so that we can automate this a bit better

Issue with sqlparse 0.4.1

With Septentrion 0.1.3 and sqlparse 0.4.1 we get this error at migration time:

Version 20.10
  [ ] Applying 20.10-0-version-ddl.sql ...Traceback (most recent call last):
  File "/home/nova/.virtualenvs/doctype_prediction/bin/septentrion", line 8, in <module>
    sys.exit(main())
  File "/home/nova/.virtualenvs/doctype_prediction/lib/python3.7/site-packages/click/core.py", line 829, in __call__
    return self.main(*args, **kwargs)
  File "/home/nova/.virtualenvs/doctype_prediction/lib/python3.7/site-packages/click/core.py", line 782, in main
    rv = self.invoke(ctx)
  File "/home/nova/.virtualenvs/doctype_prediction/lib/python3.7/site-packages/click/core.py", line 1259, in invoke
    return _process_result(sub_ctx.command.invoke(sub_ctx))
  File "/home/nova/.virtualenvs/doctype_prediction/lib/python3.7/site-packages/click/core.py", line 1066, in invoke
    return ctx.invoke(self.callback, **ctx.params)
  File "/home/nova/.virtualenvs/doctype_prediction/lib/python3.7/site-packages/click/core.py", line 610, in invoke
    return callback(*args, **kwargs)
  File "/home/nova/.virtualenvs/doctype_prediction/lib/python3.7/site-packages/septentrion/cli.py", line 184, in migrate_func
    migrate.migrate(stylist=style.stylist)
  File "/home/nova/.virtualenvs/doctype_prediction/lib/python3.7/site-packages/septentrion/migrate.py", line 56, in migrate
    run_script(path)
  File "/home/nova/.virtualenvs/doctype_prediction/lib/python3.7/site-packages/septentrion/migrate.py", line 143, in run_script
    script.run(db.get_connection())
  File "/home/nova/.virtualenvs/doctype_prediction/lib/python3.7/site-packages/septentrion/runner.py", line 118, in run
    block.run(cursor)
  File "/home/nova/.virtualenvs/doctype_prediction/lib/python3.7/site-packages/septentrion/runner.py", line 48, in run
    raise SQLRunnerException("sqlparse failed to properly split input")
septentrion.runner.SQLRunnerException: sqlparse failed to properly split input
script returned exit code 1

There wasn't any problem with sqlparse 0.3.1. So it looks like there's a change in sqlparse that breaks Septentrion.

target-version is not read from config file

Because config file only sets defaults values, and target-version is required=True, setting target-version in septentrion.ini results in Error: Missing option "--target-version".

Move all argument checks into configuration.py

In order to make septentrion CLI interface close to its lib interface, we should remove as much decisions as possible from the cli, making it a simple wrapper.

This involves:

  • Checking that versions are in the correct format
  • ...?

TypeError when loading config from --config-file CLI option

To reproduce, call septentrion CLI with --config-file option, e.g.:
septentrion --config-file=some_file.ini --target-version=1.0 migrate

It will fail:

  ...
  File "/tmp/venv/lib/python3.7/site-packages/septentrion/cli.py", line 37, in load_config
    ctx.default_map = configuration.load_configuration_files(value)
  File "/tmp/venv/lib/python3.7/site-packages/septentrion/configuration.py", line 193, in load_configuration_files
    return parse_configuration_file(file_contents)
  File "/tmp/venv/lib/python3.7/site-packages/septentrion/configuration.py", line 82, in parse_configuration_file
    parser.read_string(content)
  File "/usr/local/lib/python3.7/configparser.py", line 721, in read_string
    sfile = io.StringIO(string)
TypeError: initial_value must be str or None, not bytes
script returned exit code 1

Use Version objects all the way up

This deserves a discussion, I'm not sure about it.

We now have Version objects, but they're currently used only when comparing versions, etc. The rest of the stack passes versions as strings.
I guess it would be interesting to use Version objects all the way in the stack.

Pros:

  • We currently parse them several times (but this is negligable computation time)
  • There would be less ambiguity regarding the type expected for version arguments (but we have type annotations anyway)
  • Bugs where we pass a string that is not a proper version would not be possible, thanks to static typing

Cons:

  • We'd have to import the Version type everywhere, including in the tests etc.

Note:

  • I don't think it's a good idea to expose this to our external API. Strings are better suited than internal types to ease use, but this doesn't apply to internal calls.

Update click requirements

Due to recent vulnerability for celery (see) it better to upgrade it to version 5.2.2.
But it's impossible to integrate it with celery 5.2.2 which has click>=8.0.0,<9.0 (requirements.txt)
Septentrion requires click package >=7.1.2,<8.0.0
We should update the requirements or even unpin the versions for click 🤔

Remove test warning

There's a warning in the tests:

tests/integration/test_migration.py::test_init_schema_extra_files
  /home/joachim/src/septentrion/septentrion/migration.py:116: UserWarning: ADDITIONAL_SCHEMA_FILES will be deprecated. Use BEFORE_SCHEMA_FILES instead.
    warnings.warn(

-- Docs: https://docs.pytest.org/en/latest/warnings.html

We should catch it and check it, rather than letting it bubble.

Here's the pytest relevant doc: https://docs.pytest.org/en/stable/warnings.html

Review code and complete test coverage

It's quite nice to work with a 100% test coverage. It's not very hard to achieve on small codebases, and it gives a nice incentive to keep writing tests.

Also, I think a thorough code review would be interesting. I bet there are some things we've learned to do better.

Allow to initialize a database without schema file

Currently we need schema file to run a migration with septentrion. But when creating a project from scratch, the initial schema is the first migration.

Did we want to force the user to have schema file to initialize the database with Septentrion or do we want to allow initial migrations with only migrations files ?

Signature issue in lib.py

@dsteinberger found an issue in the lib.py code:

Traceback (most recent call last):
  File "/usr/local/lib/python3.9/threading.py", line 973, in _bootstrap_inner
    self.run()
  File "/usr/local/lib/python3.9/threading.py", line 910, in run
    self._target(*self._args, **self._kwargs)
  File "/opt/virtualenv/lib/python3.9/site-packages/django/utils/autoreload.py", line 64, in wrapper
    fn(*args, **kwargs)
  File "/opt/virtualenv/lib/python3.9/site-packages/django/core/management/commands/runserver.py", line 121, in inner_run
    self.check_migrations()
  File "/opt/virtualenv/lib/python3.9/site-packages/django_north/management/commands/runserver.py", line 18, in check_migrations
    migration_plan = septentrion.build_migration_plan(
  File "/opt/virtualenv/lib/python3.9/site-packages/septentrion/lib.py", line 35, in build_migration_plan
    return core.build_migration_plan(
TypeError: build_migration_plan() got an unexpected keyword argument 'schema_version'

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.