1 # Automatic source code provision (AGPL compliance)
6 class SourceShipmentPreparer():
7 def __init__(s
, destdir
):
8 # caller may modify, and should read after calling generate()
9 s
.output_name
= 'srcbomb.tar.gz'
10 # defaults, caller can modify after creation
11 s
.src_filter
= s
.src_filter_glob
12 s
.src_filter_globs
= ['/usr/local/*', '!/usr*', '!/etc/*']
13 s
.src_likeparent
= s
.src_likeparent_git
15 s
.excludes
= ['*~', '*.bak', '*.tmp', '#*#']
16 s
.rune_shell
= ['/bin/bash', '-ec']
21 # ^ by default, is find ... -print0
23 cpio -Hustar -o --quiet -0 -R 1000:1000 || \
24 cpio -Hustar -o --quiet -0
27 s
.rune_portmanteau
= r
'''
30 GZIP=-9 tar zcf "$outfile" "$@"'
32 s
.manifest_name
='0000-MANIFEST.txt'
38 def src_filter_glob(s
, src
): # default s.src_filter
39 for pat
in s
.src_filter_globs
:
40 negate
= pat
.startswith('!')
41 if negate
: pat
= pat
[1:]
42 if fnmatch
.fnmatch(src
, pat
):
46 def src_likeparent_git(s
, src
):
48 stat(os
.path
.join(d
, '.git/.'))
49 except FileNotFoundError
:
54 def src_parentfinder(s
, src
, infol
): # callers may monkey-patch away
55 for deref
in (False,True):
60 search
= os
.path
.realpath(search
)
63 xinfo
.append(os
.path
.basename(search
))
64 search
= os
.path
.dirname(search
)
68 except FileNotFoundError
:
70 if stat
.S_ISREG(stab
.st_mode
):
73 while not os
.path
.ismount(search
):
74 if s
.src_likeparent(search
):
76 infol
.append(os
.path
.join(*xinfo
))
81 # no .git found anywhere
84 def src_prenormaliser(s
, d
): # callers may monkey-patch away
85 return os
.path
.join(s
.cwd
, os
.path
.abspath(d
))
87 def src_find_rune(s
, d
):
88 script
= 'find -type f -perm +004'
89 for excl
in s
.excludes
:
90 assert("'" not in excl
)
91 script
+= r
" \! -name '%s'" % excl
94 def new_output_name(s
, nametail
, infol
):
95 name
= '%04d-%s' %
(s
._outcounter
++, nametail
)
96 s
._manifest
.append((name
, infol
.join(' '))
99 def open_output_fh(s
, name
, mode
):
100 return open(os
.path
.join(s
._destdir
, name
), mode
)
102 def new_output_fh(s
, nametail
, infol
):
103 name
= new_output_name(s
, nametail
, infol
)
104 return open_output_fh(name
, 'wb')
106 def mk_from_dir(s
, d
):
107 find_rune
= s
.src_find_rune(s
, d
)
108 total_rune
= s
.rune_cpio % find_rune
109 fh
= new_output_fh('src.cpio')
110 subprocess
.run(s
.rune_shell
+ [total_rune
],
112 stdin
=subprocess
.DEVNULL
,
114 restore_signals
=True)
117 def mk_from_src(s
, d
, infol
):
118 d
= s
.src_prenormaliser(d
, infol
)
119 if not s
.src_filter(d
): return
120 d
= s
.src_parentfinder(d
, infol
)
121 s
.mk_from_dir(d
, infol
)
123 def mk_from_srcs(s
, dirs
=sys
.path
):
124 s
.mk_from_src(sys
.argv
[0], ['argv[0]'])
126 s
.mk_from_src(d
, ['sys.path'])
128 def mk_portmanteau(s
):]
129 cmdl
= s
.rune_shell
+ [ s
.rune_portmanteau
, 'x',
130 s
.output_name
, s
.manifest_name
]
131 mfh
= open_output_fh(s
.manifest_name
,'w')
132 for (name
, info
) in s
._manifest
:
134 print('%s\t%s\n' %
(name
,info
), file=mfh
)
136 subprocess
.run(s
.rune_shell
+ cmdl
,
138 stdin
=subprocess
.DEVNULL
,
140 restore_signals
=True)