commit 3c0e758cabcccc099ad5640deca0f75827342591 Author: Damian Johnson atagar@torproject.org Date: Sun Oct 1 16:12:57 2017 -0700
Script to determine commit activity
Simple script that pulls all the repositories listed on https://gitweb.torproject.org/ to determine activity in the last six months. --- scripts/commit_activity.py | 140 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 140 insertions(+)
diff --git a/scripts/commit_activity.py b/scripts/commit_activity.py new file mode 100644 index 0000000..eeb5e02 --- /dev/null +++ b/scripts/commit_activity.py @@ -0,0 +1,140 @@ +import collections +import os +import subprocess +import sys + +TMP_DIR = '/tmp/tor_git_repos' + +GIT_REPOS = { + 'anonbib': 'https://git.torproject.org/anonbib.git', + 'atlas': 'https://git.torproject.org/atlas.git', + 'bridgedb': 'https://git.torproject.org/bridgedb.git', + 'builders_gitian-builder': 'https://git.torproject.org/builders/gitian-builder.git', + 'builders_rbm': 'https://git.torproject.org/builders/rbm.git', + 'builders_tor-browser-build': 'https://git.torproject.org/builders/tor-browser-build.git', + 'builders_tor-browser-bundle': 'https://git.torproject.org/builders/tor-browser-bundle.git', + 'censorship-timeline': 'https://git.torproject.org/censorship-timeline.git', + 'check': 'https://git.torproject.org/check.git', + 'chutney': 'https://git.torproject.org/chutney.git', + 'collector': 'https://git.torproject.org/collector.git', + 'community_policies': 'https://git.torproject.org/community/policies.git', + 'company_policies': 'https://git.torproject.org/company/policies.git', + 'compass': 'https://git.torproject.org/compass.git', + 'curriculum': 'https://git.torproject.org/curriculum.git', + 'depictor': 'https://git.torproject.org/depictor.git', + 'doctor': 'https://git.torproject.org/doctor.git', + 'erebus': 'https://git.torproject.org/erebus.git', + 'exonerator': 'https://git.torproject.org/exonerator.git', + 'flashproxy': 'https://git.torproject.org/flashproxy.git', + 'fuzzing-corpora': 'https://git.torproject.org/fuzzing-corpora.git', + 'gettor': 'https://git.torproject.org/gettor.git', + 'githax': 'https://git.torproject.org/githax.git', + 'guardfraction': 'https://git.torproject.org/guardfraction.git', + 'https-everywhere': 'https://git.torproject.org/https-everywhere.git', + 'metrics-base': 'https://git.torproject.org/metrics-base.git', + 'metrics-lib': 'https://git.torproject.org/metrics-lib.git', + 'metrics-tasks': 'https://git.torproject.org/metrics-tasks.git', + 'metrics-web': 'https://git.torproject.org/metrics-web.git', + 'nyx': 'https://git.torproject.org/nyx.git', + 'onionoo': 'https://git.torproject.org/onionoo.git', + 'ooni-probe': 'https://git.torproject.org/ooni-probe.git', + 'oonib': 'https://git.torproject.org/ooni/oonib.git', + 'ooni_reader': 'https://git.torproject.org/ooni/reader.git', + 'ooni_spec': 'https://git.torproject.org/ooni/spec.git', + 'ooni_web': 'https://git.torproject.org/ooni/web.git', + 'orbot': 'https://git.torproject.org/orbot.git', + 'pluggable-transports_bundle': 'https://git.torproject.org/pluggable-transports/bundle.git', + 'pluggable-transports_fog': 'https://git.torproject.org/pluggable-transports/fog.git', + 'pluggable-transports_goptlib': 'https://git.torproject.org/pluggable-transports/goptlib.git', + 'pluggable-transports_meek': 'https://git.torproject.org/pluggable-transports/meek.git', + 'pluggable-transports_obfs4': 'https://git.torproject.org/pluggable-transports/obfs4.git', + 'pluggable-transports_obfsproxy': 'https://git.torproject.org/pluggable-transports/obfsproxy.git', + 'pluggable-transports_obfsproxy-legacy': 'https://git.torproject.org/pluggable-transports/obfsproxy-legacy.git', + 'pluggable-transports_pyptlib': 'https://git.torproject.org/pluggable-transports/pyptlib.git', + 'pluggable-transports_snowflake': 'https://git.torproject.org/pluggable-transports/snowflake.git', + 'pluggable-transports_websocket': 'https://git.torproject.org/pluggable-transports/websocket.git', + 'pups': 'https://git.torproject.org/pups.git', + 'pytorctl': 'https://git.torproject.org/pytorctl.git', + 'research-web': 'https://git.torproject.org/research-web.git', + 'stegotorus': 'https://git.torproject.org/stegotorus.git', + 'stem': 'https://git.torproject.org/stem.git', + 'support-portal': 'https://git.torproject.org/support-portal.git', + 'support-tools': 'https://git.torproject.org/support-tools.git', + 'tech-reports': 'https://git.torproject.org/tech-reports.git', + 'thandy': 'https://git.torproject.org/thandy.git', + 'tor': 'https://git.torproject.org/tor.git', + 'tor-animation': 'https://git.torproject.org/tor-animation.git', + 'tor-blog': 'https://git.torproject.org/tor-blog.git', + 'tor-brochure': 'https://git.torproject.org/tor-brochure.git', + 'tor-browser': 'https://git.torproject.org/tor-browser.git', + 'tor-browser-spec': 'https://git.torproject.org/tor-browser-spec.git', + 'sandboxed-tor-browser': 'https://git.torproject.org/tor-browser/sandboxed-tor-browser.git', + 'user-manual': 'https://git.torproject.org/tor-browser/user-manual.git', + 'tor-design-2012': 'https://git.torproject.org/tor-design-2012.git', + 'tor-fw-helper': 'https://git.torproject.org/tor-fw-helper.git', + 'tor-glossary': 'https://git.torproject.org/tor-glossary.git', + 'tor-launcher': 'https://git.torproject.org/tor-launcher.git', + 'tor-manual': 'https://git.torproject.org/tor-manual.git', + 'tor-messenger-build': 'https://git.torproject.org/tor-messenger-build.git', + 'tor-ramdisk': 'https://git.torproject.org/tor-ramdisk.git', + 'torbirdy': 'https://git.torproject.org/torbirdy.git', + 'torbrowser': 'https://git.torproject.org/torbrowser.git', + 'torbutton': 'https://git.torproject.org/torbutton.git', + 'tordnsel': 'https://git.torproject.org/tordnsel.git', + 'torflow': 'https://git.torproject.org/torflow.git', + 'torperf': 'https://git.torproject.org/torperf.git', + 'torsocks': 'https://git.torproject.org/torsocks.git', + 'torspec': 'https://git.torproject.org/torspec.git', + 'translation': 'https://git.torproject.org/translation.git', + 'translation-tools': 'https://git.torproject.org/translation-tools.git', + 'trunnel': 'https://git.torproject.org/trunnel.git', + 'weather': 'https://git.torproject.org/weather.git', + 'webstats': 'https://git.torproject.org/webstats.git', +} + +def call(cmd, cwd = None): + if isinstance(cmd, str): + cmd = cmd.split() + + process = subprocess.Popen(cmd, stdout = subprocess.PIPE, stderr = subprocess.PIPE, cwd = cwd) + stdout, stderr = process.communicate() + + if process.poll() != 0: + print("'%s' failed:\n\nstdout: %s\n\nstderr: %s" % (' '.join(cmd), stdout, stderr)) + sys.exit(1) + else: + return stdout.strip() + + +def print_results(title, authors): + print('%s:\n' % title) + + if authors: + for author, count in sorted(collections.Counter(authors).items(), key = lambda entry: entry[1], reverse = True): + print(' %s: %s' % (author, count)) + else: + print(' no activity') + + print('') + + +if not os.path.exists(TMP_DIR): + os.mkdir(TMP_DIR) + +all_author_emails = [] + +for name, repo in sorted(GIT_REPOS.items(), key = lambda entry: entry[0]): + repo_path = os.path.join(TMP_DIR, name) + + if os.path.exists(repo_path): + call('git fetch --all', cwd = repo_path) + else: + call('git clone --mirror %s %s' % (repo, name), cwd = TMP_DIR) + + author_emails = call(['git', 'log', '--all', '--since', '-6 month', '--format=%an (%ae)'], cwd = repo_path).splitlines() + print_results(name, author_emails) + all_author_emails += author_emails + + +print_results('Total commit counts', all_author_emails) +