ominfra 0.0.0.dev190__py3-none-any.whl → 0.0.0.dev192__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.
@@ -5,6 +5,7 @@ TODO:
5
5
  - share more code with pyproject?
6
6
  """
7
7
  import os.path
8
+ import shutil
8
9
 
9
10
  from omdev.interp.default import get_default_interp_resolver
10
11
  from omdev.interp.types import InterpSpecifier
@@ -44,9 +45,12 @@ class DeployVenvManager:
44
45
 
45
46
  if os.path.isfile(reqs_txt):
46
47
  if spec.use_uv:
47
- await asyncio_subprocesses.check_call(venv_exe, '-m', 'pip', 'install', 'uv')
48
- pip_cmd = ['-m', 'uv', 'pip']
48
+ if shutil.which('uv') is not None:
49
+ pip_cmd = ['uv', 'pip']
50
+ else:
51
+ await asyncio_subprocesses.check_call(venv_exe, '-m', 'pip', 'install', 'uv')
52
+ pip_cmd = [venv_exe, '-m', 'uv', 'pip']
49
53
  else:
50
- pip_cmd = ['-m', 'pip']
54
+ pip_cmd = [venv_exe, '-m', 'pip']
51
55
 
52
- await asyncio_subprocesses.check_call(venv_exe, *pip_cmd,'install', '-r', reqs_txt)
56
+ await asyncio_subprocesses.check_call(*pip_cmd, 'install', '-r', reqs_txt, cwd=venv_dir)
ominfra/scripts/manage.py CHANGED
@@ -7089,7 +7089,7 @@ DEPLOY_TAG_ILLEGAL_STRS: ta.AbstractSet[str] = frozenset([
7089
7089
  ##
7090
7090
 
7091
7091
 
7092
- @dc.dataclass(frozen=True)
7092
+ @dc.dataclass(frozen=True, order=True)
7093
7093
  class DeployTag(abc.ABC): # noqa
7094
7094
  s: str
7095
7095
 
@@ -9268,20 +9268,63 @@ TODO:
9268
9268
  """
9269
9269
 
9270
9270
 
9271
+ ##
9272
+
9273
+
9271
9274
  class DeployConfManager:
9272
- def _render_app_conf_content(self, ac: DeployAppConfContent) -> str:
9275
+ def _process_conf_content(
9276
+ self,
9277
+ content: T,
9278
+ *,
9279
+ str_processor: ta.Optional[ta.Callable[[str], str]] = None,
9280
+ ) -> T:
9281
+ def rec(o):
9282
+ if isinstance(o, str):
9283
+ if str_processor is not None:
9284
+ return type(o)(str_processor(o))
9285
+
9286
+ elif isinstance(o, collections.abc.Mapping):
9287
+ return type(o)([ # type: ignore
9288
+ (rec(k), rec(v))
9289
+ for k, v in o.items()
9290
+ ])
9291
+
9292
+ elif isinstance(o, collections.abc.Iterable):
9293
+ return type(o)([ # type: ignore
9294
+ rec(e) for e in o
9295
+ ])
9296
+
9297
+ return o
9298
+
9299
+ return rec(content)
9300
+
9301
+ #
9302
+
9303
+ def _render_app_conf_content(
9304
+ self,
9305
+ ac: DeployAppConfContent,
9306
+ *,
9307
+ str_processor: ta.Optional[ta.Callable[[str], str]] = None,
9308
+ ) -> str:
9309
+ pcc = functools.partial(
9310
+ self._process_conf_content,
9311
+ str_processor=str_processor,
9312
+ )
9313
+
9273
9314
  if isinstance(ac, RawDeployAppConfContent):
9274
- return ac.body
9315
+ return pcc(ac.body)
9275
9316
 
9276
9317
  elif isinstance(ac, JsonDeployAppConfContent):
9277
- return strip_with_newline(json_dumps_pretty(ac.obj))
9318
+ json_obj = pcc(ac.obj)
9319
+ return strip_with_newline(json_dumps_pretty(json_obj))
9278
9320
 
9279
9321
  elif isinstance(ac, IniDeployAppConfContent):
9280
- return strip_with_newline(render_ini_config(ac.sections))
9322
+ ini_sections = pcc(ac.sections)
9323
+ return strip_with_newline(render_ini_config(ini_sections))
9281
9324
 
9282
9325
  elif isinstance(ac, NginxDeployAppConfContent):
9283
- ni = NginxConfigItems.of(ac.items)
9284
- return strip_with_newline(render_nginx_config_str(ni))
9326
+ nginx_items = NginxConfigItems.of(pcc(ac.items))
9327
+ return strip_with_newline(render_nginx_config_str(nginx_items))
9285
9328
 
9286
9329
  else:
9287
9330
  raise TypeError(ac)
@@ -9290,11 +9333,16 @@ class DeployConfManager:
9290
9333
  self,
9291
9334
  acf: DeployAppConfFile,
9292
9335
  app_conf_dir: str,
9336
+ *,
9337
+ str_processor: ta.Optional[ta.Callable[[str], str]] = None,
9293
9338
  ) -> None:
9294
9339
  conf_file = os.path.join(app_conf_dir, acf.path)
9295
9340
  check.arg(is_path_in_dir(app_conf_dir, conf_file))
9296
9341
 
9297
- body = self._render_app_conf_content(acf.content)
9342
+ body = self._render_app_conf_content(
9343
+ acf.content,
9344
+ str_processor=str_processor,
9345
+ )
9298
9346
 
9299
9347
  os.makedirs(os.path.dirname(conf_file), exist_ok=True)
9300
9348
 
@@ -9305,11 +9353,21 @@ class DeployConfManager:
9305
9353
  self,
9306
9354
  spec: DeployAppConfSpec,
9307
9355
  app_conf_dir: str,
9356
+ *,
9357
+ string_ns: ta.Optional[ta.Mapping[str, ta.Any]] = None,
9308
9358
  ) -> None:
9359
+ process_str: ta.Any
9360
+ if string_ns is not None:
9361
+ def process_str(s: str) -> str:
9362
+ return s.format(**string_ns)
9363
+ else:
9364
+ process_str = None
9365
+
9309
9366
  for acf in spec.files or []:
9310
9367
  await self._write_app_conf_file(
9311
9368
  acf,
9312
9369
  app_conf_dir,
9370
+ str_processor=process_str,
9313
9371
  )
9314
9372
 
9315
9373
  #
@@ -9552,6 +9610,15 @@ class DeployAppSpec(DeploySpecKeyed[DeployAppKey]):
9552
9610
  return DeployAppKey(self._key_str())
9553
9611
 
9554
9612
 
9613
+ @dc.dataclass(frozen=True)
9614
+ class DeployAppLinksSpec:
9615
+ apps: ta.Sequence[DeployApp] = ()
9616
+
9617
+ removed_apps: ta.Sequence[DeployApp] = ()
9618
+
9619
+ exclude_unspecified: bool = False
9620
+
9621
+
9555
9622
  ##
9556
9623
 
9557
9624
 
@@ -9570,6 +9637,8 @@ class DeploySpec(DeploySpecKeyed[DeployKey]):
9570
9637
 
9571
9638
  apps: ta.Sequence[DeployAppSpec] = ()
9572
9639
 
9640
+ app_links: DeployAppLinksSpec = DeployAppLinksSpec()
9641
+
9573
9642
  systemd: ta.Optional[DeploySystemdSpec] = None
9574
9643
 
9575
9644
  def __post_init__(self) -> None:
@@ -10906,7 +10975,9 @@ class DeployGitManager(SingleDirDeployPathOwner):
10906
10975
 
10907
10976
  async def fetch(self, rev: DeployRev) -> None:
10908
10977
  await self.init()
10909
- await self._call('git', 'fetch', '--depth=1', 'origin', rev)
10978
+
10979
+ # This fetch shouldn't be depth=1 - git doesn't reuse local data with shallow fetches.
10980
+ await self._call('git', 'fetch', 'origin', rev)
10910
10981
 
10911
10982
  #
10912
10983
 
@@ -10975,31 +11046,12 @@ def bind_deploy_paths() -> InjectorBindings:
10975
11046
  ########################################
10976
11047
  # ../deploy/systemd.py
10977
11048
  """
10978
- ~/.config/systemd/user/
10979
-
10980
- verify - systemd-analyze
10981
-
10982
- sudo loginctl enable-linger "$USER"
10983
-
10984
- cat ~/.config/systemd/user/sleep-infinity.service
10985
- [Unit]
10986
- Description=User-specific service to run 'sleep infinity'
10987
- After=default.target
10988
-
10989
- [Service]
10990
- ExecStart=/bin/sleep infinity
10991
- Restart=always
10992
- RestartSec=5
10993
-
10994
- [Install]
10995
- WantedBy=default.target
10996
-
10997
- systemctl --user daemon-reload
10998
-
10999
- systemctl --user enable sleep-infinity.service
11000
- systemctl --user start sleep-infinity.service
11001
-
11002
- systemctl --user status sleep-infinity.service
11049
+ TODO:
11050
+ - verify - systemd-analyze
11051
+ - sudo loginctl enable-linger "$USER"
11052
+ - idemp kill services that shouldn't be running, start ones that should
11053
+ - ideally only those defined by links to deploy home
11054
+ - ominfra.systemd / x.sd_orphans
11003
11055
  """
11004
11056
 
11005
11057
 
@@ -11038,6 +11090,8 @@ class DeploySystemdManager:
11038
11090
  if not spec:
11039
11091
  return
11040
11092
 
11093
+ #
11094
+
11041
11095
  if not (ud := spec.unit_dir):
11042
11096
  return
11043
11097
 
@@ -11045,6 +11099,8 @@ class DeploySystemdManager:
11045
11099
 
11046
11100
  os.makedirs(ud, exist_ok=True)
11047
11101
 
11102
+ #
11103
+
11048
11104
  uld = {
11049
11105
  n: p
11050
11106
  for n, p in self._scan_link_dir(ud).items()
@@ -11056,8 +11112,11 @@ class DeploySystemdManager:
11056
11112
  else:
11057
11113
  cld = {}
11058
11114
 
11059
- for n in sorted(set(uld) | set(cld)):
11060
- ul = uld.get(n) # noqa
11115
+ #
11116
+
11117
+ ns = sorted(set(uld) | set(cld))
11118
+
11119
+ for n in ns:
11061
11120
  cl = cld.get(n)
11062
11121
  if cl is None:
11063
11122
  os.unlink(os.path.join(ud, n))
@@ -11074,6 +11133,37 @@ class DeploySystemdManager:
11074
11133
  dst_swap.tmp_path,
11075
11134
  )
11076
11135
 
11136
+ #
11137
+
11138
+ if sys.platform == 'linux' and shutil.which('systemctl') is not None:
11139
+ async def reload() -> None:
11140
+ await asyncio_subprocesses.check_call('systemctl', '--user', 'daemon-reload')
11141
+
11142
+ await reload()
11143
+
11144
+ num_deleted = 0
11145
+ for n in ns:
11146
+ if n.endswith('.service'):
11147
+ cl = cld.get(n)
11148
+ ul = uld.get(n)
11149
+ if cl is not None:
11150
+ if ul is None:
11151
+ cs = ['enable', 'start']
11152
+ else:
11153
+ cs = ['restart']
11154
+ else: # noqa
11155
+ if ul is not None:
11156
+ cs = ['stop']
11157
+ num_deleted += 1
11158
+ else:
11159
+ cs = []
11160
+
11161
+ for c in cs:
11162
+ await asyncio_subprocesses.check_call('systemctl', '--user', c, n)
11163
+
11164
+ if num_deleted:
11165
+ await reload()
11166
+
11077
11167
 
11078
11168
  ########################################
11079
11169
  # ../remote/inject.py
@@ -11455,12 +11545,15 @@ class DeployVenvManager:
11455
11545
 
11456
11546
  if os.path.isfile(reqs_txt):
11457
11547
  if spec.use_uv:
11458
- await asyncio_subprocesses.check_call(venv_exe, '-m', 'pip', 'install', 'uv')
11459
- pip_cmd = ['-m', 'uv', 'pip']
11548
+ if shutil.which('uv') is not None:
11549
+ pip_cmd = ['uv', 'pip']
11550
+ else:
11551
+ await asyncio_subprocesses.check_call(venv_exe, '-m', 'pip', 'install', 'uv')
11552
+ pip_cmd = [venv_exe, '-m', 'uv', 'pip']
11460
11553
  else:
11461
- pip_cmd = ['-m', 'pip']
11554
+ pip_cmd = [venv_exe, '-m', 'pip']
11462
11555
 
11463
- await asyncio_subprocesses.check_call(venv_exe, *pip_cmd,'install', '-r', reqs_txt)
11556
+ await asyncio_subprocesses.check_call(*pip_cmd, 'install', '-r', reqs_txt, cwd=venv_dir)
11464
11557
 
11465
11558
 
11466
11559
  ########################################
@@ -11506,9 +11599,21 @@ class DeployAppManager(DeployPathOwner):
11506
11599
 
11507
11600
  #
11508
11601
 
11602
+ def _make_tags(self, spec: DeployAppSpec) -> DeployTagMap:
11603
+ return DeployTagMap(
11604
+ spec.app,
11605
+ spec.key(),
11606
+ DeployAppRev(spec.git.rev),
11607
+ )
11608
+
11609
+ #
11610
+
11509
11611
  @dc.dataclass(frozen=True)
11510
11612
  class PreparedApp:
11511
- app_dir: str
11613
+ spec: DeployAppSpec
11614
+ tags: DeployTagMap
11615
+
11616
+ dir: str
11512
11617
 
11513
11618
  git_dir: ta.Optional[str] = None
11514
11619
  venv_dir: ta.Optional[str] = None
@@ -11519,21 +11624,30 @@ class DeployAppManager(DeployPathOwner):
11519
11624
  spec: DeployAppSpec,
11520
11625
  home: DeployHome,
11521
11626
  tags: DeployTagMap,
11627
+ *,
11628
+ conf_string_ns: ta.Optional[ta.Mapping[str, ta.Any]] = None,
11522
11629
  ) -> PreparedApp:
11523
11630
  spec_json = json_dumps_pretty(self._msh.marshal_obj(spec))
11524
11631
 
11525
11632
  #
11526
11633
 
11634
+ app_tags = tags.add(*self._make_tags(spec))
11635
+
11636
+ #
11637
+
11527
11638
  check.non_empty_str(home)
11528
11639
 
11529
- app_dir = os.path.join(home, self.APP_DIR.render(tags))
11640
+ app_dir = os.path.join(home, self.APP_DIR.render(app_tags))
11530
11641
 
11531
11642
  os.makedirs(app_dir, exist_ok=True)
11532
11643
 
11533
11644
  #
11534
11645
 
11535
11646
  rkw: ta.Dict[str, ta.Any] = dict(
11536
- app_dir=app_dir,
11647
+ spec=spec,
11648
+ tags=app_tags,
11649
+
11650
+ dir=app_dir,
11537
11651
  )
11538
11652
 
11539
11653
  #
@@ -11568,15 +11682,71 @@ class DeployAppManager(DeployPathOwner):
11568
11682
  if spec.conf is not None:
11569
11683
  conf_dir = os.path.join(app_dir, 'conf')
11570
11684
  rkw.update(conf_dir=conf_dir)
11685
+
11686
+ conf_ns: ta.Dict[str, ta.Any] = dict(
11687
+ **(conf_string_ns or {}),
11688
+ app=spec.app.s,
11689
+ app_dir=app_dir.rstrip('/'),
11690
+ )
11691
+
11571
11692
  await self._conf.write_app_conf(
11572
11693
  spec.conf,
11573
11694
  conf_dir,
11695
+ string_ns=conf_ns,
11574
11696
  )
11575
11697
 
11576
11698
  #
11577
11699
 
11578
11700
  return DeployAppManager.PreparedApp(**rkw)
11579
11701
 
11702
+ async def prepare_app_link(
11703
+ self,
11704
+ tags: DeployTagMap,
11705
+ app_dir: str,
11706
+ ) -> PreparedApp:
11707
+ spec_file = os.path.join(app_dir, 'spec.json')
11708
+ with open(spec_file) as f: # noqa
11709
+ spec_json = f.read()
11710
+
11711
+ spec: DeployAppSpec = self._msh.unmarshal_obj(json.loads(spec_json), DeployAppSpec)
11712
+
11713
+ #
11714
+
11715
+ app_tags = tags.add(*self._make_tags(spec))
11716
+
11717
+ #
11718
+
11719
+ rkw: ta.Dict[str, ta.Any] = dict(
11720
+ spec=spec,
11721
+ tags=app_tags,
11722
+
11723
+ dir=app_dir,
11724
+ )
11725
+
11726
+ #
11727
+
11728
+ git_dir = os.path.join(app_dir, 'git')
11729
+ check.state(os.path.isdir(git_dir))
11730
+ rkw.update(git_dir=git_dir)
11731
+
11732
+ #
11733
+
11734
+ if spec.venv is not None:
11735
+ venv_dir = os.path.join(app_dir, 'venv')
11736
+ check.state(os.path.isdir(venv_dir))
11737
+ rkw.update(venv_dir=venv_dir)
11738
+
11739
+ #
11740
+
11741
+ if spec.conf is not None:
11742
+ conf_dir = os.path.join(app_dir, 'conf')
11743
+ check.state(os.path.isdir(conf_dir))
11744
+ rkw.update(conf_dir=conf_dir)
11745
+
11746
+ #
11747
+
11748
+ return DeployAppManager.PreparedApp(**rkw)
11749
+
11580
11750
 
11581
11751
  ########################################
11582
11752
  # ../deploy/deploy.py
@@ -11585,7 +11755,7 @@ class DeployAppManager(DeployPathOwner):
11585
11755
  ##
11586
11756
 
11587
11757
 
11588
- DEPLOY_TAG_DATETIME_FMT = '%Y%m%dT%H%M%SZ'
11758
+ DEPLOY_TAG_DATETIME_FMT = '%Y-%m-%d-T-%H-%M-%S-%f-Z'
11589
11759
 
11590
11760
 
11591
11761
  DeployManagerUtcClock = ta.NewType('DeployManagerUtcClock', Func0[datetime.datetime])
@@ -11595,16 +11765,24 @@ class DeployManager(DeployPathOwner):
11595
11765
  def __init__(
11596
11766
  self,
11597
11767
  *,
11768
+ atomics: DeployHomeAtomics,
11769
+
11598
11770
  utc_clock: ta.Optional[DeployManagerUtcClock] = None,
11599
11771
  ):
11600
11772
  super().__init__()
11601
11773
 
11774
+ self._atomics = atomics
11775
+
11602
11776
  self._utc_clock = utc_clock
11603
11777
 
11604
11778
  #
11605
11779
 
11780
+ # Home current link just points to CURRENT_DEPLOY_LINK, and is intended for user convenience.
11781
+ HOME_CURRENT_LINK = DeployPath.parse('current')
11782
+
11606
11783
  DEPLOYS_DIR = DeployPath.parse('deploys/')
11607
11784
 
11785
+ # Authoritative current symlink is not in deploy-home, just to prevent accidental corruption.
11608
11786
  CURRENT_DEPLOY_LINK = DeployPath.parse(f'{DEPLOYS_DIR}current')
11609
11787
  DEPLOYING_DEPLOY_LINK = DeployPath.parse(f'{DEPLOYS_DIR}deploying')
11610
11788
 
@@ -11637,6 +11815,11 @@ class DeployManager(DeployPathOwner):
11637
11815
 
11638
11816
  #
11639
11817
 
11818
+ def render_path(self, home: DeployHome, pth: DeployPath, tags: ta.Optional[DeployTagMap] = None) -> str:
11819
+ return os.path.join(check.non_empty_str(home), pth.render(tags))
11820
+
11821
+ #
11822
+
11640
11823
  def _utc_now(self) -> datetime.datetime:
11641
11824
  if self._utc_clock is not None:
11642
11825
  return self._utc_clock() # noqa
@@ -11646,6 +11829,22 @@ class DeployManager(DeployPathOwner):
11646
11829
  def make_deploy_time(self) -> DeployTime:
11647
11830
  return DeployTime(self._utc_now().strftime(DEPLOY_TAG_DATETIME_FMT))
11648
11831
 
11832
+ #
11833
+
11834
+ def make_home_current_link(self, home: DeployHome) -> None:
11835
+ home_current_link = os.path.join(check.non_empty_str(home), self.HOME_CURRENT_LINK.render())
11836
+ current_deploy_link = os.path.join(check.non_empty_str(home), self.CURRENT_DEPLOY_LINK.render())
11837
+ with self._atomics(home).begin_atomic_path_swap( # noqa
11838
+ 'file',
11839
+ home_current_link,
11840
+ auto_commit=True,
11841
+ ) as dst_swap:
11842
+ os.unlink(dst_swap.tmp_path)
11843
+ os.symlink(
11844
+ os.path.relpath(current_deploy_link, os.path.dirname(dst_swap.dst_path)),
11845
+ dst_swap.tmp_path,
11846
+ )
11847
+
11649
11848
 
11650
11849
  ##
11651
11850
 
@@ -11687,18 +11886,18 @@ class DeployDriver:
11687
11886
  #
11688
11887
 
11689
11888
  @property
11690
- def deploy_tags(self) -> DeployTagMap:
11889
+ def tags(self) -> DeployTagMap:
11691
11890
  return DeployTagMap(
11692
11891
  self._time,
11693
11892
  self._spec.key(),
11694
11893
  )
11695
11894
 
11696
- def render_deploy_path(self, pth: DeployPath, tags: ta.Optional[DeployTagMap] = None) -> str:
11697
- return os.path.join(self._home, pth.render(tags if tags is not None else self.deploy_tags))
11895
+ def render_path(self, pth: DeployPath, tags: ta.Optional[DeployTagMap] = None) -> str:
11896
+ return os.path.join(self._home, pth.render(tags if tags is not None else self.tags))
11698
11897
 
11699
11898
  @property
11700
- def deploy_dir(self) -> str:
11701
- return self.render_deploy_path(self._deploys.DEPLOY_DIR)
11899
+ def dir(self) -> str:
11900
+ return self.render_path(self._deploys.DEPLOY_DIR)
11702
11901
 
11703
11902
  #
11704
11903
 
@@ -11707,25 +11906,37 @@ class DeployDriver:
11707
11906
 
11708
11907
  #
11709
11908
 
11909
+ das: ta.Set[DeployApp] = {a.app for a in self._spec.apps}
11910
+ las: ta.Set[DeployApp] = set(self._spec.app_links.apps)
11911
+ ras: ta.Set[DeployApp] = set(self._spec.app_links.removed_apps)
11912
+ check.empty(das & (las | ras))
11913
+ check.empty(las & ras)
11914
+
11915
+ #
11916
+
11710
11917
  self._paths.validate_deploy_paths()
11711
11918
 
11712
11919
  #
11713
11920
 
11714
- os.makedirs(self.deploy_dir)
11921
+ os.makedirs(self.dir)
11715
11922
 
11716
11923
  #
11717
11924
 
11718
- spec_file = self.render_deploy_path(self._deploys.DEPLOY_SPEC_FILE)
11925
+ spec_file = self.render_path(self._deploys.DEPLOY_SPEC_FILE)
11719
11926
  with open(spec_file, 'w') as f: # noqa
11720
11927
  f.write(spec_json)
11721
11928
 
11722
11929
  #
11723
11930
 
11724
- deploying_link = self.render_deploy_path(self._deploys.DEPLOYING_DEPLOY_LINK)
11931
+ deploying_link = self.render_path(self._deploys.DEPLOYING_DEPLOY_LINK)
11932
+ current_link = self.render_path(self._deploys.CURRENT_DEPLOY_LINK)
11933
+
11934
+ #
11935
+
11725
11936
  if os.path.exists(deploying_link):
11726
11937
  os.unlink(deploying_link)
11727
11938
  relative_symlink(
11728
- self.deploy_dir,
11939
+ self.dir,
11729
11940
  deploying_link,
11730
11941
  target_is_directory=True,
11731
11942
  make_dirs=True,
@@ -11737,16 +11948,30 @@ class DeployDriver:
11737
11948
  self._deploys.APPS_DEPLOY_DIR,
11738
11949
  self._deploys.CONFS_DEPLOY_DIR,
11739
11950
  ]:
11740
- os.makedirs(self.render_deploy_path(md))
11951
+ os.makedirs(self.render_path(md))
11741
11952
 
11742
11953
  #
11743
11954
 
11955
+ if not self._spec.app_links.exclude_unspecified:
11956
+ cad = abs_real_path(os.path.join(current_link, 'apps'))
11957
+ if os.path.exists(cad):
11958
+ for d in os.listdir(cad):
11959
+ if (da := DeployApp(d)) not in das and da not in ras:
11960
+ las.add(da)
11961
+
11962
+ for la in las:
11963
+ await self._drive_app_link(
11964
+ la,
11965
+ current_link,
11966
+ )
11967
+
11744
11968
  for app in self._spec.apps:
11745
- await self.drive_app_deploy(app)
11969
+ await self._drive_app_deploy(
11970
+ app,
11971
+ )
11746
11972
 
11747
11973
  #
11748
11974
 
11749
- current_link = self.render_deploy_path(self._deploys.CURRENT_DEPLOY_LINK)
11750
11975
  os.replace(deploying_link, current_link)
11751
11976
 
11752
11977
  #
@@ -11754,31 +11979,33 @@ class DeployDriver:
11754
11979
  await self._systemd.sync_systemd(
11755
11980
  self._spec.systemd,
11756
11981
  self._home,
11757
- os.path.join(self.deploy_dir, 'conf', 'systemd'), # FIXME
11982
+ os.path.join(self.dir, 'conf', 'systemd'), # FIXME
11758
11983
  )
11759
11984
 
11760
- #
11985
+ #
11761
11986
 
11762
- async def drive_app_deploy(self, app: DeployAppSpec) -> None:
11763
- app_tags = self.deploy_tags.add(
11764
- app.app,
11765
- app.key(),
11766
- DeployAppRev(app.git.rev),
11767
- )
11987
+ self._deploys.make_home_current_link(self._home)
11768
11988
 
11769
- #
11989
+ #
11990
+
11991
+ async def _drive_app_deploy(self, app: DeployAppSpec) -> None:
11992
+ current_deploy_link = os.path.join(self._home, self._deploys.CURRENT_DEPLOY_LINK.render())
11770
11993
 
11771
- da = await self._apps.prepare_app(
11994
+ pa = await self._apps.prepare_app(
11772
11995
  app,
11773
11996
  self._home,
11774
- app_tags,
11997
+ self.tags,
11998
+ conf_string_ns=dict(
11999
+ deploy_home=self._home,
12000
+ current_deploy_link=current_deploy_link,
12001
+ ),
11775
12002
  )
11776
12003
 
11777
12004
  #
11778
12005
 
11779
- app_link = self.render_deploy_path(self._deploys.APP_DEPLOY_LINK, app_tags)
12006
+ app_link = self.render_path(self._deploys.APP_DEPLOY_LINK, pa.tags)
11780
12007
  relative_symlink(
11781
- da.app_dir,
12008
+ pa.dir,
11782
12009
  app_link,
11783
12010
  target_is_directory=True,
11784
12011
  make_dirs=True,
@@ -11786,12 +12013,48 @@ class DeployDriver:
11786
12013
 
11787
12014
  #
11788
12015
 
11789
- deploy_conf_dir = self.render_deploy_path(self._deploys.CONFS_DEPLOY_DIR)
11790
- if app.conf is not None:
12016
+ await self._drive_app_configure(pa)
12017
+
12018
+ async def _drive_app_link(
12019
+ self,
12020
+ app: DeployApp,
12021
+ current_link: str,
12022
+ ) -> None:
12023
+ app_link = os.path.join(abs_real_path(current_link), 'apps', app.s)
12024
+ check.state(os.path.islink(app_link))
12025
+
12026
+ app_dir = abs_real_path(app_link)
12027
+ check.state(os.path.isdir(app_dir))
12028
+
12029
+ #
12030
+
12031
+ pa = await self._apps.prepare_app_link(
12032
+ self.tags,
12033
+ app_dir,
12034
+ )
12035
+
12036
+ #
12037
+
12038
+ relative_symlink(
12039
+ app_dir,
12040
+ os.path.join(self.dir, 'apps', app.s),
12041
+ target_is_directory=True,
12042
+ )
12043
+
12044
+ #
12045
+
12046
+ await self._drive_app_configure(pa)
12047
+
12048
+ async def _drive_app_configure(
12049
+ self,
12050
+ pa: DeployAppManager.PreparedApp,
12051
+ ) -> None:
12052
+ deploy_conf_dir = self.render_path(self._deploys.CONFS_DEPLOY_DIR)
12053
+ if pa.spec.conf is not None:
11791
12054
  await self._conf.link_app_conf(
11792
- app.conf,
11793
- app_tags,
11794
- check.non_empty_str(da.conf_dir),
12055
+ pa.spec.conf,
12056
+ pa.tags,
12057
+ check.non_empty_str(pa.conf_dir),
11795
12058
  deploy_conf_dir,
11796
12059
  )
11797
12060