tarball_uploader.py 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103
  1. # Copyright Materialize, Inc. and contributors. All rights reserved.
  2. #
  3. # Use of this software is governed by the Business Source License
  4. # included in the LICENSE file at the root of this repository.
  5. #
  6. # As of the Change Date specified in that file, in accordance with
  7. # the Business Source License, use of this software will be governed
  8. # by the Apache License, Version 2.0.
  9. # This file contains a class that can be used to upload a tarball to the materialize-binaries S3 bucket.
  10. import os
  11. import tarfile
  12. import tempfile
  13. import time
  14. from pathlib import Path
  15. import boto3
  16. import humanize
  17. from materialize import git
  18. from materialize.mz_version import TypedVersionBase
  19. BINARIES_BUCKET = "materialize-binaries"
  20. class TarballUploader:
  21. def __init__(self, package_name: str, version: TypedVersionBase):
  22. self.package_name = package_name
  23. self.version = version
  24. self.version_str = f"v{version.str_without_prefix()}"
  25. def _create_tardir(self, name: str) -> tarfile.TarInfo:
  26. """Takes a dir path and creates a TarInfo. Sets the
  27. modification time, mode, and type of the dir."""
  28. d = tarfile.TarInfo(name)
  29. d.mtime = int(time.time())
  30. d.mode = 0o40755
  31. d.type = tarfile.DIRTYPE
  32. return d
  33. def _sanitize_tarinfo(self, tarinfo: tarfile.TarInfo) -> tarfile.TarInfo:
  34. tarinfo.uid = tarinfo.gid = 0
  35. tarinfo.uname = tarinfo.gname = "root"
  36. return tarinfo
  37. def _upload_redirect(self, key: str, to: str) -> None:
  38. with tempfile.NamedTemporaryFile() as empty:
  39. boto3.client("s3").upload_fileobj(
  40. Fileobj=empty,
  41. Bucket=BINARIES_BUCKET,
  42. Key=key,
  43. ExtraArgs={"WebsiteRedirectLocation": to},
  44. )
  45. def _upload_tarball(self, tarball: Path, platform: str) -> None:
  46. """Uploads the tarball (.tar) to the S3 bucket."""
  47. s3_object = f"{self.package_name}-{self.version_str}-{platform}.tar.gz"
  48. boto3.client("s3").upload_file(
  49. Filename=str(tarball),
  50. Bucket=BINARIES_BUCKET,
  51. Key=s3_object,
  52. )
  53. if "aarch64" in platform:
  54. self._upload_redirect(
  55. f"{self.package_name}-{self.version_str}-{platform.replace('aarch64', 'arm64')}.tar.gz",
  56. f"/{s3_object}",
  57. )
  58. def _upload_latest_redirect(self, platform: str) -> None:
  59. """Uploads an S3 object for the latest version redirect."""
  60. self._upload_redirect(
  61. f"{self.package_name}-latest-{platform}.tar.gz",
  62. f"/{self.package_name}-{self.version_str}-{platform}.tar.gz",
  63. )
  64. if "aarch64" in platform:
  65. self._upload_latest_redirect(platform.replace("aarch64", "arm64"))
  66. def deploy_tarball(self, platform: str, binary_path: Path) -> None:
  67. """Creates and uploads a tarball containing the binary."""
  68. tar_path = Path(f"{self.package_name}-{platform}.tar.gz")
  69. with tarfile.open(str(tar_path), "x:gz") as f:
  70. f.addfile(self._create_tardir("mz/bin"))
  71. f.add(
  72. str(binary_path),
  73. arcname=f"mz/bin/{binary_path.name}",
  74. filter=self._sanitize_tarinfo,
  75. )
  76. size = humanize.naturalsize(os.lstat(tar_path).st_size)
  77. print(f"Tarball size: {size}")
  78. self._upload_tarball(tar_path, platform)
  79. self._upload_latest_redirect(platform)
  80. def is_latest_version(version: TypedVersionBase) -> bool:
  81. latest_version = max(
  82. t
  83. for t in git.get_version_tags(version_type=type(version))
  84. if t.prerelease is None
  85. )
  86. return version == latest_version