Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 53 additions & 12 deletions .github/workflows/check.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ jobs:
uses: actions/setup-java@v5
with:
distribution: zulu
java-version: 17
java-version: 21
- name: Set up Gradle
uses: gradle/actions/setup-gradle@v5
- name: Build plugin
Expand Down Expand Up @@ -68,7 +68,7 @@ jobs:
strategy:
fail-fast: false
matrix:
ide: [PC, PY, IC, IU, GO, CL, RR]
ide: [PC, PY]
steps:
- name: Free disk space
uses: jlumbroso/free-disk-space@main
Expand All @@ -83,26 +83,55 @@ jobs:
uses: actions/setup-java@v5
with:
distribution: zulu
java-version: 17
java-version: 21
- name: Set up Gradle
uses: gradle/actions/setup-gradle@v5
with:
validate-wrappers: false
cache-read-only: true
- name: Set up verifier cache
cache-read-only: false
- name: Cache plugin verifier IDEs
uses: actions/cache@v5
with:
path: ${{ needs.build.outputs.pluginVerifierHomeDir }}/ides
key: plugin-verifier-${{ matrix.ide }}-${{ needs.build.outputs.platformVersion }}
path: ~/.pluginVerifier/ides
key: plugin-verifier-ides-${{ matrix.ide }}-${{ needs.build.outputs.platformVersion }}
- name: Clean corrupted Gradle transforms
run: |
find ~/.gradle/caches -type d -name "transforms" -exec sh -c '
for dir in "$1"/*/; do
if [ -d "$dir" ] && [ ! -f "${dir}metadata.bin" ]; then
echo "Removing corrupted transform: $dir"
rm -rf "$dir"
fi
done
' _ {} \; 2>/dev/null || true
- name: Run verification
run: ./gradlew verifyPlugin -PverifyIde=${{ matrix.ide }} -Dplugin.verifier.home.dir=${{ needs.build.outputs.pluginVerifierHomeDir }}
run: ./gradlew verifyPlugin -PverifyIde=${{ matrix.ide }}
- name: Collect verification result
if: ${{ always() }}
uses: actions/upload-artifact@v6
with:
name: pluginVerifier-result-${{ matrix.ide }}
path: ${{ github.workspace }}/build/reports/pluginVerifier

lint:
name: Lint
needs: [build]
runs-on: ubuntu-latest
steps:
- name: Checkout git repository
uses: actions/checkout@v6
- name: Set up Java
uses: actions/setup-java@v5
with:
distribution: zulu
java-version: 21
- name: Set up Gradle
uses: gradle/actions/setup-gradle@v5
with:
cache-read-only: true
- name: Run linter
run: ./gradlew ktlintCheck

test:
name: Run tests
needs: [build]
Expand All @@ -114,19 +143,31 @@ jobs:
uses: actions/setup-java@v5
with:
distribution: zulu
java-version: 17
java-version: 21
- name: Set up Gradle
uses: gradle/actions/setup-gradle@v5
with:
validate-wrappers: false
cache-read-only: true
- name: Run linter
run: ./gradlew ktlintCheck
- name: Run unit tests
run: ./gradlew test
- name: Verify 100% coverage
run: ./gradlew koverVerify
- name: Run UI tests
run: |
export DISPLAY=:99.0
Xvfb :99 -screen 0 1920x1080x24 &
./gradlew runIdeForUiTests &
echo "Waiting for IDE to start..."
timeout 180 bash -c 'until curl -s http://127.0.0.1:8082 > /dev/null 2>&1; do sleep 2; done' || { echo "IDE failed to start"; exit 1; }
echo "IDE is ready"
./gradlew uiTest
kill %1 %2 || true

releaseDraft:
name: Create release draft
if: github.event_name != 'pull_request'
needs: [build, test, verify]
needs: [build, lint, test, verify]
runs-on: ubuntu-latest
permissions:
contents: write
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/release.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ jobs:
uses: actions/setup-java@v5
with:
distribution: zulu
java-version: 17
java-version: 21
- name: Set up Gradle
uses: gradle/actions/setup-gradle@v5
- name: Extract plugin properties
Expand Down
107 changes: 107 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
# Contributing to PyVenvManage

## Development Setup

You'll need JDK 21 and Python 3.10+ (for creating test virtual environments). Build the plugin with:

```bash
./gradlew buildPlugin
```

## Testing

The project uses two complementary testing strategies: fast unit tests that mock IntelliJ platform dependencies, and
end-to-end UI tests that interact with a running IDE.

### Unit Tests

Unit tests cover business logic, action update logic, and error paths. They run quickly and don't require a running
IDE:

```bash
./gradlew test
```

### UI Tests

UI tests validate full user workflows by interacting with a running PyCharm instance via RemoteRobot. Start the IDE
with robot-server in one terminal:

```bash
./gradlew runIdeForUiTests
```

Wait for the IDE to fully start and the robot-server to be ready at http://localhost:8082, then run the tests in
another terminal:

```bash
./gradlew uiTest
```

### Coverage

Unit tests achieve full line coverage. The CI enforces this with `./gradlew test koverVerify`. UI tests are excluded
from coverage collection since they test end-to-end workflows already covered by unit tests.

To generate an HTML coverage report showing overall percentage, package breakdown, and line-by-line highlighting:

```bash
./gradlew test koverHtmlReport
open build/reports/kover/html/index.html
```

For per-test coverage analysis (which test covered which line), generate a binary report with
`./gradlew test koverBinaryReport`, then in IntelliJ IDEA go to **Run → Show Coverage Data**, click **+**, select
`build/kover/bin-reports/test.ic`, and click **Show selected**. Right-click any covered line and choose **Show Covering
Tests** to see which tests hit it.

## Code Quality

Check code style with `./gradlew ktlintCheck` or auto-fix issues with `./gradlew ktlintFormat`. Run all checks together
(lint, unit tests, coverage verification) with `./gradlew check`.

## Continuous Integration

The CI pipeline in `.github/workflows/check.yaml` builds the plugin, runs linting, executes unit tests with coverage
verification followed by UI tests, verifies the plugin against PyCharm Community and PyCharm Professional, and creates
a release draft on the main branch.

The test job runs unit tests with `koverVerify`, then starts Xvfb and the IDE with robot-server, and finally runs UI
tests for end-to-end validation.

## Making Code Changes

Before committing, run `./gradlew ktlintFormat` to fix style issues, then `./gradlew test koverVerify` to ensure tests
pass with full coverage. If you modified action classes, run UI tests for end-to-end validation by starting
`./gradlew runIdeForUiTests` in one terminal and `./gradlew uiTest` in another.

Follow conventional commit style: use `feat:` for new features, `fix:` for bug fixes, `refactor:` for code
refactoring, `test:` for test changes, `docs:` for documentation, and `chore:` for maintenance tasks.

## Troubleshooting

If UI tests timeout or fail to connect, ensure no other IDE instance is using port 8082. Kill any running IDE processes
with `pkill -f runIdeForUiTests`, delete old test projects with `rm -rf ~/projects/ui-test*`, then restart the IDE and
wait for full initialization before running tests.

If `koverVerify` fails due to coverage below 100%, generate the HTML report with `./gradlew koverHtmlReport` and open
`build/reports/kover/html/index.html` to see which lines are uncovered. Add unit tests for those code paths, or if the
code requires IntelliJ platform services that can't be mocked, add UI test coverage instead.

## Releasing

The plugin version is defined in `gradle.properties` as `pluginVersion`. To release, update the version in that file
and merge your PR to main. The CI automatically creates a draft release on GitHub with the version from
`gradle.properties`.

Review the draft release on the [Releases page](https://github.com/pyvenvmanage/PyVenvManage/releases) and edit the
release notes if needed. Click "Publish release" (not pre-release) to trigger the release workflow, which builds and
signs the plugin, publishes to [JetBrains Marketplace](https://plugins.jetbrains.com/plugin/20536-pyvenv-manage-2),
uploads the plugin ZIP to the GitHub release, and creates a PR to update CHANGELOG.md. Merge that changelog PR after
the release workflow completes.

The release workflow requires repository secrets configured by maintainers: `PUBLISH_TOKEN` for JetBrains Marketplace
upload, and `CERTIFICATE_CHAIN`, `PRIVATE_KEY`, and `PRIVATE_KEY_PASSWORD` for plugin signing.

Follow [semantic versioning](https://semver.org/): increment MAJOR for breaking changes, MINOR for new backward
compatible features, and PATCH for backward compatible bug fixes.
22 changes: 20 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,10 @@ streamlines this by enabling quick interpreter selection directly from the proje

- **Quick interpreter switching**: Right-click any virtual environment folder to set it as your project or module
interpreter instantly
- **Visual identification**: Virtual environment folders display with a distinctive icon and Python version badge
(e.g., `venv [3.11.5]`) in the project view
- **Visual identification**: Virtual environment folders display with a distinctive icon and customizable decoration
(e.g., `.venv [3.11.5 - CPython]`) in the project view
- **Customizable decorations**: Configure which fields to show (Python version, implementation, system site-packages,
creator tool), their order, and the format via Settings
- **Multi-IDE support**: Works with PyCharm (Community and Professional), IntelliJ IDEA, GoLand, CLion, and RustRover
- **Smart detection**: Automatically detects Python virtual environments by recognizing `pyvenv.cfg` files
- **Cached version display**: Python version information is cached for performance and automatically refreshed when
Expand All @@ -30,6 +32,8 @@ streamlines this by enabling quick interpreter selection directly from the proje

## Supported IDEs

Version 2025.1 or later of:

- PyCharm (Community and Professional)
- IntelliJ IDEA (Community and Ultimate)
- GoLand
Expand All @@ -51,6 +55,20 @@ The official plugin page is at https://plugins.jetbrains.com/plugin/20536-pyvenv
3. Select **Set as Project Interpreter** or **Set as Module Interpreter**
4. The interpreter is configured instantly with a confirmation notification

## Settings

Open **Settings** -> **PyVenv Manage** to customize the virtual environment decoration display:

- **Prefix/Suffix**: Characters surrounding the decoration (default: ` [` and `]`)
- **Separator**: Text between fields (default: `-`)
- **Fields**: Enable, disable, and reorder which information to display:
- Python version (e.g., `3.14.2`)
- Python implementation (e.g., `CPython`)
- System site-packages indicator (`SYSTEM`)
- Virtual environment creator (e.g., `uv@0.9.21`)

A live preview updates as you modify settings.

## License

This project is licensed under the BSD-3-Clause license - see the
Expand Down
Loading