Package: autohooks-gk-fork-wip
AutohooksLibrary for managing and writinggit hooksin Python.Looking for automatic formatting or linting, e.g., withblackandpylint,
while creating a git commit using a pure Python implementation?
Welcome toautohooks!Why?SolutionRequirementsModesPythonpath ModePipenv ModePoetry ModeInstalling autohooks1. Choosing an autohooks Mode2. Installing the autohooks Python Package into the Current Environment3. Configuring Plugins to Be Run4. Activating the Git HooksPluginsHowto: Writing a PluginLinting PluginFormatting PluginMaintainerContributingLicenseWhy?Several outstanding libraries for managing and executing git hooks exist already.
To name a few:husky,lint-staged,precise-commitsorpre-commit.However, they either need another interpreter besides python (like husky) or are
too ambiguous (like pre-commit). pre-commit is written in python but has support
hooks written in all kind of languages. Additionally, it maintains the dependencies by
itself and does not install them in the current environment.Solutionautohooks is a pure python library that installs a minimalexecutable git hook.
It allows the decision of how to maintain the hook dependencies
by supporting different modes.Requirementsautohooks supports Python 3.5+. For development Python 3.6+ is required.ModesCurrently three modes for using autohooks are supported:pythonpathpipenvpoetryThese modes handle how autohooks, the plugins and their dependencies are loaded
during git hook execution.If no mode is specified in thepyproject.tomlconfig fileand no mode is set duringactivation, autohooks
will use thepythonpath modeby default.poetryorpipenvmodes leverage the/usr/bin/envcommand using the--split-string(-S) option. Ifautohooksdetects that it is
running on an OS where/usr/bin/envis yet to supportsplit_strings(notably ubuntu < 19.x),autohookswill automatically change to an
internally chosenpoetry_multiline/pipenv_mutlilinemode. The
'multiline' modesshould notbe user-configured options; setting your
project to usepoetryorpipenvallows team members the greatest
latitude to use an OS of their choice yet leverage the sane/usr/bin/env --split-stringif possible. Thoughpoetry_multilinewould generally work for all, it is very confusing sorcery.
(Multiline shebang explained)Pythonpath ModeIn thepythonpathmode, the user has to install autohooks, the desired
plugins and their dependencies into thePYTHONPATHmanually.This can be achieved by runningpip install --user autohooks ...to put them
into the installation directory of thecurrent useror withpip install authooks ...for a system wide installation.Alternatively, avirtual environmentcould be used separating the installation from the global and user wide
Python packages.It is also possible to usepipenvfor managing the virtual
environment but activating the environment has to be done manually.Therefore it is even possible to run different versions of autohooks by
using thepythonpathmode and switching to a virtual environment.Pipenv ModeIn thepipenvmodepipenvis used to run autohooks in a dedicated virtual
environment. Pipenv uses a lock file to install exact versions. Therefore the
installation is deterministic and reliable between different developer setups.
In contrast to thepythonpathmode the activation of the virtual environment
provided bypipenvis done automatically in the background.Poetry ModeLike with thepipenv mode, it is possible to run autohooks in a
dedicated environment controlled bypoetry. By using thepoetrymode the
virtual environment will be activated automatically in the background when
executing the autohooks based git commit hook.Using thepoetrymode is highly recommended.Installing autohooksFour steps are necessary for installing autohooks:Choosing an autohooks modeInstalling the autohooks python package into the current environmentConfiguring plugins to be runActivating thegit hooks1. Choosing an autohooks ModeFor its configuration, autohooks uses thepyproject.tomlfile specified inPEP518.
Adding a[tool.autohooks]section allows to specify the desiredautohooks modeand to set python modules to be run asautohooks plugins.The mode can be set by adding amode =line to thepyproject.tomlfile.
Current possible options are"pythonpath","pipenv"and"poetry"(seeautohooks mode). If the mode setting is missing, thepythonpathmode is used.Examplepyproject.toml:[tool.autohooks]mode="pipenv"2. Installing the autohooks Python Package into the Current EnvironmentUsingpoetryis highly recommended for installing the autohooks python package.To install autohooks as a development dependency runpoetryadd--devautohooksAlternatively, autohooks can be installed directly from GitHub by runningpoetryadd--devgit+https://github.com/greenbone/autohooks3. Configuring Plugins to Be RunTo actually run an action on git hooks,autohooks pluginshave to be
installed and configured, e.g., to install python linting via pylint runpoetryadd--devautohooks-plugin-pylintAfterwards, the pylint plugin can be configured to run as a pre-commit git hook
by adding the autohooks-plugins-pylint python module name to thepre-commitsetting in the[tool.autohooks]section in thepyproject.tomlfile.Examplepyproject.toml:[tool.autohooks]mode="pipenv"pre-commit=["autohooks.plugins.pylint"]4. Activating the Git HooksBecause installing and activating git hooks automatically isn't reliable (with
using source distributions and different versions of pip) and even impossible
(with usingwheels) the hooks need
to be activated manually once in each installation.To activate the git hooks runpoetryrunautohooksactivateCallingactivatealso allows for overriding themodedefined in thepyproject.tomlsettings for testing purposes.Example:autohooksactivate--modepipenvPlease keep in mind that autohooks will always issue a warning if the mode used
in the git hooks is different from the configured mode in thepyproject.tomlfile.The activation can always be verified by runningautohooks check.PluginsPython code formatting viablackPython code formatting viaautopep8Python code linting viapylintPython import sorting viaisortHowto: Writing a PluginPlugins need to be available in thePython import path. The
easiest way to achieve this is uploading a plugin toPyPIand installing it viapiporpipenv.Alternatively, a plugin can also be put into a.autohooksdirectory in the root
directory of the git repository where the hooks should be executed.An autohooks plugin is a Python module which provides aprecommitfunction.
The function must accept arbitrary keywords because the keywords are likely to
change in future. Therefore using**kwargsis highly recommended.
Currently only aconfigkeyword argument is passed to the precommit function.Example:defprecommit(**kwargs):config=kwargs.get('config')The config can be used to receive settings from thepyproject.tomlfile, e.g.,[tool.autohooks.plugins.foo]bar=2can be received withdefprecommit(**kwargs):config=kwargs.get('config')default_value=1setting=config.get('tool','autohooks','plugins','foo').get_value('bar',default_value)return0With autohooks it is possible to write all kinds of plugins. Most common are
plugins for linting and formatting.Linting PluginUsually the standard call sequence for a linting plugin is the following:get list of staged filesfilter list of files for a specific file typestash unrelated changesapply checks on filtered list of files by calling some external toolraise exception if something did go wrongreturn 1 if check was not successfulstage changes made by the toolunstash unrelated changesreturn 0Example plugin:importsubprocessfromautohooks.apiimportok,failfromautohooks.api.gitimportget_staged_status,stash_unstaged_changesfromautohooks.api.pathimportmatchDEFAULT_INCLUDE=('*.ext')defget_include(config)ifnotconfig:returnDEFAULT_INCLUDEconfig=config.get('tool','autohooks','plugins','foo')returnconfig.get_value('include',DEFAULT_INCUDE)defprecommit(**kwargs):config=kwargs.get('config')include=get_include(config)files=[fforfinget_staged_status()ifmatch(f.path,include)]ifnotfiles:# not files to lintreturn0withstash_unstaged_changes(files):constfailed=Falseforfileinfiles:status=subprocess.call(['foolinter',str(file)])ifstatus:fail('Could not validate{}'.format(str(file)))failed=Trueelse:ok('Validated{}'.format(str(file)))return1iffailedelse0Formatting PluginUsually the standard call sequence for a formatting plugin is the following:get list of staged filesfilter list of files for a specific file typestash unrelated changesapply formatting on filtered list of files by calling some external toolraise exception if something did go wrongstage changes made by the toolunstash unrelated changesreturn 0Example plugin:importsubprocessfromautohooks.apiimportok,errorfromautohooks.api.gitimport(get_staged_status,stage_files_from_status_list,stash_unstaged_changes,)fromautohooks.api.pathimportmatchDEFAULT_INCLUDE=('*.ext')defget_include(config)ifnotconfig:returnDEFAULT_INCLUDEconfig=config.get('tool','autohooks','plugins','bar')returnconfig.get_value('include',DEFAULT_INCUDE)defprecommit(**kwargs):config=kwargs.get('config')include=get_include(config)files=[fforfinget_staged_status()ifmatch(f.path,include)]ifnotfiles:# not files to formatreturn0withstash_unstaged_changes(files):forfileinfiles:# run formatter and raise exception if it failssubprocess.run(['barformatter',str(file)],check=True)ok('Formatted{}'.format(str(file)))return0MaintainerThis project is maintained byGreenbone Networks GmbH.ContributingYour contributions are highly appreciated. Pleasecreate a pull requeston GitHub. Bigger changes need to be discussed with the development team via theissues section at GitHubfirst.LicenseCopyright (C) 2019Greenbone Networks GmbHLicensed under theGNU General Public License v3.0 or later.