Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(configuration_support): change conditional branch steps type to … #796

Closed
wants to merge 20 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
b75f759
fix(test): new docker engine version compatibility
i-keliukh Apr 4, 2023
b828b1d
fix(analyzer): incorrect program name in help
i-keliukh Apr 5, 2023
1dd6977
fix(analyzer): documentation generation broken by last change
i-keliukh Apr 7, 2023
d06d608
fix(configuration): allow False value for Step
o-andrieiev Apr 10, 2023
e4db39b
fix(report): remove redundant file
o-andrieiev Apr 11, 2023
edd9c0c
feat(analyzer): code report based on clang-format
i-keliukh Apr 12, 2023
1086bd8
fix(workflow): disable link previews in TG bot
k-dovgan Apr 12, 2023
b902d9d
fix(workflow): add comment body for review comment
k-dovgan Apr 24, 2023
6c8d3f0
feat(configuration_support): change conditional branch steps type to …
miltolstoy May 4, 2023
0f84418
documentation update
miltolstoy May 7, 2023
075d3c0
update Step docstring
miltolstoy May 10, 2023
a3b80a5
fix(swarm): remove warning suppression that is no longer relevant
k-dovgan May 10, 2023
9f0894a
update(docs): update changelog to Universum 0.19.15
k-dovgan May 10, 2023
1f26ff0
update(doc): increase version
k-dovgan May 15, 2023
d335a6b
fix(doc): fix failing RTD builds
k-dovgan May 16, 2023
0112821
fix(test): outdated dependencies
k-dovgan May 16, 2023
31281a6
chore: merge with master branch (#805)
miltolstoy May 25, 2023
6919d68
Revert "chore: merge with master branch (#805)" (#806)
miltolstoy May 25, 2023
2d7ad99
chore: merge with master
miltolstoy May 25, 2023
a035a09
Merge branch 'cs_step_to_configuration' of github.com:Samsung/Univers…
miltolstoy May 25, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions .github/workflows/telegram-bot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ jobs:
PR_MERGED: ${{ github.event.pull_request.merged_by.login }}
REVIEW_STATE: ${{ github.event.review.state }}
REVIEW_AUTHOR: ${{ github.event.review.user.login }}
REVIEW_COMMENT: ${{ github.event.review.body }}
COMMENT_AUTHOR: ${{ github.event.comment.user.login }}
COMMENT_URL: ${{ github.event.comment.html_url }}
COMMENT_BODY: ${{ github.event.comment.body }}
Expand Down Expand Up @@ -51,7 +52,10 @@ jobs:

elif [[ ! -z "${{ github.event.review }}" && "${{ env.REVIEW_STATE }}" == "changes_requested" ]]; then
TEXT=`echo -e "<b>${{ env.REVIEW_AUTHOR }}</b> requested changes for <a href=\"${{ env.PR_URL }}\">PR#${{ env.PR_NUMBER }}</a>"`
elif [[ ! -z "${{ github.event.review }}" && "${{ env.REVIEW_STATE }}" != "changes_requested" ]]; then
elif [[ ! -z "${{ github.event.review }}" && "${{ env.REVIEW_STATE }}" == "commented" ]]; then
ESCAPED_TEXT=`echo -e "${{ env.REVIEW_COMMENT }}"| sed 's/\&/\&amp;/g' | sed 's/</\&lt;/g' | sed 's/>/\&gt;/g'`
TEXT=`echo -e "<b>${{ env.REVIEW_AUTHOR }}</b> posted the following comment to <a href=\"${{ env.PR_URL }}\">PR#${{ env.PR_NUMBER }}</a>:\n<i>$ESCAPED_TEXT</i>"`
elif [[ ! -z "${{ github.event.review }}" ]]; then
TEXT=`echo -e "<b>${{ env.REVIEW_AUTHOR }}</b> ${{ env.REVIEW_STATE }} <a href=\"${{ env.PR_URL }}\">PR#${{ env.PR_NUMBER }}</a>"`
elif [[ -z "${{ github.event.review }}" && "${{ github.event.action }}" == "submitted" ]]; then
TEXT=`echo -e "Due to GitHub Actions bug we cannot identify, who approved <a href=\"${{ env.PR_URL }}\">PR#${{ env.PR_NUMBER }}</a>"`
Expand All @@ -63,7 +67,7 @@ jobs:
fi

if [[ ! -z $TEXT ]]; then
curl --get --data-urlencode "chat_id=${{ secrets.TELEGRAM_CHAT_ID }}" \
curl --get --data-urlencode "chat_id=${{ secrets.TELEGRAM_CHAT_ID }}" --data-urlencode "disable_web_page_preview=True" \
--data-urlencode "text=$TEXT" --data-urlencode "parse_mode=HTML" $URL
fi

Expand Down
17 changes: 17 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
@@ -1,6 +1,23 @@
Change log
==========

0.19.15 (2023-05-10)
--------------------

New features
~~~~~~~~~~~~

* **analyzer:** code report based on clang-format

Bug fixes
~~~~~~~~~

* **config:** fixed returning "None" instead of "False" for Step custom keys set to "False"
* **artifact:** remove redundant Static_analysis_report.json from artifacts
* **analyzer:** incorrect program name in help
* **report:** set exit code even if reporting crashes


0.19.14 (2022-11-14)
--------------------

Expand Down
42 changes: 37 additions & 5 deletions doc/code_report.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ The following analysing modules (analysers) are currently added to Universum:
* `pylint`_
* `mypy`_
* `uncrustify`_
* `clang-format`_

Analysers are separate scripts, fully compatible with Universum. It is possible to use them
as independent Python modules.
Expand Down Expand Up @@ -62,7 +63,6 @@ Pylint

.. argparse::
:ref: universum.analyzers.pylint.pylint_argument_parser
:prog: {python} -m universum.analyzers.pylint

Config example for ``universum.analyzers.pylint``:

Expand Down Expand Up @@ -99,7 +99,6 @@ Mypy

.. argparse::
:ref: universum.analyzers.mypy.mypy_argument_parser
:prog: {python} -m universum.analyzers.mypy

Config example for ``universum.analyzers.mypy``:

Expand Down Expand Up @@ -136,7 +135,6 @@ Uncrustify

.. argparse::
:ref: universum.analyzers.uncrustify.uncrustify_argument_parser
:prog: {python} -m universum.analyzers.uncrustify
:nodefault:

Config example for ``universum.analyzers.uncrustify``:
Expand All @@ -146,7 +144,7 @@ Config example for ``universum.analyzers.uncrustify``:
from universum.configuration_support import Configuration, Step

configs = Configuration([Step(name="uncrustify", code_report=True, command=[
"{python}", "-m", "universum.analyzers.uncrustify", "--files", "/home/user/workspace/temp",
"{python}", "-m", "universum.analyzers.uncrustify", "--files", "/home/user/workspace/temp/*.c",
"--cfg-file", "file_name.cfg", "--result-file", "${CODE_REPORT_FILE}", "--output-directory", "uncrustify"
])])

Expand All @@ -164,4 +162,38 @@ will produce this list of configurations:
.. testoutput::

$ ./.universum.py
[{'name': 'uncrustify', 'code_report': True, 'command': '{python} -m universum.analyzers.uncrustify --files /home/user/workspace/temp --cfg-file file_name.cfg --result-file ${CODE_REPORT_FILE} --output-directory uncrustify'}]
[{'name': 'uncrustify', 'code_report': True, 'command': '{python} -m universum.analyzers.uncrustify --files /home/user/workspace/temp/*.c --cfg-file file_name.cfg --result-file ${CODE_REPORT_FILE} --output-directory uncrustify'}]

Clang-format
------------

.. argparse::
:ref: universum.analyzers.clang_format.clang_format_argument_parser
:nodefault:

Config example for ``universum.analyzers.clang_format``:

.. testcode::

from universum.configuration_support import Configuration, Step

configs = Configuration([Step(name="clang-format", code_report=True, command=[
"{python}", "-m", "universum.analyzers.clang-format", "--files", "/home/user/workspace/temp/*.c",
"--result-file", "${CODE_REPORT_FILE}", "--output-directory", "clang-format", "-e", "clang-format-15",
])])

if __name__ == '__main__':
print(configs.dump())

will produce this list of configurations:

.. testcode::
:hide:

print("$ ./.universum.py")
print(configs.dump())

.. testoutput::

$ ./.universum.py
[{'name': 'clang-format', 'code_report': True, 'command': '{python} -m universum.analyzers.clang-format --files /home/user/workspace/temp/*.c --result-file ${CODE_REPORT_FILE} --output-directory clang-format -e clang-format-15'}]
2 changes: 1 addition & 1 deletion doc/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@
#
# This is also used if you do content translation via gettext catalogs.
# Usually you set "language" from the command line for these cases.
language = None
language = 'en'

# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
Expand Down
12 changes: 6 additions & 6 deletions doc/configuring.rst
Original file line number Diff line number Diff line change
Expand Up @@ -441,18 +441,18 @@ If any of them is missing or not set in current environment, the step will be ex
Conditional steps
---------------------

Conditional step is a :class:`Step` object, that has ``if_succeeded`` or ``if_failed`` parameters with other steps assigned.
If the conditional step succeeds, then the step from the ``if_succeeded`` parameter will be executed.
If the conditional step fails, the step from the ``if_failed`` parameter will be executed instead.
Conditional step is a :class:`Step` object, that has ``if_succeeded`` or ``if_failed`` parameters with other :class:`Configuration` objects assigned.
If the conditional step succeeds, then the Configuration from the ``if_succeeded`` parameter will be executed.
If the conditional step fails, then the Configuration from the ``if_failed`` parameter will be executed instead.

Configuration example:

.. code-block:: python

from universum.configuration_support import Configuration, Step

true_branch_step = Step(name="Positive branch step", command=["ls"])
false_branch_step = Step(name="Negative branch step", command=["pwd"])
true_branch_step = Configuration([Step(name="Positive branch step", command=["ls"])])
false_branch_step = Configuration([Step(name="Negative branch step", command=["pwd"])])
conditional_step = Step(name="Conditional step", command=["./script.sh"],
if_succeeded=true_branch_step, if_failed=false_branch_step)

Expand Down Expand Up @@ -502,7 +502,7 @@ In general, conditional steps behave as any other regular steps, but here are so
* Will always be marked as successful in the log
* TeamCity tag will not be set for the conditional step
* Branch steps
* Only one branch step will be executed
* Only one branch Configuration will be executed
* Both branches' artifacts will be checked for existence before the steps execution
* Artifacts collection or any other side-effects will not be triggered for non-executed branch step
* If chosen branch step is not set, nothing will happen.
Expand Down
5 changes: 4 additions & 1 deletion pylintrc
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,7 @@ max-parents = 10
max-line-length = 130

[SIMILARITIES]
ignore-imports=yes
ignore-imports=yes

[TYPECHECK]
signature-mutators=universum.analyzers.utils.analyzer
7 changes: 5 additions & 2 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@ def readme():
'sh',
'lxml',
'typing-extensions',
'ansi2html'
'ansi2html',
'pyyaml==6.0'
],
extras_require={
'p4': [p4],
Expand All @@ -56,7 +57,9 @@ def readme():
'coverage',
'mypy',
'types-requests',
'selenium==3.141'
'selenium==3.141',
'urllib3==1.26.15', # This is required for selenium-3.141 to work correctly
'types-PyYAML==6.0'
]
},
package_data={'': ['*.css', '*.js']}
Expand Down
3 changes: 2 additions & 1 deletion tests/deployment_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,8 @@ def add_bind_dirs(self, directories):
if self._container:
self.request.raiseerror("Container is already running, no dirs can be bound!")
for directory in directories:
self._volumes[directory] = {'bind': directory, 'mode': 'rw'}
absolute_dir = str(pathlib.Path(directory).absolute())
self._volumes[absolute_dir] = {'bind': absolute_dir, 'mode': 'rw'}

def add_environment_variables(self, variables):
if self._container:
Expand Down
63 changes: 47 additions & 16 deletions tests/test_code_report.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
def fixture_runner_with_analyzers(docker_main: UniversumRunner):
docker_main.environment.install_python_module("pylint")
docker_main.environment.install_python_module("mypy")
docker_main.environment.assert_successful_execution("apt install uncrustify")
docker_main.environment.assert_successful_execution("apt install -y uncrustify clang-format")
yield docker_main


Expand Down Expand Up @@ -128,6 +128,11 @@ def finalize(self) -> str:
input_tab_size = 2
"""

config_clang_format = """
---
AllowShortFunctionsOnASingleLine: Empty
"""

log_fail = r'Found [0-9]+ issues'
log_success = r'Issues not found'

Expand Down Expand Up @@ -167,7 +172,10 @@ def test_code_report_direct_log(runner_with_analyzers: UniversumRunner, tested_c

@pytest.mark.parametrize('analyzers, extra_args, tested_content, expected_success', [
[['uncrustify'], [], source_code_c, True],
[['uncrustify'], [], source_code_c.replace('\t', ' '), False],
[['uncrustify'], [], source_code_c.replace('\t', ' '), False], # by default uncrustify converts spaces to tabs
[['clang_format'], [], source_code_c.replace('\t', ' '), True], # by default clang-format expands tabs to 2 spaces
[['clang_format'], [], source_code_c.replace('\t', ' '), False],
[['clang_format', 'uncrustify'], [], source_code_c.replace('\t', ' '), False],
[['pylint', 'mypy'], ["--python-version", python_version()], source_code_python, True],
[['pylint'], ["--python-version", python_version()], source_code_python + '\n', False],
[['mypy'], ["--python-version", python_version()], source_code_python.replace(': str', ': int'), False],
Expand All @@ -177,6 +185,9 @@ def test_code_report_direct_log(runner_with_analyzers: UniversumRunner, tested_c
], ids=[
'uncrustify_no_issues',
'uncrustify_found_issues',
'clang_format_no_issues',
'clang_format_found_issues',
'clang_format_and_uncrustify_found_issues',
'pylint_and_mypy_both_no_issues',
'pylint_found_issues',
'mypy_found_issues',
Expand All @@ -194,6 +205,9 @@ def test_code_report_log(runner_with_analyzers: UniversumRunner, analyzers, extr
if analyzer == 'uncrustify':
args += ["--cfg-file", "cfg"]
(runner_with_analyzers.local.root_directory / "cfg").write_text(config_uncrustify)
elif analyzer == 'clang_format':
(runner_with_analyzers.local.root_directory / ".clang-format").write_text(config_clang_format)

config.add_analyzer(analyzer, args)

log = runner_with_analyzers.run(config.finalize())
Expand All @@ -210,7 +224,7 @@ def test_without_code_report_command(runner_with_analyzers: UniversumRunner):
assert not pattern.findall(log)


@pytest.mark.parametrize('analyzer', ['pylint', 'mypy', 'uncrustify'])
@pytest.mark.parametrize('analyzer', ['pylint', 'mypy', 'uncrustify', 'clang_format'])
@pytest.mark.parametrize('arg_set, expected_log', [
[["--files", "source_file.py"], "error: the following arguments are required: --result-file"],
[["--files", "source_file.py", "--result-file"], "result-file: expected one argument"],
Expand All @@ -235,10 +249,12 @@ def test_analyzer_python_version_params(runner_with_analyzers: UniversumRunner,
"--result-file", "${CODE_REPORT_FILE}", '--rcfile'],
"rcfile: expected one argument"],
['uncrustify', ["--files", "source_file", "--result-file", "${CODE_REPORT_FILE}"],
"Please specify the '--cfg_file' parameter or set an env. variable 'UNCRUSTIFY_CONFIG'"],
"Please specify the '--cfg-file' parameter or set 'UNCRUSTIFY_CONFIG' environment variable"],
['uncrustify', ["--files", "source_file", "--result-file", "${CODE_REPORT_FILE}",
"--cfg-file", "cfg", "--output-directory", "."],
"Target and source folders for uncrustify are not allowed to match"],
"Target folder must not be identical to source folder"],
['clang_format', ["--files", "source_file", "--result-file", "${CODE_REPORT_FILE}", "--output-directory", "."],
"Target folder must not be identical to source folder"],
])
def test_analyzer_specific_params(runner_with_analyzers: UniversumRunner, analyzer, arg_set, expected_log):
source_file = runner_with_analyzers.local.root_directory / "source_file"
Expand All @@ -249,39 +265,54 @@ def test_analyzer_specific_params(runner_with_analyzers: UniversumRunner, analyz
assert expected_log in log, f"'{expected_log}' is not found in '{log}'"


@pytest.mark.parametrize('extra_args, tested_content, expected_success, expected_artifact', [
[[], source_code_c, True, False],
[["--report-html"], source_code_c.replace('\t', ' '), False, True],
[[], source_code_c.replace('\t', ' '), False, False],
@pytest.mark.parametrize('analyzer, extra_args, tested_content, expected_success, expected_artifact', [
['uncrustify', ["--report-html"], source_code_c, True, False],
['uncrustify', ["--report-html"], source_code_c.replace('\t', ' '), False, True],
['uncrustify', [], source_code_c.replace('\t', ' '), False, False],
['clang_format', ["--report-html"], source_code_c.replace('\t', ' '), True, False],
['clang_format', ["--report-html"], source_code_c, False, True],
['clang_format', [], source_code_c, False, False],
], ids=[
"uncrustify_html_file_not_needed",
"uncrustify_html_file_saved",
"uncrustify_html_file_disabled",
"clang_format_html_file_not_needed",
"clang_format_html_file_saved",
"clang_format_html_file_disabled",
])
def test_uncrustify_file_diff(runner_with_analyzers: UniversumRunner,
extra_args, tested_content, expected_success, expected_artifact):
def test_diff_html_file(runner_with_analyzers: UniversumRunner, analyzer,
extra_args, tested_content, expected_success, expected_artifact):

root = runner_with_analyzers.local.root_directory
source_file = root / "source_file"
source_file.write_text(tested_content)
(root / "cfg").write_text(config_uncrustify)
common_args = [
"--result-file", "${CODE_REPORT_FILE}",
"--files", "source_file",
"--cfg-file", "cfg",
"--output-directory", "diff_temp"
]
if analyzer == 'uncrustify':
(root / "cfg").write_text(config_uncrustify)
common_args.extend(["--cfg-file", "cfg"])
elif analyzer == 'clang_format':
(root / ".clang-format").write_text(config_clang_format)


args = common_args + extra_args
extra_config = "artifacts='./uncrustify/source_file.html'"
log = runner_with_analyzers.run(ConfigData().add_analyzer('uncrustify', args, extra_config).finalize())
extra_config = "artifacts='./diff_temp/source_file.html'"
log = runner_with_analyzers.run(ConfigData().add_analyzer(analyzer, args, extra_config).finalize())

expected_log = log_success if expected_success else log_fail
assert re.findall(expected_log, log), f"'{expected_log}' is not found in '{log}'"
expected_artifacts_state = "Success" if expected_artifact else "Failed"
expected_log = f"Collecting artifacts for the 'Run uncrustify' step - [^\n]*{expected_artifacts_state}"
expected_log = f"Collecting artifacts for the 'Run {analyzer}' step - [^\n]*{expected_artifacts_state}"
assert re.findall(expected_log, log), f"'{expected_log}' is not found in '{log}'"


def test_code_report_extended_arg_search(tmp_path: pathlib.Path, stdout_checker: FuzzyCallChecker):
"""
Test if ${CODE_REPORT_FILE} is replaced not only in --result-file argument of the Step
"""
env = utils.LocalTestEnvironment(tmp_path, "main")
env.settings.Vcs.type = "none"
env.settings.LocalMainVcs.source_dir = str(tmp_path)
Expand Down
4 changes: 2 additions & 2 deletions tests/test_conditional_steps.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,9 +98,9 @@ def _build_step_command(files_to_create, exit_code) -> List[str]:
return ["bash", "-c", ";".join(commands)]

def _write_config_file(self, steps_info) -> None:
true_branch_step: str = f"Step(**{str(steps_info.true_branch_step)})" \
true_branch_step: str = f"Configuration([Step(**{str(steps_info.true_branch_step)})])" \
if steps_info.true_branch_step else "None"
false_branch_step: str = f"Step(**{str(steps_info.false_branch_step)})" \
false_branch_step: str = f"Configuration([Step(**{str(steps_info.false_branch_step)})])" \
if steps_info.false_branch_step else "None"
config_lines: List[str] = [
"from universum.configuration_support import Configuration, Step",
Expand Down
2 changes: 1 addition & 1 deletion tests/test_nonci.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ def test_launcher_output(docker_nonci: UniversumRunner):
- version control and review system are not used
- project root is set to current directory
"""
cwd = str(docker_nonci.local.root_directory)
cwd = str(docker_nonci.local.root_directory.absolute())
artifacts = docker_nonci.artifact_dir
file_output_expected = f"Adding file {artifacts}/test_step_log.txt to artifacts"
pwd_string_in_logs = f"pwd:[{cwd}]"
Expand Down
2 changes: 1 addition & 1 deletion universum/__init__.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
__title__ = "Universum"
__version__ = "0.19.15"
__version__ = "0.19.16"
Loading