2 # -*- coding: utf-8 -*-
4 # import.py - part of the FDroid server tools
5 # Copyright (C) 2010-13, 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
25 from ConfigParser import ConfigParser
26 import common, metadata
28 # Get the repo type and address from the given web page. The page is scanned
29 # in a rather naive manner for 'git clone xxxx', 'hg clone xxxx', etc, and
30 # when one of these is found it's assumed that's the information we want.
31 # Returns repotype, address, or None, reason
32 def getrepofrompage(url):
34 req = urllib.urlopen(url)
35 if req.getcode() != 200:
36 return (None, 'Unable to get ' + url + ' - return code ' + str(req.getcode()))
39 # Works for Google Code and BitBucket...
40 index = page.find('hg clone')
43 repo = page[index + 9:]
44 index = repo.find('<')
46 return (None, "Error while getting repo address")
48 repo = repo.split('"')[0]
49 return (repotype, repo)
51 # Works for Google Code and BitBucket...
52 index=page.find('git clone')
55 repo = page[index + 10:]
56 index = repo.find('<')
58 return (None, "Error while getting repo address")
60 repo = repo.split('"')[0]
61 return (repotype, repo)
64 index=page.find('svn checkout')
67 repo = page[index + 13:]
68 prefix = '<strong><em>http</em></strong>'
69 if not repo.startswith(prefix):
70 return (None, "Unexpected checkout instructions format")
71 repo = 'http' + repo[len(prefix):]
72 index = repo.find('<')
74 return (None, "Error while getting repo address - no end tag? '" + repo + "'")
77 index = repo.find(' ')
79 return (None, "Error while getting repo address - no space? '" + repo + "'")
81 repo = repo.split('"')[0]
82 return (repotype, repo)
84 return (None, "No information found." + page)
91 global config, options
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 parser.add_option("--rev", default=None,
102 help="Allows a different revision (or git branch) to be specified for the initial import")
103 (options, args) = parser.parse_args()
105 config = common.read_config(options)
108 print "Specify project url."
113 if not os.path.isdir(tmp_dir):
114 print "Creating temporary directory"
118 apps = metadata.read_metadata()
120 # Figure out what kind of project it is...
124 website = url #by default, we might override it
125 if url.startswith('git://'):
131 elif url.startswith('https://github.com'):
132 if url.endswith('/'):
134 if url.endswith('.git'):
135 print "A github URL should point to the project, not the git repo"
137 projecttype = 'github'
141 issuetracker = url + '/issues'
142 elif url.startswith('https://gitorious.org/'):
143 projecttype = 'gitorious'
144 repo = 'https://git.gitorious.org/' + url[22:] + '.git'
147 elif url.startswith('https://bitbucket.org/'):
148 if url.endswith('/'):
150 projecttype = 'bitbucket'
151 sourcecode = url + '/src'
152 issuetracker = url + '/issues'
153 # Figure out the repo type and adddress...
154 repotype, repo = getrepofrompage(sourcecode)
156 print "Unable to determine vcs type. " + repo
158 elif url.startswith('http://code.google.com/p/'):
159 if not url.endswith('/'):
161 projecttype = 'googlecode'
162 sourcecode = url + 'source/checkout'
164 sourcecode += "?repo=" + options.repo
165 issuetracker = url + 'issues/list'
167 # Figure out the repo type and adddress...
168 repotype, repo = getrepofrompage(sourcecode)
170 print "Unable to determine vcs type. " + repo
173 # Figure out the license...
174 req = urllib.urlopen(url)
175 if req.getcode() != 200:
176 print 'Unable to find project page at ' + sourcecode + ' - return code ' + str(req.getcode())
179 index = page.find('Code license')
181 print "Couldn't find license data"
184 lprefix = 'rel="nofollow">'
185 index = ltext.find(lprefix)
187 print "Couldn't find license text"
189 ltext = ltext[index + len(lprefix):]
190 index = ltext.find('<')
192 print "License text not formatted as expected"
194 ltext = ltext[:index]
195 if ltext == 'GNU GPL v3':
197 elif ltext == 'GNU GPL v2':
199 elif ltext == 'Apache License 2.0':
201 elif ltext == 'MIT License':
203 elif ltext == 'GNU Lesser GPL':
205 elif ltext == 'Mozilla Public License 1.1':
207 elif ltext == 'New BSD License':
210 print "License " + ltext + " is not recognised"
214 print "Unable to determine the project type."
215 print "The URL you supplied was not in one of the supported formats. Please consult"
216 print "the manual for a list of supported formats, and supply one of those."
219 # Get a copy of the source so we can extract some info...
220 print 'Getting source from ' + repotype + ' repo at ' + repo
221 src_dir = os.path.join(tmp_dir, 'importer')
222 if os.path.exists(src_dir):
223 shutil.rmtree(src_dir)
224 vcs = common.getvcs(repotype, repo, src_dir)
225 vcs.gotorevision(options.rev)
227 root_dir = os.path.join(src_dir, options.subdir)
231 # Extract some information...
232 paths = common.manifest_paths(root_dir, None)
235 version, vercode, package = common.parse_androidmanifests(paths)
237 print "Couldn't find package ID"
240 print "WARNING: Couldn't find latest version name"
242 print "WARNING: Couldn't find latest version code"
244 spec = os.path.join(root_dir, 'buildozer.spec')
245 if os.path.exists(spec):
246 defaults = {'orientation': 'landscape', 'icon': '',
247 'permissions': '', 'android.api': "18"}
248 bconfig = ConfigParser(defaults, allow_no_value=True)
250 package = bconfig.get('app', 'package.domain') + '.' + bconfig.get('app', 'package.name')
251 version = bconfig.get('app', 'version')
254 print "No android or kivy project could be found. Specify --subdir?"
257 # Make sure it's actually new...
259 if app['id'] == package:
260 print "Package " + package + " already exists"
263 # Construct the metadata...
264 app = metadata.parse_metadata(None)
266 app['Web Site'] = website
267 app['Source Code'] = sourcecode
269 app['Issue Tracker'] = issuetracker
271 app['License'] = license
272 app['Repo Type'] = repotype
274 app['Update Check Mode'] = "Tags"
276 # Create a build line...
278 build['version'] = version if version else '?'
279 build['vercode'] = vercode if vercode else '?'
280 build['commit'] = '?'
281 build['disable'] = 'Generated by import.py - check/set version fields and commit id'
283 build['subdir'] = options.subdir
284 if os.path.exists(os.path.join(root_dir, 'jni')):
285 build['buildjni'] = 'yes'
286 app['builds'].append(build)
288 # Keep the repo directory to save bandwidth...
289 if not os.path.exists('build'):
291 shutil.move(src_dir, os.path.join('build', package))
292 with open('build/.fdroidvcs-' + package, 'w') as f:
293 f.write(repotype + ' ' + repo)
295 metafile = os.path.join('metadata', package + '.txt')
296 metadata.write_metadata(metafile, app)
297 print "Wrote " + metafile
300 if __name__ == "__main__":