diff --git a/Pipfile b/Pipfile index 3574cdd..3a5d0c1 100644 --- a/Pipfile +++ b/Pipfile @@ -5,16 +5,20 @@ name = "pypi" [packages] click = "*" +docker = "*" +pygit2 = "*" +types-docker = "*" +types-pygit2 = "*" [dev-packages] black = "*" coveralls = "*" +ipython = "*" mypy = "*" pre-commit = "*" pytest = "*" -ruff = "*" -ipython = "*" pytest-freezegun = "*" +ruff = "*" setuptools = "*" [requires] diff --git a/Pipfile.lock b/Pipfile.lock index 187618c..e70102f 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "041643c4824c1f8a72d3f4e546ff7cdfaf0e5a74d2c73c8c450da91e83697f84" + "sha256": "35342439f8899b8dbfc77376e1edb23e57e5054972c8ca5e491b2c17dde58a19" }, "pipfile-spec": 6, "requires": { @@ -16,6 +16,183 @@ ] }, "default": { + "certifi": { + "hashes": [ + "sha256:922820b53db7a7257ffbda3f597266d435245903d80737e34f8a45ff3e3230d8", + "sha256:bec941d2aa8195e248a60b31ff9f0558284cf01a52591ceda73ea9afffd69fd9" + ], + "markers": "python_version >= '3.6'", + "version": "==2024.8.30" + }, + "cffi": { + "hashes": [ + "sha256:011aff3524d578a9412c8b3cfaa50f2c0bd78e03eb7af7aa5e0df59b158efb2f", + "sha256:0a048d4f6630113e54bb4b77e315e1ba32a5a31512c31a273807d0027a7e69ab", + "sha256:0bb15e7acf8ab35ca8b24b90af52c8b391690ef5c4aec3d31f38f0d37d2cc499", + "sha256:0d46ee4764b88b91f16661a8befc6bfb24806d885e27436fdc292ed7e6f6d058", + "sha256:0e60821d312f99d3e1569202518dddf10ae547e799d75aef3bca3a2d9e8ee693", + "sha256:0fdacad9e0d9fc23e519efd5ea24a70348305e8d7d85ecbb1a5fa66dc834e7fb", + "sha256:14b9cbc8f7ac98a739558eb86fabc283d4d564dafed50216e7f7ee62d0d25377", + "sha256:17c6d6d3260c7f2d94f657e6872591fe8733872a86ed1345bda872cfc8c74885", + "sha256:1a2ddbac59dc3716bc79f27906c010406155031a1c801410f1bafff17ea304d2", + "sha256:2404f3de742f47cb62d023f0ba7c5a916c9c653d5b368cc966382ae4e57da401", + "sha256:24658baf6224d8f280e827f0a50c46ad819ec8ba380a42448e24459daf809cf4", + "sha256:24aa705a5f5bd3a8bcfa4d123f03413de5d86e497435693b638cbffb7d5d8a1b", + "sha256:2770bb0d5e3cc0e31e7318db06efcbcdb7b31bcb1a70086d3177692a02256f59", + "sha256:331ad15c39c9fe9186ceaf87203a9ecf5ae0ba2538c9e898e3a6967e8ad3db6f", + "sha256:3aa9d43b02a0c681f0bfbc12d476d47b2b2b6a3f9287f11ee42989a268a1833c", + "sha256:41f4915e09218744d8bae14759f983e466ab69b178de38066f7579892ff2a555", + "sha256:4304d4416ff032ed50ad6bb87416d802e67139e31c0bde4628f36a47a3164bfa", + "sha256:435a22d00ec7d7ea533db494da8581b05977f9c37338c80bc86314bec2619424", + "sha256:45f7cd36186db767d803b1473b3c659d57a23b5fa491ad83c6d40f2af58e4dbb", + "sha256:48b389b1fd5144603d61d752afd7167dfd205973a43151ae5045b35793232aa2", + "sha256:4e67d26532bfd8b7f7c05d5a766d6f437b362c1bf203a3a5ce3593a645e870b8", + "sha256:516a405f174fd3b88829eabfe4bb296ac602d6a0f68e0d64d5ac9456194a5b7e", + "sha256:5ba5c243f4004c750836f81606a9fcb7841f8874ad8f3bf204ff5e56332b72b9", + "sha256:5bdc0f1f610d067c70aa3737ed06e2726fd9d6f7bfee4a351f4c40b6831f4e82", + "sha256:6107e445faf057c118d5050560695e46d272e5301feffda3c41849641222a828", + "sha256:6327b572f5770293fc062a7ec04160e89741e8552bf1c358d1a23eba68166759", + "sha256:669b29a9eca6146465cc574659058ed949748f0809a2582d1f1a324eb91054dc", + "sha256:6ce01337d23884b21c03869d2f68c5523d43174d4fc405490eb0091057943118", + "sha256:6d872186c1617d143969defeadac5a904e6e374183e07977eedef9c07c8953bf", + "sha256:6f76a90c345796c01d85e6332e81cab6d70de83b829cf1d9762d0a3da59c7932", + "sha256:70d2aa9fb00cf52034feac4b913181a6e10356019b18ef89bc7c12a283bf5f5a", + "sha256:7cbc78dc018596315d4e7841c8c3a7ae31cc4d638c9b627f87d52e8abaaf2d29", + "sha256:856bf0924d24e7f93b8aee12a3a1095c34085600aa805693fb7f5d1962393206", + "sha256:8a98748ed1a1df4ee1d6f927e151ed6c1a09d5ec21684de879c7ea6aa96f58f2", + "sha256:93a7350f6706b31f457c1457d3a3259ff9071a66f312ae64dc024f049055f72c", + "sha256:964823b2fc77b55355999ade496c54dde161c621cb1f6eac61dc30ed1b63cd4c", + "sha256:a003ac9edc22d99ae1286b0875c460351f4e101f8c9d9d2576e78d7e048f64e0", + "sha256:a0ce71725cacc9ebf839630772b07eeec220cbb5f03be1399e0457a1464f8e1a", + "sha256:a47eef975d2b8b721775a0fa286f50eab535b9d56c70a6e62842134cf7841195", + "sha256:a8b5b9712783415695663bd463990e2f00c6750562e6ad1d28e072a611c5f2a6", + "sha256:a9015f5b8af1bb6837a3fcb0cdf3b874fe3385ff6274e8b7925d81ccaec3c5c9", + "sha256:aec510255ce690d240f7cb23d7114f6b351c733a74c279a84def763660a2c3bc", + "sha256:b00e7bcd71caa0282cbe3c90966f738e2db91e64092a877c3ff7f19a1628fdcb", + "sha256:b50aaac7d05c2c26dfd50c3321199f019ba76bb650e346a6ef3616306eed67b0", + "sha256:b7b6ea9e36d32582cda3465f54c4b454f62f23cb083ebc7a94e2ca6ef011c3a7", + "sha256:bb9333f58fc3a2296fb1d54576138d4cf5d496a2cc118422bd77835e6ae0b9cb", + "sha256:c1c13185b90bbd3f8b5963cd8ce7ad4ff441924c31e23c975cb150e27c2bf67a", + "sha256:c3b8bd3133cd50f6b637bb4322822c94c5ce4bf0d724ed5ae70afce62187c492", + "sha256:c5d97162c196ce54af6700949ddf9409e9833ef1003b4741c2b39ef46f1d9720", + "sha256:c815270206f983309915a6844fe994b2fa47e5d05c4c4cef267c3b30e34dbe42", + "sha256:cab2eba3830bf4f6d91e2d6718e0e1c14a2f5ad1af68a89d24ace0c6b17cced7", + "sha256:d1df34588123fcc88c872f5acb6f74ae59e9d182a2707097f9e28275ec26a12d", + "sha256:d6bdcd415ba87846fd317bee0774e412e8792832e7805938987e4ede1d13046d", + "sha256:db9a30ec064129d605d0f1aedc93e00894b9334ec74ba9c6bdd08147434b33eb", + "sha256:dbc183e7bef690c9abe5ea67b7b60fdbca81aa8da43468287dae7b5c046107d4", + "sha256:dca802c8db0720ce1c49cce1149ff7b06e91ba15fa84b1d59144fef1a1bc7ac2", + "sha256:dec6b307ce928e8e112a6bb9921a1cb00a0e14979bf28b98e084a4b8a742bd9b", + "sha256:df8bb0010fdd0a743b7542589223a2816bdde4d94bb5ad67884348fa2c1c67e8", + "sha256:e4094c7b464cf0a858e75cd14b03509e84789abf7b79f8537e6a72152109c76e", + "sha256:e4760a68cab57bfaa628938e9c2971137e05ce48e762a9cb53b76c9b569f1204", + "sha256:eb09b82377233b902d4c3fbeeb7ad731cdab579c6c6fda1f763cd779139e47c3", + "sha256:eb862356ee9391dc5a0b3cbc00f416b48c1b9a52d252d898e5b7696a5f9fe150", + "sha256:ef9528915df81b8f4c7612b19b8628214c65c9b7f74db2e34a646a0a2a0da2d4", + "sha256:f3157624b7558b914cb039fd1af735e5e8049a87c817cc215109ad1c8779df76", + "sha256:f3e0992f23bbb0be00a921eae5363329253c3b86287db27092461c887b791e5e", + "sha256:f9338cc05451f1942d0d8203ec2c346c830f8e86469903d5126c1f0a13a2bcbb", + "sha256:ffef8fd58a36fb5f1196919638f73dd3ae0db1a878982b27a9a5a176ede4ba91" + ], + "markers": "python_version >= '3.8'", + "version": "==1.17.0" + }, + "charset-normalizer": { + "hashes": [ + "sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027", + "sha256:06a81e93cd441c56a9b65d8e1d043daeb97a3d0856d177d5c90ba85acb3db087", + "sha256:0a55554a2fa0d408816b3b5cedf0045f4b8e1a6065aec45849de2d6f3f8e9786", + "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8", + "sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09", + "sha256:122c7fa62b130ed55f8f285bfd56d5f4b4a5b503609d181f9ad85e55c89f4185", + "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574", + "sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e", + "sha256:1f79682fbe303db92bc2b1136016a38a42e835d932bab5b3b1bfcfbf0640e519", + "sha256:2127566c664442652f024c837091890cb1942c30937add288223dc895793f898", + "sha256:22afcb9f253dac0696b5a4be4a1c0f8762f8239e21b99680099abd9b2b1b2269", + "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3", + "sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f", + "sha256:3287761bc4ee9e33561a7e058c72ac0938c4f57fe49a09eae428fd88aafe7bb6", + "sha256:34d1c8da1e78d2e001f363791c98a272bb734000fcef47a491c1e3b0505657a8", + "sha256:37e55c8e51c236f95b033f6fb391d7d7970ba5fe7ff453dad675e88cf303377a", + "sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73", + "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc", + "sha256:42cb296636fcc8b0644486d15c12376cb9fa75443e00fb25de0b8602e64c1714", + "sha256:45485e01ff4d3630ec0d9617310448a8702f70e9c01906b0d0118bdf9d124cf2", + "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc", + "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce", + "sha256:4d0d1650369165a14e14e1e47b372cfcb31d6ab44e6e33cb2d4e57265290044d", + "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e", + "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6", + "sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269", + "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96", + "sha256:5b4c145409bef602a690e7cfad0a15a55c13320ff7a3ad7ca59c13bb8ba4d45d", + "sha256:6463effa3186ea09411d50efc7d85360b38d5f09b870c48e4600f63af490e56a", + "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4", + "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77", + "sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d", + "sha256:68d1f8a9e9e37c1223b656399be5d6b448dea850bed7d0f87a8311f1ff3dabb0", + "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed", + "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068", + "sha256:6c4caeef8fa63d06bd437cd4bdcf3ffefe6738fb1b25951440d80dc7df8c03ac", + "sha256:6ef1d82a3af9d3eecdba2321dc1b3c238245d890843e040e41e470ffa64c3e25", + "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8", + "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab", + "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26", + "sha256:7f04c839ed0b6b98b1a7501a002144b76c18fb1c1850c8b98d458ac269e26ed2", + "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db", + "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f", + "sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5", + "sha256:86216b5cee4b06df986d214f664305142d9c76df9b6512be2738aa72a2048f99", + "sha256:87d1351268731db79e0f8e745d92493ee2841c974128ef629dc518b937d9194c", + "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d", + "sha256:8c622a5fe39a48f78944a87d4fb8a53ee07344641b0562c540d840748571b811", + "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa", + "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a", + "sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03", + "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b", + "sha256:923c0c831b7cfcb071580d3f46c4baf50f174be571576556269530f4bbd79d04", + "sha256:95f2a5796329323b8f0512e09dbb7a1860c46a39da62ecb2324f116fa8fdc85c", + "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001", + "sha256:9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458", + "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389", + "sha256:a50aebfa173e157099939b17f18600f72f84eed3049e743b68ad15bd69b6bf99", + "sha256:a981a536974bbc7a512cf44ed14938cf01030a99e9b3a06dd59578882f06f985", + "sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537", + "sha256:ae5f4161f18c61806f411a13b0310bea87f987c7d2ecdbdaad0e94eb2e404238", + "sha256:aed38f6e4fb3f5d6bf81bfa990a07806be9d83cf7bacef998ab1a9bd660a581f", + "sha256:b01b88d45a6fcb69667cd6d2f7a9aeb4bf53760d7fc536bf679ec94fe9f3ff3d", + "sha256:b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796", + "sha256:b2b0a0c0517616b6869869f8c581d4eb2dd83a4d79e0ebcb7d373ef9956aeb0a", + "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143", + "sha256:bd8f7df7d12c2db9fab40bdd87a7c09b1530128315d047a086fa3ae3435cb3a8", + "sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c", + "sha256:c002b4ffc0be611f0d9da932eb0f704fe2602a9a949d1f738e4c34c75b0863d5", + "sha256:c083af607d2515612056a31f0a8d9e0fcb5876b7bfc0abad3ecd275bc4ebc2d5", + "sha256:c180f51afb394e165eafe4ac2936a14bee3eb10debc9d9e4db8958fe36afe711", + "sha256:c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4", + "sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6", + "sha256:d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c", + "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7", + "sha256:db364eca23f876da6f9e16c9da0df51aa4f104a972735574842618b8c6d999d4", + "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b", + "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae", + "sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12", + "sha256:e27ad930a842b4c5eb8ac0016b0a54f5aebbe679340c26101df33424142c143c", + "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae", + "sha256:eb00ed941194665c332bf8e078baf037d6c35d7c4f3102ea2d4f16ca94a26dc8", + "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887", + "sha256:eb8821e09e916165e160797a6c17edda0679379a4be5c716c260e836e122f54b", + "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4", + "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f", + "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5", + "sha256:fb69256e180cb6c8a894fee62b3afebae785babc1ee98b81cdf68bbca1987f33", + "sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519", + "sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561" + ], + "markers": "python_full_version >= '3.7.0'", + "version": "==3.3.2" + }, "click": { "hashes": [ "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28", @@ -24,6 +201,125 @@ "index": "pypi", "markers": "python_version >= '3.7'", "version": "==8.1.7" + }, + "docker": { + "hashes": [ + "sha256:ad8c70e6e3f8926cb8a92619b832b4ea5299e2831c14284663184e200546fa6c", + "sha256:c96b93b7f0a746f9e77d325bcfb87422a3d8bd4f03136ae8a85b37f1898d5fc0" + ], + "index": "pypi", + "markers": "python_version >= '3.8'", + "version": "==7.1.0" + }, + "idna": { + "hashes": [ + "sha256:050b4e5baadcd44d760cedbd2b8e639f2ff89bbc7a5730fcc662954303377aac", + "sha256:d838c2c0ed6fced7693d5e8ab8e734d5f8fda53a039c0164afb0b82e771e3603" + ], + "markers": "python_version >= '3.6'", + "version": "==3.8" + }, + "pycparser": { + "hashes": [ + "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6", + "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc" + ], + "markers": "python_version >= '3.8'", + "version": "==2.22" + }, + "pygit2": { + "hashes": [ + "sha256:0367f94cb4413bc668bcf1fd7f941bb1c1f214545d47b964442857de234799cf", + "sha256:06d42733a767bfe9245df15f4585823243f0845fab8c81a2c680a0e49a9cb012", + "sha256:0bcce4cfdabc05a2a35d709513863bcce8c929492ae7c0d56f045838bd57ea8f", + "sha256:167c23272b225ddd3be1e794bd8085b3c4e394cbdb70a1be278ab32e228ccedc", + "sha256:1b269b504d47b50e4ed7fe21326c0d046a0ab8b8897db059bdc208e2210e3070", + "sha256:1d622d0f97a34982973f9885d145b1176e912ea9f191e1c95233a6175a47fa28", + "sha256:23afb0a683285c02ff84f7ac574c39fec52b66032f92e8ca038cc81cfc68037a", + "sha256:2996180cbe7653e98839eb3afa5c040081f6e1cc835824769efe84c76ea2caf8", + "sha256:4072b80018b8c0e1743e9803b717e026d3017df291e2d81f7b869ebe18b01286", + "sha256:435b90bfddae32c6a00b48ff7da26564027dccd84e49866f48e659c9f3de6772", + "sha256:4cb1c22351c43c3cc96e842f31bd9b331a0ea7cb62aa8cf32433d45eebde0b1c", + "sha256:4d5839566491378b84dec1c35ffdb28b70fb6cd4ea2604a59052c4e4cf1c9da1", + "sha256:511b082c6d6c7b01cb8d49e108d066a1b5211c7364a0d8e7178809b8a304ac4b", + "sha256:5214ac7844e10cc279d746b588b5e6c6d73520d36d1361fe18e6e9d9c86ad357", + "sha256:5e1d338c88e1425e3dc09a3147b42683205b2dbb00b14c0ce80123f059e51de8", + "sha256:709f5d9592764ec5d6652e73882997f38cc8e6c7b495792698ecaca3e6a26088", + "sha256:738be5d3a3e7775571b14d3d110cfab10260f846078c402c041486f3582dbfbe", + "sha256:86ad7c8ec6fd545a65952066a693cb2ee4f26a0f6a8577e866f6742fc7eddb11", + "sha256:a0c6d5df5029f4cb25b0d7d8f04cb39691c107eedee1f157ee25be3b0b9df7c6", + "sha256:a5a4d288a7b0006f78e02e2c539e6218b254a8228e754051fd5532595fbf9a4c", + "sha256:b08d62ad424ba04ed7572d0a927f43cdccbf20c7c88250232a477fcb0a901701", + "sha256:bb60dbb93135e36b86dd8012ee707ea3b68c02869b6d10f23cfb86e10798bf6f", + "sha256:cd2861963bb904bd41162e9148676990f147da7dbc535ceea070ab371012bfed", + "sha256:e0a32a3c7742db8d925712344eaeb205c0a6076779035fea24574ea2507ba34c", + "sha256:e1fe8b85053d9713043c81eccc74132f9e5b603f209e80733d7955eafd22eb9d", + "sha256:e9c417d90915e59fd1a5a6532d47c8f2da5f97fd769e5ae9f5b9edec3a7bc669", + "sha256:f2418b29da5bad17e13674041790f2eda399c92d2e61c1be08f58df18dc99b56", + "sha256:f7d5329fd0658644de38bdb0ad8fad7877803f92a108acfc813525cbb5bd75a1", + "sha256:fb6abaef13b304a009584a0561acec21d1df4e57899fc85e8af4533352123c5e" + ], + "index": "pypi", + "markers": "python_version >= '3.9'", + "version": "==1.15.1" + }, + "requests": { + "hashes": [ + "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760", + "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6" + ], + "markers": "python_version >= '3.8'", + "version": "==2.32.3" + }, + "types-cffi": { + "hashes": [ + "sha256:a363e5ea54a4eb6a4a105d800685fde596bc318089b025b27dee09849fe41ff0", + "sha256:b8b20d23a2b89cfed5f8c5bc53b0cb8677c3aac6d970dbc771e28b9c698f5dee" + ], + "markers": "python_version >= '3.8'", + "version": "==1.16.0.20240331" + }, + "types-docker": { + "hashes": [ + "sha256:3b0a02da44cffef176dfc4f8b2c4a502ee66e83d79c8527c0cf5806eb599cfac", + "sha256:e71067745f0100bcc2c1a2fc794ea15204c916430b09e61bd6bddf1bf56e4343" + ], + "index": "pypi", + "markers": "python_version >= '3.8'", + "version": "==7.1.0.20240827" + }, + "types-pygit2": { + "hashes": [ + "sha256:1aca0709cdca3c0f531e7b35ddd37425bf5da1bfc27482fe794f3118251bf9aa", + "sha256:9824a56d6671e25e287a3c7937d76bb52afad54618532625cacf6d0408081211" + ], + "index": "pypi", + "markers": "python_version >= '3.8'", + "version": "==1.15.0.20240822" + }, + "types-requests": { + "hashes": [ + "sha256:90c079ff05e549f6bf50e02e910210b98b8ff1ebdd18e19c873cd237737c1358", + "sha256:f754283e152c752e46e70942fa2a146b5bc70393522257bb85bd1ef7e019dcc3" + ], + "markers": "python_version >= '3.8'", + "version": "==2.32.0.20240712" + }, + "types-setuptools": { + "hashes": [ + "sha256:4d9d18ea9214828d695a384633130009f5dee2681a157ee873d3680b62931590", + "sha256:8b4a544cc91d42a019dc1e41fd397608b4bc7e20c7d7d5bc326589ffd9e8f8a1" + ], + "markers": "python_version >= '3.8'", + "version": "==74.0.0.20240831" + }, + "urllib3": { + "hashes": [ + "sha256:a448b2f64d686155468037e1ace9f2d2199776e17f0a46610480d311f73e3472", + "sha256:dd505485549a7a552833da5e6063639d0d177c04f23bc3864e41e5dc5f612168" + ], + "markers": "python_version >= '3.8'", + "version": "==2.2.2" } }, "develop": { @@ -65,11 +361,11 @@ }, "certifi": { "hashes": [ - "sha256:5a1e7645bc0ec61a09e26c36f6106dd4cf40c6db3a1fb6352b0244e7fb057c7b", - "sha256:c198e21b1289c2ab85ee4e67bb4b4ef3ead0892059901a8d5b622f24a1101e90" + "sha256:922820b53db7a7257ffbda3f597266d435245903d80737e34f8a45ff3e3230d8", + "sha256:bec941d2aa8195e248a60b31ff9f0558284cf01a52591ceda73ea9afffd69fd9" ], "markers": "python_version >= '3.6'", - "version": "==2024.7.4" + "version": "==2024.8.30" }, "cfgv": { "hashes": [ @@ -297,11 +593,11 @@ }, "executing": { "hashes": [ - "sha256:35afe2ce3affba8ee97f2d69927fa823b08b472b7b994e36a52a964b93d16147", - "sha256:eac49ca94516ccc753f9fb5ce82603156e590b27525a8bc32cce8ae302eb61bc" + "sha256:8d63781349375b5ebccc3142f4b30350c0cd9c79f921cde38be2be4637e98eaf", + "sha256:8ea27ddd260da8150fa5a708269c4a10e76161e2496ec3e587da9e3c0fe4b9ab" ], - "markers": "python_version >= '3.5'", - "version": "==2.0.1" + "markers": "python_version >= '3.8'", + "version": "==2.1.0" }, "filelock": { "hashes": [ @@ -345,12 +641,12 @@ }, "ipython": { "hashes": [ - "sha256:1cec0fbba8404af13facebe83d04436a7434c7400e59f47acf467c64abd0956c", - "sha256:e6b347c27bdf9c32ee9d31ae85defc525755a1869f14057e900675b9e8d6e6ff" + "sha256:0b99a2dc9f15fd68692e898e5568725c6d49c527d36a9fb5960ffbdeaa82ff7e", + "sha256:f68b3cb8bde357a5d7adc9598d57e22a45dfbea19eb6b98286fa3b288c9cd55c" ], "index": "pypi", "markers": "python_version >= '3.10'", - "version": "==8.26.0" + "version": "==8.27.0" }, "jedi": { "hashes": [ @@ -599,37 +895,37 @@ }, "ruff": { "hashes": [ - "sha256:094600ee88cda325988d3f54e3588c46de5c18dae09d683ace278b11f9d4d534", - "sha256:1175d39faadd9a50718f478d23bfc1d4da5743f1ab56af81a2b6caf0a2394f23", - "sha256:17002fe241e76544448a8e1e6118abecbe8cd10cf68fde635dad480dba594570", - "sha256:239ee6beb9e91feb8e0ec384204a763f36cb53fb895a1a364618c6abb076b3be", - "sha256:279d5f7d86696df5f9549b56b9b6a7f6c72961b619022b5b7999b15db392a4da", - "sha256:2aed7e243be68487aa8982e91c6e260982d00da3f38955873aecd5a9204b1d66", - "sha256:316d418fe258c036ba05fbf7dfc1f7d3d4096db63431546163b472285668132b", - "sha256:3dbeac76ed13456f8158b8f4fe087bf87882e645c8e8b606dd17b0b66c2c1158", - "sha256:5b939f9c86d51635fe486585389f54582f0d65b8238e08c327c1534844b3bb9a", - "sha256:5c8cbc6252deb3ea840ad6a20b0f8583caab0c5ef4f9cca21adc5a92b8f79f3c", - "sha256:7438a7288f9d67ed3c8ce4d059e67f7ed65e9fe3aa2ab6f5b4b3610e57e3cb56", - "sha256:7db6880c53c56addb8638fe444818183385ec85eeada1d48fc5abe045301b2f1", - "sha256:a8f310d63af08f583363dfb844ba8f9417b558199c58a5999215082036d795a1", - "sha256:d0d62ca91219f906caf9b187dea50d17353f15ec9bb15aae4a606cd697b49b4c", - "sha256:d371f7fc9cec83497fe7cf5eaf5b76e22a8efce463de5f775a1826197feb9df8", - "sha256:d72b8b3abf8a2d51b7b9944a41307d2f442558ccb3859bbd87e6ae9be1694a5d", - "sha256:d9f3469c7dd43cd22eb1c3fc16926fb8258d50cb1b216658a07be95dd117b0f2", - "sha256:f28fcd2cd0e02bdf739297516d5643a945cc7caf09bd9bcb4d932540a5ea4fa9" + "sha256:14a9528a8b70ccc7a847637c29e56fd1f9183a9db743bbc5b8e0c4ad60592a82", + "sha256:183b99e9edd1ef63be34a3b51fee0a9f4ab95add123dbf89a71f7b1f0c991983", + "sha256:34e2824a13bb8c668c71c1760a6ac7d795ccbd8d38ff4a0d8471fdb15de910b1", + "sha256:3b061e49b5cf3a297b4d1c27ac5587954ccb4ff601160d3d6b2f70b1622194dc", + "sha256:42844ff678f9b976366b262fa2d1d1a3fe76f6e145bd92c84e27d172e3c34500", + "sha256:47021dff5445d549be954eb275156dfd7c37222acc1e8014311badcb9b4ec8c1", + "sha256:500f166d03fc6d0e61c8e40a3ff853fa8a43d938f5d14c183c612df1b0d6c58a", + "sha256:65a533235ed55f767d1fc62193a21cbf9e3329cf26d427b800fdeacfb77d296f", + "sha256:70452a10eb2d66549de8e75f89ae82462159855e983ddff91bc0bce6511d0470", + "sha256:746af39356fee2b89aada06c7376e1aa274a23493d7016059c3a72e3b296befb", + "sha256:7a62d3b5b0d7f9143d94893f8ba43aa5a5c51a0ffc4a401aa97a81ed76930521", + "sha256:7d7bd20dc07cebd68cc8bc7b3f5ada6d637f42d947c85264f94b0d1cd9d87384", + "sha256:97f58fda4e309382ad30ede7f30e2791d70dd29ea17f41970119f55bdb7a45c3", + "sha256:bddfbb8d63c460f4b4128b6a506e7052bad4d6f3ff607ebbb41b0aa19c2770d1", + "sha256:ced3eeb44df75353e08ab3b6a9e113b5f3f996bea48d4f7c027bc528ba87b672", + "sha256:d2e2c23cef30dc3cbe9cc5d04f2899e7f5e478c40d2e0a633513ad081f7361b5", + "sha256:d8a136aa7d228975a6aee3dd8bea9b28e2b43e9444aa678fb62aeb1956ff2351", + "sha256:f92fe93bc72e262b7b3f2bba9879897e2d58a989b4714ba6a5a7273e842ad2f8" ], "index": "pypi", "markers": "python_version >= '3.7'", - "version": "==0.6.2" + "version": "==0.6.3" }, "setuptools": { "hashes": [ - "sha256:0274581a0037b638b9fc1c6883cc71c0210865aaa76073f7882376b641b84e8f", - "sha256:a85e96b8be2b906f3e3e789adec6a9323abf79758ecfa3065bd740d81158b11e" + "sha256:2353af060c06388be1cecbf5953dcdb1f38362f87a2356c480b6b4d5fcfc8847", + "sha256:fc91b5f89e392ef5b77fe143b17e32f65d3024744fba66dc3afe07201684d766" ], "index": "pypi", "markers": "python_version >= '3.8'", - "version": "==74.0.0" + "version": "==74.1.1" }, "six": { "hashes": [ diff --git a/abdiff/core/__init__.py b/abdiff/core/__init__.py index 75e8cc1..3e879cc 100644 --- a/abdiff/core/__init__.py +++ b/abdiff/core/__init__.py @@ -3,7 +3,8 @@ All primary functions used by CLI are importable from here. """ +from abdiff.core.build_ab_images import build_ab_images from abdiff.core.init_job import init_job from abdiff.core.init_run import init_run -__all__ = ["init_job", "init_run"] +__all__ = ["init_job", "init_run", "build_ab_images"] diff --git a/abdiff/core/build_ab_images.py b/abdiff/core/build_ab_images.py index 70d6bf1..dbb80fb 100644 --- a/abdiff/core/build_ab_images.py +++ b/abdiff/core/build_ab_images.py @@ -1 +1,97 @@ -"""abdiff.core.build_ab_images""" +import logging +import tempfile + +import docker +import docker.models +import docker.models.images +from pygit2 import clone_repository +from pygit2.enums import ResetMode + +from abdiff.core.utils import update_or_create_job_json + +logger = logging.getLogger(__name__) + + +def build_ab_images( + job_directory: str, + commit_sha_a: str, + commit_sha_b: str, + docker_client: docker.client.DockerClient | None = None, +) -> tuple[str, str]: + """Build Docker images based on 2 commit SHAs. + + Args: + job_directory: The directory containing all files related to a job. + commit_sha_a: The SHA of the first commit for comparison. + commit_sha_b: The SHA of the second commit for comparison. + docker_client: A configured Docker client. + """ + if not docker_client: + docker_client = docker.from_env() + + image_tags = [] + for commit_sha in [commit_sha_a, commit_sha_b]: + logger.debug(f"Processing commit: {commit_sha}") + image_tag = f"transmogrifier-{job_directory.split("/")[-1]}-{commit_sha}:latest" + if docker_image_exists(docker_client, image_tag): + logger.debug(f"Docker image already exists with tag: {image_tag}") + image_tags.append(image_tag) + else: + image = build_image(job_directory, commit_sha, docker_client) + image_tags.append(image.tags[0]) + logger.debug(f"Finished processing commit: {commit_sha}") + + images_data = {"image_tag_a": image_tags[0], "image_tag_b": image_tags[1]} + update_or_create_job_json(job_directory, images_data) + return (image_tags[0], image_tags[1]) + + +def docker_image_exists( + docker_client: docker.client.DockerClient, image_tag: str +) -> bool: + """Check if Docker image already exists with a certain name. + + Args: + docker_client: A configured Docker client. + image_tag: The tag of the Docker image to be created. + """ + return image_tag in [ + image_tag for image in docker_client.images.list() for image_tag in image.tags + ] + + +def build_image( + job_directory: str, + commit_sha: str, + docker_client: docker.client.DockerClient, +) -> docker.models.images.Image: + """Clone repo and build Docker image. + + Args: + job_directory: The directory containing all files related to a job. + commit_sha: The SHA of the commit. + docker_client: A configured Docker client. + """ + with tempfile.TemporaryDirectory() as clone_directory: + image_tag = f"transmogrifier-{job_directory.split("/")[-1]}-{commit_sha}" + clone_repo_and_reset_to_commit(clone_directory, commit_sha) + image, _ = docker_client.images.build(path=clone_directory, tag=image_tag) + logger.debug(f"Docker image created with tag: {image}") + return image + + +def clone_repo_and_reset_to_commit(clone_directory: str, commit_sha: str) -> None: + """Clone GitHub repo and reset to a specified commit. + + Args: + clone_directory: The directory for the cloned repo. + commit_sha: The SHA of a repo commit. + """ + logger.debug(f"Cloning repo to: {clone_directory}") + repository = clone_repository( + "https://github.com/MITLibraries/transmogrifier.git", + clone_directory, + ) + logger.debug(f"Cloned repo to: {clone_directory}") + repository.reset(commit_sha, ResetMode.HARD) + logger.debug(f"Cloned repo reset to commit: {commit_sha}") diff --git a/tests/conftest.py b/tests/conftest.py index d4d075b..14d8880 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,3 +1,5 @@ +from unittest.mock import MagicMock + import pytest from click.testing import CliRunner @@ -7,7 +9,6 @@ @pytest.fixture(autouse=True) def _test_env(monkeypatch, tmp_path): monkeypatch.setenv("WORKSPACE", "test") - monkeypatch.setenv("ROOT_WORKING_DIRECTORY", str(tmp_path / "output")) @pytest.fixture @@ -28,3 +29,19 @@ def example_job_directory(): @pytest.fixture def job(job_directory): return init_job(job_directory) + + +@pytest.fixture +def mocked_docker_client(): + docker_client = MagicMock() + docker_images = [] + for image_tag in [ + "transmogrifier-example-job-1-abc123:latest", + "transmogrifier-example-job-1-def456:latest", + ]: + docker_image = MagicMock() + docker_image.tags = [image_tag] + docker_images.append((docker_image, "")) + docker_client.images.build.side_effect = docker_images + docker_client.images.list.return_value = [docker_image] + return docker_client diff --git a/tests/test_build_ab_images.py b/tests/test_build_ab_images.py new file mode 100644 index 0000000..9360eac --- /dev/null +++ b/tests/test_build_ab_images.py @@ -0,0 +1,68 @@ +import os +from unittest.mock import patch + +from abdiff.core.build_ab_images import ( + build_ab_images, + build_image, + clone_repo_and_reset_to_commit, + docker_image_exists, +) +from abdiff.core.utils import read_job_json + + +@patch("abdiff.core.build_ab_images.clone_repository") +def test_build_ab_images_success(mocked_clone, job_directory, mocked_docker_client): + side_effect = os.makedirs(job_directory + "/clone") + mocked_clone.side_effect = side_effect + + images = build_ab_images( + job_directory, + "abc123", + "def456", + mocked_docker_client, + ) + assert images[0] == "transmogrifier-example-job-1-abc123:latest" + assert images[1] == "transmogrifier-example-job-1-def456:latest" + assert read_job_json(job_directory) == { + "image_tag_a": "transmogrifier-example-job-1-abc123:latest", + "image_tag_b": "transmogrifier-example-job-1-def456:latest", + } + + +def test_docker_image_exists_returns_true(mocked_docker_client): + assert ( + docker_image_exists( + mocked_docker_client, "transmogrifier-example-job-1-def456:latest" + ) + is True + ) + + +def test_docker_image_exists_returns_false(mocked_docker_client): + assert ( + docker_image_exists( + mocked_docker_client, "transmogrifier-example-job-1-abc123:latest" + ) + is False + ) + + +@patch("abdiff.core.build_ab_images.clone_repository") +def test_build_image_success(mocked_clone, job_directory, mocked_docker_client): + image = build_image( + job_directory, + "abc123", + mocked_docker_client, + ) + assert image.tags[0] == "transmogrifier-example-job-1-abc123:latest" + + +@patch("abdiff.core.build_ab_images.clone_repository") +def test_clone_repo_and_reset_to_commit_success(mocked_clone, job_directory): + clone_directory = job_directory + "/clone" + assert not os.path.exists(clone_directory) + side_effect = os.makedirs(clone_directory) + mocked_clone.side_effect = side_effect + + clone_repo_and_reset_to_commit(clone_directory, "abc123") + assert os.path.exists(clone_directory)