Files
paperclip-harness/bin/project-reset

110 lines
3.7 KiB
Python
Executable File

#!/usr/bin/env python3
"""Start a project again: close its pages, remove its worktrees, keep its work.
bin/project-reset <folder> --dry-run
bin/project-reset <folder> --yes
bin/project-reset <folder> --yes --delete-folder
What it removes, and nothing else:
- the project's screen panes in its TUIOS session (the shell pane stays, so
the session survives)
- the git worktrees Paperclip made for the project's tasks, and the branches
those tasks were on
What it leaves alone: the folder, its `.harness`, its git history, and every
Paperclip record — tasks, comments, runs, cost. Paperclip owns those, so a reset
here cannot erase the history of what happened; it only clears the machinery.
`--delete-folder` is the only destructive option and it asks for the folder name
to be typed. Without it, nothing outside git and TUIOS is touched.
"""
import os
import sys
HERE = os.path.dirname(os.path.abspath(__file__))
sys.path.insert(0, os.path.join(HERE, "lib"))
import harness as h # noqa: E402
import tuios # noqa: E402
import project as pj # noqa: E402
PAGES = ["tree", "reply", "log", "new"]
def main():
argv = sys.argv[1:]
args = [a for a in argv if not a.startswith("--")]
if not args:
h.say("usage: bin/project-reset <folder> [--dry-run] [--yes] [--delete-folder]")
return 2
folder = os.path.abspath(os.path.expanduser(args[0]))
dry = "--dry-run" in argv
yes = "--yes" in argv
delete_folder = "--delete-folder" in argv
cfg = pj.load(folder)
pj.check_machine(cfg)
session = cfg["session"]
project_id = cfg["paperclip"]["project"]
tasks = pj.active_tasks(project_id)
worktrees = []
for t in tasks:
full = h.api("GET", "/api/issues/%s" % t["id"])
ws = full.get("currentExecutionWorkspace") or {}
if ws.get("cwd"):
worktrees.append((t["identifier"], ws["cwd"], ws.get("branchName") or ""))
h.say("project: %s" % project_id)
h.say("folder: %s" % folder)
h.say("session: %s" % session)
h.say("")
h.say("would remove:")
h.say(" panes: %s in %s" % (", ".join(PAGES), session))
for ref, cwd, branch in worktrees:
h.say(" worktree: %s (%s, branch %s)" % (cwd, ref, branch or "none"))
if delete_folder:
h.say(" the folder: %s (including .harness and git history)" % folder)
h.say("kept: the Paperclip project, its tasks, comments, runs and cost")
if dry:
h.say("")
h.say("dry run: nothing was removed")
return 0
if not yes:
h.say("")
h.say("Type the folder name to continue: %s" % os.path.basename(folder))
typed = input("> ").strip()
if typed != os.path.basename(folder):
h.say("nothing was removed")
return 1
for name in PAGES:
if tuios.close_pane(name, session):
h.say("closed pane %s" % name)
repo = folder
for ref, cwd, branch in worktrees:
code, out = h.run(["git", "-C", repo, "worktree", "remove", "--force", cwd])
h.say("worktree %s: %s" % (ref, "removed" if code == 0 else out.strip()[:120]))
if branch:
code, out = h.run(["git", "-C", repo, "branch", "-D", branch])
h.say("branch %s: %s" % (branch, "deleted" if code == 0 else out.strip()[:120]))
if delete_folder:
h.say("")
h.say("Type the folder name again to delete %s: " % folder)
typed = input("> ").strip()
if typed != os.path.basename(folder):
h.say("folder kept")
return 0
code, out = h.run(["rm", "-rf", folder])
h.say("folder %s" % ("deleted" if code == 0 else "not deleted: " + out.strip()))
return 0
if __name__ == "__main__":
h.main_guard(main)