| Commit message (Collapse) | Author | Age | Files | Lines |
| |
|
|
|
|
|
|
|
|
|
|
| |
duet--transfer-spec classifies both endpoints, selects a backend through the registry, determines the route, and delegates argv construction to the backend (Phase 3 in the design spec). It returns the plist the executor will run: sources, destination, backend, route, argv, and async flag.
The route is decided independently of backend by duet--transfer-route: local, local-remote, remote-same-host, remote-direct, or remote-roundtrip. Different remote hosts default to the round-trip through this machine. Direct host-to-host runs only when a per-connection override asks for it, never automatically, because a direct route can silently fail where a round-trip always works.
This phase also registers the two stage-1 backends through the same duet-register-backend seam a plugin uses: rsync for local and ssh-reachable endpoints, TRAMP as the universal fallback that costs more so rsync wins whenever it applies. rsync receives its source and destination as separate argv elements, so a filename with spaces or shell metacharacters stays inert.
The two planners are pure and prompt-free, so the dangerous decisions are testable before a byte moves. duet--plan-conflicts resolves overwrite/skip/rename per file with an apply-to-all that stops asking, taking the existence check and the resolver as injected functions. duet--plan-move pairs each source's copy with a delete gated on that source's copy success, so a failed copy can never delete its source.
Remote-to-remote execution (honoring the round-trip route as a two-step through local) and TRAMP's in-process copy land in Phase 6. Here transfer-spec records the route and the rsync path the executor will use.
|
| |
|
|
|
|
|
|
|
|
| |
This is the seam every transport plugs into (Phase 2 in the design spec). A duet-backend struct carries a scorer, a command builder, capability flags, and contract metadata; duet-register-backend keeps a registry where re-registering a name replaces the prior backend, which is how a user or plugin overrides a built-in. duet--select-backend asks every backend to score an endpoint pair and picks the lowest cost, breaking ties toward the most recently registered.
The failure-normalizer interface turns a raw process failure into a class, a cause, evidence, a safety outcome, and next actions. Backends supply a pattern table via duet-define-cli-failure-patterns; anything unmatched falls back to a minimal normalizer that covers launch failure, missing executable, stall, signal, and unknown exit. This is what will let DUET explain a failure instead of dumping stderr.
Secrets are redacted before they reach a log: a pattern keeps its group-1 field label and strips the value, and a backend with no secret surface declares :none rather than an empty pattern list, so a forgotten declaration (nil) stays distinguishable from a deliberate one.
The tiered contract checks (minimum, publishable, capability) return a list of violations a backend author asserts is empty in one ERT test. The built-in rsync and TRAMP backends register through this same API in Phase 3, once their command builders land; here the machinery is exercised by fake backends.
|
| |
|
|
|
|
|
|
| |
duet--classify-path turns a path string into a plist describing where it lives and how to reach it: :locality (local or remote), :method, :user, :host, :port, :localname, and :hop for multi-hop paths. It's the pure foundation the transfer-spec and connection layers build on (Phase 1 in the design spec).
TRAMP does the dissection, so any path file-remote-p recognizes is remote and everything else is local. A local path is expanded, so a leading ~ resolves to the home directory. A remote localname is kept verbatim because a ~ there is the remote home, not this machine's. Classification never signals: an incomplete string like /ssh:host that TRAMP rejects as a remote name falls back to local, since validating raw TRAMP input belongs to the connection reader, not here.
I probed TRAMP's real contract before writing the tests (port comes back as a string, a multi-hop path reports the final host with the leading hops in :hop), so the Normal/Boundary/Error cases assert what TRAMP actually returns rather than what I'd have guessed.
|
| |
|
|
|
|
|
|
|
|
| |
I brought the skeleton up to a working package baseline (Phase 0 in the design spec). Eask defines the package and its dev deps. A root Makefile delegates test targets to tests/Makefile and adds compile, coverage, lint, doctor, and clean, matching the layout the other packages use.
deps installs both halves DUET needs: the Emacs Lisp deps via eask, and the transport CLIs (rsync, rclone, lftp, unison) via the system package manager, so a contributor's environment is ready before the code that shells out to them.
make complexity runs a small homegrown McCabe branch counter (scripts/duet-complexity.el). No off-the-shelf tool measures Emacs Lisp: lizard doesn't support it and codemetrics is an interactive overlay, so DUET owns one. The counting is pure and covered by Normal/Boundary/Error tests. The budget is soft and the target is advisory.
The ERT harness (bootstrap, check-deps, per-file undercover coverage) and a smoke test prove the loop works end to end.
|
|
|
Initial skeleton for Duet, a two-pane file commander built on dirvish/dired: the package header and entry-command stub, the README, and the test directory. Pre-alpha — no functionality yet.
|