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.
140changedir = docs
141extras =
142    {[testenv:docs]extras}
143commands =
144    mkdir -p {envtmpdir}
145    cp -r source {envtmpdir}/source
146    cp -r ../.github {envtmpdir}/../.github
147    cp -r ../tox.ini {envtmpdir}/../tox.ini
148    cp -r ../.env.secret {envtmpdir}/../.env.secret
149    python -m sphinx -W -b html     -d {envtmpdir}/build/doctrees {envtmpdir}/source {envtmpdir}/build/html
150    python -m sphinx -W -b coverage -d {envtmpdir}/build/doctrees {envtmpdir}/source {envtmpdir}/build/coverage
151    cat {envtmpdir}/build/coverage/c.txt
152    cat {envtmpdir}/build/coverage/python.txt
153allowlist_externals =
154    cp
155    cat
156    mkdir
157
158[testenv:coverage-report]
159deps = coverage
160skip_install = true
161commands =
162    coverage combine
163    coverage report
164
165####################
166# Deployment tools #
167####################
168
169[testenv:bumpversion]
170commands = bumpversion {posargs}
171skip_install = true
172passenv = HOME
173deps =
174    bumpversion
175
176[testenv:build]
177skip_install = true
178deps =
179    wheel
180    build
181commands =
182    python -m build --sdist --wheel --no-isolation
183
184[testenv:release]
185description = Release the code to PyPI so users can pip install it
186skip_install = true
187deps =
188    {[testenv:build]deps}
189    twine >= 1.5.0
190commands =
191    {[testenv:build]commands}
192    twine upload --skip-existing dist/*
193
194[testenv:testrelease]
195description = Release the code to the test PyPI site
196skip_install = true
197deps =
198    {[testenv:build]deps}
199    twine >= 1.5.0
200commands =
201    {[testenv:build]commands}
202    twine upload --skip-existing --repository-url  https://test.pypi.org/legacy/ dist/*
203
204[testenv:finish]
205skip_install = true
206passenv =
207    HOME
208    TWINE_USERNAME
209    TWINE_PASSWORD
210deps =
211    {[testenv:release]deps}
212    bump2version
213commands =
214    bump2version release --tag
215    {[testenv:release]commands}
216    git push --tags
217    bump2version patch
218    git push
219allowlist_externals =
220    git