Skip to content

RTECO-1536 - Add native RubyGems/Bundler command with auth and build-info - #499

Open
agrasth wants to merge 27 commits into
mainfrom
RTECO-0000-rubygems-native-support
Open

RTECO-1536 - Add native RubyGems/Bundler command with auth and build-info#499
agrasth wants to merge 27 commits into
mainfrom
RTECO-0000-rubygems-native-support

Conversation

@agrasth

@agrasth agrasth commented Jun 29, 2026

Copy link
Copy Markdown
Collaborator

Summary

Implements the core business logic for the jf ruby gem|bundle native command in jfrog-cli-artifactory, covering authentication injection, repository discovery, native tool execution, and build-info collection.

Depends on: jfrog/build-info-go#394

What is included in this PR

  • RubyCommand dispatcher: Routes to gem or bundle with full stdio passthrough
  • Authentication injection (non-destructive, per-process env vars only):
    • Bundler: BUNDLE_<HOST> with full host-key normalization (. -> __, - -> ___, etc.)
    • RubyGems: GEM_HOST_API_KEY with proper Basic base64(user:pass) encoding
    • Embedded URL credentials for gem install/fetch --source (for index downloads)
    • Respects existing native credentials (skips injection if already set)
    • Host-match safety: warns and skips when source host differs from jf server host
    • Help bypass: no auth for help/-h/--help requests
    • Skips injection when no Artifactory source is discovered (even with --server-id)
  • --repo flag: Constructs the full Artifactory gems API URL from server config + repo name, eliminating the need to pass full URLs. Auto-injects --source/--host for gem install/push.
  • Repository discovery (precedence: --repo (constructed URL) > --source/--host arg > Gemfile source > gem sources --list)
  • Build-info collection:
    • Dependencies: via RubygemsFlexPack for bundle install/update/lock/add and opportunistic gem install/fetch
    • Artifacts: gem build (local .gem checksums) and gem push (+ build property tagging in Artifactory)
    • AQL checksum enrichment: single batched query with $or clauses for all deps
    • Dependency repo path included in build-info output
  • Unit tests covering auth, repo extraction, help detection, host matching, error handling, credential embedding, URL construction, and source arg injection

Bug fixes in this update

  • Bug 1: GEM_HOST_API_KEY now encodes as Basic base64(user:pass) — Artifactory expects this format
  • Bug 2: gem install/fetch embeds credentials directly in --source URL for index downloads (specs.4.8.gz) since GEM_HOST_API_KEY is not used for those requests in RubyGems 3.x
  • Bug 3: Explicit no-args error before help bypass — prevents silent fallthrough to gem help
  • Bug 4: Skips credential injection when no Artifactory gem source is discovered (even with --server-id)
  • Dep path: Build-info dependencies now include the Artifactory repository path

Auth level

Server-level (host-level) — one credential covers all repos on the same Artifactory instance. Not repo-level.

What will be in follow-up PRs

  • Future enhancements: Local gem cache checksum scan, scope classification (prod/dev/test), jf setup integration for Ruby, paginated AQL, cached build-info reuse, multi-source Gemfile auth

Test plan

  • go test ./artifactory/commands/ruby/... -v - all tests pass
  • go build ./... - compiles successfully (with local build-info-go replace)
  • go vet ./artifactory/commands/ruby/... - no issues
  • End-to-end testing with real Artifactory gems repo

Summary by CodeRabbit

  • New Features

    • Added native RubyGems and Bundler command execution with authenticated repository access.
    • Added Ruby dependency and artifact build-info collection.
    • Enhanced jf setup ruby to configure Bundler credentials and RubyGems sources automatically.
    • Preserved existing Ruby configuration while supporting multiple repositories and secure credentials.
  • Bug Fixes

    • Improved credential handling and cleanup.
    • Corrected configuration updates for multiple repositories and credential rotation.
  • Tests

    • Expanded coverage for Ruby execution, authentication, setup, dependency parsing, and configuration handling.

Implements the core business logic for `jf ruby gem|bundle` native command:

- RubyCommand dispatcher: routes to gem or bundle with stdio passthrough
- Authentication injection:
  - Bundler: BUNDLE_<HOST> env var with proper host-key normalization
  - gem: GEM_HOST_API_KEY env var
  - Non-destructive, per-process only, respects existing native credentials
  - Host-match safety: skips injection when source host differs from server
  - Help bypass: no auth for help/-h/--help requests
- Repository discovery: --source/--host args > Gemfile source > gem sources
- Build-info collection:
  - Dependencies: via RubygemsFlexPack for bundle install/update/lock/add
    and opportunistic gem install/fetch
  - Artifacts: gem build (local .gem checksums) and gem push (+ property tagging)
  - AQL checksum enrichment: single batched query for all deps
- 10 unit tests covering auth, repo extraction, help detection, host matching

Note: go.mod contains local replace directive for build-info-go (development only,
to be replaced with proper version bump at merge time).

Co-authored-by: Cursor <cursoragent@cursor.com>
@agrasth agrasth changed the title RTECO-0000: Add native RubyGems/Bundler command with auth and build-info RTECO-1536 - Add native RubyGems/Bundler command with auth and build-info Jun 29, 2026
@agrasth agrasth added the improvement Automatically generated release notes label Jun 29, 2026
Bug fixes:
- Bug 1: GEM_HOST_API_KEY now uses Basic base64 encoding (was raw
  user:pass which Artifactory rejected)
- Bug 2: gem install/fetch embeds credentials in --source URL for
  index downloads (specs.4.8.gz) since GEM_HOST_API_KEY is not used
  for those requests in RubyGems 3.x
- Bug 3: explicit no-args error before help bypass prevents silent
  fallthrough to gem help
- Bug 4: skip credential injection when no Artifactory source is
  discovered (even with --server-id)

New feature:
- --repo flag constructs the full Artifactory gems API URL from
  server config + repo name, eliminating the need to pass full URLs.
  For gem install/push, injects --source/--host automatically.

Also enriches dependency repo path from AQL results in build-info.

Co-authored-by: Cursor <cursoragent@cursor.com>
Scope classification:
- New parseGemfileGroups() parses Gemfile group blocks and inline groups
- Classifies gems as production/development/test
- Transitive deps inherit scopes from their parents
- Scopes passed to build-info for Xray scanning

jf setup integration:
- Added project.Ruby to packageManagerToRepositoryPackageType (Gems)
- New configureRuby() method configures Bundler credentials via
  'bundle config set' and adds source to ~/.gemrc
- Ruby now appears in 'jf setup' supported package managers list

Co-authored-by: Cursor <cursoragent@cursor.com>
…rovements

- Remove gem build from artifact collection (local-only, no Artifactory path)
- Only gem push now records artifacts in build-info
- Implement hybrid checksum strategy: local gem cache first, AQL fallback
- Add gem push auth via temporary ~/.gem/credentials (all RubyGems versions)
- Embed credentials in --source URL for gem install/fetch index downloads
- Strip trailing slash from --host to prevent 405 double-slash issue
- Skip auth injection entirely for gem build (pure local operation)
- Remove dead parseGemBuildOutputFile function and its tests

Co-authored-by: Cursor <cursoragent@cursor.com>
bundle lock only resolves the dependency graph and writes Gemfile.lock
without downloading any gems. No actual consumption from Artifactory
happens, so recording dependencies in build-info is incorrect.

Co-authored-by: Cursor <cursoragent@cursor.com>
Points to feature branch commit instead of local path so CI can
resolve the dependency without a local checkout.

Co-authored-by: Cursor <cursoragent@cursor.com>
Artifactory accepts basic auth with an empty username when the
password is a valid access token (reference or JWT). This matches
the Go module proxy pattern. Changed credential check from requiring
both user+pass to only requiring pass (token).

Co-authored-by: Cursor <cursoragent@cursor.com>
Previously when --server-id pointed to a non-existent config entry,
the command logged a warning and fell through to rubygems.org. Now it
returns a clear error, matching user intent.

Co-authored-by: Cursor <cursoragent@cursor.com>
Bundler credential lookup may check only the hostname key (e.g.,
BUNDLE_LOCALHOST) ignoring the port. Now inject credentials under
both the host:port key and hostname-only key to cover all versions.

Co-authored-by: Cursor <cursoragent@cursor.com>
…f setup ruby

`bundle config set <host> <user:pass>` silently misparses on Bundler 1.x
(no `set` subcommand there), writing a garbage BUNDLE_SET key while
reporting success. ~/.gemrc writes also relied on fragile substring
matching that couldn't dedupe across different repos.

configureRuby now reads/writes ~/.bundle/config and ~/.gemrc as YAML
directly (mirroring cargo/setup.go's TOML approach), reusing
ruby.BundleEnvKeyForHost (now exported) for host-key normalization so
setup-time and runtime-injection keys can't drift apart. A write
failure is now a real error instead of a silently-ignored warning.
Re-configuring a different repo keeps both sources in ~/.gemrc, most
recent first, since gem install natively searches every listed source.
@coderabbitai

coderabbitai Bot commented Jul 30, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@agrasth, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 3 minutes

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 38416a30-28bb-4c16-a345-001f69f036d7

📥 Commits

Reviewing files that changed from the base of the PR and between 409d3eb and ce78a4a.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (5)
  • artifactory/commands/ruby/native_ruby.go
  • artifactory/commands/ruby/native_ruby_test.go
  • artifactory/commands/setup/setup.go
  • artifactory/commands/setup/setup_test.go
  • go.mod
📝 Walkthrough

Walkthrough

Native RubyGems and Bundler execution now supports repository discovery, host-safe authentication, command execution, and optional build-info collection. Ruby setup writes Bundler and RubyGems YAML configuration with source ordering, credential rotation, preservation, and error handling. Comprehensive tests cover these flows.

Changes

Ruby integration

Layer / File(s) Summary
Ruby command contract and wiring
artifactory/commands/ruby/ruby.go, artifactory/commands/setup/setup.go, go.mod
RubyCommand gains native-tool, server, and build configuration state. Ruby setup maps to Gems repositories and adds YAML and build-info dependencies.
Native Ruby execution and authentication
artifactory/commands/ruby/native_ruby.go, artifactory/commands/ruby/native_ruby_test.go
Native gem and bundle commands validate inputs, discover repositories, inject authentication and source flags, execute commands, and manage temporary RubyGems credentials.
Ruby dependency and artifact build-info
artifactory/commands/ruby/native_ruby.go, artifactory/commands/ruby/native_ruby_test.go
Gem and Bundler operations collect build-info, parse dependencies and artifacts, enrich checksums through Artifactory AQL, and set uploaded artifact properties.
Ruby setup configuration
artifactory/commands/setup/setup.go, artifactory/commands/setup/setup_test.go, docs/superpowers/specs/...
jf setup ruby writes Bundler credentials and ordered .gemrc sources through YAML read–modify–write operations, preserves unrelated configuration, and rejects malformed files.

Estimated code review effort: 5 (Critical) | ~120 minutes

Sequence Diagram(s)

sequenceDiagram
  participant RubyCommand
  participant Artifactory
  participant RubyTool
  RubyCommand->>Artifactory: resolve repository and credentials
  RubyCommand->>RubyTool: run native gem or bundle command
  RubyTool->>Artifactory: read or publish gem data
  RubyTool-->>RubyCommand: return output for build-info collection
Loading

Possibly related PRs

Suggested labels: new feature

Suggested reviewers: bhanurp, itsmeleela

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the native RubyGems and Bundler command with authentication and build-info support.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
⚔️ Resolve merge conflicts 💡
  • Resolve merge conflict in branch RTECO-0000-rubygems-native-support
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch RTECO-0000-rubygems-native-support

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 8

🧹 Nitpick comments (5)
artifactory/commands/ruby/ruby.go (1)

68-73: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider caching the resolved server details.

ServerDetails() re-reads the config on every call since the resolved value isn't stored back into rc.serverDetails. Currently only called once from Run(), but caching makes it safe for repeated use.

♻️ Proposed change
 func (rc *RubyCommand) ServerDetails() (*config.ServerDetails, error) {
 	if rc.serverDetails != nil {
 		return rc.serverDetails, nil
 	}
-	return rubyResolveServerDetails(rc.serverID)
+	details, err := rubyResolveServerDetails(rc.serverID)
+	if err != nil {
+		return nil, err
+	}
+	rc.serverDetails = details
+	return details, nil
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@artifactory/commands/ruby/ruby.go` around lines 68 - 73, Update
RubyCommand.ServerDetails to store the result of rubyResolveServerDetails in
rc.serverDetails before returning it, while preserving the existing cached fast
path and error propagation.
artifactory/commands/ruby/native_ruby.go (2)

488-490: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

user parameter is unused.

rubyHasCredentials only checks pass; the user argument is dead weight and will be flagged by revive/unparam.

♻️ Proposed change
-// rubyHasCredentials returns true when at least a password or token is available.
-func rubyHasCredentials(user, pass string) bool {
-	return pass != ""
-}
+// rubyHasCredentials returns true when at least a password or token is available.
+func rubyHasCredentials(pass string) bool {
+	return pass != ""
+}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@artifactory/commands/ruby/native_ruby.go` around lines 488 - 490, Update
rubyHasCredentials to remove the unused user parameter and retain only the pass
argument, then update every call site to match the simplified signature while
preserving its existing password-present check.

194-230: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Wire in the documented rubyEmbedCredsInHostArg fallback or remove it.

rubyEmbedCredsInHostArg has no call sites outside tests, while Run only strips --host URLs and writes ~/.gem/credentials for gem push. The helper’s own comment says it’s the fallback for RubyGems ≤ 3.0.x who don’t use GEM_HOST_API_KEY, but nothing applies it.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@artifactory/commands/ruby/native_ruby.go` around lines 194 - 230, Integrate
rubyEmbedCredsInHostArg into the Run flow for RubyGems versions that do not use
GEM_HOST_API_KEY, applying it to the command arguments before execution;
alternatively remove the unused helper and its tests/comment if the fallback is
no longer supported. Preserve the existing --host handling and credential-file
behavior for supported RubyGems versions.
artifactory/commands/ruby/native_ruby_test.go (1)

576-578: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Check setup errors in the fixture.

os.MkdirAll/os.WriteFile errors are discarded; a failed fixture would surface as a confusing assertion failure later.

💚 Proposed change
-	os.MkdirAll(gemDir, 0700)
 	existingContent := "---\n:rubygems_api_key: existing-key\n"
-	os.WriteFile(filepath.Join(gemDir, "credentials"), []byte(existingContent), 0600)
+	require.NoError(t, os.MkdirAll(gemDir, 0700))
+	require.NoError(t, os.WriteFile(filepath.Join(gemDir, "credentials"), []byte(existingContent), 0600))
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@artifactory/commands/ruby/native_ruby_test.go` around lines 576 - 578, Check
and handle the errors returned by os.MkdirAll and os.WriteFile in the fixture
setup before continuing, using the test’s existing failure mechanism so setup
failures are reported immediately rather than during later assertions.
artifactory/commands/setup/setup.go (1)

626-632: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Use filepath.Join for home-relative paths.

String concatenation with / is inconsistent with the rest of this file's path handling and non-idiomatic on Windows.

♻️ Proposed change
-	bundleDir := home + "/.bundle"
-	configPath := bundleDir + "/config"
+	bundleDir := filepath.Join(home, ".bundle")
+	configPath := filepath.Join(bundleDir, "config")
-	gemrcPath := home + "/.gemrc"
+	gemrcPath := filepath.Join(home, ".gemrc")

Also applies to: 662-667

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@artifactory/commands/setup/setup.go` around lines 626 - 632, Update
writeBundleConfig to construct bundleDir and configPath with filepath.Join
instead of slash-based string concatenation, preserving the existing
~/.bundle/config locations and platform-independent path handling.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@artifactory/commands/ruby/native_ruby_test.go`:
- Around line 509-512: Update the affected tests around the HOME setup to use
t.Setenv instead of os.Setenv with deferred restoration, and override the
Windows-specific USERPROFILE variable as well as HOME using the temporary
directory. Apply this consistently to all three referenced tests so
os.UserHomeDir resolves to the isolated temporary home on every platform.

In `@artifactory/commands/ruby/native_ruby.go`:
- Around line 150-189: Remove credential embedding from
rubyEmbedCredsInSourceArg and route RubyGems authentication through the existing
rubyWriteTempGemCredentials file-based mechanism or a supported
config-file/environment path instead. Ensure install/fetch commands use that
authentication path without placing user, password, or tokens in argv, and
redact any source URL before logging.
- Around line 1045-1051: Update the artifact assembly in rubySetBuildProperties
so Artifact.Path contains the repository-relative Artifactory path rather than
filepath.Base(path). Reuse the existing AQL lookup for the matching gem artifact
name to obtain its actual repository and path before appending the
buildinfo.Artifact, preserving the <repo>/gems/<file>.gem location.
- Around line 1146-1155: Update the group-block parsing around currentGroups and
the `group`/`end` detection to track nested `do ... end` depth, rather than
clearing the group on the first closing `end`. Increment depth for nested
blocks, decrement on each matching `end`, and clear currentGroups only when the
outer group block closes so subsequent gems remain classified correctly.
- Around line 85-93: Update the native Ruby authentication branches around
rubyEmbedCredsInSourceArg and the push credential-writing logic to apply the
same configured-host validation enforced by injectAuth. Only embed credentials
in --source URLs or write ~/.gem/credentials when the source host matches the
configured Artifactory host, while preserving existing behavior for valid
matching hosts.

In `@artifactory/commands/setup/setup_test.go`:
- Around line 949-960: Update the writeBundleConfig test to create the existing
Bundler config with mode 0644, then assert its mode is 0600 after
writeBundleConfig completes. Ensure writeBundleConfig itself enforces 0600 for
existing files, using chmod or atomic replacement as needed, while preserving
the existing config contents and entries.

In `@artifactory/commands/setup/setup.go`:
- Around line 681-694: Update the :sources handling before calling
reorderGemrcSources so malformed or empty values, including nil, maps, or an
empty list, fall back to rubygemsDefaultSource. Preserve valid configured source
entries and ensure the rewritten configuration always retains the RubyGems
default when no usable sources are present.

In `@go.mod`:
- Line 201: The go.mod entry for github.com/jfrog/build-info-go currently uses a
replace directive; remove that replacement and add or update the direct require
entry to v1.13.1-0.20260715194847-6e04c9b133c8 so flexpack imports resolve
through the declared module requirement.

---

Nitpick comments:
In `@artifactory/commands/ruby/native_ruby_test.go`:
- Around line 576-578: Check and handle the errors returned by os.MkdirAll and
os.WriteFile in the fixture setup before continuing, using the test’s existing
failure mechanism so setup failures are reported immediately rather than during
later assertions.

In `@artifactory/commands/ruby/native_ruby.go`:
- Around line 488-490: Update rubyHasCredentials to remove the unused user
parameter and retain only the pass argument, then update every call site to
match the simplified signature while preserving its existing password-present
check.
- Around line 194-230: Integrate rubyEmbedCredsInHostArg into the Run flow for
RubyGems versions that do not use GEM_HOST_API_KEY, applying it to the command
arguments before execution; alternatively remove the unused helper and its
tests/comment if the fallback is no longer supported. Preserve the existing
--host handling and credential-file behavior for supported RubyGems versions.

In `@artifactory/commands/ruby/ruby.go`:
- Around line 68-73: Update RubyCommand.ServerDetails to store the result of
rubyResolveServerDetails in rc.serverDetails before returning it, while
preserving the existing cached fast path and error propagation.

In `@artifactory/commands/setup/setup.go`:
- Around line 626-632: Update writeBundleConfig to construct bundleDir and
configPath with filepath.Join instead of slash-based string concatenation,
preserving the existing ~/.bundle/config locations and platform-independent path
handling.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 263a8b8e-8c14-4519-8343-22b0a31f7fe7

📥 Commits

Reviewing files that changed from the base of the PR and between 4fd5a71 and 8a00d94.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (7)
  • artifactory/commands/ruby/native_ruby.go
  • artifactory/commands/ruby/native_ruby_test.go
  • artifactory/commands/ruby/ruby.go
  • artifactory/commands/setup/setup.go
  • artifactory/commands/setup/setup_test.go
  • docs/superpowers/specs/2026-07-31-ruby-setup-fix-design.md
  • go.mod

Comment thread artifactory/commands/ruby/native_ruby_test.go Outdated
Comment thread artifactory/commands/ruby/native_ruby.go Outdated
Comment thread artifactory/commands/ruby/native_ruby.go
Comment thread artifactory/commands/ruby/native_ruby.go
Comment thread artifactory/commands/ruby/native_ruby.go
Comment thread artifactory/commands/setup/setup_test.go Outdated
Comment thread artifactory/commands/setup/setup.go
Comment thread go.mod Outdated
Brings `jf setup ruby` to parity with cargo/nuget/apt, where setup alone is
enough for the native tool to resolve and authenticate with no manifest edit.
Verified end-to-end against a private Artifactory repo with real Bundler
1.17.2 and RubyGems.

Bundler mirror, so no Gemfile edit is needed
  ~/.bundle/config now gets a `mirror.https://rubygems.org` entry pointing at
  the Artifactory repository, the Bundler analogue of Cargo's
  `[source.crates-io] replace-with`. An unmodified `source "https://rubygems.org"`
  Gemfile resolves through Artifactory, and the lockfile still records
  rubygems.org, so it stays portable and does not leak the instance URL.
  Verified present in Bundler 1.17 through 4.0; fallback_timeout defaults to 0,
  so no TCP probe runs.

Fix: bare `gem install` could not authenticate (401)
  RubyGems has no credential store for installs, so a credential-free source in
  ~/.gemrc meant every install against a private repo failed with 401. The
  ~/.gemrc source now embeds credentials, and the file is written 0600. Entries
  are matched with credentials stripped, so rotating a token replaces the stale
  entry instead of leaving `gem install` retrying a dead credential.

Fix: credential key was wrong for dashed and ported hosts
  Bundler's key normalization changed between majors: 1.x keeps dashes, 2.x+
  turns them into "___". We only ever wrote one spelling, so a dashed host such
  as my-company.jfrog.io silently failed to authenticate on one of them.
  BundleCredentialKeys now emits every spelling Bundler may look under, and is
  shared by setup and the runtime injection in `jf ruby bundle` so the two
  cannot drift. Setup also keys on the hostname rather than host:port, because
  Bundler's fallback uses uri.host, which excludes the port; the previous
  host:port key was never read at all.

Bundler parses ~/.bundle/config with its own line-based stub serializer rather
than a real YAML parser, so tests now assert round-tripping through a port of
that parser instead of merely checking the output is valid YAML.
The host-match guard that protects against sending Artifactory credentials to
a third-party registry lived inside injectAuth and only short-circuited the
environment variables. Run() then went on to embed credentials in argv and
write ~/.gem/credentials regardless, so the guard was bypassed on exactly the
two paths that persist a credential.

  jf ruby gem install foo --source https://gems.thirdparty.com/api/gems/x/

with no --server-id logged "skipping credential injection" and then passed
--source https://user:<token>@gems.thirdparty.com/... to the child process.
The gem push path did the same via ~/.gem/credentials.

Authorization now lives in authorizedForSource and gates every credential path
in Run(), with injectAuth keeping its own check as defence in depth. It is
evaluated against the host the native tool will really contact, so
`--repo <artifactory> --host <third-party>` can no longer authorize against one
host and hand the credential to another.

Also fixes `gem push` targeting the wrong host entirely: --host was only
injected when --repo was passed, so a push whose source came from the Gemfile
or ~/.gemrc fell back to RubyGems' default host and sent both the gem and the
Artifactory credential to rubygems.org.

Two build-info correctness fixes found in the same audit:

  - AQL enrichment read the checksum from the first matching result but the
    repository path from the last, so a gem with a platform-specific sibling
    (nokogiri-1.16.0.gem alongside nokogiri-1.16.0-arm64-darwin.gem) published
    a checksum for a file the recorded path did not point at. Both now come
    from one result, and an exact <name>-<version>.gem wins over a platform
    variant.

  - `gem install -v "~> 13.0"` recorded the requirement as the version,
    producing a dependency ID of "rake:~> 13.0" that matches no artifact.
    Requirements are now rejected so the installed version is queried instead.
The build-info-go version the RubyGems code needs was pinned with a replace
directive, while the require line still named an older commit. Go ignores
replace directives from dependencies, so a consumer resolved the stale require
and failed to compile the ruby package with undefined flexpack.GemConfig and
buildinfo.Gem.

Promoting the pin to the require line makes it propagate through normal minimal
version selection, so jfrog-cli no longer needs its own matching replace. The
resolved module is byte-identical to what the replace already produced, so this
does not change the build of this module.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
artifactory/commands/ruby/native_ruby.go (1)

470-496: 🩺 Stability & Availability | 🔵 Trivial | 💤 Low value

Verify that colon-bearing Bundler keys work as environment variables.

BundleCredentialKeys("localhost:8081") returns BUNDLE_LOCALHOST:8081. The code passes every candidate as a process environment variable name. Unix accepts : in a name, but Windows environment APIs and some shells do not, and Bundler reads the ported form primarily from ~/.bundle/config. Confirm the behaviour on Windows, or filter candidates that are not valid environment variable names before appending them to extraEnv.

♻️ Optional filter
 		for _, key := range candidates {
 			if seen[key] {
 				continue
 			}
 			seen[key] = true
+			if strings.ContainsAny(key, ":=") {
+				// Not a portable environment variable name; only usable as a ~/.bundle/config key.
+				continue
+			}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@artifactory/commands/ruby/native_ruby.go` around lines 470 - 496, Update the
Bundler credential injection in the nativeTool/ toolBundle branch to avoid
passing colon-containing keys such as those produced by
BundleCredentialKeys(host) as environment variable names. Validate or filter
candidates before appending to extraEnv, while preserving valid bare-host keys
and existing-credential handling; ensure Windows-safe environment variable
behavior.

Source: Linters/SAST tools

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@artifactory/commands/ruby/native_ruby.go`:
- Around line 470-496: Update the Bundler credential injection in the
nativeTool/ toolBundle branch to avoid passing colon-containing keys such as
those produced by BundleCredentialKeys(host) as environment variable names.
Validate or filter candidates before appending to extraEnv, while preserving
valid bare-host keys and existing-credential handling; ensure Windows-safe
environment variable behavior.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: ca7d43c0-5803-4709-8926-d8ff73ded3ce

📥 Commits

Reviewing files that changed from the base of the PR and between 8a00d94 and 16b4fc3.

📒 Files selected for processing (5)
  • artifactory/commands/ruby/native_ruby.go
  • artifactory/commands/ruby/native_ruby_test.go
  • artifactory/commands/setup/setup.go
  • artifactory/commands/setup/setup_test.go
  • go.mod
🚧 Files skipped from review as they are similar to previous changes (1)
  • artifactory/commands/setup/setup.go

Three build-info reporting fixes, all found while inspecting published
build-info against a live instance.

Artifact path was the bare file name. RubyGems repositories nest gems under
"gems/", so build-info reported a location the gem was never published to:
"agrasthn-demo-gem-0.1.0.gem" instead of "gems/agrasthn-demo-gem-0.1.0.gem",
and OriginalDeploymentRepo was never set. The path now comes from the same AQL
search that already sets the build properties, so it cannot disagree with where
the file actually landed. This matters because Artifactory persists an
artifact's path, unlike a dependency's, which it drops on ingest.

Failed dependency enrichment was logged at debug level, so publishing every
dependency without a checksum looked exactly like success. It is now a warning
that names the likely cause: AQL cannot search a virtual repository, and gems
served from a local gem cache never reach Artifactory to be found.

`gem build` accepted --build-name and --build-number and silently recorded
nothing, because it is a local-only operation with no artifact in Artifactory
yet. That is intentional, but it now says so and points at `gem push`, rather
than leaving the flags looking accepted.
agrasth added 5 commits August 3, 2026 12:46
`jf setup ruby` kept https://rubygems.org at the front of ~/.gemrc's :sources:
list. RubyGems queries sources in list order, so a plain `gem install` reached
the public registry before Artifactory and setup had no practical effect on it.
The public source is now removed, matching what the Bundler mirror already does
and what setup does for npm and cargo, which replace the public registry rather
than racing it. Artifactory repositories configured across separate runs still
coexist, most recently configured first, because `gem install` genuinely does
search several sources.

This assumes the configured repository is virtual or remote-backed so it can
still serve public gems, which is the same assumption every other package
manager's setup makes.

The four "Ruby auth [...]" messages are now debug rather than info. Which
environment variable carries the credential, and whether GEM_HOST_API_KEY or a
URL-embedded credential is used, is implementation detail that only matters when
diagnosing an auth failure.
… identity

Gemfile discovery only looked in the current directory, while Bundler honours
$BUNDLE_GEMFILE and otherwise walks up until it finds one. Running any ruby
command from a subdirectory of a project therefore found no source, injected no
credentials, and failed with a bare "bundle install failed: exit status 16"
that pointed at nothing. Discovery now matches Bundler, and both the source
lookup and build-info collection are anchored on the resolved directory so
Gemfile.lock is found too.

Module IDs came from three unrelated sources: the working-directory base name
for bundle and gem push, and the literal string "gem-" plus the subcommand for
gem install. That produced IDs like "tmp.46NMMEmRLv" from a temporary directory,
made every project in the world report module "gem-install", and split one
project's dependencies and artifact across two modules whenever the build and
the push ran from different directories.

All three paths now share one helper preferring the gemspec's "<name>:<version>",
which is the Ruby equivalent of package.json or pom.xml and matches how npm and
Maven name modules. Applications with a Gemfile and no gemspec fall back to the
Gemfile's directory name. A gemspec that computes its version in Ruby cannot be
read without executing it, so those report the name alone rather than a wrong
version. The identity is also passed to flexpack, which otherwise falls back to
the working-directory name itself.
…rupts

Five defects that all caused silent wrong behaviour rather than an error.

The subcommand was read from args[0], so any global flag in front of it
(`gem --backtrace install rake`) matched no case: no credentials injected, no
source appended, no build-info collected, and still exit 0. It is now the first
non-flag argument, skipping the values of the few global flags that take one.

Injected --source/--host were appended after any "--" separator, where gem
forwards them to the C extension build instead of consuming them itself, so
`gem install nokogiri -- --use-system-libraries` resolved from the default
source and passed a bogus argument to extconf. They now go before the separator.

A block nested in a Gemfile group closed that group early, because any `end`
cleared the group state. Every gem after an inner `platforms :ruby do ... end`
was therefore scoped production instead of development, and the wrong scope
propagated to its transitive dependencies.

The publish hint dropped --project even when it was passed. Build partials are
stored per project, so following the printed command published an empty build
and looked like the build had been lost.

Ctrl-C during `gem push` left the Artifactory token in ~/.gem/credentials,
because cleanup was deferred and Go exits on SIGINT without running defers.
Interrupts are now handled, cleanup runs once whichever path is taken, and the
signal is re-raised so the exit status still reflects the interruption.
`gem build` accepted --build-name and --build-number and recorded nothing, so a
project that arrives with its gems already vendored and is built and published
without ever running an install reported no dependencies at all.

Dependencies now come from Gemfile.lock, reusing the same collector the bundle
commands use. The lock file is the only honest source for a resolved set: a
gemspec or Gemfile states requirements such as "~> 13.0", which are not versions
and would produce dependency IDs matching no artifact in Artifactory. With no
lock file present there is nothing resolved to record, and that is now stated
rather than silently accepted.

Verified against a live instance that running an install and a build under the
same build name merges by dependency ID rather than duplicating, so the
install-then-build flow stays clean.

The built .gem is still recorded only by `gem push`, which is the point it gains
an Artifactory path.
…` works

RubyGems resolves index files relative to the source URL, so a source without a
trailing slash has its last path segment replaced: it requested
/artifactory/api/gems/specs.4.8.gz, dropping the repository name entirely, and
every plain `gem install` failed with "server did not return a valid file".

That is the exact flow `jf setup ruby` exists to enable, and it was broken for
any bare gem command. Earlier manual checks missed it because they passed an
explicit --source, which jf appends already slash-terminated.

Bundler normalises the trailing slash itself, so the same URL stays correct for
the mirror entry and for the Gemfile line printed to the user. Source equality
already ignores a trailing slash, so re-running setup still replaces rather than
duplicates the entry.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
artifactory/commands/ruby/native_ruby.go (1)

1766-1796: 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

Check searchReader.GetError() after reading search records.

NextRecord can return the last record and a subsequent read error; exiting the loop without calling GetError() lets failed searches skip artifact.Path updates and still reach SetProps from Reset().

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@artifactory/commands/ruby/native_ruby.go` around lines 1766 - 1796, The
artifact search loop around NextRecord must check searchReader.GetError() after
consuming records and before Reset(). Handle any read error so failed searches
do not proceed to SetProps with incomplete artifact.Path data, while preserving
the existing matching-record update behavior.
🧹 Nitpick comments (1)
artifactory/commands/ruby/native_ruby.go (1)

944-952: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Set bundleInstalledPackages' cwd to the resolved Bundler directory.

bundleInstalledPackages calls bundle list from workingDir, while FlexPack and parseGemfileGroups use rubyGemfileDir(workingDir). Running from a project subdirectory can make bundle list fail, causing build-info to fall back to the full lockfile instead of using installed-package metadata. Update the parser to call bundle list in the resolved Gemfile directory, or expose that helper’s logic through bundleInstalledPackages.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@artifactory/commands/ruby/native_ruby.go` around lines 944 - 952, The
bundle-installed package parsing path must use the resolved Bundler directory
rather than the original working directory. Update bundleInstalledPackages, or
the parser invoking it, so bundle list runs with rubyGemfileDir(workingDir) as
its cwd, matching collectLockfileDependencies and parseGemfileGroups.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@artifactory/commands/ruby/native_ruby.go`:
- Around line 1457-1472: The nested block counter only increments when the line
ends with "do", but other block-opening patterns like `platforms :mri do |p|`
(ending with `|`) or conditional keywords like `if`, `unless`, `case`, `begin`
also open blocks without the `do` suffix. When their corresponding `end`
statements are encountered, the nested counter is not decremented, causing
currentGroups to be incorrectly cleared and remaining gems to be misclassified
as production. Create package-level regex patterns to match block openers that
do not end with "do" (such as those ending with `|` or conditional keywords),
then update the condition that currently only checks `strings.HasSuffix(line,
"do")` to also use these patterns to detect all block openers and increment
nested consistently.

---

Outside diff comments:
In `@artifactory/commands/ruby/native_ruby.go`:
- Around line 1766-1796: The artifact search loop around NextRecord must check
searchReader.GetError() after consuming records and before Reset(). Handle any
read error so failed searches do not proceed to SetProps with incomplete
artifact.Path data, while preserving the existing matching-record update
behavior.

---

Nitpick comments:
In `@artifactory/commands/ruby/native_ruby.go`:
- Around line 944-952: The bundle-installed package parsing path must use the
resolved Bundler directory rather than the original working directory. Update
bundleInstalledPackages, or the parser invoking it, so bundle list runs with
rubyGemfileDir(workingDir) as its cwd, matching collectLockfileDependencies and
parseGemfileGroups.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: c51f73df-de6a-47f4-9263-97c696b1571b

📥 Commits

Reviewing files that changed from the base of the PR and between 16b4fc3 and 409d3eb.

📒 Files selected for processing (4)
  • artifactory/commands/ruby/native_ruby.go
  • artifactory/commands/ruby/native_ruby_test.go
  • artifactory/commands/setup/setup.go
  • artifactory/commands/setup/setup_test.go
🚧 Files skipped from review as they are similar to previous changes (3)
  • artifactory/commands/setup/setup.go
  • artifactory/commands/ruby/native_ruby_test.go
  • artifactory/commands/setup/setup_test.go

Comment thread artifactory/commands/ruby/native_ruby.go
Static Check and Go-Sec both failed. No behaviour changes here.

errcheck: the temporary-home test helpers used os.Setenv with an unchecked
error and a manual restore. Replaced with t.Setenv, which restores the value
itself, and the remaining MkdirAll/WriteFile calls are now asserted.

nilerr: a missing Gemfile.lock is an expected condition rather than a failure,
so the check no longer binds an error it deliberately ignores. It reads through
a small rubyFileExists helper instead.

unparam: rubyHasCredentials only ever consulted the password, parseGemCommandOutput
never used its subCommand argument, and rubyCollectGemArtifacts could not fail.
Signatures trimmed to what they actually use.

G703: the credentials path is built from the home directory, which comes from
the environment, so taint analysis flagged the writes. The home directory is now
validated as absolute before any path is derived from it, and the writes carry a
justification, matching how this repository already handles the same pattern.

G101: a test built a fake credential inline as a password-in-URL literal. Now
assembled from parts so it is not read as a hardcoded secret.

Also removes the design note that was committed earlier; design discussion does
not belong in the branch.
…ative-support

# Conflicts:
#	artifactory/commands/setup/setup.go
#	artifactory/commands/setup/setup_test.go
#	go.mod
Merging main brought in packageManagerConfigs, which drives the note printed
after a successful setup and is asserted to cover every supported package
manager. Ruby was registered as supported but had no entry, so the map held 17
of the expected 18 and the setup tests failed.

Ruby writes ~/.gemrc and ~/.bundle/config directly, always under the user's home
directory, and honours no override variable of its own, so it is described as a
user-level configuration change rather than a credentials-only one.
The RubyGems FlexPack branch now contains main, so its pseudo-version is newer
than main's. Consumers can therefore select it through an ordinary require
instead of a replace directive, which previously could not pin backwards to a
commit older than main.
return fmt.Errorf("unsupported ruby tool %q: expected 'gem' or 'bundle'", rc.nativeTool)
}

// Bug 3 fix: explicit no-args check before help bypass so we don't

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.

remove this comment, wdym by bug 3 fix?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Fixed — reworded to describe what the check does instead of referencing an internal bug number with no context here.

subCommand := rubySubCommand(rc.args)

// Help requests bypass auth injection entirely so credentials are never
// printed in help output (same rationale as the UV native command).

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.

this comment does not look good , refactor it and write exactly what this func is doing .

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Reworded — it was pointing at "the UV native command" for the rationale instead of just stating it directly.

}

log.Info(fmt.Sprintf("Running %s %s.", rc.nativeTool, subCommand))
// For gem install/fetch, capture stdout to parse "Successfully installed"/"Downloaded" lines.

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.

let's not parse the stdout , because the format can change and it would break our code

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Looked for a machine-readable alternative before replying — gem install --help has no JSON/structured output mode, so there isn't a clean way to know exactly what was installed/fetched by this invocation without either parsing stdout or diffing gem-list state before/after (which has its own races). The code already falls back to explicit args + gem list when stdout parsing yields nothing (see collectGemInstallDependencies). Open to a different approach if you have a specific one in mind — this was the least fragile option I found.


// rubyEmbedCredsInSourceArg rewrites --source/--host URL args to embed credentials
// for gem install/fetch. RubyGems 3.x uses embedded URL credentials for index downloads
// (specs.4.8.gz) but does NOT use GEM_HOST_API_KEY for those requests.

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.

this comment is very confusing , not able to understand what exactly this function is doing.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Reworded to state directly why GEM_HOST_API_KEY doesn't cover this path (RubyGems 3.x reads the index via URL-embedded credentials for this specific request, not the env var).


// Ensure ~/.gem directory exists.
if err := os.MkdirAll(gemDir, 0700); err != nil {
return nil, fmt.Errorf("could not create ~/.gem directory: %w", err)

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.

why are we creating this the setup command would create it right?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

This is deliberate, not an oversight: jf ruby is a config-less flow (see the doc comment at the top of ruby.go) that does not assume jf setup ruby ran first, so gem push needs to be able to create ~/.gem itself on a machine where setup was never run.


// Determine the host to authenticate. Prefer the discovered source URL host;
// otherwise fall back to the Artifactory server host.
host := rubyHostOf(sourceURL)

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.

it has to be always artifactory right? what do mean by the Prefer the discovered source URL host; otherwise fall back to the Artifactory server host.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

The logic is correct as written: authorizedForSource gates every credential path on the target host matching the configured Artifactory host (or an explicit --server-id), so in practice this never authenticates against an arbitrary registry — only the discovered source or the server config host, whichever resolved. Happy to reword the comment further if the wording itself is still unclear — let me know what specifically read as ambiguous.

return err
}

// For gem install/fetch: parse stdout to determine exactly what was installed/fetched.

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.

we should not parse stdout anywhere , please check it everywhere and try ot not use it.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Same answer as the other stdout-parsing comment on this file — checked and there's no structured output mode for gem install/fetch to switch to. Happy to discuss alternatives if you have one in mind.

"Point your Gemfile/gem source at an Artifactory gems repository or pass --server-id.")
}

if err := rubySaveBuildInfo(bi, rc.buildConfiguration); err != nil {

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.

why we have rubySave BuildInfo? i think we already have a function named SaveBuidInfo why seperate for ruby?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Not a duplicate — rubySaveBuildInfo is a thin per-command wrapper around the shared bld.SaveBuildInfo(bi), the same pattern the UV command uses (see native_uv.go), not a reimplementation of it.

// collectGemBuildDependencies records the dependencies a `gem build` was built against,
// read from Gemfile.lock. Without a lock file there is nothing resolved to record, and
// saying so is better than accepting --build-name and silently producing nothing.
func (rc *RubyCommand) collectGemBuildDependencies(workingDir, repoKey string, serverDetails *coreConfig.ServerDetails) error {

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.

i see this same function repeating , remove the duplicate method.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Removed rubyEmbedCredsInHostArg — it was dead code (zero callers) and nearly identical to rubyEmbedCredsInSourceArg. If this is the duplication you meant, it's gone now. collectGemBuildDependencies itself isn't a duplicate of anything — it delegates to the shared collectLockfileDependencies, the same helper the bundle-install path uses.

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

Labels

improvement Automatically generated release notes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants