fetch-msys2-installer.py 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101
  1. #!/usr/bin/env python
  2. '''Fetch the MSYS2 installer.'''
  3. from __future__ import annotations
  4. import hashlib
  5. import json
  6. import shutil
  7. import sys
  8. from pathlib import Path
  9. from tempfile import TemporaryDirectory
  10. from typing import Final
  11. from urllib.request import Request, urlopen
  12. REPO: Final = 'msys2/msys2-installer'
  13. def get_latest_release() -> tuple[str, str]:
  14. '''Get the latest release for the repo.'''
  15. REQUEST: Final = Request(
  16. url=f'https://api.github.com/repos/{REPO}/releases',
  17. headers={
  18. 'Accept': 'application/vnd.github+json',
  19. 'X-GitHub-API-Version': '2022-11-28',
  20. },
  21. method='GET',
  22. )
  23. print('>>> Fetching release list')
  24. with urlopen(REQUEST, timeout=15) as response:
  25. if response.status != 200:
  26. print(f'!!! Failed to fetch release list, status={response.status}')
  27. sys.exit(1)
  28. data = json.load(response)
  29. data = list(filter(lambda x: x['name'] != 'Nightly Installer Build', data))
  30. name = data[0]['name']
  31. version = data[0]['tag_name'].replace('-', '')
  32. return name, version
  33. def fetch_release_asset(tmpdir: Path, name: str, file: str) -> Path:
  34. '''Fetch a specific release asset.'''
  35. REQUEST: Final = Request(
  36. url=f'https://github.com/{REPO}/releases/download/{name}/{file}',
  37. method='GET',
  38. )
  39. TARGET: Final = tmpdir / file
  40. print(f'>>> Downloading {file}')
  41. with urlopen(REQUEST, timeout=15) as response:
  42. if response.status != 200:
  43. print(f'!!! Failed to fetch {file}, status={response.status}')
  44. sys.exit(1)
  45. TARGET.write_bytes(response.read())
  46. return TARGET
  47. def main() -> None:
  48. '''Core program logic.'''
  49. if len(sys.argv) != 2:
  50. print(f'{__file__} must be run with exactly one argument.')
  51. target = Path(sys.argv[1])
  52. tmp_target = target.with_name(f'.{target.name}.tmp')
  53. name, version = get_latest_release()
  54. with TemporaryDirectory() as tmpdir:
  55. tmppath = Path(tmpdir)
  56. installer = fetch_release_asset(tmppath, name, f'msys2-base-x86_64-{version}.tar.zst')
  57. checksums = fetch_release_asset(tmppath, name, f'msys2-base-x86_64-{version}.tar.zst.sha256')
  58. print('>>> Verifying SHA256 checksum')
  59. expected_checksum = checksums.read_text().partition(' ')[0].casefold()
  60. actual_checksum = hashlib.sha256(installer.read_bytes()).hexdigest().casefold()
  61. if expected_checksum != actual_checksum:
  62. print('!!! Checksum mismatch')
  63. print(f'!!! Expected: {expected_checksum}')
  64. print(f'!!! Actual: {actual_checksum}')
  65. sys.exit(1)
  66. print(f'>>> Copying to {target}')
  67. shutil.copy(installer, tmp_target)
  68. tmp_target.replace(target)
  69. if __name__ == '__main__':
  70. main()