Audit for missing reverse integration upon committing to master

This is meant as a nag. I'm not sure there'd be a good way to
catch-us-in-the-act unless we migrate to some merge tools a la git-flow.

// FREEBIE
This commit is contained in:
Michael Kirk 2018-03-01 09:47:58 -05:00
parent b023e77d0e
commit 72428350c2
4 changed files with 190 additions and 0 deletions

View File

@ -0,0 +1 @@
Copy these git hooks into .git/hooks

3
Scripts/git_hooks/post-commit Executable file
View File

@ -0,0 +1,3 @@
#!/usr/bin/env bash
Scripts/reverse_integration_check.py

141
Scripts/git_hooks/pre-commit Executable file
View File

@ -0,0 +1,141 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import os
import sys
import subprocess
import datetime
import argparse
import commands
git_repo_path = os.path.abspath(subprocess.check_output(['git', 'rev-parse', '--show-toplevel']).strip())
def splitall(path):
allparts = []
while 1:
parts = os.path.split(path)
if parts[0] == path: # sentinel for absolute paths
allparts.insert(0, parts[0])
break
elif parts[1] == path: # sentinel for relative paths
allparts.insert(0, parts[1])
break
else:
path = parts[0]
allparts.insert(0, parts[1])
return allparts
def process(filepath):
short_filepath = filepath[len(git_repo_path):]
if short_filepath.startswith(os.sep):
short_filepath = short_filepath[len(os.sep):]
filename = os.path.basename(filepath)
if filename.startswith('.'):
return
file_ext = os.path.splitext(filename)[1]
if file_ext in ('.swift'):
env_copy = os.environ.copy()
env_copy["SCRIPT_INPUT_FILE_COUNT"] = "1"
env_copy["SCRIPT_INPUT_FILE_0"] = '%s' % ( short_filepath, )
lint_output = subprocess.check_output(['swiftlint', 'autocorrect', '--use-script-input-files'], env=env_copy)
print lint_output
try:
lint_output = subprocess.check_output(['swiftlint', 'lint', '--use-script-input-files'], env=env_copy)
except subprocess.CalledProcessError, e:
lint_output = e.output
print lint_output
with open(filepath, 'rt') as f:
text = f.read()
original_text = text
lines = text.split('\n')
while lines and lines[0].startswith('//'):
lines = lines[1:]
text = '\n'.join(lines)
text = text.strip()
header = '''//
// Copyright (c) %s Open Whisper Systems. All rights reserved.
//
''' % (
datetime.datetime.now().year,
)
text = header + text + '\n'
if original_text == text:
return
print 'Updating:', short_filepath
with open(filepath, 'wt') as f:
f.write(text)
def should_ignore_path(path):
ignore_paths = [
os.path.join(git_repo_path, '.git')
]
for ignore_path in ignore_paths:
if path.startswith(ignore_path):
return True
for component in splitall(path):
if component.startswith('.'):
return True
if component.endswith('.framework'):
return True
if component in ('Pods', 'ThirdParty', 'Carthage',):
return True
return False
def process_if_appropriate(filepath):
filename = os.path.basename(filepath)
if filename.startswith('.'):
return
file_ext = os.path.splitext(filename)[1]
if file_ext not in ('.h', '.hpp', '.cpp', '.m', '.mm', '.pch', '.swift'):
return
if should_ignore_path(filepath):
return
process(filepath)
if __name__ == "__main__":
parser = argparse.ArgumentParser(description='Precommit script.')
parser.add_argument('--all', action='store_true', help='process all files in or below current dir')
args = parser.parse_args()
if args.all:
for rootdir, dirnames, filenames in os.walk(git_repo_path):
for filename in filenames:
file_path = os.path.abspath(os.path.join(rootdir, filename))
process_if_appropriate(file_path)
else:
filepaths = []
# Staging
output = commands.getoutput('git diff --cached --name-only --diff-filter=ACMR')
filepaths.extend([line.strip() for line in output.split('\n')])
# Working
output = commands.getoutput('git diff --name-only --diff-filter=ACMR')
filepaths.extend([line.strip() for line in output.split('\n')])
# Only process each path once.
filepaths = sorted(set(filepaths))
for filepath in filepaths:
filepath = os.path.abspath(os.path.join(git_repo_path, filepath))
process_if_appropriate(filepath)
print 'git clang-format...'
print commands.getoutput('git clang-format')

View File

@ -0,0 +1,45 @@
#!/usr/bin/python
# When we make a hotfix, we need to reverse integrate our hotfix back into
# master. After commiting to master, this script audits that all tags have been
# reverse integrated.
import subprocess
from distutils.version import LooseVersion
import logging
#logging.basicConfig(level=logging.DEBUG)
def is_on_master():
output = subprocess.check_output(["git", "rev-parse", "--abbrev-ref", "HEAD"]).strip()
logging.debug("branch output: %s" % output)
return output == "master"
def main():
if not is_on_master():
# Don't interfere while on a feature or hotfix branch
logging.debug("not on master branch")
return
logging.debug("on master branch")
unmerged_tags_output = subprocess.check_output(["git", "tag", "--no-merged", "master"])
unmerged_tags = [line.strip() for line in unmerged_tags_output.split("\n") if len(line) > 0]
logging.debug("All unmerged tags: %s" % unmerged_tags)
# Before this point we weren't always reverse integrating our tags. As we
# audit old tags, we can ratchet this version number back.
epoch_tag="2.21.0"
logging.debug("ignoring tags before epoch_tag: %s" % epoch_tag)
tags_of_concern = [unmerged_tag for unmerged_tag in unmerged_tags if LooseVersion(unmerged_tag) > LooseVersion(epoch_tag)]
if len(tags_of_concern) > 0:
logging.debug("Found unmerged tags newer than epoch: %s" % tags_of_concern)
raise RuntimeError("Found unmerged tags: %s" % tags_of_concern)
else:
logging.debug("No unmerged tags newer than epoch. All good!")
if __name__ == "__main__":
main()