-
Notifications
You must be signed in to change notification settings - Fork 2.4k
Add poetry wrapper command to generate version-pinned script #10335
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Closed
Closed
Changes from all commits
Commits
Show all changes
9 commits
Select commit
Hold shift + click to select a range
de0309e
Add poetry wrapper command to generate version-pinned script
ranzyblessings 5acf377
Add documentation for `poetry wrapper` command in cli.md
ranzyblessings 883a47e
Revert "Add documentation for `poetry wrapper` command in cli.md"
ranzyblessings 543b17e
Enhance poetry wrapper command based on Sourcery-AI suggestions
ranzyblessings cd4de30
Refactor and Improve Wrapper Command for Consistency and Security Bas…
ranzyblessings a5b6adb
Refactor test case patching for improved readability and efficiency
ranzyblessings d1751b4
Refactor test_handle_properties_file_write_error for improved readabi…
ranzyblessings ef4030d
Improve Cross-Platform Compatibility of WrapperCommand's _make_execut…
ranzyblessings 80d0f0b
Fix linting and formatting issues (ruff, ruff-format)
ranzyblessings File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,172 @@ | ||
| from __future__ import annotations | ||
|
|
||
| import os | ||
| import re | ||
|
|
||
| from pathlib import Path | ||
| from typing import ClassVar | ||
|
|
||
| from cleo.io.inputs.option import Option | ||
|
|
||
| from poetry.__version__ import __version__ | ||
| from poetry.console.commands.command import Command | ||
|
|
||
|
|
||
| class WrapperCommand(Command): | ||
| name = "wrapper" | ||
| description = "Generates a wrapper script to pin a specific Poetry version." | ||
| help = """ | ||
| The <info>wrapper</info> command creates a <comment>poetryw</comment> script and a | ||
| <comment>poetry-wrapper.properties</comment> file in the current directory to ensure | ||
| Poetry commands use a specified version. | ||
|
|
||
| This is useful for projects that require a consistent Poetry version across environments. | ||
|
|
||
| <comment>Examples:</> | ||
| <info>poetry wrapper</info> | ||
| Generates a wrapper using the current Poetry version. | ||
| <info>poetry wrapper --poetry-version 2.1.2</info> | ||
| Generates a wrapper for Poetry version 2.1.2. | ||
|
|
||
| <comment>Note:</> The generated <comment>poetryw</comment> script is a Bash script and is | ||
| intended for Unix-like systems. Windows support may require additional configuration. | ||
| """ | ||
|
|
||
| options: ClassVar[list[Option]] = [ | ||
| Option( | ||
| "--poetry-version", | ||
| description="Specify the Poetry version for the wrapper (defaults to current version)", | ||
| flag=False, | ||
| default=None, | ||
| ), | ||
| ] | ||
|
|
||
| def handle(self) -> int: | ||
| """Generate the Poetry wrapper script and properties file.""" | ||
| version: str = self.option("poetry-version") or __version__ | ||
|
|
||
| # Validate version format (basic semantic version check) | ||
| if not self._is_valid_version(version): | ||
| self.line_error( | ||
| f"Invalid version format: <error>{version}</error>. Expected format: X.Y.Z" | ||
| ) | ||
| return 1 | ||
|
|
||
| project_dir: Path = ( | ||
| Path.cwd() | ||
| ) # Consider self.poetry.file.path.parent for project root | ||
| properties_file: Path = project_dir / "poetry-wrapper.properties" | ||
| script_file: Path = project_dir / "poetryw" | ||
|
|
||
| # Check for existing files and warn user | ||
| if properties_file.exists() or script_file.exists(): | ||
| self.line( | ||
| "<warning>Warning: Existing wrapper files detected. They will be overwritten.</warning>" | ||
| ) | ||
|
|
||
| # Write poetry-wrapper.properties | ||
| try: | ||
| properties_file.write_text(f"version={version}\n", encoding="utf-8") | ||
| except OSError as e: | ||
| self.line_error( | ||
| f"Failed to write <error>{properties_file}</error>: <error>{e}</error>" | ||
| ) | ||
| return 1 | ||
|
|
||
| # Generate the poetryw script content | ||
| script_content: str = self._generate_script_content(version) | ||
|
|
||
| # Write and make the script executable | ||
| try: | ||
| script_file.write_text(script_content, encoding="utf-8") | ||
| # Set executable permissions (Unix-like systems) | ||
| self._make_executable(script_file) | ||
| except OSError as e: | ||
| self.line_error( | ||
| f"Failed to write or configure <error>{script_file}</error>: <error>{e}</error>" | ||
| ) | ||
| return 1 | ||
|
|
||
| self.line( | ||
| f"Poetry wrapper generated successfully for version <info>{version}</info>." | ||
| ) | ||
| self.line(f" - Properties: <comment>{properties_file}</comment>") | ||
| self.line(f" - Script: <comment>{script_file}</comment>") | ||
| self.line("Run <info>./poetryw</info> to use Poetry with the pinned version.") | ||
| return 0 | ||
|
|
||
| def _is_valid_version(self, version: str) -> bool: | ||
| """Validate that the version string follows a semantic version format (X.Y.Z). | ||
|
|
||
| Args: | ||
| version: The version string to validate. | ||
|
|
||
| Returns: | ||
| True if the version matches X.Y.Z, False otherwise. | ||
| """ | ||
| return bool(re.match(r"^\d+\.\d+\.\d+$", version)) | ||
|
|
||
| def _generate_script_content(self, version: str) -> str: | ||
| """Generate the content of the poetryw wrapper script.""" | ||
| return f"""#!/bin/bash | ||
| # Poetry wrapper script | ||
| # Ensures Poetry version {version} is used to run commands | ||
| # | ||
| # Generated by `poetry wrapper` on {self._get_current_date()} | ||
|
|
||
| set -e # Exit on error | ||
|
|
||
| # Check if poetry-wrapper.properties exists | ||
| PROPERTIES_FILE="poetry-wrapper.properties" | ||
| if [ ! -f "$PROPERTIES_FILE" ]; then | ||
| echo "Error: $PROPERTIES_FILE not found. Run 'poetry wrapper' to generate it." | ||
| exit 1 | ||
| fi | ||
|
|
||
| # Read version from poetry-wrapper.properties | ||
| POETRY_VERSION=$(grep '^version=' "$PROPERTIES_FILE" | cut -d'=' -f2) | ||
| if [ -z "$POETRY_VERSION" ]; then | ||
| echo "Error: No version specified in $PROPERTIES_FILE." | ||
| exit 1 | ||
| fi | ||
|
|
||
| # Set POETRY_HOME to a version-specific cache | ||
| POETRY_HOME="$HOME/.poetry/versions/$POETRY_VERSION" | ||
|
|
||
| # Install Poetry if not present | ||
| if [ ! -f "$POETRY_HOME/bin/poetry" ]; then | ||
| echo "Installing Poetry version $POETRY_VERSION to $POETRY_HOME..." | ||
| mkdir -p "$POETRY_HOME" | ||
| if ! curl -sSL https://install.python-poetry.org | POETRY_HOME="$POETRY_HOME" python3 - --version "$POETRY_VERSION"; then | ||
| echo "Error: Failed to install Poetry version $POETRY_VERSION." | ||
| exit 1 | ||
| fi | ||
| # Verify Poetry executable exists after installation | ||
| if [ ! -f "$POETRY_HOME/bin/poetry" ]; then | ||
| echo "Error: Poetry executable not found at $POETRY_HOME/bin/poetry after installation." | ||
| exit 1 | ||
| fi | ||
| fi | ||
|
|
||
| # Run Poetry with provided arguments | ||
| exec "$POETRY_HOME/bin/poetry" "$@" | ||
| """ | ||
|
|
||
| def _make_executable(self, file: Path) -> None: | ||
| """Set executable permissions for the script file on Unix-like systems.""" | ||
| if os.name != "posix": | ||
| self.line( | ||
| "<warning>Setting executable permissions is not supported on this platform.</warning>" | ||
| ) | ||
| return | ||
| try: | ||
| # Equivalent to chmod 755 | ||
| file.chmod(0o755) | ||
| except OSError: | ||
| raise | ||
|
|
||
| def _get_current_date(self) -> str: | ||
| """Return the current date for script metadata.""" | ||
| from datetime import datetime | ||
|
|
||
| return datetime.now().strftime("%Y-%m-%d") | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.