]> ruderich.org/simon Gitweb - config/dotfiles.git/commitdiff
Check evtags for submodules
authorSimon Ruderich <simon@ruderich.org>
Tue, 10 Jul 2018 17:03:05 +0000 (19:03 +0200)
committerSimon Ruderich <simon@ruderich.org>
Tue, 10 Jul 2018 17:03:05 +0000 (19:03 +0200)
setup.sh
shell/bin/git-evtag-compute-py [new file with mode: 0755]
shell/bin/git-update-and-verify-submodule [new file with mode: 0755]

index d2a3815772217ae7f9dda89e9c81082a99fe1d62..56a90928169eb5ebc4fdcbaedda4bc0ae2cfe882 100755 (executable)
--- a/setup.sh
+++ b/setup.sh
 
 set -eu
 
+
+# Enforce evtag checks for submodules.
+git submodule init > /dev/null
+for x in `git config --local --get-regexp 'submodule.*\.url' \
+        | awk '{print $1}'`; do
+    git config "${x%.url}.update" '!git-update-and-verify-submodule'
+done
+
 for path in */setup.sh; do
     # Skip non executable setup.sh files as an easy way to deactivate one.
     test ! -x "$path" && continue
diff --git a/shell/bin/git-evtag-compute-py b/shell/bin/git-evtag-compute-py
new file mode 100755 (executable)
index 0000000..0babc5c
--- /dev/null
@@ -0,0 +1,108 @@
+#!/usr/bin/env python
+#
+# An implementation of Git-EVTag in a mixture of Python and
+# git-as-subprocess.  Slower than C, but easier to understand
+# and test.
+# 
+# For correct submodule handling, requires the working directory have
+# submodule checkouts.
+
+from __future__ import print_function
+
+import os
+import sys
+import argparse
+import subprocess
+import hashlib
+
+try:
+    from subprocess import DEVNULL # pylint: disable=no-name-in-module
+except ImportError:
+    import os
+    DEVNULL = open(os.devnull, 'wb')
+
+parser = argparse.ArgumentParser(description="Compute Git-EVTag checksum")
+parser.add_argument('rev', help='Revision to checksum')
+opts = parser.parse_args()
+
+csum = hashlib.sha512()
+
+stats = {'commit': 0,
+         'blob': 0,
+         'tree': 0,
+         'commitbytes': 0,
+         'blobbytes': 0,
+         'treebytes': 0}
+
+def checksum_bytes(otype, buf):
+    blen = len(buf)
+    csum.update(buf)
+    stats[otype + 'bytes'] += blen
+    return blen
+
+def checksum_object(repo, objid):
+    p = subprocess.Popen(['git', 'cat-file', '--batch'],
+                         stdin=subprocess.PIPE,
+                         stdout=subprocess.PIPE,
+                         close_fds=True,
+                         cwd=repo)
+    p.stdin.write(objid + '\n')
+    p.stdin.close()
+    (objid,objtype,lenstr) = p.stdout.readline().split(None, 2)
+    olen = int(lenstr)
+    lenstr = lenstr.strip()
+    buf = "{0} {1}\000".format(objtype, lenstr)
+    checksum_bytes(objtype, buf)
+
+    stats[objtype] += 1
+
+    if objtype == 'commit':
+        buf = p.stdout.readline()
+        olen -= checksum_bytes(objtype, buf)
+        (treestr, treeobjid) = buf.split(None, 1)
+        treeobjid = treeobjid.strip()
+        assert treestr == 'tree'
+    else:
+        treeobjid = None
+        
+    while olen > 0:
+        b = p.stdout.read(min(8192, olen))
+        bytes_read = checksum_bytes(objtype, b)
+        olen -= bytes_read
+    if olen > 0:
+        raise ValueError("Failed to read {0} bytes from object".format(olen))
+    p.wait()
+    if p.returncode != 0:
+        raise subprocess.CalledProcessError(p.returncode, 'git cat-file')
+    return treeobjid
+
+def checksum_tree(repo, objid):
+    checksum_object(repo, objid)
+    p = subprocess.Popen(['git', 'ls-tree', objid],
+                         stdin=DEVNULL,
+                         stdout=subprocess.PIPE,
+                         close_fds=True,
+                         cwd=repo)
+    for line in p.stdout:
+        (mode, otype, subid, fname) = line.split(None, 3)
+        fname = fname.strip()
+        if otype == 'blob':
+            checksum_object(repo, subid)
+        elif otype == 'tree':
+            checksum_tree(repo, subid)
+        elif otype == 'commit':
+            checksum_repo(os.path.join(repo, fname), subid)
+        else:
+            assert False
+    p.wait()
+    if p.returncode != 0:
+        raise subprocess.CalledProcessError(p.returncode, 'git ls-tree')
+
+def checksum_repo(repo, objid):
+    treeid = checksum_object(repo, objid)
+    checksum_tree(repo, treeid)
+
+checksum_repo('.', opts.rev)
+
+print("# git-evtag comment: submodules={0} commits={1} ({2}) trees={3} ({4}) blobs={5} ({6})".format(stats['commit']-1, stats['commit'], stats['commitbytes'], stats['tree'], stats['treebytes'], stats['blob'], stats['blobbytes']))
+print("Git-EVTag-v0-SHA512: {0}".format(csum.hexdigest()))
diff --git a/shell/bin/git-update-and-verify-submodule b/shell/bin/git-update-and-verify-submodule
new file mode 100755 (executable)
index 0000000..c4ba1f6
--- /dev/null
@@ -0,0 +1,51 @@
+#!/usr/bin/python
+
+# Copyright (C) 2018  Simon Ruderich
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+
+import os
+import subprocess
+import sys
+
+
+sha1 = sys.argv[1]
+
+repo_path = os.environ['OLDPWD']
+submodule_path = os.getcwd()
+submodule = os.path.relpath(submodule_path, repo_path)
+
+evtag = subprocess.check_output(['git-evtag-compute-py', sha1]).split('\n')
+assert evtag[0].startswith('# git-evtag comment: ')
+assert evtag[1].startswith('Git-EVTag-v0-SHA512: ')
+
+os.chdir(repo_path)
+
+log = subprocess.check_output(['git', 'log', '-1', '--', submodule])
+
+found_comment = False
+found_evtag = False
+for line in log.split('\n'):
+    if evtag[0] in line:
+        found_comment = True
+    elif evtag[1] in line:
+        found_evtag = True
+
+if not found_comment or not found_evtag:
+    print('missing or invalid evtag, aborting')
+    sys.exit(1)
+
+subprocess.check_call(['git', 'submodule', 'update', '--checkout', '--',
+    submodule])