def _diff_and_commit(self, commit_msg=''): """Show diff and offer commit. commit_msg is optional. If it is not there, we get the commit_msg from self.data. That is the usual mode and is at least used in prerelease and postrelease. If it is not there either, we ask. """ if not commit_msg: if 'commit_msg' not in self.data: # Ask until we get a non-empty commit message. while not commit_msg: commit_msg = utils.get_input( "What is the commit message? ") else: commit_msg = self.data['commit_msg'] diff_cmd = self.vcs.cmd_diff() diff = execute_command(diff_cmd) if sys.version.startswith('2.6.2'): # python2.6.2 bug... http://bugs.python.org/issue5170 This is the # spot it can surface as we show a part of the changelog which can # contain every kind of character. The rest is mostly ascii. print("Diff results:") print(diff) else: # Common case logger.info("The '%s':\n\n%s\n", diff_cmd, diff) if utils.ask("OK to commit this"): msg = commit_msg % self.data msg = self.update_commit_message(msg) commit_cmd = self.vcs.cmd_commit(msg) commit = execute_command(commit_cmd) logger.info(commit)
def available_tags(self): base = self._base_from_svn() tags_name = self._tags_name if tags_name is None: # Suggest to create a tags dir with the default plural /tags name. print("tags dir does not exist at %s" % base + 'tags') if utils.ask("Shall I create it"): cmd = 'svn mkdir %stags -m "Creating tags directory."' % (base) logger.info("Running %r", cmd) print(execute_command(cmd)) tags_name = self._tags_name assert tags_name == 'tags' else: sys.exit(0) tag_info = execute_command('svn list %s%s' % (base, tags_name)) network_errors = [ 'Could not resolve hostname', 'E670008', 'Repository moved', 'Unable to connect', ] found_errors = [1 for network_error in network_errors if network_error in tag_info] if found_errors: logger.error('Network problem: %s', tag_info) sys.exit(1) tags = [line.replace('/', '').strip() for line in tag_info.split('\n')] tags = [tag for tag in tags if tag] # filter empty ones logger.debug("Available tags: %r", tags) return tags
def available_tags(self): base = self._base_from_svn() tags_name = self._tags_name if tags_name is None: # Suggest to create a tags dir with the default plural /tags name. print("tags dir does not exist at %s" % base + 'tags') if utils.ask("Shall I create it"): cmd = 'svn mkdir %stags -m "Creating tags directory."' % (base) logger.info("Running %r", cmd) print(execute_command(cmd)) tags_name = self._tags_name assert tags_name == 'tags' else: sys.exit(1) tag_info = execute_command( 'svn list --non-interactive %s%s' % (base, tags_name)) network_errors = [ 'Could not resolve hostname', 'E670008', 'Repository moved', 'Unable to connect', ] found_errors = [1 for network_error in network_errors if network_error in tag_info] if found_errors: logger.error('Network problem: %s', tag_info) sys.exit(1) tags = [line.replace('/', '').strip() for line in tag_info.split('\n')] tags = [tag for tag in tags if tag] # filter empty ones logger.debug("Available tags: %r", tags) return tags
def get_setup_py_name(self): if os.path.exists('setup.py'): # First run egg_info, as that may get rid of some warnings # that otherwise end up in the extracted name, like # UserWarnings. utils.execute_command(utils.setup_py('egg_info')) return utils.execute_command(utils.setup_py('--name')).strip()
def _tags_name(self): """Return name for tags dir Normally the plural /tags, but some projects have the singular /tag. """ default_plural = 'tags' fallback_singular = 'tag' # svn 1.7 introduced a slightly different message and a warning code. failure_messages = [ "non-existent in that revision", "W160013", ] base = self._base_from_svn() tag_info = execute_command( ['svn', 'list', '%s%s' % (base, default_plural)]) # Look for one of the failure messages: found = [1 for mess in failure_messages if mess in tag_info] if not found: return default_plural logger.debug("tags dir does not exist at %s%s", base, default_plural) tag_info = execute_command( ['svn', 'list', '%s%s' % (base, fallback_singular)]) # Look for one of the failure messages: found = [1 for mess in failure_messages if mess in tag_info] if not found: return fallback_singular logger.debug("tags dir does not exist at %s%s, either", base, fallback_singular) return None
def _upload_distributions(self, package): # See if creating an sdist (and maybe a wheel) actually works. # Also, this makes the sdist (and wheel) available for plugins. # And for twine, who will just upload the created files. logger.info( "Making a source distribution of a fresh tag checkout (in %s).", self.data['tagworkingdir']) result = utils.execute_command(utils.setup_py('sdist')) utils.show_interesting_lines(result) if self.pypiconfig.create_wheel(): logger.info("Making a wheel of a fresh tag checkout (in %s).", self.data['tagworkingdir']) result = utils.execute_command(utils.setup_py('bdist_wheel')) utils.show_interesting_lines(result) if not self.pypiconfig.is_pypi_configured(): logger.error( "You must have a properly configured %s file in " "your home dir to upload to a Python package index.", pypi.DIST_CONFIG_FILE) if utils.ask("Do you want to continue without uploading?", default=False): return sys.exit(1) # Run extra entry point self._run_hooks('before_upload') # Get list of all files to upload. files_in_dist = [ os.path.join('dist', filename) for filename in os.listdir('dist') ] # Get servers/repositories. servers = self.pypiconfig.distutils_servers() register = self.pypiconfig.register_package() for server in servers: if register: question = "Register and upload" else: question = "Upload" default = True exact = False if server == 'pypi' and not package_in_pypi(package): logger.info("This package is NOT registered on PyPI.") # We are not yet on pypi. To avoid an 'Oops..., # sorry!' when registering and uploading an internal # package we default to False here. default = False exact = True if utils.ask("%s to %s" % (question, server), default=default, exact=exact): if register: logger.info("Registering...") # We only need the first file, it has all the needed info self._retry_twine('register', server, files_in_dist[0]) for filename in files_in_dist: self._retry_twine('upload', server, filename) self._close_all_repositories()
def _tags_name(self): """Return name for tags dir Normally the plural /tags, but some projects have the singular /tag. """ default_plural = 'tags' fallback_singular = 'tag' # svn 1.7 introduced a slightly different message and a warning code. failure_messages = ["non-existent in that revision", "W160013", ] base = self._base_from_svn() tag_info = execute_command('svn list %s%s' % (base, default_plural)) # Look for one of the failure messages: found = [1 for mess in failure_messages if mess in tag_info] if not found: return default_plural logger.debug("tags dir does not exist at %s%s", base, default_plural) tag_info = execute_command('svn list %s%s' % (base, fallback_singular)) # Look for one of the failure messages: found = [1 for mess in failure_messages if mess in tag_info] if not found: return fallback_singular logger.debug("tags dir does not exist at %s%s, either", base, fallback_singular) return None
def _upload_distributions(self, package): # See if creating an sdist (and maybe a wheel) actually works. # Also, this makes the sdist (and wheel) available for plugins. # And for twine, who will just upload the created files. logger.info( "Making a source distribution of a fresh tag checkout (in %s).", self.data['tagworkingdir']) result = utils.execute_command(utils.setup_py('sdist')) utils.show_interesting_lines(result) if self.pypiconfig.create_wheel(): logger.info("Making a wheel of a fresh tag checkout (in %s).", self.data['tagworkingdir']) result = utils.execute_command(utils.setup_py('bdist_wheel')) utils.show_interesting_lines(result) if not self.pypiconfig.is_pypi_configured(): logger.error( "You must have a properly configured %s file in " "your home dir to upload to a Python package index.", pypi.DIST_CONFIG_FILE) if utils.ask("Do you want to continue without uploading?", default=False): return sys.exit(1) # Run extra entry point self._run_hooks('before_upload') # Get list of all files to upload. files_in_dist = sorted([ os.path.join('dist', filename) for filename in os.listdir('dist') ]) register = self.pypiconfig.register_package() # If TWINE_REPOSITORY_URL is set, use it. if self.pypiconfig.twine_repository_url(): if not self._ask_upload( package, self.pypiconfig.twine_repository_url(), register): return if register: self._retry_twine("register", None, files_in_dist[:1]) self._retry_twine("upload", None, files_in_dist) # Only upload to the server specified in the environment return # Upload to the repository in the environment or .pypirc servers = self.pypiconfig.distutils_servers() for server in servers: if not self._ask_upload(package, server, register): continue if register: logger.info("Registering...") # We only need the first file, it has all the needed info self._retry_twine('register', server, files_in_dist[:1]) self._retry_twine('upload', server, files_in_dist)
def _upload_distributions(self, package): # See if creating an sdist (and maybe a wheel) actually works. # Also, this makes the sdist (and wheel) available for plugins. # And for twine, who will just upload the created files. logger.info( "Making a source distribution of a fresh tag checkout (in %s).", self.data['tagworkingdir']) result = utils.execute_command(utils.setup_py('sdist')) utils.show_interesting_lines(result) if self.pypiconfig.create_wheel(): logger.info("Making a wheel of a fresh tag checkout (in %s).", self.data['tagworkingdir']) result = utils.execute_command(utils.setup_py('bdist_wheel')) utils.show_interesting_lines(result) if not self.pypiconfig.is_pypi_configured(): logger.error( "You must have a properly configured %s file in " "your home dir to upload to a Python package index.", pypi.DIST_CONFIG_FILE) if utils.ask("Do you want to continue without uploading?", default=False): return sys.exit(1) # Run extra entry point self._run_hooks('before_upload') # Get list of all files to upload. files_in_dist = sorted([ os.path.join('dist', filename) for filename in os.listdir('dist')] ) # Get servers/repositories. servers = self.pypiconfig.distutils_servers() register = self.pypiconfig.register_package() for server in servers: default = True exact = False if server == 'pypi' and not package_in_pypi(package): logger.info("This package does NOT exist yet on PyPI.") # We are not yet on pypi. To avoid an 'Oops..., # sorry!' when registering and uploading an internal # package we default to False here. default = False exact = True question = "Upload" if register: question = "Register and upload" if utils.ask("%s to %s" % (question, server), default=default, exact=exact): if register: logger.info("Registering...") # We only need the first file, it has all the needed info self._retry_twine('register', server, files_in_dist[:1]) self._retry_twine('upload', server, files_in_dist)
def fillchangelog(context): default_location = None setup_cfg = pypi.SetupConfig() config = setup_cfg.config if config and config.has_option('zest.releaser', 'history_file'): default_location = config.get('zest.releaser', 'history_file') vcs = zest.releaser.choose.version_control() history_file = vcs.history_file(location=default_location) if history_file: try: found = zest.releaser.utils.get_last_tag(vcs) log_command = vcs.cmd_log_since_tag(found) except SystemExit: log_command = get_all_commits_command(vcs) if log_command: data = execute_command(log_command) pretty_data = prettyfy_logs(data, vcs) print('These are all the commits since the last tag:') print('') print('\n'.join(pretty_data)) if zest.releaser.utils.ask('Do you want to add those commits to the CHANGES file?', True): new_history_lines = [] history_lines, history_encoding = read_text_file(history_file) history_lines = history_lines.split('\n') for line in history_lines: current_position = history_lines.index(line) new_history_lines.append(line) if line.lower().find('unreleased') != -1: # current_position + 1 == ---------------- # current_position + 2 == blank # current_position + 3 == - Nothing changed yet. # current_position + 4 == blank new_history_lines.append(history_lines[current_position + 1]) new_history_lines.append(history_lines[current_position + 2]) new_history_lines.extend(pretty_data) new_history_lines.extend(history_lines[current_position + 4:]) break contents = '\n'.join(new_history_lines) write_text_file(history_file, contents) msg = 'Update changelog' commit_cmd = vcs.cmd_commit(msg) commit = execute_command(commit_cmd) print(commit) else: print('History file not found. Skipping.')
def prepare_checkout_dir(self, prefix): # Watch out: some git versions can't clone into an existing # directory, even when it is empty. temp = tempfile.mkdtemp(prefix=prefix) cwd = os.getcwd() os.chdir(temp) cmd = 'git clone %s %s' % (self.reporoot, 'gitclone') logger.debug(execute_command(cmd)) clonedir = os.path.join(temp, 'gitclone') os.chdir(clonedir) cmd = 'git submodule update --init --recursive' logger.debug(execute_command(cmd)) os.chdir(cwd) return clonedir
def prepare_checkout_dir(self, prefix): # Watch out: some git versions can't clone into an existing # directory, even when it is empty. temp = tempfile.mkdtemp(prefix=prefix) cwd = os.getcwd() os.chdir(temp) cmd = ['git', 'clone', '--depth', '1', self.reporoot, 'gitclone'] logger.debug(execute_command(cmd)) clonedir = os.path.join(temp, 'gitclone') os.chdir(clonedir) cmd = ['git', 'submodule', 'update', '--init', '--recursive'] logger.debug(execute_command(cmd)) os.chdir(cwd) return clonedir
def is_clean_checkout(self): """Is this a clean checkout? """ head = execute_command('git symbolic-ref --quiet HEAD') # This returns something like 'refs/heads/maurits-warn-on-tag' # or nothing. Nothing would be bad as that indicates a # detached head: likely a tag checkout if not head: # Greetings from Nearly Headless Nick. return False if execute_command('git status --short --untracked-files=no'): # Uncommitted changes in files that are tracked. return False return True
def _upload_distributions(self, package): # See if creating an sdist (and maybe a wheel) actually works. # Also, this makes the sdist (and wheel) available for plugins. # And for twine, who will just upload the created files. logger.info( "Making a source distribution of a fresh tag checkout (in %s).", self.data['tagworkingdir']) result = utils.execute_command(utils.setup_py('sdist')) utils.show_interesting_lines(result) if self.pypiconfig.create_wheel(): logger.info("Making a wheel of a fresh tag checkout (in %s).", self.data['tagworkingdir']) result = utils.execute_command(utils.setup_py('bdist_wheel')) utils.show_interesting_lines(result) if not self.pypiconfig.is_pypi_configured(): logger.warn( "You must have a properly configured %s file in " "your home dir to upload to a package index.", pypi.DIST_CONFIG_FILE) return # Get list of all files to upload. files_in_dist = [ os.path.join('dist', filename) for filename in os.listdir('dist') ] # Run extra entry point self._run_hooks('before_upload') # Get servers/repositories. if self.pypiconfig.is_old_pypi_config(): servers = ['pypi'] else: # The user may have defined other servers to upload to. servers = self.pypiconfig.distutils_servers() for server in servers: if self.pypiconfig.register_package(): logger.info("Registering...") # We only need the first file, it has all the needed info self._retry_twine('register', server, files_in_dist[0]) question = "Upload" default = True exact = False if utils.ask("%s to %s" % (question, server), default=default, exact=exact): for filename in files_in_dist: self._retry_twine('upload', server, filename) self._close_all_repositories()
def prepare_checkout_dir(self, prefix): # Watch out: some git versions can't clone into an existing # directory, even when it is empty. temp = tempfile.mkdtemp(prefix=prefix) cwd = os.getcwd() os.chdir(temp) cmd = 'git clone %s %s' % (self.workingdir, 'gitclone') logger.debug(execute_command(cmd)) clonedir = os.path.join(temp, 'gitclone') os.chdir(clonedir) cmd = 'git submodule update --init --recursive' logger.debug(execute_command(cmd)) os.chdir(cwd) return clonedir
def get_setup_py_version(self): if os.path.exists('setup.py'): # First run egg_info, as that may get rid of some warnings # that otherwise end up in the extracted version, like # UserWarnings. utils.execute_command(utils.setup_py('egg_info')) version = utils.execute_command( utils.setup_py('--version')).splitlines()[0] if 'Traceback' in version: # Likely cause is for example forgetting to 'import # os' when using 'os' in setup.py. logger.critical('The setup.py of this package has an error:') print(version) logger.critical('No version found.') sys.exit(1) return utils.strip_version(version)
def available_tags(self): tag_info = execute_command('hg tags') tags = [line[:line.find(' ')] for line in tag_info.split('\n')] tags = [tag for tag in tags if tag] tags.remove('tip') # Not functional for us logger.debug("Available tags: %r", tags) return tags
def checkout_from_tag(self, version): package = self.name prefix = '%s-%s-' % (package, version) tagdir = self.prepare_checkout_dir(prefix) os.chdir(tagdir) cmd = self.cmd_checkout_from_tag(version, tagdir) print(utils.execute_command(cmd))
def check_towncrier(data, check_sanity=True, do_draft=True): """Check if towncrier can and should be run. This is a zest.releaser entrypoint that is called during prerelease, postrelease, and bumpversion. Not in all cases are all checks useful, so there are some options. For example, postrelease should not complain that there are no news fragments. """ if TOWNCRIER_MARKER in data: # We have already been called. return data[TOWNCRIER_MARKER] if not data.get('update_history', True): # Someone has instructed zest.releaser to not update the history, # and it was not us, because our marker was not set, # so we should not update the history either. logger.debug( 'update_history is already False, so towncrier will not be run.') data[TOWNCRIER_MARKER] = False return False # Check if towncrier should be applied. result = _is_towncrier_wanted() if not result: logger.debug('towncrier is not wanted.') else: result = _towncrier_executable() if result: logger.debug('towncrier should be run.') # zest.releaser should not update the history. # towncrier will do that. data['update_history'] = False if check_sanity: _report_newsfragments_sanity() if do_draft: # Do a draft. cmd = deepcopy(result) cmd.extend([ '--draft', '--version', data.get('new_version', 't.b.d.'), '--yes', ]) # We would like to pass ['--package' 'package name'] as well, # but that is not yet in a release of towncrier. logger.info( 'Doing dry-run of towncrier to see what would be changed: %s', utils.format_command(cmd)) print(utils.execute_command(cmd)) else: print( dedent(""" According to the pyproject.toml file, towncrier is used to update the changelog. The problem is: we cannot find the towncrier executable. Please make sure it is on your PATH.""")) if not utils.ask('Do you want to continue anyway?', default=False): sys.exit(1) data[TOWNCRIER_MARKER] = result return result
def available_tags(self): tag_info = execute_command(['hg', 'tags']) tags = [line[:line.find(' ')] for line in tag_info.split('\n')] tags = [tag for tag in tags if tag] tags.remove('tip') # Not functional for us logger.debug("Available tags: %r", tags) return tags
def _diff_and_commit(self): diff_cmd = self.vcs.cmd_diff() diff = execute_command(diff_cmd) if sys.version.startswith('2.6.2'): # python2.6.2 bug... http://bugs.python.org/issue5170 This is the # spot it can surface as we show a part of the changelog which can # contain every kind of character. The rest is mostly ascii. print("Diff results:") print(diff) else: # Common case logger.info("The '%s':\n\n%s\n", diff_cmd, diff) if utils.ask("OK to commit this"): msg = self.data['commit_msg'] % self.data msg = self.update_commit_message(msg) commit_cmd = self.vcs.cmd_commit(msg) commit = execute_command(commit_cmd) logger.info(commit)
def _push(self): """Offer to push changes, if needed.""" push_cmds = self.vcs.push_commands() if not push_cmds: return if utils.ask("OK to push commits to the server?"): for push_cmd in push_cmds: output = execute_command(push_cmd) logger.info(output)
def is_clean_checkout(self): """Is this a clean checkout? """ # The --quiet option ignores untracked (unknown and ignored) # files, which seems reasonable. if execute_command(['hg', 'status', '--quiet']): # Local changes. return False return True
def checkout_from_tag(self, version): package = self.name prefix = '%s-%s-' % (package, version) # Not all hg versions can do a checkout in an existing or even # just in the current directory. tagdir = tempfile.mktemp(prefix=prefix) cmd = self.cmd_checkout_from_tag(version, tagdir) print(execute_command(cmd)) os.chdir(tagdir)
def is_clean_checkout(self): """Is this a clean checkout? """ # The --quiet option ignores untracked (unknown and ignored) # files, which seems reasonable. if execute_command('hg status --quiet'): # Local changes. return False return True
def _svn_info(self): """Return svn url""" our_info = execute_command('svn info') if not hasattr(self, '_cached_url'): url = [line for line in our_info.split('\n') if line.startswith('URL')][0] # In English, you have 'URL:', in French 'URL :' self._cached_url = url.split(':', 1)[1].strip() return self._cached_url
def _svn_info(self): """Return svn url""" if not hasattr(self, '_cached_url'): our_info = execute_command('svn info') url = [line for line in our_info.split('\n') if line.startswith('URL')][0] # In English, you have 'URL:', in French 'URL :' self._cached_url = url.split(':', 1)[1].strip() return self._cached_url
def _info_if_tag_already_exists(self): if self.data['tag_already_exists']: # Safety feature. version = self.data['version'] tag = self.data['tag'] q = ("There is already a tag %s, show " "if there are differences?" % version) if utils.ask(q): diff_command = self.vcs.cmd_diff_last_commit_against_tag(tag) print(utils.format_command(diff_command)) print(execute_command(diff_command))
def _push(self): """Offer to push changes, if needed.""" push_cmds = self.vcs.push_commands() if not push_cmds: return default_anwer = self.pypiconfig.push_changes() if utils.ask("OK to push commits to the server?", default=default_anwer): for push_cmd in push_cmds: output = execute_command(push_cmd) logger.info(output)
def translations(data): if data["name"] != "django-countries": return if not ask("Pull translations from transifex and compile", default=True): return _handlers = logger.handlers logger.handlers = [] try: cmd_pull(argv=["-a", "--minimum-perc=60"], path_to_tx=find_dot_tx()) finally: logger.handlers = _handlers _cwd = os.getcwd() os.chdir(os.path.dirname(django_countries.__file__)) try: call_command("compilemessages") execute_command(["git", "add", "locale"]) finally: os.chdir(_cwd)
def translations(data): if data['name'] != 'django-countries': return if not ask('Pull translations from transifex and compile', default=True): return _handlers = logger.handlers logger.handlers = [] try: cmd_pull(argv=['-a', '--minimum-perc=60'], path_to_tx=find_dot_tx()) finally: logger.handlers = _handlers _cwd = os.getcwd() os.chdir(os.path.dirname(django_countries.__file__)) try: call_command('compilemessages') execute_command(['git', 'add', 'locale']) finally: os.chdir(_cwd)
def add_files_to_release(data): """zest.releaser entry point for adding files to the release.""" if data.get('name') != 'ploneintranet': # This entry point should only do something when releasing the # ploneintranet package. return # cd to the directory that has the fresh tag checkout and use the # Makefile to fetch the release bundles. logger.info('Making fetchrelease call in tag checkout.') res = execute_command('cd {0} && make fetchrelease'.format(data['tagdir'])) print(res) logger.info('fetchrelease done.')
def call_towncrier(data): """Entrypoint: run towncrier when available and configured.""" # check_towncrier will either give a path to towncrier, or False. path = check_towncrier(data) if not path: return # path is a list cmd = deepcopy(path) cmd.extend(['--version', data['new_version'], '--yes']) # We would like to pass ['--package' 'package name'] as well, # but that is not yet in a release of towncrier. logger.info('Running command to update news: %s', utils.format_command(cmd)) print(utils.execute_command(cmd)) # towncrier stages the changes with git, # which BTW means that our plugin requires git. logger.info('The staged git changes are:') print(utils.execute_command(['git', 'diff', '--cached'])) logger.info('towncrier has finished updating the history file ' 'and has staged the above changes in git.')
def _check_if_tag_already_exists(self): """Check if tag already exists and show the difference if so""" version = self.data["version"] if self.vcs.tag_exists(version): self.data["tag_already_exists"] = True q = "There is already a tag %s, show " "if there are differences?" % version if utils.ask(q): diff_command = self.vcs.cmd_diff_last_commit_against_tag(version) print(diff_command) print(execute_command(diff_command)) else: self.data["tag_already_exists"] = False
def _push(self): """ Offer to push changes, if needed. """ push_cmds = self.vcs.push_commands() if not push_cmds: return if ask("OK to push commits to the server?"): if has_extension(self.vcs, 'gitflow'): # Push both develop and master branches. First push master, # then develop, because that is the branch we want to end on. for branch in [ self.vcs.gitflow_get_branch("master"), self.vcs.gitflow_get_branch("develop")]: if branch != self.vcs.current_branch(): self.vcs.gitflow_switch_to_branch(branch) for push_cmd in push_cmds: output = execute_command(push_cmd) logger.info(output) else: for push_cmd in push_cmds: output = execute_command(push_cmd) logger.info(output)
def _check_if_tag_already_exists(self): """Check if tag already exists and show the difference if so""" version = self.data['version'] if self.vcs.tag_exists(version): self.data['tag_already_exists'] = True q = ("There is already a tag %s, show " "if there are differences?" % version) if utils.ask(q): diff_command = self.vcs.cmd_diff_last_commit_against_tag( version) print(diff_command) print(execute_command(diff_command)) else: self.data['tag_already_exists'] = False
def main(): utils.configure_logging() vcs = zest.releaser.choose.version_control() if len(sys.argv) > 1: found = sys.argv[-1] else: found = utils.get_last_tag(vcs) name = vcs.name full_tag = vcs.tag_url(found) logger.debug("Picked tag %r for %s (currently at %r).", full_tag, name, vcs.version) logger.info("Showing log since tag %s and the last commit.", full_tag) log_command = vcs.cmd_log_since_tag(found) print(utils.format_command(log_command)) print(execute_command(log_command))
def _push(self): """ Offer to push changes, if needed. """ push_cmds = self.vcs.push_commands() if not push_cmds: return if ask("OK to push commits to the server?"): if has_extension(self.vcs, 'gitflow'): # Push both develop and master branches. First push master, # then develop, because that is the branch we want to end on. for branch in [ self.vcs.gitflow_get_branch("master"), self.vcs.gitflow_get_branch("develop") ]: if branch != self.vcs.current_branch(): self.vcs.gitflow_switch_to_branch(branch) for push_cmd in push_cmds: output = execute_command(push_cmd) logger.info(output) else: for push_cmd in push_cmds: output = execute_command(push_cmd) logger.info(output)
def main(): utils.configure_logging() vcs = zest.releaser.choose.version_control() if len(sys.argv) > 1: found = sys.argv[-1] else: found = utils.get_last_tag(vcs) name = vcs.name full_tag = vcs.tag_url(found) logger.debug("Picked tag %r for %s (currently at %r).", full_tag, name, vcs.version) logger.info("Showing log since tag %s and the last commit.", full_tag) log_command = vcs.cmd_log_since_tag(found) print(log_command) print(execute_command(log_command))
def main(): utils.configure_logging() vcs = zest.releaser.choose.version_control() if len(sys.argv) > 1: found = sys.argv[-1] else: found = utils.get_last_tag(vcs) name = vcs.name full_tag = vcs.tag_url(found) logger.debug("Picked tag '%s' for %s (currently at '%s').", full_tag, name, vcs.version) logger.info("Showing differences from the last commit against tag %s", full_tag) diff_command = vcs.cmd_diff_last_commit_against_tag(found) print(diff_command) print(execute_command(diff_command))
def is_clean_checkout(self): """Is this a clean checkout? When you try to do commits in bazaar but you are on a tag you will get this error: "working tree is out of date, run 'bzr update'" That should be clear enough already. Well, we can run 'bzr status' and see what we get. """ # Check for changes to versioned files. if execute_command('bzr status --versioned'): # Local changes. return False return True
def cmd_log_since_tag(self, version): """Return log since a tagged version till the last commit of the working copy. """ url = self._svn_info() tag_url = self.tag_url(version) tag_info = execute_command('svn info --non-interactive %s' % tag_url) # Search for: Last Changed Rev: 42761 revision = None for line in tag_info.split('\n'): line = line.lower() if len(line.split(':')) == 2: revision = line.split(':')[-1].strip() if not revision: logger.error('Could not find revision when tag was made: %s', tag_info) sys.exit(1) return "svn --non-interactive log -r%s:HEAD %s" % (revision, url)
def cmd_log_since_tag(self, version): """Return log since a tagged version till the last commit of the working copy. """ url = self._svn_info() tag_url = self.tag_url(version) tag_info = execute_command('svn info %s' % tag_url) # Search for: Last Changed Rev: 42761 revision = None for line in tag_info.split('\n'): line = line.lower() if len(line.split(':')) == 2: revision = line.split(':')[-1].strip() if not revision: logger.error('Could not find revision when tag was made: %s', tag_info) sys.exit(1) return "svn --non-interactive log -r%s:HEAD %s" % (revision, url)
def _make_tag(self): if self.data["tag_already_exists"]: return cmds = self.vcs.cmd_create_tag(self.data["version"]) if not isinstance(cmds, list): cmds = [cmds] if len(cmds) == 1: print("Tag needed to proceed, you can use the following command:") for cmd in cmds: print(cmd) if utils.ask("Run this command"): print(execute_command(cmd)) else: # all commands are needed in order to proceed normally print("Please create a tag for %s yourself and rerun." % (self.data["version"],)) sys.exit(1) if not self.vcs.tag_exists(self.data["version"]): print("\nFailed to create tag %s!" % (self.data["version"],)) sys.exit(1)
def version_control(): """Return an object that provides the version control interface based on the detected version control system.""" curdir_contents = os.listdir('.') if '.svn' in curdir_contents: return svn.Subversion() elif '.hg' in curdir_contents: return hg.Hg() elif '.bzr' in curdir_contents: return bzr.Bzr() elif '.git' in curdir_contents: return git.Git() else: # Try finding an svn checkout *not* in the root. last_try = utils.execute_command("svn info") if 'Repository' in last_try: return svn.Subversion() logger.critical('No version control system detected.') sys.exit(1)
def _pypi_command(self, command): """Run a command that accesses PyPI or similar. We offer to retry the command if it fails. """ try: # Note that if something goes wrong, it may just be # because we detect a warning: the command may have # succeeded after all. So the fail message is a bit # cautious. result = utils.execute_command( command, allow_retry=True, fail_message="Package upload may have failed.") except utils.CommandException: logger.error("Command failed: %r", command) tagdir = self.data.get('tagdir') if tagdir: logger.info("Note: we have placed a fresh tag checkout " "in %s. You can retry uploading from there " "if needed.", tagdir) sys.exit(1) return result