2 # -*- coding: utf-8 -*-
4 # import.py - part of the FDroid server tools
5 # Copyright (C) 2010-13, Ciaran Gultnieks, ciaran@ciarang.com
6 # Copyright (C) 2013-2014 Daniel Martà <mvdan@mvdan.cc>
8 # This program is free software: you can redistribute it and/or modify
9 # it under the terms of the GNU Affero General Public License as published by
10 # the Free Software Foundation, either version 3 of the License, or
11 # (at your option) any later version.
13 # This program is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 # GNU Affero General Public License for more details.
18 # You should have received a copy of the GNU Affero General Public License
19 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 from optparse import OptionParser
26 from ConfigParser import ConfigParser
27 import common, metadata
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):
35 req = urllib.urlopen(url)
36 if req.getcode() != 200:
37 return (None, 'Unable to get ' + url + ' - return code ' + str(req.getcode()))
40 # Works for Google Code and BitBucket...
41 index = page.find('hg clone')
44 repo = page[index + 9:]
45 index = repo.find('<')
47 return (None, "Error while getting repo address")
49 repo = repo.split('"')[0]
50 return (repotype, repo)
52 # Works for Google Code and BitBucket...
53 index=page.find('git clone')
56 repo = page[index + 10:]
57 index = repo.find('<')
59 return (None, "Error while getting repo address")
61 repo = repo.split('"')[0]
62 return (repotype, repo)
65 index=page.find('svn checkout')
68 repo = page[index + 13:]
69 prefix = '<strong><em>http</em></strong>'
70 if not repo.startswith(prefix):
71 return (None, "Unexpected checkout instructions format")
72 repo = 'http' + repo[len(prefix):]
73 index = repo.find('<')
75 return (None, "Error while getting repo address - no end tag? '" + repo + "'")
78 index = repo.find(' ')
80 return (None, "Error while getting repo address - no space? '" + repo + "'")
82 repo = repo.split('"')[0]
83 return (repotype, repo)
85 return (None, "No information found." + page)
92 global config, options
94 # Parse command line...
95 parser = OptionParser()
96 parser.add_option("-u", "--url", default=None,
97 help="Project URL to import from.")
98 parser.add_option("-s", "--subdir", default=None,
99 help="Path to main android project subdirectory, if not in root.")
100 parser.add_option("-r", "--repo", default=None,
101 help="Allows a different repo to be specified for a multi-repo google code project")
102 parser.add_option("--rev", default=None,
103 help="Allows a different revision (or git branch) to be specified for the initial import")
104 (options, args) = parser.parse_args()
106 config = common.read_config(options)
109 print "Specify project url."
114 if not os.path.isdir(tmp_dir):
115 print "Creating temporary directory"
119 apps = metadata.read_metadata()
121 # Figure out what kind of project it is...
125 website = url #by default, we might override it
126 if url.startswith('git://'):
132 elif url.startswith('https://github.com'):
133 if url.endswith('/'):
135 if url.endswith('.git'):
136 print "A github URL should point to the project, not the git repo"
138 projecttype = 'github'
142 issuetracker = url + '/issues'
143 elif url.startswith('https://gitorious.org/'):
144 projecttype = 'gitorious'
145 repo = 'https://git.gitorious.org/' + url[22:] + '.git'
148 elif url.startswith('https://bitbucket.org/'):
149 if url.endswith('/'):
151 projecttype = 'bitbucket'
152 sourcecode = url + '/src'
153 issuetracker = url + '/issues'
154 # Figure out the repo type and adddress...
155 repotype, repo = getrepofrompage(sourcecode)
157 print "Unable to determine vcs type. " + repo
159 elif url.startswith('http://code.google.com/p/'):
160 if not url.endswith('/'):
162 projecttype = 'googlecode'
163 sourcecode = url + 'source/checkout'
165 sourcecode += "?repo=" + options.repo
166 issuetracker = url + 'issues/list'
168 # Figure out the repo type and adddress...
169 repotype, repo = getrepofrompage(sourcecode)
171 print "Unable to determine vcs type. " + repo
174 # Figure out the license...
175 req = urllib.urlopen(url)
176 if req.getcode() != 200:
177 print 'Unable to find project page at ' + sourcecode + ' - return code ' + str(req.getcode())
180 index = page.find('Code license')
182 print "Couldn't find license data"
185 lprefix = 'rel="nofollow">'
186 index = ltext.find(lprefix)
188 print "Couldn't find license text"
190 ltext = ltext[index + len(lprefix):]
191 index = ltext.find('<')
193 print "License text not formatted as expected"
195 ltext = ltext[:index]
196 if ltext == 'GNU GPL v3':
198 elif ltext == 'GNU GPL v2':
200 elif ltext == 'Apache License 2.0':
202 elif ltext == 'MIT License':
204 elif ltext == 'GNU Lesser GPL':
206 elif ltext == 'Mozilla Public License 1.1':
208 elif ltext == 'New BSD License':
211 print "License " + ltext + " is not recognised"
215 print "Unable to determine the project type."
216 print "The URL you supplied was not in one of the supported formats. Please consult"
217 print "the manual for a list of supported formats, and supply one of those."
220 # Get a copy of the source so we can extract some info...
221 print 'Getting source from ' + repotype + ' repo at ' + repo
222 src_dir = os.path.join(tmp_dir, 'importer')
223 if os.path.exists(src_dir):
224 shutil.rmtree(src_dir)
225 vcs = common.getvcs(repotype, repo, src_dir)
226 vcs.gotorevision(options.rev)
228 root_dir = os.path.join(src_dir, options.subdir)
232 # Extract some information...
233 paths = common.manifest_paths(root_dir, None)
236 version, vercode, package = common.parse_androidmanifests(paths)
238 print "Couldn't find package ID"
241 print "WARNING: Couldn't find latest version name"
243 print "WARNING: Couldn't find latest version code"
245 spec = os.path.join(root_dir, 'buildozer.spec')
246 if os.path.exists(spec):
247 defaults = {'orientation': 'landscape', 'icon': '',
248 'permissions': '', 'android.api': "18"}
249 bconfig = ConfigParser(defaults, allow_no_value=True)
251 package = bconfig.get('app', 'package.domain') + '.' + bconfig.get('app', 'package.name')
252 version = bconfig.get('app', 'version')
255 print "No android or kivy project could be found. Specify --subdir?"
258 # Make sure it's actually new...
260 if app['id'] == package:
261 print "Package " + package + " already exists"
264 # Construct the metadata...
265 app = metadata.parse_metadata(None)
267 app['Web Site'] = website
268 app['Source Code'] = sourcecode
270 app['Issue Tracker'] = issuetracker
272 app['License'] = license
273 app['Repo Type'] = repotype
275 app['Update Check Mode'] = "Tags"
277 # Create a build line...
279 build['version'] = version if version else '?'
280 build['vercode'] = vercode if vercode else '?'
281 build['commit'] = '?'
282 build['disable'] = 'Generated by import.py - check/set version fields and commit id'
284 build['subdir'] = options.subdir
285 if os.path.exists(os.path.join(root_dir, 'jni')):
286 build['buildjni'] = 'yes'
287 app['builds'].append(build)
289 # Keep the repo directory to save bandwidth...
290 if not os.path.exists('build'):
292 shutil.move(src_dir, os.path.join('build', package))
293 with open('build/.fdroidvcs-' + package, 'w') as f:
294 f.write(repotype + ' ' + repo)
296 metafile = os.path.join('metadata', package + '.txt')
297 metadata.write_metadata(metafile, app)
298 print "Wrote " + metafile
301 if __name__ == "__main__":