install_extras.py 12 KB

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