ominfra 0.0.0.dev154__py3-none-any.whl → 0.0.0.dev156__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. ominfra/manage/bootstrap.py +4 -0
  2. ominfra/manage/bootstrap_.py +5 -0
  3. ominfra/manage/commands/inject.py +8 -11
  4. ominfra/manage/commands/{execution.py → local.py} +1 -5
  5. ominfra/manage/commands/ping.py +23 -0
  6. ominfra/manage/commands/subprocess.py +3 -4
  7. ominfra/manage/commands/types.py +8 -0
  8. ominfra/manage/deploy/apps.py +72 -0
  9. ominfra/manage/deploy/config.py +8 -0
  10. ominfra/manage/deploy/git.py +136 -0
  11. ominfra/manage/deploy/inject.py +21 -0
  12. ominfra/manage/deploy/paths.py +81 -28
  13. ominfra/manage/deploy/types.py +13 -0
  14. ominfra/manage/deploy/venvs.py +66 -0
  15. ominfra/manage/inject.py +20 -4
  16. ominfra/manage/main.py +15 -27
  17. ominfra/manage/remote/_main.py +1 -1
  18. ominfra/manage/remote/config.py +0 -2
  19. ominfra/manage/remote/connection.py +7 -24
  20. ominfra/manage/remote/execution.py +1 -1
  21. ominfra/manage/remote/inject.py +3 -14
  22. ominfra/manage/remote/spawning.py +2 -2
  23. ominfra/manage/system/commands.py +22 -2
  24. ominfra/manage/system/config.py +3 -1
  25. ominfra/manage/system/inject.py +16 -6
  26. ominfra/manage/system/packages.py +38 -14
  27. ominfra/manage/system/platforms.py +72 -0
  28. ominfra/manage/targets/__init__.py +0 -0
  29. ominfra/manage/targets/connection.py +150 -0
  30. ominfra/manage/targets/inject.py +42 -0
  31. ominfra/manage/targets/targets.py +87 -0
  32. ominfra/scripts/journald2aws.py +205 -134
  33. ominfra/scripts/manage.py +2192 -734
  34. ominfra/scripts/supervisor.py +187 -25
  35. ominfra/supervisor/configs.py +163 -18
  36. {ominfra-0.0.0.dev154.dist-info → ominfra-0.0.0.dev156.dist-info}/METADATA +3 -3
  37. {ominfra-0.0.0.dev154.dist-info → ominfra-0.0.0.dev156.dist-info}/RECORD +42 -31
  38. ominfra/manage/system/types.py +0 -5
  39. /ominfra/manage/{commands → deploy}/interp.py +0 -0
  40. {ominfra-0.0.0.dev154.dist-info → ominfra-0.0.0.dev156.dist-info}/LICENSE +0 -0
  41. {ominfra-0.0.0.dev154.dist-info → ominfra-0.0.0.dev156.dist-info}/WHEEL +0 -0
  42. {ominfra-0.0.0.dev154.dist-info → ominfra-0.0.0.dev156.dist-info}/entry_points.txt +0 -0
  43. {ominfra-0.0.0.dev154.dist-info → ominfra-0.0.0.dev156.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,66 @@
1
+ # ruff: noqa: UP006 UP007
2
+ """
3
+ TODO:
4
+ - interp
5
+ - share more code with pyproject?
6
+ """
7
+ import os.path
8
+ import typing as ta
9
+
10
+ from omlish.lite.asyncio.subprocesses import asyncio_subprocesses
11
+
12
+ from .paths import DeployPath
13
+ from .paths import DeployPathOwner
14
+ from .types import DeployAppTag
15
+ from .types import DeployHome
16
+
17
+
18
+ class DeployVenvManager(DeployPathOwner):
19
+ def __init__(
20
+ self,
21
+ *,
22
+ deploy_home: DeployHome,
23
+ ) -> None:
24
+ super().__init__()
25
+
26
+ self._deploy_home = deploy_home
27
+ self._dir = os.path.join(deploy_home, 'venvs')
28
+
29
+ def get_deploy_paths(self) -> ta.AbstractSet[DeployPath]:
30
+ return {
31
+ DeployPath.parse('venvs/@app/@tag/'),
32
+ }
33
+
34
+ async def setup_venv(
35
+ self,
36
+ app_dir: str,
37
+ venv_dir: str,
38
+ *,
39
+ use_uv: bool = True,
40
+ ) -> None:
41
+ sys_exe = 'python3'
42
+
43
+ await asyncio_subprocesses.check_call(sys_exe, '-m', 'venv', venv_dir)
44
+
45
+ #
46
+
47
+ venv_exe = os.path.join(venv_dir, 'bin', 'python3')
48
+
49
+ #
50
+
51
+ reqs_txt = os.path.join(app_dir, 'requirements.txt')
52
+
53
+ if os.path.isfile(reqs_txt):
54
+ if use_uv:
55
+ await asyncio_subprocesses.check_call(venv_exe, '-m', 'pip', 'install', 'uv')
56
+ pip_cmd = ['-m', 'uv', 'pip']
57
+ else:
58
+ pip_cmd = ['-m', 'pip']
59
+
60
+ await asyncio_subprocesses.check_call(venv_exe, *pip_cmd,'install', '-r', reqs_txt)
61
+
62
+ async def setup_app_venv(self, app_tag: DeployAppTag) -> None:
63
+ await self.setup_venv(
64
+ os.path.join(self._deploy_home, 'apps', app_tag.app, app_tag.tag),
65
+ os.path.join(self._deploy_home, 'venvs', app_tag.app, app_tag.tag),
66
+ )
ominfra/manage/inject.py CHANGED
@@ -6,8 +6,10 @@ from omlish.lite.inject import InjectorBindings
6
6
  from omlish.lite.inject import inj
7
7
  from omlish.lite.marshal import ObjMarshalerManager
8
8
 
9
+ from .bootstrap import MainBootstrap
9
10
  from .commands.inject import bind_commands
10
11
  from .config import MainConfig
12
+ from .deploy.config import DeployConfig
11
13
  from .deploy.inject import bind_deploy
12
14
  from .marshal import ObjMarshalerInstaller
13
15
  from .marshal import ObjMarshalerInstallers
@@ -15,6 +17,7 @@ from .remote.config import RemoteConfig
15
17
  from .remote.inject import bind_remote
16
18
  from .system.config import SystemConfig
17
19
  from .system.inject import bind_system
20
+ from .targets.inject import bind_targets
18
21
 
19
22
 
20
23
  ##
@@ -22,9 +25,13 @@ from .system.inject import bind_system
22
25
 
23
26
  def bind_main(
24
27
  *,
25
- main_config: MainConfig,
26
- remote_config: RemoteConfig,
27
- system_config: SystemConfig,
28
+ main_config: MainConfig = MainConfig(),
29
+
30
+ deploy_config: DeployConfig = DeployConfig(),
31
+ remote_config: RemoteConfig = RemoteConfig(),
32
+ system_config: SystemConfig = SystemConfig(),
33
+
34
+ main_bootstrap: ta.Optional[MainBootstrap] = None,
28
35
  ) -> InjectorBindings:
29
36
  lst: ta.List[InjectorBindingOrBindings] = [
30
37
  inj.bind(main_config),
@@ -33,7 +40,9 @@ def bind_main(
33
40
  main_config=main_config,
34
41
  ),
35
42
 
36
- bind_deploy(),
43
+ bind_deploy(
44
+ deploy_config=deploy_config,
45
+ ),
37
46
 
38
47
  bind_remote(
39
48
  remote_config=remote_config,
@@ -42,10 +51,17 @@ def bind_main(
42
51
  bind_system(
43
52
  system_config=system_config,
44
53
  ),
54
+
55
+ bind_targets(),
45
56
  ]
46
57
 
47
58
  #
48
59
 
60
+ if main_bootstrap is not None:
61
+ lst.append(inj.bind(main_bootstrap))
62
+
63
+ #
64
+
49
65
  def build_obj_marshaler_manager(insts: ObjMarshalerInstallers) -> ObjMarshalerManager:
50
66
  msh = ObjMarshalerManager()
51
67
  inst: ObjMarshalerInstaller
ominfra/manage/main.py CHANGED
@@ -6,7 +6,6 @@ manage.py -s 'docker run -i python:3.12'
6
6
  manage.py -s 'ssh -i /foo/bar.pem foo@bar.baz' -q --python=python3.8
7
7
  """
8
8
  import asyncio
9
- import contextlib
10
9
  import json
11
10
  import sys
12
11
  import typing as ta
@@ -22,22 +21,17 @@ from omlish.lite.pycharm import PycharmRemoteDebug
22
21
  from .bootstrap import MainBootstrap
23
22
  from .bootstrap_ import main_bootstrap
24
23
  from .commands.base import Command
25
- from .commands.base import CommandExecutor
26
- from .commands.execution import LocalCommandExecutor
27
24
  from .config import MainConfig
25
+ from .deploy.config import DeployConfig
28
26
  from .remote.config import RemoteConfig
29
- from .remote.connection import RemoteExecutionConnector
30
- from .remote.spawning import RemoteSpawning
27
+ from .targets.connection import ManageTargetConnector
28
+ from .targets.targets import ManageTarget
31
29
 
32
30
 
33
31
  class MainCli(ArgparseCli):
34
32
  @argparse_command(
35
33
  argparse_arg('--_payload-file'),
36
34
 
37
- argparse_arg('-s', '--shell'),
38
- argparse_arg('-q', '--shell-quote', action='store_true'),
39
- argparse_arg('--python', default='python3'),
40
-
41
35
  argparse_arg('--pycharm-debug-port', type=int),
42
36
  argparse_arg('--pycharm-debug-host'),
43
37
  argparse_arg('--pycharm-debug-version'),
@@ -46,8 +40,9 @@ class MainCli(ArgparseCli):
46
40
 
47
41
  argparse_arg('--debug', action='store_true'),
48
42
 
49
- argparse_arg('--local', action='store_true'),
43
+ argparse_arg('--deploy-home'),
50
44
 
45
+ argparse_arg('target'),
51
46
  argparse_arg('command', nargs='+'),
52
47
  )
53
48
  async def run(self) -> None:
@@ -58,6 +53,10 @@ class MainCli(ArgparseCli):
58
53
  debug=bool(self.args.debug),
59
54
  ),
60
55
 
56
+ deploy_config=DeployConfig(
57
+ deploy_home=self.args.deploy_home,
58
+ ),
59
+
61
60
  remote_config=RemoteConfig(
62
61
  payload_file=self.args._payload_file, # noqa
63
62
 
@@ -68,8 +67,6 @@ class MainCli(ArgparseCli):
68
67
  ) if self.args.pycharm_debug_port is not None else None,
69
68
 
70
69
  timebomb_delay_s=self.args.remote_timebomb_delay_s,
71
-
72
- use_in_process_remote_executor=True,
73
70
  ),
74
71
  )
75
72
 
@@ -83,6 +80,11 @@ class MainCli(ArgparseCli):
83
80
 
84
81
  msh = injector[ObjMarshalerManager]
85
82
 
83
+ ts = self.args.target
84
+ if not ts.startswith('{'):
85
+ ts = json.dumps({ts: {}})
86
+ tgt: ManageTarget = msh.unmarshal_obj(json.loads(ts), ManageTarget)
87
+
86
88
  cmds: ta.List[Command] = []
87
89
  cmd: Command
88
90
  for c in self.args.command:
@@ -93,21 +95,7 @@ class MainCli(ArgparseCli):
93
95
 
94
96
  #
95
97
 
96
- async with contextlib.AsyncExitStack() as es:
97
- ce: CommandExecutor
98
-
99
- if self.args.local:
100
- ce = injector[LocalCommandExecutor]
101
-
102
- else:
103
- tgt = RemoteSpawning.Target(
104
- shell=self.args.shell,
105
- shell_quote=self.args.shell_quote,
106
- python=self.args.python,
107
- )
108
-
109
- ce = await es.enter_async_context(injector[RemoteExecutionConnector].connect(tgt, bs)) # noqa
110
-
98
+ async with injector[ManageTargetConnector].connect(tgt) as ce:
111
99
  async def run_command(cmd: Command) -> None:
112
100
  res = await ce.try_execute(
113
101
  cmd,
@@ -20,7 +20,7 @@ from omlish.os.deathsig import set_process_deathsig
20
20
 
21
21
  from ...pyremote import pyremote_bootstrap_finalize
22
22
  from ..bootstrap import MainBootstrap
23
- from ..commands.execution import LocalCommandExecutor
23
+ from ..commands.local import LocalCommandExecutor
24
24
  from .channel import RemoteChannel
25
25
  from .channel import RemoteChannelImpl
26
26
  from .execution import _RemoteCommandHandler
@@ -20,5 +20,3 @@ class RemoteConfig:
20
20
  timebomb_delay_s: ta.Optional[float] = 60 * 60.
21
21
 
22
22
  heartbeat_interval_s: float = 3.
23
-
24
- use_in_process_remote_executor: bool = False
@@ -1,18 +1,18 @@
1
1
  # ruff: noqa: UP006 UP007
2
- import abc
3
2
  import asyncio
4
3
  import contextlib
5
4
  import typing as ta
6
5
 
7
6
  from omlish.asyncs.asyncio.channels import asyncio_create_bytes_channel
8
7
  from omlish.lite.cached import cached_nullary
8
+ from omlish.lite.contextmanagers import aclosing
9
9
  from omlish.lite.marshal import ObjMarshalerManager
10
10
 
11
11
  from ...pyremote import PyremoteBootstrapDriver
12
12
  from ...pyremote import PyremoteBootstrapOptions
13
13
  from ...pyremote import pyremote_build_bootstrap_cmd
14
14
  from ..bootstrap import MainBootstrap
15
- from ..commands.execution import LocalCommandExecutor
15
+ from ..commands.local import LocalCommandExecutor
16
16
  from ._main import _remote_execution_main # noqa
17
17
  from .channel import RemoteChannelImpl
18
18
  from .execution import RemoteCommandExecutor
@@ -25,20 +25,7 @@ from .spawning import RemoteSpawning
25
25
  ##
26
26
 
27
27
 
28
- class RemoteExecutionConnector(abc.ABC):
29
- @abc.abstractmethod
30
- def connect(
31
- self,
32
- tgt: RemoteSpawning.Target,
33
- bs: MainBootstrap,
34
- ) -> ta.AsyncContextManager[RemoteCommandExecutor]:
35
- raise NotImplementedError
36
-
37
-
38
- ##
39
-
40
-
41
- class PyremoteRemoteExecutionConnector(RemoteExecutionConnector):
28
+ class PyremoteRemoteExecutionConnector:
42
29
  def __init__(
43
30
  self,
44
31
  *,
@@ -104,7 +91,7 @@ class PyremoteRemoteExecutionConnector(RemoteExecutionConnector):
104
91
  await chan.send_obj(bs)
105
92
 
106
93
  rce: RemoteCommandExecutor
107
- async with contextlib.aclosing(RemoteCommandExecutor(chan)) as rce:
94
+ async with aclosing(RemoteCommandExecutor(chan)) as rce:
108
95
  await rce.start()
109
96
 
110
97
  yield rce
@@ -113,7 +100,7 @@ class PyremoteRemoteExecutionConnector(RemoteExecutionConnector):
113
100
  ##
114
101
 
115
102
 
116
- class InProcessRemoteExecutionConnector(RemoteExecutionConnector):
103
+ class InProcessRemoteExecutionConnector:
117
104
  def __init__(
118
105
  self,
119
106
  *,
@@ -126,11 +113,7 @@ class InProcessRemoteExecutionConnector(RemoteExecutionConnector):
126
113
  self._local_executor = local_executor
127
114
 
128
115
  @contextlib.asynccontextmanager
129
- async def connect(
130
- self,
131
- tgt: RemoteSpawning.Target,
132
- bs: MainBootstrap,
133
- ) -> ta.AsyncGenerator[RemoteCommandExecutor, None]:
116
+ async def connect(self) -> ta.AsyncGenerator[RemoteCommandExecutor, None]:
134
117
  r0, w0 = asyncio_create_bytes_channel()
135
118
  r1, w1 = asyncio_create_bytes_channel()
136
119
 
@@ -144,7 +127,7 @@ class InProcessRemoteExecutionConnector(RemoteExecutionConnector):
144
127
  rch_task = asyncio.create_task(rch.run()) # noqa
145
128
  try:
146
129
  rce: RemoteCommandExecutor
147
- async with contextlib.aclosing(RemoteCommandExecutor(local_chan)) as rce:
130
+ async with aclosing(RemoteCommandExecutor(local_chan)) as rce:
148
131
  await rce.start()
149
132
 
150
133
  yield rce
@@ -394,7 +394,7 @@ class RemoteCommandExecutor(CommandExecutor):
394
394
  self,
395
395
  cmd: Command,
396
396
  *,
397
- log: ta.Optional[logging.Logger] = None,
397
+ log: ta.Optional[logging.Logger] = None, # noqa
398
398
  omit_exc_object: bool = False,
399
399
  ) -> CommandOutputOrException:
400
400
  try:
@@ -8,7 +8,6 @@ from omlish.lite.inject import inj
8
8
  from .config import RemoteConfig
9
9
  from .connection import InProcessRemoteExecutionConnector
10
10
  from .connection import PyremoteRemoteExecutionConnector
11
- from .connection import RemoteExecutionConnector
12
11
  from .payload import RemoteExecutionPayloadFile
13
12
  from .spawning import RemoteSpawning
14
13
  from .spawning import SubprocessRemoteSpawning
@@ -23,20 +22,10 @@ def bind_remote(
23
22
 
24
23
  inj.bind(SubprocessRemoteSpawning, singleton=True),
25
24
  inj.bind(RemoteSpawning, to_key=SubprocessRemoteSpawning),
26
- ]
27
-
28
- #
29
25
 
30
- if remote_config.use_in_process_remote_executor:
31
- lst.extend([
32
- inj.bind(InProcessRemoteExecutionConnector, singleton=True),
33
- inj.bind(RemoteExecutionConnector, to_key=InProcessRemoteExecutionConnector),
34
- ])
35
- else:
36
- lst.extend([
37
- inj.bind(PyremoteRemoteExecutionConnector, singleton=True),
38
- inj.bind(RemoteExecutionConnector, to_key=PyremoteRemoteExecutionConnector),
39
- ])
26
+ inj.bind(PyremoteRemoteExecutionConnector, singleton=True),
27
+ inj.bind(InProcessRemoteExecutionConnector, singleton=True),
28
+ ]
40
29
 
41
30
  #
42
31
 
@@ -7,7 +7,7 @@ import shlex
7
7
  import subprocess
8
8
  import typing as ta
9
9
 
10
- from omlish.lite.asyncio.subprocesses import asyncio_subprocess_popen
10
+ from omlish.lite.asyncio.subprocesses import asyncio_subprocesses
11
11
  from omlish.lite.check import check
12
12
  from omlish.lite.subprocesses import SUBPROCESS_CHANNEL_OPTION_VALUES
13
13
  from omlish.lite.subprocesses import SubprocessChannelOption
@@ -86,7 +86,7 @@ class SubprocessRemoteSpawning(RemoteSpawning):
86
86
  if not debug:
87
87
  cmd = subprocess_maybe_shell_wrap_exec(*cmd)
88
88
 
89
- async with asyncio_subprocess_popen(
89
+ async with asyncio_subprocesses.popen(
90
90
  *cmd,
91
91
  shell=pc.shell,
92
92
  stdin=subprocess.PIPE,
@@ -1,10 +1,14 @@
1
1
  # ruff: noqa: UP006 UP007
2
2
  import dataclasses as dc
3
+ import typing as ta
3
4
 
5
+ from omlish.lite.check import check
4
6
  from omlish.lite.logs import log
5
7
 
6
8
  from ..commands.base import Command
7
9
  from ..commands.base import CommandExecutor
10
+ from .packages import SystemPackage
11
+ from .packages import SystemPackageManager
8
12
 
9
13
 
10
14
  ##
@@ -12,13 +16,29 @@ from ..commands.base import CommandExecutor
12
16
 
13
17
  @dc.dataclass(frozen=True)
14
18
  class CheckSystemPackageCommand(Command['CheckSystemPackageCommand.Output']):
19
+ pkgs: ta.Sequence[str] = ()
20
+
21
+ def __post_init__(self) -> None:
22
+ check.not_isinstance(self.pkgs, str)
23
+
15
24
  @dc.dataclass(frozen=True)
16
25
  class Output(Command.Output):
17
- pass
26
+ pkgs: ta.Sequence[SystemPackage]
18
27
 
19
28
 
20
29
  class CheckSystemPackageCommandExecutor(CommandExecutor[CheckSystemPackageCommand, CheckSystemPackageCommand.Output]):
30
+ def __init__(
31
+ self,
32
+ *,
33
+ mgr: SystemPackageManager,
34
+ ) -> None:
35
+ super().__init__()
36
+
37
+ self._mgr = mgr
38
+
21
39
  async def execute(self, cmd: CheckSystemPackageCommand) -> CheckSystemPackageCommand.Output:
22
40
  log.info('Checking system package!')
23
41
 
24
- return CheckSystemPackageCommand.Output()
42
+ ret = await self._mgr.query(*cmd.pkgs)
43
+
44
+ return CheckSystemPackageCommand.Output(list(ret.values()))
@@ -2,7 +2,9 @@
2
2
  import dataclasses as dc
3
3
  import typing as ta
4
4
 
5
+ from .platforms import Platform
6
+
5
7
 
6
8
  @dc.dataclass(frozen=True)
7
9
  class SystemConfig:
8
- platform: ta.Optional[str] = None
10
+ platform: ta.Optional[Platform] = None
@@ -1,5 +1,4 @@
1
1
  # ruff: noqa: UP006 UP007
2
- import sys
3
2
  import typing as ta
4
3
 
5
4
  from omlish.lite.inject import InjectorBindingOrBindings
@@ -13,7 +12,12 @@ from .config import SystemConfig
13
12
  from .packages import AptSystemPackageManager
14
13
  from .packages import BrewSystemPackageManager
15
14
  from .packages import SystemPackageManager
16
- from .types import SystemPlatform
15
+ from .packages import YumSystemPackageManager
16
+ from .platforms import AmazonLinuxPlatform
17
+ from .platforms import DarwinPlatform
18
+ from .platforms import LinuxPlatform
19
+ from .platforms import Platform
20
+ from .platforms import detect_system_platform
17
21
 
18
22
 
19
23
  def bind_system(
@@ -26,18 +30,24 @@ def bind_system(
26
30
 
27
31
  #
28
32
 
29
- platform = system_config.platform or sys.platform
30
- lst.append(inj.bind(platform, key=SystemPlatform))
33
+ platform = system_config.platform or detect_system_platform()
34
+ lst.append(inj.bind(platform, key=Platform))
31
35
 
32
36
  #
33
37
 
34
- if platform == 'linux':
38
+ if isinstance(platform, AmazonLinuxPlatform):
39
+ lst.extend([
40
+ inj.bind(YumSystemPackageManager, singleton=True),
41
+ inj.bind(SystemPackageManager, to_key=YumSystemPackageManager),
42
+ ])
43
+
44
+ elif isinstance(platform, LinuxPlatform):
35
45
  lst.extend([
36
46
  inj.bind(AptSystemPackageManager, singleton=True),
37
47
  inj.bind(SystemPackageManager, to_key=AptSystemPackageManager),
38
48
  ])
39
49
 
40
- elif platform == 'darwin':
50
+ elif isinstance(platform, DarwinPlatform):
41
51
  lst.extend([
42
52
  inj.bind(BrewSystemPackageManager, singleton=True),
43
53
  inj.bind(SystemPackageManager, to_key=BrewSystemPackageManager),
@@ -9,9 +9,7 @@ import json
9
9
  import os
10
10
  import typing as ta
11
11
 
12
- from omlish.lite.asyncio.subprocesses import asyncio_subprocess_check_call
13
- from omlish.lite.asyncio.subprocesses import asyncio_subprocess_check_output
14
- from omlish.lite.asyncio.subprocesses import asyncio_subprocess_run
12
+ from omlish.lite.asyncio.subprocesses import asyncio_subprocesses
15
13
  from omlish.lite.check import check
16
14
 
17
15
 
@@ -44,10 +42,10 @@ class SystemPackageManager(abc.ABC):
44
42
 
45
43
  class BrewSystemPackageManager(SystemPackageManager):
46
44
  async def update(self) -> None:
47
- await asyncio_subprocess_check_call('brew', 'update')
45
+ await asyncio_subprocesses.check_call('brew', 'update')
48
46
 
49
47
  async def upgrade(self) -> None:
50
- await asyncio_subprocess_check_call('brew', 'upgrade')
48
+ await asyncio_subprocesses.check_call('brew', 'upgrade')
51
49
 
52
50
  async def install(self, *packages: SystemPackageOrStr) -> None:
53
51
  es: ta.List[str] = []
@@ -56,11 +54,11 @@ class BrewSystemPackageManager(SystemPackageManager):
56
54
  es.append(p.name + (f'@{p.version}' if p.version is not None else ''))
57
55
  else:
58
56
  es.append(p)
59
- await asyncio_subprocess_check_call('brew', 'install', *es)
57
+ await asyncio_subprocesses.check_call('brew', 'install', *es)
60
58
 
61
59
  async def query(self, *packages: SystemPackageOrStr) -> ta.Mapping[str, SystemPackage]:
62
60
  pns = [p.name if isinstance(p, SystemPackage) else p for p in packages]
63
- o = await asyncio_subprocess_check_output('brew', 'info', '--json', *pns)
61
+ o = await asyncio_subprocesses.check_output('brew', 'info', '--json', *pns)
64
62
  j = json.loads(o.decode())
65
63
  d: ta.Dict[str, SystemPackage] = {}
66
64
  for e in j:
@@ -79,28 +77,54 @@ class AptSystemPackageManager(SystemPackageManager):
79
77
  }
80
78
 
81
79
  async def update(self) -> None:
82
- await asyncio_subprocess_check_call('apt', 'update', env={**os.environ, **self._APT_ENV})
80
+ await asyncio_subprocesses.check_call('sudo', 'apt', 'update', env={**os.environ, **self._APT_ENV})
83
81
 
84
82
  async def upgrade(self) -> None:
85
- await asyncio_subprocess_check_call('apt', 'upgrade', '-y', env={**os.environ, **self._APT_ENV})
83
+ await asyncio_subprocesses.check_call('sudo', 'apt', 'upgrade', '-y', env={**os.environ, **self._APT_ENV})
86
84
 
87
85
  async def install(self, *packages: SystemPackageOrStr) -> None:
88
86
  pns = [p.name if isinstance(p, SystemPackage) else p for p in packages] # FIXME: versions
89
- await asyncio_subprocess_check_call('apt', 'install', '-y', *pns, env={**os.environ, **self._APT_ENV})
87
+ await asyncio_subprocesses.check_call('sudo', 'apt', 'install', '-y', *pns, env={**os.environ, **self._APT_ENV})
90
88
 
91
89
  async def query(self, *packages: SystemPackageOrStr) -> ta.Mapping[str, SystemPackage]:
92
90
  pns = [p.name if isinstance(p, SystemPackage) else p for p in packages]
93
- cmd = ['dpkg-query', '-W', '-f=${Package}=${Version}\n', *pns]
94
- stdout, stderr = await asyncio_subprocess_run(
95
- *cmd,
91
+ out = await asyncio_subprocesses.run(
92
+ 'dpkg-query', '-W', '-f=${Package}=${Version}\n', *pns,
96
93
  capture_output=True,
97
94
  check=False,
98
95
  )
99
96
  d: ta.Dict[str, SystemPackage] = {}
100
- for l in check.not_none(stdout).decode('utf-8').strip().splitlines():
97
+ for l in check.not_none(out.stdout).decode('utf-8').strip().splitlines():
101
98
  n, v = l.split('=', 1)
102
99
  d[n] = SystemPackage(
103
100
  name=n,
104
101
  version=v,
105
102
  )
106
103
  return d
104
+
105
+
106
+ class YumSystemPackageManager(SystemPackageManager):
107
+ async def update(self) -> None:
108
+ await asyncio_subprocesses.check_call('sudo', 'yum', 'check-update')
109
+
110
+ async def upgrade(self) -> None:
111
+ await asyncio_subprocesses.check_call('sudo', 'yum', 'update')
112
+
113
+ async def install(self, *packages: SystemPackageOrStr) -> None:
114
+ pns = [p.name if isinstance(p, SystemPackage) else p for p in packages] # FIXME: versions
115
+ await asyncio_subprocesses.check_call('sudo', 'yum', 'install', *pns)
116
+
117
+ async def query(self, *packages: SystemPackageOrStr) -> ta.Mapping[str, SystemPackage]:
118
+ pns = [p.name if isinstance(p, SystemPackage) else p for p in packages]
119
+ d: ta.Dict[str, SystemPackage] = {}
120
+ for pn in pns:
121
+ out = await asyncio_subprocesses.run(
122
+ 'rpm', '-q', pn,
123
+ capture_output=True,
124
+ )
125
+ if not out.proc.returncode:
126
+ d[pn] = SystemPackage(
127
+ pn,
128
+ check.not_none(out.stdout).decode().strip(),
129
+ )
130
+ return d
@@ -0,0 +1,72 @@
1
+ import abc
2
+ import dataclasses as dc
3
+ import sys
4
+
5
+ from omlish.lite.cached import cached_nullary
6
+ from omlish.lite.logs import log
7
+ from omlish.os.linux import LinuxOsRelease
8
+
9
+
10
+ ##
11
+
12
+
13
+ @dc.dataclass(frozen=True)
14
+ class Platform(abc.ABC): # noqa
15
+ pass
16
+
17
+
18
+ class LinuxPlatform(Platform, abc.ABC):
19
+ pass
20
+
21
+
22
+ class UbuntuPlatform(LinuxPlatform):
23
+ pass
24
+
25
+
26
+ class AmazonLinuxPlatform(LinuxPlatform):
27
+ pass
28
+
29
+
30
+ class GenericLinuxPlatform(LinuxPlatform):
31
+ pass
32
+
33
+
34
+ class DarwinPlatform(Platform):
35
+ pass
36
+
37
+
38
+ class UnknownPlatform(Platform):
39
+ pass
40
+
41
+
42
+ ##
43
+
44
+
45
+ def _detect_system_platform() -> Platform:
46
+ plat = sys.platform
47
+
48
+ if plat == 'linux':
49
+ if (osr := LinuxOsRelease.read()) is None:
50
+ return GenericLinuxPlatform()
51
+
52
+ if osr.id == 'amzn':
53
+ return AmazonLinuxPlatform()
54
+
55
+ elif osr.id == 'ubuntu':
56
+ return UbuntuPlatform()
57
+
58
+ else:
59
+ return GenericLinuxPlatform()
60
+
61
+ elif plat == 'darwin':
62
+ return DarwinPlatform()
63
+
64
+ else:
65
+ return UnknownPlatform()
66
+
67
+
68
+ @cached_nullary
69
+ def detect_system_platform() -> Platform:
70
+ platform = _detect_system_platform()
71
+ log.info('Detected platform: %r', platform)
72
+ return platform
File without changes