/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
88 by Tim Marston
python3ification
23
from . import the, util
24
from .walker.copy_in import CopyInWalker
25
from .walker.conflict import ConflictWalker
26
from .walker.copy_out import CopyOutWalker
8 by Tim Marston
added diff command; moved all command to commands subdir; made stage-revert
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
50 by Tim Marston
added checks to not die when information is missing in deployment
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.
30 by Tim Marston
minor tweaks to variable names and comments
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
88 by Tim Marston
python3ification
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
50 by Tim Marston
added checks to not die when information is missing in deployment
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'];
3 by Tim Marston
added bzr as a vcs backend; finished init command; implemented deployment
81
82
83
	def save_deployment_state( self ):
8 by Tim Marston
added diff command; moved all command to commands subdir; made stage-revert
84
		"""Save the current deployment state (so there will be a deployment ongoing).
85
		"""
86
88 by Tim Marston
python3ification
87
		if the.verbose >= 1: print("saving deployment state")
3 by Tim Marston
added bzr as a vcs backend; finished init command; implemented deployment
88
89
		# create metadata directory, as necessary
90
		if not os.path.isdir( the.full_mddir ):
91
			os.mkdir( the.full_mddir )
92
5 by Tim Marston
moved copy-in, copy-out and deployment conflict checking to a set of "walkers";
93
		# pack deployment state
94
		state = {
30 by Tim Marston
minor tweaks to variable names and comments
95
			'imported_files': self.imported_files,
8 by Tim Marston
added diff command; moved all command to commands subdir; made stage-revert
96
			'initial_revno': self.initial_revno,
6 by Tim Marston
renamed updated_files to affected_files
97
			'affected_files': self.affected_files,
5 by Tim Marston
moved copy-in, copy-out and deployment conflict checking to a set of "walkers";
98
		}
99
3 by Tim Marston
added bzr as a vcs backend; finished init command; implemented deployment
100
		# create file
101
		file = os.path.join( the.full_mddir, "deploy.%s" % the.repo.name )
102
		f = open( file, 'w' )
5 by Tim Marston
moved copy-in, copy-out and deployment conflict checking to a set of "walkers";
103
		f.write( json.dumps( state ) );
3 by Tim Marston
added bzr as a vcs backend; finished init command; implemented deployment
104
105
106
	def remove_deployment_state( self ):
8 by Tim Marston
added diff command; moved all command to commands subdir; made stage-revert
107
		"""Remove the current deployment state (so no deployment will be ongoing).
108
		"""
3 by Tim Marston
added bzr as a vcs backend; finished init command; implemented deployment
109
5 by Tim Marston
moved copy-in, copy-out and deployment conflict checking to a set of "walkers";
110
		# check it exists
3 by Tim Marston
added bzr as a vcs backend; finished init command; implemented deployment
111
		file = os.path.join( the.full_mddir, "deploy.%s" % the.repo.name )
112
		if( os.path.isfile( file ) ):
5 by Tim Marston
moved copy-in, copy-out and deployment conflict checking to a set of "walkers";
113
114
			# delete it
88 by Tim Marston
python3ification
115
			if the.verbose >= 1: print("removing deployment state")
3 by Tim Marston
added bzr as a vcs backend; finished init command; implemented deployment
116
			os.unlink( file )
117
118
119
	def is_ongoing( self ):
8 by Tim Marston
added diff command; moved all command to commands subdir; made stage-revert
120
		"""Is there a deployment currently ongoing?
121
		"""
122
30 by Tim Marston
minor tweaks to variable names and comments
123
		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
124
125
126
	def check_ongoing( self, ongoing = True ):
8 by Tim Marston
added diff command; moved all command to commands subdir; made stage-revert
127
		"""Check that a deployment either is or is not ongoing and raise an error if
128
		not.
129
		"""
130
3 by Tim Marston
added bzr as a vcs backend; finished init command; implemented deployment
131
		if( ongoing ):
30 by Tim Marston
minor tweaks to variable names and comments
132
			if self.imported_files is None:
3 by Tim Marston
added bzr as a vcs backend; finished init command; implemented deployment
133
				raise self.DeploymentOngoing( False )
2 by Tim Marston
added global objects (the.repo, the.program), deployment object and implemented
134
		else:
30 by Tim Marston
minor tweaks to variable names and comments
135
			if self.imported_files is not None:
3 by Tim Marston
added bzr as a vcs backend; finished init command; implemented deployment
136
				raise self.DeploymentOngoing( True )
137
138
8 by Tim Marston
added diff command; moved all command to commands subdir; made stage-revert
139
	def get_initial_revno( self ):
140
		"""Get the initial revision identifier from the deployment state.
141
		"""
142
143
		return self.initial_revno
144
145
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.
149
		"""
2 by Tim Marston
added global objects (the.repo, the.program), deployment object and implemented
150
151
		# check we don't already have a file list
3 by Tim Marston
added bzr as a vcs backend; finished init command; implemented deployment
152
		self.check_ongoing( False )
153
5 by Tim Marston
moved copy-in, copy-out and deployment conflict checking to a set of "walkers";
154
		# if the repo doesn't exist, we have an empty file list
155
		if not os.path.exists( the.repo.full_dir ):
30 by Tim Marston
minor tweaks to variable names and comments
156
			self.imported_files = list()
5 by Tim Marston
moved copy-in, copy-out and deployment conflict checking to a set of "walkers";
157
		else:
158
			# copy in
88 by Tim Marston
python3ification
159
			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";
160
			walker = CopyInWalker()
161
			walker.walk()
30 by Tim Marston
minor tweaks to variable names and comments
162
			self.imported_files = walker.walk_list
3 by Tim Marston
added bzr as a vcs backend; finished init command; implemented deployment
163
8 by Tim Marston
added diff command; moved all command to commands subdir; made stage-revert
164
			# obtain initial revno
165
			self.initial_revno = the.repo.vcs.get_revno()
166
3 by Tim Marston
added bzr as a vcs backend; finished init command; implemented deployment
167
		# save state
168
		self.save_deployment_state()
2 by Tim Marston
added global objects (the.repo, the.program), deployment object and implemented
169
170
6 by Tim Marston
renamed updated_files to affected_files
171
	def get_conflicts( self, affected_files = None ):
30 by Tim Marston
minor tweaks to variable names and comments
172
		"""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
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.
176
		"""
5 by Tim Marston
moved copy-in, copy-out and deployment conflict checking to a set of "walkers";
177
178
		# check we have a file list
179
		self.check_ongoing( True )
180
181
		# set updated files
6 by Tim Marston
renamed updated_files to affected_files
182
		if affected_files is not None:
183
			self.affected_files = affected_files
5 by Tim Marston
moved copy-in, copy-out and deployment conflict checking to a set of "walkers";
184
			self.save_deployment_state()
185
186
		# check for deployment conflicts
30 by Tim Marston
minor tweaks to variable names and comments
187
		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";
188
		walker.walk()
189
190
		self.conflicts_checked = True
8 by Tim Marston
added diff command; moved all command to commands subdir; made stage-revert
191
		return walker.changed + walker.obstructed
5 by Tim Marston
moved copy-in, copy-out and deployment conflict checking to a set of "walkers";
192
193
61 by Tim Marston
added home directory change reporting to CopyOutWalker; added --quiet option to
194
	def copy_out( self, quiet ):
30 by Tim Marston
minor tweaks to variable names and comments
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.
8 by Tim Marston
added diff command; moved all command to commands subdir; made stage-revert
198
		"""
3 by Tim Marston
added bzr as a vcs backend; finished init command; implemented deployment
199
200
		# check we have a file list
201
		self.check_ongoing( True )
202
5 by Tim Marston
moved copy-in, copy-out and deployment conflict checking to a set of "walkers";
203
		# check that deployment conflicts have been checked-for
3 by Tim Marston
added bzr as a vcs backend; finished init command; implemented deployment
204
		if not self.conflicts_checked:
5 by Tim Marston
moved copy-in, copy-out and deployment conflict checking to a set of "walkers";
205
			raise RuntimeError('logic error: deployment conflicts unchecked' )
3 by Tim Marston
added bzr as a vcs backend; finished init command; implemented deployment
206
207
		# copy out
88 by Tim Marston
python3ification
208
		if the.verbose >= 1: print("exporting files...")
82 by Tim Marston
added general reporting to CopyBase and configured it via copy-in and copy-out
209
		walker = CopyOutWalker( self.affected_files, not quiet )
5 by Tim Marston
moved copy-in, copy-out and deployment conflict checking to a set of "walkers";
210
		walker.walk()
3 by Tim Marston
added bzr as a vcs backend; finished init command; implemented deployment
211
212
		# clear state
213
		self.remove_deployment_state()
214
215
216
	class DeploymentOngoing( the.program.FatalError ):
217
218
		def __init__( self, ongoing ):
219
			if( ongoing ):
220
				self.msg = "there is an ongoing deployment"
221
			else:
222
				self.msg = "there is no ongoing deployment"