Replace custom template syntax with Jinja2 #89

Merged
coding-agent-marvin8 merged 0 commits from refs/pull/89/head into main 2026-06-19 06:20:13 +00:00
coding-agent-marvin8 commented 2026-06-18 08:26:18 +00:00 (Migrated from codeberg.org)

Summary

  • Replaces the bespoke {var} / {[prefix]var[suffix]} template engine with Jinja2
  • Templates are now external .j2 files referenced via bot_post_template_file / post_template_file in config
  • Adds feed2fedi-migrate-templates CLI command to automatically convert existing configs and templates
  • Drops the \n preprocessing workaround — template files use real newlines
  • Old inline bot_post_template / post_template config fields raise a clear error pointing to the migration tool
  • Removes the legacy feed2fedi-convert-config utility (INI-to-JSON migration, long obsolete)

Breaking change

Users must migrate their templates. Run:

feed2fedi-migrate-templates --config-file config.json --apply

Test plan

  • All unit tests pass (nox -s pytest)
  • ruff check . passes
  • ty check . passes
  • complexipy . passes
  • Manual: run feed2fedi-migrate-templates --config-file config.json (dry run) against a config with old template fields
  • Manual: run with --apply and verify .j2 files are created and config is updated
  • Manual: start feed2fedi with old config fields — confirm clear error message is shown

Closes #88
Closes #90

## Summary - Replaces the bespoke `{var}` / `{[prefix]var[suffix]}` template engine with [Jinja2](https://jinja.palletsprojects.com/) - Templates are now external `.j2` files referenced via `bot_post_template_file` / `post_template_file` in config - Adds `feed2fedi-migrate-templates` CLI command to automatically convert existing configs and templates - Drops the `\n` preprocessing workaround — template files use real newlines - Old inline `bot_post_template` / `post_template` config fields raise a clear error pointing to the migration tool - Removes the legacy `feed2fedi-convert-config` utility (INI-to-JSON migration, long obsolete) ## Breaking change Users must migrate their templates. Run: ```sh feed2fedi-migrate-templates --config-file config.json --apply ``` ## Test plan - [ ] All unit tests pass (`nox -s pytest`) - [ ] `ruff check .` passes - [ ] `ty check .` passes - [ ] `complexipy .` passes - [ ] Manual: run `feed2fedi-migrate-templates --config-file config.json` (dry run) against a config with old template fields - [ ] Manual: run with `--apply` and verify `.j2` files are created and config is updated - [ ] Manual: start feed2fedi with old config fields — confirm clear error message is shown Closes #88 Closes #90
coding-agent-marvin8 commented 2026-06-18 20:49:41 +00:00 (Migrated from codeberg.org)

src/feed2fedi/control.py line 410 · line 420 — @wuming

Reading bot_post_template_file may raise a FileNotFoundError if the file doesn't exist.

Will wrap both `read_text()` calls in `load_config` with a `try/except FileNotFoundError` that re-raises as `Feed2FediError` with a descriptive message including the expected path.

[`src/feed2fedi/control.py` line 410](https://codeberg.org/marvinsmastodontools/feed2fedi/pulls/89#issuecomment-17659712) · [line 420](https://codeberg.org/marvinsmastodontools/feed2fedi/pulls/89#issuecomment-17659715) — @wuming > Reading bot_post_template_file may raise a FileNotFoundError if the file doesn't exist. ✅ Will wrap both \`read_text()\` calls in \`load_config\` with a \`try/except FileNotFoundError\` that re-raises as \`Feed2FediError\` with a descriptive message including the expected path.
coding-agent-marvin8 commented 2026-06-18 20:49:48 +00:00 (Migrated from codeberg.org)

src/feed2fedi/control.py line 410 · line 420 — @wuming

A01: Insecure Direct Object Reference / Path Traversal — bot_post_template_file / post_template_file path used without validating it stays within config_dir.

🔴 Not actioning. These paths come from the user's own `config.json`, which only they can write. An attacker who can modify `config.json` already has full control of the machine. Restricting path traversal here would prevent legitimate use cases like `../shared-templates/post.j2` across multiple configs. The threat model for a CLI tool run by its own operator does not include a malicious config file.

[`src/feed2fedi/control.py` line 410](https://codeberg.org/marvinsmastodontools/feed2fedi/pulls/89#issuecomment-17659739) · [line 420](https://codeberg.org/marvinsmastodontools/feed2fedi/pulls/89#issuecomment-17659742) — @wuming > A01: Insecure Direct Object Reference / Path Traversal — bot_post_template_file / post_template_file path used without validating it stays within config_dir. 🔴 Not actioning. These paths come from the user's own \`config.json\`, which only they can write. An attacker who can modify \`config.json\` already has full control of the machine. Restricting path traversal here would prevent legitimate use cases like \`../shared-templates/post.j2\` across multiple configs. The threat model for a CLI tool run by its own operator does not include a malicious config file.
coding-agent-marvin8 commented 2026-06-18 20:49:54 +00:00 (Migrated from codeberg.org)

src/feed2fedi/control.py line 400 — @wuming

The check for legacy bot_post_template rejects the entire config even if bot_post_template_file is also present. Could be softened with a warning. However, as designed, it's acceptable.

Confirmed by design. The hard error is intentional — it ensures users notice the breaking change rather than silently losing their custom template. The `feed2fedi-migrate-templates` tool handles the one-time conversion.

[`src/feed2fedi/control.py` line 400](https://codeberg.org/marvinsmastodontools/feed2fedi/pulls/89#issuecomment-17659727) — @wuming > The check for legacy bot_post_template rejects the entire config even if bot_post_template_file is also present. Could be softened with a warning. However, as designed, it's acceptable. ✅ Confirmed by design. The hard error is intentional — it ensures users notice the breaking change rather than silently losing their custom template. The \`feed2fedi-migrate-templates\` tool handles the one-time conversion.
coding-agent-marvin8 commented 2026-06-18 20:50:01 +00:00 (Migrated from codeberg.org)

src/feed2fedi/migrate.py line 105 — @wuming

Accessing feed_entry['url'] without a fallback will raise a KeyError if the feed entry lacks a 'url' field.

Valid point — the migration tool reads raw JSON without schema validation. Will add a check that raises a `ValueError` with a descriptive message if a feed entry is missing `url`.

[`src/feed2fedi/migrate.py` line 105](https://codeberg.org/marvinsmastodontools/feed2fedi/pulls/89#issuecomment-17659718) — @wuming > Accessing feed_entry['url'] without a fallback will raise a KeyError if the feed entry lacks a 'url' field. ✅ Valid point — the migration tool reads raw JSON without schema validation. Will add a check that raises a \`ValueError\` with a descriptive message if a feed entry is missing \`url\`.
coding-agent-marvin8 commented 2026-06-18 20:50:08 +00:00 (Migrated from codeberg.org)

src/feed2fedi/migrate.py line 117 — @wuming

Raising ValueError when no templates are found is appropriate, but the error message could be more helpful by suggesting the user may already have migrated.

The current message already says: "If you have already migrated, this config has no templates." — so this is addressed.

[`src/feed2fedi/migrate.py` line 117](https://codeberg.org/marvinsmastodontools/feed2fedi/pulls/89#issuecomment-17659721) — @wuming > Raising ValueError when no templates are found is appropriate, but the error message could be more helpful by suggesting the user may already have migrated. ✅ The current message already says: *"If you have already migrated, this config has no templates."* — so this is addressed.
coding-agent-marvin8 commented 2026-06-18 20:50:15 +00:00 (Migrated from codeberg.org)

src/feed2fedi/migrate.py line 88 — @wuming

If the config already contains both bot_post_template and bot_post_template_file, the old one is silently removed and replaced, potentially overwriting the user's existing file reference.

🔴 Not actioning. This scenario cannot arise in practice: `load_config` raises a `Feed2FediError` if both are present in the JSON, so any valid working config will never have both simultaneously. The migration tool is a one-time conversion utility; it's not expected to be run on already-migrated configs.

[`src/feed2fedi/migrate.py` line 88](https://codeberg.org/marvinsmastodontools/feed2fedi/pulls/89#issuecomment-17659724) — @wuming > If the config already contains both bot_post_template and bot_post_template_file, the old one is silently removed and replaced, potentially overwriting the user's existing file reference. 🔴 Not actioning. This scenario cannot arise in practice: \`load_config\` raises a \`Feed2FediError\` if both are present in the JSON, so any valid working config will never have both simultaneously. The migration tool is a one-time conversion utility; it's not expected to be run on already-migrated configs.
coding-agent-marvin8 commented 2026-06-18 20:55:26 +00:00 (Migrated from codeberg.org)

src/feed2fedi/migrate.py line 83 — @wuming

A02: No integrity validation — config file read without integrity check; attacker who modifies config could inject arbitrary Jinja2 templates.

🔴 Not actioning. config.json is a local file owned by the operator running feed2fedi. An attacker who can write to it already has full local access. Adding checksums would give a false sense of security without changing the threat model.

[`src/feed2fedi/migrate.py` line 83](https://codeberg.org/marvinsmastodontools/feed2fedi/pulls/89#issuecomment-17659745) — @wuming > A02: No integrity validation — config file read without integrity check; attacker who modifies config could inject arbitrary Jinja2 templates. 🔴 Not actioning. `config.json` is a local file owned by the operator running feed2fedi. An attacker who can write to it already has full local access. Adding checksums would give a false sense of security without changing the threat model.
coding-agent-marvin8 commented 2026-06-18 20:56:23 +00:00 (Migrated from codeberg.org)

Release-Notes.md line 6 — @wuming

Release notes should mention that the old bot_post_template and post_template fields are no longer accepted.

Already covered. The prose section of the release notes (line 16 onward) explicitly states: "The old inline bot_post_template and post_template config fields are no longer accepted — feed2fedi will error immediately on startup if they are present in your config."

[Release-Notes.md line 6](https://codeberg.org/marvinsmastodontools/feed2fedi/pulls/89#issuecomment-17659730) — @wuming > Release notes should mention that the old bot_post_template and post_template fields are no longer accepted. ✅ Already covered. The prose section of the release notes (line 16 onward) explicitly states: *"The old inline `bot_post_template` and `post_template` config fields are no longer accepted — feed2fedi will error immediately on startup if they are present in your config."*
coding-agent-marvin8 commented 2026-06-18 20:57:20 +00:00 (Migrated from codeberg.org)

Release-Notes.md line 12 · line 19 — @wuming

Consider breaking the long description into multiple paragraphs or bullet points. Migration steps in line 16 and line 19 are repeated.

Will split the prose block into two shorter paragraphs and remove the duplicate migration step reference.

[Release-Notes.md line 12](https://codeberg.org/marvinsmastodontools/feed2fedi/pulls/89#issuecomment-17659733) · [line 19](https://codeberg.org/marvinsmastodontools/feed2fedi/pulls/89#issuecomment-17660171) — @wuming > Consider breaking the long description into multiple paragraphs or bullet points. Migration steps in line 16 and line 19 are repeated. ✅ Will split the prose block into two shorter paragraphs and remove the duplicate migration step reference.
coding-agent-marvin8 commented 2026-06-18 21:00:20 +00:00 (Migrated from codeberg.org)

docs/Config-File-Explained.md line 250 — @wuming

The Jinja2 fallback example uses \| which might be a markdown/encoding escape.

🔴 Not actioning — this is correct Markdown. Inside a GFM table cell, a literal | must be escaped as \| to avoid breaking the table column delimiter. The rendered output is the correct Jinja2 syntax {{ description | default(title, true) }} with a proper pipe character.

[`docs/Config-File-Explained.md` line 250](https://codeberg.org/marvinsmastodontools/feed2fedi/pulls/89#issuecomment-17659736) — @wuming > The Jinja2 fallback example uses `\|` which might be a markdown/encoding escape. 🔴 Not actioning — this is correct Markdown. Inside a GFM table cell, a literal `|` must be escaped as `\|` to avoid breaking the table column delimiter. The rendered output is the correct Jinja2 syntax `{{ description | default(title, true) }}` with a proper pipe character.
coding-agent-marvin8 commented 2026-06-18 21:01:17 +00:00 (Migrated from codeberg.org)

src/feed2fedi/publish.py line 39 · line 37 — @wuming

A03/A05: SSTI risk — autoescape=False and no SandboxedEnvironment. RSS feed values could inject template expressions.

🔴 Not actioning. SSTI requires an attacker to control the template string itself, not just the interpolated values. Here, the template is a .j2 file written by the operator; RSS feed data only ever reaches the template as variable values via render(**params), which Jinja2 evaluates as data, not as template code. Enabling autoescape would HTML-escape every value in plain-text Fediverse posts (turning & into & etc.), which is incorrect. SandboxedEnvironment would restrict legitimate Jinja2 features operators may want to use. Neither change improves the actual security posture for this use case.

[`src/feed2fedi/publish.py` line 39](https://codeberg.org/marvinsmastodontools/feed2fedi/pulls/89#issuecomment-17659748) · [line 37](https://codeberg.org/marvinsmastodontools/feed2fedi/pulls/89#issuecomment-17660174) — @wuming > A03/A05: SSTI risk — autoescape=False and no SandboxedEnvironment. RSS feed values could inject template expressions. 🔴 Not actioning. SSTI requires an attacker to control the *template string itself*, not just the interpolated values. Here, the template is a `.j2` file written by the operator; RSS feed data only ever reaches the template as variable *values* via `render(**params)`, which Jinja2 evaluates as data, not as template code. Enabling `autoescape` would HTML-escape every value in plain-text Fediverse posts (turning `&` into `&` etc.), which is incorrect. `SandboxedEnvironment` would restrict legitimate Jinja2 features operators may want to use. Neither change improves the actual security posture for this use case.
coding-agent-marvin8 commented 2026-06-18 21:02:14 +00:00 (Migrated from codeberg.org)

src/feed2fedi/migrate.py line 97 · line 112 — @wuming

A01: Path traversal — derive_template_filename() does not prevent '..' sequences; attacker controlling feed URL could write files outside config directory.

🔴 Not actioning, but worth explaining: derive_template_filename runs re.sub(r'[^a-zA-Z0-9._-]', '_', netloc + path) on the URL, which replaces every / and \ with _. The resulting filename contains no path separators, so config_dir / filename cannot escape config_dir.. as two dots in a filename is inert without a separator. That said, this tool only runs on URLs the operator already trusts (they are in their own config.json), so the threat model does not include a malicious feed URL.

[`src/feed2fedi/migrate.py` line 97](https://codeberg.org/marvinsmastodontools/feed2fedi/pulls/89#issuecomment-17660177) · [line 112](https://codeberg.org/marvinsmastodontools/feed2fedi/pulls/89#issuecomment-17660180) — @wuming > A01: Path traversal — derive_template_filename() does not prevent '..' sequences; attacker controlling feed URL could write files outside config directory. 🔴 Not actioning, but worth explaining: `derive_template_filename` runs `re.sub(r'[^a-zA-Z0-9._-]', '_', netloc + path)` on the URL, which replaces every `/` and `\` with `_`. The resulting filename contains no path separators, so `config_dir / filename` cannot escape `config_dir` — `..` as two dots in a filename is inert without a separator. That said, this tool only runs on URLs the operator already trusts (they are in their own config.json), so the threat model does not include a malicious feed URL.
coding-agent-marvin8 commented 2026-06-18 21:03:11 +00:00 (Migrated from codeberg.org)

src/feed2fedi/control.py line 116 — @wuming

Field 'bot_post_template' is optional and not used by the new system; if someone creates a Configuration programmatically and sets bot_post_template, it will be silently ignored.

🔴 Not actioning. The field is retained only so msgspec can detect it in a JSON config and raise a clear error (see load_config). Direct programmatic construction of Configuration is trusted internal code — the field docstring already marks it as Legacy field — raises error if set. Removing it would silently discard the value from deserialized JSON, which is the worse outcome.

[`src/feed2fedi/control.py` line 116](https://codeberg.org/marvinsmastodontools/feed2fedi/pulls/89#issuecomment-17660168) — @wuming > Field 'bot_post_template' is optional and not used by the new system; if someone creates a Configuration programmatically and sets bot_post_template, it will be silently ignored. 🔴 Not actioning. The field is retained only so `msgspec` can detect it in a JSON config and raise a clear error (see `load_config`). Direct programmatic construction of `Configuration` is trusted internal code — the field docstring already marks it as `Legacy field — raises error if set`. Removing it would silently discard the value from deserialized JSON, which is the worse outcome.
coding-agent-marvin8 commented 2026-06-19 03:14:05 +00:00 (Migrated from codeberg.org)

src/feed2fedi/publish.py — @wuming

Jinja2 template rendering errors (e.g., syntax errors) are not caught. The old code caught KeyError/ValueError from str.format, so this is a regression.

Fixed. render_template now wraps from_string().render() in try/except TemplateError and re-raises as Feed2FediError. Test added (test_template_syntax_error_raises_feed2fedi_error).

[`src/feed2fedi/publish.py`](https://codeberg.org/marvinsmastodontools/feed2fedi/pulls/89#issuecomment-17697173) — @wuming > Jinja2 template rendering errors (e.g., syntax errors) are not caught. The old code caught KeyError/ValueError from str.format, so this is a regression. ✅ Fixed. `render_template` now wraps `from_string().render()` in `try/except TemplateError` and re-raises as `Feed2FediError`. Test added (`test_template_syntax_error_raises_feed2fedi_error`).
coding-agent-marvin8 commented 2026-06-19 03:15:02 +00:00 (Migrated from codeberg.org)

tests/unit/test_cli_apps.py — @wuming

No test case for load_config raising Feed2FediError when legacy 'bot_post_template' field is present.

Test added: test_load_config_legacy_bot_post_template_raises in test_cli_apps.py.

[`tests/unit/test_cli_apps.py`](https://codeberg.org/marvinsmastodontools/feed2fedi/pulls/89#issuecomment-17697176) — @wuming > No test case for load_config raising Feed2FediError when legacy 'bot_post_template' field is present. ✅ Test added: `test_load_config_legacy_bot_post_template_raises` in `test_cli_apps.py`.
coding-agent-marvin8 commented 2026-06-19 03:16:00 +00:00 (Migrated from codeberg.org)

src/feed2fedi/control.py — @wuming

A05: Path traversal — post_template_file path not validated against config directory.

🔴 Not actioning — same reasoning as the equivalent comment in the first review round. These paths come from the user's own config.json. An attacker who can write it already has full local access; restricting paths would prevent legitimate uses like ../shared/post.j2.

[`src/feed2fedi/control.py`](https://codeberg.org/marvinsmastodontools/feed2fedi/pulls/89#issuecomment-17697179) — @wuming > A05: Path traversal — post_template_file path not validated against config directory. 🔴 Not actioning — same reasoning as the equivalent comment in the first review round. These paths come from the user's own `config.json`. An attacker who can write it already has full local access; restricting paths would prevent legitimate uses like `../shared/post.j2`.
coding-agent-marvin8 commented 2026-06-19 03:16:57 +00:00 (Migrated from codeberg.org)

src/feed2fedi/migrate.py — @wuming

If the config directory is web-accessible, template content could be exposed.

🔴 Not actioning. feed2fedi is a local CLI tool — it has no concept of a web-accessible directory. The concern does not apply to this threat model.

[`src/feed2fedi/migrate.py`](https://codeberg.org/marvinsmastodontools/feed2fedi/pulls/89#issuecomment-17697182) — @wuming > If the config directory is web-accessible, template content could be exposed. 🔴 Not actioning. feed2fedi is a local CLI tool — it has no concept of a web-accessible directory. The concern does not apply to this threat model.
coding-agent-marvin8 commented 2026-06-19 03:17:54 +00:00 (Migrated from codeberg.org)

src/feed2fedi/migrate.py — @wuming

The regex does not handle escaped braces; a literal {var} that should remain as-is will be incorrectly converted.

🔴 Not actioning. The migration tool converts from the old feed2fedi template syntax to Jinja2. In the old syntax, every {var} was a placeholder — there was no escape mechanism for literal braces. Any {var} in an old template is intentionally converted to {{ var }}. The behavioral change (missing vars → "" instead of leaving {var} intact) is intentional and documented: silently dropping an unknown variable is better than leaking raw {title} text into a Fediverse post.

[`src/feed2fedi/migrate.py`](https://codeberg.org/marvinsmastodontools/feed2fedi/pulls/89#issuecomment-17699720) — @wuming > The regex does not handle escaped braces; a literal `{var}` that should remain as-is will be incorrectly converted. 🔴 Not actioning. The migration tool converts *from* the old feed2fedi template syntax *to* Jinja2. In the old syntax, every `{var}` was a placeholder — there was no escape mechanism for literal braces. Any `{var}` in an old template is intentionally converted to `{{ var }}`. The behavioral change (missing vars → `""` instead of leaving `{var}` intact) is intentional and documented: silently dropping an unknown variable is better than leaking raw `{title}` text into a Fediverse post.
coding-agent-marvin8 commented 2026-06-19 03:18:51 +00:00 (Migrated from codeberg.org)

src/feed2fedi/migrate.py — @wuming

If bot_post_template_file is already present, the migration will overwrite it.

🔴 Not actioning — this was also raised in the first review round. The scenario cannot arise in practice: load_config raises Feed2FediError if both bot_post_template and bot_post_template_file are present in the JSON simultaneously. A valid working config can never have both.

[`src/feed2fedi/migrate.py`](https://codeberg.org/marvinsmastodontools/feed2fedi/pulls/89#issuecomment-17699723) — @wuming > If bot_post_template_file is already present, the migration will overwrite it. 🔴 Not actioning — this was also raised in the first review round. The scenario cannot arise in practice: `load_config` raises `Feed2FediError` if both `bot_post_template` and `bot_post_template_file` are present in the JSON simultaneously. A valid working config can never have both.
coding-agent-marvin8 commented 2026-06-19 03:19:48 +00:00 (Migrated from codeberg.org)

src/feed2fedi/migrate.py — @wuming

Similarly for feed entries: post_template_file would be overwritten without warning.

🔴 Not actioning — same reasoning as above. The per-feed equivalent also cannot arise, as load_config rejects any config that has both post_template and post_template_file on the same feed.

[`src/feed2fedi/migrate.py`](https://codeberg.org/marvinsmastodontools/feed2fedi/pulls/89#issuecomment-17699726) — @wuming > Similarly for feed entries: post_template_file would be overwritten without warning. 🔴 Not actioning — same reasoning as above. The per-feed equivalent also cannot arise, as `load_config` rejects any config that has both `post_template` and `post_template_file` on the same feed.
coding-agent-marvin8 commented 2026-06-19 03:20:45 +00:00 (Migrated from codeberg.org)

src/feed2fedi/control.py — @wuming

Missing UnicodeDecodeError handling for feed template file reading.

Fixed. Extracted _read_template_file helper that catches both FileNotFoundError and UnicodeDecodeError, re-raising each as Feed2FediError with a descriptive message. Applied to both the bot and feed template file reads. Test added: test_load_config_unicode_error_bot_template_file.

[`src/feed2fedi/control.py`](https://codeberg.org/marvinsmastodontools/feed2fedi/pulls/89#issuecomment-17699729) — @wuming > Missing UnicodeDecodeError handling for feed template file reading. ✅ Fixed. Extracted `_read_template_file` helper that catches both `FileNotFoundError` and `UnicodeDecodeError`, re-raising each as `Feed2FediError` with a descriptive message. Applied to both the bot and feed template file reads. Test added: `test_load_config_unicode_error_bot_template_file`.
coding-agent-marvin8 commented 2026-06-19 03:21:42 +00:00 (Migrated from codeberg.org)

src/feed2fedi/migrate.py — @wuming

json.dumps may reorder keys; some users may expect original key order to be preserved.

🔴 Not actioning. Python dicts preserve insertion order since 3.7, and json.dumps respects that order. The only cosmetic change is that bot_post_template is removed and bot_post_template_file is appended at the end — a one-time migration tool moving a key to the end of the object is acceptable.

[`src/feed2fedi/migrate.py`](https://codeberg.org/marvinsmastodontools/feed2fedi/pulls/89#issuecomment-17699732) — @wuming > json.dumps may reorder keys; some users may expect original key order to be preserved. 🔴 Not actioning. Python dicts preserve insertion order since 3.7, and `json.dumps` respects that order. The only cosmetic change is that `bot_post_template` is removed and `bot_post_template_file` is appended at the end — a one-time migration tool moving a key to the end of the object is acceptable.
coding-agent-marvin8 commented 2026-06-19 03:24:42 +00:00 (Migrated from codeberg.org)

src/feed2fedi/control.py — @wuming

If bot_post_template_file is an absolute path, config_dir / absolute_path may produce an unexpected result.

🔴 Not actioning. Python's pathlib correctly handles this: Path('/config/dir') / '/abs/path.j2' evaluates to Path('/abs/path.j2') — the absolute right-hand side replaces the left. This is documented Python behavior and means absolute paths in the config work correctly. No special-casing is needed.

[`src/feed2fedi/control.py`](https://codeberg.org/marvinsmastodontools/feed2fedi/pulls/89#issuecomment-17699735) — @wuming > If bot_post_template_file is an absolute path, config_dir / absolute_path may produce an unexpected result. 🔴 Not actioning. Python's `pathlib` correctly handles this: `Path('/config/dir') / '/abs/path.j2'` evaluates to `Path('/abs/path.j2')` — the absolute right-hand side replaces the left. This is documented Python behavior and means absolute paths in the config *work correctly*. No special-casing is needed.
coding-agent-marvin8 commented 2026-06-19 03:25:39 +00:00 (Migrated from codeberg.org)

CLAUDE.md — @wuming

The Off-limits section appears incomplete after the diff.

🔴 Not actioning. CLAUDE.md is a project-level instruction file for Claude agents; the diff reflects edits made outside this PR's scope. The file is correct as it stands: ruff.toml, noxfile.py, CHANGELOG.md, changelog.d/, version in pyproject.toml, and version bump/release tasks are all still off-limits.

[`CLAUDE.md`](https://codeberg.org/marvinsmastodontools/feed2fedi/pulls/89#issuecomment-17699738) — @wuming > The Off-limits section appears incomplete after the diff. 🔴 Not actioning. CLAUDE.md is a project-level instruction file for Claude agents; the diff reflects edits made outside this PR's scope. The file is correct as it stands: `ruff.toml`, `noxfile.py`, `CHANGELOG.md`, `changelog.d/`, `version` in `pyproject.toml`, and version bump/release tasks are all still off-limits.
coding-agent-marvin8 commented 2026-06-19 03:26:36 +00:00 (Migrated from codeberg.org)

CLAUDE.md — @wuming

Stack line removes references to 'strict mypy' and adds 'ty'. Ensure replacement is accurate.

🔴 Not actioning. The replacement is accurate — mypy was removed from this project and replaced with ty (Astral's type checker). The diff correctly reflects the current state of the toolchain.

[`CLAUDE.md`](https://codeberg.org/marvinsmastodontools/feed2fedi/pulls/89#issuecomment-17699741) — @wuming > Stack line removes references to 'strict mypy' and adds 'ty'. Ensure replacement is accurate. 🔴 Not actioning. The replacement is accurate — `mypy` was removed from this project and replaced with `ty` (Astral's type checker). The diff correctly reflects the current state of the toolchain.
coding-agent-marvin8 commented 2026-06-19 03:27:33 +00:00 (Migrated from codeberg.org)

Release-Notes.md — @wuming

The Jinja2 link uses http, not https.

🔴 Not actioning — the link already uses https. The current file reads: [Jinja2](https://jinja.palletsprojects.com/).

[`Release-Notes.md`](https://codeberg.org/marvinsmastodontools/feed2fedi/pulls/89#issuecomment-17699744) — @wuming > The Jinja2 link uses http, not https. 🔴 Not actioning — the link already uses https. The current file reads: `[Jinja2](https://jinja.palletsprojects.com/)`.
coding-agent-marvin8 commented 2026-06-19 03:28:30 +00:00 (Migrated from codeberg.org)

docs/Config-File-Explained.md — @wuming

The anchor link #template-syntax may not match the heading ## Template Syntax (capital S).

🔴 Not actioning. GitHub Flavored Markdown (and Codeberg's renderer) automatically generates anchor IDs by lowercasing the heading text and replacing spaces with hyphens. ## Template Syntax produces the anchor #template-syntax, which is exactly what the link uses — no mismatch.

[`docs/Config-File-Explained.md`](https://codeberg.org/marvinsmastodontools/feed2fedi/pulls/89#issuecomment-17699747) — @wuming > The anchor link `#template-syntax` may not match the heading `## Template Syntax` (capital S). 🔴 Not actioning. GitHub Flavored Markdown (and Codeberg's renderer) automatically generates anchor IDs by lowercasing the heading text and replacing spaces with hyphens. `## Template Syntax` produces the anchor `#template-syntax`, which is exactly what the link uses — no mismatch.
coding-agent-marvin8 commented 2026-06-19 03:29:27 +00:00 (Migrated from codeberg.org)

tests/unit/test_feed_template_delay.py — @wuming

test_no_template was removed; fallback from empty feed template to bot template is not covered.

Already covered. test_only_bot_template in test_feed_template_delay.py explicitly tests that _determine_post_template(feed_template=None) and _determine_post_template(feed_template="") both return the bot template content. The removed test_no_template was testing old behavior that no longer exists.

[`tests/unit/test_feed_template_delay.py`](https://codeberg.org/marvinsmastodontools/feed2fedi/pulls/89#issuecomment-17703230) — @wuming > test_no_template was removed; fallback from empty feed template to bot template is not covered. ✅ Already covered. `test_only_bot_template` in `test_feed_template_delay.py` explicitly tests that `_determine_post_template(feed_template=None)` and `_determine_post_template(feed_template="")` both return the bot template content. The removed `test_no_template` was testing old behavior that no longer exists.
coding-agent-marvin8 commented 2026-06-19 03:30:24 +00:00 (Migrated from codeberg.org)

src/feed2fedi/control.py — @wuming

Line 397 uses is str which is a reference comparison and will always be False.

🔴 Not a bug. The code on that line is isinstance(new_config.bot_post_visibility, str) — the correct form. The reviewer appears to have misread the code.

[`src/feed2fedi/control.py`](https://codeberg.org/marvinsmastodontools/feed2fedi/pulls/89#issuecomment-17703224) — @wuming > Line 397 uses `is str` which is a reference comparison and will always be False. 🔴 Not a bug. The code on that line is `isinstance(new_config.bot_post_visibility, str)` — the correct form. The reviewer appears to have misread the code.
coding-agent-marvin8 commented 2026-06-19 03:44:42 +00:00 (Migrated from codeberg.org)

src/feed2fedi/migrate.py — @wuming

start_migrate_shim does not catch ValueError from migrate_config, causing a raw traceback.

Fixed. start_migrate_shim now catches ValueError from migrate_config and prints Error: <message> before returning, giving a clean user-facing message. Test added: test_migrate_app_no_templates_prints_clean_message.

[`src/feed2fedi/migrate.py`](https://codeberg.org/marvinsmastodontools/feed2fedi/pulls/89#issuecomment-17703227) — @wuming > start_migrate_shim does not catch ValueError from migrate_config, causing a raw traceback. ✅ Fixed. `start_migrate_shim` now catches `ValueError` from `migrate_config` and prints `Error: <message>` before returning, giving a clean user-facing message. Test added: `test_migrate_app_no_templates_prints_clean_message`.
coding-agent-marvin8 commented 2026-06-19 03:45:39 +00:00 (Migrated from codeberg.org)

src/feed2fedi/control.py — @wuming

If bot_post_template_file is absolute, config_dir / absolute_path produces an incorrect path.

🔴 Not actioning. As noted in response to the equivalent earlier comment: Python pathlib handles this correctly — Path(dir) / '/abs/path'Path('/abs/path'). Absolute paths work as expected without any special-casing.

[`src/feed2fedi/control.py`](https://codeberg.org/marvinsmastodontools/feed2fedi/pulls/89#issuecomment-17703575) — @wuming > If bot_post_template_file is absolute, config_dir / absolute_path produces an incorrect path. 🔴 Not actioning. As noted in response to the equivalent earlier comment: Python pathlib handles this correctly — `Path(dir) / '/abs/path'` → `Path('/abs/path')`. Absolute paths work as expected without any special-casing.
coding-agent-marvin8 commented 2026-06-19 03:46:36 +00:00 (Migrated from codeberg.org)

src/feed2fedi/control.py — @wuming

Same absolute-path issue for post_template_file.

🔴 Not actioning — same reasoning. Python pathlib correctly resolves absolute right-hand sides.

[`src/feed2fedi/control.py`](https://codeberg.org/marvinsmastodontools/feed2fedi/pulls/89#issuecomment-17703581) — @wuming > Same absolute-path issue for post_template_file. 🔴 Not actioning — same reasoning. Python pathlib correctly resolves absolute right-hand sides.
coding-agent-marvin8 commented 2026-06-19 03:47:33 +00:00 (Migrated from codeberg.org)

src/feed2fedi/publish.py — @wuming

Using Undefined causes missing variables to silently render as empty string. Previously str.format() would leave {var} intact. This is a behavioral change.

🔴 Not actioning. This change is intentional. Leaving raw {var} text in a Fediverse post (the old behavior) is confusing for the user's followers and was considered a bug, not a feature. Rendering missing variables as "" is the better default — the {{ var | default("fallback") }} filter is available for cases where a fallback is needed. StrictUndefined would make templates unusable with sparse feed data (many RSS feeds omit optional fields like author or content_html).

[`src/feed2fedi/publish.py`](https://codeberg.org/marvinsmastodontools/feed2fedi/pulls/89#issuecomment-17703578) — @wuming > Using Undefined causes missing variables to silently render as empty string. Previously str.format() would leave `{var}` intact. This is a behavioral change. 🔴 Not actioning. This change is intentional. Leaving raw `{var}` text in a Fediverse post (the old behavior) is confusing for the user's followers and was considered a bug, not a feature. Rendering missing variables as `""` is the better default — the `{{ var | default("fallback") }}` filter is available for cases where a fallback is needed. `StrictUndefined` would make templates unusable with sparse feed data (many RSS feeds omit optional fields like `author` or `content_html`).
Sign in to join this conversation.
No description provided.