install_extras.py 12 KB

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