#!/usr/bin/python
import argparse
import dateutil.parser
import fcntl
import glob
import os
import re
import subprocess
import sys
SKIP_FILES = ['.rspec', '.travis.yml', 'Gemfile', 'Gemfile.lock', 'Rakefile']
SKIP_DIRECTORIES = ['spec', '.git']
def execute_subprocess(options):
return subprocess.Popen(options, stdout=subprocess.PIPE).communicate()[0]
def get_change_from_file(file_path):
""" This function is not used because it's too slow. It spawns
a new git process which takes too long if you need to do it
thousands of times
"""
string_date = execute_subprocess(["git", "log", "-n1", "--format=%ci"])
string_date = string_date.rstrip("\n")
date = dateutil.parser.parse(string_date)
return date.isoformat()
def update_file(style_path, files_to_change):
style_path = style_path[2:] # TODO: 2: because contains ./
file = open(style_path).read()
if style_path.endswith('.csl'):
updated = files_to_change[style_path]
file = re.sub('.*', '%s' % updated, file)
return file
def write_readme_md():
text = """CSL Style distribution
======================
This repository is a copy of [github.com/citation-style-language/styles](/~https://github.com/citation-style-language/styles), refreshed twice a day, with the tag changed up to the last git change for a certain file.
Licensing
---------
Please refer to [github.com/citation-style-language/styles](/~https://github.com/citation-style-language/styles)
"""
file = open("README.md", "w")
file.write(text)
file.close()
def write_new_style(path, updated_style):
file = open(os.path.join(DISTRIBUTION_STYLES_DIRECTORY, path), 'w')
file.write(updated_style)
def original_last_commit():
""" Returns de last commit of the ORIGINAL_STYLES_DIRECTORY. """
last_dir = os.getcwd()
os.chdir(ORIGINAL_STYLES_DIRECTORY)
last_commit = execute_subprocess(["git", "log", "-n1", "--format=%H"])
os.chdir(last_dir)
return last_commit
def copy_and_update_styles(files_to_change):
for root, subDirectories, files in os.walk("."):
for file in files:
if file not in SKIP_FILES and root[2:].split('/')[0] not in SKIP_DIRECTORIES:
path = os.path.join(root, file)
updated_file = update_file(path, files_to_change)
write_new_style(path, updated_file)
def count_styles_git_index(directory):
count = 0
last_dir = os.getcwd()
os.chdir(directory)
files = execute_subprocess(["git", "ls-files"])
for file in files.split("\n"):
if file.endswith('.csl'):
count += 1
os.chdir(last_dir)
return count
def delete_styles():
styles = glob.glob(DISTRIBUTION_STYLES_DIRECTORY + "/*.csl")
dependent_styles = glob.glob(DISTRIBUTION_STYLES_DIRECTORY + "/dependent/*.csl")
for style in styles + dependent_styles:
os.unlink(style)
def prepare_files_to_change():
last_dir = os.getcwd()
files_to_change = {}
os.chdir(ORIGINAL_STYLES_DIRECTORY)
log = execute_subprocess(["git", "log", "--pretty=Date:%ai", "--name-only"])
last_date = ""
for line in log.split("\n"):
if line.startswith("Date:"):
last_date = dateutil.parser.parse(line[len('Date:'):]).isoformat()
elif line.endswith('.csl'):
if files_to_change.has_key(line):
if last_date > files_to_change[line]:
files_to_change[line] = last_date
else:
files_to_change[line] = last_date
os.chdir(last_dir)
return files_to_change
def push_changes(dry_run):
os.chdir(DISTRIBUTION_STYLES_DIRECTORY)
write_readme_md()
os.system("git add -A")
os.system("git commit -a -m 'Synced up to /~https://github.com/citation-style-language/styles/commit/%s'" % (original_last_commit()))
styles_distribution_directory_count = count_styles_git_index(DISTRIBUTION_STYLES_DIRECTORY)
styles_directory_count = count_styles_git_index(ORIGINAL_STYLES_DIRECTORY)
if styles_distribution_directory_count == styles_directory_count and \
styles_distribution_directory_count > 6000:
print "Success"
if not dry_run:
os.system("git push")
else:
sys.stderr.write("styles_distribution_directory: %d\n" % (styles_distribution_directory_count))
sys.stderr.write("styles_directory: %d\n" % (styles_directory_count))
def main(dry_run, commit):
lockfile = open("/tmp/csls.lock", "w")
fcntl.flock(lockfile, fcntl.LOCK_EX)
os.chdir(ORIGINAL_STYLES_DIRECTORY)
execute_subprocess(["git","pull"])
execute_subprocess(["git", "checkout", commit])
delete_styles()
dependents_directory = os.path.join(DISTRIBUTION_STYLES_DIRECTORY, "dependent")
if not os.path.exists(dependents_directory):
os.mkdir(dependents_directory)
files_to_change = prepare_files_to_change()
copy_and_update_styles(files_to_change)
push_changes(dry_run)
if __name__ == '__main__':
parser = argparse.ArgumentParser(description='Prepares and pushes style repository to be distributed.')
parser.add_argument('--dry-run', action='store_true', help='Do everything except git push (default: %(default)s).', default=False)
parser.add_argument('--commit', default='HEAD', help='Which commit to checkout from --original_styles_directory option (default: %(default)s)')
parser.add_argument('--original-styles-directory', required=True, help='Directory with a git checkout of /~https://github.com/citation-style-language/styles')
parser.add_argument('--distribution-styles-directory', required=True, help='Directory with a git checkout of the destination directory')
args = vars(parser.parse_args())
ORIGINAL_STYLES_DIRECTORY = args['original_styles_directory']
DISTRIBUTION_STYLES_DIRECTORY = args['distribution_styles_directory']
main(args['dry_run'], args['commit'])