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",
|
name: "Install package and test",
|
||||||
image: image,
|
image: image,
|
||||||
commands: [
|
commands: [
|
||||||
"echo Install package",
|
"test \"$(md5sum tasks.py)\" = \"18f864b3ac76119938e3317e49b4ffa1 tasks.py\"",
|
||||||
"pip install -U setuptools wheel pip; pip install .",
|
"pip install -U setuptools wheel pip; pip install invoke",
|
||||||
"echo Test to import module of package",
|
"invoke prepare-upload"
|
||||||
"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"
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -56,7 +49,7 @@ local BuildAndTestPipeline(name, image) = {
|
|||||||
steps: [
|
steps: [
|
||||||
{
|
{
|
||||||
name: "Install twine and deploy",
|
name: "Install twine and deploy",
|
||||||
image: "python3.8",
|
image: "python:3.8",
|
||||||
environment: {
|
environment: {
|
||||||
pypi_username: {
|
pypi_username: {
|
||||||
from_secret: 'pypi_username'
|
from_secret: 'pypi_username'
|
||||||
@ -66,10 +59,9 @@ local BuildAndTestPipeline(name, image) = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
commands: [
|
commands: [
|
||||||
"pip install --force-reinstall twine wheel",
|
"test \"$(md5sum tasks.py)\" = \"18f864b3ac76119938e3317e49b4ffa1 tasks.py\"",
|
||||||
"python setup.py build bdist_wheel",
|
"pip install -U setuptools wheel pip; pip install invoke",
|
||||||
"set +x",
|
"invoke upload --pypi-user \"$pypi_username\" --pypi-password \"$pypi_password\""
|
||||||
"twine upload --non-interactive -u \"$pypi_username\" -p \"$pypi_password\" dist/*"
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
37
.drone.yml
37
.drone.yml
@ -11,16 +11,9 @@ steps:
|
|||||||
- name: Install package and test
|
- name: Install package and test
|
||||||
image: python:3.8
|
image: python:3.8
|
||||||
commands:
|
commands:
|
||||||
- echo Install package
|
- test "$(md5sum tasks.py)" = "18f864b3ac76119938e3317e49b4ffa1 tasks.py"
|
||||||
- pip install -U setuptools wheel pip; pip install .
|
- pip install -U setuptools wheel pip; pip install invoke
|
||||||
- echo Test to import module of package
|
- invoke prepare-upload
|
||||||
- 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
|
|
||||||
|
|
||||||
- name: coverage
|
- name: coverage
|
||||||
image: plugins/codecov
|
image: plugins/codecov
|
||||||
@ -48,16 +41,9 @@ steps:
|
|||||||
- name: Install package and test
|
- name: Install package and test
|
||||||
image: python:3.9
|
image: python:3.9
|
||||||
commands:
|
commands:
|
||||||
- echo Install package
|
- test "$(md5sum tasks.py)" = "18f864b3ac76119938e3317e49b4ffa1 tasks.py"
|
||||||
- pip install -U setuptools wheel pip; pip install .
|
- pip install -U setuptools wheel pip; pip install invoke
|
||||||
- echo Test to import module of package
|
- invoke prepare-upload
|
||||||
- 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
|
|
||||||
|
|
||||||
- name: coverage
|
- name: coverage
|
||||||
image: plugins/codecov
|
image: plugins/codecov
|
||||||
@ -83,12 +69,11 @@ platform:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Install twine and deploy
|
- name: Install twine and deploy
|
||||||
image: python3.8
|
image: python:3.8
|
||||||
commands:
|
commands:
|
||||||
- pip install --force-reinstall twine wheel
|
- test "$(md5sum tasks.py)" = "18f864b3ac76119938e3317e49b4ffa1 tasks.py"
|
||||||
- python setup.py build bdist_wheel
|
- pip install -U setuptools wheel pip; pip install invoke
|
||||||
- set +x
|
- invoke upload --pypi-user "$pypi_username" --pypi-password "$pypi_password"
|
||||||
- twine upload --non-interactive -u "$pypi_username" -p "$pypi_password" dist/*
|
|
||||||
environment:
|
environment:
|
||||||
pypi_password:
|
pypi_password:
|
||||||
from_secret: pypi_password
|
from_secret: pypi_password
|
||||||
@ -105,6 +90,6 @@ depends_on:
|
|||||||
|
|
||||||
---
|
---
|
||||||
kind: signature
|
kind: signature
|
||||||
hmac: dfd0429e3b9f364147c56a400cf37466d0cbf0966e613f11b726777553fd9931
|
hmac: 9a24ccae6182723af71257495d7843fd40874006c5e867cdebf363f497ddb839
|
||||||
|
|
||||||
...
|
...
|
||||||
|
2
.gitignore
vendored
2
.gitignore
vendored
@ -7,3 +7,5 @@ aiohttp_pydantic.egg-info/
|
|||||||
build/
|
build/
|
||||||
coverage.xml
|
coverage.xml
|
||||||
dist/
|
dist/
|
||||||
|
dist_venv/
|
||||||
|
venv/
|
@ -1,28 +1,42 @@
|
|||||||
aiohttp==3.7.3
|
|
||||||
async-timeout==3.0.1
|
async-timeout==3.0.1
|
||||||
attrs==21.2.0
|
attrs==21.2.0
|
||||||
bleach==3.3.0
|
bleach==4.0.0
|
||||||
certifi==2021.5.30
|
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
|
codecov==2.1.11
|
||||||
|
colorama==0.4.4
|
||||||
coverage==5.5
|
coverage==5.5
|
||||||
|
cryptography==3.4.7
|
||||||
docutils==0.17.1
|
docutils==0.17.1
|
||||||
idna==2.10
|
idna==3.2
|
||||||
|
importlib-metadata==4.6.3
|
||||||
iniconfig==1.1.1
|
iniconfig==1.1.1
|
||||||
|
jeepney==0.7.1
|
||||||
|
keyring==23.0.1
|
||||||
multidict==5.1.0
|
multidict==5.1.0
|
||||||
packaging==21.0
|
packaging==21.0
|
||||||
|
pkginfo==1.7.1
|
||||||
pluggy==0.13.1
|
pluggy==0.13.1
|
||||||
py==1.10.0
|
py==1.10.0
|
||||||
|
pycparser==2.20
|
||||||
Pygments==2.9.0
|
Pygments==2.9.0
|
||||||
pyparsing==2.4.7
|
pyparsing==2.4.7
|
||||||
pytest==6.1.2
|
pytest==6.1.2
|
||||||
pytest-aiohttp==0.3.0
|
pytest-aiohttp==0.3.0
|
||||||
pytest-cov==2.10.1
|
pytest-cov==2.10.1
|
||||||
readme-renderer==29.0
|
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
|
six==1.16.0
|
||||||
toml==0.10.2
|
toml==0.10.2
|
||||||
|
tqdm==4.62.0
|
||||||
|
twine==3.4.2
|
||||||
typing-extensions==3.10.0.0
|
typing-extensions==3.10.0.0
|
||||||
urllib3==1.26.6
|
urllib3==1.26.6
|
||||||
webencodings==0.5.1
|
webencodings==0.5.1
|
||||||
yarl==1.6.3
|
yarl==1.6.3
|
||||||
|
zipp==3.5.0
|
||||||
|
@ -1,14 +1,23 @@
|
|||||||
attrs-21.2.0
|
async-timeout==3.0.1
|
||||||
coverage-5.5
|
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
|
iniconfig==1.1.1
|
||||||
|
multidict==5.1.0
|
||||||
packaging==21.0
|
packaging==21.0
|
||||||
pluggy==0.13.1
|
pluggy==0.13.1
|
||||||
py==1.10.0
|
py==1.10.0
|
||||||
|
Pygments==2.9.0
|
||||||
pyparsing==2.4.7
|
pyparsing==2.4.7
|
||||||
pytest==6.2.4
|
pytest==6.1.2
|
||||||
pytest-aiohttp==0.3.0
|
pytest-aiohttp==0.3.0
|
||||||
pytest-cov-2.12.1
|
pytest-cov==2.10.1
|
||||||
pyyaml==5.3.1
|
readme-renderer==29.0
|
||||||
six==1.15.0
|
six==1.16.0
|
||||||
toml==0.10.2
|
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
|
swagger-ui-bundle
|
||||||
|
|
||||||
[options.extras_require]
|
[options.extras_require]
|
||||||
test = pytest==6.1.2; pytest-aiohttp==0.3.0; pytest-cov==2.10.1
|
test =
|
||||||
ci = pytest==6.1.2; pytest-aiohttp==0.3.0; pytest-cov==2.10.1; codecov==2.1.11; readme-renderer==29.0
|
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]
|
[options.packages.find]
|
||||||
exclude =
|
exclude =
|
||||||
tests
|
tests*
|
||||||
demo
|
demo*
|
||||||
|
|
||||||
[options.package_data]
|
[options.package_data]
|
||||||
aiohttp_pydantic.oas = index.j2
|
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