be857e353fc298639228698131e9ec8714fe6669
[tools/git-buildpackage.git] / gbp / scripts / import_orig.py
1 # vim: set fileencoding=utf-8 :
2 #
3 # (C) 2006, 2007, 2009, 2011 Guido Guenther <agx@sigxcpu.org>
4 #    This program is free software; you can redistribute it and/or modify
5 #    it under the terms of the GNU General Public License as published by
6 #    the Free Software Foundation; either version 2 of the License, or
7 #    (at your option) any later version.
8 #
9 #    This program is distributed in the hope that it will be useful,
10 #    but WITHOUT ANY WARRANTY; without even the implied warranty of
11 #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 #    GNU General Public License for more details.
13 #
14 #    You should have received a copy of the GNU General Public License
15 #    along with this program; if not, write to the Free Software
16 #    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17 #
18 """Import a new upstream version into a GIT repository"""
19
20 import ConfigParser
21 import os
22 import sys
23 import gbp.tmpfile as tempfile
24 import gbp.command_wrappers as gbpc
25 from gbp.deb import (DebianPkgPolicy, parse_changelog_repo)
26 from gbp.deb.upstreamsource import DebianUpstreamSource
27 from gbp.deb.uscan import (Uscan, UscanError)
28 from gbp.deb.changelog import ChangeLog, NoChangeLogError
29 from gbp.deb.git import (GitRepositoryError, DebianGitRepository)
30 from gbp.config import GbpOptionParserDebian, GbpOptionGroup, no_upstream_branch_msg
31 from gbp.errors import GbpError
32 from gbp.format import format_msg
33 import gbp.log
34 from gbp.pkg import compressor_opts
35 from gbp.scripts.common.import_orig import (cleanup_tmp_tree, ask_package_name,
36                                             ask_package_version,
37                                             prepare_sources)
38
39
40 def upstream_import_commit_msg(options, version):
41     return options.import_msg % dict(version=version)
42
43
44 def detect_name_and_version(repo, source, options):
45     # Guess defaults for the package name and version from the
46     # original tarball.
47     guessed_package, guessed_version = source.guess_version()
48
49     # Try to find the source package name
50     try:
51         cp = ChangeLog(filename='debian/changelog')
52         sourcepackage = cp['Source']
53     except NoChangeLogError:
54         try:
55             # Check the changelog file from the repository, in case
56             # we're not on the debian-branch (but upstream, for
57             # example).
58             cp = parse_changelog_repo(repo, options.packaging_branch, 'debian/changelog')
59             sourcepackage = cp['Source']
60         except NoChangeLogError:
61             if options.interactive:
62                 sourcepackage = ask_package_name(guessed_package,
63                                                  DebianPkgPolicy.is_valid_packagename,
64                                                  DebianPkgPolicy.packagename_msg)
65             else:
66                 if guessed_package:
67                     sourcepackage = guessed_package
68                 else:
69                     raise GbpError("Couldn't determine upstream package name. Use --interactive.")
70
71     # Try to find the version.
72     if options.version:
73         version = options.version
74     else:
75         if options.interactive:
76             version = ask_package_version(guessed_version,
77                                           DebianPkgPolicy.is_valid_upstreamversion,
78                                           DebianPkgPolicy.upstreamversion_msg)
79         else:
80             if guessed_version:
81                 version = guessed_version
82             else:
83                 raise GbpError("Couldn't determine upstream version. Use '-u<version>' or --interactive.")
84
85     return (sourcepackage, version)
86
87
88 def find_source(use_uscan, args):
89     """Find the tarball to import - either via uscan or via command line argument
90     @return: upstream source filename or None if nothing to import
91     @rtype: string
92     @raise GbpError: raised on all detected errors
93     """
94     if use_uscan:
95         if args:
96             raise GbpError("you can't pass both --uscan and a filename.")
97
98         uscan = Uscan()
99         gbp.log.info("Launching uscan...")
100         try:
101             uscan.scan()
102         except UscanError as e:
103             raise GbpError("%s" % e)
104
105         if not uscan.uptodate:
106             if uscan.tarball:
107                 gbp.log.info("using %s" % uscan.tarball)
108                 args.append(uscan.tarball)
109             else:
110                 raise GbpError("uscan didn't download anything, and no source was found in ../")
111         else:
112             gbp.log.info("package is up to date, nothing to do.")
113             return None
114     if len(args) > 1: # source specified
115         raise GbpError("More than one archive specified. Try --help.")
116     elif len(args) == 0:
117         raise GbpError("No archive to import specified. Try --help.")
118     else:
119         archive = DebianUpstreamSource(args[0])
120         return archive
121
122
123 def pristine_tarball_name(source, pkg_name, pkg_version):
124     if source.is_tarball():
125         if source.compression:
126             comp_ext = '.' + compressor_opts[source.compression][1]
127         else:
128             comp_ext = ''
129     else:
130         # Need to repack and/or mangle filename if the archive is not
131         # pristine-tar-compatible -> we decide to create gz compressed tarball
132         comp_ext = '.gz'
133     return '%s_%s.orig.tar%s' % (pkg_name, pkg_version, comp_ext)
134
135
136 def set_bare_repo_options(options):
137     """Modify options for import into a bare repository"""
138     if options.pristine_tar or options.merge:
139         gbp.log.info("Bare repository: setting %s%s options"
140                       % (["", " '--no-pristine-tar'"][options.pristine_tar],
141                          ["", " '--no-merge'"][options.merge]))
142         options.pristine_tar = False
143         options.merge = False
144
145
146 def build_parser(name):
147     try:
148         parser = GbpOptionParserDebian(command=os.path.basename(name), prefix='',
149                                        usage='%prog [options] /path/to/upstream-version.tar.gz | --uscan')
150     except ConfigParser.ParsingError as err:
151         gbp.log.err(err)
152         return None
153
154     import_group = GbpOptionGroup(parser, "import options",
155                       "pristine-tar and filtering")
156     tag_group = GbpOptionGroup(parser, "tag options",
157                       "options related to git tag creation")
158     branch_group = GbpOptionGroup(parser, "version and branch naming options",
159                       "version number and branch layout options")
160     cmd_group = GbpOptionGroup(parser, "external command options", "how and when to invoke external commands and hooks")
161
162     for group in [import_group, branch_group, tag_group, cmd_group ]:
163         parser.add_option_group(group)
164
165     branch_group.add_option("-u", "--upstream-version", dest="version",
166                       help="Upstream Version")
167     branch_group.add_config_file_option(option_name="debian-branch",
168                       dest="packaging_branch")
169     branch_group.add_config_file_option(option_name="upstream-branch",
170                       dest="upstream_branch")
171     branch_group.add_option("--upstream-vcs-tag", dest="vcs_tag",
172                             help="Upstream VCS tag add to the merge commit")
173     branch_group.add_boolean_config_file_option(option_name="merge", dest="merge")
174     branch_group.add_boolean_config_file_option(
175                       option_name="create-missing-branches",
176                       dest="create_missing_branches")
177
178     tag_group.add_boolean_config_file_option(option_name="sign-tags",
179                       dest="sign_tags")
180     tag_group.add_config_file_option(option_name="keyid",
181                       dest="keyid")
182     tag_group.add_config_file_option(option_name="upstream-tag",
183                       dest="upstream_tag")
184     import_group.add_config_file_option(option_name="filter",
185                       dest="filters", action="append")
186     import_group.add_boolean_config_file_option(option_name="pristine-tar",
187                       dest="pristine_tar")
188     import_group.add_boolean_config_file_option(option_name="filter-pristine-tar",
189                       dest="filter_pristine_tar")
190     import_group.add_config_file_option(option_name="import-msg",
191                       dest="import_msg")
192     import_group.add_boolean_config_file_option(option_name="symlink-orig",
193                                                 dest="symlink_orig")
194     cmd_group.add_config_file_option(option_name="postimport", dest="postimport")
195
196     parser.add_boolean_config_file_option(option_name="interactive",
197                                           dest='interactive')
198     parser.add_option("-v", "--verbose", action="store_true", dest="verbose", default=False,
199                       help="verbose command execution")
200     parser.add_config_file_option(option_name="color", dest="color", type='tristate')
201     parser.add_config_file_option(option_name="color-scheme",
202                                   dest="color_scheme")
203     parser.add_config_file_option(option_name="tmp-dir", dest="tmp_dir")
204
205     # Accepted for compatibility
206     parser.add_option("--no-dch", dest='no_dch', action="store_true",
207                       default=False, help="deprecated - don't use.")
208     parser.add_option("--uscan", dest='uscan', action="store_true",
209                       default=False, help="use uscan(1) to download the new tarball.")
210     return parser
211
212
213 def parse_args(argv):
214     parser = build_parser(argv[0])
215     if not parser:
216         return None, None
217
218     (options, args) = parser.parse_args(argv[1:])
219     gbp.log.setup(options.color, options.verbose, options.color_scheme)
220
221     if options.no_dch:
222         gbp.log.warn("'--no-dch' passed. This is now the default, please remove this option.")
223
224     return options, args
225
226
227 def main(argv):
228     ret = 0
229
230     (options, args) = parse_args(argv)
231     if not options:
232         return 1
233
234     tmpdir = tempfile.mkdtemp(dir=options.tmp_dir, prefix='import-orig_')
235
236     try:
237         source = find_source(options.uscan, args)
238         if not source:
239             return ret
240
241         try:
242             repo = DebianGitRepository('.')
243         except GitRepositoryError:
244             raise GbpError("%s is not a git repository" % (os.path.abspath('.')))
245
246         # an empty repo has now branches:
247         initial_branch = repo.get_branch()
248         is_empty = False if initial_branch else True
249
250         if not repo.has_branch(options.upstream_branch) and not is_empty:
251             if options.create_missing_branches:
252                 gbp.log.info("Will create missing branch '%s'" %
253                              options.upstream_branch)
254             else:
255                 raise GbpError(no_upstream_branch_msg % options.upstream_branch)
256
257         (pkg_name, version) = detect_name_and_version(repo, source, options)
258
259         (clean, out) = repo.is_clean()
260         if not clean and not is_empty:
261             gbp.log.err("Repository has uncommitted changes, commit these first: ")
262             raise GbpError(out)
263
264         if repo.bare:
265             set_bare_repo_options(options)
266
267         # Prepare sources for importing
268         pristine_name = pristine_tarball_name(source, pkg_name, version)
269         prepare_pristine = pristine_name if options.pristine_tar else None
270         unpacked_orig, pristine_orig = prepare_sources(
271                 source, pkg_name, version, prepare_pristine, options.filters,
272                 options.filter_pristine_tar, None, tmpdir)
273
274         # Don't mess up our repo with git metadata from an upstream tarball
275         try:
276             if os.path.isdir(os.path.join(unpacked_orig, '.git/')):
277                 raise GbpError("The orig tarball contains .git metadata - giving up.")
278         except OSError:
279             pass
280
281         try:
282             upstream_branch = [ options.upstream_branch, 'master' ][is_empty]
283             filter_msg = ["", " (filtering out %s)"
284                               % options.filters][len(options.filters) > 0]
285             gbp.log.info("Importing '%s' to branch '%s'%s..." % (source.path,
286                                                                  upstream_branch,
287                                                                  filter_msg))
288             gbp.log.info("Source package is %s" % pkg_name)
289             gbp.log.info("Upstream version is %s" % version)
290
291             import_branch = [ options.upstream_branch, None ][is_empty]
292             msg = upstream_import_commit_msg(options, version)
293
294             if options.vcs_tag:
295                 parents = [repo.rev_parse("%s^{}" % options.vcs_tag)]
296             else:
297                 parents = None
298
299             commit = repo.commit_dir(unpacked_orig,
300                         msg=msg,
301                         branch=import_branch,
302                         other_parents=parents,
303                         create_missing_branch=options.create_missing_branches)
304
305             if options.pristine_tar and pristine_orig:
306                 repo.pristine_tar.commit(pristine_orig, upstream_branch)
307
308             tag = repo.version_to_tag(options.upstream_tag, version)
309             repo.create_tag(name=tag,
310                             msg="Upstream version %s" % version,
311                             commit=commit,
312                             sign=options.sign_tags,
313                             keyid=options.keyid)
314             if is_empty:
315                 repo.create_branch(options.upstream_branch, rev=commit)
316                 repo.force_head(options.upstream_branch, hard=True)
317             elif options.merge:
318                 gbp.log.info("Merging to '%s'" % options.packaging_branch)
319                 repo.set_branch(options.packaging_branch)
320                 try:
321                     repo.merge(tag)
322                 except GitRepositoryError:
323                     raise GbpError("Merge failed, please resolve.")
324                 if options.postimport:
325                     epoch = ''
326                     if os.access('debian/changelog', os.R_OK):
327                         # No need to check the changelog file from the
328                         # repository, since we're certain that we're on
329                         # the debian-branch
330                         cp = ChangeLog(filename='debian/changelog')
331                         if cp.has_epoch():
332                             epoch = '%s:' % cp.epoch
333                     info = { 'version': "%s%s-1" % (epoch, version) }
334                     env = { 'GBP_BRANCH': options.packaging_branch }
335                     gbpc.Command(format_msg(options.postimport, info), extra_env=env, shell=True)()
336             # Update working copy and index if we've possibly updated the
337             # checked out branch
338             current_branch = repo.get_branch()
339             if current_branch in [ options.upstream_branch,
340                                    repo.pristine_tar_branch]:
341                 repo.force_head(current_branch, hard=True)
342             # Create symlink, if requested
343             if options.symlink_orig:
344                 if source.is_tarball():
345                     link = os.path.join('..', pristine_name)
346                     if not (os.path.exists(link) and
347                             os.path.samefile(link, source.path)):
348                         gbp.log.info('Creating symlink to %s' % source.path)
349                         os.symlink(source.path, link)
350                 else:
351                     gbp.log.warn('Orig source not a tarball, not symlinked')
352
353         except (gbpc.CommandExecFailed, GitRepositoryError) as err:
354             msg = err.__str__() if len(err.__str__()) else ''
355             raise GbpError("Import of %s failed: %s" % (source.path, msg))
356     except (GbpError, GitRepositoryError) as err:
357         if len(err.__str__()):
358             gbp.log.err(err)
359         ret = 1
360
361     if tmpdir:
362         cleanup_tmp_tree(tmpdir)
363
364     if not ret:
365         gbp.log.info("Successfully imported version %s of %s" % (version, source.path))
366     return ret
367
368 if __name__ == "__main__":
369     sys.exit(main(sys.argv))
370
371 # vim:et:ts=4:sw=4:et:sts=4:ai:set list listchars=tab\:»·,trail\:·: