/stdhome

To get this branch, use:
bzr branch http://bzr.ed.am/stdhome
2 by Tim Marston
added global objects (the.repo, the.program), deployment object and implemented
1
# deployment.py
2
#
3 by Tim Marston
added bzr as a vcs backend; finished init command; implemented deployment
3
# Copyright (C) 2013 to 2014 Tim Marston <tim@edm.am>
2 by Tim Marston
added global objects (the.repo, the.program), deployment object and implemented
4
#
5
# This file is part of stdhome (hereafter referred to as "this program").
6
# See http://ed.am/dev/stdhome for more information.
7
#
8
# This program is free software: you can redistribute it and/or modify
9
# it under the terms of the GNU General Public License as published by
10
# the Free Software Foundation, either version 3 of the License, or
11
# (at your option) any later version.
12
#
13
# This program is distributed in the hope that it will be useful,
14
# but WITHOUT ANY WARRANTY; without even the implied warranty of
15
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
# GNU General Public License for more details.
17
#
18
# You should have received a copy of the GNU General Public License
19
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
20
21
5 by Tim Marston
moved copy-in, copy-out and deployment conflict checking to a set of "walkers";
22
import os, re, shutil, filecmp, json
3 by Tim Marston
added bzr as a vcs backend; finished init command; implemented deployment
23
import the, util
8 by Tim Marston
added diff command; moved all command to commands subdir; made stage-revert
24
from walker.copy_in import CopyInWalker
25
from walker.conflict import ConflictWalker
26
from walker.copy_out import CopyOutWalker
27
2 by Tim Marston
added global objects (the.repo, the.program), deployment object and implemented
28
29
class Deployment:
30
31
32
	def __init__( self ):
8 by Tim Marston
added diff command; moved all command to commands subdir; made stage-revert
33
		if the.repo is None:
34
			raise RuntimeError( 'logic error: Deployment initialised when '
35
								'the.repo is unset' )
3 by Tim Marston
added bzr as a vcs backend; finished init command; implemented deployment
36
		self.load_deployment_state()
37
		self.conflicts_checked = False
38
39
40
	def load_deployment_state( self ):
8 by Tim Marston
added diff command; moved all command to commands subdir; made stage-revert
41
		"""Load any deployment state.  If one is found then a deployment will be
42
		considered to be ongoing.
43
		"""
3 by Tim Marston
added bzr as a vcs backend; finished init command; implemented deployment
44
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
18 by Tim Marston
updated comment
47
        # there may have been conflicts during the update (which the user will
30 by Tim Marston
minor tweaks to variable names and comments
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
3 by Tim Marston
added bzr as a vcs backend; finished init command; implemented deployment
53
6 by Tim Marston
renamed updated_files to affected_files
54
		# list of files that were affected by a recent vcs update (so only these
8 by Tim Marston
added diff command; moved all command to commands subdir; made stage-revert
55
		# files need to be checked for deployment conflicts or copied-out)
6 by Tim Marston
renamed updated_files to affected_files
56
		self.affected_files = None
5 by Tim Marston
moved copy-in, copy-out and deployment conflict checking to a set of "walkers";
57
8 by Tim Marston
added diff command; moved all command to commands subdir; made stage-revert
58
		# the revno that the repo was as prior to a recent update
59
		self.initial_revno = None
60
3 by Tim Marston
added bzr as a vcs backend; finished init command; implemented deployment
61
		# do we have a repo?
62
		if not os.path.exists( the.repo.full_dir ): return
63
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 ):
67
			return
68
69
		# read the file list
35 by Tim Marston
made checks for verbose >= 1 explicit
70
		if the.verbose >= 1: print "deployment state found; loading"
3 by Tim Marston
added bzr as a vcs backend; finished init command; implemented deployment
71
		f = open( file, 'r' )
5 by Tim Marston
moved copy-in, copy-out and deployment conflict checking to a set of "walkers";
72
		state = json.loads( f.read() )
73
74
		# unpack deployment state
30 by Tim Marston
minor tweaks to variable names and comments
75
		self.imported_files = state['imported_files'];
8 by Tim Marston
added diff command; moved all command to commands subdir; made stage-revert
76
		self.initial_revno = state['initial_revno'];
6 by Tim Marston
renamed updated_files to affected_files
77
		self.affected_files = state['affected_files'];
3 by Tim Marston
added bzr as a vcs backend; finished init command; implemented deployment
78
79
80
	def save_deployment_state( self ):
8 by Tim Marston
added diff command; moved all command to commands subdir; made stage-revert
81
		"""Save the current deployment state (so there will be a deployment ongoing).
82
		"""
83
35 by Tim Marston
made checks for verbose >= 1 explicit
84
		if the.verbose >= 1: print "saving deployment state"
3 by Tim Marston
added bzr as a vcs backend; finished init command; implemented deployment
85
86
		# create metadata directory, as necessary
87
		if not os.path.isdir( the.full_mddir ):
88
			os.mkdir( the.full_mddir )
89
5 by Tim Marston
moved copy-in, copy-out and deployment conflict checking to a set of "walkers";
90
		# pack deployment state
91
		state = {
30 by Tim Marston
minor tweaks to variable names and comments
92
			'imported_files': self.imported_files,
8 by Tim Marston
added diff command; moved all command to commands subdir; made stage-revert
93
			'initial_revno': self.initial_revno,
6 by Tim Marston
renamed updated_files to affected_files
94
			'affected_files': self.affected_files,
5 by Tim Marston
moved copy-in, copy-out and deployment conflict checking to a set of "walkers";
95
		}
96
3 by Tim Marston
added bzr as a vcs backend; finished init command; implemented deployment
97
		# create file
98
		file = os.path.join( the.full_mddir, "deploy.%s" % the.repo.name )
99
		f = open( file, 'w' )
5 by Tim Marston
moved copy-in, copy-out and deployment conflict checking to a set of "walkers";
100
		f.write( json.dumps( state ) );
3 by Tim Marston
added bzr as a vcs backend; finished init command; implemented deployment
101
102
103
	def remove_deployment_state( self ):
8 by Tim Marston
added diff command; moved all command to commands subdir; made stage-revert
104
		"""Remove the current deployment state (so no deployment will be ongoing).
105
		"""
3 by Tim Marston
added bzr as a vcs backend; finished init command; implemented deployment
106
5 by Tim Marston
moved copy-in, copy-out and deployment conflict checking to a set of "walkers";
107
		# check it exists
3 by Tim Marston
added bzr as a vcs backend; finished init command; implemented deployment
108
		file = os.path.join( the.full_mddir, "deploy.%s" % the.repo.name )
109
		if( os.path.isfile( file ) ):
5 by Tim Marston
moved copy-in, copy-out and deployment conflict checking to a set of "walkers";
110
111
			# delete it
35 by Tim Marston
made checks for verbose >= 1 explicit
112
			if the.verbose >= 1: print "removing deployment state"
3 by Tim Marston
added bzr as a vcs backend; finished init command; implemented deployment
113
			os.unlink( file )
114
115
116
	def is_ongoing( self ):
8 by Tim Marston
added diff command; moved all command to commands subdir; made stage-revert
117
		"""Is there a deployment currently ongoing?
118
		"""
119
30 by Tim Marston
minor tweaks to variable names and comments
120
		return False if self.imported_files is None else True
3 by Tim Marston
added bzr as a vcs backend; finished init command; implemented deployment
121
122
123
	def check_ongoing( self, ongoing = True ):
8 by Tim Marston
added diff command; moved all command to commands subdir; made stage-revert
124
		"""Check that a deployment either is or is not ongoing and raise an error if
125
		not.
126
		"""
127
3 by Tim Marston
added bzr as a vcs backend; finished init command; implemented deployment
128
		if( ongoing ):
30 by Tim Marston
minor tweaks to variable names and comments
129
			if self.imported_files is None:
3 by Tim Marston
added bzr as a vcs backend; finished init command; implemented deployment
130
				raise self.DeploymentOngoing( False )
2 by Tim Marston
added global objects (the.repo, the.program), deployment object and implemented
131
		else:
30 by Tim Marston
minor tweaks to variable names and comments
132
			if self.imported_files is not None:
3 by Tim Marston
added bzr as a vcs backend; finished init command; implemented deployment
133
				raise self.DeploymentOngoing( True )
134
135
8 by Tim Marston
added diff command; moved all command to commands subdir; made stage-revert
136
	def get_initial_revno( self ):
137
		"""Get the initial revision identifier from the deployment state.
138
		"""
139
140
		return self.initial_revno
141
142
143
	def copy_in( self, initial_revno = None ):
144
		"""Copy-in changes from the home directory to the repository.  When finished,
145
		the state of deployment is saved, meaning that a deployment is ongoing.
146
		"""
2 by Tim Marston
added global objects (the.repo, the.program), deployment object and implemented
147
148
		# check we don't already have a file list
3 by Tim Marston
added bzr as a vcs backend; finished init command; implemented deployment
149
		self.check_ongoing( False )
150
5 by Tim Marston
moved copy-in, copy-out and deployment conflict checking to a set of "walkers";
151
		# if the repo doesn't exist, we have an empty file list
152
		if not os.path.exists( the.repo.full_dir ):
30 by Tim Marston
minor tweaks to variable names and comments
153
			self.imported_files = list()
5 by Tim Marston
moved copy-in, copy-out and deployment conflict checking to a set of "walkers";
154
		else:
155
			# copy in
35 by Tim Marston
made checks for verbose >= 1 explicit
156
			if the.verbose >= 1: print "importing files..."
5 by Tim Marston
moved copy-in, copy-out and deployment conflict checking to a set of "walkers";
157
			walker = CopyInWalker()
158
			walker.walk()
30 by Tim Marston
minor tweaks to variable names and comments
159
			self.imported_files = walker.walk_list
3 by Tim Marston
added bzr as a vcs backend; finished init command; implemented deployment
160
8 by Tim Marston
added diff command; moved all command to commands subdir; made stage-revert
161
			# obtain initial revno
162
			self.initial_revno = the.repo.vcs.get_revno()
163
3 by Tim Marston
added bzr as a vcs backend; finished init command; implemented deployment
164
		# save state
165
		self.save_deployment_state()
2 by Tim Marston
added global objects (the.repo, the.program), deployment object and implemented
166
167
6 by Tim Marston
renamed updated_files to affected_files
168
	def get_conflicts( self, affected_files = None ):
30 by Tim Marston
minor tweaks to variable names and comments
169
		"""Check to see if there are any deployment conflicts.  If a list of affected
8 by Tim Marston
added diff command; moved all command to commands subdir; made stage-revert
170
		files is supplied, then only those files are checked (and they are also
171
		saved with the deployment state).  Otherwise, all files in the
172
		repository are checked.
173
		"""
5 by Tim Marston
moved copy-in, copy-out and deployment conflict checking to a set of "walkers";
174
175
		# check we have a file list
176
		self.check_ongoing( True )
177
178
		# set updated files
6 by Tim Marston
renamed updated_files to affected_files
179
		if affected_files is not None:
180
			self.affected_files = affected_files
5 by Tim Marston
moved copy-in, copy-out and deployment conflict checking to a set of "walkers";
181
			self.save_deployment_state()
182
183
		# check for deployment conflicts
30 by Tim Marston
minor tweaks to variable names and comments
184
		walker = ConflictWalker( self.imported_files, self.affected_files )
5 by Tim Marston
moved copy-in, copy-out and deployment conflict checking to a set of "walkers";
185
		walker.walk()
186
187
		self.conflicts_checked = True
8 by Tim Marston
added diff command; moved all command to commands subdir; made stage-revert
188
		return walker.changed + walker.obstructed
5 by Tim Marston
moved copy-in, copy-out and deployment conflict checking to a set of "walkers";
189
190
2 by Tim Marston
added global objects (the.repo, the.program), deployment object and implemented
191
	def copy_out( self ):
30 by Tim Marston
minor tweaks to variable names and comments
192
		"""Copy-out changed files from the repository to the home directory.  If the
193
		deployment state includes a list of affected files, then only those
194
		files are copied-out.
8 by Tim Marston
added diff command; moved all command to commands subdir; made stage-revert
195
		"""
3 by Tim Marston
added bzr as a vcs backend; finished init command; implemented deployment
196
197
		# check we have a file list
198
		self.check_ongoing( True )
199
5 by Tim Marston
moved copy-in, copy-out and deployment conflict checking to a set of "walkers";
200
		# check that deployment conflicts have been checked-for
3 by Tim Marston
added bzr as a vcs backend; finished init command; implemented deployment
201
		if not self.conflicts_checked:
5 by Tim Marston
moved copy-in, copy-out and deployment conflict checking to a set of "walkers";
202
			raise RuntimeError('logic error: deployment conflicts unchecked' )
3 by Tim Marston
added bzr as a vcs backend; finished init command; implemented deployment
203
204
		# copy out
35 by Tim Marston
made checks for verbose >= 1 explicit
205
		if the.verbose >= 1: print "exporting files..."
6 by Tim Marston
renamed updated_files to affected_files
206
		walker = CopyOutWalker( self.affected_files )
5 by Tim Marston
moved copy-in, copy-out and deployment conflict checking to a set of "walkers";
207
		walker.walk()
3 by Tim Marston
added bzr as a vcs backend; finished init command; implemented deployment
208
209
		# clear state
210
		self.remove_deployment_state()
211
212
213
	class DeploymentOngoing( the.program.FatalError ):
214
215
		def __init__( self, ongoing ):
216
			if( ongoing ):
217
				self.msg = "there is an ongoing deployment"
218
			else:
219
				self.msg = "there is no ongoing deployment"
220
221
5 by Tim Marston
moved copy-in, copy-out and deployment conflict checking to a set of "walkers";
222
	class CopyInConflicts( the.program.FatalError ):
3 by Tim Marston
added bzr as a vcs backend; finished init command; implemented deployment
223
5 by Tim Marston
moved copy-in, copy-out and deployment conflict checking to a set of "walkers";
224
		def __init__( self, conflicts ):
225
			self.conflicts = conflicts