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
2 changes: 1 addition & 1 deletion .github/workflows/binaries.yml
Original file line number Diff line number Diff line change
Expand Up @@ -387,7 +387,7 @@ jobs:
id-token: write
contents: read
env:
CLOUDSMITH_ORG: ${{ vars.CLOUDSMITH_NAMESPACE }}
CLOUDSMITH_WORKSPACE: ${{ vars.CLOUDSMITH_NAMESPACE }}
CLOUDSMITH_SERVICE_SLUG: ${{ vars.CLOUDSMITH_SERVICE_SLUG }}
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/dev-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ jobs:
env:
CLOUDSMITH_NAMESPACE: ${{ vars.CLOUDSMITH_NAMESPACE }}
CLOUDSMITH_DEV_REPO: ${{ vars.CLOUDSMITH_DEV_REPO }}
CLOUDSMITH_ORG: ${{ vars.CLOUDSMITH_NAMESPACE }}
CLOUDSMITH_WORKSPACE: ${{ vars.CLOUDSMITH_NAMESPACE }}
CLOUDSMITH_SERVICE_SLUG: ${{ vars.CLOUDSMITH_SERVICE_SLUG }}
BRANCH: ${{ github.ref_name }}
steps:
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,7 @@ jobs:
env:
CLOUDSMITH_NAMESPACE: ${{ vars.CLOUDSMITH_NAMESPACE }}
CLOUDSMITH_REPO: ${{ vars.CLOUDSMITH_REPO }}
CLOUDSMITH_ORG: ${{ vars.CLOUDSMITH_NAMESPACE }}
CLOUDSMITH_WORKSPACE: ${{ vars.CLOUDSMITH_NAMESPACE }}
CLOUDSMITH_SERVICE_SLUG: ${{ vars.CLOUDSMITH_SERVICE_SLUG }}
VERSION: ${{ needs.validate.outputs.version }}
steps:
Expand Down Expand Up @@ -443,7 +443,7 @@ jobs:
CLOUDSMITH_NAMESPACE: ${{ vars.CLOUDSMITH_NAMESPACE }}
CLOUDSMITH_REPO: ${{ vars.CLOUDSMITH_REPO }}
CLOUDSMITH_SERVICE_SLUG: ${{ vars.CLOUDSMITH_SERVICE_SLUG }}
CLOUDSMITH_ORG: ${{ vars.CLOUDSMITH_NAMESPACE }}
CLOUDSMITH_WORKSPACE: ${{ vars.CLOUDSMITH_NAMESPACE }}
DOCKERHUB_USER: ${{ secrets.DOCKERHUB_USER }}
HAS_DOCKERHUB: ${{ secrets.DOCKERHUB_PAT != '' }}
VERSION: ${{ needs.validate.outputs.version }}
Expand Down
26 changes: 25 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,31 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
- Using `#!/usr/bin/env sh` as shebang path for unix credential helpers.
- The minimum version of `cloudsmith-api` is now 2.0.42.

### Fixed
### Deprecated

- The old names of renamed options, environment variables, and `config.ini` keys are deprecated, and version 2.0.0 will remove them. Until then, they work as before, but the CLI prints a warning to stderr, once per run, that names the replacement. For example: ``Warning: `--org` is deprecated and will be removed in version 2.0.0. Use `--workspace` instead.`` The old names no longer show in `--help`.

| Setting | Deprecated name | Replacement |
| -- | -- | -- |
| Proxy | `--api-proxy` | `--proxy` |
| | `CLOUDSMITH_API_PROXY` | `CLOUDSMITH_PROXY` |
| | `api_proxy` config key | `proxy` |
| | `--allowed-api-proxy-suffixes`, `CLOUDSMITH_ALLOWED_API_PROXY_SUFFIXES` (hidden) | `--allowed-proxy-suffixes`, `CLOUDSMITH_ALLOWED_PROXY_SUFFIXES` |
| SSL verification | `--without-api-ssl-verify`, `-S` | `--without-ssl-verify` |
| | `CLOUDSMITH_WITHOUT_API_SSL_VERIFY` | `CLOUDSMITH_SSL_VERIFY=0` |
| | `api_ssl_verify` config key | `ssl_verify` |
| Workspace | `--org`, `--organization`, `--oidc-org`, `--owner` | `--workspace` |
| | `-o` | `-w` |
| | `CLOUDSMITH_ORG` | `CLOUDSMITH_WORKSPACE` |
| | `org`, `organization`, `oidc_org` config keys | `workspace` |
| Pagination | `--show-all` | `--page-all` |
| `cloudsmith auth` | `--token`, `--force` | `--request-api-key` |
| | `--json` | `--output-format json` |

- The CLI warns only when an old name changes the result. It does not warn for a config key that the new key in the same section overrides, for an environment variable that the new variable overrides, for an empty value, or for `api_ssl_verify = true`, which is the default.
- The credential helpers that Docker, Cargo, pnpm, Terraform, and other tools run (`cloudsmith credential-helper docker`, `cargo`, `pnpm`, `terraform`, and `generic`) do not print the warning. This includes a `~/.terraformrc` block with `--org` that an earlier version of `cloudsmith credential-helper install terraform` wrote. To update the block, run the install again.
- `cloudsmith auth --token`, `--force`, and `--json`, and the `cloudsmith login` and `cloudsmith token` commands, now use the same warning format. The warning prefix is now `Warning:` instead of `DEPRECATION WARNING:`. `cloudsmith login` still stops working on November 20, 2026.
- When you give an old and a new option on the same command line, such as `--org a --workspace b`, the new option now wins. Previously, the last option won.

- `cloudsmith credential-helper install pnpm` now ends `.npmrc` with a newline. Previously, a line that a shell appended to the file, such as `echo '//host/:_authToken=...' >> ~/.npmrc`, joined the last `tokenHelper` line. This made the `tokenHelper` path incorrect, and npm did not read the appended token. The install now also shows each `tokenHelper` line that it sets and the file that it writes to. To add the newline to an existing `.npmrc`, run the install again. If a line is already joined, split it into two lines before you run the install. If you do not, the install removes the appended token.
- OIDC authentication now works when the CLI cannot create its config directory, for example in a CI container with a read-only home directory. Previously, the CLI did not exchange the OIDC token and ran the command without credentials. Now the CLI exchanges the token. If the CLI cannot write the token to the disk cache, it continues without the cache. A malformed entry in the disk cache no longer stops the token exchange.
Expand Down
9 changes: 5 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -323,10 +323,11 @@ See the [default config](https://raw.githubusercontent.com/cloudsmith-io/cloudsm
You can specify the following configuration options:

- `api_host`: The API host to connect to.
- `proxy`: The proxy to connect through for all HTTP requests, including API, SAML, upload, download, MCP, update, credential helper, OIDC, and error report connections. Use `--proxy` or `CLOUDSMITH_PROXY` to set it for one command. The config key `api_proxy`, flag `--api-proxy`, and environment variable `CLOUDSMITH_API_PROXY` remain supported for compatibility. In config and environment variables, the new name takes precedence over the alias. Without `proxy`, the CLI uses the `HTTPS_PROXY`, `HTTP_PROXY`, and `NO_PROXY` environment variables. When `proxy` is set, it takes precedence over them.
- `ssl_verify`: Whether or not to verify SSL certificates for HTTPS requests, including API, SAML, uploads, downloads, and MCP (default: `true`). Use `CLOUDSMITH_SSL_VERIFY=1` to enable verification or `CLOUDSMITH_SSL_VERIFY=0` / `--without-ssl-verify` to disable it. Disabling verification is dangerous; prefer disabling only strict checks where possible. The config key `api_ssl_verify`, flag `--without-api-ssl-verify` / `-S`, and environment variable `CLOUDSMITH_WITHOUT_API_SSL_VERIFY` remain supported for compatibility. Command-line settings override environment variables, which override config; within the same source, the new spelling takes precedence.
- `proxy`: The proxy to connect through for all HTTP requests, including API, SAML, upload, download, MCP, update, credential helper, OIDC, and error report connections. Use `--proxy` or `CLOUDSMITH_PROXY` to set it for one command. The config key `api_proxy`, flag `--api-proxy`, and environment variable `CLOUDSMITH_API_PROXY` still work, but they are deprecated and will be removed in version 2.0.0; the CLI prints a warning when you use them. The new name takes precedence over the old one. Without `proxy`, the CLI uses the `HTTPS_PROXY`, `HTTP_PROXY`, and `NO_PROXY` environment variables. When `proxy` is set, it takes precedence over them.
- `ssl_verify`: Whether or not to verify SSL certificates for HTTPS requests, including API, SAML, uploads, downloads, and MCP (default: `true`). Use `CLOUDSMITH_SSL_VERIFY=1` to enable verification or `CLOUDSMITH_SSL_VERIFY=0` / `--without-ssl-verify` to disable it. Disabling verification is dangerous; prefer disabling only strict checks where possible. The config key `api_ssl_verify`, flag `--without-api-ssl-verify` / `-S`, and environment variable `CLOUDSMITH_WITHOUT_API_SSL_VERIFY` still work, but they are deprecated and will be removed in version 2.0.0; the CLI prints a warning when you use them. Command-line settings override environment variables, which override config; within the same source, the new spelling takes precedence.
- `ssl_verify_strict`: Whether or not to apply strict X.509 certificate verification (`VERIFY_X509_STRICT`, enabled by default on Python 3.13+). Set to `false` (or use `--without-ssl-verify-strict` / `CLOUDSMITH_SSL_VERIFY_STRICT=0`) if a TLS-intercepting proxy issues certificates that fail these checks. `CLOUDSMITH_SSL_VERIFY_STRICT=1` keeps strict verification enabled (the default). Certificates are still verified.
- `api_user_agent`: The user agent to use for requests.
- `workspace`: The Cloudsmith Workspace slug. Use `-w`/`--workspace` or `CLOUDSMITH_WORKSPACE` to set it for one command. The config keys `org`, `organization`, and `oidc_org`, the flags `--org`, `--organization`, `--oidc-org`, `--owner`, and `-o`, and the environment variable `CLOUDSMITH_ORG` still work, but they are deprecated and will be removed in version 2.0.0; the CLI prints a warning when you use them.

### Credentials (credentials.ini)

Expand All @@ -345,7 +346,7 @@ You'll need to provide authentication to Cloudsmith for any CLI actions that res

You can authenticate using your organization's SAML provider, if configured, with the `cloudsmith auth` command:
```
cloudsmith auth --owner example
cloudsmith auth --workspace example
Beginning authentication for the example org ...
Your organization's SAML IDP URL is: https://example.com/some-saml-idp

Expand All @@ -357,7 +358,7 @@ Authentication complete
The CLI opens the IDP URL in your browser automatically. If it can't (for example in Cygwin or a headless shell), it tells you to open the URL above manually and carries on waiting for the callback. Use `--no-browser` to skip the automatic launch entirely:

```
cloudsmith auth --owner example --no-browser
cloudsmith auth --workspace example --no-browser
```

*Note:* The authentication callback is served on `127.0.0.1`, on the first free port between `12400` and `12404`. The browser you open the URL in must run on the same machine as the CLI.
Expand Down
23 changes: 4 additions & 19 deletions cloudsmith_cli/cli/commands/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
get_access_token_expiry,
renew_sso_session,
)
from .. import decorators, utils, validators
from .. import decorators, deprecation, utils, validators
from ..exceptions import handle_api_exceptions
from ..saml import get_idp_url
from ..webserver import AuthenticationWebRequestHandler, AuthenticationWebServer
Expand Down Expand Up @@ -248,28 +248,13 @@ def authenticate(
use_stderr = request_api_key_flag or json or utils.should_use_stderr(opts)

if token:
click.secho(
"DEPRECATION WARNING: The `--token` flag is deprecated and will be removed in a future release. "
"Please use `--request-api-key` instead.",
fg="yellow",
err=True,
)
deprecation.warn_option("--token", "--request-api-key", ctx=ctx)

if force:
click.secho(
"DEPRECATION WARNING: The `--force` flag is deprecated and will be removed in a future release. "
"Please use `--request-api-key` instead (force is implied).",
fg="yellow",
err=True,
)
deprecation.warn_option("--force", "--request-api-key", ctx=ctx)

if json and not utils.should_use_stderr(opts):
click.secho(
"DEPRECATION WARNING: The `--json` flag is deprecated and will be removed in a future release. "
"Please use `--output-format json` instead.",
fg="yellow",
err=True,
)
deprecation.warn_option("--json", "--output-format json", ctx=ctx)

if not force and not token and not request_api_key_flag:
session_result = _renew_existing_sso_session(
Expand Down
4 changes: 2 additions & 2 deletions cloudsmith_cli/cli/commands/credential_helper/cargo.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ def cargo(opts, cargo_plugin, provider_args): # pylint: disable=unused-argument
Provides credentials for all Cloudsmith Cargo registries: ``*.cloudsmith.io``,
``*.cloudsmith.com``, and any custom domains configured for the Workspace
(requires a Workspace - ``--workspace``, CLOUDSMITH_WORKSPACE, or
``workspace`` in ``config.ini``; legacy aliases are also accepted - and a
``workspace`` in ``config.ini``; deprecated aliases still work - and a
valid API key/token).

A registry that is not a Cloudsmith one is answered with
Expand Down Expand Up @@ -76,7 +76,7 @@ def cargo(opts, cargo_plugin, provider_args): # pylint: disable=unused-argument
\b
Environment variables:
CLOUDSMITH_API_KEY: API key for authentication (optional)
CLOUDSMITH_WORKSPACE: Workspace slug (CLOUDSMITH_ORG is also accepted)
CLOUDSMITH_WORKSPACE: Workspace slug (the deprecated CLOUDSMITH_ORG still works)
"""
# `provider_args` collects the extra arguments Cargo appends from the
# credential-provider config entry. This provider takes no configuration
Expand Down
4 changes: 2 additions & 2 deletions cloudsmith_cli/cli/commands/credential_helper/docker.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ def docker(opts, operation):
Provides credentials for all Cloudsmith Docker registries: ``*.cloudsmith.io``,
``*.cloudsmith.com``, and any custom domains configured for the Workspace
(requires a Workspace - ``--workspace``, CLOUDSMITH_WORKSPACE, or
``workspace`` in ``config.ini``; legacy aliases are also accepted - and a
``workspace`` in ``config.ini``; deprecated aliases still work - and a
valid API key/token).

\b
Expand Down Expand Up @@ -59,7 +59,7 @@ def docker(opts, operation):
\b
Environment variables:
CLOUDSMITH_API_KEY: API key for authentication (optional)
CLOUDSMITH_WORKSPACE: Workspace slug (CLOUDSMITH_ORG is also accepted)
CLOUDSMITH_WORKSPACE: Workspace slug (the deprecated CLOUDSMITH_ORG still works)
"""
exit_code, stdout, stderr = execute(
operation,
Expand Down
4 changes: 2 additions & 2 deletions cloudsmith_cli/cli/commands/credential_helper/pnpm.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
Provides credentials for all Cloudsmith npm registries: ``*.cloudsmith.io``,
``*.cloudsmith.com``, and any custom domains configured for the Workspace
(requires a Workspace - ``--workspace``, CLOUDSMITH_WORKSPACE, or
``workspace`` in ``config.ini``; legacy aliases are also accepted - and a
``workspace`` in ``config.ini``; deprecated aliases still work - and a
valid API key/token).

\b
Expand Down Expand Up @@ -59,12 +59,12 @@
\b
Environment variables:
CLOUDSMITH_API_KEY: API key for authentication (optional)
CLOUDSMITH_WORKSPACE: Workspace slug (CLOUDSMITH_ORG is also accepted)
CLOUDSMITH_WORKSPACE: Workspace slug (the deprecated CLOUDSMITH_ORG still works)
"""

exit_code, stdout, stderr = execute(
repo,
credential=opts.credential,

Check failure on line 67 in cloudsmith_cli/cli/commands/credential_helper/pnpm.py

View workflow job for this annotation

GitHub Actions / ty

ty (unresolved-attribute)

cloudsmith_cli/cli/commands/credential_helper/pnpm.py:67:20: unresolved-attribute: Object of type `Options` has no attribute `credential`
api_host=opts.api_host,
org=opts.org,
)
Expand Down
23 changes: 14 additions & 9 deletions cloudsmith_cli/cli/commands/login.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from ...core.api.exceptions import TwoFactorRequiredException
from ...core.api.user import get_user_token
from ...core.config import create_config_files, new_config_messaging
from .. import decorators, utils
from .. import decorators, deprecation, utils
from ..exceptions import handle_api_exceptions
from ..utils import maybe_spinner
from .main import main
Expand All @@ -26,9 +26,12 @@
"https://docs.cloudsmith.com/developer-tools/cli#getting-your-api-key"
)

DEPRECATION_WARNING = (
"Warning: `cloudsmith {name}` is deprecated and will stop working on "
"November 20, 2026, when Cloudsmith disables username and password login. "
DEPRECATION_REMOVAL = (
"will stop working on November 20, 2026, when Cloudsmith disables username "
"and password login"
)

DEPRECATION_DETAIL = (
"See https://cloudsmith.com/changelog\n"
"Use an API key or SAML instead: set `CLOUDSMITH_API_KEY` to an API key from "
"the Cloudsmith web app (your user menu, then Personal API Keys), or run "
Expand Down Expand Up @@ -64,11 +67,13 @@
def parse_args(self, ctx, args):
# Click prompts for --login and --password while parsing, so the
# warning must be printed here rather than in the command body.
# Shell completion also parses (resiliently) and must stay quiet.
if not ctx.resilient_parsing:
click.secho(
DEPRECATION_WARNING.format(name=ctx.info_name), fg="yellow", err=True
)
# Shell completion also parses (resiliently); the warning skips it.
deprecation.warn(
f"`cloudsmith {ctx.info_name}`",
removal=DEPRECATION_REMOVAL,
detail=DEPRECATION_DETAIL,
ctx=ctx,
)
return super().parse_args(ctx, args)


Expand Down Expand Up @@ -145,7 +150,7 @@
totp_token=totp_token,
two_factor_token=e.two_factor_token,
)
except cloudsmith_api.rest.ApiException:

Check warning on line 153 in cloudsmith_cli/cli/commands/login.py

View workflow job for this annotation

GitHub Actions / ty

ty (possibly-missing-submodule)

cloudsmith_cli/cli/commands/login.py:153:16: possibly-missing-submodule: Submodule `rest` might not have been imported help: Consider explicitly importing `cloudsmith_api.rest`
click.echo("\r\033[K", nl=False, err=use_stderr)
click.secho(
"Authentication failed: The entered TOTP token is not valid.",
Expand All @@ -154,7 +159,7 @@
)
ctx.exit(1)

except cloudsmith_api.rest.ApiException as e:

Check warning on line 162 in cloudsmith_cli/cli/commands/login.py

View workflow job for this annotation

GitHub Actions / ty

ty (possibly-missing-submodule)

cloudsmith_cli/cli/commands/login.py:162:12: possibly-missing-submodule: Submodule `rest` might not have been imported help: Consider explicitly importing `cloudsmith_api.rest`
click.echo("\r\033[K", nl=False, err=use_stderr)
click.secho(f"Authentication failed: {e!s}", fg="red", err=use_stderr)
ctx.exit(1)
Expand Down
16 changes: 15 additions & 1 deletion cloudsmith_cli/cli/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
from click_configfile import ConfigFileReader, Param, SectionSchema, matches_section

from ..core.utils import get_data_path, read_file
from . import utils, validators
from . import deprecation, utils, validators

OPTIONS = threading.local()

Expand Down Expand Up @@ -289,6 +289,14 @@ def _load_values_into_opts(opts, values):
else:
if v is None:
continue
setting = deprecation.CONFIG_KEY_REPLACEMENTS.get(k, k)
if setting in deprecation.CONFIG_KEY_REPLACEMENTS.values():
# Record which key set the value last, so a profile that sets
# the new key clears a deprecated key from [default]. The
# schema defaults api_ssl_verify to true, so only false shows
# that the key is set, and only false changes the result.
is_alias = k != setting and v is not True
opts.config_keys_in_effect[setting] = k if is_alias else None
setattr(opts, k, v)


Expand Down Expand Up @@ -412,9 +420,15 @@ def __init__(self, *args, **kwargs):
"""Initialise a new Options object."""
super().__init__(*args, **kwargs)
self.opts = {}
self.config_keys_in_effect = {}
for k, v in kwargs.items():
setattr(self, k, v)

@property
def deprecated_config_keys(self):
"""The deprecated config.ini keys whose values are in effect."""
return [key for key in self.config_keys_in_effect.values() if key]

@staticmethod
def get_config_reader():
"""Get the non-credentials config reader class."""
Expand Down
Loading
Loading