|
#!/usr/bin/env python3
|
|
# pylint: disable=invalid-name
|
|
|
|
import argparse
|
|
import json
|
|
|
|
import semver
|
|
|
|
# We need a blacklist of packages that are devDependencies but not needed to build
|
|
NPM_BLACKLIST = [
|
|
'axios-mock-adapter',
|
|
'babel-eslint',
|
|
'babel-jest',
|
|
'babel-plugin-dynamic-import-node',
|
|
'coveralls',
|
|
'cross-env',
|
|
'highlight.js',
|
|
'jest',
|
|
'prettier',
|
|
'raw-loader',
|
|
'react-addons-test-utils',
|
|
'react-remarkable',
|
|
'react-test-renderer',
|
|
'react-redux-test-utils',
|
|
'redux-mock-store',
|
|
'surge',
|
|
'webpack-bundle-analyzer',
|
|
'webpack-dev-server',
|
|
]
|
|
|
|
NPM_BLACKLIST_PREFIXES = [
|
|
'@storybook/',
|
|
'enzyme',
|
|
'eslint',
|
|
'jest-',
|
|
'stylelint',
|
|
]
|
|
|
|
def is_blacklisted(package):
|
|
return (package in NPM_BLACKLIST
|
|
or any(package.startswith(prefix) for prefix in NPM_BLACKLIST_PREFIXES))
|
|
|
|
|
|
def get_requirement(version):
|
|
if not version:
|
|
raise ValueError('Empty version received')
|
|
|
|
if version[0].isdigit():
|
|
return ['= {}'.format(version)]
|
|
|
|
if version.startswith('^') or version.startswith('~'):
|
|
modifier = version[0]
|
|
parsed = semver.parse_version_info(version[1:])
|
|
|
|
min_version = '{}.{}.{}'.format(parsed.major, parsed.minor, parsed.patch)
|
|
if modifier == '^':
|
|
max_version = semver.bump_major(min_version)
|
|
elif modifier == '~':
|
|
max_version = semver.bump_minor(min_version)
|
|
|
|
return ['>= {}'.format(min_version), '< {}'.format(max_version)]
|
|
|
|
raise ValueError('Unable to handle version {}'.format(version))
|
|
|
|
|
|
def get_requirements(dependencies, fmt):
|
|
for package, version in sorted(dependencies.items()):
|
|
for requirement in get_requirement(version):
|
|
if is_blacklisted(package):
|
|
yield '#' + fmt.format(package, requirement)
|
|
else:
|
|
yield fmt.format(package, requirement)
|
|
|
|
|
|
def get_npm_sections(packages):
|
|
for section in ('devDependencies', 'dependencies'):
|
|
for requires in ('BuildRequires', 'Requires'):
|
|
trigger = '{} {}\n'.format(section, requires)
|
|
requirements = list(get_requirements(packages[section], requires + ': npm({}) {}\n'))
|
|
yield trigger, requirements
|
|
|
|
|
|
def get_specfile_sections(prefix, fp):
|
|
trigger = prefix.replace('start', 'end', 1)
|
|
key = None
|
|
section = []
|
|
|
|
for line in fp:
|
|
if line.startswith(prefix):
|
|
if key:
|
|
raise Exception('Unclosed section {}'.format(key))
|
|
|
|
key = strip_prefix(line, prefix)
|
|
section = []
|
|
elif key:
|
|
if line.startswith(trigger):
|
|
yield (key, section)
|
|
key = None
|
|
else:
|
|
section.append(line.rstrip() + '\n')
|
|
|
|
|
|
def get_new_content(prefix, current_content, requires):
|
|
key = None
|
|
|
|
for line in current_content:
|
|
if key:
|
|
if line == key:
|
|
yield line
|
|
key = None
|
|
else:
|
|
yield line
|
|
|
|
if line.startswith(prefix):
|
|
key = strip_prefix(line, prefix)
|
|
try:
|
|
for require in requires[key]:
|
|
yield require
|
|
except KeyError:
|
|
raise SystemExit('Unrecognized section found: ' + line)
|
|
key = prefix.replace('start', 'end') + key
|
|
|
|
|
|
def strip_prefix(line, prefix):
|
|
return line[len(prefix):]
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(description='Update the dependencies within sections')
|
|
parser.add_argument('requirementstype', help='the type of conversion',
|
|
choices=('npm', 'specfile', 'direct'))
|
|
parser.add_argument('requirementsfile', help='Path to the requirements file',
|
|
type=argparse.FileType('r'))
|
|
parser.add_argument('specfile', help='path to the specfile', type=argparse.FileType('r'))
|
|
args = parser.parse_args()
|
|
|
|
if args.requirementstype == 'npm':
|
|
prefix = '# start package.json '
|
|
requires = dict(get_npm_sections(json.load(args.requirementsfile)))
|
|
elif args.requirementstype == 'specfile':
|
|
prefix = '# start specfile '
|
|
requires = dict(get_specfile_sections(prefix, args.requirementsfile))
|
|
else:
|
|
prefix = '# start '
|
|
requires = json.load(args.requirementsfile)
|
|
|
|
current_content = args.specfile.readlines()
|
|
args.specfile.close()
|
|
|
|
new_content = list(get_new_content(prefix, current_content, requires))
|
|
|
|
with open(args.specfile.name, 'w') as fp:
|
|
fp.writelines(new_content)
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main()
|