xgic-payload-cms-cli 0.2.1__tar.gz → 0.2.2__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 (18) hide show
  1. {xgic_payload_cms_cli-0.2.1 → xgic_payload_cms_cli-0.2.2}/PKG-INFO +15 -8
  2. {xgic_payload_cms_cli-0.2.1 → xgic_payload_cms_cli-0.2.2}/README.md +14 -7
  3. {xgic_payload_cms_cli-0.2.1 → xgic_payload_cms_cli-0.2.2}/pyproject.toml +1 -1
  4. {xgic_payload_cms_cli-0.2.1 → xgic_payload_cms_cli-0.2.2}/src/xgic/cli/payload/__init__.py +3 -1
  5. {xgic_payload_cms_cli-0.2.1 → xgic_payload_cms_cli-0.2.2}/src/xgic/cli/payload/commands/dev.py +2 -2
  6. {xgic_payload_cms_cli-0.2.1 → xgic_payload_cms_cli-0.2.2}/src/xgic/cli/payload/config.py +62 -1
  7. {xgic_payload_cms_cli-0.2.1 → xgic_payload_cms_cli-0.2.2}/src/xgic/cli/payload/project.py +16 -11
  8. {xgic_payload_cms_cli-0.2.1 → xgic_payload_cms_cli-0.2.2}/.gitignore +0 -0
  9. {xgic_payload_cms_cli-0.2.1 → xgic_payload_cms_cli-0.2.2}/LICENSE +0 -0
  10. {xgic_payload_cms_cli-0.2.1 → xgic_payload_cms_cli-0.2.2}/NOTICE +0 -0
  11. {xgic_payload_cms_cli-0.2.1 → xgic_payload_cms_cli-0.2.2}/src/xgic/cli/payload/commands/__init__.py +0 -0
  12. {xgic_payload_cms_cli-0.2.1 → xgic_payload_cms_cli-0.2.2}/src/xgic/cli/payload/commands/payload_env.py +0 -0
  13. {xgic_payload_cms_cli-0.2.1 → xgic_payload_cms_cli-0.2.2}/src/xgic/cli/payload/commands/reset.py +0 -0
  14. {xgic_payload_cms_cli-0.2.1 → xgic_payload_cms_cli-0.2.2}/src/xgic/cli/payload/commands/schema.py +0 -0
  15. {xgic_payload_cms_cli-0.2.1 → xgic_payload_cms_cli-0.2.2}/src/xgic/cli/payload/commands/setup.py +0 -0
  16. {xgic_payload_cms_cli-0.2.1 → xgic_payload_cms_cli-0.2.2}/src/xgic/cli/payload/env_helpers.py +0 -0
  17. {xgic_payload_cms_cli-0.2.1 → xgic_payload_cms_cli-0.2.2}/src/xgic/cli/payload/layout.py +0 -0
  18. {xgic_payload_cms_cli-0.2.1 → xgic_payload_cms_cli-0.2.2}/src/xgic/cli/payload/plugin.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: xgic-payload-cms-cli
3
- Version: 0.2.1
3
+ Version: 0.2.2
4
4
  Summary: XGIC CLI Payload CMS module - product commands and helpers (xgic.cli.payload).
5
5
  Project-URL: Homepage, https://github.com/xgic/payload-cms-cli
6
6
  Project-URL: Repository, https://github.com/xgic/payload-cms-cli
@@ -90,7 +90,7 @@ One brand (`xgic`), clear ownership, and dry-run-friendly destructive paths.
90
90
  uv pip install \
91
91
  "xgic-cli>=0.2.0,<0.3" \
92
92
  "xgic-dev-cli>=0.2.0,<0.3" \
93
- "xgic-payload-cms-cli>=0.2.1,<0.3"
93
+ "xgic-payload-cms-cli>=0.2.2,<0.3"
94
94
  xgic payload --help
95
95
  ```
96
96
 
@@ -105,14 +105,21 @@ xgic payload --help
105
105
 
106
106
  ### Typical Payload session (inside a Dev Container)
107
107
 
108
+ **First session** (one setup command — creates `.devcontainer/.env` if missing, starts the DB profile, scaffolds the app):
109
+
108
110
  ```bash
109
- xgic check
110
- xgic payload env --regenerate --yes # once
111
- xgic payload setup # scaffold app (layout from config)
112
- xgic up --profile postgres # DB if needed
111
+ xgic payload setup
113
112
  xgic payload dev
114
113
  ```
115
114
 
115
+ **Daily** (after the app exists):
116
+
117
+ ```bash
118
+ xgic payload dev
119
+ ```
120
+
121
+ Optional: `xgic payload env` (status), `xgic payload env --regenerate --yes` (rotate credentials only — not required for first setup).
122
+
116
123
  Prefer the end-user template for application work: [payload-cms](https://github.com/xgic/payload-cms).
117
124
 
118
125
  ---
@@ -124,9 +131,9 @@ All product commands nest under **`xgic payload`**:
124
131
  | Command | Purpose |
125
132
  |---------|---------|
126
133
  | `xgic payload dev` | **Primary daily command** — smart start (up if needed, DB check, `pnpm dev`) |
127
- | `xgic payload setup [--quiet]` | Ensure Payload project directory (idempotent) |
134
+ | `xgic payload setup [--quiet]` | **First-run one-shot:** env (if missing) + DB profile + scaffold (idempotent) |
128
135
  | `xgic payload env [--json]` | Product env status (project name, `.env`, services) |
129
- | `xgic payload env --regenerate --yes` | Fresh credentials in `.devcontainer/.env` |
136
+ | `xgic payload env --regenerate --yes` | Rotate credentials in `.devcontainer/.env` (optional; setup creates env when absent) |
130
137
  | `xgic payload schema` | Run template schema generator when present |
131
138
  | `xgic payload reset` | Fast targeted reset (project folder + active DB volume) |
132
139
 
@@ -55,7 +55,7 @@ One brand (`xgic`), clear ownership, and dry-run-friendly destructive paths.
55
55
  uv pip install \
56
56
  "xgic-cli>=0.2.0,<0.3" \
57
57
  "xgic-dev-cli>=0.2.0,<0.3" \
58
- "xgic-payload-cms-cli>=0.2.1,<0.3"
58
+ "xgic-payload-cms-cli>=0.2.2,<0.3"
59
59
  xgic payload --help
60
60
  ```
61
61
 
@@ -70,14 +70,21 @@ xgic payload --help
70
70
 
71
71
  ### Typical Payload session (inside a Dev Container)
72
72
 
73
+ **First session** (one setup command — creates `.devcontainer/.env` if missing, starts the DB profile, scaffolds the app):
74
+
73
75
  ```bash
74
- xgic check
75
- xgic payload env --regenerate --yes # once
76
- xgic payload setup # scaffold app (layout from config)
77
- xgic up --profile postgres # DB if needed
76
+ xgic payload setup
78
77
  xgic payload dev
79
78
  ```
80
79
 
80
+ **Daily** (after the app exists):
81
+
82
+ ```bash
83
+ xgic payload dev
84
+ ```
85
+
86
+ Optional: `xgic payload env` (status), `xgic payload env --regenerate --yes` (rotate credentials only — not required for first setup).
87
+
81
88
  Prefer the end-user template for application work: [payload-cms](https://github.com/xgic/payload-cms).
82
89
 
83
90
  ---
@@ -89,9 +96,9 @@ All product commands nest under **`xgic payload`**:
89
96
  | Command | Purpose |
90
97
  |---------|---------|
91
98
  | `xgic payload dev` | **Primary daily command** — smart start (up if needed, DB check, `pnpm dev`) |
92
- | `xgic payload setup [--quiet]` | Ensure Payload project directory (idempotent) |
99
+ | `xgic payload setup [--quiet]` | **First-run one-shot:** env (if missing) + DB profile + scaffold (idempotent) |
93
100
  | `xgic payload env [--json]` | Product env status (project name, `.env`, services) |
94
- | `xgic payload env --regenerate --yes` | Fresh credentials in `.devcontainer/.env` |
101
+ | `xgic payload env --regenerate --yes` | Rotate credentials in `.devcontainer/.env` (optional; setup creates env when absent) |
95
102
  | `xgic payload schema` | Run template schema generator when present |
96
103
  | `xgic payload reset` | Fast targeted reset (project folder + active DB volume) |
97
104
 
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "xgic-payload-cms-cli"
7
- version = "0.2.1"
7
+ version = "0.2.2"
8
8
  description = "XGIC CLI Payload CMS module - product commands and helpers (xgic.cli.payload)."
9
9
  readme = "README.md"
10
10
  license = { text = "Apache-2.0" }
@@ -5,6 +5,7 @@ from xgic.cli.payload.config import (
5
5
  DEFAULT_CONFIG_FILE,
6
6
  DEFAULT_PRIMARY_SERVICE,
7
7
  db_ready,
8
+ get_compose_project_name,
8
9
  get_db_config,
9
10
  get_db_profile,
10
11
  get_payload_project_name,
@@ -20,7 +21,7 @@ from xgic.cli.payload.project import (
20
21
  load_create_payload_config,
21
22
  )
22
23
 
23
- __version__ = "0.2.0"
24
+ __version__ = "0.2.2"
24
25
 
25
26
  __all__ = [
26
27
  "DEFAULT_COMPOSE_PROJECT",
@@ -30,6 +31,7 @@ __all__ = [
30
31
  "db_ready",
31
32
  "ensure_payload_project",
32
33
  "generate_fresh_env_content",
34
+ "get_compose_project_name",
33
35
  "get_db_config",
34
36
  "get_db_profile",
35
37
  "get_payload_project_name",
@@ -8,9 +8,9 @@ from pathlib import Path
8
8
  from xgic.cli.app import CommandContext
9
9
  from xgic.cli.core.environment import EnvironmentType
10
10
  from xgic.cli.payload.config import (
11
- DEFAULT_COMPOSE_PROJECT,
12
11
  DEFAULT_PRIMARY_SERVICE,
13
12
  db_ready,
13
+ get_compose_project_name,
14
14
  get_db_profile,
15
15
  make_payload_docker_controller,
16
16
  )
@@ -49,7 +49,7 @@ def run_dev(ctx: CommandContext) -> int:
49
49
  print_info(f"App location: {display_project_location(project_dir)}")
50
50
 
51
51
  docker = make_payload_docker_controller(env)
52
- docker.project_name = DEFAULT_COMPOSE_PROJECT
52
+ docker.project_name = get_compose_project_name()
53
53
  profile = get_db_profile()
54
54
 
55
55
  # Never recreate the primary Dev Container service; only ensure DB.
@@ -35,6 +35,67 @@ def get_payload_project_name(
35
35
  return "my-payload-cms"
36
36
 
37
37
 
38
+ def _is_compose_safe_name(name: str) -> bool:
39
+ """Return True if *name* looks safe for Docker Compose project names."""
40
+ if not name or not name[0].isalnum():
41
+ return False
42
+ return all(c.isalnum() or c in "_-" for c in name) and len(name) <= 63
43
+
44
+
45
+ def _compose_name_from_file(
46
+ compose_path: Path = Path(DEFAULT_COMPOSE_FILE),
47
+ ) -> str | None:
48
+ """Parse top-level ``name:`` from a Compose file (best-effort)."""
49
+ if not compose_path.is_file():
50
+ return None
51
+ try:
52
+ for line in compose_path.read_text(encoding="utf-8").splitlines():
53
+ stripped = line.strip()
54
+ if stripped.startswith("#") or not stripped.startswith("name:"):
55
+ continue
56
+ raw = stripped.split(":", 1)[1].strip().strip("\"'")
57
+ if raw and _is_compose_safe_name(raw.lower()):
58
+ return raw.lower()
59
+ except OSError:
60
+ return None
61
+ return None
62
+
63
+
64
+ def get_compose_project_name(
65
+ config_file: Path = DEFAULT_CONFIG_FILE,
66
+ ) -> str:
67
+ """Return Docker Compose project name for this workspace.
68
+
69
+ Precedence:
70
+ 1. ``XGIC_COMPOSE_PROJECT`` environment variable
71
+ 2. ``composeProjectName`` in create-payload-config.json
72
+ 3. Top-level ``name:`` in ``.devcontainer/docker-compose.yml``
73
+ 4. ``DEFAULT_COMPOSE_PROJECT`` (producer template default)
74
+ """
75
+ import os
76
+
77
+ env_name = os.environ.get("XGIC_COMPOSE_PROJECT", "").strip().lower()
78
+ if env_name and _is_compose_safe_name(env_name):
79
+ return env_name
80
+
81
+ if config_file.exists():
82
+ try:
83
+ with open(config_file, encoding="utf-8") as f:
84
+ data: dict[str, Any] = json.load(f)
85
+ raw = data.get("composeProjectName")
86
+ if isinstance(raw, str) and raw.strip():
87
+ name = raw.strip().lower()
88
+ if _is_compose_safe_name(name):
89
+ return name
90
+ except (json.JSONDecodeError, OSError):
91
+ pass
92
+
93
+ from_file = _compose_name_from_file()
94
+ if from_file:
95
+ return from_file
96
+
97
+ return DEFAULT_COMPOSE_PROJECT
98
+
38
99
  def get_payload_project_dir(
39
100
  config_file: Path = DEFAULT_CONFIG_FILE,
40
101
  ) -> Path:
@@ -101,7 +162,7 @@ def make_payload_docker_controller(
101
162
  return DockerComposeController(
102
163
  env=env,
103
164
  compose_file=DEFAULT_COMPOSE_FILE,
104
- project_name=DEFAULT_COMPOSE_PROJECT,
165
+ project_name=get_compose_project_name(),
105
166
  primary_service=DEFAULT_PRIMARY_SERVICE,
106
167
  )
107
168
 
@@ -13,9 +13,9 @@ from typing import Any
13
13
 
14
14
  from xgic.cli.payload.config import (
15
15
  DEFAULT_COMPOSE_FILE,
16
- DEFAULT_COMPOSE_PROJECT,
17
16
  DEFAULT_CONFIG_FILE,
18
17
  db_ready,
18
+ get_compose_project_name,
19
19
  get_db_profile,
20
20
  make_payload_docker_controller,
21
21
  )
@@ -35,6 +35,7 @@ def load_create_payload_config(
35
35
  "projectName": "my-payload-cms",
36
36
  "projectDir": None,
37
37
  "layout": "auto",
38
+ "composeProjectName": None,
38
39
  "template": "website",
39
40
  "dbAdapter": "postgres",
40
41
  "agent": "none",
@@ -269,9 +270,8 @@ def ensure_db_services(*, quiet: bool = False, wait_seconds: float = 45.0) -> in
269
270
 
270
271
  env = EnvironmentContext.detect()
271
272
  docker = make_payload_docker_controller(env)
272
- # Prefer stable default project name over config projectName for the
273
- # Dev Container stack (matches compose file + VS Code project).
274
- docker.project_name = DEFAULT_COMPOSE_PROJECT
273
+ compose_project = get_compose_project_name()
274
+ docker.project_name = compose_project
275
275
  profile = get_db_profile()
276
276
 
277
277
  if db_ready(docker):
@@ -281,11 +281,14 @@ def ensure_db_services(*, quiet: bool = False, wait_seconds: float = 45.0) -> in
281
281
 
282
282
  if not quiet:
283
283
  print_info(
284
- f"Starting database service only: profile={profile!r}, "
285
- f"service={profile!r} (project {DEFAULT_COMPOSE_PROJECT!r})"
284
+ f"Starting database service only: service={profile!r} "
285
+ f"(project {compose_project!r})"
286
286
  )
287
287
  try:
288
- docker.up(profile=profile, services=[profile])
288
+ # Start the named service without forcing --profile. Services that use
289
+ # profiles still start when listed by name if already enabled; services
290
+ # without profiles (e.g. always-on postgres) work without a profile flag.
291
+ docker.up(services=[profile])
289
292
  except FileNotFoundError:
290
293
  print_warning(
291
294
  "docker not found. Run setup inside the Dev Container "
@@ -329,15 +332,17 @@ def ensure_payload_project(*, quiet: bool = False) -> int:
329
332
  project_dir = resolve_project_dir(cfg)
330
333
  target = create_payload_target_arg(project_dir)
331
334
 
335
+ # First-run: create .devcontainer/.env when missing (no separate
336
+ # `xgic payload env --regenerate --yes` step required).
332
337
  env_rc = ensure_devcontainer_env(quiet=quiet)
333
338
  if env_rc != 0:
334
339
  return env_rc
335
340
 
336
- # Keep Compose project name aligned with the Dev Container stack default
337
- # (not the npm projectName), so CLI and VS Code share one project.
338
- if sync_compose_project_name(DEFAULT_COMPOSE_PROJECT) and not quiet:
341
+ # Compose project name from config (composeProjectName), not npm projectName.
342
+ compose_project = get_compose_project_name()
343
+ if sync_compose_project_name(compose_project) and not quiet:
339
344
  print_info(
340
- f"Docker Compose project name set to {DEFAULT_COMPOSE_PROJECT!r} "
345
+ f"Docker Compose project name set to {compose_project!r} "
341
346
  f"in {DEFAULT_COMPOSE_FILE}"
342
347
  )
343
348