Skip to content

Commit 1d78b31

Browse files
feat: Implement Python virtual environment cleaner with TUI
This commit introduces `pykill`, a cross-platform CLI tool written in Rust for finding and deleting Python virtual environments. Key features include: - An interactive TUI built with Ratatui for navigating and selecting environments found in a specified scan path (defaults to current dir). - Displays environment path, size (human-readable), and last modified date. - Deletion functionality with a confirmation step. - Keybindings for navigation (up/down), deletion ('d'), confirmation ('y'/'n'), and quit ('q'). - Command-line options: - `pykill <PATH>`: Scans the specified path. - `pykill --no-tui`: Prints scan results to stdout and exits without launching the TUI. - Cross-platform compatibility (tested via GitHub Actions for Linux, Windows, macOS). - Automated builds and releases via GitHub Actions workflow. - Unit tests for scanner logic (size calculation, venv identification) and application state management (navigation, deletion process). - Comprehensive README.md with installation, usage, and build instructions.
1 parent 67b4c50 commit 1d78b31

File tree

7 files changed

+823
-59
lines changed

7 files changed

+823
-59
lines changed

.github/workflows/release.yml

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
name: Release Build and Upload
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
- master # In case the default branch is master
8+
tags:
9+
- 'v*' # Trigger on version tags like v0.1.0
10+
11+
jobs:
12+
build_and_release:
13+
name: Build and Release
14+
runs-on: ubuntu-latest
15+
strategy:
16+
matrix:
17+
# Define targets to build for
18+
# Note: aarch64-apple-darwin might require specific setup or a newer cross version
19+
include:
20+
- target: x86_64-unknown-linux-gnu
21+
os: linux
22+
ext: ""
23+
archive_ext: ".tar.gz"
24+
artifact_name_suffix: "linux-x86_64"
25+
- target: x86_64-pc-windows-gnu
26+
os: windows
27+
ext: ".exe"
28+
archive_ext: ".zip"
29+
artifact_name_suffix: "windows-x86_64"
30+
- target: x86_64-apple-darwin
31+
os: macos
32+
ext: ""
33+
archive_ext: ".tar.gz" # Using .tar.gz for macOS, .zip is also common
34+
artifact_name_suffix: "macos-x86_64"
35+
- target: aarch64-apple-darwin
36+
os: macos
37+
ext: ""
38+
archive_ext: ".tar.gz"
39+
artifact_name_suffix: "macos-aarch64"
40+
41+
steps:
42+
- name: Checkout code
43+
uses: actions/checkout@v4
44+
45+
- name: Install Rust toolchain
46+
uses: actions-rs/toolchain@v1
47+
with:
48+
toolchain: stable
49+
override: true
50+
components: rustfmt, clippy # Optional: add if you use them in build/test
51+
52+
- name: Install cross
53+
run: cargo install cross --git https://github.com/cross-rs/cross --branch main
54+
# For a specific released version, e.g., cargo install cross --version 0.2.5
55+
56+
- name: Build binary with cross
57+
run: cross build --target ${{ matrix.target }} --release
58+
env:
59+
# For aarch64-apple-darwin, you might need to set specific linkers or SDKs
60+
# See cross-rs documentation if this target fails.
61+
# Example (might not be needed or correct for all setups):
62+
# CFLAGS_aarch64_apple_darwin: "-mmacosx-version-min=10.7"
63+
# CXXFLAGS_aarch64_apple_darwin: "-mmacosx-version-min=10.7"
64+
# For some targets, you might need to specify a Docker image for cross
65+
# CROSS_CONTAINER_ENGINE: docker # or podman
66+
# CROSS_IMAGE_aarch64_apple_darwin: your-custom-image-for-macos-arm
67+
CARGO_TERM_COLOR: always # Ensure colors in cargo output
68+
69+
- name: Prepare artifact name and binary path
70+
id: prep_artifact
71+
run: |
72+
BINARY_NAME="pykill"
73+
TARGET_DIR="target/${{ matrix.target }}/release"
74+
BASE_ARTIFACT_NAME="${BINARY_NAME}-${{ matrix.target }}"
75+
RENAMED_BINARY="${BINARY_NAME}-${{ matrix.target }}${{ matrix.ext }}"
76+
77+
echo "Original binary path: ${TARGET_DIR}/${BINARY_NAME}"
78+
echo "Renamed binary: ${RENAMED_BINARY}"
79+
80+
mv "${TARGET_DIR}/${BINARY_NAME}" "${TARGET_DIR}/${RENAMED_BINARY}"
81+
82+
echo "binary_path=${TARGET_DIR}/${RENAMED_BINARY}" >> $GITHUB_OUTPUT
83+
echo "artifact_filename=${BASE_ARTIFACT_NAME}${matrix.archive_ext}" >> $GITHUB_OUTPUT
84+
echo "renamed_binary_name=${RENAMED_BINARY}" >> $GITHUB_OUTPUT
85+
86+
87+
- name: Package binary (Linux/macOS - tar.gz)
88+
if: runner.os == 'Linux' && (matrix.os == 'linux' || matrix.os == 'macos')
89+
run: |
90+
tar -czvf ${{ steps.prep_artifact.outputs.artifact_filename }} -C target/${{ matrix.target }}/release ${{ steps.prep_artifact.outputs.renamed_binary_name }}
91+
echo "Packaged ${{ steps.prep_artifact.outputs.artifact_filename }}"
92+
93+
- name: Package binary (Windows - zip)
94+
if: runner.os == 'Linux' && matrix.os == 'windows' # Still run on Linux, but package for Windows
95+
run: |
96+
zip -j ${{ steps.prep_artifact.outputs.artifact_filename }} target/${{ matrix.target }}/release/${{ steps.prep_artifact.outputs.renamed_binary_name }}
97+
echo "Packaged ${{ steps.prep_artifact.outputs.artifact_filename }}"
98+
99+
- name: Upload build artifact
100+
uses: actions/upload-artifact@v4
101+
with:
102+
name: pykill-${{ matrix.artifact_name_suffix }} # e.g., pykill-linux-x86_64
103+
path: ${{ steps.prep_artifact.outputs.artifact_filename }}
104+
105+
# This job depends on all matrix builds completing successfully
106+
create_release:
107+
name: Create GitHub Release
108+
if: startsWith(github.ref, 'refs/tags/')
109+
needs: build_and_release # Ensure build job (all matrix variations) is complete
110+
runs-on: ubuntu-latest
111+
permissions:
112+
contents: write # Required to create releases
113+
steps:
114+
- name: Download all build artifacts
115+
uses: actions/download-artifact@v4
116+
# No 'name' specified downloads all artifacts from the workflow run
117+
# into a directory named after each artifact
118+
with:
119+
path: artifacts/ # All artifacts will be in subdirectories here
120+
121+
- name: List downloaded artifacts (for debugging)
122+
run: ls -R artifacts/
123+
124+
- name: Create Release and Upload Assets
125+
uses: softprops/action-gh-release@v1
126+
with:
127+
# token: ${{ secrets.GITHUB_TOKEN }} # Implicitly available
128+
files: |
129+
artifacts/pykill-linux-x86_64/*.tar.gz
130+
artifacts/pykill-windows-x86_64/*.zip
131+
artifacts/pykill-macos-x86_64/*.tar.gz
132+
artifacts/pykill-macos-aarch64/*.tar.gz
133+
# body_path: CHANGELOG.md # Optional: if you have a changelog
134+
# draft: false # Optional: set to true to create a draft release
135+
# prerelease: false # Optional: set to true for pre-releases
136+
env:
137+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
138+
# The GITHUB_TOKEN is automatically available to the workflow.
139+
# softprops/action-gh-release uses it by default.
140+
```

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,4 @@ crossterm = "0.27"
99
walkdir = "2.4"
1010
humansize = "2.1" # For formatting file sizes
1111
chrono = "0.4" # For timestamps
12+
clap = { version = "4.4", features = ["derive"] }

README.md

Lines changed: 106 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,117 @@
11
# pykill
22

3-
pykill is a Rust-based utility (currently in early development) designed to scan projects for Python virtual environments. The goal is to provide a fast, cross-platform tool with a terminal user interface (TUI) for managing and potentially cleaning up Python virtual environments within a given directory tree.
3+
A cross-platform TUI tool for finding and deleting Python virtual environments.
44

5-
## Features (Planned)
6-
- **Scan for Python Virtual Environments:** Detect virtual environments in a project using Python's `sys.prefix` or directory heuristics.
7-
- **Terminal User Interface:** Built with [ratatui](https://crates.io/crates/ratatui) and [crossterm](https://crates.io/crates/crossterm) for a modern, interactive TUI experience.
8-
- **Project Navigation:** Easily browse and select directories to scan.
9-
- **Environment Management:** (Planned) List, inspect, and optionally remove unused or large virtual environments.
5+
## Features
106

11-
## Status
12-
This project is in the early stages. Most modules are placeholders with TODOs for future implementation.
7+
* **Interactive TUI:** A user-friendly terminal interface for navigating and managing Python virtual environments.
8+
* **Cross-Platform:** Works on Linux, Windows, and macOS.
9+
* **Virtual Environment Detection:** Scans directories to find common Python virtual environment folders (e.g., `venv`, `.venv`, `env`).
10+
* **Interactive Deletion:** Safely delete virtual environments with a confirmation step.
11+
* **Command-Line Mode:** Option to list virtual environments without launching the TUI.
12+
* **Fast Scanning:** Built in Rust for efficient directory traversal and analysis.
1313

14-
## Dependencies
15-
- [ratatui](https://crates.io/crates/ratatui) - TUI rendering
16-
- [crossterm](https://crates.io/crates/crossterm) - Terminal handling
17-
- [walkdir](https://crates.io/crates/walkdir) - Recursive directory traversal
18-
- [humansize](https://crates.io/crates/humansize) - Human-readable file sizes
19-
- [chrono](https://crates.io/crates/chrono) - Date and time utilities
14+
## Installation
15+
16+
### From Releases (Recommended)
17+
18+
You can download pre-compiled binaries for your operating system from the GitHub Releases page:
19+
20+
[https://github.com/YOUR_GITHUB_USER/YOUR_REPO_NAME/releases](https://github.com/YOUR_GITHUB_USER/YOUR_REPO_NAME/releases)
21+
22+
Binaries are typically provided for Linux, Windows, and macOS (x86_64 and aarch64). Download the appropriate archive for your system, extract it, and place the `pykill` executable in a directory included in your system's PATH.
23+
24+
### From Source
25+
26+
If you have Rust and Cargo installed, you can build `pykill` from source.
27+
28+
1. **Install via `cargo install` (recommended for source installs):**
29+
```bash
30+
cargo install --git https://github.com/YOUR_GITHUB_USER/YOUR_REPO_NAME.git
31+
```
32+
This will build and install the `pykill` binary into your Cargo bin directory (e.g., `~/.cargo/bin/pykill`).
33+
34+
2. **Manual Build:**
35+
Alternatively, you can clone the repository and build it manually:
36+
```bash
37+
git clone https://github.com/YOUR_GITHUB_USER/YOUR_REPO_NAME.git
38+
cd YOUR_REPO_NAME
39+
cargo build --release
40+
```
41+
The binary will be located at `target/release/pykill`. You can then copy this to a location in your PATH.
2042

2143
## Usage
22-
1. **Build the project:**
23-
```sh
24-
cargo build --release
25-
```
26-
2. **Run the project:**
27-
```sh
28-
cargo run -- <project_path>
29-
```
30-
(Note: Functionality is not yet implemented; running will only print "Hello, world!" for now.)
44+
45+
### TUI Mode (Default)
46+
47+
To start `pykill` in its interactive TUI mode, simply run the command:
48+
49+
```bash
50+
pykill
51+
```
52+
53+
This will scan the current directory for virtual environments.
54+
55+
You can also specify a path to scan:
56+
57+
```bash
58+
pykill /path/to/your/projects
59+
```
60+
61+
**Keybindings:**
62+
63+
* **`` / ``**: Navigate up and down the list of detected virtual environments.
64+
* **`d`**: Mark the currently selected virtual environment for deletion. This will open a confirmation dialog.
65+
* **`y`**: (In confirmation dialog) Confirm the deletion of the virtual environment.
66+
* **`n`**: (In confirmation dialog) Cancel the deletion and close the dialog.
67+
* **`q`**: Quit the application. This works in both the main list view and the confirmation dialog.
68+
69+
### Command-Line Mode
70+
71+
If you prefer to get a simple list of virtual environments printed to your terminal without the TUI, use the `--no-tui` flag.
72+
73+
* Scan the current directory:
74+
```bash
75+
pykill --no-tui
76+
```
77+
78+
* Scan a specific directory:
79+
```bash
80+
pykill /path/to/your/projects --no-tui
81+
```
82+
83+
The output will list the path, size, and last modified date for each detected virtual environment.
84+
85+
## Building from Source
86+
87+
If you wish to contribute or build the latest version yourself:
88+
89+
1. **Prerequisites:**
90+
* Ensure you have Rust and Cargo installed. You can get them from [rustup.rs](https://rustup.rs/).
91+
92+
2. **Clone the repository:**
93+
```bash
94+
git clone https://github.com/YOUR_GITHUB_USER/YOUR_REPO_NAME.git
95+
```
96+
97+
3. **Navigate to the directory:**
98+
```bash
99+
cd YOUR_REPO_NAME
100+
```
101+
(Note: If the repository is named `pykill`, you would `cd pykill`)
102+
103+
4. **Build the release binary:**
104+
```bash
105+
cargo build --release
106+
```
107+
108+
5. **Run the binary:**
109+
The executable will be located at `target/release/pykill`.
31110

32111
## Contributing
33-
Contributions are welcome! Please open issues or pull requests to discuss features or report bugs.
112+
113+
Contributions are welcome! If you have suggestions, feature requests, or bug reports, please open an issue or submit a pull request on the GitHub repository.
34114

35115
## License
36-
This project is licensed under the MIT License.
116+
117+
This project is licensed under the MIT License. See the `LICENSE` file for details (if one is present, otherwise assume MIT).

0 commit comments

Comments
 (0)