Skip to content

Commit

Permalink
Support running npm install without package.json (#206)
Browse files Browse the repository at this point in the history
  • Loading branch information
yahavi authored Oct 19, 2023
1 parent 8eb64e7 commit e1b1924
Show file tree
Hide file tree
Showing 13 changed files with 137 additions and 98 deletions.
6 changes: 3 additions & 3 deletions build/maven_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ import (
"bytes"
"encoding/json"
"fmt"
testdatautils "github.com/jfrog/build-info-go/build/testdata"
"github.com/jfrog/build-info-go/entities"
"github.com/jfrog/build-info-go/tests"
"github.com/jfrog/build-info-go/utils"
"os"
"path/filepath"
Expand Down Expand Up @@ -45,7 +45,7 @@ func TestGenerateBuildInfoForMavenProject(t *testing.T) {
assert.NoError(t, err)
// Create maven project
projectPath := filepath.Join(testdataDir, "maven", "project")
tmpProjectPath, cleanup := testdatautils.CreateTestProject(t, projectPath)
tmpProjectPath, cleanup := tests.CreateTestProject(t, projectPath)
defer cleanup()
// Add maven project as module in build-info.
mavenModule, err := mavenBuild.AddMavenModule(tmpProjectPath)
Expand All @@ -65,7 +65,7 @@ func TestGenerateBuildInfoForMavenProject(t *testing.T) {
match, err := entities.IsEqualModuleSlices(buildInfo.Modules, expectedModules)
assert.NoError(t, err)
if !match {
testdatautils.PrintBuildInfoMismatch(t, expectedModules, buildInfo.Modules)
tests.PrintBuildInfoMismatch(t, expectedModules, buildInfo.Modules)
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion build/npm.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ func newNpmModule(srcPath string, containingBuild *Build) (*NpmModule, error) {
}

// Read module name
packageInfo, err := buildutils.ReadPackageInfoFromPackageJson(srcPath, npmVersion)
packageInfo, err := buildutils.ReadPackageInfoFromPackageJsonIfExists(srcPath, npmVersion)
if err != nil {
return nil, err
}
Expand Down
10 changes: 5 additions & 5 deletions build/npm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ import (
"testing"
"time"

testdatautils "github.com/jfrog/build-info-go/build/testdata"
buildutils "github.com/jfrog/build-info-go/build/utils"
"github.com/jfrog/build-info-go/entities"
"github.com/jfrog/build-info-go/tests"
"github.com/jfrog/build-info-go/utils"
"github.com/stretchr/testify/assert"
)
Expand All @@ -28,7 +28,7 @@ func TestGenerateBuildInfoForNpm(t *testing.T) {
// Create npm project.
path, err := filepath.Abs(filepath.Join(".", "testdata"))
assert.NoError(t, err)
projectPath, cleanup := testdatautils.CreateNpmTest(t, path, "project3", false, npmVersion)
projectPath, cleanup := tests.CreateNpmTest(t, path, "project3", false, npmVersion)
defer cleanup()

// Install dependencies in the npm project.
Expand All @@ -45,11 +45,11 @@ func TestGenerateBuildInfoForNpm(t *testing.T) {

// Verify results.
expectedBuildInfoJson := filepath.Join(projectPath, "expected_npm_buildinfo.json")
expectedBuildInfo := testdatautils.GetBuildInfo(t, expectedBuildInfoJson)
expectedBuildInfo := tests.GetBuildInfo(t, expectedBuildInfoJson)
match, err := entities.IsEqualModuleSlices(buildInfo.Modules, expectedBuildInfo.Modules)
assert.NoError(t, err)
if !match {
testdatautils.PrintBuildInfoMismatch(t, expectedBuildInfo.Modules, buildInfo.Modules)
tests.PrintBuildInfoMismatch(t, expectedBuildInfo.Modules, buildInfo.Modules)
}
}

Expand All @@ -66,7 +66,7 @@ func TestFilterNpmArgsFlags(t *testing.T) {
// Create npm project.
path, err := filepath.Abs(filepath.Join(".", "testdata"))
assert.NoError(t, err)
projectPath, cleanup := testdatautils.CreateNpmTest(t, path, "project3", false, npmVersion)
projectPath, cleanup := tests.CreateNpmTest(t, path, "project3", false, npmVersion)
defer cleanup()

// Set arguments in npmArgs.
Expand Down
8 changes: 4 additions & 4 deletions build/python_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import (
"github.com/jfrog/build-info-go/entities"
"github.com/jfrog/build-info-go/utils/pythonutils"

testdatautils "github.com/jfrog/build-info-go/build/testdata"
"github.com/jfrog/build-info-go/tests"
"github.com/stretchr/testify/assert"
)

Expand Down Expand Up @@ -47,7 +47,7 @@ func testGenerateBuildInfoForPython(t *testing.T, pythonTool pythonutils.PythonT
assert.NoError(t, err)
// Create python project
projectPath := filepath.Join(testdataDir, "python", string(pythonTool))
tmpProjectPath, cleanup := testdatautils.CreateTestProject(t, projectPath)
tmpProjectPath, cleanup := tests.CreateTestProject(t, projectPath)
defer cleanup()

// Install dependencies in the pip project.
Expand All @@ -59,11 +59,11 @@ func testGenerateBuildInfoForPython(t *testing.T, pythonTool pythonutils.PythonT
if assert.NoError(t, err) {
// Verify results.
expectedBuildInfoJson := filepath.Join(projectPath, expectedResultsJson)
expectedBuildInfo := testdatautils.GetBuildInfo(t, expectedBuildInfoJson)
expectedBuildInfo := tests.GetBuildInfo(t, expectedBuildInfoJson)
match, err := entities.IsEqualModuleSlices(buildInfo.Modules, expectedBuildInfo.Modules)
assert.NoError(t, err)
if !match {
testdatautils.PrintBuildInfoMismatch(t, expectedBuildInfo.Modules, buildInfo.Modules)
tests.PrintBuildInfoMismatch(t, expectedBuildInfo.Modules, buildInfo.Modules)
}
}
}
10 changes: 8 additions & 2 deletions build/utils/npm.go
Original file line number Diff line number Diff line change
Expand Up @@ -442,10 +442,16 @@ type PackageInfo struct {
Scope string
}

func ReadPackageInfoFromPackageJson(packageJsonDirectory string, npmVersion *version.Version) (*PackageInfo, error) {
// Read and populate package name, version and scope from the package.json file in the provided directory.
// If package.json does not exist, return an empty PackageInfo struct.
func ReadPackageInfoFromPackageJsonIfExists(packageJsonDirectory string, npmVersion *version.Version) (*PackageInfo, error) {
packageJson, err := os.ReadFile(filepath.Join(packageJsonDirectory, "package.json"))
if err != nil {
return nil, err
if os.IsNotExist(err) {
return &PackageInfo{}, nil
} else {
return nil, err
}
}
return ReadPackageInfo(packageJson, npmVersion)
}
Expand Down
116 changes: 74 additions & 42 deletions build/utils/npm_test.go
Original file line number Diff line number Diff line change
@@ -1,22 +1,24 @@
package utils

import (
"github.com/jfrog/build-info-go/entities"
"github.com/stretchr/testify/require"
"encoding/json"
"os"
"path/filepath"
"reflect"
"strings"
"testing"
"time"

testdatautils "github.com/jfrog/build-info-go/build/testdata"
"github.com/jfrog/build-info-go/entities"
"github.com/stretchr/testify/require"

"github.com/jfrog/build-info-go/tests"
"github.com/jfrog/build-info-go/utils"
"github.com/stretchr/testify/assert"
)

var logger = utils.NewDefaultLogger(utils.INFO)

func TestReadPackageInfoFromPackageJson(t *testing.T) {
func TestReadPackageInfo(t *testing.T) {
npmVersion, _, err := GetNpmVersionAndExecPath(logger)
if err != nil {
assert.NoError(t, err)
Expand All @@ -31,22 +33,66 @@ func TestReadPackageInfoFromPackageJson(t *testing.T) {
&PackageInfo{Name: "build-info-go-tests", Version: "1.0.0", Scope: ""}},
{`{ "name": "@jfrog/build-info-go-tests", "version": "1.0.0", "description": "test package"}`,
&PackageInfo{Name: "build-info-go-tests", Version: "1.0.0", Scope: "@jfrog"}},
{`{}`, &PackageInfo{}},
}
for _, test := range tests {
t.Run(test.json, func(t *testing.T) {
packInfo, err := ReadPackageInfo([]byte(test.json), npmVersion)
if err != nil {
t.Error("No error was expected in this test", err)
}
assert.NoError(t, err)
assert.Equal(t, test.pi, packInfo)
})
}
}

equals := reflect.DeepEqual(test.pi, packInfo)
if !equals {
t.Error("expected:", test.pi, "got:", packInfo)
}
func TestReadPackageInfoFromPackageJsonIfExists(t *testing.T) {
// Prepare tests data
npmVersion, _, err := GetNpmVersionAndExecPath(logger)
assert.NoError(t, err)
path, err := filepath.Abs(filepath.Join("..", "testdata"))
assert.NoError(t, err)
projectPath, cleanup := tests.CreateNpmTest(t, path, "project1", false, npmVersion)
defer cleanup()

// Prepare test cases
testCases := []struct {
testName string
packageJsonDirectory string
expectedPackageInfo *PackageInfo
}{
{"Happy flow", projectPath, &PackageInfo{Name: "build-info-go-tests", Version: "1.0.0"}},
{"No package.json in path", path, &PackageInfo{Name: "", Version: ""}},
}

for _, testCase := range testCases {
t.Run(testCase.testName, func(t *testing.T) {
// Read package info
packageInfo, err := ReadPackageInfoFromPackageJsonIfExists(testCase.packageJsonDirectory, npmVersion)
assert.NoError(t, err)

// Remove "v" prefix, if exist
removeVersionPrefixes(packageInfo)

// Check results
assert.Equal(t, testCase.expectedPackageInfo.Name, packageInfo.Name)
assert.Equal(t, testCase.expectedPackageInfo.Version, strings.TrimPrefix(packageInfo.Version, "v"))
})
}
}

func TestReadPackageInfoFromPackageJsonIfExistErr(t *testing.T) {
// Prepare test data
npmVersion, _, err := GetNpmVersionAndExecPath(logger)
assert.NoError(t, err)
tempDir, createTempDirCallback := tests.CreateTempDirWithCallbackAndAssert(t)
assert.NoError(t, err)
defer createTempDirCallback()

// Create bad package.json file and expect error
assert.NoError(t, os.WriteFile(filepath.Join(tempDir, "package.json"), []byte("non json file"), 0600))
_, err = ReadPackageInfoFromPackageJsonIfExists(tempDir, npmVersion)
assert.IsType(t, &json.SyntaxError{}, err)
}

func TestGetDeployPath(t *testing.T) {
tests := []struct {
expectedPath string
Expand All @@ -57,10 +103,7 @@ func TestGetDeployPath(t *testing.T) {
}
for _, test := range tests {
t.Run(test.expectedPath, func(t *testing.T) {
actualPath := test.pi.GetDeployPath()
if actualPath != test.expectedPath {
t.Error("expected:", test.expectedPath, "got:", actualPath)
}
assert.Equal(t, test.expectedPath, test.pi.GetDeployPath())
})
}
}
Expand Down Expand Up @@ -93,15 +136,8 @@ func TestParseDependencies(t *testing.T) {
}
dependencies := make(map[string]*dependencyInfo)
err = parseDependencies(dependenciesJsonList, []string{"root"}, dependencies, npmLsDependencyParser, utils.NewDefaultLogger(utils.INFO))
if err != nil {
t.Error(err)
}
if len(expectedDependenciesList) != len(dependencies) {
t.Error("The expected dependencies list length is", len(expectedDependenciesList), "and should be:\n", expectedDependenciesList,
"\nthe actual dependencies list length is", len(dependencies), "and the list is:\n", dependencies)
t.Error("The expected dependencies list length is", len(expectedDependenciesList), "and should be:\n", expectedDependenciesList,
"\nthe actual dependencies list length is", len(dependencies), "and the list is:\n", dependencies)
}
assert.NoError(t, err)
assert.Equal(t, len(expectedDependenciesList), len(dependencies))
for _, eDependency := range expectedDependenciesList {
found := false
for aDependency, v := range dependencies {
Expand All @@ -110,9 +146,7 @@ func TestParseDependencies(t *testing.T) {
break
}
}
if !found {
t.Error("The expected dependency:", eDependency, "is missing from the actual dependencies list:\n", dependencies)
}
assert.True(t, found, "The expected dependency:", eDependency, "is missing from the actual dependencies list:\n", dependencies)
}
}

Expand All @@ -136,9 +170,7 @@ func TestAppendScopes(t *testing.T) {
}
for _, v := range scopes {
result := appendScopes(v.a, v.b)
if !assert.ElementsMatch(t, result, v.expected) {
t.Errorf("appendScopes(\"%s\",\"%s\") => '%s', want '%s'", v.a, v.b, result, v.expected)
}
assert.ElementsMatch(t, result, v.expected, "appendScopes(\"%s\",\"%s\") => '%s', want '%s'", v.a, v.b, result, v.expected)
}
}

Expand All @@ -148,7 +180,7 @@ func TestBundledDependenciesList(t *testing.T) {
path, err := filepath.Abs(filepath.Join("..", "testdata"))
assert.NoError(t, err)

projectPath, cleanup := testdatautils.CreateNpmTest(t, path, "project1", false, npmVersion)
projectPath, cleanup := tests.CreateNpmTest(t, path, "project1", false, npmVersion)
defer cleanup()
cacachePath := filepath.Join(projectPath, "tmpcache")
npmArgs := []string{"--cache=" + cacachePath}
Expand All @@ -168,7 +200,7 @@ func TestConflictsDependenciesList(t *testing.T) {
path, err := filepath.Abs(filepath.Join("..", "testdata"))
assert.NoError(t, err)

projectPath, cleanup := testdatautils.CreateNpmTest(t, path, "project5", true, npmVersion)
projectPath, cleanup := tests.CreateNpmTest(t, path, "project5", true, npmVersion)
defer cleanup()
cacachePath := filepath.Join(projectPath, "tmpcache")
npmArgs := []string{"--cache=" + cacachePath}
Expand All @@ -186,7 +218,7 @@ func TestDependencyWithNoIntegrity(t *testing.T) {
// Create the second npm project which has a transitive dependency without integrity (ansi-regex:5.0.0).
path, err := filepath.Abs(filepath.Join("..", "testdata"))
assert.NoError(t, err)
projectPath, cleanup := testdatautils.CreateNpmTest(t, path, "project2", true, npmVersion)
projectPath, cleanup := tests.CreateNpmTest(t, path, "project2", true, npmVersion)
defer cleanup()

// Run npm CI to create this special case where the 'ansi-regex:5.0.0' is missing the integrity.
Expand All @@ -209,7 +241,7 @@ func TestDependencyPackageLockOnly(t *testing.T) {
if !npmVersion.AtLeast("7.0.0") {
t.Skip("Running on npm v7 and above only, skipping...")
}
path, cleanup := testdatautils.CreateTestProject(t, filepath.Join("..", "testdata/npm/project6"))
path, cleanup := tests.CreateTestProject(t, filepath.Join("..", "testdata/npm/project6"))
defer cleanup()
assert.NoError(t, utils.MoveFile(filepath.Join(path, "package-lock_test.json"), filepath.Join(path, "package-lock.json")))
// sleep so the package.json modified time will be bigger than the package-lock.json, this make sure it will recalculate lock file.
Expand Down Expand Up @@ -286,7 +318,7 @@ func TestDependenciesTreeDifferentBetweenOKs(t *testing.T) {
assert.NoError(t, err)
path, err := filepath.Abs(filepath.Join("..", "testdata"))
assert.NoError(t, err)
projectPath, cleanup := testdatautils.CreateNpmTest(t, path, "project4", true, npmVersion)
projectPath, cleanup := tests.CreateNpmTest(t, path, "project4", true, npmVersion)
defer cleanup()
cacachePath := filepath.Join(projectPath, "tmpcache")

Expand All @@ -299,7 +331,7 @@ func TestDependenciesTreeDifferentBetweenOKs(t *testing.T) {
dependencies, err := CalculateNpmDependenciesList("npm", projectPath, "bundle-dependencies", NpmTreeDepListParam{Args: npmArgs}, true, logger)
assert.NoError(t, err)

assert.Greaterf(t, len(dependencies), 0, "Error: dependencies are not found!")
assert.Greater(t, len(dependencies), 0, "Error: dependencies are not found!")

// Remove node_modules directory, then calculate dependencies by package-lock.
assert.NoError(t, utils.RemoveTempDir(filepath.Join(projectPath, "node_modules")))
Expand All @@ -308,7 +340,7 @@ func TestDependenciesTreeDifferentBetweenOKs(t *testing.T) {
assert.NoError(t, err)

// Asserting there is at least one dependency.
assert.Greaterf(t, len(dependencies), 0, "Error: dependencies are not found!")
assert.Greater(t, len(dependencies), 0, "Error: dependencies are not found!")
}

func TestNpmProdFlag(t *testing.T) {
Expand All @@ -325,7 +357,7 @@ func TestNpmProdFlag(t *testing.T) {
}
for _, entry := range testDependencyScopes {
func() {
projectPath, cleanup := testdatautils.CreateNpmTest(t, path, "project3", false, npmVersion)
projectPath, cleanup := tests.CreateNpmTest(t, path, "project3", false, npmVersion)
defer cleanup()
cacachePath := filepath.Join(projectPath, "tmpcache")
npmArgs := []string{"--cache=" + cacachePath, entry.scope}
Expand All @@ -350,7 +382,7 @@ func TestGetConfigCacheNpmIntegration(t *testing.T) {
// Create the first npm project which contains peerDependencies, devDependencies & bundledDependencies
path, err := filepath.Abs(filepath.Join("..", "testdata"))
assert.NoError(t, err)
projectPath, cleanup := testdatautils.CreateNpmTest(t, path, "project1", false, npmVersion)
projectPath, cleanup := tests.CreateNpmTest(t, path, "project1", false, npmVersion)
defer cleanup()
cachePath := filepath.Join(projectPath, "tmpcache")
npmArgs := []string{"--cache=" + cachePath}
Expand Down Expand Up @@ -387,7 +419,7 @@ func validateDependencies(t *testing.T, projectPath string, npmArgs []string) {
dependencies, err := CalculateNpmDependenciesList("npm", projectPath, "build-info-go-tests", NpmTreeDepListParam{Args: npmArgs}, true, logger)
assert.NoError(t, err)

assert.Greaterf(t, len(dependencies), 0, "Error: dependencies are not found!")
assert.Greater(t, len(dependencies), 0, "Error: dependencies are not found!")

// Remove node_modules directory, then calculate dependencies by package-lock.
assert.NoError(t, utils.RemoveTempDir(filepath.Join(projectPath, "node_modules")))
Expand All @@ -396,5 +428,5 @@ func validateDependencies(t *testing.T, projectPath string, npmArgs []string) {
assert.NoError(t, err)

// Asserting there is at least one dependency.
assert.Greaterf(t, len(dependencies), 0, "Error: dependencies are not found!")
assert.Greater(t, len(dependencies), 0, "Error: dependencies are not found!")
}
Loading

0 comments on commit e1b1924

Please sign in to comment.