Enable BytesWarnings.
[portage.git] / bin / binhost-snapshot
1 #!/usr/bin/python -bb
2 # Copyright 2010-2014 Gentoo Foundation
3 # Distributed under the terms of the GNU General Public License v2
4
5 import io
6 import os
7 import sys
8 import textwrap
9
10 try:
11         from urllib.parse import urlparse
12 except ImportError:
13         from urlparse import urlparse
14
15 from os import path as osp
16 pym_path = osp.join(osp.dirname(osp.dirname(osp.realpath(__file__))), "pym")
17 sys.path.insert(0, pym_path)
18 import portage
19 portage._internal_caller = True
20 from portage.util._argparse import ArgumentParser
21
22 def parse_args(argv):
23         prog_name = os.path.basename(argv[0])
24         usage = prog_name + ' [options] ' + \
25                 '<src_pkg_dir> <snapshot_dir> <snapshot_uri> <binhost_dir>'
26
27         prog_desc = "This program will copy src_pkg_dir to snapshot_dir " + \
28                 "and inside binhost_dir it will create a Packages index file " + \
29                 "which refers to snapshot_uri. This is intended to solve race " + \
30                 "conditions on binhosts as described at http://crosbug.com/3225."
31
32         usage += "\n\n"
33         for line in textwrap.wrap(prog_desc, 70):
34                 usage += line + "\n"
35
36         usage += "\n"
37         usage += "Required Arguments:\n\n"
38         usage += "  src_pkg_dir  - the source $PKGDIR\n"
39         usage += "  snapshot_dir - destination snapshot " + \
40                 "directory (must not exist)\n"
41         usage += "  snapshot_uri - URI which refers to " + \
42                 "snapshot_dir from the\n" + \
43                 "                 client side\n"
44         usage += "  binhost_dir  - directory in which to " + \
45                 "write Packages index with\n" + \
46                 "                 snapshot_uri"
47
48         parser = ArgumentParser(usage=usage)
49         parser.add_argument('--hardlinks',
50                 help='create hardlinks (y or n, default is y)',
51                 choices=('y', 'n'),
52                 default='y')
53         options, args = parser.parse_known_args(argv[1:])
54
55         if len(args) != 4:
56                 parser.error("Required 4 arguments, got %d" % (len(args),))
57
58         return parser, options, args
59
60 def main(argv):
61         parser, options, args = parse_args(argv)
62
63         src_pkg_dir, snapshot_dir, snapshot_uri, binhost_dir = args
64         src_pkgs_index = os.path.join(src_pkg_dir, 'Packages')
65
66         if not os.path.isdir(src_pkg_dir):
67                 parser.error("src_pkg_dir is not a directory: '%s'" % (src_pkg_dir,))
68
69         if not os.path.isfile(src_pkgs_index):
70                 parser.error("src_pkg_dir does not contain a " + \
71                         "'Packages' index: '%s'" % (src_pkg_dir,))
72
73         parse_result = urlparse(snapshot_uri)
74         if not (parse_result.scheme and parse_result.netloc and parse_result.path):
75                 parser.error("snapshot_uri is not a valid URI: '%s'" % (snapshot_uri,))
76
77         if os.path.isdir(snapshot_dir):
78                 parser.error("snapshot_dir already exists: '%s'" % snapshot_dir)
79
80         try:
81                 os.makedirs(os.path.dirname(snapshot_dir))
82         except OSError:
83                 pass
84         if not os.path.isdir(os.path.dirname(snapshot_dir)):
85                 parser.error("snapshot_dir parent could not be created: '%s'" % \
86                         os.path.dirname(snapshot_dir))
87
88         try:
89                 os.makedirs(binhost_dir)
90         except OSError:
91                 pass
92         if not os.path.isdir(binhost_dir):
93                 parser.error("binhost_dir could not be created: '%s'" % binhost_dir)
94
95         cp_opts = 'RP'
96         if options.hardlinks == 'n':
97                 cp_opts += 'p'
98         else:
99                 cp_opts += 'l'
100
101         cp_cmd = 'cp -%s %s %s' % (
102                 cp_opts,
103                 portage._shell_quote(src_pkg_dir),
104                 portage._shell_quote(snapshot_dir)
105         )
106
107         ret = os.system(cp_cmd)
108         if not (os.WIFEXITED(ret) and os.WEXITSTATUS(ret) == os.EX_OK):
109                 return 1
110
111         infile = io.open(portage._unicode_encode(src_pkgs_index,
112                 encoding=portage._encodings['fs'], errors='strict'),
113                 mode='r', encoding=portage._encodings['repo.content'],
114                 errors='strict')
115
116         outfile = portage.util.atomic_ofstream(
117                 os.path.join(binhost_dir, "Packages"),
118                 encoding=portage._encodings['repo.content'],
119                 errors='strict')
120
121         for line in infile:
122                 if line[:4] == 'URI:':
123                         # skip existing URI line
124                         pass
125                 else:
126                         if not line.strip():
127                                 # end of header
128                                 outfile.write("URI: %s\n\n" % snapshot_uri)
129                                 break
130                         outfile.write(line)
131
132         for line in infile:
133                 outfile.write(line)
134
135         infile.close()
136         outfile.close()
137
138         return os.EX_OK
139
140 if __name__ == "__main__":
141         sys.exit(main(sys.argv))