Skip to content

Commit 694edc0

Browse files
Enhance project view decorations and add 100% test coverage (#104)
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
1 parent 70ddeba commit 694edc0

32 files changed

Lines changed: 2335 additions & 234 deletions

.github/workflows/check.yaml

Lines changed: 53 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ jobs:
2929
uses: actions/setup-java@v5
3030
with:
3131
distribution: zulu
32-
java-version: 17
32+
java-version: 21
3333
- name: Set up Gradle
3434
uses: gradle/actions/setup-gradle@v5
3535
- name: Build plugin
@@ -68,7 +68,7 @@ jobs:
6868
strategy:
6969
fail-fast: false
7070
matrix:
71-
ide: [PC, PY, IC, IU, GO, CL, RR]
71+
ide: [PC, PY]
7272
steps:
7373
- name: Free disk space
7474
uses: jlumbroso/free-disk-space@main
@@ -83,26 +83,55 @@ jobs:
8383
uses: actions/setup-java@v5
8484
with:
8585
distribution: zulu
86-
java-version: 17
86+
java-version: 21
8787
- name: Set up Gradle
8888
uses: gradle/actions/setup-gradle@v5
8989
with:
9090
validate-wrappers: false
91-
cache-read-only: true
92-
- name: Set up verifier cache
91+
cache-read-only: false
92+
- name: Cache plugin verifier IDEs
9393
uses: actions/cache@v5
9494
with:
95-
path: ${{ needs.build.outputs.pluginVerifierHomeDir }}/ides
96-
key: plugin-verifier-${{ matrix.ide }}-${{ needs.build.outputs.platformVersion }}
95+
path: ~/.pluginVerifier/ides
96+
key: plugin-verifier-ides-${{ matrix.ide }}-${{ needs.build.outputs.platformVersion }}
97+
- name: Clean corrupted Gradle transforms
98+
run: |
99+
find ~/.gradle/caches -type d -name "transforms" -exec sh -c '
100+
for dir in "$1"/*/; do
101+
if [ -d "$dir" ] && [ ! -f "${dir}metadata.bin" ]; then
102+
echo "Removing corrupted transform: $dir"
103+
rm -rf "$dir"
104+
fi
105+
done
106+
' _ {} \; 2>/dev/null || true
97107
- name: Run verification
98-
run: ./gradlew verifyPlugin -PverifyIde=${{ matrix.ide }} -Dplugin.verifier.home.dir=${{ needs.build.outputs.pluginVerifierHomeDir }}
108+
run: ./gradlew verifyPlugin -PverifyIde=${{ matrix.ide }}
99109
- name: Collect verification result
100110
if: ${{ always() }}
101111
uses: actions/upload-artifact@v6
102112
with:
103113
name: pluginVerifier-result-${{ matrix.ide }}
104114
path: ${{ github.workspace }}/build/reports/pluginVerifier
105115

116+
lint:
117+
name: Lint
118+
needs: [build]
119+
runs-on: ubuntu-latest
120+
steps:
121+
- name: Checkout git repository
122+
uses: actions/checkout@v6
123+
- name: Set up Java
124+
uses: actions/setup-java@v5
125+
with:
126+
distribution: zulu
127+
java-version: 21
128+
- name: Set up Gradle
129+
uses: gradle/actions/setup-gradle@v5
130+
with:
131+
cache-read-only: true
132+
- name: Run linter
133+
run: ./gradlew ktlintCheck
134+
106135
test:
107136
name: Run tests
108137
needs: [build]
@@ -114,19 +143,31 @@ jobs:
114143
uses: actions/setup-java@v5
115144
with:
116145
distribution: zulu
117-
java-version: 17
146+
java-version: 21
118147
- name: Set up Gradle
119148
uses: gradle/actions/setup-gradle@v5
120149
with:
121150
validate-wrappers: false
122151
cache-read-only: true
123-
- name: Run linter
124-
run: ./gradlew ktlintCheck
152+
- name: Run unit tests
153+
run: ./gradlew test
154+
- name: Verify 100% coverage
155+
run: ./gradlew koverVerify
156+
- name: Run UI tests
157+
run: |
158+
export DISPLAY=:99.0
159+
Xvfb :99 -screen 0 1920x1080x24 &
160+
./gradlew runIdeForUiTests &
161+
echo "Waiting for IDE to start..."
162+
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; }
163+
echo "IDE is ready"
164+
./gradlew uiTest
165+
kill %1 %2 || true
125166
126167
releaseDraft:
127168
name: Create release draft
128169
if: github.event_name != 'pull_request'
129-
needs: [build, test, verify]
170+
needs: [build, lint, test, verify]
130171
runs-on: ubuntu-latest
131172
permissions:
132173
contents: write

.github/workflows/release.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ jobs:
2727
uses: actions/setup-java@v5
2828
with:
2929
distribution: zulu
30-
java-version: 17
30+
java-version: 21
3131
- name: Set up Gradle
3232
uses: gradle/actions/setup-gradle@v5
3333
- name: Extract plugin properties

CONTRIBUTING.md

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
# Contributing to PyVenvManage
2+
3+
## Development Setup
4+
5+
You'll need JDK 21 and Python 3.10+ (for creating test virtual environments). Build the plugin with:
6+
7+
```bash
8+
./gradlew buildPlugin
9+
```
10+
11+
## Testing
12+
13+
The project uses two complementary testing strategies: fast unit tests that mock IntelliJ platform dependencies, and
14+
end-to-end UI tests that interact with a running IDE.
15+
16+
### Unit Tests
17+
18+
Unit tests cover business logic, action update logic, and error paths. They run quickly and don't require a running
19+
IDE:
20+
21+
```bash
22+
./gradlew test
23+
```
24+
25+
### UI Tests
26+
27+
UI tests validate full user workflows by interacting with a running PyCharm instance via RemoteRobot. Start the IDE
28+
with robot-server in one terminal:
29+
30+
```bash
31+
./gradlew runIdeForUiTests
32+
```
33+
34+
Wait for the IDE to fully start and the robot-server to be ready at http://localhost:8082, then run the tests in
35+
another terminal:
36+
37+
```bash
38+
./gradlew uiTest
39+
```
40+
41+
### Coverage
42+
43+
Unit tests achieve full line coverage. The CI enforces this with `./gradlew test koverVerify`. UI tests are excluded
44+
from coverage collection since they test end-to-end workflows already covered by unit tests.
45+
46+
To generate an HTML coverage report showing overall percentage, package breakdown, and line-by-line highlighting:
47+
48+
```bash
49+
./gradlew test koverHtmlReport
50+
open build/reports/kover/html/index.html
51+
```
52+
53+
For per-test coverage analysis (which test covered which line), generate a binary report with
54+
`./gradlew test koverBinaryReport`, then in IntelliJ IDEA go to **Run → Show Coverage Data**, click **+**, select
55+
`build/kover/bin-reports/test.ic`, and click **Show selected**. Right-click any covered line and choose **Show Covering
56+
Tests** to see which tests hit it.
57+
58+
## Code Quality
59+
60+
Check code style with `./gradlew ktlintCheck` or auto-fix issues with `./gradlew ktlintFormat`. Run all checks together
61+
(lint, unit tests, coverage verification) with `./gradlew check`.
62+
63+
## Continuous Integration
64+
65+
The CI pipeline in `.github/workflows/check.yaml` builds the plugin, runs linting, executes unit tests with coverage
66+
verification followed by UI tests, verifies the plugin against PyCharm Community and PyCharm Professional, and creates
67+
a release draft on the main branch.
68+
69+
The test job runs unit tests with `koverVerify`, then starts Xvfb and the IDE with robot-server, and finally runs UI
70+
tests for end-to-end validation.
71+
72+
## Making Code Changes
73+
74+
Before committing, run `./gradlew ktlintFormat` to fix style issues, then `./gradlew test koverVerify` to ensure tests
75+
pass with full coverage. If you modified action classes, run UI tests for end-to-end validation by starting
76+
`./gradlew runIdeForUiTests` in one terminal and `./gradlew uiTest` in another.
77+
78+
Follow conventional commit style: use `feat:` for new features, `fix:` for bug fixes, `refactor:` for code
79+
refactoring, `test:` for test changes, `docs:` for documentation, and `chore:` for maintenance tasks.
80+
81+
## Troubleshooting
82+
83+
If UI tests timeout or fail to connect, ensure no other IDE instance is using port 8082. Kill any running IDE processes
84+
with `pkill -f runIdeForUiTests`, delete old test projects with `rm -rf ~/projects/ui-test*`, then restart the IDE and
85+
wait for full initialization before running tests.
86+
87+
If `koverVerify` fails due to coverage below 100%, generate the HTML report with `./gradlew koverHtmlReport` and open
88+
`build/reports/kover/html/index.html` to see which lines are uncovered. Add unit tests for those code paths, or if the
89+
code requires IntelliJ platform services that can't be mocked, add UI test coverage instead.
90+
91+
## Releasing
92+
93+
The plugin version is defined in `gradle.properties` as `pluginVersion`. To release, update the version in that file
94+
and merge your PR to main. The CI automatically creates a draft release on GitHub with the version from
95+
`gradle.properties`.
96+
97+
Review the draft release on the [Releases page](https://github.com/pyvenvmanage/PyVenvManage/releases) and edit the
98+
release notes if needed. Click "Publish release" (not pre-release) to trigger the release workflow, which builds and
99+
signs the plugin, publishes to [JetBrains Marketplace](https://plugins.jetbrains.com/plugin/20536-pyvenv-manage-2),
100+
uploads the plugin ZIP to the GitHub release, and creates a PR to update CHANGELOG.md. Merge that changelog PR after
101+
the release workflow completes.
102+
103+
The release workflow requires repository secrets configured by maintainers: `PUBLISH_TOKEN` for JetBrains Marketplace
104+
upload, and `CERTIFICATE_CHAIN`, `PRIVATE_KEY`, and `PRIVATE_KEY_PASSWORD` for plugin signing.
105+
106+
Follow [semantic versioning](https://semver.org/): increment MAJOR for breaking changes, MINOR for new backward
107+
compatible features, and PATCH for backward compatible bug fixes.

README.md

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,10 @@ streamlines this by enabling quick interpreter selection directly from the proje
1919

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

3133
## Supported IDEs
3234

35+
Version 2025.1 or later of:
36+
3337
- PyCharm (Community and Professional)
3438
- IntelliJ IDEA (Community and Ultimate)
3539
- GoLand
@@ -51,6 +55,20 @@ The official plugin page is at https://plugins.jetbrains.com/plugin/20536-pyvenv
5155
3. Select **Set as Project Interpreter** or **Set as Module Interpreter**
5256
4. The interpreter is configured instantly with a confirmation notification
5357

58+
## Settings
59+
60+
Open **Settings** -> **PyVenv Manage** to customize the virtual environment decoration display:
61+
62+
- **Prefix/Suffix**: Characters surrounding the decoration (default: ` [` and `]`)
63+
- **Separator**: Text between fields (default: `-`)
64+
- **Fields**: Enable, disable, and reorder which information to display:
65+
- Python version (e.g., `3.14.2`)
66+
- Python implementation (e.g., `CPython`)
67+
- System site-packages indicator (`SYSTEM`)
68+
- Virtual environment creator (e.g., `uv@0.9.21`)
69+
70+
A live preview updates as you modify settings.
71+
5472
## License
5573

5674
This project is licensed under the BSD-3-Clause license - see the

0 commit comments

Comments
 (0)