diff --git a/codecov-cli/codecov_cli/commands/upload.py b/codecov-cli/codecov_cli/commands/upload.py index 87380910..ebb75876 100644 --- a/codecov-cli/codecov_cli/commands/upload.py +++ b/codecov-cli/codecov_cli/commands/upload.py @@ -111,7 +111,10 @@ def _turn_env_vars_into_dict(ctx, params, value): "--name", help="Custom defined name of the upload. Visible in Codecov UI", cls=CodecovOption, - fallback_field=FallbackFieldEnum.build_code, + fallback_fields=( + FallbackFieldEnum.job_name, + FallbackFieldEnum.build_code, + ), ), click.option( "-B", diff --git a/codecov-cli/codecov_cli/fallbacks.py b/codecov-cli/codecov_cli/fallbacks.py index c7804ef6..099b391d 100644 --- a/codecov-cli/codecov_cli/fallbacks.py +++ b/codecov-cli/codecov_cli/fallbacks.py @@ -14,11 +14,31 @@ class FallbackFieldEnum(Enum): pull_request_number = auto() service = auto() slug = auto() + job_name = auto() + + +_FIELDS_WITH_VERSIONING_FALLBACK = frozenset( + { + FallbackFieldEnum.branch, + FallbackFieldEnum.commit_sha, + FallbackFieldEnum.slug, + FallbackFieldEnum.git_service, + } +) class CodecovOption(click.Option): + fallback_fields: typing.Optional[tuple[FallbackFieldEnum, ...]] + def __init__(self, *args, **kwargs): - self.fallback_field = kwargs.pop("fallback_field", None) + fallback_fields = kwargs.pop("fallback_fields", None) + fallback_field = kwargs.pop("fallback_field", None) + if fallback_fields is not None: + self.fallback_fields = tuple(fallback_fields) + elif fallback_field is not None: + self.fallback_fields = (fallback_field,) + else: + self.fallback_fields = None super().__init__(*args, **kwargs) def get_default( @@ -27,17 +47,19 @@ def get_default( res = super().get_default(ctx, call=call) if res is not None: return res - if self.fallback_field is not None: - if ctx.obj.get("ci_adapter") is not None: - res = ctx.obj.get("ci_adapter").get_fallback_value(self.fallback_field) - if res is not None: - return res - if ctx.obj.get("versioning_system") is not None: - res = ctx.obj.get("versioning_system").get_fallback_value( - self.fallback_field - ) - if res is not None: - return res + if self.fallback_fields is not None: + for field in self.fallback_fields: + if ctx.obj.get("ci_adapter") is not None: + res = ctx.obj.get("ci_adapter").get_fallback_value(field) + if res is not None: + return res + if ( + ctx.obj.get("versioning_system") is not None + and field in _FIELDS_WITH_VERSIONING_FALLBACK + ): + res = ctx.obj.get("versioning_system").get_fallback_value(field) + if res is not None: + return res return None diff --git a/codecov-cli/codecov_cli/helpers/ci_adapters/base.py b/codecov-cli/codecov_cli/helpers/ci_adapters/base.py index ace4af86..37af9420 100644 --- a/codecov-cli/codecov_cli/helpers/ci_adapters/base.py +++ b/codecov-cli/codecov_cli/helpers/ci_adapters/base.py @@ -17,6 +17,7 @@ def __init__(self): FallbackFieldEnum.pull_request_number: self._get_pull_request_number, FallbackFieldEnum.job_code: self._get_job_code, FallbackFieldEnum.git_service: self._get_git_service, + FallbackFieldEnum.job_name: self._get_job_name, } def get_fallback_value( @@ -100,3 +101,10 @@ def get_service_name(self): def _get_git_service(self): return None + + def _get_job_name(self): + """ + Name of the job that gets displayed in the Codecov UI + Returns: string + """ + return None \ No newline at end of file diff --git a/codecov-cli/codecov_cli/helpers/ci_adapters/github_actions.py b/codecov-cli/codecov_cli/helpers/ci_adapters/github_actions.py index fe1b4d71..5167fa10 100644 --- a/codecov-cli/codecov_cli/helpers/ci_adapters/github_actions.py +++ b/codecov-cli/codecov_cli/helpers/ci_adapters/github_actions.py @@ -87,3 +87,6 @@ def _get_service(self): def get_service_name(self): return "GithubActions" + + def _get_job_name(self): + return os.getenv("GITHUB_JOB") \ No newline at end of file diff --git a/codecov-cli/tests/factory.py b/codecov-cli/tests/factory.py index 4d8385bc..17149556 100644 --- a/codecov-cli/tests/factory.py +++ b/codecov-cli/tests/factory.py @@ -21,6 +21,7 @@ def __init__(self, values_dict: Optional[dict] = None): FallbackFieldEnum.service: "FAKE_PROVIDER", FallbackFieldEnum.pull_request_number: "PR_NUMBER", FallbackFieldEnum.job_code: "JOB_CODE", + FallbackFieldEnum.job_name: None, FallbackFieldEnum.git_service: "FAKE_PROVIDER", } final_values = {**default_values, **values_dict} @@ -53,6 +54,9 @@ def _get_pull_request_number(self): def _get_job_code(self): return self.values_dict[FallbackFieldEnum.job_code] + def _get_job_name(self): + return self.values_dict.get(FallbackFieldEnum.job_name) + def get_service_name(self): return self.values_dict[FallbackFieldEnum.git_service] diff --git a/codecov-cli/tests/test_fallbacks.py b/codecov-cli/tests/test_fallbacks.py index b0490431..b376e051 100644 --- a/codecov-cli/tests/test_fallbacks.py +++ b/codecov-cli/tests/test_fallbacks.py @@ -1,9 +1,9 @@ import click +from click.testing import CliRunner +from unittest.mock import MagicMock -from codecov_cli.fallbacks import BrandedOption from codecov_cli.branding import Branding - -from click.testing import CliRunner +from codecov_cli.fallbacks import BrandedOption, CodecovOption, FallbackFieldEnum @click.group() @@ -31,3 +31,63 @@ def test_branded_option(): result = runner.invoke(cli, ["hello-world"]) assert result.output == "None\n" + + +@click.group() +def codecov_cli_group(): + pass + + +@codecov_cli_group.command() +@click.option( + "--name", + cls=CodecovOption, + fallback_fields=( + FallbackFieldEnum.job_name, + FallbackFieldEnum.build_code, + ), +) +def with_name_fallback(name): + click.echo(name or "") + + +def test_codecov_option_fallback_fields_uses_second_when_first_is_none(): + runner = CliRunner() + adapter = MagicMock() + + def get_fallback(field): + return { + FallbackFieldEnum.job_name: None, + FallbackFieldEnum.build_code: "build-42", + }[field] + + adapter.get_fallback_value.side_effect = get_fallback + + result = runner.invoke( + codecov_cli_group, + ["with-name-fallback"], + obj={"ci_adapter": adapter, "versioning_system": None}, + ) + assert result.exit_code == 0 + assert result.output == "build-42\n" + + +def test_codecov_option_fallback_fields_prefers_first_when_set(): + runner = CliRunner() + adapter = MagicMock() + + def get_fallback(field): + return { + FallbackFieldEnum.job_name: "my-job", + FallbackFieldEnum.build_code: "build-42", + }[field] + + adapter.get_fallback_value.side_effect = get_fallback + + result = runner.invoke( + codecov_cli_group, + ["with-name-fallback"], + obj={"ci_adapter": adapter, "versioning_system": None}, + ) + assert result.exit_code == 0 + assert result.output == "my-job\n"