This is an automated email from the ASF dual-hosted git repository.
jorisvandenbossche pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/arrow.git
The following commit(s) were added to refs/heads/master by this push:
new c5a175dd61 ARROW-16358: [CI][Dev] Allow archery crossbow to generate a
CSV report for nightly builds
c5a175dd61 is described below
commit c5a175dd61b3fa94a38b918c1cc3d7b4ffaefba7
Author: Raúl Cumplido <[email protected]>
AuthorDate: Thu May 19 18:24:11 2022 +0200
ARROW-16358: [CI][Dev] Allow archery crossbow to generate a CSV report for
nightly builds
This PR will allow us to generate a CSV with the nightly builds status.
This will be used in order to generate a static page with the different builds
information and will allow us to analyze historical information from the
different builds like since when they have been failing, last successful build
commit, some tags, etcetera.
An example of a CSV generated can be seen on the following gist:
https://gist.github.com/raulcd/1799c8499ef228dfdf1ab0d986367d2a
Closes #13165 from raulcd/ARROW-16358
Authored-by: Raúl Cumplido <[email protected]>
Signed-off-by: Joris Van den Bossche <[email protected]>
---
dev/archery/archery/crossbow/cli.py | 25 ++++++++++++
dev/archery/archery/crossbow/core.py | 2 -
dev/archery/archery/crossbow/reports.py | 46 +++++++++++++++++++++-
dev/archery/archery/crossbow/tests/test_reports.py | 27 +++++++++++--
4 files changed, 93 insertions(+), 7 deletions(-)
diff --git a/dev/archery/archery/crossbow/cli.py
b/dev/archery/archery/crossbow/cli.py
index c8ae55903c..7507d4d39a 100644
--- a/dev/archery/archery/crossbow/cli.py
+++ b/dev/archery/archery/crossbow/cli.py
@@ -339,6 +339,31 @@ def report_chat(obj, job_name, send, webhook,
extra_message_success,
output.write(report_chat.render("text"))
[email protected]()
[email protected]('job-name', required=True)
[email protected]('--save/--dry-run', default=False,
+ help='Just display the report, don\'t save it')
[email protected]('--fetch/--no-fetch', default=True,
+ help='Fetch references (branches and tags) from the remote')
[email protected]_obj
+def report_csv(obj, job_name, save, fetch):
+ """
+ Generates a CSV report with the different tasks information
+ from a Crossbow run.
+ """
+ output = obj['output']
+ queue = obj['queue']
+ if fetch:
+ queue.fetch()
+
+ job = queue.get(job_name)
+ report = Report(job)
+ if save:
+ ReportUtils.write_csv(report)
+ else:
+ output.write("\n".join([str(row) for row in report.rows]))
+
+
@crossbow.command()
@click.argument('job-name', required=True)
@click.option('-t', '--target-dir',
diff --git a/dev/archery/archery/crossbow/core.py
b/dev/archery/archery/crossbow/core.py
index 40b8f5b68b..55cb87e77f 100644
--- a/dev/archery/archery/crossbow/core.py
+++ b/dev/archery/archery/crossbow/core.py
@@ -957,8 +957,6 @@ class Job(Serializable):
raise ValueError('each `tasks` mus be an instance of Task')
if not isinstance(target, Target):
raise ValueError('`target` must be an instance of Target')
- if not isinstance(target, Target):
- raise ValueError('`target` must be an instance of Target')
if not isinstance(params, dict):
raise ValueError('`params` must be an instance of dict')
diff --git a/dev/archery/archery/crossbow/reports.py
b/dev/archery/archery/crossbow/reports.py
index 111a11d043..88ecefda91 100644
--- a/dev/archery/archery/crossbow/reports.py
+++ b/dev/archery/archery/crossbow/reports.py
@@ -15,12 +15,13 @@
# specific language governing permissions and limitations
# under the License.
-import click
import collections
+import csv
import operator
import fnmatch
import functools
+import click
import requests
from archery.utils.report import JinjaReport
@@ -29,6 +30,17 @@ from archery.utils.report import JinjaReport
# TODO(kszucs): use archery.report.JinjaReport instead
class Report:
+ ROW_HEADERS = [
+ "task_name",
+ "task_status",
+ "build_links",
+ "crossbow_branch_url",
+ "ci_system",
+ "extra_params",
+ "template",
+ "arrow_commit",
+ ]
+
def __init__(self, job, task_filters=None):
self.job = job
@@ -83,6 +95,30 @@ class Report:
def show(self):
raise NotImplementedError()
+ @property
+ def rows(self):
+ """
+ Produces a generator that allow us to iterate over
+ the job tasks as a list of rows.
+ Row headers are defined at Report.ROW_HEADERS.
+ """
+ for task_name, task in sorted(self.job.tasks.items()):
+ task_status = task.status()
+ row = [
+ task_name,
+ task_status.combined_state,
+ task_status.build_links,
+ self.branch_url(task.branch),
+ task.ci,
+ # We want this to be serialized as a dict instead
+ # of an orderedict.
+ {k: v for k, v in task.params.items()},
+ task.template,
+ # Arrow repository commit
+ self.job.target.head
+ ]
+ yield row
+
class ConsoleReport(Report):
"""Report the status of a Job to the console using click"""
@@ -210,6 +246,14 @@ class ReportUtils:
server.sendmail(smtp_user, recipient_email, message)
server.close()
+ @classmethod
+ def write_csv(cls, report, add_headers=True):
+ with open(f'{report.job.branch}.csv', 'w') as csvfile:
+ task_writer = csv.writer(csvfile)
+ if add_headers:
+ task_writer.writerow(report.ROW_HEADERS)
+ task_writer.writerows(report.rows)
+
class EmailReport(JinjaReport):
templates = {
diff --git a/dev/archery/archery/crossbow/tests/test_reports.py
b/dev/archery/archery/crossbow/tests/test_reports.py
index 19e52b8b46..6f9eeec5c9 100644
--- a/dev/archery/archery/crossbow/tests/test_reports.py
+++ b/dev/archery/archery/crossbow/tests/test_reports.py
@@ -76,8 +76,27 @@ def test_crossbow_email_report(load_fixture):
job = load_fixture('crossbow-job.yaml', decoder=yaml.load)
report = Report(job)
assert report.tasks_by_state is not None
- empail_report = EmailReport(report=report, sender_name="Sender Reporter",
- sender_email="[email protected]",
- recipient_email="[email protected]")
+ email_report = EmailReport(report=report, sender_name="Sender Reporter",
+ sender_email="[email protected]",
+ recipient_email="[email protected]")
- assert empail_report.render("text") == textwrap.dedent(expected_msg)
+ assert email_report.render("text") == textwrap.dedent(expected_msg)
+
+
+def test_crossbow_export_report(load_fixture):
+ job = load_fixture('crossbow-job.yaml', decoder=yaml.load)
+ report = Report(job)
+ assert len(list(report.rows)) == 4
+ expected_first_row = [
+ 'docker-cpp-cmake32',
+ 'success',
+ ['https://github.com/apache/crossbow/runs/1'],
+ 'https://github.com/apache/crossbow/tree/'
+ 'ursabot-1-circle-docker-cpp-cmake32',
+ 'circle',
+ {'commands': ['docker-compose build cpp-cmake32',
+ 'docker-compose run cpp-cmake32']},
+ 'docker-tests/circle.linux.yml',
+ 'f766a1d615dd1b7ee706d05102e579195951a61c'
+ ]
+ assert next(report.rows) == expected_first_row