magic-pocket-cli 0.12.0__tar.gz → 0.14.0__tar.gz

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 (68) hide show
  1. {magic_pocket_cli-0.12.0 → magic_pocket_cli-0.14.0}/PKG-INFO +2 -2
  2. {magic_pocket_cli-0.12.0 → magic_pocket_cli-0.14.0}/pocket_cli/cli/deploy_cli.py +6 -1
  3. {magic_pocket_cli-0.12.0 → magic_pocket_cli-0.14.0}/pocket_cli/cli/migrate_cli.py +28 -0
  4. {magic_pocket_cli-0.12.0 → magic_pocket_cli-0.14.0}/pocket_cli/resources/aws/builders/codebuild.py +29 -1
  5. {magic_pocket_cli-0.12.0 → magic_pocket_cli-0.14.0}/pocket_cli/resources/dsql.py +5 -5
  6. {magic_pocket_cli-0.12.0 → magic_pocket_cli-0.14.0}/pocket_cli/resources/neon.py +5 -2
  7. {magic_pocket_cli-0.12.0 → magic_pocket_cli-0.14.0}/pyproject.toml +2 -2
  8. {magic_pocket_cli-0.12.0 → magic_pocket_cli-0.14.0}/.gitignore +0 -0
  9. {magic_pocket_cli-0.12.0 → magic_pocket_cli-0.14.0}/pocket_cli/__init__.py +0 -0
  10. {magic_pocket_cli-0.12.0 → magic_pocket_cli-0.14.0}/pocket_cli/cli/__init__.py +0 -0
  11. {magic_pocket_cli-0.12.0 → magic_pocket_cli-0.14.0}/pocket_cli/cli/aws_auth.py +0 -0
  12. {magic_pocket_cli-0.12.0 → magic_pocket_cli-0.14.0}/pocket_cli/cli/awscontainer_cli.py +0 -0
  13. {magic_pocket_cli-0.12.0 → magic_pocket_cli-0.14.0}/pocket_cli/cli/cloudfront_cli.py +0 -0
  14. {magic_pocket_cli-0.12.0 → magic_pocket_cli-0.14.0}/pocket_cli/cli/cloudfront_keys_cli.py +0 -0
  15. {magic_pocket_cli-0.12.0 → magic_pocket_cli-0.14.0}/pocket_cli/cli/cloudfront_waf_cli.py +0 -0
  16. {magic_pocket_cli-0.12.0 → magic_pocket_cli-0.14.0}/pocket_cli/cli/destroy_cli.py +0 -0
  17. {magic_pocket_cli-0.12.0 → magic_pocket_cli-0.14.0}/pocket_cli/cli/dsql_cli.py +0 -0
  18. {magic_pocket_cli-0.12.0 → magic_pocket_cli-0.14.0}/pocket_cli/cli/main_cli.py +0 -0
  19. {magic_pocket_cli-0.12.0 → magic_pocket_cli-0.14.0}/pocket_cli/cli/neon_cli.py +0 -0
  20. {magic_pocket_cli-0.12.0 → magic_pocket_cli-0.14.0}/pocket_cli/cli/permissions_cli.py +0 -0
  21. {magic_pocket_cli-0.12.0 → magic_pocket_cli-0.14.0}/pocket_cli/cli/rds_cli.py +0 -0
  22. {magic_pocket_cli-0.12.0 → magic_pocket_cli-0.14.0}/pocket_cli/cli/runtime_config_cli.py +0 -0
  23. {magic_pocket_cli-0.12.0 → magic_pocket_cli-0.14.0}/pocket_cli/cli/s3_cli.py +0 -0
  24. {magic_pocket_cli-0.12.0 → magic_pocket_cli-0.14.0}/pocket_cli/cli/status_cli.py +0 -0
  25. {magic_pocket_cli-0.12.0 → magic_pocket_cli-0.14.0}/pocket_cli/cli/store_url_helper.py +0 -0
  26. {magic_pocket_cli-0.12.0 → magic_pocket_cli-0.14.0}/pocket_cli/cli/tidb_cli.py +0 -0
  27. {magic_pocket_cli-0.12.0 → magic_pocket_cli-0.14.0}/pocket_cli/cli/upstash_cli.py +0 -0
  28. {magic_pocket_cli-0.12.0 → magic_pocket_cli-0.14.0}/pocket_cli/cli/url_helper.py +0 -0
  29. {magic_pocket_cli-0.12.0 → magic_pocket_cli-0.14.0}/pocket_cli/cli/vpc_cli.py +0 -0
  30. {magic_pocket_cli-0.12.0 → magic_pocket_cli-0.14.0}/pocket_cli/cli/waf_cli.py +0 -0
  31. {magic_pocket_cli-0.12.0 → magic_pocket_cli-0.14.0}/pocket_cli/django_cli.py +0 -0
  32. {magic_pocket_cli-0.12.0 → magic_pocket_cli-0.14.0}/pocket_cli/mediator.py +0 -0
  33. {magic_pocket_cli-0.12.0 → magic_pocket_cli-0.14.0}/pocket_cli/resources/__init__.py +0 -0
  34. {magic_pocket_cli-0.12.0 → magic_pocket_cli-0.14.0}/pocket_cli/resources/aws/__init__.py +0 -0
  35. {magic_pocket_cli-0.12.0 → magic_pocket_cli-0.14.0}/pocket_cli/resources/aws/builders/__init__.py +0 -0
  36. {magic_pocket_cli-0.12.0 → magic_pocket_cli-0.14.0}/pocket_cli/resources/aws/builders/depot.py +0 -0
  37. {magic_pocket_cli-0.12.0 → magic_pocket_cli-0.14.0}/pocket_cli/resources/aws/builders/docker.py +0 -0
  38. {magic_pocket_cli-0.12.0 → magic_pocket_cli-0.14.0}/pocket_cli/resources/aws/builders/dockerignore.py +0 -0
  39. {magic_pocket_cli-0.12.0 → magic_pocket_cli-0.14.0}/pocket_cli/resources/aws/cloudformation.py +0 -0
  40. {magic_pocket_cli-0.12.0 → magic_pocket_cli-0.14.0}/pocket_cli/resources/aws/ecr.py +0 -0
  41. {magic_pocket_cli-0.12.0 → magic_pocket_cli-0.14.0}/pocket_cli/resources/aws/efs.py +0 -0
  42. {magic_pocket_cli-0.12.0 → magic_pocket_cli-0.14.0}/pocket_cli/resources/aws/lambdahandler.py +0 -0
  43. {magic_pocket_cli-0.12.0 → magic_pocket_cli-0.14.0}/pocket_cli/resources/aws/s3_utils.py +0 -0
  44. {magic_pocket_cli-0.12.0 → magic_pocket_cli-0.14.0}/pocket_cli/resources/aws/state.py +0 -0
  45. {magic_pocket_cli-0.12.0 → magic_pocket_cli-0.14.0}/pocket_cli/resources/awscontainer.py +0 -0
  46. {magic_pocket_cli-0.12.0 → magic_pocket_cli-0.14.0}/pocket_cli/resources/cloudfront.py +0 -0
  47. {magic_pocket_cli-0.12.0 → magic_pocket_cli-0.14.0}/pocket_cli/resources/cloudfront_acm.py +0 -0
  48. {magic_pocket_cli-0.12.0 → magic_pocket_cli-0.14.0}/pocket_cli/resources/cloudfront_keys.py +0 -0
  49. {magic_pocket_cli-0.12.0 → magic_pocket_cli-0.14.0}/pocket_cli/resources/cloudfront_waf.py +0 -0
  50. {magic_pocket_cli-0.12.0 → magic_pocket_cli-0.14.0}/pocket_cli/resources/rds.py +0 -0
  51. {magic_pocket_cli-0.12.0 → magic_pocket_cli-0.14.0}/pocket_cli/resources/s3.py +0 -0
  52. {magic_pocket_cli-0.12.0 → magic_pocket_cli-0.14.0}/pocket_cli/resources/tidb.py +0 -0
  53. {magic_pocket_cli-0.12.0 → magic_pocket_cli-0.14.0}/pocket_cli/resources/upstash.py +0 -0
  54. {magic_pocket_cli-0.12.0 → magic_pocket_cli-0.14.0}/pocket_cli/resources/vpc.py +0 -0
  55. {magic_pocket_cli-0.12.0 → magic_pocket_cli-0.14.0}/pocket_cli/templates/cloudformation/awscontainer.yaml +0 -0
  56. {magic_pocket_cli-0.12.0 → magic_pocket_cli-0.14.0}/pocket_cli/templates/cloudformation/cf_function_api_host.js +0 -0
  57. {magic_pocket_cli-0.12.0 → magic_pocket_cli-0.14.0}/pocket_cli/templates/cloudformation/cf_function_host_redirect_prelude.js +0 -0
  58. {magic_pocket_cli-0.12.0 → magic_pocket_cli-0.14.0}/pocket_cli/templates/cloudformation/cf_function_spa_auth.js +0 -0
  59. {magic_pocket_cli-0.12.0 → magic_pocket_cli-0.14.0}/pocket_cli/templates/cloudformation/cf_function_spa_fallback.js +0 -0
  60. {magic_pocket_cli-0.12.0 → magic_pocket_cli-0.14.0}/pocket_cli/templates/cloudformation/cloudfront.yaml +0 -0
  61. {magic_pocket_cli-0.12.0 → magic_pocket_cli-0.14.0}/pocket_cli/templates/cloudformation/cloudfront_acm.yaml +0 -0
  62. {magic_pocket_cli-0.12.0 → magic_pocket_cli-0.14.0}/pocket_cli/templates/cloudformation/cloudfront_keys.yaml +0 -0
  63. {magic_pocket_cli-0.12.0 → magic_pocket_cli-0.14.0}/pocket_cli/templates/cloudformation/cloudfront_waf.yaml +0 -0
  64. {magic_pocket_cli-0.12.0 → magic_pocket_cli-0.14.0}/pocket_cli/templates/cloudformation/vpc.yaml +0 -0
  65. {magic_pocket_cli-0.12.0 → magic_pocket_cli-0.14.0}/pocket_cli/templates/init/django-dotenv.env +0 -0
  66. {magic_pocket_cli-0.12.0 → magic_pocket_cli-0.14.0}/pocket_cli/templates/init/django-settings.py +0 -0
  67. {magic_pocket_cli-0.12.0 → magic_pocket_cli-0.14.0}/pocket_cli/templates/init/pocket.Dockerfile +0 -0
  68. {magic_pocket_cli-0.12.0 → magic_pocket_cli-0.14.0}/pocket_cli/templates/init/pocket_simple.toml +0 -0
@@ -1,13 +1,13 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: magic-pocket-cli
3
- Version: 0.12.0
3
+ Version: 0.14.0
4
4
  Summary: CLI and deploy tools for magic-pocket.
5
5
  Requires-Python: >=3.10
6
6
  Requires-Dist: awscrt>=0.19.0
7
7
  Requires-Dist: click>=8.1.7
8
8
  Requires-Dist: deepdiff>=6.7.1
9
9
  Requires-Dist: jinja2>=3.1.3
10
- Requires-Dist: magic-pocket>=0.12.0
10
+ Requires-Dist: magic-pocket>=0.13.0
11
11
  Requires-Dist: pathspec>=1.0.4
12
12
  Requires-Dist: python-on-whales>=0.68.0
13
13
  Requires-Dist: pyyaml>=6.0.1
@@ -3,7 +3,7 @@ import webbrowser
3
3
 
4
4
  import click
5
5
 
6
- from pocket.context import Context
6
+ from pocket.context import Context, deploy_hash_report
7
7
  from pocket.utils import echo
8
8
  from pocket_cli.mediator import Mediator
9
9
  from pocket_cli.resources.aws.state import StateStore
@@ -173,6 +173,11 @@ def _deploy_pipeline(context: Context, *, openpath=None, skip_frontend=False):
173
173
  promote 時は context.awscontainer.promote_commit_hash が設定済みで、
174
174
  deploy_init 内の image build が retag に置き換わる以外は deploy と同一。
175
175
  """
176
+ # DEPLOY_HASH の解決結果を deploy 時に 1 回可視化する (env 伝播漏れで
177
+ # 黙って git short hash に落ちる footgun の早期発見用)。
178
+ deploy_hash_message = deploy_hash_report(context)
179
+ if deploy_hash_message:
180
+ echo.info(deploy_hash_message)
176
181
  # CodeBuildがソースアップロードにstate bucketを必要とするため、先に作成
177
182
  state_store = _create_state_store(context)
178
183
  state_store.ensure_bucket()
@@ -17,6 +17,7 @@ import hashlib
17
17
  import click
18
18
  import yaml
19
19
 
20
+ from pocket import __version__
20
21
  from pocket.context import Context
21
22
  from pocket.utils import echo
22
23
  from pocket_cli.cli.deploy_cli import get_resources
@@ -180,6 +181,32 @@ def _report_secret_path(info: dict) -> None:
180
181
  echo.info("%s: %s" % (info["key"], label))
181
182
 
182
183
 
184
+ def _warn_runtime_bump_required(stage: str) -> None:
185
+ """secret-paths 移設で旧パスが削除されることの事前警告。
186
+
187
+ 移設は copy→旧削除を一括で行うため、この stage に deploy 済みの Lambda runtime
188
+ (magic-pocket[django]) が古いままだと、旧パス (削除済) を参照して DATABASE_URL を
189
+ 解決できず cold start の INIT で落ちる。移設後に runtime を CLI と同版へ bump して
190
+ 再デプロイするまで stage が黙って壊れる footgun を、移設前に明示する
191
+ (template-hash が「先に deploy」で中断するのと同じ思想の事前ガード)。
192
+ """
193
+ echo.warning(
194
+ "⚠ 移設すると旧パスは削除されます (copy→旧削除は一括)。この stage に "
195
+ "deploy 済みの Lambda runtime (magic-pocket[django]) が古いままだと、"
196
+ "旧パスを参照して DATABASE_URL を解決できず cold start の INIT で落ちます。"
197
+ )
198
+ echo.warning(
199
+ " 移設後は速やかに runtime を CLI と同版へ上げて再デプロイしてください:\n"
200
+ " uv add 'magic-pocket[django]>=%s'\n"
201
+ " pocket deploy --stage=%s (Django なら pocket django deploy --stage=%s)"
202
+ % (__version__, stage, stage)
203
+ )
204
+ echo.warning(
205
+ " ※ copy と旧削除が一括のため完全な無停止移行にはなりません。warm instance は "
206
+ "recycle まで生存するので、移設直後に再デプロイすれば影響を最小化できます。"
207
+ )
208
+
209
+
183
210
  def _run_secret_paths(stage: str, *, yes: bool, dry_run: bool) -> None:
184
211
  """stored user secret を旧キー基準→新 type 基準パスへ移設する (冪等)。"""
185
212
  from pocket_cli.mediator import Mediator
@@ -207,6 +234,7 @@ def _run_secret_paths(stage: str, *, yes: bool, dry_run: bool) -> None:
207
234
  "secret-paths: 移設対象はありません (全て移行済み or 未 provision)。"
208
235
  )
209
236
  return
237
+ _warn_runtime_bump_required(stage)
210
238
  if dry_run:
211
239
  echo.info("secret-paths: --dry-run のため実行はしません。")
212
240
  return
@@ -2,6 +2,8 @@ from __future__ import annotations
2
2
 
3
3
  import json
4
4
  import os
5
+ import shutil
6
+ import stat
5
7
  import time
6
8
  import zipfile
7
9
  from pathlib import Path
@@ -9,6 +11,7 @@ from pathlib import Path
9
11
  import boto3
10
12
  from botocore.exceptions import ClientError
11
13
 
14
+ from pocket.utils import echo
12
15
  from pocket_cli.resources.aws.builders.dockerignore import (
13
16
  load_dockerignore,
14
17
  should_include,
@@ -287,7 +290,7 @@ class CodeBuildBuilder:
287
290
  rel = os.path.join(rel_dir, filename) if rel_dir else filename
288
291
  if not should_include(rel, spec):
289
292
  continue
290
- zf.write(os.path.join(dirpath, filename), rel)
293
+ self._add_source_file(zf, os.path.join(dirpath, filename), rel)
291
294
 
292
295
  zip_size_mb = buf.tell() / (1024 * 1024)
293
296
  buf.seek(0)
@@ -296,6 +299,31 @@ class CodeBuildBuilder:
296
299
  buf.close()
297
300
  print(" アップロード完了 (%.1f MB)" % zip_size_mb)
298
301
 
302
+ def _add_source_file(
303
+ self, zf: zipfile.ZipFile, full_path: str, arcname: str
304
+ ) -> None:
305
+ """1 ファイルを zip に追加する(forge VM 由来の footgun を吸収)。
306
+
307
+ - 壊れた symlink(host 側参照など、実体が無い)は skip して warning。
308
+ そのまま ``zipfile.write`` すると ``FileNotFoundError`` で source zip
309
+ 作成ごと落ち、deploy 全体が失敗するため。
310
+ - 通常ファイルの権限を 0644 / 実行ファイルを 0755 に正規化する。
311
+ forge VM では ``sed -i`` 編集等で 0600 のファイルが生まれやすく、
312
+ そのまま image に入ると Lambda の非 root 実行ユーザーが読めず起動時に
313
+ panic する。mode を落として(他ユーザー read を保証して)防ぐ。
314
+ """
315
+ if not os.path.exists(full_path):
316
+ # os.path.exists は symlink を辿るため、壊れた symlink で False
317
+ echo.warning(" 壊れた symlink を skip: %s" % arcname)
318
+ return
319
+ zi = zipfile.ZipInfo.from_file(full_path, arcname)
320
+ zi.compress_type = zipfile.ZIP_DEFLATED
321
+ perm = (zi.external_attr >> 16) & 0o777
322
+ new_perm = 0o755 if perm & stat.S_IXUSR else 0o644
323
+ zi.external_attr = (new_perm << 16) | (zi.external_attr & 0xFFFF)
324
+ with open(full_path, "rb") as src, zf.open(zi, "w") as dst:
325
+ shutil.copyfileobj(src, dst)
326
+
299
327
  # --- ビルド実行 ---
300
328
 
301
329
  def _start_build(
@@ -29,9 +29,9 @@ class Dsql:
29
29
  for cluster in page["clusters"]:
30
30
  identifier = cluster["identifier"]
31
31
  try:
32
- detail = self._client.get_cluster(Identifier=identifier)
32
+ detail = self._client.get_cluster(identifier=identifier)
33
33
  tags = self._client.list_tags_for_resource(
34
- ResourceArn=detail["arn"]
34
+ resourceArn=detail["arn"]
35
35
  )
36
36
  if tags.get("tags", {}).get("Name") == self.context.tag_name:
37
37
  return detail
@@ -102,7 +102,7 @@ class Dsql:
102
102
  if not self.identifier:
103
103
  return
104
104
  echo.log("Deleting DSQL cluster: %s" % self.identifier)
105
- self._client.delete_cluster(Identifier=self.identifier)
105
+ self._client.delete_cluster(identifier=self.identifier)
106
106
  echo.log("Waiting for DSQL cluster deletion...")
107
107
  self._wait_deleted(self.identifier, timeout=600)
108
108
  echo.success("DSQL cluster was deleted.")
@@ -110,7 +110,7 @@ class Dsql:
110
110
  def _wait_active(self, identifier: str, timeout: int = 600, interval: int = 5):
111
111
  for i in range(timeout // interval):
112
112
  try:
113
- res = self._client.get_cluster(Identifier=identifier)
113
+ res = self._client.get_cluster(identifier=identifier)
114
114
  if res["status"] == "ACTIVE":
115
115
  print("")
116
116
  return
@@ -125,7 +125,7 @@ class Dsql:
125
125
  def _wait_deleted(self, identifier: str, timeout: int = 600, interval: int = 5):
126
126
  for i in range(timeout // interval):
127
127
  try:
128
- self._client.get_cluster(Identifier=identifier)
128
+ self._client.get_cluster(identifier=identifier)
129
129
  except ClientError as e:
130
130
  if e.response["Error"]["Code"] == "ResourceNotFoundException":
131
131
  print("")
@@ -329,8 +329,11 @@ class Neon:
329
329
  # store_url の呼び出し側も branch 不在を確認済だが、直接呼ばれた場合の防御。
330
330
  if self.branch is not None:
331
331
  return
332
- del self.branch
333
- del self.endpoint
332
+ # branch/endpoint の cached_property を無効化し、branch 作成後の再取得を
333
+ # 強制する。endpoint はこの経路で一度も access していない (cache 未生成) ため
334
+ # 素の `del` は AttributeError になる。access 有無に依らず安全な pop で消す。
335
+ self.__dict__.pop("branch", None)
336
+ self.__dict__.pop("endpoint", None)
334
337
  data = {
335
338
  "branch": {
336
339
  "name": self.context.branch_name,
@@ -1,10 +1,10 @@
1
1
  [project]
2
2
  name = "magic-pocket-cli"
3
- version = "0.12.0"
3
+ version = "0.14.0"
4
4
  description = "CLI and deploy tools for magic-pocket."
5
5
  requires-python = ">=3.10"
6
6
  dependencies = [
7
- "magic-pocket>=0.12.0",
7
+ "magic-pocket>=0.13.0",
8
8
  "click>=8.1.7",
9
9
  "jinja2>=3.1.3",
10
10
  "python-on-whales>=0.68.0",