Skip to content

Support npm operations using native npm client with .npmrc via --run-native flag #2952

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
May 14, 2025
Merged
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
1 change: 1 addition & 0 deletions .github/workflows/accessTests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ jobs:
uses: jfrog/.github/actions/install-local-artifactory@main
with:
RTLIC: ${{ secrets.RTLIC }}
VERSION: 7.104.15

- name: Get ID Token and Exchange Token
shell: bash
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/artifactoryTests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ jobs:
uses: jfrog/.github/actions/install-local-artifactory@main
with:
RTLIC: ${{ secrets.RTLIC }}
VERSION: 7.104.15

- name: Run Artifactory tests
run: go test -v github.com/jfrog/jfrog-cli --timeout 0 --test.artifactory --jfrog.url=http://127.0.0.1:8082 --jfrog.adminToken=${{ env.JFROG_TESTS_LOCAL_ACCESS_TOKEN }}
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/gradleTests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ jobs:
uses: jfrog/.github/actions/install-local-artifactory@main
with:
RTLIC: ${{ secrets.RTLIC }}
VERSION: 7.104.15

- name: Run Gradle tests
run: go test -v github.com/jfrog/jfrog-cli --timeout 0 --test.gradle
1 change: 1 addition & 0 deletions .github/workflows/mavenTests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ jobs:
uses: jfrog/.github/actions/install-local-artifactory@main
with:
RTLIC: ${{ secrets.RTLIC }}
VERSION: 7.104.15

- name: Run Maven tests
run: go test -v github.com/jfrog/jfrog-cli --timeout 0 --test.maven
1 change: 1 addition & 0 deletions .github/workflows/npmTests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ jobs:
uses: jfrog/.github/actions/install-local-artifactory@main
with:
RTLIC: ${{ secrets.RTLIC }}
VERSION: 7.104.15

- name: Run npm tests
env:
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/nugetTests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ jobs:
uses: jfrog/.github/actions/install-local-artifactory@main
with:
RTLIC: ${{ secrets.RTLIC }}
VERSION: 7.104.15

- name: Run NuGet tests
run: go test -v github.com/jfrog/jfrog-cli --timeout 0 --test.nuget
1 change: 1 addition & 0 deletions .github/workflows/pluginsTests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ jobs:
uses: jfrog/.github/actions/install-local-artifactory@main
with:
RTLIC: ${{ secrets.RTLIC }}
VERSION: 7.104.15

- name: Run plugins tests
run: go test -v github.com/jfrog/jfrog-cli --timeout 0 --test.plugins
1 change: 1 addition & 0 deletions .github/workflows/pythonTests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ jobs:
uses: jfrog/.github/actions/install-local-artifactory@main
with:
RTLIC: ${{ secrets.RTLIC }}
VERSION: 7.104.15

- name: Run Python tests
run: go test -v github.com/jfrog/jfrog-cli --timeout 0 --test.${{ matrix.suite }}
2 changes: 1 addition & 1 deletion .github/workflows/transferTests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ jobs:
with:
RTLIC: ${{ secrets.RTLIC_V6 }}
JFROG_HOME: ${{ runner.temp }}
VERSION: 6.23.21
VERSION: 7.104.15

- name: Run transfer tests
run: go test -v github.com/jfrog/jfrog-cli --timeout 0 --test.transfer --test.installDataTransferPlugin --jfrog.targetUrl=${{ secrets.PLATFORM_URL }} --jfrog.targetAdminToken=${{ secrets.PLATFORM_ADMIN_TOKEN }} --jfrog.home=${{ runner.temp }} --ci.runId=${{ runner.os }}-transfer-6
14 changes: 11 additions & 3 deletions buildtools/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -934,9 +934,17 @@ func NpmPublishCmd(c *cli.Context) (err error) {
if npmCmd.GetXrayScan() {
commandsUtils.ConditionalUploadScanFunc = scan.ConditionalUploadDefaultScanFunc
}
printDeploymentView, detailedSummary := log.IsStdErrTerminal(), npmCmd.IsDetailedSummary()
if !detailedSummary {
npmCmd.SetDetailedSummary(printDeploymentView)

var printDeploymentView, detailedSummary bool

// Deployment view and Detailed summary is not supported when using npmrc for publishing since transfer details are not available.
if npmCmd.UseNative() {
printDeploymentView, detailedSummary = false, false
} else {
printDeploymentView, detailedSummary = log.IsStdErrTerminal(), npmCmd.IsDetailedSummary()
if !detailedSummary {
npmCmd.SetDetailedSummary(printDeploymentView)
}
}
err = commands.Exec(npmCmd)
result := npmCmd.Result()
Expand Down
6 changes: 3 additions & 3 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -189,10 +189,10 @@ require (
sigs.k8s.io/yaml v1.4.0 // indirect
)

replace github.com/jfrog/jfrog-cli-core/v2 => github.com/jfrog/jfrog-cli-core/v2 v2.31.1-0.20250508140039-bfb5e9a50dfe
replace github.com/jfrog/jfrog-cli-artifactory => github.com/jfrog/jfrog-cli-artifactory v0.2.5-0.20250514065555-2ad0e403ae04

replace github.com/jfrog/jfrog-client-go => github.com/jfrog/jfrog-client-go v1.28.1-0.20250508130334-f159cff9b11a
replace github.com/jfrog/jfrog-cli-core/v2 => github.com/jfrog/jfrog-cli-core/v2 v2.31.1-0.20250514055103-d3d0d25f7c85

replace github.com/jfrog/jfrog-cli-artifactory => github.com/jfrog/jfrog-cli-artifactory v0.2.5-0.20250509052840-40acf11c6080
replace github.com/jfrog/jfrog-client-go => github.com/jfrog/jfrog-client-go v1.28.1-0.20250508130334-f159cff9b11a

replace github.com/jfrog/jfrog-cli-security => github.com/jfrog/jfrog-cli-security v1.17.2-0.20250511132918-d9cc4cd50020
8 changes: 4 additions & 4 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -186,10 +186,10 @@ github.com/jfrog/gofrog v1.7.6 h1:QmfAiRzVyaI7JYGsB7cxfAJePAZTzFz0gRWZSE27c6s=
github.com/jfrog/gofrog v1.7.6/go.mod h1:ntr1txqNOZtHplmaNd7rS4f8jpA5Apx8em70oYEe7+4=
github.com/jfrog/jfrog-apps-config v1.0.1 h1:mtv6k7g8A8BVhlHGlSveapqf4mJfonwvXYLipdsOFMY=
github.com/jfrog/jfrog-apps-config v1.0.1/go.mod h1:8AIIr1oY9JuH5dylz2S6f8Ym2MaadPLR6noCBO4C22w=
github.com/jfrog/jfrog-cli-artifactory v0.2.5-0.20250509052840-40acf11c6080 h1:UWqfVXAvKdfru1k7PwNjZXuufcJR6HukzfThz+J2nFw=
github.com/jfrog/jfrog-cli-artifactory v0.2.5-0.20250509052840-40acf11c6080/go.mod h1:MVap+qb0Gft98yJdPe2si2GqtxQczDVEuC8oxbUTDLk=
github.com/jfrog/jfrog-cli-core/v2 v2.31.1-0.20250508140039-bfb5e9a50dfe h1:17ZlsX/X4orRHqFoyGnEBHIfKFu49AVvyVoRqk7635E=
github.com/jfrog/jfrog-cli-core/v2 v2.31.1-0.20250508140039-bfb5e9a50dfe/go.mod h1:8ODu50AZkrP5xXvUGLizFTL1+qJknSShHNGhGl7OtFQ=
github.com/jfrog/jfrog-cli-artifactory v0.2.5-0.20250514065555-2ad0e403ae04 h1:TtRvALfZVzm0PRyEg816TbuPU4pbFITEnSOQRgqNScg=
github.com/jfrog/jfrog-cli-artifactory v0.2.5-0.20250514065555-2ad0e403ae04/go.mod h1:A2HKG/6ICMzgUrhO0N1cl8x6rces0JP2GcaiQP2dpbI=
github.com/jfrog/jfrog-cli-core/v2 v2.31.1-0.20250514055103-d3d0d25f7c85 h1:8MP9xgYm9bT6LeznzhyrecjpfTZQr2pBa4hxXEV36CI=
github.com/jfrog/jfrog-cli-core/v2 v2.31.1-0.20250514055103-d3d0d25f7c85/go.mod h1:8ODu50AZkrP5xXvUGLizFTL1+qJknSShHNGhGl7OtFQ=
github.com/jfrog/jfrog-cli-platform-services v1.9.0 h1:r/ETgJuMUOUu12w20ydsF6paqEaj0khH6bxMRsdNz1Y=
github.com/jfrog/jfrog-cli-platform-services v1.9.0/go.mod h1:pMZMSwhj7yA4VKyj0Skr2lObIyGpZUxNJ40DSLKXU38=
github.com/jfrog/jfrog-cli-security v1.17.2-0.20250511132918-d9cc4cd50020 h1:CoWRc7ZLXfHojcyzifFG8fwaTniOLg/cDTPhAqQh/dc=
Expand Down
168 changes: 166 additions & 2 deletions npm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package main
import (
"fmt"
"github.com/jfrog/jfrog-cli-artifactory/artifactory/commands/generic"
utils2 "github.com/jfrog/jfrog-cli-core/v2/artifactory/commands/utils"
"github.com/jfrog/jfrog-cli-core/v2/artifactory/utils"
"github.com/jfrog/jfrog-client-go/http/httpclient"
"github.com/stretchr/testify/require"
Expand Down Expand Up @@ -148,12 +149,162 @@ func testNpm(t *testing.T, isLegacy bool) {
inttestutils.DeleteBuild(serverDetails.ArtifactoryUrl, tests.NpmBuildName, artHttpDetails)
}

func TestNpmPublishWithNpmrc(t *testing.T) {
testNpmPublishWithNpmrc(t, validateNpmPublish, "npmpublishrcproject", tests.NpmRepo, false)
}

func TestNpmPublishWithNpmrcScoped(t *testing.T) {
testNpmPublishWithNpmrc(t, validateNpmScopedPublish, "npmpublishrcscopedproject", tests.NpmScopedRepo, true)
}

func testNpmPublishWithNpmrc(t *testing.T, validationFunc func(t *testing.T, npmTest npmTestParams, isNpm7 bool), projectName string, repoName string, isScoped bool) {
initNpmTest(t)
defer cleanNpmTest(t)
wd, err := os.Getwd()
assert.NoError(t, err, "Failed to get current dir")
defer clientTestUtils.ChangeDirAndAssert(t, wd)
buildNumber := "1"
npmVersion, _, err := buildutils.GetNpmVersionAndExecPath(log.Logger)
if err != nil {
assert.NoError(t, err)
return
}

// Init npm project & npmp command for testing
npmProjectPath := initNpmPublishRcProjectTest(t, projectName)
configFilePath := filepath.Join(npmProjectPath, ".jfrog", "projects", "npm.yaml")

// fetch module id
packageJsonPath := npmProjectPath + "/package.json"
moduleName := readModuleId(t, packageJsonPath, npmVersion)

err = createNpmrcForTesting(t, configFilePath)
assert.NoError(t, err)

if isScoped {
addNpmScopeRegistryToNpmRc(t, npmProjectPath, packageJsonPath, npmVersion)
}

npmpCmd, err := publishUsingNpmrc(configFilePath, buildNumber)
assert.NoError(t, err)

result := npmpCmd.Result()
assert.NotNil(t, result)

validateNpmLocalBuildInfo(t, tests.NpmBuildName, buildNumber, moduleName)
assert.NoError(t, artifactoryCli.Exec("bp", tests.NpmBuildName, buildNumber))

// validation
testParams := npmTestParams{testName: "npm p",
nativeCommand: "npm publish",
legacyCommand: "rt npm-publish",
repo: repoName,
wd: npmProjectPath,
validationFunc: validateNpmPublish,
buildNumber: buildNumber,
moduleName: moduleName,
}
validationFunc(t, testParams, false)
}

func TestNpmInstallClientNative(t *testing.T) {
initNpmTest(t)
defer cleanNpmTest(t)
wd, err := os.Getwd()
assert.NoError(t, err, "Failed to get current dir")
defer clientTestUtils.ChangeDirAndAssert(t, wd)

npmVersion, _, err := buildutils.GetNpmVersionAndExecPath(log.Logger)
if err != nil {
assert.NoError(t, err)
return
}
buildNumber := "1"

npmProjectDirectory := initNpmProjectTest(t)
configFilePath := filepath.Join(npmProjectDirectory, ".jfrog", "projects", "npm.yaml")
err = createNpmrcForTesting(t, configFilePath)
assert.NoError(t, err)

clientTestUtils.ChangeDirAndAssert(t, npmProjectDirectory)
npmrcFileInfo, err := os.Stat(".npmrc")
if err != nil && os.IsNotExist(err) {
assert.Fail(t, err.Error())
}

packageJsonPath := npmProjectDirectory + "/package.json"
moduleName := readModuleId(t, packageJsonPath, npmVersion)
runJfrogCli(t, "npm", "i", "--run-native=true", "--build-name="+tests.NpmBuildName, "--build-number="+buildNumber)
validateNpmLocalBuildInfo(t, tests.NpmBuildName, buildNumber, moduleName)
assert.NoError(t, artifactoryCli.Exec("bp", tests.NpmBuildName, buildNumber))

npmTest := npmTestParams{
testName: "npm with run-native",
buildNumber: buildNumber,
npmArgs: "--run-native=true",
}

validateNpmInstall(t, npmTest, isNpm7(npmVersion))
postTestFileInfo, postTestFileInfoErr := os.Stat(".npmrc")
validateNpmrcFileInfo(t, npmTest, npmrcFileInfo, postTestFileInfo, err, postTestFileInfoErr)
validateIfFileWasEverModified(t, npmrcFileInfo, postTestFileInfo)
}

func createNpmrcForTesting(t *testing.T, configFilePath string) (err error) {
// Creation of npmrc - npmCommand.CreateTempNpmrc() function is used to create a npmrc file
npmCommand := npm.NewNpmCommand("install", true)
npmCommand.SetConfigFilePath(configFilePath)
npmCommand.SetServerDetails(serverDetails)
err = npmCommand.Init()
assert.NoError(t, err)
err = npmCommand.PreparePrerequisites(tests.NpmRepo)
assert.NoError(t, err)
err = npmCommand.CreateTempNpmrc()
return
}

func publishUsingNpmrc(configFilePath string, buildNumber string) (npm.NpmPublishCommand, error) {
args := []string{"--run-native=true", "--build-name=" + tests.NpmBuildName, "--build-number=" + buildNumber}
npmpCmd := npm.NewNpmPublishCommand()
npmpCmd.SetConfigFilePath(configFilePath).SetArgs(args)
err := npmpCmd.Init()
if err != nil {
return *npmpCmd, err
}
err = commands.Exec(npmpCmd)
if err != nil {
return *npmpCmd, err
}
return *npmpCmd, err
}

func readModuleId(t *testing.T, wd string, npmVersion *version.Version) string {
packageInfo, err := buildutils.ReadPackageInfoFromPackageJsonIfExists(filepath.Dir(wd), npmVersion)
assert.NoError(t, err)
return packageInfo.BuildInfoModuleId()
}

func addNpmScopeRegistryToNpmRc(t *testing.T, projectPath string, packageJsonPath string, npmVersion *version.Version) {
scope := getScopeFromPackageJson(t, packageJsonPath, npmVersion)
authConfig, err := serverDetails.CreateArtAuthConfig()
assert.NoError(t, err)
_, registry, err := utils2.GetArtifactoryNpmRepoDetails(tests.NpmScopedRepo, authConfig, false)
assert.NoError(t, err)
scopedRegistry := scope + ":registry=" + registry
npmrcFilePath := filepath.Join(projectPath, ".npmrc")
npmrcFile, err := os.OpenFile(npmrcFilePath, os.O_APPEND|os.O_WRONLY, 0644)
assert.NoError(t, err)
defer npmrcFile.Close()
_, err = npmrcFile.WriteString(scopedRegistry)
assert.NoError(t, err)
}

func getScopeFromPackageJson(t *testing.T, wd string, npmVersion *version.Version) string {
packageInfo, err := buildutils.ReadPackageInfoFromPackageJsonIfExists(filepath.Dir(wd), npmVersion)
assert.NoError(t, err)
return packageInfo.Scope
}

func TestNpmWithGlobalConfig(t *testing.T) {
initNpmTest(t)
defer cleanNpmTest(t)
Expand Down Expand Up @@ -261,6 +412,11 @@ func validateNpmrcFileInfo(t *testing.T, npmTest npmTestParams, npmrcFileInfo, p
assert.Nil(t, bcpNpmrc, "The file 'jfrog.npmrc.backup' was supposed to be deleted but it was not when running the configuration:\n%v", npmTest)
}

// if file was backed up then it's mod time should be changed
func validateIfFileWasEverModified(t *testing.T, fileInfo, postTestFileInfo os.FileInfo) {
assert.Equal(t, fileInfo.ModTime(), postTestFileInfo.ModTime())
}

func initNpmFilesTest(t *testing.T) (npmProjectPath, npmScopedProjectPath, npmNpmrcProjectPath, npmProjectCi, npmPostInstallProjectPath string) {
npmProjectPath = createNpmProject(t, "npmproject")
npmScopedProjectPath = createNpmProject(t, "npmscopedproject")
Expand All @@ -285,6 +441,14 @@ func initNpmProjectTest(t *testing.T) (npmProjectPath string) {
return
}

func initNpmPublishRcProjectTest(t *testing.T, projectName string) (npmProjectPath string) {
npmProjectPath = filepath.Dir(createNpmProject(t, projectName))
err := createConfigFileForTest([]string{npmProjectPath}, tests.NpmRemoteRepo, tests.NpmRepo, t, project.Npm, false)
assert.NoError(t, err)
prepareArtifactoryForNpmBuild(t, npmProjectPath)
return
}

func initNpmWorkspacesProjectTest(t *testing.T) (npmProjectPath string) {
npmProjectPath = filepath.Dir(createNpmProject(t, "npmworkspaces"))
err := createConfigFileForTest([]string{npmProjectPath}, tests.NpmRemoteRepo, tests.NpmRepo, t, project.Npm, false)
Expand Down Expand Up @@ -361,8 +525,8 @@ func validateNpmPublish(t *testing.T, npmTestParams npmTestParams, isNpm7 bool)
}

func validateNpmScopedPublish(t *testing.T, npmTestParams npmTestParams, isNpm7 bool) {
verifyExistInArtifactoryByProps(tests.GetNpmDeployedScopedArtifacts(isNpm7),
tests.NpmRepo+"/*",
verifyExistInArtifactoryByProps(tests.GetNpmDeployedScopedArtifacts(npmTestParams.repo, isNpm7),
npmTestParams.repo+"/*",
fmt.Sprintf("build.name=%v;build.number=%v;build.timestamp=*", tests.NpmBuildName, npmTestParams.buildNumber), t)
validateNpmCommonPublish(t, npmTestParams, isNpm7, true)
}
Expand Down
17 changes: 17 additions & 0 deletions testdata/npm/npmpublishrcproject/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"name": "jfrog-cli-tests",
"version": "1.0.0",
"description": "test package",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"dependencies": {
"xml": "1.0.1"
},
"devDependencies": {
"json": "9.0.6"
}
}
17 changes: 17 additions & 0 deletions testdata/npm/npmpublishrcscopedproject/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"name": "@jscope/jfrog-cli-tests",
"version": "1.0.0",
"description": "test package",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"dependencies": {
"xml": "1.0.1"
},
"devDependencies": {
"json": "9.0.6"
}
}
5 changes: 5 additions & 0 deletions testdata/npm_local_scoped_repository_config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"key": "${NPM_SCOPED_REPO}",
"rclass": "local",
"packageType": "npm"
}
Loading
Loading