diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fad51691..bdc0a072 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -5,121 +5,26 @@ on: branches: [ main ] pull_request: branches: [ main ] - schedule: - - cron: 45 6 * * * - # Allows you to run this workflow manually from the Actions tab - workflow_dispatch: jobs: - tests: - name: ${{ matrix.copier_config.name }} - Python ${{ matrix.python-version }} + build: + runs-on: ubuntu-latest strategy: - fail-fast: true matrix: - python-version: ['3.9', '3.10', '3.11'] - copier_config: - - name: Base example - package_name: example_package # The default package_name - extra_flags: '' - foldername: base_example - - name: No example module - package_name: 'drewtonian' # Same module name provided in `extra_flags` on the next line. - extra_flags: >- - --data project_name=new_science - --data package_name=drewtonian - --data author_name=Drew - --data author_email=ao@aol.com - --data project_license=BSD - --data mypy_type_checking=basic - --data create_example_module=no - --data include_notebooks=no - foldername: 'black_w_o_example_module' - - name: Example module - package_name: 'drewtonian' # Same module name provided in `extra_flags` on the next line. - extra_flags: >- - --data project_name=new_science - --data package_name=drewtonian - --data author_name=Drew - --data author_email=ao@aol.com - --data project_license=BSD - --data mypy_type_checking=basic - --data create_example_module=yes - --data include_notebooks=no - foldername: 'black_w_example_module' - - name: No sphinx docs - package_name: 'drewtonian' - extra_flags: >- - --data project_name=new_science - --data package_name=drewtonian - --data include_docs=no - --data include_notebooks=no - foldername: 'no_sphinx_docs' + python-version: ['3.9', '3.10', '3.11', '3.12'] steps: - - - name: Checkout code - uses: actions/checkout@v4 - - - name: Set up Python - uses: actions/setup-python@v5 + - uses: actions/checkout@main + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@main with: python-version: ${{ matrix.python-version }} - - - name: Install Python dependencies + - name: Install dependencies run: | sudo apt-get update python -m pip install --upgrade pip - python -m pip install copier mypy - - - name: Generate package - run: | - copier copy --vcs-ref HEAD --defaults ${{ matrix.copier_config.extra_flags }} ./ ../test/${{ matrix.copier_config.foldername }} - cd ../test/${{ matrix.copier_config.foldername }} - cat .copier-answers.yml - - - name: Build package - run: | - cd ../test/${{ matrix.copier_config.foldername }} - git config --global user.email "tstname@example.com" - git config --global user.name "Test Name" - git init --initial-branch=main - git add src - git commit -m initial - pip install .[dev] - - - name: black checks - uses: psf/black@stable - with: - src: "../test/${{ matrix.copier_config.foldername }}/src" - - - name: mypy checks basic - if: ${{ contains(matrix.copier_config.extra_flags, 'mypy_type_checking=basic') && !contains(matrix.copier_config.extra_flags, 'create_example_module=no') }} - run: | - cd "../test/${{ matrix.copier_config.foldername }}" - mypy src tests - - - name: mypy checks strict - if: ${{ contains(matrix.copier_config.extra_flags, 'mypy_type_checking=strict') && !contains(matrix.copier_config.extra_flags, 'create_example_module=no') }} - run: | - cd "../test/${{ matrix.copier_config.foldername }}" - mypy --strict src tests - - - name: Install notebook requirements - if: ${{ !contains(matrix.copier_config.extra_flags, 'include_notebooks=no') }} - run: | - sudo apt-get install pandoc - pip install -r ../test/${{ matrix.copier_config.foldername }}/docs/requirements.txt - cat ../test/${{ matrix.copier_config.foldername }}/docs/requirements.txt - - - name: Build docs - if: ${{ !contains(matrix.copier_config.extra_flags, 'include_docs=no') }} - run: | - cd ../test/${{ matrix.copier_config.foldername }} - sphinx-build -T -E -b html -d docs/build/doctrees ./docs docs/build/html - - - name: Tests - if: ${{ !contains(matrix.copier_config.extra_flags, 'create_example_module=no') }} + pip install -e .[dev] + - name: Run unit tests with pytest / pytest-copie run: | - cd ../test/${{ matrix.copier_config.foldername }} - python -m pytest --cov=${{ matrix.copier_config.package_name }} --cov-report=xml + python -m pytest \ No newline at end of file diff --git a/.github/workflows/smoke-test.yml b/.github/workflows/smoke-test.yml new file mode 100644 index 00000000..17b96f78 --- /dev/null +++ b/.github/workflows/smoke-test.yml @@ -0,0 +1,41 @@ +# This workflow will run daily at 06:45. +# It will install Python dependencies and run tests with a variety of Python versions. +# See documentation for help debugging smoke test issues: +# https://lincc-ppt.readthedocs.io/en/latest/practices/ci_testing.html#version-culprit + +name: Unit test smoke test + +on: + + # Runs this workflow automatically + schedule: + - cron: 45 6 * * * + + # Allows you to run this workflow manually from the Actions tab + workflow_dispatch: + +jobs: + build: + + runs-on: ubuntu-latest + strategy: + matrix: + python-version: ['3.9', '3.10', '3.11', '3.12'] + + steps: + - uses: actions/checkout@main + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@main + with: + python-version: ${{ matrix.python-version }} + - name: Install dependencies + run: | + sudo apt-get update + python -m pip install --upgrade pip + pip install -e .[dev] + - name: List dependencies + run: | + pip list + - name: Run unit tests with pytest / pytest-copie + run: | + python -m pytest \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml index 71c9efde..1e5dea83 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -26,6 +26,7 @@ dev = [ "black", # Used to format code "pre-commit", # Used to run checks prior to committing code "pytest", # Used to run tests + "pylint", # test pylint in unit tests "pytest-copie", # Used to create hydrated copier projects for testing "tox", # Used to run tests in multiple environments ] diff --git a/tests/test_package_creation.py b/tests/test_package_creation.py index 80cc11c0..fc7d8a1e 100644 --- a/tests/test_package_creation.py +++ b/tests/test_package_creation.py @@ -1,6 +1,8 @@ import pytest import subprocess +import pytest + def successfully_created_project(result): """Basic assertions that indicate the copier was able to create a project""" @@ -55,6 +57,22 @@ def unit_tests_in_project_run_successfully(result, package_name="example_package return pytest_results.returncode == 0 +def docs_build_successfully(result): + """Test that we can build the doc tree. + + !!! NOTE - This doesn't currently work because we need to `pip install` the hydrated + project before running the tests. And we don't have a way to create a temporary + virtual environment for the project. + """ + + sphinx_results = subprocess.run( + ["make", "html"], + cwd=(result.project_dir / "docs"), + ) + + return sphinx_results.returncode == 0 + + def github_workflows_are_valid(result): """Test to ensure that the GitHub workflows are valid""" workflows_results = subprocess.run( @@ -160,13 +178,22 @@ def test_code_style_combinations(copie, enforce_style): assert black_runs_successfully(result) -def test_smoke_test_notification(copie): +@pytest.mark.parametrize( + "notification", + [ + [], + ["slack"], + ["email"], + ["email", "slack"], + ], +) +def test_smoke_test_notification(copie, notification): """Confirm we can generate a "smoke_test.yaml" file, with all notification mechanisms selected.""" # provide a dictionary of the non-default answers to use extra_answers = { - "failure_notification": ["email", "slack"], + "failure_notification": notification, } # run copier to hydrate a temporary project @@ -177,6 +204,56 @@ def test_smoke_test_notification(copie): assert black_runs_successfully(result) +@pytest.mark.parametrize( + "doc_answers", + [ + { + "include_docs": True, + "include_notebooks": True, + }, + { + "include_docs": True, + "include_notebooks": False, + }, + ], +) +def test_doc_combinations(copie, doc_answers): + """Confirm the docs directory is well-formed, when including docs.""" + + # run copier to hydrate a temporary project + result = copie.copy(extra_answers=doc_answers) + + assert successfully_created_project(result) + assert directory_structure_is_correct(result) + assert black_runs_successfully(result) + assert (result.project_dir / "docs").is_dir() + + +@pytest.mark.parametrize( + "doc_answers", + [ + { + "include_docs": False, + "include_notebooks": False, + }, + { + "include_docs": False, + "include_notebooks": True, + }, + ], +) +def test_doc_combinations_no_docs(copie, doc_answers): + """Confirm there is no 'docs' directory, if not including docs.""" + + # run copier to hydrate a temporary project + result = copie.copy(extra_answers=doc_answers) + + assert successfully_created_project(result) + assert directory_structure_is_correct(result) + assert black_runs_successfully(result) + assert not (result.project_dir / "docs").is_dir() + + def test_github_workflows_schema(copie): """Confirm the current GitHub workflows have valid schemas.""" extra_answers = {