install_extras.py 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346
  1. #!/usr/bin/env python3
  2. from __future__ import annotations
  3. import os
  4. import pathlib
  5. import platform
  6. import shutil
  7. import subprocess
  8. import tarfile
  9. import zipfile
  10. import httpx
  11. CURRENT_DIR = pathlib.Path(__file__).parent
  12. def main():
  13. bat()
  14. delta()
  15. dua()
  16. dust()
  17. eza()
  18. fx()
  19. git_whence()
  20. jujutsu()
  21. lazygit()
  22. oh_my_posh()
  23. #starship()
  24. uv()
  25. yazi()
  26. def bat():
  27. if platform.system() == 'Darwin':
  28. print('$ brew install git-delta')
  29. subprocess.run(['brew', 'install', 'bat'], check=True)
  30. return
  31. else:
  32. assert platform.system() == 'Linux'
  33. if subprocess.run(['dpkg', '-l', 'bat'], stdout=subprocess.DEVNULL).returncode == 0:
  34. print_gray('bat package already installed')
  35. return
  36. subprocess.run(['sudo', 'apt', 'install', 'bat', '--yes'], check=True)
  37. def delta():
  38. if platform.system() == 'Darwin':
  39. print('$ brew install git-delta')
  40. subprocess.run(['brew', 'install', 'git-delta'], check=True)
  41. return
  42. else:
  43. assert platform.system() == 'Linux'
  44. if subprocess.run(['dpkg', '-l', 'git-delta'], stdout=subprocess.DEVNULL).returncode == 0:
  45. print_gray('git-delta package already installed')
  46. return
  47. client = httpx.Client()
  48. latest = gh_latest_version(client, 'dandavison', 'delta')
  49. version = latest['name']
  50. arch = get_output(['dpkg', '--print-architecture'])
  51. filename = f'git-delta_{version}_{arch}.deb'
  52. (asset,) = (asset for asset in latest['assets'] if asset['name'] == filename)
  53. deb_path = CURRENT_DIR / 'git-delta.deb'
  54. download(client, asset['browser_download_url'], deb_path)
  55. try:
  56. subprocess.run(['sudo', 'dpkg', '-i', deb_path], check=True)
  57. finally:
  58. os.unlink(deb_path)
  59. def dua():
  60. if platform.system() == 'Darwin':
  61. print('$ brew install dua-cli')
  62. subprocess.run(['brew', 'install', 'dua-cli'], check=True)
  63. return
  64. else:
  65. assert platform.system() == 'Linux'
  66. dua_path = CURRENT_DIR / 'dua'
  67. if dua_path.exists():
  68. print_gray('dua already downloaded')
  69. return
  70. client = httpx.Client()
  71. latest = gh_latest_version(client, 'Byron', 'dua-cli')
  72. version = latest['name']
  73. dirname = f'dua-{version}-{platform.machine()}-unknown-linux-musl'
  74. filename = dirname + '.tar.gz'
  75. (asset,) = (asset for asset in latest['assets'] if asset['name'] == filename)
  76. tarball_path = CURRENT_DIR / 'dua.tar.gz'
  77. download(client, asset['browser_download_url'], tarball_path)
  78. with tarfile.open(tarball_path) as tar:
  79. with tar.extractfile(dirname + '/dua') as binary, dua_path.open('wb') as f: # pyright: ignore[reportOptionalContextManager]
  80. shutil.copyfileobj(binary, f)
  81. os.unlink(tarball_path)
  82. dua_path.chmod(0o755)
  83. def dust():
  84. if platform.system() == 'Darwin':
  85. print('$ brew install dust')
  86. subprocess.run(['brew', 'install', 'dust'], check=True)
  87. return
  88. else:
  89. assert platform.system() == 'Linux'
  90. dust_path = CURRENT_DIR / 'dust'
  91. if dust_path.exists():
  92. print_gray('dust already downloaded')
  93. return
  94. client = httpx.Client()
  95. latest = gh_latest_version(client, 'bootandy', 'dust')
  96. version = latest['name']
  97. dirname = f'dust-{version}-{platform.machine()}-unknown-linux-musl'
  98. filename = dirname + '.tar.gz'
  99. (asset,) = (asset for asset in latest['assets'] if asset['name'] == filename)
  100. tarball_path = CURRENT_DIR / 'dust.tar.gz'
  101. download(client, asset['browser_download_url'], tarball_path)
  102. with tarfile.open(tarball_path) as tar:
  103. with tar.extractfile(dirname + '/dust') as binary, dust_path.open('wb') as f: # pyright: ignore[reportOptionalContextManager]
  104. shutil.copyfileobj(binary, f)
  105. os.unlink(tarball_path)
  106. dust_path.chmod(0o755)
  107. def eza():
  108. if platform.system() == 'Darwin':
  109. print('$ brew install eza')
  110. subprocess.run(['brew', 'install', 'eza'], check=True)
  111. return
  112. else:
  113. assert platform.system() == 'Linux'
  114. if (CURRENT_DIR / 'eza').exists():
  115. print_gray('eza already downloaded')
  116. return
  117. client = httpx.Client()
  118. url = f'https://github.com/eza-community/eza/releases/latest/download/eza_{platform.machine()}-unknown-linux-gnu.tar.gz'
  119. tarball_path = CURRENT_DIR / 'eza.tar.gz'
  120. download(client, url, tarball_path)
  121. with tarfile.open(tarball_path) as tar:
  122. tar.extract('./eza', CURRENT_DIR)
  123. tarball_path.unlink()
  124. def fx():
  125. if platform.system() == 'Darwin':
  126. print('$ brew install fx')
  127. subprocess.run(['brew', 'install', 'fx'], check=True)
  128. return
  129. else:
  130. assert platform.system() == 'Linux'
  131. if (CURRENT_DIR / 'fx').exists():
  132. print_gray('fx already downloaded')
  133. return
  134. client = httpx.Client()
  135. arch = platform.machine()
  136. if arch == 'aarch64':
  137. arch = 'arm64'
  138. url = f'https://github.com/antonmedv/fx/releases/latest/download/fx_linux_{arch}'
  139. download(client, url, CURRENT_DIR / 'fx')
  140. os.chmod(CURRENT_DIR / 'fx', 0o755)
  141. def git_whence():
  142. if platform.system() == 'Darwin':
  143. print('$ brew install raylu/formulae/git-whence')
  144. subprocess.run(['brew', 'install', 'raylu/formulae/git-whence'], check=True)
  145. return
  146. else:
  147. assert platform.system() == 'Linux'
  148. if (CURRENT_DIR / 'git-whence').exists():
  149. print_gray('git-whence already downloaded')
  150. return
  151. client = httpx.Client()
  152. url = f'https://github.com/raylu/git-whence/releases/latest/download/git-whence-{platform.machine()}-unknown-linux-gnu'
  153. download(client, url, CURRENT_DIR / 'git-whence')
  154. os.chmod(CURRENT_DIR / 'git-whence', 0o755)
  155. def jujutsu():
  156. if platform.system() == 'Darwin':
  157. print('$ brew install jj')
  158. subprocess.run(['brew', 'install', 'jj'], check=True)
  159. return
  160. else:
  161. assert platform.system() == 'Linux'
  162. if (CURRENT_DIR / 'jj').exists():
  163. print_gray('jj already downloaded')
  164. return
  165. client = httpx.Client()
  166. latest = gh_latest_version(client, 'jj-vcs', 'jj')
  167. version = latest['name']
  168. arch = platform.machine()
  169. url = f'https://github.com/jj-vcs/jj/releases/download/{version}/jj-{version}-{arch}-unknown-linux-musl.tar.gz'
  170. tarball_path = CURRENT_DIR / 'jj.tar.gz'
  171. download(client, url, tarball_path)
  172. with tarfile.open(tarball_path, 'r:gz') as tar:
  173. tar.extract('./jj', CURRENT_DIR, filter='data')
  174. tarball_path.unlink()
  175. def lazygit():
  176. if (CURRENT_DIR / 'lazygit').exists():
  177. print_gray('lazygit package already installed')
  178. return
  179. client = httpx.Client()
  180. latest = gh_latest_version(client, 'jesseduffield', 'lazygit')
  181. version = latest['name']
  182. arch = platform.machine()
  183. if arch == 'aarch64':
  184. arch = 'arm64'
  185. url = f'https://github.com/jesseduffield/lazygit/releases/download/{version}/lazygit_{version.lstrip("v")}_{platform.system()}_{arch}.tar.gz'
  186. download(client, url, CURRENT_DIR / 'lazygit.tgz')
  187. with tarfile.open(CURRENT_DIR / 'lazygit.tgz', 'r:gz') as tar:
  188. tar.extract('lazygit', CURRENT_DIR)
  189. os.unlink(CURRENT_DIR / 'lazygit.tgz')
  190. def oh_my_posh():
  191. if platform.system() == 'Darwin':
  192. print('$ brew install oh-my-posh')
  193. subprocess.run(['brew', 'install', 'oh-my-posh'], check=True)
  194. return
  195. else:
  196. assert platform.system() == 'Linux'
  197. if (CURRENT_DIR / 'oh-my-posh').exists():
  198. print_gray('oh-my-posh already downloaded')
  199. return
  200. client = httpx.Client()
  201. arch = platform.machine()
  202. if arch == 'aarch64':
  203. arch = 'arm64'
  204. elif arch == 'x86_64':
  205. arch = 'amd64'
  206. url = f'https://github.com/JanDeDobbeleer/oh-my-posh/releases/latest/download/posh-linux-{arch}'
  207. download(client, url, CURRENT_DIR / 'oh-my-posh')
  208. os.chmod(CURRENT_DIR / 'oh-my-posh', 0o755)
  209. def starship():
  210. if platform.system() == 'Darwin':
  211. print('$ brew install starship')
  212. subprocess.run(['brew', 'install', 'starship'], check=True)
  213. return
  214. else:
  215. assert platform.system() == 'Linux'
  216. if (CURRENT_DIR / 'starship').exists():
  217. print_gray('starship already downloaded')
  218. return
  219. client = httpx.Client()
  220. arch = platform.machine()
  221. if arch == 'x86_64':
  222. libc = 'gnu'
  223. else:
  224. libc = 'musl'
  225. url = f'https://github.com/starship/starship/releases/latest/download/starship-{arch}-unknown-linux-{libc}.tar.gz'
  226. tarball_path = CURRENT_DIR / 'starship.tar.gz'
  227. download(client, url, tarball_path)
  228. with tarfile.open(tarball_path, 'r:gz') as tar:
  229. tar.extract('starship', CURRENT_DIR)
  230. tarball_path.unlink()
  231. def uv():
  232. if (CURRENT_DIR / 'uv').exists():
  233. print_gray('uv already downloaded')
  234. return
  235. if which := shutil.which('uv'):
  236. print_gray('uv already at ' + which)
  237. return
  238. client = httpx.Client()
  239. os = platform.system().lower()
  240. arch = platform.machine()
  241. if arch == 'darwin':
  242. prefix = f'uv-{arch}-apple-darwin'
  243. else:
  244. prefix = f'uv-{arch}-unknown-linux-gnu'
  245. url = f'https://github.com/astral-sh/uv/releases/latest/download/{prefix}.tar.gz'
  246. tarball_path = CURRENT_DIR / 'uv.tar.gz'
  247. download(client, url, tarball_path)
  248. with tarfile.open(tarball_path, 'r:gz') as tar:
  249. for filename in ['uv', 'uvx']:
  250. target_path = CURRENT_DIR / filename
  251. with tar.extractfile(f'{prefix}/{filename}') as binary, target_path.open('wb') as f: # pyright: ignore[reportOptionalContextManager]
  252. shutil.copyfileobj(binary, f)
  253. target_path.chmod(0o755)
  254. tarball_path.unlink()
  255. def yazi():
  256. if platform.system() == 'Darwin':
  257. print('$ brew install yazi')
  258. subprocess.run(['brew', 'install', 'yazi'], check=True)
  259. return
  260. else:
  261. assert platform.system() == 'Linux'
  262. if (CURRENT_DIR / 'yazi').exists():
  263. print_gray('yazi already downloaded')
  264. return
  265. client = httpx.Client()
  266. name = f'yazi-{platform.machine()}-unknown-linux-musl'
  267. url = f'https://github.com/sxyazi/yazi/releases/latest/download/{name}.zip'
  268. zip_path = CURRENT_DIR / 'yazi.zip'
  269. download(client, url, zip_path)
  270. with zipfile.ZipFile(zip_path, 'r') as zipf:
  271. for filename in ['ya', 'yazi']:
  272. extracted = (CURRENT_DIR / filename)
  273. with zipf.open(f'{name}/{filename}', 'r') as binary, extracted.open('wb') as f:
  274. shutil.copyfileobj(binary, f)
  275. extracted.chmod(0o755)
  276. zip_path.unlink()
  277. def get_output(argv: list[str]) -> str:
  278. proc = subprocess.run(argv, check=True, capture_output=True, encoding='ascii')
  279. return proc.stdout.rstrip('\n')
  280. def gh_latest_version(client: httpx.Client, org: str, repo: str) -> dict:
  281. r = client.get(f'https://api.github.com/repos/{org}/{repo}/releases/latest',
  282. headers={'Accept': 'application/vnd.github+json', 'X-GitHub-Api-Version': '2022-11-28'})
  283. r.raise_for_status()
  284. return r.json()
  285. def download(client: httpx.Client, url: str, path: pathlib.Path) -> None:
  286. print('downloading', url, 'to', path)
  287. with client.stream('GET', url, follow_redirects=True) as r:
  288. r.raise_for_status()
  289. with path.open('wb') as f:
  290. for chunk in r.iter_bytes():
  291. f.write(chunk)
  292. def print_gray(*args: object):
  293. print('\x1B[90;m', end='')
  294. print(*args, end='')
  295. print('\x1B[0m')
  296. if __name__ == '__main__':
  297. main()