/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/vcs/bzr.py

  • Committer: Tim Marston
  • Date: 2014-02-12 21:51:08 UTC
  • Revision ID: tim@ed.am-20140212215108-stk5z0nlvgpi4oa8
added bzr as a vcs backend; finished init command; implemented deployment

Show diffs side-by-side

added added

removed removed

21
21
 
22
22
import subprocess, os, re, shutil
23
23
from subprocess import Popen
 
24
import stdhome.the as the
24
25
import StringIO
25
 
from vcs import Vcs
26
 
from stdhome import the
27
 
 
28
 
 
29
 
class BzrVcs( Vcs ):
 
26
 
 
27
 
 
28
class VcsBzr:
30
29
 
31
30
 
32
31
        def __init__( self, dir ):
45
44
                os.mkdir( self.dir )
46
45
 
47
46
                # bzr init
48
 
                try:
49
 
                        self.run( [ 'bzr', 'init', '.' ] )
50
 
                except self.VcsError as e:
 
47
                p = Popen( [ 'bzr', 'init', '.' ], cwd = self.dir,
 
48
                                   stdout = subprocess.PIPE, stderr = subprocess.STDOUT )
 
49
                output = p.communicate()[ 0 ]
 
50
                if p.returncode > 0:
51
51
 
52
52
                        # attempt to clean-up dir
53
53
                        try:
55
55
                        except OSError:
56
56
                                pass
57
57
 
58
 
                        raise
 
58
                        raise the.program.FatalError( 'bzr init failed', output )
59
59
 
60
60
 
61
61
        def checkout( self, url ):
68
68
                os.mkdir( self.dir )
69
69
 
70
70
                # bzr co
71
 
                try:
72
 
                        self.run( [ 'bzr', 'checkout', url, '.' ] )
73
 
                except self.VcsError as e:
 
71
                p = Popen( [ 'bzr', 'co', url, '.' ], cwd = self.dir,
 
72
                                   stdout = subprocess.PIPE, stderr = subprocess.STDOUT )
 
73
                output = p.communicate()[ 0 ]
 
74
                if p.returncode > 0:
74
75
 
75
76
                        # attempt to clean-up dir
76
77
                        try:
78
79
                        except OSError:
79
80
                                pass
80
81
 
81
 
                        raise
82
 
 
83
 
 
84
 
        def get_revno( self ):
85
 
                """Obtain some sort of revision identifier
 
82
                        raise the.program.FatalError( 'bzr checkout failed', output )
 
83
 
 
84
 
 
85
        def revert( self ):
 
86
                """Revert the branch so that there are no outstanding changes or unknown files.
86
87
                """
87
88
 
88
89
                # bzr revert
89
 
                output = self.run( [ 'bzr', 'revno', '--tree' ] )
90
 
 
91
 
                # parse revno
92
 
                buf = StringIO.StringIO( output )
93
 
                return buf.readline().rstrip()
94
 
 
95
 
 
96
 
        def revert( self, revno = None ):
97
 
                """Revert the branch so that there are no outstanding changes or unknown files.
98
 
                If a revno is supplied, then the repository is reverted to that
99
 
                revision.
100
 
                """
101
 
 
102
 
                # bzr revert (run twice to handle a bug in bzr where reverting a
103
 
                # directory from a symlink can cause conflicts during initial revert)
104
 
                self.run( [ 'bzr', 'revert', '--no-backup' ] )
105
 
                self.run( [ 'bzr', 'revert', '--no-backup' ] )
 
90
                p = Popen( [ 'bzr', 'revert', '--no-backup' ], cwd = self.dir,
 
91
                                   stdout = subprocess.PIPE, stderr = subprocess.STDOUT )
 
92
                output = p.communicate()[ 0 ]
 
93
                if p.returncode > 0:
 
94
                        raise the.program.FatalError( 'bzr revert failed', output )
106
95
 
107
96
                # bzr st
108
 
                output = self.run( [ 'bzr', 'status' ] )
 
97
                p = Popen( [ 'bzr', 'st' ], cwd = self.dir,
 
98
                                   stdout = subprocess.PIPE, stderr = subprocess.STDOUT )
 
99
                output = p.communicate()[ 0 ]
 
100
                if p.returncode > 0:
 
101
                        raise the.program.FatalError( 'bzr status failed', output )
109
102
                files = self.parse_file_blocks( output )
110
103
 
111
104
                # remove unknown files
112
105
                if 'unknown' in files:
113
106
                        for file in files[ 'unknown' ]:
114
 
                                if the.verbose > 1: print "removing unknown: " + file
115
107
                                full_file = os.path.join( self.dir, file )
116
108
                                if os.path.isfile( full_file ):
117
109
                                        os.unlink( full_file )
118
 
                                elif os.path.isdir( full_file ):
 
110
                                elif os.full_file.isdir( full_file ):
119
111
                                        shutil.rmtree( full_file )
120
112
                                else:
121
113
                                        raise RuntimeError( 'exotic file in repo: %s' % file )
122
114
 
123
 
                # if a revision identifyer has been given, update to that
124
 
                if revno is not None:
125
 
 
126
 
                        # bzr update
127
 
                        self.run( [ 'bzr', 'update', '-r', revno ] )
128
 
 
129
115
 
130
116
        def update( self ):
131
 
                """Update the branch, pulling down any upstream changes and merging them.  This
132
 
                method returns a list of the files that were modified as part of this
133
 
                operation.
 
117
                """Update the branch, pulling down any upstream changes and merging them.
134
118
                """
135
119
 
136
 
#               WARNING: the following might cause bzr to ask for your ssh password more than
137
 
#               once during an update!!!
138
 
#
139
 
#               # get revno
140
 
#               revno = self.get_revno()
141
 
#
142
 
#               # update to current revision (pull in history without updating tree)
143
 
#               self.run( [ 'bzr', 'update', '-r', revno ] )
144
 
#
145
 
#               # get log output
146
 
#               next_revno = str( int( revno ) + 1 )
147
 
#               output = self.run( [ 'bzr', 'log', '-r', next_revno + '..' ] )
148
 
#
149
 
#               # parse output
150
 
#               keep_files = list()
151
 
#               buf = StringIO.StringIO( output )
152
 
#               in_message = False
153
 
#               for line in buf:
154
 
#                       line = line.rstrip( '\n' )
155
 
#                       if line.lower() == 'message:':
156
 
#                               in_message = True
157
 
#                       elif in_message:
158
 
#                               if line[ : 2 ] != '  ':
159
 
#                                       in_message = False
160
 
#                               else:
161
 
#                                       line = line[ 2 : ]
162
 
#
163
 
#                                       # process directives
164
 
#                                       if line[ : 6 ].lower() == 'keep: ':
165
 
#                                               file = line[ 6 : ]
166
 
#                                               if file in rename_files: file = rename_files[ file ]
167
 
#                                               keep_files.append( file )
168
 
#                                       elif line[ : 8 ].lower() == 'rename: ':
169
 
#                                               rename_from = line[ 8 : ]
170
 
#                                       elif line[ : 4 ].lower() == 'to: ':
171
 
#                                               if rename_from in rename_files:
172
 
#                                                       rename_from = rename_files[ rename_from ]
173
 
#                                               rename_files[ line[ 4 : ] ] = rename_from
174
 
 
175
 
                # bzr update properly
176
 
                output = self.run( [ 'bzr', 'update' ] )
177
 
 
178
 
                # parse output (see logic in report() in bzrlib/delta.py)
179
 
                files = list()
180
 
                buf = StringIO.StringIO( output )
181
 
                for line in buf:
182
 
                        if not re.search( '^[-R+ ?][K NMD!][* ] ', line ): continue
183
 
                        line = line.rstrip()
184
 
                        if the.verbose > 1: print '  %s' % line
185
 
 
186
 
                        # renames show before and after file names
187
 
                        matches = re.search( '^R.. (.*?)[/@+]? => (.*?)[/@+]?$', line )
188
 
                        if matches:
189
 
                                files.append( matches.group( 1 ) )
190
 
                                files.append( matches.group( 2 ) )
191
 
                                continue
192
 
 
193
 
                        # kind changes shows the same name twice
194
 
                        matches = re.search( '^.K. (.*?)[/@+]? => (.*?)[/@+]?$', line )
195
 
                        if matches:
196
 
                                files.append( matches.group( 1 ) )
197
 
                                continue
198
 
 
199
 
                        # other entries have only one filename
200
 
                        matches = re.search( '^... (.*?)[/@+]?$', line )
201
 
                        if matches:
202
 
                                files.append( matches.group( 1 ) )
203
 
                                continue
204
 
 
205
 
                        raise RuntimeError(
206
 
                                'failed to parse bzr update output line:\n%' % line )
207
 
 
208
 
                return files
 
120
                # bzr update
 
121
                p = Popen( [ 'bzr', 'update' ], cwd = self.dir,
 
122
                                   stdout = subprocess.PIPE, stderr = subprocess.STDOUT )
 
123
                output = p.communicate()[ 0 ]
 
124
                if p.returncode > 0:
 
125
                        raise the.program.FatalError( 'bzr update failed', output )
209
126
 
210
127
 
211
128
        def has_changes( self ):
213
130
                """
214
131
 
215
132
                # bzr status
216
 
                output = self.run( [ 'bzr', 'status', '--no-pending' ] )
217
 
 
218
 
                # parse output
 
133
                p = Popen( [ 'bzr', 'status', '--no-pending' ], cwd = self.dir,
 
134
                                   stdout = subprocess.PIPE, stderr = subprocess.STDOUT )
 
135
                output = p.communicate()[ 0 ]
 
136
                if p.returncode > 0:
 
137
                        raise the.program.FatalError( 'bzr status failed', output )
219
138
                files = self.parse_file_blocks( output )
220
139
                return True if len( files ) else False
221
140
 
225
144
                """
226
145
 
227
146
                # bzr status
228
 
                output = self.run( [ 'bzr', 'status', '--no-pending' ] )
229
 
 
230
 
                # parse output
 
147
                p = Popen( [ 'bzr', 'status', '--no-pending' ], cwd = self.dir,
 
148
                                   stdout = subprocess.PIPE, stderr = subprocess.STDOUT )
 
149
                output = p.communicate()[ 0 ]
 
150
                if p.returncode > 0:
 
151
                        raise the.program.FatalError( 'bzr status failed', output )
231
152
                files = self.parse_file_blocks( output )
232
153
                return files['conflicts'] if 'conflicts' in files else None
233
154
 
234
155
 
235
 
        def run( self, cmd ):
236
 
                if the.verbose > 1: print 'exec: %s' % ' '.join( cmd )
237
 
                p = Popen( cmd, cwd = self.dir,
238
 
                                   stdout = subprocess.PIPE, stderr = subprocess.STDOUT )
239
 
                output = p.communicate()[ 0 ]
240
 
                if p.returncode > 0:
241
 
                        raise self.VcsError( ' '.join( cmd[ : 2 ] ), output )
242
 
                return output
243
 
 
244
 
 
245
156
        def parse_file_blocks( self, output ):
246
157
                res = dict()
247
158
                current = None