-
Notifications
You must be signed in to change notification settings - Fork 115
/
externals_status.py
164 lines (131 loc) · 5.54 KB
/
externals_status.py
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
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
"""ExternalStatus
Class to store status and state information about repositories and
create a string representation.
"""
from __future__ import absolute_import
from __future__ import unicode_literals
from __future__ import print_function
from .global_constants import EMPTY_STR
from .utils import printlog, indent_string
from .global_constants import VERBOSITY_VERBOSE, VERBOSITY_DUMP
class ExternalStatus(object):
"""Class to represent the status of a given source repository or tree.
Individual repositories determine their own status in the
Repository objects. This object is just resposible for storing the
information and passing it up to a higher level for reporting or
global decisions.
There are two states of concern:
* If the repository is in-sync with the externals description file.
* If the repostiory working copy is clean and there are no pending
transactions (e.g. add, remove, rename, untracked files).
"""
DEFAULT = '-'
UNKNOWN = '?'
EMPTY = 'e'
MODEL_MODIFIED = 's' # a.k.a. out-of-sync
DIRTY = 'M'
STATUS_OK = ' '
STATUS_ERROR = '!'
# source types
OPTIONAL = 'o'
STANDALONE = 's'
MANAGED = ' '
def __init__(self):
self.sync_state = self.DEFAULT
self.clean_state = self.DEFAULT
self.source_type = self.DEFAULT
self.path = EMPTY_STR
self.current_version = EMPTY_STR
self.expected_version = EMPTY_STR
self.status_output = EMPTY_STR
def log_status_message(self, verbosity):
"""Write status message to the screen and log file
"""
self._default_status_message()
if verbosity >= VERBOSITY_VERBOSE:
self._verbose_status_message()
if verbosity >= VERBOSITY_DUMP:
self._dump_status_message()
def _default_status_message(self):
"""Return the default terse status message string
"""
msg = '{sync}{clean}{src_type} {path}'.format(
sync=self.sync_state, clean=self.clean_state,
src_type=self.source_type, path=self.path)
printlog(msg)
def _verbose_status_message(self):
"""Return the verbose status message string
"""
clean_str = self.DEFAULT
if self.clean_state == self.STATUS_OK:
clean_str = 'clean sandbox'
elif self.clean_state == self.DIRTY:
clean_str = 'modified sandbox'
sync_str = 'on {0}'.format(self.current_version)
if self.sync_state != self.STATUS_OK:
sync_str = '{current} --> {expected}'.format(
current=self.current_version, expected=self.expected_version)
msg = ' {clean}, {sync}'.format(clean=clean_str, sync=sync_str)
printlog(msg)
def _dump_status_message(self):
"""Return the dump status message string
"""
msg = indent_string(self.status_output, 12)
printlog(msg)
def safe_to_update(self):
"""Report if it is safe to update a repository. Safe is defined as:
* If a repository is empty, it is safe to update.
* If a repository exists and has a clean working copy state
with no pending transactions.
"""
safe_to_update = False
repo_exists = self.exists()
if not repo_exists:
safe_to_update = True
else:
# If the repo exists, it must be in ok or modified
# sync_state. Any other sync_state at this point
# represents a logic error that should have been handled
# before now!
sync_safe = ((self.sync_state == ExternalStatus.STATUS_OK) or
(self.sync_state == ExternalStatus.MODEL_MODIFIED))
if sync_safe:
# The clean_state must be STATUS_OK to update. Otherwise we
# are dirty or there was a missed error previously.
if self.clean_state == ExternalStatus.STATUS_OK:
safe_to_update = True
return safe_to_update
def exists(self):
"""Determine if the repo exists. This is indicated by:
* sync_state is not EMPTY
* if the sync_state is empty, then the valid states for
clean_state are default, empty or unknown. Anything else
and there was probably an internal logic error.
NOTE(bja, 2017-10) For the moment we are considering a
sync_state of default or unknown to require user intervention,
but we may want to relax this convention. This is probably a
result of a network error or internal logic error but more
testing is needed.
"""
is_empty = (self.sync_state == ExternalStatus.EMPTY)
clean_valid = ((self.clean_state == ExternalStatus.DEFAULT) or
(self.clean_state == ExternalStatus.EMPTY) or
(self.clean_state == ExternalStatus.UNKNOWN))
if is_empty and clean_valid:
exists = False
else:
exists = True
return exists
def check_safe_to_update_repos(tree_status):
"""Check if *ALL* repositories are in a safe state to update. We don't
want to do a partial update of the repositories then die, leaving
the model in an inconsistent state.
Note: if there is an update to do, the repositories will by
definiation be out of synce with the externals description, so we
can't use that as criteria for updating.
"""
safe_to_update = True
for comp in tree_status:
stat = tree_status[comp]
safe_to_update &= stat.safe_to_update()
return safe_to_update