Migrate MVLog worker to webserver-local deployment
This commit is contained in:
parent
8fc2e3b4c9
commit
85c4333e77
6 changed files with 126 additions and 19 deletions
|
|
@ -51,8 +51,13 @@ def remote_q(path: str) -> str:
|
|||
return quote(path)
|
||||
|
||||
|
||||
def list_jobs(host: str, remote_root: str) -> list[str]:
|
||||
cmd = f"cd {remote_q(remote_root)} && find in-dir -mindepth 1 -maxdepth 1 -type d -printf '%f\\n' | sort"
|
||||
def list_jobs(host: str | None, remote_root: str) -> list[str]:
|
||||
if host is None:
|
||||
root = Path(remote_root) / "in-dir"
|
||||
if not root.is_dir():
|
||||
return []
|
||||
return sorted(path.name for path in root.iterdir() if path.is_dir() and not path.name.startswith('.'))
|
||||
cmd = f"cd {remote_q(remote_root)} && find in-dir -mindepth 1 -maxdepth 1 -type d ! -name '.*' -printf '%f\\n' | sort"
|
||||
out = ssh(host, cmd).stdout
|
||||
return [line.strip() for line in out.splitlines() if line.strip()]
|
||||
|
||||
|
|
@ -80,17 +85,41 @@ def read_article_id(host: str | None, job_dir: str) -> str | None:
|
|||
return None
|
||||
|
||||
|
||||
def acquire_lock(host: str, job_dir: str) -> bool:
|
||||
def acquire_lock(host: str | None, job_dir: str) -> bool:
|
||||
lock = f"{job_dir}/{LOCK_NAME}"
|
||||
if host is None:
|
||||
try:
|
||||
Path(lock).mkdir()
|
||||
Path(lock, "started_at").write_text(now() + "\n", encoding="utf-8")
|
||||
return True
|
||||
except FileExistsError:
|
||||
return False
|
||||
cmd = f"mkdir {remote_q(lock)} 2>/dev/null && date -Iseconds > {remote_q(lock + '/started_at')}"
|
||||
return ssh(host, cmd, check=False).returncode == 0
|
||||
|
||||
|
||||
def release_lock(host: str, job_dir: str) -> None:
|
||||
def release_lock(host: str | None, job_dir: str) -> None:
|
||||
if host is None:
|
||||
shutil.rmtree(Path(job_dir) / LOCK_NAME, ignore_errors=True)
|
||||
return
|
||||
ssh(host, f"rm -rf {remote_q(job_dir + '/' + LOCK_NAME)}", check=False)
|
||||
|
||||
|
||||
def remote_fingerprint(host: str, job_dir: str) -> str:
|
||||
def remote_fingerprint(host: str | None, job_dir: str) -> str:
|
||||
if host is None:
|
||||
entries = []
|
||||
root = Path(job_dir)
|
||||
for dirpath, dirnames, filenames in os.walk(root):
|
||||
dirnames[:] = [d for d in dirnames if d != LOCK_NAME]
|
||||
for name in filenames:
|
||||
if name in {STATE_NAME, ENABLED_NAME, PREVIEW_NAME, HIDDEN_NAME, PERMALINK_NAME, ARTICLE_ID_NAME, '.mvlog-hide-map'}:
|
||||
continue
|
||||
path = Path(dirpath) / name
|
||||
rel = path.relative_to(root).as_posix()
|
||||
st = path.stat()
|
||||
entries.append([rel, st.st_size, st.st_mtime_ns])
|
||||
entries.sort()
|
||||
return hashlib.sha256(json.dumps(entries, ensure_ascii=False, separators=(",", ":")).encode("utf-8")).hexdigest()
|
||||
script = r'''
|
||||
import hashlib, json, os, sys
|
||||
root = sys.argv[1]
|
||||
|
|
@ -169,15 +198,28 @@ def write_state(host: str | None, job_dir: str, state: dict) -> None:
|
|||
ssh(host, cmd, input_text=data)
|
||||
|
||||
|
||||
def copy_job_from_remote(host: str, job_dir: str, local_input: Path) -> None:
|
||||
def copy_job_from_remote(host: str | None, job_dir: str, local_input: Path) -> None:
|
||||
local_input.mkdir(parents=True, exist_ok=True)
|
||||
excludes = {LOCK_NAME, STATE_NAME, PERMALINK_NAME}
|
||||
if host is None:
|
||||
src = Path(job_dir)
|
||||
for path in src.rglob("*"):
|
||||
if any(part in excludes for part in path.relative_to(src).parts):
|
||||
continue
|
||||
dest = local_input / path.relative_to(src)
|
||||
if path.is_dir():
|
||||
dest.mkdir(parents=True, exist_ok=True)
|
||||
elif path.is_file():
|
||||
dest.parent.mkdir(parents=True, exist_ok=True)
|
||||
shutil.copy2(path, dest)
|
||||
return
|
||||
remote_cmd = (
|
||||
f"tar --exclude={quote(LOCK_NAME)} --exclude={quote(STATE_NAME)} --exclude={quote(PERMALINK_NAME)} "
|
||||
f"-C {remote_q(job_dir)} -cf - ."
|
||||
)
|
||||
p1 = subprocess.Popen(["ssh", host, remote_cmd], stdout=subprocess.PIPE)
|
||||
try:
|
||||
p2 = subprocess.run(["tar", "-xf", "-", "-C", str(local_input)], stdin=p1.stdout, check=True)
|
||||
subprocess.run(["tar", "-xf", "-", "-C", str(local_input)], stdin=p1.stdout, check=True)
|
||||
assert p1.stdout is not None
|
||||
p1.stdout.close()
|
||||
rc = p1.wait()
|
||||
|
|
@ -190,8 +232,19 @@ def copy_job_from_remote(host: str, job_dir: str, local_input: Path) -> None:
|
|||
|
||||
|
||||
|
||||
def publish_output(host: str, remote_root: str, local_mp4: Path, old_output: str | None) -> str:
|
||||
def publish_output(host: str | None, remote_root: str, local_mp4: Path, old_output: str | None) -> str:
|
||||
out_name = local_mp4.name
|
||||
if host is None:
|
||||
out_dir = Path(remote_root) / "out-dir"
|
||||
out_dir.mkdir(parents=True, exist_ok=True)
|
||||
final = out_dir / out_name
|
||||
tmp = out_dir / f".{out_name}.tmp"
|
||||
shutil.copy2(local_mp4, tmp)
|
||||
tmp.replace(final)
|
||||
(Path(remote_root) / "cache" / "videos.json").unlink(missing_ok=True)
|
||||
if old_output and old_output != out_name:
|
||||
(out_dir / old_output).unlink(missing_ok=True)
|
||||
return out_name
|
||||
remote_tmp = f"{remote_root}/out-dir/.{out_name}.tmp"
|
||||
remote_final = f"{remote_root}/out-dir/{out_name}"
|
||||
subprocess.run(["scp", str(local_mp4), f"{host}:{remote_tmp}"], check=True)
|
||||
|
|
@ -271,7 +324,7 @@ def render_job(local_input: Path, local_out: Path, extra_args: list[str]) -> Pat
|
|||
return outputs[0]
|
||||
|
||||
|
||||
def process_job(host: str, remote_root: str, job: str, work_dir: Path, extra_args: list[str], force: bool) -> None:
|
||||
def process_job(host: str | None, remote_root: str, job: str, work_dir: Path, extra_args: list[str], force: bool) -> None:
|
||||
job_dir = f"{remote_root}/in-dir/{job}"
|
||||
article_id = read_article_id(host, job_dir)
|
||||
state = read_state(host, job_dir)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue