diff --git a/.github/workflows/ci-cd.yml b/.github/workflows/ci-cd.yml index c9292e3e..b2f4c1bf 100644 --- a/.github/workflows/ci-cd.yml +++ b/.github/workflows/ci-cd.yml @@ -188,7 +188,7 @@ jobs: - name: Publish Unit Test Results if: always() run: | - docker run --workdir $GITHUB_WORKSPACE --rm -e INPUT_CHECK_NAME -e INPUT_FILES -e INPUT_TIME_UNIT -e INPUT_GITHUB_TOKEN -e INPUT_GITHUB_RETRIES -e INPUT_COMMIT -e INPUT_COMMENT_TITLE -e INPUT_FAIL_ON -e INPUT_REPORT_INDIVIDUAL_RUNS -e INPUT_DEDUPLICATE_CLASSES_BY_FILE_NAME -e INPUT_IGNORE_RUNS -e INPUT_HIDE_COMMENTS -e INPUT_COMMENT_ON_PR -e INPUT_COMMENT_MODE -e INPUT_COMPARE_TO_EARLIER_COMMIT -e INPUT_PULL_REQUEST_BUILD -e INPUT_EVENT_FILE -e INPUT_EVENT_NAME -e INPUT_TEST_CHANGES_LIMIT -e INPUT_CHECK_RUN_ANNOTATIONS -e INPUT_CHECK_RUN_ANNOTATIONS_BRANCH -e INPUT_SECONDS_BETWEEN_GITHUB_READS -e INPUT_SECONDS_BETWEEN_GITHUB_WRITES -e INPUT_JSON_FILE -e HOME -e GITHUB_JOB -e GITHUB_REF -e GITHUB_SHA -e GITHUB_REPOSITORY -e GITHUB_REPOSITORY_OWNER -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RETENTION_DAYS -e GITHUB_ACTOR -e GITHUB_WORKFLOW -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GITHUB_EVENT_NAME -e GITHUB_SERVER_URL -e GITHUB_API_URL -e GITHUB_GRAPHQL_URL -e GITHUB_WORKSPACE -e GITHUB_ACTION -e GITHUB_EVENT_PATH -e GITHUB_ACTION_REPOSITORY -e GITHUB_ACTION_REF -e GITHUB_PATH -e GITHUB_ENV -e RUNNER_OS -e RUNNER_TOOL_CACHE -e RUNNER_TEMP -e RUNNER_WORKSPACE -e ACTIONS_RUNTIME_URL -e ACTIONS_RUNTIME_TOKEN -e ACTIONS_CACHE_URL -e GITHUB_ACTIONS=true -e CI=true -v "/var/run/docker.sock":"/var/run/docker.sock" -v "$RUNNER_TEMP":"$RUNNER_TEMP" -v "$GITHUB_WORKSPACE":"$GITHUB_WORKSPACE" enricomi/publish-unit-test-result-action:latest + docker run --workdir $GITHUB_WORKSPACE --rm -e INPUT_CHECK_NAME -e INPUT_FILES -e INPUT_TIME_UNIT -e INPUT_GITHUB_TOKEN -e INPUT_GITHUB_RETRIES -e INPUT_COMMIT -e INPUT_COMMENT_TITLE -e INPUT_FAIL_ON -e INPUT_REPORT_INDIVIDUAL_RUNS -e INPUT_DEDUPLICATE_CLASSES_BY_FILE_NAME -e INPUT_IGNORE_RUNS -e INPUT_HIDE_COMMENTS -e INPUT_COMMENT_ON_PR -e INPUT_COMMENT_MODE -e INPUT_COMPARE_TO_EARLIER_COMMIT -e INPUT_PULL_REQUEST_BUILD -e INPUT_EVENT_FILE -e INPUT_EVENT_NAME -e INPUT_TEST_CHANGES_LIMIT -e INPUT_CHECK_RUN_ANNOTATIONS -e INPUT_CHECK_RUN_ANNOTATIONS_BRANCH -e INPUT_SECONDS_BETWEEN_GITHUB_READS -e INPUT_SECONDS_BETWEEN_GITHUB_WRITES -e INPUT_JSON_FILE -e INPUT_JOB_SUMMARY -e HOME -e GITHUB_JOB -e GITHUB_REF -e GITHUB_SHA -e GITHUB_REPOSITORY -e GITHUB_REPOSITORY_OWNER -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RETENTION_DAYS -e GITHUB_ACTOR -e GITHUB_WORKFLOW -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GITHUB_EVENT_NAME -e GITHUB_SERVER_URL -e GITHUB_API_URL -e GITHUB_GRAPHQL_URL -e GITHUB_WORKSPACE -e GITHUB_ACTION -e GITHUB_EVENT_PATH -e GITHUB_ACTION_REPOSITORY -e GITHUB_ACTION_REF -e GITHUB_PATH -e GITHUB_ENV -e GITHUB_STEP_SUMMARY -e RUNNER_OS -e RUNNER_TOOL_CACHE -e RUNNER_TEMP -e RUNNER_WORKSPACE -e ACTIONS_RUNTIME_URL -e ACTIONS_RUNTIME_TOKEN -e ACTIONS_CACHE_URL -e GITHUB_ACTIONS=true -e CI=true -v "/var/run/docker.sock":"/var/run/docker.sock" -v "$RUNNER_TEMP":"$RUNNER_TEMP" -v "$GITHUB_WORKSPACE":"$GITHUB_WORKSPACE" enricomi/publish-unit-test-result-action:latest env: INPUT_GITHUB_TOKEN: ${{ github.token }} INPUT_CHECK_NAME: Unit Test Results (Docker Image) diff --git a/README.md b/README.md index 4d4a72b0..aee9256c 100644 --- a/README.md +++ b/README.md @@ -141,6 +141,7 @@ See the complete list of options below. |`check_name`|`"Unit Test Results"`|An alternative name for the check result.| |`comment_title`|same as `check_name`|An alternative name for the pull request comment.| |`comment_mode`|`update last`|The action posts comments to a pull request that is associated with the commit. Set to `create new` to create a new comment on each commit, `update last` to create only one comment and update later on, `off` to not create pull request comments.| +|`job_summary`|`true`| Set to `true` to publish summary as Job Step Summary, can be used in addition or replacement of pull request decoration| |`hide_comments`|`"all but latest"`|Configures which earlier comments in a pull request are hidden by the action:
`"orphaned commits"` - comments for removed commits
`"all but latest"` - all comments but the latest
`"off"` - no hiding| |`github_token`|`${{github.token}}`|An alternative GitHub token, other than the default provided by GitHub Actions runner.| |`github_retries`|`10`|Requests to the GitHub API are retried this number of times. The value must be a positive integer or zero.| diff --git a/action.yml b/action.yml index 377a8431..bf225f2e 100644 --- a/action.yml +++ b/action.yml @@ -87,6 +87,10 @@ inputs: json_file: description: 'Results are written to this JSON file.' required: false + job_summary: + description: 'Set to `true` to publish summary as Job Step Summary, can be used in addition or replacement of pull request decoration' + required: false + default: 'true' runs: using: 'docker' image: 'docker://ghcr.io/enricomi/publish-unit-test-result-action:v1.34' diff --git a/composite/action.yml b/composite/action.yml index 0ef27ce0..309049f1 100644 --- a/composite/action.yml +++ b/composite/action.yml @@ -87,6 +87,10 @@ inputs: json_file: description: 'Results are written to this JSON file.' required: false + job_summary: + description: 'Set to `true` to publish summary as Job Step Summary, can be used in addition or replacement of pull request decoration' + required: false + default: 'true' runs: using: 'composite' steps: @@ -152,6 +156,7 @@ runs: SECONDS_BETWEEN_GITHUB_READS: ${{ inputs.seconds_between_github_reads }} SECONDS_BETWEEN_GITHUB_WRITES: ${{ inputs.seconds_between_github_writes }} JSON_FILE: ${{ inputs.json_file }} + JOB_SUMMARY: ${{ inputs.job_summary }} ROOT_LOG_LEVEL: ${{ inputs.root_log_level }} LOG_LEVEL: ${{ inputs.log_level }} shell: bash diff --git a/python/publish/publisher.py b/python/publish/publisher.py index 037ef3ed..5033a596 100644 --- a/python/publish/publisher.py +++ b/python/publish/publisher.py @@ -52,6 +52,9 @@ class Settings: check_run_annotation: List[str] seconds_between_github_reads: float seconds_between_github_writes: float + job_summary: bool = True + job_summary_file: Optional[str] = None + @dataclasses.dataclass(frozen=True) @@ -252,6 +255,7 @@ def publish_check(self, title = get_short_summary(stats) summary = get_long_summary_md(stats_with_delta) + self.publish_summary(summary) # create full json data = PublishData( @@ -305,6 +309,14 @@ def publish_json(self, data: PublishData): # provide a reduced version to Github actions self._gha.set_output('json', json.dumps(data.reduced(), ensure_ascii=False)) + def publish_summary(self, summary: str): + if self._settings.job_summary_file: + try: + with open(self._settings.job_summary_file, 'a', encoding='utf-8') as summary_file: + summary_file.write(summary) + except Exception as e: + self._gha.error(f'Failed to write summary file {self._settings.job_summary_file}: {str(e)}') + @staticmethod def get_test_lists_from_check_run(check_run: Optional[CheckRun]) -> Tuple[Optional[List[str]], Optional[List[str]]]: if check_run is None: diff --git a/python/publish_unit_test_results.py b/python/publish_unit_test_results.py index 5e4f7579..1880a85a 100644 --- a/python/publish_unit_test_results.py +++ b/python/publish_unit_test_results.py @@ -294,7 +294,9 @@ def get_settings(options: dict, gha: Optional[GithubAction] = None) -> Settings: ignore_runs=get_bool_var('IGNORE_RUNS', options, default=False, gha=gha), check_run_annotation=annotations, seconds_between_github_reads=float(seconds_between_github_reads), - seconds_between_github_writes=float(seconds_between_github_writes) + seconds_between_github_writes=float(seconds_between_github_writes), + job_summary=get_bool_var('JOB_SUMMARY', options, default=True, gha=gha), + job_summary_file=options.get('GITHUB_STEP_SUMMARY') ) check_var(settings.token, 'GITHUB_TOKEN', 'GitHub token') @@ -312,6 +314,9 @@ def get_settings(options: dict, gha: Optional[GithubAction] = None) -> Settings: deprecate_var(get_var('COMMENT_ON_PR', options) or None, 'COMMENT_ON_PR', 'Instead, use option "comment_mode" with values "off", "create new", or "update last".', gha) + if settings.job_summary: + check_var_condition(settings.job_summary_file, f'GITHUB_STEP_SUMMARY must point to a writeable file when JOB_SUMMARY is true') + return settings diff --git a/python/test/test_action_script.py b/python/test/test_action_script.py index ef52bdc5..3ba76080 100644 --- a/python/test/test_action_script.py +++ b/python/test/test_action_script.py @@ -184,7 +184,9 @@ def get_settings(token='token', ignore_runs=ignore_runs, check_run_annotation=check_run_annotation.copy(), seconds_between_github_reads=seconds_between_github_reads, - seconds_between_github_writes=seconds_between_github_writes + seconds_between_github_writes=seconds_between_github_writes, + job_summary=False, + job_summary_file=None, ) def test_get_settings(self): @@ -450,6 +452,7 @@ def do_test_get_settings(self, # annotations config tested in test_get_annotations_config* SECONDS_BETWEEN_GITHUB_READS='1.5', SECONDS_BETWEEN_GITHUB_WRITES='2.5', + JOB_SUMMARY='false' ) options.update(**kwargs) for arg in kwargs: @@ -791,7 +794,8 @@ def test_main_fork_pr_check(self): GITHUB_EVENT_PATH=file.name, GITHUB_EVENT_NAME='pull_request', GITHUB_REPOSITORY='repo', - EVENT_FILE=None + EVENT_FILE=None, + JOB_SUMMARY="false" ), gha) finally: if sys.platform == 'win32': diff --git a/python/test/test_publisher.py b/python/test/test_publisher.py index 1dd448b5..75d13345 100644 --- a/python/test/test_publisher.py +++ b/python/test/test_publisher.py @@ -53,7 +53,9 @@ def create_settings(comment_mode=comment_mode_create, event_name: str = 'event name', json_file: Optional[str] = None, pull_request_build: str = pull_request_build_mode_merge, - test_changes_limit: Optional[int] = 5): + test_changes_limit: Optional[int] = 5, + job_summary: Optional[bool] = False, + job_summary_file: Optional[str] = None): return Settings( token=None, api_url='https://the-github-api-url', @@ -81,7 +83,9 @@ def create_settings(comment_mode=comment_mode_create, ignore_runs=False, check_run_annotation=check_run_annotation, seconds_between_github_reads=1.5, - seconds_between_github_writes=2.5 + seconds_between_github_writes=2.5, + job_summary=job_summary, + job_summary_file=job_summary_file ) stats = UnitTestRunResults( @@ -1448,6 +1452,22 @@ def test_publish_json(self): } gha.set_output.assert_called_once_with('json', json.dumps(expected)) + def test_publish_summary(self): + with tempfile.TemporaryDirectory() as path: + filepath = os.path.join(path, 'summary.md') + settings = self.create_settings(job_summary=True, job_summary_file=filepath) + gh, gha, req, repo, commit = self.create_mocks(digest=self.base_digest, check_names=[settings.check_name]) + publisher = Publisher(settings, gh, gha) + + summary = 'Well, this summary is not very interesting' + + publisher.publish_summary(summary) + + with open(filepath, 'r') as summary_file: + contents = summary_file.read() + self.assertEqual(contents, summary) + + def test_publish_comment(self): settings = self.create_settings(event={'pull_request': {'base': {'sha': 'commit base'}}}, event_name='pull_request') base_commit = 'base-commit'