Repository: qiskit-community/ecosystem
Pull request #87 (2021-11-08 20:25:29)

Workflows: main repos tests results
<!--
⚠️ If you do not respect this template, your pull request will be closed.
⚠️ Your pull request title should be short detailed and understandable for all.
⚠️ Also, please add a release note file using reno if the change needs to be
  documented in the release notes.
⚠️ If your pull request fixes an open issue, please link to the issue.

- [ ] I have added the tests to cover my changes.
- [ ] I have updated the documentation accordingly.
- [ ] I have read the CONTRIBUTING document.
-->

### Summary
Add workflow to get tests results from Main repositories.


### Details and comments
As tests already run for all PRs in core Qiskit repositores we do not need to run them again. Therefore, we only need to fetch them from github action workflows.


IceKhan13: FYI @mickahell @bala-ceg 
IceKhan13: @mickahell https://github.com/IceKhan13/ecosystem/pull/18 tested 🕺 

Patch:
diff --git a/.github/workflows/main_repos_fetch.yml b/.github/workflows/main_repos_fetch.yml
new file mode 100644
index 0000000..e10b2f9
--- /dev/null
+++ b/.github/workflows/main_repos_fetch.yml
@@ -0,0 +1,43 @@
+name: Fetch tests results from GitHub action runs
+# Fetch tests results from GitHub action runs for Main tier projects
+
+on:
+  workflow_dispatch:
+
+jobs:
+  fetch_results_for_main_repos:
+    runs-on: ubuntu-latest
+    env:
+      tox_env: "py39"
+      python-version: "3.9"
+      repository: "qiskit-community/ecosystem"
+    steps:
+    - name: Setup variables
+      id: vars
+      run: |
+        echo "::set-output name=datetime::$(date +'%Y_%m_%d_%H_%M')"
+        echo "::set-output name=pr_branch_name::fetch_tests_for_main_$(date +'%Y_%m_%d_%H_%M')"
+    - 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
+
+    - name: Fetch test results
+      run: python manager.py fetch_and_update_main_tests_results
+
+    - name: Create PR for batch checks
+      id: cpr
+      uses: peter-evans/create-pull-request@v3
+      with:
+        commit-message: Fetch tests results for main projects.
+        title: AUTO ${{ steps.vars.outputs.datetime }} | Fetch tests results for main projects
+        body: |
+          ${{ steps.vars.outputs.datetime }} | Fetch tests results for main projects
+        branch: ${{ steps.vars.outputs.pr_branch_name }}
+        base: main
diff --git a/ecosystem/manager.py b/ecosystem/manager.py
index b06c593..8d4ab35 100644
--- a/ecosystem/manager.py
+++ b/ecosystem/manager.py
@@ -10,9 +10,14 @@ 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.main_repos_report_runner import RepositoryActionStatusRunner
 from ecosystem.runners.python_styles_runner import PythonStyleRunner
 from ecosystem.runners.python_coverages_runner import PythonCoverageRunner
 from ecosystem.utils import logger, parse_submission_issue
+from ecosystem.utils.custom_requests import (
+    get_dev_terra_version,
+    get_stable_terra_version,
+)
 from ecosystem.utils.utils import set_actions_output
 
 
@@ -344,3 +349,63 @@ class Manager:
             python_version=python_version,
             test_type=TestType.STANDARD,
         )
+
+    def fetch_and_update_main_tests_results(self):
+        """Gets executed results from Github actions runs of main repositories."""
+        main_repos = self.dao.get_repos_by_tier(Tier.MAIN)
+        repo_to_url_mapping = {r.name: r.url for r in main_repos}
+
+        terra_dev_version = get_dev_terra_version()
+        terra_stable_version = get_stable_terra_version()
+
+        data = [
+            # repo, workflow_name, terra_version, test_type
+            (
+                "qiskit-nature",
+                "Nature%20Unit%20Tests",
+                terra_dev_version,
+                TestType.DEV_COMPATIBLE,
+            ),
+            (
+                "qiskit-finance",
+                "Finance%20Unit%20Tests",
+                terra_dev_version,
+                TestType.DEV_COMPATIBLE,
+            ),
+            (
+                "qiskit-optimization",
+                "Optimization%20Unit%20Tests",
+                terra_dev_version,
+                TestType.DEV_COMPATIBLE,
+            ),
+            (
+                "qiskit-machine-learning",
+                "Machine%20Learning%20Unit%20Tests",
+                terra_dev_version,
+                TestType.DEV_COMPATIBLE,
+            ),
+            ("qiskit-experiments", "Tests", terra_stable_version, TestType.STANDARD),
+            ("qiskit-aer", "Tests%20Linux", terra_stable_version, TestType.STANDARD),
+        ]
+
+        for repo, workflow_name, terra_version, test_type in data:
+            self.logger.info("Updating %s repository...", repo)
+            _, results = RepositoryActionStatusRunner(
+                repo=repo, test_name=workflow_name, terra_version=terra_version
+            ).workload()
+            test_result = TestResult(
+                passed=all(r.ok for r in results),
+                terra_version=terra_version,
+                test_type=test_type,
+            )
+            result = self.dao.add_repo_test_result(
+                repo_url=repo_to_url_mapping.get(repo),
+                tier=Tier.MAIN,
+                test_result=test_result,
+            )
+            if result is None:
+                self.logger.warning(
+                    "Test result was not saved. There is not repo for url %s",
+                    repo,
+                )
+            self.logger.info("Test results for %s: %s", repo, test_result)
diff --git a/ecosystem/resources/members.json b/ecosystem/resources/members.json
index fb00b0d..093f42a 100644
--- a/ecosystem/resources/members.json
+++ b/ecosystem/resources/members.json
@@ -34,7 +34,7 @@
         "3": {
             "alternatives": null,
             "contact_info": null,
-            "created_at": 1628883441.118469,
+            "created_at": 1636403010.377695,
             "description": "Aer provides high-performance quantum computing simulators with realistic noise models.",
             "labels": [
                 "simulation"
@@ -42,14 +42,24 @@
             "licence": "Apache 2.0",
             "name": "qiskit-aer",
             "tier": "MAIN",
-            "updated_at": 1628883441.118472,
+            "updated_at": 1636403010.377697,
             "url": "https://github.com/Qiskit/qiskit-aer",
-            "tests_results": []
+            "tests_results": [
+                {
+                    "test_type": "STANDARD",
+                    "passed": true,
+                    "terra_version": "0.18.3",
+                    "timestamp": 1636403010.377469
+                }
+            ],
+            "affiliations": null,
+            "styles_results": [],
+            "coverages_results": []
         },
         "4": {
             "alternatives": null,
             "contact_info": null,
-            "created_at": 1628883441.118818,
+            "created_at": 1636403009.538761,
             "description": "Qiskit Optimization is an open-source framework that covers the whole range from high-level modeling of optimization problems, with automatic conversion of problems to different required representations, to a suite of easy-to-use quantum optimization algorithms that are ready to run on classical simulators, as well as on real quantum devices via Qiskit.",
             "labels": [
                 "algorithms",
@@ -58,9 +68,19 @@
             "licence": "Apache 2.0",
             "name": "qiskit-optimization",
             "tier": "MAIN",
-            "updated_at": 1628883441.118821,
+            "updated_at": 1636403009.538763,
             "url": "https://github.com/Qiskit/qiskit-optimization",
-            "tests_results": []
+            "tests_results": [
+                {
+                    "test_type": "DEV_COMPATIBLE",
+                    "passed": true,
+                    "terra_version": "0.19.0",
+                    "timestamp": 1636403009.53853
+                }
+            ],
+            "affiliations": null,
+            "styles_results": [],
+            "coverages_results": []
         },
         "5": {
             "alternatives": null,
@@ -81,7 +101,7 @@
         "6": {
             "alternatives": null,
             "contact_info": null,
-            "created_at": 1628883441.119526,
+            "created_at": 1636403010.012954,
             "description": "The Machine Learning package contains sample datasets and quantum ML algorithms.",
             "labels": [
                 "algorithms",
@@ -90,14 +110,24 @@
             "licence": "Apache 2.0",
             "name": "qiskit-machine-learning",
             "tier": "MAIN",
-            "updated_at": 1628883441.119529,
+            "updated_at": 1636403010.012956,
             "url": "https://github.com/Qiskit/qiskit-machine-learning",
-            "tests_results": []
+            "tests_results": [
+                {
+                    "test_type": "DEV_COMPATIBLE",
+                    "passed": true,
+                    "terra_version": "0.19.0",
+                    "timestamp": 1636403010.012718
+                }
+            ],
+            "affiliations": null,
+            "styles_results": [],
+            "coverages_results": []
         },
         "7": {
             "alternatives": null,
             "contact_info": null,
-            "created_at": 1628883441.11986,
+            "created_at": 1636403009.16708,
             "description": "Qiskit Nature allows researchers and developers in different areas of natural sciences (including physics, chemistry, material science and biology) to model and solve domain-specific problems using quantum simulations",
             "labels": [
                 "algorithms",
@@ -107,14 +137,24 @@
             "licence": "Apache 2.0",
             "name": "qiskit-nature",
             "tier": "MAIN",
-            "updated_at": 1628883441.119862,
+            "updated_at": 1636403009.167082,
             "url": "https://github.com/Qiskit/qiskit-nature",
-            "tests_results": []
+            "tests_results": [
+                {
+                    "test_type": "DEV_COMPATIBLE",
+                    "passed": true,
+                    "terra_version": "0.19.0",
+                    "timestamp": 1636403009.166847
+                }
+            ],
+            "affiliations": null,
+            "styles_results": [],
+            "coverages_results": []
         },
         "8": {
             "alternatives": null,
             "contact_info": null,
-            "created_at": 1628883441.120265,
+            "created_at": 1636403009.368607,
             "description": "Qiskit Finance is an open-source framework that contains uncertainty components for stock/securities problems, Ising translators for portfolio optimizations and data providers to source real or random data to finance experiments.",
             "labels": [
                 "algorithms",
@@ -123,9 +163,19 @@
             "licence": "Apache 2.0",
             "name": "qiskit-finance",
             "tier": "MAIN",
-            "updated_at": 1628883441.120268,
+            "updated_at": 1636403009.368609,
             "url": "https://github.com/Qiskit/qiskit-finance",
-            "tests_results": []
+            "tests_results": [
+                {
+                    "test_type": "DEV_COMPATIBLE",
+                    "passed": true,
+                    "terra_version": "0.19.0",
+                    "timestamp": 1636403009.368369
+                }
+            ],
+            "affiliations": null,
+            "styles_results": [],
+            "coverages_results": []
         },
         "9": {
             "alternatives": null,
diff --git a/ecosystem/runners/main_repos_report_runner.py b/ecosystem/runners/main_repos_report_runner.py
new file mode 100644
index 0000000..94ae0b0
--- /dev/null
+++ b/ecosystem/runners/main_repos_report_runner.py
@@ -0,0 +1,24 @@
+"""Runner for reports on main repositories."""
+from typing import Tuple, List, Union
+
+from .runner import Runner
+from ..models import CommandExecutionSummary
+from ..models.repository import Repository
+from ..utils.custom_requests import get_workflow_status
+
+
+class RepositoryActionStatusRunner(Runner):
+    """Runner to get information from CI tests from repository."""
+
+    def __init__(
+        self, repo: Union[str, Repository], test_name: str, terra_version: str
+    ):
+        super().__init__(repo)
+        self.test_name = test_name
+        self.terra_version = terra_version
+
+    def workload(self) -> Tuple[str, List[CommandExecutionSummary]]:
+        status = get_workflow_status(repo=self.repo, name_of_workflow=self.test_name)
+        return self.terra_version, [
+            CommandExecutionSummary(code=int(not status), logs=[])
+        ]
diff --git a/ecosystem/utils/custom_requests.py b/ecosystem/utils/custom_requests.py
new file mode 100644
index 0000000..ce07740
--- /dev/null
+++ b/ecosystem/utils/custom_requests.py
@@ -0,0 +1,70 @@
+"""Custom requests for GitHub to get statuses of workflows for main repos."""
+import json
+from typing import Optional
+
+import requests
+
+from ecosystem.utils import logger
+
+
+def get_workflow_status(
+    repo: str,
+    name_of_workflow: str,
+    owner: str = "Qiskit",
+    branch: str = "main",
+    event: str = "push",
+) -> Optional[bool]:
+    """Get status for GitHub workflow."""
+    url = "https://api.github.com/repos/{owner}/{repo}/actions/runs".format(
+        owner=owner, repo=repo
+    )
+    params = {"event": event, "branch": branch, "name": name_of_workflow, "per_page": 1}
+    response = requests.get(url, params=params)
+    result = None
+    if response.ok:
+        response_data = json.loads(response.text)
+        runs = response_data.get("workflow_runs", [])
+        if len(runs) > 0:
+            last_run = runs[0]
+            conclusion = last_run.get("conclusion", None)
+            logger.info("Workflow run status conclusion: %s", str(conclusion))
+            result = conclusion == "success"
+        else:
+            logger.warning("No workflow runs for params: %s", str(params))
+            result = False
+    else:
+        logger.error("Response from GitHub is not ok: %s", response.text)
+    return result
+
+
+def get_stable_terra_version() -> str:
+    """Returns stable Qiskit-terra version."""
+    url = "https://api.github.com/repos/Qiskit/qiskit-terra/releases"
+    response = requests.get(url, params={"per_page": 1})
+    version = "-"
+    if response.ok:
+        releases = json.loads(response.text)
+        if len(releases) > 0:
+            latest_release = releases[0]
+            version = latest_release["tag_name"].strip()
+        else:
+            logger.warning("No releases found during terra version fetch.")
+    else:
+        logger.warning(
+            "GitHub api returned non success code during terra release fetch."
+        )
+    return version
+
+
+def get_dev_terra_version() -> str:
+    """Returns dev Qiskit-terra version."""
+    url = (
+        "https://raw.githubusercontent.com/Qiskit/qiskit-terra/main/qiskit/VERSION.txt"
+    )
+    response = requests.get(url)
+    version = "-"
+    if response.ok:
+        version = response.text.strip()
+    else:
+        logger.warning("Cannot fetch terra version.")
+    return version


Test patch:
diff --git a/tests/test_manager.py b/tests/test_manager.py
index 688c57e..19d2599 100644
--- a/tests/test_manager.py
+++ b/tests/test_manager.py
@@ -78,3 +78,51 @@ class TestManager(TestCase):
             repo=repo,
         )
         self.assertTrue(response)
+
+    @responses.activate
+    def test_fetch_and_update_main_projects(self):
+        """Tests manager function for fetching tests results."""
+        owner = "Qiskit"
+        repos_and_test_names = [
+            ("qiskit-nature", "Nature%2520Unit%2520Tests"),
+            ("qiskit-finance", "Finance%2520Unit%2520Tests"),
+            ("qiskit-optimization", "Optimization%2520Unit%2520Tests"),
+            ("qiskit-machine-learning", "Machine%2520Learning%2520Unit%2520Tests"),
+            ("qiskit-experiments", "Tests"),
+            ("qiskit-aer", "Tests%2520Linux"),
+        ]
+
+        for repo, test_name in repos_and_test_names:
+            responses.add(
+                **{
+                    "method": responses.GET,
+                    "url": "https://api.github.com/repos/{owner}/{repo}/actions/runs"
+                    "?event=push&branch=main&name={test_name}&per_page=1".format(
+                        owner=owner, repo=repo, test_name=test_name
+                    ),
+                    "body": '{"status": "ok"}',
+                    "status": 200,
+                    "content_type": "application/json",
+                }
+            )
+        responses.add(
+            **{
+                "method": responses.GET,
+                "url": "https://raw.githubusercontent.com/Qiskit/"
+                "qiskit-terra/main/qiskit/VERSION.txt",
+                "body": "0.19.0",
+                "status": 200,
+            }
+        )
+        responses.add(
+            **{
+                "method": responses.GET,
+                "url": "https://api.github.com/repos/Qiskit/qiskit-terra/releases?per_page=1",
+                "body": '[{"tag_name": "0.18.3"}]',
+                "status": 200,
+                "content_type": "application/json",
+            }
+        )
+
+        manager = Manager(root_path=f"{os.path.abspath(os.getcwd())}/../")
+        self.assertIsNone(manager.fetch_and_update_main_tests_results())