2 # -*- coding: utf-8 -*-
4 # import.py - part of the FDroid server tools
5 # Copyright (C) 2010-12, Ciaran Gultnieks, ciaran@ciarang.com
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.
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.
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/>.
24 from optparse import OptionParser
27 # Get the repo type and address from the given web page. The page is scanned
28 # in a rather naive manner for 'git clone xxxx', 'hg clone xxxx', etc, and
29 # when one of these is found it's assumed that's the information we want.
30 # Returns repotype, address, or None, reason
31 def getrepofrompage(url):
33 req = urllib.urlopen(url)
34 if req.getcode() != 200:
35 return (None, 'Unable to find source at ' + sourcecode + ' - return code ' + str(req.getcode()))
38 # Works for Google Code and BitBucket...
39 index = page.find('hg clone')
42 repo = page[index + 9:]
43 index = repo.find('<')
45 return (None, "Error while getting repo address")
47 repo = repo.split('"')[0]
48 return (repotype, repo)
50 # Works for Google Code and BitBucket...
51 index=page.find('git clone')
54 repo = page[index + 10:]
55 index = repo.find('<')
57 return (None, "Error while getting repo address")
59 repo = repo.split('"')[0]
60 return (repotype, repo)
63 index=page.find('svn checkout')
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('<')
73 return (None, "Error while getting repo address - no end tag? '" + repo + "'")
76 index = repo.find(' ')
78 return (None, "Error while getting repo address - no space? '" + repo + "'")
80 repo = repo.split('"')[0]
81 return (repotype, repo)
83 return (None, "No information found." + page)
88 # Read configuration...
89 execfile('config.py', globals())
93 # Parse command line...
94 parser = OptionParser()
95 parser.add_option("-u", "--url", default=None,
96 help="Project URL to import from.")
97 parser.add_option("-s", "--subdir", default=None,
98 help="Path to main android project subdirectory, if not in root.")
99 parser.add_option("-r", "--repo", default=None,
100 help="Allows a different repo to be specified for a multi-repo google code project")
101 (options, args) = parser.parse_args()
104 print "Specify project url."
109 if not os.path.isdir(tmp_dir):
110 print "Creating temporary directory"
114 apps = common.read_metadata()
116 # Figure out what kind of project it is...
120 website = url #by default, we might override it
121 if url.startswith('git://'):
127 elif url.startswith('https://github.com'):
128 if url.endswith('/'):
130 if url.endswith('.git'):
131 print "A github URL should point to the project, not the git repo"
133 projecttype = 'github'
137 issuetracker = url + '/issues'
138 elif url.startswith('https://gitorious.org/'):
139 projecttype = 'gitorious'
140 repo = 'https://git.gitorious.org/' + url[22:] + '.git'
143 elif url.startswith('https://bitbucket.org/'):
144 if url.endswith('/'):
146 projecttype = 'bitbucket'
147 sourcecode = url + '/src'
148 issuetracker = url + '/issues'
149 # Figure out the repo type and adddress...
150 repotype, repo = getrepofrompage(sourcecode)
152 print "Unable to determine vcs type. " + repo
154 elif url.startswith('http://code.google.com/p/'):
155 if not url.endswith('/'):
157 projecttype = 'googlecode'
158 sourcecode = url + 'source/checkout'
160 sourcecode += "?repo=" + options.repo
161 issuetracker = url + 'issues/list'
163 # Figure out the repo type and adddress...
164 repotype, repo = getrepofrompage(sourcecode)
166 print "Unable to determine vcs type. " + repo
169 # Figure out the license...
170 req = urllib.urlopen(url)
171 if req.getcode() != 200:
172 print 'Unable to find project page at ' + sourcecode + ' - return code ' + str(req.getcode())
175 index = page.find('Code license')
177 print "Couldn't find license data"
180 lprefix = 'rel="nofollow">'
181 index = ltext.find(lprefix)
183 print "Couldn't find license text"
185 ltext = ltext[index + len(lprefix):]
186 index = ltext.find('<')
188 print "License text not formatted as expected"
190 ltext = ltext[:index]
191 if ltext == 'GNU GPL v3':
193 elif ltext == 'GNU GPL v2':
195 elif ltext == 'Apache License 2.0':
197 elif ltext == 'MIT License':
199 elif ltext == 'GNU Lesser GPL':
201 elif ltext == 'Mozilla Public License 1.1':
203 elif ltext == 'New BSD License':
206 print "License " + ltext + " is not recognised"
210 print "Unable to determine the project type."
211 print "The URL you supplied was not in one of the supported formats. Please consult"
212 print "the manual for a list of supported formats, and supply one of those."
215 # Get a copy of the source so we can extract some info...
216 print 'Getting source from ' + repotype + ' repo at ' + repo
217 src_dir = os.path.join(tmp_dir, 'importer')
218 if os.path.exists(src_dir):
219 shutil.rmtree(src_dir)
220 vcs = common.getvcs(repotype, repo, src_dir, sdk_path)
221 vcs.gotorevision(None)
223 root_dir = os.path.join(src_dir, options.subdir)
227 # Check AndroidManiifest.xml exists...
228 if not os.path.exists(root_dir + '/AndroidManifest.xml'):
229 print "AndroidManifest.xml did not exist in the expected location. Specify --subdir?"
232 # Extract some information...
233 paths = common.manifest_paths(root_dir, None)
234 version, vercode, package = common.parse_androidmanifests(paths)
236 print "Couldn't find package ID"
239 print "Couldn't find latest version name"
242 print "Couldn't find latest version code"
245 # Make sure it's actually new...
247 if app['id'] == package:
248 print "Package " + package + " already exists"
251 # Construct the metadata...
252 app = common.parse_metadata(None)
254 app['Web Site'] = website
255 app['Source Code'] = sourcecode
257 app['Issue Tracker'] = issuetracker
259 app['License'] = license
260 app['Repo Type'] = repotype
263 # Create a build line...
265 build['version'] = version
266 build['vercode'] = vercode
267 build['subvercode'] = None
268 build['commit'] = '?'
270 build['subdir'] = options.subdir
271 if os.path.exists(os.path.join(root_dir, 'jni')):
272 build['buildjni'] = 'yes'
273 app['builds'].append(build)
274 app['comments'].append(('build:' + version,
275 "#Generated by import.py - check this is the right version, and find the right commit!"))
277 # Keep the repo directory to save bandwidth...
278 if not os.path.exists('build'):
280 shutil.move(src_dir, os.path.join('build', package))
281 with open('build/.fdroidvcs-' + package, 'w') as f:
282 f.write(repotype + ' ' + repo)
284 metafile = os.path.join('metadata', package + '.txt')
285 common.write_metadata(metafile, app)
286 print "Wrote " + metafile
289 if __name__ == "__main__":