コード例 #1
0
 def test_create_initial_upstream_branch(self):
     os.makedirs(self.git_repo)
     # Setup the repo
     execute_command('git init .', cwd=self.git_repo)
     # Execute
     from bloom.import_upstream import create_initial_upstream_branch
     cmd = 'git branch --no-color'
     out = check_output(cmd, shell=True, cwd=self.git_repo)
     assert out.count('upstream') == 0
     create_initial_upstream_branch(self.git_repo)
     out = check_output(cmd, shell=True, cwd=self.git_repo)
     assert out.count('upstream') == 1
     # Clean up
     from shutil import rmtree
     rmtree(self.git_repo)
コード例 #2
0
 def test_create_initial_upstream_branch(self):
     os.makedirs(self.git_repo)
     # Setup the repo
     execute_command('git init .', cwd=self.git_repo)
     # Execute
     from bloom.import_upstream import create_initial_upstream_branch
     cmd = 'git branch --no-color'
     out = check_output(cmd, shell=True, cwd=self.git_repo)
     assert out.count('upstream') == 0
     create_initial_upstream_branch(self.git_repo)
     out = check_output(cmd, shell=True, cwd=self.git_repo)
     assert out.count('upstream') == 1
     # Clean up
     from shutil import rmtree
     rmtree(self.git_repo)
コード例 #3
0
 def test_convert_catkin_to_bloom(self):
     os.makedirs(self.git_repo)
     # Setup the repo
     execute_command('git init .', cwd=self.git_repo)
     f = open(os.path.join(self.git_repo, 'catkin.conf'), 'w+')
     f.write('[catkin]\n\tupstream = git://github.com/ros/langs.git'
             '\n\tupstreamtype = git\n')
     f.close()
     execute_command('git add ./*', cwd=self.git_repo)
     execute_command('git commit -m "Init"', cwd=self.git_repo)
     execute_command('git branch catkin', cwd=self.git_repo)
     # Execute the converter
     from bloom.import_upstream import convert_catkin_to_bloom
     convert_catkin_to_bloom(self.git_repo)
     # Assert correct behavior
     output = check_output('git branch --no-color',
                           shell=True,
                           cwd=self.git_repo)
     assert output.count('catkin') == 0
     assert output.count('* bloom') == 1, output
     expected_str = '[bloom]\n\tupstream = git://github.com/ros/langs.git' \
                    '\n\tupstreamtype = git\n'
     conf_path = os.path.join(self.git_repo, 'bloom.conf')
     assert os.path.exists(conf_path)
     assert open(conf_path, 'r').read() == expected_str
     from shutil import rmtree
     rmtree(self.git_repo)
コード例 #4
0
 def test_convert_catkin_to_bloom(self):
     os.makedirs(self.git_repo)
     # Setup the repo
     execute_command('git init .', cwd=self.git_repo)
     f = open(os.path.join(self.git_repo, 'catkin.conf'), 'w+')
     f.write('[catkin]\n\tupstream = git://github.com/ros/langs.git'
             '\n\tupstreamtype = git\n')
     f.close()
     execute_command('git add ./*', cwd=self.git_repo)
     execute_command('git commit -m "Init"', cwd=self.git_repo)
     execute_command('git branch catkin', cwd=self.git_repo)
     # Execute the converter
     from bloom.import_upstream import convert_catkin_to_bloom
     convert_catkin_to_bloom(self.git_repo)
     # Assert correct behavior
     output = check_output('git branch --no-color', shell=True,
                           cwd=self.git_repo)
     assert output.count('catkin') == 0
     assert output.count('* bloom') == 1, output
     expected_str = '[bloom]\n\tupstream = git://github.com/ros/langs.git' \
                    '\n\tupstreamtype = git\n'
     conf_path = os.path.join(self.git_repo, 'bloom.conf')
     assert os.path.exists(conf_path)
     assert open(conf_path, 'r').read() == expected_str
     from shutil import rmtree
     rmtree(self.git_repo)
コード例 #5
0
def parse_bloom_conf(cwd=None):
    """
    Parses the bloom.conf file in the current directory and returns info in it.
    """
    bloom_conf = show('bloom', 'bloom.conf', directory=cwd)
    with open('.bloom.conf', 'w+') as f:
        f.write(bloom_conf)
    cmd = 'git config -f .bloom.conf bloom.upstream'
    upstream_repo = check_output(cmd, shell=True, cwd=cwd).strip()
    cmd = 'git config -f .bloom.conf bloom.upstreamtype'
    upstream_type = check_output(cmd, shell=True, cwd=cwd).strip()
    try:
        cmd = 'git config -f .bloom.conf bloom.upstreambranch'
        upstream_branch = check_output(cmd, shell=True, cwd=cwd).strip()
    except CalledProcessError:
        upstream_branch = ''
    os.remove('.bloom.conf')
    return upstream_repo, upstream_type, upstream_branch
コード例 #6
0
def get_tags(directory=None):
    """
    Returns a list of tags in the git repository.

    :param directory: directory in which to preform this action
    :returns: list of tags

    :raises: subprocess.CalledProcessError if any git calls fail
    """
    out = check_output('git tag -l', shell=True, cwd=directory)
    return [l.strip() for l in out.splitlines()]
コード例 #7
0
ファイル: git.py プロジェクト: po1/bloom
def get_tags(directory=None):
    """
    Returns a list of tags in the git repository.

    :param directory: directory in which to preform this action
    :returns: list of tags

    :raises: subprocess.CalledProcessError if any git calls fail
    """
    out = check_output('git tag -l', shell=True, cwd=directory)
    return [l.strip() for l in out.splitlines()]
コード例 #8
0
ファイル: config.py プロジェクト: clynamen/bloom
def convert_old_bloom_conf(prefix=None):
    prefix = prefix if prefix is not None else 'convert'
    tracks_dict = get_tracks_dict_raw()
    track = prefix
    track_count = 0
    while track in tracks_dict['tracks']:
        track_count += 1
        track = prefix + str(track_count)
    track_dict = copy.copy(DEFAULT_TEMPLATE)
    cmd = 'git config -f bloom.conf bloom.upstream'
    upstream_repo = check_output(cmd, shell=True).strip()
    cmd = 'git config -f bloom.conf bloom.upstreamtype'
    upstream_type = check_output(cmd, shell=True).strip()
    try:
        cmd = 'git config -f bloom.conf bloom.upstreambranch'
        upstream_branch = check_output(cmd, shell=True).strip()
    except subprocess.CalledProcessError:
        upstream_branch = ''
    for key in template_entry_order:
        if key == 'vcs_uri':
            track_dict[key] = upstream_repo
            continue
        if key == 'vcs_type':
            track_dict[key] = upstream_type
            continue
        if key == 'vcs_uri':
            track_dict[key] = upstream_branch or None
            continue
        track_dict[key] = track_dict[key].default
    debug('Converted bloom.conf:')
    with open('bloom.conf', 'r') as f:
        debug(f.read())
    debug('To this track:')
    debug(str({track: track_dict}))
    tracks_dict['tracks'][track] = track_dict
    write_tracks_dict_raw(tracks_dict)
    execute_command('git rm bloom.conf', shell=True)
    execute_command('git commit -m "Removed bloom.conf"', shell=True)
    # Now move the old bloom branch into master
    upconvert_bloom_to_config_branch()
コード例 #9
0
def convert_old_bloom_conf(prefix=None):
    prefix = prefix if prefix is not None else 'convert'
    tracks_dict = get_tracks_dict_raw()
    track = prefix
    track_count = 0
    while track in tracks_dict['tracks']:
        track_count += 1
        track = prefix + str(track_count)
    track_dict = copy.copy(DEFAULT_TEMPLATE)
    cmd = 'git config -f bloom.conf bloom.upstream'
    upstream_repo = check_output(cmd, shell=True).strip()
    cmd = 'git config -f bloom.conf bloom.upstreamtype'
    upstream_type = check_output(cmd, shell=True).strip()
    try:
        cmd = 'git config -f bloom.conf bloom.upstreambranch'
        upstream_branch = check_output(cmd, shell=True).strip()
    except subprocess.CalledProcessError:
        upstream_branch = ''
    for key in template_entry_order:
        if key == 'vcs_uri':
            track_dict[key] = upstream_repo
            continue
        if key == 'vcs_type':
            track_dict[key] = upstream_type
            continue
        if key == 'vcs_uri':
            track_dict[key] = upstream_branch or None
            continue
        track_dict[key] = track_dict[key].default
    debug('Converted bloom.conf:')
    with open('bloom.conf', 'r') as f:
        debug(f.read())
    debug('To this track:')
    debug(str({track: track_dict}))
    tracks_dict['tracks'][track] = track_dict
    write_tracks_dict_raw(tracks_dict)
    execute_command('git rm bloom.conf', shell=True)
    execute_command('git commit -m "Removed bloom.conf"', shell=True)
    # Now move the old bloom branch into master
    upconvert_bloom_to_config_branch()
コード例 #10
0
def has_untracked_files(directory=None):
    """
    Returns True is the working branch has untracked files, False otherwise.

    :param directory: directory in which to preform this action
    :returns: True if there are untracked files (or dirs), otherwise False

    :raises: subprocess.CalledProcessError if any git calls fail
    """
    out = check_output('git status', shell=True, cwd=directory)
    if '# Untracked files:' in out:
        return True
    return False
コード例 #11
0
ファイル: git.py プロジェクト: po1/bloom
def has_untracked_files(directory=None):
    """
    Returns True is the working branch has untracked files, False otherwise.

    :param directory: directory in which to preform this action
    :returns: True if there are untracked files (or dirs), otherwise False

    :raises: subprocess.CalledProcessError if any git calls fail
    """
    out = check_output('git status', shell=True, cwd=directory)
    if '# Untracked files:' in out:
        return True
    return False
コード例 #12
0
def get_root(directory=None):
    """
    Returns the git root directory above the given dir.

    If the given dir is not in a git repository, None is returned.

    :param directory: directory to query from, if None the cwd is used
    :returns: root of git repository or None if not a git repository
    """
    cmd = 'git rev-parse --show-toplevel'
    try:
        output = check_output(cmd, shell=True, cwd=directory, stderr=PIPE)
    except CalledProcessError:
        return None
    return output.strip()
コード例 #13
0
ファイル: git.py プロジェクト: jbohren-forks/bloom
def get_last_tag_by_date(directory=None):
    """
    Returns the most recent, by date, tag in the given local git repository.

    :param directory: the directory in which to run the query
    :returns: the most recent tag by date, else '' if there are no tags

    :raises: subprocess.CalledProcessError if git command fails
    """
    cmd = "git for-each-ref --sort='*authordate' " "--format='%(refname:short)' refs/tags/upstream"
    output = check_output(cmd, shell=True, cwd=directory, stderr=PIPE)
    output = output.splitlines()
    if len(output) == 0:
        return ""
    return output[-1]
コード例 #14
0
ファイル: git.py プロジェクト: po1/bloom
def get_root(directory=None):
    """
    Returns the git root directory above the given dir.

    If the given dir is not in a git repository, None is returned.

    :param directory: directory to query from, if None the cwd is used
    :returns: root of git repository or None if not a git repository
    """
    cmd = 'git rev-parse --show-toplevel'
    try:
        output = check_output(cmd, shell=True, cwd=directory, stderr=PIPE)
    except CalledProcessError:
        return None
    return output.strip()
コード例 #15
0
ファイル: git.py プロジェクト: hershwg/bloom
def has_changes(directory=None):
    """
    Returns True if the working branch has local changes, False otherwise.

    :param directory: directory in which to preform this action
    :returns: True if there are local changes, otherwise False

    :raises: subprocess.CalledProcessError if any git calls fail
    """
    out = check_output('git status', shell=True, cwd=directory)
    if 'nothing to commit (working directory clean)' in out:
        return False
    if 'nothing added to commit' in out:
        return False
    return True
コード例 #16
0
ファイル: git.py プロジェクト: rohbotics/bloom
def get_remotes(directory=None):
    """
    Returns a list of remote names.

    :param directory: directory to list remotes from
    :returns: a list of git remotes

    :raises: RuntimeError if directory is not a git repository
    """
    root = get_root(directory)
    checked_dir = directory or os.getcwd()
    if root is None:
        raise RuntimeError("Directory '{0}' is not in a git repository.".format(checked_dir))
    cmd = "git remote -v"
    output = check_output(cmd, shell=True, cwd=root, stderr=PIPE)
    return list(set([x.split()[0].strip() for x in output.splitlines() if x.strip()]))
コード例 #17
0
def get_last_tag_by_date(directory=None):
    """
    Returns the most recent, by date, tag in the given local git repository.

    :param directory: the directory in which to run the query
    :returns: the most recent tag by date, else '' if there are no tags

    :raises: subprocess.CalledProcessError if git command fails
    """
    cmd = "git for-each-ref --sort='*authordate' " \
          "--format='%(refname:short)' refs/tags/upstream"
    output = check_output(cmd, shell=True, cwd=directory, stderr=PIPE)
    output = output.splitlines()
    if len(output) == 0:
        return ''
    return output[-1]
コード例 #18
0
ファイル: git.py プロジェクト: jbohren-forks/bloom
def get_last_tag_by_version(directory=None):
    """
    Returns the most recent, by date, tag in the given local git repository.

    :param directory: the directory in which to run the query
    :returns: the most recent tag by date, else '' if there are no tags

    :raises: subprocess.CalledProcessError if git command fails
    """
    cmd = "git for-each-ref --sort='*authordate' " "--format='%(refname:short)' refs/tags/upstream"
    output = check_output(cmd, shell=True, cwd=directory, stderr=PIPE)
    tags = []
    versions = []
    for line in output.splitlines():
        tags.append(line.strip())
        versions.append(parse_version(line.strip()))
    return tags[versions.index(max(versions))] if versions else ""
コード例 #19
0
ファイル: git.py プロジェクト: vrabaud/bloom
def has_changes(directory=None):
    """
    Returns True if the working branch has local changes, False otherwise.

    :param directory: directory in which to preform this action
    :returns: True if there are local changes, otherwise False

    :raises: subprocess.CalledProcessError if any git calls fail
    """
    out = check_output('git status', shell=True, cwd=directory)
    if 'nothing to commit (working directory clean)' in out:
        return False
    if 'nothing to commit, working directory clean' in out:
        return False
    if 'nothing added to commit' in out:
        return False
    return True
コード例 #20
0
def get_commit_hash(reference, directory=None):
    """
    Returns the SHA-1 commit hash for the given reference.

    :param reference: any git reference (branch or tag) to resolve to SHA-1
    :param directory: directory in which to preform this action
    :returns: SHA-1 commit hash for the given reference

    :raises: subprocess.CalledProcessError if any git calls fail
    """
    # Track remote branch
    if branch_exists(reference, local_only=False, directory=directory):
        if not branch_exists(reference, local_only=True, directory=directory):
            track_branches(reference, directory)
    cmd = 'git show-branch --sha1-name ' + reference
    out = check_output(cmd, shell=True, cwd=directory)
    return out.split('[')[1].split(']')[0]
コード例 #21
0
ファイル: git.py プロジェクト: po1/bloom
def get_commit_hash(reference, directory=None):
    """
    Returns the SHA-1 commit hash for the given reference.

    :param reference: any git reference (branch or tag) to resolve to SHA-1
    :param directory: directory in which to preform this action
    :returns: SHA-1 commit hash for the given reference

    :raises: subprocess.CalledProcessError if any git calls fail
    """
    # Track remote branch
    if branch_exists(reference, local_only=False, directory=directory):
        if not branch_exists(reference, local_only=True, directory=directory):
            track_branches(reference, directory)
    cmd = 'git show-branch --sha1-name ' + reference
    out = check_output(cmd, shell=True, cwd=directory)
    return out.split('[')[1].split(']')[0]
コード例 #22
0
ファイル: git.py プロジェクト: rohbotics/bloom
def has_submodules(directory=None):
    """
    Returns True if the git repository at this directory has submodules

    :param directory: directory to check for submodules in
    :returns: True if there are submodules, False otherwise

    :raises: RuntimeError if directory is not a git repository
    """
    root = get_root(directory)
    checked_dir = directory or os.getcwd()
    if root is None:
        raise RuntimeError("Directory '{0}' is not in a git repository.".format(checked_dir))
    cmd = "git submodule status"
    output = check_output(cmd, shell=True, cwd=root, stderr=PIPE)
    if not output.strip():
        return False
    return True
コード例 #23
0
def get_last_tag_by_version(directory=None):
    """
    Returns the most recent, by date, tag in the given local git repository.

    :param directory: the directory in which to run the query
    :returns: the most recent tag by date, else '' if there are no tags

    :raises: subprocess.CalledProcessError if git command fails
    """
    cmd = "git for-each-ref --sort='*authordate' " \
          "--format='%(refname:short)' refs/tags/upstream"
    output = check_output(cmd, shell=True, cwd=directory, stderr=PIPE)
    tags = []
    versions = []
    for line in output.splitlines():
        tags.append(line.strip())
        versions.append(parse_version(line.strip()))
    return tags[versions.index(max(versions))] if versions else ''
コード例 #24
0
def get_current_branch(directory=None):
    """
    Returns the current git branch by parsing the output of `git branch`

    This will raise a RuntimeError if the current working directory is not
    a git repository.  If no branch could be determined it will return None,
    i.e. (no branch) will return None.

    :param directory: directory in which to run the command
    :returns: current git branch or None if none can be determined, (no branch)

    :raises: subprocess.CalledProcessError if git command fails
    """
    cmd = 'git branch --no-color'
    output = check_output(cmd, shell=True, cwd=directory)
    output = output.splitlines()
    for token in output:
        if token.strip().startswith('*'):
            token = token[2:]
            if token == '(no branch)':
                return None
            return token
    return None
コード例 #25
0
ファイル: git.py プロジェクト: po1/bloom
def get_current_branch(directory=None):
    """
    Returns the current git branch by parsing the output of `git branch`

    This will raise a RuntimeError if the current working directory is not
    a git repository.  If no branch could be determined it will return None,
    i.e. (no branch) will return None.

    :param directory: directory in which to run the command
    :returns: current git branch or None if none can be determined, (no branch)

    :raises: subprocess.CalledProcessError if git command fails
    """
    cmd = 'git branch --no-color'
    output = check_output(cmd, shell=True, cwd=directory)
    output = output.splitlines()
    for token in output:
        if token.strip().startswith('*'):
            token = token[2:]
            if token == '(no branch)':
                return None
            return token
    return None
コード例 #26
0
ファイル: git.py プロジェクト: po1/bloom
def get_branches(local_only=False, directory=None):
    """
    Returns a list of branches in the git repository.

    :param local_only: if True, do not return remote branches, False by default
    :param directory: directory in which to preform this action
    :returns: list of branches

    :raises: subprocess.CalledProcessError if any git calls fail
    """
    cmd = 'git branch --no-color'
    if not local_only:
        cmd += ' -a'
    out = check_output(cmd, shell=True, cwd=directory)
    branches = []
    for line in out.splitlines():
        if line.count('HEAD -> ') > 0:
            continue
        if line.count('(no branch)') > 0:
            continue
        line = line.strip('*').strip()
        branches.append(line)
    return branches
コード例 #27
0
def get_branches(local_only=False, directory=None):
    """
    Returns a list of branches in the git repository.

    :param local_only: if True, do not return remote branches, False by default
    :param directory: directory in which to preform this action
    :returns: list of branches

    :raises: subprocess.CalledProcessError if any git calls fail
    """
    cmd = 'git branch --no-color'
    if not local_only:
        cmd += ' -a'
    out = check_output(cmd, shell=True, cwd=directory)
    branches = []
    for line in out.splitlines():
        if line.count('HEAD -> ') > 0:
            continue
        if line.count('(no branch)') > 0:
            continue
        line = line.strip('*').strip()
        branches.append(line)
    return branches
コード例 #28
0
ファイル: release.py プロジェクト: po1/bloom
def perform_release(repository, track, distro, new_track, interactive):
    release_repo = get_release_repo(repository, distro)
    with change_directory(release_repo.get_path()):
        # Check for push permissions
        try:
            info(fmt("@{gf}@!==> @|Testing for push permission on release repository"))
            check_output('git push', shell=True)
        except subprocess.CalledProcessError:
            error("Cannot push to remote release repository.", exit=True)
        # Check to see if the old bloom.conf exists
        if check_for_bloom_conf(repository):
            # Convert to a track
            info("Old bloom.conf file detected.")
            info(fmt("@{gf}@!==> @|Converting to bloom.conf to track"))
            convert_old_bloom_conf(None if new_track else distro)
        # Check that the track is valid
        tracks_dict = get_tracks_dict_raw()
        # If new_track, create the new track first
        if new_track:
            if not track:
                error("You must specify a track when creating a new one.", exit=True)
            overrides = {'ros_distro': distro}
            if track in tracks_dict['tracks']:
                warning("Track '{0}' exists, editing instead...".format(track))
                edit_track_cmd(track)
            else:
                # Create a new track called <track>,
                # copying an existing track if possible,
                # and overriding the ros_distro
                new_track_cmd(track, copy_track='', overrides=overrides)
                tracks_dict = get_tracks_dict_raw()
        if track and track not in tracks_dict['tracks']:
            error("Given track '{0}' does not exist in release repository."
                  .format(track))
            error("Available tracks: " + str(tracks_dict['tracks'].keys()),
                  exit=True)
        elif not track:
            tracks = tracks_dict['tracks'].keys()
            # Error out if there are no tracks
            if len(tracks) == 0:
                error("Release repository has no tracks.")
                info("Manually clone the repository:")
                info("  git clone {0}".format(release_repo.get_url()))
                info("And then create a new track:")
                info("  git-bloom-config new <track name>")
                error("Run again after creating a track.", exit=True)
            # Error out if there is more than one track
            if len(tracks) != 1:
                error("No track specified and there is not just one track.")
                error("Please specify one of the available tracks: " +
                      str(tracks), exit=True)
            # Get the only track
            track = tracks[0]
        # Ensure the track is complete
        track_dict = tracks_dict['tracks'][track]
        update_track(track_dict)
        tracks_dict['tracks'][track] = track_dict
        write_tracks_dict_raw(tracks_dict)
        # Run the release
        info(fmt("@{gf}@!==> @|") +
             "Releasing '{0}' using release track '{1}'"
             .format(repository, track))
        cmd = 'git-bloom-release ' + str(track)
        info(fmt("@{bf}@!==> @|@!" + str(cmd)))
        try:
            subprocess.check_call(cmd, shell=True)
        except subprocess.CalledProcessError:
            error("Release failed, exiting.", exit=True)
        info(fmt(_success) +
             "Released '{0}' using release track '{1}' successfully"
             .format(repository, track))
        # Check for pushing
        if interactive:
            info("Releasing complete, push?")
            if not maybe_continue():
                error("User answered no to continue prompt, aborting.",
                      exit=True)
        # Push changes to the repository
        info(fmt("@{gf}@!==> @|") +
             "Pushing changes to release repository for '{0}'"
             .format(repository))
        cmd = 'git push --all'
        info(fmt("@{bf}@!==> @|@!" + str(cmd)))
        try:
            subprocess.check_call(cmd, shell=True)
        except subprocess.CalledProcessError:
            error("Pushing changes failed, would you like to add '--force' to 'git push --all'?")
            if not maybe_continue():
                error("Pushing changes failed, exiting.", exit=True)
            cmd += ' --force'
            info(fmt("@{bf}@!==> @|@!" + str(cmd)))
            try:
                subprocess.check_call(cmd, shell=True)
            except subprocess.CalledProcessError:
                error("Pushing changes failed, exiting.", exit=True)
        info(fmt(_success) + "Pushed changes successfully")
        # Push tags to the repository
        info(fmt("@{gf}@!==> @|") +
             "Pushing tags to release repository for '{0}'"
             .format(repository))
        cmd = 'git push --tags'
        info(fmt("@{bf}@!==> @|@!" + str(cmd)))
        try:
            subprocess.check_call(cmd, shell=True)
        except subprocess.CalledProcessError:
            error("Pushing changes failed, would you like to add '--force' to 'git push --tags'?")
            if not maybe_continue():
                error("Pushing tags failed, exiting.", exit=True)
            cmd += ' --force'
            info(fmt("@{bf}@!==> @|@!" + str(cmd)))
            try:
                subprocess.check_call(cmd, shell=True)
            except subprocess.CalledProcessError:
                error("Pushing tags failed, exiting.", exit=True)
        info(fmt(_success) + "Pushed tags successfully")
        # Propose github pull request
        info(fmt("@{gf}@!==> @|") +
             "Generating pull request to distro file located at '{0}'"
             .format(ROS_DISTRO_FILE).format(distro))
        generate_ros_distro_diff(track, repository, distro)
        info("In the future this will create a pull request for you, done for now...")
        info(fmt(_success) + "Pull request opened at: '{0}'".format('Not yet Implemented'))
コード例 #29
0
ファイル: release.py プロジェクト: isherman/bloom
 def _my_run(cmd):
     info(fmt("@{bf}@!==> @|@!" + str(cmd)))
     out = check_output(cmd, stderr=subprocess.STDOUT, shell=True)
     if out:
         info(out, use_prefix=False)