sync.py 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  1. from __future__ import annotations
  2. import configparser
  3. import os
  4. import subprocess
  5. from devenv import constants
  6. from devenv.lib import venv # type: ignore
  7. from devenv.lib import colima, config, limactl, proc, volta
  8. # TODO: need to replace this with a nicer process executor in devenv.lib
  9. def run_procs(
  10. repo: str,
  11. reporoot: str,
  12. venv_path: str,
  13. _procs: tuple[tuple[str, tuple[str, ...]], ...],
  14. ) -> bool:
  15. procs: list[tuple[str, tuple[str, ...], subprocess.Popen[bytes]]] = []
  16. for name, cmd in _procs:
  17. print(f"⏳ {name}")
  18. if constants.DEBUG:
  19. proc.xtrace(cmd)
  20. procs.append(
  21. (
  22. name,
  23. cmd,
  24. subprocess.Popen(
  25. cmd,
  26. stdout=subprocess.PIPE,
  27. env={
  28. **constants.user_environ,
  29. **proc.base_env,
  30. "VIRTUAL_ENV": venv_path,
  31. "VOLTA_HOME": constants.VOLTA_HOME,
  32. "PATH": f"{venv_path}/bin:{proc.base_path}",
  33. },
  34. cwd=reporoot,
  35. ),
  36. )
  37. )
  38. all_good = True
  39. for name, final_cmd, p in procs:
  40. p.wait()
  41. if p.returncode != 0:
  42. all_good = False
  43. if p.stdout is None:
  44. out = ""
  45. else:
  46. out = p.stdout.read().decode()
  47. print(
  48. f"""
  49. ❌ {name}
  50. failed command (code p.returncode):
  51. {proc.quote(final_cmd)}
  52. Output:
  53. {out}
  54. """
  55. )
  56. else:
  57. print(f"✅ {name}")
  58. return all_good
  59. def main(context: dict[str, str]) -> int:
  60. repo = context["repo"]
  61. reporoot = context["reporoot"]
  62. venv_dir, python_version, requirements, editable_paths, bins = venv.get(reporoot, "sentry")
  63. url, sha256 = config.get_python(reporoot, python_version)
  64. print(f"ensuring venv at {venv_dir}...")
  65. venv.ensure(venv_dir, python_version, url, sha256)
  66. print(f"syncing {venv_dir} with {requirements}...")
  67. venv.sync(venv_dir, requirements, editable_paths, bins)
  68. if not run_procs(
  69. repo,
  70. reporoot,
  71. venv_dir,
  72. (
  73. (
  74. "git and precommit",
  75. # this can't be done in paralell with python dependencies
  76. # as multiple pips cannot act on the same venv
  77. ("make", "setup-git"),
  78. ),
  79. ),
  80. ):
  81. return 1
  82. # This is for engineers with existing dev environments transitioning over.
  83. # Bootstrap will set devenv-managed volta up but they won't be running
  84. # devenv bootstrap, just installing devenv then running devenv sync.
  85. # make install-js-dev will fail since our run_procs expects devenv-managed
  86. # volta.
  87. volta.install()
  88. if constants.DARWIN:
  89. repo_config = configparser.ConfigParser()
  90. repo_config.read(f"{reporoot}/devenv/config.ini")
  91. # we don't officially support colima on linux yet
  92. if constants.CI:
  93. # colima 0.6.8 doesn't work with macos-13,
  94. # but integration coverage is still handy
  95. colima.install(
  96. "v0.6.2",
  97. "https://github.com/abiosoft/colima/releases/download/v0.6.2/colima-Darwin-x86_64",
  98. "43ef3fc80a8347d51b8ec1706f9caf8863bd8727a6f7532caf1ccd20497d8485",
  99. )
  100. else:
  101. colima.install(
  102. repo_config["colima"]["version"],
  103. repo_config["colima"][constants.SYSTEM_MACHINE],
  104. repo_config["colima"][f"{constants.SYSTEM_MACHINE}_sha256"],
  105. )
  106. # TODO: move limactl version into per-repo config
  107. limactl.install()
  108. if not run_procs(
  109. repo,
  110. reporoot,
  111. venv_dir,
  112. (
  113. ("javascript dependencies", ("make", "install-js-dev")),
  114. ("python dependencies", ("make", "install-py-dev")),
  115. ),
  116. ):
  117. return 1
  118. if not os.path.exists(f"{constants.home}/.sentry/config.yml") or not os.path.exists(
  119. f"{constants.home}/.sentry/sentry.conf.py"
  120. ):
  121. proc.run((f"{venv_dir}/bin/sentry", "init", "--dev"))
  122. # TODO: check healthchecks for redis and postgres to short circuit this
  123. proc.run(
  124. (
  125. f"{venv_dir}/bin/sentry",
  126. "devservices",
  127. "up",
  128. "redis",
  129. "postgres",
  130. ),
  131. exit=True,
  132. )
  133. if run_procs(
  134. repo,
  135. reporoot,
  136. venv_dir,
  137. (
  138. (
  139. "python migrations",
  140. (f"{venv_dir}/bin/sentry", "upgrade", "--noinput"),
  141. ),
  142. ),
  143. ):
  144. return 0
  145. return 1