From b0757cccf6c6df121cbfa06d764d41722be6ca1b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C3=ABl?= Date: Thu, 21 Oct 2021 15:52:18 +0200 Subject: [PATCH] Issue 15 | CI: Submission workflow (#46) * Update configuration.py * add standart tests check * positive skeleton * align * add error job * add cli issue-parser * method --> function * black is the new mint * output of issue parser * delete perm and edit comment * make manager parse static * add comment about cli + typo * add github.event.action == 'created' check * add runs url * add_repo_2db cli * calling add_repo_2db cmd * black * doctring + green * typo + delete strategy * add cli comment * add stuff * add unit tests for CI * add test for comment trigger and correct things * add act install into tox * everything in commands opts * add logger result * add comments * add check status logs in issue comment * Update test_utils.py * Update test_utils.py * Update test_utils.py * add manager.py test cli * black * lint * Update .coveragerc * Update .coveragerc * Update utils.py * Update manager.py * from review * ecosystem/manager.py * lint & black * black * Update manager.py * just 1 logger * linter * add terra version + clean log result * add terra version to the log PASS * add full log * black * format multilines --- .coveragerc | 2 + .github/workflows/submission.yml | 171 ++++++++++++++++++ .github/workflows/test_payloads/my.env | 3 + .../submission-wf-commenttrigger.json | 11 ++ .../test_payloads/submission-wf-newissue.json | 8 + ecosystem/manager.py | 93 +++++++++- ecosystem/utils/utils.py | 13 ++ manager.py | 10 + tests/manager.py | 50 +++++ tox.ini | 7 + 10 files changed, 366 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/submission.yml create mode 100644 .github/workflows/test_payloads/my.env create mode 100644 .github/workflows/test_payloads/submission-wf-commenttrigger.json create mode 100644 .github/workflows/test_payloads/submission-wf-newissue.json create mode 100644 tests/manager.py diff --git a/.coveragerc b/.coveragerc index 0e50080217..40ff43464b 100644 --- a/.coveragerc +++ b/.coveragerc @@ -3,3 +3,5 @@ omit = # omit tempalte files ecosystem/templates/* .tox/* + .github/* + docs/* diff --git a/.github/workflows/submission.yml b/.github/workflows/submission.yml new file mode 100644 index 0000000000..eccf073e21 --- /dev/null +++ b/.github/workflows/submission.yml @@ -0,0 +1,171 @@ +name: Submission check + +### +# - install deps +# - parse issue +# - run lint, coverage, tests +# - if success: +# -- comment of success +# -- create PR with change to readme +# -- link pr to issue +# - if something went wrong: +# -- comment run url in issue + +on: + issues: + types: [opened] + issue_comment: + types: [created] + +jobs: + submission_workflow: + if: | + (startsWith(github.event.issue.title, '[Submission]:') && github.event.action == 'opened') || + (github.event.comment.body == '!redo' && startsWith(github.event.issue.title, '[Submission]:')) + runs-on: ubuntu-latest + env: + tox_env: "py39" + tier: "MAIN" + python-version: "3.9" + steps: +################################################################################################### +### Set up deps +################################################################################################### + - uses: actions/checkout@v2 + - name: Set up Python ${{ env.python-version }} + uses: actions/setup-python@v2 + with: + python-version: ${{ env.python-version }} + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -r requirements.txt + pip install -r requirements-dev.txt + +################################################################################################### +### Workflow +################################################################################################### + - name: Parse submission + id: parse-issue + run: | + python manager.py parser_issue --body="${{ github.event.issue.body }}" + + - name: Linter check + id: linter + uses: ./.github/actions/run-tests + with: + repo_url: ${{ steps.parse-issue.outputs.SUBMISSION_REPO }} + check_type: "lint" + tox_env: ${{ env.tox_env }} + tier: ${{ env.tier }} + - name: Coverage check + id: coverage + uses: ./.github/actions/run-tests + with: + repo_url: ${{ steps.parse-issue.outputs.SUBMISSION_REPO }} + check_type: "coverage" + tox_env: ${{ env.tox_env }} + tier: ${{ env.tier }} + - name: Tests stable check + id: stable + uses: ./.github/actions/run-tests + with: + repo_url: ${{ steps.parse-issue.outputs.SUBMISSION_REPO }} + check_type: "test_stable" + tox_env: ${{ env.tox_env }} + tier: ${{ env.tier }} + - name: Tests dev check + id: dev + uses: ./.github/actions/run-tests + with: + repo_url: ${{ steps.parse-issue.outputs.SUBMISSION_REPO }} + check_type: "test_dev" + tox_env: ${{ env.tox_env }} + tier: ${{ env.tier }} + +################################################################################################### +### Check result, update issue and create MR +################################################################################################### + - name: Check return + run: | + echo "PASS_LOG=True" >> $GITHUB_ENV + + declare -a return_list=( \ + "${{ steps.linter.outputs.PASS }}" \ + "${{ steps.coverage.outputs.PASS }}" \ + "${{ steps.stable.outputs.PASS }}" \ + "${{ steps.dev.outputs.PASS }}" \ + ) + for i in "${return_list[@]}"; do + if [[ "${i}" != *"True"* ]]; then + echo "PASS_LOG=False" >> $GITHUB_ENV + fi + done + + - name: Check the check + id: check_result + run: | + echo "Pass log is ${{ env.PASS_LOG }}" + echo 'full_log<> $GITHUB_ENV + curl -H "Accept: application/vnd.github.v3+json" \ + https://api.github.com/repos/qiskit-community/ecosystem/actions/jobs/${{ GITHUB_JOB }}/logs >> $GITHUB_ENV + echo 'EOF' >> $GITHUB_ENV + + - name: Update readme + if: ${{ env.PASS_LOG == 'True' }} + run: | + python manager.py add_repo_2db --repo_author=${{ steps.parse-issue.outputs.SUBMISSION_NAME }} \ + --repo_link="${{ steps.parse-issue.outputs.SUBMISSION_REPO }}" \ + --repo_description="${{ steps.parse-issue.outputs.SUBMISSION_DESCRIPTION }}" \ + --repo_licence="${{ steps.parse-issue.outputs.SUBMISSION_LICENCE }}" \ + --repo_contact="${{ steps.parse-issue.outputs.SUBMISSION_CONTACT }}" \ + --repo_alt="${{ steps.parse-issue.outputs.SUBMISSION_ALTERNATIVES }}" \ + --repo_affiliations="${{ steps.parse-issue.outputs.SUBMISSION_AFFILIATIONS }}" \ + --repo_labels="${{ steps.parse-issue.outputs.SUBMISSION_LABELS }}" + python manager.py generate_readme + + - name: Commit changes and create Pull Request + if: ${{ env.PASS_LOG == 'True' && CI == "true" }} + id: cpr + uses: peter-evans/create-pull-request@v3 + with: + repository: "qiskit-community/ecosystem" + commit-message: Submission - Add ${{ steps.parse-issue.outputs.SUBMISSION_REPO }} to list. + title: Add ${{ steps.parse-issue.outputs.SUBMISSION_NAME }} to list. + body: | + Add ${{ steps.parse-issue.outputs.SUBMISSION_NAME }} to list. + Closes #${{ github.event.issue.number }} + branch: submission-${{ github.event.issue.number }} + base: main + + - name: Create comment on success + if: ${{ env.PASS_LOG == 'True' }} + uses: peter-evans/create-or-update-comment@v1 + with: + repository: "qiskit-community/ecosystem" + issue-number: ${{ github.event.issue.number }} + body: | + Successfull submission! :sparkles: PR #${{ steps.cpr.outputs.pull-request-number }} + + - name: Create comment on failure + if: ${{ env.PASS_LOG == 'False' }} + uses: peter-evans/create-or-update-comment@v1 + with: + repository: "qiskit-community/ecosystem" + issue-number: ${{ github.event.issue.number }} + body: | + Fail submission! :warning: + Error see: https://github.com/qiskit-community/ecosystem/actions/runs/${{ GITHUB_RUN_ID }} + You can write a comment with "!redo" to rerun the submission process. + ### Checks Status ### + Linter : ${{ steps.linter.outputs.PASS }} + Coverage : ${{ steps.coverage.outputs.PASS }} + Qiskit Stable : ${{ steps.stable.outputs.PASS }} + Qiskit Dev : ${{ steps.dev.outputs.PASS }} + + ### Logs ### +
+ Full log + ${{ steps.check_result.outputs.LOG }} + ${{ env.full_log }} +
diff --git a/.github/workflows/test_payloads/my.env b/.github/workflows/test_payloads/my.env new file mode 100644 index 0000000000..c7a5d7789f --- /dev/null +++ b/.github/workflows/test_payloads/my.env @@ -0,0 +1,3 @@ +GITHUB_RUN_ID=42 +steps.cpr.outputs.pull-request-number=21 +GITHUB_JOB=42 diff --git a/.github/workflows/test_payloads/submission-wf-commenttrigger.json b/.github/workflows/test_payloads/submission-wf-commenttrigger.json new file mode 100644 index 0000000000..f27e6834ef --- /dev/null +++ b/.github/workflows/test_payloads/submission-wf-commenttrigger.json @@ -0,0 +1,11 @@ +{ + "action": "created", + "issue": { + "number": 21, + "title": "[Submission]: test", + "body": "### Github repo\n\nhttps://github.com/IceKhan13/demo-implementation\n\n### Description\n\ngrgehtehtehtbt gerh erhrieh et(ethte\n\n### Email\n\ntoto@gege.com\n\n### Alternatives\n\nrhthtjthgrer\n\n### License\n\nApache License 2.0\n\n### Affiliations\n\n_No response_\n\n### Tags\n\ntool, tutorial" + }, + "comment": { + "body": "!redo" + } +} diff --git a/.github/workflows/test_payloads/submission-wf-newissue.json b/.github/workflows/test_payloads/submission-wf-newissue.json new file mode 100644 index 0000000000..a31069240e --- /dev/null +++ b/.github/workflows/test_payloads/submission-wf-newissue.json @@ -0,0 +1,8 @@ +{ + "action": "opened", + "issue": { + "number": 21, + "title": "[Submission]: test", + "body": "### Github repo\n\nhttps://github.com/mickahell/quantum_pokemon-fight\n\n### Description\n\ngrgehtehtehtbt gerh erhrieh et(ethte\n\n### Email\n\ntoto@gege.com\n\n### Alternatives\n\nrhthtjthgrer\n\n### License\n\nApache License 2.0\n\n### Affiliations\n\n_No response_\n\n### Tags\n\ntool, tutorial" + } +} diff --git a/ecosystem/manager.py b/ecosystem/manager.py index 7c32bcbc69..dba13d6e84 100644 --- a/ecosystem/manager.py +++ b/ecosystem/manager.py @@ -1,16 +1,18 @@ """Manager class for controlling all CLI functions.""" import os -from typing import Optional, List +from typing import Optional, List, Tuple from jinja2 import Environment, PackageLoader, select_autoescape from ecosystem.daos import JsonDAO from ecosystem.models import TestResult, Tier, TestType +from ecosystem.models.repository import Repository from ecosystem.models.test_results import StyleResult, CoverageResult from ecosystem.runners import PythonTestsRunner from ecosystem.runners.python_styles_runner import PythonStyleRunner from ecosystem.runners.python_coverages_runner import PythonCoverageRunner -from ecosystem.utils import logger +from ecosystem.utils import logger, parse_submission_issue +from ecosystem.utils.utils import set_actions_output class Manager: @@ -37,6 +39,69 @@ def __init__(self): self.controller = JsonDAO(path=self.resources_dir) self.logger = logger + @staticmethod + def parser_issue(body: str) -> None: + """Command for calling body issue parsing function. + + Args: + body: body of the created issue + Returns: + logs output + We want to give the result of the parsing issue to the GitHub action + """ + + parsed_result = parse_submission_issue(body) + + to_print = [ + ("SUBMISSION_NAME", parsed_result.name), + ("SUBMISSION_REPO", parsed_result.url), + ("SUBMISSION_DESCRIPTION", parsed_result.description), + ("SUBMISSION_LICENCE", parsed_result.licence), + ("SUBMISSION_CONTACT", parsed_result.contact_info), + ("SUBMISSION_ALTERNATIVES", parsed_result.alternatives), + ("SUBMISSION_AFFILIATIONS", parsed_result.affiliations), + ("SUBMISSION_LABELS", parsed_result.labels), + ] + + set_actions_output(to_print) + + def add_repo_2db( + self, + repo_author: str, + repo_link: str, + repo_description: str, + repo_licence: str, + repo_contact: str, + repo_alt: str, + repo_affiliations: str, + repo_labels: Tuple[str], + ) -> None: + """Adds repo to list of entries. + Args: + repo_author: repo author + repo_link: repo url + repo_description: repo description + repo_contact: repo email + repo_alt: repo alternatives + repo_licence: repo licence + repo_affiliations: repo university, company, ... + repo_labels: comma separated labels + Returns: + JsonDAO: Integer + """ + + new_repo = Repository( + repo_author, + repo_link, + repo_description, + repo_licence, + repo_contact, + repo_alt, + repo_affiliations, + repo_labels, + ) + self.controller.insert(new_repo) + def generate_readme(self, path: Optional[str] = None): """Generates entire readme for ecosystem repository. @@ -65,6 +130,9 @@ def _run_python_tests( python_version: ex: py36, py37 etc test_type: [dev, stable] ecosystem_deps: extra dependencies to install for tests + Return: + output: log PASS + We want to give the result of the test to the GitHub action """ ecosystem_deps = ecosystem_deps or [] runner = PythonTestsRunner( @@ -91,8 +159,19 @@ def _run_python_tests( repo_url, ) self.logger.info("Test results for %s: %s", repo_url, test_result) + set_actions_output( + [ + ( + "PASS", + str(test_result.passed) + + " - Terra version : " + + str(test_result.terra_version), + ) + ] + ) else: self.logger.warning("Runner returned 0 results.") + set_actions_output([("PASS", "False")]) return terra_version @@ -103,6 +182,9 @@ def python_styles_check(self, repo_url: str, tier: str, style_type: str): repo_url: repository url tier: tier of project style_type: [dev, stable] + Return: + output: log PASS + We want to give the result of the test to the GitHub action """ runner = PythonStyleRunner(repo_url, working_directory=self.resources_dir) _, results = runner.run() @@ -121,8 +203,10 @@ def python_styles_check(self, repo_url: str, tier: str, style_type: str): repo_url, ) self.logger.info("Test results for %s: %s", repo_url, style_result) + set_actions_output([("PASS", style_result.passed)]) else: self.logger.warning("Runner returned 0 results.") + set_actions_output([("PASS", "False")]) def python_coverage(self, repo_url: str, tier: str, coverage_type: str): """Runs tests using python runner. @@ -131,6 +215,9 @@ def python_coverage(self, repo_url: str, tier: str, coverage_type: str): repo_url: repository url tier: tier of project coverage_type: [dev, stable] + Return: + output: log PASS + We want to give the result of the test to the GitHub action """ runner = PythonCoverageRunner(repo_url, working_directory=self.resources_dir) _, results = runner.run() @@ -149,8 +236,10 @@ def python_coverage(self, repo_url: str, tier: str, coverage_type: str): repo_url, ) self.logger.info("Test results for %s: %s", repo_url, coverage_result) + set_actions_output([("PASS", coverage_result.passed)]) else: self.logger.warning("Runner returned 0 results.") + set_actions_output([("PASS", "False")]) def python_dev_tests( self, repo_url: str, tier: str = Tier.MAIN, python_version: str = "py39" diff --git a/ecosystem/utils/utils.py b/ecosystem/utils/utils.py index c47a9d8fa9..8832fe9df8 100644 --- a/ecosystem/utils/utils.py +++ b/ecosystem/utils/utils.py @@ -1,5 +1,6 @@ """Logging module.""" import logging +from typing import Tuple, List import coloredlogs @@ -21,6 +22,18 @@ def format(self, record): return result +def set_actions_output(outputs: List[Tuple[str, str]]) -> None: + """Sets output for GitHub actions. + Args: + outputs: List of pairs: + - first element - name of output + - second element - value of output + """ + for name, value in outputs: + logger.info("Setting output variable %s: %s", name, value) + print("::set-output name={name}::{value}".format(name=name, value=value)) + + logger = logging.getLogger("ecosystem") coloredlogs.DEFAULT_FIELD_STYLES = { "name": {"color": "magenta"}, diff --git a/manager.py b/manager.py index b6656d0088..afae7a2717 100644 --- a/manager.py +++ b/manager.py @@ -16,6 +16,16 @@ ```shell python manager.py dev_compatibility_tests https://github.com// --tox_python= ``` + +4. Get parse issue. +```shell +python manager.py parser_issue --body="${{ github.event.issue.body }}" +``` + +5. Add repo to jsondb. +```shell +python manager.py add_repo_2db --repo_link="https://github.com//" --repo_author="" ... +``` """ import fire diff --git a/tests/manager.py b/tests/manager.py new file mode 100644 index 0000000000..9cf0367068 --- /dev/null +++ b/tests/manager.py @@ -0,0 +1,50 @@ +"""Tests for manager cli.""" +import os +import io +import sys +from unittest import TestCase + +from ecosystem.manager import Manager + + +class TestManager(TestCase): + """Test class for manager cli.""" + + def setUp(self) -> None: + current_dir = os.path.dirname(os.path.abspath(__file__)) + with open("{}/resources/issue.md".format(current_dir), "r") as issue_body_file: + self.issue_body = issue_body_file.read() + + def test_parser_issue(self): + """ "Tests issue parsing function""" + captured_output = io.StringIO() + sys.stdout = captured_output + Manager.parser_issue(self.issue_body) + + sys.stdout = sys.__stdout__ + output_value = captured_output.getvalue().split("\n") + + self.assertEqual(output_value[0], "::set-output name=SUBMISSION_NAME::awesome") + self.assertEqual( + output_value[1], + "::set-output name=SUBMISSION_REPO::http://github.com/awesome/awesome", + ) + self.assertEqual( + output_value[2], + "::set-output name=SUBMISSION_DESCRIPTION::An awesome repo for awesome project", + ) + self.assertEqual( + output_value[3], "::set-output name=SUBMISSION_LICENCE::Apache License 2.0" + ) + self.assertEqual( + output_value[4], "::set-output name=SUBMISSION_CONTACT::toto@gege.com" + ) + self.assertEqual( + output_value[5], "::set-output name=SUBMISSION_ALTERNATIVES::tititata" + ) + self.assertEqual( + output_value[6], "::set-output name=SUBMISSION_AFFILIATIONS::_No response_" + ) + self.assertEqual( + output_value[7], "::set-output name=SUBMISSION_LABELS::['tool', 'tutorial']" + ) diff --git a/tox.ini b/tox.ini index bef29a5442..111bdb1314 100644 --- a/tox.ini +++ b/tox.ini @@ -31,3 +31,10 @@ commands = [testenv:black] envdir = .tox/lint commands = black {posargs} ecosystem tests --check + +[testenv:ci] +commands = + apt-get install -yq docker-ce docker-ce-cli + curl https://raw.githubusercontent.com/nektos/act/master/install.sh | sudo bash + act -j submission_workflow -e .github/workflows/test_payloads/submission-wf-newissue.json --env-file .github/workflows/test_payloads/my.env + act -j submission_workflow -e .github/workflows/test_payloads/submission-wf-commenttrigger.json --env-file .github/workflows/test_payloads/my.env