From 977883df104ca21d8356386f220ba64747e94b8f Mon Sep 17 00:00:00 2001 From: German <28149841+germa89@users.noreply.github.com> Date: Thu, 8 May 2025 15:48:45 +0200 Subject: [PATCH 1/6] feat: add artifact upload steps for JSONL logs in local and remote test workflows --- .github/actions/pytest-summary/action.yml | 1 + .github/workflows/test-local.yml | 9 ++++++++- .github/workflows/test-remote.yml | 13 ++++++++++--- 3 files changed, 19 insertions(+), 4 deletions(-) diff --git a/.github/actions/pytest-summary/action.yml b/.github/actions/pytest-summary/action.yml index da23384cd3..c91603b1ee 100644 --- a/.github/actions/pytest-summary/action.yml +++ b/.github/actions/pytest-summary/action.yml @@ -30,6 +30,7 @@ runs: - name: "Download artifacts" uses: actions/download-artifact@v4 with: + pattern: "*.jsonl" path: "artifacts" - name: "Check if artifacts directory has files" diff --git a/.github/workflows/test-local.yml b/.github/workflows/test-local.yml index 1e650ec75e..42b6bb13c6 100644 --- a/.github/workflows/test-local.yml +++ b/.github/workflows/test-local.yml @@ -250,6 +250,13 @@ jobs: --report-log=$file_name.jsonl \ --cov-report=xml:$file_name.xml + - name: "Upload logs to GitHub" + if: always() + uses: actions/upload-artifact@v4.6.2 + with: + name: ${{ inputs.file-name }}.jsonl + path: ./${{ inputs.file-name }}.jsonl + - name: "Collect logs on failure" if: always() env: @@ -260,7 +267,7 @@ jobs: - name: "Upload logs to GitHub" if: always() - uses: actions/upload-artifact@master + uses: actions/upload-artifact@v4.6.2 with: name: logs-${{ inputs.file-name }}.tgz path: ./logs-${{ inputs.file-name }}.tgz diff --git a/.github/workflows/test-remote.yml b/.github/workflows/test-remote.yml index 4981883947..bdeb2c4748 100644 --- a/.github/workflows/test-remote.yml +++ b/.github/workflows/test-remote.yml @@ -213,6 +213,13 @@ jobs: --report-log=$file_name.jsonl \ --cov-report=xml:$file_name.xml + - name: "Upload logs to GitHub" + if: always() + uses: actions/upload-artifact@v4.6.2 + with: + name: ${{ inputs.file-name }}.jsonl + path: ./${{ inputs.file-name }}.jsonl + - uses: codecov/codecov-action@v5 name: "Upload coverage to Codecov" with: @@ -221,7 +228,7 @@ jobs: flags: remote,${{ steps.ubuntu_check.outputs.TAG_UBUNTU }},${{ inputs.mapdl-version }},${{ steps.distributed_mode.outputs.distributed_mode }},${{ steps.student_check.outputs.TAG_STUDENT }} - name: Upload coverage artifacts - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v4.6.2 with: name: "${{ inputs.file-name }}.xml" path: "./${{ inputs.file-name }}.xml" @@ -233,7 +240,7 @@ jobs: twine check dist/* - name: "Upload wheel and binaries" - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v4.6.2 with: name: PyMAPDL-packages-${{ inputs.mapdl-version }} path: dist/ @@ -251,7 +258,7 @@ jobs: - name: "Upload logs to GitHub" if: always() - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v4.6.2 with: name: logs-${{ inputs.file-name }}.tgz path: ./logs-${{ inputs.file-name }}.tgz From 9479ee3560f3aeea104038bf774f7f3bcb5a05a1 Mon Sep 17 00:00:00 2001 From: pyansys-ci-bot <92810346+pyansys-ci-bot@users.noreply.github.com> Date: Thu, 8 May 2025 13:51:12 +0000 Subject: [PATCH 2/6] chore: adding changelog file 3905.maintenance.md [dependabot-skip] --- doc/changelog.d/3905.maintenance.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 doc/changelog.d/3905.maintenance.md diff --git a/doc/changelog.d/3905.maintenance.md b/doc/changelog.d/3905.maintenance.md new file mode 100644 index 0000000000..9a8bb1ec1f --- /dev/null +++ b/doc/changelog.d/3905.maintenance.md @@ -0,0 +1 @@ +feat: add artifact upload steps for JSONL logs in local and remote test workflows \ No newline at end of file From 0aeee0aa14807d648141666dd15279563f429e4d Mon Sep 17 00:00:00 2001 From: German <28149841+germa89@users.noreply.github.com> Date: Thu, 8 May 2025 17:22:56 +0200 Subject: [PATCH 3/6] refactor: add type hints to functions and improve readability in pytest_summary.py --- .ci/pytest_summary.py | 77 ++++++++++++++++++++++++++++--------------- 1 file changed, 50 insertions(+), 27 deletions(-) diff --git a/.ci/pytest_summary.py b/.ci/pytest_summary.py index 9c835ffdee..d49100819e 100644 --- a/.ci/pytest_summary.py +++ b/.ci/pytest_summary.py @@ -1,16 +1,23 @@ import json import os +from typing import Any, TypedDict import click import numpy as np +from numpy.typing import NDArray BIG_WIDTH = 80 SMALL_WIDTH = 8 -def find_json_files(base_dir): +class TEST_STATS_TYPE(TypedDict): + durations: list[str | float] + n_tests: int + + +def find_json_files(base_dir: str) -> list[str]: """Recursively find all JSON files in subdirectories.""" - json_files = [] + json_files: list[str] = [] for root, _, files in os.walk(base_dir): for file in files: if file.endswith(".jsonl"): @@ -18,7 +25,7 @@ def find_json_files(base_dir): return json_files -def read_json_file(file_path): +def read_json_file(file_path: str) -> list[dict[str, str]]: """Read a JSON file and return its content as a list of test configurations.""" with open(file_path, "r", encoding="utf-8") as f: try: @@ -29,9 +36,9 @@ def read_json_file(file_path): return [] -def extract_tests_with_tags(json_files): +def extract_tests_with_tags(json_files: list[str]) -> list[dict[str, str | list[str]]]: """Extract test data and assign a tag based on the directory name.""" - tests = [] + tests: list[dict[str, str | list[str]]] = [] for file_path in json_files: directory_name = os.path.basename(os.path.dirname(file_path)) @@ -39,14 +46,18 @@ def extract_tests_with_tags(json_files): for test in test_data: if test.get("outcome", "").lower() == "passed" and test.get("duration"): - nodeid = test.get("nodeid") + nodeid: str = test.get("nodeid", "") + if nodeid.startswith("tests/"): nodeid = nodeid[6:] - when = test.get("when") - duration = test["duration"] - tags = directory_name.split("-") - tags.remove("logs") + when: str = test.get("when", "") + duration: str = test["duration"] + tags: list[str] = directory_name.split("-") + + if "logs" in tags: + tags.remove("logs") + id_ = f"{nodeid}({when})" tests.append( @@ -61,12 +72,14 @@ def extract_tests_with_tags(json_files): return tests -def compute_statistics(tests): +def compute_statistics( + tests: list[dict[str, str | list[str]]], +) -> list[dict[str, str | float]]: """Compute average duration and standard deviation per test ID.""" - test_stats = {} + test_stats: dict[str, TEST_STATS_TYPE] = {} for test in tests: - test_id = test["id"] + test_id: str = test["id"] if test_id not in test_stats: test_stats[test_id] = { "durations": [], @@ -76,10 +89,10 @@ def compute_statistics(tests): test_stats[test_id]["durations"].append(test["duration"]) test_stats[test_id]["n_tests"] += 1 - summary = [] + summary: list[dict[str, Any]] = [] for test_id, data in test_stats.items(): - durations = np.array(data["durations"]) + durations: NDArray[Any] = np.array(data["durations"]) if durations.size == 0: continue @@ -119,10 +132,15 @@ def compute_statistics(tests): return summary -def print_table(data, keys, headers, title=""): +def print_table( + data: list[dict[str, str | float]], + keys: list[str], + headers: list[str], + title: str = "", +): JUNCTION = "|" - def make_bold(s): + def make_bold(s: str) -> str: return click.style(s, bold=True) h = [headers[0].ljust(BIG_WIDTH)] @@ -135,7 +153,7 @@ def make_bold(s): + f"-{JUNCTION}-".join(["-" * len(each) for each in h]) + f"-{JUNCTION}" ) - top_sep = f"{JUNCTION}" + "-" * (len_h - 2) + f"{JUNCTION}" + # top_sep: str = f"{JUNCTION}" + "-" * (len_h - 2) + f"{JUNCTION}" if title: # click.echo(top_sep) @@ -148,17 +166,17 @@ def make_bold(s): click.echo(sep) for test in data: - s = [] + s: list[str] = [] for i, each_key in enumerate(keys): if i == 0: - id_ = test[each_key] + id_: str = test[each_key] id_ = ( - id_.replace("(", "\(") - .replace(")", "\)") - .replace("[", "\[") - .replace("]", "\]") + id_.replace("(", r"(") + .replace(")", r")") + .replace("[", r"[") + .replace("]", r"]") ) if len(id_) >= BIG_WIDTH: id_ = id_[: BIG_WIDTH - 15] + "..." + id_[-12:] @@ -177,7 +195,7 @@ def make_bold(s): # click.echo(sep) -def print_summary(summary, num=10): +def print_summary(summary: list[dict[str, str | float]], num: int = 10): """Print the top N longest tests and the top N most variable tests.""" longest_tests = sorted(summary, key=lambda x: -x["average_duration"])[:num] most_variable_tests = sorted(summary, key=lambda x: -x["std_dev"])[:num] @@ -225,15 +243,20 @@ def print_summary(summary, num=10): default=None, ) @click.option( - "--num", default=10, help="Number of top tests to display.", show_default=True + "--num", + type=int, + default=10, + help="Number of top tests to display.", + show_default=True, ) @click.option( "--save-file", default=None, + type=click.Path(exists=False, dir_okay=False), help="File to save the test durations. Default 'tests_durations.json'.", show_default=True, ) -def analyze_tests(directory, num, save_file): +def analyze_tests(directory: str, num: int, save_file: str): directory = directory or os.getcwd() # Change this to your base directory json_files = find_json_files(directory) tests = extract_tests_with_tags(json_files) From b6d221169eb3f62f891b89906852c28c71fdcffb Mon Sep 17 00:00:00 2001 From: German <28149841+germa89@users.noreply.github.com> Date: Thu, 8 May 2025 17:23:09 +0200 Subject: [PATCH 4/6] fix: update artifact upload steps to use 'reports-*.jsonl' for pytest reports --- .github/actions/pytest-summary/action.yml | 2 +- .github/workflows/test-local.yml | 4 ++-- .github/workflows/test-remote.yml | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/actions/pytest-summary/action.yml b/.github/actions/pytest-summary/action.yml index c91603b1ee..2ab26e8011 100644 --- a/.github/actions/pytest-summary/action.yml +++ b/.github/actions/pytest-summary/action.yml @@ -54,7 +54,7 @@ runs: if: ${{ env.HAS_FILES == 'true' }} shell: bash run: | - find . -mindepth 1 -maxdepth 4 -type f -name 'logs-*.tgz' -exec tar -xzvf {} -C $(dirname {}) \; + find . -mindepth 1 -maxdepth 4 -type f -name 'reports-*.tgz' -exec tar -xzvf {} -C $(dirname {}) \; - name: "List directories" if: ${{ env.HAS_FILES == 'true' }} diff --git a/.github/workflows/test-local.yml b/.github/workflows/test-local.yml index 42b6bb13c6..a8dd867285 100644 --- a/.github/workflows/test-local.yml +++ b/.github/workflows/test-local.yml @@ -250,11 +250,11 @@ jobs: --report-log=$file_name.jsonl \ --cov-report=xml:$file_name.xml - - name: "Upload logs to GitHub" + - name: "Upload pytest reports to GitHub" if: always() uses: actions/upload-artifact@v4.6.2 with: - name: ${{ inputs.file-name }}.jsonl + name: "reports-${{ inputs.file-name }}" path: ./${{ inputs.file-name }}.jsonl - name: "Collect logs on failure" diff --git a/.github/workflows/test-remote.yml b/.github/workflows/test-remote.yml index bdeb2c4748..07b511321b 100644 --- a/.github/workflows/test-remote.yml +++ b/.github/workflows/test-remote.yml @@ -213,11 +213,11 @@ jobs: --report-log=$file_name.jsonl \ --cov-report=xml:$file_name.xml - - name: "Upload logs to GitHub" + - name: "Upload pytest reports to GitHub" if: always() uses: actions/upload-artifact@v4.6.2 with: - name: ${{ inputs.file-name }}.jsonl + name: "reports-${{ inputs.file-name }}" path: ./${{ inputs.file-name }}.jsonl - uses: codecov/codecov-action@v5 From 284d0d5db59c89199adf2093732a1e02db4ee9a5 Mon Sep 17 00:00:00 2001 From: German <28149841+germa89@users.noreply.github.com> Date: Thu, 8 May 2025 18:51:34 +0200 Subject: [PATCH 5/6] fix: update artifact download pattern to match 'reports-*' for consistency --- .github/actions/pytest-summary/action.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/actions/pytest-summary/action.yml b/.github/actions/pytest-summary/action.yml index 2ab26e8011..4dd4d3fde9 100644 --- a/.github/actions/pytest-summary/action.yml +++ b/.github/actions/pytest-summary/action.yml @@ -30,7 +30,7 @@ runs: - name: "Download artifacts" uses: actions/download-artifact@v4 with: - pattern: "*.jsonl" + pattern: "reports-*" path: "artifacts" - name: "Check if artifacts directory has files" From 9d80f6ed9ca53a2a539b6d8a1163009375897dfd Mon Sep 17 00:00:00 2001 From: German <28149841+germa89@users.noreply.github.com> Date: Fri, 9 May 2025 09:35:11 +0200 Subject: [PATCH 6/6] fix: correct typos in test summary output and enhance clarity --- .github/actions/pytest-summary/action.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/actions/pytest-summary/action.yml b/.github/actions/pytest-summary/action.yml index 4dd4d3fde9..9a99615cf3 100644 --- a/.github/actions/pytest-summary/action.yml +++ b/.github/actions/pytest-summary/action.yml @@ -67,8 +67,11 @@ runs: shell: bash run: | echo "# Test summary 🚀" >> $GITHUB_STEP_SUMMARY - echo -e "The followin tables show a summary of tests duration and standard desviation for all the jobs.\n" >> $GITHUB_STEP_SUMMARY + echo -e "The following tables show a summary of tests duration and standard deviation for all the jobs.\n" >> $GITHUB_STEP_SUMMARY + echo -e "You have the duration of all tests in the artifact 'tests_durations.json'\n" >> $GITHUB_STEP_SUMMARY + echo "Running Pytest summary..." python .ci/pytest_summary.py --num 10 --save-file tests_durations.json >> summary.md + echo "Pytest summary done." echo "$(cat summary.md)" >> $GITHUB_STEP_SUMMARY cat summary.md