def script_runner(local_tree: WorkingTree, script: str, commit_pending: Optional[bool] = None) -> str: """Run a script in a tree and commit the result. This ignores newly added files. :param local_tree: Local tree to run script in :param script: Script to run :param commit_pending: Whether to commit pending changes (True, False or None: only commit if there were no commits by the script) :return: Description as reported by script """ last_revision = local_tree.last_revision() p = subprocess.Popen(script, cwd=local_tree.basedir, stdout=subprocess.PIPE, shell=True) (description_encoded, err) = p.communicate(b"") if p.returncode != 0: raise errors.BzrCommandError("Script %s failed with error code %d" % (script, p.returncode)) new_revision = local_tree.last_revision() description = description_encoded.decode() if last_revision == new_revision and commit_pending is None: # Automatically commit pending changes if the script did not # touch the branch. commit_pending = True if commit_pending: try: new_revision = local_tree.commit(description, allow_pointless=False) except PointlessCommit: pass if new_revision == last_revision: raise ScriptMadeNoChanges() return description
def script_runner( # noqa: C901 local_tree: WorkingTree, script: Union[str, List[str]], commit_pending: Optional[bool] = None, resume_metadata=None, subpath: str = '', update_changelog: Optional[bool] = None, extra_env: Optional[Dict[str, str]] = None, committer: Optional[str] = None ) -> CommandResult: # noqa: C901 """Run a script in a tree and commit the result. This ignores newly added files. Args: local_tree: Local tree to run script in script: Script to run commit_pending: Whether to commit pending changes (True, False or None: only commit if there were no commits by the script) """ if control_files_in_root(local_tree, subpath): debian_path = subpath else: debian_path = os.path.join(subpath, "debian") if update_changelog is None: dch_guess = guess_update_changelog(local_tree, debian_path) if dch_guess: if isinstance(dch_guess, tuple): # lintian-brush < 1.22 update_changelog, explanation = dch_guess else: update_changelog = dch_guess.update_changelog explanation = dch_guess.explanation logging.info('%s', explanation) else: # Assume yes. update_changelog = True cl_path = os.path.join(debian_path, 'changelog') try: with open(local_tree.abspath(cl_path), 'r') as f: cl = Changelog(f) source_name = cl[0].package except FileNotFoundError: source_name = None env = dict(os.environ) if extra_env: env.update(extra_env) env['SVP_API'] = '1' if source_name: env['DEB_SOURCE'] = source_name if update_changelog: env['DEB_UPDATE_CHANGELOG'] = 'update' else: env['DEB_UPDATE_CHANGELOG'] = 'leave' last_revision = local_tree.last_revision() orig_tags = local_tree.branch.tags.get_tag_dict() with tempfile.TemporaryDirectory() as td: env['SVP_RESULT'] = os.path.join(td, 'result.json') if resume_metadata: env['SVP_RESUME'] = os.path.join(td, 'resume-metadata.json') with open(env['SVP_RESUME'], 'w') as f: json.dump(resume_metadata, f) p = subprocess.Popen( script, cwd=local_tree.abspath(subpath), stdout=subprocess.PIPE, shell=isinstance(script, str), env=env) (description_encoded, err) = p.communicate(b"") try: with open(env['SVP_RESULT'], 'r') as f: try: result_json = json.load(f) except json.decoder.JSONDecodeError as e: raise ResultFileFormatError(e) except FileNotFoundError: result_json = None if p.returncode != 0: if result_json is not None: raise DetailedFailure.from_json(source_name, result_json) raise ScriptFailed(script, p.returncode) # If the changelog didn't exist earlier, then hopefully it was created # now. if source_name is None: try: with open(local_tree.abspath(cl_path), 'r') as f: cl = Changelog(f) source_name = cl[0].package except FileNotFoundError: raise MissingChangelog(cl_path) if result_json is not None: result = CommandResult.from_json(source_name, result_json) else: result = CommandResult(source=source_name) if not result.description: result.description = description_encoded.decode().replace("\r", "") new_revision = local_tree.last_revision() if result.tags is None: result.tags = [] for name, revid in local_tree.branch.tags.get_tag_dict().items(): if orig_tags.get(name) != revid: result.tags.append((name, revid)) if last_revision == new_revision and commit_pending is None: # Automatically commit pending changes if the script did not # touch the branch. commit_pending = True if commit_pending: if update_changelog and result.description and local_tree.has_changes(): add_changelog_entry( local_tree, os.path.join(debian_path, 'changelog'), [result.description]) local_tree.smart_add([local_tree.abspath(subpath)]) try: new_revision = local_tree.commit( result.description, allow_pointless=False, committer=committer) except PointlessCommit: pass if new_revision == last_revision: raise ScriptMadeNoChanges() result.old_revision = last_revision result.new_revision = new_revision return result
def script_runner( # noqa: C901 local_tree: WorkingTree, script: Union[str, List[str]], commit_pending: Optional[bool] = None, resume_metadata=None, subpath: str = '', committer: Optional[str] = None, extra_env: Optional[Dict[str, str]] = None, ) -> CommandResult: # noqa: C901 """Run a script in a tree and commit the result. This ignores newly added files. Args: local_tree: Local tree to run script in script: Script to run commit_pending: Whether to commit pending changes (True, False or None: only commit if there were no commits by the script) """ env = dict(os.environ) if extra_env: env.update(extra_env) env['SVP_API'] = '1' last_revision = local_tree.last_revision() orig_tags = local_tree.branch.tags.get_tag_dict() with tempfile.TemporaryDirectory() as td: env['SVP_RESULT'] = os.path.join(td, 'result.json') if resume_metadata: env['SVP_RESUME'] = os.path.join(td, 'resume-metadata.json') with open(env['SVP_RESUME'], 'w') as f: json.dump(resume_metadata, f) p = subprocess.Popen(script, cwd=local_tree.abspath(subpath), stdout=subprocess.PIPE, shell=isinstance(script, str), env=env) (description_encoded, err) = p.communicate(b"") try: with open(env['SVP_RESULT'], 'r') as f: try: result_json = json.load(f) except json.decoder.JSONDecodeError as e: raise ResultFileFormatError(e) except FileNotFoundError: result_json = None if p.returncode != 0: if result_json is not None: raise DetailedFailure.from_json(result_json) raise ScriptFailed(script, p.returncode) if result_json is not None: result = CommandResult.from_json(result_json) else: result = CommandResult() if not result.description: result.description = description_encoded.decode() new_revision = local_tree.last_revision() if result.tags is None: result.tags = [] for name, revid in local_tree.branch.tags.get_tag_dict().items(): if orig_tags.get(name) != revid: result.tags.append((name, revid)) if last_revision == new_revision and commit_pending is None: # Automatically commit pending changes if the script did not # touch the branch. commit_pending = True if commit_pending: local_tree.smart_add([local_tree.abspath(subpath)]) try: new_revision = local_tree.commit(result.description, allow_pointless=False, committer=committer) except PointlessCommit: pass if new_revision == last_revision: raise ScriptMadeNoChanges() result.old_revision = last_revision result.new_revision = local_tree.last_revision() return result