ominfra 0.0.0.dev189__tar.gz → 0.0.0.dev190__tar.gz

Sign up to get free protection for your applications and to get access to all the features.
Files changed (153) hide show
  1. {ominfra-0.0.0.dev189/ominfra.egg-info → ominfra-0.0.0.dev190}/PKG-INFO +4 -3
  2. {ominfra-0.0.0.dev189 → ominfra-0.0.0.dev190}/ominfra/__about__.py +1 -0
  3. ominfra-0.0.0.dev190/ominfra/manage/deploy/apps.py +129 -0
  4. {ominfra-0.0.0.dev189 → ominfra-0.0.0.dev190}/ominfra/manage/deploy/commands.py +1 -1
  5. {ominfra-0.0.0.dev189 → ominfra-0.0.0.dev190}/ominfra/manage/deploy/conf/manager.py +20 -12
  6. {ominfra-0.0.0.dev189 → ominfra-0.0.0.dev190}/ominfra/manage/deploy/conf/specs.py +9 -0
  7. ominfra-0.0.0.dev190/ominfra/manage/deploy/deploy.py +239 -0
  8. {ominfra-0.0.0.dev189 → ominfra-0.0.0.dev190}/ominfra/manage/deploy/inject.py +4 -6
  9. ominfra-0.0.0.dev190/ominfra/manage/deploy/nginx.py +8 -0
  10. {ominfra-0.0.0.dev189 → ominfra-0.0.0.dev190}/ominfra/manage/deploy/paths/paths.py +5 -1
  11. {ominfra-0.0.0.dev189 → ominfra-0.0.0.dev190}/ominfra/manage/deploy/specs.py +12 -1
  12. ominfra-0.0.0.dev190/ominfra/manage/deploy/systemd.py +110 -0
  13. {ominfra-0.0.0.dev189 → ominfra-0.0.0.dev190}/ominfra/manage/deploy/venvs.py +0 -2
  14. {ominfra-0.0.0.dev189 → ominfra-0.0.0.dev190}/ominfra/scripts/manage.py +489 -152
  15. {ominfra-0.0.0.dev189 → ominfra-0.0.0.dev190/ominfra.egg-info}/PKG-INFO +4 -3
  16. {ominfra-0.0.0.dev189 → ominfra-0.0.0.dev190}/ominfra.egg-info/SOURCES.txt +2 -1
  17. {ominfra-0.0.0.dev189 → ominfra-0.0.0.dev190}/ominfra.egg-info/requires.txt +3 -2
  18. {ominfra-0.0.0.dev189 → ominfra-0.0.0.dev190}/pyproject.toml +4 -3
  19. ominfra-0.0.0.dev189/ominfra/manage/deploy/apps.py +0 -177
  20. ominfra-0.0.0.dev189/ominfra/manage/deploy/deploy.py +0 -34
  21. ominfra-0.0.0.dev189/ominfra/manage/deploy/driver.py +0 -62
  22. {ominfra-0.0.0.dev189 → ominfra-0.0.0.dev190}/LICENSE +0 -0
  23. {ominfra-0.0.0.dev189 → ominfra-0.0.0.dev190}/MANIFEST.in +0 -0
  24. {ominfra-0.0.0.dev189 → ominfra-0.0.0.dev190}/README.rst +0 -0
  25. {ominfra-0.0.0.dev189 → ominfra-0.0.0.dev190}/ominfra/.manifests.json +0 -0
  26. {ominfra-0.0.0.dev189 → ominfra-0.0.0.dev190}/ominfra/__init__.py +0 -0
  27. {ominfra-0.0.0.dev189 → ominfra-0.0.0.dev190}/ominfra/clouds/__init__.py +0 -0
  28. {ominfra-0.0.0.dev189 → ominfra-0.0.0.dev190}/ominfra/clouds/aws/__init__.py +0 -0
  29. {ominfra-0.0.0.dev189 → ominfra-0.0.0.dev190}/ominfra/clouds/aws/__main__.py +0 -0
  30. {ominfra-0.0.0.dev189 → ominfra-0.0.0.dev190}/ominfra/clouds/aws/auth.py +0 -0
  31. {ominfra-0.0.0.dev189 → ominfra-0.0.0.dev190}/ominfra/clouds/aws/cli.py +0 -0
  32. {ominfra-0.0.0.dev189 → ominfra-0.0.0.dev190}/ominfra/clouds/aws/dataclasses.py +0 -0
  33. {ominfra-0.0.0.dev189 → ominfra-0.0.0.dev190}/ominfra/clouds/aws/journald2aws/__init__.py +0 -0
  34. {ominfra-0.0.0.dev189 → ominfra-0.0.0.dev190}/ominfra/clouds/aws/journald2aws/__main__.py +0 -0
  35. {ominfra-0.0.0.dev189 → ominfra-0.0.0.dev190}/ominfra/clouds/aws/journald2aws/cursor.py +0 -0
  36. {ominfra-0.0.0.dev189 → ominfra-0.0.0.dev190}/ominfra/clouds/aws/journald2aws/driver.py +0 -0
  37. {ominfra-0.0.0.dev189 → ominfra-0.0.0.dev190}/ominfra/clouds/aws/journald2aws/main.py +0 -0
  38. {ominfra-0.0.0.dev189 → ominfra-0.0.0.dev190}/ominfra/clouds/aws/journald2aws/poster.py +0 -0
  39. {ominfra-0.0.0.dev189 → ominfra-0.0.0.dev190}/ominfra/clouds/aws/logs.py +0 -0
  40. {ominfra-0.0.0.dev189 → ominfra-0.0.0.dev190}/ominfra/clouds/aws/metadata.py +0 -0
  41. {ominfra-0.0.0.dev189 → ominfra-0.0.0.dev190}/ominfra/clouds/gcp/__init__.py +0 -0
  42. {ominfra-0.0.0.dev189 → ominfra-0.0.0.dev190}/ominfra/clouds/gcp/auth.py +0 -0
  43. {ominfra-0.0.0.dev189 → ominfra-0.0.0.dev190}/ominfra/cmds.py +0 -0
  44. {ominfra-0.0.0.dev189 → ominfra-0.0.0.dev190}/ominfra/configs.py +0 -0
  45. {ominfra-0.0.0.dev189 → ominfra-0.0.0.dev190}/ominfra/journald/__init__.py +0 -0
  46. {ominfra-0.0.0.dev189 → ominfra-0.0.0.dev190}/ominfra/journald/fields.py +0 -0
  47. {ominfra-0.0.0.dev189 → ominfra-0.0.0.dev190}/ominfra/journald/genmessages.py +0 -0
  48. {ominfra-0.0.0.dev189 → ominfra-0.0.0.dev190}/ominfra/journald/messages.py +0 -0
  49. {ominfra-0.0.0.dev189 → ominfra-0.0.0.dev190}/ominfra/journald/tailer.py +0 -0
  50. {ominfra-0.0.0.dev189 → ominfra-0.0.0.dev190}/ominfra/manage/__init__.py +0 -0
  51. {ominfra-0.0.0.dev189 → ominfra-0.0.0.dev190}/ominfra/manage/__main__.py +0 -0
  52. {ominfra-0.0.0.dev189 → ominfra-0.0.0.dev190}/ominfra/manage/bootstrap.py +0 -0
  53. {ominfra-0.0.0.dev189 → ominfra-0.0.0.dev190}/ominfra/manage/bootstrap_.py +0 -0
  54. {ominfra-0.0.0.dev189 → ominfra-0.0.0.dev190}/ominfra/manage/commands/__init__.py +0 -0
  55. {ominfra-0.0.0.dev189 → ominfra-0.0.0.dev190}/ominfra/manage/commands/base.py +0 -0
  56. {ominfra-0.0.0.dev189 → ominfra-0.0.0.dev190}/ominfra/manage/commands/inject.py +0 -0
  57. {ominfra-0.0.0.dev189 → ominfra-0.0.0.dev190}/ominfra/manage/commands/local.py +0 -0
  58. {ominfra-0.0.0.dev189 → ominfra-0.0.0.dev190}/ominfra/manage/commands/marshal.py +0 -0
  59. {ominfra-0.0.0.dev189 → ominfra-0.0.0.dev190}/ominfra/manage/commands/ping.py +0 -0
  60. {ominfra-0.0.0.dev189 → ominfra-0.0.0.dev190}/ominfra/manage/commands/subprocess.py +0 -0
  61. {ominfra-0.0.0.dev189 → ominfra-0.0.0.dev190}/ominfra/manage/commands/types.py +0 -0
  62. {ominfra-0.0.0.dev189 → ominfra-0.0.0.dev190}/ominfra/manage/config.py +0 -0
  63. {ominfra-0.0.0.dev189 → ominfra-0.0.0.dev190}/ominfra/manage/deploy/__init__.py +0 -0
  64. {ominfra-0.0.0.dev189 → ominfra-0.0.0.dev190}/ominfra/manage/deploy/conf/__init__.py +0 -0
  65. {ominfra-0.0.0.dev189 → ominfra-0.0.0.dev190}/ominfra/manage/deploy/conf/inject.py +0 -0
  66. {ominfra-0.0.0.dev189 → ominfra-0.0.0.dev190}/ominfra/manage/deploy/config.py +0 -0
  67. {ominfra-0.0.0.dev189 → ominfra-0.0.0.dev190}/ominfra/manage/deploy/git.py +0 -0
  68. {ominfra-0.0.0.dev189 → ominfra-0.0.0.dev190}/ominfra/manage/deploy/inject_.py +0 -0
  69. {ominfra-0.0.0.dev189 → ominfra-0.0.0.dev190}/ominfra/manage/deploy/interp.py +0 -0
  70. {ominfra-0.0.0.dev189 → ominfra-0.0.0.dev190}/ominfra/manage/deploy/paths/__init__.py +0 -0
  71. {ominfra-0.0.0.dev189 → ominfra-0.0.0.dev190}/ominfra/manage/deploy/paths/inject.py +0 -0
  72. {ominfra-0.0.0.dev189 → ominfra-0.0.0.dev190}/ominfra/manage/deploy/paths/manager.py +0 -0
  73. {ominfra-0.0.0.dev189 → ominfra-0.0.0.dev190}/ominfra/manage/deploy/paths/owners.py +0 -0
  74. {ominfra-0.0.0.dev189 → ominfra-0.0.0.dev190}/ominfra/manage/deploy/paths/specs.py +0 -0
  75. {ominfra-0.0.0.dev189 → ominfra-0.0.0.dev190}/ominfra/manage/deploy/paths/types.py +0 -0
  76. {ominfra-0.0.0.dev189 → ominfra-0.0.0.dev190}/ominfra/manage/deploy/tags.py +0 -0
  77. {ominfra-0.0.0.dev189 → ominfra-0.0.0.dev190}/ominfra/manage/deploy/tmp.py +0 -0
  78. {ominfra-0.0.0.dev189 → ominfra-0.0.0.dev190}/ominfra/manage/deploy/types.py +0 -0
  79. {ominfra-0.0.0.dev189 → ominfra-0.0.0.dev190}/ominfra/manage/inject.py +0 -0
  80. {ominfra-0.0.0.dev189 → ominfra-0.0.0.dev190}/ominfra/manage/main.py +0 -0
  81. {ominfra-0.0.0.dev189 → ominfra-0.0.0.dev190}/ominfra/manage/marshal.py +0 -0
  82. {ominfra-0.0.0.dev189 → ominfra-0.0.0.dev190}/ominfra/manage/remote/__init__.py +0 -0
  83. {ominfra-0.0.0.dev189 → ominfra-0.0.0.dev190}/ominfra/manage/remote/_main.py +0 -0
  84. {ominfra-0.0.0.dev189 → ominfra-0.0.0.dev190}/ominfra/manage/remote/channel.py +0 -0
  85. {ominfra-0.0.0.dev189 → ominfra-0.0.0.dev190}/ominfra/manage/remote/config.py +0 -0
  86. {ominfra-0.0.0.dev189 → ominfra-0.0.0.dev190}/ominfra/manage/remote/connection.py +0 -0
  87. {ominfra-0.0.0.dev189 → ominfra-0.0.0.dev190}/ominfra/manage/remote/execution.py +0 -0
  88. {ominfra-0.0.0.dev189 → ominfra-0.0.0.dev190}/ominfra/manage/remote/inject.py +0 -0
  89. {ominfra-0.0.0.dev189 → ominfra-0.0.0.dev190}/ominfra/manage/remote/payload.py +0 -0
  90. {ominfra-0.0.0.dev189 → ominfra-0.0.0.dev190}/ominfra/manage/remote/spawning.py +0 -0
  91. {ominfra-0.0.0.dev189 → ominfra-0.0.0.dev190}/ominfra/manage/system/__init__.py +0 -0
  92. {ominfra-0.0.0.dev189 → ominfra-0.0.0.dev190}/ominfra/manage/system/commands.py +0 -0
  93. {ominfra-0.0.0.dev189 → ominfra-0.0.0.dev190}/ominfra/manage/system/config.py +0 -0
  94. {ominfra-0.0.0.dev189 → ominfra-0.0.0.dev190}/ominfra/manage/system/inject.py +0 -0
  95. {ominfra-0.0.0.dev189 → ominfra-0.0.0.dev190}/ominfra/manage/system/packages.py +0 -0
  96. {ominfra-0.0.0.dev189 → ominfra-0.0.0.dev190}/ominfra/manage/system/platforms.py +0 -0
  97. {ominfra-0.0.0.dev189 → ominfra-0.0.0.dev190}/ominfra/manage/targets/__init__.py +0 -0
  98. {ominfra-0.0.0.dev189 → ominfra-0.0.0.dev190}/ominfra/manage/targets/bestpython.py +0 -0
  99. {ominfra-0.0.0.dev189 → ominfra-0.0.0.dev190}/ominfra/manage/targets/bestpython.sh +0 -0
  100. {ominfra-0.0.0.dev189 → ominfra-0.0.0.dev190}/ominfra/manage/targets/connection.py +0 -0
  101. {ominfra-0.0.0.dev189 → ominfra-0.0.0.dev190}/ominfra/manage/targets/inject.py +0 -0
  102. {ominfra-0.0.0.dev189 → ominfra-0.0.0.dev190}/ominfra/manage/targets/targets.py +0 -0
  103. {ominfra-0.0.0.dev189 → ominfra-0.0.0.dev190}/ominfra/pyremote.py +0 -0
  104. {ominfra-0.0.0.dev189 → ominfra-0.0.0.dev190}/ominfra/scripts/__init__.py +0 -0
  105. {ominfra-0.0.0.dev189 → ominfra-0.0.0.dev190}/ominfra/scripts/journald2aws.py +0 -0
  106. {ominfra-0.0.0.dev189 → ominfra-0.0.0.dev190}/ominfra/scripts/supervisor.py +0 -0
  107. {ominfra-0.0.0.dev189 → ominfra-0.0.0.dev190}/ominfra/ssh.py +0 -0
  108. {ominfra-0.0.0.dev189 → ominfra-0.0.0.dev190}/ominfra/supervisor/LICENSE.txt +0 -0
  109. {ominfra-0.0.0.dev189 → ominfra-0.0.0.dev190}/ominfra/supervisor/__init__.py +0 -0
  110. {ominfra-0.0.0.dev189 → ominfra-0.0.0.dev190}/ominfra/supervisor/__main__.py +0 -0
  111. {ominfra-0.0.0.dev189 → ominfra-0.0.0.dev190}/ominfra/supervisor/configs.py +0 -0
  112. {ominfra-0.0.0.dev189 → ominfra-0.0.0.dev190}/ominfra/supervisor/dispatchers.py +0 -0
  113. {ominfra-0.0.0.dev189 → ominfra-0.0.0.dev190}/ominfra/supervisor/dispatchersimpl.py +0 -0
  114. {ominfra-0.0.0.dev189 → ominfra-0.0.0.dev190}/ominfra/supervisor/events.py +0 -0
  115. {ominfra-0.0.0.dev189 → ominfra-0.0.0.dev190}/ominfra/supervisor/exceptions.py +0 -0
  116. {ominfra-0.0.0.dev189 → ominfra-0.0.0.dev190}/ominfra/supervisor/groups.py +0 -0
  117. {ominfra-0.0.0.dev189 → ominfra-0.0.0.dev190}/ominfra/supervisor/groupsimpl.py +0 -0
  118. {ominfra-0.0.0.dev189 → ominfra-0.0.0.dev190}/ominfra/supervisor/http.py +0 -0
  119. {ominfra-0.0.0.dev189 → ominfra-0.0.0.dev190}/ominfra/supervisor/inject.py +0 -0
  120. {ominfra-0.0.0.dev189 → ominfra-0.0.0.dev190}/ominfra/supervisor/io.py +0 -0
  121. {ominfra-0.0.0.dev189 → ominfra-0.0.0.dev190}/ominfra/supervisor/main.py +0 -0
  122. {ominfra-0.0.0.dev189 → ominfra-0.0.0.dev190}/ominfra/supervisor/pipes.py +0 -0
  123. {ominfra-0.0.0.dev189 → ominfra-0.0.0.dev190}/ominfra/supervisor/privileges.py +0 -0
  124. {ominfra-0.0.0.dev189 → ominfra-0.0.0.dev190}/ominfra/supervisor/process.py +0 -0
  125. {ominfra-0.0.0.dev189 → ominfra-0.0.0.dev190}/ominfra/supervisor/processimpl.py +0 -0
  126. {ominfra-0.0.0.dev189 → ominfra-0.0.0.dev190}/ominfra/supervisor/setup.py +0 -0
  127. {ominfra-0.0.0.dev189 → ominfra-0.0.0.dev190}/ominfra/supervisor/setupimpl.py +0 -0
  128. {ominfra-0.0.0.dev189 → ominfra-0.0.0.dev190}/ominfra/supervisor/signals.py +0 -0
  129. {ominfra-0.0.0.dev189 → ominfra-0.0.0.dev190}/ominfra/supervisor/spawning.py +0 -0
  130. {ominfra-0.0.0.dev189 → ominfra-0.0.0.dev190}/ominfra/supervisor/spawningimpl.py +0 -0
  131. {ominfra-0.0.0.dev189 → ominfra-0.0.0.dev190}/ominfra/supervisor/states.py +0 -0
  132. {ominfra-0.0.0.dev189 → ominfra-0.0.0.dev190}/ominfra/supervisor/supervisor.py +0 -0
  133. {ominfra-0.0.0.dev189 → ominfra-0.0.0.dev190}/ominfra/supervisor/types.py +0 -0
  134. {ominfra-0.0.0.dev189 → ominfra-0.0.0.dev190}/ominfra/supervisor/utils/__init__.py +0 -0
  135. {ominfra-0.0.0.dev189 → ominfra-0.0.0.dev190}/ominfra/supervisor/utils/collections.py +0 -0
  136. {ominfra-0.0.0.dev189 → ominfra-0.0.0.dev190}/ominfra/supervisor/utils/diag.py +0 -0
  137. {ominfra-0.0.0.dev189 → ominfra-0.0.0.dev190}/ominfra/supervisor/utils/fds.py +0 -0
  138. {ominfra-0.0.0.dev189 → ominfra-0.0.0.dev190}/ominfra/supervisor/utils/fs.py +0 -0
  139. {ominfra-0.0.0.dev189 → ominfra-0.0.0.dev190}/ominfra/supervisor/utils/os.py +0 -0
  140. {ominfra-0.0.0.dev189 → ominfra-0.0.0.dev190}/ominfra/supervisor/utils/ostypes.py +0 -0
  141. {ominfra-0.0.0.dev189 → ominfra-0.0.0.dev190}/ominfra/supervisor/utils/signals.py +0 -0
  142. {ominfra-0.0.0.dev189 → ominfra-0.0.0.dev190}/ominfra/supervisor/utils/strings.py +0 -0
  143. {ominfra-0.0.0.dev189 → ominfra-0.0.0.dev190}/ominfra/supervisor/utils/users.py +0 -0
  144. {ominfra-0.0.0.dev189 → ominfra-0.0.0.dev190}/ominfra/tailscale/__init__.py +0 -0
  145. {ominfra-0.0.0.dev189 → ominfra-0.0.0.dev190}/ominfra/tailscale/api.py +0 -0
  146. {ominfra-0.0.0.dev189 → ominfra-0.0.0.dev190}/ominfra/tailscale/cli.py +0 -0
  147. {ominfra-0.0.0.dev189 → ominfra-0.0.0.dev190}/ominfra/threadworkers.py +0 -0
  148. {ominfra-0.0.0.dev189 → ominfra-0.0.0.dev190}/ominfra/tools/__init__.py +0 -0
  149. {ominfra-0.0.0.dev189 → ominfra-0.0.0.dev190}/ominfra/tools/listresources.py +0 -0
  150. {ominfra-0.0.0.dev189 → ominfra-0.0.0.dev190}/ominfra.egg-info/dependency_links.txt +0 -0
  151. {ominfra-0.0.0.dev189 → ominfra-0.0.0.dev190}/ominfra.egg-info/entry_points.txt +0 -0
  152. {ominfra-0.0.0.dev189 → ominfra-0.0.0.dev190}/ominfra.egg-info/top_level.txt +0 -0
  153. {ominfra-0.0.0.dev189 → ominfra-0.0.0.dev190}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ominfra
3
- Version: 0.0.0.dev189
3
+ Version: 0.0.0.dev190
4
4
  Summary: ominfra
5
5
  Author: wrmsr
6
6
  License: BSD-3-Clause
@@ -12,8 +12,9 @@ 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.dev189
16
- Requires-Dist: omlish==0.0.0.dev189
15
+ Requires-Dist: omdev==0.0.0.dev190
16
+ Requires-Dist: omlish==0.0.0.dev190
17
+ Requires-Dist: omserv==0.0.0.dev190
17
18
  Provides-Extra: all
18
19
  Requires-Dist: paramiko~=3.5; extra == "all"
19
20
  Requires-Dist: asyncssh~=2.18; extra == "all"
@@ -10,6 +10,7 @@ class Project(ProjectBase):
10
10
  dependencies = [
11
11
  f'omdev == {__version__}',
12
12
  f'omlish == {__version__}',
13
+ f'omserv == {__version__}',
13
14
  ]
14
15
 
15
16
  optional_dependencies = {
@@ -0,0 +1,129 @@
1
+ # ruff: noqa: UP006 UP007
2
+ import dataclasses as dc
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.lite.json import json_dumps_pretty
9
+ from omlish.lite.marshal import ObjMarshalerManager
10
+
11
+ from .conf.manager import DeployConfManager
12
+ from .git import DeployGitManager
13
+ from .paths.owners import DeployPathOwner
14
+ from .paths.paths import DeployPath
15
+ from .specs import DeployAppSpec
16
+ from .tags import DeployTagMap
17
+ from .types import DeployHome
18
+ from .venvs import DeployVenvManager
19
+
20
+
21
+ class DeployAppManager(DeployPathOwner):
22
+ def __init__(
23
+ self,
24
+ *,
25
+ git: DeployGitManager,
26
+ venvs: DeployVenvManager,
27
+ conf: DeployConfManager,
28
+
29
+ msh: ObjMarshalerManager,
30
+ ) -> None:
31
+ super().__init__()
32
+
33
+ self._git = git
34
+ self._venvs = venvs
35
+ self._conf = conf
36
+
37
+ self._msh = msh
38
+
39
+ #
40
+
41
+ APP_DIR = DeployPath.parse('apps/@app/@time--@app-rev--@app-key/')
42
+
43
+ @cached_nullary
44
+ def get_owned_deploy_paths(self) -> ta.AbstractSet[DeployPath]:
45
+ return {
46
+ self.APP_DIR,
47
+
48
+ *[
49
+ DeployPath.parse(f'{self.APP_DIR}{sfx}/')
50
+ for sfx in [
51
+ 'conf',
52
+ 'git',
53
+ 'venv',
54
+ ]
55
+ ],
56
+ }
57
+
58
+ #
59
+
60
+ @dc.dataclass(frozen=True)
61
+ class PreparedApp:
62
+ app_dir: str
63
+
64
+ git_dir: ta.Optional[str] = None
65
+ venv_dir: ta.Optional[str] = None
66
+ conf_dir: ta.Optional[str] = None
67
+
68
+ async def prepare_app(
69
+ self,
70
+ spec: DeployAppSpec,
71
+ home: DeployHome,
72
+ tags: DeployTagMap,
73
+ ) -> PreparedApp:
74
+ spec_json = json_dumps_pretty(self._msh.marshal_obj(spec))
75
+
76
+ #
77
+
78
+ check.non_empty_str(home)
79
+
80
+ app_dir = os.path.join(home, self.APP_DIR.render(tags))
81
+
82
+ os.makedirs(app_dir, exist_ok=True)
83
+
84
+ #
85
+
86
+ rkw: ta.Dict[str, ta.Any] = dict(
87
+ app_dir=app_dir,
88
+ )
89
+
90
+ #
91
+
92
+ spec_file = os.path.join(app_dir, 'spec.json')
93
+ with open(spec_file, 'w') as f: # noqa
94
+ f.write(spec_json)
95
+
96
+ #
97
+
98
+ git_dir = os.path.join(app_dir, 'git')
99
+ rkw.update(git_dir=git_dir)
100
+ await self._git.checkout(
101
+ spec.git,
102
+ home,
103
+ git_dir,
104
+ )
105
+
106
+ #
107
+
108
+ if spec.venv is not None:
109
+ venv_dir = os.path.join(app_dir, 'venv')
110
+ rkw.update(venv_dir=venv_dir)
111
+ await self._venvs.setup_venv(
112
+ spec.venv,
113
+ git_dir,
114
+ venv_dir,
115
+ )
116
+
117
+ #
118
+
119
+ if spec.conf is not None:
120
+ conf_dir = os.path.join(app_dir, 'conf')
121
+ rkw.update(conf_dir=conf_dir)
122
+ await self._conf.write_app_conf(
123
+ spec.conf,
124
+ conf_dir,
125
+ )
126
+
127
+ #
128
+
129
+ return DeployAppManager.PreparedApp(**rkw)
@@ -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 .driver import DeployDriverFactory
8
+ from .deploy import DeployDriverFactory
9
9
  from .specs import DeploySpec
10
10
 
11
11
 
@@ -24,6 +24,8 @@ from omlish.lite.json import json_dumps_pretty
24
24
  from omlish.lite.strings import strip_with_newline
25
25
  from omlish.os.paths import is_path_in_dir
26
26
  from omlish.os.paths import relative_symlink
27
+ from omserv.nginx.configs import NginxConfigItems
28
+ from omserv.nginx.configs import render_nginx_config_str
27
29
 
28
30
  from ....configs import render_ini_config
29
31
  from ..paths.paths import DeployPath
@@ -37,6 +39,7 @@ from .specs import DeployAppConfLink
37
39
  from .specs import DeployAppConfSpec
38
40
  from .specs import IniDeployAppConfContent
39
41
  from .specs import JsonDeployAppConfContent
42
+ from .specs import NginxDeployAppConfContent
40
43
  from .specs import RawDeployAppConfContent
41
44
 
42
45
 
@@ -51,6 +54,10 @@ class DeployConfManager:
51
54
  elif isinstance(ac, IniDeployAppConfContent):
52
55
  return strip_with_newline(render_ini_config(ac.sections))
53
56
 
57
+ elif isinstance(ac, NginxDeployAppConfContent):
58
+ ni = NginxConfigItems.of(ac.items)
59
+ return strip_with_newline(render_nginx_config_str(ni))
60
+
54
61
  else:
55
62
  raise TypeError(ac)
56
63
 
@@ -69,6 +76,17 @@ class DeployConfManager:
69
76
  with open(conf_file, 'w') as f: # noqa
70
77
  f.write(body)
71
78
 
79
+ async def write_app_conf(
80
+ self,
81
+ spec: DeployAppConfSpec,
82
+ app_conf_dir: str,
83
+ ) -> None:
84
+ for acf in spec.files or []:
85
+ await self._write_app_conf_file(
86
+ acf,
87
+ app_conf_dir,
88
+ )
89
+
72
90
  #
73
91
 
74
92
  class _ComputedConfLink(ta.NamedTuple):
@@ -178,23 +196,13 @@ class DeployConfManager:
178
196
  make_dirs=True,
179
197
  )
180
198
 
181
- #
182
-
183
- async def write_app_conf(
199
+ async def link_app_conf(
184
200
  self,
185
201
  spec: DeployAppConfSpec,
186
202
  tags: DeployTagMap,
187
203
  app_conf_dir: str,
188
204
  conf_link_dir: str,
189
- ) -> None:
190
- for acf in spec.files or []:
191
- await self._write_app_conf_file(
192
- acf,
193
- app_conf_dir,
194
- )
195
-
196
- #
197
-
205
+ ):
198
206
  for link in spec.links or []:
199
207
  await self._make_app_conf_link(
200
208
  link,
@@ -44,6 +44,15 @@ class IniDeployAppConfContent(DeployAppConfContent):
44
44
  sections: IniConfigSectionSettingsMap
45
45
 
46
46
 
47
+ #
48
+
49
+
50
+ @register_single_field_type_obj_marshaler('items')
51
+ @dc.dataclass(frozen=True)
52
+ class NginxDeployAppConfContent(DeployAppConfContent):
53
+ items: ta.Any
54
+
55
+
47
56
  ##
48
57
 
49
58
 
@@ -0,0 +1,239 @@
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.lite.json import json_dumps_pretty
9
+ from omlish.lite.marshal import ObjMarshalerManager
10
+ from omlish.lite.typing import Func0
11
+ from omlish.lite.typing import Func1
12
+ from omlish.os.paths import relative_symlink
13
+
14
+ from .apps import DeployAppManager
15
+ from .conf.manager import DeployConfManager
16
+ from .paths.manager import DeployPathsManager
17
+ from .paths.owners import DeployPathOwner
18
+ from .paths.paths import DeployPath
19
+ from .specs import DeployAppSpec
20
+ from .specs import DeploySpec
21
+ from .systemd import DeploySystemdManager
22
+ from .tags import DeployAppRev
23
+ from .tags import DeployTagMap
24
+ from .tags import DeployTime
25
+ from .types import DeployHome
26
+
27
+
28
+ ##
29
+
30
+
31
+ DEPLOY_TAG_DATETIME_FMT = '%Y%m%dT%H%M%SZ'
32
+
33
+
34
+ DeployManagerUtcClock = ta.NewType('DeployManagerUtcClock', Func0[datetime.datetime])
35
+
36
+
37
+ class DeployManager(DeployPathOwner):
38
+ def __init__(
39
+ self,
40
+ *,
41
+ utc_clock: ta.Optional[DeployManagerUtcClock] = None,
42
+ ):
43
+ super().__init__()
44
+
45
+ self._utc_clock = utc_clock
46
+
47
+ #
48
+
49
+ DEPLOYS_DIR = DeployPath.parse('deploys/')
50
+
51
+ CURRENT_DEPLOY_LINK = DeployPath.parse(f'{DEPLOYS_DIR}current')
52
+ DEPLOYING_DEPLOY_LINK = DeployPath.parse(f'{DEPLOYS_DIR}deploying')
53
+
54
+ DEPLOY_DIR = DeployPath.parse(f'{DEPLOYS_DIR}@time--@deploy-key/')
55
+ DEPLOY_SPEC_FILE = DeployPath.parse(f'{DEPLOY_DIR}spec.json')
56
+
57
+ APPS_DEPLOY_DIR = DeployPath.parse(f'{DEPLOY_DIR}apps/')
58
+ APP_DEPLOY_LINK = DeployPath.parse(f'{APPS_DEPLOY_DIR}@app')
59
+
60
+ CONFS_DEPLOY_DIR = DeployPath.parse(f'{DEPLOY_DIR}conf/')
61
+ CONF_DEPLOY_DIR = DeployPath.parse(f'{CONFS_DEPLOY_DIR}@conf/')
62
+
63
+ @cached_nullary
64
+ def get_owned_deploy_paths(self) -> ta.AbstractSet[DeployPath]:
65
+ return {
66
+ self.DEPLOYS_DIR,
67
+
68
+ self.CURRENT_DEPLOY_LINK,
69
+ self.DEPLOYING_DEPLOY_LINK,
70
+
71
+ self.DEPLOY_DIR,
72
+ self.DEPLOY_SPEC_FILE,
73
+
74
+ self.APPS_DEPLOY_DIR,
75
+ self.APP_DEPLOY_LINK,
76
+
77
+ self.CONFS_DEPLOY_DIR,
78
+ self.CONF_DEPLOY_DIR,
79
+ }
80
+
81
+ #
82
+
83
+ def _utc_now(self) -> datetime.datetime:
84
+ if self._utc_clock is not None:
85
+ return self._utc_clock() # noqa
86
+ else:
87
+ return datetime.datetime.now(tz=datetime.timezone.utc) # noqa
88
+
89
+ def make_deploy_time(self) -> DeployTime:
90
+ return DeployTime(self._utc_now().strftime(DEPLOY_TAG_DATETIME_FMT))
91
+
92
+
93
+ ##
94
+
95
+
96
+ class DeployDriverFactory(Func1[DeploySpec, ta.ContextManager['DeployDriver']]):
97
+ pass
98
+
99
+
100
+ class DeployDriver:
101
+ def __init__(
102
+ self,
103
+ *,
104
+ spec: DeploySpec,
105
+ home: DeployHome,
106
+ time: DeployTime,
107
+
108
+ deploys: DeployManager,
109
+ paths: DeployPathsManager,
110
+ apps: DeployAppManager,
111
+ conf: DeployConfManager,
112
+ systemd: DeploySystemdManager,
113
+
114
+ msh: ObjMarshalerManager,
115
+ ) -> None:
116
+ super().__init__()
117
+
118
+ self._spec = spec
119
+ self._home = home
120
+ self._time = time
121
+
122
+ self._deploys = deploys
123
+ self._paths = paths
124
+ self._apps = apps
125
+ self._conf = conf
126
+ self._systemd = systemd
127
+
128
+ self._msh = msh
129
+
130
+ #
131
+
132
+ @property
133
+ def deploy_tags(self) -> DeployTagMap:
134
+ return DeployTagMap(
135
+ self._time,
136
+ self._spec.key(),
137
+ )
138
+
139
+ def render_deploy_path(self, pth: DeployPath, tags: ta.Optional[DeployTagMap] = None) -> str:
140
+ return os.path.join(self._home, pth.render(tags if tags is not None else self.deploy_tags))
141
+
142
+ @property
143
+ def deploy_dir(self) -> str:
144
+ return self.render_deploy_path(self._deploys.DEPLOY_DIR)
145
+
146
+ #
147
+
148
+ async def drive_deploy(self) -> None:
149
+ spec_json = json_dumps_pretty(self._msh.marshal_obj(self._spec))
150
+
151
+ #
152
+
153
+ self._paths.validate_deploy_paths()
154
+
155
+ #
156
+
157
+ os.makedirs(self.deploy_dir)
158
+
159
+ #
160
+
161
+ spec_file = self.render_deploy_path(self._deploys.DEPLOY_SPEC_FILE)
162
+ with open(spec_file, 'w') as f: # noqa
163
+ f.write(spec_json)
164
+
165
+ #
166
+
167
+ deploying_link = self.render_deploy_path(self._deploys.DEPLOYING_DEPLOY_LINK)
168
+ if os.path.exists(deploying_link):
169
+ os.unlink(deploying_link)
170
+ relative_symlink(
171
+ self.deploy_dir,
172
+ deploying_link,
173
+ target_is_directory=True,
174
+ make_dirs=True,
175
+ )
176
+
177
+ #
178
+
179
+ for md in [
180
+ self._deploys.APPS_DEPLOY_DIR,
181
+ self._deploys.CONFS_DEPLOY_DIR,
182
+ ]:
183
+ os.makedirs(self.render_deploy_path(md))
184
+
185
+ #
186
+
187
+ for app in self._spec.apps:
188
+ await self.drive_app_deploy(app)
189
+
190
+ #
191
+
192
+ current_link = self.render_deploy_path(self._deploys.CURRENT_DEPLOY_LINK)
193
+ os.replace(deploying_link, current_link)
194
+
195
+ #
196
+
197
+ await self._systemd.sync_systemd(
198
+ self._spec.systemd,
199
+ self._home,
200
+ os.path.join(self.deploy_dir, 'conf', 'systemd'), # FIXME
201
+ )
202
+
203
+ #
204
+
205
+ async def drive_app_deploy(self, app: DeployAppSpec) -> None:
206
+ app_tags = self.deploy_tags.add(
207
+ app.app,
208
+ app.key(),
209
+ DeployAppRev(app.git.rev),
210
+ )
211
+
212
+ #
213
+
214
+ da = await self._apps.prepare_app(
215
+ app,
216
+ self._home,
217
+ app_tags,
218
+ )
219
+
220
+ #
221
+
222
+ app_link = self.render_deploy_path(self._deploys.APP_DEPLOY_LINK, app_tags)
223
+ relative_symlink(
224
+ da.app_dir,
225
+ app_link,
226
+ target_is_directory=True,
227
+ make_dirs=True,
228
+ )
229
+
230
+ #
231
+
232
+ deploy_conf_dir = self.render_deploy_path(self._deploys.CONFS_DEPLOY_DIR)
233
+ if app.conf is not None:
234
+ await self._conf.link_app_conf(
235
+ app.conf,
236
+ app_tags,
237
+ check.non_empty_str(da.conf_dir),
238
+ deploy_conf_dir,
239
+ )
@@ -16,15 +16,16 @@ from .commands import DeployCommand
16
16
  from .commands import DeployCommandExecutor
17
17
  from .conf.inject import bind_deploy_conf
18
18
  from .config import DeployConfig
19
+ from .deploy import DeployDriver
20
+ from .deploy import DeployDriverFactory
19
21
  from .deploy import DeployManager
20
- from .driver import DeployDriver
21
- from .driver import DeployDriverFactory
22
22
  from .git import DeployGitManager
23
23
  from .inject_ import bind_deploy_manager
24
24
  from .interp import InterpCommand
25
25
  from .interp import InterpCommandExecutor
26
26
  from .paths.inject import bind_deploy_paths
27
27
  from .specs import DeploySpec
28
+ from .systemd import DeploySystemdManager
28
29
  from .tags import DeployTime
29
30
  from .tmp import DeployHomeAtomics
30
31
  from .tmp import DeployTmpManager
@@ -101,13 +102,10 @@ def bind_deploy(
101
102
 
102
103
  lst.extend([
103
104
  bind_deploy_manager(DeployAppManager),
104
-
105
105
  bind_deploy_manager(DeployGitManager),
106
-
107
106
  bind_deploy_manager(DeployManager),
108
-
107
+ bind_deploy_manager(DeploySystemdManager),
109
108
  bind_deploy_manager(DeployTmpManager),
110
-
111
109
  bind_deploy_manager(DeployVenvManager),
112
110
  ])
113
111
 
@@ -0,0 +1,8 @@
1
+ # ruff: noqa: UP006 UP007
2
+ """
3
+ verify - nginx -t
4
+ """
5
+
6
+
7
+ class DeployNginxManager:
8
+ pass
@@ -33,6 +33,10 @@ class DeployPathError(Exception):
33
33
 
34
34
 
35
35
  class DeployPathRenderable(abc.ABC):
36
+ @cached_nullary
37
+ def __str__(self) -> str:
38
+ return self.render(None)
39
+
36
40
  @abc.abstractmethod
37
41
  def render(self, tags: ta.Optional[DeployTagMap] = None) -> str:
38
42
  raise NotImplementedError
@@ -174,7 +178,7 @@ class FileDeployPathPart(DeployPathPart):
174
178
 
175
179
 
176
180
  @dc.dataclass(frozen=True)
177
- class DeployPath:
181
+ class DeployPath(DeployPathRenderable):
178
182
  parts: ta.Sequence[DeployPathPart]
179
183
 
180
184
  @property
@@ -94,11 +94,22 @@ class DeployAppSpec(DeploySpecKeyed[DeployAppKey]):
94
94
  ##
95
95
 
96
96
 
97
+ @dc.dataclass(frozen=True)
98
+ class DeploySystemdSpec:
99
+ # ~/.config/systemd/user/
100
+ unit_dir: ta.Optional[str] = None
101
+
102
+
103
+ ##
104
+
105
+
97
106
  @dc.dataclass(frozen=True)
98
107
  class DeploySpec(DeploySpecKeyed[DeployKey]):
99
108
  home: DeployHome
100
109
 
101
- apps: ta.Sequence[DeployAppSpec]
110
+ apps: ta.Sequence[DeployAppSpec] = ()
111
+
112
+ systemd: ta.Optional[DeploySystemdSpec] = None
102
113
 
103
114
  def __post_init__(self) -> None:
104
115
  check.non_empty_str(self.home)
@@ -0,0 +1,110 @@
1
+ # ruff: noqa: UP006 UP007
2
+ """
3
+ ~/.config/systemd/user/
4
+
5
+ verify - systemd-analyze
6
+
7
+ sudo loginctl enable-linger "$USER"
8
+
9
+ cat ~/.config/systemd/user/sleep-infinity.service
10
+ [Unit]
11
+ Description=User-specific service to run 'sleep infinity'
12
+ After=default.target
13
+
14
+ [Service]
15
+ ExecStart=/bin/sleep infinity
16
+ Restart=always
17
+ RestartSec=5
18
+
19
+ [Install]
20
+ WantedBy=default.target
21
+
22
+ systemctl --user daemon-reload
23
+
24
+ systemctl --user enable sleep-infinity.service
25
+ systemctl --user start sleep-infinity.service
26
+
27
+ systemctl --user status sleep-infinity.service
28
+ """
29
+ import os.path
30
+ import typing as ta
31
+
32
+ from omlish.lite.check import check
33
+ from omlish.os.paths import abs_real_path
34
+ from omlish.os.paths import is_path_in_dir
35
+
36
+ from .specs import DeploySystemdSpec
37
+ from .tmp import DeployHomeAtomics
38
+ from .types import DeployHome
39
+
40
+
41
+ class DeploySystemdManager:
42
+ def __init__(
43
+ self,
44
+ *,
45
+ atomics: DeployHomeAtomics,
46
+ ) -> None:
47
+ super().__init__()
48
+
49
+ self._atomics = atomics
50
+
51
+ def _scan_link_dir(
52
+ self,
53
+ d: str,
54
+ *,
55
+ strict: bool = False,
56
+ ) -> ta.Dict[str, str]:
57
+ o: ta.Dict[str, str] = {}
58
+ for f in os.listdir(d):
59
+ fp = os.path.join(d, f)
60
+ if strict:
61
+ check.state(os.path.islink(fp))
62
+ o[f] = abs_real_path(fp)
63
+ return o
64
+
65
+ async def sync_systemd(
66
+ self,
67
+ spec: ta.Optional[DeploySystemdSpec],
68
+ home: DeployHome,
69
+ conf_dir: str,
70
+ ) -> None:
71
+ check.non_empty_str(home)
72
+
73
+ if not spec:
74
+ return
75
+
76
+ if not (ud := spec.unit_dir):
77
+ return
78
+
79
+ ud = abs_real_path(os.path.expanduser(ud))
80
+
81
+ os.makedirs(ud, exist_ok=True)
82
+
83
+ uld = {
84
+ n: p
85
+ for n, p in self._scan_link_dir(ud).items()
86
+ if is_path_in_dir(home, p)
87
+ }
88
+
89
+ if os.path.exists(conf_dir):
90
+ cld = self._scan_link_dir(conf_dir, strict=True)
91
+ else:
92
+ cld = {}
93
+
94
+ for n in sorted(set(uld) | set(cld)):
95
+ ul = uld.get(n) # noqa
96
+ cl = cld.get(n)
97
+ if cl is None:
98
+ os.unlink(os.path.join(ud, n))
99
+ else:
100
+ with self._atomics(home).begin_atomic_path_swap( # noqa
101
+ 'file',
102
+ os.path.join(ud, n),
103
+ auto_commit=True,
104
+ skip_root_dir_check=True,
105
+ ) as dst_swap:
106
+ os.unlink(dst_swap.tmp_path)
107
+ os.symlink(
108
+ os.path.relpath(cl, os.path.dirname(dst_swap.dst_path)),
109
+ dst_swap.tmp_path,
110
+ )
@@ -12,14 +12,12 @@ from omlish.asyncs.asyncio.subprocesses import asyncio_subprocesses
12
12
  from omlish.lite.check import check
13
13
 
14
14
  from .specs import DeployVenvSpec
15
- from .types import DeployHome
16
15
 
17
16
 
18
17
  class DeployVenvManager:
19
18
  async def setup_venv(
20
19
  self,
21
20
  spec: DeployVenvSpec,
22
- home: DeployHome,
23
21
  git_dir: str,
24
22
  venv_dir: str,
25
23
  ) -> None: