Repository navigation
feat(ENG-15016): keep install-time settings in the Docker credential helper - #512
cloudsmith-iduffy wants to merge 19 commits into
Conversation
There was a problem hiding this comment.
🟡 Changes recommended
Helper-name collisions and ignored root-level profile flags can overwrite or remove the wrong installation.
2 open issues
What changed in this PR
Preserves install-time settings in Docker credential helpers so they behave consistently across shells, IDEs, and Docker Desktop.
Changes:
- Adds profile/workspace-scoped launchers with persisted settings.
- Supports scoped uninstall and status reporting.
- Adds regression tests and changelog documentation.
| File | Description |
|---|---|
| cloudsmith_cli/credential_helpers/docker/installer.py | Persists settings and manages scoped helpers. |
| cloudsmith_cli/cli/tests/commands/test_credential_helper_install.py | Tests scoped installation, dry runs, removal, and status. |
| cloudsmith_cli/cli/commands/credential_helper/manage.py | Passes explicit uninstall scope to Docker. |
| CHANGELOG.md | Documents settings persistence and scoped helper behavior. |
Review effort: Balanced
872e0f1 to
a8be9d2
Compare
a8be9d2 to
97e3ea7
Compare
97e3ea7 to
4e7814a
Compare
4e7814a to
8689c22
Compare
8689c22 to
aff4b9b
Compare
The keyring module read CLOUDSMITH_NO_KEYRING, CLOUDSMITH_KEYRING_BACKEND, CLOUDSMITH_KEYRING_KEY, CLOUDSMITH_KEYRING_FILE_PATH and CLOUDSMITH_KEYRING_DIR with os.environ.get(). These settings have no flags, so Click did not know about them. - `keyring_options` on the `main` group adds hidden options that read the five variables. It gives the values to the keyring module through `keyring.configure(KeyringSettings(...))`. - The keyring module reads its settings from `configure()`, not from the environment. It still copies them to PYTHON_KEYRING_BACKEND and KEYRING_PROPERTY_*, because the keyring library reads only those. - The path settings are expanded and made absolute when Click parses them, not when the keyring uses them. - CLOUDSMITH_NO_KEYRING keeps its parsing: only 1, true and yes, in any case, turn off the keyring. Other values leave it on, as before. - A root conftest fixture resets the settings before each test, so tests no longer depend on the keyring variables of the developer shell. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ttings A package manager can run its credential helper from an IDE, Docker Desktop or a build agent that does not have the environment of the install. This change adds the shared code that writes the install-time CLOUDSMITH_* settings into a launcher. - `write_launcher(env=...)` in `launchers.py` writes the settings as `export` lines, or as `set` lines in a Windows batch file. The settings replace the values of the caller. - `batch_value()` refuses a value that a batch file cannot hold. A non-ASCII path under the user profile becomes `%USERPROFILE%`. - `install` passes `launcher_env` to an installer that sets `KEEPS_INSTALL_TIME_SETTINGS`. It holds each option with a CLOUDSMITH_* environment variable whose value came from a flag or the environment, also on a parent command, so the keyring settings of `main` are kept too. The API key, the custom headers, the keyring key and the proxy never go into it. No installer sets `KEEPS_INSTALL_TIME_SETTINGS` yet. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…helper Docker runs `docker-credential-cloudsmith` with the environment of the Docker client, for example Docker Desktop or an IDE. The flags and CLOUDSMITH_* variables of the install were lost, so later pulls used another profile, Workspace or API host. - The Docker installer now keeps the install-time settings in the launcher. - An install with a profile or a Workspace writes its own helper, `cloudsmith-<profile>-<workspace>`, so that installs can coexist. An install without them writes `cloudsmith`, as before. - The install shows a warning when it moves a host from a different Cloudsmith helper. - `uninstall docker -P <profile> -w <workspace>` removes only the helper of that install. The scope comes only from flags, so that a plain `uninstall docker` removes the unscoped helper, as before. - `list` shows the hosts of all Cloudsmith helpers and the launcher that they use. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
aff4b9b to
a3667bb
Compare
- Read the profile and the Workspace of an install from its launcher settings. - Stop an install or an uninstall when the launcher with that name has a different profile or Workspace, for example `-P acme` and `-w acme`. - Take the uninstall profile also from a `-P` on a parent command. - Do not keep the colour settings in a launcher. A `ColorMode` value caused a TypeError in the install. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Show one `list` entry for each Cloudsmith Docker helper, with its launcher and its hosts. A host whose helper has no launcher shows "not installed". - Accept `--org`, `--organization`, `-o` and `--owner` for the Workspace in `uninstall`, as `install` does. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The pnpm install wrote .npmrc without a final newline. A shell append such as `echo ... >> ~/.npmrc` then joined the last tokenHelper line. Write a final newline after each line. Add the newline to an existing .npmrc that does not have one. Show each tokenHelper line that a real install sets, as the dry run does. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: cloudsmith-iduffy <178375997+cloudsmith-iduffy@users.noreply.github.com>
…ttings A package manager can run its credential helper from an IDE, Docker Desktop or a build agent that does not have the environment of the install. This change adds the shared code that writes the install-time CLOUDSMITH_* settings into a launcher. - `write_launcher(env=...)` in `launchers.py` writes the settings as `export` lines, or as `set` lines in a Windows batch file. The settings replace the values of the caller. - `batch_value()` refuses a value that a batch file cannot hold. A non-ASCII path under the user profile becomes `%USERPROFILE%`. - `install` passes `launcher_env` to an installer that sets `KEEPS_INSTALL_TIME_SETTINGS`. It holds each option with a CLOUDSMITH_* environment variable whose value came from a flag or the environment, also on a parent command, so the keyring settings of `main` are kept too. The API key, the custom headers, the keyring key and the proxy never go into it. No installer sets `KEEPS_INSTALL_TIME_SETTINGS` yet. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…542) Do not retry a request when DNS reports that the API host or the proxy host does not exist. Retry a temporary DNS failure as before. Write one line to stderr when the CLI waits before a backoff retry. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…ttings A package manager can run its credential helper from an IDE, Docker Desktop or a build agent that does not have the environment of the install. This change adds the shared code that writes the install-time CLOUDSMITH_* settings into a launcher. - `write_launcher(env=...)` in `launchers.py` writes the settings as `export` lines, or as `set` lines in a Windows batch file. The settings replace the values of the caller. - `batch_value()` refuses a value that a batch file cannot hold. A non-ASCII path under the user profile becomes `%USERPROFILE%`. - `install` passes `launcher_env` to an installer that sets `KEEPS_INSTALL_TIME_SETTINGS`. It holds each option with a CLOUDSMITH_* environment variable whose value came from a flag or the environment, also on a parent command, so the keyring settings of `main` are kept too. The API key, the custom headers, the keyring key and the proxy never go into it. No installer sets `KEEPS_INSTALL_TIME_SETTINGS` yet. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
-docker-launcher-settings * origin/iduffy/launcher-install-time-settings: (27 commits) fix(ENG-15484): accept --color on click versions before 8.2 (#582) feat(ENG-15016): let credential-helper launchers keep install-time settings fix(no-ticket): stop retrying unknown hosts and show backoff retries (#542) fix(no-ticket): stop retrying unknown hosts and show backoff retries (#542) fix(no-ticket): stop credential helpers from overwriting config files that do not parse (#540) fix(no-ticket): remove the pnpm launcher when .npmrc does not exist (#539) fix(no-ticket): keep the user's Cargo registry credential provider (#532) build(deps): bump pyasn1 (#580) build(deps): bump the actions-all group with 4 updates (#577) build(deps): bump the uv-all group with 7 updates (#578) build(deps): bump alpine from `294b683` to `be26560` (#576) fix(no-ticket): keep credentials.ini sections and save missing API keys (#533) fix(no-ticket): set a default timeout on each API request (#538) fix(no-ticket): print the result count when a list is empty (#545) fix(no-ticket): escape the args in the terraformrc block (#543) fix(no-ticket): show the dependencies help for list deps (#546) fix(no-ticket): show a correct summary for the pnpm credential helper (#547) fix(no-ticket): show a clean error when the SAML IdP response is not a JSON object (#559) fix(no-ticket): show the API error when mcp cannot load the API schema (#561) fix(no-ticket): treat an empty trailing OWNER/REPO part as not given (#550) ... # Conflicts: # cloudsmith_cli/cli/commands/credential_helper/manage.py # cloudsmith_cli/cli/tests/commands/test_credential_helper_install.py # cloudsmith_cli/credential_helpers/docker/installer.py # cloudsmith_cli/credential_helpers/launchers.py
There was a problem hiding this comment.
🟡 Changes recommended
Unresolved issues can override the effective TLS setting, block reinstall and uninstall for newline-containing config paths, and direct conflict recovery at the wrong launcher directory.
3 open issues
2 resolved since last review
Review effort: Balanced
| # Real install | ||
| launcher_path = write_launcher( | ||
| target_dir, self.LAUNCHER_NAME, helper_command(self.name) | ||
| target_dir, launcher_name, helper_command(self.name), env=launcher_env |
Keep the error report controls after the install tests clear the CLOUDSMITH_* variables. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ker-launcher-settings Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Parse the whole Unix launcher, so a value with a line break does not stop a reinstall or an uninstall. Prove that the launcher keeps the resolved TLS setting. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
There was a problem hiding this comment.
🟡 Changes recommended
Unresolved profile-precedence mismatches can target the wrong helper during uninstall, Windows recovery commands use incompatible quoting, and uninstall rejects the supported --oidc-org alias.
4 open issues
2 resolved since last review
Review effort: Balanced
Install, the launcher settings and uninstall now use the same profile. Accept --oidc-org in uninstall. Quote the conflict command for cmd.exe on Windows. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
There was a problem hiding this comment.
🟡 Changes recommended
The suggested Windows uninstall command fails to safely quote cmd.exe metacharacters in scope values and the bin directory, allowing ampersands to break uninstallation and trigger unintended commands.
3 open issues
2 resolved since last review
Review effort: Balanced
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
There was a problem hiding this comment.
🔵 Needs a closer look
Human assessment is needed because the published review omits an empty-profile handling issue that can cause the suggested uninstall command to repeat the same conflict.
1 open issue
2 resolved since last review
Review effort: Balanced
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>


Description
credHelpersin~/.docker/config.jsonholds only a helper name, and Docker runs the helper with the environment of the Docker client. So aCLOUDSMITH_PROFILE=meta cloudsmith credential-helper install dockerwas forgotten on the next pull from Docker Desktop, an IDE or a new terminal (ENG-15016).The Docker installer now keeps the install-time settings in its launcher (see #511). An install with a profile or a Workspace also gets its own helper:
credHelpersvaluedocker-credential-cloudsmithcloudsmith-P metadocker-credential-cloudsmith-metacloudsmith-meta-w acmedocker-credential-cloudsmith-acmecloudsmith-acmedocker-credential-cloudsmith-meta-acmecloudsmith-meta-acmecredHelpersmaps one host to one helper, so a host that two installs configure, such asdocker.cloudsmith.io, uses the last install. The install shows a warning when it moves a host from a different Cloudsmith helper.uninstall docker -P meta -w acmeremoves only that helper. A-Pon the root command (cloudsmith -P meta credential-helper uninstall …) also counts, and-whas the same aliases as ininstall(--org,-o, …). The scope comes only from flags, so a plainuninstall dockerremoves the unscoped helper as before, also in a shell that setsCLOUDSMITH_WORKSPACE.listshows one Docker entry for eachcloudsmithandcloudsmith-*helper, with its launcher and its hosts. A helper whose launcher is missing showsnot installed.--dry-runshows the scoped launcher and the names of the settings that it keeps.The ticket proposed CLI flags in the launcher. I used environment variables instead, so Docker and NuGet share one mechanism and the keyring settings, which have no flags, are kept too.
Type of Change
Additional Notes
Testing. I ran a live
docker pullof a private image ondocker.cloudsmith.iowith only a SAML session. A temporaryconfig.inisent the default profile to an API host with no SAML session, and profilee2etoapi.cloudsmith.io. I installed withCLOUDSMITH_PROFILE=e2e,CLOUDSMITH_WORKSPACEandCLOUDSMITH_CONFIG_FILEinto a temporaryDOCKER_CONFIGand bin directory. Then:env -i … docker pullwith noCLOUDSMITH_*variables worked.CLOUDSMITH_PROFILE=default CLOUDSMITH_WORKSPACE=otherin the caller environment also worked.unauthorized.uninstall docker -P e2e -w <workspace>removed the launcher and thecredHelpersentry.Known limits.
[a-z0-9-], so-P acmeand-w acmeboth givecloudsmith-acme. These two installs cannot exist together: the second one stops with an error. Two installs that differ only in--api-hostshare a name, and the last install wins. The ticket suggested a hash suffix for this. I left it out, because it makes the names hard to predict and to uninstall.CLOUDSMITH_PROXYorHTTPS_PROXYwhere Docker runs.🤖 Generated with Claude Code