2023-04-23 08:37:35 +00:00
#!/usr/bin/env python3
import sys
import os
import argparse
import tempfile
import shutil
import subprocess
import re
import fileinput
from collections import OrderedDict
import json
import traceback
# Hack to combine two argparse formatters
class CustomFormatter(argparse.ArgumentDefaultsHelpFormatter, argparse.RawDescriptionHelpFormatter):
pass
parser = argparse.ArgumentParser(
2023-04-24 08:09:36 +00:00
description='Prepare build/ files for the app build process',
formatter_class=CustomFormatter)
2023-04-23 08:37:35 +00:00
2023-04-24 08:09:36 +00:00
parser.add_argument('--source-dir', '-s', required=True, metavar='BUILD_DIR', help='Directory to build from')
parser.add_argument('--output-dir', '-o', required=True, metavar='OUTPUT_DIR', help='Directory to write files to')
2023-04-23 08:37:35 +00:00
parser.add_argument('-c', '--channel', default='source', help='channel to add to dev build version number (e.g., "beta" for "5.0-beta.3+a5f28ca8"), or "release" or "source" to skip')
parser.add_argument('--commit-hash', '-m', metavar='HASH', help='Commit hash (required for non-release builds)')
args = parser.parse_args()
def main():
try:
2023-04-24 08:09:36 +00:00
app_root_dir = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
2023-04-23 08:37:35 +00:00
2023-04-24 08:09:36 +00:00
lastrev_dir = os.path.join(app_root_dir, 'lastrev')
if not os.path.exists(lastrev_dir):
os.mkdir(lastrev_dir)
tmp_dir = os.path.join(app_root_dir, 'tmp')
2023-04-23 08:37:35 +00:00
2023-04-24 08:09:36 +00:00
if args.commit_hash:
commit_hash = args.commit_hash[0:9]
elif args.channel != "release":
raise Exception("--commit-hash must be specified for non-release builds")
2023-04-23 08:37:35 +00:00
src_dir = args.source_dir
if not os.path.isdir(src_dir):
raise Exception(src_dir + " is not a directory")
2023-04-24 08:09:36 +00:00
output_dir = args.output_dir
if not os.path.isdir(output_dir):
raise Exception(output_dir + " is not a directory")
if os.listdir(output_dir):
raise Exception(output_dir + " is not empty")
2023-04-23 08:37:35 +00:00
log("Using source directory of " + src_dir)
os.chdir(src_dir)
2023-04-24 08:09:36 +00:00
if not os.path.exists('version'):
raise FileNotFoundError("version file not found in {0}".format(src_dir))
2023-04-23 08:37:35 +00:00
2023-04-24 08:09:36 +00:00
# Extract version number from version file
with open('version') as f:
2023-04-23 08:37:35 +00:00
rdf = f.read()
2023-04-24 08:09:36 +00:00
m = re.search('([0-9].+)\\.SOURCE', rdf)
2023-04-23 08:37:35 +00:00
if not m:
2023-04-24 08:09:36 +00:00
raise Exception("Version number not found in version file")
2023-04-23 08:37:35 +00:00
version = m.group(1)
# Remove tmp build directory if it already exists
if os.path.exists(tmp_dir):
shutil.rmtree(tmp_dir)
os.mkdir(tmp_dir)
2023-04-24 08:09:36 +00:00
2023-04-23 08:37:35 +00:00
tmp_src_dir = os.path.join(tmp_dir, 'zotero')
# Export a clean copy of the source tree
subprocess.check_call([
'rsync',
'-aL',
# Exclude hidden files
'--exclude', '.*',
'--exclude', '#*',
'--exclude', 'package.json',
'--exclude', 'package-lock.json',
'.' + os.sep,
tmp_src_dir + os.sep
])
# Make sure rsync worked
d = os.path.join(tmp_src_dir, 'chrome')
if not os.path.isdir(d):
raise FileNotFoundError(d + " not found")
log("Deleting CSL locale support files")
subprocess.check_call([
'find',
os.path.normpath(tmp_src_dir + '/chrome/content/zotero/locale/csl/'),
'-mindepth', '1',
'!', '-name', '*.xml',
'!', '-name', 'locales.json',
'-print',
'-delete'
])
# Delete styles build script
os.remove(os.path.join(tmp_src_dir, 'styles', 'update'))
translators_dir = os.path.join(tmp_src_dir, 'translators')
# Move deleted.txt out of translators directory
f = os.path.join(translators_dir, 'deleted.txt')
if os.path.exists(f):
shutil.move(f, tmp_src_dir)
# Build translator index
index = OrderedDict()
for fn in sorted((fn for fn in os.listdir(translators_dir)), key=str.lower):
if not fn.endswith('.js'):
continue
with open(os.path.join(translators_dir, fn), 'r', encoding='utf-8') as f:
contents = f.read()
# Parse out the JSON metadata block
m = re.match('^\s*{[\S\s]*?}\s*?[\r\n]', contents)
if not m:
raise Exception("Metadata block not found in " + f.name)
metadata = json.loads(m.group(0))
index[metadata["translatorID"]] = {
"fileName": fn,
"label": metadata["label"],
"lastUpdated": metadata["lastUpdated"]
}
# Write translator index as JSON file
with open(os.path.join(tmp_src_dir, 'translators.json'), 'w', encoding='utf-8') as f:
json.dump(index, f, indent=True, ensure_ascii=False)
2023-04-24 08:09:36 +00:00
version_file = os.path.join(tmp_src_dir, 'version')
2023-04-23 08:37:35 +00:00
2023-04-24 08:09:36 +00:00
log('')
2023-04-23 08:37:35 +00:00
log_line()
2023-04-24 08:09:36 +00:00
log('Original version:\n')
dump_file(version_file)
2023-04-23 08:37:35 +00:00
2023-04-24 08:09:36 +00:00
# Modify version as necessary
2023-04-23 08:37:35 +00:00
# The dev build revision number is stored in build/lastrev-{version}-{channel}.
#
# If we're including it, get the current version number and increment it.
if args.channel not in ["release", "source"]:
lastrev_file = os.path.join(
2023-04-24 08:09:36 +00:00
lastrev_dir, 'lastrev-{0}-{1}'.format(version, args.channel)
2023-04-23 08:37:35 +00:00
)
if not os.path.exists(lastrev_file):
with open(lastrev_file, 'w') as f:
f.write("0")
rev = 1
else:
with open(lastrev_file, 'r') as f:
rev = f.read()
rev = int(rev if rev else 0) + 1
if args.channel == "release":
rev_sub_str = ""
elif args.channel == "source":
rev_sub_str = ".SOURCE.{0}".format(commit_hash)
else:
rev_sub_str = "-{0}.{1}+{2}".format(args.channel, str(rev), commit_hash)
2023-04-24 08:09:36 +00:00
# Update version
for line in fileinput.FileInput(version_file, inplace=1):
2023-04-23 08:37:35 +00:00
line = line.replace('.SOURCE', rev_sub_str)
print(line, file=sys.stdout, end='')
2023-04-24 08:09:36 +00:00
log('Modified version:\n')
dump_file(version_file)
log('')
2023-04-23 08:37:35 +00:00
log_line()
2023-04-24 08:09:36 +00:00
# Move source files to output directory
os.rmdir(output_dir)
shutil.move(tmp_src_dir, output_dir)
2023-04-23 08:37:35 +00:00
# Update lastrev file with new revision number
if args.channel not in ["release", "source"]:
with open(lastrev_file, 'w') as f:
f.write(str(rev))
return 0
except Exception as err:
sys.stderr.write("\n" + traceback.format_exc())
return 1
# Clean up
finally:
2023-04-29 20:28:27 +00:00
if 'tmp_src_dir' in locals() and os.path.exists(tmp_src_dir):
2023-04-24 08:09:36 +00:00
shutil.rmtree(tmp_src_dir)
2023-04-23 08:37:35 +00:00
def dump_file(f):
with open(f, 'r') as f:
log(f.read())
def log(msg):
print(msg, file=sys.stdout)
def log_line():
log('======================================================\n\n')
if __name__ == '__main__':
sys.exit(main())