Skip to content

feat(ENG-13683): add Maven credential helper - #339

Open
cloudsmith-iduffy wants to merge 1 commit into
masterfrom
maven-shell-plugin
Open

feat(ENG-13683): add Maven credential helper#339
cloudsmith-iduffy wants to merge 1 commit into
masterfrom
maven-shell-plugin

Conversation

@cloudsmith-iduffy

@cloudsmith-iduffycloudsmith-iduffy commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

Description

The last of the stack, rebased onto master now that #336, #337 and #338 have
landed, and squashed to a single commit.

Maven has no credential-helper protocol, so this authenticates it with a shell
plugin instead. credential-helper install maven --org <org> --repo <repo>
writes an mvn shim that wraps every invocation in cloudsmith exec, which
injects an ephemeral mode-0600 settings.xml via mvn -s and deletes it when
the run ends — no token is ever written to durable configuration.

  • credential-helper shell-init prints the shell initialisation (bash, zsh,
    fish) that puts the shims directory first on PATH.
  • cloudsmith exec -- <command> is callable directly, for CI that should not
    touch PATH.
  • Publishing is opt-in: install prints the distributionManagement snippet to
    add to pom.xml.
  • Custom download/upload domains are discovered from the organisation at
    install time. Domains bound to a single repository are left out — they need
    URLs of a different shape, which is its own change — so a repository-scoped
    domain falls back to the default host. Which kind each host is gets recorded
    in package-managers.ini, so wrapped runs need no lookup — the run path
    makes no API call and no cache read.

Credential binding. One <server> id covers download and upload. It
defaults to the literal cloudsmith, matching the distributionManagement
snippet install prints, so a team can share one pom.xml. Maven matches a
server's credentials to a repository by id alone, with no host check, so a
checked-out pom.xml declaring a repository under that id receives the token
on an ordinary mvn compile — the same exposure as the ~/.m2/settings.xml a
Maven user would otherwise keep. Pass --server-id at install time to bind
the credential to an id a third-party pom.xml cannot guess; minting a random
id by default would break a team sharing one pom.xml, so it is opt-in.
Documented in the CHANGELOG's Security section and settings.py's module
docstring.

Type of Change

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

Additional Notes

Test suite: 682 passed, 40 skipped at this commit.

An earlier, larger draft of this branch was verified end to end in GitHub
Actions with OIDC as the only credential
(run):
a plain mvn -B clean deploy resolved a private dependency through the
organisation's discovered custom domain and published the built jar to
maven.cloudsmith.io/iduffy-demo/default/. That draft carried a cached
settings.xml, a shell fast path and deploy-goal detection; this PR ships
without them to halve the diff, and the shim path itself is unchanged.

Two things a reviewer should know:

  • Wrapped runs do not consult ~/.m2/settings.xml; mirrors, proxies and other
    <server> entries are not seen. Passing your own -s/--settings runs Maven
    unwrapped, with a warning. This is the designed mechanism — Maven has no
    settings-merge — and is called out at install time and in the CHANGELOG.
  • One known limitation ships knowingly. The ephemeral settings.xml is removed
    in a finally, so a SIGKILL/SIGTERM of cloudsmith exec (a cancelled CI step)
    leaves the 0600 token file in /tmp until the agent is recycled; a signal
    handler is the fix and wants its own change.

@cloudsmith-iduffycloudsmith-iduffy changed the title maven shell pluginfeat(ENG-13683): add Maven credential helper via shell pluginAug 1, 2026
@BartoszBlizniak
BartoszBlizniak changed the base branch from credential-generic to custom-domainsAugust 3, 2026 11:45
cloudsmith-iduffy added a commit that referenced this pull request Aug 4, 2026
This branch shipped an API surface nothing in it calls. Every consumer is in
the Maven helper (#339), which is stacked on top of this branch, so the code
and its tests move there, where the first caller lives and where a reviewer
can see what they are for.
Moved: select_custom_domain and CustomDomain.serves_repository; the
single-host resolvers default_host, default_host_for_type, builtin_host and
builtin_host_for_type with their two private helpers; and domain_scope.
With them go the twelve select_custom_domain tests, the six host-resolver
tests and the persisted-scope test.
Staying: _precedence_key and the CustomDomain.scope / is_bound_to pair it
reads, because get_format_domains sorts by it and the Docker installer and
runtime helper both call that. The DomainScope enum stays with them; only
its string parser moves.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Base automatically changed from custom-domains to masterAugust 9, 2026 01:15
@cloudsmith-iduffy
cloudsmith-iduffyforce-pushed the maven-shell-plugin branch 2 times, most recently from d58abdb to da913d2CompareAugust 9, 2026 02:35
@cloudsmith-iduffycloudsmith-iduffy changed the title feat(ENG-13683): add Maven credential helper via shell pluginfeat(ENG-13683): add Maven credential helperAug 16, 2026
@cloudsmith-iduffy
cloudsmith-iduffy marked this pull request as ready for review August 16, 2026 20:09
@cloudsmith-iduffy
cloudsmith-iduffy requested a review from a team as a code ownerAugust 16, 2026 20:09
CopilotAI lite review requested due to automatic review settings August 16, 2026 20:09

CopilotAI 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.

Pull request overview

Adds Maven support to the credential-helper stack by installing an mvn shim that routes through a new cloudsmith exec command, which injects an ephemeral settings.xml (mode 0600) for authenticated dependency resolution and optional publishing, and cleans it up after each run. This extends the existing credential-helper infrastructure (launchers + default/custom domain handling) and adds targeted CLI wiring plus tests.

Changes:

  • Added Maven credential-helper implementation (binding persistence, domain selection, settings.xml generation, and runtime wrapper that injects -s <temp settings.xml> for Maven invocations).
  • Added cloudsmith exec -- <command> and cloudsmith credential-helper shell-init to support shim-based workflows and CI usage without PATH mutation.
  • Refactored/extended helper plumbing for frozen builds and default-domain resolution, plus comprehensive tests and CHANGELOG entries.

Reviewed changes

Copilot reviewed 21 out of 21 changed files in this pull request and generated 2 comments.

Show a summary per file
FileDescription
cloudsmith_cli/templates/maven_settings.xml.tmplNew template for injected Maven settings.xml (server credentials + active profile + repo URLs).
cloudsmith_cli/templates/maven_distribution_management.xml.tmplNew template for opt-in distributionManagement snippet for publishing.
cloudsmith_cli/credential_helpers/maven/settings.pyBuilds and securely writes ephemeral settings.xml; constructs download/upload URLs.
cloudsmith_cli/credential_helpers/maven/runner.pyImplements runtime command wrapper (cloudsmith exec) and Maven detection/settings injection logic.
cloudsmith_cli/credential_helpers/maven/installer.pyImplements install/uninstall/status for Maven shim and repository/domain binding.
cloudsmith_cli/credential_helpers/maven/config.pyPersists Maven binding in package-managers.ini and defines shims directory.
cloudsmith_cli/credential_helpers/maven/init.pyAdds Maven helper package entry point/module doc.
cloudsmith_cli/credential_helpers/launchers.pyAdds frozen-build-safe command construction and public launcher filename helper.
cloudsmith_cli/credential_helpers/docker/installer.pySwitches Docker launcher target command to shared cloudsmith_command(...) helper.
cloudsmith_cli/credential_helpers/default_domains.pyAdds default_host/default_host_for_type helpers that honor trusted [domains] overrides strictly.
cloudsmith_cli/credential_helpers/common.pyAdds default-host detection and repository path shaping based on host kind.
cloudsmith_cli/cli/tests/commands/test_default_domains.pyAdds coverage for new default-host resolution semantics (including strict override behavior).
cloudsmith_cli/cli/tests/commands/test_credential_helper_maven.pyNew tests for Maven settings generation, binding persistence, and exec/shim runtime behavior.
cloudsmith_cli/cli/tests/commands/test_credential_helper_maven_installer.pyNew tests for Maven install/uninstall/list wiring and domain-binding behavior.
cloudsmith_cli/cli/tests/commands/conftest.pyAdds fixtures for isolated CLI config dir and a resolved credential for helper tests.
cloudsmith_cli/cli/commands/exec_.pyNew cloudsmith exec Click command that resolves credentials and runs the wrapper.
cloudsmith_cli/cli/commands/credential_helper/shell.pyNew credential-helper shell-init command for PATH initialization snippets (bash/zsh/fish).
cloudsmith_cli/cli/commands/credential_helper/manage.pyWires Maven into credential-helper install/uninstall; adds --repo and --server-id.
cloudsmith_cli/cli/commands/credential_helper/init.pyRegisters shell-init and updates help text/examples to include Maven + generic usage.
cloudsmith_cli/cli/commands/init.pyRegisters new exec_ command module for CLI import-time command registration.
CHANGELOG.mdDocuments new Maven helper, exec, and shell-init, plus Maven server-id security note.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +80 to +81
if not (org and credential):
return []
Comment on lines +118 to +122
def _write(parser: configparser.ConfigParser) -> None:
path = config_path()
path.parent.mkdir(parents=True, exist_ok=True)
with click.open_file(str(path), "w") as handle:
parser.write(handle)
Maven has no credential-helper protocol, so `credential-helper install
maven --org <org> --repo <repo>` writes an `mvn` shim that wraps every
invocation in `cloudsmith exec`. Wrapped runs get a mode-0600
settings.xml injected via `mvn -s` and deleted when the run ends, so
dependency resolution authenticates with no pom.xml edits and nothing is
ever written to ~/.m2.
`cloudsmith exec -- <command>` is the same machinery callable directly,
for CI that would rather not touch PATH. `credential-helper shell-init`
prints the PATH line that activates the shims directory.
Publishing stays opt-in: install prints the distributionManagement
snippet to paste into pom.xml, keyed by the same `<server>` id the
generated settings.xml uses (`cloudsmith`, or `--server-id`).
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for freeto 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.

3 participants

@cloudsmith-iduffy@github-advanced-security