codecov-cli 11.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (83) hide show
  1. codecov_cli/__init__.py +3 -0
  2. codecov_cli/commands/__init__.py +0 -0
  3. codecov_cli/commands/base_picking.py +75 -0
  4. codecov_cli/commands/commit.py +72 -0
  5. codecov_cli/commands/create_report_result.py +41 -0
  6. codecov_cli/commands/empty_upload.py +80 -0
  7. codecov_cli/commands/get_report_results.py +50 -0
  8. codecov_cli/commands/labelanalysis.py +269 -0
  9. codecov_cli/commands/process_test_results.py +273 -0
  10. codecov_cli/commands/report.py +65 -0
  11. codecov_cli/commands/send_notifications.py +46 -0
  12. codecov_cli/commands/staticanalysis.py +62 -0
  13. codecov_cli/commands/upload.py +316 -0
  14. codecov_cli/commands/upload_coverage.py +186 -0
  15. codecov_cli/commands/upload_process.py +133 -0
  16. codecov_cli/fallbacks.py +41 -0
  17. codecov_cli/helpers/__init__.py +0 -0
  18. codecov_cli/helpers/args.py +31 -0
  19. codecov_cli/helpers/ci_adapters/__init__.py +63 -0
  20. codecov_cli/helpers/ci_adapters/appveyor_ci.py +54 -0
  21. codecov_cli/helpers/ci_adapters/azure_pipelines.py +44 -0
  22. codecov_cli/helpers/ci_adapters/base.py +102 -0
  23. codecov_cli/helpers/ci_adapters/bitbucket_ci.py +42 -0
  24. codecov_cli/helpers/ci_adapters/bitrise_ci.py +37 -0
  25. codecov_cli/helpers/ci_adapters/buildkite.py +45 -0
  26. codecov_cli/helpers/ci_adapters/circleci.py +47 -0
  27. codecov_cli/helpers/ci_adapters/cirrus_ci.py +36 -0
  28. codecov_cli/helpers/ci_adapters/cloudbuild.py +70 -0
  29. codecov_cli/helpers/ci_adapters/codebuild.py +49 -0
  30. codecov_cli/helpers/ci_adapters/droneci.py +36 -0
  31. codecov_cli/helpers/ci_adapters/github_actions.py +90 -0
  32. codecov_cli/helpers/ci_adapters/gitlab_ci.py +56 -0
  33. codecov_cli/helpers/ci_adapters/heroku.py +36 -0
  34. codecov_cli/helpers/ci_adapters/jenkins.py +38 -0
  35. codecov_cli/helpers/ci_adapters/local.py +39 -0
  36. codecov_cli/helpers/ci_adapters/teamcity.py +37 -0
  37. codecov_cli/helpers/ci_adapters/travis_ci.py +44 -0
  38. codecov_cli/helpers/ci_adapters/woodpeckerci.py +36 -0
  39. codecov_cli/helpers/config.py +66 -0
  40. codecov_cli/helpers/encoder.py +49 -0
  41. codecov_cli/helpers/folder_searcher.py +114 -0
  42. codecov_cli/helpers/git.py +97 -0
  43. codecov_cli/helpers/git_services/__init__.py +14 -0
  44. codecov_cli/helpers/git_services/github.py +40 -0
  45. codecov_cli/helpers/glob.py +146 -0
  46. codecov_cli/helpers/logging_utils.py +77 -0
  47. codecov_cli/helpers/options.py +51 -0
  48. codecov_cli/helpers/request.py +198 -0
  49. codecov_cli/helpers/upload_type.py +15 -0
  50. codecov_cli/helpers/validators.py +13 -0
  51. codecov_cli/helpers/versioning_systems.py +201 -0
  52. codecov_cli/main.py +99 -0
  53. codecov_cli/opentelemetry.py +26 -0
  54. codecov_cli/plugins/__init__.py +92 -0
  55. codecov_cli/plugins/compress_pycoverage_contexts.py +141 -0
  56. codecov_cli/plugins/gcov.py +69 -0
  57. codecov_cli/plugins/pycoverage.py +134 -0
  58. codecov_cli/plugins/types.py +8 -0
  59. codecov_cli/plugins/xcode.py +117 -0
  60. codecov_cli/runners/__init__.py +80 -0
  61. codecov_cli/runners/dan_runner.py +64 -0
  62. codecov_cli/runners/pytest_standard_runner.py +184 -0
  63. codecov_cli/runners/types.py +33 -0
  64. codecov_cli/services/__init__.py +0 -0
  65. codecov_cli/services/commit/__init__.py +86 -0
  66. codecov_cli/services/commit/base_picking.py +24 -0
  67. codecov_cli/services/empty_upload/__init__.py +42 -0
  68. codecov_cli/services/report/__init__.py +169 -0
  69. codecov_cli/services/upload/__init__.py +169 -0
  70. codecov_cli/services/upload/file_finder.py +320 -0
  71. codecov_cli/services/upload/legacy_upload_sender.py +132 -0
  72. codecov_cli/services/upload/network_finder.py +49 -0
  73. codecov_cli/services/upload/upload_collector.py +198 -0
  74. codecov_cli/services/upload/upload_sender.py +232 -0
  75. codecov_cli/services/upload_completion/__init__.py +38 -0
  76. codecov_cli/services/upload_coverage/__init__.py +93 -0
  77. codecov_cli/types.py +88 -0
  78. codecov_cli-11.0.0.dist-info/METADATA +298 -0
  79. codecov_cli-11.0.0.dist-info/RECORD +83 -0
  80. codecov_cli-11.0.0.dist-info/WHEEL +5 -0
  81. codecov_cli-11.0.0.dist-info/entry_points.txt +3 -0
  82. codecov_cli-11.0.0.dist-info/licenses/LICENSE +201 -0
  83. codecov_cli-11.0.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,273 @@
1
+ import json
2
+ import logging
3
+ import os
4
+ import pathlib
5
+ from dataclasses import dataclass
6
+ from typing import Any, Dict, List, Optional
7
+
8
+ import click
9
+ import sentry_sdk
10
+ from test_results_parser import (
11
+ Outcome,
12
+ ParserError,
13
+ Testrun,
14
+ build_message,
15
+ parse_junit_xml,
16
+ )
17
+
18
+ from codecov_cli.helpers.args import get_cli_args
19
+ from codecov_cli.helpers.request import (
20
+ log_warnings_and_errors_if_any,
21
+ send_get_request,
22
+ send_post_request,
23
+ )
24
+ from codecov_cli.helpers.upload_type import ReportType
25
+ from codecov_cli.services.upload.file_finder import select_file_finder
26
+ from codecov_cli.types import CommandContext, RequestResult, UploadCollectionResultFile
27
+
28
+ logger = logging.getLogger("codecovcli")
29
+
30
+ # Search marker so that we can find the comment when looking for previously created comments
31
+ CODECOV_SEARCH_MARKER = "<!-- Codecov -->"
32
+
33
+
34
+ _process_test_results_options = [
35
+ click.option(
36
+ "-s",
37
+ "--dir",
38
+ "--files-search-root-folder",
39
+ "dir",
40
+ help="Folder where to search for test results files",
41
+ type=click.Path(path_type=pathlib.Path),
42
+ default=pathlib.Path.cwd,
43
+ show_default="Current Working Directory",
44
+ ),
45
+ click.option(
46
+ "-f",
47
+ "--file",
48
+ "--files-search-direct-file",
49
+ "files",
50
+ help="Explicit files to upload. These will be added to the test results files to be processed. If you wish to only process the specified files, please consider using --disable-search to disable processing other files.",
51
+ type=click.Path(path_type=pathlib.Path),
52
+ multiple=True,
53
+ default=[],
54
+ ),
55
+ click.option(
56
+ "--exclude",
57
+ "--files-search-exclude-folder",
58
+ "exclude_folders",
59
+ help="Folders to exclude from search",
60
+ type=click.Path(path_type=pathlib.Path),
61
+ multiple=True,
62
+ default=[],
63
+ ),
64
+ click.option(
65
+ "--disable-search",
66
+ help="Disable search for coverage files. This is helpful when specifying what files you want to upload with the --file option.",
67
+ is_flag=True,
68
+ default=False,
69
+ ),
70
+ click.option(
71
+ "--github-token",
72
+ help="If specified, output the message to the specified GitHub PR.",
73
+ type=str,
74
+ default=None,
75
+ ),
76
+ ]
77
+
78
+
79
+ def process_test_results_options(func):
80
+ for option in reversed(_process_test_results_options):
81
+ func = option(func)
82
+ return func
83
+
84
+
85
+ @dataclass
86
+ class TestResultsNotificationPayload:
87
+ failures: List[Testrun]
88
+ failed: int = 0
89
+ passed: int = 0
90
+ skipped: int = 0
91
+
92
+
93
+ @click.command()
94
+ @process_test_results_options
95
+ @click.pass_context
96
+ def process_test_results(
97
+ ctx: CommandContext,
98
+ dir=None,
99
+ files=None,
100
+ exclude_folders=None,
101
+ disable_search=None,
102
+ github_token=None,
103
+ ):
104
+ with sentry_sdk.start_transaction(op="task", name="Process Test Results"):
105
+ with sentry_sdk.start_span(name="process_test_results"):
106
+ file_finder = select_file_finder(
107
+ dir,
108
+ exclude_folders,
109
+ files,
110
+ disable_search,
111
+ report_type=ReportType.TEST_RESULTS,
112
+ )
113
+
114
+ upload_collection_results: List[UploadCollectionResultFile] = (
115
+ file_finder.find_files()
116
+ )
117
+ if len(upload_collection_results) == 0:
118
+ raise click.ClickException(
119
+ "No JUnit XML files were found. Make sure to specify them using the --file option."
120
+ )
121
+
122
+ payload: TestResultsNotificationPayload = generate_message_payload(
123
+ upload_collection_results
124
+ )
125
+
126
+ message: str = f"{build_message(payload)} {CODECOV_SEARCH_MARKER}"
127
+
128
+ args: Dict[str, str] = get_cli_args(ctx)
129
+
130
+ maybe_write_to_github_action(message, github_token, args)
131
+
132
+ click.echo(message)
133
+
134
+
135
+ def maybe_write_to_github_action(
136
+ message: str, github_token: str, args: Dict[str, str]
137
+ ) -> None:
138
+ if github_token is None:
139
+ # If no token is passed, then we will assume users are not running in a GitHub Action
140
+ return
141
+
142
+ maybe_write_to_github_comment(message, github_token, args)
143
+
144
+
145
+ def maybe_write_to_github_comment(
146
+ message: str, github_token: str, args: Dict[str, str]
147
+ ) -> None:
148
+ slug = os.getenv("GITHUB_REPOSITORY")
149
+ if slug is None:
150
+ raise click.ClickException(
151
+ "Error getting repo slug from environment. "
152
+ "Can't find GITHUB_REPOSITORY environment variable."
153
+ )
154
+
155
+ ref = os.getenv("GITHUB_REF")
156
+ if ref is None or "pull" not in ref:
157
+ raise click.ClickException(
158
+ "Error getting PR number from environment. "
159
+ "Can't find GITHUB_REF environment variable."
160
+ )
161
+ # GITHUB_REF is documented here: https://docs.github.com/en/actions/learn-github-actions/variables#default-environment-variables
162
+ pr_number = ref.split("/")[2]
163
+
164
+ existing_comment = find_existing_github_comment(github_token, slug, pr_number)
165
+ comment_id = None
166
+ if existing_comment is not None:
167
+ comment_id = existing_comment.get("id")
168
+
169
+ create_or_update_github_comment(
170
+ github_token, slug, pr_number, message, comment_id, args
171
+ )
172
+
173
+
174
+ def find_existing_github_comment(
175
+ github_token: str, repo_slug: str, pr_number: int
176
+ ) -> Optional[Dict[str, Any]]:
177
+ url = f"https://api.github.com/repos/{repo_slug}/issues/{pr_number}/comments"
178
+
179
+ headers = {
180
+ "Accept": "application/vnd.github+json",
181
+ "Authorization": f"Bearer {github_token}",
182
+ "X-GitHub-Api-Version": "2022-11-28",
183
+ }
184
+ page = 1
185
+
186
+ results = get_github_response_or_error(url, headers, page)
187
+ while results != []:
188
+ for comment in results:
189
+ comment_user = comment.get("user")
190
+ if (
191
+ CODECOV_SEARCH_MARKER in comment.get("body", "")
192
+ and comment_user
193
+ and comment_user.get("login", "") == "github-actions[bot]"
194
+ ):
195
+ return comment
196
+
197
+ page += 1
198
+ results = get_github_response_or_error(url, headers, page)
199
+
200
+ # No matches, return None
201
+ return None
202
+
203
+
204
+ def get_github_response_or_error(
205
+ url: str, headers: Dict[str, str], page: int
206
+ ) -> Dict[str, Any]:
207
+ request_results: RequestResult = send_get_request(
208
+ url, headers, params={"page": page}
209
+ )
210
+ if request_results.status_code != 200:
211
+ raise click.ClickException("Cannot find existing GitHub comment for PR.")
212
+ results = json.loads(request_results.text)
213
+ return results
214
+
215
+
216
+ def create_or_update_github_comment(
217
+ token: str,
218
+ repo_slug: str,
219
+ pr_number: str,
220
+ message: str,
221
+ comment_id: Optional[str],
222
+ args: Dict[str, Any],
223
+ ) -> None:
224
+ if comment_id is not None:
225
+ url = f"https://api.github.com/repos/{repo_slug}/issues/comments/{comment_id}"
226
+ else:
227
+ url = f"https://api.github.com/repos/{repo_slug}/issues/{pr_number}/comments"
228
+
229
+ headers = {
230
+ "Accept": "application/vnd.github+json",
231
+ "Authorization": f"Bearer {token}",
232
+ "X-GitHub-Api-Version": "2022-11-28",
233
+ }
234
+ logger.info(f"Posting GitHub comment {comment_id}")
235
+
236
+ log_warnings_and_errors_if_any(
237
+ send_post_request(
238
+ url=url,
239
+ data={
240
+ "body": message,
241
+ "cli_args": args,
242
+ },
243
+ headers=headers,
244
+ ),
245
+ "Posting test results comment",
246
+ )
247
+
248
+
249
+ def generate_message_payload(
250
+ upload_collection_results: List[UploadCollectionResultFile],
251
+ ) -> TestResultsNotificationPayload:
252
+ payload = TestResultsNotificationPayload(failures=[])
253
+
254
+ for result in upload_collection_results:
255
+ try:
256
+ logger.info(f"Parsing {result.get_filename()}")
257
+ parsed_info = parse_junit_xml(result.get_content())
258
+ for testrun in parsed_info.testruns:
259
+ if (
260
+ testrun.outcome == Outcome.Failure
261
+ or testrun.outcome == Outcome.Error
262
+ ):
263
+ payload.failed += 1
264
+ payload.failures.append(testrun)
265
+ elif testrun.outcome == Outcome.Skip:
266
+ payload.skipped += 1
267
+ else:
268
+ payload.passed += 1
269
+ except ParserError as err:
270
+ raise click.ClickException(
271
+ f"Error parsing {result.get_filename()} with error: {err}"
272
+ )
273
+ return payload
@@ -0,0 +1,65 @@
1
+ import logging
2
+
3
+ import click
4
+ import sentry_sdk
5
+
6
+ from codecov_cli.fallbacks import CodecovOption, FallbackFieldEnum
7
+ from codecov_cli.helpers.args import get_cli_args
8
+ from codecov_cli.helpers.options import global_options
9
+ from codecov_cli.services.report import create_report_logic
10
+ from codecov_cli.types import CommandContext
11
+
12
+ logger = logging.getLogger("codecovcli")
13
+
14
+
15
+ @click.command()
16
+ @click.option(
17
+ "--code", help="The code of the report. If unsure, leave default", default="default", hidden=True
18
+ )
19
+ @click.option(
20
+ "-P",
21
+ "--pr",
22
+ "--pull-request-number",
23
+ "pull_request_number",
24
+ help="Specify the pull request number manually. Used to override pre-existing CI environment variables",
25
+ cls=CodecovOption,
26
+ fallback_field=FallbackFieldEnum.pull_request_number,
27
+ )
28
+ @global_options
29
+ @click.pass_context
30
+ def create_report(
31
+ ctx: CommandContext,
32
+ commit_sha: str,
33
+ code: str,
34
+ slug: str,
35
+ git_service: str,
36
+ token: str,
37
+ fail_on_error: bool,
38
+ pull_request_number: int,
39
+ ):
40
+ with sentry_sdk.start_transaction(op="task", name="Create Report"):
41
+ with sentry_sdk.start_span(name="create_report"):
42
+ enterprise_url = ctx.obj.get("enterprise_url")
43
+ args = get_cli_args(ctx)
44
+ logger.debug(
45
+ "Starting create report process",
46
+ extra=dict(
47
+ extra_log_attributes=args,
48
+ ),
49
+ )
50
+ res = create_report_logic(
51
+ commit_sha,
52
+ code,
53
+ slug,
54
+ git_service,
55
+ token,
56
+ enterprise_url,
57
+ pull_request_number,
58
+ fail_on_error,
59
+ args,
60
+ )
61
+ if not res.error:
62
+ logger.info(
63
+ "Finished creating report successfully",
64
+ extra=dict(extra_log_attributes=dict(response=res.text)),
65
+ )
@@ -0,0 +1,46 @@
1
+ import logging
2
+ import typing
3
+
4
+ import click
5
+ import sentry_sdk
6
+
7
+ from codecov_cli.fallbacks import CodecovOption, FallbackFieldEnum
8
+ from codecov_cli.helpers.args import get_cli_args
9
+ from codecov_cli.helpers.git import GitService
10
+ from codecov_cli.helpers.options import global_options
11
+ from codecov_cli.services.upload_completion import upload_completion_logic
12
+ from codecov_cli.types import CommandContext
13
+
14
+ logger = logging.getLogger("codecovcli")
15
+
16
+
17
+ @click.command()
18
+ @global_options
19
+ @click.pass_context
20
+ def send_notifications(
21
+ ctx: CommandContext,
22
+ commit_sha: str,
23
+ slug: typing.Optional[str],
24
+ token: typing.Optional[str],
25
+ git_service: typing.Optional[str],
26
+ fail_on_error: bool,
27
+ ):
28
+ with sentry_sdk.start_transaction(op="task", name="Send Notifications"):
29
+ with sentry_sdk.start_span(name="send_notifications"):
30
+ enterprise_url = ctx.obj.get("enterprise_url")
31
+ args = get_cli_args(ctx)
32
+ logger.debug(
33
+ "Sending notifications process has started",
34
+ extra=dict(
35
+ extra_log_attributes=args,
36
+ ),
37
+ )
38
+ return upload_completion_logic(
39
+ commit_sha,
40
+ slug,
41
+ token,
42
+ git_service,
43
+ enterprise_url,
44
+ fail_on_error,
45
+ args,
46
+ )
@@ -0,0 +1,62 @@
1
+ import logging
2
+ import pathlib
3
+ import typing
4
+
5
+ import click
6
+ import sentry_sdk
7
+
8
+ from codecov_cli.fallbacks import CodecovOption, FallbackFieldEnum
9
+ from codecov_cli.helpers.validators import validate_commit_sha
10
+ from codecov_cli.types import CommandContext
11
+
12
+ logger = logging.getLogger("codecovcli")
13
+
14
+
15
+ @click.command(hidden=True, deprecated=True)
16
+ @click.option(
17
+ "--foldertosearch",
18
+ default=".",
19
+ help="Folder to search",
20
+ type=click.Path(path_type=pathlib.Path),
21
+ )
22
+ @click.option(
23
+ "--numberprocesses", type=click.INT, default=None, help="number of processes to use"
24
+ )
25
+ @click.option("--pattern", default="*", help="file pattern to search for")
26
+ @click.option("--force/--no-force", default=False)
27
+ @click.option(
28
+ "--commit-sha",
29
+ "commit",
30
+ help="Commit SHA (with 40 chars)",
31
+ cls=CodecovOption,
32
+ fallback_field=FallbackFieldEnum.commit_sha,
33
+ callback=validate_commit_sha,
34
+ required=True,
35
+ )
36
+ @click.option(
37
+ "--folders-to-exclude",
38
+ help="Folders not to search",
39
+ type=click.Path(path_type=pathlib.Path),
40
+ multiple=True,
41
+ default=[],
42
+ )
43
+ @click.option(
44
+ "--token",
45
+ required=True,
46
+ envvar="CODECOV_STATIC_TOKEN",
47
+ help="The static analysis token (NOT the same token as upload)",
48
+ )
49
+ @click.pass_context
50
+ def static_analysis(
51
+ ctx: CommandContext,
52
+ foldertosearch,
53
+ numberprocesses,
54
+ pattern,
55
+ commit,
56
+ token,
57
+ force,
58
+ folders_to_exclude: typing.List[pathlib.Path],
59
+ ):
60
+ with sentry_sdk.start_transaction(op="task", name="Static Analysis"):
61
+ with sentry_sdk.start_span(name="static_analysis"):
62
+ pass