Skip to content

Commit

Permalink
[feature] Add config and README
Browse files Browse the repository at this point in the history
  • Loading branch information
bp72 committed Jan 14, 2024
1 parent b962867 commit 69670a5
Show file tree
Hide file tree
Showing 9 changed files with 181 additions and 34 deletions.
35 changes: 35 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# simple-migrations (beta)

[![PyPI version](https://img.shields.io/pypi/v/simple-migrations.svg)](https://pypi.org/project/simple-migrations)
[![Python versions](https://img.shields.io/pypi/pyversions/simple-migrations.svg)](https://pypi.org/project/simple-migrations)

### Please, notice that the library is early in the development, use it at your own risk. The feedback and contributions are appreciated!

Make your database migrations as simple as possible!

`simple-migrations` is a library that allows you to write database migrations in a pure SQL.

The order of migrations will be tracked in the database table.

You can specify the `forwards` and `backwards` command and even write your own scripts.

No need to apply the scripts manually, you can now automate your deployment flow!

Keep in mind that the library is still in development and is distributed "as is".

## How to use

The example of the config can be found in `simple_migrations.ini` file.

Create a `simple_migrations.ini` file in the project root and set up the database credentials.

Run `simple-migrations init` to generate the migrations directory and migrations table.

Run `simple-migrations generate` to generate the migration file from the template.

Run `simple-migrations migrate` to apply all unapplied migrations.

Run `simple-migrations migrate <num>` to migrate or rollback to <num> migration, for example:

- If the last applied migration was #2, `simple-migrations migrate 4` will apply migrations 3 and 4.
- If the last applied migration was #4, `simple-migrations migrate 2` will rollback migrations 3 and 4.
85 changes: 84 additions & 1 deletion poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,14 @@ readme = "README.md"
[tool.poetry.dependencies]
python = "^3.12"
psycopg = "^3.1"
configparser = "^6.0"

[tool.poetry.scripts]
simple-migrations = "simple_migrations.cli:main"

[tool.poetry.group.dev.dependencies]
pytest = "^7.4.3"

[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"
7 changes: 7 additions & 0 deletions simple_migrations.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[simple-migrations]
database_host=localhost
database_port=5432
database_name=simple
database_user=simple
database_password=simple
migrations_dir=migrations
6 changes: 5 additions & 1 deletion simple_migrations/cli.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import sys
from enum import StrEnum

from simple_migrations.config import configure
from simple_migrations.migrate import generate_migration, migrate
from simple_migrations.setup import initial_setup
from simple_migrations.initial_setup import initial_setup


class ActionType(StrEnum):
Expand All @@ -12,6 +13,7 @@ class ActionType(StrEnum):


def main(argv: list[str] | None = None) -> int:
configure()
argv = argv or sys.argv[1:]
if not argv:
# TODO: print help
Expand All @@ -34,4 +36,6 @@ def main(argv: list[str] | None = None) -> int:
sys.stderr.write(f"Invalid version: {argv[1]}, please pass the migration number")
return 1
return migrate(until=until, fake=fake)
else:
sys.stderr.write("Unknown command\n")
return 1
50 changes: 34 additions & 16 deletions simple_migrations/config.py
Original file line number Diff line number Diff line change
@@ -1,22 +1,17 @@
import os.path
from configparser import ConfigParser
from contextvars import ContextVar
from dataclasses import dataclass


@dataclass
class Config:

def __init__(
self,
database_host: str = "localhost",
database_port: str = "5432",
database_name: str = "simple",
database_user: str = "simple",
database_password: str = "simple",
migrations_dir: str = "migrations",
) -> None:
self.database_host = database_host
self.database_port = database_port
self.database_name = database_name
self.database_user = database_user
self.database_password = database_password
self.migrations_dir = migrations_dir
database_host: str
database_port: str
database_name: str
database_user: str
database_password: str
migrations_dir: str

@property
def connection_string(self) -> str:
Expand All @@ -26,3 +21,26 @@ def connection_string(self) -> str:
f"{self.database_host}:{self.database_port}/"
f"{self.database_name}"
)


simple_migrations_config = ContextVar("simple_migrations_config")


def configure() -> None:
config_path = "simple_migrations.ini"
if not os.path.exists(config_path):
raise ValueError("Simple migrations: config file doesn't exist")

config = ConfigParser()
config.read(config_path)

simple_migrations_config.set(
Config(
database_host=config["simple-migrations"]["database_host"],
database_port=config["simple-migrations"]["database_port"],
database_name=config["simple-migrations"]["database_name"],
database_user=config["simple-migrations"]["database_user"],
database_password=config["simple-migrations"]["database_password"],
migrations_dir=config["simple-migrations"]["migrations_dir"],
),
)
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
import os
import sys

from simple_migrations.config import Config
from simple_migrations.config import simple_migrations_config
import psycopg


def initial_setup(

) -> int:
config = Config()
def initial_setup() -> int:
config = simple_migrations_config.get()
with psycopg.connect(config.connection_string) as conn:
with conn.cursor() as cur:
cur.execute("""
Expand Down
13 changes: 6 additions & 7 deletions simple_migrations/migrate.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,13 @@

import psycopg

from simple_migrations.config import Config

from simple_migrations.config import simple_migrations_config

FILE_DIR = os.path.dirname(__file__)


def generate_migration() -> int:
config = Config()
config = simple_migrations_config.get()
next_migration_id = _format_migration_id(_get_last_migration_id() + 1)
conflicting_file_name = next(
(
Expand All @@ -35,7 +34,7 @@ def generate_migration() -> int:


def migrate(until: int | None, fake: bool) -> int:
config = Config()
config = simple_migrations_config.get()
migrations_map = {}
for file_name in os.listdir(config.migrations_dir):
migration_id = _get_migration_id_from_name(file_name)
Expand Down Expand Up @@ -90,7 +89,7 @@ def migrate(until: int | None, fake: bool) -> int:


def _get_last_migration_id() -> int:
config = Config()
config = simple_migrations_config.get()
with psycopg.connect(config.connection_string) as conn:
with conn.cursor() as cur:
cur.execute("""
Expand Down Expand Up @@ -118,7 +117,7 @@ def _get_migration_id_from_name(name: str) -> int | None:

def _insert_migration(m_id: int, name: str) -> None:
created_at = datetime.now(timezone.utc)
config = Config()
config = simple_migrations_config.get()
with psycopg.connect(config.connection_string) as conn:
with conn.cursor() as cur:
cur.execute("""
Expand All @@ -128,7 +127,7 @@ def _insert_migration(m_id: int, name: str) -> None:


def _delete_migration(m_id: int) -> None:
config = Config()
config = simple_migrations_config.get()
with psycopg.connect(config.connection_string) as conn:
with conn.cursor() as cur:
cur.execute("""
Expand Down
7 changes: 3 additions & 4 deletions simple_migrations/migration_template.py
Original file line number Diff line number Diff line change
@@ -1,21 +1,20 @@
import psycopg

from simple_migrations.config import Config

from simple_migrations.config import simple_migrations_config

FORWARDS_SQL = ""
BACKWARDS_SQL = ""


def forwards() -> None:
config = Config()
config = simple_migrations_config.get()
with psycopg.connect(config.connection_string) as conn:
with conn.cursor() as cur:
cur.execute(FORWARDS_SQL)


def backwards() -> None:
config = Config()
config = simple_migrations_config.get()
with psycopg.connect(config.connection_string) as conn:
with conn.cursor() as cur:
cur.execute(BACKWARDS_SQL)

0 comments on commit 69670a5

Please sign in to comment.