diff --git a/.codeclimate.yml b/.codeclimate.yml index 848a65d..cc7695f 100644 --- a/.codeclimate.yml +++ b/.codeclimate.yml @@ -10,6 +10,8 @@ engines: - reporter/components/ci.py fixme: enabled: true + markdownlint: + enabled: true pep8: enabled: true radon: diff --git a/Dockerfile b/Dockerfile index 5b1c72f..99b49f6 100644 --- a/Dockerfile +++ b/Dockerfile @@ -4,14 +4,12 @@ WORKDIR /usr/src/app RUN apk --update add git -COPY dev_requirements.txt /usr/src/app/ +COPY requirements.txt /usr/src/app/ RUN pip install --upgrade pip && \ - pip install -r dev_requirements.txt + pip install -r requirements.txt COPY . /usr/src/app -RUN python setup.py install - RUN adduser -u 9000 -D app RUN chown -R app:app /usr/src/app USER app diff --git a/Makefile b/Makefile index 685e56c..b71b0b9 100644 --- a/Makefile +++ b/Makefile @@ -4,18 +4,6 @@ IMAGE_NAME ?= codeclimate/python-test-reporter all: image -citest: - docker run \ - --rm \ - --env COVERAGE_FILE=/tmp/coverage.txt \ - --env CIRCLECI \ - --env CIRCLE_BRANCH \ - --env CIRCLE_SHA1 \ - --env CODECLIMATE_REPO_TOKEN \ - --entrypoint=/bin/sh \ - --volume /tmp:/tmp \ - $(IMAGE_NAME) -c 'python setup.py test' - image: docker build --tag $(IMAGE_NAME) . diff --git a/README.md b/README.md index 8cabfb4..fe1054b 100644 --- a/README.md +++ b/README.md @@ -1,31 +1,150 @@ # codeclimate-test-reporter -[![Code Climate](https://codeclimate.com/github/codeclimate/python-test-reporter/badges/gpa.svg)](https://codeclimate.com/github/codeclimate/ruby-test-reporter) +[![Code Climate][cc-badge]][cc-repo] + +[cc-badge]: https://codeclimate.com/github/codeclimate/python-test-reporter/badges/gpa.svg +[cc-repo]: https://codeclimate.com/github/codeclimate/ruby-test-reporter Collects test coverage data from your Python test suite and sends it to Code Climate's hosted, automated code review service. Code Climate - [https://codeclimate.com](https://codeclimate.com) -# Important FYIs +## Usage + +The `codeclimate-test-reporter` is compatible with [coverage.py][] coverage +reports. By default, coverage.py will generate a `.coverage` file in the current +directory. `codeclimate-test-reporter`, run without arguments, will +look for a coverage report at this default location. + +```console +$ codeclimate-test-reporter +Submitting payload to https://codeclimate.com +``` + +### Alternate .coverage file location + +If you configure coverage.py to generate a coverage report at an alternate +location, pass that to the `codeclimate-test-reporter`: + +```console +$ codeclimate-test-reporter --file ./alternate/location/.coverage +Submitting payload to https://codeclimate.com +``` + +[coverage.py]: https://coverage.readthedocs.org/ + +### CI Setup + +When adding the `codeclimate-test-reporter` to your CI environment, you should +execute the reporter after your test suite and coverage run: + +```yaml +test: + - python setup.py test + - codeclimate-test-reporter +``` + +### Configuration + +The `codeclimate-test-reporter` requires a repo token from [codeclimate.com][], +so if you don't have one, the first step is to signup and configure your repo. +Then: + +You can place the repo token in the environment under the key +`CODECLIMATE_REPO_TOKEN` or pass the token as a CLI argument: + +```console +$ CODECLIMATE_REPO_TOKEN=[token] codeclimate-test-reporter +Submitting payload to https://codeclimate.com +``` + +```console +$ codeclimate-test-reporter --token [token] +Submitting payload to https://codeclimate.com +``` + +We recommend configuring the repo token in the environment through your CI +settings which will hide the value during runs. The token should be considered a +scoped password. Anyone with the token can submit test coverage data to your +Code Climate repo. -Across the many different testing frameworks, setups, and environments, there are lots of variables at play. Before setting up test coverage, it's important to understand what we do and do not currently support: +[codeclimate.com]: https://codeclimate.com -* **Default branch only:** We only support test coverage for your [default branch](http://docs.codeclimate.com/article/151-glossary-default-branch). Be sure to check out this branch before running your tests. -* **Single payload:** We currently only support a single test coverage payload per commit. If you run your tests in multiple steps, or via parallel tests, Code Climate will only process the first payload that we receive. If you are using a CI, be sure to check if you are running your tests in a parallel mode. +## Installation - **Note:** If you've configured Code Climate to analyze multiple languages in the same repository (e.g., Python and JavaScript), we can nonetheless only process test coverage information for one of these languages. We'll process the first payload that we receive. -* **Invalid File Paths:** By default, our test reporters expect your application to exist at the root of your repository. If this is not the case, the file paths in your test coverage payload will not match the file paths that Code Climate expects. +You can install the codeclimate-test-reporter using pip: + +```console +$ pip install codeclimate-test-reporter +Successfully installed codeclimate-test-reporter-[version] +``` + +Or you can add the reporter to your `requirements.txt` file and install it along +with other project dependencies: + +```txt +# requirements.txt +codeclimate-test-reporter +``` + +```console +$ pip install -r requirements.txt +Successfully installed codeclimate-test-reporter-[version] +``` + +## Important FYIs + +Across the many different testing frameworks, setups, and environments, there +are lots of variables at play. Before setting up test coverage, it's important +to understand what we do and do not currently support: + +* **Default branch only:** We only support test coverage for your default + branch. Be sure to check out this branch before running your tests. + +* **Single payload:** We currently only support a single test coverage payload + per commit. If you run your tests in multiple steps, or via parallel tests, + Code Climate will only process the first payload that we receive. If you are + using a CI, be sure to check if you are running your tests in a parallel mode. + + **Note:** If you've configured Code Climate to analyze multiple languages in + the same repository (e.g., Python and JavaScript), we can nonetheless only + process test coverage information for one of these languages. We'll process + the first payload that we receive. + +* **Invalid File Paths:** By default, our test reporters expect your application + to exist at the root of your repository. If this is not the case, the file + paths in your test coverage payload will not match the file paths that Code + Climate expects. ## Troubleshooting -If you're having trouble setting up or working with our test coverage feature, [see our detailed help doc](http://docs.codeclimate.com/article/220-help-im-having-trouble-with-test-coverage), which covers the most common issues encountered. +If you're having trouble setting up or working with our test coverage feature, +see our detailed [help doc][], which covers the most common issues encountered. + +[help doc]: http://docs.codeclimate.com/article/220-help-im-having-trouble-with-test-coverage + +If the issue persists, feel free to [open up an issue][issue] here or contact +help with debug information from the reporter: + +[issue]: https://github.com/codeclimate/python-test-reporter/issues/new + +```console +$ codeclimate-test-reporter --debug +codeclimate-test-reporter [version] +Requests 2.9.1 +Python 3.5.1 (default, Jan 22 2016, 08:54:32) +[GCC 4.2.1 Compatible Apple LLVM 7.0.2 (clang-700.1.81)] +/usr/local/opt/python3/bin/python3.5 +Darwin 15.4.0 +``` ## Contributions -Patches, bug fixes, feature requests, and pull requests are welcome on the -GitHub page for this project: [https://github.com/codeclimate/python-test-reporter](https://github.com/codeclimate/python-test-reporter) +Patches, bug fixes, feature requests, and pull requests are welcome. ## Copyright -See LICENSE.txt +See [LICENSE.txt][license] + +[license]: https://github.com/codeclimate/python-test-reporter/blob/master/LICENSE.txt diff --git a/circle.yml b/circle.yml index 84e1d0f..5263e4f 100644 --- a/circle.yml +++ b/circle.yml @@ -1,13 +1,13 @@ machine: - services: - - docker + python: + version: 3.5.1 dependencies: override: - - pip install codeclimate-test-reporter - - make image + - pip install -r requirements.txt + - python setup.py install test: override: - - make citest - - codeclimate-test-reporter --file /tmp/coverage.txt + - python setup.py test + - codeclimate-test-reporter diff --git a/reporter/VERSION b/reporter/VERSION index bcab45a..81340c7 100644 --- a/reporter/VERSION +++ b/reporter/VERSION @@ -1 +1 @@ -0.0.3 +0.0.4 diff --git a/reporter/components/api_client.py b/reporter/components/api_client.py index 58208ec..a2e574b 100644 --- a/reporter/components/api_client.py +++ b/reporter/components/api_client.py @@ -5,7 +5,7 @@ class ApiClient: def __init__(self, host=None, timeout=5): - self.host = host or self.__default_host() + self.host = host or self.__default_host().rstrip("/") self.timeout = timeout def post(self, payload): @@ -22,4 +22,4 @@ def post(self, payload): return response def __default_host(self): - return os.environ.get("CODECLIMATE_HOST", "https://codeclimate.com") + return os.environ.get("CODECLIMATE_API_HOST", "https://codeclimate.com") diff --git a/reporter/components/ci.py b/reporter/components/ci.py index 9d071cb..9c3a7f4 100644 --- a/reporter/components/ci.py +++ b/reporter/components/ci.py @@ -12,7 +12,7 @@ def predicate(self, service): return service["matcher"](self.env) def data(self): - service = next(filter(self.predicate, self.__services()), None) + service = next(iter(filter(self.predicate, self.__services())), None) if service: return service["data"](self.env) diff --git a/reporter/components/reporter.py b/reporter/components/reporter.py index c52ba50..f9c2509 100644 --- a/reporter/components/reporter.py +++ b/reporter/components/reporter.py @@ -8,6 +8,10 @@ from ..components.payload_validator import PayloadValidator +class CoverageFileNotFound(Exception): + pass + + class Reporter: def __init__(self, args): self.args = args @@ -21,6 +25,10 @@ def run(self): """ + if not os.path.isfile(self.args.file): + message = "Coverage file `" + self.args.file + "` file not found. " + raise CoverageFileNotFound(message) + xml_filepath = self.__create_xml_report(self.args.file) formatter = Formatter(xml_filepath) payload = formatter.payload() diff --git a/reporter/components/runner.py b/reporter/components/runner.py index 4f9afe0..6fdcc36 100644 --- a/reporter/components/runner.py +++ b/reporter/components/runner.py @@ -17,7 +17,7 @@ from ..__init__ import __version__ as reporter_version from ..components.argument_parser import ArgumentParser from ..components.payload_validator import InvalidPayload -from ..components.reporter import Reporter +from ..components.reporter import CoverageFileNotFound, Reporter class Runner: @@ -45,11 +45,12 @@ def run(self, args=sys.argv[1:], env=os.environ): reporter = Reporter(parsed_args) exit_status = reporter.run() return exit_status - except CoverageException as e: + except CoverageFileNotFound as e: return self.__handle_error( - "Coverage file `" + parsed_args.file + "` file not found. " - "Use --file to specifiy an alternate location." + str(e) + "\nUse --file to specifiy an alternate location." ) + except CoverageException as e: + return self.__handle_error(str(e), support=True) except InvalidPayload as e: return self.__handle_error("Invalid Payload: " + str(e), support=True) except requests.exceptions.HTTPError as e: diff --git a/reporter/tests/fixtures/coverage.txt b/reporter/tests/fixtures/coverage.txt index 91f2b69..26f9607 100644 --- a/reporter/tests/fixtures/coverage.txt +++ b/reporter/tests/fixtures/coverage.txt @@ -1 +1 @@ -!coverage.py: This is a private format, don't read it directly!{"lines": {"/Users/devonblandin/cc/python-test-reporter/reporter/tests/fixtures/source.py": [4, 5, 6, 7, 9, 10, 12, 15, 16]}} +!coverage.py: This is a private format, don't read it directly!{"lines": {"reporter/tests/fixtures/source.py": [4, 5, 6, 7, 9, 10, 12, 15, 16]}} diff --git a/reporter/tests/fixtures/coverage.xml b/reporter/tests/fixtures/coverage.xml index 91dae9b..3263aed 100644 --- a/reporter/tests/fixtures/coverage.xml +++ b/reporter/tests/fixtures/coverage.xml @@ -8,7 +8,7 @@ - + diff --git a/dev_requirements.txt b/requirements.txt similarity index 100% rename from dev_requirements.txt rename to requirements.txt diff --git a/setup.py b/setup.py index 53e6ad3..9107c61 100644 --- a/setup.py +++ b/setup.py @@ -28,6 +28,8 @@ def run(self): url="http://github.com/codeclimate/python-test-reporter", author="Code Climate", author_email="hello@codeclimate.com", + maintainer="Code Climate", + maintainer_email="hello@codeclimate.com", license="MIT", packages=find_packages(exclude=["tests"]), zip_safe=False,