/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-04-18 14:45:39 UTC
  • Revision ID: tim@ed.am-20140418144539-uhprdfn5oyc8wc82
implemented ignore lists in copyer and made symlink substitution work only one
way (so that symlink substitutions can only occur in ~/ and not in the repo)

Show diffs side-by-side

added added

removed removed

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