This is an automated email from the ASF dual-hosted git repository.
terrymanu pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/shardingsphere.git
The following commit(s) were added to refs/heads/master by this push:
new afa90ba3243 Replace SQL parser Excel export with JUnit report Action
(#40119)
afa90ba3243 is described below
commit afa90ba32439806aed88cdf1890ad3968579fc8b
Author: Liang Zhang <[email protected]>
AuthorDate: Sat Oct 10 00:02:30 2026 +0800
Replace SQL parser Excel export with JUnit report Action (#40119)
---
.github/scripts/generate-sql-parser-report.py | 145 --------------------------
.github/workflows/nightly-sql-parser.yml | 29 ++++--
2 files changed, 18 insertions(+), 156 deletions(-)
diff --git a/.github/scripts/generate-sql-parser-report.py
b/.github/scripts/generate-sql-parser-report.py
deleted file mode 100644
index 0f0175f37b0..00000000000
--- a/.github/scripts/generate-sql-parser-report.py
+++ /dev/null
@@ -1,145 +0,0 @@
-#!/usr/bin/env python3
-#
-# 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.
-#
-
-import argparse
-from collections import Counter
-from pathlib import Path
-import re
-import xml.etree.ElementTree as ET
-
-from openpyxl import Workbook
-from openpyxl.cell import Cell
-from openpyxl.styles import Alignment, Font, PatternFill
-
-
-def split_cell_text(value):
- if len(value.encode("utf-16-le")) // 2 <= 32767 and value.count("\n") <=
253:
- return [value]
- chunks = []
- start = units = line_feeds = 0
- for offset, character in enumerate(value):
- size = 2 if ord(character) > 0xFFFF else 1
- if units + size > 32767 or line_feeds + (character == "\n") > 253:
- chunks.append(value[start:offset])
- start, units, line_feeds = offset, 0, 0
- units += size
- line_feeds += character == "\n"
- chunks.append(value[start:])
- return chunks
-
-
-def append_row(sheet, values):
- cells = []
- for value in values:
- cell = Cell(sheet, value=value)
- if isinstance(value, str):
- cell.data_type = "s"
- cells.append(cell)
- sheet.append(cells)
-
-
-def append_error(details, long_text, values):
- for index, value in enumerate(values):
- if not isinstance(value, str):
- continue
- chunks = split_cell_text(value)
- if len(chunks) == 1:
- continue
- field = details.cell(1, index + 1).value
- for part, chunk in enumerate(chunks, 1):
- append_row(long_text, [values[0], field, part, chunk])
- values[index] = f"See Long Text: error {values[0]}, field '{field}'
({len(chunks)} parts)."
- append_row(details, values)
-
-
-def format_sheet(sheet, widths):
- sheet.freeze_panes = "A2"
- sheet.auto_filter.ref = sheet.dimensions
- for cell in sheet[1]:
- cell.font = Font(bold=True, color="FFFFFF")
- cell.fill = PatternFill("solid", fgColor="17365D")
- for column, width in widths.items():
- sheet.column_dimensions[column].width = width
- for row in sheet.iter_rows(min_row=2):
- for cell in row:
- cell.alignment = Alignment(vertical="top", wrap_text=True)
- sheet.row_dimensions[row[0].row].height = 60
-
-
-def generate_report(reports_dir, test_class, database, output):
- reports = sorted(reports_dir.glob(f"TEST-*{test_class}.xml"))
- if not reports:
- raise FileNotFoundError(f"No Failsafe XML reports for {test_class} in
{reports_dir}")
- workbook = Workbook()
- summary = workbook.active
- summary.title = "Summary"
- details = workbook.create_sheet("Errors")
- long_text = workbook.create_sheet("Long Text")
- append_row(details, ["Error ID", "Database", "Case ID", "SQL", "Result",
"Exception Type", "Error Message", "Stack Trace", "Test Name", "Report File"])
- append_row(long_text, ["Error ID", "Field", "Part", "Text"])
- totals = Counter()
- exceptions = Counter()
- for report in reports:
- for _, testcase in ET.iterparse(report, events=("end",)):
- if testcase.tag != "testcase":
- continue
- totals["tests"] += 1
- totals["skipped"] += testcase.find("skipped") is not None
- totals["errors"] += testcase.find("error") is not None
- totals["failures"] += testcase.find("failure") is not None
- name = testcase.attrib["name"]
- parameters = re.fullmatch(r"assertParseSQL\([^)]*\) (.*?)
\(([^()]*)\) -> (.*)", name, re.DOTALL)
- for failure in list(testcase.findall("error")) +
list(testcase.findall("failure")):
- totals["error_entries"] += 1
- exceptions[failure.get("type", "")] += 1
- append_error(details, long_text, [
- totals["error_entries"], parameters[2] if parameters else
database, parameters[1] if parameters else "", parameters[3] if parameters else
"",
- failure.tag, failure.get("type", ""),
failure.get("message", ""), failure.text or "", name, report.name,
- ])
- testcase.clear()
- append_row(summary, ["Metric", "Count or Value"])
- for label, value in [
- ("Database", database), ("Test Class", test_class), ("Total Tests",
totals["tests"]),
- ("Errors", totals["errors"]), ("Failures", totals["failures"]),
("Skipped", totals["skipped"]),
- ("Passed", totals["tests"] - totals["errors"] - totals["failures"] -
totals["skipped"]),
- ("Notes", "Only error/failure entries are listed. Initialization
failures may have no SQL. Text exceeding Excel cell limits is stored in Long
Text by error ID and field."),
- ]:
- append_row(summary, [label, value])
- append_row(summary, ["Exception Type", "Error Entries"])
- for exception, count in exceptions.most_common():
- append_row(summary, [exception, count])
- format_sheet(summary, {"A": 65, "B": 100})
- format_sheet(details, {"A": 12, "B": 16, "C": 45, "D": 100, "E": 12, "F":
65, "G": 100, "H": 100, "I": 65, "J": 65})
- format_sheet(long_text, {"A": 12, "B": 20, "C": 12, "D": 120})
- output.parent.mkdir(parents=True, exist_ok=True)
- workbook.save(output)
- print(f"Excel report: {output}; tests={totals['tests']};
errors={totals['errors']}; failures={totals['failures']}")
-
-
-def main():
- parser = argparse.ArgumentParser(description="Export SQL parser Failsafe
errors to Excel.")
- parser.add_argument("--reports-dir", type=Path, required=True)
- parser.add_argument("--test-class", required=True)
- parser.add_argument("--database", required=True)
- parser.add_argument("--output", type=Path, required=True)
- args = parser.parse_args()
- generate_report(args.reports_dir, args.test_class, args.database,
args.output)
-
-
-if __name__ == "__main__":
- main()
diff --git a/.github/workflows/nightly-sql-parser.yml
b/.github/workflows/nightly-sql-parser.yml
index 8083128a7fd..84709c0ad55 100644
--- a/.github/workflows/nightly-sql-parser.yml
+++ b/.github/workflows/nightly-sql-parser.yml
@@ -67,22 +67,29 @@ jobs:
./mvnw -nsu -B install -f test/it/parser/pom.xml
-Pexternal-sql-parser-report \
-Dsql.parser.external.it.enabled=true -Dit.test=${{ matrix.test }}
\
-Dsql.parser.external.it.report.path="$RUNNER_TEMP/sql-parser-report-${{
matrix.database }}/"
- - name: Set up Python for SQL Report
+ - name: Generate SQL Parser Report
if: ${{ !cancelled() && steps.sql-parser.outcome != 'skipped' }}
- uses: actions/setup-python@v6
+ uses:
mikepenz/action-junit-report@b7b80d7ac5b1da46d6aaff9fb57ad50d8453bcb3 # v6.6.0
with:
- python-version: '3.12'
- - name: Generate SQL Parser Excel Report
- if: ${{ !cancelled() && steps.sql-parser.outcome != 'skipped' }}
- run: |
- python -m pip install --disable-pip-version-check --target
"$RUNNER_TEMP/sql-parser-report-dependencies" openpyxl==3.1.5
- PYTHONPATH="$RUNNER_TEMP/sql-parser-report-dependencies" python
.github/scripts/generate-sql-parser-report.py \
- --reports-dir test/it/parser/target/failsafe-reports --test-class
${{ matrix.test }} \
- --database ${{ matrix.database }} \
- --output "$RUNNER_TEMP/sql-parser-report-${{ matrix.database
}}/${{ matrix.database }}-errors.xlsx"
+ report_paths: 'test/it/parser/target/failsafe-reports/TEST-*${{
matrix.test }}.xml'
+ check_name: 'SQL Parser - ${{ matrix.database }}'
+ skip_annotations: true
+ detailed_summary: true
+ failed_summary_logs: true
+ include_skipped: false
+ truncate_stack_traces: false
+ require_tests: true
+ fail_on_parse_error: true
- uses: actions/upload-artifact@v6
if: ${{ !cancelled() && steps.sql-parser.outcome != 'skipped' }}
with:
name: sql-report-${{ matrix.database }}
path: ${{ runner.temp }}/sql-parser-report-${{ matrix.database }}/
if-no-files-found: error
+ - name: Upload SQL Parser XML
+ if: ${{ !cancelled() && steps.sql-parser.outcome != 'skipped' }}
+ uses: actions/upload-artifact@v6
+ with:
+ name: sql-parser-xml-${{ matrix.database }}
+ path: test/it/parser/target/failsafe-reports/TEST-*${{ matrix.test
}}.xml
+ if-no-files-found: error