/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-01-05 11:51:35 UTC
  • Revision ID: tim@ed.am-20140105115135-6ses87ggwyjrxzfj
added global objects (the.repo, the.program), deployment object and implemented
init command

Show diffs side-by-side

added added

removed removed

1
1
# deployment.py
2
2
#
3
 
# Copyright (C) 2013 to 2014 Tim Marston <tim@edm.am>
 
3
# Copyright (C) 2013 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, 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
 
22
import os
 
23
import the
 
24
 
27
25
 
28
26
class Deployment:
29
27
 
30
28
 
31
29
        def __init__( self ):
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 ):
 
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 = []
 
35
                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 .....
58
45
                        return
59
46
 
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 )
108
 
                else:
109
 
                        if self.deploy_files is not None:
110
 
                                raise self.DeploymentOngoing( True )
 
47
                # load it
111
48
 
112
49
 
113
50
        def copy_in( self ):
114
51
 
115
52
                # check we don't already have a file list
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
 
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
150
58
 
151
59
 
152
60
        def copy_out( self ):
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
 
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 +