File: rsync.py

package info (click to toggle)
repopush 0.1.0-4
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 208 kB
  • sloc: python: 388; sh: 162; makefile: 58; xml: 2
file content (79 lines) | stat: -rw-r--r-- 2,481 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
"""A wrapper for the `rsync` command."""

import json
import os
import pathlib
import sys

from . import util


def check_path(path: pathlib.Path, base: pathlib.Path) -> None:
    """Validate a source or destination path."""
    print(f"Validating {path} under {base}")
    if not path.is_absolute():
        sys.exit(f"rsync wrapper invoked with non-absolute path {path}")
    if (path.exists() or path.is_symlink()) and not path.is_dir():
        sys.exit(f"rsync wrapper invoked with bad path {path}")

    try:
        path.relative_to(base)
    except ValueError:
        sys.exit(f"rsync wrapper invoked with path {path} not under {base}")


def main() -> None:
    """Wrap the `rsync` command, check for some things before and after."""
    print("rsync wrapper invoked as " + util.cmdstr(sys.argv))
    state_file = pathlib.Path(os.environ["REPOPUSH_TEST_STATE"])
    state_data = json.loads(state_file.read_text(encoding="UTF-8"))

    state_data["steps"] += 1
    state_file.write_text(json.dumps(state_data), encoding="UTF-8")

    if state_data["noop"]:
        if "-n" not in sys.argv:
            sys.exit("rsync wrapper invoked without `-n`")
    else:
        if "-n" in sys.argv:
            sys.exit("rsync wrapper invoked with `-n")

    if "-az" not in sys.argv:
        sys.exit("rsync wrapper invoked without `-az`")

    if len(sys.argv) < 5:
        sys.exit("rsync wrapper invoked with too few arguments")

    if sys.argv[-3] != "--":
        sys.exit("rsync wrapper invoked without `-- path path`")

    src_path = pathlib.Path(sys.argv[-2])
    dst_path = pathlib.Path(sys.argv[-1])
    check_path(src_path, state_data["src"])
    check_path(dst_path, state_data["dst"])

    expected_steps = util.expected_steps(pathlib.Path(state_data["src"]))
    step = state_data["steps"]
    print(f"Step {step} of {expected_steps}")
    if step > expected_steps:
        sys.exit(
            f"rsync wrapper invoked for step {step}, "
            f"only expected {expected_steps}"
        )
    if step == expected_steps:
        if "--delete" not in sys.argv:
            sys.exit(
                "rsync wrapper invoked without `--delete` on the last step"
            )
    else:
        if "--delete" in sys.argv:
            sys.exit(
                "rsync wrapper invoked with `--delete` before the last step"
            )

    rsync = os.environ["REPOPUSH_TEST_RSYNC"]
    os.execv(rsync, [rsync] + sys.argv[1:])


if __name__ == "__main__":
    main()