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
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 get ' + url + ' - 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)
90 global config, options
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 parser.add_option("--rev", default=None,
101 help="Allows a different revision (or git branch) to be specified for the initial import")
102 (options, args) = parser.parse_args()
104 config = common.read_config(options)
107 print "Specify project url."
112 if not os.path.isdir(tmp_dir):
113 print "Creating temporary directory"
117 apps = common.read_metadata()
119 # Figure out what kind of project it is...
123 website = url #by default, we might override it
124 if url.startswith('git://'):
130 elif url.startswith('https://github.com'):
131 if url.endswith('/'):
133 if url.endswith('.git'):
134 print "A github URL should point to the project, not the git repo"
136 projecttype = 'github'
140 issuetracker = url + '/issues'
141 elif url.startswith('https://gitorious.org/'):
142 projecttype = 'gitorious'
143 repo = 'https://git.gitorious.org/' + url[22:] + '.git'
146 elif url.startswith('https://bitbucket.org/'):
147 if url.endswith('/'):
149 projecttype = 'bitbucket'
150 sourcecode = url + '/src'
151 issuetracker = url + '/issues'
152 # Figure out the repo type and adddress...
153 repotype, repo = getrepofrompage(sourcecode)
155 print "Unable to determine vcs type. " + repo
157 elif url.startswith('http://code.google.com/p/'):
158 if not url.endswith('/'):
160 projecttype = 'googlecode'
161 sourcecode = url + 'source/checkout'
163 sourcecode += "?repo=" + options.repo
164 issuetracker = url + 'issues/list'
166 # Figure out the repo type and adddress...
167 repotype, repo = getrepofrompage(sourcecode)
169 print "Unable to determine vcs type. " + repo
172 # Figure out the license...
173 req = urllib.urlopen(url)
174 if req.getcode() != 200:
175 print 'Unable to find project page at ' + sourcecode + ' - return code ' + str(req.getcode())
178 index = page.find('Code license')
180 print "Couldn't find license data"
183 lprefix = 'rel="nofollow">'
184 index = ltext.find(lprefix)
186 print "Couldn't find license text"
188 ltext = ltext[index + len(lprefix):]
189 index = ltext.find('<')
191 print "License text not formatted as expected"
193 ltext = ltext[:index]
194 if ltext == 'GNU GPL v3':
196 elif ltext == 'GNU GPL v2':
198 elif ltext == 'Apache License 2.0':
200 elif ltext == 'MIT License':
202 elif ltext == 'GNU Lesser GPL':
204 elif ltext == 'Mozilla Public License 1.1':
206 elif ltext == 'New BSD License':
209 print "License " + ltext + " is not recognised"
213 print "Unable to determine the project type."
214 print "The URL you supplied was not in one of the supported formats. Please consult"
215 print "the manual for a list of supported formats, and supply one of those."
218 # Get a copy of the source so we can extract some info...
219 print 'Getting source from ' + repotype + ' repo at ' + repo
220 src_dir = os.path.join(tmp_dir, 'importer')
221 if os.path.exists(src_dir):
222 shutil.rmtree(src_dir)
223 vcs = common.getvcs(repotype, repo, src_dir)
224 vcs.gotorevision(options.rev)
226 root_dir = os.path.join(src_dir, options.subdir)
230 # Extract some information...
231 paths = common.manifest_paths(root_dir, None)
233 print "No android project could be found. Specify --subdir?"
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 # 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
262 app['Update Check Mode'] = "Tags"
264 # Create a build line...
266 build['version'] = version if version else '?'
267 build['vercode'] = vercode if vercode else '?'
268 build['commit'] = '?'
269 build['disable'] = 'Generated by import.py - check/set version fields and commit id'
271 build['subdir'] = options.subdir
272 if os.path.exists(os.path.join(root_dir, 'jni')):
273 build['buildjni'] = 'yes'
274 app['builds'].append(build)
276 # Keep the repo directory to save bandwidth...
277 if not os.path.exists('build'):
279 shutil.move(src_dir, os.path.join('build', package))
280 with open('build/.fdroidvcs-' + package, 'w') as f:
281 f.write(repotype + ' ' + repo)
283 metafile = os.path.join('metadata', package + '.txt')
284 common.write_metadata(metafile, app)
285 print "Wrote " + metafile
288 if __name__ == "__main__":