FILEDECK 1.0.0 Spreadsheet-driven bulk file operations for desktop records work. -------------------------------------------------------------------------- WHAT IT IS -------------------------------------------------------------------------- FileDeck takes a CSV plan - the kind a team exports from Excel or Google Sheets - in which each row names one file and says exactly what should happen to it: move it, copy it, rename it, or retire it. FileDeck checks the whole plan first, refuses the entire run if any row is wrong, executes what is left over as one unit, and writes a journal that reverses it. Its sibling tools derive operations from patterns. FileDeck does the opposite: a human (or another system) specifies every operation explicitly, row by row, which is how real migrations, reorganisations and records-retention jobs actually arrive. -------------------------------------------------------------------------- COMMANDS -------------------------------------------------------------------------- filedeck validate --plan [--root ] [--trash ] [--json] filedeck apply --plan [--root ] --journal [--trash ] [--apply] [--json] filedeck undo --journal [--apply] [--json] filedeck template filedeck help (also -h, --help) validate Reads the plan and reports EVERY problem it finds in one pass, each with the CSV row number. Touches nothing. apply Runs the same validation and REFUSES THE WHOLE RUN if anything is wrong. Dry run unless --apply is given. undo Replays a journal backwards. Dry run unless --apply is given. template Prints an example plan CSV with one row per action. -------------------------------------------------------------------------- PLAN FORMAT -------------------------------------------------------------------------- Real RFC 4180 CSV (Go's encoding/csv), so quoted fields containing commas and double quotes work end to end. * The first line that is not a comment is the header row. * The header must contain the columns action, source and destination. Their order does not matter and matching is case-insensitive. * Any other columns - ticket, owner, approved, notes - are ignored, so a spreadsheet export can be handed over unmodified. * Blank lines are skipped. Lines beginning with # are skipped. * Leading and trailing whitespace in a field is trimmed. * Row numbers in messages are the CSV file's LINE numbers, so they line up with what the spreadsheet shows. Each message also gives the data-row ordinal, e.g. "row 5 (data row 4)". ACTIONS move destination is a new path. Parent directories are created. copy destination is a new path for a duplicate. The source stays exactly where it was. rename destination is a new NAME inside the source's own directory. A destination containing a path separator is rejected; use move. retire the file is moved into the --trash directory. destination may be left empty, in which case the file keeps its layout relative to --root inside the trash; or it may be a relative path inside the trash. RETIRE NEVER DELETES ANYTHING. EXAMPLE action,source,destination,notes move,inbox/2024-report.txt,archive/2024/report.txt,relocate copy,inbox/contract.txt,shared/contract.txt,source stays put rename,inbox/draft final.txt,draft-final.txt,same folder retire,inbox/old-memo.txt,,goes to --trash move,"inbox/Smith, John.txt",archive/smith-john.txt,quoted path -------------------------------------------------------------------------- SAFETY MODEL -------------------------------------------------------------------------- THE PLAN IS VALIDATED IN FULL BEFORE ANY CHANGE IS MADE. apply performs exactly the same checks validate does, and if a single row fails, zero rows are executed and no journal is written. There is no partial run and no "skip the bad rows" mode. Checks performed on every row: * the action is one of move, copy, rename, retire * source is present and names an existing regular file (not a directory) * no source appears in two rows * no two rows target the same destination * no destination already exists on disk, unless that exact path is being moved away by another row in the same plan (so swaps and chains work) * no destination is created under a path that is a file, not a directory * neither source nor destination escapes --root * a retire destination cannot escape --trash NOTHING IS EVER HARD-DELETED. retire moves files into --trash. Undoing a copy moves the duplicate that the run created into --trash as well, rather than removing it. --trash defaults to /.filedeck-trash and is created on demand. Execution order is chosen so that all-or-nothing holds: every file that is leaving its place is first parked under a temporary name in its own directory, then duplicates are written, then the parked files land on their destinations. If any step fails, completed steps are reversed and the tree is left as it was found. Files retired into the trash get a -2, -3 ... suffix if something is already parked at that path, so nothing in the trash is overwritten either. --root defaults to the current working directory. Containment is checked lexically on the cleaned absolute paths; it is not a defence against a hostile symlink placed inside the tree by another process. UNDO The journal records the absolute from/to path of every executed operation, plus the root and trash directories. undo replays them newest first, verifies up front that every recorded destination is still present and that every original path is free (or is freed by another step of the same reversal), and refuses the whole reversal otherwise. -------------------------------------------------------------------------- WHAT IS NOT IMPLEMENTED -------------------------------------------------------------------------- * No XLSX, ODS or Google Sheets input. CSV only. Export the sheet first. * NO NETWORK OR SHARED-DRIVE CREDENTIAL HANDLING of any kind. FileDeck only touches paths the operating system has already mounted and that the current user can read and write. It does not log in to SharePoint, S3, SMB, Google Drive or anything else. * No directories as sources. Rows operate on regular files. Symlinks, devices, sockets and FIFOs are rejected as sources. * A source may appear only once per plan. You cannot copy and move the same file in one run; use two runs. * No recursion or globbing. One row equals one file. Generate the rows in the spreadsheet, or from another tool. * No content checks: FileDeck does not hash, diff or deduplicate files. * No overwriting. A destination that already exists is an error, not a replacement, unless another row moves that file away first. * No scheduling, no daemon, no watching. It runs when you run it. * No permission, ACL or extended-attribute migration beyond the basic Unix mode bits copied to duplicates. * The journal is not marked as consumed. It stays on disk after a reversal, and a second undo of it is refused because the paths it recorded are no longer there - except for a perfectly symmetric plan (a pure a<->b swap), which a second undo will simply swap back. * No progress bar or parallelism; runs are sequential and single-process. * Concurrent modification of the tree while filedeck runs is not detected. Validate and apply are separate reads of the disk. -------------------------------------------------------------------------- EXIT CODES -------------------------------------------------------------------------- 0 success, or a dry run that found no problems, or help 1 plan rejected, bad invocation, unreadable plan or journal, I/O failure -------------------------------------------------------------------------- ROADMAP -------------------------------------------------------------------------- * XLSX input read directly, so a team can hand over the workbook itself instead of exporting a CSV first, including sheet and range selection. * A shared plan server with approvals: plans are submitted, reviewed and signed off by a second person before apply will execute them, with the approval recorded in the journal. * Scheduled retention runs: a stored plan plus a retention policy that re-runs on a calendar, retiring files as they age out and mailing the journal to the records owner. -------------------------------------------------------------------------- BUILD -------------------------------------------------------------------------- Go 1.24, standard library only, no third-party modules. GOPROXY=off go build -o filedeck . GOPROXY=off GOOS=windows GOARCH=amd64 go build -ldflags="-s -w" \ -o dist/filedeck-windows-amd64.exe . Prebuilt binaries live in dist/: filedeck-windows-amd64.exe filedeck-darwin-arm64 filedeck-darwin-amd64 filedeck-linux-amd64