From c0c77cb6ea0dce1b289a4df4d111e0c13dc0d7a3 Mon Sep 17 00:00:00 2001 From: Emanuele Aina <emanuele.aina@collabora.com> Date: Fri, 5 Nov 2021 13:39:14 +0100 Subject: [PATCH] Use more structured reports Rather than using plaintext error messages, use readable codes and structured metadata for errors and updates to make them easier to process. This will be particularly useful for filtering: for instance we preserve the branch information rather than muddling it in the error message. Signed-off-by: Emanuele Aina <emanuele.aina@collabora.com> --- .gitlab-ci.yml | 2 + bin/classes.py | 34 ++++ bin/dashboard | 14 +- bin/packaging-check-invariants | 237 +++++++++++++++++++-------- bin/packaging-updates | 7 +- bin/packaging-updates-upstream-linux | 51 +++--- templates/index.html.jinja2 | 216 +++++++++++++++++++----- 7 files changed, 429 insertions(+), 132 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 6853b9f..15f8794 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -266,6 +266,7 @@ packaging-updates: before_script: - apt update && apt install -y --no-install-recommends python3-debian + python3-gitlab python3-yaml script: - ./bin/packaging-updates @@ -287,6 +288,7 @@ packaging-updates-upstream-linux: ca-certificates git python3-debian + python3-gitlab python3-yaml script: - ./bin/packaging-updates-upstream-linux diff --git a/bin/classes.py b/bin/classes.py index 8820c92..da10349 100644 --- a/bin/classes.py +++ b/bin/classes.py @@ -1,4 +1,5 @@ import dataclasses +import enum import typing import debian.debian_support @@ -15,6 +16,39 @@ RENAMED = { } +class Report(enum.Enum): + def _generate_next_value_(name, start, count, last_values): + return name.lower().replace("_", "-") + + APT_PACKAGE_BINARIES_AMBIGUOUS = enum.auto() + APT_PACKAGE_BINARIES_MISSING = enum.auto() + APT_PACKAGE_MISSING = enum.auto() + APT_PACKAGE_MISSING_BUT_ON_OBS = enum.auto() + APT_PACKAGE_SOURCE_AMBIGUOUS = enum.auto() + APT_PACKAGE_SOURCE_MISSING = enum.auto() + APT_PACKAGE_SOURCE_VERSION_MISMATCH_BINARIES = enum.auto() + APT_PACKAGE_VERSION_MISMATCH_OBS = enum.auto() + GIT_BRANCH_COMPONENT_MISSING = enum.auto() + GIT_BRANCH_FOLDED_BUT_NOT_REMOVED = enum.auto() + GIT_BRANCH_HAS_AMBIGUOUS_TAGS = enum.auto() + GIT_BRANCH_LICENSING_REPORT_MISSING = enum.auto() + GIT_BRANCH_MISSING_BUT_ON_OBS = enum.auto() + GIT_BRANCH_NOT_POINTING_TO_TAGGED_COMMIT = enum.auto() + GIT_BRANCH_PIPELINE_FAILED = enum.auto() + GIT_CHANNEL_LAGGING = enum.auto() + GIT_PROJECT_MISSING = enum.auto() + GIT_UPSTREAM_BRANCH_DROPPED = enum.auto() + GIT_UPSTREAM_BRANCH_NOT_MERGED = enum.auto() + OBS_PACKAGE_AMBIGUOUS = enum.auto() + OBS_PACKAGE_BUILD_FAILED = enum.auto() + OBS_PACKAGE_MISSING_BUT_IN_GIT = enum.auto() + OBS_PACKAGE_MISSING_BUT_ON_APT = enum.auto() + OBS_PACKAGE_MISSING_BUT_PUBLISHED = enum.auto() + OBS_PACKAGE_VERSION_MISMATCH = enum.auto() + UPDATE_AVAILABLE = enum.auto() + UPDATE_AVAILABLE_MAINLINE = enum.auto() + + @dataclasses.dataclass class UpstreamPackage: name: str diff --git a/bin/dashboard b/bin/dashboard index bd49c6e..78a7f9c 100755 --- a/bin/dashboard +++ b/bin/dashboard @@ -11,12 +11,22 @@ import jinja2 import yaml +def count_reports(package, func): + reports = package.get("reports", []) + return sum(1 for r in reports if func(r)) + + def preprocess_packaging_data(data): packages = data["packages"].values() summary = { "total_downstream_packages": sum(int("git" in p) for p in packages), - "total_errors_count": sum(len(p.get("errors", [])) for p in packages), - "total_updates_count": sum(len(p.get("updates", [])) for p in packages), + "total_errors_count": sum( + count_reports(p, lambda r: r["severity"] == "error") for p in packages + ), + "total_updates_count": sum( + count_reports(p, lambda r: r["kind"].startswith("update-available")) + for p in packages + ), } data["summary"] = summary diff --git a/bin/packaging-check-invariants b/bin/packaging-check-invariants index 0c4992d..8d126e4 100755 --- a/bin/packaging-check-invariants +++ b/bin/packaging-check-invariants @@ -5,7 +5,7 @@ import logging import debian.debian_support import yaml -from classes import GitBranch +from classes import GitBranch, Report BINARY_VERSION_IGNORELIST = { "cross-toolchain-base": "generates the binary version from the gcc sources", @@ -107,10 +107,15 @@ class InvariantChecker: self.data = yaml.load(yamlfile, Loader=yaml.CSafeLoader) self.packages = {} - def error(self, package, msg, **kwargs): - logging.error(msg) - errors = self.packages.setdefault(package, {}).setdefault("errors", []) - errors.append(dict(msg=msg, **kwargs)) + def error(self, package, kind, **kwargs): + logging.error( + "%s: %s: %s", + package, + kind.value, + ", ".join(f"{k}={v}" for k, v in kwargs.items()), + ) + reports = self.packages.setdefault(package, {}).setdefault("reports", []) + reports.append(dict(kind=kind.value, severity="error", **kwargs)) def check_source_tags(self): sources = self.data.sources @@ -128,11 +133,11 @@ class InvariantChecker: branch_addon_merged = branch_base in branch.descendant_branches if branch_is_channel and branch_is_addon: if branch_addon_merged: - msg = f"Branch {package.git.path_with_namespace}:{branch.name} has been folded into {branch_base} but has not been removed" self.error( packagename, - msg, + Report.GIT_BRANCH_FOLDED_BUT_NOT_REMOVED, branch=branch.name, + folded_in=branch_base, ) continue tags = [ @@ -143,14 +148,15 @@ class InvariantChecker: if not tags and branch.name in sources: self.error( packagename, - f"Branch {package.git.path_with_namespace}:{branch.name} does not point to a tagged commit", + Report.GIT_BRANCH_NOT_POINTING_TO_TAGGED_COMMIT, branch=branch.name, ) if len(tags) > 1: self.error( packagename, - f'Branch {package.git.path_with_namespace}:{branch.name} has ambiguous version tags: {", ".join(sorted(tags))}', + Report.GIT_BRANCH_HAS_AMBIGUOUS_TAGS, branch=branch.name, + tags=tags, ) source = sources.get(branch.name) if source: @@ -166,8 +172,9 @@ class InvariantChecker: ): self.error( packagename, - f"Package {packagename} not found in the upstreams for {branch.name} but it is still in development channels: {', '.join(development_channels)}", + Report.GIT_UPSTREAM_BRANCH_DROPPED, branch=branch.name, + development_channels=development_channels, ) channels = set( @@ -180,12 +187,13 @@ class InvariantChecker: descendant_channels = set( branch_split_addon(b)[0] for b in branch.descendant_branches ) - unmerged = channels - descendant_channels + unmerged = list(sorted(channels - descendant_channels)) if unmerged: self.error( packagename, - f"Branch {package.git.path_with_namespace}:{branch.name} has not been merged into {', '.join(sorted(unmerged))}", + Report.GIT_UPSTREAM_BRANCH_NOT_MERGED, branch=branch.name, + downstreams=unmerged, ) def check_obs_duplicates(self): @@ -201,8 +209,11 @@ class InvariantChecker: for (releasename, section), obsprojectnames in count.items(): if len(obsprojectnames) == 1: continue - msg = f"Package {obspackagename} is ambiguous on OBS for {releasename}: {', '.join(sorted(obsprojectnames))}" - self.error(obspackagename, msg, projects=obsprojectnames) + self.error( + obspackagename, + Report.OBS_PACKAGE_AMBIGUOUS, + projects=obsprojectnames, + ) def check_git_and_obs_versions(self): """Compare versions in git and OBS to ensure they match @@ -214,45 +225,55 @@ class InvariantChecker: if not set(package).intersection({"git", "obs", "published"}): continue if "published" not in package: - msg = f"Package {packagename} is not published anywhere in the APT repositories" - self.error(packagename, msg) + self.error(packagename, Report.APT_PACKAGE_MISSING) continue if "obs" not in package: - pub = ( - f"{entry.source.source}:{entry.source.component}" - for channel in package.published.values() - for entry in channel - if "source" in entry - ) - msg = f"Package {packagename} is not on OBS anywhere but it is published on {', '.join(pub)}" - self.error(packagename, msg) + # reported elsewhere, see OBS_PACKAGE_MISSING_BUT_ON_APT continue if "git" not in package: obsprojectnames = list(package.obs.keys()) - msg = f"Package {packagename} has no git repository" - self.error(packagename, msg, projects=obsprojectnames) + self.error( + packagename, Report.GIT_PROJECT_MISSING, obsprojects=obsprojectnames + ) continue for branchname, branch in package.git.branches.items(): - if branchname not in self.data.channels: + channel = self.data.channels.get(branchname) + if not channel: continue # when a branch does not point to a tagged commit we can't easily detect the version if not branch.version: continue branch_version_without_epoch = branch.version.split(":", 1)[-1] - prefix = branch_to_obs_project(branchname) - projects = { - name.rsplit(":", 1)[0]: p for name, p in package.obs.items() - } - project = projects.get(prefix) + component = branch.component + if not component: + if channel.release < "v2021": + # prior to v2021 the component was not tracked in git + continue + self.error( + packagename, + Report.GIT_BRANCH_COMPONENT_MISSING, + branch=branchname, + ) + continue + projectname = branch_to_obs_project(branchname, component) + project = package.obs.get(projectname) if not project: - msg = f"Branch {package.git.path_with_namespace}:{branchname} has no matching package on OBS" - self.error(packagename, msg, branch=branchname) + self.error( + packagename, + Report.OBS_PACKAGE_MISSING_BUT_IN_GIT, + branch=branchname, + obsproject=projectname, + ) continue if branch_version_without_epoch != project.version_without_epoch: - msg = f"Branch {package.git.path_with_namespace}:{branchname} has version {branch_version_without_epoch} which does not match version {project.version_without_epoch} in {project.project} on OBS" self.error( - packagename, msg, branch=branchname, projects=[project.project] + packagename, + Report.OBS_PACKAGE_VERSION_MISMATCH, + branch=branchname, + gitversion=branch_version_without_epoch, + obsversion=project.version_without_epoch, + obsproject=project.project, ) for obspackage in package.obs.values(): branch, component = obs_project_to_branch(obspackage.project) @@ -261,8 +282,12 @@ class InvariantChecker: f"Checking that {obspackage.project}:{package.name} has a branch {branch} in git" ) if branch not in package.git.branches: - msg = f"Package {packagename} from {obspackage.project} has no {branch} branch in git" - self.error(packagename, msg, projects=[obspackage.project]) + self.error( + packagename, + Report.GIT_BRANCH_MISSING_BUT_ON_OBS, + branch=branch, + obsproject=obspackage.project, + ) logging.debug( f"Checking publishing status of {package.name} branch {branch} in {component} component" @@ -272,8 +297,11 @@ class InvariantChecker: lambda p: "source" in p and p.source.component == component, ) if not published: - msg = f"Package {packagename} from {obspackage.project} has not been published in the APT source repositories" - self.error(packagename, msg, projects=[obspackage.project]) + self.error( + packagename, + Report.APT_PACKAGE_MISSING_BUT_ON_OBS, + obsproject=obspackage.project, + ) continue version = published.source.version published_version_without_epoch = version.split(":", 1)[-1] @@ -281,8 +309,15 @@ class InvariantChecker: f"Package {package.name} {published_version_without_epoch} published on {branch}/{component}" ) if published_version_without_epoch != obspackage.version_without_epoch: - msg = f"Package {packagename} from {obspackage.project} has version {obspackage.version_without_epoch} but version {published_version_without_epoch} has been published in the APT repositories" - self.error(packagename, msg, projects=[obspackage.project]) + self.error( + packagename, + Report.APT_PACKAGE_VERSION_MISMATCH_OBS, + branch=branch, + component=component, + aptversion=published_version_without_epoch, + obsversion=obspackage.version_without_epoch, + obsproject=obspackage.project, + ) def check_published_packages(self): """Compare source and binary published versions to ensure they match @@ -300,68 +335,110 @@ class InvariantChecker: ) sources = [p["source"] for p in entry if "source" in p] if len(sources) < 1: - msg = f"Package {packagename} branch {branch} has no source published in the APT repositories" - self.error(packagename, msg) + self.error( + packagename, Report.APT_PACKAGE_SOURCE_MISSING, branch=branch + ) continue if len(sources) > 1: - msg = f"Package {packagename} branch {branch} has multiple sources published" - self.error(packagename, msg) + srcs = [ + {"component": s["component"], "version": s["version"]} + for s in sources + ] + self.error( + packagename, + Report.APT_PACKAGE_SOURCE_AMBIGUOUS, + branch=branch, + sources=srcs, + ) continue - source = max(sources, key=lambda s: debian.debian_support.Version(s["version"])) + source = max( + sources, key=lambda s: debian.debian_support.Version(s["version"]) + ) obsproject = branch_to_obs_project(branch, source.component) logging.debug( f"Checking if {packagename} branch {branch}/{source.component} is in {obsproject}" ) if obsproject not in package.get("obs", {}): - msg = f"Package {packagename} branch {branch}/{source.component} not found in {obsproject} on OBS" - self.error(packagename, msg) + self.error( + packagename, + Report.OBS_PACKAGE_MISSING_BUT_ON_APT, + branch=branch, + source=dict( + component=source.component, + version=source.version, + ), + obsproject=obsproject, + ) logging.debug( f"Checking binaries are only published for one component for {package.name} branch {branch}" ) binaries = list(filter(lambda p: "binaries" in p, entry)) if len(binaries) < 1: - msg = f"Package {packagename} branch {branch} has no binary published in the APT repositories" - self.error(packagename, msg) + self.error( + packagename, Report.APT_PACKAGE_BINARIES_MISSING, branch=branch + ) continue if len(binaries) > 1: - pub = (f"{binary.component}" for binary in binaries) - msg = f"Package {packagename} branch {branch} has binaries published from multiple components: {', '.join(pub)}" - self.error(packagename, msg) + pub = [binary.component for binary in binaries] + self.error( + packagename, + Report.APT_PACKAGE_BINARIES_AMBIGUOUS, + branch=branch, + components=pub, + ) continue logging.debug( f"Checking version mismatch between source and binaries for {package.name} branch {branch}" ) - for pkg in binaries[0].binaries: + pkgs = binaries[0].binaries + component = binaries[0].component + for pkg in pkgs: for name, pkg_entry in pkg.items(): for version, archs in pkg_entry.items(): if not source_binary_versions_match( source.version, version ): - msg = f"Package {packagename} branch {branch} version mismatch between source ({source.version}) and binary {name} ({version}, {', '.join(archs)})" ignore_reason = BINARY_VERSION_IGNORELIST.get( packagename ) if ignore_reason: - logging.info(f"{msg}: ignoring, {ignore_reason}") + logging.info( + f"{packagename}: Mismatch version source {source.version} and binary {name} {version} ignoring, {ignore_reason}" + ) else: - self.error(packagename, msg) + self.error( + packagename, + Report.APT_PACKAGE_SOURCE_VERSION_MISMATCH_BINARIES, + branch=branch, + source=dict( + version=source.version, + component=source.component, + ), + binary=dict( + component=component, + name=name, + version=version, + architectures=archs, + ), + ) def check_pipelines(self): for obspackagename, package in self.data.packages.items(): for branch in package.get("git", {}).get("branches", {}).values(): - msg = f"Pipeline failed on {package.git.path_with_namespace}:{branch.name}" ignore_reason = FAILED_PIPELINE_BRANCH_IGNORELIST.get(branch.name) if ignore_reason: - logging.info(f"{msg}: ignoring, {ignore_reason}") + logging.info( + f"{obspackagename}: Pipeline failed on {package.git.path_with_namespace}:{branch.name}, ignoring, {ignore_reason}" + ) continue if branch.get("pipeline", {}).get("status") != "failed": continue self.error( obspackagename, - msg, + Report.GIT_BRANCH_PIPELINE_FAILED, branch=branch.name, pipeline_web_url=branch.pipeline.web_url, ) @@ -374,20 +451,28 @@ class InvariantChecker: if not branches: return # coalesce things like `apertis/v2020` and `apertis/v2020-security` as `apertis/v2020` - channels = list({b.name.split("-", 1)[0]: b.version for b in branches}.items()) - latest_channel, latest_version = channels.pop() - for channel, version in channels: + channels = list( + {b.name.split("-", 1)[0]: (b.name, b.version) for b in branches}.items() + ) + latest_channel, (latest_branch, latest_version) = channels.pop() + for channel, (branch, version) in channels: release = channel.split("/", 1)[-1] # ignore things like base-files where each branch targets a different release if release in version: continue if version != latest_version: - msg = f"Channel {package.git.path_with_namespace}:{channel} has version {version} which lags behind {latest_channel} with version {latest_version}" ignore_reason = LAGGING_VERSION_BRANCH_IGNORELIST.get(channel) if ignore_reason: - logging.info(f"{msg}: ignoring, {ignore_reason}") + logging.info( + f"{package.name}: {channel} lags behind with {branch}:{version} compared to {latest_branch}:{latest_version}: ignoring, {ignore_reason}" + ) continue - self.error(package.name, msg) + self.error( + package.name, + Report.GIT_CHANNEL_LAGGING, + branch=branch, + latest_branch=latest_branch, + ) def catch_missing_updates(self): """Compare versions in branches and ensure they are all aligned. @@ -426,9 +511,14 @@ class InvariantChecker: for repository, result in repositories.items(): if result.code in ok_codes: continue - details = result.get("details", "") - msg = f"Package {obspackagename} failed to build on {projectname}/{arch}/{repository}: <{result.code}> {details}" - self.error(obspackagename, msg, projects=[projectname]) + self.error( + obspackagename, + Report.OBS_PACKAGE_BUILD_FAILED, + obsproject=projectname, + architecture=arch, + repository=repository, + result=dict(result), + ) def check_missing_license_report(self): for package in self.data.packages.values(): @@ -441,8 +531,11 @@ class InvariantChecker: f"Checking if license report is found for {package.name} branch {branch.name}" ) if branch.component == "target" and not branch.license_report: - msg = f"Package {package.name} does not have a license report on branch {branch.name}" - self.error(package.name, msg) + self.error( + package.name, + Report.GIT_BRANCH_LICENSING_REPORT_MISSING, + branch=branch.name, + ) if __name__ == "__main__": diff --git a/bin/packaging-updates b/bin/packaging-updates index 5d3ba63..5af7b42 100755 --- a/bin/packaging-updates +++ b/bin/packaging-updates @@ -8,6 +8,7 @@ import types import debian.debian_support import yaml +from classes import Report def base_for_source(data, sourcename): @@ -36,14 +37,16 @@ def compute_updates(data): msg += f" can be updated to {upstream['version']}" logging.info(msg) update = { - "branch": {"name": branch.name, "version": branch.version}, + "kind": Report.UPDATE_AVAILABLE.value, + "severity": "info", + "branch": branch.name, "upstream": upstream, } if base: update["base"] = {"name": base.name, "version": base.version} p = ret["packages"].setdefault(package.name, {}) p["git"] = {"path_with_namespace": package.git["path_with_namespace"]} - p.setdefault("updates", []).append(update) + p.setdefault("reports", []).append(update) for package in data["packages"].values(): if "git" not in package: diff --git a/bin/packaging-updates-upstream-linux b/bin/packaging-updates-upstream-linux index ef092fc..4445c82 100755 --- a/bin/packaging-updates-upstream-linux +++ b/bin/packaging-updates-upstream-linux @@ -10,6 +10,7 @@ from collections import defaultdict import debian.debian_support import yaml +from classes import Report def base_latest_version(entries, package): @@ -48,13 +49,23 @@ def compute_linux_updates(data): logging.debug("No entry for linux found in the packaging data, skipping") return ret - def error(package, err): - name = package["name"] + def report(severity, packagename, kind, **kwargs): + logfunc = getattr(logging, severity) + logfunc( + "%s: %s: %s", + packagename, + kind.value, + ", ".join(f"{k}={v}" for k, v in kwargs.items()), + ) packages = ret["packages"] - p = packages.setdefault(name, {}) - logging.error(f"{name}: {err['branch']}: {err['msg']}") - errors = p.setdefault("errors", []) - errors.append(err) + reports = packages.setdefault(packagename, {}).setdefault("reports", []) + reports.append(dict(kind=kind.value, severity=severity, **kwargs)) + + def update(packagename, kind, **kwargs): + report("info", packagename, kind, **kwargs) + + def error(packagename, kind, **kwargs): + report("error", packagename, kind, **kwargs) branch_versions = {} url = "https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git" @@ -87,11 +98,12 @@ def compute_linux_updates(data): ) downstream_version = debian.debian_support.Version(version) if downstream_version < debian_version: - err = { - "branch": debian_base, - "msg": f"Branch {debian_base}={debian_version} has not been merged into {branch}={version}", - } - error(package, err) + error( + package["name"], + Report.GIT_UPSTREAM_BRANCH_NOT_MERGED, + branch=debian_base, + downstreams=[branch], + ) continue # Fetch the versions available upstream for the downstream tag (for e.g. 5.4) using @@ -128,15 +140,16 @@ def compute_linux_updates(data): logging.debug(f"{branch}: Downstream has {version}, git has {latest_tag}") downstream_version = debian.debian_support.Version(version) if downstream_version < latest_tag: - err = { - "branch": branch, - "msg": f"Branch {branch}={version} lags behind upstream {latest_tag}", - "git": { - "url": url, - "tag": f"v{latest_tag}", - }, + mainline = { + "url": f"{url}/tag/?h=v{latest_tag}", + "tag": f"v{latest_tag}", } - error(package, err) + update( + package["name"], + Report.UPDATE_AVAILABLE_MAINLINE, + branch=branch, + mainline=mainline, + ) return ret diff --git a/templates/index.html.jinja2 b/templates/index.html.jinja2 index 810c9f9..dcc82e2 100644 --- a/templates/index.html.jinja2 +++ b/templates/index.html.jinja2 @@ -1,5 +1,69 @@ {% extends "base.html.jinja2" %} +{%- macro badge_class(severity) -%} + {%- if severity == "error" -%} + danger + {%- else -%} + {{ severity }} + {%- endif -%} +{%- endmacro -%} + +{%- macro branch_link(package, branchname, show_version=False, classes="") -%} + {%- if "git" in package -%} + <a class="{{classes}}" href="{{ package.git.web_url }}/-/commits/{{ branchname }}">{{ branchname }}</a> + {%- if show_version and branchname in package.git.branches and package.git.branches[branchname].version -%} + /<code>{{ package.git.branches[branchname].version }}</code> + {%- endif -%} + {%- else -%} + <span class="{{classes}}">{{ branchname }}</span> + {%- endif -%} +{%- endmacro -%} + +{%- macro branch(package, branchname) -%} + <span class="branch">{{ branch_link(package, branchname, show_version=True) }}</span> +{%- endmacro -%} + +{%- macro obsproject(package, project, version=None) -%} + <span class="obsproject"> + <a href="{{ obs[project].web_url }}">{{ project }}</a>/ + {%- if "obs" in package and project in package.obs -%} + <a href="{{ package.obs[project].web_url }}"> + {{- package.name -}} + {%- if version -%} + /<code>{{ version }}</code> + {%- endif -%} + </a> + {%- else -%} + {{ package.name }} + {%- endif -%} + </span> +{%- endmacro -%} + +{%- macro obsprojects(package, projects) -%} + <span class="obsprojects"> + {%- for project in projects -%} + {{ obsproject(package, project) }} + {%- endfor -%} + </span> +{%- endmacro -%} + +{%- macro aptsources(package, sources) -%} + <span class="aptsources"> + {%- for source in sources -%} + {{ source.component }}/<code>{{ source.version }}</code> + {%- if not loop.last %}, {% endif -%} + {%- endfor -%} + </span> +{%- endmacro -%} + +{%- macro pipeline(package, pipeline_web_url) -%} + {%- if pipeline_web_url -%} + <span class="pipeline"> + <a href="{{ pipeline_web_url }}">pipeline 🚀</a> + </span> + {%- endif -%} +{%- endmacro -%} + {% block title %} Packages {% endblock %} {% block summary %} @@ -36,9 +100,11 @@ {% block content %} <div class="list-group"> - {% for package in packages.values() %} - {% if package.errors or package.updates %} - <div id="pkg-{{package.name}}" class="list-group-item list-group-item-action flex-column align-items-start"> + {% for package in packages.values() if package.reports or package.updates %} + <div + id="pkg-{{package.name}}" + data-package="{{package.name}}" + class="package list-group-item list-group-item-action flex-column align-items-start"> <div class="d-flex w-100 justify-content-between"> <h5 class="mb-1"> <strong> @@ -52,43 +118,119 @@ </h5> </div> - {% for error in package.errors %} - <p class="mb-1"> - <big><span class="badge badge-danger">error</span></big> - - {% if error.branch %} - Branch <a href="{{ package.git.web_url }}/-/commits/{{ error.branch }}">{{ error.branch }}</a> - {% if package.git.branches[error.branch].version %} - (<code>{{ package.git.branches[error.branch].version }}</code>) - {%- endif %}: - {% endif %} - - {% if error.projects %} - Projects - {% for project in error.projects %} - <a href="{{ obs[project].web_url }}">{{ project }}</a>/<a href="{{ package.obs[project].web_url }}">{{ package.name }}</a> - {%- if not loop.last %},{% endif %} - {%- endfor %}: - {% endif %} - - {{ error.msg }} - - {%- if error.pipeline_web_url -%} - : <a href="{{ error.pipeline_web_url }}">pipeline 🚀</a> - {% endif %} - </p> - {% endfor %} + {% for report in package.reports %} + {%- set report_channels = [] -%} + {%- if report.kind == "git-upstream-branch-not-merged" -%} + {%- for downstream in report.downstreams -%} + {{- report_channels.append(channels[downstream].base) or "" -}} + {%- endfor -%} + {%- elif report.kind == "update-available" -%} + {%- for channel in channels.values() if channel.source.distribution+"/"+channel.source.release == sources[report.upstream.source].base -%} + {{- report_channels.append(channel.base) or "" -}} + {%- endfor -%} + {%- elif report.branch in sources -%} + {%- for channel in channels.values() if channel.source.distribution+"/"+channel.source.release == sources[report.branch].base -%} + {{- report_channels.append(channel.base) or "" -}} + {%- endfor -%} + {%- elif report.branch in channels -%} + {{- report_channels.append(channels[report.branch].base) or "" -}} + {%- endif -%} + <p + data-report="{{ report.kind }}" + data-severity="{{ report.severity }}" + data-channels="{{- report_channels|sort|unique|join(" ") -}}" + class="mb-1 report"> + <big> + <span class="report-severity badge badge-{{ badge_class(report.severity) }} border border-{{ badge_class(report.severity) }}"> + {% if report.kind.startswith("update-") -%} + update + {%- else -%} + {{ report.severity }} + {%- endif %} + </span> + </big> + {% for channel in (report_channels + [report.branch])|select|sort|unique -%} + <big> + {{ branch_link(package, channel, classes="report-branch badge badge-light border border-secondary text-muted") }} + </big> + {%- endfor %} + {% for obsproject in (report.get("obsprojects") or [report.get("obsproject")]) if obsproject -%} + <big> + <a class="report-obsproject badge badge-light border border-secondary text-muted" href="{{obs[obsproject].web_url}}">{{ obsproject }}</a> + </big> + {%- endfor %} - {% for u in package.updates %} - <p class="mb-1"><big><span class="badge badge-info">update</span></big> - Branch {{u.branch.name}} can be updated from - <a href="{{ package.git.web_url }}/pipelines/new?ref={{ (u.base or u.branch).name|urlencode }}"> - <code>{{u.branch.version}}</code> - to <code>{{u.upstream.version}}</code></a> from {{u.upstream.source}} {{u.upstream.component}} - </p> + {% if report.kind == "apt-package-missing" -%} + Not published on APT + {%- elif report.kind == "apt-package-missing-but-on-obs" -%} + Not published on APT, found on OBS: {{ obsprojects(package, report.obsprojects) }} + {%- elif report.kind == "apt-package-version-mismatch-obs" -%} + Mismatch between APT {{ report.component }}/<code>{{ report.aptversion}}</code> and OBS {{ obsproject(package, report.obsproject, report.obsversion ) }} ({{ branch(package, report.branch) }}) + {%- elif report.kind == "apt-package-source-version-mismatch-binaries" -%} + Mismatch between APT source {{ aptsources(package, [report.source]) }} and binary + {{report.binary.component}}/{{report.binary.name}}/<code>{{report.binary.version}}</code> + on {{ report.binary.architectures|join(", ") }} ({{ branch(package, report.branch) }}) + {%- elif report.kind == "apt-package-source-missing" -%} + Sources not published on APT ({{ branch(package, report.branch) }}) + {%- elif report.kind == "apt-package-source-ambiguous" -%} + Ambiguous sources on APT: {{ aptsources(package, report.sources) }} ({{ branch(package, report.branch) }}) + {%- elif report.kind == "apt-package-binaries-missing" -%} + Binaries not published on APT ({{ branch(package, report.branch) }}) + {%- elif report.kind == "apt-package-binaries-ambiguous" -%} + Ambiguous binaries on APT: {{report.components|join(", ")}} ({{ branch(package, report.branch) }}) + {%- elif report.kind == "git-channel-lagging" -%} + Branch {{ branch(package, report.branch) }} lags behind {{ branch(package, report.latest_branch) }} + {%- elif report.kind == "git-branch-folded-but-not-removed" -%} + Branch {{ branch(package, report.branch) }} folded in {{ branch(package, report.folded_in) }} but not removed + {%- elif report.kind == "git-branch-has-ambiguous-tags" -%} + Branch {{ branch(package, report.branch) }} has ambiguous tags: {{report.tags|join(", ")}} + {%- elif report.kind == "git-branch-missing-but-on-obs" -%} + Branch {{ report.branch }} missing but found on OBS: {{ obsproject(package, report.obsproject) }} + {%- elif report.kind == "git-branch-not-pointing-to-tagged-commit" -%} + Branch {{branch(package, report.branch)}} not pointing to a tagged commit + {%- elif report.kind == "git-project-missing" -%} + GitLab project missing but found on OBS: {{ obsprojects(package, report.obsprojects) }} + {%- elif report.kind == "git-upstream-branch-not-merged" -%} + Upstream {{branch(package, report.branch)}} not merged in + {% for downstream in report.downstreams -%} + {{branch(package, downstream)}} + {%- if not loop.last %}, {% endif -%} + {%- endfor -%} + {%- elif report.kind == "git-upstream-branch-dropped" -%} + Upstream branch {{branch(package, report.branch)}} dropped + {%- elif report.kind == "git-branch-pipeline-failed" -%} + Pipeline failed on {{ branch(package, report.branch) }}: {{ pipeline(package, report.pipeline_web_url) }} + {%- elif report.kind == "git-branch-licensing-report-missing" -%} + Missing licensing report on {{branch(package, report.branch)}} + {%- elif report.kind == "git-branch-component-missing" -%} + Missing component on {{branch(package, report.branch)}} + {%- elif report.kind == "obs-package-ambiguous" -%} + Ambiguous package on OBS: {{ obsprojects(package, report.projects) }} + {%- elif report.kind == "obs-package-missing-but-on-apt" -%} + Missing on OBS {{ obsproject(package, report.obsproject) }}, found on APT in {{ aptsources(package, [report.source]) }} ({{ branch(package, report.branch) }}) + {%- elif report.kind == "obs-package-missing-but-in-git" -%} + Missing on OBS {{ obsproject(package, report.obsproject) }}, found on Git {{ branch(package, report.branch) }} + {%- elif report.kind == "obs-package-version-mismatch" -%} + Mismatch between OBS {{ obsproject(package, report.obsproject, report.obsversion ) }} and Git {{ branch(package, report.branch) }} + {%- elif report.kind == "obs-package-build-failed" -%} + Failed to build on OBS {{ obsproject(package, report.obsproject) }} {{report.repository}}/{{report.architecture}}: + <<code class="build-result-code">{{report.result.code}}</code>> + {%- if "details" in report.result -%} + <span class="build-result-details">{{ report.result.details }}</span> + {%- endif -%} + {%- elif report.kind == "update-available" -%} + Branch {{branch(package, report.branch)}} can be updated + <a href="{{ package.git.web_url }}/pipelines/new?ref={{ (report.base or report.branch).name|urlencode }}"> + to <code>{{report.upstream.version}}</code> from {{report.upstream.source}} {{report.upstream.component}} 🚀 + </a> + {%- elif report.kind == "update-available-mainline" -%} + Branch {{branch(package, report.branch)}} lags behind mainline <a href="{{report.mainline.url}}">{{report.mainline.tag}}</a> + {%- else -%} + Unknown report: {{ report }} + {%- endif %} + </p> {% endfor %} </div> - {% endif %} {% endfor %} </div> {% endblock %} -- GitLab