Skip to content

RTECO-1646 - Enhance Maven command with server details and build info collection - #526

Open
fluxxBot wants to merge 4 commits into
mainfrom
RTECO-1646-mvnWorkspace
Open

RTECO-1646 - Enhance Maven command with server details and build info collection#526
fluxxBot wants to merge 4 commits into
mainfrom
RTECO-1646-mvnWorkspace

Conversation

@fluxxBot

@fluxxBot fluxxBot commented Aug 3, 2026

Copy link
Copy Markdown
Collaborator
  • All tests passed. If this feature is not already covered by the tests, I added new tests.
  • All static analysis checks passed.
  • Appropriate label is added to auto generate release notes.
  • I used gofmt for formatting the code before submitting the pull request.
  • PR description is clear and concise, and it includes the proposed solution/fix.

  • Added server details to the Maven command execution to improve build info collection.
  • Introduced new utility functions for extracting resolution arguments and sanitizing packaging types.
  • Implemented tests for various Maven functionalities including artifact collection and module ID splitting.
  • Updated the build-info-go dependency to a newer version for enhanced features.

Summary by CodeRabbit

  • New Features

    • Improved Maven build-info collection using invoked Maven arguments and resolution settings.
    • Added support for multi-module projects and module-specific artifact collection.
    • Build artifacts are now associated with effective deployment repositories and verified using SHA-256 checksums.
    • Maven wrapper detection now works from the current directory or parent directories.
  • Bug Fixes

    • Improved validation of Maven coordinates, packaging, repository URLs, and missing module locations.
    • Enhanced Maven build-mode metadata handling for legacy collection workflows.

@fluxxBot
fluxxBot requested a review from a team August 3, 2026 10:24
@fluxxBot fluxxBot added the new feature Automatically generated release notes label Aug 3, 2026
@coderabbitai

coderabbitai Bot commented Aug 3, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

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

Next review available in: 20 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: 7e7a45dc-9dfa-46c1-91a2-7bdd83557db1

📥 Commits

Reviewing files that changed from the base of the PR and between 6c12d00 and 37b49cc.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (1)
  • go.mod
📝 Walkthrough

Walkthrough

Maven FlexPack collection now receives Maven arguments and server details, collects artifacts from multiple module locations, resolves deployment repositories, records repository metadata, and applies SHA-256-based build properties. Native Maven execution also improves wrapper discovery and legacy build metadata handling.

Changes

Maven FlexPack build-info flow

Layer / File(s) Summary
Maven inputs and module artifact collection
artifactory/commands/flexpack/maven.go, artifactory/commands/flexpack/maven_test.go
The collector extracts resolution arguments, validates module coordinates and packaging, and collects module-specific artifacts from FlexPack locations.
Deployment repository resolution and artifact tagging
artifactory/commands/flexpack/maven.go, artifactory/commands/flexpack/maven_test.go
The collector resolves effective repositories, records OriginalDeploymentRepo, queries artifacts by SHA-256, and applies build properties.
Native Maven wiring and legacy build metadata
artifactory/commands/mvn/mvn.go, artifactory/commands/mvn/utils.go, go.mod
Native Maven execution forwards server details and goals, searches parent directories for wrappers, stamps legacy extractor output, and pins the build-info dependency revision.

Estimated code review effort: 4 (Complex) | ~60 minutes

Suggested reviewers: bhanurp, agrasth, itsmeleela

Sequence Diagram(s)

sequenceDiagram
  participant MavenCommand
  participant MvnUtils
  participant FlexPackCollector
  participant Artifactory
  MavenCommand->>MvnUtils: Execute Maven goals
  MvnUtils->>FlexPackCollector: Pass goals and server details
  FlexPackCollector->>FlexPackCollector: Collect artifacts from module locations
  FlexPackCollector->>Artifactory: Resolve repositories and query SHA-256 matches
  FlexPackCollector->>Artifactory: Apply build properties and save build info
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 66.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the Maven server-details and build-info collection changes.
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
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch RTECO-1646-mvnWorkspace

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.

… collection

- Added server details to the Maven command execution to improve build info collection.
- Introduced new utility functions for extracting resolution arguments and sanitizing packaging types.
- Implemented tests for various Maven functionalities including artifact collection and module ID splitting.
- Updated the build-info-go dependency to a newer version for enhanced features.

@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: 3

🧹 Nitpick comments (2)
artifactory/commands/flexpack/maven.go (1)

351-358: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Remove the stale doc comment.

Lines 351-352 document resolutionArgReferencesSettings, which no longer exists. Keep only the isSeparateValueFlag comment.

♻️ Proposed change
-// resolutionArgReferencesSettings reports whether arg selects a settings file (its value may be the
-// following token).
 // isSeparateValueFlag reports whether arg is a resolution flag that consumes the next token as its value.
🤖 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/flexpack/maven.go` around lines 351 - 358, Remove the
stale resolutionArgReferencesSettings comment above isSeparateValueFlag, leaving
only the documentation that describes isSeparateValueFlag and its separate-value
behavior.
artifactory/commands/flexpack/maven_test.go (1)

64-92: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Add a case for the space-separated -P form.

mvn deploy -P prod is valid Maven. No case covers it, which is why the dangling--P defect flagged in artifactory/commands/flexpack/maven.go (lines 370-393) is not detected.

💚 Proposed test case
 		{name: "activate-profiles long form", args: []string{"deploy", "--activate-profiles", "prod,ci"}, want: []string{"--activate-profiles", "prod,ci"}},
+		{name: "profiles short flag with separate value", args: []string{"deploy", "-P", "prod,ci"}, want: []string{"-P", "prod,ci"}},
🤖 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/flexpack/maven_test.go` around lines 64 - 92, Add a
table-driven test case in TestExtractResolutionArgs for the space-separated
Maven profile form, using arguments such as "-P", "prod" and expecting both
tokens preserved in order. Ensure the test exercises extractResolutionArgs and
detects the dangling "-P" behavior.
🤖 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/flexpack/maven.go`:
- Around line 370-393: Update extractResolutionArgs so exact -P and -D tokens
are handled in the separate-value branch, consuming and forwarding their
following value token. Keep prefixed forms such as -Pprofile and -Dkey=value in
the existing inline-value branch, while preserving the current handling of the
other separate-value flags.
- Around line 217-232: Update tagArtifactsInRepo to inspect reader.GetError()
after SetProps consumes the search reader, before returning the count. Propagate
the reader error when present so streaming or parse failures are returned
instead of reporting zero artifacts; preserve the existing SetProps error
handling and reader.Close behavior.
- Around line 431-455: Update the main artifact resolution around
sanitizePackaging and mainArtifactName so Maven packagings maven-plugin, bundle,
ejb, and maven-archetype use a .jar extension while preserving packagingType as
the artifact Type. If the computed default filename is absent, scan targetDir
for the expected artifact extension to support custom finalName values, then
apply the existing validation and path-safety checks before appending the
artifact.

---

Nitpick comments:
In `@artifactory/commands/flexpack/maven_test.go`:
- Around line 64-92: Add a table-driven test case in TestExtractResolutionArgs
for the space-separated Maven profile form, using arguments such as "-P", "prod"
and expecting both tokens preserved in order. Ensure the test exercises
extractResolutionArgs and detects the dangling "-P" behavior.

In `@artifactory/commands/flexpack/maven.go`:
- Around line 351-358: Remove the stale resolutionArgReferencesSettings comment
above isSeparateValueFlag, leaving only the documentation that describes
isSeparateValueFlag and its separate-value behavior.
🪄 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: 56895234-2af5-49b5-aaf5-fd56a561e5d6

📥 Commits

Reviewing files that changed from the base of the PR and between 0565658 and cd71b62.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (5)
  • artifactory/commands/flexpack/maven.go
  • artifactory/commands/flexpack/maven_test.go
  • artifactory/commands/mvn/mvn.go
  • artifactory/commands/mvn/utils.go
  • go.mod

Comment on lines +217 to 232
func tagArtifactsInRepo(servicesManager artifactory.ArtifactoryServicesManager, repo string, sha256s []string, buildProps string) (int, error) {
reader, err := servicesManager.SearchFiles(services.SearchParams{
CommonParams: &specutils.CommonParams{Aql: specutils.Aql{ItemsFind: checksumAql(repo, sha256s)}},
})
if err != nil {
return nil, fmt.Errorf("failed to resolve absolute path for settings.xml: %w", err)
return 0, err
}
cleanedPath := filepath.Clean(absPath)
if cleanedPath != absPath {
return nil, fmt.Errorf("invalid path detected: %s", settingsPath)
count, setErr := servicesManager.SetProps(services.PropsParams{Reader: reader, Props: buildProps})
if closeErr := reader.Close(); closeErr != nil {
log.Debug("Failed to close search reader: " + closeErr.Error())
}

data, err := os.ReadFile(absPath)
if err != nil {
return nil, err
if setErr != nil {
return 0, setErr
}

var settings SettingsXml
if err := xml.Unmarshal(data, &settings); err != nil {
return nil, err
}

return &settings, nil
return count, nil
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify the ContentReader idiom used elsewhere in this repository after SearchFiles/SetProps.
rg -nP -C4 '\bSearchFiles\s*\(' --type=go | head -100
rg -nP -C2 '\bGetError\s*\(\s*\)' --type=go | head -60

Repository: jfrog/jfrog-cli-artifactory

Length of output: 165


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Go toolchain availability:"
command -v go || true
go version 2>/dev/null || true

echo
echo "Locate maven.go and inspect relevant section(s):"
fd -a 'maven\.go$' . | sed 's#^\./##'
file="$(fd 'maven\.go$' . | head -1)"
if [ -n "${file:-}" ]; then
  wc -l "$file"
  sed -n '180,250p' "$file" | nl -ba -v180
fi

echo
echo "Search for SearchFiles, SetProps, ContentReader GetError patterns in checked-in Go files:"
rg -n -C4 '\bSearchFiles\s*\(' --ext go . || true
rg -n -C3 '\.GetError\s*\(\s*\)' --ext go . || true
rg -n -C3 '\bSetProps\s*\(' --ext go . || true

Repository: jfrog/jfrog-cli-artifactory

Length of output: 439


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Repository go module info:"
if [ -f go.mod ]; then
  rg -n 'jfrog-client-go|jfrog-cli-core|jfrog-jfrog-client-go' go.mod || true
fi

echo
echo "Dependabot/modules references to client-go version/tag in repo files:"
rg -n 'jfrog/jfrog-client-go|jfrog-client-go' -g 'go.mod' -g 'go.sum' . || true

Repository: jfrog/jfrog-cli-artifactory

Length of output: 896


🏁 Script executed:

#!/bin/bash
set -euo pipefail

file='./artifactory/commands/flexpack/maven.go'

echo "Relevant section of $file:"
awk 'NR>=180 && NR<=250 {printf "%d\t%s\n", NR, $0}' "$file"

echo
echo "SearchFiles/SetProps/GetError usages in $file:"
rg -n -C3 '\bSearchFiles\s*\(|\bSetProps\s*\(|\.GetError\s*\(\s*\)' "$file" || true

echo
echo "Call sites of tagArtifactsInRepo:"
rg -n -C4 'tagArtifactsInRepo\s*\(' "$file" || true

Repository: jfrog/jfrog-cli-artifactory

Length of output: 5725


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Fetch jfrog-client-go ContentReader and SetProps definitions from declared module without installing:"
tmp="$(mktemp -d)"
cd "$tmp"
go env -w GOPRIVATE='' >/dev/null 2>&1 || true
go env >/dev/null 2>&1 || true

cat > go.mod <<'EOF'
module testprobe
go 1.23
require github.com/jfrog/jfrog-client-go v1.55.1-0.20260508101905-a17af78a38d7
EOF

go mod download github.com/jfrog/jfrog-client-go@v1.55.1-0.20260508101905-a17af78a38d7 >/dev/null

echo "Module path:"
go mod edit -json | jq '.Module.Path'

echo
echo "Locate ContentReader and SetProps definitions:"
rg -l 'type ContentReader|func .*SetProps' "$GOPATH/pkg/mod/github.com/jfrog/jfrog-client-go" >/tmp/client_go_matches.txt || true
cat /tmp/client_go_matches.txt

echo
echo "GetError definition usages:"
rg -n -C4 'func .*GetError|type ContentReader|GetError\(\s*\)' "$GOPATH/pkg/mod/github.com/jfrog/jfrog-client-go" >/tmp/content_reader_geterror.txt || true
cat /tmp/content_reader_geterror.txt

echo
echo "SetProps definition(s):"
rg -n -C8 'func .*SetProps|SetPropsParams' "$GOPATH/pkg/mod/github.com/jfrog/jfrog-client-go" >/tmp/setprops.txt || true
cat /tmp/setprops.txt

Repository: jfrog/jfrog-cli-artifactory

Length of output: 961


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Fetch jfrog-client-go v1.55.1 source to tmp without touching GOPATH:"
tmp="$(mktemp -d)"
cd "$tmp"
cat > go.mod <<EOF
module testprobe
go 1.23
require github.com/jfrog/jfrog-client-go v1.55.1-0.20260508101905-a17af78a38d7
EOF

env GOPATH="$tmp/gopath" go mod download -json github.com/jfrog/jfrog-client-go@v1.55.1-0.20260508101905-a17af78a38d7
path="$(env GOPATH="$tmp/gopath" go env GOPATH)/pkg/mod/github.com/jfrog/jfrog-client-go@v1.55.1-0.20260508101905-a17af78a38d7"
echo "Module path: $path"

echo
echo "Candidate files:"
find "$path" -type f \( -name 'content_reader.go' -o -name '*setprops*' -o -name '*search*' \) -print | sed "s|$path||" | sort

echo
echo "GetError definitions/usages:"
rg -n -C4 'func .*GetError|type ContentReader|GetError\(\s*\)' "$path" || true

echo
echo "ContentReader type and methods:"
rg -n -C6 'type ContentReader|func \(.*ContentReader\).*GetError|func \(.*ContentReader\).*Close|func \(.*ContentReader\).*ReadFile' "$path" || true

echo
echo "SetProps implementations:"
rg -n -C10 'func .*SetProps|func .*PropsParams|SetPropsParams' "$path" || true

echo
echo "Relevant code snippets extracted by file:"
for f in \
  "$path/artifactory/servic"*".go" \
  "$path/artifactory/servics/props.go" \
  "$path/artifactory/servics/props.go" \
  "$path/artifactory/servics/props.go" \
  "$path/content/**/*.go" \
  "$path/content/**/..." ""
do
  [ -f "$f" ] || continue
  echo "--- $f"
  rg -n -C6 'type ContentReader|func .*GetError|func .*SetProps|reader\.GetError|GetError\(\s*\)' "$f" || true
done

Repository: jfrog/jfrog-cli-artifactory

Length of output: 1377


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "GitHub raw source snippets for jfrog-client-go ContentReader and props SetProps:"
base="https://raw.githubusercontent.com/jfrog/jfrog-client-go/v1.55.1-0.20260508101905-a17af105-a17af78a38d7"

printf '\n--- ContentReader definition/usages ---\n'
for f in \
  "$base/artifactory/services/props.go" \
  "$base/artifactory/services/props.go"; do
  [ "${f#*://}" != "$f" ] || continue
  echo "URL: $f"
  curl -fsSL "$f" 2>/dev/null | rg -n -C8 'type ContentReader|func .*GetError|reader\.GetError|GetError\(\s*\)|func .*SetProps|PropsParams' || true
done

printf '\n--- direct content search ---\n'
curl -fsSL "https://api.github.com/search/code?q=repo:%22jfrog/jfrog-client-go%22+get+ContentReader+GetError+version:%22v1.55.1-0.20260508101905-a17af78a38d7%22" \
  | jq -r '.items[].html_url?:' | sort -u | head -20

printf '\n--- props.go from GitHub tag with curl only ---\n'
url="https://raw.githubusercontent.com/jfrog/jfrog-client-go/v1.55.1-0.20260508101905-a17af78a38d7/artifactory/services/props.go"
curl -fsSL "$url" 2>/dev/null | sed -n '1,260p'

Repository: jfrog/jfrog-cli-artifactory

Length of output: 809


🌐 Web query:

jfrog-client-go v1.55.1 ContentReader GetError SetProps reader.GetError

💡 Result:

In jfrog-client-go, the ContentReader struct is a utility used to handle large API results (such as search results) efficiently without loading all data into memory [1][2]. The methods you mentioned are used as follows: 1. ContentReader.GetError: This method is called after iterating through the reader's records to check if any errors occurred during the streaming process (e.g., during NextRecord) [1][2]. It should be called after your loop finishes [1]. 2. SetProps: This is a method on the Artifactory services manager (e.g., ArtifactoryServicesManagerImp) used to apply properties to files [3][4]. When using the version of SetProps that operates on a collection of artifacts, it requires a PropsParams struct, which contains a Reader field populated with a ContentReader [3][5]. Typical workflow: 1. Obtain a ContentReader by calling an API like SearchFiles [1][2]. 2. Iterate through the records using reader.NextRecord(target) [1][2]. 3. If necessary, use the reader to set properties via SetProps by assigning the reader to the Reader field in the services.PropsParams struct [5]. 4. Always close the reader using reader.Close to release resources, often deferred immediately after opening [1][2]. 5. Check for any errors encountered during streaming by calling reader.GetError after the iteration [1][2]. Note that there are known concurrency considerations with ContentReader, and recent updates (such as those in 2026) have addressed race conditions involving the internal channels of the reader [6].

Citations:


Check the search reader error after SetProps.

SearchFiles returns a ContentReader that reports streaming/parse failures through GetError(). SetProps consumes the reader, so a read failure can leave count at 0 without returning tagErr, and the caller reports “No deployed artifacts found to tag” instead of the search error.

♻️ Proposed change
 	count, setErr := servicesManager.SetProps(services.PropsParams{Reader: reader, Props: buildProps})
+	if readerErr := reader.GetError(); readerErr != nil {
+		log.Debug("Search reader reported an error: " + readerErr.Error())
+	}
 	if closeErr := reader.Close(); closeErr != nil {
 		log.Debug("Failed to close search reader: " + closeErr.Error())
 	}
🧰 Tools
🪛 ast-grep (0.45.0)

[warning] 225-225: A log/format call (log.Print/Printf/Println, the Fatal/Panic variants, fmt.Sprintf, or a structured logger's Info/Warn/Error/Debug method) is given a message built by concatenating a string literal with a non-literal value such as request data. Unsanitized, attacker-controlled input written to logs enables log forging / CRLF injection: an attacker can inject newlines to spoof log entries or break log parsers. Do not concatenate raw input into the log message; pass it as a separate structured field/argument (e.g. 'log.Printf("user: %s", user)' or 'logger.Info("login", "user", user)') and strip or escape newline characters first.
Context: log.Debug("Failed to close search reader: " + closeErr.Error())
Note: [CWE-117] Improper Output Neutralization for Logs.

(log-injection-request-data-concat-go)

🤖 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/flexpack/maven.go` around lines 217 - 232, Update
tagArtifactsInRepo to inspect reader.GetError() after SetProps consumes the
search reader, before returning the count. Propagate the reader error when
present so streaming or parse failures are returned instead of reporting zero
artifacts; preserve the existing SetProps error handling and reader.Close
behavior.

Comment on lines +370 to +393
func extractResolutionArgs(userArgs []string) []string {
var extracted []string
for i := 0; i < len(userArgs); i++ {
arg := userArgs[i]
switch {
case strings.HasPrefix(arg, "-P"), strings.HasPrefix(arg, "--activate-profiles="),
strings.HasPrefix(arg, "-D"), strings.HasPrefix(arg, "--define="),
strings.HasPrefix(arg, "--settings="), strings.HasPrefix(arg, "-f="),
strings.HasPrefix(arg, "--file="), strings.HasPrefix(arg, "-gs="),
strings.HasPrefix(arg, "--global-settings="),
arg == "-o", arg == "--offline":
extracted = append(extracted, arg)
case isSeparateValueFlag(arg),
arg == "--activate-profiles", arg == "--define":
extracted = append(extracted, arg)
// These flags take their value as the next token.
if i+1 < len(userArgs) {
i++
extracted = append(extracted, userArgs[i])
}
}
}
return extracted
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Handle -P with a space-separated value.

Maven accepts mvn deploy -P prod. The first case matches -P through strings.HasPrefix, so the value token prod is dropped and a bare -P is forwarded to the internal mvn dependency:tree call. Maven then either fails with a missing-argument error or consumes the next internal argument. Dependency resolution no longer matches the build, which is the purpose of this function.

Match the exact -P (and -D) tokens in the separate-value branch before the prefix cases.

🐛 Proposed fix
 func isSeparateValueFlag(arg string) bool {
-	return arg == "-s" || arg == "--settings" ||
+	return arg == "-P" || arg == "-D" ||
+		arg == "-s" || arg == "--settings" ||
 		arg == "-f" || arg == "--file" ||
 		arg == "-gs" || arg == "--global-settings"
 }
 		switch {
-		case strings.HasPrefix(arg, "-P"), strings.HasPrefix(arg, "--activate-profiles="),
+		case isSeparateValueFlag(arg),
+			arg == "--activate-profiles", arg == "--define":
+			extracted = append(extracted, arg)
+			// These flags take their value as the next token.
+			if i+1 < len(userArgs) {
+				i++
+				extracted = append(extracted, userArgs[i])
+			}
+		case strings.HasPrefix(arg, "-P"), strings.HasPrefix(arg, "--activate-profiles="),
 			strings.HasPrefix(arg, "-D"), strings.HasPrefix(arg, "--define="),
 			strings.HasPrefix(arg, "--settings="), strings.HasPrefix(arg, "-f="),
 			strings.HasPrefix(arg, "--file="), strings.HasPrefix(arg, "-gs="),
 			strings.HasPrefix(arg, "--global-settings="),
 			arg == "-o", arg == "--offline":
 			extracted = append(extracted, arg)
-		case isSeparateValueFlag(arg),
-			arg == "--activate-profiles", arg == "--define":
-			extracted = append(extracted, arg)
-			// These flags take their value as the next token.
-			if i+1 < len(userArgs) {
-				i++
-				extracted = append(extracted, userArgs[i])
-			}
 		}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
func extractResolutionArgs(userArgs []string) []string {
var extracted []string
for i := 0; i < len(userArgs); i++ {
arg := userArgs[i]
switch {
case strings.HasPrefix(arg, "-P"), strings.HasPrefix(arg, "--activate-profiles="),
strings.HasPrefix(arg, "-D"), strings.HasPrefix(arg, "--define="),
strings.HasPrefix(arg, "--settings="), strings.HasPrefix(arg, "-f="),
strings.HasPrefix(arg, "--file="), strings.HasPrefix(arg, "-gs="),
strings.HasPrefix(arg, "--global-settings="),
arg == "-o", arg == "--offline":
extracted = append(extracted, arg)
case isSeparateValueFlag(arg),
arg == "--activate-profiles", arg == "--define":
extracted = append(extracted, arg)
// These flags take their value as the next token.
if i+1 < len(userArgs) {
i++
extracted = append(extracted, userArgs[i])
}
}
}
return extracted
}
func extractResolutionArgs(userArgs []string) []string {
var extracted []string
for i := 0; i < len(userArgs); i++ {
arg := userArgs[i]
switch {
case isSeparateValueFlag(arg),
arg == "--activate-profiles", arg == "--define":
extracted = append(extracted, arg)
// These flags take their value as the next token.
if i+1 < len(userArgs) {
i++
extracted = append(extracted, userArgs[i])
}
case strings.HasPrefix(arg, "-P"), strings.HasPrefix(arg, "--activate-profiles="),
strings.HasPrefix(arg, "-D"), strings.HasPrefix(arg, "--define="),
strings.HasPrefix(arg, "--settings="), strings.HasPrefix(arg, "-f="),
strings.HasPrefix(arg, "--file="), strings.HasPrefix(arg, "-gs="),
strings.HasPrefix(arg, "--global-settings="),
arg == "-o", arg == "--offline":
extracted = append(extracted, arg)
}
}
return extracted
}
🤖 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/flexpack/maven.go` around lines 370 - 393, Update
extractResolutionArgs so exact -P and -D tokens are handled in the
separate-value branch, consuming and forwarding their following value token.
Keep prefixed forms such as -Pprofile and -Dkey=value in the existing
inline-value branch, while preserving the current handling of the other
separate-value flags.

Comment thread artifactory/commands/flexpack/maven.go

@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: 3

🤖 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/flexpack/maven.go`:
- Around line 145-165: Validate each module’s deployURL against serverDetails
before using extractRepoKeyFromUrl and repository-resolution logic in the
build-property tagging loop. If the URL targets another Artifactory instance,
resolve it with matching server details when supported; otherwise skip
finalization for that module rather than using servicesManager against the
configured server.

In `@artifactory/commands/mvn/utils.go`:
- Around line 271-276: Replace the direct os.WriteFile call in the build-info
update flow with an atomic same-directory temporary-file write: create the temp
file, apply the existing perm mode, write updated, close it successfully, then
rename it over buildInfoPath. Preserve the best-effort debug logging and ensure
cleanup of the temporary file on any failure before the rename.
- Around line 171-173: Resolve the selected Maven server via ServerDetails()
before constructing MvnUtils, including the native-mode path where mc.init() is
skipped. Pass the resolved server details into MvnUtils so
CollectMavenBuildInfoWithFlexPack receives the configured --server-id rather
than nil or the default server.
🪄 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: 57c4d15d-ba4a-4fc0-9974-0d4f59005204

📥 Commits

Reviewing files that changed from the base of the PR and between cd71b62 and 6c12d00.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (5)
  • artifactory/commands/flexpack/maven.go
  • artifactory/commands/flexpack/maven_test.go
  • artifactory/commands/mvn/mvn.go
  • artifactory/commands/mvn/utils.go
  • go.mod
🚧 Files skipped from review as they are similar to previous changes (3)
  • artifactory/commands/mvn/mvn.go
  • artifactory/commands/flexpack/maven_test.go
  • go.mod

Comment on lines 145 to +165
servicesManager, err := utils.CreateServiceManager(serverDetails, -1, 0, false)
if err != nil {
return fmt.Errorf("failed to create services manager: %w", err)
}

// Get Maven artifact info from pom.xml
groupId, artifactId, version, err := getMavenArtifactCoordinates(workingDir)
if err != nil {
return fmt.Errorf("failed to get Maven artifact coordinates: %w", err)
}

// Get the repository Maven deployed to from settings.xml or pom.xml
targetRepo, err := getMavenDeployRepository(workingDir)
if err != nil {
log.Warn("Could not determine Maven deploy repository, skipping build properties: " + err.Error())
return nil
}

// Create search pattern for the specific deployed artifacts in the target repository
artifactPath := fmt.Sprintf("%s/%s/%s/%s/%s-*",
targetRepo,
strings.ReplaceAll(groupId, ".", "/"), artifactId, version, artifactId)

// Search for deployed artifacts using the specific pattern
searchParams := services.SearchParams{
CommonParams: &specutils.CommonParams{
Pattern: artifactPath,
},
}

searchReader, err := servicesManager.SearchFiles(searchParams)
if err != nil {
return fmt.Errorf("failed to search for deployed artifacts: %w", err)
}
defer func() {
if closeErr := searchReader.Close(); closeErr != nil {
log.Debug(fmt.Sprintf("Failed to close search reader: %s", closeErr))
// Group each module's artifact checksums by the PHYSICAL repo it deployed to (resolving virtual ->
// default-deployment). Modules may deploy to DIFFERENT repos, so this is per-module, not one repo
// for the whole reactor. overrideURL (-DaltDeploymentRepository / settings) applies to every module.
physicalByKey := make(map[string]string) // repoKey -> physical repo (GetRepository cache)
sha256sByRepo := make(map[string][]string) // physical repo -> checksums to tag
for i := range buildInfo.Modules {
module := &buildInfo.Modules[i]
deployURL := overrideURL
if deployURL == "" {
deployURL = moduleDeployURLs[module.Id]
}
}()

// Filter to only artifacts modified in the last 2 minutes (just deployed)
cutoffTime := time.Now().Add(-2 * time.Minute)
var recentArtifacts []specutils.ResultItem

for item := new(specutils.ResultItem); searchReader.NextRecord(item) == nil; item = new(specutils.ResultItem) {
// Parse the modified time
modTime, err := time.Parse("2006-01-02T15:04:05.999Z", item.Modified)
if err != nil {
log.Debug("Could not parse modified time for " + item.Name + ": " + err.Error())
if deployURL == "" {
log.Debug("No deployment repository for module " + module.Id + "; skipping its build-property tagging")
continue
}

// Only include artifacts modified after cutoff
if modTime.After(cutoffTime) {
recentArtifacts = append(recentArtifacts, *item)
}
}

if len(recentArtifacts) == 0 {
log.Warn("No recently deployed artifacts found")
return nil
}

// Create build properties in the same format as NPM/traditional implementations
timestamp := strconv.FormatInt(time.Now().UnixNano()/int64(time.Millisecond), 10) // Unix milliseconds like NPM
buildProps := fmt.Sprintf("build.name=%s;build.number=%s;build.timestamp=%s", buildName, buildNumber, timestamp)
if projectKey := buildArgs.GetProject(); projectKey != "" {
buildProps += fmt.Sprintf(";build.project=%s", projectKey)
}

buildProps = civcs.MergeWithUserProps(buildProps, workingDir)

// Set properties on each recent artifact individually
for _, artifact := range recentArtifacts {
// ResultItem has Repo, Path, and Name fields already separated
// Use AQL to find the exact artifact
aqlPattern := fmt.Sprintf(`{"repo":"%s","path":"%s","name":"%s"}`,
targetRepo, artifact.Path, artifact.Name)

searchParams := services.SearchParams{
CommonParams: &specutils.CommonParams{
Aql: specutils.Aql{
ItemsFind: aqlPattern,
},
},
}

reader, err := servicesManager.SearchFiles(searchParams)
if err != nil {
log.Warn(fmt.Sprintf("Failed to search for artifact %s: %s", artifact.Name, err))
repoKey, keyErr := extractRepoKeyFromUrl(deployURL)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Keep repository resolution on the Maven deployment server.

servicesManager targets serverDetails, but deployURL contributes only its repository key. If Maven deploys to another Artifactory instance, this code can search and tag the same repository key on the configured instance.

Validate that each deployment URL belongs to serverDetails. Otherwise, select server details for that deployment URL or skip finalization.

🧰 Tools
🪛 ast-grep (0.45.0)

[warning] 161-161: A log/format call (log.Print/Printf/Println, the Fatal/Panic variants, fmt.Sprintf, or a structured logger's Info/Warn/Error/Debug method) is given a message built by concatenating a string literal with a non-literal value such as request data. Unsanitized, attacker-controlled input written to logs enables log forging / CRLF injection: an attacker can inject newlines to spoof log entries or break log parsers. Do not concatenate raw input into the log message; pass it as a separate structured field/argument (e.g. 'log.Printf("user: %s", user)' or 'logger.Info("login", "user", user)') and strip or escape newline characters first.
Context: log.Debug("No deployment repository for module " + module.Id + "; skipping its build-property tagging")
Note: [CWE-117] Improper Output Neutralization for Logs.

(log-injection-request-data-concat-go)

🤖 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/flexpack/maven.go` around lines 145 - 165, Validate each
module’s deployURL against serverDetails before using extractRepoKeyFromUrl and
repository-resolution logic in the build-property tagging loop. If the URL
targets another Artifactory instance, resolve it with matching server details
when supported; otherwise skip finalization for that module rather than using
servicesManager against the configured server.

Comment on lines +171 to +173
// Use FlexPack to collect Maven build info. The user's goals/flags are forwarded so the
// internal dependency resolution matches the profiles/settings the build ran with.
err = flexpack.CollectMavenBuildInfoWithFlexPack(workingDir, buildName, buildNumber, mu.buildConf, mu.goals, mu.serverDetails)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

ast-grep outline artifactory/commands/mvn --items all --match 'MvnCommand|ServerDetails|Run'
rg -n -C6 '\bServerDetails\s*\(|\.ServerDetails\s*\(|SetServerDetails\s*\(' --type=go .

Repository: jfrog/jfrog-cli-artifactory

Length of output: 50384


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '--- mvn.go outline ---\n'
ast-grep outline artifactory/commands/mvn/mvn.go --items all --match 'MvnCommand|ServerDetails|Run|serverId|Set' || true

printf '\n--- serverDetails usages in mvn package ---\n'
rg -n -C5 'serverDetails|ServerDetails|serverID|server-id|--server-id|SetServerDetails' artifactory/commands/mvn --type=go

printf '\n--- relevant utils.go section ---\n'
sed -n '1,230p' artifactory/commands/mvn/utils.go | nl -ba | sed -n '1,230p'

Repository: jfrog/jfrog-cli-artifactory

Length of output: 256


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- mvn.go outline ---'
ast-grep outline artifactory/commands/mvn/mvn.go --items all --match 'MvnCommand|ServerDetails|Run|serverId|Set' || true

printf '%s\n' ''
printf '%s\n' '--- serverDetails usages in mvn package ---'
rg -n -C5 'serverDetails|ServerDetails|serverID|server-id|--server-id|SetServerDetails' artifactory/commands/mvn --type=go

printf '%s\n' ''
printf '%s\n' '--- relevant utils.go section ---'
sed -n '1,230p' artifactory/commands/mvn/utils.go | nl -ba | sed -n '1,230p'

Repository: jfrog/jfrog-cli-artifactory

Length of output: 11586


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- MvnUtils serverDetails call sites ---'
rg -n -C4 'SetServerDetails\(' --type=go . | sed -n '1,240p'

printf '%s\n' ''
printf '%s\n' '--- command creation and serverDetails assignment ---'
rg -n -C8 'NewMvnCommand|SetServerDetails|serverDetails|serverID|server-id' . --type=go | sed -n '1,260p'

Repository: jfrog/jfrog-cli-artifactory

Length of output: 34486


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- locate mvn cli setup ---'
rg -n -C5 'NewMvnCommand|JFrogCLI|rt_mvn|mvn' --type=go . | sed -n '1,320p'

printf '%s\n' ''
printf '%s\n' '--- core/config server resolution helpers ---'
rg -n -C5 'func ResolveServer|ResolveServer|ServerID|serverID|GetSpecificConfig|GetDefaultServerConf|server-id' jfrog-cli-core v2 config --type=go | sed -n '1,360p' || true

Repository: jfrog/jfrog-cli-artifactory

Length of output: 22844


Resolve the Maven server before forwarding it to FlexPack.

In native mode, Run skips mc.init() and passes mc.serverDetails directly to MvnUtils. Since NewMvnCommand() leaves serverDetails nil, the selected --server-id is not included in the FlexPack build-info extraction, and metadata/dependency tagging can use the configured default server instead. Resolve the server through ServerDetails() before creating MvnUtils.

🤖 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/mvn/utils.go` around lines 171 - 173, Resolve the
selected Maven server via ServerDetails() before constructing MvnUtils,
including the native-mode path where mc.init() is skipped. Pass the resolved
server details into MvnUtils so CollectMavenBuildInfoWithFlexPack receives the
configured --server-id rather than nil or the default server.

Comment on lines +271 to +276
perm := os.FileMode(0644)
if info, statErr := os.Stat(buildInfoPath); statErr == nil {
perm = info.Mode().Perm()
}
if err = os.WriteFile(buildInfoPath, updated, perm); err != nil {
log.Debug("Skipping maven build-mode stamp, could not write build info: " + err.Error())

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Write the build-info update atomically.

os.WriteFile truncates the existing build-info file before it writes the new content. A short write, disk-full error, or interruption can destroy valid extractor output even though this helper treats the stamp as best-effort.

Write to a temporary file in the same directory. Apply the original permissions. Rename the completed file over the original only after a successful close.

🧰 Tools
🪛 ast-grep (0.45.0)

[warning] 275-275: A log/format call (log.Print/Printf/Println, the Fatal/Panic variants, fmt.Sprintf, or a structured logger's Info/Warn/Error/Debug method) is given a message built by concatenating a string literal with a non-literal value such as request data. Unsanitized, attacker-controlled input written to logs enables log forging / CRLF injection: an attacker can inject newlines to spoof log entries or break log parsers. Do not concatenate raw input into the log message; pass it as a separate structured field/argument (e.g. 'log.Printf("user: %s", user)' or 'logger.Info("login", "user", user)') and strip or escape newline characters first.
Context: log.Debug("Skipping maven build-mode stamp, could not write build info: " + err.Error())
Note: [CWE-117] Improper Output Neutralization for Logs.

(log-injection-request-data-concat-go)

🤖 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/mvn/utils.go` around lines 271 - 276, Replace the direct
os.WriteFile call in the build-info update flow with an atomic same-directory
temporary-file write: create the temp file, apply the existing perm mode, write
updated, close it successfully, then rename it over buildInfoPath. Preserve the
best-effort debug logging and ensure cleanup of the temporary file on any
failure before the rename.

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

Labels

new feature Automatically generated release notes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant