ominfra 0.0.0.dev171__tar.gz → 0.0.0.dev173__tar.gz

Sign up to get free protection for your applications and to get access to all the features.
Files changed (147) hide show
  1. {ominfra-0.0.0.dev171/ominfra.egg-info → ominfra-0.0.0.dev173}/PKG-INFO +3 -3
  2. ominfra-0.0.0.dev173/ominfra/manage/deploy/apps.py +176 -0
  3. {ominfra-0.0.0.dev171 → ominfra-0.0.0.dev173}/ominfra/manage/deploy/conf.py +55 -50
  4. ominfra-0.0.0.dev173/ominfra/manage/deploy/deploy.py +71 -0
  5. {ominfra-0.0.0.dev171 → ominfra-0.0.0.dev173}/ominfra/manage/deploy/paths/paths.py +39 -45
  6. ominfra-0.0.0.dev173/ominfra/manage/deploy/paths/types.py +8 -0
  7. {ominfra-0.0.0.dev171 → ominfra-0.0.0.dev173}/ominfra/manage/deploy/specs.py +47 -13
  8. ominfra-0.0.0.dev173/ominfra/manage/deploy/tags.py +225 -0
  9. ominfra-0.0.0.dev173/ominfra/manage/deploy/types.py +9 -0
  10. ominfra-0.0.0.dev173/ominfra/manage/targets/bestpython.sh +18 -0
  11. {ominfra-0.0.0.dev171 → ominfra-0.0.0.dev173}/ominfra/scripts/manage.py +489 -200
  12. {ominfra-0.0.0.dev171 → ominfra-0.0.0.dev173/ominfra.egg-info}/PKG-INFO +3 -3
  13. {ominfra-0.0.0.dev171 → ominfra-0.0.0.dev173}/ominfra.egg-info/SOURCES.txt +2 -0
  14. {ominfra-0.0.0.dev171 → ominfra-0.0.0.dev173}/ominfra.egg-info/requires.txt +2 -2
  15. {ominfra-0.0.0.dev171 → ominfra-0.0.0.dev173}/pyproject.toml +3 -3
  16. ominfra-0.0.0.dev171/ominfra/manage/deploy/apps.py +0 -208
  17. ominfra-0.0.0.dev171/ominfra/manage/deploy/deploy.py +0 -27
  18. ominfra-0.0.0.dev171/ominfra/manage/deploy/types.py +0 -39
  19. ominfra-0.0.0.dev171/ominfra/manage/targets/bestpython.sh +0 -21
  20. {ominfra-0.0.0.dev171 → ominfra-0.0.0.dev173}/LICENSE +0 -0
  21. {ominfra-0.0.0.dev171 → ominfra-0.0.0.dev173}/MANIFEST.in +0 -0
  22. {ominfra-0.0.0.dev171 → ominfra-0.0.0.dev173}/README.rst +0 -0
  23. {ominfra-0.0.0.dev171 → ominfra-0.0.0.dev173}/ominfra/.manifests.json +0 -0
  24. {ominfra-0.0.0.dev171 → ominfra-0.0.0.dev173}/ominfra/__about__.py +0 -0
  25. {ominfra-0.0.0.dev171 → ominfra-0.0.0.dev173}/ominfra/__init__.py +0 -0
  26. {ominfra-0.0.0.dev171 → ominfra-0.0.0.dev173}/ominfra/clouds/__init__.py +0 -0
  27. {ominfra-0.0.0.dev171 → ominfra-0.0.0.dev173}/ominfra/clouds/aws/__init__.py +0 -0
  28. {ominfra-0.0.0.dev171 → ominfra-0.0.0.dev173}/ominfra/clouds/aws/__main__.py +0 -0
  29. {ominfra-0.0.0.dev171 → ominfra-0.0.0.dev173}/ominfra/clouds/aws/auth.py +0 -0
  30. {ominfra-0.0.0.dev171 → ominfra-0.0.0.dev173}/ominfra/clouds/aws/cli.py +0 -0
  31. {ominfra-0.0.0.dev171 → ominfra-0.0.0.dev173}/ominfra/clouds/aws/dataclasses.py +0 -0
  32. {ominfra-0.0.0.dev171 → ominfra-0.0.0.dev173}/ominfra/clouds/aws/journald2aws/__init__.py +0 -0
  33. {ominfra-0.0.0.dev171 → ominfra-0.0.0.dev173}/ominfra/clouds/aws/journald2aws/__main__.py +0 -0
  34. {ominfra-0.0.0.dev171 → ominfra-0.0.0.dev173}/ominfra/clouds/aws/journald2aws/cursor.py +0 -0
  35. {ominfra-0.0.0.dev171 → ominfra-0.0.0.dev173}/ominfra/clouds/aws/journald2aws/driver.py +0 -0
  36. {ominfra-0.0.0.dev171 → ominfra-0.0.0.dev173}/ominfra/clouds/aws/journald2aws/main.py +0 -0
  37. {ominfra-0.0.0.dev171 → ominfra-0.0.0.dev173}/ominfra/clouds/aws/journald2aws/poster.py +0 -0
  38. {ominfra-0.0.0.dev171 → ominfra-0.0.0.dev173}/ominfra/clouds/aws/logs.py +0 -0
  39. {ominfra-0.0.0.dev171 → ominfra-0.0.0.dev173}/ominfra/clouds/aws/metadata.py +0 -0
  40. {ominfra-0.0.0.dev171 → ominfra-0.0.0.dev173}/ominfra/clouds/gcp/__init__.py +0 -0
  41. {ominfra-0.0.0.dev171 → ominfra-0.0.0.dev173}/ominfra/clouds/gcp/auth.py +0 -0
  42. {ominfra-0.0.0.dev171 → ominfra-0.0.0.dev173}/ominfra/cmds.py +0 -0
  43. {ominfra-0.0.0.dev171 → ominfra-0.0.0.dev173}/ominfra/configs.py +0 -0
  44. {ominfra-0.0.0.dev171 → ominfra-0.0.0.dev173}/ominfra/journald/__init__.py +0 -0
  45. {ominfra-0.0.0.dev171 → ominfra-0.0.0.dev173}/ominfra/journald/fields.py +0 -0
  46. {ominfra-0.0.0.dev171 → ominfra-0.0.0.dev173}/ominfra/journald/genmessages.py +0 -0
  47. {ominfra-0.0.0.dev171 → ominfra-0.0.0.dev173}/ominfra/journald/messages.py +0 -0
  48. {ominfra-0.0.0.dev171 → ominfra-0.0.0.dev173}/ominfra/journald/tailer.py +0 -0
  49. {ominfra-0.0.0.dev171 → ominfra-0.0.0.dev173}/ominfra/manage/__init__.py +0 -0
  50. {ominfra-0.0.0.dev171 → ominfra-0.0.0.dev173}/ominfra/manage/__main__.py +0 -0
  51. {ominfra-0.0.0.dev171 → ominfra-0.0.0.dev173}/ominfra/manage/bootstrap.py +0 -0
  52. {ominfra-0.0.0.dev171 → ominfra-0.0.0.dev173}/ominfra/manage/bootstrap_.py +0 -0
  53. {ominfra-0.0.0.dev171 → ominfra-0.0.0.dev173}/ominfra/manage/commands/__init__.py +0 -0
  54. {ominfra-0.0.0.dev171 → ominfra-0.0.0.dev173}/ominfra/manage/commands/base.py +0 -0
  55. {ominfra-0.0.0.dev171 → ominfra-0.0.0.dev173}/ominfra/manage/commands/inject.py +0 -0
  56. {ominfra-0.0.0.dev171 → ominfra-0.0.0.dev173}/ominfra/manage/commands/local.py +0 -0
  57. {ominfra-0.0.0.dev171 → ominfra-0.0.0.dev173}/ominfra/manage/commands/marshal.py +0 -0
  58. {ominfra-0.0.0.dev171 → ominfra-0.0.0.dev173}/ominfra/manage/commands/ping.py +0 -0
  59. {ominfra-0.0.0.dev171 → ominfra-0.0.0.dev173}/ominfra/manage/commands/subprocess.py +0 -0
  60. {ominfra-0.0.0.dev171 → ominfra-0.0.0.dev173}/ominfra/manage/commands/types.py +0 -0
  61. {ominfra-0.0.0.dev171 → ominfra-0.0.0.dev173}/ominfra/manage/config.py +0 -0
  62. {ominfra-0.0.0.dev171 → ominfra-0.0.0.dev173}/ominfra/manage/deploy/__init__.py +0 -0
  63. {ominfra-0.0.0.dev171 → ominfra-0.0.0.dev173}/ominfra/manage/deploy/commands.py +0 -0
  64. {ominfra-0.0.0.dev171 → ominfra-0.0.0.dev173}/ominfra/manage/deploy/config.py +0 -0
  65. {ominfra-0.0.0.dev171 → ominfra-0.0.0.dev173}/ominfra/manage/deploy/git.py +0 -0
  66. {ominfra-0.0.0.dev171 → ominfra-0.0.0.dev173}/ominfra/manage/deploy/inject.py +0 -0
  67. {ominfra-0.0.0.dev171 → ominfra-0.0.0.dev173}/ominfra/manage/deploy/interp.py +0 -0
  68. {ominfra-0.0.0.dev171 → ominfra-0.0.0.dev173}/ominfra/manage/deploy/paths/__init__.py +0 -0
  69. {ominfra-0.0.0.dev171 → ominfra-0.0.0.dev173}/ominfra/manage/deploy/paths/inject.py +0 -0
  70. {ominfra-0.0.0.dev171 → ominfra-0.0.0.dev173}/ominfra/manage/deploy/paths/manager.py +0 -0
  71. {ominfra-0.0.0.dev171 → ominfra-0.0.0.dev173}/ominfra/manage/deploy/paths/owners.py +0 -0
  72. {ominfra-0.0.0.dev171 → ominfra-0.0.0.dev173}/ominfra/manage/deploy/tmp.py +0 -0
  73. {ominfra-0.0.0.dev171 → ominfra-0.0.0.dev173}/ominfra/manage/deploy/venvs.py +0 -0
  74. {ominfra-0.0.0.dev171 → ominfra-0.0.0.dev173}/ominfra/manage/inject.py +0 -0
  75. {ominfra-0.0.0.dev171 → ominfra-0.0.0.dev173}/ominfra/manage/main.py +0 -0
  76. {ominfra-0.0.0.dev171 → ominfra-0.0.0.dev173}/ominfra/manage/marshal.py +0 -0
  77. {ominfra-0.0.0.dev171 → ominfra-0.0.0.dev173}/ominfra/manage/remote/__init__.py +0 -0
  78. {ominfra-0.0.0.dev171 → ominfra-0.0.0.dev173}/ominfra/manage/remote/_main.py +0 -0
  79. {ominfra-0.0.0.dev171 → ominfra-0.0.0.dev173}/ominfra/manage/remote/channel.py +0 -0
  80. {ominfra-0.0.0.dev171 → ominfra-0.0.0.dev173}/ominfra/manage/remote/config.py +0 -0
  81. {ominfra-0.0.0.dev171 → ominfra-0.0.0.dev173}/ominfra/manage/remote/connection.py +0 -0
  82. {ominfra-0.0.0.dev171 → ominfra-0.0.0.dev173}/ominfra/manage/remote/execution.py +0 -0
  83. {ominfra-0.0.0.dev171 → ominfra-0.0.0.dev173}/ominfra/manage/remote/inject.py +0 -0
  84. {ominfra-0.0.0.dev171 → ominfra-0.0.0.dev173}/ominfra/manage/remote/payload.py +0 -0
  85. {ominfra-0.0.0.dev171 → ominfra-0.0.0.dev173}/ominfra/manage/remote/spawning.py +0 -0
  86. {ominfra-0.0.0.dev171 → ominfra-0.0.0.dev173}/ominfra/manage/system/__init__.py +0 -0
  87. {ominfra-0.0.0.dev171 → ominfra-0.0.0.dev173}/ominfra/manage/system/commands.py +0 -0
  88. {ominfra-0.0.0.dev171 → ominfra-0.0.0.dev173}/ominfra/manage/system/config.py +0 -0
  89. {ominfra-0.0.0.dev171 → ominfra-0.0.0.dev173}/ominfra/manage/system/inject.py +0 -0
  90. {ominfra-0.0.0.dev171 → ominfra-0.0.0.dev173}/ominfra/manage/system/packages.py +0 -0
  91. {ominfra-0.0.0.dev171 → ominfra-0.0.0.dev173}/ominfra/manage/system/platforms.py +0 -0
  92. {ominfra-0.0.0.dev171 → ominfra-0.0.0.dev173}/ominfra/manage/targets/__init__.py +0 -0
  93. {ominfra-0.0.0.dev171 → ominfra-0.0.0.dev173}/ominfra/manage/targets/bestpython.py +0 -0
  94. {ominfra-0.0.0.dev171 → ominfra-0.0.0.dev173}/ominfra/manage/targets/connection.py +0 -0
  95. {ominfra-0.0.0.dev171 → ominfra-0.0.0.dev173}/ominfra/manage/targets/inject.py +0 -0
  96. {ominfra-0.0.0.dev171 → ominfra-0.0.0.dev173}/ominfra/manage/targets/targets.py +0 -0
  97. {ominfra-0.0.0.dev171 → ominfra-0.0.0.dev173}/ominfra/pyremote.py +0 -0
  98. {ominfra-0.0.0.dev171 → ominfra-0.0.0.dev173}/ominfra/scripts/__init__.py +0 -0
  99. {ominfra-0.0.0.dev171 → ominfra-0.0.0.dev173}/ominfra/scripts/journald2aws.py +0 -0
  100. {ominfra-0.0.0.dev171 → ominfra-0.0.0.dev173}/ominfra/scripts/supervisor.py +0 -0
  101. {ominfra-0.0.0.dev171 → ominfra-0.0.0.dev173}/ominfra/ssh.py +0 -0
  102. {ominfra-0.0.0.dev171 → ominfra-0.0.0.dev173}/ominfra/supervisor/LICENSE.txt +0 -0
  103. {ominfra-0.0.0.dev171 → ominfra-0.0.0.dev173}/ominfra/supervisor/__init__.py +0 -0
  104. {ominfra-0.0.0.dev171 → ominfra-0.0.0.dev173}/ominfra/supervisor/__main__.py +0 -0
  105. {ominfra-0.0.0.dev171 → ominfra-0.0.0.dev173}/ominfra/supervisor/configs.py +0 -0
  106. {ominfra-0.0.0.dev171 → ominfra-0.0.0.dev173}/ominfra/supervisor/dispatchers.py +0 -0
  107. {ominfra-0.0.0.dev171 → ominfra-0.0.0.dev173}/ominfra/supervisor/dispatchersimpl.py +0 -0
  108. {ominfra-0.0.0.dev171 → ominfra-0.0.0.dev173}/ominfra/supervisor/events.py +0 -0
  109. {ominfra-0.0.0.dev171 → ominfra-0.0.0.dev173}/ominfra/supervisor/exceptions.py +0 -0
  110. {ominfra-0.0.0.dev171 → ominfra-0.0.0.dev173}/ominfra/supervisor/groups.py +0 -0
  111. {ominfra-0.0.0.dev171 → ominfra-0.0.0.dev173}/ominfra/supervisor/groupsimpl.py +0 -0
  112. {ominfra-0.0.0.dev171 → ominfra-0.0.0.dev173}/ominfra/supervisor/http.py +0 -0
  113. {ominfra-0.0.0.dev171 → ominfra-0.0.0.dev173}/ominfra/supervisor/inject.py +0 -0
  114. {ominfra-0.0.0.dev171 → ominfra-0.0.0.dev173}/ominfra/supervisor/io.py +0 -0
  115. {ominfra-0.0.0.dev171 → ominfra-0.0.0.dev173}/ominfra/supervisor/main.py +0 -0
  116. {ominfra-0.0.0.dev171 → ominfra-0.0.0.dev173}/ominfra/supervisor/pipes.py +0 -0
  117. {ominfra-0.0.0.dev171 → ominfra-0.0.0.dev173}/ominfra/supervisor/privileges.py +0 -0
  118. {ominfra-0.0.0.dev171 → ominfra-0.0.0.dev173}/ominfra/supervisor/process.py +0 -0
  119. {ominfra-0.0.0.dev171 → ominfra-0.0.0.dev173}/ominfra/supervisor/processimpl.py +0 -0
  120. {ominfra-0.0.0.dev171 → ominfra-0.0.0.dev173}/ominfra/supervisor/setup.py +0 -0
  121. {ominfra-0.0.0.dev171 → ominfra-0.0.0.dev173}/ominfra/supervisor/setupimpl.py +0 -0
  122. {ominfra-0.0.0.dev171 → ominfra-0.0.0.dev173}/ominfra/supervisor/signals.py +0 -0
  123. {ominfra-0.0.0.dev171 → ominfra-0.0.0.dev173}/ominfra/supervisor/spawning.py +0 -0
  124. {ominfra-0.0.0.dev171 → ominfra-0.0.0.dev173}/ominfra/supervisor/spawningimpl.py +0 -0
  125. {ominfra-0.0.0.dev171 → ominfra-0.0.0.dev173}/ominfra/supervisor/states.py +0 -0
  126. {ominfra-0.0.0.dev171 → ominfra-0.0.0.dev173}/ominfra/supervisor/supervisor.py +0 -0
  127. {ominfra-0.0.0.dev171 → ominfra-0.0.0.dev173}/ominfra/supervisor/types.py +0 -0
  128. {ominfra-0.0.0.dev171 → ominfra-0.0.0.dev173}/ominfra/supervisor/utils/__init__.py +0 -0
  129. {ominfra-0.0.0.dev171 → ominfra-0.0.0.dev173}/ominfra/supervisor/utils/collections.py +0 -0
  130. {ominfra-0.0.0.dev171 → ominfra-0.0.0.dev173}/ominfra/supervisor/utils/diag.py +0 -0
  131. {ominfra-0.0.0.dev171 → ominfra-0.0.0.dev173}/ominfra/supervisor/utils/fds.py +0 -0
  132. {ominfra-0.0.0.dev171 → ominfra-0.0.0.dev173}/ominfra/supervisor/utils/fs.py +0 -0
  133. {ominfra-0.0.0.dev171 → ominfra-0.0.0.dev173}/ominfra/supervisor/utils/os.py +0 -0
  134. {ominfra-0.0.0.dev171 → ominfra-0.0.0.dev173}/ominfra/supervisor/utils/ostypes.py +0 -0
  135. {ominfra-0.0.0.dev171 → ominfra-0.0.0.dev173}/ominfra/supervisor/utils/signals.py +0 -0
  136. {ominfra-0.0.0.dev171 → ominfra-0.0.0.dev173}/ominfra/supervisor/utils/strings.py +0 -0
  137. {ominfra-0.0.0.dev171 → ominfra-0.0.0.dev173}/ominfra/supervisor/utils/users.py +0 -0
  138. {ominfra-0.0.0.dev171 → ominfra-0.0.0.dev173}/ominfra/tailscale/__init__.py +0 -0
  139. {ominfra-0.0.0.dev171 → ominfra-0.0.0.dev173}/ominfra/tailscale/api.py +0 -0
  140. {ominfra-0.0.0.dev171 → ominfra-0.0.0.dev173}/ominfra/tailscale/cli.py +0 -0
  141. {ominfra-0.0.0.dev171 → ominfra-0.0.0.dev173}/ominfra/threadworkers.py +0 -0
  142. {ominfra-0.0.0.dev171 → ominfra-0.0.0.dev173}/ominfra/tools/__init__.py +0 -0
  143. {ominfra-0.0.0.dev171 → ominfra-0.0.0.dev173}/ominfra/tools/listresources.py +0 -0
  144. {ominfra-0.0.0.dev171 → ominfra-0.0.0.dev173}/ominfra.egg-info/dependency_links.txt +0 -0
  145. {ominfra-0.0.0.dev171 → ominfra-0.0.0.dev173}/ominfra.egg-info/entry_points.txt +0 -0
  146. {ominfra-0.0.0.dev171 → ominfra-0.0.0.dev173}/ominfra.egg-info/top_level.txt +0 -0
  147. {ominfra-0.0.0.dev171 → ominfra-0.0.0.dev173}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ominfra
3
- Version: 0.0.0.dev171
3
+ Version: 0.0.0.dev173
4
4
  Summary: ominfra
5
5
  Author: wrmsr
6
6
  License: BSD-3-Clause
@@ -12,8 +12,8 @@ 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: omdev==0.0.0.dev171
16
- Requires-Dist: omlish==0.0.0.dev171
15
+ Requires-Dist: omdev==0.0.0.dev173
16
+ Requires-Dist: omlish==0.0.0.dev173
17
17
  Provides-Extra: all
18
18
  Requires-Dist: paramiko~=3.5; extra == "all"
19
19
  Requires-Dist: asyncssh~=2.18; extra == "all"
@@ -0,0 +1,176 @@
1
+ # ruff: noqa: UP006 UP007
2
+ import os.path
3
+ import typing as ta
4
+
5
+ from omlish.lite.cached import cached_nullary
6
+ from omlish.lite.check import check
7
+ from omlish.os.paths import relative_symlink
8
+
9
+ from .conf import DeployConfManager
10
+ from .git import DeployGitManager
11
+ from .paths.owners import DeployPathOwner
12
+ from .paths.paths import DeployPath
13
+ from .specs import DeployAppSpec
14
+ from .tags import DeployTagMap
15
+ from .types import DeployHome
16
+ from .venvs import DeployVenvManager
17
+
18
+
19
+ class DeployAppManager(DeployPathOwner):
20
+ def __init__(
21
+ self,
22
+ *,
23
+ deploy_home: ta.Optional[DeployHome] = None,
24
+
25
+ conf: DeployConfManager,
26
+ git: DeployGitManager,
27
+ venvs: DeployVenvManager,
28
+ ) -> None:
29
+ super().__init__()
30
+
31
+ self._deploy_home = deploy_home
32
+
33
+ self._conf = conf
34
+ self._git = git
35
+ self._venvs = venvs
36
+
37
+ #
38
+
39
+ _APP_DIR_STR = 'apps/@app/@time--@app-rev--@app-key/'
40
+ _APP_DIR = DeployPath.parse(_APP_DIR_STR)
41
+
42
+ _DEPLOY_DIR_STR = 'deploys/@time--@deploy-key/'
43
+ _DEPLOY_DIR = DeployPath.parse(_DEPLOY_DIR_STR)
44
+
45
+ _APP_DEPLOY_LINK = DeployPath.parse(f'{_DEPLOY_DIR_STR}apps/@app')
46
+ _CONF_DEPLOY_DIR = DeployPath.parse(f'{_DEPLOY_DIR_STR}conf/@conf/')
47
+
48
+ @cached_nullary
49
+ def get_owned_deploy_paths(self) -> ta.AbstractSet[DeployPath]:
50
+ return {
51
+ self._APP_DIR,
52
+
53
+ self._DEPLOY_DIR,
54
+
55
+ self._APP_DEPLOY_LINK,
56
+ self._CONF_DEPLOY_DIR,
57
+
58
+ *[
59
+ DeployPath.parse(f'{self._APP_DIR_STR}{sfx}/')
60
+ for sfx in [
61
+ 'conf',
62
+ 'git',
63
+ 'venv',
64
+ ]
65
+ ],
66
+ }
67
+
68
+ #
69
+
70
+ async def prepare_app(
71
+ self,
72
+ spec: DeployAppSpec,
73
+ tags: DeployTagMap,
74
+ ) -> None:
75
+ deploy_home = check.non_empty_str(self._deploy_home)
76
+
77
+ def build_path(pth: DeployPath) -> str:
78
+ return os.path.join(deploy_home, pth.render(tags))
79
+
80
+ app_dir = build_path(self._APP_DIR)
81
+ deploy_dir = build_path(self._DEPLOY_DIR)
82
+ app_deploy_link = build_path(self._APP_DEPLOY_LINK)
83
+
84
+ #
85
+
86
+ os.makedirs(deploy_dir, exist_ok=True)
87
+
88
+ deploying_link = os.path.join(deploy_home, 'deploys/deploying')
89
+ relative_symlink(
90
+ deploy_dir,
91
+ deploying_link,
92
+ target_is_directory=True,
93
+ make_dirs=True,
94
+ )
95
+
96
+ #
97
+
98
+ os.makedirs(app_dir)
99
+ relative_symlink(
100
+ app_dir,
101
+ app_deploy_link,
102
+ target_is_directory=True,
103
+ make_dirs=True,
104
+ )
105
+
106
+ #
107
+
108
+ deploy_conf_dir = os.path.join(deploy_dir, 'conf')
109
+ os.makedirs(deploy_conf_dir, exist_ok=True)
110
+
111
+ #
112
+
113
+ # def mirror_symlinks(src: str, dst: str) -> None:
114
+ # def mirror_link(lp: str) -> None:
115
+ # check.state(os.path.islink(lp))
116
+ # shutil.copy2(
117
+ # lp,
118
+ # os.path.join(dst, os.path.relpath(lp, src)),
119
+ # follow_symlinks=False,
120
+ # )
121
+ #
122
+ # for dp, dns, fns in os.walk(src, followlinks=False):
123
+ # for fn in fns:
124
+ # mirror_link(os.path.join(dp, fn))
125
+ #
126
+ # for dn in dns:
127
+ # dp2 = os.path.join(dp, dn)
128
+ # if os.path.islink(dp2):
129
+ # mirror_link(dp2)
130
+ # else:
131
+ # os.makedirs(os.path.join(dst, os.path.relpath(dp2, src)))
132
+
133
+ current_link = os.path.join(deploy_home, 'deploys/current')
134
+
135
+ # if os.path.exists(current_link):
136
+ # mirror_symlinks(
137
+ # os.path.join(current_link, 'conf'),
138
+ # conf_tag_dir,
139
+ # )
140
+ # mirror_symlinks(
141
+ # os.path.join(current_link, 'apps'),
142
+ # os.path.join(deploy_dir, 'apps'),
143
+ # )
144
+
145
+ #
146
+
147
+ app_git_dir = os.path.join(app_dir, 'git')
148
+ await self._git.checkout(
149
+ spec.git,
150
+ app_git_dir,
151
+ )
152
+
153
+ #
154
+
155
+ if spec.venv is not None:
156
+ app_venv_dir = os.path.join(app_dir, 'venv')
157
+ await self._venvs.setup_venv(
158
+ spec.venv,
159
+ app_git_dir,
160
+ app_venv_dir,
161
+ )
162
+
163
+ #
164
+
165
+ if spec.conf is not None:
166
+ app_conf_dir = os.path.join(app_dir, 'conf')
167
+ await self._conf.write_app_conf(
168
+ spec.conf,
169
+ tags,
170
+ app_conf_dir,
171
+ deploy_conf_dir,
172
+ )
173
+
174
+ #
175
+
176
+ os.replace(deploying_link, current_link)
@@ -23,13 +23,15 @@ from omlish.lite.check import check
23
23
  from omlish.os.paths import is_path_in_dir
24
24
  from omlish.os.paths import relative_symlink
25
25
 
26
- from .paths.paths import DEPLOY_PATH_PLACEHOLDER_SEPARATOR
27
- from .specs import AppDeployConfLink
28
- from .specs import DeployConfFile
29
- from .specs import DeployConfLink
30
- from .specs import DeployConfSpec
31
- from .specs import TagDeployConfLink
32
- from .types import DeployAppTag
26
+ from .paths.paths import DeployPath
27
+ from .specs import AllActiveDeployAppConfLink
28
+ from .specs import CurrentOnlyDeployAppConfLink
29
+ from .specs import DeployAppConfFile
30
+ from .specs import DeployAppConfLink
31
+ from .specs import DeployAppConfSpec
32
+ from .tags import DEPLOY_TAG_SEPARATOR
33
+ from .tags import DeployApp
34
+ from .tags import DeployTagMap
33
35
  from .types import DeployHome
34
36
 
35
37
 
@@ -45,18 +47,18 @@ class DeployConfManager:
45
47
 
46
48
  #
47
49
 
48
- async def _write_conf_file(
50
+ async def _write_app_conf_file(
49
51
  self,
50
- cf: DeployConfFile,
51
- conf_dir: str,
52
+ acf: DeployAppConfFile,
53
+ app_conf_dir: str,
52
54
  ) -> None:
53
- conf_file = os.path.join(conf_dir, cf.path)
54
- check.arg(is_path_in_dir(conf_dir, conf_file))
55
+ conf_file = os.path.join(app_conf_dir, acf.path)
56
+ check.arg(is_path_in_dir(app_conf_dir, conf_file))
55
57
 
56
58
  os.makedirs(os.path.dirname(conf_file), exist_ok=True)
57
59
 
58
60
  with open(conf_file, 'w') as f: # noqa
59
- f.write(cf.body)
61
+ f.write(acf.body)
60
62
 
61
63
  #
62
64
 
@@ -65,15 +67,18 @@ class DeployConfManager:
65
67
  link_src: str
66
68
  link_dst: str
67
69
 
68
- def _compute_conf_link_dst(
70
+ _UNIQUE_LINK_NAME_STR = '@app--@time--@app-key'
71
+ _UNIQUE_LINK_NAME = DeployPath.parse(_UNIQUE_LINK_NAME_STR)
72
+
73
+ def _compute_app_conf_link_dst(
69
74
  self,
70
- link: DeployConfLink,
71
- app_tag: DeployAppTag,
72
- conf_dir: str,
73
- link_dir: str,
75
+ link: DeployAppConfLink,
76
+ tags: DeployTagMap,
77
+ app_conf_dir: str,
78
+ conf_link_dir: str,
74
79
  ) -> _ComputedConfLink:
75
- link_src = os.path.join(conf_dir, link.src)
76
- check.arg(is_path_in_dir(conf_dir, link_src))
80
+ link_src = os.path.join(app_conf_dir, link.src)
81
+ check.arg(is_path_in_dir(app_conf_dir, link_src))
77
82
 
78
83
  #
79
84
 
@@ -88,7 +93,7 @@ class DeployConfManager:
88
93
  d, f = os.path.split(link.src)
89
94
  # TODO: check filename :|
90
95
  link_dst_pfx = d + '/'
91
- link_dst_sfx = DEPLOY_PATH_PLACEHOLDER_SEPARATOR + f
96
+ link_dst_sfx = DEPLOY_TAG_SEPARATOR + f
92
97
 
93
98
  else: # noqa
94
99
  # @conf(.ext)* - links a single file in root of app conf dir to conf/@conf/@dst(.ext)*
@@ -102,10 +107,10 @@ class DeployConfManager:
102
107
 
103
108
  #
104
109
 
105
- if isinstance(link, AppDeployConfLink):
106
- link_dst_mid = str(app_tag.app)
107
- elif isinstance(link, TagDeployConfLink):
108
- link_dst_mid = DEPLOY_PATH_PLACEHOLDER_SEPARATOR.join([app_tag.app, app_tag.tag])
110
+ if isinstance(link, CurrentOnlyDeployAppConfLink):
111
+ link_dst_mid = str(tags[DeployApp].s)
112
+ elif isinstance(link, AllActiveDeployAppConfLink):
113
+ link_dst_mid = self._UNIQUE_LINK_NAME.render(tags)
109
114
  else:
110
115
  raise TypeError(link)
111
116
 
@@ -116,7 +121,7 @@ class DeployConfManager:
116
121
  link_dst_mid,
117
122
  link_dst_sfx,
118
123
  ])
119
- link_dst = os.path.join(link_dir, link_dst_name)
124
+ link_dst = os.path.join(conf_link_dir, link_dst_name)
120
125
 
121
126
  return DeployConfManager._ComputedConfLink(
122
127
  is_dir=is_dir,
@@ -124,24 +129,24 @@ class DeployConfManager:
124
129
  link_dst=link_dst,
125
130
  )
126
131
 
127
- async def _make_conf_link(
132
+ async def _make_app_conf_link(
128
133
  self,
129
- link: DeployConfLink,
130
- app_tag: DeployAppTag,
131
- conf_dir: str,
132
- link_dir: str,
134
+ link: DeployAppConfLink,
135
+ tags: DeployTagMap,
136
+ app_conf_dir: str,
137
+ conf_link_dir: str,
133
138
  ) -> None:
134
- comp = self._compute_conf_link_dst(
139
+ comp = self._compute_app_conf_link_dst(
135
140
  link,
136
- app_tag,
137
- conf_dir,
138
- link_dir,
141
+ tags,
142
+ app_conf_dir,
143
+ conf_link_dir,
139
144
  )
140
145
 
141
146
  #
142
147
 
143
- check.arg(is_path_in_dir(conf_dir, comp.link_src))
144
- check.arg(is_path_in_dir(link_dir, comp.link_dst))
148
+ check.arg(is_path_in_dir(app_conf_dir, comp.link_src))
149
+ check.arg(is_path_in_dir(conf_link_dir, comp.link_dst))
145
150
 
146
151
  if comp.is_dir:
147
152
  check.arg(os.path.isdir(comp.link_src))
@@ -159,25 +164,25 @@ class DeployConfManager:
159
164
 
160
165
  #
161
166
 
162
- async def write_conf(
167
+ async def write_app_conf(
163
168
  self,
164
- spec: DeployConfSpec,
165
- app_tag: DeployAppTag,
166
- conf_dir: str,
167
- link_dir: str,
169
+ spec: DeployAppConfSpec,
170
+ tags: DeployTagMap,
171
+ app_conf_dir: str,
172
+ conf_link_dir: str,
168
173
  ) -> None:
169
- for cf in spec.files or []:
170
- await self._write_conf_file(
171
- cf,
172
- conf_dir,
174
+ for acf in spec.files or []:
175
+ await self._write_app_conf_file(
176
+ acf,
177
+ app_conf_dir,
173
178
  )
174
179
 
175
180
  #
176
181
 
177
182
  for link in spec.links or []:
178
- await self._make_conf_link(
183
+ await self._make_app_conf_link(
179
184
  link,
180
- app_tag,
181
- conf_dir,
182
- link_dir,
185
+ tags,
186
+ app_conf_dir,
187
+ conf_link_dir,
183
188
  )
@@ -0,0 +1,71 @@
1
+ # ruff: noqa: UP006 UP007
2
+ import datetime
3
+ import typing as ta
4
+
5
+ from omlish.lite.typing import Func0
6
+
7
+ from .apps import DeployAppManager
8
+ from .paths.manager import DeployPathsManager
9
+ from .specs import DeploySpec
10
+ from .tags import DeployAppRev
11
+ from .tags import DeployTagMap
12
+ from .tags import DeployTime
13
+
14
+
15
+ DEPLOY_TAG_DATETIME_FMT = '%Y%m%dT%H%M%SZ'
16
+
17
+
18
+ DeployManagerUtcClock = ta.NewType('DeployManagerUtcClock', Func0[datetime.datetime])
19
+
20
+
21
+ class DeployManager:
22
+ def __init__(
23
+ self,
24
+ *,
25
+ apps: DeployAppManager,
26
+ paths: DeployPathsManager,
27
+
28
+ utc_clock: ta.Optional[DeployManagerUtcClock] = None,
29
+ ):
30
+ super().__init__()
31
+
32
+ self._apps = apps
33
+ self._paths = paths
34
+
35
+ self._utc_clock = utc_clock
36
+
37
+ def _utc_now(self) -> datetime.datetime:
38
+ if self._utc_clock is not None:
39
+ return self._utc_clock() # noqa
40
+ else:
41
+ return datetime.datetime.now(tz=datetime.timezone.utc) # noqa
42
+
43
+ def _make_deploy_time(self) -> DeployTime:
44
+ return DeployTime(self._utc_now().strftime(DEPLOY_TAG_DATETIME_FMT))
45
+
46
+ async def run_deploy(
47
+ self,
48
+ spec: DeploySpec,
49
+ ) -> None:
50
+ self._paths.validate_deploy_paths()
51
+
52
+ #
53
+
54
+ deploy_tags = DeployTagMap(
55
+ self._make_deploy_time(),
56
+ spec.key(),
57
+ )
58
+
59
+ #
60
+
61
+ for app in spec.apps:
62
+ app_tags = deploy_tags.add(
63
+ app.app,
64
+ app.key(),
65
+ DeployAppRev(app.git.rev),
66
+ )
67
+
68
+ await self._apps.prepare_app(
69
+ app,
70
+ app_tags,
71
+ )
@@ -13,38 +13,28 @@ import dataclasses as dc
13
13
  import itertools
14
14
  import typing as ta
15
15
 
16
+ from omlish.lite.cached import cached_nullary
16
17
  from omlish.lite.check import check
17
18
  from omlish.lite.strings import split_keep_delimiter
18
19
 
19
- from ..types import DeployPathKind
20
- from ..types import DeployPathPlaceholder
20
+ from ..tags import DEPLOY_TAG_DELIMITERS
21
+ from ..tags import DEPLOY_TAG_SIGIL
22
+ from ..tags import DEPLOY_TAGS_BY_NAME
23
+ from ..tags import DeployTag
24
+ from ..tags import DeployTagMap
25
+ from .types import DeployPathKind
21
26
 
22
27
 
23
28
  ##
24
29
 
25
30
 
26
- DEPLOY_PATH_PLACEHOLDER_SIGIL = '@'
27
- DEPLOY_PATH_PLACEHOLDER_SEPARATOR = '--'
28
-
29
- DEPLOY_PATH_PLACEHOLDER_DELIMITERS: ta.AbstractSet[str] = frozenset([
30
- DEPLOY_PATH_PLACEHOLDER_SEPARATOR,
31
- '.',
32
- ])
33
-
34
- DEPLOY_PATH_PLACEHOLDERS: ta.FrozenSet[str] = frozenset([
35
- 'app',
36
- 'tag',
37
- 'conf',
38
- ])
39
-
40
-
41
31
  class DeployPathError(Exception):
42
32
  pass
43
33
 
44
34
 
45
35
  class DeployPathRenderable(abc.ABC):
46
36
  @abc.abstractmethod
47
- def render(self, placeholders: ta.Optional[ta.Mapping[DeployPathPlaceholder, str]] = None) -> str:
37
+ def render(self, tags: ta.Optional[DeployTagMap] = None) -> str:
48
38
  raise NotImplementedError
49
39
 
50
40
 
@@ -55,26 +45,30 @@ class DeployPathNamePart(DeployPathRenderable, abc.ABC):
55
45
  @classmethod
56
46
  def parse(cls, s: str) -> 'DeployPathNamePart':
57
47
  check.non_empty_str(s)
58
- if s.startswith(DEPLOY_PATH_PLACEHOLDER_SIGIL):
59
- return PlaceholderDeployPathNamePart(s[1:])
60
- elif s in DEPLOY_PATH_PLACEHOLDER_DELIMITERS:
48
+ if s.startswith(DEPLOY_TAG_SIGIL):
49
+ return TagDeployPathNamePart(s[1:])
50
+ elif s in DEPLOY_TAG_DELIMITERS:
61
51
  return DelimiterDeployPathNamePart(s)
62
52
  else:
63
53
  return ConstDeployPathNamePart(s)
64
54
 
65
55
 
66
56
  @dc.dataclass(frozen=True)
67
- class PlaceholderDeployPathNamePart(DeployPathNamePart):
68
- placeholder: str # DeployPathPlaceholder
57
+ class TagDeployPathNamePart(DeployPathNamePart):
58
+ name: str
69
59
 
70
60
  def __post_init__(self) -> None:
71
- check.in_(self.placeholder, DEPLOY_PATH_PLACEHOLDERS)
61
+ check.in_(self.name, DEPLOY_TAGS_BY_NAME)
72
62
 
73
- def render(self, placeholders: ta.Optional[ta.Mapping[DeployPathPlaceholder, str]] = None) -> str:
74
- if placeholders is not None:
75
- return placeholders[self.placeholder] # type: ignore
63
+ @property
64
+ def tag(self) -> ta.Type[DeployTag]:
65
+ return DEPLOY_TAGS_BY_NAME[self.name]
66
+
67
+ def render(self, tags: ta.Optional[DeployTagMap] = None) -> str:
68
+ if tags is not None:
69
+ return tags[self.tag].s
76
70
  else:
77
- return DEPLOY_PATH_PLACEHOLDER_SIGIL + self.placeholder
71
+ return DEPLOY_TAG_SIGIL + self.name
78
72
 
79
73
 
80
74
  @dc.dataclass(frozen=True)
@@ -82,9 +76,9 @@ class DelimiterDeployPathNamePart(DeployPathNamePart):
82
76
  delimiter: str
83
77
 
84
78
  def __post_init__(self) -> None:
85
- check.in_(self.delimiter, DEPLOY_PATH_PLACEHOLDER_DELIMITERS)
79
+ check.in_(self.delimiter, DEPLOY_TAG_DELIMITERS)
86
80
 
87
- def render(self, placeholders: ta.Optional[ta.Mapping[DeployPathPlaceholder, str]] = None) -> str:
81
+ def render(self, tags: ta.Optional[DeployTagMap] = None) -> str:
88
82
  return self.delimiter
89
83
 
90
84
 
@@ -94,10 +88,10 @@ class ConstDeployPathNamePart(DeployPathNamePart):
94
88
 
95
89
  def __post_init__(self) -> None:
96
90
  check.non_empty_str(self.const)
97
- for c in [*DEPLOY_PATH_PLACEHOLDER_DELIMITERS, DEPLOY_PATH_PLACEHOLDER_SIGIL, '/']:
91
+ for c in [*DEPLOY_TAG_DELIMITERS, DEPLOY_TAG_SIGIL, '/']:
98
92
  check.not_in(c, self.const)
99
93
 
100
- def render(self, placeholders: ta.Optional[ta.Mapping[DeployPathPlaceholder, str]] = None) -> str:
94
+ def render(self, tags: ta.Optional[DeployTagMap] = None) -> str:
101
95
  return self.const
102
96
 
103
97
 
@@ -112,8 +106,8 @@ class DeployPathName(DeployPathRenderable):
112
106
  if len(gl := list(g)) > 1:
113
107
  raise DeployPathError(f'May not have consecutive path name part types: {k} {gl}')
114
108
 
115
- def render(self, placeholders: ta.Optional[ta.Mapping[DeployPathPlaceholder, str]] = None) -> str:
116
- return ''.join(p.render(placeholders) for p in self.parts)
109
+ def render(self, tags: ta.Optional[DeployTagMap] = None) -> str:
110
+ return ''.join(p.render(tags) for p in self.parts)
117
111
 
118
112
  @classmethod
119
113
  def parse(cls, s: str) -> 'DeployPathName':
@@ -123,7 +117,7 @@ class DeployPathName(DeployPathRenderable):
123
117
  i = 0
124
118
  ps = []
125
119
  while i < len(s):
126
- ns = [(n, d) for d in DEPLOY_PATH_PLACEHOLDER_DELIMITERS if (n := s.find(d, i)) >= 0]
120
+ ns = [(n, d) for d in DEPLOY_TAG_DELIMITERS if (n := s.find(d, i)) >= 0]
127
121
  if not ns:
128
122
  ps.append(s[i:])
129
123
  break
@@ -147,8 +141,8 @@ class DeployPathPart(DeployPathRenderable, abc.ABC): # noqa
147
141
  def kind(self) -> DeployPathKind:
148
142
  raise NotImplementedError
149
143
 
150
- def render(self, placeholders: ta.Optional[ta.Mapping[DeployPathPlaceholder, str]] = None) -> str:
151
- return self.name.render(placeholders) + ('/' if self.kind == 'dir' else '')
144
+ def render(self, tags: ta.Optional[DeployTagMap] = None) -> str:
145
+ return self.name.render(tags) + ('/' if self.kind == 'dir' else '')
152
146
 
153
147
  @classmethod
154
148
  def parse(cls, s: str) -> 'DeployPathPart':
@@ -194,20 +188,20 @@ class DeployPath:
194
188
  for p in self.parts[:-1]:
195
189
  check.equal(p.kind, 'dir')
196
190
 
197
- pd: ta.Dict[DeployPathPlaceholder, ta.List[int]] = {}
191
+ @cached_nullary
192
+ def tag_indices(self) -> ta.Mapping[ta.Type[DeployTag], ta.Sequence[int]]:
193
+ pd: ta.Dict[ta.Type[DeployTag], ta.List[int]] = {}
198
194
  for i, np in enumerate(self.name_parts):
199
- if isinstance(np, PlaceholderDeployPathNamePart):
200
- pd.setdefault(ta.cast(DeployPathPlaceholder, np.placeholder), []).append(i)
201
-
202
- # if 'tag' in pd and 'app' not in pd:
203
- # raise DeployPathError('Tag placeholder in path without app', self)
195
+ if isinstance(np, TagDeployPathNamePart):
196
+ pd.setdefault(np.tag, []).append(i)
197
+ return pd
204
198
 
205
199
  @property
206
200
  def kind(self) -> ta.Literal['file', 'dir']:
207
201
  return self.parts[-1].kind
208
202
 
209
- def render(self, placeholders: ta.Optional[ta.Mapping[DeployPathPlaceholder, str]] = None) -> str:
210
- return ''.join([p.render(placeholders) for p in self.parts])
203
+ def render(self, tags: ta.Optional[DeployTagMap] = None) -> str:
204
+ return ''.join([p.render(tags) for p in self.parts])
211
205
 
212
206
  @classmethod
213
207
  def parse(cls, s: str) -> 'DeployPath':
@@ -0,0 +1,8 @@
1
+ # ruff: noqa: UP006 UP007
2
+ import typing as ta
3
+
4
+
5
+ DeployPathKind = ta.Literal['dir', 'file'] # ta.TypeAlias
6
+
7
+
8
+ ##