19
19
# along with this program. If not, see <http://www.gnu.org/licenses/>.
22
import os, re, shutil, filecmp, json
24
from walker.copy_in import CopyInWalker
25
from walker.conflict import ConflictWalker
26
from walker.copy_out import CopyOutWalker
29
32
def __init__( self ):
31
# initialise the file list
33
if not os.path.exists( the.repo.expanded_dir ):
34
raise RuntimeError( 'logic error: Deployment initialised when '
36
self.load_deployment_state()
37
self.conflicts_checked = False
40
def load_deployment_state( self ):
41
"""Load any deployment state. If one is found then a deployment will be
42
considered to be ongoing.
45
# list of files that were copied-in (or at least given the opportunity
46
# to be) and updated through the vcs update. This means that, while
47
# there may have been conflicts during the update (which the user will
48
# have to have dealt with in the repo), any conflicts arising with these
49
# files in the home directory are no longer important and can be
50
# ignored. In short, this is a list of files that can safely be
51
# deployed, regardless of the state of the home directory.
52
self.imported_files = None
54
# list of files that were affected by a recent vcs update (so only these
55
# files need to be checked for deployment conflicts or copied-out)
56
self.affected_files = None
58
# the revno that the repo was as prior to a recent update
59
self.initial_revno = None
62
if not os.path.exists( the.repo.full_dir ): return
64
# if no file list exists, we're done
65
file = os.path.join( the.full_mddir, "deploy.%s" % the.repo.name )
66
if not os.path.isfile( file ):
70
if the.verbose >= 1: print "deployment state found; loading"
72
state = json.loads( f.read() )
74
# unpack deployment state
75
if 'imported_files' in state:
76
self.imported_files = state['imported_files'];
77
if 'initial_revno' in state:
78
self.initial_revno = state['initial_revno'];
79
if 'affected_files' in state:
80
self.affected_files = state['affected_files'];
83
def save_deployment_state( self ):
84
"""Save the current deployment state (so there will be a deployment ongoing).
87
if the.verbose >= 1: print "saving deployment state"
89
# create metadata directory, as necessary
90
if not os.path.isdir( the.full_mddir ):
91
os.mkdir( the.full_mddir )
93
# pack deployment state
95
'imported_files': self.imported_files,
96
'initial_revno': self.initial_revno,
97
'affected_files': self.affected_files,
101
file = os.path.join( the.full_mddir, "deploy.%s" % the.repo.name )
102
f = open( file, 'w' )
103
f.write( json.dumps( state ) );
106
def remove_deployment_state( self ):
107
"""Remove the current deployment state (so no deployment will be ongoing).
111
file = os.path.join( the.full_mddir, "deploy.%s" % the.repo.name )
112
if( os.path.isfile( file ) ):
115
if the.verbose >= 1: print "removing deployment state"
119
def is_ongoing( self ):
120
"""Is there a deployment currently ongoing?
123
return False if self.imported_files is None else True
126
def check_ongoing( self, ongoing = True ):
127
"""Check that a deployment either is or is not ongoing and raise an error if
132
if self.imported_files is None:
133
raise self.DeploymentOngoing( False )
38
assert False # not implemented
41
def load_file_list( self ):
43
# if no file list, don't load one
135
if self.imported_files is not None:
136
raise self.DeploymentOngoing( True )
139
def get_initial_revno( self ):
140
"""Get the initial revision identifier from the deployment state.
143
return self.initial_revno
146
def copy_in( self, initial_revno = None ):
147
"""Copy-in changes from the home directory to the repository. When finished,
148
the state of deployment is saved, meaning that a deployment is ongoing.
52
151
# check we don't already have a file list
53
if self.file_list is not None:
54
raise the.FatalError( 'deployment in progress; ' + \
55
'see "%s resolve --help" for information' % the.program.name )
57
assert False # not implemented
61
if self.pre_copy_out_list is None:
64
print "deploying files"
67
for ( repodir, directories, files ) in os.walk( the.repo.expanded_dir ):
68
assert path[ : len( the.repo.expanded_dir ) ] == \
70
relative_path = path[ len( the.repo.expanded_dir ) : ]
71
fsdir = the.expanded_fsdir + relative_path
76
for dir in directories:
77
if os.path.exists( fsdir + dir ) and \
78
not os.path.isdir( fsdir + dir ):
82
# if exists in repo as directory and also in fs as non-directory
83
if os.path.isdir( the.repo.expnaded_dir + relative_path +
152
self.check_ongoing( False )
154
# if the repo doesn't exist, we have an empty file list
155
if not os.path.exists( the.repo.full_dir ):
156
self.imported_files = list()
159
if the.verbose >= 1: print "importing files..."
160
walker = CopyInWalker()
162
self.imported_files = walker.walk_list
164
# obtain initial revno
165
self.initial_revno = the.repo.vcs.get_revno()
168
self.save_deployment_state()
171
def get_conflicts( self, affected_files = None ):
172
"""Check to see if there are any deployment conflicts. If a list of affected
173
files is supplied, then only those files are checked (and they are also
174
saved with the deployment state). Otherwise, all files in the
175
repository are checked.
178
# check we have a file list
179
self.check_ongoing( True )
182
if affected_files is not None:
183
self.affected_files = affected_files
184
self.save_deployment_state()
186
# check for deployment conflicts
187
walker = ConflictWalker( self.imported_files, self.affected_files )
190
self.conflicts_checked = True
191
return walker.changed + walker.obstructed
194
def copy_out( self, quiet ):
195
"""Copy-out changed files from the repository to the home directory. If the
196
deployment state includes a list of affected files, then only those
197
files are copied-out.
200
# check we have a file list
201
self.check_ongoing( True )
203
# check that deployment conflicts have been checked-for
204
if not self.conflicts_checked:
205
raise RuntimeError('logic error: deployment conflicts unchecked' )
208
if the.verbose >= 1: print "exporting files..."
209
walker = CopyOutWalker( self.affected_files, quiet )
213
self.remove_deployment_state()
216
class DeploymentOngoing( the.program.FatalError ):
218
def __init__( self, ongoing ):
220
self.msg = "there is an ongoing deployment"
222
self.msg = "there is no ongoing deployment"