def unrep_with_dbd_issues():
    log.info('running unrep_with_dbd_issues check...')
    without_dbd = []
    bad_dbd = []
    sources_without_dbd = set()
    query = '''SELECT s.name, r.version, s.suite, s.architecture
               FROM sources AS s JOIN results AS r ON r.package_id=s.id
               WHERE r.status='unreproducible'
               ORDER BY s.name ASC, s.suite DESC, s.architecture ASC'''
    results = query_db(query)
    for pkg, version, suite, arch in results:
        eversion = strip_epoch(version)
        dbd = DBD_PATH + '/' + suite + '/' + arch + '/' + pkg + '_' + \
            eversion + '.diffoscope.html'
        if not os.access(dbd, os.R_OK):
            without_dbd.append((pkg, version, suite, arch))
            sources_without_dbd.add(pkg)
            log.warning(suite + '/' + arch + '/' + pkg + ' (' + version +
                        ') is '
                        'unreproducible without diffoscope file.')
        else:
            log.debug(dbd + ' found.')
            data = open(dbd, 'br').read(3)
            if b'<' not in data:
                bad_dbd.append((pkg, version, suite, arch))
                log.warning(suite + '/' + arch + '/' + pkg + ' (' + version +
                            ') has '
                            'diffoscope output, but it does not seem to '
                            'be an HTML page.')
                sources_without_dbd.add(pkg)
    return without_dbd, bad_dbd, sources_without_dbd
def pbuilder_dep_fail():
    log.info('running pbuilder_dep_fail check...')
    bad_pkgs = []
    # we only care about these failures in the !unstable !experimental suites
    # as they happen all the time in there, as packages are buggy
    # and specific versions also come and go
    query = '''SELECT s.name, r.version, s.suite, s.architecture
               FROM sources AS s JOIN results AS r ON r.package_id=s.id
               WHERE r.status = 'FTBFS'
               AND s.suite NOT IN ('unstable', 'experimental')
               ORDER BY s.name ASC, s.suite DESC, s.architecture ASC'''
    results = query_db(query)
    for pkg, version, suite, arch in results:
        eversion = strip_epoch(version)
        rbuild = RBUILD_PATH + '/' + suite + '/' + arch + '/' + pkg + '_' + \
            eversion + '.rbuild.log'
        if os.access(rbuild, os.R_OK):
            log.debug('\tlooking at ' + rbuild)
            with open(rbuild, "br") as fd:
                for line in fd:
                    if re.search(b'E: pbuilder-satisfydepends failed.', line):
                        bad_pkgs.append((pkg, version, suite, arch))
                        log.warning(suite + '/' + arch + '/' + pkg + ' (' +
                                    version +
                                    ') failed to satisfy its dependencies.')
    return bad_pkgs
def alien_log(directory=None):
    if directory is None:
        bad_files = []
        for path in RBUILD_PATH, LOGS_PATH, DIFFS_PATH:
            bad_files.extend(alien_log(path))
        return bad_files
    log.info('running alien_log check over ' + directory + '...')
    query = '''SELECT r.version
               FROM sources AS s JOIN results AS r ON r.package_id=s.id
               WHERE r.status != '' AND s.name='{pkg}' AND s.suite='{suite}'
               AND s.architecture='{arch}'
               ORDER BY s.name ASC, s.suite DESC, s.architecture ASC'''
    bad_files = []
    for root, dirs, files in os.walk(directory):
        if not files:
            continue
        suite, arch = root.rsplit('/', 2)[1:]
        for file in files:
            # different file have differnt name patterns and different splitting needs
            if file.endswith('.diff.gz'):
                rsplit_level = 2
            elif file.endswith('.gz'):
                rsplit_level = 3
            else:
                rsplit_level = 2
            try:
                pkg, version = file.rsplit('.', rsplit_level)[0].rsplit('_', 1)
            except ValueError:
                log.critical(
                    bcolors.FAIL + '/'.join([root, file]) +
                    ' does not seem to be a file that should be there' +
                    bcolors.ENDC)
                continue
            try:
                rversion = query_db(
                    query.format(pkg=pkg, suite=suite, arch=arch))[0][0]
            except IndexError:  # that package is not known (or not yet tested)
                rversion = ''  # continue towards the "bad file" path
            if strip_epoch(rversion) != version:
                try:
                    if os.path.getmtime('/'.join([root, file
                                                  ])) < time.time() - 86400:
                        os.remove('/'.join([root, file]))
                        log.warning(
                            '/'.join([root, file]) +
                            ' should not be there and and was older than a day so it was removed.'
                        )
                    else:
                        bad_files.append('/'.join([root, file]))
                        log.info(
                            '/'.join([root, file]) +
                            ' should not be there, but is also less than 24h old and will probably soon be gone. Probably diffoscope is running on that package right now.'
                        )
                except FileNotFoundError:
                    pass  # that bad file is already gone.
    return bad_files
def lack_rbuild():
    log.info('running lack_rbuild check...')
    bad_pkgs = []
    query = '''SELECT s.name, r.version, s.suite, s.architecture
               FROM sources AS s JOIN results AS r ON r.package_id=s.id
               WHERE r.status NOT IN ('blacklisted', '')
               ORDER BY s.name ASC, s.suite DESC, s.architecture ASC'''
    results = query_db(query)
    for pkg, version, suite, arch in results:
        rbuild = os.path.join(RBUILD_PATH, suite, arch) + \
                '/{}_{}.rbuild.log.gz'.format(pkg, strip_epoch(version))
        if not os.access(rbuild, os.R_OK):
            bad_pkgs.append((pkg, version, suite, arch))
            log.warning(suite + '/' + arch + '/' + pkg + ' (' + version +
                        ') has been '
                        'built, but a buildlog is missing.')
    return bad_pkgs
def alien_buildinfo():
    log.info('running alien_buildinfo check...')
    query = '''SELECT r.version
               FROM sources AS s JOIN results AS r ON r.package_id=s.id
               WHERE r.status != '' AND s.name='{pkg}' AND s.suite='{suite}'
               AND s.architecture='{arch}'
               AND r.status IN ('reproducible', 'unreproducible')
               ORDER BY s.name ASC, s.suite DESC, s.architecture ASC'''
    bad_files = []
    for root, dirs, files in os.walk(BUILDINFO_PATH):
        if not files:
            continue
        suite, arch = root.rsplit('/', 2)[1:]
        for file in files:
            try:
                pkg, version = file.rsplit('.', 1)[0].split('_')[:2]
            except ValueError:
                log.critical(
                    bcolors.FAIL + '/'.join([root, file]) +
                    ' does not seem to be a file that should be there' +
                    bcolors.ENDC)
                continue
            try:
                rversion = query_db(
                    query.format(pkg=pkg, suite=suite, arch=arch))[0][0]
            except IndexError:  # that package is not known (or not yet tested)
                rversion = ''  # continue towards the "bad file" path
            if strip_epoch(rversion) != version:
                try:
                    if os.path.getmtime('/'.join([root, file
                                                  ])) < time.time() - 86400:
                        os.remove('/'.join([root, file]))
                        log.warning(
                            '/'.join([root, file]) +
                            ' should not be there and and was older than a day so it was removed.'
                        )
                    else:
                        bad_files.append('/'.join([root, file]))
                        log.info(
                            '/'.join([root, file]) +
                            ' should not be there, but is also less than 24h old and will probably soon be gone.'
                        )
                except FileNotFoundError:
                    pass  # that bad file is already gone.
    return bad_files
def not_unrep_with_dbd_file():
    log.info('running not_unrep_with_dbd_file check...')
    bad_pkgs = []
    query = '''SELECT s.name, r.version, s.suite, s.architecture
               FROM sources AS s JOIN results AS r ON r.package_id=s.id
               WHERE r.status != 'unreproducible'
               ORDER BY s.name ASC, s.suite DESC, s.architecture ASC'''
    results = query_db(query)
    for pkg, version, suite, arch in results:
        eversion = strip_epoch(version)
        dbd = DBD_PATH + '/' + suite + '/' + arch + '/' + pkg + '_' + \
            eversion + '.diffoscope.html'
        if os.access(dbd, os.R_OK):
            bad_pkgs.append((pkg, version, suite, arch))
            log.warning(dbd + ' exists but ' + suite + '/' + arch + '/' + pkg +
                        ' (' + version + ')'
                        ' is not unreproducible.')
    return bad_pkgs
def lack_buildinfo():
    log.info('running lack_buildinfo check...')
    bad_pkgs = []
    query = '''SELECT s.name, r.version, s.suite, s.architecture
               FROM sources AS s JOIN results AS r ON r.package_id=s.id
               WHERE r.status NOT IN
                ('blacklisted', 'not for us', 'FTBFS', 'depwait', '404', '')
               ORDER BY s.name ASC, s.suite DESC, s.architecture ASC'''
    results = query_db(query)
    for pkg, version, suite, arch in results:
        eversion = strip_epoch(version)
        buildinfo = BUILDINFO_PATH + '/' + suite + '/' + arch + '/' + pkg + \
            '_' + eversion + '_' + arch + '.buildinfo'
        if not os.access(buildinfo, os.R_OK):
            bad_pkgs.append((pkg, version, suite, arch))
            log.warning(suite + '/' + arch + '/' + pkg + ' (' + version +
                        ') has been '
                        'successfully built, but a .buildinfo is missing')
    return bad_pkgs
def not_unrep_with_dbd_file():
    log.info('running not_unrep_with_dbd_file check...')
    bad_pkgs = []
    query = '''SELECT s.name, r.version, s.suite, s.architecture
               FROM sources AS s JOIN results AS r ON r.package_id=s.id
               WHERE r.status != 'FTBR'
               ORDER BY s.name ASC, s.suite DESC, s.architecture ASC'''
    results = query_db(query)
    for pkg, version, suite, arch in results:
        eversion = strip_epoch(version)
        for prefix, extension in ((
            (DBD_PATH, 'html'),
            (DBDTXT_PATH, 'txt.gz'),
            (DBDJSON_PATH, 'json.gz'),
        )):
            filename = '{}/{}/{}/{}_{}.diffoscope.{}.gz'.format(
                prefix, suite, arch, pkg, eversion, extension)
            if not os.access(filename, os.R_OK):
                continue
            bad_pkgs.append((pkg, version, suite, arch))
            log.warning(filename + ' exists but ' + suite + '/' + arch + '/' + pkg + ' (' + version + ')'
                        ' is not FTBR.')
    return bad_pkgs
Example #9
0
def gen_packages_html(packages, no_clean=False):
    """
    generate the /rb-pkg/package.HTML pages.
    packages should be a list of Package objects.
    """
    total = len(packages)
    log.info('Generating the pages of ' + str(total) + ' package(s)')
    for package in sorted(packages, key=lambda x: x.name):
        assert isinstance(package, Package)
        gen_history_page(package)
        for arch in ARCHS:
            gen_history_page(package, arch)

        pkg = package.name

        notes_uri = ''
        notes_file = NOTES_PATH + '/' + pkg + '_note.html'
        if os.access(notes_file, os.R_OK):
            notes_uri = NOTES_URI + '/' + pkg + '_note.html'

        for suite in SUITES:
            for arch in ARCHS:

                status = package.builds[suite][arch].status
                version = package.builds[suite][arch].version
                build_date = package.builds[suite][arch].build_date
                if status is None:  # the package is not in the checked suite
                    continue
                log.debug('Generating the page of %s/%s/%s @ %s built at %s',
                          pkg, suite, arch, version, build_date)

                suitearch_section_html, default_view, reproducible = \
                    gen_suitearch_section(package, suite, arch)

                history_uri = '{}/{}.html'.format(HISTORY_URI, pkg)
                history_archs = []
                for a in ARCHS:
                    history_archs.append({
                        'history_arch':
                        a,
                        'history_arch_uri':
                        '{}/{}/{}.html'.format(HISTORY_URI, a, pkg)
                    })
                project_links = renderer.render(project_links_template)
                desturl = '{}{}/{}/{}/{}.html'.format(
                    REPRODUCIBLE_URL,
                    RB_PKG_URI,
                    suite,
                    arch,
                    pkg,
                )

                navigation_html = renderer.render(
                    package_navigation_template, {
                        'package': pkg,
                        'suite': suite,
                        'arch': arch,
                        'version': version,
                        'history_uri': history_uri,
                        'history_archs': history_archs,
                        'notes_uri': notes_uri,
                        'notify_maintainer': package.notify_maint,
                        'suitearch_section_html': suitearch_section_html,
                        'project_links_html': project_links,
                        'reproducible': reproducible,
                        'dashboard_url': DISTRO_URL,
                        'desturl': desturl,
                    })

                body_html = renderer.render(package_page_template, {
                    'default_view': default_view,
                })

                destfile = os.path.join(RB_PKG_PATH, suite, arch,
                                        pkg + '.html')
                title = pkg + ' - reproducible builds result'
                write_html_page(title=title,
                                body=body_html,
                                destfile=destfile,
                                no_header=True,
                                noendpage=True,
                                left_nav_html=navigation_html)
                log.debug("Package page generated at " + desturl)

                # Optionally generate a page in which the main iframe shows the
                # diffoscope results by default. Needed for navigation between
                # diffoscope pages for different suites/archs
                eversion = strip_epoch(version)
                dbd_links = get_and_clean_dbd_links(pkg, eversion, suite, arch,
                                                    status)
                # only generate the diffoscope page if diffoscope results exist
                if 'dbd_uri' in dbd_links:
                    body_html = renderer.render(
                        package_page_template, {
                            'default_view': dbd_links['dbd_uri'],
                        })
                    destfile = dbd_links['dbd_page_file']
                    desturl = REPRODUCIBLE_URL + "/" + dbd_links['dbd_page_uri']
                    title = "{} ({}) diffoscope results in {}/{}".format(
                        pkg, version, suite, arch)
                    write_html_page(title=title,
                                    body=body_html,
                                    destfile=destfile,
                                    no_header=True,
                                    noendpage=True,
                                    left_nav_html=navigation_html)
                    log.debug("Package diffoscope page generated at " +
                              desturl)

    if not no_clean:
        purge_old_pages()  # housekeep is always good
Example #10
0
def gen_suitearch_section(package, current_suite, current_arch):
    # keep track of whether the package is entirely reproducible
    final_version = ''
    final_status = ''

    default_view = ''
    context = {}
    context['architectures'] = []
    for a in ARCHS:

        suites = []
        for s in SUITES:

            status = package.builds[s][a].status
            if not status:  # The package is not available in that suite/arch
                continue
            version = package.builds[s][a].version

            if not final_version or not final_status:
                final_version = version
                final_status = status
            else:
                final_status, final_version = determine_reproducibility(
                    final_status, final_version, status, version)

            build_date = package.builds[s][a].build_date
            status = Status.get(status)

            if not (build_date and status != 'blacklisted'):
                build_date = ''

            li_classes = ['suite']
            if s == current_suite and a == current_arch:
                li_classes.append('active')
            package_uri = ('{}/{}/{}/{}.html').format(RB_PKG_URI, s, a,
                                                      package.name)

            suitearch_details_html = ''
            if (s == current_suite and a == current_arch):
                suitearch_details_html, default_view = gen_suitearch_details(
                    package.name, version, s, a, status.value.name,
                    status.value.spokenstatus, build_date)

            dbd_links = get_dbd_links(package.name, strip_epoch(version), s, a)
            dbd_page_uri = dbd_links.get('dbd_page_uri', '')
            suites.append({
                'package':
                package.name,
                'package_quote_plus':
                urllib.parse.quote_plus(package.name),
                'status':
                status.value.name,
                'version':
                version,
                'build_date':
                build_date,
                'icon':
                status.value.icon,
                'spokenstatus':
                status.value.spokenstatus,
                'li_classes':
                ' '.join(li_classes),
                'arch':
                a,
                'suite':
                s,
                'untested':
                status == Status.UNTESTED,
                'current_suitearch':
                s == current_suite and a == current_arch,
                'package_uri':
                package_uri,
                'suitearch_details_html':
                suitearch_details_html,
                'dbd_page_uri':
                dbd_page_uri
            })

        if len(suites):
            context['architectures'].append({
                'arch': a,
                'suites': suites,
            })

    html = renderer.render(suitearch_section_template, context)
    reproducible = True if final_status == 'reproducible' else False
    return html, default_view, reproducible
Example #11
0
def gen_suitearch_details(package, version, suite, arch, status, spokenstatus,
                          build_date):
    eversion = strip_epoch(version)  # epoch_free_version is too long
    pkg = Package(package)
    build = pkg.builds[suite][arch]

    context = {}
    default_view = ''

    # Make notes the default default view
    notes_file = NOTES_PATH + '/' + package + '_note.html'
    notes_uri = NOTES_URI + '/' + package + '_note.html'
    if os.access(notes_file, os.R_OK):
        default_view = notes_uri

    # Get summary context
    context['status_html'] = gen_status_link_icon(status, spokenstatus, None,
                                                  suite, arch)
    context['build_date'] = build_date

    # Get diffoscope differences context
    dbd_links = get_dbd_links(package, eversion, suite, arch)
    dbd_uri = dbd_links.get('dbd_uri', '')
    if dbd_uri:
        context['dbd'] = {
            'dbd_page_uri': dbd_links['dbd_page_uri'],
            'dbdtxt_uri': dbd_links.get('dbdtxt_uri', ''),
            'dbdjson_uri': dbd_links.get('dbdjson_uri', ''),
        }
        default_view = default_view if default_view else dbd_uri

    # Get buildinfo context
    if build.buildinfo:
        context['buildinfo_uri'] = build.buildinfo.url
        default_view = default_view if default_view else build.buildinfo.url
    elif not args.ignore_missing_files and status not in \
        ('untested', 'blacklisted', 'FTBFS', 'NFU', 'depwait', '404'):
        log.critical('buildinfo not detected at ' + build.buildinfo.path)

    # Get rbuild, build2 and build diffs context
    if build.rbuild:
        context['rbuild_uri'] = build.rbuild.url
        context['rbuild_size'] = sizeof_fmt(build.rbuild.size)
        default_view = default_view if default_view else build.rbuild.url
        context['buildlogs'] = {}
        if build.build2 and build.logdiff:
            context['buildlogs']['build2_uri'] = build.build2.url
            context['buildlogs']['build2_size'] = build.build2.size
            context['buildlogs']['diff_uri'] = build.logdiff.url
        else:
            log.error('Either {} or {} is missing'.format(
                build.build2.path, build.logdiff.path))
    elif status not in ('untested', 'blacklisted') and \
         not args.ignore_missing_files:
        log.critical(
            DISTRO_URL + '/' + suite + '/' + arch + '/' + package +
            ' didn\'t produce a buildlog, even though it has been built.')

    context['has_buildloginfo'] = 'buildinfo_uri' in context or \
                                  'buildlogs' in context or \
                                  'rbuild_uri' in context

    default_view = '/untested.html' if not default_view else default_view
    suitearch_details_html = renderer.render(suitearch_details_template,
                                             context)
    return (suitearch_details_html, default_view)