def docker_stop(name, kill=False, signal='KILL', rm=False, exception=False, show=False): """Return True if successfully stopped - name: name of the container - kill: if True, kill the container instead of stopping - signal: signal to send to the container if kill is True - rm: if True, remove the container after stop/kill - exception: if True and docker has an error response, raise an exception - show: if True, show the docker commands and output """ if not docker_ok(exception=exception): return False if kill is False: cmd = 'docker stop {}'.format(name) else: cmd = 'docker kill --signal {} {}'.format(signal, name) output = bh.run_output(cmd, show=show) if show is True: print(output) if "Error response from daemon:" in output: return False if rm is True: cmd = 'docker rm {}'.format(name) output = bh.run_output(cmd, show=show) if show is True: print(output) if "Error response from daemon:" in output: return False return True
def git_branch_date(path='', branch='', fetch=False, debug=False, timeout=None, exception=False, show=False): """Return datetime string (and relative age) of branch - path: path to git repo, if not using current working directory - branch: name of branch - fetch: if True, call git_fetch func before calling the generated `git` command - debug: if True, insert breakpoint right before subprocess.check_output - timeout: number of seconds to wait before stopping cmd - exception: if True, raise a ValueError if path is not in a repo - also raise Exception if git command has an error - show: if True, show the `git` command before executing Prefix branch name with 'origin/' to get date info of remote branch """ common_kwargs = dict(debug=debug, timeout=timeout, exception=exception, show=show) cmd = 'git show --format="%ci %cr" {} | head -n 1'.format(branch) with ctx_repo_path_root(path, fetch=fetch, **common_kwargs): output = bh.run_output(cmd, **common_kwargs) return output
def git_stashlist(path='', debug=False, timeout=None, exception=True, show=False): """Return a list of any local stashes - path: path to git repo, if not using current working directory - debug: if True, insert breakpoint right before subprocess.check_output - timeout: number of seconds to wait before stopping cmd - exception: if True, raise a ValueError if path is not in a repo - also raise Exception if git command has an error - show: if True, show the `git` command before executing """ common_kwargs = dict(debug=debug, timeout=timeout, exception=exception, show=show) stashes = [] cmd = 'git stash list' with ctx_repo_path_root(path, **common_kwargs): output = bh.run_output(cmd, **common_kwargs) if not output or 'fatal:' in output: return stashes stashes = ih.splitlines(output) return stashes
def git_tags(path='', debug=False, timeout=None, exception=True, show=False): """Return a list of all tags with most recent first - path: path to git repo, if not using current working directory - debug: if True, insert breakpoint right before subprocess.check_output - timeout: number of seconds to wait before stopping cmd - exception: if True, raise a ValueError if path is not in a repo - also raise Exception if git command has an error - show: if True, show the `git` command before executing """ common_kwargs = dict(debug=debug, timeout=timeout, exception=exception, show=show) tags = [] cmd = 'git describe --tags $(git rev-list --tags) 2>/dev/null' with ctx_repo_path_root(path, **common_kwargs): output = bh.run_output(cmd, **common_kwargs) if not output or 'fatal:' in output: return tags for tag in re.split('\r?\n', output): if not RX_NON_TAG.match(tag): tags.append(tag) return tags
def git_tag_message(path='', debug=False, tag='', timeout=None, exception=True, show=False): """Return the message for specified tag - path: path to git repo, if not using current working directory - tag: name of a tag that was made - debug: if True, insert breakpoint right before subprocess.check_output - timeout: number of seconds to wait before stopping cmd - exception: if True, raise a ValueError if path is not in a repo - also raise Exception if git command has an error - show: if True, show the `git` command before executing """ common_kwargs = dict(debug=debug, timeout=timeout, exception=exception, show=show) message = '' with ctx_repo_path_root(path, **common_kwargs): if not tag: tag = git_last_tag(**common_kwargs) if not tag: return message cmd = 'git tag -n99 {}'.format(tag) output = bh.run_output(cmd, **common_kwargs) message = output.replace(tag, '').strip() return message
def git_last_tag(path='', debug=False, timeout=None, exception=True, show=False): """Return the most recent tag made - path: path to git repo, if not using current working directory - debug: if True, insert breakpoint right before subprocess.check_output - timeout: number of seconds to wait before stopping cmd - exception: if True, raise a ValueError if path is not in a repo - also raise Exception if git command has an error - show: if True, show the `git` command before executing """ common_kwargs = dict(debug=debug, timeout=timeout, exception=exception, show=show) cmd = 'git describe --tags $(git rev-list --tags --max-count=1 2>/dev/null) 2>/dev/null' with ctx_repo_path_root(path, **common_kwargs): common_kwargs['exception'] = False output = bh.run_output(cmd, **common_kwargs) output = '' if 'fatal:' in output else output return output
def git_current_tracking_branch(path='', debug=False, timeout=None, exception=True, show=False): """Return remote tracking branch for current branch - path: path to git repo, if not using current working directory - debug: if True, insert breakpoint right before subprocess.check_output - timeout: number of seconds to wait before stopping cmd - exception: if True, raise a ValueError if path is not in a repo - also raise Exception if git command has an error - show: if True, show the `git` command before executing """ common_kwargs = dict(debug=debug, timeout=timeout, exception=exception, show=show) cmd = 'git branch -r' with ctx_repo_path_root(path, **common_kwargs): output = bh.run_output(cmd, **common_kwargs) branch = git_current_branch(**common_kwargs) results = bh.tools.grep_output(output, pattern='/{}$'.format(branch), extra_pipe='grep -v HEAD') if results: return results[0]
def get_branch_date(branch): """Return datetime (and relative age) of branch Prefix branch name with 'origin/' to get date info of remote branch """ cmd = 'git show --format="%ci %cr" {} | head -n 1'.format(branch) return bh.run_output(cmd)
def git_do(path='', fetch=False, cmd=None, output=False, debug=False, timeout=None, exception=True, show=False): """Run specified cmd and either return the output or the exit status Return a list of any local files that are not tracked in the git repo - path: path to git repo, if not using current working directory - fetch: if True, call git_fetch func before calling the generated `git` command - cmd: string with shell command (required) - output: if True, capture output of cmd and return it; otherwise return exit status of cmd - debug: if True, insert breakpoint right before subprocess.check_output - timeout: number of seconds to wait before stopping cmd - exception: if True, raise a ValueError if path is not in a repo - also raise Exception if git command has an error - show: if True, show the `git` command before executing """ assert cmd, 'The cmd argument is required' common_kwargs = dict(debug=debug, timeout=timeout, exception=exception, show=show) with ctx_repo_path_root(path, fetch=fetch, **common_kwargs): if output: result = bh.run_output(cmd, **common_kwargs) else: result = bh.run(cmd, **common_kwargs) return result
def select_commit_to_tag(n=10): """Select a commit hash from recent commits - n: number of recent commits to choose from """ SOURCE_BRANCH = _get_repo_settings('SOURCE_BRANCH') branch = get_branch_name() assert branch == SOURCE_BRANCH, ( 'Must be on {} branch to select commit, not {}'.format( SOURCE_BRANCH, branch)) last_tag = get_last_tag() cmd_part = 'git log --find-renames --no-merges --oneline' if last_tag: cmd = cmd_part + ' {}..'.format(last_tag) else: cmd = cmd_part + ' -{}'.format(n) output = bh.run_output(cmd) if not output: return items = re.split('\r?\n', output)[:n] selected = ih.make_selections(items, wrap=False, prompt='Select commit to tag') if selected: return selected[0].split(' ', 1)[0]
def seek(n): """Move forward or backwaard by n seconds""" output = bh.run_output('mocp --seek {}'.format(n), timeout=2) if 'server is not running' in output: start_server() elif output: print(output)
def git_fetch(path='', output=False, debug=False, timeout=None, exception=True, show=False): """Perform `git fetch --all --prune` - path: path to git repo, if not using current working directory - output: if True, return output of `git fetch --all --prune` - debug: if True, insert breakpoint right before subprocess.check_output - timeout: number of seconds to wait before stopping cmd - exception: if True, raise a ValueError if path is not in a repo - also raise Exception if git command has an error - show: if True, show the `git` command before executing """ common_kwargs = dict(debug=debug, timeout=timeout, exception=exception, show=show) cmd = 'git fetch --all --prune' with ctx_repo_path_root(path, fetch=False, **common_kwargs): output = bh.run_output(cmd, **common_kwargs) if show: print(output) if output: return output
def do_ssh(ip, pem_file, user, command='', timeout=None, verbose=False): """Actually SSH to a server - ip: IP address - pem_file: absolute path to pem file - user: remote SSH user - command: an optional command to run on the remote server - if a command is specified, it will be run on the remote server and the output will be returned - if no command is specified, the SSH session will be interactive """ ssh_command = 'ssh -i {} -o "StrictHostKeyChecking no" -o ConnectTimeout=2 {}@{}' cmd = ssh_command.format(pem_file, user, ip) if command: cmd = cmd + ' -t {}'.format(repr(command)) if verbose: print(cmd) result = None if command: result = bh.run_output(cmd, timeout=timeout) if verbose: print(result) else: result = bh.run(cmd) return result
def grep_output(output, pattern=None, regex=None, invert=False, extra_pipe=None): """Use grep to match lines of output against pattern - output: some output you would be piping to grep in a shell environment - pattern: grep pattern string - regex: a compiled regular expression (from re.compile) - or a sting that can be passed to re.compile - invert: if True, select non-matching items (`grep -v`) - only applied when using pattern, not regex - extra_pipe: string containing other command(s) to pipe grepped output to - only applied when using pattern, not regex Return list of strings (split on newline) """ results = [] if regex: if type(regex) != re.Pattern: regex = re.compile(r'{}'.format(regex)) results = [ line for line in re.split('\r?\n', output) if regex.match(line) ] else: if pattern: if invert: cmd = 'echo {} | grep -ivE {}'.format(repr(output), repr(pattern)) else: cmd = 'echo {} | grep -iE {}'.format(repr(output), repr(pattern)) if extra_pipe: cmd += ' | {}'.format(extra_pipe) new_output = bh.run_output(cmd) else: if extra_pipe: cmd = 'echo {} | {}'.format(repr(output), extra_pipe) new_output = bh.run_output(cmd) else: new_output = output results = ih.splitlines(new_output) return results
def get_status(): """Return a list of any modified or untracked files""" cmd = 'git status -s' output = bh.run_output(cmd) results = [] if output: results = re.split('\r?\n\s*', output) return results
def get_untracked_files(): """Return a list of any local files that are not tracked in the git repo""" cmd = 'git ls-files -o --exclude-standard' output = bh.run_output(cmd) files = [] if output: files = re.split('\r?\n', output) return files
def stop_server(): output = bh.run_output('mocp --exit', timeout=2) if 'server is not running' in output: pass elif output: print(output) else: rm_pid_and_kill_jack()
def start_server(n=1, max_calls=3): output = bh.run_output('mocp --server', timeout=2) if 'Server is already running' in output: pass elif 'No valid sound driver' in output: _start_jack() sleep(.5) print(bh.run_output('mocp --server', timeout=2)) elif 'It seems that the server is already running' in output: rm_pid_and_kill_jack() if n < max_calls: start_server(n + 1) else: print(output) print('\nstart_server func was called {} times'.format(n)) elif output: print(output)
def get_unpushed_commits(): """Return a list of any local commits that have not been pushed""" cmd = 'git log --find-renames --no-merges --oneline @{u}.. 2>/dev/null' output = bh.run_output(cmd) commits = [] if output: commits = re.split('\r?\n', output) return commits
def get_stashlist(): """Return a list of any local stashes""" cmd = 'git stash list' output = bh.run_output(cmd) stashes = [] if output: stashes = re.split('\r?\n', output) return stashes
def docker_container_id(name): """Return the container ID for running container name - name: name of the container """ if not docker_ok(): return '' cmd = "docker ps | grep '\\b{}\\b$'".format(name) + " | awk '{print $1}'" return bh.run_output(cmd)
def get_info_dict(): output = bh.run_output('mocp --info', timeout=2) return dict([ (k.lower(), v.strip()) for k, v in [ line.split(':', 1) for line in output.split('\n') if line ] ])
def get_local_branches(grep=''): """Return list of local branch names (via git branch) - grep: grep pattern to filter branches by (case-insensitive) """ output = bh.run_output('git branch | cut -c 3- | grep -iE {}'.format( repr(grep))) if not output: return [] branches = re.split('\r?\n', output) return branches
def get_tag_message(tag=''): """Return the message for the most recent tag made - tag: name of a tag that was made """ if not tag: tag = get_last_tag() if not tag: return output = bh.run_output('git tag -n99 {}'.format(tag)) return output.replace(tag, '').strip()
def get_tags(): """Return a list of all tags with most recent first""" cmd = 'git describe --tags $(git rev-list --tags) 2>/dev/null' output = bh.run_output(cmd) tags = [] if not output: return tags for tag in re.split('\r?\n', output): if not RX_NON_TAG.match(tag): tags.append(tag) return tags
def get_merged_local_branches(): """Return a list of local branches that have been merged into SOURCE_BRANCH""" SOURCE_BRANCH = _get_repo_settings('SOURCE_BRANCH') cmd = 'git branch --merged {} | cut -c 3- | grep -v "^{}$"'.format( SOURCE_BRANCH, SOURCE_BRANCH) output = bh.run_output(cmd) branches = [] if not output: return branches branches = re.split('\r?\n', output) return branches
def docker_ok(exception=False): """Return True if docker is available and the docker daemon is running - exception: if True and docker not available, raise an exception """ output = bh.run_output('docker ps') if 'CONTAINER ID' not in output: if exception: raise Exception(output) else: return False return True
def ssh_to_server(ip_or_hostname, user=None, pem_file=None, private_key_file=None, command='', timeout=None, verbose=False): """Actually SSH to a server and run a command or start interactive seesion - ip_or_hostname: IP address or hostname of server - user: remote SSH user - pem_file: absolute path to pem file - private_key_file: absolute path to private key file - command: an optional command to run on the remote server - if a command is specified, it will be run on the remote server and the output will be returned - if no command is specified, the SSH session will be interactive - timeout: the number of seconds to wait for a specified command to run on the remote server - verbose: if True, print the generated SSH command - if a command is specified, print it's result as well If ip_or_hostname is NOT a configured Host in the ~/.ssh/config file, you must specify a user and either a pem_file or private_key_file. You cannot specify BOTH a pem_file and a private_key_file """ if ip_or_hostname in ssh_configured_hosts(): ssh_command = 'ssh -o "StrictHostKeyChecking no" -o ConnectTimeout=2 {}' cmd = ssh_command.format(ip_or_hostname) else: ssh_command = 'ssh -i {} -o "StrictHostKeyChecking no" -o ConnectTimeout=2 {}@{}' assert user, 'Must specify user since {} is not in ~/.ssh/config'.format( ip_or_hostname) assert pem_file or private_key_file, 'Must specify pem_file or private_key_file' assert not (pem_file and private_key_file), ( 'Cannot specify pem_file and private_key_file') if pem_file: cmd = ssh_command.format(pem_file, user, ip_or_hostname) elif private_key_file: cmd = ssh_command.format(private_key_file, user, ip_or_hostname) if command: cmd = cmd + ' -t {}'.format(repr(command)) if verbose: print(cmd) result = None if command: result = bh.run_output(cmd, timeout=timeout) if verbose: print(result) else: result = bh.run(cmd) return result
def get_origin_url(): """Return url to remote origin (from .git/config file)""" local_path = get_local_repo_path() if not local_path: return cmd = 'grep "remote \\"origin\\"" -A 2 {}/.git/config | grep url'.format( local_path) output = bh.run_output(cmd) match = RX_CONFIG_URL.match(output) if match: return match.group(1) return ''
def get_merged_remote_branches(): """Return a list of branches on origin that have been merged into SOURCE_BRANCH""" SOURCE_BRANCH = _get_repo_settings('SOURCE_BRANCH') bh.run('git fetch --all --prune >/dev/null 2>&1') cmd = 'git branch -r --merged origin/{} | grep -v origin/{} | cut -c 10-'.format( SOURCE_BRANCH, SOURCE_BRANCH) output = bh.run_output(cmd) branches = [] if not output: return branches branches = re.split('\r?\n', output) return branches
def update_branch(branch='', pop_stash=False): """Get latest changes from origin into branch - branch: name of branch to update (if not current checked out) - pop_stash: if True, do `git stash pop` at the end if a stash was made Return True if update was successful """ if branch: if branch not in get_local_branches(): cmd = 'git checkout origin/{}'.format(branch) else: cmd = 'git checkout {}'.format(branch) bh.run_or_die(cmd, show=True) branch = get_branch_name() url = get_origin_url() tracking = get_tracking_branch() if not url: print('\nLocal-only repo, not updating') return elif tracking: SOURCE_BRANCH = _get_repo_settings('SOURCE_BRANCH') NON_SELECTABLE_BRANCHES = _get_repo_settings('NON_SELECTABLE_BRANCHES') stash_output = bh.run_output('git stash', show=True) print(stash_output) ret_code = bh.run('git pull --rebase', show=True) if ret_code != 0: return if branch != SOURCE_BRANCH and branch not in NON_SELECTABLE_BRANCHES: cmd = 'git rebase origin/{}'.format(SOURCE_BRANCH) ret_code = bh.run(cmd, show=True) if ret_code != 0: return if pop_stash and stash_output != 'No local changes to save': bh.run_output('git stash pop', show=True) else: bh.run_output('git fetch', show=True) return True
def sync_settings_file(module_name): """Use vimdiff to compare default settings file with settings file in use Return None if the files already have the same content """ settings_file = get_settings_file(module_name) default_settings_file = get_default_settings_file(module_name) cmd = 'diff {} {}'.format(repr(settings_file), repr(default_settings_file)) output = bh.run_output(cmd) if output: cmd = 'vimdiff {} {}'.format(repr(settings_file), repr(default_settings_file)) bh.run(cmd)
def find_and_play(*paths): """Find all audio files at the given paths and play - paths: filename and dirname globs that either are audio files, or contain audio files """ found = ' '.join(find_audio(*paths)) if not found: print('No files found matching {}'.format(repr(paths))) return start_server() output = bh.run_output('mocp --playit {}'.format(found), timeout=10) if output: print(output)
def git_origin_url(path=''): """Return url to remote origin (from .git/config file) - path: path to git repo, if not using current working directory """ result = '' local_path = git_repo_path_root(path=path) if not local_path: return result cmd = 'grep "remote \\"origin\\"" -A 2 {}/.git/config | grep url'.format( local_path) output = bh.run_output(cmd) match = RX_CONFIG_URL.match(output) if match: result = match.group(1) return result
def git_remote_branches(path='', fetch=False, grep='', include_times=False, debug=False, timeout=None, exception=True, show=False): """Return list of remote branch names or list of dicts (via `git ls-remote --heads`) - path: path to git repo, if not using current working directory - fetch: if True, call git_fetch func before calling the generated `git` command - grep: `grep -iE` pattern to filter branches by (case-insensitive) - specify multiple patterns with '(first|second|...)' - include_times: if True, include info from git_branch_date in results - debug: if True, insert breakpoint right before subprocess.check_output - timeout: number of seconds to wait before stopping cmd - exception: if True, raise a ValueError if path is not in a repo - also raise Exception if git command has an error - show: if True, show the `git` command before executing Results are alphabetized if include_times is False, otherwise ordered by most recent commit """ common_kwargs = dict(debug=debug, timeout=timeout, exception=exception, show=show) results = [] cmd = 'git ls-remote --heads | cut -f 2- | cut -c 12-' with ctx_repo_path_root(path, fetch=fetch, **common_kwargs): output = bh.run_output(cmd, **common_kwargs) if not output or 'fatal:' in output: return results matches = bh.tools.grep_output(output, pattern=grep) branches = [ branch for branch in matches if not branch.startswith('From ') ] if include_times: results = _dates_for_branches(branches=branches, **common_kwargs) else: results = branches return results
def go(timestamp): """Jump to timestamp in the current file and play (wrapper to 'seek') - timestamp: a string in one the following formats: '3h4m5s', '2h15s', '47m', '300s', '3:04:05', '2:00:15', '47:00', '300' """ seconds = ih.timestamp_to_seconds(timestamp) if seconds is None: return if get_info_dict().get('state') == 'PAUSE': toggle_pause() output = bh.run_output('mocp --jump {}s'.format(seconds), timeout=2) if 'server is not running' in output: start_server() elif 'Segmentation fault' in output: seek(seconds - int(get_info_dict()['currentsec'])) elif output: print(output)
def find_select_and_play(*paths): """Find all audio files at the given paths, select interactively, and play - paths: filename and dirname globs that either are audio files, or contain audio files """ results = find_audio(*paths) if results: selected = ih.make_selections( results, wrap=False ) if selected: start_server() output = bh.run_output( 'mocp --playit {}'.format(' '.join(selected)), timeout=10 ) if output: print(output) else: print('No files found matching {}'.format(repr(paths)))
def toggle_pause(): output = bh.run_output('mocp --toggle-pause', timeout=2) if 'server is not running' in output: start_server() elif output: print(output)
def stop(): output = bh.run_output('mocp --stop', timeout=2) if 'server is not running' in output: pass elif output: print(output)
def previous(): output = bh.run_output('mocp --previous', timeout=2) if 'server is not running' in output: start_server() elif output: print(output)
def next(): output = bh.run_output('mocp --next', timeout=2) if 'server is not running' in output: start_server() elif output: print(output)
def volume(n): output = bh.run_output('mocp --volume {}'.format(n), timeout=2) if 'server is not running' in output: start_server() elif output: print(output)