Progressia/tools/pre-commit.py

117 lines
3.3 KiB
Python
Executable File

#!/usr/bin/env python3
usage = \
'''Usage: %(me)s [--dry-run] [--verbose] [--dont-update]
or: %(me)s restore
or: %(me)s update
In the 1st form, run standard pre-commit procedure for Progressia.
In the 2nd form, attempt to restore workspace if the pre-commit hook failed.
In the 3rd form, only update git pre-commit hook
--dry-run do not change anything in git or in the working tree
--verbose print commands and diagnostics
--dont-update do not update git pre-commit hook
--help display this help and exit
Currently, the pre-commit procedure performs the following:
1. format staged changes
2. attempt to compile with staged changes only'''
import sys
import os
import subprocess
def fail(*args):
print(my_name + ':', *args, file=sys.stderr)
sys.exit(1)
def invoke(*cmd, result_when_dry=None, quiet=True):
if verbose:
print(my_name + ': command "' + '" "'.join(cmd) + '"')
if dry_run and result_when_dry != None:
print(my_name + ': skipped: --dry-run')
return result_when_dry
output = ''
popen = subprocess.Popen(cmd,
stdout=subprocess.PIPE,
text=True,
universal_newlines=True)
for line in popen.stdout:
if (not quiet):
print(line, end='')
output += line
popen.stdout.close()
return_code = popen.wait()
if return_code != 0:
raise subprocess.CalledProcessError(return_code, cmd)
return output
STASH_NAME = 'progressia_pre_commit_stash'
def run_safety_checks():
if invoke('git', 'stash', 'list', '--grep', f"^{STASH_NAME}$") != '':
fail(f"Cannot run pre-commit checks: stash {STASH_NAME} exists. " +
f"Use `{my_name} restore` to restore workspace and repository " +
f"state")
# Let's hope there are no files with weird names
indexed_changes = \
set(invoke('git', 'diff', '--name-only', '--cached') \
.strip().split('\n'))
unindexed_changes = \
set(invoke('git', 'diff', '--name-only') \
.strip().split('\n'))
both_changes = indexed_changes & unindexed_changes
if len(both_changes) != 0:
fail(f"Cannot run pre-commit checks: files with indexed and " +
"unindexed changes exist:\n\n\t" +
"\n\t".join(both_changes) +
"\n")
if __name__ == '__main__':
my_name = os.path.basename(sys.argv[0])
verbose = False
dry_run = False
verbose = True
dry_run = True
run_safety_checks()
#update()
unindexed_changes = invoke('git', 'diff', '--name-status')
if unindexed_changes != '':
print('Unindexed changes found in files:')
print(unindexed_changes)
print('These changes will be ignored')
invoke('git', 'stash', 'push',
'--keep-index',
'--include-untracked',
'--message', STASH_NAME,
result_when_dry='')
# check that ORIGINAL does not exist
# check that staged files & files with unstaged changes = 0
# update pre-commit hook
# if any unstaged changes:
# stash ORIGINAL
# remove unstaged changes
# format staged files
# compile
# git add
# if any unstaged changes:
# unstash ORIGINAL