├── .coveragerc ├── .gitignore ├── .hooks4git.ini ├── .markdownlint.json ├── .travis.yml ├── LICENSE ├── MANIFEST.in ├── Pipfile ├── Pipfile.lock ├── README.md ├── docker_runme_first.sh ├── docker_runme_inside.sh ├── hooks4git ├── .hooks4git.ini ├── __init__.py ├── app.py ├── console.py ├── git │ └── hooks │ │ ├── applypatch-msg │ │ ├── commit-msg │ │ ├── fsmonitor-watchman │ │ ├── post-applypatch │ │ ├── post-checkout │ │ ├── post-commit │ │ ├── post-merge │ │ ├── post-receive │ │ ├── post-rewrite │ │ ├── post-update │ │ ├── pre-applypatch │ │ ├── pre-auto-gc │ │ ├── pre-commit │ │ ├── pre-push │ │ ├── pre-rebase │ │ ├── pre-receive │ │ ├── prepare-commit-msg │ │ ├── push-to-checkout │ │ ├── sendemail-validate │ │ └── update ├── h4g │ ├── __init__.py │ ├── check_branch_name │ ├── check_branch_name.sh │ └── get_staged_files ├── hook.py ├── hooks4git.sh └── tools.py ├── package-lock.json ├── package.json ├── requirements-dev.txt ├── requirements.txt ├── setup.py └── tests ├── test_console_divider.py ├── test_console_say.py ├── test_hook_get_hooks_path.py ├── test_hook_hook_it.py ├── test_hook_report.py ├── test_tools_copy_file.py ├── test_tools_get_platform.py ├── test_tools_os_call.py ├── test_tools_usersite.py └── tests.py /.coveragerc: -------------------------------------------------------------------------------- 1 | [run] 2 | source = hooks4git 3 | omit = *docs*, *virtualenv*, hooks4git/setup.py -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | MANIFEST 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .coverage 42 | .coverage.* 43 | .cache 44 | nosetests.xml 45 | coverage.xml 46 | *.cover 47 | .hypothesis/ 48 | .pytest_cache/ 49 | 50 | # Translations 51 | *.mo 52 | *.pot 53 | 54 | # Django stuff: 55 | *.log 56 | local_settings.py 57 | db.sqlite3 58 | 59 | # Flask stuff: 60 | instance/ 61 | .webassets-cache 62 | 63 | # Scrapy stuff: 64 | .scrapy 65 | 66 | # Sphinx documentation 67 | docs/_build/ 68 | 69 | # PyBuilder 70 | target/ 71 | 72 | # Jupyter Notebook 73 | .ipynb_checkpoints 74 | 75 | # pyenv 76 | .python-version 77 | 78 | # celery beat schedule file 79 | celerybeat-schedule 80 | 81 | # SageMath parsed files 82 | *.sage.py 83 | 84 | # Environments 85 | .env 86 | .venv 87 | env/ 88 | venv/ 89 | ENV/ 90 | env.bak/ 91 | venv.bak/ 92 | 93 | # Spyder project settings 94 | .spyderproject 95 | .spyproject 96 | 97 | # Rope project settings 98 | .ropeproject 99 | 100 | # mkdocs documentation 101 | /site 102 | 103 | # mypy 104 | .mypy_cache/ 105 | 106 | .vscode 107 | node_modules/ 108 | -------------------------------------------------------------------------------- /.hooks4git.ini: -------------------------------------------------------------------------------- 1 | [vars] 2 | STAGED_FILES_IF_ANY = h4g/get_staged_files --format csv --prefix "--filename=" --suffix "" 3 | 4 | [scripts] 5 | flake8 = flake8 --max-line-length=119 --exclude .git,build,dist ${STAGED_FILES_IF_ANY} 6 | pytest = python -m pytest --cov=hooks4git tests/ 7 | travis_linter = ./node_modules/.bin/travis-lint .travis.yml 8 | md_linter = ./node_modules/.bin/markdownlint README.md 9 | bandit = bandit -r hooks4git 10 | checkbranch = h4g/check_branch_name.sh "^(feature|bugfix|hotfix|fix)\/.+" 11 | black = black . --line-length=119 --check -t py36 12 | make_reqs = pipenv run pipenv_to_requirements -f 13 | pipenv = pipenv install --dev 14 | 15 | [hooks.pre-commit.scripts] 16 | checkbranch = checkbranch 17 | check1 = flake8 18 | check2 = black 19 | 20 | [hooks.pre-push.scripts] 21 | doc_linter = md_linter 22 | tests = pytest 23 | ; ci_linter = travis_linter 24 | security = bandit 25 | 26 | [hooks.post-merge.scripts] 27 | update_deps = pipenv 28 | make_reqs = make_reqs 29 | 30 | [hooks.ci.scripts] 31 | update_deps = pipenv 32 | doc_linter = md_linter 33 | check1 = flake8 34 | security = bandit 35 | tests = pytest 36 | make_reqs = make_reqs 37 | -------------------------------------------------------------------------------- /.markdownlint.json: -------------------------------------------------------------------------------- 1 | { 2 | "default": true, 3 | "MD013": false 4 | } -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | os: 3 | - linux 4 | # - osx 5 | python: 6 | # - '2.7' due to configparser problem 7 | - '3.5' 8 | - '3.6' 9 | # - '3.7' 10 | cache: 11 | - pip 12 | - npm 13 | addons: 14 | apt: 15 | update: true 16 | packages: 17 | - nodejs 18 | - npm 19 | homebrew: 20 | update: true 21 | packages: 22 | - node 23 | before_install: 24 | - pip install pipenv 25 | install: 26 | - sed -i 's/black = "\*"//g' Pipfile 27 | - npm install 28 | before_script: 29 | - pip install . 30 | script: 31 | - hooks4git -t ci 32 | after_success: 33 | - coveralls 34 | deploy: 35 | - provider: pypi 36 | user: marco.lovato 37 | password: 38 | secure: E1ayTfE3K+HL8qk6aMmh4qNItmn1DtDDBBAXrtUica/o9dtToSFePFqjDR3nJQAe58pjky/TWzCX494iAZwOfFKVBABj0LUc97fefQyHhnBkIoPYt0saZPEKV6vi08qqaYlSFESwxQ70n/wXOvsJjWmafP0sLledB3AtQ6x790CspioFAW+USl/CZC8SLtK5zJHjLWZeoKPC4S15YX6l57T5LaC+qGeUPAaEhzG/sjJKvunw/ZdM9d8l6YchXfvPDPH2siaYJN3jU49tTjVWy6NoIax2LjutRf9WRvmsfQFgknIprWel8GZ8GvCI3yv8mGIYyuch+6sT3EcRg7+wX6bbd4p8bFAgAk0dDKOdDFi6j8L95+SHRKa2d0OLhm0apQX+lRmq3QizH2mFz3mMpvRYsw9oNh/nYTkAOUlpk75k2Uwoqh5Aqc1ljivBJIbkt4zbsKpUiLEmq+hEoVNZlhjGnfrJiHM34UIMCJuPl+vbp3LoW6xtoICaWSW0MGcsiy7+dOzYRzhVo6Y2a5HKoM5/buz1RZlR3UsJ3K7fmYL0JbyCXJbCP0Nhm7H70G5sS01rmSFmuN2/jkcf91DzyHFZ6SyKxCYxBmMyHy4MMG72DeuNLYBf7fRXiUdEZD/oM+PAIH/PwSUBLhpWMntIbAaDgbCuc3w1uvNaeECf3BE= 39 | on: 40 | branch: master 41 | python: '3.6' 42 | - provider: pypi 43 | server: https://test.pypi.org/legacy/ 44 | user: marco.lovato 45 | password: 46 | secure: E1ayTfE3K+HL8qk6aMmh4qNItmn1DtDDBBAXrtUica/o9dtToSFePFqjDR3nJQAe58pjky/TWzCX494iAZwOfFKVBABj0LUc97fefQyHhnBkIoPYt0saZPEKV6vi08qqaYlSFESwxQ70n/wXOvsJjWmafP0sLledB3AtQ6x790CspioFAW+USl/CZC8SLtK5zJHjLWZeoKPC4S15YX6l57T5LaC+qGeUPAaEhzG/sjJKvunw/ZdM9d8l6YchXfvPDPH2siaYJN3jU49tTjVWy6NoIax2LjutRf9WRvmsfQFgknIprWel8GZ8GvCI3yv8mGIYyuch+6sT3EcRg7+wX6bbd4p8bFAgAk0dDKOdDFi6j8L95+SHRKa2d0OLhm0apQX+lRmq3QizH2mFz3mMpvRYsw9oNh/nYTkAOUlpk75k2Uwoqh5Aqc1ljivBJIbkt4zbsKpUiLEmq+hEoVNZlhjGnfrJiHM34UIMCJuPl+vbp3LoW6xtoICaWSW0MGcsiy7+dOzYRzhVo6Y2a5HKoM5/buz1RZlR3UsJ3K7fmYL0JbyCXJbCP0Nhm7H70G5sS01rmSFmuN2/jkcf91DzyHFZ6SyKxCYxBmMyHy4MMG72DeuNLYBf7fRXiUdEZD/oM+PAIH/PwSUBLhpWMntIbAaDgbCuc3w1uvNaeECf3BE= 47 | on: 48 | branch: develop 49 | python: '3.6' 50 | notifications: 51 | email: false 52 | slack: 53 | on_success: always 54 | on_failure: always 55 | rooms: 56 | secure: LwmtPkwyfoITVgDsbejKchM2vwsPq70zVYpfyqVbFUCys8qFII16HEmz38PDV8SujggbUsmJd1Oug0vW6K0Ie/VSXrZoDleJBTVxsgACNjiFXYca3ZDxpKw0tkfCF9MMM2Wjns5JfW28sqktHddpsC+VWgEG2Fv169/ouOAxiiCiUiTxM9Pmsg9RT89CaHjlrjlHvKg9OgGai3rEvp5rAyvJNvr4yfMyXBozvamNA8u1i7DniB9Jueq06OdBM6JjhiNC0lLoDHvcuvjhhm5P3eh4lzuUxbktyyujcBb6UGXH0Pd0mnyWRlHILseCL/a26dxNtgE3SNcbZHR/aSNhXu7vlVPx9Tf+s/XpHhWTJeofka/+iAGDXkOwlw+ycmrZZt5gDtq0vYS3f+lYDFcCxst6ToJfB9lXV7hnpg2WDvjo7aHx4dvxI/18CGwfOK4JNQxpJfsAMIGjwPPes4FmewSIsDSBygrn39CAhKryOifdR9bqqihjEFqGfSUe3JjOV95Wb13SwTEO17qoZQSpqjt6VwH7VCeAKaHfcWBDEQzRJxLi/Az25iOxbloJdc0wl22t7o6zMr3EaAGjI8HaE9KTIiBiBwU/4HjxjnuKFSH6c6LdHa5BHtfhgqSpNnXnHmdCschBPD5qEwrjPdQ73g/dkzel1RmNIwwUzCmpksw= 57 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Marco Lovato 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include LICENSE 2 | include requirements.txt 3 | include hooks4git/build.info 4 | include hooks4git/.hooks4git.ini 5 | include LICENSE 6 | include README.md 7 | 8 | recursive-include hooks4git/git * 9 | recursive-include hooks4git/h4g * 10 | 11 | recursive-exclude tests * 12 | recursive-exclude * __pycache__ 13 | recursive-exclude * *.py[co] 14 | -------------------------------------------------------------------------------- /Pipfile: -------------------------------------------------------------------------------- 1 | [[source]] 2 | name = "pypi" 3 | url = "https://pypi.org/simple" 4 | verify_ssl = true 5 | 6 | [dev-packages] 7 | black = "*" 8 | coverage = "*" 9 | coveralls = "*" 10 | pytest = "*" 11 | flake8 = "*" 12 | mock = "*" 13 | bandit = "*" 14 | pytest-cov = "*" 15 | autopep8 = "*" 16 | pipenv-to-requirements = "*" 17 | 18 | [packages] 19 | colorama = "*" 20 | 21 | [requires] 22 | python_version = "3.6" 23 | 24 | [pipenv] 25 | allow_prereleases = true 26 | -------------------------------------------------------------------------------- /Pipfile.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "hash": { 4 | "sha256": "644c9fd8c8c7ca408fbfa6a41b7cf8748cd669e586927f47198f0044f14164b7" 5 | }, 6 | "pipfile-spec": 6, 7 | "requires": { 8 | "python_version": "3.6" 9 | }, 10 | "sources": [ 11 | { 12 | "name": "pypi", 13 | "url": "https://pypi.org/simple", 14 | "verify_ssl": true 15 | } 16 | ] 17 | }, 18 | "default": { 19 | "colorama": { 20 | "hashes": [ 21 | "sha256:05eed71e2e327246ad6b38c540c4a3117230b19679b875190486ddd2d721422d", 22 | "sha256:f8ac84de7840f5b9c4e3347b3c1eaa50f7e49c2b07596221daec5edaabbd7c48" 23 | ], 24 | "index": "pypi", 25 | "version": "==0.4.1" 26 | } 27 | }, 28 | "develop": { 29 | "appdirs": { 30 | "hashes": [ 31 | "sha256:9e5896d1372858f8dd3344faf4e5014d21849c756c8d5701f78f8a103b372d92", 32 | "sha256:d8b24664561d0d34ddfaec54636d502d7cea6e29c3eaf68f3df6180863e2166e" 33 | ], 34 | "version": "==1.4.3" 35 | }, 36 | "atomicwrites": { 37 | "hashes": [ 38 | "sha256:03472c30eb2c5d1ba9227e4c2ca66ab8287fbfbbda3888aa93dc2e28fc6811b4", 39 | "sha256:75a9445bac02d8d058d5e1fe689654ba5a6556a1dfd8ce6ec55a0ed79866cfa6" 40 | ], 41 | "version": "==1.3.0" 42 | }, 43 | "attrs": { 44 | "hashes": [ 45 | "sha256:69c0dbf2ed392de1cb5ec704444b08a5ef81680a61cb899dc08127123af36a79", 46 | "sha256:f0b870f674851ecbfbbbd364d6b5cbdff9dcedbc7f3f5e18a6891057f21fe399" 47 | ], 48 | "version": "==19.1.0" 49 | }, 50 | "autopep8": { 51 | "hashes": [ 52 | "sha256:4d8eec30cc81bc5617dbf1218201d770dc35629363547f17577c61683ccfb3ee" 53 | ], 54 | "index": "pypi", 55 | "version": "==1.4.4" 56 | }, 57 | "bandit": { 58 | "hashes": [ 59 | "sha256:d31a7b0819fe95d591106ba2d6c35568a513aba24db537ca71984781312a8e95", 60 | "sha256:e50fb4ed4ee8a98b8329385e48e606fded0999a2cb3e2acb6e7213c962ff0de1" 61 | ], 62 | "index": "pypi", 63 | "version": "==1.6.0" 64 | }, 65 | "black": { 66 | "hashes": [ 67 | "sha256:09a9dcb7c46ed496a9850b76e4e825d6049ecd38b611f1224857a79bd985a8cf", 68 | "sha256:68950ffd4d9169716bcb8719a56c07a2f4485354fec061cdd5910aa07369731c" 69 | ], 70 | "index": "pypi", 71 | "version": "==19.3b0" 72 | }, 73 | "certifi": { 74 | "hashes": [ 75 | "sha256:59b7658e26ca9c7339e00f8f4636cdfe59d34fa37b9b04f6f9e9926b3cece1a5", 76 | "sha256:b26104d6835d1f5e49452a26eb2ff87fe7090b89dfcaee5ea2212697e1e1d7ae" 77 | ], 78 | "version": "==2019.3.9" 79 | }, 80 | "chardet": { 81 | "hashes": [ 82 | "sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae", 83 | "sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691" 84 | ], 85 | "version": "==3.0.4" 86 | }, 87 | "click": { 88 | "hashes": [ 89 | "sha256:2335065e6395b9e67ca716de5f7526736bfa6ceead690adf616d925bdc622b13", 90 | "sha256:5b94b49521f6456670fdb30cd82a4eca9412788a93fa6dd6df72c94d5a8ff2d7" 91 | ], 92 | "version": "==7.0" 93 | }, 94 | "coverage": { 95 | "hashes": [ 96 | "sha256:0402b1822d513d0231589494bceddb067d20581f5083598c451b56c684b0e5d6", 97 | "sha256:0644e28e8aea9d9d563607ee8b7071b07dd57a4a3de11f8684cd33c51c0d1b93", 98 | "sha256:0874a283686803884ec0665018881130604956dbaa344f2539c46d82cbe29eda", 99 | "sha256:0988c3837df4bc371189bb3425d5232cf150055452034c232dda9cbe04f9c38e", 100 | "sha256:20bc3205b3100956bb72293fabb97f0ed972c81fed10b3251c90c70dcb0599ab", 101 | "sha256:2cc9142a3367e74eb6b19d58c53ebb1dfd7336b91cdcc91a6a2888bf8c7af984", 102 | "sha256:3ae9a0a59b058ce0761c3bd2c2d66ecb2ee2b8ac592620184370577f7a546fb3", 103 | "sha256:3b2e30b835df58cb973f478d09f3d82e90c98c8e5059acc245a8e4607e023801", 104 | "sha256:401e9b04894eb1498c639c6623ee78a646990ce5f095248e2440968aafd6e90e", 105 | "sha256:41ec5812d5decdaa72708be3018e7443e90def4b5a71294236a4df192cf9eab9", 106 | "sha256:475769b638a055e75b3d3219e054fe2a023c0b077ff15bff6c95aba7e93e6cac", 107 | "sha256:61424f4e2e82c4129a4ba71e10ebacb32a9ecd6f80de2cd05bdead6ba75ed736", 108 | "sha256:811969904d4dd0bee7d958898be8d9d75cef672d9b7e7db819dfeac3d20d2d0c", 109 | "sha256:86224bb99abfd672bf2f9fcecad5e8d7a3fa94f7f71513f2210460a0350307cd", 110 | "sha256:9a238a20a3af00665f8381f7e53e9c606f9bb652d2423f6b822f6cb790d887e8", 111 | "sha256:a23b3fbc14d4e6182ecebfd22f3729beef0636d151d94764a1c28330d185e4e5", 112 | "sha256:ac162b4ebe51b7a2b7f5e462c4402802633eb81e77c94f8a7c1ed8a556e72c75", 113 | "sha256:b6187378726c84365bf297b5dcdae8789b6a5823b200bea23797777e5a63be09", 114 | "sha256:bcd5723d905ed4a825f17410a53535f880b6d7548ae3d89078db7b1ceefcd853", 115 | "sha256:c48a4f9c5fb385269bb7fbaf9c1326a94863b65ec7f5c96b2ea56b252f01ad08", 116 | "sha256:cd40199d6f1c29c85b170d25589be9a97edff8ee7e62be180a2a137823896030", 117 | "sha256:d1bc331a7d069485ac1d8c25a0ea1f6aab6cb2a87146fb652222481c1bddc9ff", 118 | "sha256:d7e0cdc249aa0f94aa2e531b03999ddaf03a10b4fa090a894712d4c8066abd89", 119 | "sha256:e9ee8fcd8e067fcc5d7276d46e07e863102b70a52545ef4254df1ff0893ce75f", 120 | "sha256:eb313c23d983b7810504f42104e8dcd1c7ccdda8fbaab82aab92ab79fea19345", 121 | "sha256:f9cfd478654b509941b85ed70f870f5e3c74678f566bec12fd26545e5340ba47", 122 | "sha256:fae1fa144034d021a52cb9ea200eb8dedf91869c6df8202ad5d149b41ed91cc8" 123 | ], 124 | "index": "pypi", 125 | "version": "==5.0a5" 126 | }, 127 | "coveralls": { 128 | "hashes": [ 129 | "sha256:baa26648430d5c2225ab12d7e2067f75597a4b967034bba7e3d5ab7501d207a1", 130 | "sha256:ff9b7823b15070f26f654837bb02a201d006baaf2083e0514ffd3b34a3ffed81" 131 | ], 132 | "index": "pypi", 133 | "version": "==1.7.0" 134 | }, 135 | "docopt": { 136 | "hashes": [ 137 | "sha256:49b3a825280bd66b3aa83585ef59c4a8c82f2c8a522dbe754a8bc8d08c85c491" 138 | ], 139 | "version": "==0.6.2" 140 | }, 141 | "entrypoints": { 142 | "hashes": [ 143 | "sha256:589f874b313739ad35be6e0cd7efde2a4e9b6fea91edcc34e58ecbb8dbe56d19", 144 | "sha256:c70dd71abe5a8c85e55e12c19bd91ccfeec11a6e99044204511f9ed547d48451" 145 | ], 146 | "version": "==0.3" 147 | }, 148 | "flake8": { 149 | "hashes": [ 150 | "sha256:859996073f341f2670741b51ec1e67a01da142831aa1fdc6242dbf88dffbe661", 151 | "sha256:a796a115208f5c03b18f332f7c11729812c8c3ded6c46319c59b53efd3819da8" 152 | ], 153 | "index": "pypi", 154 | "version": "==3.7.7" 155 | }, 156 | "gitdb2": { 157 | "hashes": [ 158 | "sha256:83361131a1836661a155172932a13c08bda2db3674e4caa32368aa6eb02f38c2", 159 | "sha256:e3a0141c5f2a3f635c7209d56c496ebe1ad35da82fe4d3ec4aaa36278d70648a" 160 | ], 161 | "version": "==2.0.5" 162 | }, 163 | "gitpython": { 164 | "hashes": [ 165 | "sha256:563221e5a44369c6b79172f455584c9ebbb122a13368cc82cb4b5addff788f82", 166 | "sha256:8237dc5bfd6f1366abeee5624111b9d6879393d84745a507de0fda86043b65a8" 167 | ], 168 | "version": "==2.1.11" 169 | }, 170 | "idna": { 171 | "hashes": [ 172 | "sha256:c357b3f628cf53ae2c4c05627ecc484553142ca23264e593d327bcde5e9c3407", 173 | "sha256:ea8b7f6188e6fa117537c3df7da9fc686d485087abf6ac197f9c46432f7e4a3c" 174 | ], 175 | "version": "==2.8" 176 | }, 177 | "mccabe": { 178 | "hashes": [ 179 | "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42", 180 | "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f" 181 | ], 182 | "version": "==0.6.1" 183 | }, 184 | "mock": { 185 | "hashes": [ 186 | "sha256:83657d894c90d5681d62155c82bda9c1187827525880eda8ff5df4ec813437c3", 187 | "sha256:d157e52d4e5b938c550f39eb2fd15610db062441a9c2747d3dbfa9298211d0f8" 188 | ], 189 | "index": "pypi", 190 | "version": "==3.0.5" 191 | }, 192 | "more-itertools": { 193 | "hashes": [ 194 | "sha256:2112d2ca570bb7c3e53ea1a35cd5df42bb0fd10c45f0fb97178679c3c03d64c7", 195 | "sha256:c3e4748ba1aad8dba30a4886b0b1a2004f9a863837b8654e7059eebf727afa5a" 196 | ], 197 | "markers": "python_version > '2.7'", 198 | "version": "==7.0.0" 199 | }, 200 | "pbr": { 201 | "hashes": [ 202 | "sha256:6901995b9b686cb90cceba67a0f6d4d14ae003cd59bc12beb61549bdfbe3bc89", 203 | "sha256:d950c64aeea5456bbd147468382a5bb77fe692c13c9f00f0219814ce5b642755" 204 | ], 205 | "version": "==5.2.0" 206 | }, 207 | "pipenv": { 208 | "hashes": [ 209 | "sha256:56ad5f5cb48f1e58878e14525a6e3129d4306049cb76d2f6a3e95df0d5fc6330", 210 | "sha256:7df8e33a2387de6f537836f48ac6fcd94eda6ed9ba3d5e3fd52e35b5bc7ff49e", 211 | "sha256:a673e606e8452185e9817a987572b55360f4d28b50831ef3b42ac3cab3fee846" 212 | ], 213 | "version": "==2018.11.26" 214 | }, 215 | "pipenv-to-requirements": { 216 | "hashes": [ 217 | "sha256:04dc5b1fedfea18858efe3f28d62ed70ac0cd04c34bc32a8c3531384d11be460", 218 | "sha256:44bfefa3f6ded62e3bdcc5a6bb4de7a821b2658f821c1126f6abc3bffecb2c1e" 219 | ], 220 | "index": "pypi", 221 | "version": "==0.7.1" 222 | }, 223 | "pluggy": { 224 | "hashes": [ 225 | "sha256:25a1bc1d148c9a640211872b4ff859878d422bccb59c9965e04eed468a0aa180", 226 | "sha256:964cedd2b27c492fbf0b7f58b3284a09cf7f99b0f715941fb24a439b3af1bd1a" 227 | ], 228 | "version": "==0.11.0" 229 | }, 230 | "py": { 231 | "hashes": [ 232 | "sha256:64f65755aee5b381cea27766a3a147c3f15b9b6b9ac88676de66ba2ae36793fa", 233 | "sha256:dc639b046a6e2cff5bbe40194ad65936d6ba360b52b3c3fe1d08a82dd50b5e53" 234 | ], 235 | "version": "==1.8.0" 236 | }, 237 | "pycodestyle": { 238 | "hashes": [ 239 | "sha256:95a2219d12372f05704562a14ec30bc76b05a5b297b21a5dfe3f6fac3491ae56", 240 | "sha256:e40a936c9a450ad81df37f549d676d127b1b66000a6c500caa2b085bc0ca976c" 241 | ], 242 | "version": "==2.5.0" 243 | }, 244 | "pyflakes": { 245 | "hashes": [ 246 | "sha256:17dbeb2e3f4d772725c777fabc446d5634d1038f234e77343108ce445ea69ce0", 247 | "sha256:d976835886f8c5b31d47970ed689944a0262b5f3afa00a5a7b4dc81e5449f8a2" 248 | ], 249 | "version": "==2.1.1" 250 | }, 251 | "pytest": { 252 | "hashes": [ 253 | "sha256:136632a40451162cdfc18fe4d7ecc5d169b558a3d4bbb1603d4005308a42fd03", 254 | "sha256:62b129bf8368554ca7a942cbdb57ea26aafef46cc65bc317cdac3967e54483a3" 255 | ], 256 | "index": "pypi", 257 | "version": "==4.4.2" 258 | }, 259 | "pytest-cov": { 260 | "hashes": [ 261 | "sha256:2b097cde81a302e1047331b48cadacf23577e431b61e9c6f49a1170bbe3d3da6", 262 | "sha256:e00ea4fdde970725482f1f35630d12f074e121a23801aabf2ae154ec6bdd343a" 263 | ], 264 | "index": "pypi", 265 | "version": "==2.7.1" 266 | }, 267 | "pyyaml": { 268 | "hashes": [ 269 | "sha256:1adecc22f88d38052fb787d959f003811ca858b799590a5eaa70e63dca50308c", 270 | "sha256:436bc774ecf7c103814098159fbb84c2715d25980175292c648f2da143909f95", 271 | "sha256:460a5a4248763f6f37ea225d19d5c205677d8d525f6a83357ca622ed541830c2", 272 | "sha256:5a22a9c84653debfbf198d02fe592c176ea548cccce47553f35f466e15cf2fd4", 273 | "sha256:7a5d3f26b89d688db27822343dfa25c599627bc92093e788956372285c6298ad", 274 | "sha256:9372b04a02080752d9e6f990179a4ab840227c6e2ce15b95e1278456664cf2ba", 275 | "sha256:a5dcbebee834eaddf3fa7366316b880ff4062e4bcc9787b78c7fbb4a26ff2dd1", 276 | "sha256:aee5bab92a176e7cd034e57f46e9df9a9862a71f8f37cad167c6fc74c65f5b4e", 277 | "sha256:c51f642898c0bacd335fc119da60baae0824f2cde95b0330b56c0553439f0673", 278 | "sha256:c68ea4d3ba1705da1e0d85da6684ac657912679a649e8868bd850d2c299cce13", 279 | "sha256:e23d0cc5299223dcc37885dae624f382297717e459ea24053709675a976a3e19" 280 | ], 281 | "version": "==5.1" 282 | }, 283 | "requests": { 284 | "hashes": [ 285 | "sha256:502a824f31acdacb3a35b6690b5fbf0bc41d63a24a45c4004352b0242707598e", 286 | "sha256:7bf2a778576d825600030a110f3c0e3e8edc51dfaafe1c146e39a2027784957b" 287 | ], 288 | "version": "==2.21.0" 289 | }, 290 | "six": { 291 | "hashes": [ 292 | "sha256:3350809f0555b11f552448330d0b52d5f24c91a322ea4a15ef22629740f3761c", 293 | "sha256:d16a0141ec1a18405cd4ce8b4613101da75da0e9a7aec5bdd4fa804d0e0eba73" 294 | ], 295 | "version": "==1.12.0" 296 | }, 297 | "smmap2": { 298 | "hashes": [ 299 | "sha256:0555a7bf4df71d1ef4218e4807bbf9b201f910174e6e08af2e138d4e517b4dde", 300 | "sha256:29a9ffa0497e7f2be94ca0ed1ca1aa3cd4cf25a1f6b4f5f87f74b46ed91d609a" 301 | ], 302 | "version": "==2.0.5" 303 | }, 304 | "stevedore": { 305 | "hashes": [ 306 | "sha256:7be098ff53d87f23d798a7ce7ae5c31f094f3deb92ba18059b1aeb1ca9fec0a0", 307 | "sha256:7d1ce610a87d26f53c087da61f06f9b7f7e552efad2a7f6d2322632b5f932ea2" 308 | ], 309 | "version": "==1.30.1" 310 | }, 311 | "toml": { 312 | "hashes": [ 313 | "sha256:229f81c57791a41d65e399fc06bf0848bab550a9dfd5ed66df18ce5f05e73d5c", 314 | "sha256:235682dd292d5899d361a811df37e04a8828a5b1da3115886b73cf81ebc9100e" 315 | ], 316 | "version": "==0.10.0" 317 | }, 318 | "urllib3": { 319 | "hashes": [ 320 | "sha256:2393a695cd12afedd0dcb26fe5d50d0cf248e5a66f75dbd89a3d4eb333a61af4", 321 | "sha256:a637e5fae88995b256e3409dc4d52c2e2e0ba32c42a6365fee8bbd2238de3cfb" 322 | ], 323 | "version": "==1.24.3" 324 | }, 325 | "virtualenv": { 326 | "hashes": [ 327 | "sha256:15ee248d13e4001a691d9583948ad3947bcb8a289775102e4c4aa98a8b7a6d73", 328 | "sha256:bfc98bb9b42a3029ee41b96dc00a34c2f254cbf7716bec824477b2c82741a5c4" 329 | ], 330 | "version": "==16.5.0" 331 | }, 332 | "virtualenv-clone": { 333 | "hashes": [ 334 | "sha256:532f789a5c88adf339506e3ca03326f20ee82fd08ee5586b44dc859b5b4468c5", 335 | "sha256:c88ae171a11b087ea2513f260cdac9232461d8e9369bcd1dc143fc399d220557" 336 | ], 337 | "version": "==0.5.3" 338 | } 339 | } 340 | } 341 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # hooks4git 2 | 3 | [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) 4 | [![Build Status](https://travis-ci.org/lovato/hooks4git.svg?branch=master)](https://travis-ci.org/lovato/hooks4git) 5 | ![Travis (.org) branch](https://img.shields.io/travis/lovato/hooks4git/develop.svg?label=build%20develop) 6 | [![Coverage Status](https://coveralls.io/repos/github/lovato/hooks4git/badge.svg?branch=master&label=2)](https://coveralls.io/github/lovato/hooks4git?branch=master) 7 | ![Coveralls github](https://img.shields.io/coveralls/github/lovato/hooks4git/develop.svg?color=red&label=coverage%20develop) 8 | [![PyPI version](https://badge.fury.io/py/hooks4git.svg)](https://badge.fury.io/py/hooks4git) 9 | [![asciicast](https://asciinema.org/a/197368.png)](https://asciinema.org/a/197368) 10 | 11 | A fully configurable and extensible language-agnostic Hook Management System for GIT 12 | 13 | Auto checks your code before you ship it. Works with any programmning language. If not, let me know. 14 | 15 | ## Availability 16 | 17 | Production version is available from [Pypi](https://pypi.org/project/hooks4git), and development branch is also published by Travis-CI to [Pypi TestServer](https://test.pypi.org/project/hooks4git). Both can be downloaded and installed via the pip command. 18 | 19 | ### More information on Git Hooks 20 | 21 | [Here](https://githooks.com). There is lots of quick information, and as well other githooks management approaches. 22 | 23 | ## Getting started 24 | 25 | These instructions will show you how to install and use the application. 26 | 27 | ### Supported OSs 28 | 29 | Supported OSs are Linux, MAC and Windows. However, I was not able to make it work `cmd.exe` (like if cmd.exe even works...). If you are using Windows, use it inside GitBash. **DO NOT**, I repeat, do not use it on `cmd.exe`. 30 | 31 | ### Installation 32 | 33 | ```bash 34 | pip install hooks4git --user 35 | ``` 36 | 37 | Depending on your setup, you might want to use `pip3` instead of `pip`. Sometimes, during execution, Python2.7 complains about not finding module `configparser`. Using Python3x this doesn't happen. 38 | 39 | Please, keep in mind that `--user` folder might not be on your PATH environment var. Usually you can find it under `~/.local/bin`. If you fix your `$PATH` now, it will be automatically fixed for any other python tool you might eventually install inside your user context. 40 | 41 | Then, a script called `hooks4git` will be available all the time, to hook any project you are currently in. By running with the `--init` argument, hooks will be applied (i.e replace all your sample hook files). 42 | 43 | Please note you need to manually keep upgrading your system tools, like you do for other tools, like pip itself. 44 | 45 | ### Built-in Scripts 46 | 47 | Those are currently built-in scripts, some implemented, some planned: 48 | 49 | - `check_branch_name`: Written in bash, receives a regex as parameter to match your branch name. 50 | - `get_staged_files`: Written in Python, will fix Issue#21 ... perhaps. It is still under testing. 51 | - `print_leftovers`: Planned, to find print statements. A few can be legit, so a little planning is required here. 52 | 53 | If you want to use, just follow the exemple on the default .ini file, on sub-section 'checkbranch'. This is the way to trigger built-in scripts, prefixing them with 'h4g/'. Make sure you wrap parameters properly with double quotes. 54 | 55 | What else do you hate people often push to the repo? Have an idea? Open an issue and let's talk about it. Those IDE .ini files? Yeah, I hate that too. 56 | 57 | ### CLI Usage 58 | 59 | After installation, your repo needs to be hooked for all events. Prior version used YAML for configuration management, but that caused PyYAML to be a dependency, and things went a little wrong when running it as a tool. So I choose .ini files over .json files (both have Python native parsers) because it looked less ugly. 60 | 61 | Inside your git repository, just type: 62 | 63 | ```bash 64 | hooks4git --init 65 | ``` 66 | 67 | And get all your regular non-sense-hard-to-use-and-hard-to-maintain-and-hard-to-share hook scripts updated. 68 | 69 | Then, you just need to open [.hooks4git.ini](hooks4git/.hooks4git.ini) file on the root of your project and configure it the way you want. 70 | 71 | This first example section is meant for Python, but you can use any tool you want, at any given git hook event. 72 | 73 | Example section for pre-commit, for Python: 74 | 75 | ```bash 76 | [scripts] 77 | flake8 = flake8 --max-line-length=119 --exclude .git,build,dist,.env,.venv 78 | nosetests = nosetests --with-coverage 79 | 80 | [hooks.pre-commit.scripts] 81 | check = flake8 82 | ``` 83 | 84 | It also could be for NodeJS: 85 | 86 | ```bash 87 | [scripts] 88 | eslint = eslint -f checkstyle index.js > checkstyle-result.xml 89 | jshint = jshint *.js 90 | 91 | [hooks.pre-commit.scripts] 92 | check_a = eslint 93 | check_b = jslint 94 | ``` 95 | 96 | Note: All scripts you add here need to be available on your PATH for execution. So you need to make all of them depedencies on your current project, no matter the language it is written with. Per default, the available hooks are only `echo` commands, which will always pass! 97 | 98 | ### CI Usage 99 | 100 | When running inside CI, if you manage to have `hooks4git` package available, you can force trigger a hook this way: 101 | 102 | ```bash 103 | hooks4git -t --ci 104 | ``` 105 | 106 | This will run the very same set of scrips you ran on your development workstation prior to the commit. Please note that `` is any valid entry on `.hooks4git.ini` file, not only necessarily a git-hook. See below about "Custom Hooks". 107 | 108 | #### Colors 109 | 110 | The `--ci` parameter tells hooks4git to not print in nice colors, just plain strings. But first check if your CI output handle colors or not. For instance, Bitbucket Pipelines handle it nicely, while Jenkins doesn't. 111 | 112 | #### "Custom Hooks" 113 | 114 | Hooks have those static names because they are automatically triggered by GIT. However, you can create others inside `.hooks4git.ini` file. And you can trigger them using the `-t` parameter. 115 | 116 | So, if you like `check_branch_name` feature, you might think running it inside CI wouldn't be a great idea. How to solve it? 117 | 118 | ```bash 119 | [hooks.ci-develop.scripts] 120 | check = flake8 121 | tests = tests_with_report 122 | ``` 123 | 124 | As said, there is no "ci-develop" git hook. But due to internal `hooks4git` mechanics, using `-t` flag, `hooks4git` will try to find and run that configuration. 125 | 126 | So, it would be a matter of adding this to your CI script: 127 | 128 | ```bash 129 | - pip install hooks4git 130 | - hooks4git -t ci-develop 131 | ``` 132 | 133 | And since you were using flake8 and tests already on your commit and push hooks, you guarantee to run the same tools with the same parameters on CI, with a nice output, colored or not. 134 | 135 | Disclaimer: This feature was never intended to exist, and happened to work by accident. Since it is kind of cool and doesn't break the law, I decided to document it. 136 | 137 | ### Output 138 | 139 | Here is a sample output for a Python configuration, with Flake8 (black and white... it has actually a full colored output if --ci parameter is not issued): 140 | 141 | ```bash 142 | ——————————————————————————————————————————————————————————————————————————————— 143 | hooks4git v0.4.x :: Pre-Commit :: hook triggered 144 | ——————————————————————————————————————————————————————————————————————————————— 145 | STEP | $ flake8 --max-line-length=119 --exclude .git,__pycache__,build,dist 146 | OUT | None 147 | PASS | 'flake8' step executed successfully 148 | ——————————————————————————————————————————————————————————————————————————————— 149 | STEPS| 1 were executed 150 | TIME | Execution took 0:00:00.684762 151 | PASS | All green! Good! 152 | ——————————————————————————————————————————————————————————————————————————————— 153 | ``` 154 | 155 | ## Contribute 156 | 157 | If you are willing to code something on this project, it is quite simple. You first need to fork it directly on GitHub, so you can get a copy on your computer that you can push to. Therefore, you would be able to open a Pull Request to the original repository. 158 | 159 | ```bash 160 | > git clone git@github.com:/hooks4git.git 161 | > cd hooks4git 162 | > pipenv install --dev 163 | > pip uninstall hooks4git # just in case, it may fail 164 | > pip install -e . --user 165 | > hooks4git --init # OF COURSE!!! 166 | > git checkout -b feature/super_cool_feature 167 | ``` 168 | 169 | The above will install hooks4git linked to the folder you cloned the repository to, instead of the module you normally download from Pypi. This way, every change you make on the source code will affect your environment, makeing it easy to use. Of course there are several other ways, like using virtualenv, for instance. That was only a suggestion and affects all repos you have. This is the way I usually test develop versions for a few days prior to a release. 170 | 171 | ## License 172 | 173 | This project is licensed under MIT license. See the [LICENSE](LICENSE) file for details 174 | 175 | ## Authors 176 | 177 | See list of [contributors](../../graphs/contributors) who participated in this project. 178 | 179 | ## Credits 180 | 181 | - [Marco Lovato](https://github.com/lovato) 182 | - [Collins Abitekaniza](https://github.com/collin5/precommit-hook) (where I forked from) 183 | 184 | ## Change Log 185 | 186 | ### 0.4.x 187 | 188 | - Major rework on classes and dependencies usage 189 | - Added more tests 190 | - Fixed Issue#57 - Subprocess Call Error 191 | - Fixed Issue#54 - Pipenv usage 192 | - Fixed Issue#47 - Colorama usage 193 | 194 | ### 0.3.x 195 | 196 | - Major rework on how strings are printed out 197 | - Added --ci parameter, so no color will be printed out (Idea from [Fernando Espíndola](https://github.com/fernandoe)) 198 | - Auto create hooks folder (inside .git) if it is missing (Idea from [Édouard Lopez](https://github.com/edouard-lopez)) 199 | 200 | ### 0.2.x 201 | 202 | - Support for Windows with GitBash 203 | - Added docker scripts for quick clean machine testing environment 204 | - Better exception handling when user configures duplicate sections by mistake 205 | - FIXED: Changed default max line length example to 119 instead of 120 206 | - Replaced copying code to .git/hooks with a safe bash caller 207 | - Replaced '\_' folder (or 'scripts' folder) with 'h4g' folder for internal scripts 208 | - FIXED: Script order inside a hook definition was random 209 | - Standard Error Output was not being handled and printed accordingly 210 | 211 | ### 0.1.x 212 | 213 | - Initial release 214 | -------------------------------------------------------------------------------- /docker_runme_first.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | docker run --network host -v $(pwd):/tmp/hooks4git:ro -w /tmp/hooks4git -it lovato/bob-python -------------------------------------------------------------------------------- /docker_runme_inside.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | echo INSTALLING from local folder /tmp/hooks4git 3 | echo 4 | cd /tmp 5 | pip install -U /tmp/hooks4git 6 | echo 7 | mkdir /tmp/hooks4git_workdir 8 | cp -r /tmp/hooks4git /tmp/hooks4git_workdir 9 | cd /tmp/hooks4git_workdir 10 | rm -rf .git 11 | echo GIT INIT 12 | git init 13 | echo 14 | echo HOOKS4GIT INIT 15 | hooks4git --init 16 | echo 17 | echo HOOKS4GIT -v 18 | hooks4git -v 19 | echo 20 | echo HOOKS4GIT -h 21 | hooks4git -h 22 | echo 23 | echo HOOKS4GIT -t 24 | hooks4git -t pre-commit 25 | -------------------------------------------------------------------------------- /hooks4git/.hooks4git.ini: -------------------------------------------------------------------------------- 1 | [scripts] 2 | echo = echo Wheeee! Running a hook! 3 | flake8 = flake8 --max-line-length=119 --exclude .git,build,dist,.env,.venv 4 | black = black . --line-length=119 --check 5 | pytest = python -m pytest tests 6 | eslint = eslint -f checkstyle index.js > checkstyle-result.xml 7 | checkbranch = h4g/check_branch_name.sh "^(feature|bugfix|hotfix|fix)\/.+" 8 | pipenv = pip install --dev 9 | 10 | [hooks.pre-commit.scripts] 11 | msg = echo 12 | checkbranch = checkbranch 13 | ; syntax = flake8 14 | 15 | [hooks.pre-push.scripts] 16 | msg = echo 17 | checkbranch = checkbranch 18 | ; syntax = flake8 19 | ; tests = pytest 20 | 21 | [hooks.post-merge.scripts] 22 | ; update_deps = pipenv 23 | 24 | [hooks.applypatch-msg.scripts] 25 | 26 | [hooks.commit-msg.scripts] 27 | 28 | [hooks.fsmonitor-watchman.scripts] 29 | 30 | [hooks.post-applypatch.scripts] 31 | 32 | [hooks.post-checkout.scripts] 33 | 34 | [hooks.post-commit.scripts] 35 | 36 | [hooks.post-receive.scripts] 37 | 38 | [hooks.post-rewrite.scripts] 39 | 40 | [hooks.post-update.scripts] 41 | 42 | [hooks.pre-applypatch.scripts] 43 | 44 | [hooks.pre-auto-gc.scripts] 45 | 46 | [hooks.prepare-commit-msg.scripts] 47 | 48 | [hooks.pre-rebase.scripts] 49 | 50 | [hooks.pre-receive.scripts] 51 | 52 | [hooks.push-to-checkout.scripts] 53 | 54 | [hooks.sendemail-validate.scripts] 55 | 56 | [hooks.update.scripts] 57 | -------------------------------------------------------------------------------- /hooks4git/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | # pep8: disable-msg=E501 3 | # pylint: disable=C0301 4 | import os 5 | import logging 6 | import getpass 7 | import tempfile 8 | 9 | __version__ = "0.4" 10 | __author__ = "Marco Lovato" 11 | __author_username__ = "lovato" 12 | __author_email__ = "maglovato@gmail.com" 13 | __description__ = "Extensible Hook System for GIT" 14 | 15 | log_filename = os.path.join(tempfile.gettempdir(), "hooks4git-" + getpass.getuser() + ".log") 16 | 17 | log = logging 18 | log.basicConfig( 19 | level=logging.DEBUG, format="%(asctime)s %(levelname)s %(message)s", filename=log_filename, filemode="a" 20 | ) 21 | 22 | 23 | def __path(filename): 24 | return os.path.join(os.path.dirname(os.path.realpath(__file__)), filename) 25 | 26 | 27 | # Travis 28 | if os.getenv("TRAVIS_BUILD_NUMBER"): # pragma: no cover 29 | file_ = open(__path("build.info"), "w") 30 | file_.write(os.getenv("TRAVIS_BUILD_NUMBER")) 31 | file_.close() 32 | 33 | __build__ = None 34 | if os.path.exists(__path("build.info")): 35 | __build__ = open(__path("build.info")).read().strip() # pragma: no cover 36 | if __build__ is None: 37 | __build__ = "0" 38 | 39 | __version__ = __version__ + "." + __build__ 40 | -------------------------------------------------------------------------------- /hooks4git/app.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import argparse 3 | import sys 4 | from hooks4git import __version__ 5 | from hooks4git.hook import run_trigger, hook_it 6 | 7 | 8 | def parse_args(args): 9 | parser = argparse.ArgumentParser(description="Extensible Hook System for GIT") 10 | 11 | if len(sys.argv) < 2: 12 | parser.print_usage() 13 | sys.exit(1) 14 | 15 | parser.add_argument("-v", "--version", action="version", version="hooks4git {ver}".format(ver=__version__)) 16 | parser.add_argument( 17 | "--init", 18 | dest="init", 19 | action="store_const", 20 | help="Install hooks4git on current repository (.git/hooks)", 21 | const=True, 22 | ) 23 | parser.add_argument( 24 | "--ci", 25 | dest="ci", 26 | action="store_const", 27 | help="Tells hooks4git to not use colors, so output on CI will look good", 28 | const=True, 29 | ) 30 | parser.add_argument("-t", "--trigger", dest="git_hook", help="Select which hook to trigger manually") 31 | return parser.parse_args(args) 32 | 33 | 34 | def main(): 35 | args = parse_args(sys.argv[1:]) 36 | 37 | if args.init: 38 | hook_it() 39 | else: 40 | if args.git_hook: 41 | ci = False 42 | if args.ci: 43 | ci = True 44 | run_trigger(args.git_hook, ci) 45 | 46 | 47 | if __name__ == "__main__": 48 | main() 49 | -------------------------------------------------------------------------------- /hooks4git/console.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from colorama import init, Fore, Back, Style 3 | from hooks4git.tools import get_dash 4 | import sys 5 | 6 | 7 | class Display(object): 8 | color = None 9 | cmdbarwidth = 5 10 | 11 | def __init__(self, color=True): 12 | init() 13 | self.color = color 14 | 15 | @staticmethod 16 | def bareprint(msg): 17 | print(msg) 18 | 19 | def say(self, msg_type, msg): 20 | label = msg_type 21 | prefix = "" 22 | color = "" 23 | bgcolor = "" 24 | msg_color = "" 25 | style = "" 26 | msg_style = "" 27 | reset = "" 28 | if self.color is True: 29 | style = Style.BRIGHT 30 | reset = Style.RESET_ALL 31 | if msg_type == "FAIL": 32 | color = Fore.RED 33 | if msg_type == "SOUT": 34 | style = Style.DIM 35 | msg_style = Style.DIM 36 | if msg_type == "SERR": 37 | style = Style.DIM 38 | msg_style = Style.DIM 39 | if msg_type == "INFO": 40 | color = Fore.BLUE 41 | if msg_type == "TITLE": 42 | msg_color = Fore.YELLOW 43 | msg_style = Style.BRIGHT 44 | if msg_type == "WARN": 45 | color = Fore.YELLOW 46 | if msg_type in ["STEP", "STEPS", "TIME"]: 47 | color = Fore.BLUE 48 | if msg_type == "ERR!": 49 | color = Fore.RED 50 | if msg_type == "PASS": 51 | color = Fore.GREEN 52 | msg_color = Fore.GREEN 53 | if msg_type == "FAIL": 54 | color = Fore.RED 55 | msg_color = Fore.RED 56 | msg_style = Style.BRIGHT 57 | if msg_type == "PASS ": 58 | color = Fore.WHITE 59 | bgcolor = Back.GREEN 60 | if msg_type == "FAIL ": 61 | color = Fore.YELLOW 62 | bgcolor = Back.RED 63 | if msg_type not in ["DIV", "TITLE"]: 64 | prefix = label.ljust(self.cmdbarwidth) + "|" + reset + color + " " 65 | line = style + color + bgcolor + prefix + reset + msg_style + msg_color + msg + reset 66 | print(line) 67 | sys.stdout.flush() 68 | return line 69 | 70 | def divider(self): 71 | dash = get_dash() 72 | return self.say("DIV", dash * self.cmdbarwidth + dash + dash * (79 - 1 - self.cmdbarwidth)) 73 | -------------------------------------------------------------------------------- /hooks4git/git/hooks/applypatch-msg: -------------------------------------------------------------------------------- 1 | ../../hooks4git.sh -------------------------------------------------------------------------------- /hooks4git/git/hooks/commit-msg: -------------------------------------------------------------------------------- 1 | ../../hooks4git.sh -------------------------------------------------------------------------------- /hooks4git/git/hooks/fsmonitor-watchman: -------------------------------------------------------------------------------- 1 | ../../hooks4git.sh -------------------------------------------------------------------------------- /hooks4git/git/hooks/post-applypatch: -------------------------------------------------------------------------------- 1 | ../../hooks4git.sh -------------------------------------------------------------------------------- /hooks4git/git/hooks/post-checkout: -------------------------------------------------------------------------------- 1 | ../../hooks4git.sh -------------------------------------------------------------------------------- /hooks4git/git/hooks/post-commit: -------------------------------------------------------------------------------- 1 | ../../hooks4git.sh -------------------------------------------------------------------------------- /hooks4git/git/hooks/post-merge: -------------------------------------------------------------------------------- 1 | ../../hooks4git.sh -------------------------------------------------------------------------------- /hooks4git/git/hooks/post-receive: -------------------------------------------------------------------------------- 1 | ../../hooks4git.sh -------------------------------------------------------------------------------- /hooks4git/git/hooks/post-rewrite: -------------------------------------------------------------------------------- 1 | ../../hooks4git.sh -------------------------------------------------------------------------------- /hooks4git/git/hooks/post-update: -------------------------------------------------------------------------------- 1 | ../../hooks4git.sh -------------------------------------------------------------------------------- /hooks4git/git/hooks/pre-applypatch: -------------------------------------------------------------------------------- 1 | ../../hooks4git.sh -------------------------------------------------------------------------------- /hooks4git/git/hooks/pre-auto-gc: -------------------------------------------------------------------------------- 1 | ../../hooks4git.sh -------------------------------------------------------------------------------- /hooks4git/git/hooks/pre-commit: -------------------------------------------------------------------------------- 1 | ../../hooks4git.sh -------------------------------------------------------------------------------- /hooks4git/git/hooks/pre-push: -------------------------------------------------------------------------------- 1 | ../../hooks4git.sh -------------------------------------------------------------------------------- /hooks4git/git/hooks/pre-rebase: -------------------------------------------------------------------------------- 1 | ../../hooks4git.sh -------------------------------------------------------------------------------- /hooks4git/git/hooks/pre-receive: -------------------------------------------------------------------------------- 1 | ../../hooks4git.sh -------------------------------------------------------------------------------- /hooks4git/git/hooks/prepare-commit-msg: -------------------------------------------------------------------------------- 1 | ../../hooks4git.sh -------------------------------------------------------------------------------- /hooks4git/git/hooks/push-to-checkout: -------------------------------------------------------------------------------- 1 | ../../hooks4git.sh -------------------------------------------------------------------------------- /hooks4git/git/hooks/sendemail-validate: -------------------------------------------------------------------------------- 1 | ../../hooks4git.sh -------------------------------------------------------------------------------- /hooks4git/git/hooks/update: -------------------------------------------------------------------------------- 1 | ../../hooks4git.sh -------------------------------------------------------------------------------- /hooks4git/h4g/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lovato/hooks4git/102a953352230e31568f660b56134dd03b0eff00/hooks4git/h4g/__init__.py -------------------------------------------------------------------------------- /hooks4git/h4g/check_branch_name: -------------------------------------------------------------------------------- 1 | check_branch_name.sh -------------------------------------------------------------------------------- /hooks4git/h4g/check_branch_name.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ -z "$1" ] 4 | then 5 | valid='^(feature|bugfix|hotfix)\/.+' 6 | else 7 | valid="$1" 8 | fi 9 | 10 | # branch=`git rev-parse --abbrev-ref HEAD` 11 | # branch=`git symbolic-ref --short HEAD` # https://stackoverflow.com/questions/6245570/how-to-get-the-current-branch-name-in-git 12 | branch=$(git status | head -1 | sed -n '/On branch /s///p') 13 | 14 | if [[ $branch =~ $valid ]]; then 15 | echo Branch is OK with naming conventions 16 | else 17 | echo Your branch \'$branch\' needs a better name to match conventions. Sorry. 18 | echo This rules branch naming: $valid 19 | echo You can try a few at https://regex101.com/ 20 | echo Then, please, follow these steps if needed: 21 | echo - http://tinyurl.com/renamegitbranches 22 | exit 1 23 | fi 24 | -------------------------------------------------------------------------------- /hooks4git/h4g/get_staged_files: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | import subprocess 4 | import sys 5 | import argparse 6 | 7 | 8 | def parse_args(args): 9 | parser = argparse.ArgumentParser(description="Gets staged files from a GIT Repository") 10 | 11 | parser.add_argument("--prefix", dest="prefix", help="String to be added before output") 12 | parser.add_argument("--suffix", dest="suffix", help="String to be added after output") 13 | parser.add_argument("--format", dest="format", help="Defines output format") 14 | return parser.parse_args(args) 15 | 16 | 17 | def system(*args, **kwargs): 18 | """ 19 | Run system command. 20 | """ 21 | result_out = "" 22 | result_err = "" 23 | returncode = -1 24 | try: 25 | proc = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 26 | out, err = proc.communicate() 27 | try: 28 | tmp_out = out.decode("utf-8") 29 | result_out = str(tmp_out) 30 | except Exception as e: # noqa 31 | result_out = str(out) 32 | try: 33 | tmp_err = err.decode("utf-8") 34 | result_err = str(tmp_err) 35 | except Exception as e: # noqa 36 | result_err = str(err) 37 | returncode = proc.returncode 38 | except Exception as e: # noqa 39 | err = str(e) 40 | return returncode, result_out, result_err 41 | 42 | 43 | def get_staged_files(): 44 | git_root = system("git", "rev-parse", "--show-toplevel")[1].replace("\n", "") 45 | files = [] 46 | filelist = system("git", "diff", "--name-status")[1].replace("\t", ";").split("\n") 47 | filelist.pop() 48 | for line in filelist: 49 | try: 50 | action, filename = line.strip().split(";") 51 | if action != "D": 52 | files.append("%s/%s" % (git_root, filename)) 53 | except Exception as ex: # noqa 54 | pass 55 | return files 56 | 57 | 58 | if __name__ == "__main__": 59 | args = parse_args(sys.argv[1:]) 60 | result = get_staged_files() 61 | if args.format == "csv": 62 | print("%s%s%s" % (args.prefix, ",".join(result), args.suffix)) 63 | -------------------------------------------------------------------------------- /hooks4git/hook.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import os 3 | import sys 4 | import configparser 5 | import datetime 6 | from hooks4git import __version__ 7 | from hooks4git.tools import os_call, copy_file, add_usersitepackages_to_path, get_platform 8 | from hooks4git.console import Display 9 | import shlex 10 | 11 | steps_executed = 0 12 | display = None 13 | 14 | 15 | def get_hooks_path(git_root_path): 16 | if git_root_path is None: 17 | msg = "I am afraid I can't to that. You are not inside a GIT repo. Reach one and re-run this tool." 18 | Display.bareprint(msg) 19 | return None 20 | if git_root_path.replace("\\", "/").endswith("/.git") is False: 21 | msg = "Humm, this is odd. Your GIT repo must have a .git folder. Looks like you are not inside a GIT repo." 22 | Display.bareprint(msg) 23 | return None 24 | hooks_path = os.path.join(git_root_path, "hooks") 25 | if not os.path.isdir(hooks_path): 26 | message = "Looks like your '.git/hooks' folder is missing." 27 | Display.bareprint("%s Let's try to fix this..." % message) 28 | try: 29 | os.makedirs(hooks_path) 30 | Display.bareprint("Cool! '.git/hooks' folder was created.") 31 | return hooks_path 32 | except: # noqa # pragma: no cover 33 | return None 34 | else: 35 | return hooks_path 36 | 37 | 38 | def hook_it(path=os.environ["PWD"]): 39 | # setup_path = os.path.join(os_call('git', 'rev-parse', '--show-toplevel')[1].replace('\n', ''), 'hooks4git') 40 | setup_path = os.path.dirname(os.path.realpath(__file__)) 41 | try: 42 | path = os_call("git -C %s rev-parse --show-toplevel" % path)[1].replace("\n", "") 43 | git_path = os_call("git -C %s rev-parse --git-dir" % path)[1].replace("\n", "") 44 | if git_path == ".git": 45 | git_path = os.path.join(path, git_path) 46 | except: # noqa # pragma: no cover 47 | git_path = None 48 | 49 | path = os.path.abspath(path) 50 | setup_path = os.path.abspath(setup_path) 51 | hooks_path = get_hooks_path(git_path) 52 | if hooks_path: 53 | origin_config = os.path.join(setup_path, ".hooks4git.ini") 54 | target_config = os.path.join(path, ".hooks4git.ini") 55 | if os.path.isfile(target_config): 56 | target_config = target_config.replace(".ini", "-" + __version__ + ".ini") 57 | copy_file(origin_config, target_config) 58 | files_to_copy = os_call("ls %s" % os.path.join(setup_path, "git/hooks")) 59 | for file in files_to_copy[1].split("\n"): 60 | if file not in ["__pycache__", "", "hooks4git.py"]: 61 | src = os.path.join(setup_path, "git/hooks", file) 62 | target = os.path.join(git_path, "hooks", file) 63 | copy_file(src, target) 64 | Display.bareprint("Wow! hooks4git files were installed successfully! Thanks for hooking!") 65 | Display.bareprint("If you are a courious person, take a look at .git/hooks folder.") 66 | Display.bareprint("TIP: To get rid of hooks, comment lines on the .hooks4git.ini file.") 67 | return True 68 | 69 | 70 | def run_hook_cmd(command, files): 71 | """ 72 | Prepare system command. 73 | """ 74 | splited_args = shlex.split(command) 75 | cmd = splited_args[0] 76 | args = splited_args[1:] 77 | builtin_path = "" 78 | 79 | # backward compatibility to 0.1.x 80 | if cmd[0] == "_": 81 | cmd = "h4g/" + cmd[1:] 82 | display.say("WARN", "Please upgrade your ini file to call built in scripts prefixed by 'h4g/'") 83 | # backward compatibility to early 0.2.x 84 | if cmd[0:8] == "scripts/": 85 | cmd = "h4g/" + cmd[8:] 86 | display.say("WARN", "Please upgrade your ini file to call built in scripts prefixed by 'h4g/'") 87 | # backward compatibility to 0.3.x 88 | if cmd[0:21] == "h4g/check_branch_name": 89 | _command = shlex.split(command) 90 | command = _command[0] 91 | if "Windows" not in get_platform(): 92 | if len(_command) > 1: 93 | command = command + ' "' + _command[1] + '"' 94 | # end 95 | 96 | cmd_list = cmd.split("/") 97 | git_root = os_call("git rev-parse --show-toplevel")[1].replace("\n", "") 98 | if cmd_list[0] == "h4g": 99 | sys.path.insert(0, git_root) 100 | add_usersitepackages_to_path("python") 101 | add_usersitepackages_to_path("python2") 102 | add_usersitepackages_to_path("python3") 103 | for path in sys.path: 104 | builtin_path = os.path.realpath(path + "/hooks4git/h4g/") 105 | _cmd = os.path.realpath(os.path.join(builtin_path, cmd_list[1])) 106 | if os.path.exists(_cmd): 107 | cmd = os.path.join(builtin_path, cmd_list[1]) 108 | break 109 | 110 | display_message = "%s" % (command) 111 | if cmd == "echo": 112 | if files: 113 | _arg = "--filename=%s" % ",".join(files) 114 | args.append(_arg) 115 | display_message += " " + _arg.replace(git_root, ".")[: (66 - len(display_message))] + "..." 116 | 117 | display.say("STEP", "$ %s" % display_message) 118 | params = "" 119 | try: 120 | params = command.split(" ", 1)[1] 121 | except: # noqa # nosec 122 | pass 123 | code, result, err = os_call("%s %s" % (cmd, params)) 124 | result = result.strip().replace("\n", "\n".ljust(display.cmdbarwidth + 1) + "| ") 125 | err = err.strip().replace("\n", "\n".ljust(display.cmdbarwidth + 1) + "| ") 126 | if len(result) > 0: 127 | display.say("SOUT", result) 128 | if len(err) > 0: 129 | display.say("SERR", err) 130 | return code, result, err 131 | 132 | 133 | def main(cmd): 134 | git_root = os_call("git rev-parse --show-toplevel")[1].replace("\n", "") 135 | configfile = "%s/.hooks4git.ini" % git_root 136 | config = configparser.ConfigParser() 137 | cfg = {} 138 | exception_message = "" 139 | try: 140 | config.read(configfile) 141 | cfg = dict(config._sections) 142 | except Exception as e: # noqa 143 | exception_message = str(e) 144 | 145 | global steps_executed 146 | steps_executed = 0 147 | no_fails = True 148 | try: 149 | scripts = cfg.get("scripts", {}) 150 | hook = cfg.get("hooks.%s.scripts" % cmd, {}) 151 | commands = hook.keys() 152 | # If section is ommited, app outputs absolutelly nothing to stdout 153 | if "hooks." + cmd.lower() + ".scripts" in config.sections(): 154 | display.divider() 155 | title = "hooks4git v%s :: %s :: hook triggered" % (__version__, cmd.title()) 156 | display.say("TITLE", title) 157 | if len(exception_message) > 0: 158 | display.divider() 159 | Display.bareprint("Oops! " + exception_message) 160 | display.divider() 161 | exit(1) 162 | display.divider() 163 | for command_item in commands: 164 | steps_executed += 1 165 | files = [] 166 | # files = get_changed_files() 167 | command = scripts[hook[command_item]] 168 | result = run_hook_cmd(command, files) 169 | if result[0] != 0: 170 | no_fails = False 171 | display.say("FAIL", "'%s/%s' step failed to execute" % (command_item, hook[command_item])) # noqa 172 | else: 173 | display.say( 174 | "PASS", "'%s/%s' step executed successfully" % (command_item, hook[command_item]) 175 | ) # noqa 176 | display.divider() 177 | return no_fails 178 | except Exception as e: # noqa 179 | display.say("ERR!", str(e)) 180 | raise (e) 181 | 182 | 183 | def report(start_time, display): 184 | line1 = None 185 | line2 = None 186 | if steps_executed > 0: 187 | end_time = datetime.datetime.now() 188 | if steps_executed > 1: 189 | to_be = "were" 190 | else: 191 | to_be = "was" 192 | line1 = display.say("STEPS", "%s %s executed" % (steps_executed, to_be)) 193 | line2 = display.say("TIME", "Execution took " + str(end_time - start_time)) 194 | return line1, line2 195 | 196 | 197 | def run_trigger(cmd, ci=False): 198 | color_flag = not ci 199 | global display 200 | display = Display(color_flag) 201 | 202 | start_time = datetime.datetime.now() 203 | 204 | if main(cmd): 205 | report(start_time, display) 206 | if steps_executed > 0: 207 | display.say("PASS ", "All green! Good!") 208 | display.divider() 209 | sys.exit(0) 210 | else: 211 | report(start_time, display) 212 | display.say("FAIL ", "You have failed. One or more steps failed to execute.") 213 | display.divider() 214 | sys.exit(1) 215 | -------------------------------------------------------------------------------- /hooks4git/hooks4git.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | if [ -x "$(command -v hooks4git)" ]; then 3 | hooks4git -t $(basename $0) 4 | fi -------------------------------------------------------------------------------- /hooks4git/tools.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # from setuptools.command.install import install 3 | import os 4 | import sys 5 | import shutil 6 | import subprocess # nosec 7 | 8 | 9 | def copy_file(src, dest): 10 | try: 11 | shutil.copy(src, dest) 12 | return True 13 | except: # noqa 14 | return False 15 | 16 | 17 | def os_call(command): 18 | """ 19 | Run system command. 20 | """ 21 | command = command.strip() 22 | result_out = "" 23 | result_err = "" 24 | returncode = -1 25 | try: 26 | pipe1 = subprocess.PIPE 27 | pipe2 = subprocess.PIPE 28 | bash = "/bin/bash" 29 | shell = True 30 | 31 | if "Windows" in get_platform(): 32 | bash = None 33 | shell = False 34 | if "h4g" in command: 35 | command = "/" + command[0].lower() + command[2:].replace(":\\", "/").replace("\\", "/") # noqa 36 | command = 'c:\\Program Files\\Git\\bin\\bash.exe -c "' + command + '"' # noqa 37 | 38 | proc = subprocess.Popen(command, stdout=pipe1, stderr=pipe2, shell=shell, executable=bash) # noqa # nosec 39 | out, err = proc.communicate() 40 | try: 41 | tmp_out = out.decode("utf-8") 42 | result_out = str(tmp_out) 43 | except Exception as e: # noqa # pragma: no cover 44 | result_out = str(out) 45 | try: 46 | tmp_err = err.decode("utf-8") 47 | result_err = str(tmp_err) 48 | except Exception as e: # noqa # pragma: no cover 49 | result_err = str(err) 50 | returncode = proc.returncode 51 | except Exception as e: # noqa # pragma: no cover 52 | result_err = str(e) 53 | return returncode, result_out, result_err 54 | 55 | 56 | def add_usersitepackages_to_path(binary): 57 | try: 58 | user_site = os_call("%s -m site --user-site" % binary)[1].replace("\n", "") 59 | sys.path.insert(0, user_site) 60 | return user_site 61 | except: # noqa # nosec # pragma: no cover 62 | return False 63 | 64 | 65 | def get_platform(): 66 | platform = sys.platform 67 | environ = os.environ 68 | platforms = { 69 | "linux": "Linux", 70 | "linux1": "Linux", 71 | "linux2": "Linux", 72 | "darwin": "Mac", 73 | "win32": "Windows", 74 | "win32MINGW64": "WindowsGitBash", 75 | } 76 | _platform = platform + environ.get("MSYSTEM", "") 77 | if _platform not in platforms: 78 | return platform 79 | return platforms[_platform] 80 | 81 | 82 | def get_dash(): 83 | dash = "-" 84 | if get_platform() == "Linux": 85 | if sys.version_info[0] < 3: 86 | dash = unichr(8213) # noqa # pragma: no cover 87 | else: 88 | dash = chr(8213) 89 | if get_platform() == "Mac": # pragma: no cover 90 | if sys.version_info[0] < 3: 91 | dash = unichr(8212) # noqa 92 | else: 93 | dash = chr(8212) 94 | if get_platform() == "Windows": # CMD.exe # pragma: no cover 95 | if sys.version_info[0] < 3: 96 | dash = "-" 97 | else: 98 | dash = chr(8212) 99 | if get_platform() == "WindowsGitBash": # pragma: no cover 100 | if sys.version_info[0] < 3: 101 | dash = "-" 102 | else: 103 | dash = "-" 104 | return dash 105 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hooks4git", 3 | "version": "1.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "ajv": { 8 | "version": "6.10.0", 9 | "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.0.tgz", 10 | "integrity": "sha512-nffhOpkymDECQyR0mnsUtoCE8RlX38G0rYP+wgLWFyZuUyuuojSSvi/+euOiQBIn63whYwYVIIH1TvE3tu4OEg==", 11 | "dev": true, 12 | "requires": { 13 | "fast-deep-equal": "^2.0.1", 14 | "fast-json-stable-stringify": "^2.0.0", 15 | "json-schema-traverse": "^0.4.1", 16 | "uri-js": "^4.2.2" 17 | } 18 | }, 19 | "argparse": { 20 | "version": "1.0.10", 21 | "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", 22 | "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", 23 | "dev": true, 24 | "requires": { 25 | "sprintf-js": "~1.0.2" 26 | } 27 | }, 28 | "asn1": { 29 | "version": "0.2.4", 30 | "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", 31 | "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", 32 | "dev": true, 33 | "requires": { 34 | "safer-buffer": "~2.1.0" 35 | } 36 | }, 37 | "assert-plus": { 38 | "version": "1.0.0", 39 | "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", 40 | "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", 41 | "dev": true 42 | }, 43 | "asynckit": { 44 | "version": "0.4.0", 45 | "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", 46 | "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", 47 | "dev": true 48 | }, 49 | "aws-sign2": { 50 | "version": "0.7.0", 51 | "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", 52 | "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", 53 | "dev": true 54 | }, 55 | "aws4": { 56 | "version": "1.8.0", 57 | "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz", 58 | "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==", 59 | "dev": true 60 | }, 61 | "balanced-match": { 62 | "version": "1.0.0", 63 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", 64 | "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", 65 | "dev": true 66 | }, 67 | "bcrypt-pbkdf": { 68 | "version": "1.0.2", 69 | "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", 70 | "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", 71 | "dev": true, 72 | "requires": { 73 | "tweetnacl": "^0.14.3" 74 | } 75 | }, 76 | "brace-expansion": { 77 | "version": "1.1.11", 78 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 79 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 80 | "dev": true, 81 | "requires": { 82 | "balanced-match": "^1.0.0", 83 | "concat-map": "0.0.1" 84 | } 85 | }, 86 | "caseless": { 87 | "version": "0.12.0", 88 | "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", 89 | "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", 90 | "dev": true 91 | }, 92 | "combined-stream": { 93 | "version": "1.0.7", 94 | "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.7.tgz", 95 | "integrity": "sha512-brWl9y6vOB1xYPZcpZde3N9zDByXTosAeMDo4p1wzo6UMOX4vumB+TP1RZ76sfE6Md68Q0NJSrE/gbezd4Ul+w==", 96 | "dev": true, 97 | "requires": { 98 | "delayed-stream": "~1.0.0" 99 | } 100 | }, 101 | "commander": { 102 | "version": "2.9.0", 103 | "resolved": "https://registry.npmjs.org/commander/-/commander-2.9.0.tgz", 104 | "integrity": "sha1-nJkJQXbhIkDLItbFFGCYQA/g99Q=", 105 | "dev": true, 106 | "requires": { 107 | "graceful-readlink": ">= 1.0.0" 108 | } 109 | }, 110 | "concat-map": { 111 | "version": "0.0.1", 112 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 113 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", 114 | "dev": true 115 | }, 116 | "core-util-is": { 117 | "version": "1.0.2", 118 | "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", 119 | "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", 120 | "dev": true 121 | }, 122 | "dashdash": { 123 | "version": "1.14.1", 124 | "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", 125 | "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", 126 | "dev": true, 127 | "requires": { 128 | "assert-plus": "^1.0.0" 129 | } 130 | }, 131 | "deep-extend": { 132 | "version": "0.5.1", 133 | "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.5.1.tgz", 134 | "integrity": "sha512-N8vBdOa+DF7zkRrDCsaOXoCs/E2fJfx9B9MrKnnSiHNh4ws7eSys6YQE4KvT1cecKmOASYQBhbKjeuDD9lT81w==", 135 | "dev": true 136 | }, 137 | "delayed-stream": { 138 | "version": "1.0.0", 139 | "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", 140 | "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", 141 | "dev": true 142 | }, 143 | "ecc-jsbn": { 144 | "version": "0.1.2", 145 | "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", 146 | "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", 147 | "dev": true, 148 | "requires": { 149 | "jsbn": "~0.1.0", 150 | "safer-buffer": "^2.1.0" 151 | } 152 | }, 153 | "entities": { 154 | "version": "1.1.2", 155 | "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz", 156 | "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==", 157 | "dev": true 158 | }, 159 | "esprima": { 160 | "version": "4.0.1", 161 | "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", 162 | "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", 163 | "dev": true 164 | }, 165 | "extend": { 166 | "version": "3.0.2", 167 | "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", 168 | "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", 169 | "dev": true 170 | }, 171 | "extsprintf": { 172 | "version": "1.3.0", 173 | "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", 174 | "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", 175 | "dev": true 176 | }, 177 | "fast-deep-equal": { 178 | "version": "2.0.1", 179 | "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", 180 | "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=", 181 | "dev": true 182 | }, 183 | "fast-json-stable-stringify": { 184 | "version": "2.0.0", 185 | "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", 186 | "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=", 187 | "dev": true 188 | }, 189 | "forever-agent": { 190 | "version": "0.6.1", 191 | "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", 192 | "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", 193 | "dev": true 194 | }, 195 | "form-data": { 196 | "version": "2.3.3", 197 | "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", 198 | "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", 199 | "dev": true, 200 | "requires": { 201 | "asynckit": "^0.4.0", 202 | "combined-stream": "^1.0.6", 203 | "mime-types": "^2.1.12" 204 | } 205 | }, 206 | "fs.realpath": { 207 | "version": "1.0.0", 208 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 209 | "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", 210 | "dev": true 211 | }, 212 | "get-stdin": { 213 | "version": "5.0.1", 214 | "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-5.0.1.tgz", 215 | "integrity": "sha1-Ei4WFZHiH/TFJTAwVpPyDmOTo5g=", 216 | "dev": true 217 | }, 218 | "getpass": { 219 | "version": "0.1.7", 220 | "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", 221 | "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", 222 | "dev": true, 223 | "requires": { 224 | "assert-plus": "^1.0.0" 225 | } 226 | }, 227 | "github": { 228 | "version": "0.1.16", 229 | "resolved": "https://registry.npmjs.org/github/-/github-0.1.16.tgz", 230 | "integrity": "sha1-iV0qhbD+t5gNiawM5PRNyqA/F7U=", 231 | "dev": true 232 | }, 233 | "glob": { 234 | "version": "7.1.4", 235 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz", 236 | "integrity": "sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==", 237 | "dev": true, 238 | "requires": { 239 | "fs.realpath": "^1.0.0", 240 | "inflight": "^1.0.4", 241 | "inherits": "2", 242 | "minimatch": "^3.0.4", 243 | "once": "^1.3.0", 244 | "path-is-absolute": "^1.0.0" 245 | } 246 | }, 247 | "graceful-readlink": { 248 | "version": "1.0.1", 249 | "resolved": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz", 250 | "integrity": "sha1-TK+tdrxi8C+gObL5Tpo906ORpyU=", 251 | "dev": true 252 | }, 253 | "har-schema": { 254 | "version": "2.0.0", 255 | "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", 256 | "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", 257 | "dev": true 258 | }, 259 | "har-validator": { 260 | "version": "5.1.3", 261 | "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz", 262 | "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==", 263 | "dev": true, 264 | "requires": { 265 | "ajv": "^6.5.5", 266 | "har-schema": "^2.0.0" 267 | } 268 | }, 269 | "http-signature": { 270 | "version": "1.2.0", 271 | "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", 272 | "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", 273 | "dev": true, 274 | "requires": { 275 | "assert-plus": "^1.0.0", 276 | "jsprim": "^1.2.2", 277 | "sshpk": "^1.7.0" 278 | } 279 | }, 280 | "inflight": { 281 | "version": "1.0.6", 282 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 283 | "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", 284 | "dev": true, 285 | "requires": { 286 | "once": "^1.3.0", 287 | "wrappy": "1" 288 | } 289 | }, 290 | "inherits": { 291 | "version": "2.0.3", 292 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", 293 | "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", 294 | "dev": true 295 | }, 296 | "ini": { 297 | "version": "1.3.7", 298 | "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.7.tgz", 299 | "integrity": "sha512-iKpRpXP+CrP2jyrxvg1kMUpXDyRUFDWurxbnVT1vQPx+Wz9uCYsMIqYuSBLV+PAaZG/d7kRLKRFc9oDMsH+mFQ==", 300 | "dev": true 301 | }, 302 | "is-typedarray": { 303 | "version": "1.0.0", 304 | "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", 305 | "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", 306 | "dev": true 307 | }, 308 | "isstream": { 309 | "version": "0.1.2", 310 | "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", 311 | "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", 312 | "dev": true 313 | }, 314 | "js-yaml": { 315 | "version": "3.13.1", 316 | "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", 317 | "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", 318 | "dev": true, 319 | "requires": { 320 | "argparse": "^1.0.7", 321 | "esprima": "^4.0.0" 322 | } 323 | }, 324 | "jsbn": { 325 | "version": "0.1.1", 326 | "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", 327 | "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", 328 | "dev": true 329 | }, 330 | "json-schema": { 331 | "version": "0.2.3", 332 | "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", 333 | "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=", 334 | "dev": true 335 | }, 336 | "json-schema-traverse": { 337 | "version": "0.4.1", 338 | "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", 339 | "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", 340 | "dev": true 341 | }, 342 | "json-stringify-safe": { 343 | "version": "5.0.1", 344 | "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", 345 | "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", 346 | "dev": true 347 | }, 348 | "jsprim": { 349 | "version": "1.4.1", 350 | "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", 351 | "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", 352 | "dev": true, 353 | "requires": { 354 | "assert-plus": "1.0.0", 355 | "extsprintf": "1.3.0", 356 | "json-schema": "0.2.3", 357 | "verror": "1.10.0" 358 | } 359 | }, 360 | "linkify-it": { 361 | "version": "2.1.0", 362 | "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-2.1.0.tgz", 363 | "integrity": "sha512-4REs8/062kV2DSHxNfq5183zrqXMl7WP0WzABH9IeJI+NLm429FgE1PDecltYfnOoFDFlZGh2T8PfZn0r+GTRg==", 364 | "dev": true, 365 | "requires": { 366 | "uc.micro": "^1.0.1" 367 | } 368 | }, 369 | "lodash": { 370 | "version": "1.3.1", 371 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-1.3.1.tgz", 372 | "integrity": "sha1-pGY7U2hriV/wdOK6UE37dqjit3A=", 373 | "dev": true 374 | }, 375 | "lodash.differencewith": { 376 | "version": "4.5.0", 377 | "resolved": "https://registry.npmjs.org/lodash.differencewith/-/lodash.differencewith-4.5.0.tgz", 378 | "integrity": "sha1-uvr7yRi1UVTheRdqALsK76rIVLc=", 379 | "dev": true 380 | }, 381 | "lodash.flatten": { 382 | "version": "4.4.0", 383 | "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz", 384 | "integrity": "sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8=", 385 | "dev": true 386 | }, 387 | "markdown-it": { 388 | "version": "8.4.2", 389 | "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-8.4.2.tgz", 390 | "integrity": "sha512-GcRz3AWTqSUphY3vsUqQSFMbgR38a4Lh3GWlHRh/7MRwz8mcu9n2IO7HOh+bXHrR9kOPDl5RNCaEsrneb+xhHQ==", 391 | "dev": true, 392 | "requires": { 393 | "argparse": "^1.0.7", 394 | "entities": "~1.1.1", 395 | "linkify-it": "^2.0.0", 396 | "mdurl": "^1.0.1", 397 | "uc.micro": "^1.0.5" 398 | } 399 | }, 400 | "markdownlint": { 401 | "version": "0.13.0", 402 | "resolved": "https://registry.npmjs.org/markdownlint/-/markdownlint-0.13.0.tgz", 403 | "integrity": "sha512-mGeDEbvmRk2T8f043lK7Lhcdi7mr68hQqGsJCS4p52uvEyOLym28s3r7GA6aaFQFtwT0MfFaJ/7Ex4n/HVPUnQ==", 404 | "dev": true, 405 | "requires": { 406 | "markdown-it": "8.4.2" 407 | } 408 | }, 409 | "markdownlint-cli": { 410 | "version": "0.15.0", 411 | "resolved": "https://registry.npmjs.org/markdownlint-cli/-/markdownlint-cli-0.15.0.tgz", 412 | "integrity": "sha512-UTstPEXMix4CO1RIgjX3Bd1M+xklc4KXMP2309Zfp5y3UJOASWLSXnTnzkztBn5aZRt7bzLZmxTaR8M4J8cbbQ==", 413 | "dev": true, 414 | "requires": { 415 | "commander": "~2.9.0", 416 | "deep-extend": "~0.5.1", 417 | "get-stdin": "~5.0.1", 418 | "glob": "~7.1.2", 419 | "js-yaml": "~3.13.0", 420 | "lodash.differencewith": "~4.5.0", 421 | "lodash.flatten": "~4.4.0", 422 | "markdownlint": "~0.13.0", 423 | "minimatch": "~3.0.4", 424 | "rc": "~1.2.7" 425 | } 426 | }, 427 | "mdurl": { 428 | "version": "1.0.1", 429 | "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz", 430 | "integrity": "sha1-/oWy7HWlkDfyrf7BAP1sYBdhFS4=", 431 | "dev": true 432 | }, 433 | "mime-db": { 434 | "version": "1.40.0", 435 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.40.0.tgz", 436 | "integrity": "sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA==", 437 | "dev": true 438 | }, 439 | "mime-types": { 440 | "version": "2.1.24", 441 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.24.tgz", 442 | "integrity": "sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ==", 443 | "dev": true, 444 | "requires": { 445 | "mime-db": "1.40.0" 446 | } 447 | }, 448 | "minimatch": { 449 | "version": "3.0.4", 450 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", 451 | "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", 452 | "dev": true, 453 | "requires": { 454 | "brace-expansion": "^1.1.7" 455 | } 456 | }, 457 | "minimist": { 458 | "version": "1.2.5", 459 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", 460 | "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", 461 | "dev": true 462 | }, 463 | "oauth-sign": { 464 | "version": "0.9.0", 465 | "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", 466 | "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", 467 | "dev": true 468 | }, 469 | "once": { 470 | "version": "1.4.0", 471 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 472 | "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", 473 | "dev": true, 474 | "requires": { 475 | "wrappy": "1" 476 | } 477 | }, 478 | "path-is-absolute": { 479 | "version": "1.0.1", 480 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 481 | "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", 482 | "dev": true 483 | }, 484 | "performance-now": { 485 | "version": "2.1.0", 486 | "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", 487 | "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", 488 | "dev": true 489 | }, 490 | "psl": { 491 | "version": "1.1.31", 492 | "resolved": "https://registry.npmjs.org/psl/-/psl-1.1.31.tgz", 493 | "integrity": "sha512-/6pt4+C+T+wZUieKR620OpzN/LlnNKuWjy1iFLQ/UG35JqHlR/89MP1d96dUfkf6Dne3TuLQzOYEYshJ+Hx8mw==", 494 | "dev": true 495 | }, 496 | "punycode": { 497 | "version": "2.1.1", 498 | "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", 499 | "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", 500 | "dev": true 501 | }, 502 | "qs": { 503 | "version": "6.5.2", 504 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", 505 | "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", 506 | "dev": true 507 | }, 508 | "rc": { 509 | "version": "1.2.8", 510 | "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", 511 | "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", 512 | "dev": true, 513 | "requires": { 514 | "deep-extend": "^0.6.0", 515 | "ini": "~1.3.0", 516 | "minimist": "^1.2.0", 517 | "strip-json-comments": "~2.0.1" 518 | }, 519 | "dependencies": { 520 | "deep-extend": { 521 | "version": "0.6.0", 522 | "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", 523 | "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", 524 | "dev": true 525 | } 526 | } 527 | }, 528 | "request": { 529 | "version": "2.88.0", 530 | "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz", 531 | "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==", 532 | "dev": true, 533 | "requires": { 534 | "aws-sign2": "~0.7.0", 535 | "aws4": "^1.8.0", 536 | "caseless": "~0.12.0", 537 | "combined-stream": "~1.0.6", 538 | "extend": "~3.0.2", 539 | "forever-agent": "~0.6.1", 540 | "form-data": "~2.3.2", 541 | "har-validator": "~5.1.0", 542 | "http-signature": "~1.2.0", 543 | "is-typedarray": "~1.0.0", 544 | "isstream": "~0.1.2", 545 | "json-stringify-safe": "~5.0.1", 546 | "mime-types": "~2.1.19", 547 | "oauth-sign": "~0.9.0", 548 | "performance-now": "^2.1.0", 549 | "qs": "~6.5.2", 550 | "safe-buffer": "^5.1.2", 551 | "tough-cookie": "~2.4.3", 552 | "tunnel-agent": "^0.6.0", 553 | "uuid": "^3.3.2" 554 | } 555 | }, 556 | "safe-buffer": { 557 | "version": "5.1.2", 558 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", 559 | "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", 560 | "dev": true 561 | }, 562 | "safer-buffer": { 563 | "version": "2.1.2", 564 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", 565 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", 566 | "dev": true 567 | }, 568 | "sprintf-js": { 569 | "version": "1.0.3", 570 | "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", 571 | "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", 572 | "dev": true 573 | }, 574 | "sshpk": { 575 | "version": "1.16.1", 576 | "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", 577 | "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", 578 | "dev": true, 579 | "requires": { 580 | "asn1": "~0.2.3", 581 | "assert-plus": "^1.0.0", 582 | "bcrypt-pbkdf": "^1.0.0", 583 | "dashdash": "^1.12.0", 584 | "ecc-jsbn": "~0.1.1", 585 | "getpass": "^0.1.1", 586 | "jsbn": "~0.1.0", 587 | "safer-buffer": "^2.0.2", 588 | "tweetnacl": "~0.14.0" 589 | } 590 | }, 591 | "strip-json-comments": { 592 | "version": "2.0.1", 593 | "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", 594 | "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", 595 | "dev": true 596 | }, 597 | "tough-cookie": { 598 | "version": "2.4.3", 599 | "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz", 600 | "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==", 601 | "dev": true, 602 | "requires": { 603 | "psl": "^1.1.24", 604 | "punycode": "^1.4.1" 605 | }, 606 | "dependencies": { 607 | "punycode": { 608 | "version": "1.4.1", 609 | "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", 610 | "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", 611 | "dev": true 612 | } 613 | } 614 | }, 615 | "travis-ci": { 616 | "version": "2.2.0", 617 | "resolved": "https://registry.npmjs.org/travis-ci/-/travis-ci-2.2.0.tgz", 618 | "integrity": "sha512-6m+VoKD/va53D4O8I1SLtoLXKLHVUoEL2GksMKLUR0yUqYqco2kj5QB4gdBdFAMw3XL0VBozFsGw8jb6MrrIEQ==", 619 | "dev": true, 620 | "requires": { 621 | "github": "~0.1.10", 622 | "lodash": "~1.3.1", 623 | "request": "^2.87.0", 624 | "underscore.string": "~2.2.0rc" 625 | } 626 | }, 627 | "travis-lint": { 628 | "version": "1.0.0", 629 | "resolved": "https://registry.npmjs.org/travis-lint/-/travis-lint-1.0.0.tgz", 630 | "integrity": "sha1-rFLSNUWvuw4qySFYe45qdlFNtZI=", 631 | "dev": true, 632 | "requires": { 633 | "travis-ci": "^2.0.3" 634 | } 635 | }, 636 | "tunnel-agent": { 637 | "version": "0.6.0", 638 | "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", 639 | "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", 640 | "dev": true, 641 | "requires": { 642 | "safe-buffer": "^5.0.1" 643 | } 644 | }, 645 | "tweetnacl": { 646 | "version": "0.14.5", 647 | "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", 648 | "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", 649 | "dev": true 650 | }, 651 | "uc.micro": { 652 | "version": "1.0.6", 653 | "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz", 654 | "integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==", 655 | "dev": true 656 | }, 657 | "underscore.string": { 658 | "version": "2.2.1", 659 | "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-2.2.1.tgz", 660 | "integrity": "sha1-18D6KvXVoaZ/QlPa7pgTLnM/Dxk=", 661 | "dev": true 662 | }, 663 | "uri-js": { 664 | "version": "4.2.2", 665 | "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", 666 | "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", 667 | "dev": true, 668 | "requires": { 669 | "punycode": "^2.1.0" 670 | } 671 | }, 672 | "uuid": { 673 | "version": "3.3.2", 674 | "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", 675 | "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==", 676 | "dev": true 677 | }, 678 | "verror": { 679 | "version": "1.10.0", 680 | "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", 681 | "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", 682 | "dev": true, 683 | "requires": { 684 | "assert-plus": "^1.0.0", 685 | "core-util-is": "1.0.2", 686 | "extsprintf": "^1.2.0" 687 | } 688 | }, 689 | "wrappy": { 690 | "version": "1.0.2", 691 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 692 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", 693 | "dev": true 694 | } 695 | } 696 | } 697 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hooks4git", 3 | "version": "1.0.0", 4 | "description": "Node Tools Installation Helper", 5 | "main": "", 6 | "directories": { 7 | "test": "tests" 8 | }, 9 | "dependencies": {}, 10 | "devDependencies": { 11 | "markdownlint-cli": "^0.15.0", 12 | "travis-lint": "^1.0.0" 13 | }, 14 | "bugs": { 15 | "url": "https://github.com/lovato/hooks4git/issues" 16 | }, 17 | "homepage": "https://github.com/lovato/hooks4git" 18 | } 19 | -------------------------------------------------------------------------------- /requirements-dev.txt: -------------------------------------------------------------------------------- 1 | ################################################################################ 2 | # This requirements file has been automatically generated from `Pipfile` with 3 | # `pipenv-to-requirements` 4 | # 5 | # 6 | # This has been done to maintain backward compatibility with tools and services 7 | # that do not support `Pipfile` yet. 8 | # 9 | # Do NOT edit it directly, use `pipenv install [-d]` to modify `Pipfile` and 10 | # `Pipfile.lock` and then regenerate `requirements*.txt`. 11 | ################################################################################ 12 | 13 | appdirs==1.4.3 14 | atomicwrites==1.3.0 15 | attrs==19.1.0 16 | autopep8==1.4.4 17 | bandit==1.6.0 18 | black==19.3b0 19 | certifi==2019.3.9 20 | chardet==3.0.4 21 | click==7.0 22 | coverage==5.0a5 23 | coveralls==1.7.0 24 | docopt==0.6.2 25 | entrypoints==0.3 26 | flake8==3.7.7 27 | gitdb2==2.0.5 28 | gitpython==2.1.11 29 | idna==2.8 30 | mccabe==0.6.1 31 | mock==3.0.5 32 | more-itertools==7.0.0 33 | pbr==5.2.0 34 | pipenv-to-requirements==0.7.1 35 | pipenv==2018.11.26 36 | pluggy==0.11.0 37 | py==1.8.0 38 | pycodestyle==2.5.0 39 | pyflakes==2.1.1 40 | pytest-cov==2.7.1 41 | pytest==4.4.2 42 | pyyaml==5.1 43 | requests==2.21.0 44 | six==1.12.0 45 | smmap2==2.0.5 46 | stevedore==1.30.1 47 | toml==0.10.0 48 | urllib3==1.24.3 49 | virtualenv-clone==0.5.3 50 | virtualenv==16.5.0 51 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | ################################################################################ 2 | # This requirements file has been automatically generated from `Pipfile` with 3 | # `pipenv-to-requirements` 4 | # 5 | # 6 | # This has been done to maintain backward compatibility with tools and services 7 | # that do not support `Pipfile` yet. 8 | # 9 | # Do NOT edit it directly, use `pipenv install [-d]` to modify `Pipfile` and 10 | # `Pipfile.lock` and then regenerate `requirements*.txt`. 11 | ################################################################################ 12 | 13 | colorama==0.4.1 14 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from setuptools import setup, find_packages 4 | 5 | # from hooks4git.scripts import Post_install 6 | import codecs 7 | from os import path 8 | 9 | here = path.abspath(path.dirname(__file__)) 10 | with codecs.open(path.join(here, "README.md"), encoding="utf-8") as f: 11 | long_description = f.read() 12 | 13 | # Get the requirements from the requirements.txt file 14 | with codecs.open(path.join(here, "requirements.txt"), encoding="utf-8") as f: 15 | requirements = f.read() 16 | 17 | try: 18 | # Get the dev requirements from the requirements-dev.txt file 19 | with codecs.open(path.join(here, "requirements-dev.txt"), encoding="utf-8") as f: 20 | requirements_dev = f.read() 21 | except: # noqa 22 | # It is ok, this file only exists when developing this package, not when installing. 23 | requirements_dev = "" 24 | 25 | project_name = "hooks4git" 26 | __version__ = __import__(project_name).__version__ 27 | __author__ = __import__(project_name).__author__ 28 | __author_email__ = __import__(project_name).__author_email__ 29 | __author_username__ = __import__(project_name).__author_username__ 30 | __description__ = __import__(project_name).__description__ 31 | 32 | setup( 33 | name=project_name, 34 | version=__version__, 35 | author=__author__, 36 | author_email=__author_email__, 37 | download_url="https://github.com/{}/{}/tarball/{}".format(__author_username__, project_name, __version__), 38 | packages=find_packages(), 39 | include_package_data=True, 40 | license="MIT", 41 | url="https://github.com/lovato/hooks4git", 42 | description=__description__, 43 | long_description=long_description, 44 | long_description_content_type="text/markdown", 45 | install_requires=requirements, 46 | platforms=["any"], 47 | extras_require={"dev": [requirements_dev.split("\n")]}, 48 | entry_points={"console_scripts": ["hooks4git=hooks4git.app:main"]}, 49 | zip_safe=True, 50 | ) 51 | -------------------------------------------------------------------------------- /tests/test_console_divider.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from tests import BaseTestCase 3 | from hooks4git.console import Display 4 | from hooks4git.tools import get_dash 5 | 6 | 7 | class DividerTestCase(BaseTestCase): 8 | def test_divider(self): 9 | d = Display() 10 | dash = get_dash() 11 | response = d.divider() 12 | self.assertTrue(dash in response) 13 | self.assertTrue(len(response) == 91) 14 | -------------------------------------------------------------------------------- /tests/test_console_say.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from tests import BaseTestCase 3 | from hooks4git.console import Display 4 | 5 | 6 | class SayTestCase(BaseTestCase): 7 | def test_divider(self): 8 | d = Display() 9 | check = "CHECK" 10 | 11 | response = d.say("PASS", check) 12 | self.assertTrue(check in response) 13 | self.assertTrue(len(response) == 43) 14 | 15 | response = d.say("PASS ", check) 16 | self.assertTrue(check in response) 17 | self.assertTrue(len(response) == 43) 18 | 19 | response = d.say("FAIL", check) 20 | self.assertTrue(check in response) 21 | self.assertTrue(len(response) == 47) 22 | 23 | response = d.say("FAIL ", check) 24 | self.assertTrue(check in response) 25 | self.assertTrue(len(response) == 43) 26 | 27 | response = d.say("SOUT", check) 28 | self.assertTrue(check in response) 29 | self.assertTrue(len(response) == 32) 30 | 31 | response = d.say("SERR", check) 32 | self.assertTrue(check in response) 33 | self.assertTrue(len(response) == 32) 34 | 35 | response = d.say("INFO", check) 36 | self.assertTrue(check in response) 37 | self.assertTrue(len(response) == 38) 38 | 39 | response = d.say("WARN", check) 40 | self.assertTrue(check in response) 41 | self.assertTrue(len(response) == 38) 42 | 43 | response = d.say("STEP", check) 44 | self.assertTrue(check in response) 45 | self.assertTrue(len(response) == 38) 46 | 47 | response = d.say("STEPS", check) 48 | self.assertTrue(check in response) 49 | self.assertTrue(len(response) == 38) 50 | 51 | response = d.say("TIME", check) 52 | self.assertTrue(check in response) 53 | self.assertTrue(len(response) == 38) 54 | 55 | response = d.say("ERR!", check) 56 | self.assertTrue(check in response) 57 | self.assertTrue(len(response) == 38) 58 | 59 | response = d.say("TITLE", check) 60 | self.assertTrue(check in response) 61 | self.assertTrue(len(response) == 26) 62 | -------------------------------------------------------------------------------- /tests/test_hook_get_hooks_path.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from tests import BaseTestCase 3 | from hooks4git.hook import get_hooks_path 4 | 5 | 6 | class HooksFolderTestCase(BaseTestCase): 7 | def test_get_hooks_path_empty(self): 8 | path = get_hooks_path(None) 9 | self.assertTrue(path is None) 10 | 11 | def test_get_hooks_path_missing_folder(self): 12 | path = get_hooks_path("/tmp") 13 | self.assertTrue(path is None) 14 | 15 | def test_get_hooks_path_valid_folder(self): 16 | path = get_hooks_path(BaseTestCase.tmp_valid_git_path) 17 | self.assertTrue(path is not None) 18 | 19 | def test_get_hooks_path_invalid_folder(self): 20 | path = get_hooks_path(BaseTestCase.tmp_invalid_git_path) 21 | self.assertTrue(path is not None) 22 | -------------------------------------------------------------------------------- /tests/test_hook_hook_it.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from tests import BaseTestCase 3 | from hooks4git.hook import hook_it 4 | import os 5 | 6 | 7 | class HookHookItTestCase(BaseTestCase): 8 | def test_add_precommit_successfully(self): 9 | action = hook_it(BaseTestCase.tmp_valid_path) 10 | self.assertTrue(action) 11 | action = hook_it(BaseTestCase.tmp_valid_path) 12 | self.assertTrue(action) 13 | precommit_path = os.path.join(BaseTestCase.tmp_valid_git_path, "hooks/pre-commit") 14 | self.assertTrue(os.path.isfile(precommit_path)) # noqa 15 | -------------------------------------------------------------------------------- /tests/test_hook_report.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from tests import BaseTestCase 3 | from hooks4git import hook 4 | import time 5 | import datetime 6 | from hooks4git.console import Display 7 | 8 | 9 | class HookReportTestCase(BaseTestCase): 10 | display = None 11 | 12 | def test_report(self): 13 | self.display = Display(False) 14 | self._test(2, 1, "was") 15 | self._test(1, 3, "were") 16 | 17 | def _test(self, delay, steps, verb): 18 | hook.steps_executed = steps 19 | start_time = datetime.datetime.now() 20 | time.sleep(delay) 21 | line1, line2 = hook.report(start_time, self.display) 22 | self.assertEqual(line1, "STEPS| %s %s executed" % (hook.steps_executed, verb)) 23 | expected_line2 = "TIME | Execution took 0:00:%02d." % delay 24 | self.assertEqual(line2[:30], expected_line2) 25 | -------------------------------------------------------------------------------- /tests/test_tools_copy_file.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from tests import BaseTestCase 3 | from hooks4git.tools import copy_file 4 | import os 5 | 6 | 7 | class CopyFileTestCase(BaseTestCase): 8 | def test_copy_file_source_invalid(self): 9 | flag = copy_file( 10 | os.path.join(BaseTestCase.tmp_path, "invalid_file"), 11 | os.path.join(BaseTestCase.tmp_path, "invalid_file_copy"), 12 | ) # noqa 13 | self.assertTrue(flag is False) 14 | 15 | def test_copy_file_destination_invalid(self): 16 | flag = copy_file(os.path.join(BaseTestCase.tmp_path, "invalid_file"), "/invalid_file_copy") # noqa 17 | self.assertTrue(flag is False) 18 | 19 | def test_copy_file_both_valid(self): 20 | flag = copy_file( 21 | os.path.join(BaseTestCase.tmp_path, "valid_file"), os.path.join(BaseTestCase.tmp_path, "valid_file_copy") 22 | ) # noqa 23 | self.assertTrue(flag is True) 24 | -------------------------------------------------------------------------------- /tests/test_tools_get_platform.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from tests import BaseTestCase 3 | from hooks4git.tools import get_platform 4 | import mock 5 | 6 | 7 | class GetPlatformTestCase(BaseTestCase): 8 | @mock.patch("sys.platform", "FakeOS") 9 | def test_get_platform_invalid(self): 10 | platform = get_platform() 11 | self.assertTrue(platform == "FakeOS") 12 | 13 | @mock.patch("sys.platform", "linux") 14 | def test_get_platform_linux(self): 15 | platform = get_platform() 16 | self.assertTrue(platform == "Linux") 17 | 18 | @mock.patch("sys.platform", "linux1") 19 | def test_get_platform_linux1(self): 20 | platform = get_platform() 21 | self.assertTrue(platform == "Linux") 22 | 23 | @mock.patch("sys.platform", "linux2") 24 | def test_get_platform_linux2(self): 25 | platform = get_platform() 26 | self.assertTrue(platform == "Linux") 27 | 28 | @mock.patch("sys.platform", "darwin") 29 | def test_get_platform_darwin(self): 30 | platform = get_platform() 31 | self.assertTrue(platform == "Mac") 32 | 33 | @mock.patch("sys.platform", "win32") 34 | def test_get_platform_win32(self): 35 | platform = get_platform() 36 | self.assertTrue(platform == "Windows") 37 | 38 | @mock.patch("sys.platform", "win32") 39 | @mock.patch.dict("os.environ", {"MSYSTEM": "MINGW64"}) 40 | def test_get_platform_win32git(self): 41 | platform = get_platform() 42 | self.assertTrue(platform == "WindowsGitBash") 43 | -------------------------------------------------------------------------------- /tests/test_tools_os_call.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from tests import BaseTestCase 3 | from hooks4git.tools import os_call 4 | 5 | 6 | class OsCallTestCase(BaseTestCase): 7 | def test_os_call_invalid(self): 8 | response = os_call("/bin/eecho") 9 | self.assertTupleEqual(response, (127, "", "/bin/bash: /bin/eecho: No such file or directory\n")) 10 | 11 | def test_os_call_valid(self): 12 | response = os_call("/bin/echo success") 13 | self.assertTupleEqual(response, (0, "success\n", "")) 14 | -------------------------------------------------------------------------------- /tests/test_tools_usersite.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from tests import BaseTestCase 3 | from hooks4git import tools 4 | import sys 5 | 6 | 7 | class UserSiteTestCase(BaseTestCase): 8 | display = None 9 | 10 | def test_usersite(self): 11 | path = tools.add_usersitepackages_to_path("python") 12 | self.assertEqual(path, sys.path[0]) 13 | -------------------------------------------------------------------------------- /tests/tests.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from unittest import TestCase 3 | import os 4 | 5 | 6 | class BaseTestCase(TestCase): 7 | base_path = os.getcwd() 8 | tmp_path = os.path.join(base_path, "tmp") 9 | tmp_valid_path = os.path.join(base_path, "tmp/valid") 10 | tmp_invalid_path = os.path.join(base_path, "tmp/invalid") 11 | tmp_valid_git_path = os.path.join(tmp_valid_path, ".git") 12 | tmp_invalid_git_path = os.path.join(tmp_invalid_path, ".git") 13 | 14 | def setUp(self): 15 | # create test temporary directory 16 | os.system("mkdir -p tmp/valid && cd tmp/valid && git init") # noqa 17 | os.system("mkdir -p tmp/invalid && cd tmp/invalid && git init && cd .git && rm -rf hooks") # noqa 18 | os.system("touch tmp/valid_file") 19 | 20 | def tearDown(self): 21 | # remove all test files 22 | os.system("rm -r tmp/") # noqa 23 | --------------------------------------------------------------------------------