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 +4 -4
- data/CHANGELOG.md +4 -0
- data/lib/capistrano/git_copy/version.rb +1 -1
- data/vendor/git-archive-all/git_archive_all.py +155 -52
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 100d26afc3422d8280d7ec211f0af3c9f2b2802c66aa5ffcdaa17d919de52292
|
4
|
+
data.tar.gz: 1d44966f13986f6ebd4f5c4d37a00b9fddb452ce0b82da0f8a863894bc320b40
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3b38135587cf0d941be9664bbe3573544180398fcc13e9f466815ae47b4a1adcad2317fcd8ae4376a25f1e6d9413f264804045f8f5b4a805bea53fc798b015eb
|
7
|
+
data.tar.gz: a59f7e452bc4f0a54f8262c4b08576a3337c03c797b1531e3f36ed8dac88c86f972228580a14bf7b4da656fcfb3ed5eff2fb3c93e0febb45766de7da72a044a8
|
data/CHANGELOG.md
CHANGED
@@ -32,7 +32,7 @@ from subprocess import CalledProcessError, Popen, PIPE
|
|
32
32
|
import sys
|
33
33
|
import re
|
34
34
|
|
35
|
-
__version__ = "1.
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
199
|
-
|
200
|
-
|
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
|
-
|
239
|
-
|
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
|
-
|
244
|
-
|
245
|
-
|
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
|
-
|
248
|
-
|
249
|
-
|
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
|
-
|
252
|
-
|
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
|
-
|
264
|
+
if self.is_file_excluded(repo_abspath, repo_file_path):
|
265
|
+
continue
|
255
266
|
|
256
|
-
|
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
|
-
|
261
|
-
|
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
|
-
|
264
|
-
|
273
|
+
try:
|
274
|
+
repo_gitmodules_abspath = path.join(repo_abspath, ".gitmodules")
|
265
275
|
|
266
|
-
|
267
|
-
|
276
|
+
with open(repo_gitmodules_abspath) as f:
|
277
|
+
lines = f.readlines()
|
268
278
|
|
269
|
-
|
270
|
-
|
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
|
274
|
-
|
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
|
-
|
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
|
-
|
282
|
-
|
283
|
-
|
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
|
-
@
|
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 =
|
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
|
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.
|
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-
|
11
|
+
date: 2018-11-02 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: capistrano
|