Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions artifactory/commands/mvn/mvn.go
Original file line number Diff line number Diff line change
Expand Up @@ -146,17 +146,17 @@ func (mc *MvnCommand) isDeploymentRequested() bool {
if strings.HasSuffix(goal, ":help") || goal == "help" {
continue
}

// Exact match for standard Maven phases (most common case)
if goal == "install" || goal == "deploy" {
return true
}

// Prefix match for plugin:goal format (e.g., deploy:deploy-file, install:install-file)
if strings.HasPrefix(goal, "deploy:") || strings.HasPrefix(goal, "install:") {
return true
}

// Suffix match for full plugin name format (e.g., maven-deploy-plugin:deploy, maven-install-plugin:install)
// Note: Using suffix instead of Contains() to avoid false positives like "uninstall", "reinstall"
if strings.HasSuffix(goal, ":deploy") || strings.HasSuffix(goal, ":install") {
Expand Down Expand Up @@ -199,7 +199,8 @@ func (mc *MvnCommand) Run() error {
SetGoals(mc.goals).
SetInsecureTls(mc.insecureTls).
SetDisableDeploy(mc.deploymentDisabled).
SetThreads(mc.threads)
SetThreads(mc.threads).
SetServerDetails(mc.serverDetails)
if err = RunMvn(mvnParams); err != nil {
return err
}
Expand Down
22 changes: 22 additions & 0 deletions artifactory/commands/mvn/utils.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package mvn

import (
"context"
"errors"
"io"
"os"
"os/exec"
Expand Down Expand Up @@ -34,6 +36,7 @@ type MvnUtils struct {
insecureTls bool
disableDeploy bool
outputWriter io.Writer
serverDetails *config.ServerDetails
}

func NewMvnUtils() *MvnUtils {
Expand Down Expand Up @@ -85,6 +88,11 @@ func (mu *MvnUtils) SetOutputWriter(writer io.Writer) *MvnUtils {
return mu
}

func (mu *MvnUtils) SetServerDetails(serverDetails *config.ServerDetails) *MvnUtils {
mu.serverDetails = serverDetails
return mu
}

func RunMvn(mu *MvnUtils) error {
// FlexPack completely bypasses traditional Maven Build Info Extractor
if utils.ShouldRunNative(mu.configPath) {
Expand Down Expand Up @@ -156,6 +164,20 @@ func RunMvn(mu *MvnUtils) error {
if err != nil {
return err
}
workingDir, wdErr := os.Getwd()
if wdErr != nil {
return errorutils.CheckError(wdErr)
}
var restoreHealing func() error
restoreHealing, _, err = mu.runXrayComponentHealing(context.Background(), workingDir, mu.serverDetails)
if err != nil {
return err
}
defer func() {
if err != nil && restoreHealing != nil {
err = errors.Join(err, restoreHealing())
}
}()
var mvnOpts []string
if v := os.Getenv("MAVEN_OPTS"); v != "" {
mvnOpts = strings.Fields(v)
Expand Down
55 changes: 55 additions & 0 deletions artifactory/commands/mvn/xrayheal.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package mvn

import (
"context"

"github.com/jfrog/jfrog-cli-artifactory/artifactory/healcomponents"
cmaven "github.com/jfrog/jfrog-cli-artifactory/artifactory/healcomponents/maven"
buildUtils "github.com/jfrog/jfrog-cli-core/v2/common/build"
"github.com/jfrog/jfrog-cli-core/v2/common/project"
"github.com/jfrog/jfrog-cli-core/v2/utils/config"
"github.com/jfrog/jfrog-cli-core/v2/utils/xray"
)

func (mu *MvnUtils) runXrayComponentHealing(ctx context.Context, workingDir string, serverDetails *config.ServerDetails) (restore func() error, healed bool, err error) {
if healcomponents.IsComponentResolutionDisabled() {
return healcomponents.SkipHealing("Xray component healing disabled", nil)
}
command := cmaven.DeriveResolutionCommand(mu.goals)
if command == "" || mu.vConfig == nil || !mu.vConfig.IsSet("resolver") {
return healcomponents.SkipHealing("Xray component healing skipped", nil)
}
resolverRepo, resolverErr := mu.resolverRepoForHealing()
if resolverErr != nil {
return healcomponents.SkipHealing("Xray component healing skipped: could not determine resolver repo: ", resolverErr)
}
if resolverRepo == "" {
return healcomponents.SkipHealing("Xray component healing skipped: resolver repo is empty", nil)
}
if serverDetails == nil {
serverDetails, err = buildUtils.GetServerDetails(mu.vConfig)
if err != nil {
return healcomponents.SkipHealing("Xray component healing skipped: could not determine server details: ", err)
}
if serverDetails == nil {
return healcomponents.SkipHealing("Xray component healing skipped: could not determine server details", nil)
}
}
var projectKey string
if mu.buildConf != nil {
projectKey = mu.buildConf.GetProject()
}
xrayManager, xrayErr := xray.CreateXrayServiceManager(serverDetails, xray.WithScopedProjectKey(projectKey))
if xrayErr != nil {
return healcomponents.SkipHealing("Xray component healing skipped: could not create Xray service manager: ", xrayErr)
}
return healcomponents.RunIfEnabled(ctx, xrayManager, resolverRepo, cmaven.NewBuildToolWithGoals(mu.goals), command, workingDir, nil)
}

func (mu *MvnUtils) resolverRepoForHealing() (string, error) {
repoConfig, err := project.GetRepoConfigByPrefix(mu.configPath, project.ProjectConfigResolverPrefix, mu.vConfig)
if err != nil {
return "", err
}
return repoConfig.TargetRepo(), nil
}
17 changes: 17 additions & 0 deletions artifactory/commands/mvn/xrayheal_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package mvn

import (
"testing"

"github.com/jfrog/jfrog-cli-artifactory/artifactory/healcomponents"
"github.com/spf13/viper"
"github.com/stretchr/testify/assert"
)

func TestRunXrayComponentHealing_RespectsDisabledEnv(t *testing.T) {
t.Setenv(healcomponents.HealComponentsDisabledEnvVar, "true")
mu := NewMvnUtils().SetGoals([]string{"install"}).SetConfig(viper.New())
_, healed, err := mu.runXrayComponentHealing(t.Context(), t.TempDir(), nil)
assert.NoError(t, err)
assert.False(t, healed)
}
12 changes: 12 additions & 0 deletions artifactory/commands/npm/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ type CommonArgs struct {
npmArgs []string
serverDetails *config.ServerDetails
useNative bool
configFilePath string
executablePath string
}

func (ca *CommonArgs) SetServerDetails(serverDetails *config.ServerDetails) *CommonArgs {
Expand Down Expand Up @@ -45,6 +47,16 @@ func (ca *CommonArgs) SetUseNative(useNpmRc bool) *CommonArgs {
return ca
}

func (ca *CommonArgs) SetConfigFilePath(configFilePath string) *CommonArgs {
ca.configFilePath = configFilePath
return ca
}

func (ca *CommonArgs) SetExecutablePath(executablePath string) *CommonArgs {
ca.executablePath = executablePath
return ca
}

// CheckIsNativeAndFetchFilteredArgs checks if native mode should be enabled.
// It first checks the JFROG_RUN_NATIVE environment variable (preferred),
// then falls back to the deprecated --run-native flag for backward compatibility.
Expand Down
33 changes: 28 additions & 5 deletions artifactory/commands/npm/npmcommand.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package npm

import (
"bufio"
"context"
"errors"
"fmt"
"net/http"
Expand Down Expand Up @@ -56,9 +57,8 @@ var (

type NpmCommand struct {
CommonArgs
cmdName string
jsonOutput bool
executablePath string
cmdName string
jsonOutput bool
// Function to be called to restore the user's old npmrc and delete the one we created.
restoreNpmrcFunc func() error
workingDirectory string
Expand All @@ -73,6 +73,8 @@ type NpmCommand struct {
collectBuildInfo bool
buildInfoModule *build.NpmModule
installHandler *NpmInstallStrategy
// When true, the subsequent install uses npm ci to honor healed lockfile integrity.
healedLockfile bool
// When true, skips the 404 error handling that checks if packages are blocked by curation
disableCVSCheck bool
}
Expand Down Expand Up @@ -135,7 +137,7 @@ func (nc *NpmCommand) Init() error {
if err != nil {
return err
}

repoConfig, err := nc.getRepoConfig(vConfig)
if err != nil {
return err
Expand Down Expand Up @@ -348,6 +350,16 @@ func (nc *NpmCommand) Run() (err error) {
if err = nc.PreparePrerequisites(nc.repo); err != nil {
return
}
var restoreResolution func() error
restoreResolution, nc.healedLockfile, err = nc.runXrayComponentHealing(context.Background(), nc.cmdName, nc.workingDirectory, nc.npmArgs)
if err != nil {
return err
}
defer func() {
if err != nil && restoreResolution != nil {
err = errors.Join(err, restoreResolution())
}
}()
defer func() {
err = errors.Join(err, nc.installHandler.RestoreNpmrc())
}()
Expand Down Expand Up @@ -508,8 +520,19 @@ func (nc *NpmCommand) prepareBuildInfoModule() error {
return nil
}

func (nc *NpmCommand) effectiveNpmCommand() string {
if nc.healedLockfile && nc.cmdName == "install" {
return "ci"
}
return nc.cmdName
}

func (nc *NpmCommand) collectDependencies() error {
nc.buildInfoModule.SetNpmArgs(append([]string{nc.cmdName}, nc.npmArgs...))
npmCommand := nc.effectiveNpmCommand()
if npmCommand != nc.cmdName {
log.Info("Using npm ci after component resolution to install from the healed lockfile")
}
nc.buildInfoModule.SetNpmArgs(append([]string{npmCommand}, nc.npmArgs...))
return errorutils.CheckError(nc.buildInfoModule.Build())
}

Expand Down
13 changes: 12 additions & 1 deletion artifactory/commands/npm/publish.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package npm
import (
"archive/tar"
"compress/gzip"
"context"
"encoding/json"
"errors"
"fmt"
Expand Down Expand Up @@ -37,7 +38,6 @@ const (

type NpmPublishCommandArgs struct {
CommonArgs
executablePath string
workingDirectory string
collectBuildInfo bool
packedFilePaths []string
Expand Down Expand Up @@ -79,6 +79,7 @@ func (npc *NpmPublishCommand) ServerDetails() (*config.ServerDetails, error) {

func (npc *NpmPublishCommand) SetConfigFilePath(configFilePath string) *NpmPublishCommand {
npc.configFilePath = configFilePath
npc.CommonArgs.SetConfigFilePath(configFilePath)
return npc
}

Expand Down Expand Up @@ -172,6 +173,16 @@ func (npc *NpmPublishCommand) Run() (err error) {
if err != nil {
return err
}
var restoreResolution func() error
restoreResolution, _, err = npc.runXrayComponentHealingForPublish(context.Background(), "publish", npc.workingDirectory, npc.publishPath, npc.npmArgs)
if err != nil {
return err
}
defer func() {
if err != nil && restoreResolution != nil {
err = errors.Join(err, restoreResolution())
}
}()

var npmBuild *build.Build
var buildName, buildNumber, projectKey string
Expand Down
Loading
Loading