Skip to content

Commit

Permalink
Merge pull request #42 from mangolang/commit_chks
Browse files Browse the repository at this point in the history
Git commit checks
  • Loading branch information
mverleg authored May 12, 2018
2 parents aff81b9 + 4e82b76 commit 0c93724
Show file tree
Hide file tree
Showing 14 changed files with 520 additions and 86 deletions.
6 changes: 2 additions & 4 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
language: rust
rust:
- beta
- nightly
before_script:
- rustup component add rustfmt-preview
sudo: false
cache: cargo
script:
- # cargo fmt --verbose --all -- --write-mode=diff
- cargo build --verbose --all
- cargo test --verbose --all
- cargo test --all
Empty file added dev/hook_utils/__init__.py
Empty file.
89 changes: 89 additions & 0 deletions dev/hook_utils/check_version_tag.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
#!/usr/bin/env python3
# -*- coding: UTF-8 -*-

"""
This git hooks makes sure a tag is created each time the version is increased.
This version works with version for `Cargo.toml`, `setup.py` and `settings.gradle`, but can be adapted.
https://gist.github.com/mverleg/9988b44d57b63b0eef40dd9ce7b48e45
"""
import traceback
from re import findall
from sys import stdout
from os.path import join

from util_cmd import git, get_root


def get_existing_versions():
"""
Get all versions for which there is a tag.
"""
return set(tag.strip().lstrip('v') for tag in
git('tag --list --format "%(refname:short)"').splitlines())


def get_setup_py_version():
try:
with open(join(get_root(), 'setup.py'), 'r') as fh:
res = findall(r'\n[^#\n]*version\s*=\s*[\'"]([\w.-_]+)[\'"]', fh.read())
assert len(res) != 0, 'Did not find version inside setup.py'
assert len(res) < 2, 'Found multiple versions inside setup.py'
return res[0].strip().lstrip('v')
except IOError:
return None


def get_settings_gradle_version():
try:
with open(join(get_root(), 'settings.gradle'), 'r') as fh:
""" Only single-quotes supported, because double quotes may contain variables. """
res = findall(r'\n\s*version\s*=\s*\'([\w.-_]+)\'', fh.read())
assert len(res) != 0, 'Did not find version inside settings.gradle'
assert len(res) < 2, 'Found multiple versions inside settings.gradle'
return res[0].strip().lstrip('v')
except IOError:
return None


def get_cargo_toml_version():
pth = join(get_root(), 'Cargo.toml')
try:
with open(pth, 'r') as fh:
res = findall(r'\n[^#\n]*version\s*=\s*[\'"]([\w.-_]+)[\'"]', fh.read())
assert len(res) != 0, 'Did not find version inside {:s}'.format(pth)
assert len(res) < 2, 'Found multiple versions inside {:s}'.format(pth)
return res[0].strip().lstrip('v')
except IOError:
return None


def get_current_version():
"""
Get the current version of the project, according to package file.
"""
return get_cargo_toml_version() or get_setup_py_version() or get_settings_gradle_version() or None


def version_tag():
"""
Create a tag for the current version if there isn't one yet.
"""
version = get_current_version()
if version is None:
""" No version found. """
return
versions = get_existing_versions()
if version in versions:
""" Version already tagged. """
return
stdout.write('\nCREATING TAG v{0:} (use `git tag --delete v{0:}` if not desired)\n\n'.format(version))
git('tag v{0:}'.format(version))


try:
exit(version_tag())
except Exception as err:
traceback.print_exc()
exit(1)
32 changes: 32 additions & 0 deletions dev/hook_utils/run_on_staged.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
"""
Run a shell command on the staged changes only.
"""

from sys import stderr, argv
from time import time
from util_cmd import git, run


def run_on_staged(cmd):
"""
Temporarily remove all unstaged changed, so that any checks are run on what is actually to be committed.
Note that `diff` cannot include untracked files, and `stash` has trouble retrieving by name, but was chosen anyway.
"""
name = 'before_hook_check_{0:d}s'.format(int(time()))
git("-c commit.gpgsign=false stash save --include-untracked --keep-index '{0:s}'".format(name))
try:
try:
run(cmd, print=True)
except Exception as err:
stderr.write(err)
return 1
finally:
# git("stash apply 'stash^{{/{0:s}}}'".format(name))
git("stash pop")
return 0 # ok


if __name__ == '__main__':
assert len(argv) >= 2
exit(run_on_staged(argv[1]))
37 changes: 37 additions & 0 deletions dev/hook_utils/util_cmd.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
from subprocess import Popen, PIPE
# noinspection PyUnresolvedReferences
from sys import stdout, stderr

from os.path import isdir


def run(cmd, *, allow_err=False, print=False):
"""
Run a shell command and return the output.
"""
if print:
stdout.write('{0:s}\n'.format(cmd))
out, err = Popen('{0:s}'.format(cmd), shell=True, stdout=PIPE, stderr=PIPE).communicate()
if allow_err:
return (out + '\n' + err).decode('utf-8')
if err.strip():
msg = 'an error occurred while running:\n{0:s}\n{1:s}'.format(cmd, err.decode('utf-8'))
raise AssertionError(msg)
return out.decode('utf-8')


def git(cmd, *, allow_err=False):
"""
Run a git command and return the output.
"""
return run('git {0:s}'.format(cmd), allow_err=allow_err)


def get_root():
"""
Get the root path of the project (cached).
"""
if not getattr(get_root, 'root_cache', None):
get_root.root_cache = git("rev-parse --show-toplevel").strip()
assert isdir(get_root.root_cache), 'not found: {:s}'.format(get_root.root_cache)
return get_root.root_cache
11 changes: 11 additions & 0 deletions dev/hooks/commit-msg
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#!/usr/bin/env bash

# Make sure to fail if any check fails
set -e
set -o pipefail

# Get the hook utils directory
util_dir="$(dirname "$(realpath "${BASH_SOURCE[0]}")")/utils"

# Check that the formatting is correct
PYTHONPATH="$util_dir":$PYTHONPATH python3 "$util_dir/message_validation.py" "$1"
8 changes: 8 additions & 0 deletions dev/hooks/note.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@

To enable, on git 2.9+, use::

chmod u+x dev/hooks/*
git config core.hooksPath dev/hooks

On older versions, copy or symlink the hooks to `.git/hooks`

91 changes: 9 additions & 82 deletions dev/hooks/post-commit
100644 → 100755
Original file line number Diff line number Diff line change
@@ -1,86 +1,13 @@
#!/usr/bin/env python3
# -*- coding: UTF-8 -*-
#!/usr/bin/env bash

"""
This git hooks makes sure a tag is created each time the version is increased.
This version only works with version from `setup.py` and `settings.gradle`, but can be adapted.
To enable it on git 2.9+, use::
chmod u+x dev/hooks/*
git config core.hooksPath dev/hooks
On older versions, copy or symlink the hooks to `.git/hooks`
https://gist.github.com/mverleg/9988b44d57b63b0eef40dd9ce7b48e45
"""
exit 0; # TODO

from re import findall
from sys import stderr
from subprocess import Popen, PIPE
from os.path import exists
# Make sure to fail if any check fails
set -e
set -o pipefail

# Get the hook utils directory
util_dir="$(dirname "$(realpath "${BASH_SOURCE[0]}")")/utils"

def git(cmd):
"""
Run a git command and return the output.
"""
out, err = Popen('git {0:s}'.format(cmd), shell=True, stdout=PIPE, stderr=PIPE).communicate()
assert not err.strip()
return out.decode('utf-8')


def get_existing_versions():
"""
Get all versions for which there is a tag.
"""
return set(tag.strip().lstrip('v') for tag in
git('tag --list --format "%(refname:short)"').splitlines())


def get_setup_py_version():
if exists('setup.py'):
with open('setup.py', 'r') as fh:
res = findall(r'\n[^#\n]*version\s*=[\'"]([\w.-_]+)[\'"]', fh.read())
assert len(res) != 0, 'Did not find version inside setup.py'
assert len(res) < 2, 'Found multiple versions inside setup.py'
return res[0].strip().lstrip('v')
return None


def get_settings_gradle_version():
if exists('settings.gradle'):
with open('settings.gradle', 'r') as fh:
""" Only single-quotes supported, because double quotes may contain variables. """
res = findall(r'\n\s*version\s*=\s*\'([\w.-_]+)\'', fh.read())
assert len(res) != 0, 'Did not find version inside settings.gradle'
assert len(res) < 2, 'Found multiple versions inside settings.gradle'
return res[0].strip().lstrip('v')
return None


def get_current_version():
"""
Get the current version of the project, according to package file.
PWD is the root of the git project.
"""
return get_setup_py_version() or get_settings_gradle_version() or None


def version_tag():
"""
Create a tag for the current version if there isn't one yet.
"""
version = get_current_version()
if version is None:
""" No version found. """
return
versions = get_existing_versions()
if version in versions:
""" Version already tagged. """
return
print('CREATING TAG {0:} (use `git tag --delete {0:}` if not desired)'.format(version))
git('tag v{0:}'.format(version))


try:
exit(version_tag())
except Exception as err:
stderr.write(err)
exit(1)
# Check that a tag was created for this version
PYTHONPATH="$util_dir":$PYTHONPATH python3 "$util_dir/check_version_tag.py"
11 changes: 11 additions & 0 deletions dev/hooks/pre-commit
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#!/usr/bin/env bash

# Make sure to fail if any check fails
set -e
set -o pipefail

# Get the hook utils directory
util_dir="$(dirname "$(realpath "${BASH_SOURCE[0]}")")/utils"

# Check that the formatting is correct
PYTHONPATH="$util_dir":$PYTHONPATH python3 "$util_dir/run_on_staged.py" 'cargo +nightly fmt --verbose --all -- --write-mode=diff' 'cargo test --all'
Empty file added dev/hooks/utils/__init__.py
Empty file.
89 changes: 89 additions & 0 deletions dev/hooks/utils/check_version_tag.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
#!/usr/bin/env python3
# -*- coding: UTF-8 -*-

"""
This git hooks makes sure a tag is created each time the version is increased.
This version works with version for `Cargo.toml`, `setup.py` and `settings.gradle`, but can be adapted.
https://gist.github.com/mverleg/9988b44d57b63b0eef40dd9ce7b48e45
"""
import traceback
from re import findall
from sys import stdout
from os.path import join

from util_cmd import git, get_root


def get_existing_versions():
"""
Get all versions for which there is a tag.
"""
return set(tag.strip().lstrip('v') for tag in
git('tag --list --format "%(refname:short)"').splitlines())


def get_setup_py_version():
try:
with open(join(get_root(), 'setup.py'), 'r') as fh:
res = findall(r'\n[^#\n]*version\s*=\s*[\'"]([\w.-_]+)[\'"]', fh.read())
assert len(res) != 0, 'Did not find version inside setup.py'
assert len(res) < 2, 'Found multiple versions inside setup.py'
return res[0].strip().lstrip('v')
except IOError:
return None


def get_settings_gradle_version():
try:
with open(join(get_root(), 'settings.gradle'), 'r') as fh:
""" Only single-quotes supported, because double quotes may contain variables. """
res = findall(r'\n\s*version\s*=\s*\'([\w.-_]+)\'', fh.read())
assert len(res) != 0, 'Did not find version inside settings.gradle'
assert len(res) < 2, 'Found multiple versions inside settings.gradle'
return res[0].strip().lstrip('v')
except IOError:
return None


def get_cargo_toml_version():
pth = join(get_root(), 'Cargo.toml')
try:
with open(pth, 'r') as fh:
res = findall(r'\n[^#\n]*version\s*=\s*[\'"]([\w.-_]+)[\'"]', fh.read())
assert len(res) != 0, 'Did not find version inside {:s}'.format(pth)
assert len(res) < 2, 'Found multiple versions inside {:s}'.format(pth)
return res[0].strip().lstrip('v')
except IOError:
return None


def get_current_version():
"""
Get the current version of the project, according to package file.
"""
return get_cargo_toml_version() or get_setup_py_version() or get_settings_gradle_version() or None


def version_tag():
"""
Create a tag for the current version if there isn't one yet.
"""
version = get_current_version()
if version is None:
""" No version found. """
return
versions = get_existing_versions()
if version in versions:
""" Version already tagged. """
return
stdout.write('\nCREATING TAG v{0:} (use `git tag --delete v{0:}` if not desired)\n\n'.format(version))
git('tag v{0:}'.format(version))


try:
exit(version_tag())
except Exception as err:
traceback.print_exc()
exit(1)
Loading

0 comments on commit 0c93724

Please sign in to comment.