/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: 2021-07-05 19:14:32 UTC
  • Revision ID: tim@ed.am-20210705191432-243ayb7s2nmussvi
python3ification

Show diffs side-by-side

added added

removed removed

Lines of Context:
20
20
 
21
21
 
22
22
import os, re, shutil, filecmp, json
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
 
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
 
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
 
        # 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
 
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
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
                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'];
68
81
 
69
82
 
70
83
        def save_deployment_state( self ):
71
 
                if the.verbose: print "saving deployment state"
 
84
                """Save the current deployment state (so there will be a deployment ongoing).
 
85
                """
 
86
 
 
87
                if the.verbose >= 1: print("saving deployment state")
72
88
 
73
89
                # create metadata directory, as necessary
74
90
                if not os.path.isdir( the.full_mddir ):
76
92
 
77
93
                # pack deployment state
78
94
                state = {
79
 
                        'deploy_files': self.deploy_files,
80
 
                        'updated_files': self.updated_files,
 
95
                        'imported_files': self.imported_files,
 
96
                        'initial_revno': self.initial_revno,
 
97
                        'affected_files': self.affected_files,
81
98
                }
82
99
 
83
100
                # create file
87
104
 
88
105
 
89
106
        def remove_deployment_state( self ):
 
107
                """Remove the current deployment state (so no deployment will be ongoing).
 
108
                """
90
109
 
91
110
                # check it exists
92
111
                file = os.path.join( the.full_mddir, "deploy.%s" % the.repo.name )
93
112
                if( os.path.isfile( file ) ):
94
113
 
95
114
                        # delete it
96
 
                        if the.verbose: print "removing deployment state"
 
115
                        if the.verbose >= 1: print("removing deployment state")
97
116
                        os.unlink( file )
98
117
 
99
118
 
100
119
        def is_ongoing( self ):
101
 
                return False if self.deploy_files is None else True
 
120
                """Is there a deployment currently ongoing?
 
121
                """
 
122
 
 
123
                return False if self.imported_files is None else True
102
124
 
103
125
 
104
126
        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
 
105
131
                if( ongoing ):
106
 
                        if self.deploy_files is None:
 
132
                        if self.imported_files is None:
107
133
                                raise self.DeploymentOngoing( False )
108
134
                else:
109
 
                        if self.deploy_files is not None:
 
135
                        if self.imported_files is not None:
110
136
                                raise self.DeploymentOngoing( True )
111
137
 
112
138
 
113
 
        def copy_in( self ):
 
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
                """
114
150
 
115
151
                # check we don't already have a file list
116
152
                self.check_ongoing( False )
117
153
 
118
154
                # if the repo doesn't exist, we have an empty file list
119
155
                if not os.path.exists( the.repo.full_dir ):
120
 
                        self.deploy_files = list()
 
156
                        self.imported_files = list()
121
157
                else:
122
158
                        # copy in
123
 
                        if the.verbose: print "importing files..."
 
159
                        if the.verbose >= 1: print("importing files...")
124
160
                        walker = CopyInWalker()
125
161
                        walker.walk()
126
 
                        if( walker.changed ):
127
 
                                raise self.CopyInConflicts( walker.changed )
128
 
                        self.deploy_files = walker.walk_list
 
162
                        self.imported_files = walker.walk_list
 
163
 
 
164
                        # obtain initial revno
 
165
                        self.initial_revno = the.repo.vcs.get_revno()
129
166
 
130
167
                # save state
131
168
                self.save_deployment_state()
132
169
 
133
170
 
134
 
        def get_conflicts( self, updated_files = None ):
 
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
                """
135
177
 
136
178
                # check we have a file list
137
179
                self.check_ongoing( True )
138
180
 
139
181
                # set updated files
140
 
                if updated_files is not None:
141
 
                        self.updated_files = updated_files
 
182
                if affected_files is not None:
 
183
                        self.affected_files = affected_files
142
184
                        self.save_deployment_state()
143
185
 
144
186
                # check for deployment conflicts
145
 
                walker = ConflictWalker( self.deploy_files, self.updated_files )
 
187
                walker = ConflictWalker( self.imported_files, self.affected_files )
146
188
                walker.walk()
147
189
 
148
190
                self.conflicts_checked = True
149
 
                return walker.changed
150
 
 
151
 
 
152
 
        def copy_out( self ):
 
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
                """
153
199
 
154
200
                # check we have a file list
155
201
                self.check_ongoing( True )
159
205
                        raise RuntimeError('logic error: deployment conflicts unchecked' )
160
206
 
161
207
                # copy out
162
 
                if the.verbose: print "exporting files..."
163
 
                walker = CopyOutWalker( self.updated_files )
 
208
                if the.verbose >= 1: print("exporting files...")
 
209
                walker = CopyOutWalker( self.affected_files, not quiet )
164
210
                walker.walk()
165
211
 
166
212
                # clear state
174
220
                                self.msg = "there is an ongoing deployment"
175
221
                        else:
176
222
                                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