|
#!/usr/bin/env python |
|
|
|
from __future__ import absolute_import, print_function |
|
|
|
import os |
|
import sys |
|
import json |
|
import time |
|
import zipfile |
|
import shutil |
|
import argparse |
|
|
|
|
|
DOWNGRADE_FILEPATH = '~/.sketch-downgrade.json' |
|
|
|
|
|
def load_downgrade_info(): |
|
downgrade_filepath = os.path.expanduser(DOWNGRADE_FILEPATH) |
|
try: |
|
with open(downgrade_filepath) as downgrade_file: |
|
downgrade_info = json.load(downgrade_file) |
|
except IOError: |
|
downgrade_info = {} |
|
downgrade_info.setdefault('app-version', '43.2') |
|
downgrade_info.setdefault('build-code', '39069') |
|
downgrade_info.setdefault('version-code', '88') |
|
return downgrade_info |
|
|
|
|
|
def load_file_info(path): |
|
with zipfile.ZipFile(path) as sketch_file: |
|
sketch_file.testzip() |
|
meta = extract_meta(path, sketch_file) |
|
return { |
|
'app-version': meta['created']['appVersion'], |
|
'build-code': meta['created']['build'], |
|
'version-code': meta['created']['version']} |
|
|
|
|
|
def change_file_info(path, info): |
|
backup_path = '%s.%d.bak' % (path, int(time.time() * 1000)) |
|
target_path = '%s.%d.new' % (path, int(time.time() * 1000)) |
|
shutil.copyfile(path, backup_path) |
|
with zipfile.ZipFile(backup_path) as origin_file, \ |
|
zipfile.ZipFile(target_path, 'w') as target_file: |
|
origin_file.testzip() |
|
meta = extract_meta(path, origin_file) |
|
for item in meta, meta.setdefault('created', {}): |
|
item['appVersion'] = info['app-version'] |
|
item['build'] = info['build-code'] |
|
item['version'] = info['version-code'] |
|
item['compatibilityVersion'] = info['version-code'] |
|
target_file.comment = origin_file.comment |
|
for item in origin_file.infolist(): |
|
if item.filename == 'meta.json': |
|
continue |
|
target_file.writestr(item, origin_file.read(item.filename)) |
|
target_file.writestr('meta.json', json.dumps(meta)) |
|
shutil.move(target_path, path) |
|
|
|
|
|
def extract_meta(path, sketch_file): |
|
try: |
|
with sketch_file.open('meta.json') as meta_file: |
|
meta = json.load(meta_file) |
|
except KeyError: |
|
sketch_file.printdir() |
|
print('-' * 79, file=sys.stderr) |
|
print('Unrecognized sketch file: %s' % path, file=sys.stderr) |
|
print('-' * 79, file=sys.stderr) |
|
sys.exit(1) |
|
return meta |
|
|
|
|
|
def command_inspect(args): |
|
file_info = load_file_info(args.file) |
|
result = json.dumps(file_info, sort_keys=True, indent=2) |
|
print(result) |
|
if args.save: |
|
downgrade_filepath = os.path.expanduser(DOWNGRADE_FILEPATH) |
|
with open(downgrade_filepath, 'w') as downgrade_file: |
|
downgrade_file.write(result) |
|
downgrade_file.write('\n') |
|
print('Saved to %s' % downgrade_filepath, file=sys.stderr) |
|
|
|
|
|
def command_downgrade(args): |
|
file_info = load_file_info(args.file) |
|
downgrade_info = load_downgrade_info() |
|
for key in ('app-version', 'build-code', 'version-code'): |
|
print('%s\t%s (old)\t%s' % (key, file_info[key], downgrade_info[key])) |
|
if file_info == downgrade_info: |
|
sys.exit(0) |
|
if not args.assumeyes: |
|
answer = '' |
|
while answer.lower() not in ('y', 'n'): |
|
answer = raw_input('Continue? (Y/n) ') |
|
if answer == 'n': |
|
sys.exit(1) |
|
change_file_info(args.file, downgrade_info) |
|
|
|
|
|
def parse_args(): |
|
downgrade_info = load_downgrade_info() |
|
|
|
parser = argparse.ArgumentParser() |
|
subparsers = parser.add_subparsers(dest='subparser') |
|
parser_inspect = subparsers.add_parser('inspect') |
|
parser_inspect.add_argument( |
|
'-s', '--save', action='store_true', default=False, |
|
help='save the inspect result to %s' % DOWNGRADE_FILEPATH) |
|
parser_inspect.add_argument('file', metavar='FILE') |
|
parser_downgrade = subparsers.add_parser('downgrade') |
|
parser_downgrade.add_argument( |
|
'-y', '--assumeyes', action='store_true', default=False, |
|
help='answer yes to all questions') |
|
parser_downgrade.add_argument( |
|
'--app-version', default=downgrade_info['app-version'], |
|
help='default: %(app-version)s' % downgrade_info) |
|
parser_downgrade.add_argument( |
|
'--build-code', default=downgrade_info['build-code'], |
|
help='default: %(build-code)s' % downgrade_info) |
|
parser_downgrade.add_argument( |
|
'--version-code', default=downgrade_info['version-code'], |
|
help='default: %(version-code)s' % downgrade_info) |
|
parser_downgrade.add_argument('file', metavar='FILE') |
|
return parser.parse_args() |
|
|
|
|
|
def main(): |
|
args = parse_args() |
|
command = { |
|
'inspect': command_inspect, |
|
'downgrade': command_downgrade, |
|
}[args.subparser] |
|
command(args) |
|
|
|
|
|
if __name__ == '__main__': |
|
main() |