coverallsapp / coverallsapp/github-action

422 Client Error: Unprocessable Entity

Open
#87 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
No language data
Stars
520
Forks
78
PR merge metrics
No merged PRs in 30d

Description

Okay, I have the infamous issue. I've spent a few hours trying to make this work and I feel like I'm very close to it. But now I've got this 422 Client Error. Nothing that I could find online actually helped. So maybe it's an actual new bug (or maybe I'm just blind).

I've tried many different ways to do it.

This version is (mostly) a copy-paste of https://coveralls-python.readthedocs.io/en/latest/usage/configuration.html#github-actions-support

As we can see here, on a non-debug run: https://github.com/Mulugruntz/aiosubprocess/runs/2657803772?check_suite_focus=true#step:9:1

coveralls.exception.CoverallsException: Could not submit coverage: 422 Client Error: Unprocessable Entity for url: https://coveralls.io/api/v1/jobs
Error: Process completed with exit code 1.

Whereas on a debug run: https://github.com/Mulugruntz/aiosubprocess/runs/2657448470?check_suite_focus=true#step:9:1

Run coveralls debug --service=github
  coveralls debug --service=github
  shell: /usr/bin/bash -e {0}
  env:
    pythonLocation: /opt/hostedtoolcache/Python/3.6.13/x64
    COVERALLS_REPO_TOKEN: ***
    GITHUB_TOKEN: ***
    COVERALLS_FLAG_NAME: run-ubuntu-latest-py3.6
    COVERALLS_PARALLEL: true
Missing .coveralls.yml file. Using only env variables.
Testing coveralls-python...
{"source_files": [{"name": "aiosubprocess.py", "source": "\"\"\"Boilerplate for asyncio applications\"\"\"\n\nimport asyncio\nimport logging\nfrom asyncio import AbstractEventLoop\nfrom asyncio import create_subprocess_exec, create_subprocess_shell\nfrom asyncio.subprocess import PIPE, Process as asyncioProcess\nfrom functools import partial\nfrom itertools import count\nfrom typing import Callable, Tuple, Optional, Coroutine, Any\n\n__all__ = [\"Process\"]\n__version__ = \"2021.05.24\"\nlogger = logging.getLogger(\"aiosubprocess\")\n\nSLEEP_RESOLUTION = 0.1\n\n\nclass Process:\n    name_count = count()\n\n    def __init__(\n        self,\n        *command: str,\n        loop: Optional[AbstractEventLoop] = None,\n        name: Optional[str] = None,\n        expected_returncode: int = 0,\n        stdout: Callable[[str], None] = logger.info,\n        stderr: Callable[[str], None] = logger.error,\n        with_prefix: bool = True,\n        sleep_resolution: float = SLEEP_RESOLUTION,\n    ) -> None:\n        \"\"\"An async subprocess that keeps on getting stdout and stderr.\n\n        :param command: The shell command.\n        :param loop: An asyncio event loop.\n        :param name: Optional name of the subprocess (useful when logging).\n                    If not provided, will assign one automatically.\n        :param expected_returncode: Which error code is considered a success?\n        :param stdout: A callback called for every line of stdout.\n                    Useful for printing and logging.\n        :param stderr: A callback called for every line of stderr.\n                    Useful for printing and logging.\n        :param with_prefix: Should a prefix (based on `name`) be added before\n                    each stdout/stderr?\n        :param sleep_resolution: The minimum time resolution at which stdout/stderr\n                    will be checked. This is not guaranteed, as there might be\n                    long blocking operations somewhere else in the loop.\n        \"\"\"\n        self._run_command: Tuple[str, ...] = command\n        self.loop: AbstractEventLoop = loop or asyncio.get_event_loop()\n        self.child: Optional[asyncioProcess] = None\n        self.name = name or f\"AIO Subprocess-{next(Process.name_count)}\"\n        self.expected_returncode = expected_returncode\n        self.__prefix = f\"[{self.name}] \"\n        self.__stdout = stdout\n        self.__stderr = stderr\n        self.with_prefix = with_prefix\n        self.sleep_resolution = sleep_resolution\n\n    async def exec(self) -> bool:\n        return await self._run(partial(create_subprocess_exec, *self._run_command))\n\n    async def shell(self) -> bool:\n        return await self._run(\n            partial(create_subprocess_shell, \" \".join(self._run_command))\n        )\n\n    async def _run(\n        self, create_subprocess_function: Callable[..., Coroutine[Any, Any, asyncioProcess]]\n    ) -> bool:\n        logger.info(\"%sAbout to start %s\", self.__prefix, \" \".join(self._run_command))\n        self.child = await create_subprocess_function(stdout=PIPE, stderr=PIPE)\n        while True:\n            retcode = self.child.returncode\n            if retcode is not None:  # Process finished\n                await self._check_io()\n                break\n            else:\n                await asyncio.sleep(self.sleep_resolution)\n            await self._check_io()\n\n        if retcode != self.expected_returncode:\n            logger.error(\n                \"%sError! The subprocess terminated with a non-0 return code: %s\",\n                self.__prefix,\n                retcode,\n            )\n            return False\n\n        logger.info(\n            \"%sSuccess! The subprocess completed.\",\n            self.__prefix,\n        )\n        return True\n\n    async def _check_io(self):\n        out = asyncio.ensure_future(self._pipe_stdout(), loop=self.loop)\n        err = asyncio.ensure_future(self._pipe_stderr(), loop=self.loop)\n        await asyncio.gather(out, err, loop=self.loop)\n\n    async def _pipe_stdout(self):\n        while True:\n            output = (\n                (await self.child.stdout.readline())\n                .decode(errors=\"backslashreplace\")\n                .rstrip()\n            )\n            if not output:\n                break\n            if self.with_prefix:\n                self.__stdout(f\"{self.__prefix}{output}\")\n            else:\n                self.__stdout(output)\n\n    async def _pipe_stderr(self):\n        while True:\n            error = (\n                (await self.child.stderr.readline())\n                .decode(errors=\"backslashreplace\")\n                .rstrip()\n            )\n            if not error:\n                break\n            if self.with_prefix:\n                self.__stderr(f\"{self.__prefix}{error}\")\n            else:\n                self.__stderr(error)\n", "coverage": [null, null, 1, 1, 1, 1, 1, 1, 1, 1, null, 1, 1, 1, null, 1, null, null, 1, 1, null, 1, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, null, 1, 1, null, 1, 1, null, null, null, 1, null, null, 1, 1, 1, 1, 1, 1, 1, null, 1, 1, null, 1, 1, null, null, null, null, 1, null, 1, null, null, null, 1, null, 1, 1, 1, 1, null, 1, 1, 1, null, null, null, null, 1, 1, 1, 1, null, 1, null, 1, 1, 1, null, null, null, null, 1, 1, 1, 1, null, 1], "branches": [76, 0, 77, 1, 76, 0, 80, 1, 83, 0, 84, 1, 83, 0, 91, 1, 109, 0, 110, 1, 109, 0, 111, 1, 111, 0, 112, 1, 111, 0, 114, 1, 123, 0, 124, 1, 123, 0, 125, 1, 125, 0, 126, 1, 125, 0, 128, 1]}], "git": {"branch": "master", "head": {"id": "9b69ec3dcc5c922d637d95e8a407d6268cc7e501", "author_name": "Samuel Giffard", "author_email": "samuel@giffard.co", "committer_name": "Samuel Giffard", "committer_email": "samuel@giffard.co", "message": "(WIP) Fixing Code coverage (coveralls.io)."}, "remotes": [{"name": "origin", "url": "https://github.com/Mulugruntz/aiosubprocess"}]}, "service_job_id": null, "repo_token": "[secure]", "service_name": "github", "service_number": "871918197", "parallel": true, "flag_name": "run-ubuntu-latest-py3.6", "config_file": ".coveragerc"}
==
Reporting 1 files
==

aiosubprocess.py - 64/128

All the data seems to be there. But it won't proceed if I remove the debug command.

Any idea?

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the GitHub Actions workflow that runs coveralls and the mentioned .coveralls.yml configuration, comparing the debug and non-debug invocations. Reproduce the 422 submission against the reported environment and determine the missing or invalid input; done means the normal coverage submission succeeds without the debug command.

Written by the indexing model from the issue text.

Assessment

Tech stack
github-actions, python
Domain
ci-cd, testing-qa
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Needs clarification
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.