diff --git a/Pipfile b/Pipfile index 4ed72fe..8dea813 100644 --- a/Pipfile +++ b/Pipfile @@ -6,9 +6,17 @@ verify_ssl = true [dev-packages] records = "*" flake8 = "*" +black = "*" +pytest = "*" +psycopg2 = "*" [packages] docopt = "*" +click = "*" +crayons = "*" [requires] python_version = "3.7" + +[pipenv] +allow_prereleases = true diff --git a/Pipfile.lock b/Pipfile.lock index cfb2aa9..0b29ff1 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "fe88af79e6daea7246fa74c44179e7f83e1385ee8ff52c52907815184c88f0fd" + "sha256": "eff8afd597adb8a7d54fa0e772abf913770b977144a5a47bee0f57700d15eb90" }, "pipfile-spec": 6, "requires": { @@ -16,6 +16,29 @@ ] }, "default": { + "click": { + "hashes": [ + "sha256:2335065e6395b9e67ca716de5f7526736bfa6ceead690adf616d925bdc622b13", + "sha256:5b94b49521f6456670fdb30cd82a4eca9412788a93fa6dd6df72c94d5a8ff2d7" + ], + "index": "pypi", + "version": "==7.0" + }, + "colorama": { + "hashes": [ + "sha256:05eed71e2e327246ad6b38c540c4a3117230b19679b875190486ddd2d721422d", + "sha256:f8ac84de7840f5b9c4e3347b3c1eaa50f7e49c2b07596221daec5edaabbd7c48" + ], + "version": "==0.4.1" + }, + "crayons": { + "hashes": [ + "sha256:5e17691605e564d63482067eb6327d01a584bbaf870beffd4456a3391bd8809d", + "sha256:6f51241d0c4faec1c04c1c0ac6a68f1d66a4655476ce1570b3f37e5166a599cc" + ], + "index": "pypi", + "version": "==0.1.2" + }, "docopt": { "hashes": [ "sha256:49b3a825280bd66b3aa83585ef59c4a8c82f2c8a522dbe754a8bc8d08c85c491" @@ -25,6 +48,50 @@ } }, "develop": { + "appdirs": { + "hashes": [ + "sha256:9e5896d1372858f8dd3344faf4e5014d21849c756c8d5701f78f8a103b372d92", + "sha256:d8b24664561d0d34ddfaec54636d502d7cea6e29c3eaf68f3df6180863e2166e" + ], + "version": "==1.4.3" + }, + "atomicwrites": { + "hashes": [ + "sha256:03472c30eb2c5d1ba9227e4c2ca66ab8287fbfbbda3888aa93dc2e28fc6811b4", + "sha256:75a9445bac02d8d058d5e1fe689654ba5a6556a1dfd8ce6ec55a0ed79866cfa6" + ], + "version": "==1.3.0" + }, + "attrs": { + "hashes": [ + "sha256:10cbf6e27dbce8c30807caf056c8eb50917e0eaafe86347671b57254006c3e69", + "sha256:ca4be454458f9dec299268d472aaa5a11f67a4ff70093396e1ceae9c76cf4bbb" + ], + "version": "==18.2.0" + }, + "black": { + "hashes": [ + "sha256:817243426042db1d36617910df579a54f1afd659adb96fc5032fcf4b36209739", + "sha256:e030a9a28f542debc08acceb273f228ac422798e5215ba2a791a6ddeaaca22a5" + ], + "index": "pypi", + "version": "==18.9b0" + }, + "click": { + "hashes": [ + "sha256:2335065e6395b9e67ca716de5f7526736bfa6ceead690adf616d925bdc622b13", + "sha256:5b94b49521f6456670fdb30cd82a4eca9412788a93fa6dd6df72c94d5a8ff2d7" + ], + "index": "pypi", + "version": "==7.0" + }, + "colorama": { + "hashes": [ + "sha256:05eed71e2e327246ad6b38c540c4a3117230b19679b875190486ddd2d721422d", + "sha256:f8ac84de7840f5b9c4e3347b3c1eaa50f7e49c2b07596221daec5edaabbd7c48" + ], + "version": "==0.4.1" + }, "defusedxml": { "hashes": [ "sha256:24d7f2f94f7f3cb6061acb215685e5125fbcdc40a857eff9de22518820b0a4f4", @@ -74,6 +141,14 @@ ], "version": "==0.6.1" }, + "more-itertools": { + "hashes": [ + "sha256:38a936c0a6d98a38bcc2d03fdaaedaba9f412879461dd2ceff8d37564d6522e4", + "sha256:c0a5785b1109a6bd7fac76d6837fd1feca158e54e521ccd2ae8bfe393cc9d4fc", + "sha256:fe7a7cae1ccb57d33952113ff4fa1bc5f879963600ed74918f1236e212ee50b9" + ], + "version": "==5.0.0" + }, "odfpy": { "hashes": [ "sha256:596021f0519623ca8717331951c95e3b8d7b21e86edc7efe8cb650a0d0f59a2b" @@ -82,9 +157,59 @@ }, "openpyxl": { "hashes": [ - "sha256:a5285901fff7b99a011462f18506a4fbfe4055191149ff42f59345828f8cf7b2" + "sha256:93c00466fe15469bffc74e05ce83982b6efb5e0c1529cea13dc2024379db5d3b" ], - "version": "==2.5.14" + "version": "==2.6.0b1" + }, + "pluggy": { + "hashes": [ + "sha256:8ddc32f03971bfdf900a81961a48ccf2fb677cf7715108f85295c67405798616", + "sha256:980710797ff6a041e9a73a5787804f848996ecaa6f8a1b1e08224a5894f2074a" + ], + "version": "==0.8.1" + }, + "psycopg2": { + "hashes": [ + "sha256:02445ebbb3a11a3fe8202c413d5e6faf38bb75b4e336203ee144ca2c46529f94", + "sha256:0e9873e60f98f0c52339abf8f0339d1e22bfe5aae0bcf7aabd40c055175035ec", + "sha256:1148a5eb29073280bf9057c7fc45468592c1bb75a28f6df1591adb93c8cb63d0", + "sha256:259a8324e109d4922b0fcd046e223e289830e2568d6f4132a3702439e5fd532b", + "sha256:28dffa9ed4595429e61bacac41d3f9671bb613d1442ff43bcbec63d4f73ed5e8", + "sha256:314a74302d4737a3865d40ea50e430ce1543c921ba10f39d562e807cfe2edf2a", + "sha256:36b60201b6d215d7658a71493fdf6bd5e60ad9a0cffed39906627ff9f4f3afd3", + "sha256:3f9d532bce54c4234161176ff3b8688ff337575ca441ea27597e112dfcd0ee0c", + "sha256:5d222983847b40af989ad96c07fc3f07e47925e463baa5de716be8f805b41d9b", + "sha256:6757a6d2fc58f7d8f5d471ad180a0bd7b4dd3c7d681f051504fbea7ae29c8d6f", + "sha256:6a0e0f1e74edb0ab57d89680e59e7bfefad2bfbdf7c80eb38304d897d43674bb", + "sha256:6ca703ccdf734e886a1cf53eb702261110f6a8b0ed74bcad15f1399f74d3f189", + "sha256:8513b953d8f443c446aa79a4cc8a898bd415fc5e29349054f03a7d696d495542", + "sha256:9262a5ce2038570cb81b4d6413720484cb1bc52c064b2f36228d735b1f98b794", + "sha256:97441f851d862a0c844d981cbee7ee62566c322ebb3d68f86d66aa99d483985b", + "sha256:a07feade155eb8e69b54dd6774cf6acf2d936660c61d8123b8b6b1f9247b67d6", + "sha256:a9b9c02c91b1e3ec1f1886b2d0a90a0ea07cc529cb7e6e472b556bc20ce658f3", + "sha256:ae88216f94728d691b945983140bf40d51a1ff6c7fe57def93949bf9339ed54a", + "sha256:b360ffd17659491f1a6ad7c928350e229c7b7bd83a2b922b6ee541245c7a776f", + "sha256:b4221957ceccf14b2abdabef42d806e791350be10e21b260d7c9ce49012cc19e", + "sha256:b90758e49d5e6b152a460d10b92f8a6ccf318fcc0ee814dcf53f3a6fc5328789", + "sha256:c669ea986190ed05fb289d0c100cc88064351f2b85177cbfd3564c4f4847d18c", + "sha256:d1b61999d15c79cf7f4f7cc9021477aef35277fc52452cf50fd13b713c84424d", + "sha256:de7bb043d1adaaf46e38d47e7a5f703bb3dab01376111e522b07d25e1a79c1e1", + "sha256:e393568e288d884b94d263f2669215197840d097c7e5b0acd1a51c1ea7d1aba8", + "sha256:ed7e0849337bd37d89f2c2b0216a0de863399ee5d363d31b1e5330a99044737b", + "sha256:f153f71c3164665d269a5d03c7fa76ba675c7a8de9dc09a4e2c2cdc9936a7b41", + "sha256:f1fb5a8427af099beb7f65093cbdb52e021b8e6dbdfaf020402a623f4181baf5", + "sha256:f36b333e9f86a2fba960c72b90c34be6ca71819e300f7b1fc3d2b0f0b2c546cd", + "sha256:f4526d078aedd5187d0508aa5f9a01eae6a48a470ed678406da94b4cd6524b7e" + ], + "index": "pypi", + "version": "==2.7.7" + }, + "py": { + "hashes": [ + "sha256:bf92637198836372b520efcba9e020c330123be8ce527e535d185ed4b6f45694", + "sha256:e76826342cefe3c3d5f7e8ee4316b80d1dd8a300781612ddbc765c17ba25a6c6" + ], + "version": "==1.7.0" }, "pycodestyle": { "hashes": [ @@ -100,21 +225,23 @@ ], "version": "==2.1.0" }, + "pytest": { + "hashes": [ + "sha256:65aeaa77ae87c7fc95de56285282546cfa9c886dc8e5dc78313db1c25e21bc07", + "sha256:6ac6d467d9f053e95aaacd79f831dbecfe730f419c6c7022cb316b365cd9199d" + ], + "index": "pypi", + "version": "==4.2.0" + }, "pyyaml": { "hashes": [ - "sha256:3d7da3009c0f3e783b2c873687652d83b1bbfd5c88e9813fb7e5b03c0dd3108b", - "sha256:3ef3092145e9b70e3ddd2c7ad59bdd0252a94dfe3949721633e41344de00a6bf", - "sha256:40c71b8e076d0550b2e6380bada1f1cd1017b882f7e16f09a65be98e017f211a", - "sha256:558dd60b890ba8fd982e05941927a3911dc409a63dcb8b634feaa0cda69330d3", - "sha256:a7c28b45d9f99102fa092bb213aa12e0aaf9a6a1f5e395d36166639c1f96c3a1", - "sha256:aa7dd4a6a427aed7df6fb7f08a580d68d9b118d90310374716ae90b710280af1", - "sha256:bc558586e6045763782014934bfaf39d48b8ae85a2713117d16c39864085c613", - "sha256:d46d7982b62e0729ad0175a9bc7e10a566fc07b224d2c79fafb5e032727eaa04", - "sha256:d5eef459e30b09f5a098b9cea68bebfeb268697f78d647bd255a085371ac7f3f", - "sha256:e01d3203230e1786cd91ccfdc8f8454c8069c91bee3962ad93b87a4b2860f537", - "sha256:e170a9e6fcfd19021dd29845af83bb79236068bf5fd4df3327c1be18182b2531" + "sha256:254bf6fda2b7c651837acb2c718e213df29d531eebf00edb54743d10bcb694eb", + "sha256:3108529b78577327d15eec243f0ff348a0640b0c3478d67ad7f5648f93bac3e2", + "sha256:3c17fb92c8ba2f525e4b5f7941d850e7a48c3a59b32d331e2502a3cdc6648e76", + "sha256:8d6d96001aa7f0a6a4a95e8143225b5d06e41b1131044913fecb8f85a125714b", + "sha256:c8a88edd93ee29ede719080b2be6cb2333dfee1dccba213b422a9c8e97f2967b" ], - "version": "==3.13" + "version": "==4.2b4" }, "records": { "hashes": [ @@ -124,11 +251,18 @@ "index": "pypi", "version": "==0.5.2" }, + "six": { + "hashes": [ + "sha256:3350809f0555b11f552448330d0b52d5f24c91a322ea4a15ef22629740f3761c", + "sha256:d16a0141ec1a18405cd4ce8b4613101da75da0e9a7aec5bdd4fa804d0e0eba73" + ], + "version": "==1.12.0" + }, "sqlalchemy": { "hashes": [ - "sha256:52a42dbf02d0562d6e90e7af59f177f1cc027e72833cc29c3a821eefa009c71d" + "sha256:c08cee353acaa05dd4ddf8ae0b0844ae779ed88e0b0784a2c9e0c0f9118eb64c" ], - "version": "==1.2.17" + "version": "==1.3.0b2" }, "tablib": { "hashes": [ @@ -136,6 +270,13 @@ ], "version": "==0.12.1" }, + "toml": { + "hashes": [ + "sha256:229f81c57791a41d65e399fc06bf0848bab550a9dfd5ed66df18ce5f05e73d5c", + "sha256:235682dd292d5899d361a811df37e04a8828a5b1da3115886b73cf81ebc9100e" + ], + "version": "==0.10.0" + }, "unicodecsv": { "hashes": [ "sha256:018c08037d48649a0412063ff4eda26eaa81eff1546dbffa51fa5293276ff7fc" diff --git a/README.md b/README.md index 76b918b..6059c7e 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,10 @@ Assuming `DATABASE_URL` is set: Created file migrations/0003.sql. Feel free to add a suffix to the file name. $ records-migrate apply - Applied migration 3/5... + Applied migration 3/5...,, + + $ records-migrate schema + Dumps out SQL script for generating the schema. ## Considerations diff --git a/records_migrate/adapters.py b/records_migrate/adapters.py index 208458e..2077356 100644 --- a/records_migrate/adapters.py +++ b/records_migrate/adapters.py @@ -2,6 +2,7 @@ import os from .models import Migration + class MigrationAdapter: def __init__(self, db, *, _dir="migrations", default_format="{:06d}"): # Make path of dir absolute. @@ -19,8 +20,11 @@ class MigrationAdapter: @property def files(self): """Returns a list of file names in the migrations directory, in order.""" + def gen(): - for root, dirs, files in os.walk(self.migrations_dir, topdown=True, followlinks=True): + for root, dirs, files in os.walk( + self.migrations_dir, topdown=True, followlinks=True + ): for _file in files: # Yield the file name. yield f"{root}{os.path.sep}{_file}" @@ -37,6 +41,24 @@ class MigrationAdapter: def query(self, query): return self.db.query(query) + def init(self): + q = """ + CREATE TABLE migrations ( + name varchar(80), + timestamp timestamp + ); + + ALTER TABLE migrations ALTER COLUMN timestamp SET DEFAULT now(); + """ + + self.query(q) + @property def last_migration_applied(self): - raise NotImplementedError + records = self.query("SELECT * from migrations;") + try: + result = records[-1]["timestamp"] + except IndexError: + result = None + + return result diff --git a/records_migrate/cli.py b/records_migrate/cli.py index e69de29..b851485 100644 --- a/records_migrate/cli.py +++ b/records_migrate/cli.py @@ -0,0 +1,63 @@ +""" +Naval Fate. + +Usage: + records-migrate check + records-migrate new + records-migrate apply + records-migrate schema + +Options: + -h --help Show this screen. + --version Show version. + --db= Database to connect to [default: $DATABASE_URL]. +""" + +import os +import sys + +# import crayons +from docopt import docopt + +def puts_error(msg, exit_code=False): + if exit_code is True: + exit_code = 1 + + # msg = f"{str(crayons.red('Error'))}: {msg}" + msg = f"Error: {msg}" + print(msg, file=sys.stderr) + + if isinstance(exit_code, int): + sys.exit(exit_code) + + +def main(): + args = docopt(doc=__doc__) + + do_check = args['check'] + do_new = args['new'] + do_apply = args['apply'] + do_schema = args['schema'] + + # Prepare database_url. + if "--db" not in args: + try: + args["--db"] = os.environ["DATABASE_URL"] + except KeyError: + puts_error("Either --db or $DATABASE_URL must be provided!", exit_code=1) + + + if do_check: + pass + + elif do_new: + pass + + elif do_apply: + pass + + elif do_schema: + pass + +if __name__ == "__main__": + main() diff --git a/t.py b/t.py index 598bb61..4c2e40a 100644 --- a/t.py +++ b/t.py @@ -1,5 +1,9 @@ +from records import Database from records_migrate.adapters import MigrationAdapter -a = MigrationAdapter(db=None) -for file in a.load(): - print(file) +db = Database() +a = MigrationAdapter(db=db) +print(a.last_migration_applied) +# a.init() +# for file in a.load(): +# print(file)