/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:
1
1
# deployment.py
2
2
#
3
 
# Copyright (C) 2013 Tim Marston <tim@edm.am>
 
3
# Copyright (C) 2013 to 2014 Tim Marston <tim@edm.am>
4
4
#
5
5
# This file is part of stdhome (hereafter referred to as "this program").
6
6
# See http://ed.am/dev/stdhome for more information.
19
19
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
20
20
 
21
21
 
22
 
import os
23
 
import the
24
 
 
 
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
25
27
 
26
28
class Deployment:
27
29
 
28
30
 
29
31
        def __init__( self ):
30
 
 
31
 
                # initialise the file list
32
 
                self.file_list = None
33
 
                if not os.path.exists( the.repo.expanded_dir ):
34
 
                        self.file_list = []
 
32
                self.load_deployment_state()
 
33
                self.conflicts_checked = False
 
34
 
 
35
 
 
36
        def load_deployment_state( self ):
 
37
 
 
38
                # 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
 
51
 
 
52
                # do we have a repo?
 
53
                if not os.path.exists( the.repo.full_dir ): return
 
54
 
 
55
                # if no file list exists, we're done
 
56
                file = os.path.join( the.full_mddir, "deploy.%s" % the.repo.name )
 
57
                if not os.path.isfile( file ):
 
58
                        return
 
59
 
 
60
                # read the file list
 
61
                if the.verbose: print "loading deployment state"
 
62
                f = open( file, 'r' )
 
63
                state = json.loads( f.read() )
 
64
 
 
65
                # unpack deployment state
 
66
                self.deploy_files = state['deploy_files'];
 
67
                self.updated_files = state['updated_files'];
 
68
 
 
69
 
 
70
        def save_deployment_state( self ):
 
71
                if the.verbose: print "saving deployment state"
 
72
 
 
73
                # create metadata directory, as necessary
 
74
                if not os.path.isdir( the.full_mddir ):
 
75
                        os.mkdir( the.full_mddir )
 
76
 
 
77
                # pack deployment state
 
78
                state = {
 
79
                        'deploy_files': self.deploy_files,
 
80
                        'updated_files': self.updated_files,
 
81
                }
 
82
 
 
83
                # create file
 
84
                file = os.path.join( the.full_mddir, "deploy.%s" % the.repo.name )
 
85
                f = open( file, 'w' )
 
86
                f.write( json.dumps( state ) );
 
87
 
 
88
 
 
89
        def remove_deployment_state( self ):
 
90
 
 
91
                # check it exists
 
92
                file = os.path.join( the.full_mddir, "deploy.%s" % the.repo.name )
 
93
                if( os.path.isfile( file ) ):
 
94
 
 
95
                        # delete it
 
96
                        if the.verbose: print "removing deployment state"
 
97
                        os.unlink( file )
 
98
 
 
99
 
 
100
        def is_ongoing( self ):
 
101
                return False if self.deploy_files is None else True
 
102
 
 
103
 
 
104
        def check_ongoing( self, ongoing = True ):
 
105
                if( ongoing ):
 
106
                        if self.deploy_files is None:
 
107
                                raise self.DeploymentOngoing( False )
35
108
                else:
36
 
                        self.load_file_list()
37
 
 
38
 
                assert False # not implemented
39
 
 
40
 
 
41
 
        def load_file_list( self ):
42
 
 
43
 
                # if no file list, don't load one
44
 
                if .....
45
 
                        return
46
 
 
47
 
                # load it
 
109
                        if self.deploy_files is not None:
 
110
                                raise self.DeploymentOngoing( True )
48
111
 
49
112
 
50
113
        def copy_in( self ):
51
114
 
52
115
                # check we don't already have a file list
53
 
                if self.file_list is not None:
54
 
                        raise the.FatalError( 'deployment in progress; ' + \
55
 
                                'see "%s resolve --help" for information' % the.program.name )
56
 
                        
57
 
                assert False # not implemented
 
116
                self.check_ongoing( False )
 
117
 
 
118
                # if the repo doesn't exist, we have an empty file list
 
119
                if not os.path.exists( the.repo.full_dir ):
 
120
                        self.deploy_files = list()
 
121
                else:
 
122
                        # copy in
 
123
                        if the.verbose: print "importing files..."
 
124
                        walker = CopyInWalker()
 
125
                        walker.walk()
 
126
                        if( walker.changed ):
 
127
                                raise self.CopyInConflicts( walker.changed )
 
128
                        self.deploy_files = walker.walk_list
 
129
 
 
130
                # save state
 
131
                self.save_deployment_state()
 
132
 
 
133
 
 
134
        def get_conflicts( self, updated_files = None ):
 
135
 
 
136
                # check we have a file list
 
137
                self.check_ongoing( True )
 
138
 
 
139
                # set updated files
 
140
                if updated_files is not None:
 
141
                        self.updated_files = updated_files
 
142
                        self.save_deployment_state()
 
143
 
 
144
                # check for deployment conflicts
 
145
                walker = ConflictWalker( self.deploy_files, self.updated_files )
 
146
                walker.walk()
 
147
 
 
148
                self.conflicts_checked = True
 
149
                return walker.changed
58
150
 
59
151
 
60
152
        def copy_out( self ):
61
 
                if self.pre_copy_out_list is None:
62
 
                        return
63
 
 
64
 
                print "deploying files"
65
 
 
66
 
                # walk the repo
67
 
                for ( repodir, directories, files ) in os.walk( the.repo.expanded_dir ):
68
 
                        assert path[ : len( the.repo.expanded_dir ) ] == \
69
 
                                the.repo.expanded_dir
70
 
                        relative_path = path[ len( the.repo.expanded_dir ) : ]
71
 
                        fsdir = the.expanded_fsdir + relative_path
72
 
 
73
 
                        print relative_path
74
 
 
75
 
                        # check directories
76
 
                        for dir in directories:
77
 
                                if os.path.exists( fsdir + dir ) and \
78
 
                                   not os.path.isdir( fsdir + dir ):
79
 
 
80
 
                                        #
81
 
 
82
 
                        # if exists in repo as directory and also in fs as non-directory
83
 
                        if os.path.isdir( the.repo.expnaded_dir + relative_path + 
 
153
 
 
154
                # check we have a file list
 
155
                self.check_ongoing( True )
 
156
 
 
157
                # check that deployment conflicts have been checked-for
 
158
                if not self.conflicts_checked:
 
159
                        raise RuntimeError('logic error: deployment conflicts unchecked' )
 
160
 
 
161
                # copy out
 
162
                if the.verbose: print "exporting files..."
 
163
                walker = CopyOutWalker( self.updated_files )
 
164
                walker.walk()
 
165
 
 
166
                # clear state
 
167
                self.remove_deployment_state()
 
168
 
 
169
 
 
170
        class DeploymentOngoing( the.program.FatalError ):
 
171
 
 
172
                def __init__( self, ongoing ):
 
173
                        if( ongoing ):
 
174
                                self.msg = "there is an ongoing deployment"
 
175
                        else:
 
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