-
-
Save darrnshn/5b36692391313793994c3c8e650cd45a to your computer and use it in GitHub Desktop.
Script to automatically upload diffs of generated code as Gists. Put this somewhere and set the USERNAME, ACCESS_TOKEN and WORKING_DIR variables correctly.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python | |
""" | |
Usage: | |
On the branch that you want to submit a CL for, run: | |
./gist_diff.py <directory/containing/generated/files> | |
This script uploads a diff of generated files (called build artifacts here) | |
to a Gist on GitHub. This is useful to show reviewers the changes you made | |
to generated code, since they are not visible on rietveld. | |
To use this script, you'll need to modify some configuration below. | |
The script does the following: | |
1. Build current branch. | |
2. Copy build artifacts to a temporary folder. | |
3. Build upstream branch. | |
4. Find build artifacts that differ. | |
5. Upload original build artifacts as a new Gist on GitHub. | |
6. Upload new build artifacts as a revision of the original Gist. | |
7. Switch back to original branch. | |
Example Usage: | |
gist_diff ~/chromium/src/out/Default/gen/blink/core | |
""" | |
from collections import namedtuple | |
import requests | |
import json | |
from subprocess import check_output | |
from glob import glob | |
import os | |
import shutil | |
import argparse | |
# Your Github username | |
USERNAME = '' | |
# Your access token which has permissions to create gists. | |
# To get an access token: | |
# 1. Login to Github and go to https://github.com/settings/tokens | |
# 2. Click "Generate new token" | |
# 3. Give it a descriptive name | |
# 4. Tick the "gist" scope | |
# 5. Click "Generate token" | |
# 6. Copy the token (looks like a hash) into the variable below | |
ACCESS_TOKEN = '' | |
# The directory from which all the commands in this script are run. | |
# Normally it's the absolute path to your chromium src directory. | |
WORKING_DIR = '~/chromium/src' | |
# The name of the build (the folder name in the out/ directory, e.g. Debug, Release) | |
BUILD_NAME = 'Default' | |
# The build target containing generated files. | |
# The default is a shortcut that only runs the generator scripts, | |
# which is much faster than a full build. | |
BUILD_TARGET = 'third_party/WebKit/Source/core:all_generators' | |
#BUILD_TARGET = 'blink_tests' | |
# The command to build the generated files. | |
CMD_BUILD = ['ninja', '-C', 'out/' + BUILD_NAME, '-j1000', BUILD_TARGET] | |
# The command to clean the generated files. | |
CMD_CLEAN = ['ninja', '-C', 'out/' + BUILD_NAME, '-t', 'clean', BUILD_TARGET] | |
# A temporary directory for copying the generated files to. | |
# Make sure it's only used by this script. | |
TMP_DIR = '/tmp/diffs' | |
# Command to get the current branch name. | |
CMD_GET_BRANCH = ['git', 'rev-parse', '--abbrev-ref', 'HEAD'] | |
# Command to checkout a branch | |
CMD_CHECKOUT = ['git', 'checkout'] | |
def auth_user(username, access_token): | |
print '[Authenticating user...]' | |
r = requests.get('https://api.github.com/user', auth=(username, access_token)) | |
assert r.status_code == 200 | |
result = r.json() | |
print '=> Authenticated successfully as {} ({})'.format(result['login'], result['name']) | |
return result | |
def put_gist(files): | |
data = { | |
'description': '', | |
'public': True, | |
'files': {fname.split('/')[-1]:{'content': content} for fname, content in files.items()} | |
} | |
r = requests.post('https://api.github.com/gists', auth=(USERNAME, ACCESS_TOKEN), | |
data=json.dumps(data)) | |
assert r.status_code == 201, 'Error: {}'.format(r.text) | |
result = r.json() | |
print '=> Created new gist at {}.'.format(result['url']) | |
return result | |
def patch_gist(gist_id, files): | |
data = { | |
'description': '', | |
'files': {fname.split('/')[-1]:{'content': content} for fname, content in files.items()} | |
} | |
r = requests.patch('https://api.github.com/gists/{}'.format(gist_id), | |
auth=(USERNAME, ACCESS_TOKEN), | |
data=json.dumps(data)) | |
assert r.status_code == 200, 'Error: {}'.format(r.text) | |
result = r.json() | |
print '=> Updated gist at {}.'.format(result['url']) | |
return result | |
def run_cmd(description, cmd): | |
print '[{}] {}'.format(description, ' '.join(cmd)) | |
with open(os.devnull, 'w') as devnull: | |
output = check_output(cmd, cwd=os.path.expanduser(WORKING_DIR), stderr=devnull) | |
return output | |
def copy_dir_to_tmp(path): | |
# Ensure the tmp dir is clean | |
print '[Cleaning "{}"...]'.format(TMP_DIR) | |
try: | |
shutil.rmtree(TMP_DIR) | |
except OSError: | |
pass | |
# Copy all the files to tmp | |
print '[Copying "{}" to "{}"...]'.format(path, TMP_DIR) | |
shutil.copytree(path, TMP_DIR) | |
def has_file_changed(a_path, b_path): | |
with open(a_path) as a_file, open(b_path) as b_file: | |
for a_line, b_line in zip(a_file, b_file): | |
if a_line != b_line: | |
return True | |
return False | |
def get_paths_in_tree(path, recursive): | |
if recursive: | |
for root, _, fnames in os.walk(path): | |
for fname in fnames: | |
yield os.path.relpath(os.path.join(root, fname), path) | |
else: | |
for fname in os.listdir(path): | |
if os.path.isfile(os.path.join(path, fname)): | |
yield fname | |
def read_file(path): | |
with open(path, 'r') as f: | |
return f.read() | |
def is_valid(path): | |
ext = os.path.splitext(path)[1] | |
return ext in ['.h', '.cpp'] | |
def main(): | |
parser = argparse.ArgumentParser(description='Diff generated files and upload them to GitHub') | |
parser.add_argument('directory', type=str) | |
parser.add_argument('--no-recursive', action='store_true', default=False) | |
parser.add_argument('--dry-run', action='store_true', default=False) | |
args = parser.parse_args() | |
# Authenticate the user to check credentials | |
if not args.dry_run: | |
auth_user(USERNAME, ACCESS_TOKEN) | |
# Get the path to artifacts directory | |
artifacts_dir = args.directory | |
# Build the current branch | |
working_branch = run_cmd('Getting current branch...', CMD_GET_BRANCH).strip() | |
print '=> Working branch:', working_branch | |
run_cmd('Cleaning...', CMD_CLEAN) | |
run_cmd('Build artifacts...', CMD_BUILD) | |
# Copy build artifacts to tmp | |
copy_dir_to_tmp(artifacts_dir) | |
# Move upstream and rebuild artifacts | |
try: | |
run_cmd('Moving to upstream...', CMD_CHECKOUT + ['@{u}']) | |
except: | |
print 'Could not move to upstream branch. Are you on the right branch?' | |
return | |
run_cmd('Cleaning...', CMD_CLEAN) | |
run_cmd('Rebuilding artifacts on upstream...', CMD_BUILD) | |
# Compare old artifacts in tmp and new artifacts in build to find which ones have changed | |
old_paths = set(get_paths_in_tree(artifacts_dir, not args.no_recursive)) | |
new_paths = set(get_paths_in_tree(TMP_DIR, not args.no_recursive)) | |
changed_paths = old_paths.union(new_paths) | |
for path in old_paths.intersection(new_paths): | |
if not has_file_changed(os.path.join(TMP_DIR, path), | |
os.path.join(artifacts_dir, path)) or not is_valid(path): | |
changed_paths.remove(path) | |
if len(changed_paths) == 0: | |
print 'No changes' | |
# Move back to original branch | |
run_cmd('Moving back to original branch...', CMD_CHECKOUT + [working_branch]) | |
return | |
if args.dry_run: | |
print 'Not creating gist for dry run ({} files changed)'.format(len(changed_paths)) | |
# Move back to original branch | |
run_cmd('Moving back to original branch...', CMD_CHECKOUT + [working_branch]) | |
else: | |
# Create the gist for the original file | |
print '[Creating new gist with {} files...]'.format(len(changed_paths)) | |
old_files = {path:read_file(os.path.join(artifacts_dir, path)) for path in changed_paths if path in old_paths} | |
old_output = put_gist(old_files) | |
# Patch the gist with the new artifacts to create a diff | |
print '[Updating gist...]'.format(len(changed_paths)) | |
new_files = {path:read_file(os.path.join(TMP_DIR, path)) for path in changed_paths if path in new_paths} | |
new_output = patch_gist(old_output['id'], new_files) | |
# Move back to original branch | |
run_cmd('Moving back to original branch...', CMD_CHECKOUT + [working_branch]) | |
print '-- Success! --' | |
print 'Gist: {}'.format(new_output['html_url']) | |
print 'Diff: {}'.format(new_output['html_url'] + '/revisions') | |
if __name__ == '__main__': | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment