diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index f8af9bc..a4daecf 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -2,8 +2,6 @@ name: mechanical-markdown on: push: - branches: - - main tags: - v* pull_request: @@ -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 }} diff --git a/README.md b/README.md index 30fb51d..bb590ec 100644 --- a/README.md +++ b/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. diff --git a/examples/README.md b/examples/README.md index d95467d..4de1b71 100644 --- a/examples/README.md +++ b/examples/README.md @@ -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" --> diff --git a/examples/io.md b/examples/io.md index 6b85990..cc62a72 100644 --- a/examples/io.md +++ b/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: @@ -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: +## Checking return code + +By default, all code blocks are expected to return 0. You can change this behavior with the directive ```expected_return_code```: + + + +```bash +exit 1 +``` + + + +A missing or null value for ```expected_return_code``` will ignore all return codes. + + + +```bash +exit 15 +``` + + + # Navigation * Back to [README](README.md) diff --git a/mechanical_markdown/__init__.py b/mechanical_markdown/__init__.py index 327db29..2e7bb74 100644 --- a/mechanical_markdown/__init__.py +++ b/mechanical_markdown/__init__.py @@ -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] diff --git a/mechanical_markdown/step.py b/mechanical_markdown/step.py index a645b86..8344bc5 100644 --- a/mechanical_markdown/step.py +++ b/mechanical_markdown/step.py @@ -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 @@ -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) @@ -69,6 +70,8 @@ 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): @@ -76,7 +79,7 @@ def wait_for_all_background_commands(self): 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 @@ -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)) diff --git a/setup.py b/setup.py index 324d0b8..1881842 100644 --- a/setup.py +++ b/setup.py @@ -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", diff --git a/tests/test_mechanical_markdown.py b/tests/test_mechanical_markdown.py index 06fd625..f481747 100644 --- a/tests/test_mechanical_markdown.py +++ b/tests/test_mechanical_markdown.py @@ -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': @@ -353,6 +354,7 @@ def test_dryrun(self): \t\tfoo \tExpected stderr: \t\tbar +\tExpected return code: 0 """ self.assertEqual(expected_output, output) @@ -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 = """ + + +```bash +exit 1 +``` + + + + + +```bash +exit 15 +``` + + +""" + 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)