/stdhome

To get this branch, use:
bzr branch http://bzr.ed.am/stdhome

« back to all changes in this revision

Viewing changes to lib/stdhome/deployment.py

  • Committer: Tim Marston
  • Date: 2014-02-26 19:10:31 UTC
  • Revision ID: tim@ed.am-20140226191031-elcqy5j09h2syn2j
moved copy-in, copy-out and deployment conflict checking to a set of "walkers";
bzr vcs back-end now parses affected files during update; deployment state now
includes affected files

Show diffs side-by-side

added added

removed removed

Lines of Context:
21
21
 
22
22
import os, re, shutil, filecmp, json
23
23
import the, util
24
 
from walker.copy_in import CopyInWalker
25
 
from walker.conflict import ConflictWalker
26
 
from walker.copy_out import CopyOutWalker
27
 
 
 
24
from walker.copy_in_walker import CopyInWalker
 
25
from walker.conflict_walker import ConflictWalker
 
26
from walker.copy_out_walker import CopyOutWalker
28
27
 
29
28
class Deployment:
30
29
 
31
30
 
32
31
        def __init__( self ):
33
 
                if the.repo is None:
34
 
                        raise RuntimeError( 'logic error: Deployment initialised when '
35
 
                                                                'the.repo is unset' )
36
32
                self.load_deployment_state()
37
33
                self.conflicts_checked = False
38
34
 
39
35
 
40
36
        def load_deployment_state( self ):
41
 
                """Load any deployment state.  If one is found then a deployment will be
42
 
                considered to be ongoing.
43
 
                """
44
37
 
45
38
                # 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
53
 
 
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
57
 
 
58
 
                # the revno that the repo was as prior to a recent update
59
 
                self.initial_revno = None
 
39
        # to be) and updated through the vcs update.  This means that, while
 
40
        # there may have been conflicts during the update (which will be dealt
 
41
        # with in the repo), any arising conflicts with the filesystem are now
 
42
        # assumed to be because of the vcs update and can be ignored (that is to
 
43
        # say, we no longer care about the file in the home directory).  In
 
44
        # short, this is a list of files that it is safe to deploy, regardless
 
45
        # of the state of the filesystem.
 
46
                self.deploy_files = None
 
47
 
 
48
                # list of files that were changed by a recent vcs update (so only these
 
49
                # need to be checked for deployment conflicts or copied-out)
 
50
                self.updated_files = None
60
51
 
61
52
                # do we have a repo?
62
53
                if not os.path.exists( the.repo.full_dir ): return
67
58
                        return
68
59
 
69
60
                # read the file list
70
 
                if the.verbose >= 1: print "deployment state found; loading"
 
61
                if the.verbose: print "loading deployment state"
71
62
                f = open( file, 'r' )
72
63
                state = json.loads( f.read() )
73
64
 
74
65
                # 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'];
 
66
                self.deploy_files = state['deploy_files'];
 
67
                self.updated_files = state['updated_files'];
81
68
 
82
69
 
83
70
        def save_deployment_state( self ):
84
 
                """Save the current deployment state (so there will be a deployment ongoing).
85
 
                """
86
 
 
87
 
                if the.verbose >= 1: print "saving deployment state"
 
71
                if the.verbose: print "saving deployment state"
88
72
 
89
73
                # create metadata directory, as necessary
90
74
                if not os.path.isdir( the.full_mddir ):
92
76
 
93
77
                # pack deployment state
94
78
                state = {
95
 
                        'imported_files': self.imported_files,
96
 
                        'initial_revno': self.initial_revno,
97
 
                        'affected_files': self.affected_files,
 
79
                        'deploy_files': self.deploy_files,
 
80
                        'updated_files': self.updated_files,
98
81
                }
99
82
 
100
83
                # create file
104
87
 
105
88
 
106
89
        def remove_deployment_state( self ):
107
 
                """Remove the current deployment state (so no deployment will be ongoing).
108
 
                """
109
90
 
110
91
                # check it exists
111
92
                file = os.path.join( the.full_mddir, "deploy.%s" % the.repo.name )
112
93
                if( os.path.isfile( file ) ):
113
94
 
114
95
                        # delete it
115
 
                        if the.verbose >= 1: print "removing deployment state"
 
96
                        if the.verbose: print "removing deployment state"
116
97
                        os.unlink( file )
117
98
 
118
99
 
119
100
        def is_ongoing( self ):
120
 
                """Is there a deployment currently ongoing?
121
 
                """
122
 
 
123
 
                return False if self.imported_files is None else True
 
101
                return False if self.deploy_files is None else True
124
102
 
125
103
 
126
104
        def check_ongoing( self, ongoing = True ):
127
 
                """Check that a deployment either is or is not ongoing and raise an error if
128
 
                not.
129
 
                """
130
 
 
131
105
                if( ongoing ):
132
 
                        if self.imported_files is None:
 
106
                        if self.deploy_files is None:
133
107
                                raise self.DeploymentOngoing( False )
134
108
                else:
135
 
                        if self.imported_files is not None:
 
109
                        if self.deploy_files is not None:
136
110
                                raise self.DeploymentOngoing( True )
137
111
 
138
112
 
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
 
                """
 
113
        def copy_in( self ):
150
114
 
151
115
                # check we don't already have a file list
152
116
                self.check_ongoing( False )
153
117
 
154
118
                # if the repo doesn't exist, we have an empty file list
155
119
                if not os.path.exists( the.repo.full_dir ):
156
 
                        self.imported_files = list()
 
120
                        self.deploy_files = list()
157
121
                else:
158
122
                        # copy in
159
 
                        if the.verbose >= 1: print "importing files..."
 
123
                        if the.verbose: print "importing files..."
160
124
                        walker = CopyInWalker()
161
125
                        walker.walk()
162
 
                        self.imported_files = walker.walk_list
163
 
 
164
 
                        # obtain initial revno
165
 
                        self.initial_revno = the.repo.vcs.get_revno()
 
126
                        if( walker.changed ):
 
127
                                raise self.CopyInConflicts( walker.changed )
 
128
                        self.deploy_files = walker.walk_list
166
129
 
167
130
                # save state
168
131
                self.save_deployment_state()
169
132
 
170
133
 
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.
176
 
                """
 
134
        def get_conflicts( self, updated_files = None ):
177
135
 
178
136
                # check we have a file list
179
137
                self.check_ongoing( True )
180
138
 
181
139
                # set updated files
182
 
                if affected_files is not None:
183
 
                        self.affected_files = affected_files
 
140
                if updated_files is not None:
 
141
                        self.updated_files = updated_files
184
142
                        self.save_deployment_state()
185
143
 
186
144
                # check for deployment conflicts
187
 
                walker = ConflictWalker( self.imported_files, self.affected_files )
 
145
                walker = ConflictWalker( self.deploy_files, self.updated_files )
188
146
                walker.walk()
189
147
 
190
148
                self.conflicts_checked = True
191
 
                return walker.changed + walker.obstructed
192
 
 
193
 
 
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.
198
 
                """
 
149
                return walker.changed
 
150
 
 
151
 
 
152
        def copy_out( self ):
199
153
 
200
154
                # check we have a file list
201
155
                self.check_ongoing( True )
205
159
                        raise RuntimeError('logic error: deployment conflicts unchecked' )
206
160
 
207
161
                # copy out
208
 
                if the.verbose >= 1: print "exporting files..."
209
 
                walker = CopyOutWalker( self.affected_files, not quiet )
 
162
                if the.verbose: print "exporting files..."
 
163
                walker = CopyOutWalker( self.updated_files )
210
164
                walker.walk()
211
165
 
212
166
                # clear state
220
174
                                self.msg = "there is an ongoing deployment"
221
175
                        else:
222
176
                                self.msg = "there is no ongoing deployment"
 
177
 
 
178
 
 
179
        class CopyInConflicts( the.program.FatalError ):
 
180
 
 
181
                def __init__( self, conflicts ):
 
182
                        self.conflicts = conflicts