mirror of
https://github.com/akadeepesh/contextzip.git
synced 2026-10-20 08:36:20 +02:00
fix(cleanup): remove backup handling and references from apply-zip and related components
This commit is contained in:
+1
-6
@@ -354,8 +354,6 @@ def apply_zip(
|
||||
classify every file as new, modified, unchanged, or one with no safe
|
||||
baseline (locally edited since zipping, or never part of the original
|
||||
manifest). Only adds and modifies files — nothing is ever deleted.
|
||||
Every overwritten file is backed up first, under
|
||||
``.contextzip/backups/<timestamp>/``.
|
||||
|
||||
Unlike the CLI, this applies immediately and does not prompt — check
|
||||
``contextzip.applier.build_plan()`` yourself first if you want to
|
||||
@@ -375,8 +373,7 @@ def apply_zip(
|
||||
Returns
|
||||
-------
|
||||
ApplyResult
|
||||
``written`` (list of relative paths written), ``backup_dir``
|
||||
(``Path`` or ``None`` if nothing needed backing up), and
|
||||
``written`` (list of relative paths written) and
|
||||
``applied_zip_path`` (where the consumed zip ended up).
|
||||
|
||||
Raises
|
||||
@@ -393,8 +390,6 @@ def apply_zip(
|
||||
|
||||
result = apply_zip() # picks up .contextzip/inbox/*.zip
|
||||
print(f"Wrote {len(result.written)} files")
|
||||
if result.backup_dir:
|
||||
print(f"Backup at {result.backup_dir}")
|
||||
"""
|
||||
from contextzip.applier import (
|
||||
ApplyError,
|
||||
|
||||
+9
-33
@@ -48,7 +48,6 @@ from pathlib import Path
|
||||
_MANIFEST_SUFFIX = ".manifest.json"
|
||||
_INBOX_DIRNAME = "inbox"
|
||||
_APPLIED_DIRNAME = "applied"
|
||||
_BACKUPS_DIRNAME = "backups"
|
||||
_OUTPUT_DIRNAME = "output" # must match packager._OUTPUT_DIRNAME
|
||||
|
||||
# Files contextzip itself writes into outgoing ZIPs that should never be
|
||||
@@ -146,7 +145,6 @@ class ApplyPlan:
|
||||
@dataclass
|
||||
class ApplyResult:
|
||||
written: list[str]
|
||||
backup_dir: Path | None
|
||||
applied_zip_path: Path
|
||||
|
||||
|
||||
@@ -647,29 +645,6 @@ def discard_plan(plan: ApplyPlan) -> None:
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
def _backup_entries(
|
||||
entries: list[ApplyEntry], project_dir: Path, workspace_root: Path
|
||||
) -> Path | None:
|
||||
"""
|
||||
Copy the current, pre-apply version of every existing file about to be
|
||||
touched into .contextzip/backups/<timestamp>/, preserving relative
|
||||
paths. Files that don't exist yet locally (new files) have nothing to
|
||||
back up. Returns the backup directory, or None if nothing needed one.
|
||||
"""
|
||||
to_backup = [e for e in entries if (project_dir / e.rel_path).is_file()]
|
||||
if not to_backup:
|
||||
return None
|
||||
|
||||
stamp = time.strftime("%Y%m%d-%H%M%S")
|
||||
backup_dir = workspace_root / _BACKUPS_DIRNAME / stamp
|
||||
for entry in to_backup:
|
||||
src = project_dir / entry.rel_path
|
||||
dst = backup_dir / entry.rel_path
|
||||
dst.parent.mkdir(parents=True, exist_ok=True)
|
||||
shutil.copy2(src, dst)
|
||||
return backup_dir
|
||||
|
||||
|
||||
def _move_zip_to_applied(
|
||||
zip_path: Path, project_dir: Path, workspace_root: Path, retain: int = 1
|
||||
) -> Path:
|
||||
@@ -716,9 +691,14 @@ def execute_plan(plan: ApplyPlan, project_dir: Path, retain: int = 1) -> ApplyRe
|
||||
"""
|
||||
Write every non-unchanged entry from *plan* into the project.
|
||||
|
||||
Backs up whatever's about to be overwritten first. Assumes the caller
|
||||
has already decided to proceed — dry-run and confirmation prompts are
|
||||
handled upstream (see cli.py's `apply-zip` command).
|
||||
Assumes the caller has already decided to proceed — dry-run and
|
||||
confirmation prompts are handled upstream (see cli.py's `apply-zip`
|
||||
command). Files are overwritten directly, with no backup taken first:
|
||||
`build_plan`'s manifest diff and the confirmation prompt it drives
|
||||
(see `ApplyPlan.is_risky`) are the safety net here, and the zip you
|
||||
just applied is itself always recoverable — it's archived to
|
||||
`.contextzip/inbox/applied/` (or left wherever you pointed at it)
|
||||
rather than deleted.
|
||||
|
||||
*retain* caps how many applied zips are kept in
|
||||
.contextzip/inbox/applied/ — typically a project's
|
||||
@@ -727,8 +707,6 @@ def execute_plan(plan: ApplyPlan, project_dir: Path, retain: int = 1) -> ApplyRe
|
||||
workspace_root = _workspace_dir(project_dir)
|
||||
to_write = plan.writable_entries
|
||||
|
||||
backup_dir = _backup_entries(to_write, project_dir, workspace_root)
|
||||
|
||||
written: list[str] = []
|
||||
for entry in to_write:
|
||||
dest = project_dir / entry.rel_path
|
||||
@@ -741,6 +719,4 @@ def execute_plan(plan: ApplyPlan, project_dir: Path, retain: int = 1) -> ApplyRe
|
||||
)
|
||||
shutil.rmtree(plan.extraction_dir, ignore_errors=True)
|
||||
|
||||
return ApplyResult(
|
||||
written=written, backup_dir=backup_dir, applied_zip_path=applied_zip_path
|
||||
)
|
||||
return ApplyResult(written=written, applied_zip_path=applied_zip_path)
|
||||
|
||||
+24
-24
@@ -2,21 +2,16 @@
|
||||
cleanup.py — Automatic workspace housekeeping for contextzip.
|
||||
|
||||
.contextzip/ accumulates disposable artifacts over time: a zip+manifest+report
|
||||
set every run, a timestamped backup folder every risky `apply-zip`, and (until
|
||||
pruned) old archived applied-zips. None of it is precious — every zip is
|
||||
trivially reproducible by just re-running contextzip, and a backup only
|
||||
matters until the apply that made it is confirmed good. So cleanup here is
|
||||
deliberately brutal rather than cautious: it runs automatically after every
|
||||
successful command (see `_auto_cleanup` in cli.py), with no prompt and no
|
||||
separate command to remember, and keeps only the most recent
|
||||
`cleanup.keep_recent` item(s) in each category — everything else is deleted
|
||||
immediately, every time.
|
||||
set every run, and (until pruned) old archived applied-zips and loose apply
|
||||
reports. None of it is precious — every zip is trivially reproducible by just
|
||||
re-running contextzip. So cleanup here is deliberately brutal rather than
|
||||
cautious: it runs automatically after every successful command (see
|
||||
`_auto_cleanup` in cli.py), with no prompt and no separate command to
|
||||
remember, and keeps only the most recent `cleanup.keep_recent` item(s) in
|
||||
each category — everything else is deleted immediately, every time.
|
||||
|
||||
The heavier caution used elsewhere in contextzip (backing up before overwrite
|
||||
in apply-zip, never inferring deletions) is reserved for the project's own
|
||||
files, where losing something can mean losing real work. Nothing here ever
|
||||
touches the project — only disposable metadata under .contextzip/ that costs
|
||||
nothing to regenerate.
|
||||
Nothing here ever touches the project — only disposable metadata under
|
||||
.contextzip/ that costs nothing to regenerate.
|
||||
|
||||
Never touched, under any settings:
|
||||
- .contextzip/config.json (project preferences)
|
||||
@@ -28,7 +23,6 @@ Never touched, under any settings:
|
||||
Kept only-the-most-recent-N, everything older deleted immediately:
|
||||
- zip/manifest/report sets under .contextzip/output/<mode>/, per mode
|
||||
folder
|
||||
- timestamped folders under .contextzip/backups/
|
||||
- archived zips under .contextzip/inbox/applied/ (this is also governed
|
||||
by `applied_zip_retention`, already enforced on every apply-zip run —
|
||||
scanning it here too just catches the case where zips piled up before
|
||||
@@ -40,6 +34,12 @@ Kept only-the-most-recent-N, everything older deleted immediately:
|
||||
put it; its report always lands in inbox/ itself), so nothing else
|
||||
here would ever catch them piling up run after run
|
||||
|
||||
Removed outright, no matter how recent:
|
||||
- .contextzip/backups/ in its entirety. apply-zip no longer creates
|
||||
per-run backups there at all — this only ever catches a leftover
|
||||
folder from a project that used an older contextzip version, and it's
|
||||
dead weight the moment that version is gone.
|
||||
|
||||
Scanning only ever touches .contextzip/ directory listings and file mtimes —
|
||||
no hashing, no reading file contents — so this stays fast even called on
|
||||
every command, on a large project.
|
||||
@@ -53,7 +53,7 @@ from pathlib import Path
|
||||
|
||||
|
||||
_OUTPUT_DIRNAME = "output"
|
||||
_BACKUPS_DIRNAME = "backups"
|
||||
_LEGACY_BACKUPS_DIRNAME = "backups"
|
||||
_INBOX_DIRNAME = "inbox"
|
||||
_APPLIED_DIRNAME = "applied"
|
||||
|
||||
@@ -66,7 +66,7 @@ _APPLIED_DIRNAME = "applied"
|
||||
@dataclass
|
||||
class CleanupItem:
|
||||
path: Path
|
||||
kind: str # "zip-set" | "backup" | "applied-zip"
|
||||
kind: str # "zip-set" | "applied-zip" | "apply-report" | "legacy-backups"
|
||||
size_bytes: int
|
||||
|
||||
|
||||
@@ -166,14 +166,14 @@ def scan(
|
||||
for f in _zip_set_files(zp):
|
||||
items.append(CleanupItem(path=f, kind="zip-set", size_bytes=_size_of(f)))
|
||||
|
||||
# ── backups/<timestamp>/ ─────────────────────────────────────────────
|
||||
backups_root = workspace / _BACKUPS_DIRNAME
|
||||
# ── backups/ — the feature that created these is gone entirely; wipe
|
||||
# the whole folder outright rather than pruning it, so upgrading
|
||||
# from an older contextzip version doesn't leave it behind forever ──
|
||||
backups_root = workspace / _LEGACY_BACKUPS_DIRNAME
|
||||
if backups_root.is_dir():
|
||||
backups = _newest_first([p for p in backups_root.iterdir() if p.is_dir()])
|
||||
for backup_dir in backups[keep:]:
|
||||
items.append(
|
||||
CleanupItem(path=backup_dir, kind="backup", size_bytes=_size_of(backup_dir))
|
||||
)
|
||||
items.append(
|
||||
CleanupItem(path=backups_root, kind="legacy-backups", size_bytes=_size_of(backups_root))
|
||||
)
|
||||
|
||||
# ── inbox/applied/ ───────────────────────────────────────────────────
|
||||
applied_root = workspace / _INBOX_DIRNAME / _APPLIED_DIRNAME
|
||||
|
||||
+6
-5
@@ -382,8 +382,7 @@ def cmd_apply_zip(
|
||||
every file as new, modified, unchanged, or one needing a closer look
|
||||
(edited locally since zipping, or with no baseline at all) before
|
||||
writing anything. Only adds and modifies files — nothing is ever
|
||||
deleted. Every overwritten file is backed up first, under
|
||||
.contextzip/backups/<timestamp>/.
|
||||
deleted.
|
||||
"""
|
||||
project_dir = Path(os.getcwd()).resolve()
|
||||
project_cfg = load_project_config(project_dir)
|
||||
@@ -857,9 +856,11 @@ def _auto_cleanup(project_dir: Path, project_cfg) -> None:
|
||||
to remember. Every zip is trivially reproducible by re-running
|
||||
contextzip, so this is deliberately brutal rather than cautious: it
|
||||
keeps only the `cleanup.keep_recent` most recent zip/manifest/report
|
||||
set per mode folder, the most recent `cleanup.keep_recent` backup
|
||||
folder(s), and the most recent `cleanup.keep_recent` archived
|
||||
applied-zip(s) — everything else is deleted immediately, every run.
|
||||
set per mode folder, the most recent `cleanup.keep_recent` archived
|
||||
applied-zip(s), and the most recent `cleanup.keep_recent` loose
|
||||
apply-report(s) in inbox/ — everything else is deleted immediately,
|
||||
every run. Also wipes .contextzip/backups/ outright if a leftover one
|
||||
still exists from an older contextzip version.
|
||||
|
||||
Gated on `cleanup.enabled` (default True) — set to False in
|
||||
.contextzip/config.json to turn this off entirely.
|
||||
|
||||
@@ -380,10 +380,8 @@ def print_apply_plan(
|
||||
|
||||
|
||||
def print_apply_result(result, *, con: Console = console) -> None:
|
||||
"""Two or three lines: what was written, backed up, and archived."""
|
||||
"""Two lines: what was written and archived."""
|
||||
ok(f"Applied {len(result.written)} files", con=con)
|
||||
if result.backup_dir:
|
||||
ok("Backed up to", str(result.backup_dir), con=con)
|
||||
ok("Archived zip to", str(result.applied_zip_path), con=con)
|
||||
|
||||
|
||||
|
||||
@@ -110,24 +110,25 @@ built-in default for the newer preference fields.
|
||||
|
||||
cleanup
|
||||
Automatic housekeeping for `.contextzip/`, which otherwise only
|
||||
grows over time (a zip+manifest+report set per run, a timestamped
|
||||
folder under backups/ per risky apply-zip). See cleanup.py — after
|
||||
every successful contextzip command, the workspace is pruned down
|
||||
to only the most recent `keep_recent` item(s) per category, no
|
||||
confirmation, no separate command needed. This is deliberately
|
||||
brutal rather than cautious: every zip is trivially reproducible
|
||||
by re-running contextzip, so there's little reason to let old ones
|
||||
pile up.
|
||||
grows over time (a zip+manifest+report set per run, archived zips
|
||||
under inbox/applied/, loose apply-report.txt files in inbox/). See
|
||||
cleanup.py — after every successful contextzip command, the
|
||||
workspace is pruned down to only the most recent `keep_recent`
|
||||
item(s) per category, no confirmation, no separate command needed.
|
||||
This is deliberately brutal rather than cautious: every zip is
|
||||
trivially reproducible by re-running contextzip, so there's little
|
||||
reason to let old ones pile up.
|
||||
enabled — if true (default), auto-cleanup runs after every
|
||||
successful command. Set to false to keep
|
||||
everything contextzip has ever generated.
|
||||
keep_recent — how many most-recent item(s) are kept per
|
||||
category before the rest are deleted: zip+manifest
|
||||
+report sets per mode folder (output/codebase/,
|
||||
output/git-changes/, output/prompt/), timestamped
|
||||
folders under backups/, and archived zips under
|
||||
inbox/applied/. Default 1 — keep only the latest
|
||||
of each, delete everything older immediately.
|
||||
output/git-changes/, output/prompt/), archived
|
||||
zips under inbox/applied/, and loose
|
||||
apply-report.txt files in inbox/. Default 1 —
|
||||
keep only the latest of each, delete everything
|
||||
older immediately.
|
||||
|
||||
The schema is intentionally a flat, easily-extended dict so future
|
||||
preferences can be added without another migration.
|
||||
|
||||
@@ -175,7 +175,6 @@ def write_apply_report(
|
||||
if result is not None:
|
||||
w("Result")
|
||||
w(f" written : {len(result.written)}")
|
||||
w(f" backup : {result.backup_dir or 'none needed'}")
|
||||
w(f" archived: {result.applied_zip_path}")
|
||||
w("")
|
||||
|
||||
|
||||
Reference in New Issue
Block a user