xgic-payload-cms-cli 0.2.3__tar.gz → 0.2.5__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.
- {xgic_payload_cms_cli-0.2.3 → xgic_payload_cms_cli-0.2.5}/PKG-INFO +2 -2
- {xgic_payload_cms_cli-0.2.3 → xgic_payload_cms_cli-0.2.5}/README.md +1 -1
- {xgic_payload_cms_cli-0.2.3 → xgic_payload_cms_cli-0.2.5}/pyproject.toml +1 -1
- {xgic_payload_cms_cli-0.2.3 → xgic_payload_cms_cli-0.2.5}/src/xgic/cli/payload/__init__.py +1 -1
- {xgic_payload_cms_cli-0.2.3 → xgic_payload_cms_cli-0.2.5}/src/xgic/cli/payload/commands/reset.py +30 -10
- {xgic_payload_cms_cli-0.2.3 → xgic_payload_cms_cli-0.2.5}/src/xgic/cli/payload/config.py +5 -1
- {xgic_payload_cms_cli-0.2.3 → xgic_payload_cms_cli-0.2.5}/src/xgic/cli/payload/env_helpers.py +5 -0
- {xgic_payload_cms_cli-0.2.3 → xgic_payload_cms_cli-0.2.5}/src/xgic/cli/payload/layout.py +6 -1
- {xgic_payload_cms_cli-0.2.3 → xgic_payload_cms_cli-0.2.5}/src/xgic/cli/payload/project.py +77 -11
- {xgic_payload_cms_cli-0.2.3 → xgic_payload_cms_cli-0.2.5}/.gitignore +0 -0
- {xgic_payload_cms_cli-0.2.3 → xgic_payload_cms_cli-0.2.5}/LICENSE +0 -0
- {xgic_payload_cms_cli-0.2.3 → xgic_payload_cms_cli-0.2.5}/NOTICE +0 -0
- {xgic_payload_cms_cli-0.2.3 → xgic_payload_cms_cli-0.2.5}/src/xgic/cli/payload/commands/__init__.py +0 -0
- {xgic_payload_cms_cli-0.2.3 → xgic_payload_cms_cli-0.2.5}/src/xgic/cli/payload/commands/dev.py +0 -0
- {xgic_payload_cms_cli-0.2.3 → xgic_payload_cms_cli-0.2.5}/src/xgic/cli/payload/commands/payload_env.py +0 -0
- {xgic_payload_cms_cli-0.2.3 → xgic_payload_cms_cli-0.2.5}/src/xgic/cli/payload/commands/schema.py +0 -0
- {xgic_payload_cms_cli-0.2.3 → xgic_payload_cms_cli-0.2.5}/src/xgic/cli/payload/commands/setup.py +0 -0
- {xgic_payload_cms_cli-0.2.3 → xgic_payload_cms_cli-0.2.5}/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.
|
|
3
|
+
Version: 0.2.5
|
|
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.
|
|
93
|
+
"xgic-payload-cms-cli>=0.2.5,<0.3"
|
|
94
94
|
xgic payload --help
|
|
95
95
|
```
|
|
96
96
|
|
|
@@ -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.
|
|
58
|
+
"xgic-payload-cms-cli>=0.2.5,<0.3"
|
|
59
59
|
xgic payload --help
|
|
60
60
|
```
|
|
61
61
|
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "xgic-payload-cms-cli"
|
|
7
|
-
version = "0.2.
|
|
7
|
+
version = "0.2.5"
|
|
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" }
|
{xgic_payload_cms_cli-0.2.3 → xgic_payload_cms_cli-0.2.5}/src/xgic/cli/payload/commands/reset.py
RENAMED
|
@@ -3,23 +3,31 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
import shutil
|
|
6
|
-
from pathlib import Path
|
|
7
6
|
|
|
8
7
|
from xgic.cli.app import CommandContext
|
|
9
8
|
from xgic.cli.payload.config import (
|
|
10
9
|
get_db_config,
|
|
11
10
|
get_db_profile,
|
|
12
|
-
get_payload_project_name,
|
|
13
11
|
make_payload_docker_controller,
|
|
14
12
|
)
|
|
15
13
|
from xgic.cli.payload.env_helpers import perform_env_regenerate
|
|
16
|
-
from xgic.cli.payload.
|
|
14
|
+
from xgic.cli.payload.layout import (
|
|
15
|
+
display_project_location,
|
|
16
|
+
is_workspace_root,
|
|
17
|
+
resolve_project_dir,
|
|
18
|
+
)
|
|
19
|
+
from xgic.cli.payload.project import (
|
|
20
|
+
ensure_payload_project,
|
|
21
|
+
load_create_payload_config,
|
|
22
|
+
)
|
|
17
23
|
from xgic.cli.utils.output import print_info, print_success, print_warning
|
|
18
24
|
|
|
19
25
|
|
|
20
26
|
def run_reset(ctx: CommandContext) -> int:
|
|
21
27
|
"""Delete generated project folder and reset the active DB volume.
|
|
22
28
|
|
|
29
|
+
Uses ``projectDir`` (same resolver as setup), not ``projectName``.
|
|
30
|
+
App-root layout (workspace ``.``) is never ``rmtree``'d.
|
|
23
31
|
Credentials in ``.env`` are left alone unless ``--rotate-credentials``.
|
|
24
32
|
"""
|
|
25
33
|
dry_run = bool(getattr(ctx.args, "dry_run", False))
|
|
@@ -27,13 +35,20 @@ def run_reset(ctx: CommandContext) -> int:
|
|
|
27
35
|
rotate = bool(getattr(ctx.args, "rotate_credentials", False))
|
|
28
36
|
|
|
29
37
|
docker = make_payload_docker_controller(ctx.env)
|
|
30
|
-
|
|
31
|
-
|
|
38
|
+
project_dir = resolve_project_dir(load_create_payload_config())
|
|
39
|
+
skip_dir = is_workspace_root(project_dir)
|
|
40
|
+
location = display_project_location(project_dir)
|
|
32
41
|
db_service = get_db_profile()
|
|
33
42
|
db_volume = docker.db_volume_name(db_service)
|
|
34
43
|
|
|
35
44
|
print_info("Planned actions for reset:")
|
|
36
|
-
|
|
45
|
+
if skip_dir:
|
|
46
|
+
print_info(
|
|
47
|
+
" - Skip project directory delete "
|
|
48
|
+
f"({location} is the workspace; not removing .git / .devcontainer)"
|
|
49
|
+
)
|
|
50
|
+
else:
|
|
51
|
+
print_info(f" - Delete directory: {location}")
|
|
37
52
|
print_info(f" - Remove Docker volume: {db_volume}")
|
|
38
53
|
if rotate:
|
|
39
54
|
print_warning(" - ALSO rotate database credentials (DANGEROUS)")
|
|
@@ -50,11 +65,16 @@ def run_reset(ctx: CommandContext) -> int:
|
|
|
50
65
|
|
|
51
66
|
print_info("Performing reset...")
|
|
52
67
|
|
|
53
|
-
if
|
|
54
|
-
|
|
55
|
-
|
|
68
|
+
if skip_dir:
|
|
69
|
+
print_info(
|
|
70
|
+
"App-root layout: left workspace files in place. "
|
|
71
|
+
"Remove generated app sources manually if you need a clean scaffold."
|
|
72
|
+
)
|
|
73
|
+
elif project_dir.exists():
|
|
74
|
+
shutil.rmtree(project_dir)
|
|
75
|
+
print_success(f"Deleted project directory: {location}")
|
|
56
76
|
else:
|
|
57
|
-
print_info(f"Project directory {
|
|
77
|
+
print_info(f"Project directory {location} did not exist.")
|
|
58
78
|
|
|
59
79
|
docker.rm_service(db_service, force=True, stop=True, remove_volumes=False)
|
|
60
80
|
|
|
@@ -191,8 +191,10 @@ def db_ready(
|
|
|
191
191
|
except Exception:
|
|
192
192
|
return False
|
|
193
193
|
|
|
194
|
-
|
|
194
|
+
db_name, db_user = get_db_config(config_file)
|
|
195
195
|
try:
|
|
196
|
+
# -d is required: libpq defaults the database name to the user
|
|
197
|
+
# (POSTGRES_USER=payload) while POSTGRES_DB is payload_db.
|
|
196
198
|
result = docker._run_compose( # noqa: SLF001
|
|
197
199
|
"exec",
|
|
198
200
|
"-T",
|
|
@@ -200,6 +202,8 @@ def db_ready(
|
|
|
200
202
|
"pg_isready",
|
|
201
203
|
"-U",
|
|
202
204
|
db_user,
|
|
205
|
+
"-d",
|
|
206
|
+
db_name,
|
|
203
207
|
capture_output=True,
|
|
204
208
|
check=False,
|
|
205
209
|
)
|
{xgic_payload_cms_cli-0.2.3 → xgic_payload_cms_cli-0.2.5}/src/xgic/cli/payload/env_helpers.py
RENAMED
|
@@ -53,9 +53,14 @@ DATABASE_URL=mongodb://{db_user}:{mongo_pass}@mongodb:27017/{db_name}?authSource
|
|
|
53
53
|
"""
|
|
54
54
|
|
|
55
55
|
pg_pass = secrets.token_hex(16)
|
|
56
|
+
# PGUSER/PGDATABASE are libpq defaults. Without them, `pg_isready -U payload`
|
|
57
|
+
# and other clients open a database named after the user (`payload`) while
|
|
58
|
+
# POSTGRES_DB is `payload_db`.
|
|
56
59
|
return f"""POSTGRES_USER={db_user}
|
|
57
60
|
POSTGRES_PASSWORD={pg_pass}
|
|
58
61
|
POSTGRES_DB={db_name}
|
|
62
|
+
PGUSER={db_user}
|
|
63
|
+
PGDATABASE={db_name}
|
|
59
64
|
PAYLOAD_SECRET={payload_secret}
|
|
60
65
|
DATABASE_URL=postgres://{db_user}:{pg_pass}@postgres:5432/{db_name}
|
|
61
66
|
"""
|
|
@@ -83,8 +83,13 @@ def create_payload_target_arg(project_dir: Path) -> str:
|
|
|
83
83
|
return project_dir.as_posix().strip("/")
|
|
84
84
|
|
|
85
85
|
|
|
86
|
+
def is_workspace_root(project_dir: Path) -> bool:
|
|
87
|
+
"""Return True when *project_dir* is the workspace root (app-root layout)."""
|
|
88
|
+
return project_dir == Path(".") or str(project_dir).strip() in (".", "./", "")
|
|
89
|
+
|
|
90
|
+
|
|
86
91
|
def display_project_location(project_dir: Path) -> str:
|
|
87
92
|
"""Human-readable location for logs."""
|
|
88
|
-
if project_dir
|
|
93
|
+
if is_workspace_root(project_dir):
|
|
89
94
|
return "workspace root (.)"
|
|
90
95
|
return f"{project_dir.as_posix()}/"
|
|
@@ -100,6 +100,25 @@ def pnpx_allow_build_args(
|
|
|
100
100
|
return [f"--allow-build={name}" for name in packages]
|
|
101
101
|
|
|
102
102
|
|
|
103
|
+
def detect_pnpm_major() -> int | None:
|
|
104
|
+
"""Return the major version of ``pnpm`` on PATH, or None if unknown."""
|
|
105
|
+
try:
|
|
106
|
+
result = subprocess.run(
|
|
107
|
+
["pnpm", "--version"],
|
|
108
|
+
check=False,
|
|
109
|
+
capture_output=True,
|
|
110
|
+
text=True,
|
|
111
|
+
)
|
|
112
|
+
except OSError:
|
|
113
|
+
return None
|
|
114
|
+
if result.returncode != 0:
|
|
115
|
+
return None
|
|
116
|
+
first = result.stdout.strip().split(".", 1)[0]
|
|
117
|
+
if first.isdigit():
|
|
118
|
+
return int(first)
|
|
119
|
+
return None
|
|
120
|
+
|
|
121
|
+
|
|
103
122
|
def merge_package_json_only_built(
|
|
104
123
|
text: str, names: tuple[str, ...] = NATIVE_PNPM_BUILDS
|
|
105
124
|
) -> str:
|
|
@@ -117,6 +136,41 @@ def merge_package_json_only_built(
|
|
|
117
136
|
return json.dumps(data, indent=2) + "\n"
|
|
118
137
|
|
|
119
138
|
|
|
139
|
+
def strip_package_json_only_built(text: str) -> str:
|
|
140
|
+
"""Remove ``pnpm.onlyBuiltDependencies`` (pnpm 11+ ignores that field)."""
|
|
141
|
+
data = json.loads(text)
|
|
142
|
+
pnpm = data.get("pnpm")
|
|
143
|
+
if not isinstance(pnpm, dict) or "onlyBuiltDependencies" not in pnpm:
|
|
144
|
+
return text
|
|
145
|
+
pnpm.pop("onlyBuiltDependencies", None)
|
|
146
|
+
if pnpm:
|
|
147
|
+
data["pnpm"] = pnpm
|
|
148
|
+
else:
|
|
149
|
+
data.pop("pnpm", None)
|
|
150
|
+
return json.dumps(data, indent=2) + "\n"
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
def yaml_map_key(name: str) -> str:
|
|
154
|
+
"""Quote YAML mapping keys that are not plain scalars (e.g. ``@swc/core``)."""
|
|
155
|
+
if not name or name[0] in "@`&*!|>%{}[]'\"" or any(
|
|
156
|
+
ch in name for ch in ":#"
|
|
157
|
+
):
|
|
158
|
+
return json.dumps(name)
|
|
159
|
+
return name
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def parse_yaml_map_key(raw: str) -> str:
|
|
163
|
+
"""Strip optional YAML quotes from a mapping key token."""
|
|
164
|
+
raw = raw.strip()
|
|
165
|
+
if len(raw) >= 2 and raw[0] == raw[-1] and raw[0] in "\"'":
|
|
166
|
+
return raw[1:-1]
|
|
167
|
+
return raw
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
def _allow_builds_block(names: list[str]) -> list[str]:
|
|
171
|
+
return ["allowBuilds:"] + [f" {yaml_map_key(n)}: true" for n in names]
|
|
172
|
+
|
|
173
|
+
|
|
120
174
|
def merge_workspace_allow_builds(
|
|
121
175
|
text: str, names: tuple[str, ...] = NATIVE_PNPM_BUILDS
|
|
122
176
|
) -> str:
|
|
@@ -124,6 +178,7 @@ def merge_workspace_allow_builds(
|
|
|
124
178
|
|
|
125
179
|
Avoids a YAML dependency: only handles the generated website-template
|
|
126
180
|
shape (a top-level ``allowBuilds:`` mapping of ``name: true``).
|
|
181
|
+
Always re-emits the block so unquoted ``@`` keys from 0.2.3 are repaired.
|
|
127
182
|
"""
|
|
128
183
|
normalized = text if text.endswith("\n") or text == "" else text + "\n"
|
|
129
184
|
lines = normalized.splitlines()
|
|
@@ -132,7 +187,7 @@ def merge_workspace_allow_builds(
|
|
|
132
187
|
None,
|
|
133
188
|
)
|
|
134
189
|
if start is None:
|
|
135
|
-
extra =
|
|
190
|
+
extra = _allow_builds_block(list(names))
|
|
136
191
|
return normalized + "\n".join(extra) + "\n"
|
|
137
192
|
|
|
138
193
|
existing: list[str] = []
|
|
@@ -143,35 +198,46 @@ def merge_workspace_allow_builds(
|
|
|
143
198
|
end += 1
|
|
144
199
|
continue
|
|
145
200
|
if ln.startswith(" ") or ln.startswith("\t"):
|
|
146
|
-
|
|
201
|
+
key_raw = ln.strip().split(":", 1)[0]
|
|
202
|
+
existing.append(parse_yaml_map_key(key_raw))
|
|
147
203
|
end += 1
|
|
148
204
|
continue
|
|
149
205
|
break
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
insert = [f" {n}: true" for n in missing]
|
|
154
|
-
new_lines = lines[:end] + insert + lines[end:]
|
|
206
|
+
merged = list(dict.fromkeys([*existing, *names]))
|
|
207
|
+
block = _allow_builds_block(merged)
|
|
208
|
+
new_lines = lines[:start] + block + lines[end:]
|
|
155
209
|
return "\n".join(new_lines) + "\n"
|
|
156
210
|
|
|
157
211
|
|
|
158
212
|
def ensure_native_pnpm_builds(
|
|
159
213
|
project_dir: Path, *, quiet: bool = False
|
|
160
214
|
) -> None:
|
|
161
|
-
"""Allow-list native install scripts in a generated Payload CMS app.
|
|
215
|
+
"""Allow-list native install scripts in a generated Payload CMS app.
|
|
216
|
+
|
|
217
|
+
pnpm 10 reads ``package.json#pnpm.onlyBuiltDependencies``. pnpm 11+
|
|
218
|
+
ignores that field and uses ``pnpm-workspace.yaml`` ``allowBuilds``.
|
|
219
|
+
"""
|
|
162
220
|
changed = False
|
|
163
221
|
workspace = project_dir / "pnpm-workspace.yaml"
|
|
164
222
|
package_json = project_dir / "package.json"
|
|
223
|
+
major = detect_pnpm_major()
|
|
165
224
|
try:
|
|
166
|
-
if workspace.is_file():
|
|
167
|
-
old =
|
|
225
|
+
if workspace.is_file() or (major is not None and major >= 11):
|
|
226
|
+
old = (
|
|
227
|
+
workspace.read_text(encoding="utf-8")
|
|
228
|
+
if workspace.is_file()
|
|
229
|
+
else ""
|
|
230
|
+
)
|
|
168
231
|
new = merge_workspace_allow_builds(old)
|
|
169
232
|
if new != old:
|
|
170
233
|
workspace.write_text(new, encoding="utf-8")
|
|
171
234
|
changed = True
|
|
172
235
|
if package_json.is_file():
|
|
173
236
|
old = package_json.read_text(encoding="utf-8")
|
|
174
|
-
|
|
237
|
+
if major is not None and major >= 11:
|
|
238
|
+
new = strip_package_json_only_built(old)
|
|
239
|
+
else:
|
|
240
|
+
new = merge_package_json_only_built(old)
|
|
175
241
|
if new != old:
|
|
176
242
|
package_json.write_text(new, encoding="utf-8")
|
|
177
243
|
changed = True
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{xgic_payload_cms_cli-0.2.3 → xgic_payload_cms_cli-0.2.5}/src/xgic/cli/payload/commands/__init__.py
RENAMED
|
File without changes
|
{xgic_payload_cms_cli-0.2.3 → xgic_payload_cms_cli-0.2.5}/src/xgic/cli/payload/commands/dev.py
RENAMED
|
File without changes
|
|
File without changes
|
{xgic_payload_cms_cli-0.2.3 → xgic_payload_cms_cli-0.2.5}/src/xgic/cli/payload/commands/schema.py
RENAMED
|
File without changes
|
{xgic_payload_cms_cli-0.2.3 → xgic_payload_cms_cli-0.2.5}/src/xgic/cli/payload/commands/setup.py
RENAMED
|
File without changes
|
|
File without changes
|