/sample-ci-python

Sample project for setting-up Gitlab CI/CD with pytest and pytest-cov for Python-based applications

Primary LanguagePython

Gitlab CI/CD Example for Python-based Apps

pipeline status coverage report

CONTENTS

DEPENDENCIES

  • Python-specific
    • distutils
      • For installing the sample src/app module into the Python environment
      • This facilitates importing the app-under-test modules in tests codes
      • The alternative to not using this is to append src/app to sys.path
    • pytest
      • For creating and running the tests
    • pytest-cov
      • For gathering and reporting code coverage
    • For other example-specific dependencies, see requirements.txt.
  • Gitlab-specific
    • Access to a Gitlab instance
    • Access to a build/test/server PC for gitlab-runner

SETUP

  • Configure the app installation in setup.py (reference)
  • Configure the tests configuration in pytest.ini (reference)
  • Configure the coverage collection in .coveragerc (reference)
  • Setup a local testing environment
    • Using a virtual environment
      • Create/Activate a virtual environment
        $ python3.8 -m venv "~/.venvs/samples"
        $ source ~/.venvs/samples/bin/activate
        (samples) $ python -V
        Python 3.8.5
        
        
      • Install dependencies
        (samples) $ pip install -r requirements.txt
        
        
      • Install the app as a module in editable mode
        (samples) $ pip install -e .
        (samples) $ python
        ...
        >>> from app.models.stack import Stack
        >>> ss = Stack()
        
        
    • Using a Docker container
      • Build a Docker image using the Dockerfile
        $ docker build --tag sample-ci-python:3.8 .
        
        
      • Start the container
        $ docker run -it sample-ci-python:3.8 /bin/bash
        root@7648c3c82d32:/workspace#
        root@27f8a8b7be41:/workspace# ls -al
        total 36
        drwxr-xr-x 1 root root 4096 Aug  1 07:16 .
        drwxr-xr-x 1 root root 4096 Aug  1 07:16 ..
        -rw-r--r-- 1 root root   66 Aug  1 06:11 .coveragerc
        -rw-r--r-- 1 root root  266 Aug 12  2019 .gitlab-ci.yml
        -rw-r--r-- 1 root root  223 Aug  1 06:27 pytest.ini
        -rw-r--r-- 1 root root  393 Aug  1 06:09 requirements.txt
        -rw-r--r-- 1 root root  288 Aug  1 05:57 setup.py
        drwxr-xr-x 1 root root 4096 Aug  1 06:14 src
        drwxr-xr-x 3 root root 4096 Aug  1 05:56 tests
        root@27f8a8b7be41:/workspace# python
        Python 3.8.2 (default, Feb 26 2020, 14:58:38)
        [GCC 8.3.0] on linux
        Type "help", "copyright", "credits" or "license" for more information.
        >>> from app.models.stack import Stack
        >>> ss = Stack()
        
        
  • Setup Gitlab CI

USAGE

  • Run the tests
    • From the virtual environment or from the Docker container
      $ python -m pytest
      ==================== test session starts ====================
      platform linux -- Python 3.8.2, pytest-6.0.1, py-1.9.0, pluggy-0.13.1 -- /usr/local/bin/python
      cachedir: .pytest_cache
      rootdir: /workspace, configfile: pytest.ini, testpaths: tests
      plugins: cov-2.10.0
      collected 3 items
      
      tests/model_tests/stack_test.py::constructor_test PASSED    [1/3]
      tests/model_tests/stack_test.py::push_test PASSED           [2/3]
      tests/model_tests/stack_test.py::pop_test PASSED            [3/3]
      
      ----------- coverage: platform linux, python 3.8.2-final-0 -----------
      Name                         Stmts   Miss Branch BrPart  Cover
      --------------------------------------------------------------
      src/app/__init__.py              0      0      0      0   100%
      src/app/models/__init__.py       0      0      0      0   100%
      src/app/models/stack.py         12      0      0      0   100%
      --------------------------------------------------------------
      TOTAL                           12      0      0      0   100%
      Coverage HTML written to dir coverage
      
      
      ==================== 3 passed in 0.08s ==========================
      
      
    • From Gitlab
      • Make changes to the codes in src/app and/or in tests
      • Make changes to the .gitlab-ci.yml configuration (if necessary)
      • Commit the changes then push to Gitlab
      • Go to your Gitlab project > CI/CD > Pipelines
      • Select the currently running job to view progress/result
  • Get the code coverage report
    • From the generated coverage directory in the same level as src and tests
      (samples) sample-ci-python$ tree -L 1 .
      .
      ├── Dockerfile
      ├── README.md
      ├── coverage      <-----------------------
      ├── docs
      ├── pytest.ini
      ├── requirements.txt
      ├── setup.py
      ├── src
      └── tests
      
      
    • From the downloadable artifacts of the CI job

ISSUES

  • pytest uses cached codes instead of latest
  • "This job is stuck, because you don’t have any active runners that can run this job."
    • Make sure that the .gitlab-ci.yml has the correct tags
    • Make sure the gitlab-runner service is running
    • Make sure the machine running gitlab-runner is accessible by the Gitlab instance
  • "yaml invalid"
    • Go to the Gitlab project > CI/CD
    • On the top-right portion, click the CI Lint button
    • Paste the contents of gitlab-ci.yml file and validate
  • The jobs are not running on the same runner/environment
  • The gitlab-runner is leaving a lot of -cache- containers/volumes
    • See a discussion of this behavior here
    • Possible solutions:
      • Manually regularly run docker system prune
      • Setup a cron job docker system prune -f
        # Cleanup docker containers/volumes every 3am every monday
        0 3 * * 1 /usr/bin/docker system prune -f
        
        

REFERENCES