capistrano-git-copy 1.5.2 → 1.5.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1ff186491f9d22f79fab6c98f8e8f803240d94c3e364b74ecad60b0d358df60e
4
- data.tar.gz: fef778eafe2a6b6f188577fa6e36d7fc1b0caec838b854274a8e933b485ef8c4
3
+ metadata.gz: 100d26afc3422d8280d7ec211f0af3c9f2b2802c66aa5ffcdaa17d919de52292
4
+ data.tar.gz: 1d44966f13986f6ebd4f5c4d37a00b9fddb452ce0b82da0f8a863894bc320b40
5
5
  SHA512:
6
- metadata.gz: 80a5adb7628716d21644b2e7f5a14bde6737bc23443949f20656e10e0017d4751af50fe2cc8884f7f2172fb86d8c00db1fd30429f5901e6b0072edf7be144973
7
- data.tar.gz: 7e6f01a93481ca06c2a95e958b070187a46ab126403706b6e953183ee7176067cbb731a3e0bd67147231a1055d69523b194a5ff9a6f875897dde629b1e199ba7
6
+ metadata.gz: 3b38135587cf0d941be9664bbe3573544180398fcc13e9f466815ae47b4a1adcad2317fcd8ae4376a25f1e6d9413f264804045f8f5b4a805bea53fc798b015eb
7
+ data.tar.gz: a59f7e452bc4f0a54f8262c4b08576a3337c03c797b1531e3f36ed8dac88c86f972228580a14bf7b4da656fcfb3ed5eff2fb3c93e0febb45766de7da72a044a8
@@ -1,5 +1,9 @@
1
1
  # Change Log
2
2
 
3
+ ## 1.5.3 (2018-11-02)
4
+ ### Changes
5
+ - updated _git-archive-all_ to 1.19.1
6
+
3
7
  ## 1.5.2 (2018-10-07)
4
8
  ### Changes
5
9
  - updated _git-archive-all_ to 1.18.3
@@ -5,6 +5,6 @@ module Capistrano
5
5
  # GitCopy
6
6
  module GitCopy
7
7
  # gem version
8
- VERSION = '1.5.2'
8
+ VERSION = '1.5.3'
9
9
  end
10
10
  end
@@ -32,7 +32,7 @@ from subprocess import CalledProcessError, Popen, PIPE
32
32
  import sys
33
33
  import re
34
34
 
35
- __version__ = "1.18.3"
35
+ __version__ = "1.19.1"
36
36
 
37
37
 
38
38
  try:
@@ -121,7 +121,9 @@ class GitArchiver(object):
121
121
  self.force_sub = force_sub
122
122
  self.main_repo_abspath = main_repo_abspath
123
123
 
124
- def create(self, output_path, dry_run=False, output_format=None):
124
+ self._check_attr_gens = {}
125
+
126
+ def create(self, output_path, dry_run=False, output_format=None, compresslevel=None):
125
127
  """
126
128
  Create the archive at output_file_path.
127
129
 
@@ -147,7 +149,13 @@ class GitArchiver(object):
147
149
  if output_format in self.ZIPFILE_FORMATS:
148
150
  from zipfile import ZipFile, ZipInfo, ZIP_DEFLATED
149
151
 
150
- archive = ZipFile(path.abspath(output_path), 'w')
152
+ if compresslevel is not None:
153
+ if sys.version_info > (3, 7):
154
+ archive = ZipFile(path.abspath(output_path), 'w', compresslevel=compresslevel)
155
+ else:
156
+ raise ValueError("Compression level for zip archives requires Python 3.7+")
157
+ else:
158
+ archive = ZipFile(path.abspath(output_path), 'w')
151
159
 
152
160
  def add_file(file_path, arcname):
153
161
  if not path.islink(file_path):
@@ -161,7 +169,14 @@ class GitArchiver(object):
161
169
  import tarfile
162
170
 
163
171
  mode = self.TARFILE_FORMATS[output_format]
164
- archive = tarfile.open(path.abspath(output_path), mode)
172
+
173
+ if compresslevel is not None:
174
+ try:
175
+ archive = tarfile.open(path.abspath(output_path), mode, compresslevel=compresslevel)
176
+ except TypeError:
177
+ raise ValueError("{0} cannot be compressed".format(output_format))
178
+ else:
179
+ archive = tarfile.open(path.abspath(output_path), mode)
165
180
 
166
181
  def add_file(file_path, arcname):
167
182
  archive.add(file_path, arcname)
@@ -195,15 +210,9 @@ class GitArchiver(object):
195
210
  @return: True if file should be excluded. Otherwise False.
196
211
  @rtype: bool
197
212
  """
198
- out = self.run_git_shell(
199
- 'git check-attr -z export-ignore -- %s' % quote(repo_file_path),
200
- cwd=repo_abspath
201
- ).split('\0')
202
-
203
- try:
204
- return out[2] == 'set'
205
- except IndexError:
206
- return False
213
+ next(self._check_attr_gens[repo_abspath])
214
+ attrs = self._check_attr_gens[repo_abspath].send(repo_file_path)
215
+ return attrs['export-ignore'] == 'set'
207
216
 
208
217
  def archive_all_files(self, archiver):
209
218
  """
@@ -235,55 +244,73 @@ class GitArchiver(object):
235
244
  @rtype: Iterable
236
245
  """
237
246
  repo_abspath = path.join(self.main_repo_abspath, repo_path)
238
- repo_file_paths = self.run_git_shell(
239
- 'git ls-files -z --cached --full-name --no-empty-directory',
240
- repo_abspath
241
- ).split('\0')[:-1]
247
+ assert repo_abspath not in self._check_attr_gens
248
+ self._check_attr_gens[repo_abspath] = self.check_attr(repo_abspath, ['export-ignore'])
242
249
 
243
- for repo_file_path in repo_file_paths:
244
- repo_file_abspath = path.join(repo_abspath, repo_file_path) # absolute file path
245
- main_repo_file_path = path.join(repo_path, repo_file_path) # file path relative to the main repo
250
+ try:
251
+ repo_file_paths = self.run_git_shell(
252
+ 'git ls-files -z --cached --full-name --no-empty-directory',
253
+ repo_abspath
254
+ ).split('\0')[:-1]
246
255
 
247
- # Only list symlinks and files.
248
- if not path.islink(repo_file_abspath) and path.isdir(repo_file_abspath):
249
- continue
256
+ for repo_file_path in repo_file_paths:
257
+ repo_file_abspath = path.join(repo_abspath, repo_file_path) # absolute file path
258
+ main_repo_file_path = path.join(repo_path, repo_file_path) # relative to main_repo_abspath
250
259
 
251
- if self.is_file_excluded(repo_abspath, repo_file_path):
252
- continue
260
+ # Only list symlinks and files.
261
+ if not path.islink(repo_file_abspath) and path.isdir(repo_file_abspath):
262
+ continue
253
263
 
254
- yield main_repo_file_path
264
+ if self.is_file_excluded(repo_abspath, repo_file_path):
265
+ continue
255
266
 
256
- if self.force_sub:
257
- self.run_git_shell('git submodule init', repo_abspath)
258
- self.run_git_shell('git submodule update', repo_abspath)
267
+ yield main_repo_file_path
259
268
 
260
- try:
261
- repo_gitmodules_abspath = path.join(repo_abspath, ".gitmodules")
269
+ if self.force_sub:
270
+ self.run_git_shell('git submodule init', repo_abspath)
271
+ self.run_git_shell('git submodule update', repo_abspath)
262
272
 
263
- with open(repo_gitmodules_abspath) as f:
264
- lines = f.readlines()
273
+ try:
274
+ repo_gitmodules_abspath = path.join(repo_abspath, ".gitmodules")
265
275
 
266
- for l in lines:
267
- m = re.match("^\\s*path\\s*=\\s*(.*)\\s*$", l)
276
+ with open(repo_gitmodules_abspath) as f:
277
+ lines = f.readlines()
268
278
 
269
- if m:
270
- repo_submodule_path = m.group(1) # relative to repo_path
271
- main_repo_submodule_path = path.join(repo_path, repo_submodule_path) # relative to main_repo_abspath
279
+ for l in lines:
280
+ m = re.match("^\\s*path\\s*=\\s*(.*)\\s*$", l)
272
281
 
273
- if self.is_file_excluded(repo_abspath, repo_submodule_path):
274
- continue
282
+ if m:
283
+ repo_submodule_path = m.group(1) # relative to repo_path
284
+ main_repo_submodule_path = path.join(repo_path, repo_submodule_path) # relative to main_repo_abspath
275
285
 
276
- for main_repo_submodule_file_path in self.walk_git_files(main_repo_submodule_path):
277
- repo_submodule_file_path = main_repo_submodule_file_path.replace(repo_path, "", 1).strip("/")
278
- if self.is_file_excluded(repo_abspath, repo_submodule_file_path):
286
+ if self.is_file_excluded(repo_abspath, repo_submodule_path):
279
287
  continue
280
288
 
281
- yield main_repo_submodule_file_path
282
- except IOError:
283
- pass
289
+ for main_repo_submodule_file_path in self.walk_git_files(main_repo_submodule_path):
290
+ repo_submodule_file_path = main_repo_submodule_file_path.replace(repo_path, "", 1).strip("/") # relative to repo_path
291
+ if self.is_file_excluded(repo_abspath, repo_submodule_file_path):
292
+ continue
293
+
294
+ yield main_repo_submodule_file_path
295
+ except IOError:
296
+ pass
297
+ finally:
298
+ self._check_attr_gens[repo_abspath].close()
299
+ del self._check_attr_gens[repo_abspath]
300
+
301
+ @classmethod
302
+ def decode_git_output(cls, output):
303
+ """
304
+ Decode Git's binary output handeling the way it escapes unicode characters.
305
+
306
+ @type output: bytes
307
+
308
+ @rtype: str
309
+ """
310
+ return output.decode('unicode_escape').encode('raw_unicode_escape').decode('utf-8')
284
311
 
285
- @staticmethod
286
- def run_git_shell(cmd, cwd=None):
312
+ @classmethod
313
+ def run_git_shell(cls, cmd, cwd=None):
287
314
  """
288
315
  Runs git shell command, reads output and decodes it into unicode string.
289
316
 
@@ -300,7 +327,7 @@ class GitArchiver(object):
300
327
  """
301
328
  p = Popen(cmd, shell=True, stdout=PIPE, cwd=cwd)
302
329
  output, _ = p.communicate()
303
- output = output.decode('unicode_escape').encode('raw_unicode_escape').decode('utf-8')
330
+ output = cls.decode_git_output(output)
304
331
 
305
332
  if p.returncode:
306
333
  if sys.version_info > (2, 6):
@@ -310,13 +337,82 @@ class GitArchiver(object):
310
337
 
311
338
  return output
312
339
 
340
+ @classmethod
341
+ def check_attr(cls, repo_abspath, attrs):
342
+ """
343
+ Generator that returns attributes for given paths relative to repo_abspath.
344
+
345
+ >>> g = GitArchiver.check_attr('repo_path', ['export-ignore'])
346
+ >>> next(g)
347
+ >>> attrs = g.send('relative_path')
348
+ >>> print(attrs['export-ignore'])
349
+
350
+ @param repo_abspath: Absolute path to a git repository.
351
+ @type repo_abspath: str
352
+
353
+ @param attrs: Attributes to check.
354
+ @type attrs: [str]
355
+
356
+ @rtype: generator
357
+ """
358
+ def make_process():
359
+ cmd = 'GIT_FLUSH=1 git check-attr --stdin -z {0}'.format(' '.join(attrs))
360
+ return Popen(cmd, shell=True, stdin=PIPE, stdout=PIPE, cwd=repo_abspath)
361
+
362
+ def read_attrs(process, repo_file_path):
363
+ process.stdin.write(repo_file_path.encode('utf-8') + b'\0')
364
+ process.stdin.flush()
365
+
366
+ # For every attribute check-attr will output: <path> NUL <attribute> NUL <info> NUL
367
+ path, attr, info = b'', b'', b''
368
+ nuls_count = 0
369
+ nuls_expected = 3 * len(attrs)
370
+
371
+ while nuls_count != nuls_expected:
372
+ b = process.stdout.read(1)
373
+
374
+ if b == b'' and process.poll() is not None:
375
+ raise RuntimeError("check-attr exited prematurely")
376
+ elif b == b'\0':
377
+ nuls_count += 1
378
+
379
+ if nuls_count % 3 == 0:
380
+ yield map(cls.decode_git_output, (path, attr, info))
381
+ path, attr, info = b'', b'', b''
382
+ elif nuls_count % 3 == 0:
383
+ path += b
384
+ elif nuls_count % 3 == 1:
385
+ attr += b
386
+ elif nuls_count % 3 == 2:
387
+ info += b
388
+
389
+ if not attrs:
390
+ return
391
+
392
+ process = make_process()
393
+
394
+ try:
395
+ while True:
396
+ repo_file_path = yield
397
+ repo_file_attrs = {}
398
+
399
+ for path, attr, value in read_attrs(process, repo_file_path):
400
+ assert path == repo_file_path
401
+ assert attr in attrs
402
+ repo_file_attrs[attr] = value
403
+
404
+ yield repo_file_attrs
405
+ finally:
406
+ process.stdin.close()
407
+ process.wait()
408
+
313
409
 
314
410
  def main():
315
- from optparse import OptionParser
411
+ from optparse import OptionParser, SUPPRESS_HELP
316
412
 
317
413
  parser = OptionParser(
318
414
  usage="usage: %prog [-v] [--prefix PREFIX] [--no-exclude] [--force-submodules]"
319
- " [--extra EXTRA1 [EXTRA2]] [--dry-run] OUTPUT_FILE",
415
+ " [--extra EXTRA1 ...] [--dry-run] [-0 | ... | -9] OUTPUT_FILE",
320
416
  version="%prog {0}".format(__version__)
321
417
  )
322
418
 
@@ -355,6 +451,13 @@ def main():
355
451
  dest='dry_run',
356
452
  help="don't actually archive anything, just show what would be done")
357
453
 
454
+ for i in range(10):
455
+ parser.add_option('-{0}'.format(i),
456
+ action='store_const',
457
+ const=i,
458
+ dest='compresslevel',
459
+ help=SUPPRESS_HELP)
460
+
358
461
  options, args = parser.parse_args()
359
462
 
360
463
  if len(args) != 1:
@@ -386,7 +489,7 @@ def main():
386
489
  options.exclude,
387
490
  options.force_sub,
388
491
  options.extra)
389
- archiver.create(output_file_path, options.dry_run)
492
+ archiver.create(output_file_path, options.dry_run, compresslevel=options.compresslevel)
390
493
  except Exception as e:
391
494
  parser.exit(2, "{0}\n".format(e))
392
495
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: capistrano-git-copy
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.5.2
4
+ version: 1.5.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Florian Schwab
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-10-07 00:00:00.000000000 Z
11
+ date: 2018-11-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: capistrano