16 Commits

Author SHA1 Message Date
19b79b26ff fix workflow 2025-11-09 10:54:53 +00:00
Marco D'Aleo
a67e31c65d chore: re-trigger release
chore: add new line to end of file core.py
2025-11-09 10:45:50 +00:00
9d4608bd34 add new line to end of file core.py 2025-11-09 10:44:48 +00:00
Marco D'Aleo
27468ae0d0 feat: prepare release cycle
feat: prepare release cycle
2025-11-09 10:35:52 +00:00
8b1d9a81a1 minor change to core.py 2025-11-09 10:34:46 +00:00
6eb3f5a210 Minor fix to inline documentation 2025-11-09 10:25:33 +00:00
Marco D'Aleo
9a64bef661 chore: trigger release
docs: remove end-of-the-file line in CONTRIBUTING.md
2025-11-09 10:10:44 +00:00
5b46a3af01 docs: remove end-of-the-file line in CONTRIBUTING.md 2025-11-09 10:09:07 +00:00
Marco D'Aleo
c8cc694e3c Merge pull request #1 from mdaleo404/restore_many
Add support for restoring multiple files, minor changes to READMEs
2025-11-09 09:54:36 +00:00
756a6af4ac docs: minor update to README files 2025-11-09 09:52:01 +00:00
a20bbeb9f8 feat: add support for restoring multiple files with --restore 2025-11-09 09:48:14 +00:00
668d6bbba4 Add CONTRIBUTING.md file 2025-11-09 09:02:11 +00:00
62264ea115 Add release-please-manifest 2025-11-09 08:33:03 +00:00
d38ec538a4 Switch to new release-please config 2025-11-09 08:29:55 +00:00
166f2dfac2 Add GitHub Actions workflows for packaging and release 2025-11-09 08:21:34 +00:00
ac284d29e3 Delete __pycache__, change README files 2025-11-08 09:59:56 +00:00
12 changed files with 228 additions and 11 deletions

48
.github/workflows/publish.yml vendored Normal file
View File

@@ -0,0 +1,48 @@
name: Publish to PyPI
on:
push:
tags:
- "v*"
jobs:
build-publish:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.13"
- name: Install Poetry
uses: snok/install-poetry@v1
with:
version: 1.8.4
- name: Install dependencies
run: poetry install --no-root --only main
- name: Build package
run: poetry build
- name: Generate checksums
run: |
cd dist
for file in *; do
sha256sum "$file" > "$file.sha256"
done
- name: Publish to PyPI
env:
POETRY_PYPI_TOKEN_PYPI: ${{ secrets.PYPI_TOKEN }}
run: poetry publish
- name: Upload artifacts to GitHub Release
uses: softprops/action-gh-release@v1
with:
files: |
dist/*

18
.github/workflows/release-please.yml vendored Normal file
View File

@@ -0,0 +1,18 @@
name: Release Please
on:
push:
branches: ["main"]
permissions:
contents: write
pull-requests: write
jobs:
release-please:
runs-on: ubuntu-latest
steps:
- name: Release Please
uses: googleapis/release-please-action@v4
with:
token: ${{ secrets.GITHUB_TOKEN }}

View File

@@ -0,0 +1,3 @@
{
".": "0.1.1"
}

95
CONTRIBUTING.md Normal file
View File

@@ -0,0 +1,95 @@
# How to Contribute to `resrm`
Thanks for your interest in contributing to `resrm`! This guide walks you through the process step by step so you can send contributions with confidence.
### For external contributors
1. **Fork the repository** via GitHub: click *Fork* at the top right.
2. **Clone your fork**:
```bash
git clone https://github.com/<your-username>/resrm.git
cd resrm
```
3. **Add the upstream repo** so you can sync later:
```bash
git remote add upstream https://github.com/mdaleo404/resrm.git
```
4. **Create a feature branch**:
```bash
git checkout -b feature/my-new-thing
```
5. Make your changes and test them locally.
6. **Push to your fork**:
```bash
git push -u origin feature/my-new-thing
```
7. Open a **Pull Request** targeting the `main` branch of the upstream repo.
### For maintainers
You may create branches directly in the main repository without forking.
## Commit Message Style Commit Message Style
To keep the automated changelog clean, follow this commit style:
**Format:**
```
type: short description
```
**Accepted types:**
* `feat:` new features
* `fix:` bug fixes
* `refactor:` internal improvements
* `perf:` performance changes
* `docs:` documentation updates
* `chore:` maintenance
* `test:` test-only changes
**Examples:**
```
feat: add --dry-run flag
fix: handle unicode paths
refactor: simplify cleanup logic
```
Dont overthink it — the maintainer can adjust commit types during merge if needed.
## What Happens After You Submit a PR?
After reviewing your changes, your PR (hopefully) gets merged. Then:
* **Release Please** automatically creates a **release PR** summarizing changes
* The maintainer merges that release PR
* A GitHub Release is created
* The package is published to PyPI
* Wheels, source tarballs, and checksums are attached to the GitHub Release
No need to bump versions manually (unless needed).
## Code Style
* Follow standard Python formatting
* Add type hints when possible
* Keep functions small and readable
* Stick to clear naming
## Need Help, Found a Bug, Have a Request?
If you're unsure about anything:
* **Open an issue**
* Start a discussion
* Ask questions before writing lots of code
## Thank You
Your contributions — large or small — help make `resrm` better for everyone. Thanks for being part of the project!

View File

@@ -17,17 +17,20 @@ It moves files to a per-user _trash_ instead of permanently deleting them, while
## Installation
Install via Poetry:
**NOTE:** To use `resrm` with `sudo`, the path to `resrm` must be in the `$PATH` seen by `root`.\
Either install `resrm` as `root` (_preferred_), use `sudo -E resrm`, or add the `$PATH` to `/etc/sudoers` using its `Defaults secure_path` parameter.
Install via PyPI (_preferred_):
```bash
poetry add resrm
pip install resrm
```
Or clone the repo and install locally:
```bash
git clone https://github.com/mdaleo404/resrm.git
cd resrm
cd resrm/resrm
poetry install
```

View File

@@ -0,0 +1,12 @@
{
"release-type": "python",
"package-name": "resrm",
"include-v-in-tag": true,
"changelog-sections": [
{ "type": "feat", "section": "Added", "hidden": false },
{ "type": "fix", "section": "Fixed", "hidden": false },
{ "type": "refactor", "section": "Changed", "hidden": false },
{ "type": "perf", "section": "Changed", "hidden": false },
{ "type": "docs", "section": "Documentation", "hidden": false }
]
}

View File

@@ -17,17 +17,20 @@ It moves files to a per-user _trash_ instead of permanently deleting them, while
## Installation
Install via Poetry:
**NOTE:** To use `resrm` with `sudo`, the path to `resrm` must be in the `$PATH` seen by `root`.\
Either install `resrm` as `root` (_preferred_), use `sudo -E resrm`, or add the `$PATH` to `/etc/sudoers` using its `Defaults secure_path` parameter.
Install via PyPI (_preferred_):
```bash
poetry add resrm
pip install resrm
```
Or clone the repo and install locally:
```bash
git clone https://github.com/mdaleo404/resrm.git
cd resrm
cd resrm/resrm
poetry install
```

View File

@@ -1,6 +1,6 @@
[tool.poetry]
name = "resrm"
version = "0.1.0"
version = "0.1.1"
description = "drop-in replacement for rm with undo/restore built-in."
authors = ["Marco D'Aleo <marco@marcodaleo.com>"]
license = "GPL-3.0-or-later"

View File

@@ -116,6 +116,41 @@ def find_candidates(identifier: str) -> List[Dict]:
if id_matches:
return id_matches
def restore_many(identifiers: List[str]):
"""Restore multiple files, prompting when needed."""
for identifier in identifiers:
candidates = find_candidates(identifier)
if not candidates:
print(f"No match found for '{identifier}'")
continue
# Only one match - restore immediately
if len(candidates) == 1:
restore_one(candidates[0])
continue
# Multiple matches - prompt user
print(f"Multiple matches for '{identifier}':")
for i, entry in enumerate(candidates, start=1):
print(f"{i}) {short_id(entry['id'])} {entry['orig_path']} ({entry['timestamp']})")
try:
choice = input("Choose number to restore (or skip): ").strip()
except KeyboardInterrupt:
print("\nAborted.")
return
if not choice.isdigit():
print("Skipped.")
continue
idx = int(choice) - 1
if 0 <= idx < len(candidates):
restore_one(candidates[idx])
else:
print("Invalid selection. Skipped.")
def restore_one(entry: Dict) -> bool:
src = TRASH_DIR / entry["id"]
dest = Path(entry["orig_path"])
@@ -209,7 +244,7 @@ def move_to_trash(path: Path, interactive: bool, force: bool, recursive: bool, p
print(f"Failed permanent delete: {e}")
return
# 🚫 Prevent non-root user deleting root-owned files
# Prevent non-root user deleting root-owned files
try:
st = path.stat()
if st.st_uid == 0 and os.geteuid() != 0:
@@ -218,7 +253,7 @@ def move_to_trash(path: Path, interactive: bool, force: bool, recursive: bool, p
except Exception:
pass
# 🧭 Detect which trash to use (based on file owner)
# Detect which trash to use (based on file owner)
try:
import pwd
owner_uid = path.stat().st_uid
@@ -273,7 +308,7 @@ def main(argv: Optional[List[str]] = None):
parser.add_argument("-f", action="store_true", help="force")
parser.add_argument("-i", action="store_true", help="interactive")
parser.add_argument("--perma", action="store_true", help="permanent delete")
parser.add_argument("--restore", nargs=1, help="restore by id or basename")
parser.add_argument("--restore", nargs="+", help="restore by id or basename")
parser.add_argument("-l", action="store_true", help="list trash")
parser.add_argument("--empty", action="store_true", help="empty the trash permanently")
parser.add_argument("--help", action="store_true", help="show help")
@@ -293,7 +328,7 @@ def main(argv: Optional[List[str]] = None):
return
if args.restore:
restore(args.restore[0])
restore_many(args.restore)
return
if not args.paths: