sync.py 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  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, repo)
  63. url, sha256 = config.get_python(reporoot, python_version)
  64. print(f"ensuring {repo} venv at {venv_dir}...")
  65. venv.ensure(venv_dir, python_version, url, sha256)
  66. if not run_procs(
  67. repo,
  68. reporoot,
  69. venv_dir,
  70. (
  71. (
  72. "git and precommit",
  73. # this can't be done in paralell with python dependencies
  74. # as multiple pips cannot act on the same venv
  75. ("make", "setup-git"),
  76. ),
  77. ),
  78. ):
  79. return 1
  80. # This is for engineers with existing dev environments transitioning over.
  81. # Bootstrap will set devenv-managed volta up but they won't be running
  82. # devenv bootstrap, just installing devenv then running devenv sync.
  83. # make install-js-dev will fail since our run_procs expects devenv-managed
  84. # volta.
  85. volta.install()
  86. if constants.DARWIN:
  87. repo_config = configparser.ConfigParser()
  88. repo_config.read(f"{reporoot}/devenv/config.ini")
  89. # we don't officially support colima on linux yet
  90. if constants.CI:
  91. # colima 0.6.8 doesn't work with macos-13,
  92. # but integration coverage is still handy
  93. colima.install(
  94. "v0.6.2",
  95. "https://github.com/abiosoft/colima/releases/download/v0.6.2/colima-Darwin-x86_64",
  96. "43ef3fc80a8347d51b8ec1706f9caf8863bd8727a6f7532caf1ccd20497d8485",
  97. )
  98. else:
  99. colima.install(
  100. repo_config["colima"]["version"],
  101. repo_config["colima"][constants.SYSTEM_MACHINE],
  102. repo_config["colima"][f"{constants.SYSTEM_MACHINE}_sha256"],
  103. )
  104. # TODO: move limactl version into per-repo config
  105. limactl.install()
  106. if not run_procs(
  107. repo,
  108. reporoot,
  109. venv_dir,
  110. (
  111. ("javascript dependencies", ("make", "install-js-dev")),
  112. ("python dependencies", ("make", "install-py-dev")),
  113. ),
  114. ):
  115. return 1
  116. if not os.path.exists(f"{constants.home}/.sentry/config.yml") or not os.path.exists(
  117. f"{constants.home}/.sentry/sentry.conf.py"
  118. ):
  119. proc.run((f"{venv_dir}/bin/{repo}", "init", "--dev"))
  120. # TODO: check healthchecks for redis and postgres to short circuit this
  121. proc.run(
  122. (
  123. f"{venv_dir}/bin/{repo}",
  124. "devservices",
  125. "up",
  126. "redis",
  127. "postgres",
  128. ),
  129. exit=True,
  130. )
  131. if run_procs(
  132. repo,
  133. reporoot,
  134. venv_dir,
  135. (
  136. (
  137. "python migrations",
  138. (f"{venv_dir}/bin/{repo}", "upgrade", "--noinput"),
  139. ),
  140. ),
  141. ):
  142. return 0
  143. return 1