Skip to content

Commit c33f99f

Browse files
authored
Modify create-entry to call Collection.create_entry (#30)
* Use `ContentManager` for creating an entry in a `Collection`. * Fix tests. * Add `__version__.py` dynamic creation so we can have the version inside the app if desired. * Add `__version__.py` to `.gitignore` * Add `--prerelease=allow` to GH actions so we can run with prerelease versions of Render Engine.
1 parent 662ba2d commit c33f99f

File tree

9 files changed

+545
-512
lines changed

9 files changed

+545
-512
lines changed

.github/workflows/publish.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ jobs:
2323
- name: Install uv
2424
uses: astral-sh/setup-uv@v5
2525
- run: echo "SOURCE_DATE_EPOCH=$(git log -1 --pretty=%ct)" >> $GITHUB_ENV
26-
- run: uv build
26+
- run: uv build --prerelease=allow
2727
- uses: pypa/gh-action-pypi-publish@release/v1
2828
with:
2929
packages-dir: ./dist

.github/workflows/test.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ jobs:
2626
uses: astral-sh/setup-uv@v5
2727
- name: Run tests
2828
run: |
29-
uv run -p ${{ matrix.python-version }} pytest
29+
uv run -p ${{ matrix.python-version }} --prerelease=allow pytest
3030
3131
test-entry-point:
3232
runs-on: ubuntu-latest
@@ -36,7 +36,7 @@ jobs:
3636
- name: Install uv
3737
uses: astral-sh/setup-uv@v5
3838
- name: Set up env
39-
run: uv sync
39+
run: uv sync --prerelease=allow
4040
- name: Install package
4141
run: source .venv/bin/activate && uv pip install .
4242
- name: Check entry point

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,3 +166,6 @@ untitled-site/
166166

167167
.DS_Store
168168
.python-version
169+
170+
# We do not want to commit the version file.
171+
__version__.py

pyproject.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ requires-python = ">=3.10"
77
dependencies = [
88
"click>=8.2.1",
99
"cookiecutter>=2.6.0",
10-
"render-engine>=2025.7.1a1",
10+
"render-engine>=2025.11.1a1",
1111
"rich>=14.0.0",
1212
"toml>=0.10.2",
1313
"watchfiles>=1.1.0",
@@ -42,6 +42,7 @@ addopts = ["--cov=src", "--cov-report=term-missing", "-ra", "-q"]
4242

4343
[tool.setuptools_scm]
4444
local_scheme = "no-local-version"
45+
version_file = "src/render_engine_cli/__version__.py"
4546

4647
[tool.ruff]
4748
line-length = 120

src/render_engine_cli/cli.py

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
import datetime
22
import json
3-
import re
4-
import subprocess
53
from pathlib import Path
64

75
import click
@@ -299,6 +297,7 @@ def new_entry(
299297
)
300298
):
301299
raise click.exceptions.BadParameter(f"Unknown collection: {collection}")
300+
# pdb.set_trace()
302301
filepath = Path(_collection.content_path).joinpath(filename)
303302
if filepath.exists():
304303
if not click.confirm(
@@ -310,16 +309,19 @@ def new_entry(
310309
raise TypeError("Both content and content_file provided. At most one may be provided.")
311310
if content_file:
312311
content = content_file
313-
entry = create_collection_entry(content=content or "", collection=_collection, **parsed_args)
314312
if title:
315-
# If we had a title earlier this is where we replace the default that is added by the template handler with
316-
# the one supplied by the user.
317-
entry = re.sub(r"title: Untitled Entry", f"title: {title}", entry)
318-
filepath.write_text(entry)
319-
Console().print(f'New {collection} entry created at "{filepath}"')
320-
321-
if editor:
322-
subprocess.run([editor, filepath])
313+
parsed_args["title"] = title
314+
try:
315+
entry = create_collection_entry(
316+
editor=editor,
317+
filepath=filepath,
318+
content=content or "",
319+
collection=_collection,
320+
**parsed_args,
321+
)
322+
except ValueError as e:
323+
raise click.BadParameter from e
324+
click.echo(entry)
323325

324326

325327
@app.command

src/render_engine_cli/utils.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -130,9 +130,11 @@ def get_available_themes(console: Console, site: Site, theme_name: str) -> list[
130130
return []
131131

132132

133-
def create_collection_entry(content: str | None, collection: Collection, **context):
133+
def create_collection_entry(
134+
filepath: Path, editor: str | None, content: str | None, collection: Collection, **context
135+
) -> str:
134136
"""Creates a new entry for a collection"""
135-
return collection.Parser.create_entry(content=content, **collection._metadata_attrs(), **context)
137+
return collection.create_entry(filepath=filepath, editor=editor, content=content, metadata=context)
136138

137139

138140
def split_args(args: list[str] | None) -> dict[str, str]:

tests/test_cli.py

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -55,21 +55,33 @@ def test_import_lib_gets_site():
5555
pass
5656

5757

58-
def test_collection_call():
58+
def test_collection_call(tmp_path_factory):
5959
"""Tests that you can get content from the parser using `new_entry`"""
6060
test_collection = Collection()
61-
content = create_collection_entry(content=None, collection=test_collection, foo="bar")
62-
post = frontmatter.loads(content)
61+
content_path1 = tmp_path_factory.getbasetemp()
62+
filepath = content_path1 / "test.md"
63+
content = create_collection_entry(
64+
collection=test_collection, content=None, foo="bar", filepath=filepath, editor=None
65+
)
66+
assert content.strip() == f"New entry created at {filepath} ."
6367

68+
post = frontmatter.loads(filepath.read_text())
69+
print(filepath.read_text())
6470
assert post["title"] == "Untitled Entry"
6571
assert post["foo"] == "bar"
6672

6773

68-
def test_collection_call_with_content():
74+
def test_collection_call_with_content(tmp_path_factory):
6975
"""Tests that you can get content from the parser using `new_entry`"""
7076
test_collection = Collection()
71-
content = create_collection_entry(content="This is a test", collection=test_collection, foo="bar")
72-
post = frontmatter.loads(content)
77+
content_path1 = tmp_path_factory.getbasetemp()
78+
filepath = content_path1 / "test.md"
79+
create_collection_entry(
80+
content="This is a test", collection=test_collection, foo="bar", filepath=filepath, editor=None
81+
)
82+
print(filepath.read_text())
83+
84+
post = frontmatter.loads(filepath.read_text())
7385

7486
assert post["title"] == "Untitled Entry"
7587
assert post["foo"] == "bar"
@@ -148,16 +160,21 @@ def test_config_loading_invalid_file(tmp_path, monkeypatch, capsys):
148160
CliConfig().load_config(str(config_file))
149161

150162

151-
def test_collection_entry_with_custom_attributes():
163+
def test_collection_entry_with_custom_attributes(tmp_path_factory):
152164
"""Tests that custom attributes are passed through to collection entry"""
153165
test_collection = Collection()
154-
content = create_collection_entry(
166+
content_path1 = tmp_path_factory.getbasetemp()
167+
filepath = content_path1 / "test.md"
168+
169+
create_collection_entry(
155170
content="Test content",
156171
collection=test_collection,
157172
author="Test Author",
158173
tags="test,example",
174+
filepath=filepath,
175+
editor=None,
159176
)
160-
post = frontmatter.loads(content)
177+
post = frontmatter.loads(filepath.read_text())
161178

162179
assert post["author"] == "Test Author"
163180
assert post["tags"] == "test,example"

tests/test_cli_commands.py

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -162,10 +162,6 @@ def test_new_entry_command_success(runner, test_site_module, monkeypatch):
162162
assert result.exit_code == 0, result.output
163163
mock_create_entry.assert_called_once()
164164

165-
# Check that the file was created
166-
created_file = content_dir / "test.md"
167-
assert created_file.exists()
168-
169165

170166
def test_new_entry_command_with_args(runner, test_site_module, monkeypatch):
171167
"""Tests new_entry command with --args parameter"""
@@ -227,7 +223,12 @@ def test_new_entry_command_missing_required_args(runner):
227223
"slug": "slug1",
228224
"content": "content",
229225
},
230-
{"date": datetime.datetime.fromisoformat("2025-05-23T00:00:00"), "slug": "slug1", "content": "content"},
226+
{
227+
"date": datetime.datetime.fromisoformat("2025-05-23T00:00:00"),
228+
"slug": "slug1",
229+
"content": "content",
230+
"title": "New Entry",
231+
},
231232
),
232233
(
233234
{
@@ -239,7 +240,12 @@ def test_new_entry_command_missing_required_args(runner):
239240
"content": "content",
240241
"include-date": True,
241242
},
242-
{"date": datetime.datetime.fromisoformat("2025-05-23T00:00:00"), "slug": "slug1", "content": "content"},
243+
{
244+
"date": datetime.datetime.fromisoformat("2025-05-23T00:00:00"),
245+
"slug": "slug1",
246+
"content": "content",
247+
"title": "New Entry",
248+
},
243249
),
244250
(
245251
{
@@ -249,7 +255,7 @@ def test_new_entry_command_missing_required_args(runner):
249255
"slug": "slug1",
250256
"content": "content",
251257
},
252-
{"slug": "slug1", "content": "content"},
258+
{"slug": "slug1", "content": "content", "title": "New Entry"},
253259
),
254260
],
255261
)
@@ -290,12 +296,15 @@ def mock_create_collection_entry(**kwargs):
290296
else:
291297
formatted_options.extend([f"--{key}", value])
292298

293-
print(formatted_options)
294-
result = runner.invoke(app, ["new-entry", "--module-site", module_site, *formatted_options])
295-
print(result.output)
299+
runner.invoke(app, ["new-entry", "--module-site", module_site, *formatted_options])
296300
# Pop the collection from the passed arguments since it's not relevant.
297301
passed_args.pop("collection", None)
298302

303+
# We can remove the editor because we're not actually testing with that.
304+
passed_args.pop("editor")
305+
306+
assert passed_args.pop("filepath") == content_dir / options["filename"]
307+
299308
# include_date needs some special handling.
300309
if "include_date" in options:
301310
if not any(arg.startswith("date=") or arg.startswith("date:") for arg in options.get("args", [])):

0 commit comments

Comments
 (0)