rc.xsh 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228
  1. $VI_MODE = True
  2. $AUTO_PUSHD = True
  3. $XONSH_AUTOPAIR = True
  4. # tab selection: do not execute cmd when pressing enter
  5. $COMPLETIONS_CONFIRM = True
  6. $_Z_EXCLUDE_DIRS = ['/tmp']
  7. xontrib load vox z
  8. def _last_exit_status():
  9. try:
  10. exit_status = __xonsh_history__.rtns[-1]
  11. return exit_status if exit_status != 0 else None
  12. except IndexError:
  13. return None
  14. $PROMPT_FIELDS['last_exit_status'] = _last_exit_status
  15. $SHLVL = int($SHLVL) + 1 if 'SHLVL' in ${...} else 1
  16. $XONSH_STDERR_PREFIX = '{RED}'
  17. $XONSH_STDERR_POSTFIX = '{NO_COLOR}'
  18. $DYNAMIC_CWD_WIDTH = '30%'
  19. $DYNAMIC_CWD_ELISION_CHAR = '…'
  20. $PROMPT = ''.join([
  21. '{RED}{last_exit_status:[{}] }',
  22. '{BOLD_GREEN}{user}@{hostname} ',
  23. '{YELLOW}{cwd} ',
  24. '{{BLUE}}{} '.format('{prompt_end}' * $SHLVL),
  25. '{NO_COLOR}',
  26. ])
  27. $RIGHT_PROMPT = '{gitstatus}{env_name: {}}'
  28. $XONSH_APPEND_NEWLINE = True
  29. import datetime as dt
  30. import io
  31. import json
  32. import os
  33. import re
  34. import select
  35. import shlex
  36. import shutil
  37. import stat
  38. import subprocess
  39. import sys
  40. import threading
  41. os.umask(stat.S_IWGRP | stat.S_IRWXO) # 027
  42. # default locale
  43. # will be used for all non-explicitly set LC_* variables
  44. $LANG = 'en_US.UTF-8'
  45. # fallback locales
  46. # GNU gettext gives preference to LANGUAGE over LC_ALL and LANG
  47. # for the purpose of message handling
  48. # https://www.gnu.org/software/gettext/manual/html_node/The-LANGUAGE-variable.html
  49. # cave: if this list contains 'de(_.*)?' at any (sic!) position
  50. # vim 7.4.1689 will switch to german
  51. $LANGUAGE = ':'.join(['en_US', 'en'])
  52. $LC_COLLATE = 'C.UTF-8'
  53. # char classification, case conversion & other char attrs
  54. $LC_CTYPE = 'de_AT.UTF-8'
  55. # $ locale currency_symbol
  56. $LC_MONETARY = 'de_AT.UTF-8'
  57. # $ locale -k LC_NUMERIC | head -n 3
  58. # decimal_point="."
  59. # thousands_sep=""
  60. # grouping=-1
  61. $LC_NUMERIC = 'C.UTF-8'
  62. # A4
  63. $LC_PAPER = 'de_AT.UTF-8'
  64. USER_BIN_PATH = os.path.join($HOME, '.local', 'bin')
  65. if os.path.isdir(USER_BIN_PATH):
  66. $PATH.insert(0, USER_BIN_PATH)
  67. $PAGER = 'less'
  68. $EDITOR = 'vim'
  69. # required by pinentry-tty when using gpg command:
  70. $GPG_TTY = $(tty)
  71. if shutil.which('gpgconf'):
  72. # required by scute
  73. $GPG_AGENT_INFO = $(gpgconf --list-dir agent-socket).rstrip() + ':0:1'
  74. if not 'SSH_CLIENT' in ${...}:
  75. # in gnupg 2.1.13 the location of agents socket changed
  76. $SSH_AUTH_SOCK = $(gpgconf --list-dir agent-ssh-socket).rstrip()
  77. # wrapper for termite required when launching termite from ranger:
  78. $TERMCMD = os.path.join(os.path.dirname(__file__), 'ranger-termite-termcmd')
  79. # https://docs.docker.com/engine/security/trust/content_trust/
  80. $DOCKER_CONTENT_TRUST = 1
  81. class DockerImage:
  82. def __init__(self, image):
  83. attrs, = json.loads(subprocess.check_output(['sudo', 'docker', 'image', 'inspect', image])
  84. .decode(sys.stdout.encoding))
  85. self._id = attrs['Id']
  86. self._tags = attrs['RepoTags']
  87. def __repr__(self):
  88. return '{}(id={!r}, tags={!r})'.format(type(self).__name__, self._id, self._tags)
  89. @classmethod
  90. def build(cls, dockerfile):
  91. out = io.BytesIO()
  92. with StdoutTee(out) as tee:
  93. p = subprocess.Popen(['sudo', 'docker', 'build', '-'],
  94. stdin=subprocess.PIPE, stdout=tee)
  95. p.stdin.write(dockerfile.encode())
  96. p.stdin.close()
  97. assert p.wait() == 0, 'docker build failed'
  98. image_id, = re.search(rb'^Successfully built (\S+)$', out.getvalue(), re.MULTILINE).groups()
  99. return cls(image_id.decode(sys.stdout.encoding))
  100. @classmethod
  101. def pull(cls, image):
  102. out = io.BytesIO()
  103. with StdoutTee(out) as tee:
  104. subprocess.run(['sudo', 'docker', 'image', 'pull', image], stdout=tee)
  105. repo_digest, = re.search(rb'^Digest: (sha\S+:\S+)$', out.getvalue(), re.MULTILINE).groups()
  106. return cls('{}@{}'.format(image, repo_digest.decode()))
  107. def run(self, detach=False, publish_ports=[], args=[], caps=[]):
  108. params = ['sudo', 'docker', 'run', '--rm']
  109. if detach:
  110. params.append('--detach')
  111. else:
  112. params.extend(['--interactive', '--tty'])
  113. params.extend('--publish=' + ':'.join([str(a) for a in p]) for p in publish_ports)
  114. params.extend(['--security-opt=no-new-privileges', '--cap-drop=all'])
  115. params.extend(['--cap-add={}'.format(c) for c in caps])
  116. params.append(self._id)
  117. params.extend(args)
  118. sys.stderr.write('{}\n'.format(shlex_join(params)))
  119. subprocess.run(params, check=True)
  120. class StdoutTee:
  121. def __init__(self, sink):
  122. self._sink = sink
  123. def __enter__(self):
  124. self._read_fd, self._write_fd = os.pipe()
  125. self._thread = threading.Thread(target=self._loop)
  126. self._thread.start()
  127. return self
  128. def _loop(self):
  129. while True:
  130. try:
  131. data = os_read_non_blocking(self._read_fd)
  132. except OSError: # fd closed
  133. return
  134. if data:
  135. self._sink.write(data)
  136. sys.stdout.buffer.write(data)
  137. sys.stdout.flush()
  138. def fileno(self):
  139. return self._write_fd
  140. def __exit__(self, exc_type, exc_value, traceback):
  141. os.close(self._read_fd)
  142. os.close(self._write_fd)
  143. self._thread.join()
  144. def dpkg_listfiles(pkg_name):
  145. assert isinstance(pkg_name, str)
  146. paths = $(dpkg --listfiles @(pkg_name)).split('\n')[:-1]
  147. assert len(paths) > 0, 'pkg {!r} not installed'.format(pkg_name)
  148. return paths
  149. def dpkg_search(path_search_pattern):
  150. assert isinstance(path_search_pattern, str)
  151. return re.findall(
  152. '^(\S+): (.*)$\n',
  153. $(dpkg --search @(path_search_pattern)),
  154. flags=re.MULTILINE,
  155. )
  156. def dpkg_welse(cmd):
  157. pkg_name, cmd_path = dpkg_which(cmd)
  158. return dpkg_listfiles(pkg_name)
  159. def dpkg_which(cmd):
  160. cmd_path = shutil.which(cmd)
  161. assert cmd_path, 'cmd {!r} not found'.format(cmd)
  162. matches = dpkg_search(cmd_path)
  163. assert len(matches) != 0, '{!r} not installed via dpkg'.format(cmd_path)
  164. assert len(matches) == 1
  165. return matches[0]
  166. def locate(*patterns, match_all=True, ignore_case=True):
  167. params = []
  168. if match_all:
  169. params.insert(0, '--all')
  170. if ignore_case:
  171. params.insert(0, '--ignore-case')
  172. return $(locate @(params) -- @(patterns)).split('\n')[:-1]
  173. def os_read_non_blocking(fd, buffer_size_bytes=8*1024, timeout_seconds=0.1):
  174. if fd in select.select([fd], [], [], timeout_seconds)[0]:
  175. return os.read(fd, buffer_size_bytes)
  176. else:
  177. return None
  178. def shlex_join(params):
  179. return ' '.join(shlex.quote(p) for p in params)
  180. def timestamp_now_utc():
  181. return dt.datetime.utcnow().replace(tzinfo=dt.timezone.utc)
  182. def timestamp_now_local():
  183. # if called without tz argument astimezone() assumes
  184. # the system local timezone for the target timezone
  185. return timestamp_now_utc().astimezone()
  186. def timestamp_iso_local():
  187. # if called without tz argument astimezone() assumes
  188. # the system local timezone for the target timezone
  189. return timestamp_now_local().strftime('%Y%m%dT%H%M%S%z')
  190. aliases['d'] = ['sudo', 'docker']
  191. aliases['dpkg-welse'] = lambda args: '\n'.join(dpkg_welse(args[0]))
  192. aliases['dpkg-which'] = lambda args: '\t'.join(dpkg_which(args[0]))
  193. aliases['g'] = ['git']
  194. aliases['ll'] = ['ls', '-l', '--all', '--indicator-style=slash',
  195. '--human-readable', '--time-style=long-iso', '--color=auto']
  196. if shutil.which('startx') and $(tty).rstrip() == '/dev/tty1':
  197. startx
  198. # vim: filetype=python