diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 00000000..491deae0 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,7 @@ +version: 2 +updates: +- package-ecosystem: pip + directory: "/" + schedule: + interval: daily + open-pull-requests-limit: 10 diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml index 05b442a9..154d8a07 100644 --- a/.github/workflows/build-test.yml +++ b/.github/workflows/build-test.yml @@ -15,14 +15,14 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: [3.5, 3.6, 3.7] + python-version: ["3.7", "3.8", "3.9", "3.10", "3.11"] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 with: ref: ${{ github.event.pull_request.head.sha }} - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2 + uses: actions/setup-python@v3 with: python-version: ${{ matrix.python-version }} - name: Install dependencies diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 2e6150f9..00000000 --- a/.travis.yml +++ /dev/null @@ -1,39 +0,0 @@ -language: python -# cache: pip -notifications: - email: false -dist: xenial -sudo: false -python: - - 2.7 - - pypy - - 3.4 - - 3.5 - - 3.6 - - 3.7 - - pypy3 -matrix: - include: - - python: 2.7 - dist: trusty - virtualenv: - system_site_packages: true - addons: - apt: - packages: - - python-requests - - python-coverage - - python-mock - - python: 3.7 - dist: xenial - script: make format - -install: - - pip install -r tests/requirements.txt - - python setup.py install -script: - - make reinstall - - make test - # - pytest tests/test.py --cov=codecov -after_success: - - codecov diff --git a/CHANGELOG.md b/CHANGELOG.md index e77e6c84..d5eb65e5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,43 @@ +### `2.1.12` + +#### Fixes +- #322 Add Cirrus CI + +#### Dependencies and Misc +- #311 Bump coverage from 5.3 to 5.3.1 +- #312 Bump coverage from 5.3.1 to 5.4 +- #314 Bump coverage from 5.4 to 5.5 +- #320 Upgrade to GitHub-native Dependabot + +### `2.1.11` + +#### Fixes +- #305 Added option to disable printing of gcov-out +- #308 Handle exceptions that don't have a returncode + +#### Dependencies and Misc +- #301 Update to Python 3.9 + +### `2.1.10` + +#### Fixes +- [#148](https://github.com/codecov/codecov-python/pull/148) Output elapsed time with S3 upload +- [#153](https://github.com/codecov/codecov-python/pull/153) Improve error reporting in the "try_run" function and correctly include original command output in the error message +- [#295](https://github.com/codecov/codecov-python/pull/295) Added sleep between upload retries. +- [#297](https://github.com/codecov/codecov-python/pull/297) Ignore emacs lisp files +- [#298](https://github.com/codecov/codecov-python/pull/298) Fix error try_to_run using | without shell=True (fix #284) + +#### Dependencies and Misc +- [#290](https://github.com/codecov/codecov-python/pull/290) Bump coverage from 4.5.4 to 5.2.1 +- [#291](https://github.com/codecov/codecov-python/pull/291) Update python versions +- [#292](https://github.com/codecov/codecov-python/pull/292) Add license scan report and status +- [#294](https://github.com/codecov/codecov-python/pull/294) Update README with accurate links +- [#296](https://github.com/codecov/codecov-python/pull/296) Bump coverage from 5.2.1 to 5.3 + +### `2.1.9` + +- [#289](https://github.com/codecov/codecov-python/pull/289)Remove token restriction as it is changed server-side + ### `2.1.8` - [#285](https://github.com/codecov/codecov-python/pull/285)Add support for CODECOV_FLAGS diff --git a/Makefile b/Makefile index 7eae04f4..fae2de1e 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,7 @@ test: py.test tests/test.py --cov=codecov format: - black . --check + black . -v -t py38 --check --diff compare: hub compare $(shell git tag --sort=refname | tail -1)...master diff --git a/README.md b/README.md index ab54a802..27531afd 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,20 @@ -Codecov Global Python Uploader ![PyPI](https://img.shields.io/pypi/v/codecov) [![codecov.io](https://codecov.io/github/codecov/codecov-python/coverage.svg?branch=master)](https://codecov.io/github/codecov/codecov-python) [![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black) +🚨🚨 Deprecation Notice 🚨🚨 + +This uploader is being deprecated by the Codecov team. We recommend migrating to our [new uploader](https://docs.codecov.com/docs/codecov-uploader) as soon as possible to prevent any lapses in coverage. [The new uploader is open source](https://github.com/codecov/uploader), and we highly encourage submitting Issues and Pull Requests. + +You can visit our [migration guide](https://docs.codecov.com/docs/deprecated-uploader-migration-guide#python-uploader) for help moving to our new uploader, and our blog post to learn more about our [deprecation plan](https://about.codecov.io/blog/codecov-uploader-deprecation-plan/) + +**On February 1, 2022 this uploader will be completely deprecated and will no longer be able to upload coverage to Codecov.** + +# Codecov Global Python Uploader + +[![codecov.io](https://codecov.io/github/codecov/codecov-python/coverage.svg?branch=master)](https://codecov.io/github/codecov/codecov-python) +![PyPI](https://img.shields.io/pypi/v/codecov) +[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black) +[![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2Fcodecov%2Fcodecov-python.svg?type=shield)](https://app.fossa.com/projects/git%2Bgithub.com%2Fcodecov%2Fcodecov-python?ref=badge_shield) ======= -| [https://codecov.io/][1] | [https://community.codecov.io/][2] | [@codecov][3] | [hello@codecov.io][4] | -| ------------------------ | ---------------------------------- | ------------- | --------------------- | +| [Support][1] | [Documentation][2] | [Community Boards][3] | [Twitter][4] | +| ------------ | ------------------ | --------------------- | ------------ | Find coverage reports for all the [languages below](#languages), gather them and submit them to Codecov. @@ -74,7 +87,7 @@ after_success: ## CI Providers -| Company | Supported | Token Required | +| Company | Supported | Token Required | | ----------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------- | | [AppVeyor](https://www.appveyor.com/) | Yes [![Build status](https://ci.appveyor.com/api/projects/status/sw18lsj7786bw806/branch/master?svg=true)](https://ci.appveyor.com/project/stevepeak/codecov-python/branch/master) | Private only | | [Bamboo](https://www.atlassian.com/software/bamboo) | `coming soon` | | @@ -91,6 +104,7 @@ after_success: | [Solano Labs](https://www.solanolabs.com/) | `coming soon` | | | [Travis CI](https://travis-ci.org/) | Yes [![Build Status](https://secure.travis-ci.org/codecov/codecov-python.svg?branch=master)](https://travis-ci.org/codecov/codecov-python) | Private only | | [Wercker](http://wercker.com/) | Yes | Public & Private | +| [Cirrus CI](https://cirrus-ci.org/) | Yes | Private only | | Git / Mercurial | Yes (as a fallback) | Public & Private | @@ -100,11 +114,15 @@ If you're seeing an **HTTP 400 error when uploading reports to S3**, make sure y -[1]: https://codecov.io/ -[2]: https://community.codecov.io/ -[3]: https://twitter.com/codecov -[4]: mailto:hello@codecov.io +[1]: https://codecov.io/support/ +[2]: https://docs.codecov.io/ +[3]: https://community.codecov.io/ +[4]: https://twitter.com/codecov ## Copyright -> Copyright 2014-2020 codecov +> Copyright 2014-2022 codecov + + +## License +[![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2Fcodecov%2Fcodecov-python.svg?type=large)](https://app.fossa.com/projects/git%2Bgithub.com%2Fcodecov%2Fcodecov-python?ref=badge_large) diff --git a/codecov/__init__.py b/codecov/__init__.py index be62793d..99109edf 100644 --- a/codecov/__init__.py +++ b/codecov/__init__.py @@ -88,6 +88,7 @@ def sanitize_arg(replacement, arg): r"(/\.coverage.*)|" r"(\.coveragerc)|" r"(\.egg)|" + r"(\.el)|" r"(\.gif)|" r"(\.ini)|" r"(\.less)|" @@ -213,7 +214,7 @@ def check_output(cmd, **popen_args): process = Popen(cmd, stdout=PIPE, **popen_args) output, _ = process.communicate() if process.returncode: - raise CalledProcessError(process.returncode, cmd) + raise CalledProcessError(process.returncode, cmd, output) else: assert process.returncode == 0 return output.decode("utf-8") @@ -223,7 +224,14 @@ def try_to_run(cmd, shell=False, cwd=None): try: return check_output(cmd, shell=shell, cwd=cwd) except Exception as e: - write(" Error running `%s`: %s" % (cmd, e or str(e))) + write( + " Error running `%s`: returncode=%s, output=%s" + % ( + cmd, + str(getattr(e, "returncode", None)), + str(getattr(e, "output", str(e))), + ) + ) return None @@ -287,11 +295,14 @@ def generate_toc(root): return str(res).strip() or "" -def retry_upload(url, request_method, retries=3, break_codes=(200,), **kwargs): - for _ in range(retries): +def retry_upload(url, request_method, retries=5, break_codes=(200,), **kwargs): + wait_seconds = 2 + for i in range(retries): res = request_method(url, **kwargs) if res.status_code in break_codes: return res + write(" Retrying {0}/{1} in {2}s..".format(i + 1, retries, wait_seconds)) + sleep(wait_seconds) return res @@ -318,7 +329,7 @@ def main(*argv, **kwargs): "--token", "-t", default=os.getenv("CODECOV_TOKEN"), - help="Private repository token or @filename for file containing the token. Defaults to $CODECOV_TOKEN. Not required for public repositories on Travis CI, CircleCI and AppVeyor", + help="Private repository token or @filename for file containing the token. Defaults to $CODECOV_TOKEN. Not required for public repositories on Travis CI, CircleCI, AppVeyor and CirrusCI", ) basics.add_argument( "--file", @@ -369,6 +380,9 @@ def main(*argv, **kwargs): gcov.add_argument( "--gcov-exec", default="gcov", help="gcov executable to run. Defaults to 'gcov'" ) + gcov.add_argument( + "--no-gcov-out", action="store_true", default=False, help="Disable gcov output" + ) gcov.add_argument("--gcov-args", default="", help="extra arguments to pass to gcov") advanced = parser.add_argument_group( @@ -409,7 +423,7 @@ def main(*argv, **kwargs): advanced.add_argument("--tag", default=None, help="Git tag") advanced.add_argument( "--tries", - default=3, + default=5, type=int, help="Specify the total number of attempts to make when uploading coverage report", ) @@ -831,6 +845,25 @@ def main(*argv, **kwargs): write(" GitHub Actions CI Detected") + # --------- + # Cirrus CI + # --------- + elif os.getenv("CIRRUS_CI"): + # https://cirrus-ci.org/guide/writing-tasks/#environment-variables + query.update( + dict( + service="cirrus-ci", + slug=os.getenv("CIRRUS_REPO_FULL_NAME"), + branch=os.getenv("CIRRUS_BRANCH"), + pr=os.getenv("CIRRUS_PR"), + commit=os.getenv("CIRRUS_CHANGE_IN_REPO"), + build=os.getenv("CIRRUS_BUILD_ID"), + build_url="https://cirrus-ci.com/task/" + os.getenv("CIRRUS_TASK_ID"), + job=os.getenv("CIRRUS_TASK_NAME"), + ) + ) + write(" Cirrus CI Detected") + else: query.update( dict( @@ -862,7 +895,7 @@ def main(*argv, **kwargs): try: query["commit"] = try_to_run( ["git", "rev-parse", "HEAD"] - ) or try_to_run(["hg", "id", "-i", "--debug", "|", "tr", "-d", "'+'"]) + ) or try_to_run(["hg", "log", "-r", ".", "-T", "{node}"]) write(" -> Got sha from git/hg") except: # pragma: no cover @@ -953,8 +986,6 @@ def main(*argv, **kwargs): if _slug: query["slug"] = _slug.groups()[1] - assert query.get("job") or query.get("token"), "Missing repository upload token" - # Processing gcov # --------------- if "gcov" in codecov.disable: @@ -977,8 +1008,11 @@ def main(*argv, **kwargs): if codecov.gcov_args: cmd.append(sanitize_arg("", codecov.gcov_args or "")) cmd.append(path) - write(" Executing gcov (%s)" % cmd) - write(try_to_run(cmd)) + if not codecov.no_gcov_out: + write(" Executing gcov (%s)" % cmd) + gcov_out = try_to_run(cmd) + if not codecov.no_gcov_out: + write(gcov_out) # Collect Reports # --------------- @@ -1145,6 +1179,7 @@ def main(*argv, **kwargs): ) s3.raise_for_status() assert s3.status_code == 200 + write(" Uploading to S3 took %s" % s3.elapsed) write(" " + result) success = True @@ -1189,13 +1224,6 @@ def main(*argv, **kwargs): "Tip: See all example repositories: https://github.com/codecov?query=example" ) - write("Support channels:", "green") - write( - " Email: hello@codecov.io\n" - " IRC: #codecov\n" - " Gitter: https://gitter.im/codecov/support\n" - " Twitter: @codecov\n" - ) sys.exit(1 if codecov.required else 0) else: diff --git a/codecov/__version__.py b/codecov/__version__.py index 8e0b8295..60d08bb3 100644 --- a/codecov/__version__.py +++ b/codecov/__version__.py @@ -5,4 +5,4 @@ __license__ = "Apache 2.0" __title__ = "codecov" __url__ = "https://github.com/codecov/codecov-python" -__version__ = "2.1.8" +__version__ = "2.1.13" diff --git a/setup.py b/setup.py index 4d418234..1025c49b 100644 --- a/setup.py +++ b/setup.py @@ -2,6 +2,13 @@ from codecs import open import os from setuptools import setup +from setuptools.command.install import install + +class PostInstallCommand(install): + def run(self): + install.run(self) + print("\n**** The codecov package has been deprecated and will be removed by the team in the future. Please update to use the uploader (https://docs.codecov.com/docs/codecov-uploader) to prevent any breakages in workflow. ****\n") + classifiers = [ "Development Status :: 5 - Production/Stable", @@ -15,6 +22,8 @@ "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", "Programming Language :: Python :: Implementation :: PyPy", "License :: OSI Approved :: Apache Software License", "Topic :: Software Development :: Testing", @@ -43,4 +52,7 @@ install_requires=["requests>=2.7.9", "coverage"], entry_points={"console_scripts": ["codecov=codecov:main"]}, python_requires=">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*", + cmdclass={ + 'install': PostInstallCommand, + }, ) diff --git a/tests/requirements.txt b/tests/requirements.txt index b6f891ef..7dbe1dda 100644 --- a/tests/requirements.txt +++ b/tests/requirements.txt @@ -1,4 +1,4 @@ -coverage==4.5.4 +coverage==5.5 ddt mock pytest>=4.6.0 diff --git a/tests/test.py b/tests/test.py index de4af8e8..98a4412a 100644 --- a/tests/test.py +++ b/tests/test.py @@ -278,7 +278,7 @@ def test_send(self): gzip_worker.decompress(put.call_args[1]["data"]) + gzip_worker.flush() ) - assert u"tests/test.py".encode("utf-8") in reports + assert "tests/test.py".encode("utf-8") in reports def test_send_error(self): with patch("requests.post") as post: @@ -294,18 +294,6 @@ def test_send_error(self): else: raise Exception("400 never raised") - @data((dict(commit="sha"), "Missing repository upload token"),) - def test_require_branch(self, dd): - (kwargs, reason) = dd - # this is so we dont get branch for local git - self.set_env(JENKINS_URL="hello") - try: - self.run_cli(**kwargs) - except AssertionError as e: - self.assertEqual(str(e), reason) - else: - raise Exception("Did not raise AssertionError") - @unittest.skipIf( os.getenv("CI") == "True" and os.getenv("APPVEYOR") == "True", "Skip AppVeyor CI test", @@ -878,7 +866,9 @@ def test_ci_gitlab(self): ) def test_ci_github(self): self.set_env( - HOME="/", CODECOV_TOKEN="token", CODECOV_NAME="name", + HOME="/", + CODECOV_TOKEN="token", + CODECOV_NAME="name", ) self.fake_report() res = self.run_cli() @@ -889,6 +879,37 @@ def test_ci_github(self): self.assertEqual(res["codecov"].token, "token") self.assertEqual(res["codecov"].name, "name") + @unittest.skipUnless( + os.getenv("CI") == "true" and os.getenv("CIRRUS_CI") == "true", + "Skip Cirrus CI", + ) + def test_ci_cirrus(self): + # The data used in this test follows the test case data in + # https://github.com/codecov/codecov-bash/pull/127 + # Discussion about using codecov without token for Cirrus CI can be seen in: + # https://community.codecov.com/t/add-support-of-uploading-from-cirrus-ci-without-token/1028/36 + self.set_env( + HOME="/", + CIRRUS_CI="true", + CIRRUS_REPO_FULL_NAME="codecov/ci-repo", + CIRRUS_BRANCH="master", + CIRRUS_PR="1", + CIRRUS_CHANGE_IN_REPO="180c0d097354fc1a451da8a3be5aba255f2ffd9f", + CIRRUS_BUILD_ID="777", + CIRRUS_TASK_ID="239", + CIRRUS_TASK_NAME="test" + ) + self.fake_report() + res = self.run_cli() + self.assertEqual(res["query"]["service"], "cirrus-ci") + self.assertEqual(res["query"]["slug"], "codecov/ci-repo") + self.assertEqual(res["query"]["branch"], "master") + self.assertEqual(res["query"]["pr"], "1") + self.assertEqual(res["query"]["commit"], os.getenv("CIRRUS_CHANGE_IN_REPO")) + self.assertEqual(res["query"]["build"], "777") + self.assertEqual(res["query"]["build_url"], "https://cirrus-ci.com/task/239") + self.assertEqual(res["query"]["job"], "test") + @unittest.skip("Skip CI None") def test_ci_none(self): self.set_env(CODECOV_TOKEN="token", CODECOV_NAME="name")