Skip to content

Instantly share code, notes, and snippets.

@rotu
Last active May 26, 2020 23:33
Show Gist options
  • Save rotu/385645e9b35a395f7b1b6b80413d13d5 to your computer and use it in GitHub Desktop.
Save rotu/385645e9b35a395f7b1b6b80413d13d5 to your computer and use it in GitHub Desktop.
ros2 prepare-commit-msg
#!/usr/bin/env python3
# shared commit message trigger
# this file should exist in /opt/ros/_common/githooks and should be executable
# install everywhere (respected by GitKraken since 7):
# vcs custom --git --args config --local core.hooksPath "/opt/ros/_common/githooks/"
# uninstall everywhere:
# vcs custom --git --args config --local --unset core.hooksPath
import logging
import sys
import subprocess
import re
from pathlib import Path
import os
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(Path(__file__).name)
SIGNOFF = True
REFORMAT = True
def log_and_run(cmd, **kwargs):
kwargs.setdefault('text', True)
kwargs.setdefault('capture_output', True)
kwargs.setdefault('check', True)
logger.debug(f'Running subprocess: {cmd}')
c = subprocess.run(cmd, **kwargs)
logger.debug(c.stderr)
logger.debug(c.stdout)
return c
def main(msg_file, msg_source=None, msg_source_ref=None):
source = Path.cwd()
logger.info(f'CWD = {source}')
if SIGNOFF:
contributing = Path(source, 'CONTRIBUTING.md')
trailer_name = 'Signed-off-by'
if not contributing.is_file():
logger.info(f'No contributing file at {contributing}. Not adding signoff')
elif trailer_name not in contributing.read_text():
logger.info('Contributing file does not mention signoff. Not adding signoff')
else:
logger.info('Adding signoff...')
name = log_and_run(['git', 'config', 'user.name']).stdout.strip()
email = log_and_run(['git', 'config', 'user.email']).stdout.strip()
trailer = f'{trailer_name}:{name} <{email}>'
log_and_run(
[
'git',
'interpret-trailers',
'--in-place',
'--trailer',
trailer,
'--if-exists',
'replace',
'--if-missing',
'add',
msg_file,
]
)
colcon_env = dict(os.environ)
for d in Path.cwd().parents:
colcon_yaml = d / 'colcon.yaml'
if colcon_yaml.is_file():
colcon_env['COLCON_DEFAULTS_FILE'] = str(colcon_yaml.absolute())
colcon_env['COLCON_WS'] = str(d.resolve())
break
if REFORMAT:
diff = log_and_run(['git', 'diff', '--name-only', 'HEAD~']).stdout
changed_files = diff.splitlines()
logger.info('\n '.join(['changed files:', *changed_files]))
c_suffixes = ['.c', '.cc', '.cpp', '.cxx', '.h', '.hh', '.hpp', '.hxx']
files_to_tidy = [
str(filename) for filename in changed_files if Path(filename).suffix in c_suffixes
]
colcon_list = log_and_run(
['colcon', 'list', '--paths-only', '--base-paths', '.'], env=colcon_env
).stdout
colcon_info = log_and_run(
['colcon', 'info', '--base-paths', str(source)], env=colcon_env
).stdout
if re.search(
r'^\s*test:[^:]*\W(ament_cmake_uncrustify|ament_lint_common)\W', colcon_info, re.M
):
logger.info('uncrustifying...')
cfg = '/opt/ros/master/src/ament/ament_lint/ament_uncrustify/ament_uncrustify/configuration/ament_code_style.cfg'
log_and_run(['uncrustify', '--replace', '--no-backup', '-c', cfg, *files_to_tidy])
# subprocess.run(['ament_uncrustify', '--reformat', *files_to_tidy], check=True)
if re.search(r'^\s*test:[^:]*\W(ament_clang_format)\W', colcon_info, re.M):
logger.info('clang formatting...')
log_and_run(['ament_clang_format', '--reformat', *files_to_tidy], check=True)
if __name__ == '__main__':
logger.info(f'Starting {__file__} with arguments {sys.argv[1:]}')
# for uncrustify
os.environ['PATH'] = (
'/opt/ros/master/install/bin/'
+ os.pathsep
+ '/home/dan/.local/bin/'
+ os.pathsep
+ os.environ['PATH']
)
os.environ['PYTHONPATH'] = '/opt/ros/master/install/lib/python3.8/site-packages'
main(*sys.argv[1:])
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment