diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index ae9b4e36b..9e15d18ad 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -22,6 +22,6 @@ jobs: python -m pip install --upgrade pip setuptools six wheel python -m pip install pytest-cov -r requirements.txt - name: Run tests - run: pytest --doctest-modules --ignore=project_euler/ --cov-report=term-missing:skip-covered --cov=. . + run: pytest --doctest-modules --ignore=project_euler/ --ignore=scripts/ --cov-report=term-missing:skip-covered --cov=. . - if: ${{ success() }} run: scripts/build_directory_md.py 2>&1 | tee DIRECTORY.md diff --git a/.github/workflows/project_euler.yml b/.github/workflows/project_euler.yml index e8b011af2..995295fca 100644 --- a/.github/workflows/project_euler.yml +++ b/.github/workflows/project_euler.yml @@ -1,12 +1,14 @@ on: pull_request: - # only check if a file is changed within the project_euler directory and related files + # Run only if a file is changed within the project_euler directory and related files paths: - - 'project_euler/**' - - '.github/workflows/project_euler.yml' - - 'scripts/validate_solutions.py' + - "project_euler/**" + - ".github/workflows/project_euler.yml" + - "scripts/validate_solutions.py" + schedule: + - cron: "0 0 * * *" # Run everyday -name: 'Project Euler' +name: "Project Euler" jobs: project-euler: @@ -24,8 +26,10 @@ jobs: steps: - uses: actions/checkout@v2 - uses: actions/setup-python@v2 - - name: Install pytest + - name: Install pytest and requests run: | python -m pip install --upgrade pip - python -m pip install --upgrade pytest + python -m pip install --upgrade pytest requests - run: pytest scripts/validate_solutions.py + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/scripts/validate_solutions.py b/scripts/validate_solutions.py index e1f68ff84..fd804ea5a 100755 --- a/scripts/validate_solutions.py +++ b/scripts/validate_solutions.py @@ -1,11 +1,13 @@ #!/usr/bin/env python3 import importlib.util import json +import os import pathlib from types import ModuleType from typing import Dict, List import pytest +import requests PROJECT_EULER_DIR_PATH = pathlib.Path.cwd().joinpath("project_euler") PROJECT_EULER_ANSWERS_PATH = pathlib.Path.cwd().joinpath( @@ -24,7 +26,7 @@ def convert_path_to_module(file_path: pathlib.Path) -> ModuleType: return module -def collect_solution_file_paths() -> List[pathlib.Path]: +def all_solution_file_paths() -> List[pathlib.Path]: """Collects all the solution file path in the Project Euler directory""" solution_file_paths = [] for problem_dir_path in PROJECT_EULER_DIR_PATH.iterdir(): @@ -37,12 +39,51 @@ def collect_solution_file_paths() -> List[pathlib.Path]: return solution_file_paths +def get_files_url() -> str: + """Return the pull request number which triggered this action.""" + with open(os.environ["GITHUB_EVENT_PATH"]) as file: + event = json.load(file) + return event["pull_request"]["url"] + "/files" + + +def added_solution_file_path() -> List[pathlib.Path]: + """Collects only the solution file path which got added in the current + pull request. + + This will only be triggered if the script is ran from GitHub Actions. + """ + solution_file_paths = [] + headers = { + "Accept": "application/vnd.github.v3+json", + "Authorization": "token " + os.environ["GITHUB_TOKEN"], + } + files = requests.get(get_files_url(), headers=headers).json() + for file in files: + filepath = pathlib.Path.cwd().joinpath(file["filename"]) + if ( + filepath.suffix != ".py" + or filepath.name.startswith(("_", "test")) + or not filepath.name.startswith("sol") + ): + continue + solution_file_paths.append(filepath) + return solution_file_paths + + +def collect_solution_file_paths() -> List[pathlib.Path]: + if os.environ.get("CI") and os.environ.get("GITHUB_EVENT_NAME") == "pull_request": + # Return only if there are any, otherwise default to all solutions + if filepaths := added_solution_file_path(): + return filepaths + return all_solution_file_paths() + + @pytest.mark.parametrize( "solution_path", collect_solution_file_paths(), ids=lambda path: f"{path.parent.name}/{path.name}", ) -def test_project_euler(solution_path: pathlib.Path): +def test_project_euler(solution_path: pathlib.Path) -> None: """Testing for all Project Euler solutions""" # problem_[extract this part] and pad it with zeroes for width 3 problem_number: str = solution_path.parent.name[8:].zfill(3)