Skip to content

Commit 23e58fd

Browse files
committed
Fix reinstall not detecting changed permissions by itself
1 parent e3263b3 commit 23e58fd

File tree

3 files changed

+78
-17
lines changed

3 files changed

+78
-17
lines changed

cylc/flow/remote.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -180,9 +180,14 @@ def get_includes_to_rsync(rsync_includes=None):
180180
DEFAULT_RSYNC_OPTS = [
181181
'-a',
182182
'--checksum',
183-
'--out-format=%o %n%L',
183+
'--out-format=%o %i %n%L', # see comment below
184184
'--no-t'
185185
]
186+
# %o: the operation (send or del.)
187+
# %i: itemized changes (needed for rsync to report files with
188+
# changed permissions
189+
# %n: filename
190+
# %L: "-> symlink_target" if applicable
186191

187192
DEFAULT_INCLUDES = [
188193
'/ana/***', # Rose ana analysis modules

cylc/flow/scripts/reinstall.py

Lines changed: 27 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -68,33 +68,42 @@
6868
"cylc install" even if present in the source directory.
6969
"""
7070

71+
from functools import partial
7172
from pathlib import Path
73+
import re
7274
import sys
73-
from typing import Optional, TYPE_CHECKING, List, Callable
74-
from functools import partial
75+
from typing import (
76+
TYPE_CHECKING,
77+
Callable,
78+
List,
79+
Optional,
80+
)
7581

7682
from ansimarkup import parse as cparse
7783

7884
from cylc.flow.exceptions import (
7985
ServiceFileError,
8086
WorkflowFilesError,
8187
)
82-
from cylc.flow.install import (
83-
reinstall_workflow,
84-
)
88+
from cylc.flow.install import reinstall_workflow
8589
from cylc.flow.network.multi import call_multi
8690
from cylc.flow.option_parsers import (
91+
ID_MULTI_ARG_DOC,
8792
CylcOptionParser as COP,
8893
OptionSettings,
89-
ID_MULTI_ARG_DOC
9094
)
9195
from cylc.flow.pathutil import get_workflow_run_dir
9296
from cylc.flow.plugins import run_plugins_async
97+
from cylc.flow.terminal import (
98+
DIM,
99+
cli_function,
100+
is_terminal,
101+
)
93102
from cylc.flow.workflow_files import (
94103
get_workflow_source_dir,
95104
load_contact_file,
96105
)
97-
from cylc.flow.terminal import cli_function, DIM, is_terminal
106+
98107

99108
if TYPE_CHECKING:
100109
from optparse import Values
@@ -319,7 +328,7 @@ def format_rsync_out(out: str) -> List[str]:
319328
320329
Example:
321330
>>> format_rsync_out(
322-
... 'send foo\ndel. bar\nbaz'
331+
... 'send >f+++++++++ foo\ndel. *deleting bar\nbaz'
323332
... '\ncannot delete non-empty directory: opt'
324333
... ) == [
325334
... cparse('<green>send foo</green>'),
@@ -331,18 +340,20 @@ def format_rsync_out(out: str) -> List[str]:
331340
"""
332341
lines = []
333342
for line in out.splitlines():
334-
if line[0:4] == 'send':
335-
# file added or updated
336-
lines.append(cparse(f'<green>{line}</green>'))
337-
elif line[0:4] == 'del.':
338-
# file deleted
339-
lines.append(cparse(f'<red>{line}</red>'))
340-
elif line == 'cannot delete non-empty directory: opt':
343+
if line == 'cannot delete non-empty directory: opt':
341344
# These "cannot delete non-empty directory" messages can arise
342345
# as a result of excluding files within sub-directories.
343-
# This opt dir message is likely to occur when a rose-suit.conf
346+
# This opt dir message is likely to occur when a rose-suite.conf
344347
# file is present.
345348
continue
349+
match = re.match(r'^(send|del\.)\s.{11}\s(.*)$', line)
350+
# .{11} is the itemized changes bit, which users don't need to see
351+
if match:
352+
operation = match.group(1)
353+
color = 'green' if operation == 'send' else 'red'
354+
lines.append(
355+
cparse(f"<{color}>{operation}</{color}> {match.group(2)}")
356+
)
346357
else:
347358
# other uncategorised log line
348359
lines.append(line)
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
# THIS FILE IS PART OF THE CYLC WORKFLOW ENGINE.
2+
# Copyright (C) NIWA & British Crown (Met Office) & Contributors.
3+
#
4+
# This program is free software: you can redistribute it and/or modify
5+
# it under the terms of the GNU General Public License as published by
6+
# the Free Software Foundation, either version 3 of the License, or
7+
# (at your option) any later version.
8+
#
9+
# This program is distributed in the hope that it will be useful,
10+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
# GNU General Public License for more details.
13+
#
14+
# You should have received a copy of the GNU General Public License
15+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
16+
17+
from textwrap import dedent
18+
19+
from ansimarkup import parse as cparse
20+
from cylc.flow.scripts.reinstall import format_rsync_out
21+
22+
23+
def test_format_rsync_out():
24+
"""It should:
25+
- colorize the output
26+
- remove the itemized changes from rsync format
27+
- remove the "cannot delete non-empty directory" message
28+
"""
29+
rsync_output = dedent("""
30+
del. *deleting Cloud.jpg
31+
send >f+++++++++ cloud.jpg
32+
send .f...p..... foo
33+
send >fcsTp..... bar
34+
cannot delete non-empty directory: opt
35+
send >f+++++++++ meow.txt
36+
send cL+++++++++ garage -> foo
37+
""").strip()
38+
assert format_rsync_out(rsync_output) == [
39+
cparse("<red>del.</red> Cloud.jpg"),
40+
cparse("<green>send</green> cloud.jpg"),
41+
cparse("<green>send</green> foo"),
42+
cparse("<green>send</green> bar"),
43+
cparse("<green>send</green> meow.txt"),
44+
cparse("<green>send</green> garage -> foo"),
45+
]

0 commit comments

Comments
 (0)