From dfb8cabe3019bfa4a22398996975bf14f22b0bf5 Mon Sep 17 00:00:00 2001 From: Nat Noordanus Date: Sat, 7 Oct 2023 20:11:59 +0200 Subject: [PATCH] Show helpful error message when task executable is not on the path --- .github/workflows/ci.yml | 74 +++++++++++++++++++++++++++---- poethepoet/executor/base.py | 28 +++++++----- poethepoet/executor/poetry.py | 10 +++++ poethepoet/executor/virtualenv.py | 9 ++++ 4 files changed, 103 insertions(+), 18 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 21cdb576..3cd35a10 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -61,8 +61,8 @@ jobs: - name: Run tests run: poetry run pytest -v - build-release: - name: Build and release + build: + name: Build distribution runs-on: ubuntu-latest needs: [code-quality, run-tests] steps: @@ -78,14 +78,39 @@ jobs: - name: Build package run: poetry build - - name: Publish package to PyPI + - name: Store the distribution packages if: github.event_name == 'push' && startsWith(github.event.ref, 'refs/tags') - run: poetry publish -n - env: - POETRY_PYPI_TOKEN_PYPI: ${{ secrets.pypi }} + uses: actions/upload-artifact@v3 + with: + name: python-package-distributions + path: dist/ - - name: Update homebrew formula - # if: github.event_name == 'push' && startsWith(github.event.ref, 'refs/tags') + pypi-publish: + name: Upload release to PyPI + if: github.event_name == 'push' && startsWith(github.event.ref, 'refs/tags') + needs: [build] + runs-on: ubuntu-latest + environment: + name: pypi + url: https://pypi.org/p/poethepoet + permissions: + id-token: write + steps: + - name: Download all the dists + uses: actions/download-artifact@v3 + with: + name: python-package-distributions + path: dist/ + + - name: Publish distribution 📦 to PyPI + uses: pypa/gh-action-pypi-publish@release/v1 + + homebrew-publish: + name: Upload homebrew formula + needs: [pypi-publish] + runs-on: ubuntu-latest + steps: + - name: Trigger update of homebrew formula run: > curl -L -X POST -H "Accept: application/vnd.github+json" @@ -93,3 +118,36 @@ jobs: -H "X-GitHub-Api-Version: 2022-11-28" https://api.github.com/repos/nat-n/homebrew-poethepoet/actions/workflows/71211730/dispatches -d '{"ref":"main", "inputs":{}}' + + github-release: + name: >- + Sign the Python 🐍 distribution 📦 with Sigstore and upload them to GitHub Release + needs: [pypi-publish] + runs-on: ubuntu-latest + + permissions: + contents: write + id-token: write + + steps: + - name: Download all the dists + uses: actions/download-artifact@v3 + with: + name: python-package-distributions + path: dist/ + - name: Sign the dists with Sigstore + uses: sigstore/gh-action-sigstore-python@v1.2.3 + with: + inputs: >- + ./dist/*.tar.gz + ./dist/*.whl + - name: Upload artifact signatures to GitHub Release + env: + GITHUB_TOKEN: ${{ github.token }} + # Upload to GitHub Release using the `gh` CLI. + # `dist/` contains the built packages, and the + # sigstore-produced signatures and certificates. + run: >- + gh release upload + '${{ github.ref_name }}' dist/** + --repo '${{ github.repository }}' diff --git a/poethepoet/executor/base.py b/poethepoet/executor/base.py index e9d4b911..1594da86 100644 --- a/poethepoet/executor/base.py +++ b/poethepoet/executor/base.py @@ -151,16 +151,24 @@ def _execute_cmd( process. Using exec supports fewer options, and doesn't work on windows. """ - if use_exec: - if input: - raise ExecutionError("Cannot exec task that requires input!") - if shell: - raise ExecutionError("Cannot exec task that requires shell!") - if not self._is_windows: - # execvpe doesn't work properly on windows so we just don't go there - return self._exec(cmd, env=env) - - return self._exec_via_subproc(cmd, input=input, env=env, shell=shell) + try: + if use_exec: + if input: + raise ExecutionError("Cannot exec task that requires input!") + if shell: + raise ExecutionError("Cannot exec task that requires shell!") + if not self._is_windows: + # execvpe doesn't work properly on windows so we just don't go there + return self._exec(cmd, env=env) + + return self._exec_via_subproc(cmd, input=input, env=env, shell=shell) + except FileNotFoundError as error: + return self._handle_file_not_found(cmd, error) + + def _handle_file_not_found( + self, cmd: Sequence[str], error: FileNotFoundError + ) -> int: + raise PoeException(f"executable {cmd[0]!r} could not be found") from error def _exec( self, diff --git a/poethepoet/executor/poetry.py b/poethepoet/executor/poetry.py index 3c552f59..474b8467 100644 --- a/poethepoet/executor/poetry.py +++ b/poethepoet/executor/poetry.py @@ -2,6 +2,7 @@ from pathlib import Path from typing import Dict, Optional, Sequence, Type +from ..exceptions import PoeException from .base import PoeExecutor @@ -46,6 +47,15 @@ def execute( use_exec=use_exec, ) + def _handle_file_not_found( + self, cmd: Sequence[str], error: FileNotFoundError + ) -> int: + poetry_env = self._get_poetry_virtualenv() + error_context = f" using virtualenv {poetry_env!r}" if poetry_env else "" + raise PoeException( + f"executable {cmd[0]!r} could not be found{error_context}" + ) from error + def _get_poetry_virtualenv(self, force: bool = True): """ Ask poetry where it put the virtualenv for this project. diff --git a/poethepoet/executor/virtualenv.py b/poethepoet/executor/virtualenv.py index bd03d109..f6a3f45e 100644 --- a/poethepoet/executor/virtualenv.py +++ b/poethepoet/executor/virtualenv.py @@ -30,6 +30,15 @@ def execute( use_exec=use_exec, ) + def _handle_file_not_found( + self, cmd: Sequence[str], error: FileNotFoundError + ) -> int: + venv = self._resolve_virtualenv() + error_context = f" using virtualenv {str(venv.path)!r}" if venv else "" + raise PoeException( + f"executable {cmd[0]!r} could not be found{error_context}" + ) from error + def _resolve_virtualenv(self) -> "Virtualenv": from ..virtualenv import Virtualenv