office: --new creates the Paperclip project, --remote adopts a folder on another machine
This commit is contained in:
92
bin/office
92
bin/office
@@ -2,12 +2,19 @@
|
||||
"""Open a folder as a project: adopt it, remember it, build its pages.
|
||||
|
||||
bin/office <folder> [--project <id|urlKey|name>] [--session NAME] [--dry]
|
||||
bin/office . # the folder you are in
|
||||
bin/office . # the folder you are in
|
||||
bin/office <folder> --new "Name" # create the Paperclip project too
|
||||
bin/office --remote <ssh-host> <folder> --project "Name" # the folder is on another machine
|
||||
|
||||
Adopting writes one file, <folder>/.harness/harness.toml, and nothing else. An
|
||||
existing folder is never overwritten: what is already there stays, and only the
|
||||
missing file is written. Paperclip's project record is read, never created —
|
||||
bin/office adopts a project that exists, it does not invent one.
|
||||
Adopting writes <folder>/.harness/ (the project file, check.sh, NOTES.md) and
|
||||
nothing else. An existing file is never overwritten. With --new, Paperclip gets
|
||||
a project for the folder, with the per-task worktree policy set; without it,
|
||||
--project adopts a project that already exists.
|
||||
|
||||
A session is created on the daemon of the machine that runs this command, and
|
||||
the panes run on that machine. A program cannot move your client to the new
|
||||
session: TUIOS refuses that from a pane. Switch with TUIOS's session switcher,
|
||||
or `tuios attach --host <machine> <session>`.
|
||||
|
||||
The machine is remembered, so the flag is stated once. Running the same command
|
||||
on a machine that is not the recorded one is refused rather than silently
|
||||
@@ -16,6 +23,7 @@ producing a second copy of the same project.
|
||||
|
||||
import json
|
||||
import os
|
||||
import shlex
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
@@ -27,21 +35,34 @@ import tuios # noqa: E402
|
||||
|
||||
|
||||
def parse(argv):
|
||||
opts = {"folder": ".", "project": "", "session": "", "dry": "--dry" in argv,
|
||||
"host": "", "no_switch": "--no-switch" in argv, "new": ""}
|
||||
for i, a in enumerate(argv):
|
||||
if a == "--new" and i + 1 < len(argv):
|
||||
opts["new"] = argv[i + 1]
|
||||
args = [a for a in argv if not a.startswith("--")]
|
||||
if args:
|
||||
opts["folder"] = args[0]
|
||||
for i, a in enumerate(argv):
|
||||
if a == "--project" and i + 1 < len(argv):
|
||||
opts["project"] = argv[i + 1]
|
||||
if a == "--session" and i + 1 < len(argv):
|
||||
opts["session"] = argv[i + 1]
|
||||
if a == "--host" and i + 1 < len(argv):
|
||||
opts["host"] = argv[i + 1]
|
||||
"""Read the flags, then take what is left as the folder.
|
||||
|
||||
A flag's value is not a folder. The first version of this treated the value
|
||||
of --remote as the folder, which is exactly the kind of bug that only shows
|
||||
up when someone uses the flag.
|
||||
"""
|
||||
opts = {"folder": ".", "project": "", "session": "", "dry": False,
|
||||
"host": "", "no_switch": False, "new": "", "remote": ""}
|
||||
positional = []
|
||||
i = 0
|
||||
while i < len(argv):
|
||||
a = argv[i]
|
||||
if a in ("--project", "--session", "--host", "--new", "--remote"):
|
||||
if i + 1 < len(argv):
|
||||
opts[a[2:]] = argv[i + 1]
|
||||
i += 2
|
||||
continue
|
||||
i += 1
|
||||
continue
|
||||
if a == "--dry":
|
||||
opts["dry"] = True
|
||||
elif a == "--no-switch":
|
||||
opts["no_switch"] = True
|
||||
elif not a.startswith("-"):
|
||||
positional.append(a)
|
||||
i += 1
|
||||
if positional:
|
||||
opts["folder"] = positional[0]
|
||||
return opts
|
||||
|
||||
|
||||
@@ -145,6 +166,34 @@ exit 0
|
||||
return written
|
||||
|
||||
|
||||
|
||||
def run_on_remote(host, folder, opts):
|
||||
"""Adopt a folder that lives on another machine, from where you are sitting.
|
||||
|
||||
The session has to be made by the daemon that will hold the panes, and the
|
||||
panes have to run on the machine that owns the folder. So the work is done
|
||||
over ssh on that machine, and only the last step — showing the session — is
|
||||
the client's own business.
|
||||
"""
|
||||
here = os.path.dirname(os.path.abspath(__file__))
|
||||
inner = [os.path.join(here, "office"), folder, "--no-switch"]
|
||||
for flag, value in (("--project", opts["project"]), ("--session", opts["session"]), ("--new", opts["new"])):
|
||||
if value:
|
||||
inner += [flag, value]
|
||||
if opts["dry"]:
|
||||
inner.append("--dry")
|
||||
cmd = " ".join(shlex.quote(a) for a in inner)
|
||||
h.say("on %s: %s" % (host, cmd))
|
||||
out = subprocess.run(["ssh", "-o", "StrictHostKeyChecking=accept-new", host, cmd],
|
||||
capture_output=True, text=True, timeout=600)
|
||||
sys.stdout.write(out.stdout)
|
||||
if out.returncode != 0:
|
||||
raise h.Refused(3, "the other machine refused: %s" % (out.stderr or out.stdout).strip()[:300])
|
||||
h.say("")
|
||||
h.say("switch to it: tuios attach --host %s %s" % (host, opts["session"] or os.path.basename(folder)))
|
||||
return 0
|
||||
|
||||
|
||||
def main():
|
||||
opts = parse(sys.argv[1:])
|
||||
folder = os.path.abspath(os.path.expanduser(opts["folder"]))
|
||||
@@ -168,6 +217,9 @@ def main():
|
||||
# so one command takes a person from a folder to a working layout. TUIOS has
|
||||
# the verb for exactly this; asking the person to run `tuios new` first was
|
||||
# the step everyone forgot.
|
||||
if opts["remote"]:
|
||||
return run_on_remote(opts["remote"], folder, opts)
|
||||
|
||||
if opts["dry"]:
|
||||
h.say("would create a Paperclip project %r for %s" % (opts["new"] or opts["project"], folder))
|
||||
h.say("would write %s" % os.path.join(folder, ".harness", "harness.toml"))
|
||||
|
||||
Reference in New Issue
Block a user