All Projects → greenbone → Autohooks

greenbone / Autohooks

Licence: gpl-3.0
Library for managing git hooks

Programming Languages

python
139335 projects - #7 most used programming language
python3
1442 projects

Projects that are alternatives of or similar to Autohooks

Lint Action
✨ GitHub Action for detecting and auto-fixing lint errors
Stars: ✭ 161 (+27.78%)
Mutual labels:  linting, formatting
Golite
Add essential language support for the Go language to Sublime Text 3.
Stars: ✭ 14 (-88.89%)
Mutual labels:  linting, formatting
dockerfile-utils
A library and command line interface for formatting and linting Dockerfiles.
Stars: ✭ 17 (-86.51%)
Mutual labels:  linting, formatting
Clean State
🐻 A pure and compact state manager, using React-hooks native implementation, automatically connect the module organization architecture. 🍋
Stars: ✭ 107 (-15.08%)
Mutual labels:  hooks
Use Conditional Effect
React.useEffect, except you can pass a comparison function.
Stars: ✭ 111 (-11.9%)
Mutual labels:  hooks
Hooks
Async middleware for JavaScript and TypeScript
Stars: ✭ 117 (-7.14%)
Mutual labels:  hooks
Ansible Lint Action
GitHub Action for running ansible-lint as part of your workflows! [ https://github.com/marketplace/actions/ansible-lint ]
Stars: ✭ 124 (-1.59%)
Mutual labels:  linting
Rx React Container
Use RxJS in React components, via HOC or Hook
Stars: ✭ 105 (-16.67%)
Mutual labels:  hooks
Methodhook
hook java methods
Stars: ✭ 122 (-3.17%)
Mutual labels:  hooks
Sound
🔊 A Vue composable for playing sound effects
Stars: ✭ 116 (-7.94%)
Mutual labels:  hooks
Mobify Code Style
📚 Mobify's coding style and standards!
Stars: ✭ 115 (-8.73%)
Mutual labels:  linting
Lefthook
Fast and powerful Git hooks manager for any type of projects.
Stars: ✭ 1,848 (+1366.67%)
Mutual labels:  hooks
Biblatex Check
A python script for checking BibLatex .bib files for common referencing mistakes!
Stars: ✭ 118 (-6.35%)
Mutual labels:  linting
Hooked Form
Performant 2KB React library to manage your forms
Stars: ✭ 110 (-12.7%)
Mutual labels:  hooks
Editorconfig Netbeans
A NetBeans IDE plugin supporting the EditorConfig standard. ⛺
Stars: ✭ 123 (-2.38%)
Mutual labels:  formatting
Portfolio
💼 My personal portfolio built with React and three.js.
Stars: ✭ 106 (-15.87%)
Mutual labels:  hooks
Wshook
Easily intercept and modify WebSocket requests and message events.
Stars: ✭ 121 (-3.97%)
Mutual labels:  hooks
React Tensorflow
Tensorflow hooks for React.js
Stars: ✭ 115 (-8.73%)
Mutual labels:  hooks
Graphql Hooks
🎣 Minimal hooks-first GraphQL client
Stars: ✭ 1,610 (+1177.78%)
Mutual labels:  hooks
Hooks
A high-quality & reliable React Hooks library.
Stars: ✭ 7,841 (+6123.02%)
Mutual labels:  hooks

Greenbone Logo

Autohooks

PyPI release

Library for managing and writing git hooks in Python.

Looking for automatic formatting or linting, e.g., with black and pylint, while creating a git commit using a pure Python implementation? Welcome to autohooks!

Why?

Several outstanding libraries for managing and executing git hooks exist already. To name a few: husky, lint-staged, precise-commits or pre-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.

Solution

autohooks is a pure python library that installs a minimal executable git hook. It allows the decision of how to maintain the hook dependencies by supporting different modes.

Requirements

Python 3.7+ is required for autohooks.

Modes

Currently three modes for using autohooks are supported:

  • pythonpath
  • pipenv
  • poetry

These modes handle how autohooks, the plugins and their dependencies are loaded during git hook execution.

If no mode is specified in the pyproject.toml config file and no mode is set during activation, autohooks will use the pythonpath mode by default.

poetry or pipenv modes leverage the /usr/bin/env command using the --split-string (-S) option. If autohooks detects that it is running on an OS where /usr/bin/env is yet to support split_strings (notably ubuntu < 19.x), autohooks will automatically change to an internally chosen poetry_multiline/pipenv_mutliline mode. The 'multiline' modes should not be user-configured options; setting your project to use poetry or pipenvallows team members the greatest latitude to use an OS of their choice yet leverage the sane /usr/bin/env --split-string if possible. Though poetry_multiline would generally work for all, it is very confusing sorcery. (Multiline shebang explained)

Pythonpath Mode

In the pythonpath mode, the user has to install autohooks, the desired plugins and their dependencies into the PYTHONPATH manually.

This can be achieved by running pip install --user autohooks ... to put them into the installation directory of the current user or with pip install authooks ... for a system wide installation.

Alternatively, a virtual environment could be used separating the installation from the global and user wide Python packages.

It is also possible to use pipenv for 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 the pythonpath mode and switching to a virtual environment.

Pipenv Mode

In the pipenv mode pipenv is 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 the pythonpath mode the activation of the virtual environment provided by pipenv is done automatically in the background.

Poetry Mode

Like with the pipenv mode, it is possible to run autohooks in a dedicated environment controlled by poetry. By using the poetry mode the virtual environment will be activated automatically in the background when executing the autohooks based git commit hook.

Using the poetry mode is highly recommended.

Installing autohooks

Four steps are necessary for installing autohooks:

  1. Choosing an autohooks mode
  2. Installing the autohooks python package into the current environment
  3. Configuring plugins to be run
  4. Activating the git hooks

1. Choosing an autohooks Mode

For its configuration, autohooks uses the pyproject.toml file specified in PEP518. Adding a [tool.autohooks] section allows to specify the desired autohooks mode and to set python modules to be run as autohooks plugins.

The mode can be set by adding a mode = line to the pyproject.toml file. Current possible options are "pythonpath", "pipenv" and "poetry" (see autohooks mode). If the mode setting is missing, the pythonpath mode is used.

Example pyproject.toml:

[tool.autohooks]
mode = "pipenv"

2. Installing the autohooks Python Package into the Current Environment

Using poetry is highly recommended for installing the autohooks python package.

To install autohooks as a development dependency run

poetry add --dev autohooks

Alternatively, autohooks can be installed directly from GitHub by running

poetry add --dev git+https://github.com/greenbone/autohooks

3. Configuring Plugins to Be Run

To actually run an action on git hooks, autohooks plugins have to be installed and configured, e.g., to install python linting via pylint run

poetry add --dev autohooks-plugin-pylint

Afterwards, the pylint plugin can be configured to run as a pre-commit git hook by adding the autohooks-plugins-pylint python module name to the pre-commit setting in the [tool.autohooks] section in the pyproject.toml file.

Example pyproject.toml:

[tool.autohooks]
mode = "pipenv"
pre-commit = ["autohooks.plugins.pylint"]

4. Activating the Git Hooks

Because installing and activating git hooks automatically isn't reliable (with using source distributions and different versions of pip) and even impossible (with using wheels) the hooks need to be activated manually once in each installation.

To activate the git hooks run

poetry run autohooks activate

Calling activate also allows for overriding the mode defined in the pyproject.toml settings for testing purposes.

Example:

autohooks activate --mode pipenv

Please 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 the pyproject.toml file.

The activation can always be verified by running autohooks check.

Plugins

  • Python code formatting via black

  • Python code formatting via autopep8

  • Python code linting via pylint

  • Python import sorting via isort

Howto: Writing a Plugin

Plugins need to be available in the Python import path. The easiest way to achieve this is uploading a plugin to PyPI and installing it via pip or pipenv.

Alternatively, a plugin can also be put into a .autohooks directory in the root directory of the git repository where the hooks should be executed.

An autohooks plugin is a Python module which provides a precommit function. The function must accept arbitrary keywords because the keywords are likely to change in future. Therefore using **kwargs is highly recommended. Currently only a config keyword argument is passed to the precommit function.

Example:

def precommit(**kwargs):
    config = kwargs.get('config')

The config can be used to receive settings from the pyproject.toml file, e.g.,

[tool.autohooks.plugins.foo]
bar = 2

can be received with

def precommit(**kwargs):
    config = kwargs.get('config')
    default_value = 1
    setting = config
      .get('tool', 'autohooks', 'plugins', 'foo')
      .get_value('bar', default_value)
    return 0

With autohooks it is possible to write all kinds of plugins. Most common are plugins for linting and formatting.

Linting Plugin

Usually the standard call sequence for a linting plugin is the following:

  1. get list of staged files
  2. filter list of files for a specific file type
  3. stash unrelated changes
  4. apply checks on filtered list of files by calling some external tool
  5. raise exception if something did go wrong
  6. return 1 if check was not successful
  7. stage changes made by the tool
  8. unstash unrelated changes
  9. return 0

Example plugin:

import subprocess

from autohooks.api import ok, fail
from autohooks.api.git import get_staged_status, stash_unstaged_changes
from autohooks.api.path import match

DEFAULT_INCLUDE = ('*.ext')


def get_include(config)
    if not config:
        return DEFAULT_INCLUDE

    config = config.get('tool', 'autohooks', 'plugins', 'foo')
    return config.get_value('include', DEFAULT_INCUDE)


def precommit(**kwargs):
    config = kwargs.get('config')
    include = get_include(config)

    files = [f for f in get_staged_status() if match(f.path, include)]

    if not files:
      # not files to lint
      return 0

    with stash_unstaged_changes(files):
        const failed = False
        for file in files:
            status = subprocess.call(['foolinter', str(file)])
            if status:
                fail('Could not validate {}'.format(str(file)))
                failed = True
            else:
                ok('Validated {}'.format(str(file)))

        return 1 if failed else 0

Formatting Plugin

Usually the standard call sequence for a formatting plugin is the following:

  1. get list of staged files
  2. filter list of files for a specific file type
  3. stash unrelated changes
  4. apply formatting on filtered list of files by calling some external tool
  5. raise exception if something did go wrong
  6. stage changes made by the tool
  7. unstash unrelated changes
  8. return 0

Example plugin:

import subprocess

from autohooks.api import ok, error
from autohooks.api.git import (
    get_staged_status,
    stage_files_from_status_list,
    stash_unstaged_changes,
)
from autohooks.api.path import match

DEFAULT_INCLUDE = ('*.ext')


def get_include(config)
    if not config:
        return DEFAULT_INCLUDE

    config = config.get('tool', 'autohooks', 'plugins', 'bar')
    return config.get_value('include', DEFAULT_INCUDE)


def precommit(**kwargs):
    config = kwargs.get('config')
    include = get_include(config)

    files = [f for f in get_staged_status() if match(f.path, include)]

    if not files:
      # not files to format
      return 0

    with stash_unstaged_changes(files):
        for file in files:
            # run formatter and raise exception if it fails
            subprocess.run(['barformatter', str(file)], check=True)
            ok('Formatted {}'.format(str(file)))

        return 0

Maintainer

This project is maintained by Greenbone Networks GmbH.

Contributing

Your contributions are highly appreciated. Please create a pull request on GitHub. Bigger changes need to be discussed with the development team via the issues section at GitHub first.

License

Copyright (C) 2019 Greenbone Networks GmbH

Licensed under the GNU General Public License v3.0 or later.

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].