From d67c9cc09f408fa0c31a345f776a611ffbd4e09e Mon Sep 17 00:00:00 2001 From: Henning Hinze Date: Fri, 24 Sep 2021 09:55:59 +0200 Subject: [PATCH 01/13] add rsh port flag --- README.md | 1 + poetry.lock | 123 ++++++++++++++++++++++---------------- sysrsync/command_maker.py | 5 +- sysrsync/helpers/rsync.py | 4 +- tests/sysrsync.py | 2 +- 5 files changed, 80 insertions(+), 55 deletions(-) diff --git a/README.md b/README.md index 5f2610e..7f43fd0 100644 --- a/README.md +++ b/README.md @@ -121,6 +121,7 @@ sysrsync.run(source='/home/user/files', | sync_source_contents | bool | True | Abstracts the elusive trailing slash behaviour that `source` normally has when using rsync directly, i.e. when a trailing slash is present in `source`, the folder's content is synchronized with destination. When no trailing slash is present, the folder itself is synchronized with destination. | | options | Optional[Iterable[str]] | None | List of options to be used right after rsync call, e.g. `['-a', '-v']` translates to `rsync -a -v` | | private_key | Optional[str] | None | Configures an explicit key to be used with rsync --rsh command | +| rsh_port | int | 22 | Specify port to be used if for --rsh command **returns**: `List[str]` -> the compiled list of commands to be used directly in `subprocess.run` diff --git a/poetry.lock b/poetry.lock index 4fbee76..b9b9030 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,6 +1,6 @@ [[package]] name = "astroid" -version = "2.5.6" +version = "2.8.0" description = "An abstract syntax tree for Python with inference support." category = "dev" optional = false @@ -9,6 +9,7 @@ python-versions = "~=3.6" [package.dependencies] lazy-object-proxy = ">=1.4.0" typed-ast = {version = ">=1.4.0,<1.5", markers = "implementation_name == \"cpython\" and python_version < \"3.8\""} +typing-extensions = {version = ">=3.10", markers = "python_version < \"3.10\""} wrapt = ">=1.11,<1.13" [[package]] @@ -33,7 +34,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" [[package]] name = "flake8" -version = "3.9.1" +version = "3.9.2" description = "the modular source code checker: pep8 pyflakes and co" category = "dev" optional = false @@ -47,7 +48,7 @@ pyflakes = ">=2.3.0,<2.4.0" [[package]] name = "importlib-metadata" -version = "4.0.1" +version = "4.8.1" description = "Read metadata from Python packages" category = "dev" optional = false @@ -59,7 +60,8 @@ zipp = ">=0.5" [package.extras] docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"] -testing = ["pytest (>=4.6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "packaging", "pep517", "pyfakefs", "flufl.flake8", "pytest-black (>=0.3.7)", "pytest-mypy", "importlib-resources (>=1.3)"] +testing = ["pytest (>=4.6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "packaging", "pep517", "pyfakefs", "flufl.flake8", "pytest-perf (>=0.9.2)", "pytest-black (>=0.3.7)", "pytest-mypy", "importlib-resources (>=1.3)"] +perf = ["ipython"] [[package]] name = "isort" @@ -70,9 +72,9 @@ optional = false python-versions = ">=3.6,<4.0" [package.extras] +colors = ["colorama (>=0.4.3,<0.5.0)"] pipfile_deprecated_finder = ["pipreqs", "requirementslib"] requirements_deprecated_finder = ["pipreqs", "pip-api"] -colors = ["colorama (>=0.4.3,<0.5.0)"] [[package]] name = "lazy-object-proxy" @@ -92,7 +94,7 @@ python-versions = "*" [[package]] name = "mypy" -version = "0.812" +version = "0.910" description = "Optional static typing for Python" category = "dev" optional = false @@ -100,11 +102,13 @@ python-versions = ">=3.5" [package.dependencies] mypy-extensions = ">=0.4.3,<0.5.0" -typed-ast = ">=1.4.0,<1.5.0" +toml = "*" +typed-ast = {version = ">=1.4.0,<1.5.0", markers = "python_version < \"3.8\""} typing-extensions = ">=3.7.4" [package.extras] dmypy = ["psutil (>=4.0)"] +python2 = ["typed-ast (>=1.4.0,<1.5.0)"] [[package]] name = "mypy-extensions" @@ -122,6 +126,18 @@ category = "dev" optional = false python-versions = "*" +[[package]] +name = "platformdirs" +version = "2.3.0" +description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.extras] +test = ["appdirs (==1.4.4)", "pytest (>=6)", "pytest-cov (>=2.7)", "pytest-mock (>=3.6)"] +docs = ["Sphinx (>=4)", "furo (>=2021.7.5b38)", "proselint (>=0.10.2)", "sphinx-autodoc-typehints (>=1.12)"] + [[package]] name = "pycodestyle" version = "2.7.0" @@ -140,29 +156,31 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" [[package]] name = "pylint" -version = "2.8.2" +version = "2.11.1" description = "python code static checker" category = "dev" optional = false python-versions = "~=3.6" [package.dependencies] -astroid = ">=2.5.6,<2.7" +astroid = ">=2.8.0,<2.9" colorama = {version = "*", markers = "sys_platform == \"win32\""} isort = ">=4.2.5,<6" mccabe = ">=0.6,<0.7" +platformdirs = ">=2.2.0" toml = ">=0.7.1" +typing-extensions = {version = ">=3.10.0", markers = "python_version < \"3.10\""} [[package]] name = "rope" -version = "0.19.0" +version = "0.20.1" description = "a python refactoring library..." category = "dev" optional = false python-versions = "*" [package.extras] -dev = ["pytest"] +dev = ["pytest", "pytest-timeout"] [[package]] name = "toml" @@ -182,7 +200,7 @@ python-versions = "*" [[package]] name = "typing-extensions" -version = "3.10.0.0" +version = "3.10.0.2" description = "Backported and Experimental Type Hints for Python 3.5+" category = "dev" optional = false @@ -198,7 +216,7 @@ python-versions = "*" [[package]] name = "zipp" -version = "3.4.1" +version = "3.5.0" description = "Backport of pathlib-compatible object wrapper for zip files" category = "dev" optional = false @@ -206,7 +224,7 @@ python-versions = ">=3.6" [package.extras] docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"] -testing = ["pytest (>=4.6)", "pytest-checkdocs (>=1.2.3)", "pytest-flake8", "pytest-cov", "pytest-enabler", "jaraco.itertools", "func-timeout", "pytest-black (>=0.3.7)", "pytest-mypy"] +testing = ["pytest (>=4.6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "jaraco.itertools", "func-timeout", "pytest-black (>=0.3.7)", "pytest-mypy"] [metadata] lock-version = "1.1" @@ -215,8 +233,8 @@ content-hash = "aa3c53d3a4e8c9f1ac14e75b337c07e576ead22a6a67ca127b174cdd4a1abff8 [metadata.files] astroid = [ - {file = "astroid-2.5.6-py3-none-any.whl", hash = "sha256:4db03ab5fc3340cf619dbc25e42c2cc3755154ce6009469766d7143d1fc2ee4e"}, - {file = "astroid-2.5.6.tar.gz", hash = "sha256:8a398dfce302c13f14bab13e2b14fe385d32b73f4e4853b9bdfb64598baa1975"}, + {file = "astroid-2.8.0-py3-none-any.whl", hash = "sha256:dcc06f6165f415220013801642bd6c9808a02967070919c4b746c6864c205471"}, + {file = "astroid-2.8.0.tar.gz", hash = "sha256:fe81f80c0b35264acb5653302ffbd935d394f1775c5e4487df745bf9c2442708"}, ] autopep8 = [ {file = "autopep8-1.5.7-py2.py3-none-any.whl", hash = "sha256:aa213493c30dcdac99537249ee65b24af0b2c29f2e83cd8b3f68760441ed0db9"}, @@ -227,12 +245,12 @@ colorama = [ {file = "colorama-0.4.4.tar.gz", hash = "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b"}, ] flake8 = [ - {file = "flake8-3.9.1-py2.py3-none-any.whl", hash = "sha256:3b9f848952dddccf635be78098ca75010f073bfe14d2c6bda867154bea728d2a"}, - {file = "flake8-3.9.1.tar.gz", hash = "sha256:1aa8990be1e689d96c745c5682b687ea49f2e05a443aff1f8251092b0014e378"}, + {file = "flake8-3.9.2-py2.py3-none-any.whl", hash = "sha256:bf8fd333346d844f616e8d47905ef3a3384edae6b4e9beb0c5101e25e3110907"}, + {file = "flake8-3.9.2.tar.gz", hash = "sha256:07528381786f2a6237b061f6e96610a4167b226cb926e2aa2b6b1d78057c576b"}, ] importlib-metadata = [ - {file = "importlib_metadata-4.0.1-py3-none-any.whl", hash = "sha256:d7eb1dea6d6a6086f8be21784cc9e3bcfa55872b52309bc5fad53a8ea444465d"}, - {file = "importlib_metadata-4.0.1.tar.gz", hash = "sha256:8c501196e49fb9df5df43833bdb1e4328f64847763ec8a50703148b73784d581"}, + {file = "importlib_metadata-4.8.1-py3-none-any.whl", hash = "sha256:b618b6d2d5ffa2f16add5697cf57a46c76a56229b0ed1c438322e4e95645bd15"}, + {file = "importlib_metadata-4.8.1.tar.gz", hash = "sha256:f284b3e11256ad1e5d03ab86bb2ccd6f5339688ff17a4d797a0fe7df326f23b1"}, ] isort = [ {file = "isort-5.8.0-py3-none-any.whl", hash = "sha256:2bb1680aad211e3c9944dbce1d4ba09a989f04e238296c87fe2139faa26d655d"}, @@ -267,28 +285,29 @@ mccabe = [ {file = "mccabe-0.6.1.tar.gz", hash = "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"}, ] mypy = [ - {file = "mypy-0.812-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:a26f8ec704e5a7423c8824d425086705e381b4f1dfdef6e3a1edab7ba174ec49"}, - {file = "mypy-0.812-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:28fb5479c494b1bab244620685e2eb3c3f988d71fd5d64cc753195e8ed53df7c"}, - {file = "mypy-0.812-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:9743c91088d396c1a5a3c9978354b61b0382b4e3c440ce83cf77994a43e8c521"}, - {file = "mypy-0.812-cp35-cp35m-win_amd64.whl", hash = "sha256:d7da2e1d5f558c37d6e8c1246f1aec1e7349e4913d8fb3cb289a35de573fe2eb"}, - {file = "mypy-0.812-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:4eec37370483331d13514c3f55f446fc5248d6373e7029a29ecb7b7494851e7a"}, - {file = "mypy-0.812-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:d65cc1df038ef55a99e617431f0553cd77763869eebdf9042403e16089fe746c"}, - {file = "mypy-0.812-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:61a3d5b97955422964be6b3baf05ff2ce7f26f52c85dd88db11d5e03e146a3a6"}, - {file = "mypy-0.812-cp36-cp36m-win_amd64.whl", hash = "sha256:25adde9b862f8f9aac9d2d11971f226bd4c8fbaa89fb76bdadb267ef22d10064"}, - {file = "mypy-0.812-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:552a815579aa1e995f39fd05dde6cd378e191b063f031f2acfe73ce9fb7f9e56"}, - {file = "mypy-0.812-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:499c798053cdebcaa916eef8cd733e5584b5909f789de856b482cd7d069bdad8"}, - {file = "mypy-0.812-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:5873888fff1c7cf5b71efbe80e0e73153fe9212fafdf8e44adfe4c20ec9f82d7"}, - {file = "mypy-0.812-cp37-cp37m-win_amd64.whl", hash = "sha256:9f94aac67a2045ec719ffe6111df543bac7874cee01f41928f6969756e030564"}, - {file = "mypy-0.812-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d23e0ea196702d918b60c8288561e722bf437d82cb7ef2edcd98cfa38905d506"}, - {file = "mypy-0.812-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:674e822aa665b9fd75130c6c5f5ed9564a38c6cea6a6432ce47eafb68ee578c5"}, - {file = "mypy-0.812-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:abf7e0c3cf117c44d9285cc6128856106183938c68fd4944763003decdcfeb66"}, - {file = "mypy-0.812-cp38-cp38-win_amd64.whl", hash = "sha256:0d0a87c0e7e3a9becdfbe936c981d32e5ee0ccda3e0f07e1ef2c3d1a817cf73e"}, - {file = "mypy-0.812-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7ce3175801d0ae5fdfa79b4f0cfed08807af4d075b402b7e294e6aa72af9aa2a"}, - {file = "mypy-0.812-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:b09669bcda124e83708f34a94606e01b614fa71931d356c1f1a5297ba11f110a"}, - {file = "mypy-0.812-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:33f159443db0829d16f0a8d83d94df3109bb6dd801975fe86bacb9bf71628e97"}, - {file = "mypy-0.812-cp39-cp39-win_amd64.whl", hash = "sha256:3f2aca7f68580dc2508289c729bd49ee929a436208d2b2b6aab15745a70a57df"}, - {file = "mypy-0.812-py3-none-any.whl", hash = "sha256:2f9b3407c58347a452fc0736861593e105139b905cca7d097e413453a1d650b4"}, - {file = "mypy-0.812.tar.gz", hash = "sha256:cd07039aa5df222037005b08fbbfd69b3ab0b0bd7a07d7906de75ae52c4e3119"}, + {file = "mypy-0.910-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:a155d80ea6cee511a3694b108c4494a39f42de11ee4e61e72bc424c490e46457"}, + {file = "mypy-0.910-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:b94e4b785e304a04ea0828759172a15add27088520dc7e49ceade7834275bedb"}, + {file = "mypy-0.910-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:088cd9c7904b4ad80bec811053272986611b84221835e079be5bcad029e79dd9"}, + {file = "mypy-0.910-cp35-cp35m-win_amd64.whl", hash = "sha256:adaeee09bfde366d2c13fe6093a7df5df83c9a2ba98638c7d76b010694db760e"}, + {file = "mypy-0.910-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:ecd2c3fe726758037234c93df7e98deb257fd15c24c9180dacf1ef829da5f921"}, + {file = "mypy-0.910-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:d9dd839eb0dc1bbe866a288ba3c1afc33a202015d2ad83b31e875b5905a079b6"}, + {file = "mypy-0.910-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:3e382b29f8e0ccf19a2df2b29a167591245df90c0b5a2542249873b5c1d78212"}, + {file = "mypy-0.910-cp36-cp36m-win_amd64.whl", hash = "sha256:53fd2eb27a8ee2892614370896956af2ff61254c275aaee4c230ae771cadd885"}, + {file = "mypy-0.910-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b6fb13123aeef4a3abbcfd7e71773ff3ff1526a7d3dc538f3929a49b42be03f0"}, + {file = "mypy-0.910-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:e4dab234478e3bd3ce83bac4193b2ecd9cf94e720ddd95ce69840273bf44f6de"}, + {file = "mypy-0.910-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:7df1ead20c81371ccd6091fa3e2878559b5c4d4caadaf1a484cf88d93ca06703"}, + {file = "mypy-0.910-cp37-cp37m-win_amd64.whl", hash = "sha256:0aadfb2d3935988ec3815952e44058a3100499f5be5b28c34ac9d79f002a4a9a"}, + {file = "mypy-0.910-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ec4e0cd079db280b6bdabdc807047ff3e199f334050db5cbb91ba3e959a67504"}, + {file = "mypy-0.910-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:119bed3832d961f3a880787bf621634ba042cb8dc850a7429f643508eeac97b9"}, + {file = "mypy-0.910-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:866c41f28cee548475f146aa4d39a51cf3b6a84246969f3759cb3e9c742fc072"}, + {file = "mypy-0.910-cp38-cp38-win_amd64.whl", hash = "sha256:ceb6e0a6e27fb364fb3853389607cf7eb3a126ad335790fa1e14ed02fba50811"}, + {file = "mypy-0.910-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1a85e280d4d217150ce8cb1a6dddffd14e753a4e0c3cf90baabb32cefa41b59e"}, + {file = "mypy-0.910-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:42c266ced41b65ed40a282c575705325fa7991af370036d3f134518336636f5b"}, + {file = "mypy-0.910-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:3c4b8ca36877fc75339253721f69603a9c7fdb5d4d5a95a1a1b899d8b86a4de2"}, + {file = "mypy-0.910-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:c0df2d30ed496a08de5daed2a9ea807d07c21ae0ab23acf541ab88c24b26ab97"}, + {file = "mypy-0.910-cp39-cp39-win_amd64.whl", hash = "sha256:c6c2602dffb74867498f86e6129fd52a2770c48b7cd3ece77ada4fa38f94eba8"}, + {file = "mypy-0.910-py3-none-any.whl", hash = "sha256:ef565033fa5a958e62796867b1df10c40263ea9ded87164d67572834e57a174d"}, + {file = "mypy-0.910.tar.gz", hash = "sha256:704098302473cb31a218f1775a873b376b30b4c18229421e9e9dc8916fd16150"}, ] mypy-extensions = [ {file = "mypy_extensions-0.4.3-py2.py3-none-any.whl", hash = "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d"}, @@ -299,6 +318,10 @@ nose = [ {file = "nose-1.3.7-py3-none-any.whl", hash = "sha256:9ff7c6cc443f8c51994b34a667bbcf45afd6d945be7477b52e97516fd17c53ac"}, {file = "nose-1.3.7.tar.gz", hash = "sha256:f1bffef9cbc82628f6e7d7b40d7e255aefaa1adb6a1b1d26c69a8b79e6208a98"}, ] +platformdirs = [ + {file = "platformdirs-2.3.0-py3-none-any.whl", hash = "sha256:8003ac87717ae2c7ee1ea5a84a1a61e87f3fbd16eb5aadba194ea30a9019f648"}, + {file = "platformdirs-2.3.0.tar.gz", hash = "sha256:15b056538719b1c94bdaccb29e5f81879c7f7f0f4a153f46086d155dffcd4f0f"}, +] pycodestyle = [ {file = "pycodestyle-2.7.0-py2.py3-none-any.whl", hash = "sha256:514f76d918fcc0b55c6680472f0a37970994e07bbb80725808c17089be302068"}, {file = "pycodestyle-2.7.0.tar.gz", hash = "sha256:c389c1d06bf7904078ca03399a4816f974a1d590090fecea0c63ec26ebaf1cef"}, @@ -308,11 +331,11 @@ pyflakes = [ {file = "pyflakes-2.3.1.tar.gz", hash = "sha256:f5bc8ecabc05bb9d291eb5203d6810b49040f6ff446a756326104746cc00c1db"}, ] pylint = [ - {file = "pylint-2.8.2-py3-none-any.whl", hash = "sha256:f7e2072654a6b6afdf5e2fb38147d3e2d2d43c89f648637baab63e026481279b"}, - {file = "pylint-2.8.2.tar.gz", hash = "sha256:586d8fa9b1891f4b725f587ef267abe2a1bad89d6b184520c7f07a253dd6e217"}, + {file = "pylint-2.11.1-py3-none-any.whl", hash = "sha256:0f358e221c45cbd4dad2a1e4b883e75d28acdcccd29d40c76eb72b307269b126"}, + {file = "pylint-2.11.1.tar.gz", hash = "sha256:2c9843fff1a88ca0ad98a256806c82c5a8f86086e7ccbdb93297d86c3f90c436"}, ] rope = [ - {file = "rope-0.19.0.tar.gz", hash = "sha256:64e6d747532e1f5c8009ec5aae3e5523a5bcedf516f39a750d57d8ed749d90da"}, + {file = "rope-0.20.1.tar.gz", hash = "sha256:505a2f6b4ac7b18e0429be179f4d8712243a194da5c866b0731f9d91ce7590bb"}, ] toml = [ {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, @@ -351,14 +374,14 @@ typed-ast = [ {file = "typed_ast-1.4.3.tar.gz", hash = "sha256:fb1bbeac803adea29cedd70781399c99138358c26d05fcbd23c13016b7f5ec65"}, ] typing-extensions = [ - {file = "typing_extensions-3.10.0.0-py2-none-any.whl", hash = "sha256:0ac0f89795dd19de6b97debb0c6af1c70987fd80a2d62d1958f7e56fcc31b497"}, - {file = "typing_extensions-3.10.0.0-py3-none-any.whl", hash = "sha256:779383f6086d90c99ae41cf0ff39aac8a7937a9283ce0a414e5dd782f4c94a84"}, - {file = "typing_extensions-3.10.0.0.tar.gz", hash = "sha256:50b6f157849174217d0656f99dc82fe932884fb250826c18350e159ec6cdf342"}, + {file = "typing_extensions-3.10.0.2-py2-none-any.whl", hash = "sha256:d8226d10bc02a29bcc81df19a26e56a9647f8b0a6d4a83924139f4a8b01f17b7"}, + {file = "typing_extensions-3.10.0.2-py3-none-any.whl", hash = "sha256:f1d25edafde516b146ecd0613dabcc61409817af4766fbbcfb8d1ad4ec441a34"}, + {file = "typing_extensions-3.10.0.2.tar.gz", hash = "sha256:49f75d16ff11f1cd258e1b988ccff82a3ca5570217d7ad8c5f48205dd99a677e"}, ] wrapt = [ {file = "wrapt-1.12.1.tar.gz", hash = "sha256:b62ffa81fb85f4332a4f609cab4ac40709470da05643a082ec1eb88e6d9b97d7"}, ] zipp = [ - {file = "zipp-3.4.1-py3-none-any.whl", hash = "sha256:51cb66cc54621609dd593d1787f286ee42a5c0adbb4b29abea5a63edc3e03098"}, - {file = "zipp-3.4.1.tar.gz", hash = "sha256:3607921face881ba3e026887d8150cca609d517579abe052ac81fc5aeffdbd76"}, + {file = "zipp-3.5.0-py3-none-any.whl", hash = "sha256:957cfda87797e389580cb8b9e3870841ca991e2125350677b2ca83a0e99390a3"}, + {file = "zipp-3.5.0.tar.gz", hash = "sha256:f5812b1e007e48cff63449a5e9f4e7ebea716b4111f9c4f9a645f91d579bf0c4"}, ] diff --git a/sysrsync/command_maker.py b/sysrsync/command_maker.py index d60d300..f5aa164 100644 --- a/sysrsync/command_maker.py +++ b/sysrsync/command_maker.py @@ -14,7 +14,8 @@ def get_rsync_command(source: str, exclusions: Optional[Iterable[str]] = None, sync_source_contents: bool = True, options: Optional[Iterable[str]] = None, - private_key: Optional[str] = None) -> List[str]: + private_key: Optional[str] = None, + rsh_port: int = 22) -> List[str]: if source_ssh is not None and destination_ssh is not None: raise RemotesError() @@ -31,7 +32,7 @@ def get_rsync_command(source: str, if exclusions else []) - rsh = (get_rsh_command(private_key) + rsh = (get_rsh_command(private_key, rsh_port) if private_key is not None else []) diff --git a/sysrsync/helpers/rsync.py b/sysrsync/helpers/rsync.py index 68f0bff..8187893 100644 --- a/sysrsync/helpers/rsync.py +++ b/sysrsync/helpers/rsync.py @@ -12,10 +12,10 @@ def get_exclusions(exclusions: Iterable[str]) -> Iterable[str]: if exclusion != '--exclude')) -def get_rsh_command(private_key: str): +def get_rsh_command(private_key: str, port: int): expanded_key_file = os.path.expandvars(os.path.expanduser(private_key)) if not Path(expanded_key_file).exists(): raise PrivateKeyError(expanded_key_file) - return [f"--rsh='ssh -i {expanded_key_file}'"] + return [f"--rsh='ssh -i {expanded_key_file} -p {str(port)}'"] diff --git a/tests/sysrsync.py b/tests/sysrsync.py index a027b5e..7f81617 100644 --- a/tests/sysrsync.py +++ b/tests/sysrsync.py @@ -101,7 +101,7 @@ def test_rsync_private_key(): source_dir = '/home/user/files/' target_dir = '/home/server/files' destination_ssh = 'myserver' - expect = ['rsync', f"--rsh='ssh -i {temp_file}'", source_dir, f'{destination_ssh}:{target_dir}'] + expect = ['rsync', f"--rsh='ssh -i {temp_file} -p 22'", source_dir, f'{destination_ssh}:{target_dir}'] actual = get_rsync_command(source=source_dir, destination=target_dir, destination_ssh=destination_ssh, From 184b1a3fa6aaa1de74534e116d7afd15f7a0dd1f Mon Sep 17 00:00:00 2001 From: Henning Hinze Date: Fri, 24 Sep 2021 15:22:40 +0200 Subject: [PATCH 02/13] added StrictHostKeyChecking property --- README.md | 3 ++- pyproject.toml | 2 +- sysrsync/command_maker.py | 5 +++-- sysrsync/helpers/rsync.py | 6 ++++-- tests/sysrsync.py | 6 ++++-- 5 files changed, 14 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 7f43fd0..bdbbb42 100644 --- a/README.md +++ b/README.md @@ -121,7 +121,8 @@ sysrsync.run(source='/home/user/files', | sync_source_contents | bool | True | Abstracts the elusive trailing slash behaviour that `source` normally has when using rsync directly, i.e. when a trailing slash is present in `source`, the folder's content is synchronized with destination. When no trailing slash is present, the folder itself is synchronized with destination. | | options | Optional[Iterable[str]] | None | List of options to be used right after rsync call, e.g. `['-a', '-v']` translates to `rsync -a -v` | | private_key | Optional[str] | None | Configures an explicit key to be used with rsync --rsh command | -| rsh_port | int | 22 | Specify port to be used if for --rsh command +| rsh_port | int | 22 | Specify port to be used for --rsh command +| strict_host_key_checking | bool | True | set StrictHostKeyChecking property for rsh #cf. https://superuser.com/questions/125324/how-can-i-avoid-sshs-host-verification-for-known-hosts **returns**: `List[str]` -> the compiled list of commands to be used directly in `subprocess.run` diff --git a/pyproject.toml b/pyproject.toml index d20334c..d64ef25 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,7 +1,7 @@ [tool] [tool.poetry] name = "sysrsync" -version = "1.0.0" +version = "1.1.1" description = "Simple and safe python wrapper for calling system rsync" authors = ["Gabriel Chamon "] readme = "README.md" diff --git a/sysrsync/command_maker.py b/sysrsync/command_maker.py index f5aa164..59fc2c6 100644 --- a/sysrsync/command_maker.py +++ b/sysrsync/command_maker.py @@ -15,7 +15,8 @@ def get_rsync_command(source: str, sync_source_contents: bool = True, options: Optional[Iterable[str]] = None, private_key: Optional[str] = None, - rsh_port: int = 22) -> List[str]: + rsh_port: int = 22, + strict_host_key_checking: bool = True) -> List[str]: if source_ssh is not None and destination_ssh is not None: raise RemotesError() @@ -32,7 +33,7 @@ def get_rsync_command(source: str, if exclusions else []) - rsh = (get_rsh_command(private_key, rsh_port) + rsh = (get_rsh_command(private_key, rsh_port, strict_host_key_checking) if private_key is not None else []) diff --git a/sysrsync/helpers/rsync.py b/sysrsync/helpers/rsync.py index 8187893..39e9945 100644 --- a/sysrsync/helpers/rsync.py +++ b/sysrsync/helpers/rsync.py @@ -12,10 +12,12 @@ def get_exclusions(exclusions: Iterable[str]) -> Iterable[str]: if exclusion != '--exclude')) -def get_rsh_command(private_key: str, port: int): +def get_rsh_command(private_key: str, port: int, strict_host_key_checking: bool): expanded_key_file = os.path.expandvars(os.path.expanduser(private_key)) + strict_host_key_checking_str: str = "yes" if strict_host_key_checking else "no" + if not Path(expanded_key_file).exists(): raise PrivateKeyError(expanded_key_file) - return [f"--rsh='ssh -i {expanded_key_file} -p {str(port)}'"] + return [f"--rsh='ssh -i {expanded_key_file} -p {str(port)} -o \"StrictHostKeyChecking {strict_host_key_checking_str}\"'"] diff --git a/tests/sysrsync.py b/tests/sysrsync.py index 7f81617..7add945 100644 --- a/tests/sysrsync.py +++ b/tests/sysrsync.py @@ -101,11 +101,13 @@ def test_rsync_private_key(): source_dir = '/home/user/files/' target_dir = '/home/server/files' destination_ssh = 'myserver' - expect = ['rsync', f"--rsh='ssh -i {temp_file} -p 22'", source_dir, f'{destination_ssh}:{target_dir}'] + strict_host_key_checking = False + expect = ['rsync', f"--rsh='ssh -i {temp_file} -p 22 -o \"StrictHostKeyChecking no\"'", source_dir, f'{destination_ssh}:{target_dir}'] actual = get_rsync_command(source=source_dir, destination=target_dir, destination_ssh=destination_ssh, - private_key=temp_file) + private_key=temp_file, + strict_host_key_checking=strict_host_key_checking) eq_(expect, actual) @raises(PrivateKeyError) From a00b8b828d6d8fb93e5d3c3092f3b63bfb26e63b Mon Sep 17 00:00:00 2001 From: Henning Hinze Date: Fri, 24 Sep 2021 15:27:41 +0200 Subject: [PATCH 03/13] doc --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index bdbbb42..fd57e69 100644 --- a/README.md +++ b/README.md @@ -121,8 +121,8 @@ sysrsync.run(source='/home/user/files', | sync_source_contents | bool | True | Abstracts the elusive trailing slash behaviour that `source` normally has when using rsync directly, i.e. when a trailing slash is present in `source`, the folder's content is synchronized with destination. When no trailing slash is present, the folder itself is synchronized with destination. | | options | Optional[Iterable[str]] | None | List of options to be used right after rsync call, e.g. `['-a', '-v']` translates to `rsync -a -v` | | private_key | Optional[str] | None | Configures an explicit key to be used with rsync --rsh command | -| rsh_port | int | 22 | Specify port to be used for --rsh command -| strict_host_key_checking | bool | True | set StrictHostKeyChecking property for rsh #cf. https://superuser.com/questions/125324/how-can-i-avoid-sshs-host-verification-for-known-hosts +| rsh_port | int | 22 | Specify port to be used for --rsh command | +| strict_host_key_checking | bool | True | set StrictHostKeyChecking property for rsh #cf. https://superuser.com/questions/125324/how-can-i-avoid-sshs-host-verification-for-known-hosts | **returns**: `List[str]` -> the compiled list of commands to be used directly in `subprocess.run` From 4dce1f47873a68f19ebfa3f49d4bbc72a6c93a83 Mon Sep 17 00:00:00 2001 From: henning hinze Date: Sun, 26 Sep 2021 17:23:48 +0200 Subject: [PATCH 04/13] changed port and strict_host_key_checking to optional types. determine rsh command if private key or rsh port or strict_host_key_checking are provided --- README.md | 4 ++-- sysrsync/command_maker.py | 6 +++--- sysrsync/helpers/rsync.py | 26 +++++++++++++++++++------- tests/sysrsync.py | 4 +++- 4 files changed, 27 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index fd57e69..7c0290e 100644 --- a/README.md +++ b/README.md @@ -121,8 +121,8 @@ sysrsync.run(source='/home/user/files', | sync_source_contents | bool | True | Abstracts the elusive trailing slash behaviour that `source` normally has when using rsync directly, i.e. when a trailing slash is present in `source`, the folder's content is synchronized with destination. When no trailing slash is present, the folder itself is synchronized with destination. | | options | Optional[Iterable[str]] | None | List of options to be used right after rsync call, e.g. `['-a', '-v']` translates to `rsync -a -v` | | private_key | Optional[str] | None | Configures an explicit key to be used with rsync --rsh command | -| rsh_port | int | 22 | Specify port to be used for --rsh command | -| strict_host_key_checking | bool | True | set StrictHostKeyChecking property for rsh #cf. https://superuser.com/questions/125324/how-can-i-avoid-sshs-host-verification-for-known-hosts | +| rsh_port | Optional[int] | None | Specify port to be used for --rsh command | +| strict_host_key_checking | Optional[bool] | None | set StrictHostKeyChecking property for rsh #cf. https://superuser.com/questions/125324/how-can-i-avoid-sshs-host-verification-for-known-hosts | **returns**: `List[str]` -> the compiled list of commands to be used directly in `subprocess.run` diff --git a/sysrsync/command_maker.py b/sysrsync/command_maker.py index 59fc2c6..f1a6f86 100644 --- a/sysrsync/command_maker.py +++ b/sysrsync/command_maker.py @@ -15,8 +15,8 @@ def get_rsync_command(source: str, sync_source_contents: bool = True, options: Optional[Iterable[str]] = None, private_key: Optional[str] = None, - rsh_port: int = 22, - strict_host_key_checking: bool = True) -> List[str]: + rsh_port: Optional[int] = None, + strict_host_key_checking: Optional[bool] = None) -> List[str]: if source_ssh is not None and destination_ssh is not None: raise RemotesError() @@ -34,7 +34,7 @@ def get_rsync_command(source: str, else []) rsh = (get_rsh_command(private_key, rsh_port, strict_host_key_checking) - if private_key is not None + if private_key is not None or rsh_port is not None or strict_host_key_checking is not None else []) if options is None: diff --git a/sysrsync/helpers/rsync.py b/sysrsync/helpers/rsync.py index 39e9945..31e8d31 100644 --- a/sysrsync/helpers/rsync.py +++ b/sysrsync/helpers/rsync.py @@ -1,6 +1,6 @@ import os from pathlib import Path -from typing import Iterable +from typing import Iterable, List, Optional from sysrsync.exceptions import PrivateKeyError from sysrsync.helpers.iterators import flatten @@ -12,12 +12,24 @@ def get_exclusions(exclusions: Iterable[str]) -> Iterable[str]: if exclusion != '--exclude')) -def get_rsh_command(private_key: str, port: int, strict_host_key_checking: bool): - expanded_key_file = os.path.expandvars(os.path.expanduser(private_key)) +def get_rsh_command(private_key: Optional[str] = None, port: Optional[int] = None, strict_host_key_checking: Optional[bool] = None): - strict_host_key_checking_str: str = "yes" if strict_host_key_checking else "no" + args: List[str] = [] - if not Path(expanded_key_file).exists(): - raise PrivateKeyError(expanded_key_file) + if private_key is not None: + expanded_key_file = os.path.expandvars(os.path.expanduser(private_key)) - return [f"--rsh='ssh -i {expanded_key_file} -p {str(port)} -o \"StrictHostKeyChecking {strict_host_key_checking_str}\"'"] + if not Path(expanded_key_file).exists(): + raise PrivateKeyError(expanded_key_file) + + args.extend(["-i", expanded_key_file]) + + if port is not None: + args.extend(["-p", str(port)]) + + if strict_host_key_checking is not None: + args.extend(["-o", f'"StrictHostKeyChecking {"yes" if strict_host_key_checking else "no"}']) + + string_args = " ".join(args) + + return [f"--rsh='ssh {string_args}'"] diff --git a/tests/sysrsync.py b/tests/sysrsync.py index 7add945..914eff8 100644 --- a/tests/sysrsync.py +++ b/tests/sysrsync.py @@ -101,12 +101,14 @@ def test_rsync_private_key(): source_dir = '/home/user/files/' target_dir = '/home/server/files' destination_ssh = 'myserver' + port = 2222 strict_host_key_checking = False - expect = ['rsync', f"--rsh='ssh -i {temp_file} -p 22 -o \"StrictHostKeyChecking no\"'", source_dir, f'{destination_ssh}:{target_dir}'] + expect = ['rsync', f"--rsh='ssh -i {temp_file} -p {port} -o \"StrictHostKeyChecking no\"'", source_dir, f'{destination_ssh}:{target_dir}'] actual = get_rsync_command(source=source_dir, destination=target_dir, destination_ssh=destination_ssh, private_key=temp_file, + rsh_port=port, strict_host_key_checking=strict_host_key_checking) eq_(expect, actual) From f146f42c7c052999e6e0a90eac72cdd0b0cc35f8 Mon Sep 17 00:00:00 2001 From: Henning Hinze Date: Mon, 27 Sep 2021 16:36:07 +0200 Subject: [PATCH 05/13] attach stdout to completed process --- pyproject.toml | 2 +- sysrsync/runner.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index d64ef25..6b8776a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,7 +1,7 @@ [tool] [tool.poetry] name = "sysrsync" -version = "1.1.1" +version = "1.1.2" description = "Simple and safe python wrapper for calling system rsync" authors = ["Gabriel Chamon "] readme = "README.md" diff --git a/sysrsync/runner.py b/sysrsync/runner.py index 8f5b141..1c44e07 100644 --- a/sysrsync/runner.py +++ b/sysrsync/runner.py @@ -12,7 +12,7 @@ def run(cwd=os.getcwd(), strict=True, verbose=False, **kwargs): if verbose is True: print(f'[sysrsync runner] running command on "{cwd}":') print(rsync_string) - process = subprocess.run(rsync_string, cwd=cwd, shell=True) + process = subprocess.run(rsync_string, cwd=cwd, shell=True, stdout=subprocess.PIPE) if strict is True: code = process.returncode From f6ac1b12d5dd97ae32b2dd20913436419f3da997 Mon Sep 17 00:00:00 2001 From: Henning Hinze Date: Wed, 29 Sep 2021 09:46:52 +0200 Subject: [PATCH 06/13] typo --- sysrsync/helpers/rsync.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sysrsync/helpers/rsync.py b/sysrsync/helpers/rsync.py index 31e8d31..90d454e 100644 --- a/sysrsync/helpers/rsync.py +++ b/sysrsync/helpers/rsync.py @@ -28,7 +28,7 @@ def get_rsh_command(private_key: Optional[str] = None, port: Optional[int] = Non args.extend(["-p", str(port)]) if strict_host_key_checking is not None: - args.extend(["-o", f'"StrictHostKeyChecking {"yes" if strict_host_key_checking else "no"}']) + args.extend(["-o", f'"StrictHostKeyChecking {"yes" if strict_host_key_checking else "no"}"']) string_args = " ".join(args) From 62c1b984f6c0c15a99e9ae9112070c03afa49457 Mon Sep 17 00:00:00 2001 From: Gabriel Chamon Araujo Date: Sun, 29 May 2022 15:41:59 -0300 Subject: [PATCH 07/13] minor refactor rsh comamnd check for readability --- sysrsync/command_maker.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sysrsync/command_maker.py b/sysrsync/command_maker.py index f1a6f86..b11d9b8 100644 --- a/sysrsync/command_maker.py +++ b/sysrsync/command_maker.py @@ -34,7 +34,7 @@ def get_rsync_command(source: str, else []) rsh = (get_rsh_command(private_key, rsh_port, strict_host_key_checking) - if private_key is not None or rsh_port is not None or strict_host_key_checking is not None + if not any((private_key, rsh_port, strict_host_key_checking)) else []) if options is None: From 3ade0a1213d1a8cfc232455bc426651c4d98600b Mon Sep 17 00:00:00 2001 From: Gabriel Chamon Araujo Date: Sun, 29 May 2022 18:45:26 -0300 Subject: [PATCH 08/13] adding end-to-end tests using docker-compose --- docker-compose.yml | 28 +++++++++++++++ end-to-end-tests/Dockerfile.ssh-server | 3 ++ end-to-end-tests/Dockerfile.sysrsync-client | 3 ++ end-to-end-tests/keys/test-key | 38 ++++++++++++++++++++ end-to-end-tests/keys/test-key.pub | 1 + end-to-end-tests/test-cases/file with spaces | 0 end-to-end-tests/test-cases/test_file | 1 + end-to-end-tests/tests.py | 32 +++++++++++++++++ 8 files changed, 106 insertions(+) create mode 100644 docker-compose.yml create mode 100644 end-to-end-tests/Dockerfile.ssh-server create mode 100644 end-to-end-tests/Dockerfile.sysrsync-client create mode 100644 end-to-end-tests/keys/test-key create mode 100644 end-to-end-tests/keys/test-key.pub create mode 100644 end-to-end-tests/test-cases/file with spaces create mode 100644 end-to-end-tests/test-cases/test_file create mode 100644 end-to-end-tests/tests.py diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..13959ff --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,28 @@ +--- +version: "2.1" +services: + openssh-server: + build: + context: ./end-to-end-tests + dockerfile: Dockerfile.ssh-server + container_name: openssh-server + hostname: openssh-server #optional + environment: + PUID: 1000 + PGID: 1000 + PUBLIC_KEY_FILE: /keys/test-key.pub + SUDO_ACCESS: false + PASSWORD_ACCESS: false + USER_NAME: test + volumes: + - ./end-to-end-tests/keys:/keys:ro + ports: + - 2222:2222 + sysrsync-client: + build: + context: ./end-to-end-tests + dockerfile: Dockerfile.sysrsync-client + volumes: + - ./:/app + command: python end-to-end-tests/tests.py + working_dir: /app diff --git a/end-to-end-tests/Dockerfile.ssh-server b/end-to-end-tests/Dockerfile.ssh-server new file mode 100644 index 0000000..a06a5a6 --- /dev/null +++ b/end-to-end-tests/Dockerfile.ssh-server @@ -0,0 +1,3 @@ +FROM linuxserver/openssh-server:version-8.8_p1-r1 + +RUN apk add --no-cache rsync diff --git a/end-to-end-tests/Dockerfile.sysrsync-client b/end-to-end-tests/Dockerfile.sysrsync-client new file mode 100644 index 0000000..c300146 --- /dev/null +++ b/end-to-end-tests/Dockerfile.sysrsync-client @@ -0,0 +1,3 @@ +FROM python:3.10-alpine3.16 + +RUN apk add --no-cache rsync openssh diff --git a/end-to-end-tests/keys/test-key b/end-to-end-tests/keys/test-key new file mode 100644 index 0000000..e8c60b0 --- /dev/null +++ b/end-to-end-tests/keys/test-key @@ -0,0 +1,38 @@ +-----BEGIN OPENSSH PRIVATE KEY----- +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 +-----END OPENSSH PRIVATE KEY----- diff --git a/end-to-end-tests/keys/test-key.pub b/end-to-end-tests/keys/test-key.pub new file mode 100644 index 0000000..db46f04 --- /dev/null +++ b/end-to-end-tests/keys/test-key.pub @@ -0,0 +1 @@ +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQC0nNJXgZA6Hd1snjFt6A4Ed6gOaZj564ISS1uHRtX7KML0Gf1RRnlGcuecu+F4V+wNeH3FPskna5rmhdjoiHcOse3/a1mLsXQkOUT4RdAOZp9omW9WzA0j7K4B1sFTySG24xe0alYYWIamXfICwbvnzpC9G+X5GIuCADM3yG8169H5oAsppgguO6mzHxbXMQKACA2I9uIbURTBCWxrBmDR6z5f3YpsM1MyrFg8o8K/Wzob2MqPCgwdseaPmSCmjFniv610uMccB1mrXFgOSOlvHk7szyW9N7tlJnNVvYG9fU7PN+0ztQnOo+Jvd/Z48NCHKIm3GLuKnomniN0p+rm5jTl7s7OZWXJwa+Y9X2Q8ID8XA7KroxYED/4QUgYxMmsSCai695TFl/gU6NKcpwF5ReTPqvQImxOg6P7pyRcVBUJPq+knPFkaG/jtDX7AmmOVGd8SmHKj4Fhx9P5HeW0cQYpfoEpFSRQTkP/xSiSUsAM+f5wMUhfm4JG/vwg+55c= gchamon@github.com diff --git a/end-to-end-tests/test-cases/file with spaces b/end-to-end-tests/test-cases/file with spaces new file mode 100644 index 0000000..e69de29 diff --git a/end-to-end-tests/test-cases/test_file b/end-to-end-tests/test-cases/test_file new file mode 100644 index 0000000..9daeafb --- /dev/null +++ b/end-to-end-tests/test-cases/test_file @@ -0,0 +1 @@ +test diff --git a/end-to-end-tests/tests.py b/end-to-end-tests/tests.py new file mode 100644 index 0000000..7f50b07 --- /dev/null +++ b/end-to-end-tests/tests.py @@ -0,0 +1,32 @@ +import inspect +import os +import sys + +currentdir = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe()))) +parentdir = os.path.dirname(currentdir) +sys.path.insert(0, parentdir) + +import unittest +import sysrsync + + +class TestE2E(unittest.TestCase): + def test_send_file(self): + sysrsync.run(source="end-to-end-tests/test-cases/test_file", + destination="/tmp/target_test_file", + destination_ssh="test@openssh-server", + private_key="end-to-end-tests/keys/test-key", + rsh_port=2222, + strict_host_key_checking=False) + + def test_send_file_with_spaces(self): + sysrsync.run(source="end-to-end-tests/test-cases/file with spaces", + destination="/tmp/target_test_file", + destination_ssh="test@openssh-server", + private_key="end-to-end-tests/keys/test-key", + rsh_port=2222, + strict_host_key_checking=False) + + +if __name__ == '__main__': + unittest.main() From a45379f2884dc02d0d7fe9b85c180e3dcc9f4d28 Mon Sep 17 00:00:00 2001 From: Gabriel Chamon Araujo Date: Sun, 29 May 2022 18:49:11 -0300 Subject: [PATCH 09/13] adding helper script for end-to-end-tests --- end-to-end-tests/run-tests.sh | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100755 end-to-end-tests/run-tests.sh diff --git a/end-to-end-tests/run-tests.sh b/end-to-end-tests/run-tests.sh new file mode 100755 index 0000000..db3f54a --- /dev/null +++ b/end-to-end-tests/run-tests.sh @@ -0,0 +1,14 @@ +#!/usr/bin/env bash +SCRIPT_DIR="$( cd -- "$( dirname -- "${BASH_SOURCE[0]:-$0}"; )" &> /dev/null && pwd 2> /dev/null; )"; + +cd $SCRIPT_DIR/.. + +set -euo pipefail + +echo starting ssh server... +docker-compose up --detach openssh-server + +echo running tests +docker-compose up --exit-code-from sysrsync-client sysrsync-client + +docker-compose down From 60b4e0c23b1b17413a6b71c74bfa014b12da3dcc Mon Sep 17 00:00:00 2001 From: Gabriel Chamon Araujo Date: Sun, 29 May 2022 18:50:13 -0300 Subject: [PATCH 10/13] adding entry in README --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 7c0290e..c08b991 100644 --- a/README.md +++ b/README.md @@ -137,4 +137,5 @@ sysrsync.run(source='/home/user/files', - Make changes - Lint with `poetry run pylint ./sysrsync` - Test with `poetry run python -m nose tests/*` +- Run end-to-end tests with `bash end-to-end-tests/run-tests.sh` - Submit changes with a pull request From f505c69f9f5bb5f2cebb2f78621a4f5cca789461 Mon Sep 17 00:00:00 2001 From: Gabriel Chamon Araujo Date: Sun, 29 May 2022 15:41:59 -0300 Subject: [PATCH 11/13] minor refactor rsh comamnd check for readability --- sysrsync/command_maker.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sysrsync/command_maker.py b/sysrsync/command_maker.py index f1a6f86..89fb468 100644 --- a/sysrsync/command_maker.py +++ b/sysrsync/command_maker.py @@ -34,7 +34,7 @@ def get_rsync_command(source: str, else []) rsh = (get_rsh_command(private_key, rsh_port, strict_host_key_checking) - if private_key is not None or rsh_port is not None or strict_host_key_checking is not None + if any((private_key, rsh_port, strict_host_key_checking)) else []) if options is None: From 78028d6777be58db369f0bdb7f09d336b618aff0 Mon Sep 17 00:00:00 2001 From: Gabriel Chamon Araujo Date: Sun, 29 May 2022 19:05:56 -0300 Subject: [PATCH 12/13] removing shell and making necessary changes to RSH command maker --- sysrsync/helpers/rsync.py | 2 +- sysrsync/runner.py | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/sysrsync/helpers/rsync.py b/sysrsync/helpers/rsync.py index 90d454e..a194737 100644 --- a/sysrsync/helpers/rsync.py +++ b/sysrsync/helpers/rsync.py @@ -32,4 +32,4 @@ def get_rsh_command(private_key: Optional[str] = None, port: Optional[int] = Non string_args = " ".join(args) - return [f"--rsh='ssh {string_args}'"] + return ["--rsh", f"ssh {string_args}"] diff --git a/sysrsync/runner.py b/sysrsync/runner.py index 1c44e07..c0b40f4 100644 --- a/sysrsync/runner.py +++ b/sysrsync/runner.py @@ -7,12 +7,13 @@ def run(cwd=os.getcwd(), strict=True, verbose=False, **kwargs): rsync_command = get_rsync_command(**kwargs) + rsync_string = ' '.join(rsync_command) if verbose is True: print(f'[sysrsync runner] running command on "{cwd}":') print(rsync_string) - process = subprocess.run(rsync_string, cwd=cwd, shell=True, stdout=subprocess.PIPE) + process = subprocess.run(rsync_command, cwd=cwd, shell=False) if strict is True: code = process.returncode From 122c68b8d502d38e6487cb49c3b4d16fc2d88e0c Mon Sep 17 00:00:00 2001 From: Gabriel Chamon Araujo Date: Sun, 29 May 2022 19:30:56 -0300 Subject: [PATCH 13/13] porting nose to unittests and fixing broken tests --- tests/directories_helper.py | 147 ++++++++++++++++++------------------ tests/iterators_helper.py | 36 +++++---- tests/package.py | 125 ++++++++++++++++++++++++++++++ tests/sysrsync.py | 125 ------------------------------ 4 files changed, 217 insertions(+), 216 deletions(-) create mode 100644 tests/package.py delete mode 100644 tests/sysrsync.py diff --git a/tests/directories_helper.py b/tests/directories_helper.py index af3402d..dc64c08 100644 --- a/tests/directories_helper.py +++ b/tests/directories_helper.py @@ -1,99 +1,96 @@ -from sysrsync.helpers import directories -from nose.tools import eq_ - - -def test_strip_trailing_slash(): - """test strip trailing slash""" - test_dir = '/a/' - expect = '/a' - result = directories.strip_trailing_slash(test_dir) - - eq_(expect, result) - - -def test_skip_strip_trailing_slash(): - """test skip strip trailing slash when not necessary""" - test_dir = '/a' - result = directories.strip_trailing_slash(test_dir) - - eq_(result, test_dir) +import unittest +from sysrsync.helpers import directories -def test_add_trailing_slash(): - """test add trailing slash""" - test_dir = '/a' - expect = '/a/' - result = directories.add_trailing_slash(test_dir) - eq_(expect, result) +class TestDirectoriesHelper(unittest.TestCase): + def test_strip_trailing_slash(self): + """test strip trailing slash""" + test_dir = '/a/' + expect = '/a' + result = directories.strip_trailing_slash(test_dir) + self.assertEqual(expect, result) -def test_skip_add_trailing_slash(): - """test skip add trailing slash when not necessary""" - test_dir = '/a/' - result = directories.add_trailing_slash(test_dir) + def test_skip_strip_trailing_slash(self): + """test skip strip trailing slash when not necessary""" + test_dir = '/a' + result = directories.strip_trailing_slash(test_dir) - eq_(result, test_dir) + self.assertEqual(result, test_dir) + def test_add_trailing_slash(self): + """test add trailing slash""" + test_dir = '/a' + expect = '/a/' + result = directories.add_trailing_slash(test_dir) -def test_sanitize_trailing_slash(): - """test sanitize trailing slash when syncing source contents""" - source, target = '/a', '/b/' - expect_source, expect_target = '/a/', '/b' - result_source, result_target = directories.sanitize_trailing_slash( - source, target) + self.assertEqual(expect, result) - eq_(expect_source, result_source) - eq_(expect_target, result_target) + def test_skip_add_trailing_slash(self): + """test skip add trailing slash when not necessary""" + test_dir = '/a/' + result = directories.add_trailing_slash(test_dir) + self.assertEqual(result, test_dir) -def test_sanitize_trailing_slash_no_action_needed(): - """test sanitize trailing slash when syncing source contents when already sanitized""" - source, target = '/a/', '/b' - expect_source, expect_target = '/a/', '/b' - result_source, result_target = directories.sanitize_trailing_slash( - source, target) + def test_sanitize_trailing_slash(self): + """test sanitize trailing slash when syncing source contents""" + source, target = '/a', '/b/' + expect_source, expect_target = '/a/', '/b' + result_source, result_target = directories.sanitize_trailing_slash( + source, target) - eq_(expect_source, result_source) - eq_(expect_target, result_target) + self.assertEqual(expect_source, result_source) + self.assertEqual(expect_target, result_target) + def test_sanitize_trailing_slash_no_action_needed(self): + """test sanitize trailing slash when syncing source contents when already sanitized""" + source, target = '/a/', '/b' + expect_source, expect_target = '/a/', '/b' + result_source, result_target = directories.sanitize_trailing_slash( + source, target) -def test_sanitize_trailing_slash_whole_source(): - """test sanitize trailing slash when syncing whole source""" - source, target = '/a/', '/b/' - expect_source, expect_target = '/a', '/b' - result_source, result_target = directories.sanitize_trailing_slash( - source, target, sync_sourcedir_contents=False) + self.assertEqual(expect_source, result_source) + self.assertEqual(expect_target, result_target) - eq_(expect_source, result_source) - eq_(expect_target, result_target) + def test_sanitize_trailing_slash_whole_source(self): + """test sanitize trailing slash when syncing whole source""" + source, target = '/a/', '/b/' + expect_source, expect_target = '/a', '/b' + result_source, result_target = directories.sanitize_trailing_slash( + source, target, sync_sourcedir_contents=False) + self.assertEqual(expect_source, result_source) + self.assertEqual(expect_target, result_target) -def test_sanitize_trailing_slash_whole_source_no_action_needed(): - """test sanitize trailing slash when syncing whole source when already sanitized""" - source, target = '/a', '/b/' - expect_source, expect_target = '/a', '/b' - result_source, result_target = directories.sanitize_trailing_slash( - source, target, sync_sourcedir_contents=False) + def test_sanitize_trailing_slash_whole_source_no_action_needed(self): + """test sanitize trailing slash when syncing whole source when already sanitized""" + source, target = '/a', '/b/' + expect_source, expect_target = '/a', '/b' + result_source, result_target = directories.sanitize_trailing_slash( + source, target, sync_sourcedir_contents=False) - eq_(expect_source, result_source) - eq_(expect_target, result_target) + self.assertEqual(expect_source, result_source) + self.assertEqual(expect_target, result_target) + def test_dir_with_ssh(self): + """should compose string with ssh for rsync connection""" + directory = '/a' + ssh = 'host' + expect = 'host:/a' + result = directories.get_directory_with_ssh(directory, ssh) -def test_dir_with_ssh(): - """should compose string with ssh for rsync connection""" - directory = '/a' - ssh = 'host' - expect = 'host:/a' - result = directories.get_directory_with_ssh(directory, ssh) + self.assertEqual(result, expect) - eq_(result, expect) + def test_dir_without_ssh(self): + """should return directory when ssh is None""" + directory = '/a' + ssh = None + result = directories.get_directory_with_ssh(directory, ssh) + self.assertEqual(result, directory) -def test_dir_without_ssh(): - """should return directory when ssh is None""" - directory = '/a' - ssh = None - result = directories.get_directory_with_ssh(directory, ssh) - eq_(result, directory) +if __name__ == '__main__': + unittest.main() diff --git a/tests/iterators_helper.py b/tests/iterators_helper.py index ae5f33f..e50a20a 100644 --- a/tests/iterators_helper.py +++ b/tests/iterators_helper.py @@ -1,26 +1,30 @@ +import unittest + from sysrsync.helpers import iterators -from nose.tools import eq_ -def test_list_flatten(): - list_input = [1, [2, 3], [4]] - expect = [1, 2, 3, 4] - result = iterators.flatten(list_input) +class TestIteratorsHelper(unittest.TestCase): + def test_list_flatten(self): + list_input = [1, [2, 3], [4]] + expect = [1, 2, 3, 4] + result = iterators.flatten(list_input) - eq_(expect, result) + self.assertEqual(expect, result) + def test_tuple_flatten(self): + tuple_input = (1, [2, 3], [4]) + expect = [1, 2, 3, 4] + result = iterators.flatten(tuple_input) -def test_tuple_flatten(): - tuple_input = (1, [2, 3], [4]) - expect = [1, 2, 3, 4] - result = iterators.flatten(tuple_input) + self.assertEqual(expect, result) - eq_(expect, result) + def test_tuples_and_lists_list_flatten(self): + tuple_input = (1, (2, 3), [4]) + expect = [1, 2, 3, 4] + result = iterators.flatten(tuple_input) + self.assertEqual(expect, result) -def test_tuples_and_lists_list_flatten(): - tuple_input = (1, (2, 3), [4]) - expect = [1, 2, 3, 4] - result = iterators.flatten(tuple_input) - eq_(expect, result) +if __name__ == '__main__': + unittest.main() diff --git a/tests/package.py b/tests/package.py new file mode 100644 index 0000000..0f6ce8c --- /dev/null +++ b/tests/package.py @@ -0,0 +1,125 @@ +import unittest +from tempfile import NamedTemporaryFile + +from sysrsync import get_exclusions +from sysrsync.command_maker import get_rsync_command +from sysrsync.exceptions import RemotesError, PrivateKeyError + + +class TestPackage(unittest.TestCase): + def test_get_exclusions(self): + """should map list of exclusions to a list with each element following a --exclude statement""" + exclusions = ['a', 'b'] + expect = ['--exclude', 'a', '--exclude', 'b'] + result = get_exclusions(exclusions) + + self.assertEqual(expect, result) + + def test_get_exclusions_already_in_rsync_format(self): + """should ignore --exclude in exclusions""" + exclusions = ['--exclude', 'a', '--exclude', 'b'] + expect = ['--exclude', 'a', '--exclude', 'b'] + result = get_exclusions(exclusions) + + self.assertEqual(expect, result) + + def test_simple_rsync_command(self): + source = '/a' + target = '/b' + expect = 'rsync /a/ /b'.split() + result = get_rsync_command(source, target) + + self.assertEqual(expect, result) + + def test_rsync_options(self): + source = '/a' + target = '/b' + options = ['-a', '--verbose'] + expect = 'rsync -a --verbose /a/ /b'.split() + result = get_rsync_command(source, target, options=options) + + self.assertEqual(expect, result) + + def test_simple_rsync_command_content_false(self): + source = '/a' + target = '/b' + expect = 'rsync /a /b'.split() + result = get_rsync_command(source, target, sync_source_contents=False) + + self.assertEqual(expect, result) + + def test_rsync_exclusions(self): + source = '/a' + target = '/b' + exclusions = ['file1', 'file2'] + expect = 'rsync /a/ /b --exclude file1 --exclude file2'.split() + result = get_rsync_command(source, target, exclusions=exclusions) + + self.assertEqual(expect, result) + + def test_rsync_exclusions_source_ssh(self): + source = '/a' + source_ssh = 'host1' + target = '/b' + exclusions = ['file1', 'file2'] + expect = 'rsync host1:/a/ /b --exclude file1 --exclude file2'.split() + result = get_rsync_command(source, target, exclusions=exclusions, source_ssh=source_ssh) + + self.assertEqual(expect, result) + + def test_rsync_exclusions_target_ssh(self): + source = '/a' + target_ssh = 'host1' + target = '/b' + exclusions = ['file1', 'file2'] + expect = 'rsync /a/ host1:/b --exclude file1 --exclude file2'.split() + result = get_rsync_command(source, target, exclusions=exclusions, destination_ssh=target_ssh) + + self.assertEqual(expect, result) + + def test_rsync_throws_both_remotes(self): + """raises RemotesError when both source and destination are remotes""" + source_ssh = 'host1' + source = '/a' + target_ssh = 'host2' + target = '/b' + with self.assertRaises(RemotesError): + get_rsync_command(source, target, source_ssh=source_ssh, destination_ssh=target_ssh) + + def test_rsync_private_key(self): + """test if correctly creates rsh option when passing a private key""" + with NamedTemporaryFile() as temp_file: + source_dir = '/home/user/files/' + target_dir = '/home/server/files' + destination_ssh = 'myserver' + temp_file_name = temp_file.name + port = 2222 + strict_host_key_checking = False + expect = ['rsync', + "--rsh", + f'ssh -i {temp_file_name} -p {port} -o "StrictHostKeyChecking no"', + source_dir, + f'{destination_ssh}:{target_dir}'] + actual = get_rsync_command(source=source_dir, + destination=target_dir, + destination_ssh=destination_ssh, + private_key=temp_file_name, + rsh_port=port, + strict_host_key_checking=strict_host_key_checking) + self.assertEqual(expect, actual) + + def test_rsync_private_key_missing(self): + """test if get_rsync_command raises PrivateKeyError when key missing""" + source_dir = '/home/user/files/' + target_dir = '/home/server/files' + destination_ssh = 'myserver' + private_key = 'this_file_does_not_exist' + with self.assertRaises(PrivateKeyError): + get_rsync_command(source=source_dir, + destination=target_dir, + destination_ssh=destination_ssh, + private_key=private_key) + + +if __name__ == '__main__': + unittest.main() diff --git a/tests/sysrsync.py b/tests/sysrsync.py deleted file mode 100644 index 914eff8..0000000 --- a/tests/sysrsync.py +++ /dev/null @@ -1,125 +0,0 @@ -from tempfile import TemporaryFile - -from nose.tools import eq_, raises - -from sysrsync import get_exclusions -from sysrsync.command_maker import get_rsync_command -from sysrsync.exceptions import RemotesError, PrivateKeyError - - -def test_get_exclusions(): - """should map list of exclusions to a list with each element following a --exclude statement""" - exclusions = ['a', 'b'] - expect = ['--exclude', 'a', '--exclude', 'b'] - result = get_exclusions(exclusions) - - eq_(expect, result) - - -def test_get_exclusions_already_in_rsync_format(): - """should ignore --exclude in exclusions""" - exclusions = ['--exclude', 'a', '--exclude', 'b'] - expect = ['--exclude', 'a', '--exclude', 'b'] - result = get_exclusions(exclusions) - - eq_(expect, result) - - -def test_simple_rsync_command(): - source = '/a' - target = '/b' - expect = 'rsync /a/ /b'.split() - result = get_rsync_command(source, target) - - eq_(expect, result) - - -def test_rsync_options(): - source = '/a' - target = '/b' - options = ['-a', '--verbose'] - expect = 'rsync -a --verbose /a/ /b'.split() - result = get_rsync_command(source, target, options=options) - - eq_(expect, result) - - -def test_simple_rsync_command_content_false(): - source = '/a' - target = '/b' - expect = 'rsync /a /b'.split() - result = get_rsync_command(source, target, sync_source_contents=False) - - eq_(expect, result) - - -def test_rsync_exclusions(): - source = '/a' - target = '/b' - exclusions = ['file1', 'file2'] - expect = 'rsync /a/ /b --exclude file1 --exclude file2'.split() - result = get_rsync_command(source, target, exclusions=exclusions) - - eq_(expect, result) - - -def test_rsync_exclusions_source_ssh(): - source = '/a' - source_ssh = 'host1' - target = '/b' - exclusions = ['file1', 'file2'] - expect = 'rsync host1:/a/ /b --exclude file1 --exclude file2'.split() - result = get_rsync_command(source, target, exclusions=exclusions, source_ssh=source_ssh) - - eq_(expect, result) - - -def test_rsync_exclusions_target_ssh(): - source = '/a' - target_ssh = 'host1' - target = '/b' - exclusions = ['file1', 'file2'] - expect = 'rsync /a/ host1:/b --exclude file1 --exclude file2'.split() - result = get_rsync_command(source, target, exclusions=exclusions, destination_ssh=target_ssh) - - eq_(expect, result) - - -@raises(RemotesError) -def test_rsync_throws_both_remotes(): - """raises RemotesError when both source and destination are remotes""" - source_ssh = 'host1' - source = '/a' - target_ssh = 'host2' - target = '/b' - get_rsync_command(source, target, source_ssh=source_ssh, destination_ssh=target_ssh) - - -def test_rsync_private_key(): - """test if correctly creates rsh option when passing a private key""" - with TemporaryFile() as temp_file: - source_dir = '/home/user/files/' - target_dir = '/home/server/files' - destination_ssh = 'myserver' - port = 2222 - strict_host_key_checking = False - expect = ['rsync', f"--rsh='ssh -i {temp_file} -p {port} -o \"StrictHostKeyChecking no\"'", source_dir, f'{destination_ssh}:{target_dir}'] - actual = get_rsync_command(source=source_dir, - destination=target_dir, - destination_ssh=destination_ssh, - private_key=temp_file, - rsh_port=port, - strict_host_key_checking=strict_host_key_checking) - eq_(expect, actual) - -@raises(PrivateKeyError) -def test_rsync_private_key(): - """test if get_rsync_command raises PrivateKeyError when key missing""" - source_dir = '/home/user/files/' - target_dir = '/home/server/files' - destination_ssh = 'myserver' - private_key = 'this_file_does_not_exist' - actual = get_rsync_command(source=source_dir, - destination=target_dir, - destination_ssh=destination_ssh, - private_key=private_key)