Workflow

The bohicalog module has codified its workflow using tox.

Quick Start

To get started, you need to install tox:

$ pip install tox

Then, you can run the tests using tox:

$ tox

For example, by default it will:

$ tox
lint: commands[0]> black .
All done!  🍰 28 files left unchanged.
lint: commands[1]> isort .
Skipped 4 files
lint: commands[2]> nbqa isort .
No notebooks found in given path(s)
lint: OK  in 0.76 seconds
manifest: commands[0]> check-manifest
...
lint: OK (0.76=setup[0.02]+cmd[0.18,0.10,0.45] seconds)
manifest: FAIL code 1 (8.50=setup[0.01]+cmd[8.49] seconds)
pyroma: OK (0.50=setup[0.01]+cmd[0.48] seconds)
flake8: FAIL code 1 (1.48=setup[0.01]+cmd[1.47] seconds)
mypy: OK (0.13=setup[0.01]+cmd[0.12] seconds)
doc8: FAIL code 1 (0.22=setup[0.01]+cmd[0.21] seconds)
docstr-coverage: OK (0.16=setup[0.01]+cmd[0.15] seconds)
docs-test: FAIL code 2 (4.97=setup[2.85]+cmd[0.01,0.01,2.10] seconds)
py: OK (2.93=setup[2.43]+cmd[0.31,0.06,0.13] seconds)
evaluation failed :( (19.78 seconds)

The above is defined by the tox section of the tox.ini file.

Tox Commands

The tox.ini file also defines a number of environments, which can be ran individually:

  • doctests: runs document tests, xdoctest

  • coverge-clean: cleans up the coverage files, coverage erase

  • lint: runs linting, black, isort, nbqa isort

  • doclint: runs linting on the documentation. This will fail on custom directives, rstfmt

  • manifest: checks the manifest, check-manifest

  • flake8: runs flake8 linting

  • pyroma: runs pyroma to check the package friendliness of the project, pyroma

  • mypy: runs mypy type checking, mypy

  • doc8: runs doc8 to check the documentation, doc8

  • docstr-coverage: runs docstr-coverage to check the documentation coverage, docstr-coverage

  • docs: builds the documentation, sphinx

  • docs-test: Test building the documentation in an isolated environment, sphinx

  • coverage-report: generates a coverage report, coverage report

  • bumpversion: bumps the version, bumpversion

  • build: builds the package, python setup.py sdist bdist_wheel

  • release: releases the package, twine

  • testrelease: releases the package to test.pypi.org, twine

  • finish: finishes the release, bumpversion

To run a specific environment, you can use the -e flag:

$ tox -e lint

Release Workflow

The release workflow is defined below:

  1. tox -e lint

  2. tox -e manifest

  3. tox -e pyroma

  4. tox -e flake8

  5. tox -e mypy

  6. tox -e doc8

  7. tox -e docstr-coverage

  8. tox -e docs-test

  9. tox -e py

  10. tox

  11. tox -e bumpversion – {major, minor} <– Only if required, finish automatically bumps the minor version after each release

  12. tox -e finish

Ensure you have the following environment variables set:

  • TWINE_USERNAME

  • TWINE_PASSWORD

  • TELEGRAM_BOT_TOKEN

  • TELEGRAM_BOT_CHAT_ID

Execute steps 1-10 to ensure that the code is ready for release. If any of the steps fail, fix the issues and repeat the steps.

If the release is a major or minor release, execute step 11 to bump the version. If the release is a patch release, skip step 11.

Execute step 12 to finish the release. This will automatically bump the version to the next minor version. This is to ensure that the version is always ahead of the latest release.

Tox File

The full tox.ini is listed below:

  1# Tox (http://tox.testrun.org/) is a tool for running tests
  2# in multiple virtualenvs. This configuration file will run the
  3# test suite on all supported python versions. To use it, "pip install tox"
  4# and then run "tox" from this directory.
  5
  6[tox]
  7# To use a PEP 517 build-backend you are required to configure tox to use an isolated_build:
  8# https://tox.readthedocs.io/en/latest/example/package.html
  9isolated_build = True
 10
 11# These environments are run in order if you just use `tox`:
 12envlist =
 13    # always keep coverage-clean first
 14    # coverage-clean
 15    # code linters/stylers
 16    lint
 17    manifest
 18    pyroma
 19    flake8
 20    mypy
 21    # documentation linters/checkers
 22    doc8
 23    docstr-coverage
 24    docs-test
 25    # the actual tests
 26    py
 27    # always keep coverage-report last
 28    # coverage-report
 29
 30[testenv]
 31# Runs on the "tests" directory by default, or passes the positional
 32# arguments from `tox -e py <posargs_1> ... <posargs_n>
 33commands =
 34    coverage run -p -m pytest --durations=20 {posargs:tests}
 35    coverage combine
 36    coverage xml
 37extras =
 38    # See the [options.extras_require] entry in setup.cfg for "tests"
 39    tests
 40    
 41[testenv:doctests]
 42commands =
 43    xdoctest -m src
 44deps =
 45    xdoctest
 46    pygments
 47
 48[testenv:coverage-clean]
 49deps = coverage
 50skip_install = true
 51commands = coverage erase
 52
 53[testenv:lint]
 54deps =
 55    black[jupyter]
 56    isort
 57    nbqa
 58skip_install = true
 59commands =
 60    black .
 61    isort .
 62    nbqa isort .
 63description = Run linters.
 64
 65[testenv:doclint]
 66deps =
 67    rstfmt
 68skip_install = true
 69commands =
 70    rstfmt docs/source/
 71description = Run documentation linters.
 72
 73[testenv:manifest]
 74deps = check-manifest
 75skip_install = true
 76commands = check-manifest
 77description = Check that the MANIFEST.in is written properly and give feedback on how to fix it.
 78
 79[testenv:flake8]
 80skip_install = true
 81deps =
 82    darglint
 83    flake8
 84    flake8-black
 85    flake8-bandit
 86    flake8-bugbear
 87    flake8-colors
 88    flake8-docstrings
 89    flake8-isort
 90    flake8-print
 91    pep8-naming
 92    pydocstyle
 93commands =
 94    flake8 src/ tests/
 95description = Run the flake8 tool with several plugins (bandit, docstrings, import order, pep8 naming). See https://cthoyt.com/2020/04/25/how-to-code-with-me-flake8.html for more information.
 96
 97[testenv:pyroma]
 98deps =
 99    pygments
100    pyroma
101skip_install = true
102commands = pyroma --min=10 .
103description = Run the pyroma tool to check the package friendliness of the project.
104
105[testenv:mypy]
106deps = mypy
107skip_install = true
108commands = mypy --install-types --non-interactive --ignore-missing-imports src/
109description = Run the mypy tool to check static typing on the project.
110
111[testenv:doc8]
112skip_install = true
113deps =
114    sphinx
115    doc8
116commands =
117    doc8 docs/source/
118description = Run the doc8 tool to check the style of the RST files in the project docs.
119
120[testenv:docstr-coverage]
121skip_install = true
122deps =
123    docstr-coverage
124commands =
125    docstr-coverage src/ tests/ --skip-private --skip-magic
126description = Run the docstr-coverage tool to check documentation coverage
127
128[testenv:docs]
129description = Build the documentation locally.
130extras =
131    # See the [options.extras_require] entry in setup.cfg for "docs"
132    docs
133    # You might need to add additional extras if your documentation covers it
134commands =
135    pip install -e .
136    python -m sphinx -W -b html -d docs/build/doctrees docs/source docs/build/html -a -E
137
138[testenv:docs-test]
139description = Test building the documentation in an isolated environment.
140passenv =
141    TELEGRAM_BOT_TOKEN
142    TELEGRAM_BOT_CHAT_ID
143changedir = docs
144extras =
145    {[testenv:docs]extras}
146commands =
147    mkdir -p {envtmpdir}
148    cp -r source {envtmpdir}/source
149    cp -r ../.github {envtmpdir}/../.github
150    cp -r ../tox.ini {envtmpdir}/../tox.ini
151    python -m sphinx -W -b html     -d {envtmpdir}/build/doctrees {envtmpdir}/source {envtmpdir}/build/html
152    python -m sphinx -W -b coverage -d {envtmpdir}/build/doctrees {envtmpdir}/source {envtmpdir}/build/coverage
153    cat {envtmpdir}/build/coverage/c.txt
154    cat {envtmpdir}/build/coverage/python.txt
155allowlist_externals =
156    cp
157    cat
158    mkdir
159
160[testenv:coverage-report]
161deps = coverage
162skip_install = true
163commands =
164    coverage combine
165    coverage report
166
167####################
168# Deployment tools #
169####################
170
171[testenv:bumpversion]
172commands = bumpversion {posargs}
173skip_install = true
174passenv = HOME
175deps =
176    bumpversion
177
178[testenv:build]
179skip_install = true
180deps =
181    wheel
182    build
183commands =
184    python -m build --sdist --wheel --no-isolation
185
186[testenv:release]
187description = Release the code to PyPI so users can pip install it
188passenv =
189    TWINE_USERNAME
190    TWINE_PASSWORD
191skip_install = true
192deps =
193    {[testenv:build]deps}
194    twine >= 1.5.0
195commands =
196    {[testenv:build]commands}
197    twine upload --skip-existing dist/*
198
199[testenv:testrelease]
200description = Release the code to the test PyPI site
201skip_install = true
202deps =
203    {[testenv:build]deps}
204    twine >= 1.5.0
205commands =
206    {[testenv:build]commands}
207    twine upload --skip-existing --repository-url  https://test.pypi.org/legacy/ dist/*
208
209[testenv:finish]
210skip_install = true
211passenv =
212    HOME
213    TWINE_USERNAME
214    TWINE_PASSWORD
215deps =
216    {[testenv:release]deps}
217    bump2version
218commands =
219    bump2version release --tag
220    {[testenv:release]commands}
221    git push --tags
222    bump2version patch
223    git push
224allowlist_externals =
225    git