#!/usr/bin/python # vim: set fileencoding=utf-8 : # # (C) 2009,2012,2015,2016 Guido Guenther # """gbp-posttag-push: post tag hook to be called by 'gbp buildpackage' to push out the newly created tag and to forward the remote branch to that position. It checks for explicit push destinations, if none are found it pushes to where the branch got merged from. Before pushing it checks if the tag is signed. It will only push changes up to the tag and it will figure out if it needs to push an upstream tag and the upstream branch as well (given the -u option). Before performing a package upload it checks if it can push the changes to the repo to ensure the Debian archive and the VCS stay in sync (as good as possible given the async nature of the upload into the archive). Configuration ------------- In your gbp.conf use [buildpackage] posttag = gbp-posttag-push -u to enable the hook. To also enable archive uploads configure an apropriate upload command: [gbp-posttag-push] upload-cmd = dput Options ------- -d: dry-run -u: push upstream branch too, if not on remote already --verbose: verbose command output """ from __future__ import print_function import glob import os import subprocess import sys import gbp.log from gbp.command_wrappers import Command, CommandExecFailed from gbp.config import GbpOptionParserDebian from gbp.deb.git import DebianGitRepository, GitRepositoryError from gbp.deb.source import DebianSource from gbp.errors import GbpError from gbp.git.vfs import GitVfs from gbp.scripts.common import ExitCodes class Env(object): pass def get_push_targets(env): """get a list of push targets""" dests = {} cmd = "git config --get-regexp 'remote\..*\.push' '^%s(:.*)?$'" % env.branch for remote in subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=True).communicate()[0].split("\n"): if not len(remote): continue repo, refspec = remote.split() repo = ".".join(repo.split('.')[1:-1]) # remote..push try: remote = refspec.split(':')[1] # src:dest except IndexError: remote = refspec dests[repo] = remote return dests def get_pull(env): """where did we pull from?""" cmd = 'git config --get branch."%s".remote' % env.branch remote = subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=True).communicate()[0].strip() if not remote: remote = 'origin' return {remote: env.branch} def git_push_sim(*args): print("git push %s" % " ".join(args)) def get_upstream_tag(repo, tag, tag_format): # FIXME: This assumes the debian version is the last part after the slash: version = tag.split('/')[-1] upstream = version.rsplit('-')[0] tag = tag_format % dict(version=upstream) if repo.has_tag(tag): return tag return None def build_parser(name): # Until we moved this out of examples os.environ['GBP_DISABLE_SECTION_DEPRECTATION'] = '1' GbpOptionParserDebian.defaults['upload-cmd'] = "" GbpOptionParserDebian.help['upload-cmd'] = "Upload command, default is '%(upload-cmd)s'" try: parser = GbpOptionParserDebian(command=os.path.basename(name), usage='%prog [options]') except GbpError as err: gbp.log.err(err) return None parser.add_option("-d", "--dry-run", dest="dryrun", default=False, action="store_true", help="dry run, don't push.") parser.add_option("-u", "--push-upstream", dest="push_upstream", default=False, action="store_true", help="also push upstream branch changes") parser.add_config_file_option(option_name="upstream-branch", dest="upstream_branch") parser.add_config_file_option(option_name="upstream-tag", dest="upstream_tag") parser.add_config_file_option(option_name="debian-tag", dest="debian_tag") parser.add_config_file_option(option_name="upload-cmd", dest="upload_cmd") parser.add_config_file_option(option_name="color", dest="color", type='tristate') parser.add_config_file_option(option_name="color-scheme", dest="color_scheme") parser.add_option("--verbose", action="store_true", dest="verbose", default=False, help="verbose command execution") return parser def parse_args(argv): parser = build_parser(argv[0]) if not parser: return None, None return parser.parse_args(argv) def find_changes(sourcename, version): glob_ex = "../%s_%s_*.changes" % (sourcename, version) gbp.log.info("Looking for changes at %s" % glob_ex) # FIXME: verify version in .changes file return glob.glob(glob_ex) def upload_changes(changes, cmd, dryrun): ret = 0 for name in changes: gbp.log.info("Running %s" % " ".join([cmd, name])) try: if not dryrun: Command(cmd, [name], shell=False)() except CommandExecFailed: gbp.log.err("Upload of '%s' failed." % name) ret = 1 return ret def do_push(repo, dests, debian_tag, debian_sha1, upstream_tag, upstream_sha1, upstream_branch, dry_run): verb = "Dry-run: Pushing" if dry_run else "Pushing" for dest in dests: gbp.log.info("%s %s to %s" % (verb, debian_tag, dest)) repo.push_tag(dest, debian_tag, dry_run) gbp.log.info("%s %s to %s:%s" % (verb, debian_sha1, dest, dests[dest])) repo.push(dest, debian_sha1, dests[dest]) if upstream_tag: gbp.log.info("%s %s to %s" % (verb, upstream_tag, dest)) repo.push_tag(dest, upstream_tag, dry_run) if not repo.branch_contains("%s/%s" % (dest, upstream_branch), upstream_sha1, remote=True): gbp.log.info("%s %s to %s:%s" % (verb, upstream_sha1, dest, upstream_branch)) repo.push(dest, upstream_sha1, upstream_branch, dry_run) def main(argv): retval = 0 env = Env() upstream_sha1 = None (options, args) = parse_args(argv) if not options: return ExitCodes.parse_error gbp.log.setup(options.color, options.verbose, options.color_scheme) repo = DebianGitRepository('.') if options.dryrun: gbp.log.warn("Dry run mode. Not pushing, not uploading.") repo.push = git_push_sim repo.push_tag = git_push_sim for envvar in ["GBP_TAG", "GBP_BRANCH", "GBP_SHA1"]: var = os.getenv(envvar) if var: env.__dict__.setdefault("%s" % envvar.split("_")[1].lower(), var) else: gbp.log.err("%s not set." % envvar) return 1 dests = get_push_targets(env) if not dests: dests = get_pull(env) upstream_tag = get_upstream_tag(repo, env.tag, options.upstream_tag) if upstream_tag: upstream_sha1 = repo.rev_parse("%s^{}" % upstream_tag) if not repo.verify_tag(env.tag): gbp.log.info("Not pushing non-existent or unsigned tag '%s'." % env.tag) return 0 source = DebianSource(GitVfs(repo, env.tag)) try: if options.upload_cmd: do_push(repo, dests, env.tag, env.sha1, upstream_tag if options.push_upstream and upstream_tag else None, upstream_sha1, options.upstream_branch, dry_run=True) version = repo.tag_to_version(env.tag, options.debian_tag).split(':')[-1] changes = find_changes(source.sourcepkg, version) if len(changes): retval = upload_changes(changes, options.upload_cmd, options.dryrun) else: gbp.log.info("No changes file found, nothing to upload.") else: gbp.log.info("No upload command defined, not uploading to the archive") do_push(repo, dests, env.tag, env.sha1, upstream_tag if options.push_upstream and upstream_tag else None, upstream_sha1, options.upstream_branch, dry_run=False) except (GbpError, GitRepositoryError) as err: if str(err): gbp.log.err(err) retval = 1 return retval if __name__ == '__main__': sys.exit(main(sys.argv)) # vim:et:ts=4:sw=4:et:sts=4:ai:set list listchars=tab\:»·,trail\:·: