Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.

### Fixed

- The package metadata commands no longer wait without a limit for a v2 API that does not respond. A connection that takes more than 30 seconds fails. A request that gets no data for 30 seconds also fails, except a request to add or validate metadata. The CLI then retries the request as `--error-retry-max` sets.
- The CLI no longer sends the API key to another host when a request is redirected there. This applies to all API calls, uploads and downloads.

## [1.28.0] - 2026-10-07

### Added
Expand Down
12 changes: 9 additions & 3 deletions cloudsmith_cli/cli/commands/metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,9 @@ def list_metadata(
slug_perm = api_get_package_slug_perm(
owner=owner, repo=repo, identifier=package
)
entry = api_get_metadata(slug_perm, metadata_slug_perm)
entry = api_get_metadata(
slug_perm, metadata_slug_perm, credential=opts.credential
)

click.secho("OK", fg="green", err=use_stderr)
_print_metadata_entry(opts, entry)
Expand All @@ -208,6 +210,7 @@ def list_metadata(
page=page,
page_size=page_size,
package_slug_perm=slug_perm,
credential=opts.credential,
source_kind=source_kind,
classification=classification,
)
Expand Down Expand Up @@ -328,6 +331,7 @@ def add_metadata(
)
entry = api_create_metadata(
slug_perm,
credential=opts.credential,
content=metadata.content,
content_type=metadata.content_type,
source_identity=metadata.source_identity,
Expand Down Expand Up @@ -434,7 +438,9 @@ def update_metadata(
slug_perm = api_get_package_slug_perm(
owner=owner, repo=repo, identifier=package
)
entry = api_update_metadata(slug_perm, metadata_slug_perm, **patch_kwargs)
entry = api_update_metadata(
slug_perm, metadata_slug_perm, credential=opts.credential, **patch_kwargs
)

click.secho("OK", fg="green", err=use_stderr)
_print_metadata_entry(opts, entry)
Expand Down Expand Up @@ -496,7 +502,7 @@ def remove_metadata(ctx, opts, owner_repo_package, metadata_slug_perm, yes):
slug_perm = api_get_package_slug_perm(
owner=owner, repo=repo, identifier=package
)
api_delete_metadata(slug_perm, metadata_slug_perm)
api_delete_metadata(slug_perm, metadata_slug_perm, credential=opts.credential)

click.secho("OK", fg="green", err=use_stderr)

Expand Down
5 changes: 4 additions & 1 deletion cloudsmith_cli/cli/commands/push.py
Original file line number Diff line number Diff line change
Expand Up @@ -300,7 +300,9 @@ def validate_metadata_payload(

try:
with maybe_spinner(opts):
api_validate_metadata(content=content, content_type=content_type)
api_validate_metadata(
credential=opts.credential, content=content, content_type=content_type
)
except ApiException as exc:
http_status = getattr(exc, "status", None)
detail = (
Expand Down Expand Up @@ -385,6 +387,7 @@ def attach_metadata_to_package(
with maybe_spinner(opts):
entry = api_create_metadata(
slug_perm,
credential=opts.credential,
content=content,
content_type=content_type,
source_identity=source_identity,
Expand Down
12 changes: 8 additions & 4 deletions cloudsmith_cli/cli/tests/commands/test_metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import json
import unittest
from unittest.mock import patch
from unittest.mock import ANY, patch

import click
from click.testing import CliRunner
Expand Down Expand Up @@ -248,7 +248,7 @@ def test_single_fetch_calls_get_and_skips_list(
)

self.assertEqual(result.exit_code, 0, msg=result.output)
mock_get.assert_called_once_with("pkg-slug-perm", "meta-slug")
mock_get.assert_called_once_with("pkg-slug-perm", "meta-slug", credential=ANY)
mock_list.assert_not_called()

@patch("cloudsmith_cli.cli.commands.metadata.api_get_metadata")
Expand Down Expand Up @@ -654,7 +654,9 @@ def test_remove_calls_delete(self, mock_resolve, mock_delete):
)

self.assertEqual(result.exit_code, 0, msg=result.output)
mock_delete.assert_called_once_with("pkg-slug-perm", "meta-slug")
mock_delete.assert_called_once_with(
"pkg-slug-perm", "meta-slug", credential=ANY
)

@patch("cloudsmith_cli.cli.commands.metadata.api_delete_metadata")
@patch("cloudsmith_cli.cli.commands.metadata.api_get_package_slug_perm")
Expand All @@ -680,7 +682,9 @@ def test_remove_alias_rm(self, mock_resolve, mock_delete):
)

self.assertEqual(result.exit_code, 0, msg=result.output)
mock_delete.assert_called_once_with("pkg-slug-perm", "meta-slug")
mock_delete.assert_called_once_with(
"pkg-slug-perm", "meta-slug", credential=ANY
)

@patch("cloudsmith_cli.cli.commands.metadata.api_delete_metadata")
@patch("cloudsmith_cli.cli.commands.metadata.api_get_package_slug_perm")
Expand Down
27 changes: 16 additions & 11 deletions cloudsmith_cli/cli/tests/test_push.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import tempfile
import unittest
from types import SimpleNamespace
from unittest.mock import MagicMock, patch
from unittest.mock import ANY, MagicMock, patch

import click
import pytest
Expand Down Expand Up @@ -187,6 +187,7 @@ def test_upload_files_and_create_package_with_metadata(self):

mock_create_metadata.assert_called_once_with(
"slug-perm-abc",
credential=ANY,
content={"git_sha": "abc123"},
content_type="application/vnd.jfrog.buildinfo+json",
source_identity="github-actions@example",
Expand All @@ -204,7 +205,7 @@ def test_upload_files_and_create_package_with_json_null_metadata(self):
):
upload_files_and_create_package(
self.mock_ctx,
MagicMock(spec=[]),
MagicMock(spec=["credential"]),
self.package_type,
[self.owner, self.repo],
self.dry_run,
Expand Down Expand Up @@ -232,7 +233,7 @@ def test_upload_attach_publishes_metadata_info_to_opts(self):
"content": {"git_sha": "abc123"},
}

opts = MagicMock(spec=[])
opts = MagicMock(spec=["credential"])

with (
patch("cloudsmith_cli.cli.commands.push.validate_create_package"),
Expand Down Expand Up @@ -321,7 +322,7 @@ def test_upload_files_and_create_package_metadata_failure_warn_does_not_fail_pus
status=422, detail="Schema validation failed"
)

opts = MagicMock(spec=[])
opts = MagicMock(spec=["credential"])

# Push must complete without raising, returning the slug pair.
result = upload_files_and_create_package(
Expand Down Expand Up @@ -385,6 +386,7 @@ def test_upload_files_and_create_package_metadata_failure_default_aborts_push(se
patched_env.pop("CLOUDSMITH_METADATA_FAILURE_MODE", None)
opts = SimpleNamespace(
output=None,
credential=None,
push_metadata_info=None,
verbose=False,
debug=False,
Expand Down Expand Up @@ -444,6 +446,7 @@ def test_prevalidate_metadata_404_aborts_before_upload_by_default(self):
):
opts = SimpleNamespace(
output=None,
credential=None,
push_metadata_info=None,
verbose=False,
debug=False,
Expand Down Expand Up @@ -503,7 +506,7 @@ def test_prevalidate_metadata_warn_skips_attach_but_uploads_package(self):
{"CLOUDSMITH_METADATA_FAILURE_MODE": "warn"},
),
):
opts = MagicMock(spec=[])
opts = MagicMock(spec=["credential"])
result = upload_files_and_create_package(
self.mock_ctx,
opts,
Expand Down Expand Up @@ -558,7 +561,7 @@ def test_local_metadata_content_warn_skips_attach_but_uploads_package(self):
{"CLOUDSMITH_METADATA_FAILURE_MODE": "warn"},
),
):
opts = MagicMock(spec=[])
opts = MagicMock(spec=["credential"])
result = upload_files_and_create_package(
self.mock_ctx,
opts,
Expand Down Expand Up @@ -608,6 +611,7 @@ def test_prevalidate_metadata_default_aborts_before_upload(self):
patched_env.pop("CLOUDSMITH_METADATA_FAILURE_MODE", None)
opts = SimpleNamespace(
output=None,
credential=None,
push_metadata_info=None,
verbose=False,
debug=False,
Expand Down Expand Up @@ -895,7 +899,7 @@ def fake_get_text_stream(name):
for package_file in ("path-1", "path-2"):
upload_files_and_create_package(
self.mock_ctx,
MagicMock(spec=[]),
MagicMock(spec=["credential"]),
self.package_type,
[self.owner, self.repo],
self.dry_run,
Expand Down Expand Up @@ -947,7 +951,7 @@ def test_metadata_content_file_round_trip(self):
):
upload_files_and_create_package(
self.mock_ctx,
MagicMock(spec=[]),
MagicMock(spec=["credential"]),
self.package_type,
[self.owner, self.repo],
self.dry_run,
Expand Down Expand Up @@ -996,7 +1000,7 @@ def test_metadata_default_source_identity(self):
):
upload_files_and_create_package(
self.mock_ctx,
MagicMock(spec=[]),
MagicMock(spec=["credential"]),
self.package_type,
[self.owner, self.repo],
self.dry_run,
Expand All @@ -1018,7 +1022,7 @@ def test_metadata_default_source_identity(self):

def test_metadata_retry_hint_emitted_on_attach_warn_failure(self):
"""Attach failure (warn mode, file payload) routes to the hint helper."""
opts = SimpleNamespace(output=None, push_metadata_info=None)
opts = SimpleNamespace(output=None, credential=None, push_metadata_info=None)
metadata = ResolvedMetadata(
provided=True,
content={"x": 1},
Expand Down Expand Up @@ -1083,7 +1087,7 @@ def test_metadata_retry_hint_emitted_on_attach_warn_failure(self):

def test_metadata_retry_hint_emitted_on_validation_warn_failure(self):
"""Pre-validation warn failure routes to the hint helper after create."""
opts = SimpleNamespace(output=None, push_metadata_info=None)
opts = SimpleNamespace(output=None, credential=None, push_metadata_info=None)
metadata = ResolvedMetadata(
provided=True,
content={"x": 1},
Expand Down Expand Up @@ -1276,6 +1280,7 @@ def _hint_opts(output=None):
def _api_opts(output="json"):
return SimpleNamespace(
output=output,
credential=None,
push_metadata_info=None,
verbose=False,
debug=False,
Expand Down
Loading
Loading