From 18f1f613c9a9928d2db5d7858ec22c597b25f18e Mon Sep 17 00:00:00 2001 From: Leandro Lucarella Date: Tue, 4 Oct 2022 13:22:04 +0200 Subject: [PATCH 01/13] ci: Run on all branches Since we are now encouraging using forks, there is no need to limit the branches we run on, which needs extra maintenance. Signed-off-by: Leandro Lucarella --- .github/workflows/ci.yaml | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 3a7c97b28..adcf76f66 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -1,10 +1,6 @@ name: frequenz-sdk-python -on: - push: - branches: [ v0.x.x ] - - pull_request: +on: [pull_request, push, workflow_dispatch] env: REGISTRY: ghcr.io From 164b2c09fbfd9d5ac1defa0270de5873a4dbf02b Mon Sep 17 00:00:00 2001 From: Leandro Lucarella Date: Tue, 4 Oct 2022 13:24:14 +0200 Subject: [PATCH 02/13] ci: Upgrade GitHub action versions Signed-off-by: Leandro Lucarella --- .github/workflows/ci.yaml | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index adcf76f66..9a432f803 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -2,9 +2,6 @@ name: frequenz-sdk-python on: [pull_request, push, workflow_dispatch] -env: - REGISTRY: ghcr.io - jobs: test: strategy: @@ -17,17 +14,17 @@ jobs: steps: - name: Fetch sources - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: token: ${{ secrets.CI_ACCESS_TOKEN || github.token }} submodules: true - name: Set up Python - uses: actions/setup-python@v2 + uses: actions/setup-python@v4 with: python-version: ${{ matrix.python }} - - uses: actions/cache@v2 + - uses: actions/cache@v3 with: path: ~/.cache/pip key: ${{ runner.os }}-pip-${{ hashFiles('**/minimum-requirements-ci.txt') }} @@ -49,17 +46,17 @@ jobs: steps: - name: Fetch sources - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: token: ${{ secrets.CI_ACCESS_TOKEN || github.token }} submodules: true - name: Set up Python - uses: actions/setup-python@v2 + uses: actions/setup-python@v4 with: python-version: 3.8 - - uses: actions/cache@v2 + - uses: actions/cache@v3 with: path: ~/.cache/pip key: ${{ runner.os }}-pip-${{ hashFiles('**/minimum-requirements-ci.txt') }} @@ -75,7 +72,7 @@ jobs: run: python -m pip wheel --no-deps -w dist . - name: upload wheels - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v3 with: name: frequenz-sdk-python-wheels path: dist/*.whl From 9e1b4c9733d804606c6df154ab9a6dbbd02464c5 Mon Sep 17 00:00:00 2001 From: Leandro Lucarella Date: Tue, 4 Oct 2022 13:24:55 +0200 Subject: [PATCH 03/13] ci: Remove unnecessary credentials from checkout Also don't fetch submodules, as they are not used. Signed-off-by: Leandro Lucarella --- .github/workflows/ci.yaml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 9a432f803..0f24a6a19 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -15,9 +15,6 @@ jobs: steps: - name: Fetch sources uses: actions/checkout@v3 - with: - token: ${{ secrets.CI_ACCESS_TOKEN || github.token }} - submodules: true - name: Set up Python uses: actions/setup-python@v4 @@ -47,9 +44,6 @@ jobs: steps: - name: Fetch sources uses: actions/checkout@v3 - with: - token: ${{ secrets.CI_ACCESS_TOKEN || github.token }} - submodules: true - name: Set up Python uses: actions/setup-python@v4 From 3f8b8a6113e5c4e7af80c8e22074eaf0e834f3c2 Mon Sep 17 00:00:00 2001 From: Leandro Lucarella Date: Tue, 4 Oct 2022 13:27:54 +0200 Subject: [PATCH 04/13] ci: Improve formatting This makes it easier to add or remove items from the matrix. Signed-off-by: Leandro Lucarella --- .github/workflows/ci.yaml | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 0f24a6a19..fb92d3b11 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -7,9 +7,12 @@ jobs: strategy: fail-fast: false matrix: - python: [ "3.8", "3.9", "3.10" ] - os: [ ubuntu-20.04 ] - + os: + - ubuntu-20.04 + python: + - "3.8" + - "3.9" + - "3.10" runs-on: ${{ matrix.os }} steps: From 9be0b29de89861a4af876fbea7ca8f602fc387ea Mon Sep 17 00:00:00 2001 From: Leandro Lucarella Date: Tue, 4 Oct 2022 13:49:13 +0200 Subject: [PATCH 05/13] Move project metadata/config to pyproject.toml We also remove both the setup.cfg and setup.py files as they are not required by newer versions of pip and setuptools (but for this we also need to bump the setuptools version). If you are getting this error locally after the upgrade: ERROR: Project file:///home/luca/devel/frequenz-sdk-python has a 'pyproject.toml' and its build backend is missing the 'build_editable' hook. Since it does not have a 'setup.py' nor a 'setup.cfg', it cannot be installed in editable mode. Consider using a build backend that supports PEP 660. You might need to upgrade the dependencies in your local virtual environment (re-run `python -m pip install -e .`). We now need to depend on `pyproject.toml` too to cache dependencies in the CI. Signed-off-by: Leandro Lucarella --- .github/workflows/ci.yaml | 4 ++-- pyproject.toml | 37 +++++++++++++++++++++++++++++++++---- setup.cfg | 29 ----------------------------- setup.py | 8 -------- 4 files changed, 35 insertions(+), 43 deletions(-) delete mode 100644 setup.cfg delete mode 100644 setup.py diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index fb92d3b11..9c5a2302b 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -27,7 +27,7 @@ jobs: - uses: actions/cache@v3 with: path: ~/.cache/pip - key: ${{ runner.os }}-pip-${{ hashFiles('**/minimum-requirements-ci.txt') }} + key: ${{ runner.os }}-pip-${{ hashFiles('minimum-requirements-ci.txt', 'pyproject.toml') }} restore-keys: | ${{ runner.os }}-pip- @@ -56,7 +56,7 @@ jobs: - uses: actions/cache@v3 with: path: ~/.cache/pip - key: ${{ runner.os }}-pip-${{ hashFiles('**/minimum-requirements-ci.txt') }} + key: ${{ runner.os }}-pip-${{ hashFiles('minimum-requirements-ci.txt', 'pyproject.toml') }} restore-keys: | ${{ runner.os }}-pip- diff --git a/pyproject.toml b/pyproject.toml index a6cd4bf6f..8fb254d61 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,11 +1,43 @@ [build-system] requires = [ - "setuptools >= 60.10.0, < 61", + "setuptools >= 65.3.0, < 66", "setuptools_scm[toml] >= 7.0.5, < 8", "wheel" + ] build-backend = "setuptools.build_meta" +[project] +name = "frequenz-sdk" +requires-python = ">= 3.8, < 4" +dependencies = [ + "frequenz-api-microgrid >= 0.11.0, < 0.12.0", + "frequenz-channels >= 0.10.0, < 0.11.0", + "google-api-python-client >= 2.15, < 3", + "grpcio >= 1.47, < 2", + "grpcio-tools >= 1.47, < 2", + "networkx >= 2.8, < 3", + "pandas >= 1.3.5, < 2", + "protobuf >= 3.20, < 4", + "pyarrow >= 6.0.0, < 6.1", + "pydantic >= 1.9", + "pytz >= 2021.3", + "sympy >= 1.10.1, < 2", + "toml >= 0.10", + "tqdm >= 4.38.0, < 5", + "watchfiles >= 0.15.0", +] +dynamic = [ "version" ] + +[[project.authors]] +name ="Frequenz Energy-as-a-Service GmbH" + +[tool.setuptools] +include-package-data = true + +[tool.setuptools_scm] +version_scheme = "post-release" + [tool.black] line-length = 88 target-version = ['py38'] @@ -29,9 +61,6 @@ include = '\.pyi?$' [tool.pylint.'DESIGN'] max-attributes=12 -[tool.setuptools_scm] -version_scheme = "post-release" - [tool.isort] profile = "black" line_length = 88 diff --git a/setup.cfg b/setup.cfg deleted file mode 100644 index 777565463..000000000 --- a/setup.cfg +++ /dev/null @@ -1,29 +0,0 @@ -[metadata] -name = frequenz-sdk -author = Frequenz Energy-as-a-Service GmbH - -[options] -install_requires = - frequenz-api-microgrid >= 0.11.0, < 0.12.0 - frequenz-channels >= 0.10.0, < 0.11.0 - google-api-python-client >= 2.15, < 3 - grpcio >= 1.47, < 2 - grpcio-tools >= 1.47, < 2 - networkx >= 2.8, < 3 - pandas >= 1.3.5, < 2 - protobuf >= 3.20, < 4 - pyarrow >= 6.0.0, < 6.1 - pydantic >= 1.9 - pytz >= 2021.3 - sympy >= 1.10.1, < 2 - toml >= 0.10 - tqdm >= 4.38.0, < 5 - watchfiles >= 0.15.0 -package_dir = - = src -include_package_data = True -packages = find_namespace: -python_requires = >= 3.8, < 4 - -[options.packages.find] -where = src diff --git a/setup.py b/setup.py deleted file mode 100644 index 90f6cdacf..000000000 --- a/setup.py +++ /dev/null @@ -1,8 +0,0 @@ -""" -Simple setuptools shim to allow editable installs from setup.cfg -""" - -import setuptools - -if __name__ == "__main__": - setuptools.setup() From f8cb544a552802c3ab2f7a3ba13d11db19c12ccc Mon Sep 17 00:00:00 2001 From: Leandro Lucarella Date: Tue, 4 Oct 2022 13:53:41 +0200 Subject: [PATCH 06/13] ci: Take into account the python version for caching Different Python versions could potentially have different dependencies (for example if a newer library version only works with a newer python) so it is safer to have one cache per python version. Signed-off-by: Leandro Lucarella --- .github/workflows/ci.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 9c5a2302b..07ffb0305 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -27,9 +27,9 @@ jobs: - uses: actions/cache@v3 with: path: ~/.cache/pip - key: ${{ runner.os }}-pip-${{ hashFiles('minimum-requirements-ci.txt', 'pyproject.toml') }} + key: ${{ runner.os }}-${{ matrix.python-version }}-pip-${{ hashFiles('minimum-requirements-ci.txt', 'pyproject.toml') }} restore-keys: | - ${{ runner.os }}-pip- + ${{ runner.os }}-${{ matrix.python-version }}-pip- - name: Install required Python packages run: | @@ -56,9 +56,9 @@ jobs: - uses: actions/cache@v3 with: path: ~/.cache/pip - key: ${{ runner.os }}-pip-${{ hashFiles('minimum-requirements-ci.txt', 'pyproject.toml') }} + key: ${{ runner.os }}-${{ matrix.python-version }}-pip-${{ hashFiles('minimum-requirements-ci.txt', 'pyproject.toml') }} restore-keys: | - ${{ runner.os }}-pip- + ${{ runner.os }}-${{ matrix.python-version }}-pip- - name: Install required Python packages run: | From 99621c0ea5675a414422f1340dbe7533965b342e Mon Sep 17 00:00:00 2001 From: Leandro Lucarella Date: Tue, 4 Oct 2022 13:59:56 +0200 Subject: [PATCH 07/13] Add extra metadata to pyproject.toml Add the following configuration keys to have more complete and descriptive metadata: * readme * keywords * classifiers * project.authors.email * project.urls Signed-off-by: Leandro Lucarella --- pyproject.toml | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index 8fb254d61..1a9f0303d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -9,6 +9,21 @@ build-backend = "setuptools.build_meta" [project] name = "frequenz-sdk" +description = "Frequenz Python SDK" +readme = "README.md" +license = { text = "MIT" } +keywords = [ "frequenz", "sdk", "microgrid", "actor" ] +classifiers = [ + "Development Status :: 3 - Alpha", + "Intended Audience :: Developers", + "License :: OSI Approved :: MIT License", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3 :: Only", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Topic :: Software Development :: Libraries", +] requires-python = ">= 3.8, < 4" dependencies = [ "frequenz-api-microgrid >= 0.11.0, < 0.12.0", @@ -31,6 +46,13 @@ dynamic = [ "version" ] [[project.authors]] name ="Frequenz Energy-as-a-Service GmbH" +email = "floss@frequenz.com" + +[project.urls] +Changelog = "https://github.com/frequenz-floss/frequenz-sdk-python/releases" +Repository = "https://github.com/frequenz-floss/frequenz-sdk-python" +Issues = "https://github.com/frequenz-floss/frequenz-sdk-python/issues" +Support = "https://github.com/frequenz-floss/frequenz-sdk-python/discussions/categories/support" [tool.setuptools] include-package-data = true From 6f0f6a2ba204c0b6f749518a79976cb31810a663 Mon Sep 17 00:00:00 2001 From: Leandro Lucarella Date: Tue, 4 Oct 2022 14:02:43 +0200 Subject: [PATCH 08/13] Exclude supporting files from the distribution Exclude files used for version control, testing, benchmarking, etc. from the source distribution, which only needs the files to build a wheel. Signed-off-by: Leandro Lucarella --- MANIFEST.in | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 MANIFEST.in diff --git a/MANIFEST.in b/MANIFEST.in new file mode 100644 index 000000000..a69e51f11 --- /dev/null +++ b/MANIFEST.in @@ -0,0 +1,9 @@ +exclude .darglint +exclude .gitignore +exclude CODEOWNERS +exclude minimum-requirements-ci.txt +exclude noxfile.py +recursive-exclude .github * +recursive-exclude benchmarks * +recursive-exclude examples * +recursive-exclude tests * From a1703dc565740db96888cbe87797a922aa91857c Mon Sep 17 00:00:00 2001 From: Leandro Lucarella Date: Tue, 4 Oct 2022 14:06:42 +0200 Subject: [PATCH 09/13] ci: Publish distribution files to PyPI Add steps to publish distribution files to PyPI when a tag is created. The Python version used to create the wheel is updated just for consistency with other projects, it shouldn't really affect the resulting wheel because this is a pure python package, so it doesn't depend on the Python version, is just shipping text files. Signed-off-by: Leandro Lucarella --- .github/workflows/ci.yaml | 63 ++++++++++++++++++++++----------------- 1 file changed, 36 insertions(+), 27 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 07ffb0305..e4c83a633 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -34,42 +34,51 @@ jobs: - name: Install required Python packages run: | python -m pip install --upgrade pip - python -m pip install nox wheel + python -m pip install nox - name: run nox run: nox -e ci_checks_max pytest_min timeout-minutes: 10 - build-wheels: + build-dist: runs-on: ubuntu-20.04 - needs: test - steps: - - name: Fetch sources - uses: actions/checkout@v3 + - name: Fetch sources + uses: actions/checkout@v3 - - name: Set up Python - uses: actions/setup-python@v4 - with: - python-version: 3.8 + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: "3.10" - - uses: actions/cache@v3 - with: - path: ~/.cache/pip - key: ${{ runner.os }}-${{ matrix.python-version }}-pip-${{ hashFiles('minimum-requirements-ci.txt', 'pyproject.toml') }} - restore-keys: | - ${{ runner.os }}-${{ matrix.python-version }}-pip- + - name: Install build dependencies + run: | + python -m pip install -U pip + python -m pip install -U build - - name: Install required Python packages - run: | - python -m pip install --upgrade pip - python -m pip install wheel + - name: Build the source and binary distribution + run: python -m build - - name: make wheel - run: python -m pip wheel --no-deps -w dist . + - name: Upload dist files + uses: actions/upload-artifact@v3 + with: + name: frequenz-sdk-python-dist + path: dist/ + if-no-files-found: error + + publish-to-pypi: + needs: ["test", "build-dist"] + # Publish only on tags creation + if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags') + runs-on: ubuntu-20.04 + steps: + - name: Download dist files + uses: actions/download-artifact@v3 + with: + name: frequenz-sdk-python-dist - - name: upload wheels - uses: actions/upload-artifact@v3 - with: - name: frequenz-sdk-python-wheels - path: dist/*.whl + - name: Publish the Python distribution to PyPI + uses: pypa/gh-action-pypi-publish@release/v1 + with: + password: ${{ secrets.PYPI_API_TOKEN }} + skip_existing: true From f461eda27e65863318275c0cbb51b7bdad897be7 Mon Sep 17 00:00:00 2001 From: Leandro Lucarella Date: Tue, 4 Oct 2022 14:18:49 +0200 Subject: [PATCH 10/13] ci: Create GitHub Release automatically Add a GitHub workflow to create the GitHub Release automatically when a version is tagged. Also use the GitHub feature to create the CHANGELOG automatically. When creating the GitHub release the contents of the file RELEASE_NOTES.md will be used as release notes, so it should only contain an explanation on the release main changes and be user-facing. Also add the release notes for the current release. Signed-off-by: Leandro Lucarella --- .github/workflows/ci.yaml | 52 +++++++++++++++++++++++++++++++++++++-- RELEASE_NOTES.md | 11 +++++++++ 2 files changed, 61 insertions(+), 2 deletions(-) create mode 100644 RELEASE_NOTES.md diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index e4c83a633..b4e1bdb16 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -66,16 +66,64 @@ jobs: path: dist/ if-no-files-found: error - publish-to-pypi: + create-github-release: needs: ["test", "build-dist"] - # Publish only on tags creation + # Create a release only on tags creation if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags') + permissions: + # We need write permissions on contents to create GitHub releases and on + # discussions to create the release announcement in the discussion forums + contents: write + discussions: write + runs-on: ubuntu-20.04 + steps: + - name: Download dist files + uses: actions/download-artifact@v3 + with: + name: frequenz-sdk-python-dist + path: dist + + - name: Download RELEASE_NOTES.md + run: | + set -ux + gh api \ + -X GET \ + -f ref=$REF \ + -H "Accept: application/vnd.github.raw" \ + "/repos/$REPOSITORY/contents/RELEASE_NOTES.md" \ + > RELEASE_NOTES.md + env: + REF: ${{ github.ref }} + REPOSITORY: ${{ github.repository }} + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Create GitHub release + run: | + set -ux + extra_opts= + if echo "$REF_NAME" | grep -- -; then extra_opts=" --prerelease"; fi + gh release create \ + -R "$REPOSITORY" \ + --discussion-category announcements \ + --notes-file RELEASE_NOTES.md \ + --generate-notes \ + $extra_opts \ + $REF_NAME \ + dist/* + env: + REF_NAME: ${{ github.ref_name }} + REPOSITORY: ${{ github.repository }} + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + publish-to-pypi: + needs: ["create-github-release"] runs-on: ubuntu-20.04 steps: - name: Download dist files uses: actions/download-artifact@v3 with: name: frequenz-sdk-python-dist + path: dist - name: Publish the Python distribution to PyPI uses: pypa/gh-action-pypi-publish@release/v1 diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md new file mode 100644 index 000000000..8bfd37e1c --- /dev/null +++ b/RELEASE_NOTES.md @@ -0,0 +1,11 @@ +# `frequenz-sdk` Release Notes + +## Summary + +This is the first public open source release based on the internal SDK version v0.10.0. There are no breaking changes in this release, only changes to the project structure, metadata, and automation. Packages are also now uploaded to PyPI as [`frequenz-sdk`](https://pypi.org/project/frequenz-sdk/), so this project now can be installed normally via `pip`: + +```sh +python -m pip install frequenz-sdk +``` + +The GitHub issues were also improved, adding templates for [reporting issues](https://github.com/frequenz-floss/frequenz-sdk-python/issues/new?assignees=&labels=priority%3A%E2%9D%93%2C+type%3Abug&template=bug.yml) and [requesting features](https://github.com/frequenz-floss/frequenz-sdk-python/issues/new?assignees=&labels=part%3A%E2%9D%93%2C+priority%3A%E2%9D%93%2C+type%3Aenhancement&template=feature.yml). Users are also pointed to the [Discussion forums](https://github.com/frequenz-floss/frequenz-sdk-python/issues/new/choose) when trying to open an issue if they have questions instead. Also many labels are assigned automatically on issue and pull request creation. From 38298c34e2eb1fdfc942f2308076807ed1944b30 Mon Sep 17 00:00:00 2001 From: Leandro Lucarella Date: Tue, 4 Oct 2022 14:55:45 +0200 Subject: [PATCH 11/13] Create contributing guide Add a CONTRIBUTING.md guide explaining how to build, test, setup a development environment and release the library. Also mention the contributing guide in the README.md and add a RELEASE_NOTES template, so when releasing new versions is easy to clear the release notes by just copying the template. Signed-off-by: Leandro Lucarella --- .github/RELEASE_NOTES.template.md | 17 +++++++ CONTRIBUTING.md | 83 +++++++++++++++++++++++++++++++ README.md | 41 ++------------- 3 files changed, 105 insertions(+), 36 deletions(-) create mode 100644 .github/RELEASE_NOTES.template.md create mode 100644 CONTRIBUTING.md diff --git a/.github/RELEASE_NOTES.template.md b/.github/RELEASE_NOTES.template.md new file mode 100644 index 000000000..afb10a8ef --- /dev/null +++ b/.github/RELEASE_NOTES.template.md @@ -0,0 +1,17 @@ +# Release Notes + +## Summary + + + +## Upgrading + + + +## New Features + + + +## Bug Fixes + + diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 000000000..e1f009821 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,83 @@ +# Contributing to `frequenz-sdk` + +## Build + +You can use `build` to simply build the source and binary distribution: + +```sh +python -m pip install build +python -m build +``` + +## Local development + +You can use editable installs to develop the project locally (it will install +all the dependencies too): + +```sh +python -m pip install -e . +``` + +You can also use `nox` to run the tests and other checks: + +```sh +python -m pip install nox +nox +``` + +You can also use `nox -R` to reuse the current testing environment to speed up +test at the expense of a higher chance to end up with a dirty test environment. + +### Running tests individually + +For a better development test cycle you can install the runtime and test +dependencies and run `pytest` manually. + +```sh +python -m pip install . +python -m pip install pytest pytest-asyncio + +# And for example +pytest tests/test_sdk.py +``` + +## Releasing + +These are the steps to create a new release: + +1. Get the latest head you want to create a release from. + +2. Update the `RELEASE_NOTES.md` file if it is not complete, up to date, and + clean from template comments (`