def bump_version_after_tagging(self): """Bump the version in the trunk / branch / master after creating the tag. Update version.txt and HISTORY.txt """ output.part_title('Bumping versions in trunk') version_file = os.path.join(scm.get_package_name('.').replace('.', '/'), 'version.txt') print '* updating %s: set version to %s' % (version_file, self.new_trunk_version) version_txt = open(version_file, 'w') version_txt.write(self.new_trunk_version) version_txt.close() history_file = 'docs/HISTORY.txt' print '* updating %s' % history_file insert_title_on_line = 3 version = self.new_trunk_version.split('-')[0] data = open(history_file).read().split('\n') data = data[:insert_title_on_line] +\ ['', version,'-' * len(version), '', ] +\ data[insert_title_on_line:] file = open(history_file, 'w') file.write('\n'.join(data)) file.close() # commit ci_msg = 'bumping versions in trunk of %s after tagging %s' % ( scm.get_package_name('.'), self.new_tag_version, ) scm.commit_files(ci_msg, '*', push=True)
def check_pyprc(self): output.part_title('Checking .pypirc for egg-release targets') pypirc_path = os.path.expanduser('~/.pypirc') if not os.path.isfile(pypirc_path): # ~/.pypirc required output.error('Could not find the file %s' % pypirc_path, exit=True) config = ConfigParser.ConfigParser() config.readfp(open(pypirc_path)) indexservers = config.get('distutils', 'index-servers').strip().split('\n') sections = [] basic_namespace = scm.get_package_name('.').split('.')[0] for srv in indexservers: # test if its properly configured if config.has_section(srv): print '* found target "%s"' % output.colorize(srv, output.WARNING) sections.append(srv) if basic_namespace in sections: self.pypi_target = basic_namespace else: self.pypi_target = '' msg = 'Please specify a pypi target for the egg relase [%s]' % \ output.colorize(self.pypi_target, output.BOLD_WARNING) pypi_target_input = input.prompt(msg, lambda v:\ (self.pypi_target and not v) or v in sections and True or 'Please select a target listed above') if pypi_target_input: self.pypi_target = pypi_target_input
def __call__(self): scm.tested_for_scms(('svn', 'gitsvn', 'git'), '.') scm.require_package_root_cwd() self.check_conditions() package_name = scm.get_package_name('.') if self.options.domain: domain = self.options.domain else: domain = package_name package_root = scm.get_package_root_path('.') package_dir = os.path.join(package_root, *package_name.split('.')) pot_path = os.path.join(self.locales_dir, '%s.pot' % domain) output.part_title('Rebuilding pot file at %s' % pot_path) # rebuild cmd = ['%s rebuild-pot' % self.i18ndude] cmd.append('--pot %s' % pot_path) # manual file manual_file = os.path.join(self.locales_dir, '%s-manual.pot' % domain) if os.path.exists(manual_file): print ' merging manual pot file:', manual_file cmd.append('--merge %s' % manual_file) cmd.append('--create %s %s' % ( domain, package_dir, )) cmd = ' \\\n'.join(cmd) runcmd(cmd)
def check_versions(self): output.part_title('Checking package versions') version_file = os.path.join(scm.get_package_name('.').replace('.', '/'), 'version.txt') trunk_version = open(version_file).read().strip() print ' * Current version of trunk: %s' %\ output.colorize(trunk_version, output.WARNING) next_version = trunk_version.split('-')[0] existing_tags = scm.get_existing_tags('.') if next_version in existing_tags.keys(): output.warning('Tag %s already existing' % next_version) # ask for next tag version prompt_msg = 'Which version do you want to release now? [%s]' % \ output.colorize(next_version, output.BOLD_WARNING) next_version_input = input.prompt(prompt_msg, lambda v:v in existing_tags.keys() and 'Tag already existing' or True) if next_version_input: next_version = next_version_input # ask for next trunk version next_trunk_version = next_version + '-dev' next_trunk_version = self.bump_version_proposal(next_trunk_version) prompt_msg = 'Which version should trunk have afterwards? [%s]' % \ output.colorize(next_trunk_version, output.BOLD_WARNING) next_trunk_version_input = input.prompt(prompt_msg) if next_trunk_version_input: next_trunk_version = next_trunk_version_input print ' * The version of the tag will be: %s' %\ output.colorize(next_version, output.WARNING) print ' * New version of the trunk will be: %s' %\ output.colorize(next_trunk_version, output.WARNING) self.new_tag_version = next_version self.new_trunk_version = next_trunk_version
def dependency_packages(self): dependencies = [(scm.get_package_name('.'), None, None)] if int(self.options.limit) > 0: return dependencies if self.egg: dependencies += scm.get_egg_dependencies(self.egg, with_extra='*') else: # source directory ? for pkg in os.listdir('.'): path = os.path.abspath(pkg) if os.path.isdir(path) and scm.lazy_is_scm(path): dependencies.append((pkg, None, None)) dependencies = list(set(dependencies)) dependencies.sort() return dependencies
def bump_version_before_tagging(self): """Sets the version in version.txt and HISTORY.txt from the trunk version (e.g. 1.0-dev) to the tag-version (1.0). """ output.part_title('Bumping versions') version_file = os.path.join(scm.get_package_name('.').replace('.', '/'), 'version.txt') print '* updating %s: set version to %s' % (version_file, self.new_tag_version) version_txt = open(version_file, 'w') version_txt.write(self.new_tag_version) version_txt.close() # commit msg = 'bumping version to %s' % self.new_tag_version scm.commit_files(msg, version_file)
def __call__(self): scm.tested_for_scms(('svn', 'gitsvn'), '.') svn_url = scm.get_svn_url('.').split('/') svn_root_url = scm.get_package_root_url('.').split('/') package_name = scm.get_package_name('.') path = os.path.abspath(os.path.join( (len(svn_url) - len(svn_root_url) - 1) * '../', package_name.replace('.', '/'), 'version.txt', )) if not os.path.isfile(path): output.error('Could not find file %s' % path, exit=1) version = open(path).read().strip() print ' Version of %s: %s' % ( output.colorize(package_name, output.WARNING), output.colorize(version, output.WARNING), )
def _get_packages(self): """ Returns a list of [(pkgname, extra, version),(...)] including parameter packages and dependency packages """ packages = list([(pkg, None, None) for pkg in self.args]) if self.options.dependencies: packages.extend(self._get_dependency_packages()) if len(packages) == 0: try: packages.append((scm.get_package_name('.'), None, None)) except: # maybe we are not in a package - thats not a error pass if len(packages) == 0: # maybe we are in a src folder.. use every item for pkg in os.listdir('.'): packages.append((pkg, None, None)) return packages
def __call__(self): scm.tested_for_scms(('svn', 'gitsvn', 'git'), '.') scm.require_package_root_cwd() if len(self.args) < 1: output.error('Language code is required', exit=1) lang = self.args[0] # check self.check_conditions() package_name = scm.get_package_name('.') if self.options.domain: domain = self.options.domain else: domain = package_name # check pot file pot_path = os.path.join(self.locales_dir, '%s.pot' % domain) if not os.path.exists(pot_path): output.error('Could not find pot file at: %s' % pot_path, exit=1) # check language directory lang_dir = os.path.join(self.locales_dir, lang, 'LC_MESSAGES') if not os.path.isdir(lang_dir): runcmd('mkdir -p %s' % lang_dir) # touch po file po_file = os.path.join(lang_dir, '%s.po' % domain) if not os.path.isfile(po_file): runcmd('touch %s' % po_file) # sync output.part_title('Syncing language "%s"' % lang) cmd = '%s sync --pot %s %s' % ( self.i18ndude, pot_path, po_file, ) runcmd(cmd) # remove language output.part_title('Removing language code from po file') data = open(po_file).read().split('\n') file = open(po_file, 'w') for row in data: if not row.startswith('"Language-Code') and \ not row.startswith('"Language-Name'): file.write(row) file.write('\n') file.close()
def check_conditions(self): self.i18ndude = 'i18ndude' # we should be in a local repository try: package_name = scm.get_package_name('.') print ' using package name:', package_name except scm.NotAScm: output.error('Not in a SVN- or GIT-Checkout, unable to guess ' 'package name', exit=1) # get package root package_root = scm.get_package_root_path('.') print ' using package root path:', package_root # check locales directory self.locales_dir = os.path.abspath(os.path.join( package_root, '/'.join(package_name.split('.')), 'locales', )) print ' using locales dir:', self.locales_dir if not os.path.isdir(self.locales_dir): runcmd('mkdir -p %s' % self.locales_dir)
def check_zcml(self): """ ZCML Checks: - locales registration """ self.notify_part('ZCML checks') locales_directories = [] # locations self.notify_check('Location of locales directory') failed = False pkg_root_path = './%s' % scm.get_package_name('.').replace('.', '/') for dirpath, dirnames, filenames in os.walk('.'): if 'locales' in dirnames: locales_directories.append((dirpath, 'locales')) if dirpath != pkg_root_path: failed = True self.notify(False, 'Found locales-directory in suspicious location: ' '%s/locales' % dirpath, 'Expected locales directory in %s' % pkg_root_path, 2) if not failed: self.notify(True) # registration self.notify_check('Check registration of locales dirs') failed = False for dirpath, dirname in locales_directories: zcmlpath = os.path.join(dirpath, 'configure.zcml') if not os.path.exists(zcmlpath): self.notify(False, 'Could not find zcml file at %s' % zcmlpath) failed = True elif '"%s"' % dirname not in open(zcmlpath).read(): self.notify(False, '%s/%s seems not to be registered in ' % ( dirpath, dirname, zcmlpath)) if not failed: self.notify(True)
def check_paster_stuff(self): """ Paster does some bad things, so lets fix them. """ self.notify_part('Check paster problems') setuppy = open('setup.py').read() package_base_path = scm.get_package_name('.').replace('.', '/') # CHANGES.txt self.notify_check('CHANGES.txt should not be used') changes_used = False if 'CHANGES.txt' in setuppy: self.notify(False, 'CHANGES.txt is used in setup.py somehow', 'Fix setup.py: remove the CHANGES.txt stuff', 1) changes_used = True if os.path.exists('CHANGES.txt'): self.notify(False, 'A ./CHANGES.txt exists', 'Remove the CHANGES.txt, we use the docs/HISTORY.txt', 1, pause=changes_used) if not changes_used: if input.prompt_bool( 'Should I remove the CHANGES.txt for you?'): scm.remove_files('CHANGES.txt') scm.commit_files('Removed unused CHANGES.txt', 'CHANGES.txt') self.notify_fix_completed() elif not changes_used: self.notify(True) # CONTRIBUTORS.txt self.notify_check('CONTRIBUTORS.txt should not be used') contributors_used = False if 'CONTRIBUTORS.txt' in setuppy: self.notify(False, 'CONTRIBUTORS.txt is used in setup.py somehow', 'Fix setup.py: remove the CONTRIBUTORS.txt stuff', 1) contributors_used = True if os.path.exists('CONTRIBUTORS.txt'): self.notify(False, 'A ./CONTRIBUTORS.txt exists', 'Remove the CONTRIBUTORS.txt', 1, pause=False) if not contributors_used: if input.prompt_bool('Should I remove the CONTRIBUTORS.txt ' 'for you?'): scm.remove_files('CONTRIBUTORS.txt') scm.commit_files('Removed unused CONTRIBUTORS.txt', 'CONTRIBUTORS.txt') self.notify_fix_completed() elif not contributors_used: self.notify(True) # interfaces not as folder self.notify_check('%s.interfaces should not be a folder' % scm.get_package_name('.')) path = os.path.join(package_base_path, 'interfaces') if os.path.isdir(path): self.notify(False, '%s is a folder' % path, 'Use a interfaces.py, not a interfaces folder', 2) else: self.notify(True) # viewlets / portlets self.notify_check('Portlets and viewlets folders') dirs = ('viewlets', 'portlets') all_ok = True for dir in dirs: bad_path = os.path.join(package_base_path, 'browser', dir) good_path = os.path.join(package_base_path, dir) if os.path.exists(bad_path): all_ok = False self.notify(False, 'Directory exists at: %s' % bad_path, 'Move it to %s' % good_path, 2) if all_ok: self.notify(True) # setup.cfg self.notify_check('Do not use setup.cfg') if os.path.exists('setup.cfg'): self.notify(False, 'Found a setup.cfg', 'Remove the setup.cfg', 1, pause=False) if input.prompt_bool('Should I remove the setup.cfg?'): scm.remove_files('setup.cfg') scm.commit_files('Removed setup.cfg', 'setup.cfg') self.notify_fix_completed() else: self.notify(True)
def _get_dependency_packages(self): dependencies = [(scm.get_package_name('.'), None, None)] dependencies += scm.get_egg_dependencies(self.egg) return dependencies
def print_history(self): versions = self.package_versions force_reload = self.options.refresh packages_data = {} list_trunk_modifications = self.options.history_dev limit = int(self.options.limit) if limit > 0: packages = [] # packages is only *this* package, so lets walk up the dependencies def _follow_dependencies(pkg, level=0, extra=None, version=None): # add it to the package list if pkg not in packages: packages.append([pkg, extra, version]) if level != limit: # load some infos about the package and cache them scm.PackageInfoMemory().get_info(pkg, prompt=False) # load and follow the dependencies deps = scm.PackageInfoMemory().get_dependencies_for( pkg, with_extra=extra) if deps: for subpkg, subextra, subversion in deps: _follow_dependencies(subpkg, level=level + 1, extra=subextra, version=subversion) _follow_dependencies(scm.get_package_name('.')) packages.sort() else: packages = self.dependency_packages for package, extra, v in packages: if self.options.verbose: print package ctag = package in versions.keys() and str(versions[package]) or '' info = scm.PackageInfoMemory().get_info(package, force_reload=force_reload, prompt=False) ntag = info and str(info['newest_tag']) or '' if list_trunk_modifications and info and info['changes']: history = scm.PackageInfoMemory().get_history_for(package, 'trunk', prompt=False) if not history: continue history = history.strip().split('\n') if ctag not in history: print '* ERROR in', package, ': could not find tag', ctag, print 'in changelog' continue packages_data[package] = history[2:history.index(ctag)] elif ntag and ctag and ntag != ctag: history = scm.PackageInfoMemory().get_history_for(package, ntag, prompt=False) if not history: continue history = history.strip().split('\n') if ctag not in history: print '* ERROR in', package, ': could not find tag', ctag, print 'in changelog' continue if ntag not in history: print '* ERROR in', package, ': could not find tag', ntag, print 'in changelog' continue packages_data[package] = history[history.index(ntag): history.index(ctag)] # change tag headlines to: * package ntag, remove empty lines # and indent any other row with 4 spaces pat = '^([ ]*)\* (\[(.*?)\]){0,1}(.{2,}}?)\[(.*?)\]' old_entry_format = re.compile(pat) skip_next = False for package, diff in packages_data.items(): for i, line in enumerate(diff): line = aggressive_decode(line).encode('utf8') next_line = len(diff) > (i + 1) and diff[i + 1] or None if skip_next: skip_next = False continue elif len(line.strip()) == 0: continue elif next_line and (next_line == len(line) * '-' or next_line == len(line) * '='): # current line is a tag-name, next line contains # only '-' or '=' print '' print '*', package, '-', line.strip() skip_next = True else: # check the format old_entry = old_entry_format.search(line) if old_entry: indent, foo, date, text, author = old_entry.groups() print ' ', indent, '*', text.strip() date_author = date and '%s, %s' % (date, author) or \ author print ' ', indent, '[%s]' % date_author else: print ' ', line
def check_setup_py(self): """setup.py checks """ self.notify_part('Check setup.py') # MAINTAINER self.notify_check('Maintainer should be defined') maintainer = self.egginfo.get_maintainer() if maintainer and maintainer != 'UNKNOWN': self.notify(True) else: if len(filter(lambda row: row.startswith('maintainer'), open('setup.py').read().split('\n'))) > 0: self.notify(False, 'maintainer is defined as variable but is ' 'not used in setup call', 'add "maintainer=maintainer," to the setup call', 1, pause=False) if input.prompt_bool('Should I try to fix it?'): rows = open('setup.py').read().split('\n') file_ = open('setup.py', 'w') found = False for i, row in enumerate(rows): file_.write(row) if i != len(rows) - 1: file_.write('\n') if row.strip().startswith('author_email='): file_.write(' ' * 6) file_.write('maintainer=maintainer,') file_.write('\n') found = True file_.close() if not found: output.error('Could not find keyword author_email in ' 'your setup.py, you have to fix it ' 'manually, sorry.', exit=1) else: self._validate_setup_py() scm.add_and_commit_files( 'setup.py: register maintainer', 'setup.py') self.notify_fix_completed() print '' else: self.notify(False, 'maintainer is not defined in the egg at all', 'check %s on how to define a maintainer' % \ WIKI_PYTHON_EGGS) # VERSION.TXT self.notify_check('version.txt file exists') versiontxt_path = scm.get_package_name('.').replace('.', '/') + \ '/version.txt' if os.path.exists(versiontxt_path) and os.path.isfile(versiontxt_path): self.notify(True) elif os.path.exists(versiontxt_path): self.notify(False, '%s exists but is not a file !?' % \ versiontxt_path, 'it should be a file containing the package version', 0) else: self.notify(False, '%s does not exist' % versiontxt_path, pause=False) if input.prompt_bool('Should I try to fix it?'): version = self.egginfo.get_version() file_ = open(versiontxt_path, 'w') file_.write(version) file_.close() scm.add_and_commit_files('Added version.txt file', versiontxt_path) self.notify_fix_completed() print '' # VERSION self.notify_check('Version is taken form version.txt') rows = open('setup.py').read().split('\n') version_rows = filter(lambda row: row.startswith('version ='), rows) if len(version_rows) != 1: self.notify(False, 'I\'m confused, it seems that you have a mess ' 'with your versions.', 'check %s on how to define versions properly' % \ WIKI_PYTHON_EGGS) elif not version_rows[0].startswith('version = open('): self.notify(False, 'I\'m guessing that the version in your ' 'setup.py is not taken from %s' % versiontxt_path, 'check %s on how to define versions properly' % \ WIKI_PYTHON_EGGS, pause=False) if input.prompt_bool('Should I try to fix it?'): new_version_row = "version = open('%s').read().strip()" % \ versiontxt_path rows[rows.index(version_rows[0])] = new_version_row file_ = open('setup.py', 'w') file_.write('\n'.join(rows)) file_.close() self._validate_setup_py() scm.add_and_commit_files('setup.py: using version.txt', 'setup.py') self.notify_fix_completed() else: self.notify(True) # NAMESPACES self.notify_check('Check namespaces') guessed_namespaces = [] namespace_parts = scm.get_package_name('.').split('.') for i, space in enumerate(namespace_parts[:-1]): guessed_namespaces.append('.'.join(namespace_parts[:i + 1])) if set(guessed_namespaces) == set(self.egginfo.namespace_packages): self.notify(True) else: print ' current namespaces: ', str( self.egginfo.namespace_packages) print ' expected namespaces:', str(guessed_namespaces) print ' package name: ', scm.get_package_name('.') self.notify(False, 'I think your namespace_packages declaration ' 'is wrong', pause=False) if input.prompt_bool('Should I try to fix it?'): guessed_namespaces.sort() rows = open('setup.py').read().split('\n') nsrows = filter(lambda x: x.strip().startswith('namespace_packages'), rows) if len(nsrows) != 1: output.error('Could not fix it: expected one and only one' ' line beginning with "namespace_packages"' ' in setup.py..', exit=True) else: new_row = nsrows[0].split('=')[0] + '=' + \ str(guessed_namespaces) + ',' rows[rows.index(nsrows[0])] = new_row file_ = open('setup.py', 'w') file_.write('\n'.join(rows)) file_.close() self._validate_setup_py() scm.add_and_commit_files( 'setup.py: fixed namespace_packages', 'setup.py') self.notify_fix_completed() # VARIOUS CHECKS self.notify_check('Various setup.py checks') failure = False # .. name if self.egginfo.get_name() != scm.get_package_name('.'): failure = True self.notify(False, 'Name: Expected name in setup.py to be ' + \ '"%s" ' % scm.get_package_name('.') + \ 'but it was "%s"' % self.egginfo.get_name()) # maintainer in description if self.egginfo.get_maintainer() not in self.egginfo.get_description(): failure = True self.notify(False, 'Description: Maintainer is not defined ' 'in description', 'Check out %s' % WIKI_PYTHON_EGGS, 2) # docs/HISTORY.txt setuppy = open('setup.py').read() if 'HISTORY.txt' not in setuppy or \ not os.path.exists('docs/HISTORY.txt') or \ open('docs/HISTORY.txt').read() not in \ self.egginfo.get_long_description(): self.notify(False, 'docs/HISTORY.txt embedded be used in ' 'long_description', 'Check long_description on %s' % WIKI_PYTHON_EGGS) # author: use maintainer expected_author = '%s, 4teamwork GmbH' % self.egginfo.get_maintainer() if self.egginfo.get_author() != expected_author: failure = True self.notify(False, 'Author: Expected author to be "%s""' % \ expected_author + \ ' but it is "%s"' % self.egginfo.get_author(), 'Check out %s' % WIKI_PYTHON_EGGS, 2) # author email if self.egginfo.get_author_email() != 'mailto:[email protected]': failure = True self.notify(False, 'Author email: the email should be' ' "mailto:[email protected]"', 'Check out %s' % WIKI_PYTHON_EGGS, 2) # license if self.egginfo.get_license() != 'GPL2': failure = True self.notify(False, 'License: the license should be "GPL2"', 'Check out %s' % WIKI_PYTHON_EGGS, 2) # .. ok? if not failure: self.notify(True) # run egg_info self.notify_check('we should be able to run `setup.py egg_info`') state, out, errors = self._validate_setup_py() if out: print ' ', out.replace('\n', '\n ') if errors: print ' ', errors.replace('\n', '\n ') if state == 0: self.notify(True) else: state.notify(False, 'Cant run `python setup.py egg_info`, see ' 'errors above', 0)
def analyse(self): output.part_title('Checking subversion project') if not scm.is_scm('.'): # without subversion or gitsvn it doesnt work... output.error('Current directory is not a repository of type svn, ' 'git-svn, git.', exit=True) # update newest remote changes if scm.is_git('.') or scm.is_git_svn('.'): git.pull_changes('.') git.push_committed_changes('.') elif scm.is_subversion('.'): svn.update('.') # remote should be there if scm.is_git('.') and not scm.is_git_svn('.'): if not git.has_remote('origin'): output.error('There is no remote "origin", which is needd', exit=True) # run it at repo root if scm.is_subversion('.') or scm.is_git_svn('.'): root_svn = scm.get_package_root_url('.') if not svn.check_project_layout(root_svn, raise_exception=False, ask_for_creation=False): # we should have the folders trunk, tags, branches in the project output.error('Project does not have default layout with trunk, ' +\ 'tags and branches. At least one folder is missing.', exit=True) if not self.options.release_egg_only: here_url = scm.get_svn_url('.') here_root = scm.get_package_root_url('.') is_trunk = here_url == here_root +'/trunk' is_branch = '/'.join(here_url.split('/')[:-1]) == here_root + '/branches' if not is_trunk and not is_branch: # command must be run at the "trunk" folder of a package output.error('Please run this command at the root of the package ' +\ '(trunk/branch folder)', exit=True) elif scm.is_git('.'): if not os.path.exists('.git'): output.error('Please run this command at the root of the package ' +\ 'checkout', exit=True) # .. other checks if not os.path.isfile('setup.py'): # setup.py is required output.error('Could not find the file ./setup.py', exit=True) if not os.path.isfile('docs/HISTORY.txt'): # docs/HISTORY.txt is required output.error('Could not find the file ./docs/HISTORY.txt', exit=True) if os.path.isfile('setup.cfg'): # setup.cfg is not necessary, it should not be used since the development # stuff makes bad distribution versions output.error('setup.cfg should not be used anymore') if input.prompt_bool('Should I delete setup.cfg?'): scm.remove_files('setup.cfg') scm.commit_files('Removed setup.cfg', 'setup.cfg') version_file = os.path.join(scm.get_package_name('.').replace('.', '/'), 'version.txt') if not os.path.isfile(version_file): # version.txt is required output.error('Could not find the file %s' % version_file, exit=True) # check MANIFEST.in self.check_manifest() # check subversion state if scm.has_local_changes('.'): output.error('You have local changes, please commit them first.', exit=True)
def __call__(self): scm.tested_for_scms(('svn', 'gitsvn'), '.') package_name = scm.get_package_name('.') sys.argv = [sys.argv[0], 'zopeinstance', 'test', '-s', package_name] zopeinstance.ZopeInstanceCommand(self.maincommand)()
def check_manifest(self): """ Checks the MANIFEST.in file and gives advices. It returns if the release action can be continued """ if self.options.release_egg_only: return True namespace = scm.get_package_name('.').split('.')[0] required_lines = ( 'recursive-include %s *' % namespace, 'recursive-include docs *', 'include *.txt', 'global-exclude *.pyc', 'global-exclude ._*', ) unused_lines = ( 'include setup.py', 'include README.txt', 'include CONTRIBUTORS.txt', 'global-exclude *pyc', 'global-exclude *mo', 'global-exclude *.mo', ) created = False modified = False commit_message = '' if not os.path.isfile('MANIFEST.in'): output.warning('Could not find the file ./MANIFEST.in, creating one') f = open('MANIFEST.in', 'w') f.write('\n'.join(required_lines)) f.close() print 'created MANIFEST.in with following content:' print output.colorize(open('MANIFEST.in').read(), output.INFO) print '' commit_message = 'added MANIFEST.in for %s' % scm.get_package_name('.') created = True else: # check the existing file current_lines = [x.strip() for x in open('MANIFEST.in').readlines()] missing_lines = [x for x in required_lines if x.strip() not in current_lines] files_to_remove = [x for x in unused_lines if x.strip() in current_lines] if len(missing_lines) > 0 or len(files_to_remove) > 0: new_lines = current_lines if len(missing_lines) > 0: output.warning('./MANIFEST.in: added some required lines:') print output.colorize('\n'.join(missing_lines), output.INFO) print '' new_lines += missing_lines if len(files_to_remove) > 0: output.warning('./MANIFEST.in: removed some unused lines:') print output.colorize('\n'.join(files_to_remove), output.ERROR) print '' new_lines = filter(lambda x:x.strip() not in files_to_remove, new_lines) f = open('MANIFEST.in', 'w') f.write('\n'.join(new_lines)) f.close() commit_message = 'updated MANIFEST.in for %s' % scm.get_package_name('.') modified = True if created or modified: # commit it ? if input.prompt_bool('Would you like to commit the MANIFEST.in?'): if created: scm.add_and_commit_files(commit_message, 'MANIFEST.in') return True elif modified: scm.commit_files(commit_message, 'MANIFEST.in') return True return False else: return True
def pre_build_check(self): """ Check if a build will work later. Check this before doing anything by building and loading the egg. """ output.part_title('Make a test-build for preventing bad dists') cwd = os.getcwd() # make a sdist runcmd('%s setup.py sdist' % sys.executable) os.chdir('dist') # switch dir print output.colorize('cd dist', output.INFO) # extract runcmd('tar -xf *.tar.gz') # find extracted dir / chdir distdir = None for file_ in os.listdir('.'): if os.path.isdir(file_): distdir = file_ break if not distdir: output.error('Something is wrong: could not find extracted dist directory', exit=1) os.chdir(distdir) print output.colorize('cd %s' % distdir, output.INFO) # test setup.py cmd = '%s setup.py egg_info' % sys.executable state, response, error = runcmd_with_exitcode(cmd, respond=True, respond_error=True) # cd back to original dir os.chdir(cwd) # remove stuff runcmd('rm -rf dist') # did it work? if state != 0: output.error('Something\'s wrong: could not load setup.py on distribution, ' +\ 'you may have a problem with your setup.py / MANIFEST.in:', exit=(not error and True or False)) if response: print output.colorize(response, output.INFO) if error: output.error(error, exit=True) # check locales locales_dir = os.path.join(scm.get_package_name('.').replace('.', '/'), 'locales') if os.path.isdir(locales_dir): for basedir, dirs, files in os.walk(locales_dir): for file_ in files: path = os.path.join(basedir, file_) if path.endswith('.po'): # check with msgfmt exitcode, errors = runcmd_with_exitcode( 'msgfmt -o /dev/null %s' % path, log=True, respond_error=True) if exitcode > 0: output.error(errors, exit=True) data = open(path).read() if 'fuzzy' in data: print path output.error('You have "Fuzzy" entries in your ' 'translations! I\'m not releasing ' 'it like this.', exit=True)
def check_requires(self): """ Checks, if there are missing dependencies """ self.notify_part('Check dependencies') # get current requires requires = self.egginfo.install_requires # extend it with extra requires if self.egginfo.extras_require: for ename, erequires in self.egginfo.extras_require.items(): requires.extend(erequires) print ' current requirements (including extras):' for egg in requires: print ' -', egg print '' # add the requirements without extras too for egg in requires[:]: if '[' in egg: requires.append(egg.split('[')[0]) self.notify_check('Its not necessary to import some default plone / zope stuff') failures = False for egg in requires: if egg in PACKAGE_REQUIREMENTS_INADVISABLE and egg not in TESTING_PACKAGES: self.notify(False, 'Maybe you should remove the requirement ' +\ output.colorize(egg, output.ERROR) +\ output.colorize('. It seems to be in a python, ' +\ 'zope or plone distribution and ' +\ 'those packages should not be ' +\ 'set as requirement.', output.WARNING), problem_level=2) failures = True if not failures: self.notify(True) self.notify_check('Check imports on python files and zcml stuff') propose_requires = [] # contains ipath:file mapping of python and zcml imports ipath_file_mapping = {} # SEARCH PYTHON FILES # make a grep on python files py_grep_results = runcmd("find . -name '*.py' -exec grep -Hr 'import ' {} \;", respond=True) # cleanup: # - strip rows # - remove the rows with spaces (they are not realle imports) # - remove "as xxx" py_grep_results = filter(lambda row: ' ' in row, [row.strip().split(' as ')[0] for row in py_grep_results]) for row in py_grep_results: file_, statement = row.split(':') # make a import path ipath = statement.replace('from ', '').replace(' import ', '.').replace('import', '').strip() ipath = ipath.replace('...', '').replace('>>>', '') ipath_parts = ipath.split('.') if '#' in ipath: continue # ignore namespace imports (python internals etc) if len(ipath_parts) == 1: continue ipath_file_mapping[ipath] = file_ # SEARCH ZCML FILES cmd = "find . -name '*.zcml' -exec grep -Hr '\(layer\|package\|for\)=' {} \;" zcml_grep_results = runcmd(cmd, respond=True) # cleanup results zcml_xpr = re.compile('(for|layer)="(.*?)("|$)') for row in zcml_grep_results: file_, stmt = row.split(':', 1) stmt = stmt.strip() match = zcml_xpr.search(stmt) if not match: # maybe we have a more complicated statement (e.g. multiline) break ipath = match.groups()[1].strip() ipath = ipath.replace('*', '').strip() if '#' in ipath: continue if not ipath.startswith('.'): ipath_file_mapping[ipath] = file_ # for later use guessed_related_packages = self._get_guessed_related_packages() # HANDLE ALL IMPORTS for ipath, file_ in ipath_file_mapping.items(): ipath_parts = ipath.split('.') # ignore local imports if ipath.startswith(scm.get_package_name('.')): continue # is it already required? found = False for egg in requires: if ipath.startswith(egg): found = True break if not found: # is it already proposed? for egg in propose_requires: if ipath.startswith(egg): found = True break if not found: # is it ignored? for egg in PACKAGE_REQUIREMENTS_INADVISABLE + TESTING_PACKAGES: if ipath.startswith(egg): found = True break if found: continue # maybe we have a module which import relatively module_path = os.path.join(os.path.dirname(file_), ipath_parts[0]) if os.path.isfile(module_path + '.py') or \ os.path.isfile(module_path + '/__init__.py'): continue # start on level 2 and for searching egg guessed_egg_names = ['.'.join(ipath_parts[:i]) for i, part in enumerate(ipath_parts) if i > 1] # does one of the eggs exist? found = False # is there package in our src directory, if we are in one? for egg_name in guessed_egg_names: if egg_name.strip() in guessed_related_packages: if egg_name.strip() not in propose_requires: propose_requires.append(egg_name.strip()) found = True break # .. in pypi if not found: for egg_name in guessed_egg_names: if len(self.find_egg_in_index(egg_name)) > 0: if egg_name.strip() not in propose_requires: propose_requires.append(egg_name.strip()) found = True break # .. or do we have one in the svn cache? if not found: for egg_name in guessed_egg_names: if scm.guess_package_url(egg_name): if egg_name.strip() not in propose_requires: propose_requires.append(egg_name.strip()) found = True break if not found: print ' ', output.colorize(ipath, output.INFO), 'in', \ output.colorize(file_, output.INFO), \ output.colorize('is not covered by requirements ' 'and I could find a egg with such a name', output.WARNING) if scm.get_package_name('.') in propose_requires: propose_requires.remove(scm.get_package_name('.')) if len(propose_requires)==0: self.notify(True) return propose_requires.sort() print '' print ' There are some requirements missing. I propose to add these:' for egg in propose_requires: print ' ', egg print '' # try to add them automatically: if input.prompt_bool('Should I try to add them?'): rows = open('setup.py').read().split('\n') # find "install_requires" frows = filter(lambda row:row.strip().startswith('install_requires='), rows) if len(frows) != 1: output.error('Somethings wrong with your setup.py: expected only ' 'one row containing "install_requires=", but ' 'got %i' % len(frows), exit=1) insert_at = rows.index(frows[0]) + 1 for egg in propose_requires: rows.insert(insert_at, ' ' * 8 + "'%s'," % egg) file_ = open('setup.py', 'w') file_.write('\n'.join(rows)) file_.close() self._validate_setup_py() scm.add_and_commit_files('setup.py: added missing dependencies', 'setup.py') self.notify_fix_completed()