ominfra 0.0.0.dev159__py3-none-any.whl → 0.0.0.dev161__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.
@@ -57,15 +57,14 @@ class DeployAppManager(DeployPathOwner):
57
57
  async def prepare_app(
58
58
  self,
59
59
  spec: DeploySpec,
60
- ):
61
- app_tag = DeployAppTag(spec.app, make_deploy_tag(spec.rev, spec.key()))
60
+ ) -> None:
61
+ app_tag = DeployAppTag(spec.app, make_deploy_tag(spec.checkout.rev, spec.key()))
62
62
  app_dir = os.path.join(self._dir(), spec.app, app_tag.tag)
63
63
 
64
64
  #
65
65
 
66
66
  await self._git.checkout(
67
- spec.repo,
68
- spec.rev,
67
+ spec.checkout,
69
68
  app_dir,
70
69
  )
71
70
 
@@ -5,6 +5,8 @@ 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
9
+ from .specs import DeploySpec
8
10
 
9
11
 
10
12
  ##
@@ -12,13 +14,20 @@ from ..commands.base import CommandExecutor
12
14
 
13
15
  @dc.dataclass(frozen=True)
14
16
  class DeployCommand(Command['DeployCommand.Output']):
17
+ spec: DeploySpec
18
+
15
19
  @dc.dataclass(frozen=True)
16
20
  class Output(Command.Output):
17
21
  pass
18
22
 
19
23
 
24
+ @dc.dataclass(frozen=True)
20
25
  class DeployCommandExecutor(CommandExecutor[DeployCommand, DeployCommand.Output]):
26
+ _apps: DeployAppManager
27
+
21
28
  async def execute(self, cmd: DeployCommand) -> DeployCommand.Output:
22
- log.info('Deploying!')
29
+ log.info('Deploying! %r', cmd.spec)
30
+
31
+ await self._apps.prepare_app(cmd.spec)
23
32
 
24
33
  return DeployCommand.Output()
@@ -15,9 +15,10 @@ import typing as ta
15
15
  from omlish.asyncs.asyncio.subprocesses import asyncio_subprocesses
16
16
  from omlish.lite.cached import async_cached_nullary
17
17
  from omlish.lite.check import check
18
+ from omlish.os.atomics import AtomicPathSwapping
18
19
 
19
- from .atomics import DeployAtomicPathSwapping
20
20
  from .paths import SingleDirDeployPathOwner
21
+ from .specs import DeployGitCheckout
21
22
  from .specs import DeployGitRepo
22
23
  from .types import DeployHome
23
24
  from .types import DeployRev
@@ -31,7 +32,7 @@ class DeployGitManager(SingleDirDeployPathOwner):
31
32
  self,
32
33
  *,
33
34
  deploy_home: ta.Optional[DeployHome] = None,
34
- atomics: DeployAtomicPathSwapping,
35
+ atomics: AtomicPathSwapping,
35
36
  ) -> None:
36
37
  super().__init__(
37
38
  owned_dir='git',
@@ -69,12 +70,16 @@ class DeployGitManager(SingleDirDeployPathOwner):
69
70
  else:
70
71
  return f'https://{self._repo.host}/{self._repo.path}'
71
72
 
73
+ #
74
+
72
75
  async def _call(self, *cmd: str) -> None:
73
76
  await asyncio_subprocesses.check_call(
74
77
  *cmd,
75
78
  cwd=self._dir,
76
79
  )
77
80
 
81
+ #
82
+
78
83
  @async_cached_nullary
79
84
  async def init(self) -> None:
80
85
  os.makedirs(self._dir, exist_ok=True)
@@ -88,7 +93,9 @@ class DeployGitManager(SingleDirDeployPathOwner):
88
93
  await self.init()
89
94
  await self._call('git', 'fetch', '--depth=1', 'origin', rev)
90
95
 
91
- async def checkout(self, rev: DeployRev, dst_dir: str) -> None:
96
+ #
97
+
98
+ async def checkout(self, checkout: DeployGitCheckout, dst_dir: str) -> None:
92
99
  check.state(not os.path.exists(dst_dir))
93
100
  with self._git._atomics.begin_atomic_path_swap( # noqa
94
101
  'dir',
@@ -96,14 +103,14 @@ class DeployGitManager(SingleDirDeployPathOwner):
96
103
  auto_commit=True,
97
104
  make_dirs=True,
98
105
  ) as dst_swap:
99
- await self.fetch(rev)
106
+ await self.fetch(checkout.rev)
100
107
 
101
108
  dst_call = functools.partial(asyncio_subprocesses.check_call, cwd=dst_swap.tmp_path)
102
109
  await dst_call('git', 'init')
103
110
 
104
111
  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)
112
+ await dst_call('git', 'fetch', '--depth=1', 'local', checkout.rev)
113
+ await dst_call('git', 'checkout', checkout.rev, *(checkout.subtrees or []))
107
114
 
108
115
  def get_repo_dir(self, repo: DeployGitRepo) -> RepoDir:
109
116
  try:
@@ -112,5 +119,5 @@ class DeployGitManager(SingleDirDeployPathOwner):
112
119
  repo_dir = self._repo_dirs[repo] = DeployGitManager.RepoDir(self, repo)
113
120
  return repo_dir
114
121
 
115
- async def checkout(self, repo: DeployGitRepo, rev: DeployRev, dst_dir: str) -> None:
116
- await self.get_repo_dir(repo).checkout(rev, dst_dir)
122
+ async def checkout(self, checkout: DeployGitCheckout, dst_dir: str) -> None:
123
+ await self.get_repo_dir(checkout.repo).checkout(checkout, dst_dir)
@@ -5,10 +5,10 @@ import typing as ta
5
5
  from omlish.lite.inject import InjectorBindingOrBindings
6
6
  from omlish.lite.inject import InjectorBindings
7
7
  from omlish.lite.inject import inj
8
+ from omlish.os.atomics import AtomicPathSwapping
8
9
 
9
10
  from ..commands.inject import bind_command
10
11
  from .apps import DeployAppManager
11
- from .atomics import DeployAtomicPathSwapping
12
12
  from .commands import DeployCommand
13
13
  from .commands import DeployCommandExecutor
14
14
  from .config import DeployConfig
@@ -34,7 +34,7 @@ def bind_deploy(
34
34
  inj.bind(DeployGitManager, singleton=True),
35
35
 
36
36
  inj.bind(DeployTmpManager, singleton=True),
37
- inj.bind(DeployAtomicPathSwapping, to_key=DeployTmpManager),
37
+ inj.bind(AtomicPathSwapping, to_key=DeployTmpManager),
38
38
 
39
39
  inj.bind(DeployVenvManager, singleton=True),
40
40
 
@@ -1,40 +1,11 @@
1
1
  # ruff: noqa: UP006 UP007
2
2
  """
3
- ~deploy
4
- deploy.pid (flock)
5
- /app
6
- /<appplaceholder> - shallow clone
7
- /conf
8
- /env
9
- <appplaceholder>.env
10
- /nginx
11
- <appplaceholder>.conf
12
- /supervisor
13
- <appplaceholder>.conf
14
- /venv
15
- /<appplaceholder>
16
-
17
- /tmp
18
-
19
- ?
20
- /logs
21
- /wrmsr--omlish--<placeholder>
22
-
23
- placeholder = <name>--<rev>--<when>
24
-
25
- ==
26
-
27
- for dn in [
28
- 'app',
29
- 'conf',
30
- 'conf/env',
31
- 'conf/nginx',
32
- 'conf/supervisor',
33
- 'venv',
34
- ]:
35
-
36
- ==
37
-
3
+ TODO:
4
+ - run/pidfile
5
+ - logs/...
6
+ - current symlink
7
+ - conf/{nginx,supervisor}
8
+ - env/?
38
9
  """
39
10
  import abc
40
11
  import dataclasses as dc
@@ -59,7 +30,7 @@ DEPLOY_PATH_PLACEHOLDER_SEPARATORS = '-.'
59
30
 
60
31
  DEPLOY_PATH_PLACEHOLDERS: ta.FrozenSet[str] = frozenset([
61
32
  'app',
62
- 'tag', # <rev>-<dt>
33
+ 'tag',
63
34
  ])
64
35
 
65
36
 
@@ -25,14 +25,28 @@ class DeployGitRepo:
25
25
  check.not_in('.', check.non_empty_str(self.path))
26
26
 
27
27
 
28
+ @dc.dataclass(frozen=True)
29
+ class DeployGitCheckout:
30
+ repo: DeployGitRepo
31
+ rev: DeployRev
32
+
33
+ subtrees: ta.Optional[ta.Sequence[str]] = None
34
+
35
+ def __post_init__(self) -> None:
36
+ hash(self)
37
+ check.non_empty_str(self.rev)
38
+ if self.subtrees is not None:
39
+ for st in self.subtrees:
40
+ check.non_empty_str(st)
41
+
42
+
28
43
  ##
29
44
 
30
45
 
31
46
  @dc.dataclass(frozen=True)
32
47
  class DeploySpec:
33
48
  app: DeployApp
34
- repo: DeployGitRepo
35
- rev: DeployRev
49
+ checkout: DeployGitCheckout
36
50
 
37
51
  def __post_init__(self) -> None:
38
52
  hash(self)
@@ -3,18 +3,18 @@ import typing as ta
3
3
 
4
4
  from omlish.lite.cached import cached_nullary
5
5
  from omlish.lite.check import check
6
+ from omlish.os.atomics import AtomicPathSwap
7
+ from omlish.os.atomics import AtomicPathSwapKind
8
+ from omlish.os.atomics import AtomicPathSwapping
9
+ from omlish.os.atomics import TempDirAtomicPathSwapping
6
10
 
7
- from .atomics import DeployAtomicPathSwap
8
- from .atomics import DeployAtomicPathSwapKind
9
- from .atomics import DeployAtomicPathSwapping
10
- from .atomics import TempDirDeployAtomicPathSwapping
11
11
  from .paths import SingleDirDeployPathOwner
12
12
  from .types import DeployHome
13
13
 
14
14
 
15
15
  class DeployTmpManager(
16
16
  SingleDirDeployPathOwner,
17
- DeployAtomicPathSwapping,
17
+ AtomicPathSwapping,
18
18
  ):
19
19
  def __init__(
20
20
  self,
@@ -27,18 +27,18 @@ class DeployTmpManager(
27
27
  )
28
28
 
29
29
  @cached_nullary
30
- def _swapping(self) -> DeployAtomicPathSwapping:
31
- return TempDirDeployAtomicPathSwapping(
30
+ def _swapping(self) -> AtomicPathSwapping:
31
+ return TempDirAtomicPathSwapping(
32
32
  temp_dir=self._make_dir(),
33
33
  root_dir=check.non_empty_str(self._deploy_home),
34
34
  )
35
35
 
36
36
  def begin_atomic_path_swap(
37
37
  self,
38
- kind: DeployAtomicPathSwapKind,
38
+ kind: AtomicPathSwapKind,
39
39
  dst_path: str,
40
40
  **kwargs: ta.Any,
41
- ) -> DeployAtomicPathSwap:
41
+ ) -> AtomicPathSwap:
42
42
  return self._swapping().begin_atomic_path_swap(
43
43
  kind,
44
44
  dst_path,
@@ -10,8 +10,8 @@ import typing as ta
10
10
  from omlish.asyncs.asyncio.subprocesses import asyncio_subprocesses
11
11
  from omlish.lite.cached import cached_nullary
12
12
  from omlish.lite.check import check
13
+ from omlish.os.atomics import AtomicPathSwapping
13
14
 
14
- from .atomics import DeployAtomicPathSwapping
15
15
  from .paths import DeployPath
16
16
  from .paths import DeployPathOwner
17
17
  from .types import DeployAppTag
@@ -23,7 +23,7 @@ class DeployVenvManager(DeployPathOwner):
23
23
  self,
24
24
  *,
25
25
  deploy_home: ta.Optional[DeployHome] = None,
26
- atomics: DeployAtomicPathSwapping,
26
+ atomics: AtomicPathSwapping,
27
27
  ) -> None:
28
28
  super().__init__()
29
29
 
@@ -105,7 +105,7 @@ class DockerManageTargetConnector(ManageTargetConnector):
105
105
  if dmt.image is not None:
106
106
  sh_parts.extend(['run', '-i', dmt.image])
107
107
  elif dmt.container_id is not None:
108
- sh_parts.extend(['exec', dmt.container_id])
108
+ sh_parts.extend(['exec', '-i', dmt.container_id])
109
109
  else:
110
110
  raise ValueError(dmt)
111
111
 
@@ -1487,6 +1487,10 @@ def is_new_type(spec: ta.Any) -> bool:
1487
1487
  return isinstance(spec, types.FunctionType) and spec.__code__ is ta.NewType.__code__.co_consts[1] # type: ignore # noqa
1488
1488
 
1489
1489
 
1490
+ def get_new_type_supertype(spec: ta.Any) -> ta.Any:
1491
+ return spec.__supertype__
1492
+
1493
+
1490
1494
  def deep_subclasses(cls: ta.Type[T]) -> ta.Iterator[ta.Type[T]]:
1491
1495
  seen = set()
1492
1496
  todo = list(reversed(cls.__subclasses__()))
@@ -2450,9 +2454,7 @@ class aclosing(contextlib.AbstractAsyncContextManager): # noqa
2450
2454
  """
2451
2455
  TODO:
2452
2456
  - pickle stdlib objs? have to pin to 3.8 pickle protocol, will be cross-version
2453
- - namedtuple
2454
2457
  - literals
2455
- - newtypes?
2456
2458
  """
2457
2459
 
2458
2460
 
@@ -2462,7 +2464,7 @@ TODO:
2462
2464
  @dc.dataclass(frozen=True)
2463
2465
  class ObjMarshalOptions:
2464
2466
  raw_bytes: bool = False
2465
- nonstrict_dataclasses: bool = False
2467
+ non_strict_fields: bool = False
2466
2468
 
2467
2469
 
2468
2470
  class ObjMarshaler(abc.ABC):
@@ -2591,10 +2593,10 @@ class IterableObjMarshaler(ObjMarshaler):
2591
2593
 
2592
2594
 
2593
2595
  @dc.dataclass(frozen=True)
2594
- class DataclassObjMarshaler(ObjMarshaler):
2596
+ class FieldsObjMarshaler(ObjMarshaler):
2595
2597
  ty: type
2596
2598
  fs: ta.Mapping[str, ObjMarshaler]
2597
- nonstrict: bool = False
2599
+ non_strict: bool = False
2598
2600
 
2599
2601
  def marshal(self, o: ta.Any, ctx: 'ObjMarshalContext') -> ta.Any:
2600
2602
  return {
@@ -2606,7 +2608,7 @@ class DataclassObjMarshaler(ObjMarshaler):
2606
2608
  return self.ty(**{
2607
2609
  k: self.fs[k].unmarshal(v, ctx)
2608
2610
  for k, v in o.items()
2609
- if not (self.nonstrict or ctx.options.nonstrict_dataclasses) or k in self.fs
2611
+ if not (self.non_strict or ctx.options.non_strict_fields) or k in self.fs
2610
2612
  })
2611
2613
 
2612
2614
 
@@ -2738,7 +2740,7 @@ class ObjMarshalerManager:
2738
2740
  ty: ta.Any,
2739
2741
  rec: ta.Callable[[ta.Any], ObjMarshaler],
2740
2742
  *,
2741
- nonstrict_dataclasses: bool = False,
2743
+ non_strict_fields: bool = False,
2742
2744
  ) -> ObjMarshaler:
2743
2745
  if isinstance(ty, type):
2744
2746
  if abc.ABC in ty.__bases__:
@@ -2760,12 +2762,22 @@ class ObjMarshalerManager:
2760
2762
  return EnumObjMarshaler(ty)
2761
2763
 
2762
2764
  if dc.is_dataclass(ty):
2763
- return DataclassObjMarshaler(
2765
+ return FieldsObjMarshaler(
2764
2766
  ty,
2765
2767
  {f.name: rec(f.type) for f in dc.fields(ty)},
2766
- nonstrict=nonstrict_dataclasses,
2768
+ non_strict=non_strict_fields,
2769
+ )
2770
+
2771
+ if issubclass(ty, tuple) and hasattr(ty, '_fields'):
2772
+ return FieldsObjMarshaler(
2773
+ ty,
2774
+ {p.name: rec(p.annotation) for p in inspect.signature(ty).parameters.values()},
2775
+ non_strict=non_strict_fields,
2767
2776
  )
2768
2777
 
2778
+ if is_new_type(ty):
2779
+ return rec(get_new_type_supertype(ty))
2780
+
2769
2781
  if is_generic_alias(ty):
2770
2782
  try:
2771
2783
  mt = self._generic_mapping_types[ta.get_origin(ty)]