Skip to content

feat(ENG-15016): keep install-time settings in the Docker credential helper - #512

Draft
cloudsmith-iduffy wants to merge 19 commits into
masterfrom
iduffy/eng-15016-docker-launcher-settings
Draft

cloudsmith-iduffy wants to merge 19 commits into
masterfrom
iduffy/eng-15016-docker-launcher-settings

Conversation

@cloudsmith-iduffy

@cloudsmith-iduffy cloudsmith-iduffy commented Oct 21, 2026 •

Copy link
Copy Markdown
Collaborator

Description

credHelpers in ~/.docker/config.json holds only a helper name, and Docker runs the helper with the environment of the Docker client. So a CLOUDSMITH_PROFILE=meta cloudsmith credential-helper install docker was 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:

Install Launcher credHelpers value
no profile or Workspace docker-credential-cloudsmith cloudsmith
-P meta docker-credential-cloudsmith-meta cloudsmith-meta
-w acme docker-credential-cloudsmith-acme cloudsmith-acme
both docker-credential-cloudsmith-meta-acme cloudsmith-meta-acme
  • An install without a profile or a Workspace writes the same names as before. A scoped install does not touch the launcher of an earlier version.
  • credHelpers maps one host to one helper, so a host that two installs configure, such as docker.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 acme removes only that helper. A -P on the root command (cloudsmith -P meta credential-helper uninstall …) also counts, and -w has the same aliases as in install (--org, -o, …). The scope comes only from flags, so a plain uninstall docker removes the unscoped helper as before, also in a shell that sets CLOUDSMITH_WORKSPACE.
  • Two scopes can give the same name. Install and uninstall read the profile and the Workspace from the existing launcher, and stop if they differ. The error shows the uninstall command for that launcher.
  • list shows one Docker entry for each cloudsmith and cloudsmith-* helper, with its launcher and its hosts. A helper whose launcher is missing shows not installed.
  • --dry-run shows 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

  • Bug fix
  • New feature
  • Breaking change
  • Documentation update
  • Refactoring
  • Other (please describe)

Additional Notes

Testing. I ran a live docker pull of a private image on docker.cloudsmith.io with only a SAML session. A temporary config.ini sent the default profile to an API host with no SAML session, and profile e2e to api.cloudsmith.io. I installed with CLOUDSMITH_PROFILE=e2e, CLOUDSMITH_WORKSPACE and CLOUDSMITH_CONFIG_FILE into a temporary DOCKER_CONFIG and bin directory. Then:

  • env -i … docker pull with no CLOUDSMITH_* variables worked.
  • The same pull with CLOUDSMITH_PROFILE=default CLOUDSMITH_WORKSPACE=other in the caller environment also worked.
  • Without the helper, the pull failed with unauthorized.
  • uninstall docker -P e2e -w <workspace> removed the launcher and the credHelpers entry.

Known limits.

  • The name segments are sanitized to [a-z0-9-], so -P acme and -w acme both give cloudsmith-acme. These two installs cannot exist together: the second one stops with an error. Two installs that differ only in --api-host share 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.
  • The proxy and the colour settings are never written. Behind a proxy, set CLOUDSMITH_PROXY or HTTPS_PROXY where Docker runs.
  • The pnpm and cargo launchers do not keep install-time settings yet.

🤖 Generated with Claude Code

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 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

@cloudsmith-iduffy
cloudsmith-iduffy force-pushed the iduffy/eng-15016-docker-launcher-settings branch from 872e0f1 to a8be9d2 Compare October 21, 2026 14:22
@cloudsmith-iduffy
cloudsmith-iduffy force-pushed the iduffy/eng-15016-docker-launcher-settings branch from a8be9d2 to 97e3ea7 Compare October 21, 2026 15:09
@cloudsmith-iduffy
cloudsmith-iduffy force-pushed the iduffy/eng-15016-docker-launcher-settings branch from 97e3ea7 to 4e7814a Compare October 21, 2026 15:13
@cloudsmith-iduffy
cloudsmith-iduffy force-pushed the iduffy/eng-15016-docker-launcher-settings branch from 4e7814a to 8689c22 Compare October 21, 2026 15:34
@cloudsmith-iduffy
cloudsmith-iduffy removed this pull request from stack #513 October 21, 2026 16:30
@cloudsmith-iduffy
cloudsmith-iduffy force-pushed the iduffy/eng-15016-docker-launcher-settings branch from 8689c22 to aff4b9b Compare October 21, 2026 16:30
cloudsmith-iduffy and others added 3 commits October 21, 2026 18:10
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>
@cloudsmith-iduffy
cloudsmith-iduffy force-pushed the iduffy/eng-15016-docker-launcher-settings branch from aff4b9b to a3667bb Compare October 21, 2026 17:11
cloudsmith-iduffy and others added 2 commits October 21, 2026 23:19
- 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>
cloudsmith-iduffy and others added 2 commits October 28, 2026 11:42
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>
cloudsmith-iduffy and others added 3 commits October 28, 2026 12:02
-docker-launcher-settings

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>
* origin/master:
  fix(ENG-15484): accept --color on click versions before 8.2 (#582)
-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

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 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
cloudsmith-iduffy and others added 3 commits November 3, 2026 10:42
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>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 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

Base automatically changed from iduffy/launcher-install-time-settings to master November 3, 2026 10:51
cloudsmith-iduffy and others added 2 commits November 3, 2026 10:54
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>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 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>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 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>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Approval recommended

No active review issues are recorded for the credential-helper settings persistence and scoped-management changes.

1 open issue

Review effort: Balanced

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

2 participants