Skip to content

Commit f7cb28c

Browse files
feat: Use job for GHA CLI workflow name (#119)
Closes codecov/core-engineering#32 - adds a new fallback field `job_name` - adds a new `fallback_fields` argument to take multiple fields in a chain with precedence - uses GHA `GITHUB_JOB` and `GITHUB_WORKFLOW` as job_name first fallback before using job_code Uses the YAML job name as the first level of fallback for name before currently used `build_code` from GHA. This built in env does **NOT** use the "name" field for the job even if it is set by the user. This also does **NOT** have any info on the current matrix value. As a future option, we could possibly ask users to provide the matrix value through their YAML file with a `with` statement like: ``` with: matrix: ${{ toJSON(matrix) }} ```
1 parent 45e518e commit f7cb28c

6 files changed

Lines changed: 120 additions & 16 deletions

File tree

codecov-cli/codecov_cli/commands/upload.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,10 @@ def _turn_env_vars_into_dict(ctx, params, value):
111111
"--name",
112112
help="Custom defined name of the upload. Visible in Codecov UI",
113113
cls=CodecovOption,
114-
fallback_field=FallbackFieldEnum.build_code,
114+
fallback_fields=(
115+
FallbackFieldEnum.job_name,
116+
FallbackFieldEnum.build_code,
117+
),
115118
),
116119
click.option(
117120
"-B",

codecov-cli/codecov_cli/fallbacks.py

Lines changed: 34 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,31 @@ class FallbackFieldEnum(Enum):
1414
pull_request_number = auto()
1515
service = auto()
1616
slug = auto()
17+
job_name = auto()
18+
19+
20+
_FIELDS_WITH_VERSIONING_FALLBACK = frozenset(
21+
{
22+
FallbackFieldEnum.branch,
23+
FallbackFieldEnum.commit_sha,
24+
FallbackFieldEnum.slug,
25+
FallbackFieldEnum.git_service,
26+
}
27+
)
1728

1829

1930
class CodecovOption(click.Option):
31+
fallback_fields: typing.Optional[tuple[FallbackFieldEnum, ...]]
32+
2033
def __init__(self, *args, **kwargs):
21-
self.fallback_field = kwargs.pop("fallback_field", None)
34+
fallback_fields = kwargs.pop("fallback_fields", None)
35+
fallback_field = kwargs.pop("fallback_field", None)
36+
if fallback_fields is not None:
37+
self.fallback_fields = tuple(fallback_fields)
38+
elif fallback_field is not None:
39+
self.fallback_fields = (fallback_field,)
40+
else:
41+
self.fallback_fields = None
2242
super().__init__(*args, **kwargs)
2343

2444
def get_default(
@@ -27,17 +47,19 @@ def get_default(
2747
res = super().get_default(ctx, call=call)
2848
if res is not None:
2949
return res
30-
if self.fallback_field is not None:
31-
if ctx.obj.get("ci_adapter") is not None:
32-
res = ctx.obj.get("ci_adapter").get_fallback_value(self.fallback_field)
33-
if res is not None:
34-
return res
35-
if ctx.obj.get("versioning_system") is not None:
36-
res = ctx.obj.get("versioning_system").get_fallback_value(
37-
self.fallback_field
38-
)
39-
if res is not None:
40-
return res
50+
if self.fallback_fields is not None:
51+
for field in self.fallback_fields:
52+
if ctx.obj.get("ci_adapter") is not None:
53+
res = ctx.obj.get("ci_adapter").get_fallback_value(field)
54+
if res is not None:
55+
return res
56+
if (
57+
ctx.obj.get("versioning_system") is not None
58+
and field in _FIELDS_WITH_VERSIONING_FALLBACK
59+
):
60+
res = ctx.obj.get("versioning_system").get_fallback_value(field)
61+
if res is not None:
62+
return res
4163
return None
4264

4365

codecov-cli/codecov_cli/helpers/ci_adapters/base.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ def __init__(self):
1717
FallbackFieldEnum.pull_request_number: self._get_pull_request_number,
1818
FallbackFieldEnum.job_code: self._get_job_code,
1919
FallbackFieldEnum.git_service: self._get_git_service,
20+
FallbackFieldEnum.job_name: self._get_job_name,
2021
}
2122

2223
def get_fallback_value(
@@ -100,3 +101,10 @@ def get_service_name(self):
100101

101102
def _get_git_service(self):
102103
return None
104+
105+
def _get_job_name(self):
106+
"""
107+
Name of the job that gets displayed in the Codecov UI
108+
Returns: string
109+
"""
110+
return None

codecov-cli/codecov_cli/helpers/ci_adapters/github_actions.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,3 +87,10 @@ def _get_service(self):
8787

8888
def get_service_name(self):
8989
return "GithubActions"
90+
91+
def _get_job_name(self):
92+
github_workflow = os.getenv("GITHUB_WORKFLOW")
93+
github_job = os.getenv("GITHUB_JOB")
94+
if github_workflow and github_job:
95+
return f"{github_workflow} - {github_job}"
96+
return github_workflow or github_job or None

codecov-cli/tests/factory.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ def __init__(self, values_dict: Optional[dict] = None):
2121
FallbackFieldEnum.service: "FAKE_PROVIDER",
2222
FallbackFieldEnum.pull_request_number: "PR_NUMBER",
2323
FallbackFieldEnum.job_code: "JOB_CODE",
24+
FallbackFieldEnum.job_name: None,
2425
FallbackFieldEnum.git_service: "FAKE_PROVIDER",
2526
}
2627
final_values = {**default_values, **values_dict}
@@ -53,6 +54,9 @@ def _get_pull_request_number(self):
5354
def _get_job_code(self):
5455
return self.values_dict[FallbackFieldEnum.job_code]
5556

57+
def _get_job_name(self):
58+
return self.values_dict.get(FallbackFieldEnum.job_name)
59+
5660
def get_service_name(self):
5761
return self.values_dict[FallbackFieldEnum.git_service]
5862

codecov-cli/tests/test_fallbacks.py

Lines changed: 63 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import click
2+
from click.testing import CliRunner
3+
from unittest.mock import MagicMock
24

3-
from codecov_cli.fallbacks import BrandedOption
45
from codecov_cli.branding import Branding
5-
6-
from click.testing import CliRunner
6+
from codecov_cli.fallbacks import BrandedOption, CodecovOption, FallbackFieldEnum
77

88

99
@click.group()
@@ -31,3 +31,63 @@ def test_branded_option():
3131

3232
result = runner.invoke(cli, ["hello-world"])
3333
assert result.output == "None\n"
34+
35+
36+
@click.group()
37+
def codecov_cli_group():
38+
pass
39+
40+
41+
@codecov_cli_group.command()
42+
@click.option(
43+
"--name",
44+
cls=CodecovOption,
45+
fallback_fields=(
46+
FallbackFieldEnum.job_name,
47+
FallbackFieldEnum.build_code,
48+
),
49+
)
50+
def with_name_fallback(name):
51+
click.echo(name or "")
52+
53+
54+
def test_codecov_option_fallback_fields_uses_second_when_first_is_none():
55+
runner = CliRunner()
56+
adapter = MagicMock()
57+
58+
def get_fallback(field):
59+
return {
60+
FallbackFieldEnum.job_name: None,
61+
FallbackFieldEnum.build_code: "build-42",
62+
}[field]
63+
64+
adapter.get_fallback_value.side_effect = get_fallback
65+
66+
result = runner.invoke(
67+
codecov_cli_group,
68+
["with-name-fallback"],
69+
obj={"ci_adapter": adapter, "versioning_system": None},
70+
)
71+
assert result.exit_code == 0
72+
assert result.output == "build-42\n"
73+
74+
75+
def test_codecov_option_fallback_fields_prefers_first_when_set():
76+
runner = CliRunner()
77+
adapter = MagicMock()
78+
79+
def get_fallback(field):
80+
return {
81+
FallbackFieldEnum.job_name: "my-job",
82+
FallbackFieldEnum.build_code: "build-42",
83+
}[field]
84+
85+
adapter.get_fallback_value.side_effect = get_fallback
86+
87+
result = runner.invoke(
88+
codecov_cli_group,
89+
["with-name-fallback"],
90+
obj={"ci_adapter": adapter, "versioning_system": None},
91+
)
92+
assert result.exit_code == 0
93+
assert result.output == "my-job\n"

0 commit comments

Comments
 (0)