6
6
import json
7
7
import webbrowser
8
8
from dataclasses import dataclass
9
- from typing import TYPE_CHECKING
9
+ from pathlib import Path
10
+ from types import MappingProxyType
11
+ from typing import TYPE_CHECKING , Dict
10
12
11
- from anyio import Path
12
13
from connector_ops .utils import console # type: ignore
13
14
from jinja2 import Environment , PackageLoader , select_autoescape
14
15
from pipelines .consts import GCS_PUBLIC_DOMAIN
15
16
from pipelines .helpers .utils import format_duration
17
+ from pipelines .models .artifacts import Artifact
16
18
from pipelines .models .reports import Report
17
19
from pipelines .models .steps import StepStatus
18
20
from rich .console import Group
@@ -42,13 +44,19 @@ def report_output_prefix(self) -> str:
42
44
def html_report_file_name (self ) -> str :
43
45
return self .filename + ".html"
44
46
47
+ def file_remote_storage_key (self , file_name : str ) -> str :
48
+ return f"{ self .report_output_prefix } /{ file_name } "
49
+
45
50
@property
46
51
def html_report_remote_storage_key (self ) -> str :
47
- return f"{ self .report_output_prefix } /{ self .html_report_file_name } "
52
+ return self .file_remote_storage_key (self .html_report_file_name )
53
+
54
+ def file_url (self , file_name : str ) -> str :
55
+ return f"{ GCS_PUBLIC_DOMAIN } /{ self .pipeline_context .ci_report_bucket } /{ self .file_remote_storage_key (file_name )} "
48
56
49
57
@property
50
58
def html_report_url (self ) -> str :
51
- return f" { GCS_PUBLIC_DOMAIN } / { self .pipeline_context . ci_report_bucket } / { self .html_report_remote_storage_key } "
59
+ return self .file_url ( self .html_report_file_name )
52
60
53
61
def to_json (self ) -> str :
54
62
"""Create a JSON representation of the connector test report.
@@ -81,7 +89,7 @@ def to_json(self) -> str:
81
89
}
82
90
)
83
91
84
- async def to_html (self ) -> str :
92
+ def to_html (self ) -> str :
85
93
env = Environment (
86
94
loader = PackageLoader ("pipelines.airbyte_ci.connectors.test.steps" ),
87
95
autoescape = select_autoescape (),
@@ -91,7 +99,18 @@ async def to_html(self) -> str:
91
99
template = env .get_template ("test_report.html.j2" )
92
100
template .globals ["StepStatus" ] = StepStatus
93
101
template .globals ["format_duration" ] = format_duration
94
- local_icon_path = await Path (f"{ self .pipeline_context .connector .code_directory } /icon.svg" ).resolve ()
102
+ local_icon_path = Path (f"{ self .pipeline_context .connector .code_directory } /icon.svg" ).resolve ()
103
+ step_result_to_artifact_links : Dict [str , List [Dict ]] = {}
104
+ for step_result in self .steps_results :
105
+ for artifact in step_result .artifacts :
106
+ if artifact .gcs_url :
107
+ url = artifact .gcs_url
108
+ elif artifact .local_path :
109
+ url = artifact .local_path .resolve ().as_uri ()
110
+ else :
111
+ continue
112
+ step_result_to_artifact_links .setdefault (step_result .step .title , []).append ({"name" : artifact .name , "url" : url })
113
+
95
114
template_context = {
96
115
"connector_name" : self .pipeline_context .connector .technical_name ,
97
116
"step_results" : self .steps_results ,
@@ -104,6 +123,8 @@ async def to_html(self) -> str:
104
123
"git_revision" : self .pipeline_context .git_revision ,
105
124
"commit_url" : None ,
106
125
"icon_url" : local_icon_path .as_uri (),
126
+ "report" : self ,
127
+ "step_result_to_artifact_links" : MappingProxyType (step_result_to_artifact_links ),
107
128
}
108
129
109
130
if self .pipeline_context .is_ci :
@@ -116,18 +137,32 @@ async def to_html(self) -> str:
116
137
] = f"https://raw.githubusercontent.com/airbytehq/airbyte/{ self .pipeline_context .git_revision } /{ self .pipeline_context .connector .code_directory } /icon.svg"
117
138
return template .render (template_context )
118
139
140
+ async def save_html_report (self ) -> None :
141
+ """Save the report as HTML, upload it to GCS if the pipeline is running in CI"""
142
+
143
+ html_report_path = self .report_dir_path / self .html_report_file_name
144
+ report_dir = self .pipeline_context .dagger_client .host ().directory (str (self .report_dir_path ))
145
+ local_html_report_file = report_dir .with_new_file (self .html_report_file_name , self .to_html ()).file (self .html_report_file_name )
146
+ html_report_artifact = Artifact (name = "HTML Report" , content_type = "text/html" , content = local_html_report_file )
147
+ await html_report_artifact .save_to_local_path (html_report_path )
148
+ absolute_path = html_report_path .absolute ()
149
+ self .pipeline_context .logger .info (f"Report saved locally at { absolute_path } " )
150
+ if self .remote_storage_enabled and self .pipeline_context .ci_gcs_credentials_secret and self .pipeline_context .ci_report_bucket :
151
+ gcs_url = await html_report_artifact .upload_to_gcs (
152
+ dagger_client = self .pipeline_context .dagger_client ,
153
+ bucket = self .pipeline_context .ci_report_bucket ,
154
+ key = self .html_report_remote_storage_key ,
155
+ gcs_credentials = self .pipeline_context .ci_gcs_credentials_secret ,
156
+ )
157
+ self .pipeline_context .logger .info (f"HTML report uploaded to { gcs_url } " )
158
+
159
+ elif self .pipeline_context .enable_report_auto_open :
160
+ self .pipeline_context .logger .info ("Opening HTML report in browser." )
161
+ webbrowser .open (absolute_path .as_uri ())
162
+
119
163
async def save (self ) -> None :
120
- local_html_path = await self .save_local (self .html_report_file_name , await self .to_html ())
121
- absolute_path = await local_html_path .resolve ()
122
- if self .pipeline_context .enable_report_auto_open :
123
- self .pipeline_context .logger .info (f"HTML report saved locally: { absolute_path } " )
124
- if self .pipeline_context .enable_report_auto_open :
125
- self .pipeline_context .logger .info ("Opening HTML report in browser." )
126
- webbrowser .open (absolute_path .as_uri ())
127
- if self .remote_storage_enabled :
128
- await self .save_remote (local_html_path , self .html_report_remote_storage_key , "text/html" )
129
- self .pipeline_context .logger .info (f"HTML report uploaded to { self .html_report_url } " )
130
164
await super ().save ()
165
+ await self .save_html_report ()
131
166
132
167
def print (self ) -> None :
133
168
"""Print the test report to the console in a nice way."""
0 commit comments