omdev 0.0.0.dev213__tar.gz → 0.0.0.dev214__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (229) hide show
  1. {omdev-0.0.0.dev213/omdev.egg-info → omdev-0.0.0.dev214}/PKG-INFO +2 -2
  2. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/.manifests.json +1 -1
  3. omdev-0.0.0.dev214/omdev/ci/cache.py +147 -0
  4. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/ci/ci.py +120 -118
  5. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/ci/cli.py +50 -24
  6. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/ci/compose.py +1 -8
  7. omdev-0.0.0.dev214/omdev/ci/consts.py +1 -0
  8. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/ci/docker.py +4 -6
  9. omdev-0.0.0.dev213/omdev/ci/github/cacheapi.py → omdev-0.0.0.dev214/omdev/ci/github/api.py +0 -1
  10. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/ci/github/bootstrap.py +8 -1
  11. omdev-0.0.0.dev214/omdev/ci/github/cache.py +71 -0
  12. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/ci/github/cli.py +9 -5
  13. omdev-0.0.0.dev214/omdev/ci/github/client.py +492 -0
  14. omdev-0.0.0.dev214/omdev/ci/github/env.py +21 -0
  15. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/ci/requirements.py +0 -1
  16. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/ci/shell.py +0 -1
  17. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/ci/utils.py +2 -14
  18. omdev-0.0.0.dev214/omdev/pyproject/__init__.py +1 -0
  19. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/scripts/ci.py +1149 -922
  20. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/tools/docker.py +6 -0
  21. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214/omdev.egg-info}/PKG-INFO +2 -2
  22. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev.egg-info/SOURCES.txt +4 -2
  23. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev.egg-info/requires.txt +1 -1
  24. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/pyproject.toml +2 -2
  25. omdev-0.0.0.dev213/omdev/ci/cache.py +0 -168
  26. omdev-0.0.0.dev213/omdev/ci/github/cache.py +0 -324
  27. omdev-0.0.0.dev213/omdev/ci/github/curl.py +0 -209
  28. omdev-0.0.0.dev213/omdev/tools/pawk/__init__.py +0 -0
  29. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/LICENSE +0 -0
  30. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/MANIFEST.in +0 -0
  31. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/README.rst +0 -0
  32. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/__about__.py +0 -0
  33. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/__init__.py +0 -0
  34. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/amalg/__init__.py +0 -0
  35. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/amalg/__main__.py +0 -0
  36. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/amalg/gen.py +0 -0
  37. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/amalg/imports.py +0 -0
  38. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/amalg/main.py +0 -0
  39. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/amalg/manifests.py +0 -0
  40. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/amalg/resources.py +0 -0
  41. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/amalg/srcfiles.py +0 -0
  42. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/amalg/strip.py +0 -0
  43. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/amalg/types.py +0 -0
  44. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/amalg/typing.py +0 -0
  45. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/antlr/__init__.py +0 -0
  46. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/antlr/consts.py +0 -0
  47. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/antlr/gen.py +0 -0
  48. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/bracepy.py +0 -0
  49. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/cache/__init__.py +0 -0
  50. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/cache/compute/__init__.py +0 -0
  51. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/cache/compute/cache.py +0 -0
  52. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/cache/compute/contexts.py +0 -0
  53. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/cache/compute/currents.py +0 -0
  54. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/cache/compute/fns.py +0 -0
  55. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/cache/compute/resolvers.py +0 -0
  56. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/cache/compute/storage.py +0 -0
  57. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/cache/compute/types.py +0 -0
  58. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/cache/data/__init__.py +0 -0
  59. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/cache/data/actions.py +0 -0
  60. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/cache/data/cache.py +0 -0
  61. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/cache/data/consts.py +0 -0
  62. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/cache/data/defaults.py +0 -0
  63. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/cache/data/manifests.py +0 -0
  64. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/cache/data/specs.py +0 -0
  65. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/cc/__init__.py +0 -0
  66. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/cc/__main__.py +0 -0
  67. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/cc/cdeps.py +0 -0
  68. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/cc/cdeps.toml +0 -0
  69. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/cc/cli.py +0 -0
  70. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/cexts/__init__.py +0 -0
  71. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/cexts/_boilerplate.cc +0 -0
  72. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/cexts/_distutils/LICENSE +0 -0
  73. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/cexts/_distutils/__init__.py +0 -0
  74. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/cexts/_distutils/build_ext.py +0 -0
  75. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/cexts/_distutils/compilers/__init__.py +0 -0
  76. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/cexts/_distutils/compilers/ccompiler.py +0 -0
  77. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/cexts/_distutils/compilers/options.py +0 -0
  78. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/cexts/_distutils/compilers/unixccompiler.py +0 -0
  79. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/cexts/_distutils/dir_util.py +0 -0
  80. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/cexts/_distutils/errors.py +0 -0
  81. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/cexts/_distutils/extension.py +0 -0
  82. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/cexts/_distutils/file_util.py +0 -0
  83. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/cexts/_distutils/modified.py +0 -0
  84. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/cexts/_distutils/spawn.py +0 -0
  85. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/cexts/_distutils/sysconfig.py +0 -0
  86. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/cexts/_distutils/util.py +0 -0
  87. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/cexts/_distutils/version.py +0 -0
  88. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/cexts/build.py +0 -0
  89. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/cexts/cmake.py +0 -0
  90. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/cexts/importhook.py +0 -0
  91. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/cexts/magic.py +0 -0
  92. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/cexts/scan.py +0 -0
  93. {omdev-0.0.0.dev213/omdev/interp → omdev-0.0.0.dev214/omdev/ci}/__init__.py +0 -0
  94. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/ci/__main__.py +0 -0
  95. {omdev-0.0.0.dev213/omdev/ci → omdev-0.0.0.dev214/omdev/ci/github}/__init__.py +0 -0
  96. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/classdot.py +0 -0
  97. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/cli/__init__.py +0 -0
  98. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/cli/__main__.py +0 -0
  99. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/cli/_pathhack.py +0 -0
  100. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/cli/clicli.py +0 -0
  101. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/cli/install.py +0 -0
  102. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/cli/main.py +0 -0
  103. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/cli/managers.py +0 -0
  104. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/cli/types.py +0 -0
  105. {omdev-0.0.0.dev213/omdev/ci/github → omdev-0.0.0.dev214/omdev/clipboard}/__init__.py +0 -0
  106. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/clipboard/clipboard.py +0 -0
  107. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/clipboard/darwin_cf.py +0 -0
  108. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/clipboard/linux_x11.py +0 -0
  109. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/cmake.py +0 -0
  110. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/findimports.py +0 -0
  111. {omdev-0.0.0.dev213/omdev/clipboard → omdev-0.0.0.dev214/omdev/git}/__init__.py +0 -0
  112. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/git/revisions.py +0 -0
  113. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/git/shallow.py +0 -0
  114. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/git/status.py +0 -0
  115. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/imgur.py +0 -0
  116. {omdev-0.0.0.dev213/omdev/manifests → omdev-0.0.0.dev214/omdev/interp}/__init__.py +0 -0
  117. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/interp/__main__.py +0 -0
  118. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/interp/cli.py +0 -0
  119. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/interp/default.py +0 -0
  120. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/interp/inject.py +0 -0
  121. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/interp/inspect.py +0 -0
  122. {omdev-0.0.0.dev213/omdev/git → omdev-0.0.0.dev214/omdev/interp/providers}/__init__.py +0 -0
  123. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/interp/providers/base.py +0 -0
  124. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/interp/providers/inject.py +0 -0
  125. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/interp/providers/running.py +0 -0
  126. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/interp/providers/standalone.py +0 -0
  127. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/interp/providers/system.py +0 -0
  128. {omdev-0.0.0.dev213/omdev/interp/providers → omdev-0.0.0.dev214/omdev/interp/pyenv}/__init__.py +0 -0
  129. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/interp/pyenv/inject.py +0 -0
  130. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/interp/pyenv/install.py +0 -0
  131. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/interp/pyenv/provider.py +0 -0
  132. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/interp/pyenv/pyenv.py +0 -0
  133. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/interp/resolvers.py +0 -0
  134. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/interp/types.py +0 -0
  135. {omdev-0.0.0.dev213/omdev/interp/pyenv → omdev-0.0.0.dev214/omdev/interp/uv}/__init__.py +0 -0
  136. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/interp/uv/inject.py +0 -0
  137. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/interp/uv/provider.py +0 -0
  138. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/interp/uv/uv.py +0 -0
  139. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/interp/venvs.py +0 -0
  140. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/magic/__init__.py +0 -0
  141. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/magic/__main__.py +0 -0
  142. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/magic/cli.py +0 -0
  143. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/magic/find.py +0 -0
  144. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/magic/magic.py +0 -0
  145. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/magic/prepare.py +0 -0
  146. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/magic/styles.py +0 -0
  147. {omdev-0.0.0.dev213/omdev/pyproject → omdev-0.0.0.dev214/omdev/manifests}/__init__.py +0 -0
  148. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/manifests/__main__.py +0 -0
  149. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/manifests/build.py +0 -0
  150. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/manifests/main.py +0 -0
  151. {omdev-0.0.0.dev213/omdev/interp/uv → omdev-0.0.0.dev214/omdev/mypy}/__init__.py +0 -0
  152. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/mypy/debug.py +0 -0
  153. {omdev-0.0.0.dev213/omdev/mypy → omdev-0.0.0.dev214/omdev/packaging}/__init__.py +0 -0
  154. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/packaging/marshal.py +0 -0
  155. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/packaging/names.py +0 -0
  156. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/packaging/requires.py +0 -0
  157. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/packaging/specifiers.py +0 -0
  158. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/packaging/versions.py +0 -0
  159. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/pip.py +0 -0
  160. {omdev-0.0.0.dev213/omdev/packaging → omdev-0.0.0.dev214/omdev/precheck}/__init__.py +0 -0
  161. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/precheck/__main__.py +0 -0
  162. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/precheck/base.py +0 -0
  163. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/precheck/git.py +0 -0
  164. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/precheck/lite.py +0 -0
  165. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/precheck/main.py +0 -0
  166. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/precheck/manifests.py +0 -0
  167. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/precheck/scripts.py +0 -0
  168. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/ptk/__init__.py +0 -0
  169. {omdev-0.0.0.dev213/omdev/precheck → omdev-0.0.0.dev214/omdev/ptk/apps}/__init__.py +0 -0
  170. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/ptk/apps/ncdu.py +0 -0
  171. {omdev-0.0.0.dev213/omdev/ptk/apps → omdev-0.0.0.dev214/omdev/pycharm}/__init__.py +0 -0
  172. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/pycharm/__main__.py +0 -0
  173. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/pycharm/cli.py +0 -0
  174. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/pyproject/__main__.py +0 -0
  175. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/pyproject/cexts.py +0 -0
  176. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/pyproject/cli.py +0 -0
  177. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/pyproject/configs.py +0 -0
  178. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/pyproject/inject.py +0 -0
  179. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/pyproject/pkg.py +0 -0
  180. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/pyproject/reqs.py +0 -0
  181. {omdev-0.0.0.dev213/omdev/pycharm → omdev-0.0.0.dev214/omdev/pyproject/resources}/__init__.py +0 -0
  182. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/pyproject/resources/docker-dev.sh +0 -0
  183. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/pyproject/resources/python.sh +0 -0
  184. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/pyproject/venvs.py +0 -0
  185. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/revisions.py +0 -0
  186. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/scripts/__init__.py +0 -0
  187. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/scripts/bumpversion.py +0 -0
  188. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/scripts/execrss.py +0 -0
  189. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/scripts/exectime.py +0 -0
  190. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/scripts/importtrace.py +0 -0
  191. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/scripts/interp.py +0 -0
  192. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/scripts/pyproject.py +0 -0
  193. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/scripts/slowcat.py +0 -0
  194. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/scripts/tmpexec.py +0 -0
  195. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/secrets.py +0 -0
  196. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/tagstrings.py +0 -0
  197. {omdev-0.0.0.dev213/omdev/pyproject/resources → omdev-0.0.0.dev214/omdev/tokens}/__init__.py +0 -0
  198. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/tokens/all.py +0 -0
  199. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/tokens/tokenizert.py +0 -0
  200. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/tokens/utils.py +0 -0
  201. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/tools/__init__.py +0 -0
  202. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/tools/cloc.py +0 -0
  203. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/tools/doc.py +0 -0
  204. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/tools/git.py +0 -0
  205. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/tools/importscan.py +0 -0
  206. {omdev-0.0.0.dev213/omdev/tokens → omdev-0.0.0.dev214/omdev/tools/json}/__init__.py +0 -0
  207. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/tools/json/__main__.py +0 -0
  208. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/tools/json/cli.py +0 -0
  209. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/tools/json/formats.py +0 -0
  210. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/tools/json/io.py +0 -0
  211. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/tools/json/parsing.py +0 -0
  212. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/tools/json/processing.py +0 -0
  213. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/tools/json/rendering.py +0 -0
  214. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/tools/linehisto.py +0 -0
  215. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/tools/mkenv.py +0 -0
  216. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/tools/mkrelimp.py +0 -0
  217. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/tools/notebook.py +0 -0
  218. {omdev-0.0.0.dev213/omdev/tools/json → omdev-0.0.0.dev214/omdev/tools/pawk}/__init__.py +0 -0
  219. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/tools/pawk/__main__.py +0 -0
  220. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/tools/pawk/pawk.py +0 -0
  221. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/tools/pip.py +0 -0
  222. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/tools/prof.py +0 -0
  223. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/tools/qr.py +0 -0
  224. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/tools/sqlrepl.py +0 -0
  225. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/wheelfile.py +0 -0
  226. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev.egg-info/dependency_links.txt +0 -0
  227. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev.egg-info/entry_points.txt +0 -0
  228. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev.egg-info/top_level.txt +0 -0
  229. {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: omdev
3
- Version: 0.0.0.dev213
3
+ Version: 0.0.0.dev214
4
4
  Summary: omdev
5
5
  Author: wrmsr
6
6
  License: BSD-3-Clause
@@ -12,7 +12,7 @@ Classifier: Operating System :: OS Independent
12
12
  Classifier: Operating System :: POSIX
13
13
  Requires-Python: >=3.12
14
14
  License-File: LICENSE
15
- Requires-Dist: omlish==0.0.0.dev213
15
+ Requires-Dist: omlish==0.0.0.dev214
16
16
  Provides-Extra: all
17
17
  Requires-Dist: black~=24.10; extra == "all"
18
18
  Requires-Dist: pycparser~=2.22; extra == "all"
@@ -267,7 +267,7 @@
267
267
  "module": ".tools.docker",
268
268
  "attr": "_CLI_MODULE",
269
269
  "file": "omdev/tools/docker.py",
270
- "line": 258,
270
+ "line": 264,
271
271
  "value": {
272
272
  "$.cli.types.CliModule": {
273
273
  "cmd_name": "docker",
@@ -0,0 +1,147 @@
1
+ # ruff: noqa: UP006 UP007
2
+ import abc
3
+ import os.path
4
+ import shutil
5
+ import typing as ta
6
+
7
+ from omlish.lite.cached import cached_nullary
8
+ from omlish.lite.check import check
9
+ from omlish.lite.logs import log
10
+
11
+ from .consts import CI_CACHE_VERSION
12
+
13
+
14
+ ##
15
+
16
+
17
+ @abc.abstractmethod
18
+ class FileCache(abc.ABC):
19
+ def __init__(
20
+ self,
21
+ *,
22
+ version: int = CI_CACHE_VERSION,
23
+ ) -> None:
24
+ super().__init__()
25
+
26
+ check.isinstance(version, int)
27
+ check.arg(version >= 0)
28
+ self._version = version
29
+
30
+ @property
31
+ def version(self) -> int:
32
+ return self._version
33
+
34
+ #
35
+
36
+ @abc.abstractmethod
37
+ def get_file(self, key: str) -> ta.Awaitable[ta.Optional[str]]:
38
+ raise NotImplementedError
39
+
40
+ @abc.abstractmethod
41
+ def put_file(
42
+ self,
43
+ key: str,
44
+ file_path: str,
45
+ *,
46
+ steal: bool = False,
47
+ ) -> ta.Awaitable[str]:
48
+ raise NotImplementedError
49
+
50
+
51
+ #
52
+
53
+
54
+ class DirectoryFileCache(FileCache):
55
+ def __init__(
56
+ self,
57
+ dir: str, # noqa
58
+ *,
59
+ no_create: bool = False,
60
+ no_purge: bool = False,
61
+ **kwargs: ta.Any,
62
+ ) -> None: # noqa
63
+ super().__init__(**kwargs)
64
+
65
+ self._dir = dir
66
+ self._no_create = no_create
67
+ self._no_purge = no_purge
68
+
69
+ #
70
+
71
+ VERSION_FILE_NAME = '.ci-cache-version'
72
+
73
+ @cached_nullary
74
+ def setup_dir(self) -> None:
75
+ version_file = os.path.join(self._dir, self.VERSION_FILE_NAME)
76
+
77
+ if self._no_create:
78
+ check.state(os.path.isdir(self._dir))
79
+
80
+ elif not os.path.isdir(self._dir):
81
+ os.makedirs(self._dir)
82
+ with open(version_file, 'w') as f:
83
+ f.write(str(self._version))
84
+ return
85
+
86
+ with open(version_file) as f:
87
+ dir_version = int(f.read().strip())
88
+
89
+ if dir_version == self._version:
90
+ return
91
+
92
+ if self._no_purge:
93
+ raise RuntimeError(f'{dir_version=} != {self._version=}')
94
+
95
+ dirs = [n for n in sorted(os.listdir(self._dir)) if os.path.isdir(os.path.join(self._dir, n))]
96
+ if dirs:
97
+ raise RuntimeError(
98
+ f'Refusing to remove stale cache dir {self._dir!r} '
99
+ f'due to present directories: {", ".join(dirs)}',
100
+ )
101
+
102
+ for n in sorted(os.listdir(self._dir)):
103
+ if n.startswith('.'):
104
+ continue
105
+ fp = os.path.join(self._dir, n)
106
+ check.state(os.path.isfile(fp))
107
+ log.debug('Purging stale cache file: %s', fp)
108
+ os.unlink(fp)
109
+
110
+ os.unlink(version_file)
111
+
112
+ with open(version_file, 'w') as f:
113
+ f.write(str(self._version))
114
+
115
+ #
116
+
117
+ def get_cache_file_path(
118
+ self,
119
+ key: str,
120
+ ) -> str:
121
+ self.setup_dir()
122
+ return os.path.join(self._dir, key)
123
+
124
+ def format_incomplete_file(self, f: str) -> str:
125
+ return os.path.join(os.path.dirname(f), f'_{os.path.basename(f)}.incomplete')
126
+
127
+ #
128
+
129
+ async def get_file(self, key: str) -> ta.Optional[str]:
130
+ cache_file_path = self.get_cache_file_path(key)
131
+ if not os.path.exists(cache_file_path):
132
+ return None
133
+ return cache_file_path
134
+
135
+ async def put_file(
136
+ self,
137
+ key: str,
138
+ file_path: str,
139
+ *,
140
+ steal: bool = False,
141
+ ) -> str:
142
+ cache_file_path = self.get_cache_file_path(key)
143
+ if steal:
144
+ shutil.move(file_path, cache_file_path)
145
+ else:
146
+ shutil.copyfile(file_path, cache_file_path)
147
+ return cache_file_path
@@ -1,20 +1,15 @@
1
1
  # ruff: noqa: UP006 UP007
2
- # @omlish-lite
3
2
  import dataclasses as dc
4
3
  import os.path
5
- import shutil
6
- import tarfile
7
- import tempfile
8
4
  import typing as ta
9
5
 
10
6
  from omlish.lite.cached import async_cached_nullary
11
7
  from omlish.lite.cached import cached_nullary
12
8
  from omlish.lite.check import check
13
9
  from omlish.lite.contextmanagers import AsyncExitStacked
14
- from omlish.lite.contextmanagers import defer
10
+ from omlish.os.temp import temp_file_context
15
11
 
16
12
  from .cache import FileCache
17
- from .cache import ShellCache
18
13
  from .compose import DockerComposeRun
19
14
  from .compose import get_compose_service_dependencies
20
15
  from .docker import build_docker_file_hash
@@ -25,13 +20,12 @@ from .docker import pull_docker_image
25
20
  from .docker import save_docker_tar_cmd
26
21
  from .docker import tag_docker_image
27
22
  from .requirements import build_requirements_hash
28
- from .requirements import download_requirements
29
23
  from .shell import ShellCmd
30
24
  from .utils import log_timing_context
31
25
 
32
26
 
33
27
  class Ci(AsyncExitStacked):
34
- FILE_NAME_HASH_LEN = 16
28
+ KEY_HASH_LEN = 16
35
29
 
36
30
  @dc.dataclass(frozen=True)
37
31
  class Config:
@@ -44,6 +38,8 @@ class Ci(AsyncExitStacked):
44
38
 
45
39
  cmd: ShellCmd
46
40
 
41
+ #
42
+
47
43
  requirements_txts: ta.Optional[ta.Sequence[str]] = None
48
44
 
49
45
  always_pull: bool = False
@@ -51,6 +47,10 @@ class Ci(AsyncExitStacked):
51
47
 
52
48
  no_dependencies: bool = False
53
49
 
50
+ run_options: ta.Optional[ta.Sequence[str]] = None
51
+
52
+ #
53
+
54
54
  def __post_init__(self) -> None:
55
55
  check.not_isinstance(self.requirements_txts, str)
56
56
 
@@ -58,42 +58,15 @@ class Ci(AsyncExitStacked):
58
58
  self,
59
59
  cfg: Config,
60
60
  *,
61
- shell_cache: ta.Optional[ShellCache] = None,
62
61
  file_cache: ta.Optional[FileCache] = None,
63
62
  ) -> None:
64
63
  super().__init__()
65
64
 
66
65
  self._cfg = cfg
67
- self._shell_cache = shell_cache
68
66
  self._file_cache = file_cache
69
67
 
70
68
  #
71
69
 
72
- async def _load_cache_docker_image(self, key: str) -> ta.Optional[str]:
73
- if self._shell_cache is None:
74
- return None
75
-
76
- get_cache_cmd = self._shell_cache.get_file_cmd(key)
77
- if get_cache_cmd is None:
78
- return None
79
-
80
- get_cache_cmd = dc.replace(get_cache_cmd, s=f'{get_cache_cmd.s} | zstd -cd --long') # noqa
81
-
82
- return await load_docker_tar_cmd(get_cache_cmd)
83
-
84
- async def _save_cache_docker_image(self, key: str, image: str) -> None:
85
- if self._shell_cache is None:
86
- return
87
-
88
- with self._shell_cache.put_file_cmd(key) as put_cache:
89
- put_cache_cmd = put_cache.cmd
90
-
91
- put_cache_cmd = dc.replace(put_cache_cmd, s=f'zstd | {put_cache_cmd.s}')
92
-
93
- await save_docker_tar_cmd(image, put_cache_cmd)
94
-
95
- #
96
-
97
70
  async def _load_docker_image(self, image: str) -> None:
98
71
  if not self._cfg.always_pull and (await is_docker_image_present(image)):
99
72
  return
@@ -114,24 +87,38 @@ class Ci(AsyncExitStacked):
114
87
  with log_timing_context(f'Load docker image: {image}'):
115
88
  await self._load_docker_image(image)
116
89
 
117
- @async_cached_nullary
118
- async def load_compose_service_dependencies(self) -> None:
119
- deps = get_compose_service_dependencies(
120
- self._cfg.compose_file,
121
- self._cfg.service,
122
- )
90
+ #
123
91
 
124
- for dep_image in deps.values():
125
- await self.load_docker_image(dep_image)
92
+ async def _load_cache_docker_image(self, key: str) -> ta.Optional[str]:
93
+ if self._file_cache is None:
94
+ return None
126
95
 
127
- #
96
+ cache_file = await self._file_cache.get_file(key)
97
+ if cache_file is None:
98
+ return None
128
99
 
129
- @cached_nullary
130
- def docker_file_hash(self) -> str:
131
- return build_docker_file_hash(self._cfg.docker_file)[:self.FILE_NAME_HASH_LEN]
100
+ get_cache_cmd = ShellCmd(f'cat {cache_file} | zstd -cd --long')
132
101
 
133
- async def _resolve_ci_image(self) -> str:
134
- cache_key = f'ci-{self.docker_file_hash()}'
102
+ return await load_docker_tar_cmd(get_cache_cmd)
103
+
104
+ async def _save_cache_docker_image(self, key: str, image: str) -> None:
105
+ if self._file_cache is None:
106
+ return
107
+
108
+ with temp_file_context() as tmp_file:
109
+ write_tmp_cmd = ShellCmd(f'zstd > {tmp_file}')
110
+
111
+ await save_docker_tar_cmd(image, write_tmp_cmd)
112
+
113
+ await self._file_cache.put_file(key, tmp_file, steal=True)
114
+
115
+ #
116
+
117
+ async def _resolve_docker_image(
118
+ self,
119
+ cache_key: str,
120
+ build_and_tag: ta.Callable[[str], ta.Awaitable[str]],
121
+ ) -> str:
135
122
  image_tag = f'{self._cfg.service}:{cache_key}'
136
123
 
137
124
  if not self._cfg.always_build and (await is_docker_image_present(image_tag)):
@@ -144,21 +131,35 @@ class Ci(AsyncExitStacked):
144
131
  )
145
132
  return image_tag
146
133
 
147
- image_id = await build_docker_image(
148
- self._cfg.docker_file,
149
- tag=image_tag,
150
- cwd=self._cfg.project_dir,
151
- )
134
+ image_id = await build_and_tag(image_tag)
152
135
 
153
136
  await self._save_cache_docker_image(cache_key, image_id)
154
137
 
155
138
  return image_tag
156
139
 
140
+ #
141
+
142
+ @cached_nullary
143
+ def docker_file_hash(self) -> str:
144
+ return build_docker_file_hash(self._cfg.docker_file)[:self.KEY_HASH_LEN]
145
+
146
+ async def _resolve_ci_base_image(self) -> str:
147
+ async def build_and_tag(image_tag: str) -> str:
148
+ return await build_docker_image(
149
+ self._cfg.docker_file,
150
+ tag=image_tag,
151
+ cwd=self._cfg.project_dir,
152
+ )
153
+
154
+ cache_key = f'ci-base-{self.docker_file_hash()}'
155
+
156
+ return await self._resolve_docker_image(cache_key, build_and_tag)
157
+
157
158
  @async_cached_nullary
158
- async def resolve_ci_image(self) -> str:
159
- with log_timing_context('Resolve ci image') as ltc:
160
- image_id = await self._resolve_ci_image()
161
- ltc.set_description(f'Resolve ci image: {image_id}')
159
+ async def resolve_ci_base_image(self) -> str:
160
+ with log_timing_context('Resolve ci base image') as ltc:
161
+ image_id = await self._resolve_ci_base_image()
162
+ ltc.set_description(f'Resolve ci base image: {image_id}')
162
163
  return image_id
163
164
 
164
165
  #
@@ -172,82 +173,85 @@ class Ci(AsyncExitStacked):
172
173
 
173
174
  @cached_nullary
174
175
  def requirements_hash(self) -> str:
175
- return build_requirements_hash(self.requirements_txts())[:self.FILE_NAME_HASH_LEN]
176
-
177
- async def _resolve_requirements_dir(self) -> str:
178
- tar_file_key = f'requirements-{self.docker_file_hash()}-{self.requirements_hash()}'
179
- tar_file_name = f'{tar_file_key}.tar'
180
-
181
- temp_dir = tempfile.mkdtemp()
182
- self._enter_context(defer(lambda: shutil.rmtree(temp_dir))) # noqa
176
+ return build_requirements_hash(self.requirements_txts())[:self.KEY_HASH_LEN]
183
177
 
184
- if self._file_cache is not None and (cache_tar_file := self._file_cache.get_file(tar_file_key)):
185
- with tarfile.open(cache_tar_file) as tar:
186
- tar.extractall(path=temp_dir) # noqa
178
+ async def _resolve_ci_image(self) -> str:
179
+ async def build_and_tag(image_tag: str) -> str:
180
+ base_image = await self.resolve_ci_base_image()
181
+
182
+ setup_cmds = [
183
+ ' '.join([
184
+ 'pip install',
185
+ '--no-cache-dir',
186
+ '--root-user-action ignore',
187
+ 'uv',
188
+ ]),
189
+ ' '.join([
190
+ 'uv pip install',
191
+ '--no-cache',
192
+ '--index-strategy unsafe-best-match',
193
+ '--system',
194
+ *[f'-r /project/{rf}' for rf in self._cfg.requirements_txts or []],
195
+ ]),
196
+ ]
197
+ setup_cmd = ' && '.join(setup_cmds)
198
+
199
+ docker_file_lines = [
200
+ f'FROM {base_image}',
201
+ 'RUN mkdir /project',
202
+ *[f'COPY {rf} /project/{rf}' for rf in self._cfg.requirements_txts or []],
203
+ f'RUN {setup_cmd}',
204
+ 'RUN rm /project/*',
205
+ 'WORKDIR /project',
206
+ ]
207
+
208
+ with temp_file_context() as docker_file:
209
+ with open(docker_file, 'w') as f: # noqa
210
+ f.write('\n'.join(docker_file_lines))
211
+
212
+ return await build_docker_image(
213
+ docker_file,
214
+ tag=image_tag,
215
+ cwd=self._cfg.project_dir,
216
+ )
217
+
218
+ cache_key = f'ci-{self.docker_file_hash()}-{self.requirements_hash()}'
219
+
220
+ return await self._resolve_docker_image(cache_key, build_and_tag)
187
221
 
188
- return temp_dir
222
+ @async_cached_nullary
223
+ async def resolve_ci_image(self) -> str:
224
+ with log_timing_context('Resolve ci image') as ltc:
225
+ image_id = await self._resolve_ci_image()
226
+ ltc.set_description(f'Resolve ci image: {image_id}')
227
+ return image_id
189
228
 
190
- temp_requirements_dir = os.path.join(temp_dir, 'requirements')
191
- os.makedirs(temp_requirements_dir)
229
+ #
192
230
 
193
- download_requirements(
194
- await self.resolve_ci_image(),
195
- temp_requirements_dir,
196
- self.requirements_txts(),
231
+ @async_cached_nullary
232
+ async def load_dependencies(self) -> None:
233
+ deps = get_compose_service_dependencies(
234
+ self._cfg.compose_file,
235
+ self._cfg.service,
197
236
  )
198
237
 
199
- if self._file_cache is not None:
200
- temp_tar_file = os.path.join(temp_dir, tar_file_name)
201
-
202
- with tarfile.open(temp_tar_file, 'w') as tar:
203
- for requirement_file in os.listdir(temp_requirements_dir):
204
- tar.add(
205
- os.path.join(temp_requirements_dir, requirement_file),
206
- arcname=requirement_file,
207
- )
208
-
209
- self._file_cache.put_file(os.path.basename(tar_file_key), temp_tar_file)
210
-
211
- return temp_requirements_dir
212
-
213
- @async_cached_nullary
214
- async def resolve_requirements_dir(self) -> str:
215
- with log_timing_context('Resolve requirements dir') as ltc:
216
- requirements_dir = await self._resolve_requirements_dir()
217
- ltc.set_description(f'Resolve requirements dir: {requirements_dir}')
218
- return requirements_dir
238
+ for dep_image in deps.values():
239
+ await self.load_docker_image(dep_image)
219
240
 
220
241
  #
221
242
 
222
243
  async def _run_compose_(self) -> None:
223
- setup_cmds = [
224
- 'pip install --root-user-action ignore --find-links /requirements --no-index uv',
225
- (
226
- 'uv pip install --system --find-links /requirements ' +
227
- ' '.join(f'-r /project/{rf}' for rf in self._cfg.requirements_txts or [])
228
- ),
229
- ]
230
-
231
- #
232
-
233
- ci_cmd = dc.replace(self._cfg.cmd, s=' && '.join([
234
- *setup_cmds,
235
- f'({self._cfg.cmd.s})',
236
- ]))
237
-
238
- #
239
-
240
244
  async with DockerComposeRun(DockerComposeRun.Config(
241
245
  compose_file=self._cfg.compose_file,
242
246
  service=self._cfg.service,
243
247
 
244
248
  image=await self.resolve_ci_image(),
245
249
 
246
- cmd=ci_cmd,
250
+ cmd=self._cfg.cmd,
247
251
 
248
252
  run_options=[
249
253
  '-v', f'{os.path.abspath(self._cfg.project_dir)}:/project',
250
- '-v', f'{os.path.abspath(await self.resolve_requirements_dir())}:/requirements',
254
+ *(self._cfg.run_options or []),
251
255
  ],
252
256
 
253
257
  cwd=self._cfg.project_dir,
@@ -263,10 +267,8 @@ class Ci(AsyncExitStacked):
263
267
  #
264
268
 
265
269
  async def run(self) -> None:
266
- await self.load_compose_service_dependencies()
267
-
268
270
  await self.resolve_ci_image()
269
271
 
270
- await self.resolve_requirements_dir()
272
+ await self.load_dependencies()
271
273
 
272
274
  await self._run_compose()
@@ -1,6 +1,5 @@
1
1
  # @omlish-amalg ../scripts/ci.py
2
2
  # ruff: noqa: UP006 UP007
3
- # @omlish-lite
4
3
  """
5
4
  Inputs:
6
5
  - requirements.txt
@@ -9,9 +8,11 @@ Inputs:
9
8
 
10
9
  ==
11
10
 
12
- ./python -m ci run --cache-dir ci/cache ci/project omlish-ci
11
+ ./python -m omdev.ci run --cache-dir omdev/ci/tests/cache omdev/ci/tests/project omlish-ci
13
12
  """
13
+ import argparse
14
14
  import asyncio
15
+ import itertools
15
16
  import os.path
16
17
  import sys
17
18
  import typing as ta
@@ -20,15 +21,15 @@ from omlish.argparse.cli import ArgparseCli
20
21
  from omlish.argparse.cli import argparse_arg
21
22
  from omlish.argparse.cli import argparse_cmd
22
23
  from omlish.lite.check import check
24
+ from omlish.lite.logs import log
23
25
  from omlish.logs.standard import configure_standard_logging
24
26
 
25
27
  from .cache import DirectoryFileCache
26
- from .cache import DirectoryShellCache
27
28
  from .cache import FileCache
28
- from .cache import ShellCache
29
29
  from .ci import Ci
30
30
  from .compose import get_compose_service_dependencies
31
- from .github.cache import GithubShellCache
31
+ from .github.bootstrap import is_in_github_actions
32
+ from .github.cache import GithubFileCache
32
33
  from .github.cli import GithubCli
33
34
  from .requirements import build_requirements_hash
34
35
  from .shell import ShellCmd
@@ -65,8 +66,8 @@ class CiCli(ArgparseCli):
65
66
  @argparse_cmd(
66
67
  accepts_unknown=True,
67
68
  )
68
- def github(self) -> ta.Optional[int]:
69
- return GithubCli(self.unknown_args).cli_run()
69
+ async def github(self) -> ta.Optional[int]:
70
+ return await GithubCli(self.unknown_args).async_cli_run()
70
71
 
71
72
  #
72
73
 
@@ -77,13 +78,20 @@ class CiCli(ArgparseCli):
77
78
  argparse_arg('--compose-file'),
78
79
  argparse_arg('-r', '--requirements-txt', action='append'),
79
80
 
80
- argparse_arg('--github-cache', action='store_true'),
81
81
  argparse_arg('--cache-dir'),
82
82
 
83
+ argparse_arg('--github', action='store_true'),
84
+ argparse_arg('--github-detect', action='store_true'),
85
+
83
86
  argparse_arg('--always-pull', action='store_true'),
84
87
  argparse_arg('--always-build', action='store_true'),
85
88
 
86
89
  argparse_arg('--no-dependencies', action='store_true'),
90
+
91
+ argparse_arg('-e', '--env', action='append'),
92
+ argparse_arg('-v', '--volume', action='append'),
93
+
94
+ argparse_arg('cmd', nargs=argparse.REMAINDER),
87
95
  )
88
96
  async def run(self) -> None:
89
97
  project_dir = self.args.project_dir
@@ -94,6 +102,11 @@ class CiCli(ArgparseCli):
94
102
 
95
103
  #
96
104
 
105
+ cmd = ' '.join(self.args.cmd)
106
+ check.non_empty_str(cmd)
107
+
108
+ #
109
+
97
110
  check.state(os.path.isdir(project_dir))
98
111
 
99
112
  #
@@ -102,6 +115,7 @@ class CiCli(ArgparseCli):
102
115
  for alt in alts:
103
116
  alt_file = os.path.abspath(os.path.join(project_dir, alt))
104
117
  if os.path.isfile(alt_file):
118
+ log.debug('Using %s', alt_file)
105
119
  return alt_file
106
120
  return None
107
121
 
@@ -135,6 +149,7 @@ class CiCli(ArgparseCli):
135
149
  'requirements-ci.txt',
136
150
  ]:
137
151
  if os.path.exists(os.path.join(project_dir, rf)):
152
+ log.debug('Using %s', rf)
138
153
  requirements_txts.append(rf)
139
154
  else:
140
155
  for rf in requirements_txts:
@@ -142,21 +157,34 @@ class CiCli(ArgparseCli):
142
157
 
143
158
  #
144
159
 
145
- shell_cache: ta.Optional[ShellCache] = None
160
+ github = self.args.github
161
+ if not github and self.args.github_detect:
162
+ github = is_in_github_actions()
163
+ if github:
164
+ log.debug('Github detected')
165
+
166
+ #
167
+
146
168
  file_cache: ta.Optional[FileCache] = None
147
169
  if cache_dir is not None:
148
- if not os.path.exists(cache_dir):
149
- os.makedirs(cache_dir)
150
- check.state(os.path.isdir(cache_dir))
151
-
152
- directory_file_cache = DirectoryFileCache(cache_dir)
170
+ cache_dir = os.path.abspath(cache_dir)
171
+ log.debug('Using cache dir %s', cache_dir)
172
+ if github:
173
+ file_cache = GithubFileCache(cache_dir)
174
+ else:
175
+ file_cache = DirectoryFileCache(cache_dir)
153
176
 
154
- file_cache = directory_file_cache
177
+ #
155
178
 
156
- if self.args.github_cache:
157
- shell_cache = GithubShellCache(cache_dir)
158
- else:
159
- shell_cache = DirectoryShellCache(directory_file_cache)
179
+ run_options: ta.List[str] = []
180
+ for run_arg, run_arg_vals in [
181
+ ('-e', self.args.env or []),
182
+ ('-v', self.args.volume or []),
183
+ ]:
184
+ run_options.extend(itertools.chain.from_iterable(
185
+ [run_arg, run_arg_val]
186
+ for run_arg_val in run_arg_vals
187
+ ))
160
188
 
161
189
  #
162
190
 
@@ -171,18 +199,16 @@ class CiCli(ArgparseCli):
171
199
 
172
200
  requirements_txts=requirements_txts,
173
201
 
174
- cmd=ShellCmd(' && '.join([
175
- 'cd /project',
176
- 'python3 -m pytest -svv test.py',
177
- ])),
202
+ cmd=ShellCmd(cmd),
178
203
 
179
204
  always_pull=self.args.always_pull,
180
205
  always_build=self.args.always_build,
181
206
 
182
207
  no_dependencies=self.args.no_dependencies,
208
+
209
+ run_options=run_options,
183
210
  ),
184
211
  file_cache=file_cache,
185
- shell_cache=shell_cache,
186
212
  ) as ci:
187
213
  await ci.run()
188
214