ominfra 0.0.0.dev157__tar.gz → 0.0.0.dev159__tar.gz

Sign up to get free protection for your applications and to get access to all the features.
Files changed (135) hide show
  1. {ominfra-0.0.0.dev157/ominfra.egg-info → ominfra-0.0.0.dev159}/PKG-INFO +3 -3
  2. {ominfra-0.0.0.dev157 → ominfra-0.0.0.dev159}/ominfra/clouds/aws/journald2aws/main.py +1 -1
  3. {ominfra-0.0.0.dev157 → ominfra-0.0.0.dev159}/ominfra/journald/tailer.py +2 -2
  4. {ominfra-0.0.0.dev157 → ominfra-0.0.0.dev159}/ominfra/manage/bootstrap_.py +1 -1
  5. {ominfra-0.0.0.dev157 → ominfra-0.0.0.dev159}/ominfra/manage/commands/subprocess.py +4 -4
  6. {ominfra-0.0.0.dev157 → ominfra-0.0.0.dev159}/ominfra/manage/deploy/apps.py +23 -21
  7. ominfra-0.0.0.dev159/ominfra/manage/deploy/atomics.py +207 -0
  8. {ominfra-0.0.0.dev157 → ominfra-0.0.0.dev159}/ominfra/manage/deploy/config.py +3 -0
  9. {ominfra-0.0.0.dev157 → ominfra-0.0.0.dev159}/ominfra/manage/deploy/git.py +27 -47
  10. {ominfra-0.0.0.dev157 → ominfra-0.0.0.dev159}/ominfra/manage/deploy/inject.py +11 -0
  11. ominfra-0.0.0.dev159/ominfra/manage/deploy/paths.py +268 -0
  12. ominfra-0.0.0.dev159/ominfra/manage/deploy/specs.py +42 -0
  13. ominfra-0.0.0.dev159/ominfra/manage/deploy/tmp.py +46 -0
  14. {ominfra-0.0.0.dev157 → ominfra-0.0.0.dev159}/ominfra/manage/deploy/types.py +1 -0
  15. {ominfra-0.0.0.dev157 → ominfra-0.0.0.dev159}/ominfra/manage/deploy/venvs.py +16 -6
  16. {ominfra-0.0.0.dev157 → ominfra-0.0.0.dev159}/ominfra/manage/remote/spawning.py +3 -3
  17. {ominfra-0.0.0.dev157 → ominfra-0.0.0.dev159}/ominfra/manage/system/packages.py +1 -1
  18. {ominfra-0.0.0.dev157 → ominfra-0.0.0.dev159}/ominfra/pyremote.py +26 -26
  19. {ominfra-0.0.0.dev157 → ominfra-0.0.0.dev159}/ominfra/scripts/journald2aws.py +467 -354
  20. {ominfra-0.0.0.dev157 → ominfra-0.0.0.dev159}/ominfra/scripts/manage.py +1426 -1037
  21. {ominfra-0.0.0.dev157 → ominfra-0.0.0.dev159}/ominfra/scripts/supervisor.py +359 -336
  22. {ominfra-0.0.0.dev157 → ominfra-0.0.0.dev159}/ominfra/supervisor/http.py +1 -1
  23. {ominfra-0.0.0.dev157 → ominfra-0.0.0.dev159}/ominfra/supervisor/main.py +2 -2
  24. {ominfra-0.0.0.dev157 → ominfra-0.0.0.dev159/ominfra.egg-info}/PKG-INFO +3 -3
  25. {ominfra-0.0.0.dev157 → ominfra-0.0.0.dev159}/ominfra.egg-info/SOURCES.txt +3 -0
  26. {ominfra-0.0.0.dev157 → ominfra-0.0.0.dev159}/ominfra.egg-info/requires.txt +2 -2
  27. {ominfra-0.0.0.dev157 → ominfra-0.0.0.dev159}/pyproject.toml +3 -3
  28. ominfra-0.0.0.dev157/ominfra/manage/deploy/paths.py +0 -230
  29. {ominfra-0.0.0.dev157 → ominfra-0.0.0.dev159}/LICENSE +0 -0
  30. {ominfra-0.0.0.dev157 → ominfra-0.0.0.dev159}/MANIFEST.in +0 -0
  31. {ominfra-0.0.0.dev157 → ominfra-0.0.0.dev159}/README.rst +0 -0
  32. {ominfra-0.0.0.dev157 → ominfra-0.0.0.dev159}/ominfra/.manifests.json +0 -0
  33. {ominfra-0.0.0.dev157 → ominfra-0.0.0.dev159}/ominfra/__about__.py +0 -0
  34. {ominfra-0.0.0.dev157 → ominfra-0.0.0.dev159}/ominfra/__init__.py +0 -0
  35. {ominfra-0.0.0.dev157 → ominfra-0.0.0.dev159}/ominfra/clouds/__init__.py +0 -0
  36. {ominfra-0.0.0.dev157 → ominfra-0.0.0.dev159}/ominfra/clouds/aws/__init__.py +0 -0
  37. {ominfra-0.0.0.dev157 → ominfra-0.0.0.dev159}/ominfra/clouds/aws/__main__.py +0 -0
  38. {ominfra-0.0.0.dev157 → ominfra-0.0.0.dev159}/ominfra/clouds/aws/auth.py +0 -0
  39. {ominfra-0.0.0.dev157 → ominfra-0.0.0.dev159}/ominfra/clouds/aws/cli.py +0 -0
  40. {ominfra-0.0.0.dev157 → ominfra-0.0.0.dev159}/ominfra/clouds/aws/dataclasses.py +0 -0
  41. {ominfra-0.0.0.dev157 → ominfra-0.0.0.dev159}/ominfra/clouds/aws/journald2aws/__init__.py +0 -0
  42. {ominfra-0.0.0.dev157 → ominfra-0.0.0.dev159}/ominfra/clouds/aws/journald2aws/__main__.py +0 -0
  43. {ominfra-0.0.0.dev157 → ominfra-0.0.0.dev159}/ominfra/clouds/aws/journald2aws/cursor.py +0 -0
  44. {ominfra-0.0.0.dev157 → ominfra-0.0.0.dev159}/ominfra/clouds/aws/journald2aws/driver.py +0 -0
  45. {ominfra-0.0.0.dev157 → ominfra-0.0.0.dev159}/ominfra/clouds/aws/journald2aws/poster.py +0 -0
  46. {ominfra-0.0.0.dev157 → ominfra-0.0.0.dev159}/ominfra/clouds/aws/logs.py +0 -0
  47. {ominfra-0.0.0.dev157 → ominfra-0.0.0.dev159}/ominfra/clouds/aws/metadata.py +0 -0
  48. {ominfra-0.0.0.dev157 → ominfra-0.0.0.dev159}/ominfra/clouds/gcp/__init__.py +0 -0
  49. {ominfra-0.0.0.dev157 → ominfra-0.0.0.dev159}/ominfra/clouds/gcp/auth.py +0 -0
  50. {ominfra-0.0.0.dev157 → ominfra-0.0.0.dev159}/ominfra/cmds.py +0 -0
  51. {ominfra-0.0.0.dev157 → ominfra-0.0.0.dev159}/ominfra/configs.py +0 -0
  52. {ominfra-0.0.0.dev157 → ominfra-0.0.0.dev159}/ominfra/journald/__init__.py +0 -0
  53. {ominfra-0.0.0.dev157 → ominfra-0.0.0.dev159}/ominfra/journald/fields.py +0 -0
  54. {ominfra-0.0.0.dev157 → ominfra-0.0.0.dev159}/ominfra/journald/genmessages.py +0 -0
  55. {ominfra-0.0.0.dev157 → ominfra-0.0.0.dev159}/ominfra/journald/messages.py +0 -0
  56. {ominfra-0.0.0.dev157 → ominfra-0.0.0.dev159}/ominfra/manage/__init__.py +0 -0
  57. {ominfra-0.0.0.dev157 → ominfra-0.0.0.dev159}/ominfra/manage/__main__.py +0 -0
  58. {ominfra-0.0.0.dev157 → ominfra-0.0.0.dev159}/ominfra/manage/bootstrap.py +0 -0
  59. {ominfra-0.0.0.dev157 → ominfra-0.0.0.dev159}/ominfra/manage/commands/__init__.py +0 -0
  60. {ominfra-0.0.0.dev157 → ominfra-0.0.0.dev159}/ominfra/manage/commands/base.py +0 -0
  61. {ominfra-0.0.0.dev157 → ominfra-0.0.0.dev159}/ominfra/manage/commands/inject.py +0 -0
  62. {ominfra-0.0.0.dev157 → ominfra-0.0.0.dev159}/ominfra/manage/commands/local.py +0 -0
  63. {ominfra-0.0.0.dev157 → ominfra-0.0.0.dev159}/ominfra/manage/commands/marshal.py +0 -0
  64. {ominfra-0.0.0.dev157 → ominfra-0.0.0.dev159}/ominfra/manage/commands/ping.py +0 -0
  65. {ominfra-0.0.0.dev157 → ominfra-0.0.0.dev159}/ominfra/manage/commands/types.py +0 -0
  66. {ominfra-0.0.0.dev157 → ominfra-0.0.0.dev159}/ominfra/manage/config.py +0 -0
  67. {ominfra-0.0.0.dev157 → ominfra-0.0.0.dev159}/ominfra/manage/deploy/__init__.py +0 -0
  68. {ominfra-0.0.0.dev157 → ominfra-0.0.0.dev159}/ominfra/manage/deploy/commands.py +0 -0
  69. {ominfra-0.0.0.dev157 → ominfra-0.0.0.dev159}/ominfra/manage/deploy/interp.py +0 -0
  70. {ominfra-0.0.0.dev157 → ominfra-0.0.0.dev159}/ominfra/manage/inject.py +0 -0
  71. {ominfra-0.0.0.dev157 → ominfra-0.0.0.dev159}/ominfra/manage/main.py +0 -0
  72. {ominfra-0.0.0.dev157 → ominfra-0.0.0.dev159}/ominfra/manage/marshal.py +0 -0
  73. {ominfra-0.0.0.dev157 → ominfra-0.0.0.dev159}/ominfra/manage/remote/__init__.py +0 -0
  74. {ominfra-0.0.0.dev157 → ominfra-0.0.0.dev159}/ominfra/manage/remote/_main.py +0 -0
  75. {ominfra-0.0.0.dev157 → ominfra-0.0.0.dev159}/ominfra/manage/remote/channel.py +0 -0
  76. {ominfra-0.0.0.dev157 → ominfra-0.0.0.dev159}/ominfra/manage/remote/config.py +0 -0
  77. {ominfra-0.0.0.dev157 → ominfra-0.0.0.dev159}/ominfra/manage/remote/connection.py +0 -0
  78. {ominfra-0.0.0.dev157 → ominfra-0.0.0.dev159}/ominfra/manage/remote/execution.py +0 -0
  79. {ominfra-0.0.0.dev157 → ominfra-0.0.0.dev159}/ominfra/manage/remote/inject.py +0 -0
  80. {ominfra-0.0.0.dev157 → ominfra-0.0.0.dev159}/ominfra/manage/remote/payload.py +0 -0
  81. {ominfra-0.0.0.dev157 → ominfra-0.0.0.dev159}/ominfra/manage/system/__init__.py +0 -0
  82. {ominfra-0.0.0.dev157 → ominfra-0.0.0.dev159}/ominfra/manage/system/commands.py +0 -0
  83. {ominfra-0.0.0.dev157 → ominfra-0.0.0.dev159}/ominfra/manage/system/config.py +0 -0
  84. {ominfra-0.0.0.dev157 → ominfra-0.0.0.dev159}/ominfra/manage/system/inject.py +0 -0
  85. {ominfra-0.0.0.dev157 → ominfra-0.0.0.dev159}/ominfra/manage/system/platforms.py +0 -0
  86. {ominfra-0.0.0.dev157 → ominfra-0.0.0.dev159}/ominfra/manage/targets/__init__.py +0 -0
  87. {ominfra-0.0.0.dev157 → ominfra-0.0.0.dev159}/ominfra/manage/targets/connection.py +0 -0
  88. {ominfra-0.0.0.dev157 → ominfra-0.0.0.dev159}/ominfra/manage/targets/inject.py +0 -0
  89. {ominfra-0.0.0.dev157 → ominfra-0.0.0.dev159}/ominfra/manage/targets/targets.py +0 -0
  90. {ominfra-0.0.0.dev157 → ominfra-0.0.0.dev159}/ominfra/scripts/__init__.py +0 -0
  91. {ominfra-0.0.0.dev157 → ominfra-0.0.0.dev159}/ominfra/ssh.py +0 -0
  92. {ominfra-0.0.0.dev157 → ominfra-0.0.0.dev159}/ominfra/supervisor/LICENSE.txt +0 -0
  93. {ominfra-0.0.0.dev157 → ominfra-0.0.0.dev159}/ominfra/supervisor/__init__.py +0 -0
  94. {ominfra-0.0.0.dev157 → ominfra-0.0.0.dev159}/ominfra/supervisor/__main__.py +0 -0
  95. {ominfra-0.0.0.dev157 → ominfra-0.0.0.dev159}/ominfra/supervisor/configs.py +0 -0
  96. {ominfra-0.0.0.dev157 → ominfra-0.0.0.dev159}/ominfra/supervisor/dispatchers.py +0 -0
  97. {ominfra-0.0.0.dev157 → ominfra-0.0.0.dev159}/ominfra/supervisor/dispatchersimpl.py +0 -0
  98. {ominfra-0.0.0.dev157 → ominfra-0.0.0.dev159}/ominfra/supervisor/events.py +0 -0
  99. {ominfra-0.0.0.dev157 → ominfra-0.0.0.dev159}/ominfra/supervisor/exceptions.py +0 -0
  100. {ominfra-0.0.0.dev157 → ominfra-0.0.0.dev159}/ominfra/supervisor/groups.py +0 -0
  101. {ominfra-0.0.0.dev157 → ominfra-0.0.0.dev159}/ominfra/supervisor/groupsimpl.py +0 -0
  102. {ominfra-0.0.0.dev157 → ominfra-0.0.0.dev159}/ominfra/supervisor/inject.py +0 -0
  103. {ominfra-0.0.0.dev157 → ominfra-0.0.0.dev159}/ominfra/supervisor/io.py +0 -0
  104. {ominfra-0.0.0.dev157 → ominfra-0.0.0.dev159}/ominfra/supervisor/pipes.py +0 -0
  105. {ominfra-0.0.0.dev157 → ominfra-0.0.0.dev159}/ominfra/supervisor/privileges.py +0 -0
  106. {ominfra-0.0.0.dev157 → ominfra-0.0.0.dev159}/ominfra/supervisor/process.py +0 -0
  107. {ominfra-0.0.0.dev157 → ominfra-0.0.0.dev159}/ominfra/supervisor/processimpl.py +0 -0
  108. {ominfra-0.0.0.dev157 → ominfra-0.0.0.dev159}/ominfra/supervisor/setup.py +0 -0
  109. {ominfra-0.0.0.dev157 → ominfra-0.0.0.dev159}/ominfra/supervisor/setupimpl.py +0 -0
  110. {ominfra-0.0.0.dev157 → ominfra-0.0.0.dev159}/ominfra/supervisor/signals.py +0 -0
  111. {ominfra-0.0.0.dev157 → ominfra-0.0.0.dev159}/ominfra/supervisor/spawning.py +0 -0
  112. {ominfra-0.0.0.dev157 → ominfra-0.0.0.dev159}/ominfra/supervisor/spawningimpl.py +0 -0
  113. {ominfra-0.0.0.dev157 → ominfra-0.0.0.dev159}/ominfra/supervisor/states.py +0 -0
  114. {ominfra-0.0.0.dev157 → ominfra-0.0.0.dev159}/ominfra/supervisor/supervisor.py +0 -0
  115. {ominfra-0.0.0.dev157 → ominfra-0.0.0.dev159}/ominfra/supervisor/types.py +0 -0
  116. {ominfra-0.0.0.dev157 → ominfra-0.0.0.dev159}/ominfra/supervisor/utils/__init__.py +0 -0
  117. {ominfra-0.0.0.dev157 → ominfra-0.0.0.dev159}/ominfra/supervisor/utils/collections.py +0 -0
  118. {ominfra-0.0.0.dev157 → ominfra-0.0.0.dev159}/ominfra/supervisor/utils/diag.py +0 -0
  119. {ominfra-0.0.0.dev157 → ominfra-0.0.0.dev159}/ominfra/supervisor/utils/fds.py +0 -0
  120. {ominfra-0.0.0.dev157 → ominfra-0.0.0.dev159}/ominfra/supervisor/utils/fs.py +0 -0
  121. {ominfra-0.0.0.dev157 → ominfra-0.0.0.dev159}/ominfra/supervisor/utils/os.py +0 -0
  122. {ominfra-0.0.0.dev157 → ominfra-0.0.0.dev159}/ominfra/supervisor/utils/ostypes.py +0 -0
  123. {ominfra-0.0.0.dev157 → ominfra-0.0.0.dev159}/ominfra/supervisor/utils/signals.py +0 -0
  124. {ominfra-0.0.0.dev157 → ominfra-0.0.0.dev159}/ominfra/supervisor/utils/strings.py +0 -0
  125. {ominfra-0.0.0.dev157 → ominfra-0.0.0.dev159}/ominfra/supervisor/utils/users.py +0 -0
  126. {ominfra-0.0.0.dev157 → ominfra-0.0.0.dev159}/ominfra/tailscale/__init__.py +0 -0
  127. {ominfra-0.0.0.dev157 → ominfra-0.0.0.dev159}/ominfra/tailscale/api.py +0 -0
  128. {ominfra-0.0.0.dev157 → ominfra-0.0.0.dev159}/ominfra/tailscale/cli.py +0 -0
  129. {ominfra-0.0.0.dev157 → ominfra-0.0.0.dev159}/ominfra/threadworkers.py +0 -0
  130. {ominfra-0.0.0.dev157 → ominfra-0.0.0.dev159}/ominfra/tools/__init__.py +0 -0
  131. {ominfra-0.0.0.dev157 → ominfra-0.0.0.dev159}/ominfra/tools/listresources.py +0 -0
  132. {ominfra-0.0.0.dev157 → ominfra-0.0.0.dev159}/ominfra.egg-info/dependency_links.txt +0 -0
  133. {ominfra-0.0.0.dev157 → ominfra-0.0.0.dev159}/ominfra.egg-info/entry_points.txt +0 -0
  134. {ominfra-0.0.0.dev157 → ominfra-0.0.0.dev159}/ominfra.egg-info/top_level.txt +0 -0
  135. {ominfra-0.0.0.dev157 → ominfra-0.0.0.dev159}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ominfra
3
- Version: 0.0.0.dev157
3
+ Version: 0.0.0.dev159
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.dev157
16
- Requires-Dist: omlish==0.0.0.dev157
15
+ Requires-Dist: omdev==0.0.0.dev159
16
+ Requires-Dist: omlish==0.0.0.dev159
17
17
  Provides-Extra: all
18
18
  Requires-Dist: paramiko~=3.5; extra == "all"
19
19
  Requires-Dist: asyncssh~=2.18; extra == "all"
@@ -5,7 +5,7 @@ import dataclasses as dc
5
5
  import os.path
6
6
  import sys
7
7
 
8
- from omlish.lite.logs import configure_standard_logging
8
+ from omlish.logs.standard import configure_standard_logging
9
9
 
10
10
  from ....configs import read_config_file
11
11
  from .driver import JournalctlToAwsDriver
@@ -410,8 +410,8 @@ import typing as ta
410
410
  from omlish.lite.cached import cached_nullary
411
411
  from omlish.lite.check import check
412
412
  from omlish.lite.logs import log
413
- from omlish.lite.subprocesses import subprocess_close
414
- from omlish.lite.subprocesses import subprocess_shell_wrap_exec
413
+ from omlish.subprocesses import subprocess_close
414
+ from omlish.subprocesses import subprocess_shell_wrap_exec
415
415
 
416
416
  from ..threadworkers import ThreadWorker
417
417
  from .messages import JournalctlMessage # noqa
@@ -1,7 +1,7 @@
1
1
  # ruff: noqa: UP006 UP007
2
2
  from omlish.lite.inject import Injector
3
3
  from omlish.lite.inject import inj
4
- from omlish.lite.logs import configure_standard_logging
4
+ from omlish.logs.standard import configure_standard_logging
5
5
 
6
6
  from .bootstrap import MainBootstrap
7
7
  from .inject import bind_main
@@ -6,11 +6,11 @@ import subprocess
6
6
  import time
7
7
  import typing as ta
8
8
 
9
- from omlish.lite.asyncio.subprocesses import asyncio_subprocesses
9
+ from omlish.asyncs.asyncio.subprocesses import asyncio_subprocesses
10
10
  from omlish.lite.check import check
11
- from omlish.lite.subprocesses import SUBPROCESS_CHANNEL_OPTION_VALUES
12
- from omlish.lite.subprocesses import SubprocessChannelOption
13
- from omlish.lite.subprocesses import subprocess_maybe_shell_wrap_exec
11
+ from omlish.subprocesses import SUBPROCESS_CHANNEL_OPTION_VALUES
12
+ from omlish.subprocesses import SubprocessChannelOption
13
+ from omlish.subprocesses import subprocess_maybe_shell_wrap_exec
14
14
 
15
15
  from .base import Command
16
16
  from .base import CommandExecutor
@@ -3,14 +3,16 @@ import datetime
3
3
  import os.path
4
4
  import typing as ta
5
5
 
6
+ from omlish.lite.cached import cached_nullary
7
+ from omlish.lite.check import check
8
+
6
9
  from .git import DeployGitManager
7
- from .git import DeployGitRepo
8
- from .git import DeployGitSpec
9
10
  from .paths import DeployPath
10
11
  from .paths import DeployPathOwner
11
- from .types import DeployApp
12
+ from .specs import DeploySpec
12
13
  from .types import DeployAppTag
13
14
  from .types import DeployHome
15
+ from .types import DeployKey
14
16
  from .types import DeployRev
15
17
  from .types import DeployTag
16
18
  from .venvs import DeployVenvManager
@@ -18,20 +20,22 @@ from .venvs import DeployVenvManager
18
20
 
19
21
  def make_deploy_tag(
20
22
  rev: DeployRev,
21
- now: ta.Optional[datetime.datetime] = None,
23
+ key: DeployKey,
24
+ *,
25
+ utcnow: ta.Optional[datetime.datetime] = None,
22
26
  ) -> DeployTag:
23
- if now is None:
24
- now = datetime.datetime.utcnow() # noqa
25
- now_fmt = '%Y%m%dT%H%M%S'
26
- now_str = now.strftime(now_fmt)
27
- return DeployTag('-'.join([rev, now_str]))
27
+ if utcnow is None:
28
+ utcnow = datetime.datetime.now(tz=datetime.timezone.utc) # noqa
29
+ now_fmt = '%Y%m%dT%H%M%SZ'
30
+ now_str = utcnow.strftime(now_fmt)
31
+ return DeployTag('-'.join([now_str, rev, key]))
28
32
 
29
33
 
30
34
  class DeployAppManager(DeployPathOwner):
31
35
  def __init__(
32
36
  self,
33
37
  *,
34
- deploy_home: DeployHome,
38
+ deploy_home: ta.Optional[DeployHome] = None,
35
39
  git: DeployGitManager,
36
40
  venvs: DeployVenvManager,
37
41
  ) -> None:
@@ -41,29 +45,27 @@ class DeployAppManager(DeployPathOwner):
41
45
  self._git = git
42
46
  self._venvs = venvs
43
47
 
44
- self._dir = os.path.join(deploy_home, 'apps')
48
+ @cached_nullary
49
+ def _dir(self) -> str:
50
+ return os.path.join(check.non_empty_str(self._deploy_home), 'apps')
45
51
 
46
- def get_deploy_paths(self) -> ta.AbstractSet[DeployPath]:
52
+ def get_owned_deploy_paths(self) -> ta.AbstractSet[DeployPath]:
47
53
  return {
48
54
  DeployPath.parse('apps/@app/@tag'),
49
55
  }
50
56
 
51
57
  async def prepare_app(
52
58
  self,
53
- app: DeployApp,
54
- rev: DeployRev,
55
- repo: DeployGitRepo,
59
+ spec: DeploySpec,
56
60
  ):
57
- app_tag = DeployAppTag(app, make_deploy_tag(rev))
58
- app_dir = os.path.join(self._dir, app, app_tag.tag)
61
+ app_tag = DeployAppTag(spec.app, make_deploy_tag(spec.rev, spec.key()))
62
+ app_dir = os.path.join(self._dir(), spec.app, app_tag.tag)
59
63
 
60
64
  #
61
65
 
62
66
  await self._git.checkout(
63
- DeployGitSpec(
64
- repo=repo,
65
- rev=rev,
66
- ),
67
+ spec.repo,
68
+ spec.rev,
67
69
  app_dir,
68
70
  )
69
71
 
@@ -0,0 +1,207 @@
1
+ # ruff: noqa: UP006 UP007
2
+ import abc
3
+ import os
4
+ import shutil
5
+ import tempfile
6
+ import typing as ta
7
+
8
+ from omlish.lite.check import check
9
+ from omlish.lite.strings import attr_repr
10
+
11
+
12
+ DeployAtomicPathSwapKind = ta.Literal['dir', 'file']
13
+ DeployAtomicPathSwapState = ta.Literal['open', 'committed', 'aborted'] # ta.TypeAlias
14
+
15
+
16
+ ##
17
+
18
+
19
+ class DeployAtomicPathSwap(abc.ABC):
20
+ def __init__(
21
+ self,
22
+ kind: DeployAtomicPathSwapKind,
23
+ dst_path: str,
24
+ *,
25
+ auto_commit: bool = False,
26
+ ) -> None:
27
+ super().__init__()
28
+
29
+ self._kind = kind
30
+ self._dst_path = dst_path
31
+ self._auto_commit = auto_commit
32
+
33
+ self._state: DeployAtomicPathSwapState = 'open'
34
+
35
+ def __repr__(self) -> str:
36
+ return attr_repr(self, 'kind', 'dst_path', 'tmp_path')
37
+
38
+ @property
39
+ def kind(self) -> DeployAtomicPathSwapKind:
40
+ return self._kind
41
+
42
+ @property
43
+ def dst_path(self) -> str:
44
+ return self._dst_path
45
+
46
+ @property
47
+ @abc.abstractmethod
48
+ def tmp_path(self) -> str:
49
+ raise NotImplementedError
50
+
51
+ #
52
+
53
+ @property
54
+ def state(self) -> DeployAtomicPathSwapState:
55
+ return self._state
56
+
57
+ def _check_state(self, *states: DeployAtomicPathSwapState) -> None:
58
+ if self._state not in states:
59
+ raise RuntimeError(f'Atomic path swap not in correct state: {self._state}, {states}')
60
+
61
+ #
62
+
63
+ @abc.abstractmethod
64
+ def _commit(self) -> None:
65
+ raise NotImplementedError
66
+
67
+ def commit(self) -> None:
68
+ if self._state == 'committed':
69
+ return
70
+ self._check_state('open')
71
+ try:
72
+ self._commit()
73
+ except Exception: # noqa
74
+ self._abort()
75
+ raise
76
+ else:
77
+ self._state = 'committed'
78
+
79
+ #
80
+
81
+ @abc.abstractmethod
82
+ def _abort(self) -> None:
83
+ raise NotImplementedError
84
+
85
+ def abort(self) -> None:
86
+ if self._state == 'aborted':
87
+ return
88
+ self._abort()
89
+ self._state = 'aborted'
90
+
91
+ #
92
+
93
+ def __enter__(self) -> 'DeployAtomicPathSwap':
94
+ return self
95
+
96
+ def __exit__(self, exc_type, exc_val, exc_tb):
97
+ if (
98
+ exc_type is None and
99
+ self._auto_commit and
100
+ self._state == 'open'
101
+ ):
102
+ self.commit()
103
+ else:
104
+ self.abort()
105
+
106
+
107
+ #
108
+
109
+
110
+ class DeployAtomicPathSwapping(abc.ABC):
111
+ @abc.abstractmethod
112
+ def begin_atomic_path_swap(
113
+ self,
114
+ kind: DeployAtomicPathSwapKind,
115
+ dst_path: str,
116
+ *,
117
+ name_hint: ta.Optional[str] = None,
118
+ make_dirs: bool = False,
119
+ **kwargs: ta.Any,
120
+ ) -> DeployAtomicPathSwap:
121
+ raise NotImplementedError
122
+
123
+
124
+ ##
125
+
126
+
127
+ class OsRenameDeployAtomicPathSwap(DeployAtomicPathSwap):
128
+ def __init__(
129
+ self,
130
+ kind: DeployAtomicPathSwapKind,
131
+ dst_path: str,
132
+ tmp_path: str,
133
+ **kwargs: ta.Any,
134
+ ) -> None:
135
+ if kind == 'dir':
136
+ check.state(os.path.isdir(tmp_path))
137
+ elif kind == 'file':
138
+ check.state(os.path.isfile(tmp_path))
139
+ else:
140
+ raise TypeError(kind)
141
+
142
+ super().__init__(
143
+ kind,
144
+ dst_path,
145
+ **kwargs,
146
+ )
147
+
148
+ self._tmp_path = tmp_path
149
+
150
+ @property
151
+ def tmp_path(self) -> str:
152
+ return self._tmp_path
153
+
154
+ def _commit(self) -> None:
155
+ os.rename(self._tmp_path, self._dst_path)
156
+
157
+ def _abort(self) -> None:
158
+ shutil.rmtree(self._tmp_path, ignore_errors=True)
159
+
160
+
161
+ class TempDirDeployAtomicPathSwapping(DeployAtomicPathSwapping):
162
+ def __init__(
163
+ self,
164
+ *,
165
+ temp_dir: ta.Optional[str] = None,
166
+ root_dir: ta.Optional[str] = None,
167
+ ) -> None:
168
+ super().__init__()
169
+
170
+ if root_dir is not None:
171
+ root_dir = os.path.abspath(root_dir)
172
+ self._root_dir = root_dir
173
+ self._temp_dir = temp_dir
174
+
175
+ def begin_atomic_path_swap(
176
+ self,
177
+ kind: DeployAtomicPathSwapKind,
178
+ dst_path: str,
179
+ *,
180
+ name_hint: ta.Optional[str] = None,
181
+ make_dirs: bool = False,
182
+ **kwargs: ta.Any,
183
+ ) -> DeployAtomicPathSwap:
184
+ dst_path = os.path.abspath(dst_path)
185
+ if self._root_dir is not None and not dst_path.startswith(check.non_empty_str(self._root_dir)):
186
+ raise RuntimeError(f'Atomic path swap dst must be in root dir: {dst_path}, {self._root_dir}')
187
+
188
+ dst_dir = os.path.dirname(dst_path)
189
+ if make_dirs:
190
+ os.makedirs(dst_dir, exist_ok=True)
191
+ if not os.path.isdir(dst_dir):
192
+ raise RuntimeError(f'Atomic path swap dst dir does not exist: {dst_dir}')
193
+
194
+ if kind == 'dir':
195
+ tmp_path = tempfile.mkdtemp(prefix=name_hint, dir=self._temp_dir)
196
+ elif kind == 'file':
197
+ fd, tmp_path = tempfile.mkstemp(prefix=name_hint, dir=self._temp_dir)
198
+ os.close(fd)
199
+ else:
200
+ raise TypeError(kind)
201
+
202
+ return OsRenameDeployAtomicPathSwap(
203
+ kind,
204
+ dst_path,
205
+ tmp_path,
206
+ **kwargs,
207
+ )
@@ -3,6 +3,9 @@ import dataclasses as dc
3
3
  import typing as ta
4
4
 
5
5
 
6
+ ##
7
+
8
+
6
9
  @dc.dataclass(frozen=True)
7
10
  class DeployConfig:
8
11
  deploy_home: ta.Optional[str] = None
@@ -8,17 +8,17 @@ git/github.com/wrmsr/omlish <- bootstrap repo
8
8
 
9
9
  github.com/wrmsr/omlish@rev
10
10
  """
11
- import dataclasses as dc
12
11
  import functools
13
12
  import os.path
14
13
  import typing as ta
15
14
 
16
- from omlish.lite.asyncio.subprocesses import asyncio_subprocesses
15
+ from omlish.asyncs.asyncio.subprocesses import asyncio_subprocesses
17
16
  from omlish.lite.cached import async_cached_nullary
18
17
  from omlish.lite.check import check
19
18
 
20
- from .paths import DeployPath
21
- from .paths import DeployPathOwner
19
+ from .atomics import DeployAtomicPathSwapping
20
+ from .paths import SingleDirDeployPathOwner
21
+ from .specs import DeployGitRepo
22
22
  from .types import DeployHome
23
23
  from .types import DeployRev
24
24
 
@@ -26,44 +26,22 @@ from .types import DeployRev
26
26
  ##
27
27
 
28
28
 
29
- @dc.dataclass(frozen=True)
30
- class DeployGitRepo:
31
- host: ta.Optional[str] = None
32
- username: ta.Optional[str] = None
33
- path: ta.Optional[str] = None
34
-
35
- def __post_init__(self) -> None:
36
- check.not_in('..', check.non_empty_str(self.host))
37
- check.not_in('.', check.non_empty_str(self.path))
38
-
39
-
40
- @dc.dataclass(frozen=True)
41
- class DeployGitSpec:
42
- repo: DeployGitRepo
43
- rev: DeployRev
44
-
45
-
46
- ##
47
-
48
-
49
- class DeployGitManager(DeployPathOwner):
29
+ class DeployGitManager(SingleDirDeployPathOwner):
50
30
  def __init__(
51
31
  self,
52
32
  *,
53
- deploy_home: DeployHome,
33
+ deploy_home: ta.Optional[DeployHome] = None,
34
+ atomics: DeployAtomicPathSwapping,
54
35
  ) -> None:
55
- super().__init__()
36
+ super().__init__(
37
+ owned_dir='git',
38
+ deploy_home=deploy_home,
39
+ )
56
40
 
57
- self._deploy_home = deploy_home
58
- self._dir = os.path.join(deploy_home, 'git')
41
+ self._atomics = atomics
59
42
 
60
43
  self._repo_dirs: ta.Dict[DeployGitRepo, DeployGitManager.RepoDir] = {}
61
44
 
62
- def get_deploy_paths(self) -> ta.AbstractSet[DeployPath]:
63
- return {
64
- DeployPath.parse('git'),
65
- }
66
-
67
45
  class RepoDir:
68
46
  def __init__(
69
47
  self,
@@ -75,7 +53,7 @@ class DeployGitManager(DeployPathOwner):
75
53
  self._git = git
76
54
  self._repo = repo
77
55
  self._dir = os.path.join(
78
- self._git._dir, # noqa
56
+ self._git._make_dir(), # noqa
79
57
  check.non_empty_str(repo.host),
80
58
  check.non_empty_str(repo.path),
81
59
  )
@@ -112,18 +90,20 @@ class DeployGitManager(DeployPathOwner):
112
90
 
113
91
  async def checkout(self, rev: DeployRev, dst_dir: str) -> None:
114
92
  check.state(not os.path.exists(dst_dir))
93
+ with self._git._atomics.begin_atomic_path_swap( # noqa
94
+ 'dir',
95
+ dst_dir,
96
+ auto_commit=True,
97
+ make_dirs=True,
98
+ ) as dst_swap:
99
+ await self.fetch(rev)
115
100
 
116
- await self.fetch(rev)
117
-
118
- # FIXME: temp dir swap
119
- os.makedirs(dst_dir)
120
-
121
- dst_call = functools.partial(asyncio_subprocesses.check_call, cwd=dst_dir)
122
- await dst_call('git', 'init')
101
+ dst_call = functools.partial(asyncio_subprocesses.check_call, cwd=dst_swap.tmp_path)
102
+ await dst_call('git', 'init')
123
103
 
124
- await dst_call('git', 'remote', 'add', 'local', self._dir)
125
- await dst_call('git', 'fetch', '--depth=1', 'local', rev)
126
- await dst_call('git', 'checkout', rev)
104
+ await dst_call('git', 'remote', 'add', 'local', self._dir)
105
+ await dst_call('git', 'fetch', '--depth=1', 'local', rev)
106
+ await dst_call('git', 'checkout', rev)
127
107
 
128
108
  def get_repo_dir(self, repo: DeployGitRepo) -> RepoDir:
129
109
  try:
@@ -132,5 +112,5 @@ class DeployGitManager(DeployPathOwner):
132
112
  repo_dir = self._repo_dirs[repo] = DeployGitManager.RepoDir(self, repo)
133
113
  return repo_dir
134
114
 
135
- async def checkout(self, spec: DeployGitSpec, dst_dir: str) -> None:
136
- await self.get_repo_dir(spec.repo).checkout(spec.rev, dst_dir)
115
+ async def checkout(self, repo: DeployGitRepo, rev: DeployRev, dst_dir: str) -> None:
116
+ await self.get_repo_dir(repo).checkout(rev, dst_dir)
@@ -8,12 +8,14 @@ from omlish.lite.inject import inj
8
8
 
9
9
  from ..commands.inject import bind_command
10
10
  from .apps import DeployAppManager
11
+ from .atomics import DeployAtomicPathSwapping
11
12
  from .commands import DeployCommand
12
13
  from .commands import DeployCommandExecutor
13
14
  from .config import DeployConfig
14
15
  from .git import DeployGitManager
15
16
  from .interp import InterpCommand
16
17
  from .interp import InterpCommandExecutor
18
+ from .tmp import DeployTmpManager
17
19
  from .types import DeployHome
18
20
  from .venvs import DeployVenvManager
19
21
 
@@ -25,10 +27,19 @@ def bind_deploy(
25
27
  lst: ta.List[InjectorBindingOrBindings] = [
26
28
  inj.bind(deploy_config),
27
29
 
30
+ #
31
+
28
32
  inj.bind(DeployAppManager, singleton=True),
33
+
29
34
  inj.bind(DeployGitManager, singleton=True),
35
+
36
+ inj.bind(DeployTmpManager, singleton=True),
37
+ inj.bind(DeployAtomicPathSwapping, to_key=DeployTmpManager),
38
+
30
39
  inj.bind(DeployVenvManager, singleton=True),
31
40
 
41
+ #
42
+
32
43
  bind_command(DeployCommand, DeployCommandExecutor),
33
44
  bind_command(InterpCommand, InterpCommandExecutor),
34
45
  ]