codecov-cli 0.3.4__cp311-cp311-win_amd64.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 (84) hide show
  1. codecov_cli/__init__.py +4 -0
  2. codecov_cli/commands/__init__.py +0 -0
  3. codecov_cli/commands/base_picking.py +77 -0
  4. codecov_cli/commands/commit.py +75 -0
  5. codecov_cli/commands/create_report_result.py +45 -0
  6. codecov_cli/commands/empty_upload.py +42 -0
  7. codecov_cli/commands/get_report_results.py +53 -0
  8. codecov_cli/commands/labelanalysis.py +398 -0
  9. codecov_cli/commands/report.py +50 -0
  10. codecov_cli/commands/send_notifications.py +46 -0
  11. codecov_cli/commands/staticanalysis.py +88 -0
  12. codecov_cli/commands/upload.py +266 -0
  13. codecov_cli/commands/upload_process.py +129 -0
  14. codecov_cli/fallbacks.py +41 -0
  15. codecov_cli/helpers/__init__.py +0 -0
  16. codecov_cli/helpers/ci_adapters/__init__.py +59 -0
  17. codecov_cli/helpers/ci_adapters/appveyor_ci.py +54 -0
  18. codecov_cli/helpers/ci_adapters/azure_pipelines.py +42 -0
  19. codecov_cli/helpers/ci_adapters/base.py +102 -0
  20. codecov_cli/helpers/ci_adapters/bitbucket_ci.py +42 -0
  21. codecov_cli/helpers/ci_adapters/bitrise_ci.py +37 -0
  22. codecov_cli/helpers/ci_adapters/buildkite.py +45 -0
  23. codecov_cli/helpers/ci_adapters/circleci.py +47 -0
  24. codecov_cli/helpers/ci_adapters/cirrus_ci.py +36 -0
  25. codecov_cli/helpers/ci_adapters/codebuild.py +49 -0
  26. codecov_cli/helpers/ci_adapters/droneci.py +36 -0
  27. codecov_cli/helpers/ci_adapters/github_actions.py +90 -0
  28. codecov_cli/helpers/ci_adapters/gitlab_ci.py +56 -0
  29. codecov_cli/helpers/ci_adapters/heroku.py +36 -0
  30. codecov_cli/helpers/ci_adapters/jenkins.py +38 -0
  31. codecov_cli/helpers/ci_adapters/local.py +39 -0
  32. codecov_cli/helpers/ci_adapters/teamcity.py +37 -0
  33. codecov_cli/helpers/ci_adapters/travis_ci.py +44 -0
  34. codecov_cli/helpers/ci_adapters/woodpeckerci.py +36 -0
  35. codecov_cli/helpers/config.py +20 -0
  36. codecov_cli/helpers/encoder.py +29 -0
  37. codecov_cli/helpers/folder_searcher.py +103 -0
  38. codecov_cli/helpers/git.py +84 -0
  39. codecov_cli/helpers/logging_utils.py +77 -0
  40. codecov_cli/helpers/options.py +52 -0
  41. codecov_cli/helpers/request.py +107 -0
  42. codecov_cli/helpers/validators.py +13 -0
  43. codecov_cli/helpers/versioning_systems.py +131 -0
  44. codecov_cli/main.py +81 -0
  45. codecov_cli/plugins/__init__.py +76 -0
  46. codecov_cli/plugins/compress_pycoverage_contexts.py +140 -0
  47. codecov_cli/plugins/gcov.py +64 -0
  48. codecov_cli/plugins/pycoverage.py +131 -0
  49. codecov_cli/plugins/types.py +8 -0
  50. codecov_cli/plugins/xcode.py +109 -0
  51. codecov_cli/runners/__init__.py +61 -0
  52. codecov_cli/runners/dan_runner.py +64 -0
  53. codecov_cli/runners/python_standard_runner.py +151 -0
  54. codecov_cli/runners/types.py +33 -0
  55. codecov_cli/services/__init__.py +0 -0
  56. codecov_cli/services/commit/__init__.py +55 -0
  57. codecov_cli/services/commit/base_picking.py +23 -0
  58. codecov_cli/services/empty_upload/__init__.py +28 -0
  59. codecov_cli/services/report/__init__.py +139 -0
  60. codecov_cli/services/staticanalysis/__init__.py +280 -0
  61. codecov_cli/services/staticanalysis/analyzers/__init__.py +14 -0
  62. codecov_cli/services/staticanalysis/analyzers/general.py +124 -0
  63. codecov_cli/services/staticanalysis/analyzers/javascript_es6/__init__.py +122 -0
  64. codecov_cli/services/staticanalysis/analyzers/javascript_es6/node_wrappers.py +71 -0
  65. codecov_cli/services/staticanalysis/analyzers/python/__init__.py +111 -0
  66. codecov_cli/services/staticanalysis/analyzers/python/node_wrappers.py +117 -0
  67. codecov_cli/services/staticanalysis/exceptions.py +2 -0
  68. codecov_cli/services/staticanalysis/finders.py +47 -0
  69. codecov_cli/services/staticanalysis/types.py +19 -0
  70. codecov_cli/services/upload/__init__.py +128 -0
  71. codecov_cli/services/upload/coverage_file_finder.py +264 -0
  72. codecov_cli/services/upload/legacy_upload_sender.py +131 -0
  73. codecov_cli/services/upload/network_finder.py +21 -0
  74. codecov_cli/services/upload/upload_collector.py +163 -0
  75. codecov_cli/services/upload/upload_sender.py +147 -0
  76. codecov_cli/services/upload_completion/__init__.py +29 -0
  77. codecov_cli/types.py +72 -0
  78. codecov_cli-0.3.4.dist-info/LICENSE +201 -0
  79. codecov_cli-0.3.4.dist-info/METADATA +321 -0
  80. codecov_cli-0.3.4.dist-info/RECORD +84 -0
  81. codecov_cli-0.3.4.dist-info/WHEEL +5 -0
  82. codecov_cli-0.3.4.dist-info/entry_points.txt +2 -0
  83. codecov_cli-0.3.4.dist-info/top_level.txt +2 -0
  84. staticcodecov_languages.cpython-311-x86_64-linux-gnu.so +0 -0
@@ -0,0 +1,4 @@
1
+ with open("VERSION", encoding="utf-8") as f:
2
+ version_number = f.readline().strip()
3
+
4
+ __version__ = version_number
File without changes
@@ -0,0 +1,77 @@
1
+ import logging
2
+ import typing
3
+ import uuid
4
+
5
+ import click
6
+
7
+ from codecov_cli.fallbacks import CodecovOption, FallbackFieldEnum
8
+ from codecov_cli.helpers.encoder import slug_without_subgroups_is_invalid
9
+ from codecov_cli.services.commit.base_picking import base_picking_logic
10
+
11
+ logger = logging.getLogger("codecovcli")
12
+
13
+
14
+ @click.command()
15
+ @click.option(
16
+ "--base-sha",
17
+ help="Base commit SHA (with 40 chars)",
18
+ cls=CodecovOption,
19
+ fallback_field=FallbackFieldEnum.commit_sha,
20
+ required=True,
21
+ )
22
+ @click.option(
23
+ "--pr",
24
+ help="Pull Request id to associate commit with",
25
+ cls=CodecovOption,
26
+ fallback_field=FallbackFieldEnum.pull_request_number,
27
+ )
28
+ @click.option(
29
+ "--slug",
30
+ cls=CodecovOption,
31
+ fallback_field=FallbackFieldEnum.slug,
32
+ help="owner/repo slug",
33
+ envvar="CODECOV_SLUG",
34
+ )
35
+ @click.option(
36
+ "-t",
37
+ "--token",
38
+ help="Codecov upload token",
39
+ type=click.UUID,
40
+ envvar="CODECOV_TOKEN",
41
+ )
42
+ @click.option(
43
+ "--service",
44
+ cls=CodecovOption,
45
+ fallback_field=FallbackFieldEnum.service,
46
+ help="Specify the service provider of the repo e.g. github",
47
+ )
48
+ @click.pass_context
49
+ def pr_base_picking(
50
+ ctx,
51
+ base_sha: str,
52
+ pr: typing.Optional[int],
53
+ slug: typing.Optional[str],
54
+ token: typing.Optional[uuid.UUID],
55
+ service: typing.Optional[str],
56
+ ):
57
+ enterprise_url = ctx.obj.get("enterprise_url")
58
+ logger.debug(
59
+ "Starting base picking process",
60
+ extra=dict(
61
+ extra_log_attributes=dict(
62
+ pr=pr,
63
+ slug=slug,
64
+ token=token,
65
+ service=service,
66
+ enterprise_url=enterprise_url,
67
+ )
68
+ ),
69
+ )
70
+
71
+ if slug_without_subgroups_is_invalid(slug):
72
+ logger.error(
73
+ "Slug is invalid. Slug should be in the form of owner_username/repo_name"
74
+ )
75
+ return
76
+
77
+ base_picking_logic(base_sha, pr, slug, token, service, enterprise_url)
@@ -0,0 +1,75 @@
1
+ import logging
2
+ import typing
3
+ import uuid
4
+
5
+ import click
6
+
7
+ from codecov_cli.fallbacks import CodecovOption, FallbackFieldEnum
8
+ from codecov_cli.helpers.git import GitService
9
+ from codecov_cli.helpers.options import global_options
10
+ from codecov_cli.services.commit import create_commit_logic
11
+
12
+ logger = logging.getLogger("codecovcli")
13
+
14
+
15
+ @click.command()
16
+ @click.option(
17
+ "--parent-sha",
18
+ help="SHA (with 40 chars) of what should be the parent of this commit",
19
+ )
20
+ @click.option(
21
+ "-P",
22
+ "--pr",
23
+ "--pull-request-number",
24
+ "pull_request_number",
25
+ help="Specify the pull request number mannually. Used to override pre-existing CI environment variables",
26
+ cls=CodecovOption,
27
+ fallback_field=FallbackFieldEnum.pull_request_number,
28
+ )
29
+ @click.option(
30
+ "-B",
31
+ "--branch",
32
+ help="Branch to which this commit belongs to",
33
+ cls=CodecovOption,
34
+ fallback_field=FallbackFieldEnum.branch,
35
+ )
36
+ @global_options
37
+ @click.pass_context
38
+ def create_commit(
39
+ ctx,
40
+ commit_sha: str,
41
+ parent_sha: typing.Optional[str],
42
+ pull_request_number: typing.Optional[int],
43
+ branch: typing.Optional[str],
44
+ slug: typing.Optional[str],
45
+ token: typing.Optional[uuid.UUID],
46
+ git_service: typing.Optional[str],
47
+ fail_on_error: bool,
48
+ ):
49
+ enterprise_url = ctx.obj.get("enterprise_url")
50
+ logger.debug(
51
+ "Starting create commit process",
52
+ extra=dict(
53
+ extra_log_attributes=dict(
54
+ commit_sha=commit_sha,
55
+ parent_sha=parent_sha,
56
+ pr=pull_request_number,
57
+ branch=branch,
58
+ slug=slug,
59
+ token=token,
60
+ service=git_service,
61
+ enterprise_url=enterprise_url,
62
+ )
63
+ ),
64
+ )
65
+ create_commit_logic(
66
+ commit_sha,
67
+ parent_sha,
68
+ pull_request_number,
69
+ branch,
70
+ slug,
71
+ token,
72
+ git_service,
73
+ enterprise_url,
74
+ fail_on_error,
75
+ )
@@ -0,0 +1,45 @@
1
+ import logging
2
+ import uuid
3
+
4
+ import click
5
+
6
+ from codecov_cli.fallbacks import CodecovOption, FallbackFieldEnum
7
+ from codecov_cli.helpers.git import GitService
8
+ from codecov_cli.helpers.options import global_options
9
+ from codecov_cli.services.report import create_report_results_logic
10
+
11
+ logger = logging.getLogger("codecovcli")
12
+
13
+
14
+ @click.command()
15
+ @click.option(
16
+ "--code", help="The code of the report. If unsure, leave default", default="default"
17
+ )
18
+ @global_options
19
+ @click.pass_context
20
+ def create_report_results(
21
+ ctx,
22
+ commit_sha: str,
23
+ code: str,
24
+ slug: str,
25
+ git_service: str,
26
+ token: uuid.UUID,
27
+ fail_on_error: bool,
28
+ ):
29
+ enterprise_url = ctx.obj.get("enterprise_url")
30
+ logger.debug(
31
+ "Creating report results",
32
+ extra=dict(
33
+ extra_log_attributes=dict(
34
+ commit_sha=commit_sha,
35
+ code=code,
36
+ slug=slug,
37
+ service=git_service,
38
+ enterprise_url=enterprise_url,
39
+ token=token,
40
+ )
41
+ ),
42
+ )
43
+ create_report_results_logic(
44
+ commit_sha, code, slug, git_service, token, enterprise_url, fail_on_error
45
+ )
@@ -0,0 +1,42 @@
1
+ import logging
2
+ import typing
3
+ import uuid
4
+
5
+ import click
6
+
7
+ from codecov_cli.fallbacks import CodecovOption, FallbackFieldEnum
8
+ from codecov_cli.helpers.git import GitService
9
+ from codecov_cli.helpers.options import global_options
10
+ from codecov_cli.services.empty_upload import empty_upload_logic
11
+
12
+ logger = logging.getLogger("codecovcli")
13
+
14
+
15
+ @click.command()
16
+ @global_options
17
+ @click.pass_context
18
+ def empty_upload(
19
+ ctx,
20
+ commit_sha: str,
21
+ slug: typing.Optional[str],
22
+ token: typing.Optional[uuid.UUID],
23
+ git_service: typing.Optional[str],
24
+ fail_on_error: typing.Optional[bool],
25
+ ):
26
+ enterprise_url = ctx.obj.get("enterprise_url")
27
+ logger.debug(
28
+ "Starting empty upload process",
29
+ extra=dict(
30
+ extra_log_attributes=dict(
31
+ commit_sha=commit_sha,
32
+ slug=slug,
33
+ token=token,
34
+ service=git_service,
35
+ enterprise_url=enterprise_url,
36
+ fail_on_error=fail_on_error,
37
+ )
38
+ ),
39
+ )
40
+ return empty_upload_logic(
41
+ commit_sha, slug, token, git_service, enterprise_url, fail_on_error
42
+ )
@@ -0,0 +1,53 @@
1
+ import logging
2
+ import uuid
3
+
4
+ import click
5
+
6
+ from codecov_cli.fallbacks import CodecovOption, FallbackFieldEnum
7
+ from codecov_cli.helpers.encoder import encode_slug
8
+ from codecov_cli.helpers.git import GitService
9
+ from codecov_cli.helpers.options import global_options
10
+ from codecov_cli.services.report import send_reports_result_get_request
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"
18
+ )
19
+ @global_options
20
+ @click.pass_context
21
+ def get_report_results(
22
+ ctx,
23
+ commit_sha: str,
24
+ code: str,
25
+ slug: str,
26
+ git_service: str,
27
+ token: uuid.UUID,
28
+ fail_on_error: bool,
29
+ ):
30
+ enterprise_url = ctx.obj.get("enterprise_url")
31
+ logger.debug(
32
+ "Getting report results",
33
+ extra=dict(
34
+ extra_log_attributes=dict(
35
+ commit_sha=commit_sha,
36
+ code=code,
37
+ slug=slug,
38
+ service=git_service,
39
+ enterprise_url=enterprise_url,
40
+ token=token,
41
+ )
42
+ ),
43
+ )
44
+ encoded_slug = encode_slug(slug)
45
+ send_reports_result_get_request(
46
+ commit_sha=commit_sha,
47
+ report_code=code,
48
+ encoded_slug=encoded_slug,
49
+ service=git_service,
50
+ token=token,
51
+ enterprise_url=enterprise_url,
52
+ fail_on_error=fail_on_error,
53
+ )
@@ -0,0 +1,398 @@
1
+ import json
2
+ import logging
3
+ import pathlib
4
+ import time
5
+ from typing import List, Optional
6
+
7
+ import click
8
+ import requests
9
+
10
+ from codecov_cli.fallbacks import CodecovOption, FallbackFieldEnum
11
+ from codecov_cli.helpers.config import CODECOV_API_URL
12
+ from codecov_cli.helpers.validators import validate_commit_sha
13
+ from codecov_cli.runners import get_runner
14
+ from codecov_cli.runners.types import (
15
+ LabelAnalysisRequestResult,
16
+ LabelAnalysisRunnerInterface,
17
+ )
18
+
19
+ logger = logging.getLogger("codecovcli")
20
+
21
+
22
+ @click.command()
23
+ @click.option(
24
+ "--token",
25
+ required=True,
26
+ envvar="CODECOV_STATIC_TOKEN",
27
+ help="The static analysis token (NOT the same token as upload)",
28
+ )
29
+ @click.option(
30
+ "--head-sha",
31
+ "head_commit_sha",
32
+ help="Commit SHA (with 40 chars)",
33
+ cls=CodecovOption,
34
+ fallback_field=FallbackFieldEnum.commit_sha,
35
+ callback=validate_commit_sha,
36
+ required=True,
37
+ )
38
+ @click.option(
39
+ "--base-sha",
40
+ "base_commit_sha",
41
+ help="Commit SHA (with 40 chars)",
42
+ cls=CodecovOption,
43
+ callback=validate_commit_sha,
44
+ required=True,
45
+ )
46
+ @click.option(
47
+ "--runner-name", "--runner", "runner_name", help="Runner to use", default="python"
48
+ )
49
+ @click.option(
50
+ "--max-wait-time",
51
+ "max_wait_time",
52
+ help="Max time (in seconds) to wait for the label analysis result before falling back to running all tests. Default is to wait forever.",
53
+ default=None,
54
+ type=int,
55
+ )
56
+ @click.option(
57
+ "--dry-run",
58
+ "dry_run",
59
+ help=(
60
+ "Print list of tests to run AND tests skipped (and options that need to be added to the test runner) to stdout. "
61
+ + "Also prints the same information in JSON format. "
62
+ + "JSON will have keys 'ats_tests_to_run', 'ats_tests_to_skip' and 'runner_options'. "
63
+ + "List of tests to run is prefixed with ATS_TESTS_TO_RUN= "
64
+ + "List of tests to skip is prefixed with ATS_TESTS_TO_SKIP="
65
+ ),
66
+ is_flag=True,
67
+ )
68
+ @click.option(
69
+ "--dry-run-output-path",
70
+ "dry_run_output_path",
71
+ help=(
72
+ "Prints the dry-run list (ATS_TESTS_TO_RUN) into dry_run_output_path (in addition to stdout)\n"
73
+ + "AND prints ATS_TESTS_TO_SKIP into dry_run_output_path_skipped\n"
74
+ + "AND prints dry-run JSON output into dry_run_output_path.json"
75
+ ),
76
+ type=pathlib.Path,
77
+ default=None,
78
+ )
79
+ @click.pass_context
80
+ def label_analysis(
81
+ ctx: click.Context,
82
+ token: str,
83
+ head_commit_sha: str,
84
+ base_commit_sha: str,
85
+ runner_name: str,
86
+ max_wait_time: str,
87
+ dry_run: bool,
88
+ dry_run_output_path: Optional[pathlib.Path],
89
+ ):
90
+ enterprise_url = ctx.obj.get("enterprise_url")
91
+ logger.debug(
92
+ "Starting label analysis",
93
+ extra=dict(
94
+ extra_log_attributes=dict(
95
+ head_commit_sha=head_commit_sha,
96
+ base_commit_sha=base_commit_sha,
97
+ token=token,
98
+ runner_name=runner_name,
99
+ enterprise_url=enterprise_url,
100
+ max_wait_time=max_wait_time,
101
+ dry_run=dry_run,
102
+ )
103
+ ),
104
+ )
105
+ if head_commit_sha == base_commit_sha:
106
+ logger.error(
107
+ "Base and head sha can't be the same",
108
+ extra=dict(
109
+ extra_log_attributes=dict(
110
+ head_commit_sha=head_commit_sha,
111
+ base_commit_sha=base_commit_sha,
112
+ )
113
+ ),
114
+ )
115
+ raise click.ClickException(
116
+ click.style("Unable to run label analysis", fg="red")
117
+ )
118
+
119
+ codecov_yaml = ctx.obj["codecov_yaml"] or {}
120
+ cli_config = codecov_yaml.get("cli", {})
121
+ # Raises error if no runner is found
122
+ runner = get_runner(cli_config, runner_name)
123
+ logger.debug(
124
+ f"Selected runner: {runner}",
125
+ extra=dict(extra_log_attributes=dict(config=runner.params)),
126
+ )
127
+
128
+ upload_url = enterprise_url or CODECOV_API_URL
129
+ url = f"{upload_url}/labels/labels-analysis"
130
+ token_header = f"Repotoken {token}"
131
+ payload = {
132
+ "base_commit": base_commit_sha,
133
+ "head_commit": head_commit_sha,
134
+ "requested_labels": None,
135
+ }
136
+ # Send the initial label analysis request without labels
137
+ # Because labels might take a long time to collect
138
+ eid = _send_labelanalysis_request(payload, url, token_header)
139
+
140
+ logger.info("Collecting labels...")
141
+ requested_labels = runner.collect_tests()
142
+ logger.info(f"Collected {len(requested_labels)} test labels")
143
+ logger.debug(
144
+ "Labels collected",
145
+ extra=dict(extra_log_attributes=dict(labels_collected=requested_labels)),
146
+ )
147
+ payload["requested_labels"] = requested_labels
148
+
149
+ if eid:
150
+ # Initial request with no labels was successful
151
+ # Now we PATCH the labels in
152
+ patch_url = f"{upload_url}/labels/labels-analysis/{eid}"
153
+ _patch_labels(payload, patch_url, token_header)
154
+ else:
155
+ # Initial request with no labels failed
156
+ # Retry it
157
+ eid = _send_labelanalysis_request(payload, url, token_header)
158
+ if eid is None:
159
+ _fallback_to_collected_labels(
160
+ requested_labels,
161
+ runner,
162
+ dry_run=dry_run,
163
+ dry_run_output_path=dry_run_output_path,
164
+ )
165
+ return
166
+
167
+ has_result = False
168
+ logger.info("Waiting for list of tests to run...")
169
+ start_wait = time.monotonic()
170
+ time.sleep(1)
171
+ while not has_result:
172
+ resp_data = requests.get(
173
+ f"{upload_url}/labels/labels-analysis/{eid}",
174
+ headers={"Authorization": token_header},
175
+ )
176
+ resp_json = resp_data.json()
177
+ if resp_json["state"] == "finished":
178
+ logger.info(
179
+ "Received list of tests from Codecov",
180
+ extra=dict(
181
+ extra_log_attributes=dict(
182
+ processing_errors=resp_json.get("errors", [])
183
+ )
184
+ ),
185
+ )
186
+ request_result = _potentially_calculate_absent_labels(
187
+ resp_json["result"], requested_labels
188
+ )
189
+ if not dry_run:
190
+ runner.process_labelanalysis_result(request_result)
191
+ else:
192
+ _dry_run_output(
193
+ LabelAnalysisRequestResult(request_result),
194
+ runner,
195
+ dry_run_output_path,
196
+ )
197
+ return
198
+ if resp_json["state"] == "error":
199
+ logger.error(
200
+ "Request had problems calculating",
201
+ extra=dict(extra_log_attributes=dict(resp_json=resp_json)),
202
+ )
203
+ _fallback_to_collected_labels(
204
+ collected_labels=requested_labels,
205
+ runner=runner,
206
+ dry_run=dry_run,
207
+ dry_run_output_path=dry_run_output_path,
208
+ )
209
+ return
210
+ if max_wait_time and (time.monotonic() - start_wait) > max_wait_time:
211
+ logger.error(
212
+ f"Exceeded max waiting time of {max_wait_time} seconds. Running all tests.",
213
+ )
214
+ _fallback_to_collected_labels(
215
+ collected_labels=requested_labels,
216
+ runner=runner,
217
+ dry_run=dry_run,
218
+ dry_run_output_path=dry_run_output_path,
219
+ )
220
+ return
221
+ logger.info("Waiting more time for result...")
222
+ time.sleep(5)
223
+
224
+
225
+ def _potentially_calculate_absent_labels(
226
+ request_result, requested_labels
227
+ ) -> LabelAnalysisRequestResult:
228
+ if request_result["absent_labels"]:
229
+ # This means that Codecov already calculated everything for us
230
+ final_result = LabelAnalysisRequestResult(request_result)
231
+ else:
232
+ # Here we have to calculate the absent labels
233
+ # And also remove labels that maybe don't exist anymore from the set of labels to test
234
+ # Because codecov didn't have this info previously
235
+ requested_labels_set = set(requested_labels)
236
+ present_diff_labels_set = set(request_result.get("present_diff_labels", []))
237
+ present_report_labels_set = set(request_result.get("present_report_labels", []))
238
+ global_level_labels_set = set(request_result.get("global_level_labels", []))
239
+ final_result = LabelAnalysisRequestResult(
240
+ {
241
+ "present_report_labels": sorted(
242
+ present_report_labels_set & requested_labels_set
243
+ ),
244
+ "present_diff_labels": sorted(
245
+ present_diff_labels_set & requested_labels_set
246
+ ),
247
+ "absent_labels": sorted(
248
+ requested_labels_set - present_report_labels_set
249
+ ),
250
+ "global_level_labels": sorted(
251
+ global_level_labels_set & requested_labels_set
252
+ ),
253
+ }
254
+ )
255
+ logger.info(
256
+ "Received information about tests to run",
257
+ extra=dict(
258
+ extra_log_attributes=dict(
259
+ absent_labels=len(final_result.absent_labels),
260
+ present_diff_labels=len(final_result.present_diff_labels),
261
+ global_level_labels=len(final_result.global_level_labels),
262
+ present_report_labels=len(final_result.present_report_labels),
263
+ )
264
+ ),
265
+ )
266
+ return final_result
267
+
268
+
269
+ def _patch_labels(payload, url, token_header):
270
+ logger.info("Sending collected labels to Codecov...")
271
+ try:
272
+ response = requests.patch(
273
+ url, json=payload, headers={"Authorization": token_header}
274
+ )
275
+ if response.status_code < 300:
276
+ logger.info("Labels successfully sent to Codecov")
277
+ except requests.RequestException:
278
+ raise click.ClickException(click.style("Unable to reach Codecov", fg="red"))
279
+
280
+
281
+ def _send_labelanalysis_request(payload, url, token_header):
282
+ logger.info(
283
+ "Requesting set of labels to run...",
284
+ extra=dict(
285
+ extra_log_attributes=dict(
286
+ with_labels=(payload["requested_labels"] is not None)
287
+ )
288
+ ),
289
+ )
290
+ try:
291
+ response = requests.post(
292
+ url, json=payload, headers={"Authorization": token_header}
293
+ )
294
+ if response.status_code >= 500:
295
+ logger.warning(
296
+ "Sorry. Codecov is having problems",
297
+ extra=dict(extra_log_attributes=dict(status_code=response.status_code)),
298
+ )
299
+ return None
300
+ if response.status_code >= 400:
301
+ logger.warning(
302
+ "Got a 4XX status code back from Codecov",
303
+ extra=dict(
304
+ extra_log_attributes=dict(
305
+ status_code=response.status_code, response_json=response.json()
306
+ )
307
+ ),
308
+ )
309
+ raise click.ClickException(
310
+ "There is some problem with the submitted information"
311
+ )
312
+ except requests.RequestException:
313
+ raise click.ClickException(click.style("Unable to reach Codecov", fg="red"))
314
+ eid = response.json()["external_id"]
315
+ logger.info(
316
+ "Label Analysis request successful",
317
+ extra=dict(extra_log_attributes=dict(request_id=eid)),
318
+ )
319
+ return eid
320
+
321
+
322
+ def _dry_run_output(
323
+ result: LabelAnalysisRequestResult,
324
+ runner: LabelAnalysisRunnerInterface,
325
+ dry_run_output_path: Optional[pathlib.Path],
326
+ ):
327
+ labels_to_run = set(
328
+ result.absent_labels + result.global_level_labels + result.present_diff_labels
329
+ )
330
+ labels_skipped = set(result.present_report_labels) - labels_to_run
331
+ # If the test label can contain spaces and dashes the test runner might
332
+ # interpret it as an option and not a label
333
+ # So we wrap it in doublequotes just to be extra sure
334
+ labels_run_wrapped_double_quotes = sorted(
335
+ map(lambda l: '"' + l + '"', labels_to_run)
336
+ )
337
+ labels_skip_wrapped_double_quotes = sorted(
338
+ map(lambda l: '"' + l + '"', labels_skipped)
339
+ )
340
+
341
+ output_as_dict = dict(
342
+ runner_options=runner.dry_run_runner_options,
343
+ ats_tests_to_run=labels_run_wrapped_double_quotes,
344
+ ats_tests_to_skip=labels_skip_wrapped_double_quotes,
345
+ )
346
+ if dry_run_output_path is not None:
347
+ with open(dry_run_output_path, "w") as fd:
348
+ fd.write(
349
+ " ".join(
350
+ runner.dry_run_runner_options + labels_run_wrapped_double_quotes
351
+ )
352
+ + "\n"
353
+ )
354
+ with open(str(dry_run_output_path) + "_skipped", "w") as fd:
355
+ fd.write(
356
+ " ".join(
357
+ runner.dry_run_runner_options + labels_skip_wrapped_double_quotes
358
+ )
359
+ + "\n"
360
+ )
361
+ with open(str(dry_run_output_path) + ".json", "w") as fd:
362
+ fd.write(json.dumps(output_as_dict) + "\n")
363
+
364
+ click.echo(json.dumps(output_as_dict))
365
+ click.echo(
366
+ f"ATS_TESTS_TO_RUN={' '.join(runner.dry_run_runner_options + labels_run_wrapped_double_quotes)}"
367
+ )
368
+ click.echo(
369
+ f"ATS_TESTS_TO_SKIP={' '.join(runner.dry_run_runner_options + labels_skip_wrapped_double_quotes)}"
370
+ )
371
+
372
+
373
+ def _fallback_to_collected_labels(
374
+ collected_labels: List[str],
375
+ runner: LabelAnalysisRunnerInterface,
376
+ *,
377
+ dry_run: bool = False,
378
+ dry_run_output_path: Optional[pathlib.Path] = None,
379
+ ) -> dict:
380
+ logger.info("Trying to fallback on collected labels")
381
+ if collected_labels:
382
+ logger.info("Using collected labels as tests to run")
383
+ fake_response = LabelAnalysisRequestResult(
384
+ {
385
+ "present_report_labels": [],
386
+ "absent_labels": collected_labels,
387
+ "present_diff_labels": [],
388
+ "global_level_labels": [],
389
+ }
390
+ )
391
+ if not dry_run:
392
+ return runner.process_labelanalysis_result(fake_response)
393
+ else:
394
+ return _dry_run_output(
395
+ LabelAnalysisRequestResult(fake_response), runner, dry_run_output_path
396
+ )
397
+ logger.error("Cannot fallback to collected labels because no labels were collected")
398
+ raise click.ClickException("Failed to get list of labels to run")