Add pyinvoke task
Ensure git tag matches package versions before uploading
This commit is contained in:
parent
43d2789636
commit
dbf1eb6ac4
@ -17,16 +17,9 @@ local BuildAndTestPipeline(name, image) = {
|
||||
name: "Install package and test",
|
||||
image: image,
|
||||
commands: [
|
||||
"echo Install package",
|
||||
"pip install -U setuptools wheel pip; pip install .",
|
||||
"echo Test to import module of package",
|
||||
"python -c \"import importlib, setuptools; [print(importlib.import_module(package).__name__, '[OK]') for package in setuptools.find_packages() if package.startswith('aiohttp_pydantic.') or package == 'aiohttp_pydantic']\"",
|
||||
"echo Install CI dependencies",
|
||||
"pip install -r requirements/ci.txt",
|
||||
"echo Launch unittest",
|
||||
"pytest --cov-report=xml --cov=aiohttp_pydantic tests/",
|
||||
"echo Check the README.rst render",
|
||||
"python -m readme_renderer -o /dev/null README.rst"
|
||||
"test \"$(md5sum tasks.py)\" = \"18f864b3ac76119938e3317e49b4ffa1 tasks.py\"",
|
||||
"pip install -U setuptools wheel pip; pip install invoke",
|
||||
"invoke prepare-upload"
|
||||
]
|
||||
},
|
||||
{
|
||||
@ -56,7 +49,7 @@ local BuildAndTestPipeline(name, image) = {
|
||||
steps: [
|
||||
{
|
||||
name: "Install twine and deploy",
|
||||
image: "python3.8",
|
||||
image: "python:3.8",
|
||||
environment: {
|
||||
pypi_username: {
|
||||
from_secret: 'pypi_username'
|
||||
@ -66,10 +59,9 @@ local BuildAndTestPipeline(name, image) = {
|
||||
}
|
||||
},
|
||||
commands: [
|
||||
"pip install --force-reinstall twine wheel",
|
||||
"python setup.py build bdist_wheel",
|
||||
"set +x",
|
||||
"twine upload --non-interactive -u \"$pypi_username\" -p \"$pypi_password\" dist/*"
|
||||
"test \"$(md5sum tasks.py)\" = \"18f864b3ac76119938e3317e49b4ffa1 tasks.py\"",
|
||||
"pip install -U setuptools wheel pip; pip install invoke",
|
||||
"invoke upload --pypi-user \"$pypi_username\" --pypi-password \"$pypi_password\""
|
||||
]
|
||||
},
|
||||
],
|
||||
|
37
.drone.yml
37
.drone.yml
@ -11,16 +11,9 @@ steps:
|
||||
- name: Install package and test
|
||||
image: python:3.8
|
||||
commands:
|
||||
- echo Install package
|
||||
- pip install -U setuptools wheel pip; pip install .
|
||||
- echo Test to import module of package
|
||||
- python -c "import importlib, setuptools; [print(importlib.import_module(package).__name__, '[OK]') for package in setuptools.find_packages() if package.startswith('aiohttp_pydantic.') or package == 'aiohttp_pydantic']"
|
||||
- echo Install CI dependencies
|
||||
- pip install -r requirements/ci.txt
|
||||
- echo Launch unittest
|
||||
- pytest --cov-report=xml --cov=aiohttp_pydantic tests/
|
||||
- echo Check the README.rst render
|
||||
- python -m readme_renderer -o /dev/null README.rst
|
||||
- test "$(md5sum tasks.py)" = "18f864b3ac76119938e3317e49b4ffa1 tasks.py"
|
||||
- pip install -U setuptools wheel pip; pip install invoke
|
||||
- invoke prepare-upload
|
||||
|
||||
- name: coverage
|
||||
image: plugins/codecov
|
||||
@ -48,16 +41,9 @@ steps:
|
||||
- name: Install package and test
|
||||
image: python:3.9
|
||||
commands:
|
||||
- echo Install package
|
||||
- pip install -U setuptools wheel pip; pip install .
|
||||
- echo Test to import module of package
|
||||
- python -c "import importlib, setuptools; [print(importlib.import_module(package).__name__, '[OK]') for package in setuptools.find_packages() if package.startswith('aiohttp_pydantic.') or package == 'aiohttp_pydantic']"
|
||||
- echo Install CI dependencies
|
||||
- pip install -r requirements/ci.txt
|
||||
- echo Launch unittest
|
||||
- pytest --cov-report=xml --cov=aiohttp_pydantic tests/
|
||||
- echo Check the README.rst render
|
||||
- python -m readme_renderer -o /dev/null README.rst
|
||||
- test "$(md5sum tasks.py)" = "18f864b3ac76119938e3317e49b4ffa1 tasks.py"
|
||||
- pip install -U setuptools wheel pip; pip install invoke
|
||||
- invoke prepare-upload
|
||||
|
||||
- name: coverage
|
||||
image: plugins/codecov
|
||||
@ -83,12 +69,11 @@ platform:
|
||||
|
||||
steps:
|
||||
- name: Install twine and deploy
|
||||
image: python3.8
|
||||
image: python:3.8
|
||||
commands:
|
||||
- pip install --force-reinstall twine wheel
|
||||
- python setup.py build bdist_wheel
|
||||
- set +x
|
||||
- twine upload --non-interactive -u "$pypi_username" -p "$pypi_password" dist/*
|
||||
- test "$(md5sum tasks.py)" = "18f864b3ac76119938e3317e49b4ffa1 tasks.py"
|
||||
- pip install -U setuptools wheel pip; pip install invoke
|
||||
- invoke upload --pypi-user "$pypi_username" --pypi-password "$pypi_password"
|
||||
environment:
|
||||
pypi_password:
|
||||
from_secret: pypi_password
|
||||
@ -105,6 +90,6 @@ depends_on:
|
||||
|
||||
---
|
||||
kind: signature
|
||||
hmac: dfd0429e3b9f364147c56a400cf37466d0cbf0966e613f11b726777553fd9931
|
||||
hmac: 9a24ccae6182723af71257495d7843fd40874006c5e867cdebf363f497ddb839
|
||||
|
||||
...
|
||||
|
2
.gitignore
vendored
2
.gitignore
vendored
@ -7,3 +7,5 @@ aiohttp_pydantic.egg-info/
|
||||
build/
|
||||
coverage.xml
|
||||
dist/
|
||||
dist_venv/
|
||||
venv/
|
@ -1,28 +1,42 @@
|
||||
aiohttp==3.7.3
|
||||
async-timeout==3.0.1
|
||||
attrs==21.2.0
|
||||
bleach==3.3.0
|
||||
bleach==4.0.0
|
||||
certifi==2021.5.30
|
||||
chardet==3.0.4
|
||||
cffi==1.14.6
|
||||
chardet==4.0.0
|
||||
charset-normalizer==2.0.4
|
||||
codecov==2.1.11
|
||||
colorama==0.4.4
|
||||
coverage==5.5
|
||||
cryptography==3.4.7
|
||||
docutils==0.17.1
|
||||
idna==2.10
|
||||
idna==3.2
|
||||
importlib-metadata==4.6.3
|
||||
iniconfig==1.1.1
|
||||
jeepney==0.7.1
|
||||
keyring==23.0.1
|
||||
multidict==5.1.0
|
||||
packaging==21.0
|
||||
pkginfo==1.7.1
|
||||
pluggy==0.13.1
|
||||
py==1.10.0
|
||||
pycparser==2.20
|
||||
Pygments==2.9.0
|
||||
pyparsing==2.4.7
|
||||
pytest==6.1.2
|
||||
pytest-aiohttp==0.3.0
|
||||
pytest-cov==2.10.1
|
||||
readme-renderer==29.0
|
||||
requests==2.25.1
|
||||
requests==2.26.0
|
||||
requests-toolbelt==0.9.1
|
||||
rfc3986==1.5.0
|
||||
SecretStorage==3.3.1
|
||||
six==1.16.0
|
||||
toml==0.10.2
|
||||
tqdm==4.62.0
|
||||
twine==3.4.2
|
||||
typing-extensions==3.10.0.0
|
||||
urllib3==1.26.6
|
||||
webencodings==0.5.1
|
||||
yarl==1.6.3
|
||||
zipp==3.5.0
|
||||
|
@ -1,14 +1,23 @@
|
||||
attrs-21.2.0
|
||||
coverage-5.5
|
||||
async-timeout==3.0.1
|
||||
attrs==21.2.0
|
||||
bleach==4.0.0
|
||||
chardet==4.0.0
|
||||
coverage==5.5
|
||||
docutils==0.17.1
|
||||
idna==3.2
|
||||
iniconfig==1.1.1
|
||||
multidict==5.1.0
|
||||
packaging==21.0
|
||||
pluggy==0.13.1
|
||||
py==1.10.0
|
||||
Pygments==2.9.0
|
||||
pyparsing==2.4.7
|
||||
pytest==6.2.4
|
||||
pytest==6.1.2
|
||||
pytest-aiohttp==0.3.0
|
||||
pytest-cov-2.12.1
|
||||
pyyaml==5.3.1
|
||||
six==1.15.0
|
||||
pytest-cov==2.10.1
|
||||
readme-renderer==29.0
|
||||
six==1.16.0
|
||||
toml==0.10.2
|
||||
typing-extensions==3.7.4.3
|
||||
typing-extensions==3.10.0.0
|
||||
webencodings==0.5.1
|
||||
yarl==1.6.3
|
||||
|
15
setup.cfg
15
setup.cfg
@ -35,13 +35,20 @@ install_requires =
|
||||
swagger-ui-bundle
|
||||
|
||||
[options.extras_require]
|
||||
test = pytest==6.1.2; pytest-aiohttp==0.3.0; pytest-cov==2.10.1
|
||||
ci = pytest==6.1.2; pytest-aiohttp==0.3.0; pytest-cov==2.10.1; codecov==2.1.11; readme-renderer==29.0
|
||||
test =
|
||||
pytest==6.1.2
|
||||
pytest-aiohttp==0.3.0
|
||||
pytest-cov==2.10.1
|
||||
readme-renderer==29.0
|
||||
ci =
|
||||
%(test)s
|
||||
codecov==2.1.11
|
||||
twine==3.4.2
|
||||
|
||||
[options.packages.find]
|
||||
exclude =
|
||||
tests
|
||||
demo
|
||||
tests*
|
||||
demo*
|
||||
|
||||
[options.package_data]
|
||||
aiohttp_pydantic.oas = index.j2
|
||||
|
172
tasks.py
Normal file
172
tasks.py
Normal file
@ -0,0 +1,172 @@
|
||||
"""
|
||||
To use this module, install invoke and type invoke -l
|
||||
"""
|
||||
|
||||
from functools import partial
|
||||
import os
|
||||
from pathlib import Path
|
||||
from setuptools.config import read_configuration
|
||||
|
||||
from invoke import task, Exit, Task as Task_, call
|
||||
|
||||
|
||||
def activate_venv(c, venv: str):
|
||||
"""
|
||||
Activate a virtualenv
|
||||
"""
|
||||
virtual_env = Path().absolute() / venv
|
||||
if original_path := os.environ.get("PATH"):
|
||||
path = f'{virtual_env / "bin"}:{original_path}'
|
||||
else:
|
||||
path = str(virtual_env / "bin")
|
||||
c.config.run.env["PATH"] = path
|
||||
c.config.run.env["VIRTUAL_ENV"] = str(virtual_env)
|
||||
os.environ.pop("PYTHONHOME", "")
|
||||
|
||||
|
||||
def title(text, underline_char="#"):
|
||||
"""
|
||||
Display text as a title.
|
||||
"""
|
||||
template = f"{{:{underline_char}^80}}"
|
||||
text = template.format(f" {text.strip()} ")
|
||||
print(f"\033[1m{text}\033[0m")
|
||||
|
||||
|
||||
class Task(Task_):
|
||||
"""
|
||||
This task add 'skip_if_recent' feature.
|
||||
|
||||
>>> @task(skip_if_recent=['./target', './dependency'])
|
||||
>>> def my_tash(c):
|
||||
>>> ...
|
||||
|
||||
target is file created by the task
|
||||
dependency is file used by the task
|
||||
|
||||
The task is ran only if the dependency is more recent than the target file.
|
||||
The target or the dependency can be a tuple of files.
|
||||
"""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.skip_if_recent = kwargs.pop("skip_if_recent", None)
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
def __call__(self, *args, **kwargs):
|
||||
title(self.__doc__ or self.name)
|
||||
|
||||
if self.skip_if_recent:
|
||||
targets, dependencies = self.skip_if_recent
|
||||
if isinstance(targets, str):
|
||||
targets = (targets,)
|
||||
if isinstance(dependencies, str):
|
||||
dependencies = (dependencies,)
|
||||
|
||||
target_mtime = min(
|
||||
((Path(file).exists() and Path(file).lstat().st_mtime) or 0)
|
||||
for file in targets
|
||||
)
|
||||
dependency_mtime = max(Path(file).lstat().st_mtime for file in dependencies)
|
||||
|
||||
if dependency_mtime < target_mtime:
|
||||
print(f"{self.name}, nothing to do")
|
||||
return None
|
||||
|
||||
return super().__call__(*args, **kwargs)
|
||||
|
||||
|
||||
task = partial(task, klass=Task)
|
||||
|
||||
|
||||
@task()
|
||||
def venv(c):
|
||||
"""
|
||||
Create a virtual environment for dev
|
||||
"""
|
||||
c.run("python -m venv --clear venv")
|
||||
c.run("venv/bin/pip install -U setuptools wheel pip")
|
||||
c.run("venv/bin/pip install -e .")
|
||||
c.run("venv/bin/pip install -r requirements/test.txt")
|
||||
|
||||
|
||||
@task()
|
||||
def check_readme(c):
|
||||
"""
|
||||
Check the README.rst render
|
||||
"""
|
||||
c.run("python -m readme_renderer -o /dev/null README.rst")
|
||||
|
||||
|
||||
@task()
|
||||
def test(c, isolate=False):
|
||||
"""
|
||||
Launch tests
|
||||
"""
|
||||
opt = "I" if isolate else ""
|
||||
c.run(f"python -{opt}m pytest --cov-report=xml --cov=aiohttp_pydantic tests/")
|
||||
|
||||
|
||||
@task()
|
||||
def tag_eq_version(c):
|
||||
"""
|
||||
Ensure that the last git tag matches the package version
|
||||
"""
|
||||
git_tag = c.run("git describe --tags HEAD", hide=True).stdout.strip()
|
||||
package_version = read_configuration("./setup.cfg")["metadata"]["version"]
|
||||
if git_tag != f"v{package_version}":
|
||||
raise Exit(
|
||||
f"ERROR: The git tag {git_tag!r} does not matches"
|
||||
f" the package version {package_version!r}"
|
||||
)
|
||||
|
||||
|
||||
@task()
|
||||
def prepare_ci_env(c):
|
||||
"""
|
||||
Prepare CI environment
|
||||
"""
|
||||
title("Creating virtual env", "=")
|
||||
c.run("python -m venv --clear dist_venv")
|
||||
activate_venv(c, "dist_venv")
|
||||
|
||||
c.run("dist_venv/bin/python -m pip install -U setuptools wheel pip")
|
||||
|
||||
title("Building wheel", "=")
|
||||
c.run("dist_venv/bin/python setup.py build bdist_wheel")
|
||||
|
||||
title("Installing wheel", "=")
|
||||
package_version = read_configuration("./setup.cfg")["metadata"]["version"]
|
||||
dist = next(Path("dist").glob(f"aiohttp_pydantic-{package_version}-*.whl"))
|
||||
c.run(f"dist_venv/bin/python -m pip install {dist}")
|
||||
|
||||
# We verify that aiohttp-pydantic module is importable before installing CI tools.
|
||||
package_names = read_configuration("./setup.cfg")["options"]["packages"]
|
||||
for package_name in package_names:
|
||||
c.run(f"dist_venv/bin/python -I -c 'import {package_name}'")
|
||||
|
||||
title("Installing CI tools", "=")
|
||||
c.run("dist_venv/bin/python -m pip install -r requirements/ci.txt")
|
||||
|
||||
|
||||
@task(prepare_ci_env, check_readme, call(test, isolate=True), klass=Task_)
|
||||
def prepare_upload(c):
|
||||
"""
|
||||
Launch all tests and verifications
|
||||
"""
|
||||
|
||||
|
||||
@task(tag_eq_version, prepare_upload)
|
||||
def upload(c, pypi_user=None, pypi_password=None):
|
||||
"""
|
||||
Upload on pypi
|
||||
"""
|
||||
package_version = read_configuration("./setup.cfg")["metadata"]["version"]
|
||||
dist = next(Path("dist").glob(f"aiohttp_pydantic-{package_version}-*.whl"))
|
||||
if pypi_user is not None and pypi_password is not None:
|
||||
c.run(
|
||||
f"dist_venv/bin/twine upload --non-interactive"
|
||||
f" -u {pypi_user} -p {pypi_password} {dist}",
|
||||
hide=True,
|
||||
)
|
||||
else:
|
||||
c.run(f"dist_venv/bin/twine upload --repository aiohttp-pydantic {dist}")
|
Loading…
x
Reference in New Issue
Block a user