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
Binary file added .DS_Store
Binary file not shown.
279 changes: 277 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,8 @@ jobs:
python-version: ["3.9", "3.10", "3.11", "3.12", "3.13", "3.14"]
os: [macos-14, macos-latest]
exclude:
# Python 3.9 not available on macos-14 (arm64 runner)
- os: macos-14
python-version: "3.9"

steps:
- name: Checkout
uses: actions/checkout@v4
Expand Down Expand Up @@ -108,3 +106,280 @@ jobs:
path: dist/
retention-days: 7

version-check:
name: Version Gate (PyPI vs local)
needs: build-check
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
runs-on: macos-latest
timeout-minutes: 10
outputs:
should_publish: ${{ steps.compare.outputs.should_publish }}
local_version: ${{ steps.compare.outputs.local_version }}
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 1
persist-credentials: false

- name: Set up Python 3.11
uses: actions/setup-python@v5
with:
python-version: "3.11"
cache: pip

- name: Compare versions
id: compare
run: |
python - <<'EOF'
import json, sys, urllib.request

try:
import tomllib
except ImportError:
import tomli as tomllib

with open("pyproject.toml", "rb") as f:
data = tomllib.load(f)

local = data["project"]["version"]
print(f"Local version : {local}")

package = "mac-deep-cleaner"
url = f"https://pypi.org/pypi/{package}/json"
try:
with urllib.request.urlopen(url, timeout=15) as r:
pypi = json.load(r)["info"]["version"]
except urllib.error.HTTPError as e:
if e.code == 404:
pypi = "0.0.0"
print("Package not found on PyPI — treating as new release.")
else:
raise

print(f"PyPI version : {pypi}")

import os
out = os.environ["GITHUB_OUTPUT"]
publish = "true" if local != pypi else "false"
with open(out, "a") as f:
f.write(f"should_publish={publish}\n")
f.write(f"local_version={local}\n")

if publish == "true":
print(f"✅ New version detected ({pypi} → {local}). Will publish.")
else:
print(f"⏭ Version {local} already on PyPI. Skipping publish.")
EOF

publish:
name: Publish to PyPI (${{ needs.version-check.outputs.local_version }})
needs: version-check
if: needs.version-check.outputs.should_publish == 'true'
runs-on: macos-latest
timeout-minutes: 20
environment:
name: pypi
url: https://pypi.org/project/mac-deep-cleaner/
permissions:
contents: read
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 1
persist-credentials: false

- name: Set up Python 3.11
uses: actions/setup-python@v5
with:
python-version: "3.11"
cache: pip

- name: Install build tools
run: pip install --upgrade pip && pip install build twine

- name: Build distributions
run: python -m build

- name: Verify distributions
run: twine check dist/*

- name: Publish to PyPI
env:
TWINE_USERNAME: __token__
TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }}
run: twine upload dist/*

- name: Summary
run: |
echo "### 🚀 Published mac-deep-cleaner ${{ needs.version-check.outputs.local_version }} to PyPI" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "📦 https://pypi.org/project/mac-deep-cleaner/${{ needs.version-check.outputs.local_version }}/" >> $GITHUB_STEP_SUMMARY

# ─────────────────────────────────────────────────────────────────────────────
# Creates a GitHub Release tagged v<version> with the matching CHANGELOG entry.
# Runs after a successful publish.
# ─────────────────────────────────────────────────────────────────────────────
github-release:
name: Create GitHub Release (v${{ needs.version-check.outputs.local_version }})
needs: [version-check, publish]
if: needs.version-check.outputs.should_publish == 'true'
runs-on: macos-latest
timeout-minutes: 10
permissions:
contents: write # needed to create tags + releases
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0 # full history so the tag can be pushed
persist-credentials: true

- name: Set up Python 3.11
uses: actions/setup-python@v5
with:
python-version: "3.11"
cache: pip

- name: Extract changelog section for this version
id: changelog
run: |
python - <<'EOF'
import re, os, sys

version = "${{ needs.version-check.outputs.local_version }}"
tag = f"v{version}"

try:
with open("CHANGELOG.md", "r") as f:
content = f.read()
except FileNotFoundError:
print("CHANGELOG.md not found — using empty release notes.")
body = f"Release {tag}"
with open(os.environ["GITHUB_OUTPUT"], "a") as out:
out.write(f"tag={tag}\n")
with open("release_notes.md", "w") as rn:
rn.write(body)
sys.exit(0)

# Match the section that starts with "## v<version>" up to the next "## v" heading
pattern = rf"(## {re.escape(tag)}.*?)(?=\n## v|\Z)"
match = re.search(pattern, content, re.DOTALL)

if match:
body = match.group(1).strip()
print(f"✅ Found changelog section for {tag}")
else:
body = f"Release {tag}\n\nNo changelog entry found for this version."
print(f"⚠️ No changelog section found for {tag} — using fallback.")

with open(os.environ["GITHUB_OUTPUT"], "a") as out:
out.write(f"tag={tag}\n")

with open("release_notes.md", "w") as rn:
rn.write(body)
EOF

- name: Create and push tag
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
TAG="${{ steps.changelog.outputs.tag }}"
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
# Only create the tag if it doesn't already exist
if git rev-parse "$TAG" >/dev/null 2>&1; then
echo "Tag $TAG already exists — skipping tag creation."
else
git tag -a "$TAG" -m "Release $TAG"
git push origin "$TAG"
echo "✅ Pushed tag $TAG"
fi

- name: Create GitHub Release
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ steps.changelog.outputs.tag }}
name: "mac-deep-cleaner ${{ steps.changelog.outputs.tag }}"
body_path: release_notes.md
draft: false
prerelease: false
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

# ─────────────────────────────────────────────────────────────────────────────
# Auto-bumps the patch version on `develop` after a successful main publish
# so the branch is always ahead of what's on PyPI.
# Commits directly to develop via the default GITHUB_TOKEN.
# ─────────────────────────────────────────────────────────────────────────────
auto-version-bump:
name: Auto-bump patch version on develop
needs: [version-check, publish]
if: needs.version-check.outputs.should_publish == 'true'
runs-on: macos-latest
timeout-minutes: 10
permissions:
contents: write # needed to push the commit
steps:
- name: Checkout develop
uses: actions/checkout@v4
with:
ref: develop
fetch-depth: 1
persist-credentials: true

- name: Set up Python 3.11
uses: actions/setup-python@v5
with:
python-version: "3.11"
cache: pip

- name: Bump patch version in pyproject.toml
id: bump
run: |
python - <<'EOF'
import re, os

published = "${{ needs.version-check.outputs.local_version }}"
major, minor, patch = map(int, published.split("."))
next_version = f"{major}.{minor}.{patch + 1}"

with open("pyproject.toml", "r") as f:
content = f.read()

# Replace the version field — matches: version = "x.y.z"
updated = re.sub(
r'^(version\s*=\s*")[^"]+(")',
rf'\g<1>{next_version}\g<2>',
content,
flags=re.MULTILINE,
)

if updated == content:
print(f"⚠️ version field not found or already bumped — no change written.")
else:
with open("pyproject.toml", "w") as f:
f.write(updated)
print(f"✅ Bumped {published} → {next_version} in pyproject.toml")

with open(os.environ["GITHUB_OUTPUT"], "a") as out:
out.write(f"next_version={next_version}\n")
EOF

- name: Commit and push version bump
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
NEXT="${{ steps.bump.outputs.next_version }}"
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add pyproject.toml
# Only commit if there's an actual change
if git diff --cached --quiet; then
echo "Nothing to commit — develop is already at $NEXT or bump failed."
else
git commit -m "chore: bump version to $NEXT [skip ci]"
git push origin develop
echo "✅ Pushed version bump to develop: $NEXT"
fi
84 changes: 59 additions & 25 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,33 +2,67 @@

All notable changes to **mac-deep-cleaner** will be documented in this file.

## Unreleased
## v2.0.0 (2026-05-14)

### Added

## Cross-cutting Work
- Add new CLI subcommands and options in src/cli.py
- Extend config schema in src/config/config.py for new features
- Add logging and safe-path validation in src/core/safety.py where needed
- Expand reporting exports in src/reporting for new outputs
- Add tests for parsers and non-destructive scanners in tests/

## External Dependencies (tentative)
- textual or prompt_toolkit for interactive TUI picker
- rumps for menu bar companion
- requests or urllib for HIBP API (prefer urllib to avoid new deps)
- pandas/pyarrow NOT planned (keep lightweight)

## Safety Gates
- All destructive operations honor --dry-run and undo staging.
- Time Machine guard before bulk deletes.
- APFS snapshot support behind explicit flags.
- Restore checksum verification for staged files.

## Open Decisions (needs confirmation)
- Preferred TUI library (textual vs prompt_toolkit)
- Whether to add optional dependencies vs strict core only
- Handling sudo-required operations (auto prompt vs printed instructions)
- HIBP API key provisioning and storage
- Minimum supported macOS version for system commands
- CI mode via `mdc uninstall-cli` ti uninstall this package

##DOUCMENTATIONS
-Added the documentations for the command references and architecture


### Changed
- CLI wiring for new feature commands
- Added reporting utilities
- Updated Readme for new features
- Version bump to v2.0.0 across docs and UI
- Updated the ci/cd pipelining to publish the newer version to pypi directly
- Updated the readme.md file for the references too

## v1.5.0 (2026-05-12)
### Added
#### P0 (baseline UX and safety)
- [x] Global --dry-run flag (src/core/dry_run.py)
- [x] Shell completion command (src/core/completions.py)
- [x] Full app uninstaller (src/core/uninstaller.py)

#### P1 (highest demand data and visibility)
- [x] Browser data cleaner (src/scanners/browser_data.py)
- [x] Visual disk space map (src/scanners/space_map.py)
- [x] Photo library analyzer (src/scanners/photos_analyzer.py)
- [x] iOS simulator deep cleaner (src/scanners/simulators.py)

#### P2 (system utilities and maintenance)
- [x] Memory pressure reliever (src/core/memory_pressure.py)
- [x] Homebrew deep manager (src/core/brew_manager.py)
- [x] Storage trend tracker (src/reporting/storage_trend.py)
- [x] Recent files and activity cleaner (src/scanners/recent_activity.py)

#### P3 (advanced and higher risk features)
- [x] Permissions auditor (src/core/permissions_auditor.py)
- [x] APFS snapshot guard (src/core/apfs_snapshots.py)
- [x] Menu bar companion (src/core/menubar.py)
- [x] Data breach monitor (src/core/breach_monitor.py)
- [x] Cloud storage junk scanner (src/scanners/cloud_junk.py)
- Global --dry-run flag (src/core/dry_run.py)
- Shell completion command (src/core/completions.py)
- Full app uninstaller (src/core/uninstaller.py)
- Browser data cleaner (src/scanners/browser_data.py)
- Visual disk space map (src/scanners/space_map.py)
- Photo library analyzer (src/scanners/photos_analyzer.py)
- iOS simulator deep cleaner (src/scanners/simulators.py)
- Memory pressure reliever (src/core/memory_pressure.py)
- Homebrew deep manager (src/core/brew_manager.py)
- Storage trend tracker (src/reporting/storage_trend.py)
- Recent files and activity cleaner (src/scanners/recent_activity.py)
- Permissions auditor (src/core/permissions_auditor.py)
- APFS snapshot guard (src/core/apfs_snapshots.py)
- Menu bar companion (src/core/menubar.py)
- Data breach monitor (src/core/breach_monitor.py)
- Cloud storage junk scanner (src/scanners/cloud_junk.py)

### Changed
- CLI wiring for new P0/P1 commands and dry-run behavior
Expand All @@ -47,7 +81,7 @@ All notable changes to **mac-deep-cleaner** will be documented in this file.
### Changed
- Live dashboard now shows top findings and dev junk totals
- Scan history schema extended with developer junk totals
- Version bump to v1.5.0 across docs and UI
- Version bump to v1.2.0 across docs and UI

## v1.0.0 (2026-05-10)
### Added
Expand Down
Loading
Loading