capistrano-git-copy 1.5.2 → 1.5.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|