def main(options, args):
    if len(args):
        distros = [Distro.get(a) for a in args]
    else:
        distros = Distro.all()

    # Run through our default distribution and use that for the base
    # package names.  Expire from all distributions.
    for target in config.targets(args):
        d = target.distro
        for pkg in d.packages(target.dist, target.component):
            if options.package and pkg.name not in options.package:
                continue

            try:
                output_dir = result_dir(target, pkg.name)
                report = read_report(output_dir)
                base = report["base_version"]
            except ValueError:
                logger.exception('Skipping package %s: unable to read merge '
                                 'report', pkg.name)
                continue

            if report['result'] not in (MergeResult.SYNC_THEIRS,
                                        MergeResult.KEEP_OURS,
                                        MergeResult.MERGED,
                                        MergeResult.CONFLICTS):
                logger.debug('Skipping expiry for package %s: result=%s',
                             pkg.name, report['result'])
                continue

            if base is None:
                # If there's no suitable base for merges, we don't
                # automatically expire any versions.
                logger.debug('Skipping expiry for package %s: '
                             'no base version found (result=%s)',
                             pkg.name, report['result'])
                continue

            base = Version(base)
            logger.debug("%s base is %s", pkg.name, base)

            for distro in distros:
                if distro.shouldExpire():
                    for component in distro.components():
                        try:
                            distro_pkg = distro.package(target.dist, component,
                                                        pkg.name)
                            expire_pool_sources(distro_pkg, base)
                        except PackageNotFound:
                            continue
def main(options, args):
    if len(args):
        distros = [Distro.get(a) for a in args]
    else:
        distros = Distro.all()

    # Run through our default distribution and use that for the base
    # package names.  Expire from all distributions.
    for target in DISTRO_TARGETS.keys():
        our_distro, our_dist, our_component = get_target_distro_dist_component(target)
        d = Distro.get(our_distro)
        for source in d.getSources(our_dist, our_component):
            if options.package and source['Package'] not in options.package:
                continue

            try:
                output_dir = result_dir(target, source['Package'])
                report = read_report(output_dir)
                base = report["base_version"]
            except ValueError:
                logger.debug('Skipping package %s: unable to read merge report',
                        source['Package'])
                continue

            if report['result'] not in (MergeResult.SYNC_THEIRS,
                    MergeResult.KEEP_OURS, MergeResult.MERGED,
                    MergeResult.CONFLICTS):
                logger.debug('Skipping expiry for package %s: result=%s',
                        source['Package'], report['result'])
                continue

            if base is None:
                # If there's no suitable base for merges, we don't
                # automatically expire any versions.
                logger.debug('Skipping expiry for package %s: '
                        'no base version found (result=%s)',
                        source['Package'], report['result'])
                continue

            logger.debug("%s %s", source["Package"], source["Version"])
            logger.debug("base is %s", base)

            for distro in distros:
                if distro.shouldExpire():
                    for component in distro.components():
                      expire_pool_sources(distro, component, source["Package"], base)
def main(options, args):
    logger.debug('Sending email if actions are needed...')

    for target in config.targets(args):
        logger.debug('%r', target)
        d = target.distro

        if not isinstance(d, OBSDistro):
            logger.debug('Skipping %r distro %r: not an OBSDistro', target, d)
            continue

        for pkg in d.packages(target.dist, target.component):
            if options.package and pkg.name not in options.package:
                logger.debug('Skipping package %s: not selected', pkg.name)
                continue

            if pkg.name in target.blacklist:
                logger.debug('Skipping package %s: blacklisted', pkg.name)
                continue

            try:
                output_dir = result_dir(target.name, pkg.name)
                report = read_report(output_dir)
            except ValueError:
                logger.debug('Skipping package %s: unable to read report',
                             pkg.name)
                continue

            if report.result == MergeResult.KEEP_OURS:
                logger.debug('Skipping package %s: result=%s',
                             pkg.name, report.result)
                continue

            if (target.committable
                    and report.result in (MergeResult.MERGED,
                                          MergeResult.SYNC_THEIRS)):
                logger.debug('Skipping package %s: result=%s, would already '
                             'have been committed', pkg.name, report.result)
                continue

            try:
                notify_action_needed(target, output_dir, report)
            except Exception:
                logger.exception('Error processing %s:', pkg.name)
def main(options, args):
    logger.info('Producing merges...')

    excludes = []
    if options.exclude is not None:
        for filename in options.exclude:
            logger.info('excluding packages from %s', filename)
            excludes.extend(read_package_list(filename))

    includes = []
    if options.include is not None:
        for filename in options.include:
            logger.info('including packages from %s', filename)
            includes.extend(read_package_list(filename))

    # For each package in the destination distribution, locate the latest in
    # the source distribution; calculate the base from the destination and
    # produce a merge combining both sets of changes
    for target in config.targets(args):
        logger.info('considering target %s', target)
        our_dist = target.dist
        our_component = target.component
        d = target.distro
        for pkg in d.packages(target.dist, target.component):
          if options.package is not None and pkg.name not in options.package:
            logger.debug('skipping package %s: not the selected package',
                         pkg.name)
            continue
          if len(includes) and pkg.name not in includes:
            logger.info('skipping package %s: not in include list', pkg.name)
            continue
          if len(excludes) and pkg.name in excludes:
            logger.info('skipping package %s: in exclude list', pkg.name)
            continue
          if pkg.name in target.blacklist:
            logger.info("%s is blacklisted, skipping", pkg.name)
            continue
          logger.info('considering package %s', pkg.name)
          if options.version:
            our_version = Version(options.version)
            logger.debug('our version: %s (from command line)', our_version)
          else:
            our_version = pkg.newestVersion()
            logger.debug('our version: %s', our_version)
          upstream = None

          for srclist in target.getSourceLists(pkg.name, include_unstable=False):
            for src in srclist:
              logger.debug('considering source %s', src)
              try:
                for possible in src.distro.findPackage(pkg.name,
                    searchDist=src.dist):
                  logger.debug('- contains version %s', possible)
                  if upstream is None or possible > upstream:
                    logger.debug('  - that version is the best yet seen')
                    upstream = possible
              except model.error.PackageNotFound:
                pass

          output_dir = result_dir(target.name, pkg.name)

          # There are two situations in which we will look in unstable distros
          # for a better version:
          try_unstable = False
      
          # 1. If our version is newer than the stable upstream version, we
          #    assume that our version was sourced from unstable, so let's
          #    check for an update there.
          #    However we must use the base version for the comparison here,
          #    otherwise we would consider our version 1.0-1endless1 newer
          #    than the stable 1.0-1 and look in unstable for an update.
          if upstream is not None and our_version >= upstream:
            our_base_version = our_version.version.base()
            logger.info("our version %s >= their version %s, checking base version %s", our_version, upstream, our_base_version)
            if our_base_version > upstream.version:
              logger.info("base version still newer than their version, checking in unstable")
              try_unstable = True

          # 2. If we didn't find any upstream version at all, it's possible
          #    that it's a brand new package where our version was imported
          #    from unstable, so let's see if we can find a better version
          #    there.
          if upstream is None:
            try_unstable = True

          # However, if this package has been assigned a specific source,
          # we'll honour that.
          if target.packageHasSpecificSource(pkg.name):
            try_unstable = False

          if try_unstable:
            for srclist in target.unstable_sources:
              for src in srclist:
                logger.debug('considering unstable source %s', src)
                try:
                  for possible in src.distro.findPackage(pkg.name,
                      searchDist=src.dist):
                    logger.debug('- contains version %s', possible)
                    if upstream is None or possible > upstream:
                      logger.debug('  - that version is the best yet seen')
                      upstream = possible
                except model.error.PackageNotFound:
                  pass

          if upstream is None:
            logger.info("%s not available upstream, skipping", our_version)
            cleanup(output_dir)
            report = MergeReport(left=our_version)
            report.target = target.name
            report.result = MergeResult.KEEP_OURS
            report.merged_version = our_version.version
            report.write_report(output_dir)
            continue

          try:
            report = read_report(output_dir)
            # See if sync_upstream_packages already set
            if not options.force and \
               pkg.name in target.sync_upstream_packages and \
               Version(report['right_version']) == upstream.version and \
               Version(report['left_version']) == our_version.version and \
               Version(report['merged_version']) == upstream.version and \
               report['result'] == MergeResult.SYNC_THEIRS:
                logger.info("sync to upstream for %s [ours=%s, theirs=%s] "
                            "already produced, skipping run", pkg,
                            our_version.version, upstream.version)
                continue
            elif (not options.force and
                    Version(report['right_version']) == upstream.version and
                    Version(report['left_version']) == our_version.version and
                    # we'll retry the merge if there was an unexpected
                    # failure, a missing base or an unknown result last time
                    report['result'] in (MergeResult.KEEP_OURS,
                        MergeResult.SYNC_THEIRS, MergeResult.MERGED,
                        MergeResult.CONFLICTS)):
              logger.info("merge for %s [ours=%s, theirs=%s] already produced, skipping run", pkg, our_version.version, upstream.version)
              continue
          except (AttributeError, ValueError, KeyError):
            pass

          if our_version >= upstream:
            logger.info("our version %s >= their version %s, skipping",
                    our_version, upstream)
            cleanup(output_dir)
            report = MergeReport(left=our_version, right=upstream)
            report.target = target.name
            report.result = MergeResult.KEEP_OURS
            report.merged_version = our_version.version
            report.write_report(output_dir)
            continue
          elif our_version < upstream and \
               pkg.name in target.sync_upstream_packages:
            logger.info("Syncing to %s per sync_upstream_packages", upstream)
            cleanup(output_dir)
            report = MergeReport(left=our_version, right=upstream)
            report.target = target.name
            report.result = MergeResult.SYNC_THEIRS
            report.merged_version = upstream.version
            report.message = "Using version in upstream distro per " \
                             "sync_upstream_packages configuration"
            report.write_report(output_dir)
            continue

          logger.info("local: %s, upstream: %s", our_version, upstream)

          try:
            produce_merge(target, our_version, upstream, output_dir)
          except ValueError as e:
            logger.exception("Could not produce merge, perhaps %s changed components upstream?", pkg)
            report = MergeReport(left=our_version, right=upstream)
            report.target = target.name
            report.result = MergeResult.FAILED
            report.message = 'Could not produce merge: %s' % e
            report.write_report(output_dir)
            continue
Exemple #5
0
def handle_package(options, output_dir, target, pkg, our_version):
    update_info = UpdateInfo(pkg)

    if update_info.version is None:
        logger.error('UpdateInfo version %s does not match our version %s"',
                     update_info.version, our_version)
        report = MergeReport(left=our_version)
        report.target = target.name
        report.result = MergeResult.FAILED
        report.message = 'Could not find update info for version %s' % \
            our_version
        return report

    if update_info.upstream_version is None \
            or our_version.version >= update_info.upstream_version:
        logger.debug("No updated upstream version available for %s",
                     our_version)
        cleanup(output_dir)
        report = MergeReport(left=our_version)
        report.target = target.name
        report.result = MergeResult.KEEP_OURS
        report.merged_version = our_version.version
        return report

    if update_info.base_version is None:
        logger.info("No base version available for %s", our_version)
        cleanup(output_dir)
        report = MergeReport(left=our_version)
        report.target = target.name
        report.result = MergeResult.NO_BASE
        return report

    upstream = target.findSourcePackage(pkg.name, update_info.upstream_version)
    if not upstream:
        logger.error('Could not find upstream version %s in pool',
                     update_info.upstream_version)
        cleanup(output_dir)
        report = MergeReport(left=our_version)
        report.target = target.name
        report.result = MergeResult.FAILED
        report.message = 'Could not find upstream version %s in pool' % \
            update_info.upstream_version
        return report

    upstream = upstream[0]
    base = None
    pool_versions = target.getAllPoolVersions(pkg.name)
    for pv in pool_versions:
        if pv.version == update_info.base_version:
            base = pv

    if base is None:
        logger.error('Could not find base version %s in pool',
                     update_info.base_version)
        cleanup(output_dir)
        report = MergeReport(left=our_version)
        report.target = target.name
        report.result = MergeResult.FAILED
        report.message = 'Could not find base version %s in pool' % \
            update_info.base_version
        return report

    try:
        report = read_report(output_dir)
        # See if sync_upstream_packages already set
        if not options.force and \
                pkg.name in target.sync_upstream_packages and \
                Version(report['right_version']) == upstream.version and \
                Version(report['left_version']) == our_version.version and \
                Version(report['merged_version']) == upstream.version and \
                report['result'] == MergeResult.SYNC_THEIRS:
            logger.info("sync to upstream for %s [ours=%s, theirs=%s] "
                        "already produced, skipping run", pkg,
                        our_version.version, upstream.version)
            return None
        elif (not options.force and
              Version(report['right_version']) == upstream.version and
              Version(report['left_version']) == our_version.version and
              # we'll retry the merge if there was an unexpected
              # failure, a missing base or an unknown result last time
              report['result'] in (MergeResult.KEEP_OURS,
                                   MergeResult.SYNC_THEIRS, MergeResult.MERGED,
                                   MergeResult.CONFLICTS)):
            logger.info("merge for %s [ours=%s, theirs=%s] already produced, "
                        "skipping run",
                        pkg, our_version.version, upstream.version)
            return None
    except (AttributeError, ValueError, KeyError):
        pass

    if pkg.name in target.sync_upstream_packages:
        logger.info("Syncing to %s per sync_upstream_packages", upstream)
        cleanup(output_dir)
        report = MergeReport(left=our_version, right=upstream)
        report.target = target.name
        report.result = MergeResult.SYNC_THEIRS
        report.merged_version = upstream.version
        report.message = "Using version in upstream distro per " \
            "sync_upstream_packages configuration"
        if update_info.specific_upstream:
            report.notes.append('Synced with specific upstream %s due to '
                                'runtime parameters'
                                % update_info.specific_upstream)
        return report

    if options.sync_to_upstream:
        logger.info("Syncing to %s per command line", upstream)
        cleanup(output_dir)
        report = MergeReport(left=our_version, right=upstream)
        report.target = target.name
        report.result = MergeResult.SYNC_THEIRS
        report.merged_version = upstream.version
        if update_info.specific_upstream:
            report.notes.append('Synced with specific upstream %s due to '
                                'runtime parameters'
                                % update_info.specific_upstream)
        else:
            report.notes.append('Force-synced to upstream due to runtime '
                                'parameters')
        return report

    logger.info("local: %s, upstream: %s", our_version, upstream)

    try:
        report = produce_merge(target, base, our_version, upstream, output_dir)
        if update_info.specific_upstream:
            report.notes.append('Merged with specific upstream %s due to '
                                'runtime parameters'
                                % update_info.specific_upstream)
        return report
    except ValueError as e:
        logger.exception("Could not produce merge, "
                         "perhaps %s changed components upstream?", pkg)
        report = MergeReport(left=our_version, right=upstream)
        report.target = target.name
        report.result = MergeResult.FAILED
        report.message = 'Could not produce merge: %s' % e
        return report
def main(options, args):
    logger.debug('Committing merges...')

    for target in config.targets(args):
      d = target.distro

      if not isinstance(d, OBSDistro):
        logger.debug('Skipping %r distro %r: not an OBSDistro', target, d)
        continue

      for source in d.newestSources(target.dist, target.component):
        if options.package and source['Package'] not in options.package:
          logger.debug('Skipping package %s: not selected', source['Package'])
          continue

        if source['Package'] in target.blacklist:
          logger.debug('Skipping package %s: blacklisted', source['Package'])
          continue

        try:
          output_dir = result_dir(target.name, source['Package'])
          report = read_report(output_dir)
        except ValueError:
          logger.debug('Skipping package %s: unable to read report',
                  source['Package'])
          continue

        package = d.package(target.dist, target.component,
                report.source_package)

        if report['committed']:
          if options.force:
            logger.info("Forcing commit of %s", package)
          else:
            logger.debug("%s already committed, skipping!", package)
            continue

        if report['result'] not in (MergeResult.MERGED,
                MergeResult.SYNC_THEIRS):
            logger.debug("%s has nothing to commit: result=%s",
                    package, report['result'])
            continue

        filepaths = report['merged_files']
        if filepaths == []:
            logger.warning("Empty merged file list in %s/REPORT" % output_dir)
            continue

        if target.committable:
          # we can commit directly to the target distribution
          # FIXME: is this still a supported configuration? I wouldn't
          # want to commit automated merges without some sort of manual
          # check on the debdiff...
          logger.info("Committing changes to %s", package)
          if not options.dry_run:
            try:
              package.commit('Automatic update by Merge-O-Matic')
            except urllib2.HTTPError as e:
              logger.exception('Failed to commit %s: HTTP error %s at <%s>:',
                  package, e.code, e.geturl())
              update_report(report, output_dir, False,
                  "HTTP error %s" % e.code)
            except Exception as e:
              logger.exception('Failed to commit %s:', package)
              # deliberately rather vague, as below
              update_report(report, output_dir, False,
                  "%s" % e.__class__.__name__)
            else:
              update_report(report, output_dir, True,
                      committed_to=d.obsProject(target.dist, target.component))
          continue

        # else we need to branch it and commit to the branch
        try:
          logger.debug("Branching %s", package)

          branchPkg = package.branch("home:%s:branches"%(d.obsUser))

          branch = branchPkg.distro
          branch.sync(target.dist, target.component, [branchPkg,])
          logger.info("Committing changes to %s, and submitting merge request to %s", branchPkg, package)
          if report['result'] == MergeResult.SYNC_THEIRS:
            srcDistro = Distro.get(report['right_distro'])

            version = Version(report['right_version'])

            logger.debug('Copying updated upstream version %s from %r into %r',
                    version,
                    srcDistro,
                    target)
            for upstream in target.getSourceLists(package.name):
              for src in upstream:
                srcDistro = src.distro
                try:
                  pkg = srcDistro.findPackage(package.name, searchDist=src.dist,
                      version=version)[0]
                  pfx = pkg.poolDirectory().path
                  break
                except model.error.PackageNotFound:
                  pass
          else:
            logger.debug('Copying merged version from %r into %r',
                    branch, target)
            pfx = result_dir(target.name, package.name)

          # this might raise an error
          obsFiles = branchPkg.getOBSFiles()

          # Get the linked target files since the checkout is expanded
          # and may contain them
          linkedFiles = package.getOBSFiles()

          for f in obsFiles:
            if f.endswith(".dsc"):
              oldDsc = '%s/%s'%(branchPkg.obsDir(), f)
              break
          for f in filepaths:
            if f.endswith(".dsc"):
              newDsc = '%s/%s'%(pfx, f)
              break

          #logger.debug("Running debdiff on %s and %s", oldDsc, newDsc)
          #comment = shell.get(("debdiff", oldDsc, newDsc), okstatus=(0,1))
          # FIXME: Debdiff needs implemented in OBS, as large merge descriptions break clucene.
          comment = ''
          if report['result'] == MergeResult.SYNC_THEIRS:
            comment += 'Sync to '
          elif report['result'] == MergeResult.MERGED:
            comment += 'Merge with '
          comment += 'version %s from %s %s' %(report['right_version'],
                                               report['right_distro'],
                                               report['right_suite'])
          comment += "\n\nMerge report is available at %s"%('/'.join((config.get('MOM_URL'), subdir(config.get('ROOT'), output_dir), 'REPORT.html')))

          # The newlines seem to cause create_submit_request to send
          # UTF-32 over the wire, which OBS promptly chokes on. Encode
          # the message to UTF-8 first.
          comment = comment.encode('utf-8')
          if not options.dry_run:
            filesUpdated = False
            for f in obsFiles + linkedFiles:
              if f == "_link":
                continue
              try:
                logger.debug('deleting %s/%s', branchPkg.obsDir(), f)
                os.unlink('%s/%s'%(branchPkg.obsDir(), f))
                filesUpdated = True
              except OSError:
                pass
            for f in filepaths:
              if f == "_link":
                continue
              logger.debug('copying %s/%s -> %s', pfx, f, branchPkg.obsDir())
              shutil.copy2("%s/%s"%(pfx, f), branchPkg.obsDir())
              filesUpdated = True
            if filesUpdated:
              logger.debug('Submitting request to merge %r from %r into %r',
                      branchPkg, branch, target)
              try:
                branchPkg.commit('Automatic update by Merge-O-Matic')
                obs_project = d.obsProject(target.dist, target.component)
                reqid = branchPkg.submitMergeRequest(obs_project, comment)
                update_report(report, output_dir, True,
                        committed_to=obs_project,
                        request_url=branchPkg.webMergeRequest(reqid))
              except xml.etree.cElementTree.ParseError:
                logger.exception("Failed to commit %s", branchPkg)
                update_report(report, output_dir, False, "OBS API Error")
              except urllib2.HTTPError as e:
                logger.exception("Failed to commit %s: HTTP error %s at <%s>:",
                    branchPkg, e.code, e.geturl())
                update_report(report, output_dir, False,
                    "HTTP error %s" % e.code)
              except Exception as e:
                logger.exception("Failed to commit %s", branchPkg)
                # deliberately being a bit vague here in case the exact
                # exception leaks internal info
                update_report(report, output_dir, False,
                    "%s" % e.__class__.__name__)
          else:
            logger.info("Not committing, due to --dry-run")

        except urllib2.HTTPError as e:
          logger.exception('Failed to branch %s: HTTP error %s at <%s>:',
              package, e.code, e.geturl())
          update_report(report, output_dir, False,
              "Failed to branch: HTTP error %s" % e.code)

        except Exception as e:
          logger.exception('Failed to branch %s:', package)
          # deliberately being a bit vague here in case the exact
          # exception leaks internal info
          update_report(report, output_dir, False,
              "Failed to branch: %s" % e.__class__.__name__)
def main(options, args):
    logger.info('Summarizing merge status...')

    if options.target:
        targets = [options.target]
    else:
        targets = DISTRO_TARGETS.keys()

    outstanding = []
    if os.path.isfile("%s/outstanding-merges.txt" % ROOT):
        after_uvf = True

        with open("%s/outstanding-merges.txt" % ROOT) as f:
            for line in f:
                outstanding.append(line.strip())
    else:
        after_uvf = False
        SECTIONS.remove("new")

    # For each package in the destination distribution, find out whether
    # there's an open merge, and if so add an entry to the table for it.
    for target in targets:
        logger.info('Considering target %s', target)
        our_distro, our_dist, our_component = get_target_distro_dist_component(target)
        merges = []

        d = Distro.get(our_distro)
        for source in d.getSources(our_dist, our_component):
            logger.debug('Considering package %s', source["Package"])
            try:
                output_dir = result_dir(target, source["Package"])
                report = read_report(output_dir)
            except ValueError:
                continue

            if report['result'] == MergeResult.KEEP_OURS:
                logger.debug('Skipping merge status for %s: result=%s',
                        source['Package'], report['result'])
                continue

            try:
                priority_idx = PRIORITY.index(source["Priority"])
            except (KeyError, ValueError) as e:
                # either it has no priority, or the priority is something
                # not in our array; Debian packages can end up with
                # Priority: source
                priority_idx = 0

            if report["committed"]:
                section = "committed"
            elif not after_uvf:
                section = "outstanding"
            elif source["Package"] in outstanding:
                section = "outstanding"
            else:
                section = "new"

            merges.append((section, priority_idx, source["Package"],
                        source, report["base_version"],
                        report["left_version"], report["right_version"],
                        report["right_distro"], output_dir, report))

        merges.sort()

        if isinstance(d, OBSDistro):
            obs_project = d.obsProject(our_dist, our_component)
        else:
            obs_project = None

        write_status_page(target, merges, our_distro, obs_project)
        write_status_json(target, merges)

        status_file = "%s/merges/tomerge-%s" % (ROOT, target)
        remove_old_comments(status_file, merges)
        write_status_file(status_file, merges)
Exemple #8
0
def main(options, args):
    logger.debug('Committing merges...')

    for target in config.targets(args):
      d = target.distro

      if not isinstance(d, OBSDistro):
        logger.debug('Skipping %r distro %r: not an OBSDistro', target, d)
        continue

      for package in d.packages(target.dist, target.component):
        if options.package and package.name not in options.package:
          logger.debug('Skipping package %s: not selected', package.name)
          continue

        if package.name in target.blacklist:
          logger.debug('Skipping package %s: blacklisted', package.name)
          continue

        try:
          output_dir = result_dir(target.name, pkg.name)
          report = read_report(output_dir)
        except ValueError:
          logger.debug('Skipping package %s: unable to read report',
                       package.name)
          continue

        if report['committed']:
          if options.force:
            logger.info("Forcing commit of %s", package)
          else:
            logger.debug("%s already committed, skipping!", package)
            continue

        if report['result'] not in (MergeResult.MERGED,
                MergeResult.SYNC_THEIRS):
            logger.debug("%s has nothing to commit: result=%s",
                    package, report['result'])
            continue

        filepaths = report['merged_files']
        if filepaths == []:
            logger.warning("Empty merged file list in %s/REPORT" % output_dir)
            continue

        if target.committable:
          # we can commit directly to the target distribution
          # FIXME: is this still a supported configuration? I wouldn't
          # want to commit automated merges without some sort of manual
          # check on the debdiff...
          logger.info("Committing changes to %s", package)
          if not options.dry_run:
            try:
              package.commit('Automatic update by Merge-O-Matic')
            except urllib2.HTTPError as e:
              logger.exception('Failed to commit %s: HTTP error %s at <%s>:',
                  package, e.code, e.geturl())
              update_report(report, output_dir, False,
                  "HTTP error %s" % e.code)
            except Exception as e:
              logger.exception('Failed to commit %s:', package)
              # deliberately rather vague, as below
              update_report(report, output_dir, False,
                  "%s" % e.__class__.__name__)
            else:
              update_report(report, output_dir, True,
                      committed_to=d.obsProject(target.dist, target.component))
          continue

        # else we need to branch it and commit to the branch
        try:
          logger.debug("Branching %s", package)

          branchPkg = package.branch("home:%s:branches"%(d.obsUser))

          branch = branchPkg.distro
          branch.sync(target.dist, target.component, [branchPkg,])
          logger.info("Committing changes to %s, and submitting merge request to %s", branchPkg, package)
          if report['result'] == MergeResult.SYNC_THEIRS:
            srcDistro = Distro.get(report['right_distro'])

            version = Version(report['right_version'])

            logger.debug('Copying updated upstream version %s from %r into %r',
                    version,
                    srcDistro,
                    target)
            for upstream in target.getSourceLists(package.name):
              for src in upstream:
                srcDistro = src.distro
                try:
                  pkg = srcDistro.findPackage(package.name, searchDist=src.dist,
                      version=version)[0]
                  pfx = pkg.poolPath
                  break
                except model.error.PackageNotFound:
                  pass
          else:
            logger.debug('Copying merged version from %r into %r',
                    branch, target)
            pfx = result_dir(target.name, package.name)

          # this might raise an error
          obsFiles = branchPkg.getOBSFiles()

          # Get the linked target files since the checkout is expanded
          # and may contain them
          linkedFiles = package.getOBSFiles()

          for f in obsFiles:
            if f.endswith(".dsc"):
              oldDsc = '%s/%s'%(branchPkg.obsDir(), f)
              break
          for f in filepaths:
            if f.endswith(".dsc"):
              newDsc = '%s/%s'%(pfx, f)
              break

          #logger.debug("Running debdiff on %s and %s", oldDsc, newDsc)
          #comment = shell.get(("debdiff", oldDsc, newDsc), okstatus=(0,1))
          # FIXME: Debdiff needs implemented in OBS, as large merge descriptions break clucene.
          comment = ''
          if report['result'] == MergeResult.SYNC_THEIRS:
            comment += 'Sync to '
          elif report['result'] == MergeResult.MERGED:
            comment += 'Merge with '
          comment += 'version %s from %s %s' %(report['right_version'],
                                               report['right_distro'],
                                               report['right_suite'])
          comment += "\n\nMerge report is available at %s"%('/'.join((config.get('MOM_URL'), subdir(config.get('ROOT'), output_dir), 'REPORT.html')))

          # The newlines seem to cause create_submit_request to send
          # UTF-32 over the wire, which OBS promptly chokes on. Encode
          # the message to UTF-8 first.
          comment = comment.encode('utf-8')
          if not options.dry_run:
            filesUpdated = False
            for f in obsFiles + linkedFiles:
              if f == "_link":
                continue
              try:
                logger.debug('deleting %s/%s', branchPkg.obsDir(), f)
                os.unlink('%s/%s'%(branchPkg.obsDir(), f))
                filesUpdated = True
              except OSError:
                pass
            for f in filepaths:
              if f == "_link":
                continue
              logger.debug('copying %s/%s -> %s', pfx, f, branchPkg.obsDir())
              shutil.copy2("%s/%s"%(pfx, f), branchPkg.obsDir())
              filesUpdated = True
            if filesUpdated:
              logger.debug('Submitting request to merge %r from %r into %r',
                      branchPkg, branch, target)
              try:
                branchPkg.commit('Automatic update by Merge-O-Matic')
                obs_project = d.obsProject(target.dist, target.component)
                reqid = branchPkg.submitMergeRequest(obs_project, comment)
                update_report(report, output_dir, True,
                        committed_to=obs_project,
                        request_url=branchPkg.webMergeRequest(reqid))
              except xml.etree.cElementTree.ParseError:
                logger.exception("Failed to commit %s", branchPkg)
                update_report(report, output_dir, False, "OBS API Error")
              except urllib2.HTTPError as e:
                logger.exception("Failed to commit %s: HTTP error %s at <%s>:",
                    branchPkg, e.code, e.geturl())
                update_report(report, output_dir, False,
                    "HTTP error %s" % e.code)
              except Exception as e:
                logger.exception("Failed to commit %s", branchPkg)
                # deliberately being a bit vague here in case the exact
                # exception leaks internal info
                update_report(report, output_dir, False,
                    "%s" % e.__class__.__name__)
          else:
            logger.info("Not committing, due to --dry-run")

        except urllib2.HTTPError as e:
          logger.exception('Failed to branch %s: HTTP error %s at <%s>:',
              package, e.code, e.geturl())
          update_report(report, output_dir, False,
              "Failed to branch: HTTP error %s" % e.code)

        except Exception as e:
          logger.exception('Failed to branch %s:', package)
          # deliberately being a bit vague here in case the exact
          # exception leaks internal info
          update_report(report, output_dir, False,
              "Failed to branch: %s" % e.__class__.__name__)
Exemple #9
0
def main(options, args):
    logger.info('Summarizing merge status...')

    if options.target:
        targets = [options.target]
    else:
        targets = config.get('DISTRO_TARGETS').keys()

    outstanding = []
    if os.path.isfile("%s/outstanding-merges.txt" % config.get('ROOT')):
        after_uvf = True

        with open("%s/outstanding-merges.txt" % config.get('ROOT')) as f:
            for line in f:
                outstanding.append(line.strip())
    else:
        after_uvf = False
        SECTIONS.remove("new")

    # For each package in the destination distribution, find out whether
    # there's an open merge, and if so add an entry to the table for it.
    for target in targets:
        logger.info('Considering target %s', target)
        our_distro, our_dist, our_component = get_target_distro_dist_component(
            target)
        merges = []

        d = Distro.get(our_distro)
        for source in d.getSources(our_dist, our_component):
            logger.debug('Considering package %s', source["Package"])
            try:
                output_dir = result_dir(target, source["Package"])
                report = read_report(output_dir)
            except ValueError:
                continue

            if report['result'] == MergeResult.KEEP_OURS:
                logger.debug('Skipping merge status for %s: result=%s',
                             source['Package'], report['result'])
                continue

            try:
                priority_idx = PRIORITY.index(source["Priority"])
            except (KeyError, ValueError) as e:
                # either it has no priority, or the priority is something
                # not in our array; Debian packages can end up with
                # Priority: source
                priority_idx = 0

            if report["committed"]:
                section = "committed"
            elif not after_uvf:
                section = "outstanding"
            elif source["Package"] in outstanding:
                section = "outstanding"
            else:
                section = "new"

            merges.append((section, priority_idx, source["Package"], source,
                           report["base_version"], report["left_version"],
                           report["right_version"], report["right_distro"],
                           output_dir, report))

        merges.sort()

        if isinstance(d, OBSDistro):
            obs_project = d.obsProject(our_dist, our_component)
        else:
            obs_project = None

        write_status_page(target, merges, our_distro, obs_project)
        write_status_json(target, merges)

        status_file = "%s/merges/tomerge-%s" % (config.get('ROOT'), target)
        remove_old_comments(status_file, merges)
        write_status_file(status_file, merges)