From 71c61b6ceb2efd4b1f12f70c501a506640b5dc2a Mon Sep 17 00:00:00 2001 From: Benjamin Hackl Date: Tue, 7 May 2024 14:22:02 +0200 Subject: [PATCH 01/11] Fixed some typos (#734) --- CHANGES.rst | 4 ++-- aiodocker/containers.py | 2 +- aiodocker/services.py | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index 7f15286f..07522c11 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -18,7 +18,7 @@ Changes Bugfixes -------- -- Use ssl_context passsed to Docker constructor for creating underlying connection to docker engine. (#536) +- Use ssl_context passed to Docker constructor for creating underlying connection to docker engine. (#536) - Fix an error when attach/exec when container stops before close connection to it. (#608) @@ -98,7 +98,7 @@ Bugfixes Bugfixes -------- -- Improve the errror message when connection is closed by Docker Engine on TCP hijacking. (#424) +- Improve the error message when connection is closed by Docker Engine on TCP hijacking. (#424) 0.18.0 (2020-03-25) diff --git a/aiodocker/containers.py b/aiodocker/containers.py index a390dcd5..0cc87425 100644 --- a/aiodocker/containers.py +++ b/aiodocker/containers.py @@ -75,7 +75,7 @@ async def run( try: container = await self.create(config, name=name) except DockerError as err: - # image not fount, try pulling it + # image not found, try pulling it if err.status == 404 and "Image" in config: await self.docker.pull(config["Image"], auth=auth) container = await self.create(config, name=name) diff --git a/aiodocker/services.py b/aiodocker/services.py index bd06f95f..afc276d9 100644 --- a/aiodocker/services.py +++ b/aiodocker/services.py @@ -75,7 +75,7 @@ async def create( if auth and registry is None: raise KeyError( - "When auth is specified you need to specifiy also the registry" + "When auth is specified you need to specify also the registry" ) # from {"key":"value"} to ["key=value"] From 0507328ab5e674c97bc4324ad4d25da9397ccf5e Mon Sep 17 00:00:00 2001 From: Naufal Afif Date: Tue, 7 May 2024 19:54:01 +0700 Subject: [PATCH 02/11] Add `docker.networks` documentation (#828) Co-authored-by: Joongi Kim --- docs/networks.rst | 44 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 docs/networks.rst diff --git a/docs/networks.rst b/docs/networks.rst new file mode 100644 index 00000000..38a0a440 --- /dev/null +++ b/docs/networks.rst @@ -0,0 +1,44 @@ +========== +Networks +========== + +Create a network +================== + +.. code-block:: python + + import asyncio + import aiodocker + + async def create_network(): + docker = aiodocker.Docker() + network_config = { + "Name": "isolated_nw", + "Driver": "bridge", + "EnableIPv6": False, + "IPAM": { + "Driver": "default" + } + } + network = await docker.networks.create(config=network_config) + print(network) + await docker.close() + + if __name__ == '__main__': + asyncio.run(create_network()) + +--------- +Reference +--------- + +DockerNetworks +================ +.. autoclass:: aiodocker.docker.DockerNetworks + :members: + :undoc-members: + +DockerNetwork +=============== +.. autoclass:: aiodocker.docker.DockerNetwork + :members: + :undoc-members: From 15a3290ca693efff60727187df5f9b5334bda9f7 Mon Sep 17 00:00:00 2001 From: Seo Sanghyeon Date: Wed, 8 May 2024 00:49:35 +0900 Subject: [PATCH 03/11] Document DockerSystem (#808) --- docs/index.rst | 1 + docs/system.rst | 14 ++++++++++++++ 2 files changed, 15 insertions(+) create mode 100644 docs/system.rst diff --git a/docs/index.rst b/docs/index.rst index b7b57e0c..a3c6d041 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -128,6 +128,7 @@ It's *Apache 2* licensed and freely available. secrets services swarm + system volumes tasks log diff --git a/docs/system.rst b/docs/system.rst new file mode 100644 index 00000000..6ea31271 --- /dev/null +++ b/docs/system.rst @@ -0,0 +1,14 @@ +====== +System +====== + +---------- +Reference +---------- + +DockerSystem +============= + +.. autoclass:: aiodocker.system.DockerSystem + :members: + :undoc-members: From aae59d4d55a981c92767daa9eb95a579c03c84a2 Mon Sep 17 00:00:00 2001 From: Joongi Kim Date: Tue, 7 May 2024 21:35:09 +0400 Subject: [PATCH 04/11] Update asyncio usage patterns in the doc examples (#837) --- CHANGES/837.doc | 1 + README.rst | 75 ++++++++++++++++++++++----------------------- docs/configs.rst | 49 ++++++++++++++--------------- docs/containers.rst | 46 +++++++++++++-------------- docs/index.rst | 69 ++++++++++++++++++++--------------------- docs/networks.rst | 2 +- docs/secrets.rst | 45 ++++++++++++++------------- docs/services.rst | 42 ++++++++++++------------- 8 files changed, 161 insertions(+), 168 deletions(-) create mode 100644 CHANGES/837.doc diff --git a/CHANGES/837.doc b/CHANGES/837.doc new file mode 100644 index 00000000..c06f7f0e --- /dev/null +++ b/CHANGES/837.doc @@ -0,0 +1 @@ +Update the documentation examples to use the modern `asyncio.run()` pattern and initialize `aiodocker.Docker()` instance inside async functions where there is a valid running event loop diff --git a/README.rst b/README.rst index 6f973a29..019bda9b 100644 --- a/README.rst +++ b/README.rst @@ -10,9 +10,9 @@ AsyncIO bindings for docker.io :target: https://pypi.org/project/aiodocker/ :alt: Python Versions -.. image:: https://travis-ci.com/aio-libs/aiodocker.svg?branch=master - :target: https://travis-ci.com/aio-libs/aiodocker - :alt: Build Status +.. image:: https://github.com/aio-libs/aiodocker/actions/workflows/ci.yml/badge.svg?branch=master + :target: https://github.com/aio-libs/aiodocker/actions?query=workflow%3ACI+branch%3Amaster + :alt: GitHub Actions status for the main branch .. image:: https://codecov.io/gh/aio-libs/aiodocker/branch/master/graph/badge.svg :target: https://codecov.io/gh/aio-libs/aiodocker @@ -43,38 +43,37 @@ Examples .. code-block:: python - import asyncio - import aiodocker - - async def list_things(): - docker = aiodocker.Docker() - print('== Images ==') - for image in (await docker.images.list()): - tags = image['RepoTags'][0] if image['RepoTags'] else '' - print(image['Id'], tags) - print('== Containers ==') - for container in (await docker.containers.list()): - print(f" {container._id}") - await docker.close() - - async def run_container(): - docker = aiodocker.Docker() - print('== Running a hello-world container ==') - container = await docker.containers.create_or_replace( - config={ - 'Cmd': ['/bin/ash', '-c', 'echo "hello world"'], - 'Image': 'alpine:latest', - }, - name='testing', - ) - await container.start() - logs = await container.log(stdout=True) - print(''.join(logs)) - await container.delete(force=True) - await docker.close() - - if __name__ == '__main__': - loop = asyncio.get_event_loop() - loop.run_until_complete(list_things()) - loop.run_until_complete(run_container()) - loop.close() + import asyncio + import aiodocker + + async def list_things(docker): + print('== Images ==') + for image in (await docker.images.list()): + tags = image['RepoTags'][0] if image['RepoTags'] else '' + print(image['Id'], tags) + print('== Containers ==') + for container in (await docker.containers.list()): + print(f" {container._id}") + + async def run_container(docker): + print('== Running a hello-world container ==') + container = await docker.containers.create_or_replace( + config={ + 'Cmd': ['/bin/ash', '-c', 'echo "hello world"'], + 'Image': 'alpine:latest', + }, + name='testing', + ) + await container.start() + logs = await container.log(stdout=True) + print(''.join(logs)) + await container.delete(force=True) + + async def main(): + docker = aiodocker.Docker() + await list_things(docker) + await run_container(docker) + await docker.close() + + if __name__ == "__main__": + asyncio.run(main()) diff --git a/docs/configs.rst b/docs/configs.rst index 4686dafd..c9566b55 100644 --- a/docs/configs.rst +++ b/docs/configs.rst @@ -11,45 +11,46 @@ Create a config import asyncio import aiodocker - docker = aiodocker.Docker() - - async def create_config(): + async def create_config(docker): config = await docker.configs.create( name="my_config", - data="This is my config data" + data="This is my config data", ) - await docker.close() return config - async def create_service(TaskTemplate): + async def create_service(docker, task_template): service = await docker.services.create( - task_template=TaskTemplate, - name="my_service" + task_template=task_template, + name="my_service", ) - await docker.close() + return service - if __name__ == '__main__': - loop = asyncio.get_event_loop() - my_config = loop.run_until_complete(create_config()) - TaskTemplate = { + async def main(): + docker = aiodocker.Docker() + my_config = await create_config(docker) + task_template = { "ContainerSpec": { "Image": "redis", "Configs": [ - { - "File": { - "Name": my_config["Spec"]["Name"], - "UID": "0", - "GID": "0", - "Mode": 292 + { + "File": { + "Name": my_config["Spec"]["Name"], + "UID": "0", + "GID": "0", + "Mode": 292 + }, + "ConfigID": my_config["ID"], + "ConfigName": my_config["Spec"]["Name"], }, - "ConfigID": my_config["ID"], - "ConfigName": my_config["Spec"]["Name"], - } ], }, } - loop.run_until_complete(create_service(TaskTemplate)) - loop.close() + service = await create_service(docker, task_template) + print(service) + await docker.close() + + if __name__ == "__main__": + asyncio.run(main()) ------------ diff --git a/docs/containers.rst b/docs/containers.rst index b825a29f..3a3a9e7a 100644 --- a/docs/containers.rst +++ b/docs/containers.rst @@ -7,31 +7,27 @@ Create a container .. code-block:: python - import asyncio - import aiodocker - - docker = aiodocker.Docker() - - config = { - "Cmd": ["/bin/ls"], - "Image": "alpine:latest", - "AttachStdin": False, - "AttachStdout": False, - "AttachStderr": False, - "Tty": False, - "OpenStdin": False, - } - - async def create_container(): - container = await docker.containers.create(config=config) - print(container) - await docker.close() - - - if __name__ == '__main__': - loop = asyncio.get_event_loop() - loop.run_until_complete(create_container()) - loop.close() + import asyncio + import aiodocker + + config = { + "Cmd": ["/bin/ls"], + "Image": "alpine:latest", + "AttachStdin": False, + "AttachStdout": False, + "AttachStderr": False, + "Tty": False, + "OpenStdin": False, + } + + async def create_container(): + docker = aiodocker.Docker() + container = await docker.containers.create(config=config) + print(container) + await docker.close() + + if __name__ == "__main__": + asyncio.run(create_container()) --------- Reference diff --git a/docs/index.rst b/docs/index.rst index a3c6d041..59cdf46f 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -44,41 +44,40 @@ Examples .. code-block:: python - import asyncio - import aiodocker - - async def list_things(): - docker = aiodocker.Docker() - print('== Images ==') - for image in (await docker.images.list()): - tags = image['RepoTags'][0] if image['RepoTags'] else '' - print(image['Id'], tags) - print('== Containers ==') - for container in (await docker.containers.list()): - print(f" {container._id}") - await docker.close() - - async def run_container(): - docker = aiodocker.Docker() - print('== Running a hello-world container ==') - container = await docker.containers.create_or_replace( - config={ - 'Cmd': ['/bin/ash', '-c', 'echo "hello world"'], - 'Image': 'alpine:latest', - }, - name='testing', - ) - await container.start() - logs = await container.log(stdout=True) - print(''.join(logs)) - await container.delete(force=True) - await docker.close() - - if __name__ == '__main__': - loop = asyncio.get_event_loop() - loop.run_until_complete(list_things()) - loop.run_until_complete(run_container()) - loop.close() + import asyncio + import aiodocker + + async def list_things(docker): + print('== Images ==') + for image in (await docker.images.list()): + tags = image['RepoTags'][0] if image['RepoTags'] else '' + print(image['Id'], tags) + print('== Containers ==') + for container in (await docker.containers.list()): + print(f" {container._id}") + + async def run_container(docker): + print('== Running a hello-world container ==') + container = await docker.containers.create_or_replace( + config={ + 'Cmd': ['/bin/ash', '-c', 'echo "hello world"'], + 'Image': 'alpine:latest', + }, + name='testing', + ) + await container.start() + logs = await container.log(stdout=True) + print(''.join(logs)) + await container.delete(force=True) + + async def main(): + docker = aiodocker.Docker() + await list_things(docker) + await run_container(docker) + await docker.close() + + if __name__ == "__main__": + asyncio.run(main()) Source code diff --git a/docs/networks.rst b/docs/networks.rst index 38a0a440..23aa1bfe 100644 --- a/docs/networks.rst +++ b/docs/networks.rst @@ -24,7 +24,7 @@ Create a network print(network) await docker.close() - if __name__ == '__main__': + if __name__ == "__main__": asyncio.run(create_network()) --------- diff --git a/docs/secrets.rst b/docs/secrets.rst index d77b62cf..47607dfb 100644 --- a/docs/secrets.rst +++ b/docs/secrets.rst @@ -11,45 +11,46 @@ Create a secret import asyncio import aiodocker - docker = aiodocker.Docker() - - async def create_secret(): + async def create_secret(docker): secret = await docker.secrets.create( name="my_secret", data="you can not read that terrible secret" ) - await docker.close() return secret - async def create_service(TaskTemplate): + async def create_service(docker, task_template): service = await docker.services.create( - task_template=TaskTemplate, + task_template=task_template, name="my_service" ) - await docker.close() + return service - if __name__ == '__main__': - loop = asyncio.get_event_loop() - my_secret = loop.run_until_complete(create_secret()) - TaskTemplate = { + async def main(): + docker = aiodocker.Docker() + my_secret = await create_secret(docker) + task_template = { "ContainerSpec": { "Image": "redis", "Secrets": [ - { - "File": { - "Name": my_secret["Spec"]["Name"], - "UID": "0", - "GID": "0", - "Mode": 292 + { + "File": { + "Name": my_secret["Spec"]["Name"], + "UID": "0", + "GID": "0", + "Mode": 292 + }, + "SecretID": my_secret["ID"], + "SecretName": my_secret["Spec"]["Name"] }, - "SecretID": my_secret["ID"], - "SecretName": my_secret["Spec"]["Name"] - } ], }, } - loop.run_until_complete(create_service(TaskTemplate)) - loop.close() + service = await create_service(docker, task_template) + print(service) + await docker.close() + + if __name__ == "__main__": + asyncio.run(main()) ------------ diff --git a/docs/services.rst b/docs/services.rst index b15141b1..ba570270 100644 --- a/docs/services.rst +++ b/docs/services.rst @@ -8,29 +8,25 @@ Create a service .. code-block:: python - import asyncio - import aiodocker - - docker = aiodocker.Docker() - - TaskTemplate = { - "ContainerSpec": { - "Image": "redis", - }, - } - - async def create_service(): - service = await docker.services.create( - task_template=TaskTemplate, - name="my_service" - ) - await docker.close() - - - if __name__ == '__main__': - loop = asyncio.get_event_loop() - loop.run_until_complete(create_service()) - loop.close() + import asyncio + import aiodocker + + async def create_service(): + docker = aiodocker.Docker() + task_template = { + "ContainerSpec": { + "Image": "redis", + }, + } + service = await docker.services.create( + task_template=task_template, + name="my_service" + ) + print(service) + await docker.close() + + if __name__ == "__main__": + asyncio.run(create_service()) ------------ Reference From 50350d42c68ac273e70a4ac3e80c9eeac12587b7 Mon Sep 17 00:00:00 2001 From: Sviatoslav Sydorenko Date: Tue, 14 May 2024 01:13:40 +0200 Subject: [PATCH 05/11] =?UTF-8?q?=F0=9F=A7=AA=20Standardize=20GHA=20workfl?= =?UTF-8?q?ow=20name=20as=20`ci-cd.yml`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/{ci.yml => ci-cd.yml} | 0 README.rst | 4 ++-- 2 files changed, 2 insertions(+), 2 deletions(-) rename .github/workflows/{ci.yml => ci-cd.yml} (100%) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci-cd.yml similarity index 100% rename from .github/workflows/ci.yml rename to .github/workflows/ci-cd.yml diff --git a/README.rst b/README.rst index 019bda9b..120ff2c8 100644 --- a/README.rst +++ b/README.rst @@ -10,8 +10,8 @@ AsyncIO bindings for docker.io :target: https://pypi.org/project/aiodocker/ :alt: Python Versions -.. image:: https://github.com/aio-libs/aiodocker/actions/workflows/ci.yml/badge.svg?branch=master - :target: https://github.com/aio-libs/aiodocker/actions?query=workflow%3ACI+branch%3Amaster +.. image:: https://github.com/aio-libs/aiodocker/actions/workflows/ci-cd.yml/badge.svg?branch=master + :target: https://github.com/aio-libs/aiodocker/actions/workflows/ci-cd.yml?query=branch%3Amaster :alt: GitHub Actions status for the main branch .. image:: https://codecov.io/gh/aio-libs/aiodocker/branch/master/graph/badge.svg From dc05f79c770cda0baca2878ec7068ebb149384a7 Mon Sep 17 00:00:00 2001 From: Sviatoslav Sydorenko Date: Tue, 14 May 2024 01:20:28 +0200 Subject: [PATCH 06/11] =?UTF-8?q?=F0=9F=92=85=F0=9F=A7=AA=20Mark=20the=20m?= =?UTF-8?q?ain=20GHA=20workflow=20as=20CI/CD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/ci-cd.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci-cd.yml b/.github/workflows/ci-cd.yml index da71f16f..258d75e2 100644 --- a/.github/workflows/ci-cd.yml +++ b/.github/workflows/ci-cd.yml @@ -1,4 +1,6 @@ -name: CI +--- + +name: CI/CD on: push: From 13fd9e8d5de793c2b6f931196894168f938c9cfe Mon Sep 17 00:00:00 2001 From: Sviatoslav Sydorenko Date: Tue, 14 May 2024 01:20:56 +0200 Subject: [PATCH 07/11] =?UTF-8?q?=F0=9F=A7=AA=20Run=20CI/CD=20on=20merge?= =?UTF-8?q?=20queues?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/ci-cd.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci-cd.yml b/.github/workflows/ci-cd.yml index 258d75e2..3d4d8550 100644 --- a/.github/workflows/ci-cd.yml +++ b/.github/workflows/ci-cd.yml @@ -3,6 +3,7 @@ name: CI/CD on: + merge_group: push: branches: [ master ] tags: [ 'v*' ] From bbcbf79fe12131d10cb7c96484e836190c4e28ad Mon Sep 17 00:00:00 2001 From: Sviatoslav Sydorenko Date: Tue, 14 May 2024 01:21:44 +0200 Subject: [PATCH 08/11] =?UTF-8?q?=F0=9F=A7=AA=F0=9F=92=84=20Put=20project?= =?UTF-8?q?=20name=20into=20a=20common=20GHA=20var?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/ci-cd.yml | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci-cd.yml b/.github/workflows/ci-cd.yml index 3d4d8550..ec9ecc55 100644 --- a/.github/workflows/ci-cd.yml +++ b/.github/workflows/ci-cd.yml @@ -13,6 +13,10 @@ on: - cron: '0 6 * * *' # Daily 6AM UTC build +env: + PROJECT_NAME: aiodocker + + jobs: lint: @@ -86,8 +90,8 @@ jobs: - name: Start Docker services if: ${{ matrix.registry == '1' }} run: | - docker run -d --name aiodocker-test-registry -p 5000:5000 registry:2 - docker run -d -p 5001:5001 --name aiodocker-test-registry2 -v `pwd`/tests/certs:/certs -e "REGISTRY_AUTH=htpasswd" -e "REGISTRY_AUTH_HTPASSWD_REALM=Registry Realm" -e REGISTRY_AUTH_HTPASSWD_PATH=/certs/htpasswd -e REGISTRY_HTTP_ADDR=0.0.0.0:5001 -e REGISTRY_HTTP_TLS_CERTIFICATE=/certs/registry.crt -e REGISTRY_HTTP_TLS_KEY=/certs/registry.key registry:2 + docker run -d --name ${{ env.PROJECT_NAME }}-test-registry -p 5000:5000 registry:2 + docker run -d -p 5001:5001 --name ${{ env.PROJECT_NAME }}-test-registry2 -v `pwd`/tests/certs:/certs -e "REGISTRY_AUTH=htpasswd" -e "REGISTRY_AUTH_HTPASSWD_REALM=Registry Realm" -e REGISTRY_AUTH_HTPASSWD_PATH=/certs/htpasswd -e REGISTRY_HTTP_ADDR=0.0.0.0:5001 -e REGISTRY_HTTP_TLS_CERTIFICATE=/certs/registry.crt -e REGISTRY_HTTP_TLS_KEY=/certs/registry.key registry:2 - name: Run unittests env: COLOR: 'yes' @@ -136,7 +140,7 @@ jobs: uses: aio-libs/create-release@v1.6.5 with: changes_file: CHANGES.rst - name: aiodocker + name: ${{ env.PROJECT_NAME }} github_token: ${{ secrets.GITHUB_TOKEN }} pypi_token: ${{ secrets.PYPI_TOKEN }} head_line: "{version}\\s+\\({date}\\)\n====+\n?" From 2b7692a8c0150061d35477590d8099e3a802f73f Mon Sep 17 00:00:00 2001 From: Sviatoslav Sydorenko Date: Tue, 14 May 2024 01:22:54 +0200 Subject: [PATCH 09/11] =?UTF-8?q?=F0=9F=A7=AA=20Wire=20tokenless=20publish?= =?UTF-8?q?ing=20to=20PyPI=20@=20GHA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Resolves #835 --- .github/workflows/ci-cd.yml | 108 ++++++++++++++++++++++++++++-------- 1 file changed, 85 insertions(+), 23 deletions(-) diff --git a/.github/workflows/ci-cd.yml b/.github/workflows/ci-cd.yml index ec9ecc55..4c38ffe4 100644 --- a/.github/workflows/ci-cd.yml +++ b/.github/workflows/ci-cd.yml @@ -14,13 +14,43 @@ on: env: + PYTHON_LATEST: 3.12 PROJECT_NAME: aiodocker + # For re-actors/checkout-python-sdist + dists-artifact-name: python-package-distributions + jobs: + build: + name: 📦 Build the distribution packages + runs-on: ubuntu-latest + steps: + - name: Checkout project + uses: actions/checkout@v4 + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: ${{ env.PYTHON_LATEST }} + cache: pip + - name: Install core libraries for build + run: python -Im pip install build + - name: Build artifacts + run: python -Im build + - name: Upload built artifacts for testing + uses: actions/upload-artifact@v3 + with: + name: ${{ env.dists-artifact-name }} + path: | + dist/${{ env.PROJECT_NAME }}*.tar.gz + dist/${{ env.PROJECT_NAME }}*.whl + retention-days: 15 + lint: name: Linter + needs: + - build runs-on: ubuntu-latest timeout-minutes: 5 outputs: @@ -28,10 +58,15 @@ jobs: steps: - name: Checkout uses: actions/checkout@v4 - - name: Setup Python 3.12 + - name: Download all the dists + uses: actions/download-artifact@v3 + with: + name: ${{ env.dists-artifact-name }} + path: dist + - name: Setup Python ${{ env.PYTHON_LATEST }} uses: actions/setup-python@v5 with: - python-version: '3.12' + python-version: ${{ env.PYTHON_LATEST }} cache: pip cache-dependency-path: | setup.py @@ -44,21 +79,18 @@ jobs: with: path: ~/.cache/pre-commit/ key: pre-commit-4|${{ env.pythonLocation }}|${{ hashFiles('.pre-commit-config.yaml') }} - - name: Generate dists - run: | - pip install build - python -m build - name: Run linters run: | make lint - name: Run twine checker run: | pip install twine - twine check dist/* + twine check --strict dist/* test: name: test - needs: [lint] + needs: + - build strategy: matrix: python-version: ['3.8', '3.9', '3.10', '3.11', '3.12'] @@ -117,30 +149,60 @@ jobs: uses: codecov/codecov-action@v4 with: token: ${{ secrets.CODECOV_TOKEN }} - deploy: - name: Deploy on PyPI - needs: [lint, test] + + publish: # Run only on creating release for new tag + name: 📦 Publish to PyPI + needs: + - build + - lint + - test runs-on: ubuntu-latest # Run only on pushing a tag if: github.event_name == 'push' && contains(github.ref, 'refs/tags/') + + permissions: + contents: write # IMPORTANT: mandatory for making GitHub Releases + id-token: write # IMPORTANT: mandatory for trusted publishing & sigstore + + environment: + name: pypi + url: >- + https://pypi.org/project/${{ env.PROJECT_NAME }}/${{ github.ref_name }} + steps: - - name: Checkout - uses: actions/checkout@v4 - - name: Setup Python 3.12 - uses: actions/setup-python@v5 + - name: Download all the dists + uses: actions/download-artifact@v3 with: - python-version: '3.12' - - name: Install dependencies - run: | - pip install build wheel - - name: Build wheels - run: | - python -m build + name: ${{ env.dists-artifact-name }} + path: dist + - name: Release uses: aio-libs/create-release@v1.6.5 with: changes_file: CHANGES.rst name: ${{ env.PROJECT_NAME }} github_token: ${{ secrets.GITHUB_TOKEN }} - pypi_token: ${{ secrets.PYPI_TOKEN }} head_line: "{version}\\s+\\({date}\\)\n====+\n?" + + - name: >- + Publish 🐍📦 to PyPI + uses: pypa/gh-action-pypi-publish@release/v1 + + - name: Sign the dists with Sigstore + uses: sigstore/gh-action-sigstore-python@v2.1.1 + with: + inputs: >- + ./dist/${{ env.PROJECT_NAME }}*.tar.gz + ./dist/${{ env.PROJECT_NAME }}*.whl + + - name: Upload artifact signatures to GitHub Release + # Confusingly, this action also supports updating releases, not + # just creating them. This is what we want here, since we've manually + # created the release above. + uses: softprops/action-gh-release@v2 + with: + # dist/ contains the built packages, which smoketest-artifacts/ + # contains the signatures and certificates. + files: dist/** + +... From 0342b492f94dd4f324b510c281b76d24ec7cd636 Mon Sep 17 00:00:00 2001 From: Sviatoslav Sydorenko Date: Tue, 14 May 2024 01:55:36 +0200 Subject: [PATCH 10/11] =?UTF-8?q?=F0=9F=A7=AA=F0=9F=90=9B=20Integrate=20al?= =?UTF-8?q?ls-green=20@=20GHA=20properly?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/ci-cd.yml | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci-cd.yml b/.github/workflows/ci-cd.yml index 4c38ffe4..8a40ffcd 100644 --- a/.github/workflows/ci-cd.yml +++ b/.github/workflows/ci-cd.yml @@ -135,12 +135,18 @@ jobs: with: key: unit-${{ matrix.python-version }}-${{ matrix.os }}-${{ matrix.cmd }}-${{ matrix.registry }} - check: - # All's good, upload coverage (also good for branch protection rule) - name: Check - needs: [test] + check: # This job does nothing and is only used for the branch protection + name: ✅ Ensure the required checks passing + if: always() + needs: + - lint + - test runs-on: ubuntu-latest steps: + - name: Decide whether the needed jobs succeeded or failed + uses: re-actors/alls-green@release/v1 + with: + jobs: ${{ toJSON(needs) }} - name: Checkout uses: actions/checkout@v4 with: From 98716a82304fb0c9d92ebe487329d2e5b6de11af Mon Sep 17 00:00:00 2001 From: Sviatoslav Sydorenko Date: Tue, 14 May 2024 02:03:56 +0200 Subject: [PATCH 11/11] =?UTF-8?q?=F0=9F=A7=AA=20Make=20PyPI=20publishing?= =?UTF-8?q?=20job=20dependent=20on=20the=20gate?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/ci-cd.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.github/workflows/ci-cd.yml b/.github/workflows/ci-cd.yml index 8a40ffcd..86d053bf 100644 --- a/.github/workflows/ci-cd.yml +++ b/.github/workflows/ci-cd.yml @@ -159,9 +159,7 @@ jobs: publish: # Run only on creating release for new tag name: 📦 Publish to PyPI needs: - - build - - lint - - test + - check runs-on: ubuntu-latest # Run only on pushing a tag if: github.event_name == 'push' && contains(github.ref, 'refs/tags/')