You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@arrow.apache.org by ks...@apache.org on 2022/05/06 18:20:45 UTC
[arrow] branch master updated: ARROW-16448: [CI][Archery] Refactor EmailReport to be a JinjaReport
This is an automated email from the ASF dual-hosted git repository.
kszucs 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 be08e92f52 ARROW-16448: [CI][Archery] Refactor EmailReport to be a JinjaReport
be08e92f52 is described below
commit be08e92f5216b451229eef649f495c049d03d64d
Author: Raúl Cumplido <ra...@gmail.com>
AuthorDate: Fri May 6 20:20:32 2022 +0200
ARROW-16448: [CI][Archery] Refactor EmailReport to be a JinjaReport
This PR tries to move the current `EmailReport` used for the Nightly report emails to a `JinjaReport` and adds a test for the report generation.
I also have tested locally sending an email successfully.
Closes #13074 from raulcd/ARROW-16448
Authored-by: Raúl Cumplido <ra...@gmail.com>
Signed-off-by: Krisztián Szűcs <sz...@gmail.com>
---
dev/archery/archery/crossbow/cli.py | 12 ++-
dev/archery/archery/crossbow/reports.py | 109 +++------------------
.../crossbow/tests/fixtures/email-report.txt | 27 +++++
dev/archery/archery/crossbow/tests/test_reports.py | 17 +++-
.../archery/templates/email_nightly_report.txt.j2 | 59 +++++++++++
5 files changed, 124 insertions(+), 100 deletions(-)
diff --git a/dev/archery/archery/crossbow/cli.py b/dev/archery/archery/crossbow/cli.py
index 6005c22de9..07d00ab3bd 100644
--- a/dev/archery/archery/crossbow/cli.py
+++ b/dev/archery/archery/crossbow/cli.py
@@ -279,8 +279,8 @@ def report(obj, job_name, sender_name, sender_email, recipient_email,
queue.fetch()
job = queue.get(job_name)
- report = EmailReport(
- job=job,
+ email_report = EmailReport(
+ report=Report(job),
sender_name=sender_name,
sender_email=sender_email,
recipient_email=recipient_email
@@ -293,14 +293,16 @@ def report(obj, job_name, sender_name, sender_email, recipient_email,
)
if send:
- report.send(
+ ReportUtils.send_email(
smtp_user=smtp_user,
smtp_password=smtp_password,
smtp_server=smtp_server,
- smtp_port=smtp_port
+ smtp_port=smtp_port,
+ recipient_email=recipient_email,
+ message=email_report.render("text")
)
else:
- report.show(output)
+ output.write(email_report.render("text"))
@crossbow.command()
diff --git a/dev/archery/archery/crossbow/reports.py b/dev/archery/archery/crossbow/reports.py
index 3858106a90..969c6c4f72 100644
--- a/dev/archery/archery/crossbow/reports.py
+++ b/dev/archery/archery/crossbow/reports.py
@@ -20,8 +20,6 @@ import collections
import operator
import fnmatch
import functools
-from io import StringIO
-import textwrap
import requests
@@ -194,105 +192,30 @@ class ReportUtils:
)
return resp
-
-class EmailReport(Report):
-
- HEADER = textwrap.dedent("""
- Arrow Build Report for Job {job_name}
-
- All tasks: {all_tasks_url}
- """)
-
- TASK = textwrap.dedent("""
- - {name}:
- URL: {url}
- """).strip()
-
- EMAIL = textwrap.dedent("""
- From: {sender_name} <{sender_email}>
- To: {recipient_email}
- Subject: {subject}
-
- {body}
- """).strip()
-
- STATUS_HEADERS = {
- # from CombinedStatus
- 'error': 'Errored Tasks:',
- 'failure': 'Failed Tasks:',
- 'pending': 'Pending Tasks:',
- 'success': 'Succeeded Tasks:',
- }
-
- def __init__(self, job, sender_name, sender_email, recipient_email):
- self.sender_name = sender_name
- self.sender_email = sender_email
- self.recipient_email = recipient_email
- super().__init__(job)
-
- def listing(self, tasks):
- return '\n'.join(
- sorted(
- self.TASK.format(
- name=task_name,
- url=self.task_url(task)
- )
- for task_name, task in tasks.items()
- )
- )
-
- def header(self):
- url = self.url(self.job.branch)
- return self.HEADER.format(job_name=self.job.branch, all_tasks_url=url)
-
- def subject(self):
- failures = len(self.tasks_by_state.get("failure", []))
- errors = len(self.tasks_by_state.get("error", []))
- pending = len(self.tasks_by_state.get("pending", []))
- return (
- f"[NIGHTLY] Arrow Build Report for Job {self.job.branch}: "
- f"{failures+errors} failed, {pending} pending"
- )
-
- def body(self):
- buffer = StringIO()
- buffer.write(self.header())
-
- for state in ('failure', 'error', 'pending', 'success'):
- if state in self.tasks_by_state:
- tasks = self.tasks_by_state[state]
- buffer.write('\n')
- buffer.write(self.STATUS_HEADERS[state])
- buffer.write('\n')
- buffer.write(self.listing(tasks))
- buffer.write('\n')
-
- return buffer.getvalue()
-
- def email(self):
- return self.EMAIL.format(
- sender_name=self.sender_name,
- sender_email=self.sender_email,
- recipient_email=self.recipient_email,
- subject=self.subject(),
- body=self.body()
- )
-
- def show(self, outstream):
- outstream.write(self.email())
-
- def send(self, smtp_user, smtp_password, smtp_server, smtp_port):
+ @classmethod
+ def send_email(cls, smtp_user, smtp_password, smtp_server, smtp_port,
+ recipient_email, message):
import smtplib
- email = self.email()
-
server = smtplib.SMTP_SSL(smtp_server, smtp_port)
server.ehlo()
server.login(smtp_user, smtp_password)
- server.sendmail(smtp_user, self.recipient_email, email)
+ server.sendmail(smtp_user, recipient_email, message)
server.close()
+class EmailReport(JinjaReport):
+ templates = {
+ 'text': 'email_nightly_report.txt.j2',
+ }
+ fields = [
+ 'report',
+ 'sender_name',
+ 'sender_email',
+ 'recipient_email',
+ ]
+
+
class CommentReport(Report):
_markdown_badge = '[![{title}]({badge})]({url})'
diff --git a/dev/archery/archery/crossbow/tests/fixtures/email-report.txt b/dev/archery/archery/crossbow/tests/fixtures/email-report.txt
new file mode 100644
index 0000000000..9eee0a3246
--- /dev/null
+++ b/dev/archery/archery/crossbow/tests/fixtures/email-report.txt
@@ -0,0 +1,27 @@
+From: Sender Reporter <se...@arrow.com>
+To: recipient@arrow.com
+Subject: [NIGHTLY] Arrow Build Report for Job ursabot-1: 2 failed, 1 pending
+
+Arrow Build Report for Job ursabot-1
+
+All tasks: https://github.com/apache/crossbow/branches/all?query=ursabot-1
+
+Failed Tasks:
+
+- wheel-osx-cp37m
+ https://github.com/apache/crossbow/runs/2
+
+Errored Tasks:
+
+- wheel-osx-cp36m
+ https://github.com/apache/crossbow/runs/3
+
+Pending Tasks:
+
+- wheel-win-cp36m
+ https://github.com/apache/crossbow/runs/4
+
+Succeeded Tasks:
+
+- docker-cpp-cmake32
+ https://github.com/apache/crossbow/runs/1
diff --git a/dev/archery/archery/crossbow/tests/test_reports.py b/dev/archery/archery/crossbow/tests/test_reports.py
index deb90c47cf..91d98cfb9a 100644
--- a/dev/archery/archery/crossbow/tests/test_reports.py
+++ b/dev/archery/archery/crossbow/tests/test_reports.py
@@ -18,7 +18,8 @@
import textwrap
from archery.crossbow.core import yaml
-from archery.crossbow.reports import ChatReport, CommentReport, Report
+from archery.crossbow.reports import (ChatReport, CommentReport, EmailReport,
+ Report)
def test_crossbow_comment_formatter(load_fixture):
@@ -35,7 +36,7 @@ def test_crossbow_comment_formatter(load_fixture):
assert report.show() == textwrap.dedent(expected).strip()
-def test_crossbow_report(load_fixture):
+def test_crossbow_chat_report(load_fixture):
expected_msg = load_fixture('chat-report.txt')
job = load_fixture('crossbow-job.yaml', decoder=yaml.load)
report = Report(job)
@@ -43,3 +44,15 @@ def test_crossbow_report(load_fixture):
report_chat = ChatReport(report=report)
assert report_chat.render("text") == textwrap.dedent(expected_msg)
+
+
+def test_crossbow_email_report(load_fixture):
+ expected_msg = load_fixture('email-report.txt')
+ 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="sender@arrow.com",
+ recipient_email="recipient@arrow.com")
+
+ assert empail_report.render("text") == textwrap.dedent(expected_msg)
diff --git a/dev/archery/archery/templates/email_nightly_report.txt.j2 b/dev/archery/archery/templates/email_nightly_report.txt.j2
new file mode 100644
index 0000000000..7e068f202d
--- /dev/null
+++ b/dev/archery/archery/templates/email_nightly_report.txt.j2
@@ -0,0 +1,59 @@
+{#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#}
+{%- if True -%}
+{%- endif -%}
+From: {{ sender_name }} <{{ sender_email }}>
+To: {{ recipient_email }}
+Subject: [NIGHTLY] Arrow Build Report for Job {{report.job.branch}}: {{ (report.tasks_by_state["error"] | length) + (report.tasks_by_state["failure"] | length) }} failed, {{ report.tasks_by_state["pending"] | length }} pending
+
+Arrow Build Report for Job {{ report.job.branch }}
+
+All tasks: {{ report.url(report.job.branch) }}
+{% if report.tasks_by_state["failure"] %}
+Failed Tasks:
+
+{% for task_name, task in report.tasks_by_state["failure"] | dictsort -%}
+- {{ task_name }}
+ {{ report.task_url(task) }}
+{% endfor %}
+{% endif %}
+{%- if report.tasks_by_state["error"] -%}
+Errored Tasks:
+
+{% for task_name, task in report.tasks_by_state["error"] | dictsort -%}
+- {{ task_name }}
+ {{ report.task_url(task) }}
+{% endfor %}
+{% endif %}
+{%- if report.tasks_by_state["pending"] -%}
+Pending Tasks:
+
+{% for task_name, task in report.tasks_by_state["pending"] | dictsort -%}
+- {{ task_name }}
+ {{ report.task_url(task) }}
+{% endfor %}
+{% endif %}
+{%- if report.tasks_by_state["success"] -%}
+Succeeded Tasks:
+
+{% for task_name, task in report.tasks_by_state["success"] | dictsort -%}
+- {{ task_name }}
+ {{ report.task_url(task) }}
+{% endfor %}
+{%- endif -%}
\ No newline at end of file