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

Additional improvements #2

Merged
merged 4 commits into from Jan 16, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
6 changes: 2 additions & 4 deletions .github/workflows/build.yaml
Expand Up @@ -2,8 +2,6 @@ name: mechanical-markdown

on:
push:
branches:
- main
tags:
- v*
pull_request:
Expand All @@ -16,8 +14,8 @@ jobs:
runs-on: ubuntu-latest
env:
PYTHON_VER: 3.7
TWINE_USERNAME: ${{ secrets.PYPI_UPLOAD_USER }}
TWINE_PASSWORD: ${{ secrets.PYPI_UPLOAD_PASS }}
TWINE_USERNAME: "__token__"
TWINE_PASSWORD: ${{ secrets.PYPI_TOKEN }}
steps:
- uses: actions/checkout@v2
- name: Set up Python ${{ env.PYTHON_VER }}
Expand Down
2 changes: 1 addition & 1 deletion README.md
@@ -1,6 +1,6 @@
# Mechanical Markdown

[![codecov](https://codecov.io/gh/wcs1only/mechanical-markdown/branch/main/graph/badge.svg)](https://codecov.io/gh/wcs1only/mechanical-markdown)
[![codecov](https://codecov.io/gh/dapr/mechanical-markdown/branch/main/graph/badge.svg)](https://codecov.io/gh/dapr/mechanical-markdown)

If you are using markdown to create tutorials for your users, these markdown files will often be a series of shell commands that a user will copy and paste into their shell of choice, along with detailed text description of what each command is doing.

Expand Down
1 change: 1 addition & 0 deletions examples/README.md
Expand Up @@ -154,6 +154,7 @@ expected_stdout_lines:
- "Would run the following validation steps:"
- "Step: Hello World"
- "Step: CLI help"
- "Step: CLI version"
- "Step: CLI dry run"
- "Step: Pause for manual validation"
-->
Expand Down
31 changes: 31 additions & 0 deletions examples/io.md
@@ -1,5 +1,6 @@
# I/O Validation

## Using This Guide
This is an example markdown file with an annotated test command.

To see a summary of what commands will be run:
Expand All @@ -16,6 +17,8 @@ mm.py io.md

Be sure to checkout the raw version of this file to see the annotations.

## Checking stdout/stderr

This is an annotated command. When the ```mm.py``` utility is run, the following code block will be executed:

<!-- STEP
Expand Down Expand Up @@ -53,6 +56,34 @@ echo "error" 1>&2

<!-- END_STEP -->

## Checking return code

By default, all code blocks are expected to return 0. You can change this behavior with the directive ```expected_return_code```:

<!-- STEP
name: Non-zero Return Code
expected_return_code: 1
-->

```bash
exit 1
```

<!-- END_STEP -->

A missing or null value for ```expected_return_code``` will ignore all return codes.

<!-- STEP
name: Ignore Return Code
expected_return_code:
-->

```bash
exit 15
```

<!-- END_STEP -->

# Navigation

* Back to [README](README.md)
Expand Down
2 changes: 1 addition & 1 deletion mechanical_markdown/__init__.py
Expand Up @@ -7,6 +7,6 @@
from mechanical_markdown.recipe import Recipe as MechanicalMarkdown
from mechanical_markdown.parsers import MarkdownAnnotationError

__version__ = '0.1.8'
__version__ = '0.2.2'

__all__ = [MechanicalMarkdown, MarkdownAnnotationError]
11 changes: 8 additions & 3 deletions mechanical_markdown/step.py
Expand Up @@ -29,6 +29,7 @@ def __init__(self, parameters):
self.expected_lines['stdout'] = parameters["expected_stdout_lines"]
if "expected_stderr_lines" in parameters and parameters["expected_stderr_lines"] is not None:
self.expected_lines['stderr'] = parameters["expected_stderr_lines"]
self.expect_return_code = 0 if "expected_return_code" not in parameters else parameters["expected_return_code"]
self.working_dir = os.getcwd() if "working_dir" not in parameters else parameters["working_dir"]
self.timeout = default_timeout_seconds if "timeout_seconds" not in parameters else parameters["timeout_seconds"]
self.env = dict(os.environ, **parameters['env']) if "env" in parameters else os.environ
Expand All @@ -50,7 +51,7 @@ def run_all_commands(self, manual, shell):
command.run(self.working_dir, self.env, shell)
if not self.background:
command.wait_or_timeout(self.timeout)
if command.return_code != 0:
if self.expect_return_code is not None and command.return_code != self.expect_return_code:
return False
if self.sleep:
time.sleep(self.sleep)
Expand All @@ -69,14 +70,16 @@ def dryrun(self, shell):
for expected in self.expected_lines[out]:
retstr += "\t\t{}\n".format(expected)

retstr += "\tExpected return code: {}\n".format(self.expect_return_code)

return retstr + "\n"

def wait_for_all_background_commands(self):
success = True
for command in self.commands:
if self.background:
command.wait_or_timeout(self.timeout)
if command.return_code != 0:
if self.expect_return_code is not None and command.return_code != self.expect_return_code:
success = False
return success

Expand All @@ -89,7 +92,9 @@ def validate_and_report(self):
for c in self.commands:
if c.process is not None:
color = 'green'
if c.return_code != 0:
if self.expect_return_code is None:
color = 'yellow'
elif c.return_code != self.expect_return_code:
color = 'red'
report += "\tcommand: `{}`\n\treturn_code: {}\n".format(c.command, colored(c.return_code, color))

Expand Down
2 changes: 1 addition & 1 deletion setup.py
Expand Up @@ -23,7 +23,7 @@
description="Run markdown recipes as shell scripts",
long_description=README,
long_description_content_type="text/markdown",
url="https://github.com/wcs1only/mechanical-markdown",
url="https://github.com/dapr/mechanical-markdown",
author="Charlie Stanley",
author_email="Charlie.Stanley@microsoft.com",
license="MIT",
Expand Down
44 changes: 44 additions & 0 deletions tests/test_mechanical_markdown.py
Expand Up @@ -344,6 +344,7 @@ def test_dryrun(self):
\t\ttest
\t\ttest2
\tExpected stderr:
\tExpected return code: 0

Step: step 2
\tcommands to run with 'bash -c':
Expand All @@ -353,6 +354,7 @@ def test_dryrun(self):
\t\tfoo
\tExpected stderr:
\t\tbar
\tExpected return code: 0

"""
self.assertEqual(expected_output, output)
Expand Down Expand Up @@ -468,3 +470,45 @@ def test_missing_extra_tag_throws_exception(self):
"""
with self.assertRaises(MarkdownAnnotationError):
MechanicalMarkdown(test_data)

def test_expect_status_code_success(self):
test_data = """
<!-- STEP
name: expect returns 1
expected_return_code: 1
-->

```bash
exit 1
```

<!-- END_STEP -->

<!-- STEP
name: ignore return code
expected_return_code:
-->

```bash
exit 15
```

<!-- END_STEP -->
"""
self.prep_command_ouput("test", "", 1)
self.prep_command_ouput("test", "", 15)
mm = MechanicalMarkdown(test_data)
success, report = mm.exectute_steps(False)
self.assertTrue(success)
calls = [call(['bash', '-c', 'exit 1'],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
universal_newlines=True,
env=os.environ),
call().communicate(timeout=60),
call(['bash', '-c', 'exit 15'],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
universal_newlines=True,
env=os.environ)]
self.popen_mock.assert_has_calls(calls)