Add label-driven release automation - #8
Conversation
|
Warning Review limit reached
Your plan includes 1 review of capacity. Refill in 6 minutes and 23 seconds. Your organization has run out of usage credits. Purchase more in the billing tab. ⌛ How to resolve this issue?After more review capacity refills, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than trial, open-source, and free plans. In all cases, review capacity refills continuously over time. Please see our FAQ for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (6)
📝 WalkthroughWalkthroughThis PR adds a label-driven automated release pipeline (label detection, release-level resolution, version bumping, workflows, docs, and tracking) and a new MCP tool to create scheduled YNAB transactions with accompanying tests and a server re-export. ChangesAutomated Release Workflow
MCP tools, resources, state, and tests
Sequence Diagram(s)sequenceDiagram
participant Dev as Developer
participant PR as PullRequest
participant Labeler as release_labeler
participant Level as release_level
participant Workflow as release.yml
participant GitHub as GitHub Release
participant PyPI as PyPI
Dev->>PR: open / label / change files
PR->>Labeler: run on PR events (detect files, ensure/remove `patch`)
PR->>Level: on merged PR, label set passed to release_level
Level->>Workflow: sets should_release & level
Workflow->>Workflow: bump pyproject.toml and uv.lock
Workflow->>Workflow: run CI checks, build dists
Workflow->>GitHub: create tag and GitHub Release
Workflow->>PyPI: publish artifact when should_release == true
🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs:
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 4
🧹 Nitpick comments (3)
tests/test_release_version.py (1)
9-9: ⚡ Quick winAdd explicit type hints on test helpers/functions to match repo standards.
Please annotate the helper return type and test function signatures (
-> None), including thetmp_pathfixture type.Suggested fix
import importlib.util from pathlib import Path +from types import ModuleType -def load_bump_version_module(): +def load_bump_version_module() -> ModuleType: @@ -def test_bump_version_levels(): +def test_bump_version_levels() -> None: @@ -def test_bump_pyproject_updates_static_project_version(tmp_path): +def test_bump_pyproject_updates_static_project_version(tmp_path: Path) -> None:As per coding guidelines,
**/*.py: Use type hints consistently with modern Python typing.Also applies to: 19-19, 27-27
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@tests/test_release_version.py` at line 9, Add explicit type hints: annotate the helper load_bump_version_module to return types.ModuleType (or typing.Any if you prefer) and update the test function signatures to include return type -> None and type the tmp_path fixture as pathlib.Path; specifically modify load_bump_version_module(), and the test functions named in the file (eg. test_bump_version_from_pyproject and test_bump_version_from_setup_py) to accept tmp_path: Path and return None. Also add the necessary imports (from types import ModuleType and from pathlib import Path) if not already present.src/mcp_ynab/__init__.py (1)
17-20: 💤 Low valueConsider adding a type hint for
__version__.For consistency with the coding guideline "Use type hints consistently with modern Python typing," consider adding an explicit type annotation.
📝 Proposed enhancement
try: - __version__ = version("mcp-ynab") + __version__: str = version("mcp-ynab") except PackageNotFoundError: __version__ = "0+unknown"As per coding guidelines: "Use type hints consistently with modern Python typing" for files matching
**/*.py.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/mcp_ynab/__init__.py` around lines 17 - 20, Add an explicit type annotation for the module-level variable __version__: declare __version__ as a str and keep the existing try/except that sets it via version("mcp-ynab") and PackageNotFoundError fallback; update the __version__ assignment site (the try/except block using version and PackageNotFoundError) to use the annotated variable so the module consistently follows modern Python typing..github/workflows/release-labels.yml (1)
17-28: 💤 Low valueSecurity pattern for pull_request_target is correctly implemented.
The static analysis tool flagged
pull_request_targetas dangerous, but this implementation follows the secure pattern:
- Line 20 checks out code from
default_branch, NOT from the PR branch- The script executed comes from the trusted default branch
- PR content is only read via GitHub API, never executed
This pattern is necessary to write labels on PRs (including those from forks) while maintaining security.
Optional: Consider pinning actions to commit SHAs.
For additional supply chain security, you could pin actions to specific commit hashes instead of version tags:
actions/checkout@v4→actions/checkout@<commit-sha>actions/github-script@v7→actions/github-script@<commit-sha>This prevents tag-moving attacks, though the risk is low for official GitHub actions.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In @.github/workflows/release-labels.yml around lines 17 - 28, The workflow currently uses actions referenced by tags (actions/checkout@v4 and actions/github-script@v7); to harden supply-chain security, replace those tagged references with specific commit SHAs for each action (e.g., actions/checkout@<commit-sha> and actions/github-script@<commit-sha>) while keeping the existing pattern that checks out the default branch and requires the release_labeler.cjs script; update the two "uses" entries that mention actions/checkout and actions/github-script accordingly and verify the checkout ref: ${{ github.event.repository.default_branch }} and the labeler import remain unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.github/scripts/bump_version.py:
- Line 11: The long single-line regex assigned to VERSION_RE exceeds the
100-character limit; split it across lines by using a multi-line string or
re.VERBOSE and combine flags so the pattern stays readable and under 100 chars
per line (e.g., build the pattern with implicit string concatenation or use
re.VERBOSE and pass re.MULTILINE | re.VERBOSE to re.compile) while preserving
the named groups (?P<version>, ?P<minor>, ?P<patch>) and the raw string prefix.
In @.github/workflows/release.yml:
- Line 27: Pin every uses: entry to an immutable commit SHA instead of tag names
for actions referenced (e.g., replace actions/checkout@v4,
actions/github-script@v7, astral-sh/setup-uv@v3, actions/upload-artifact@v4,
actions/download-artifact@v4, pypa/gh-action-pypi-publish@release/v1 with their
corresponding full commit SHAs) and for the second checkout step (the "Checkout
main" checkout action) add with: persist-credentials: false so the GITHUB_TOKEN
is not retained; then enable authentication only on the specific step(s) that
perform git push/tag (configure that push/tag step to use a short-lived token or
set persist-credentials: true just for that step via a separate checkout if
needed).
- Around line 40-45: The "Checkout main" step currently uses actions/checkout@v4
without disabling credential persistence; update that step (named "Checkout
main", referencing uses: actions/checkout@v4 and ref: main) to add
persist-credentials: false so the default GITHUB_TOKEN is not left configured
for subsequent steps, and ensure you explicitly set up an authenticated remote
only for the push step (e.g., by adding a dedicated git remote or using an
authenticated action/step) before "Push version commit and tag".
In `@docs/release-process.md`:
- Line 34: Replace the all-caps platform name "GITHUB" with the proper
capitalization "GitHub" in the document; search for the token "GITHUB"
(including the occurrence in the phrase "PyPI publishing requires a trusted
publisher configured for this repository and the") and update every occurrence
to "GitHub" to ensure consistent, user-facing platform naming.
---
Nitpick comments:
In @.github/workflows/release-labels.yml:
- Around line 17-28: The workflow currently uses actions referenced by tags
(actions/checkout@v4 and actions/github-script@v7); to harden supply-chain
security, replace those tagged references with specific commit SHAs for each
action (e.g., actions/checkout@<commit-sha> and
actions/github-script@<commit-sha>) while keeping the existing pattern that
checks out the default branch and requires the release_labeler.cjs script;
update the two "uses" entries that mention actions/checkout and
actions/github-script accordingly and verify the checkout ref: ${{
github.event.repository.default_branch }} and the labeler import remain
unchanged.
In `@src/mcp_ynab/__init__.py`:
- Around line 17-20: Add an explicit type annotation for the module-level
variable __version__: declare __version__ as a str and keep the existing
try/except that sets it via version("mcp-ynab") and PackageNotFoundError
fallback; update the __version__ assignment site (the try/except block using
version and PackageNotFoundError) to use the annotated variable so the module
consistently follows modern Python typing.
In `@tests/test_release_version.py`:
- Line 9: Add explicit type hints: annotate the helper load_bump_version_module
to return types.ModuleType (or typing.Any if you prefer) and update the test
function signatures to include return type -> None and type the tmp_path fixture
as pathlib.Path; specifically modify load_bump_version_module(), and the test
functions named in the file (eg. test_bump_version_from_pyproject and
test_bump_version_from_setup_py) to accept tmp_path: Path and return None. Also
add the necessary imports (from types import ModuleType and from pathlib import
Path) if not already present.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: d3c3c1cd-cf9a-4eda-911f-8c0542b43e4c
📒 Files selected for processing (11)
.beads/interactions.jsonl.beads/issues.jsonl.github/scripts/bump_version.py.github/scripts/release_labeler.cjs.github/scripts/release_level.cjs.github/workflows/release-labels.yml.github/workflows/release.ymldocs/release-process.mdpyproject.tomlsrc/mcp_ynab/__init__.pytests/test_release_version.py
| 8. Creates a GitHub release with the built distributions attached. | ||
| 9. Publishes to PyPI with trusted publishing. | ||
|
|
||
| PyPI publishing requires a trusted publisher configured for this repository and the |
There was a problem hiding this comment.
Use the official GitHub capitalization.
Line 34 uses GITHUB; update it to GitHub for correct platform naming in user-facing docs.
✏️ Suggested fix
-PyPI publishing requires a trusted publisher configured for this repository and the
+PyPI publishing requires a trusted publisher configured for this repository and the
`.github/workflows/release.yml` workflow.And ensure any GITHUB occurrences are changed to GitHub.
🧰 Tools
🪛 LanguageTool
[uncategorized] ~34-~34: The official name of this software platform is spelled with a capital “H”.
Context: ... configured for this repository and the .github/workflows/release.yml workflow.
(GITHUB)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@docs/release-process.md` at line 34, Replace the all-caps platform name
"GITHUB" with the proper capitalization "GitHub" in the document; search for the
token "GITHUB" (including the occurrence in the phrase "PyPI publishing requires
a trusted publisher configured for this repository and the") and update every
occurrence to "GitHub" to ensure consistent, user-facing platform naming.
…sactions Wraps ScheduledTransactionsApi.create_scheduled_transaction() to expose scheduled transaction creation via MCP. Accepts amount in dollars (converted to milliunits), ISO start date (defaults to today), and a Literal-typed frequency with all 13 valid YNAB values. Supports all SaveScheduledTransaction fields: payee_id/payee_name, category_id, memo, flag_color.
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/mcp_ynab/tools/transactions.py`:
- Around line 1099-1135: The create_scheduled_transaction implementation
violates the declared contract by allowing both payee_id and payee_name to be
sent and truncates amounts; update create_scheduled_transaction to validate
exclusivity (raise ValueError if both payee_id and payee_name are provided) and
ensure milliunit conversion matches the rest of the module by using rounding
(use int(round(amount * 1000))) before passing the value into
SaveScheduledTransaction (reference SaveScheduledTransaction, txn/txn_date, and
the function create_scheduled_transaction for locating the changes).
In `@tests/test_tools.py`:
- Around line 1922-1926: The current assertion checks for "$15.99" in result but
doesn't ensure the negative sign is present; update the test in
tests/test_tools.py to assert the negative sign is included (e.g., require
"-$15.99" in result) or use a regex match against result (for example matching a
leading minus or Unicode minus before $15.99) so the test fails if the negative
sign is dropped; locate the assertion referring to result and replace the final
assert that checks "$15.99" with the stricter check for the negative formatted
amount.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 8c4e866e-f2e5-4704-a415-a85b951a888b
📒 Files selected for processing (4)
.beads/issues.jsonlsrc/mcp_ynab/server.pysrc/mcp_ynab/tools/transactions.pytests/test_tools.py
…ution - set_preference: 6 tests for code_mode_enabled, code_mode_replace_tools, code_mode_timeout_s (float coercion + gt=0/le=60 bounds), and code_mode_max_output_chars (int ge=0 including zero) - _resolve_config_dir: 2 tests for XDG_CONFIG_HOME override and ~/.config fallback via Path.home() monkeypatch - 268 total tests pass (up from 260)
Add any, all, abs, round, hasattr, and isinstance to the Code Mode builtin allow-list. These were missing and causing failures in read-only analysis snippets (e.g. hasattr on YNAB API response dicts, any/all for list-filtering patterns). hasattr is safe here: the existing dunder-string-literal AST audit blocks hasattr(obj, "__class__") style probes at parse time, so the expanded surface doesn't widen the escape-hatch surface. 12 new tests cover each new builtin, the dunder-string guard, and a realistic isinstance-based type-filter pattern.
Field description now reads: "Amount in dollars. Negative for outflows (expenses, e.g. -42.50), positive for inflows (deposits, e.g. 1500.00)." Docstring adds a one-line example sentence so the sign convention is visible to both MCP clients reading the schema and developers reading the source.
Add notes to get_categories, update_category, and get_scheduled_transactions describing what the YNAB API cannot do: - No POST /categories endpoint — categories must be created in the app - SaveCategory write model exposes no goal fields — goals are read-only - No update/delete endpoint for scheduled transactions — only create+list
Fetch the transaction summary (date, payee, amount, category) and show a confirmation prompt via ctx.elicit before issuing the destructive DELETE. Returns "cancelled" string without deleting if the user declines or dismisses the prompt. No ctx (batch/automation flows) skips the prompt and deletes directly, preserving backward compatibility. Three new tests cover the confirm, decline, and dismiss paths.
…is fixture Replace inline DummyApi/DummyCtx boilerplate in 4 categorize_transaction tests with the shared mock_ynab_apis fixture. Each test shrinks by ~15 lines. Behavioral assertions preserved: no-GET guarantee, PATCH-only semantics, 404 not-found path, and 500 re-raise path all still verified.
Exposes all non-deleted payees for a budget as a markdown table (name, ID, transfer_account_id) via the MCP resource protocol — avoids burning a tool call for payee lookup. - YNABResources gains cache_payees() + get_cached_payee_records() backed by payees_cache.json (same envelope pattern as category cache) - list_payees_resource fetches live, filters deleted, caches, returns markdown - conftest mock_ynab_apis now patches server.PayeesApi so resource tests can control the mock the same way tool tests do - 4 tests covering markdown render, empty list, deleted-filter, caching
…=True
Adds test_code_mode_replace_tools_filters_instance_list_tools to verify
the instance-attribute patch (mcp.list_tools = _list_tools_with_code_mode_filter)
returns exactly {search, execute} + bootstrap tools when replace_tools is
enabled — closing the regression gap alongside the existing protocol-handler
and escape-hatch tests.
Closes mcp-ynab-fsv.2
Adds bulk approve, spending-by-category, triage-uncategorized-by-payee, and spending-by-payee examples to ynab://code-mode/examples. File stays at 4.9KB, well under the 8KB cap. Namespace regression test updated. Closes mcp-ynab-fsv.4
Both create_transaction and delete_transaction now check ynab_resources.preferences.confirm_before_post before triggering ctx.elicit(). When the preference is False, the confirmation prompt is skipped regardless of the per-call confirm= param — giving users a global escape hatch to disable confirmation dialogs. Design: should_confirm = confirm AND confirm_before_post (both must be True; per-call can opt-out, preference is the global gate). Closes mcp-ynab-6ha.6
…esource Live-fetches all category groups for a budget and renders a per-group markdown table with budgeted, activity, and balance columns — allowing the model to answer 'what's left in Groceries?' from a resource read without spending a tool call. - Uses getattr() throughout to work correctly with both SDK models and mocks - Filters deleted categories; empty budgets return a clear message - Registered in server.py re-export block Closes mcp-ynab-g9z.10
Makes from_category_id, to_category_id, and amount all Optional in the move_money tool signature. When either category ID is missing and an MCP context is available, the user is prompted to choose from the cached category list (which is refreshed from the API if empty). The amount parameter remains required (infer-from-overspend deferred). Existing positional callers are unaffected by the signature change. Closes mcp-ynab-qlh.5
There was a problem hiding this comment.
Actionable comments posted: 4
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.github/workflows/release.yml:
- Around line 26-30: The checkout steps currently pin `with: ref: main` which
can cause the release job to operate on a moved main tip; update both checkout
steps (the "Checkout workflow scripts" step and the other actions/checkout at
lines referenced around 40-46) to use the PR merge commit SHA via
`github.event.pull_request.merge_commit_sha` for the release flow (fallback to
`github.sha` or fail fast if the merge SHA is not set), so the release
builds/tags/pushes the exact PR merge commit rather than a moving `main` tip;
alternatively add a guard that compares `main` head with the expected merge SHA
and fails/retries before `git tag`/`git push` if they differ.
In `@src/mcp_ynab/code_mode/examples.md`:
- Around line 119-121: The example calls ynab.write.approve_transactions using
budget_id in the else branch before it’s defined; fix by computing an
effective_budget_id first (e.g., set effective_budget_id =
transactions[0].budget_id if hasattr(transactions[0], "budget_id") else
provided_budget_id or a default) and then pass effective_budget_id to
ynab.write.approve_transactions (reference symbols: transactions, budget_id,
ynab.write.approve_transactions).
In `@src/mcp_ynab/resources.py`:
- Around line 186-190: The loop over groups should skip YNAB groups that are
marked deleted so stale categories aren't rendered; before deriving
group_name/categories/active (in the for group in groups loop in
src/mcp_ynab/resources.py) add a guard that continues if getattr(group,
"deleted", False) is True (or equivalently check group.deleted), and keep the
existing check that continues when there are no non-deleted categories (the
active list) so only non-deleted groups with active categories are processed.
In `@src/mcp_ynab/tools/budgeting.py`:
- Around line 310-317: In move_money, add an early check after resolving
from_category_id and to_category_id to detect if they refer to the same category
(compare the resolved IDs or Category objects) and short-circuit as a no-op
(return immediately) to avoid performing the two-step update that would
incorrectly change the budget; place this check before computing delta
(int(amount * 1000)) and before calling _resolve_month so the function exits
cleanly when from_category_id == to_category_id (or their resolved equivalents).
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 910065a5-4303-4dc9-9917-444c5332bd77
📒 Files selected for processing (20)
.beads/interactions.jsonl.beads/issues.jsonl.github/scripts/bump_version.py.github/workflows/release-labels.yml.github/workflows/release.ymlsrc/mcp_ynab/__init__.pysrc/mcp_ynab/code_mode/examples.mdsrc/mcp_ynab/code_mode/runner.pysrc/mcp_ynab/resources.pysrc/mcp_ynab/server.pysrc/mcp_ynab/state.pysrc/mcp_ynab/tools/budgeting.pysrc/mcp_ynab/tools/transactions.pytests/conftest.pytests/test_budgeting_core.pytests/test_code_mode.pytests/test_release_version.pytests/test_server.pytests/test_state.pytests/test_tools.py
✅ Files skipped from review due to trivial changes (1)
- .beads/issues.jsonl
|
@coderabbitai help |
ChatThere are 3 ways to chat with CodeRabbit:
CodeRabbit commands
Other keywords and placeholders
CodeRabbit configuration file (
|
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
Summary
Verification
Summary by CodeRabbit
New Features
Documentation
Tests
Chores