chiark / gitweb /
Yet another silly mistake
[fdroidserver.git] / fdroidserver / import.py
1 #!/usr/bin/env python
2 # -*- coding: utf-8 -*-
3 #
4 # import.py - part of the FDroid server tools
5 # Copyright (C) 2010-12, Ciaran Gultnieks, ciaran@ciarang.com
6 #
7 # This program is free software: you can redistribute it and/or modify
8 # it under the terms of the GNU Affero General Public License as published by
9 # the Free Software Foundation, either version 3 of the License, or
10 # (at your option) any later version.
11 #
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 # GNU Affero General Public License for more details.
16 #
17 # You should have received a copy of the GNU Affero General Public License
18 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
19
20 import sys
21 import os
22 import shutil
23 import subprocess
24 import re
25 import urllib
26 from optparse import OptionParser
27
28
29 # Get the repo type and address from the given web page. The page is scanned
30 # in a rather naive manner for 'git clone xxxx', 'hg clone xxxx', etc, and
31 # when one of these is found it's assumed that's the information we want.
32 # Returns repotype, address, or None, reason
33 def getrepofrompage(url):
34
35     req = urllib.urlopen(url)
36     if req.getcode() != 200:
37         return (None, 'Unable to find source at ' + sourcecode + ' - return code ' + str(req.getcode()))
38     page = req.read()
39
40     # Works for Google Code and BitBucket...
41     index = page.find('hg clone')
42     if index != -1:
43         repotype = 'hg'
44         repo = page[index + 9:]
45         index = repo.find('<')
46         if index == -1:
47             return (None, "Error while getting repo address")
48         repo = repo[:index]
49         return (repotype, repo)
50
51     # Works for Google Code and BitBucket...
52     index=page.find('git clone')
53     if index != -1:
54         repotype = 'git'
55         repo = page[index + 10:]
56         index = repo.find('<')
57         if index == -1:
58             return (None, "Error while getting repo address")
59         repo = repo[:index]
60         return (repotype, repo)
61
62     # Google Code only...
63     index=page.find('svn checkout')
64     if index != -1:
65         repotype = 'git-svn'
66         repo = page[index + 13:]
67         prefix = '<strong><em>http</em></strong>'
68         if not repo.startswith(prefix):
69             return (None, "Unexpected checkout instructions format")
70         repo = 'http' + repo[len(prefix):]
71         index = repo.find('<')
72         if index == -1:
73             return (None, "Error while getting repo address - no end tag? '" + repo + "'")
74             sys.exit(1)
75         repo = repo[:index]
76         index = repo.find(' ')
77         if index == -1:
78             return (None, "Error while getting repo address - no space? '" + repo + "'")
79         repo = repo[:index]
80         return (repotype, repo)
81
82     return (None, "No information found." + page)
83
84
85 def main():
86
87     # Read configuration...
88     execfile('config.py', globals())
89
90     import common
91
92     # Parse command line...
93     parser = OptionParser()
94     parser.add_option("-u", "--url", default=None,
95                       help="Project URL to import from.")
96     parser.add_option("-s", "--subdir", default=None,
97                       help="Path to main android project subdirectory, if not in root.")
98     parser.add_option("-r", "--repo", default=None,
99                       help="Allows a different repo to be specified for a multi-repo google code project")
100     (options, args) = parser.parse_args()
101
102     if not options.url:
103         print "Specify project url."
104         sys.exit(1)
105     url = options.url
106
107     tmp_dir = 'tmp'
108     if not os.path.isdir(tmp_dir):
109         print "Creating temporary directory"
110         os.makedirs(tmp_dir)
111
112     # Get all apps...
113     apps = common.read_metadata()
114
115     # Figure out what kind of project it is...
116     projecttype = None
117     issuetracker = None
118     license = None
119     website = url #by default, we might override it
120     if url.startswith('git://'):
121         projecttype = 'git'
122         repo = url
123         repotype = 'git'
124         sourcecode = ""
125         website = ""
126     elif url.startswith('https://github.com'):
127         if url.endswith('/'):
128             url = url[:-1]
129         if url.endswith('.git'):
130             print "A github URL should point to the project, not the git repo"
131             sys.exit(1)
132         projecttype = 'github'
133         repo = url + '.git'
134         repotype = 'git'
135         sourcecode = url
136     elif url.startswith('https://gitorious.org/'):
137         projecttype = 'gitorious'
138         repo = 'https://git.gitorious.org/' + url[22:] + '.git'
139         repotype = 'git'
140         sourcecode = url
141     elif url.startswith('https://bitbucket.org/'):
142         if url.endswith('/'):
143             url = url[:-1]
144         projecttype = 'bitbucket'
145         sourcecode = url + '/src'
146         issuetracker = url + '/issues'
147         # Figure out the repo type and adddress...
148         repotype, repo = getrepofrompage(sourcecode)
149         if not repotype:
150             print "Unable to determine vcs type. " + repo
151             sys.exit(1)
152     elif url.startswith('http://code.google.com/p/'):
153         if not url.endswith('/'):
154             url += '/';
155         projecttype = 'googlecode'
156         sourcecode = url + 'source/checkout'
157         if options.repo:
158             sourcecode += "?repo=" + options.repo
159         issuetracker = url + 'issues/list'
160
161         # Figure out the repo type and adddress...
162         repotype, repo = getrepofrompage(sourcecode)
163         if not repotype:
164             print "Unable to determine vcs type. " + repo
165             sys.exit(1)
166
167         # Figure out the license...
168         req = urllib.urlopen(url)
169         if req.getcode() != 200:
170             print 'Unable to find project page at ' + sourcecode + ' - return code ' + str(req.getcode())
171             sys.exit(1)
172         page = req.read()
173         index = page.find('Code license')
174         if index == -1:
175             print "Couldn't find license data"
176             sys.exit(1)
177         ltext = page[index:]
178         lprefix = 'rel="nofollow">'
179         index = ltext.find(lprefix)
180         if index == -1:
181             print "Couldn't find license text"
182             sys.exit(1)
183         ltext = ltext[index + len(lprefix):]
184         index = ltext.find('<')
185         if index == -1:
186             print "License text not formatted as expected"
187             sys.exit(1)
188         ltext = ltext[:index]
189         if ltext == 'GNU GPL v3':
190             license = 'GPLv3'
191         elif ltext == 'GNU GPL v2':
192             license = 'GPLv2'
193         elif ltext == 'Apache License 2.0':
194             license = 'Apache2'
195         elif ltext == 'MIT License':
196             license = 'MIT'
197         elif ltext == 'GNU Lesser GPL':
198             license = 'LGPL'
199         elif ltext == 'Mozilla Public License 1.1':
200             license = 'MPL'
201         elif ltext == 'New BSD License':
202             license = 'NewBSD'
203         else:
204             print "License " + ltext + " is not recognised"
205             sys.exit(1)
206
207     if not projecttype:
208         print "Unable to determine the project type."
209         print "The URL you supplied was not in one of the supported formats. Please consult"
210         print "the manual for a list of supported formats, and supply one of those."
211         sys.exit(1)
212
213     # Get a copy of the source so we can extract some info...
214     print 'Getting source from ' + repotype + ' repo at ' + repo
215     src_dir = os.path.join(tmp_dir, 'importer')
216     if os.path.exists(src_dir):
217         shutil.rmtree(src_dir)
218     vcs = common.getvcs(repotype, repo, src_dir, sdk_path)
219     vcs.gotorevision(None)
220     if options.subdir:
221         root_dir = os.path.join(src_dir, options.subdir)
222     else:
223         root_dir = src_dir
224
225     # Check AndroidManiifest.xml exists...
226     if not os.path.exists(root_dir + '/AndroidManifest.xml'):
227         print "AndroidManifest.xml did not exist in the expected location. Specify --subdir?"
228         sys.exit(1)
229
230     # Extract some information...
231     paths = common.manifest_paths(root_dir, None)
232     version, vercode, package = common.parse_androidmanifests(paths)
233     if not package:
234         print "Couldn't find package ID"
235         sys.exit(1)
236     if not version:
237         print "Couldn't find latest version name"
238         sys.exit(1)
239     if not vercode:
240         print "Couldn't find latest version code"
241         sys.exit(1)
242
243     # Make sure it's actually new...
244     for app in apps:
245         if app['id'] == package:
246             print "Package " + package + " already exists"
247             sys.exit(1)
248
249     # Construct the metadata...
250     app = common.parse_metadata(None)
251     app['id'] = package
252     app['Web Site'] = website
253     app['Source Code'] = sourcecode
254     if issuetracker:
255         app['Issue Tracker'] = issuetracker
256     if license:
257         app['License'] = license
258     app['Repo Type'] = repotype
259     app['Repo'] = repo
260
261     # Create a build line...
262     build = {}
263     build['version'] = version
264     build['vercode'] = vercode
265     build['commit'] = '?'
266     if options.subdir:
267         build['subdir'] = options.subdir
268     if os.path.exists(os.path.join(root_dir, 'jni')):
269         build['buildjni'] = 'yes'
270     app['builds'].append(build)
271     app['comments'].append(('build:' + version,
272         "#Generated by import.py - check this is the right version, and find the right commit!"))
273
274     # Keep the repo directory to save bandwidth...
275     if not os.path.exists('build'):
276         os.mkdir('build')
277     shutil.move(src_dir, os.path.join('build', package))
278     with open('build/.fdroidvcs-' + package, 'w') as f:
279         f.write(repotype + ' ' + repo)
280
281     metafile = os.path.join('metadata', package + '.txt')
282     common.write_metadata(metafile, app)
283     print "Wrote " + metafile
284
285
286 if __name__ == "__main__":
287     main()
288