### Example JSON output Source: https://github.com/enricomi/publish-unit-test-result-action/blob/master/README.md Example of the JSON output from the action, showing the structure with stats, formatted values, and annotations. The `formatted` key provides string-formatted copies of stats and stats_with_delta, with thousands separators configurable via `json_thousands_separator`. ```json { "title": "4 parse errors, 4 errors, 23 fail, 18 skipped, 227 pass in 39m 12s", "summary": "  24 files  ±0      4 errors  21 suites  ±0   39m 12s [:stopwatch:](https://github.com/EnricoMi/publish-unit-test-result-action/blob/v2.6.1/README.md#the-symbols \"duration of all tests\") ±0s\n272 tests ±0  227 [:white_check_mark:](https://github.com/EnricoMi/publish-unit-test-result-action/blob/v2.6.1/README.md#the-symbols \"passed tests\") ±0  18 [:zzz:](https://github.com/EnricoMi/publish-unit-test-result-action/blob/v2.6.1/README.md#the-symbols \"skipped / disabled tests\") ±0  23 [:x:](https://github.com/EnricoMi/publish-unit-test-result-action/blob/v2.6.1/README.md#the-symbols \"failed tests\") ±0  4 [:fire:](https://github.com/EnricoMi/publish-unit-test-result-action/blob/v2.6.1/README.md#the-symbols \"test errors\") ±0 \n\nResults for commit 11c02e56. ± Comparison against earlier commit d8ce4b6c.\n", "conclusion": "success", "stats": { "files": 24, "errors": 4, "suites": 21, "duration": 2352, "tests": 272, "tests_succ": 227, "tests_skip": 18, "tests_fail": 23, "tests_error": 4, "runs": 437, "runs_succ": 354, "runs_skip": 53, "runs_fail": 25, "runs_error": 5, "commit": "11c02e561e0eb51ee90f1c744c0ca7f306f1f5f9" }, "stats_with_delta": { "files": { "number": 24, "delta": 0 }, …, "commit": "11c02e561e0eb51ee90f1c744c0ca7f306f1f5f9", "reference_type": "earlier", "reference_commit": "d8ce4b6c62ebfafe1890c55bf7ea30058ebf77f2" }, "check_url": "https://github.com/EnricoMi/publish-unit-test-result-action/runs/5397876970", "formatted": { "stats": { "duration": "2 352", … }, "stats_with_delta": { "duration": { "number": "2 352", "delta": "+12" }, … } }, "annotations": 31 } ``` -------------------------------- ### Setup Python for self-hosted runners Source: https://github.com/enricomi/publish-unit-test-result-action/blob/master/README.md Shows how to set up Python 3.8 on self-hosted runners using actions/setup-python, since the non-Docker action requires a Python3 environment. GitHub-hosted runners already provide Python3. ```yaml - name: Setup Python uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 with: python-version: 3.8 ``` -------------------------------- ### Example workflow YAML Source: https://github.com/enricomi/publish-unit-test-result-action/blob/master/README.md This workflow publishes test results, sets a badge color based on the conclusion, creates a badge with the badge-action, and uploads it to a Gist. It requires a personal access token with gist permission stored as a secret named GIST_TOKEN, and the gistURL must be set to the target Gist URL. ```yaml steps: - … - name: Publish Test Results uses: EnricoMi/publish-unit-test-result-action@v2 id: test-results if: (!cancelled()) with: files: "test-results/**/*.xml" - name: Set badge color shell: bash run: | case ${{ fromJSON( steps.test-results.outputs.json ).conclusion }} in success) echo "BADGE_COLOR=31c653" >> $GITHUB_ENV ;; failure) echo "BADGE_COLOR=800000" >> $GITHUB_ENV ;; neutral) echo "BADGE_COLOR=696969" >> $GITHUB_ENV ;; esac - name: Create badge uses: emibcn/badge-action@f9150fde070fcca0c4e832437611b44838fcd325 # v2.0.4 with: label: Tests status: '${{ fromJSON( steps.test-results.outputs.json ).formatted.stats.tests }} tests, ${{ fromJSON( steps.test-results.outputs.json ).formatted.stats.runs }} runs: ${{ fromJSON( steps.test-results.outputs.json ).conclusion }}' color: ${{ env.BADGE_COLOR }} path: badge.svg - name: Upload badge to Gist # Upload only for master branch if: > github.event_name == 'workflow_run' && github.event.workflow_run.head_branch == 'master' || github.event_name != 'workflow_run' && github.ref == 'refs/heads/master' uses: andymckay/append-gist-action@ab30bf28df67017c696ad500b218558c7c04db3 # v0.3 with: token: ${{ secrets.GIST_TOKEN }} gistURL: https://gist.githubusercontent.com/{user}/{id} file: badge.svg ``` -------------------------------- ### Docker usage example for publish-unit-test-result-action Source: https://github.com/enricomi/publish-unit-test-result-action/blob/master/README.md Use this YAML snippet to run the action with the Docker image, specifying the Docker registry and test result files. The `if: (!cancelled())` condition ensures the action runs even if previous steps are cancelled. ```yaml - name: Publish Test Results uses: EnricoMi/publish-unit-test-result-action/docker@v2 if: (!cancelled()) with: docker_registry: ghcr.io files: … ``` -------------------------------- ### Configure files option with glob patterns Source: https://github.com/enricomi/publish-unit-test-result-action/blob/master/README.md Use the files option to specify test result file patterns. Supports glob wildcards like *, **, ?, and [] character ranges. Multiple patterns can be provided as a multiline string; patterns starting with ! exclude matching files. At least one pattern must not start with !. ```yaml with: files: | *.xml !config.xml ``` -------------------------------- ### Example JSON output with errors, annotations, and cases Source: https://github.com/enricomi/publish-unit-test-result-action/blob/master/README.md Shows the structure of the JSON file written when `json_file` is configured. The `errors` array contains objects with `file`, `message`, `line`, and `column`. The `annotations` array includes objects with `path`, `start_line`, `end_line`, `annotation_level`, `message`, `title`, and `raw_details`. The `cases` array (enabled by `json_test_case_results`) contains objects with `class_name`, `test_name`, and `states` mapping result states to arrays of test run details. ```json { …, "stats": { …, "errors": [ { "file": "test-files/empty.xml", "message": "File is empty.", "line": null, "column": null } ], … }, …, "annotations": [ { "path": "test/test.py", "start_line": 819, "end_line": 819, "annotation_level": "warning", "message": "test-files/junit.fail.xml", "title": "1 out of 3 runs failed: test_events (test.Tests)", "raw_details": "self = \n\n def test_events(self):\n > self.do_test_events(3)\n\n test.py:821:\n _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _\n test.py:836: in do_test_events\n self.do_test_rsh(command, 143, events=events)\n test.py:852: in do_test_rsh\n self.assertEqual(expected_result, res)\n E AssertionError: 143 != 0\n " } ], …, "cases": [ { "class_name": "test.test_spark_keras.SparkKerasTests", "test_name": "test_batch_generator_fn", "states": { "success": [ { "result_file": "test-files/junit-xml/pytest/junit.spark.integration.1.xml", "test_file": "test/test_spark_keras.py", "line": 454, "class_name": "test.test_spark_keras.SparkKerasTests", "test_name": "test_batch_generator_fn", "result": "success", "time": 0.006 }, { "result_file": "test-files/junit-xml/pytest/junit.spark.integration.2.xml", "test_file": "test/test_spark_keras.py", "line": 454, "class_name": "test.test_spark_keras.SparkKerasTests", "test_name": "test_batch_generator_fn", "result": "success", "time": 0.006 } ] } } ], … } ``` -------------------------------- ### Example workflow YAML for matrix strategy Source: https://github.com/enricomi/publish-unit-test-result-action/blob/master/README.md A complete GitHub Actions workflow that runs tests on multiple Python versions, uploads JUnit XML results as artifacts, and then publishes them in a separate job. The publish job depends on the test job, uses the EnricoMi/publish-unit-test-result-action, and requires appropriate permissions. The workflow is triggered on push and uses the if: (!cancelled()) condition to run even if tests fail. ```yaml name: CI on: [push] permissions: {} jobs: build-and-test: name: Build and Test (Python ${{ matrix.python-version }}) runs-on: ubuntu-latest strategy: fail-fast: false matrix: python-version: [3.6, 3.7, 3.8] steps: - name: Checkout uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Setup Python ${{ matrix.python-version }} uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 with: python-version: ${{ matrix.python-version }} - name: PyTest run: python -m pytest test --junit-xml pytest.xml - name: Upload Test Results if: (!cancelled()) uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: Test Results (Python ${{ matrix.python-version }}) path: pytest.xml publish-test-results: name: "Publish Tests Results" needs: build-and-test runs-on: ubuntu-latest permissions: checks: write # only needed unless run with comment_mode: off pull-requests: write # only needed for private repository contents: read # only needed for private repository issues: read if: (!cancelled()) steps: - name: Download Artifacts uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: path: artifacts - name: Publish Test Results uses: EnricoMi/publish-unit-test-result-action@v2 with: files: "artifacts/**/*.xml" ``` -------------------------------- ### Using OS-specific actions in a workflow Source: https://github.com/enricomi/publish-unit-test-result-action/blob/master/README.md This YAML example shows how to replace the deprecated composite action with three conditional steps, each using the appropriate action for the runner's operating system. The Linux, macOS, and Windows (Bash) actions are used with the 'if' condition to run only on the matching OS. Each step publishes test results from the 'test-results/**/*.xml' files. ```yaml - name: Publish Test Results uses: EnricoMi/publish-unit-test-result-action/linux@v2 if: runner.os == 'Linux' with: files: test-results/**/*.xml - name: Publish Test Results uses: EnricoMi/publish-unit-test-result-action/macos@v2 if: runner.os == 'macOS' with: files: test-results/**/*.xml - name: Publish Test Results uses: EnricoMi/publish-unit-test-result-action/windows/bash@v2 if: runner.os == 'Windows' with: files: test-results/**/*.xml ``` -------------------------------- ### Self-hosted Linux runner without Docker Source: https://github.com/enricomi/publish-unit-test-result-action/blob/master/README.md Usage for self-hosted Linux GitHub Actions runners without Docker installed. The action is referenced with the /linux suffix. ```yaml - name: Publish Test Results uses: EnricoMi/publish-unit-test-result-action/linux@v2 if: (!cancelled()) with: files: … ``` -------------------------------- ### Access JSON via step outputs Source: https://github.com/enricomi/publish-unit-test-result-action/blob/master/README.md Use the action's `json` output through the `steps..outputs.json` expression. The example shows a workflow step that publishes test results and a subsequent step that reads the conclusion from the JSON output. ```yaml - name: Publish Test Results uses: EnricoMi/publish-unit-test-result-action@v2 id: test-results if: (!cancelled()) with: files: "test-results/**/*.xml" - name: Conclusion run: echo "Conclusion is ${{ fromJSON( steps.test-results.outputs.json ).conclusion }}" ``` -------------------------------- ### Windows runner without PowerShell (Bash variant) Source: https://github.com/enricomi/publish-unit-test-result-action/blob/master/README.md Usage for Windows runners without PowerShell installed, using the Bash shell variant. The action is referenced with the /windows/bash suffix. ```yaml - name: Publish Test Results uses: EnricoMi/publish-unit-test-result-action/windows/bash@v2 if: (!cancelled()) with: files: … ``` -------------------------------- ### Add event name to check_name to avoid overwriting results Source: https://github.com/enricomi/publish-unit-test-result-action/blob/master/README.md Use this YAML workflow step to include the triggering event name in the check name, so that results from different event types (e.g., pull_request vs push) do not overwrite each other. The expression uses github.event.workflow_run.event or github.event_name to get the event name. This is one of two ways to prevent overwriting results across event types. ```yaml - name: Publish Test Results uses: EnricoMi/publish-unit-test-result-action@v2 if: (!cancelled()) with: check_name: "Test Results (${{ github.event.workflow_run.event || github.event_name }})" files: "test-results/**/*.xml" ``` -------------------------------- ### Basic usage with files input Source: https://github.com/enricomi/publish-unit-test-result-action/blob/master/README.md Basic usage of the action with the files input to specify test result files. The if: (!cancelled()) clause ensures the action runs even if earlier steps fail, but not if the workflow is cancelled. ```yaml - name: Publish Test Results uses: EnricoMi/publish-unit-test-result-action@v2 if: (!cancelled()) with: files: | test-results/**/*.xml test-results/**/*.trx test-results/**/*.json ``` -------------------------------- ### Copy test results to relative path for Docker variant Source: https://github.com/enricomi/publish-unit-test-result-action/blob/master/README.md Shows how to copy test results from an absolute path to a relative path before using the Docker variant, because the Docker variant requires relative paths. Includes the copy step and the publish step with the files input. ```yaml - name: Copy Test Results if: (!cancelled()) run: | cp -Lpr /tmp/test-results test-results shell: bash - name: Publish Test Results uses: EnricoMi/publish-unit-test-result-action@v2 if: (!cancelled()) with: files: | test-results/**/*.xml test-results/**/*.trx test-results/**/*.json ``` -------------------------------- ### Non-Docker action variants for absolute paths Source: https://github.com/enricomi/publish-unit-test-result-action/blob/master/README.md Lists the non-Docker action variants (linux, macos, windows) that support most absolute paths. These are recommended over the Docker variant when using absolute paths. ```yaml uses: EnricoMi/publish-unit-test-result-action/linux@v2 uses: EnricoMi/publish-unit-test-result-action/macos@v2 uses: EnricoMi/publish-unit-test-result-action/windows@v2 ``` -------------------------------- ### Windows runner usage Source: https://github.com/enricomi/publish-unit-test-result-action/blob/master/README.md Usage for Windows runners (e.g., runs-on: windows-latest) without Docker. The action is referenced with the /windows suffix. ```yaml - name: Publish Test Results uses: EnricoMi/publish-unit-test-result-action/windows@v2 if: (!cancelled()) with: files: … ``` -------------------------------- ### Publish test results workflow Source: https://github.com/enricomi/publish-unit-test-result-action/blob/master/README.md Add this workflow to publish test results. It triggers on workflow_run completion, downloads and extracts artifacts into 'artifacts/ARTIFACT_NAME/', and runs the action on files matching 'artifacts/**/*.xml'. Adjust the 'workflows' value and 'files' pattern as needed. ```yaml name: Test Results on: workflow_run: workflows: ["CI"] types: - completed permissions: {} jobs: test-results: name: Test Results runs-on: ubuntu-latest if: github.event.workflow_run.conclusion == 'success' || github.event.workflow_run.conclusion == 'failure' permissions: checks: write # needed unless run with comment_mode: off pull-requests: write # only needed for private repository contents: read # only needed for private repository issues: read # required by download step to access artifacts API actions: read steps: - name: Download and Extract Artifacts uses: dawidd6/action-download-artifact@d63b86af1b34672e53c440b1b83979861906bad7 # v24 with: run_id: ${{ github.event.workflow_run.id }} path: artifacts - name: Publish Test Results uses: EnricoMi/publish-unit-test-result-action@v2 with: commit: ${{ github.event.workflow_run.head_sha }} event_file: artifacts/Event File/event.json event_name: ${{ github.event.workflow_run.event }} files: "artifacts/**/*.xml" ``` -------------------------------- ### macOS runner usage Source: https://github.com/enricomi/publish-unit-test-result-action/blob/master/README.md Usage for macOS runners (e.g., runs-on: macos-latest) without Docker. The action is referenced with the /macos suffix. ```yaml - name: Publish Test Results uses: EnricoMi/publish-unit-test-result-action/macos@v2 if: (!cancelled()) with: files: … ``` -------------------------------- ### Upload event file as artifact Source: https://github.com/enricomi/publish-unit-test-result-action/blob/master/README.md Add this job to your CI workflow to upload the GitHub event file as an artifact. The artifact is named 'Event File' and is required for the separate publishing workflow to access the event data. ```yaml event_file: name: "Event File" runs-on: ubuntu-latest steps: - name: Upload uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: Event File path: ${{ github.event_path }} ``` -------------------------------- ### Upload test results as artifacts Source: https://github.com/enricomi/publish-unit-test-result-action/blob/master/README.md Add this action step to your CI workflow to upload test result files as artifacts. Adjust the 'path' value to match your test results location. The artifact is named 'Test Results'. ```yaml - name: Upload Test Results if: (!cancelled()) uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: Test Results path: | test-results/*.xml ``` -------------------------------- ### Minimal permissions for private repositories Source: https://github.com/enricomi/publish-unit-test-result-action/blob/master/README.md Required workflow job permissions for private GitHub repositories. Includes contents, issues, checks, and pull-requests read/write permissions. ```yaml permissions: contents: read issues: read checks: write pull-requests: write ``` -------------------------------- ### Minimal permissions for public repositories Source: https://github.com/enricomi/publish-unit-test-result-action/blob/master/README.md Required workflow job permissions for public GitHub repositories. Set checks and pull-requests to write. ```yaml permissions: checks: write pull-requests: write ``` -------------------------------- ### Unique artifact name for strategy matrix Source: https://github.com/enricomi/publish-unit-test-result-action/blob/master/README.md When running tests in a strategy matrix, make the artifact name unique for each job by including the matrix variable, e.g., 'Test Results (${{ matrix.python-version }})'. ```yaml with: name: Test Results (${{ matrix.python-version }}) path: … ``` -------------------------------- ### Set comment_mode based on event type to avoid overwriting PR comments Source: https://github.com/enricomi/publish-unit-test-result-action/blob/master/README.md Use this YAML workflow step to disable pull request comments for non-pull_request events by setting comment_mode to 'off' unless the event is a pull_request. The expression evaluates to 'always' for pull_request events and 'off' for all others, preventing other event types from overwriting pull request comments. This is the second way to avoid overwriting results across event types. ```yaml - name: Publish Test Results uses: EnricoMi/publish-unit-test-result-action@v2 if: (!cancelled()) with: # set comment_mode to "always" for pull_request event, set to "off" for all other event types comment_mode: ${{ (github.event.workflow_run.event == 'pull_request' || github.event_name == 'pull_request') && 'always' || 'off' }} files: "test-results/**/*.xml" ```