Skip to content

Commit da02d47

Browse files
authored
operations: add files.copy operation
1 parent d50ccce commit da02d47

File tree

9 files changed

+215
-0
lines changed

9 files changed

+215
-0
lines changed

src/pyinfra/operations/files.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -756,6 +756,15 @@ def _file_equal(local_path: str | IO[Any] | None, remote_path: str) -> bool:
756756
return False
757757

758758

759+
def _remote_file_equal(remote_path_a: str, remote_path_b: str) -> bool:
760+
for fact in [Sha1File, Md5File, Sha256File]:
761+
sum_a = host.get_fact(fact, path=remote_path_a)
762+
sum_b = host.get_fact(fact, path=remote_path_b)
763+
if sum_a and sum_b:
764+
return sum_a == sum_b
765+
return False
766+
767+
759768
@operation(
760769
# We don't (currently) cache the local state, so there's nothing we can
761770
# update to flag the local file as present.
@@ -1335,6 +1344,39 @@ def move(src: str, dest: str, overwrite=False):
13351344
yield StringCommand("mv", QuoteString(src), QuoteString(dest))
13361345

13371346

1347+
@operation()
1348+
def copy(src: str, dest: str, overwrite=False):
1349+
"""
1350+
Copy remote file/directory/link into remote directory
1351+
1352+
+ src: remote file/directory to copy
1353+
+ dest: remote directory to copy `src` into
1354+
+ overwrite: whether to overwrite dest, if present
1355+
"""
1356+
src_is_dir = host.get_fact(Directory, src)
1357+
if not host.get_fact(File, src) and not src_is_dir:
1358+
raise OperationError(f"src {src} does not exist")
1359+
1360+
if not host.get_fact(Directory, dest):
1361+
raise OperationError(f"dest {dest} is not an existing directory")
1362+
1363+
dest_file_path = os.path.join(dest, os.path.basename(src))
1364+
dest_file_exists = host.get_fact(File, dest_file_path)
1365+
if dest_file_exists and not overwrite:
1366+
if _remote_file_equal(src, dest_file_path):
1367+
host.noop(f"{dest_file_path} already exists")
1368+
return
1369+
else:
1370+
raise OperationError(f"{dest_file_path} already exists and is different than src")
1371+
1372+
cp_cmd = ["cp -r"]
1373+
1374+
if overwrite:
1375+
cp_cmd.append("-f")
1376+
1377+
yield StringCommand(*cp_cmd, QuoteString(src), QuoteString(dest))
1378+
1379+
13381380
def _validate_path(path):
13391381
try:
13401382
return os.fspath(path)
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
{
2+
"require_platform": ["Darwin", "Linux"],
3+
"kwargs": {
4+
"src": "/tmp/src_dir",
5+
"dest": "/tmp/dest_dir",
6+
"overwrite": false
7+
},
8+
"facts": {
9+
"files.File": {
10+
"path=/tmp/src_dir/file": true,
11+
12+
"path=/tmp/src_dir": null,
13+
"path=/tmp/dest_dir/src_dir": null,
14+
"path=/tmp/dest_dir/src_dir/file": null
15+
},
16+
"files.Directory": {
17+
"path=/tmp/src_dir": true,
18+
"path=/tmp/dest_dir": true
19+
}
20+
},
21+
"commands": ["cp -r /tmp/src_dir /tmp/dest_dir"]
22+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
{
2+
"require_platform": ["Darwin", "Linux"],
3+
"kwargs": {
4+
"src": "/tmp/src_dir",
5+
"dest": "/tmp/dest_dir",
6+
"overwrite": true
7+
},
8+
"facts": {
9+
"files.File": {
10+
"path=/tmp/src_dir/file": true,
11+
"path=/tmp/src_dir": null,
12+
"path=/tmp/dest_dir/src_dir": null,
13+
"path=/tmp/dest_dir/src_dir/file": null
14+
},
15+
"files.Directory": {
16+
"path=/tmp/src_dir": true,
17+
"path=/tmp/dest_dir": true
18+
}
19+
},
20+
"commands": ["cp -r -f /tmp/src_dir /tmp/dest_dir"]
21+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
{
2+
"require_platform": ["Darwin", "Linux"],
3+
"kwargs": {
4+
"src": "/tmp/src_dir/file",
5+
"dest": "/tmp/dest_dir",
6+
"overwrite": false
7+
},
8+
"facts": {
9+
"files.File": {
10+
"path=/tmp/src_dir/file": true,
11+
"path=/tmp/dest_dir/file": null
12+
},
13+
"files.Directory": {
14+
"path=/tmp/dest_dir": true,
15+
"path=/tmp/src_dir/file": null
16+
}
17+
},
18+
"commands": ["cp -r /tmp/src_dir/file /tmp/dest_dir"]
19+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
{
2+
"require_platform": ["Darwin", "Linux"],
3+
"kwargs": {
4+
"src": "/tmp/src_dir/file",
5+
"dest": "/tmp/dest_dir",
6+
"overwrite": true
7+
},
8+
"facts": {
9+
"files.File": {
10+
"path=/tmp/src_dir/file": true,
11+
"path=/tmp/dest_dir/file": true
12+
},
13+
"files.Directory": {
14+
"path=/tmp/dest_dir": true,
15+
"path=/tmp/src_dir/file": null
16+
}
17+
},
18+
"commands": ["cp -r -f /tmp/src_dir/file /tmp/dest_dir"]
19+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
{
2+
"kwargs": {
3+
"src": "/tmp/src_dir/file",
4+
"dest": "/tmp/dest_dir",
5+
"overwrite": false
6+
},
7+
"facts": {
8+
"files.File": {
9+
"path=/tmp/src_dir/file": true,
10+
"path=/tmp/dest_dir/file": null
11+
},
12+
"files.Directory": {
13+
"path=/tmp/dest_dir": null,
14+
"path=/tmp/src_dir/file": null
15+
}
16+
},
17+
"exception": {
18+
"name": "OperationError",
19+
"message": "dest /tmp/dest_dir is not an existing directory"
20+
}
21+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
{
2+
"require_platform": ["Darwin", "Linux"],
3+
"kwargs": {
4+
"src": "/tmp/src_dir/file",
5+
"dest": "/tmp/dest_dir",
6+
"overwrite": false
7+
},
8+
"facts": {
9+
"files.File": {
10+
"path=/tmp/src_dir/file": true,
11+
"path=/tmp/dest_dir/file": true
12+
},
13+
"files.Directory": {
14+
"path=/tmp/dest_dir": true,
15+
"path=/tmp/src_dir/file": null
16+
},
17+
"files.Sha1File": {
18+
"path=/tmp/src_dir/file": "abc",
19+
"path=/tmp/dest_dir/file": "xyz"
20+
}
21+
},
22+
"exception": {
23+
"name": "OperationError",
24+
"message": "/tmp/dest_dir/file already exists and is different than src"
25+
}
26+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
{
2+
"kwargs": {
3+
"src": "/tmp/src_dir/file",
4+
"dest": "/tmp/dest_dir",
5+
"overwrite": false
6+
},
7+
"facts": {
8+
"files.File": {
9+
"path=/tmp/src_dir/file": null,
10+
"path=/tmp/dest_dir/file": null
11+
},
12+
"files.Directory": {
13+
"path=/tmp/dest_dir": true,
14+
"path=/tmp/src_dir/file": null
15+
}
16+
},
17+
"exception": {
18+
"name": "OperationError",
19+
"message": "src /tmp/src_dir/file does not exist"
20+
}
21+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
{
2+
"require_platform": ["Darwin", "Linux"],
3+
"kwargs": {
4+
"src": "/tmp/src_dir/file",
5+
"dest": "/tmp/dest_dir",
6+
"overwrite": false
7+
},
8+
"facts": {
9+
"files.File": {
10+
"path=/tmp/src_dir/file": true,
11+
"path=/tmp/dest_dir/file": true
12+
},
13+
"files.Directory": {
14+
"path=/tmp/dest_dir": true,
15+
"path=/tmp/src_dir/file": null
16+
},
17+
"files.Sha1File": {
18+
"path=/tmp/src_dir/file": "abc",
19+
"path=/tmp/dest_dir/file": "abc"
20+
}
21+
},
22+
"commands": [],
23+
"noop_description": "/tmp/dest_dir/file already exists"
24+
}

0 commit comments

Comments
 (0)