feat: Install Poetry requires-plugins before running Poetry commands#14707
feat: Install Poetry requires-plugins before running Poetry commands#14707markhallen merged 10 commits intomainfrom
Conversation
Introduce a new PoetryPluginInstaller class that reads [tool.poetry.requires-plugins] from pyproject.toml and installs the declared Poetry plugins before running Poetry commands. Includes input validation to prevent command injection and comprehensive test coverage with fixture files.
Wire the PoetryPluginInstaller into the lockfile generator, file updater, and version resolver so that required Poetry plugins are installed before any Poetry commands are executed. Adds integration tests verifying plugin installation is invoked in each component.
Fix unnecessary safe navigation in file_parser.rb where pyproject is already checked for nil. Remove redundant T.cast in package_manager.rb and add clarifying comment about Poetry-specific requires-poetry constraint support.
155ba5b to
8303d33
Compare
There was a problem hiding this comment.
Pull request overview
Adds support in the Python (Poetry) ecosystem for Poetry v2’s requires-plugins field by ensuring declared Poetry plugins are installed before Dependabot runs Poetry commands, reducing confusing failures when plugins are required.
Changes:
- Introduces
Dependabot::Python::PoetryPluginInstallerto parsetool.poetry.requires-pluginsfrompyproject.tomland install plugins viapoetry self addwith validation/escaping. - Integrates plugin installation into Poetry’s lockfile generation, file updating, and version resolution workflows.
- Adds unit/integration specs plus fixtures covering single/multiple/no plugins and invalid/malicious inputs.
Show a summary per file
| File | Description |
|---|---|
| python/lib/dependabot/python/poetry_plugin_installer.rb | New installer that reads requires-plugins and runs poetry self add safely. |
| python/lib/dependabot/python/update_checker/poetry_version_resolver.rb | Calls plugin installer before running Poetry update/resolve commands. |
| python/lib/dependabot/python/file_updater/poetry_file_updater.rb | Calls plugin installer before running Poetry update during lockfile regeneration. |
| python/lib/dependabot/python/dependency_grapher/lockfile_generator.rb | Calls plugin installer before running poetry lock for dependency graphing. |
| python/lib/dependabot/python/file_parser.rb | Minor nil-guard cleanup for requires-poetry parsing. |
| python/lib/dependabot/python/package_manager.rb | Removes redundant T.cast in raise_if_unsupported!. |
| python/spec/dependabot/python/poetry_plugin_installer_spec.rb | New unit tests for parsing/validation/idempotency and failure handling. |
| python/spec/dependabot/python/update_checker/poetry_version_resolver_spec.rb | Adds integration coverage asserting plugin installation is invoked during resolution. |
| python/spec/dependabot/python/file_updater/poetry_file_updater_spec.rb | Adds integration coverage asserting plugin installation is invoked during updates. |
| python/spec/dependabot/python/dependency_grapher/lockfile_generator_spec.rb | Adds integration coverage asserting plugin installation is invoked during lock generation. |
| python/spec/fixtures/pyproject_files/requires_plugins.toml | Fixture with a single required Poetry plugin. |
| python/spec/fixtures/pyproject_files/requires_plugins_multiple.toml | Fixture with multiple required Poetry plugins. |
| python/spec/fixtures/pyproject_files/requires_plugins_none.toml | Fixture with no requires-plugins section. |
Copilot's findings
Comments suppressed due to low confidence (2)
python/spec/dependabot/python/file_updater/poetry_file_updater_spec.rb:988
- Rescuing
StandardErrorhere can mask unexpected failures and allow the example to pass even if the code under test raises before reaching the expectation. Prefer stubbing the remaining collaborators (e.g., file reads / poetry command runners) soupdated_dependency_filescan run to completion, and assert that no unexpected error is raised.
allow(File).to receive(:read).with("poetry.lock").and_return("lock content")
begin
updater.updated_dependency_files
rescue StandardError
python/spec/dependabot/python/update_checker/poetry_version_resolver_spec.rb:691
- Catching
StandardErrorin the test body can hide failures unrelated to plugin installation (e.g., file I/O issues) and still let the expectation run. Prefer making the resolver call succeed by stubbing required file reads/writes and helpers, and assert that no error is raised while verifying the installer is invoked.
begin
resolver.latest_resolvable_version(requirement: "*")
rescue StandardError
nil
end
- Files reviewed: 13/13 changed files
- Comments generated: 2
The tests were stubbing SharedHelpers.in_a_temporary_repo_directory, but the production code uses SharedHelpers.in_a_temporary_directory with SharedHelpers.with_git_configured. Updated both spec files to stub the correct helpers, and added python_version to the LanguageVersionManager double for the file updater spec.
- Add fallback regex in Pep621Updater when source_requirement metadata is absent (e.g. after DependencySet merge or deserialization) - Make Poetry plugin installation failures non-fatal (log warning instead of raising DependabotError) - Handle != operator explicitly in bump_single_requirement - Clarify > to >= conversion comment in requirements updater - Add tests for all edge cases
When BumpVersions bumps a lower bound (e.g. >=0.2.0,<1.0.0 to >=0.3.5,<1.0.0) but the lockfile already has the target version locked, the pyproject changes but poetry update produces the same lockfile. Previously this raised 'Expected lockfile to change!' — now we simply return the updated pyproject without the lockfile, which is a valid constraint-only update. Fixes DELTAFORCE-1B39
Post-deploy observation:
|
What are you trying to accomplish?
Poetry v2 supports a
requires-pluginsfield inpyproject.tomlthat declares which Poetry plugins a project depends on. When Dependabot runs Poetry commands (lock, update, resolve) on a project that requires plugins, those plugins must be installed first — otherwise Poetry may fail with confusing errors or produce incorrect results.This builds on the
requires-poetryconstraint support merged in #14684 by adding automatic plugin installation.Anything you want to highlight for special attention from reviewers?
PoetryPluginInstallerclass — reads[tool.poetry.requires-plugins]frompyproject.tomland installs each declared plugin viapoetry self add. Input validation (regex allowlists for plugin names and version constraints) prevents command injection. UsesShellwords.shellescapeas an additional safety layer.poetry_plugin_installer.install_required_pluginsbefore executing Poetry commands, following the same pattern aslanguage_version_manager.install_required_python.Pep621Updaternow falls back to matching against the normalized requirement string whensource_requirementmetadata is absent (e.g. afterDependencySetmerge or deserialization), preventing silent update failures.!=operator handling —bump_single_requirementnow handles!=explicitly (keeps unchanged) instead of relying on a catch-allelsebranch.How will you know you've accomplished your goal?
PoetryPluginInstallerhas comprehensive unit tests covering: single plugin, multiple plugins, missing/empty section, invalid plugin names and constraints (command injection prevention), TOML parse errors, failure resilience (warns instead of raising), and idempotent installation.install_required_pluginsis invoked during their respective workflows.source_requirementmetadata still get updated correctly.>lower bound conversion and!=exclusion alongside ranges for bothBumpVersionsandBumpVersionsIfNecessarystrategies.requires_plugins.toml,requires_plugins_multiple.toml,requires_plugins_none.toml) support the test scenarios.Checklist