ominfra 0.0.0.dev167__tar.gz → 0.0.0.dev169__tar.gz

Sign up to get free protection for your applications and to get access to all the features.
Files changed (142) hide show
  1. {ominfra-0.0.0.dev167/ominfra.egg-info → ominfra-0.0.0.dev169}/PKG-INFO +3 -3
  2. ominfra-0.0.0.dev169/ominfra/manage/deploy/apps.py +175 -0
  3. {ominfra-0.0.0.dev167 → ominfra-0.0.0.dev169}/ominfra/manage/deploy/commands.py +3 -3
  4. {ominfra-0.0.0.dev167 → ominfra-0.0.0.dev169}/ominfra/manage/deploy/conf.py +73 -33
  5. ominfra-0.0.0.dev169/ominfra/manage/deploy/deploy.py +27 -0
  6. {ominfra-0.0.0.dev167 → ominfra-0.0.0.dev169}/ominfra/manage/deploy/git.py +1 -1
  7. {ominfra-0.0.0.dev167 → ominfra-0.0.0.dev169}/ominfra/manage/deploy/inject.py +7 -7
  8. ominfra-0.0.0.dev169/ominfra/manage/deploy/paths/inject.py +21 -0
  9. ominfra-0.0.0.dev169/ominfra/manage/deploy/paths/manager.py +36 -0
  10. ominfra-0.0.0.dev169/ominfra/manage/deploy/paths/owners.py +50 -0
  11. ominfra-0.0.0.dev169/ominfra/manage/deploy/paths/paths.py +216 -0
  12. {ominfra-0.0.0.dev167 → ominfra-0.0.0.dev169}/ominfra/manage/deploy/specs.py +1 -2
  13. {ominfra-0.0.0.dev167 → ominfra-0.0.0.dev169}/ominfra/manage/deploy/tmp.py +1 -1
  14. ominfra-0.0.0.dev169/ominfra/manage/deploy/types.py +39 -0
  15. {ominfra-0.0.0.dev167 → ominfra-0.0.0.dev169}/ominfra/scripts/journald2aws.py +24 -0
  16. {ominfra-0.0.0.dev167 → ominfra-0.0.0.dev169}/ominfra/scripts/manage.py +923 -697
  17. {ominfra-0.0.0.dev167 → ominfra-0.0.0.dev169}/ominfra/scripts/supervisor.py +24 -0
  18. ominfra-0.0.0.dev169/ominfra/tools/__init__.py +0 -0
  19. {ominfra-0.0.0.dev167 → ominfra-0.0.0.dev169/ominfra.egg-info}/PKG-INFO +3 -3
  20. {ominfra-0.0.0.dev167 → ominfra-0.0.0.dev169}/ominfra.egg-info/SOURCES.txt +6 -1
  21. {ominfra-0.0.0.dev167 → ominfra-0.0.0.dev169}/ominfra.egg-info/requires.txt +2 -2
  22. {ominfra-0.0.0.dev167 → ominfra-0.0.0.dev169}/pyproject.toml +3 -3
  23. ominfra-0.0.0.dev167/ominfra/manage/deploy/apps.py +0 -125
  24. ominfra-0.0.0.dev167/ominfra/manage/deploy/paths.py +0 -243
  25. ominfra-0.0.0.dev167/ominfra/manage/deploy/types.py +0 -14
  26. {ominfra-0.0.0.dev167 → ominfra-0.0.0.dev169}/LICENSE +0 -0
  27. {ominfra-0.0.0.dev167 → ominfra-0.0.0.dev169}/MANIFEST.in +0 -0
  28. {ominfra-0.0.0.dev167 → ominfra-0.0.0.dev169}/README.rst +0 -0
  29. {ominfra-0.0.0.dev167 → ominfra-0.0.0.dev169}/ominfra/.manifests.json +0 -0
  30. {ominfra-0.0.0.dev167 → ominfra-0.0.0.dev169}/ominfra/__about__.py +0 -0
  31. {ominfra-0.0.0.dev167 → ominfra-0.0.0.dev169}/ominfra/__init__.py +0 -0
  32. {ominfra-0.0.0.dev167 → ominfra-0.0.0.dev169}/ominfra/clouds/__init__.py +0 -0
  33. {ominfra-0.0.0.dev167 → ominfra-0.0.0.dev169}/ominfra/clouds/aws/__init__.py +0 -0
  34. {ominfra-0.0.0.dev167 → ominfra-0.0.0.dev169}/ominfra/clouds/aws/__main__.py +0 -0
  35. {ominfra-0.0.0.dev167 → ominfra-0.0.0.dev169}/ominfra/clouds/aws/auth.py +0 -0
  36. {ominfra-0.0.0.dev167 → ominfra-0.0.0.dev169}/ominfra/clouds/aws/cli.py +0 -0
  37. {ominfra-0.0.0.dev167 → ominfra-0.0.0.dev169}/ominfra/clouds/aws/dataclasses.py +0 -0
  38. {ominfra-0.0.0.dev167 → ominfra-0.0.0.dev169}/ominfra/clouds/aws/journald2aws/__init__.py +0 -0
  39. {ominfra-0.0.0.dev167 → ominfra-0.0.0.dev169}/ominfra/clouds/aws/journald2aws/__main__.py +0 -0
  40. {ominfra-0.0.0.dev167 → ominfra-0.0.0.dev169}/ominfra/clouds/aws/journald2aws/cursor.py +0 -0
  41. {ominfra-0.0.0.dev167 → ominfra-0.0.0.dev169}/ominfra/clouds/aws/journald2aws/driver.py +0 -0
  42. {ominfra-0.0.0.dev167 → ominfra-0.0.0.dev169}/ominfra/clouds/aws/journald2aws/main.py +0 -0
  43. {ominfra-0.0.0.dev167 → ominfra-0.0.0.dev169}/ominfra/clouds/aws/journald2aws/poster.py +0 -0
  44. {ominfra-0.0.0.dev167 → ominfra-0.0.0.dev169}/ominfra/clouds/aws/logs.py +0 -0
  45. {ominfra-0.0.0.dev167 → ominfra-0.0.0.dev169}/ominfra/clouds/aws/metadata.py +0 -0
  46. {ominfra-0.0.0.dev167 → ominfra-0.0.0.dev169}/ominfra/clouds/gcp/__init__.py +0 -0
  47. {ominfra-0.0.0.dev167 → ominfra-0.0.0.dev169}/ominfra/clouds/gcp/auth.py +0 -0
  48. {ominfra-0.0.0.dev167 → ominfra-0.0.0.dev169}/ominfra/cmds.py +0 -0
  49. {ominfra-0.0.0.dev167 → ominfra-0.0.0.dev169}/ominfra/configs.py +0 -0
  50. {ominfra-0.0.0.dev167 → ominfra-0.0.0.dev169}/ominfra/journald/__init__.py +0 -0
  51. {ominfra-0.0.0.dev167 → ominfra-0.0.0.dev169}/ominfra/journald/fields.py +0 -0
  52. {ominfra-0.0.0.dev167 → ominfra-0.0.0.dev169}/ominfra/journald/genmessages.py +0 -0
  53. {ominfra-0.0.0.dev167 → ominfra-0.0.0.dev169}/ominfra/journald/messages.py +0 -0
  54. {ominfra-0.0.0.dev167 → ominfra-0.0.0.dev169}/ominfra/journald/tailer.py +0 -0
  55. {ominfra-0.0.0.dev167 → ominfra-0.0.0.dev169}/ominfra/manage/__init__.py +0 -0
  56. {ominfra-0.0.0.dev167 → ominfra-0.0.0.dev169}/ominfra/manage/__main__.py +0 -0
  57. {ominfra-0.0.0.dev167 → ominfra-0.0.0.dev169}/ominfra/manage/bootstrap.py +0 -0
  58. {ominfra-0.0.0.dev167 → ominfra-0.0.0.dev169}/ominfra/manage/bootstrap_.py +0 -0
  59. {ominfra-0.0.0.dev167 → ominfra-0.0.0.dev169}/ominfra/manage/commands/__init__.py +0 -0
  60. {ominfra-0.0.0.dev167 → ominfra-0.0.0.dev169}/ominfra/manage/commands/base.py +0 -0
  61. {ominfra-0.0.0.dev167 → ominfra-0.0.0.dev169}/ominfra/manage/commands/inject.py +0 -0
  62. {ominfra-0.0.0.dev167 → ominfra-0.0.0.dev169}/ominfra/manage/commands/local.py +0 -0
  63. {ominfra-0.0.0.dev167 → ominfra-0.0.0.dev169}/ominfra/manage/commands/marshal.py +0 -0
  64. {ominfra-0.0.0.dev167 → ominfra-0.0.0.dev169}/ominfra/manage/commands/ping.py +0 -0
  65. {ominfra-0.0.0.dev167 → ominfra-0.0.0.dev169}/ominfra/manage/commands/subprocess.py +0 -0
  66. {ominfra-0.0.0.dev167 → ominfra-0.0.0.dev169}/ominfra/manage/commands/types.py +0 -0
  67. {ominfra-0.0.0.dev167 → ominfra-0.0.0.dev169}/ominfra/manage/config.py +0 -0
  68. {ominfra-0.0.0.dev167 → ominfra-0.0.0.dev169}/ominfra/manage/deploy/__init__.py +0 -0
  69. {ominfra-0.0.0.dev167 → ominfra-0.0.0.dev169}/ominfra/manage/deploy/config.py +0 -0
  70. {ominfra-0.0.0.dev167 → ominfra-0.0.0.dev169}/ominfra/manage/deploy/interp.py +0 -0
  71. {ominfra-0.0.0.dev167/ominfra/manage/remote → ominfra-0.0.0.dev169/ominfra/manage/deploy/paths}/__init__.py +0 -0
  72. {ominfra-0.0.0.dev167 → ominfra-0.0.0.dev169}/ominfra/manage/deploy/venvs.py +0 -0
  73. {ominfra-0.0.0.dev167 → ominfra-0.0.0.dev169}/ominfra/manage/inject.py +0 -0
  74. {ominfra-0.0.0.dev167 → ominfra-0.0.0.dev169}/ominfra/manage/main.py +0 -0
  75. {ominfra-0.0.0.dev167 → ominfra-0.0.0.dev169}/ominfra/manage/marshal.py +0 -0
  76. {ominfra-0.0.0.dev167/ominfra/manage/system → ominfra-0.0.0.dev169/ominfra/manage/remote}/__init__.py +0 -0
  77. {ominfra-0.0.0.dev167 → ominfra-0.0.0.dev169}/ominfra/manage/remote/_main.py +0 -0
  78. {ominfra-0.0.0.dev167 → ominfra-0.0.0.dev169}/ominfra/manage/remote/channel.py +0 -0
  79. {ominfra-0.0.0.dev167 → ominfra-0.0.0.dev169}/ominfra/manage/remote/config.py +0 -0
  80. {ominfra-0.0.0.dev167 → ominfra-0.0.0.dev169}/ominfra/manage/remote/connection.py +0 -0
  81. {ominfra-0.0.0.dev167 → ominfra-0.0.0.dev169}/ominfra/manage/remote/execution.py +0 -0
  82. {ominfra-0.0.0.dev167 → ominfra-0.0.0.dev169}/ominfra/manage/remote/inject.py +0 -0
  83. {ominfra-0.0.0.dev167 → ominfra-0.0.0.dev169}/ominfra/manage/remote/payload.py +0 -0
  84. {ominfra-0.0.0.dev167 → ominfra-0.0.0.dev169}/ominfra/manage/remote/spawning.py +0 -0
  85. {ominfra-0.0.0.dev167/ominfra/manage/targets → ominfra-0.0.0.dev169/ominfra/manage/system}/__init__.py +0 -0
  86. {ominfra-0.0.0.dev167 → ominfra-0.0.0.dev169}/ominfra/manage/system/commands.py +0 -0
  87. {ominfra-0.0.0.dev167 → ominfra-0.0.0.dev169}/ominfra/manage/system/config.py +0 -0
  88. {ominfra-0.0.0.dev167 → ominfra-0.0.0.dev169}/ominfra/manage/system/inject.py +0 -0
  89. {ominfra-0.0.0.dev167 → ominfra-0.0.0.dev169}/ominfra/manage/system/packages.py +0 -0
  90. {ominfra-0.0.0.dev167 → ominfra-0.0.0.dev169}/ominfra/manage/system/platforms.py +0 -0
  91. {ominfra-0.0.0.dev167/ominfra/scripts → ominfra-0.0.0.dev169/ominfra/manage/targets}/__init__.py +0 -0
  92. {ominfra-0.0.0.dev167 → ominfra-0.0.0.dev169}/ominfra/manage/targets/connection.py +0 -0
  93. {ominfra-0.0.0.dev167 → ominfra-0.0.0.dev169}/ominfra/manage/targets/inject.py +0 -0
  94. {ominfra-0.0.0.dev167 → ominfra-0.0.0.dev169}/ominfra/manage/targets/targets.py +0 -0
  95. {ominfra-0.0.0.dev167 → ominfra-0.0.0.dev169}/ominfra/pyremote.py +0 -0
  96. {ominfra-0.0.0.dev167/ominfra/supervisor/utils → ominfra-0.0.0.dev169/ominfra/scripts}/__init__.py +0 -0
  97. {ominfra-0.0.0.dev167 → ominfra-0.0.0.dev169}/ominfra/ssh.py +0 -0
  98. {ominfra-0.0.0.dev167 → ominfra-0.0.0.dev169}/ominfra/supervisor/LICENSE.txt +0 -0
  99. {ominfra-0.0.0.dev167 → ominfra-0.0.0.dev169}/ominfra/supervisor/__init__.py +0 -0
  100. {ominfra-0.0.0.dev167 → ominfra-0.0.0.dev169}/ominfra/supervisor/__main__.py +0 -0
  101. {ominfra-0.0.0.dev167 → ominfra-0.0.0.dev169}/ominfra/supervisor/configs.py +0 -0
  102. {ominfra-0.0.0.dev167 → ominfra-0.0.0.dev169}/ominfra/supervisor/dispatchers.py +0 -0
  103. {ominfra-0.0.0.dev167 → ominfra-0.0.0.dev169}/ominfra/supervisor/dispatchersimpl.py +0 -0
  104. {ominfra-0.0.0.dev167 → ominfra-0.0.0.dev169}/ominfra/supervisor/events.py +0 -0
  105. {ominfra-0.0.0.dev167 → ominfra-0.0.0.dev169}/ominfra/supervisor/exceptions.py +0 -0
  106. {ominfra-0.0.0.dev167 → ominfra-0.0.0.dev169}/ominfra/supervisor/groups.py +0 -0
  107. {ominfra-0.0.0.dev167 → ominfra-0.0.0.dev169}/ominfra/supervisor/groupsimpl.py +0 -0
  108. {ominfra-0.0.0.dev167 → ominfra-0.0.0.dev169}/ominfra/supervisor/http.py +0 -0
  109. {ominfra-0.0.0.dev167 → ominfra-0.0.0.dev169}/ominfra/supervisor/inject.py +0 -0
  110. {ominfra-0.0.0.dev167 → ominfra-0.0.0.dev169}/ominfra/supervisor/io.py +0 -0
  111. {ominfra-0.0.0.dev167 → ominfra-0.0.0.dev169}/ominfra/supervisor/main.py +0 -0
  112. {ominfra-0.0.0.dev167 → ominfra-0.0.0.dev169}/ominfra/supervisor/pipes.py +0 -0
  113. {ominfra-0.0.0.dev167 → ominfra-0.0.0.dev169}/ominfra/supervisor/privileges.py +0 -0
  114. {ominfra-0.0.0.dev167 → ominfra-0.0.0.dev169}/ominfra/supervisor/process.py +0 -0
  115. {ominfra-0.0.0.dev167 → ominfra-0.0.0.dev169}/ominfra/supervisor/processimpl.py +0 -0
  116. {ominfra-0.0.0.dev167 → ominfra-0.0.0.dev169}/ominfra/supervisor/setup.py +0 -0
  117. {ominfra-0.0.0.dev167 → ominfra-0.0.0.dev169}/ominfra/supervisor/setupimpl.py +0 -0
  118. {ominfra-0.0.0.dev167 → ominfra-0.0.0.dev169}/ominfra/supervisor/signals.py +0 -0
  119. {ominfra-0.0.0.dev167 → ominfra-0.0.0.dev169}/ominfra/supervisor/spawning.py +0 -0
  120. {ominfra-0.0.0.dev167 → ominfra-0.0.0.dev169}/ominfra/supervisor/spawningimpl.py +0 -0
  121. {ominfra-0.0.0.dev167 → ominfra-0.0.0.dev169}/ominfra/supervisor/states.py +0 -0
  122. {ominfra-0.0.0.dev167 → ominfra-0.0.0.dev169}/ominfra/supervisor/supervisor.py +0 -0
  123. {ominfra-0.0.0.dev167 → ominfra-0.0.0.dev169}/ominfra/supervisor/types.py +0 -0
  124. {ominfra-0.0.0.dev167/ominfra/tailscale → ominfra-0.0.0.dev169/ominfra/supervisor/utils}/__init__.py +0 -0
  125. {ominfra-0.0.0.dev167 → ominfra-0.0.0.dev169}/ominfra/supervisor/utils/collections.py +0 -0
  126. {ominfra-0.0.0.dev167 → ominfra-0.0.0.dev169}/ominfra/supervisor/utils/diag.py +0 -0
  127. {ominfra-0.0.0.dev167 → ominfra-0.0.0.dev169}/ominfra/supervisor/utils/fds.py +0 -0
  128. {ominfra-0.0.0.dev167 → ominfra-0.0.0.dev169}/ominfra/supervisor/utils/fs.py +0 -0
  129. {ominfra-0.0.0.dev167 → ominfra-0.0.0.dev169}/ominfra/supervisor/utils/os.py +0 -0
  130. {ominfra-0.0.0.dev167 → ominfra-0.0.0.dev169}/ominfra/supervisor/utils/ostypes.py +0 -0
  131. {ominfra-0.0.0.dev167 → ominfra-0.0.0.dev169}/ominfra/supervisor/utils/signals.py +0 -0
  132. {ominfra-0.0.0.dev167 → ominfra-0.0.0.dev169}/ominfra/supervisor/utils/strings.py +0 -0
  133. {ominfra-0.0.0.dev167 → ominfra-0.0.0.dev169}/ominfra/supervisor/utils/users.py +0 -0
  134. {ominfra-0.0.0.dev167/ominfra/tools → ominfra-0.0.0.dev169/ominfra/tailscale}/__init__.py +0 -0
  135. {ominfra-0.0.0.dev167 → ominfra-0.0.0.dev169}/ominfra/tailscale/api.py +0 -0
  136. {ominfra-0.0.0.dev167 → ominfra-0.0.0.dev169}/ominfra/tailscale/cli.py +0 -0
  137. {ominfra-0.0.0.dev167 → ominfra-0.0.0.dev169}/ominfra/threadworkers.py +0 -0
  138. {ominfra-0.0.0.dev167 → ominfra-0.0.0.dev169}/ominfra/tools/listresources.py +0 -0
  139. {ominfra-0.0.0.dev167 → ominfra-0.0.0.dev169}/ominfra.egg-info/dependency_links.txt +0 -0
  140. {ominfra-0.0.0.dev167 → ominfra-0.0.0.dev169}/ominfra.egg-info/entry_points.txt +0 -0
  141. {ominfra-0.0.0.dev167 → ominfra-0.0.0.dev169}/ominfra.egg-info/top_level.txt +0 -0
  142. {ominfra-0.0.0.dev167 → ominfra-0.0.0.dev169}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ominfra
3
- Version: 0.0.0.dev167
3
+ Version: 0.0.0.dev169
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.dev167
16
- Requires-Dist: omlish==0.0.0.dev167
15
+ Requires-Dist: omdev==0.0.0.dev169
16
+ Requires-Dist: omlish==0.0.0.dev169
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,175 @@
1
+ # ruff: noqa: UP006 UP007
2
+ import datetime
3
+ import os.path
4
+ import typing as ta
5
+
6
+ from omlish.lite.cached import cached_nullary
7
+ from omlish.lite.check import check
8
+ from omlish.os.paths import relative_symlink
9
+
10
+ from .conf import DeployConfManager
11
+ from .git import DeployGitManager
12
+ from .paths.owners import DeployPathOwner
13
+ from .paths.paths import DeployPath
14
+ from .specs import DeploySpec
15
+ from .types import DeployAppTag
16
+ from .types import DeployHome
17
+ from .types import DeployKey
18
+ from .types import DeployRev
19
+ from .types import DeployTag
20
+ from .venvs import DeployVenvManager
21
+
22
+
23
+ def make_deploy_tag(
24
+ rev: DeployRev,
25
+ key: DeployKey,
26
+ *,
27
+ utcnow: ta.Optional[datetime.datetime] = None,
28
+ ) -> DeployTag:
29
+ if utcnow is None:
30
+ utcnow = datetime.datetime.now(tz=datetime.timezone.utc) # noqa
31
+ now_fmt = '%Y%m%dT%H%M%SZ'
32
+ now_str = utcnow.strftime(now_fmt)
33
+ return DeployTag('-'.join([now_str, rev, key]))
34
+
35
+
36
+ class DeployAppManager(DeployPathOwner):
37
+ def __init__(
38
+ self,
39
+ *,
40
+ deploy_home: ta.Optional[DeployHome] = None,
41
+
42
+ conf: DeployConfManager,
43
+ git: DeployGitManager,
44
+ venvs: DeployVenvManager,
45
+ ) -> None:
46
+ super().__init__()
47
+
48
+ self._deploy_home = deploy_home
49
+
50
+ self._conf = conf
51
+ self._git = git
52
+ self._venvs = venvs
53
+
54
+ #
55
+
56
+ _APP_TAG_DIR_STR = 'tags/apps/@app/@tag/'
57
+ _APP_TAG_DIR = DeployPath.parse(_APP_TAG_DIR_STR)
58
+
59
+ _CONF_TAG_DIR_STR = 'tags/conf/@tag--@app/'
60
+ _CONF_TAG_DIR = DeployPath.parse(_CONF_TAG_DIR_STR)
61
+
62
+ _DEPLOY_DIR_STR = 'deploys/@tag--@app/'
63
+ _DEPLOY_DIR = DeployPath.parse(_DEPLOY_DIR_STR)
64
+
65
+ _APP_DEPLOY_LINK = DeployPath.parse(f'{_DEPLOY_DIR_STR}apps/@app')
66
+ _CONF_DEPLOY_LINK = DeployPath.parse(f'{_DEPLOY_DIR_STR}conf')
67
+
68
+ @cached_nullary
69
+ def get_owned_deploy_paths(self) -> ta.AbstractSet[DeployPath]:
70
+ return {
71
+ self._APP_TAG_DIR,
72
+
73
+ self._CONF_TAG_DIR,
74
+
75
+ self._DEPLOY_DIR,
76
+
77
+ self._APP_DEPLOY_LINK,
78
+ self._CONF_DEPLOY_LINK,
79
+
80
+ *[
81
+ DeployPath.parse(f'{self._APP_TAG_DIR_STR}{sfx}/')
82
+ for sfx in [
83
+ 'conf',
84
+ 'git',
85
+ 'venv',
86
+ ]
87
+ ],
88
+ }
89
+
90
+ #
91
+
92
+ async def prepare_app(
93
+ self,
94
+ spec: DeploySpec,
95
+ ) -> None:
96
+ app_tag = DeployAppTag(spec.app, make_deploy_tag(spec.git.rev, spec.key()))
97
+
98
+ #
99
+
100
+ deploy_home = check.non_empty_str(self._deploy_home)
101
+
102
+ def build_path(pth: DeployPath) -> str:
103
+ return os.path.join(deploy_home, pth.render(app_tag.placeholders()))
104
+
105
+ app_tag_dir = build_path(self._APP_TAG_DIR)
106
+ conf_tag_dir = build_path(self._CONF_TAG_DIR)
107
+ deploy_dir = build_path(self._DEPLOY_DIR)
108
+ app_deploy_link = build_path(self._APP_DEPLOY_LINK)
109
+ conf_deploy_link_file = build_path(self._CONF_DEPLOY_LINK)
110
+
111
+ #
112
+
113
+ os.makedirs(deploy_dir)
114
+
115
+ deploying_link = os.path.join(deploy_home, 'deploys/deploying')
116
+ relative_symlink(
117
+ deploy_dir,
118
+ deploying_link,
119
+ target_is_directory=True,
120
+ make_dirs=True,
121
+ )
122
+
123
+ #
124
+
125
+ os.makedirs(app_tag_dir)
126
+ relative_symlink(
127
+ app_tag_dir,
128
+ app_deploy_link,
129
+ target_is_directory=True,
130
+ make_dirs=True,
131
+ )
132
+
133
+ #
134
+
135
+ os.makedirs(conf_tag_dir)
136
+ relative_symlink(
137
+ conf_tag_dir,
138
+ conf_deploy_link_file,
139
+ target_is_directory=True,
140
+ make_dirs=True,
141
+ )
142
+
143
+ #
144
+
145
+ git_dir = os.path.join(app_tag_dir, 'git')
146
+ await self._git.checkout(
147
+ spec.git,
148
+ git_dir,
149
+ )
150
+
151
+ #
152
+
153
+ if spec.venv is not None:
154
+ venv_dir = os.path.join(app_tag_dir, 'venv')
155
+ await self._venvs.setup_venv(
156
+ spec.venv,
157
+ git_dir,
158
+ venv_dir,
159
+ )
160
+
161
+ #
162
+
163
+ if spec.conf is not None:
164
+ conf_dir = os.path.join(app_tag_dir, 'conf')
165
+ await self._conf.write_conf(
166
+ spec.conf,
167
+ app_tag,
168
+ conf_dir,
169
+ conf_tag_dir,
170
+ )
171
+
172
+ #
173
+
174
+ current_link = os.path.join(deploy_home, 'deploys/current')
175
+ os.replace(deploying_link, current_link)
@@ -5,7 +5,7 @@ from omlish.lite.logs import log
5
5
 
6
6
  from ..commands.base import Command
7
7
  from ..commands.base import CommandExecutor
8
- from .apps import DeployAppManager
8
+ from .deploy import DeployManager
9
9
  from .specs import DeploySpec
10
10
 
11
11
 
@@ -23,11 +23,11 @@ class DeployCommand(Command['DeployCommand.Output']):
23
23
 
24
24
  @dc.dataclass(frozen=True)
25
25
  class DeployCommandExecutor(CommandExecutor[DeployCommand, DeployCommand.Output]):
26
- _apps: DeployAppManager
26
+ _deploy: DeployManager
27
27
 
28
28
  async def execute(self, cmd: DeployCommand) -> DeployCommand.Output:
29
29
  log.info('Deploying! %r', cmd.spec)
30
30
 
31
- await self._apps.prepare_app(cmd.spec)
31
+ await self._deploy.run_deploy(cmd.spec)
32
32
 
33
33
  return DeployCommand.Output()
@@ -23,7 +23,7 @@ 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 import SingleDirDeployPathOwner
26
+ from .paths.paths import DEPLOY_PATH_PLACEHOLDER_SEPARATOR
27
27
  from .specs import AppDeployConfLink
28
28
  from .specs import DeployConfFile
29
29
  from .specs import DeployConfLink
@@ -33,16 +33,17 @@ from .types import DeployAppTag
33
33
  from .types import DeployHome
34
34
 
35
35
 
36
- class DeployConfManager(SingleDirDeployPathOwner):
36
+ class DeployConfManager:
37
37
  def __init__(
38
38
  self,
39
39
  *,
40
40
  deploy_home: ta.Optional[DeployHome] = None,
41
41
  ) -> None:
42
- super().__init__(
43
- owned_dir='conf',
44
- deploy_home=deploy_home,
45
- )
42
+ super().__init__()
43
+
44
+ self._deploy_home = deploy_home
45
+
46
+ #
46
47
 
47
48
  async def _write_conf_file(
48
49
  self,
@@ -57,32 +58,40 @@ class DeployConfManager(SingleDirDeployPathOwner):
57
58
  with open(conf_file, 'w') as f: # noqa
58
59
  f.write(cf.body)
59
60
 
60
- async def _make_conf_link(
61
+ #
62
+
63
+ class _ComputedConfLink(ta.NamedTuple):
64
+ is_dir: bool
65
+ link_src: str
66
+ link_dst: str
67
+
68
+ def _compute_conf_link_dst(
61
69
  self,
62
70
  link: DeployConfLink,
63
- conf_dir: str,
64
71
  app_tag: DeployAppTag,
72
+ conf_dir: str,
65
73
  link_dir: str,
66
- ) -> None:
74
+ ) -> _ComputedConfLink:
67
75
  link_src = os.path.join(conf_dir, link.src)
68
76
  check.arg(is_path_in_dir(conf_dir, link_src))
69
77
 
70
- is_link_dir = link.src.endswith('/')
71
- if is_link_dir:
78
+ #
79
+
80
+ if (is_dir := link.src.endswith('/')):
81
+ # @conf/ - links a directory in root of app conf dir to conf/@conf/@dst/
72
82
  check.arg(link.src.count('/') == 1)
73
- check.arg(os.path.isdir(link_src))
74
83
  link_dst_pfx = link.src
75
84
  link_dst_sfx = ''
76
85
 
77
86
  elif '/' in link.src:
78
- check.arg(os.path.isfile(link_src))
87
+ # @conf/file - links a single file in a single subdir to conf/@conf/@dst--file
79
88
  d, f = os.path.split(link.src)
80
89
  # TODO: check filename :|
81
90
  link_dst_pfx = d + '/'
82
- link_dst_sfx = '-' + f
91
+ link_dst_sfx = DEPLOY_PATH_PLACEHOLDER_SEPARATOR + f
83
92
 
84
- else:
85
- check.arg(os.path.isfile(link_src))
93
+ else: # noqa
94
+ # @conf(.ext)* - links a single file in root of app conf dir to conf/@conf/@dst(.ext)*
86
95
  if '.' in link.src:
87
96
  l, _, r = link.src.partition('.')
88
97
  link_dst_pfx = l + '/'
@@ -91,41 +100,72 @@ class DeployConfManager(SingleDirDeployPathOwner):
91
100
  link_dst_pfx = link.src + '/'
92
101
  link_dst_sfx = ''
93
102
 
103
+ #
104
+
94
105
  if isinstance(link, AppDeployConfLink):
95
106
  link_dst_mid = str(app_tag.app)
96
- sym_root = link_dir
97
107
  elif isinstance(link, TagDeployConfLink):
98
- link_dst_mid = '-'.join([app_tag.app, app_tag.tag])
99
- sym_root = conf_dir
108
+ link_dst_mid = DEPLOY_PATH_PLACEHOLDER_SEPARATOR.join([app_tag.app, app_tag.tag])
100
109
  else:
101
110
  raise TypeError(link)
102
111
 
103
- link_dst = ''.join([
112
+ #
113
+
114
+ link_dst_name = ''.join([
104
115
  link_dst_pfx,
105
116
  link_dst_mid,
106
117
  link_dst_sfx,
107
118
  ])
119
+ link_dst = os.path.join(link_dir, link_dst_name)
108
120
 
109
- root_conf_dir = self._make_dir()
110
- sym_src = os.path.join(sym_root, link.src)
111
- sym_dst = os.path.join(root_conf_dir, link_dst)
112
- check.arg(is_path_in_dir(root_conf_dir, sym_dst))
113
-
114
- os.makedirs(os.path.dirname(sym_dst), exist_ok=True)
115
- relative_symlink(sym_src, sym_dst, target_is_directory=is_link_dir)
121
+ return DeployConfManager._ComputedConfLink(
122
+ is_dir=is_dir,
123
+ link_src=link_src,
124
+ link_dst=link_dst,
125
+ )
116
126
 
117
- async def write_conf(
127
+ async def _make_conf_link(
118
128
  self,
119
- spec: DeployConfSpec,
120
- conf_dir: str,
129
+ link: DeployConfLink,
121
130
  app_tag: DeployAppTag,
131
+ conf_dir: str,
122
132
  link_dir: str,
123
133
  ) -> None:
124
- conf_dir = os.path.abspath(conf_dir)
125
- os.makedirs(conf_dir)
134
+ comp = self._compute_conf_link_dst(
135
+ link,
136
+ app_tag,
137
+ conf_dir,
138
+ link_dir,
139
+ )
126
140
 
127
141
  #
128
142
 
143
+ check.arg(is_path_in_dir(conf_dir, comp.link_src))
144
+ check.arg(is_path_in_dir(link_dir, comp.link_dst))
145
+
146
+ if comp.is_dir:
147
+ check.arg(os.path.isdir(comp.link_src))
148
+ else:
149
+ check.arg(os.path.isfile(comp.link_src))
150
+
151
+ #
152
+
153
+ relative_symlink( # noqa
154
+ comp.link_src,
155
+ comp.link_dst,
156
+ target_is_directory=comp.is_dir,
157
+ make_dirs=True,
158
+ )
159
+
160
+ #
161
+
162
+ async def write_conf(
163
+ self,
164
+ spec: DeployConfSpec,
165
+ app_tag: DeployAppTag,
166
+ conf_dir: str,
167
+ link_dir: str,
168
+ ) -> None:
129
169
  for cf in spec.files or []:
130
170
  await self._write_conf_file(
131
171
  cf,
@@ -137,7 +177,7 @@ class DeployConfManager(SingleDirDeployPathOwner):
137
177
  for link in spec.links or []:
138
178
  await self._make_conf_link(
139
179
  link,
140
- conf_dir,
141
180
  app_tag,
181
+ conf_dir,
142
182
  link_dir,
143
183
  )
@@ -0,0 +1,27 @@
1
+ # ruff: noqa: UP006 UP007
2
+ from .apps import DeployAppManager
3
+ from .paths.manager import DeployPathsManager
4
+ from .specs import DeploySpec
5
+
6
+
7
+ class DeployManager:
8
+ def __init__(
9
+ self,
10
+ *,
11
+ apps: DeployAppManager,
12
+ paths: DeployPathsManager,
13
+ ):
14
+ super().__init__()
15
+
16
+ self._apps = apps
17
+ self._paths = paths
18
+
19
+ async def run_deploy(
20
+ self,
21
+ spec: DeploySpec,
22
+ ) -> None:
23
+ self._paths.validate_deploy_paths()
24
+
25
+ #
26
+
27
+ await self._apps.prepare_app(spec)
@@ -17,7 +17,7 @@ from omlish.lite.cached import async_cached_nullary
17
17
  from omlish.lite.check import check
18
18
  from omlish.os.atomics import AtomicPathSwapping
19
19
 
20
- from .paths import SingleDirDeployPathOwner
20
+ from .paths.owners import SingleDirDeployPathOwner
21
21
  from .specs import DeployGitRepo
22
22
  from .specs import DeployGitSpec
23
23
  from .types import DeployHome
@@ -13,11 +13,12 @@ from .commands import DeployCommand
13
13
  from .commands import DeployCommandExecutor
14
14
  from .conf import DeployConfManager
15
15
  from .config import DeployConfig
16
+ from .deploy import DeployManager
16
17
  from .git import DeployGitManager
17
18
  from .interp import InterpCommand
18
19
  from .interp import InterpCommandExecutor
19
- from .paths import DeployPathOwner
20
- from .paths import DeployPathOwners
20
+ from .paths.inject import bind_deploy_paths
21
+ from .paths.owners import DeployPathOwner
21
22
  from .tmp import DeployTmpManager
22
23
  from .types import DeployHome
23
24
  from .venvs import DeployVenvManager
@@ -29,6 +30,8 @@ def bind_deploy(
29
30
  ) -> InjectorBindings:
30
31
  lst: ta.List[InjectorBindingOrBindings] = [
31
32
  inj.bind(deploy_config),
33
+
34
+ bind_deploy_paths(),
32
35
  ]
33
36
 
34
37
  #
@@ -40,11 +43,6 @@ def bind_deploy(
40
43
  *([inj.bind(DeployPathOwner, to_key=cls, array=True)] if issubclass(cls, DeployPathOwner) else []),
41
44
  )
42
45
 
43
- lst.extend([
44
- inj.bind_array(DeployPathOwner),
45
- inj.bind_array_type(DeployPathOwner, DeployPathOwners),
46
- ])
47
-
48
46
  #
49
47
 
50
48
  lst.extend([
@@ -54,6 +52,8 @@ def bind_deploy(
54
52
 
55
53
  bind_manager(DeployGitManager),
56
54
 
55
+ bind_manager(DeployManager),
56
+
57
57
  bind_manager(DeployTmpManager),
58
58
  inj.bind(AtomicPathSwapping, to_key=DeployTmpManager),
59
59
 
@@ -0,0 +1,21 @@
1
+ # ruff: noqa: UP006 UP007
2
+ import typing as ta
3
+
4
+ from omlish.lite.inject import InjectorBindingOrBindings
5
+ from omlish.lite.inject import InjectorBindings
6
+ from omlish.lite.inject import inj
7
+
8
+ from .manager import DeployPathsManager
9
+ from .owners import DeployPathOwner
10
+ from .owners import DeployPathOwners
11
+
12
+
13
+ def bind_deploy_paths() -> InjectorBindings:
14
+ lst: ta.List[InjectorBindingOrBindings] = [
15
+ inj.bind_array(DeployPathOwner),
16
+ inj.bind_array_type(DeployPathOwner, DeployPathOwners),
17
+
18
+ inj.bind(DeployPathsManager, singleton=True),
19
+ ]
20
+
21
+ return inj.as_bindings(*lst)
@@ -0,0 +1,36 @@
1
+ # ruff: noqa: UP006 UP007
2
+ import typing as ta
3
+
4
+ from omlish.lite.cached import cached_nullary
5
+
6
+ from ..types import DeployHome
7
+ from .owners import DeployPathOwner
8
+ from .owners import DeployPathOwners
9
+ from .paths import DeployPath
10
+ from .paths import DeployPathError
11
+
12
+
13
+ class DeployPathsManager:
14
+ def __init__(
15
+ self,
16
+ *,
17
+ deploy_home: ta.Optional[DeployHome],
18
+ deploy_path_owners: DeployPathOwners,
19
+ ) -> None:
20
+ super().__init__()
21
+
22
+ self._deploy_home = deploy_home
23
+ self._deploy_path_owners = deploy_path_owners
24
+
25
+ @cached_nullary
26
+ def owners_by_path(self) -> ta.Mapping[DeployPath, DeployPathOwner]:
27
+ dct: ta.Dict[DeployPath, DeployPathOwner] = {}
28
+ for o in self._deploy_path_owners:
29
+ for p in o.get_owned_deploy_paths():
30
+ if p in dct:
31
+ raise DeployPathError(f'Duplicate deploy path owner: {p}')
32
+ dct[p] = o
33
+ return dct
34
+
35
+ def validate_deploy_paths(self) -> None:
36
+ self.owners_by_path()
@@ -0,0 +1,50 @@
1
+ # ruff: noqa: UP006 UP007
2
+ import abc
3
+ import os.path
4
+ import typing as ta
5
+
6
+ from omlish.lite.cached import cached_nullary
7
+ from omlish.lite.check import check
8
+
9
+ from ..types import DeployHome
10
+ from .paths import DeployPath
11
+
12
+
13
+ class DeployPathOwner(abc.ABC):
14
+ @abc.abstractmethod
15
+ def get_owned_deploy_paths(self) -> ta.AbstractSet[DeployPath]:
16
+ raise NotImplementedError
17
+
18
+
19
+ DeployPathOwners = ta.NewType('DeployPathOwners', ta.Sequence[DeployPathOwner])
20
+
21
+
22
+ class SingleDirDeployPathOwner(DeployPathOwner, abc.ABC):
23
+ def __init__(
24
+ self,
25
+ *args: ta.Any,
26
+ owned_dir: str,
27
+ deploy_home: ta.Optional[DeployHome],
28
+ **kwargs: ta.Any,
29
+ ) -> None:
30
+ super().__init__(*args, **kwargs)
31
+
32
+ check.not_in('/', owned_dir)
33
+ self._owned_dir: str = check.non_empty_str(owned_dir)
34
+
35
+ self._deploy_home = deploy_home
36
+
37
+ self._owned_deploy_paths = frozenset([DeployPath.parse(self._owned_dir + '/')])
38
+
39
+ @cached_nullary
40
+ def _dir(self) -> str:
41
+ return os.path.join(check.non_empty_str(self._deploy_home), self._owned_dir)
42
+
43
+ @cached_nullary
44
+ def _make_dir(self) -> str:
45
+ if not os.path.isdir(d := self._dir()):
46
+ os.makedirs(d, exist_ok=True)
47
+ return d
48
+
49
+ def get_owned_deploy_paths(self) -> ta.AbstractSet[DeployPath]:
50
+ return self._owned_deploy_paths