install_extras.py 13 KB

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