Skip to content

Commit

Permalink
Support running npm install without package.json
Browse files Browse the repository at this point in the history
  • Loading branch information
yahavi committed Oct 18, 2023
1 parent 8eb64e7 commit 57b4655
Show file tree
Hide file tree
Showing 7 changed files with 96 additions and 55 deletions.
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.ReadPackageInfoFromPackageJsonIfExist(srcPath, npmVersion)
if err != nil {
return nil, err
}
Expand Down
8 changes: 8 additions & 0 deletions build/testdata/test_helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,3 +73,11 @@ func PrintBuildInfoMismatch(t *testing.T, expected, actual []entities.Module) {
assert.NoError(t, err)
t.Errorf("build-info don't match. want: \n%v\ngot:\n%s\n", string(excpectedStr), string(actualStr))
}

func CreateTempDirWithCallbackAndAssert(t *testing.T) (string, func()) {
tempDirPath, err := utils.CreateTempDir()
assert.NoError(t, err, "Couldn't create temp dir")
return tempDirPath, func() {
assert.NoError(t, utils.RemoveTempDir(tempDirPath), "Couldn't remove temp dir")
}
}
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 object.
func ReadPackageInfoFromPackageJsonIfExist(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
100 changes: 66 additions & 34 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"

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

testdatautils "github.com/jfrog/build-info-go/build/testdata"
"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 TestReadPackageInfoFromPackageJsonIfExist(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 := testdatautils.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 := ReadPackageInfoFromPackageJsonIfExist(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 := testdatautils.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 = ReadPackageInfoFromPackageJsonIfExist(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 Down Expand Up @@ -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 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!")
}
12 changes: 2 additions & 10 deletions build/utils_test.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
package build

import (
"testing"

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

func validateModule(t *testing.T, module entities.Module, expectedDependencies, expectedArtifacts int, moduleName string, moduleType entities.ModuleType, depsContainChecksums bool) {
Expand All @@ -27,11 +27,3 @@ func validateModule(t *testing.T, module entities.Module, expectedDependencies,
assert.NotEmpty(t, module.Dependencies[0].Checksum.Sha256, "Empty SHA256 field.")
}
}

func createTempDirWithCallbackAndAssert(t *testing.T) (string, func()) {
tempDirPath, err := utils.CreateTempDir()
assert.NoError(t, err, "Couldn't create temp dir")
return tempDirPath, func() {
assert.NoError(t, utils.RemoveTempDir(tempDirPath), "Couldn't remove temp dir")
}
}
7 changes: 4 additions & 3 deletions build/yarn.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,14 @@ package build
import (
"errors"
"fmt"
"os"
"os/exec"

buildutils "github.com/jfrog/build-info-go/build/utils"
"github.com/jfrog/build-info-go/entities"
"github.com/jfrog/build-info-go/utils"
"github.com/jfrog/gofrog/version"
"golang.org/x/exp/slices"
"os"
"os/exec"
)

const minSupportedYarnVersion = "2.4.0"
Expand Down Expand Up @@ -49,7 +50,7 @@ func newYarnModule(srcPath string, containingBuild *Build) (*YarnModule, error)
}

// Read module name
packageInfo, err := buildutils.ReadPackageInfoFromPackageJson(srcPath, nil)
packageInfo, err := buildutils.ReadPackageInfoFromPackageJsonIfExist(srcPath, nil)
if err != nil {
return nil, err
}
Expand Down
12 changes: 7 additions & 5 deletions build/yarn_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@ package build

import (
"errors"
buildutils "github.com/jfrog/build-info-go/build/utils"
"path/filepath"
"reflect"
"testing"

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/utils"
"github.com/jfrog/gofrog/parallel"
Expand Down Expand Up @@ -79,7 +81,7 @@ func TestAppendDependencyRecursively(t *testing.T) {

func TestGenerateBuildInfoForYarnProject(t *testing.T) {
// Copy the project directory to a temporary directory
tempDirPath, createTempDirCallback := createTempDirWithCallbackAndAssert(t)
tempDirPath, createTempDirCallback := testdatautils.CreateTempDirWithCallbackAndAssert(t)
defer createTempDirCallback()
testDataSource := filepath.Join("testdata", "yarn", "v2")
testDataTarget := filepath.Join(tempDirPath, "yarn")
Expand All @@ -105,7 +107,7 @@ func TestGenerateBuildInfoForYarnProject(t *testing.T) {

func TestCollectDepsForYarnProjectWithTraverse(t *testing.T) {
// Copy the project directory to a temporary directory
tempDirPath, createTempDirCallback := createTempDirWithCallbackAndAssert(t)
tempDirPath, createTempDirCallback := testdatautils.CreateTempDirWithCallbackAndAssert(t)
defer createTempDirCallback()
testDataSource := filepath.Join("testdata", "yarn", "v2")
testDataTarget := filepath.Join(tempDirPath, "yarn")
Expand Down Expand Up @@ -146,7 +148,7 @@ func TestCollectDepsForYarnProjectWithTraverse(t *testing.T) {

func TestCollectDepsForYarnProjectWithErrorInTraverse(t *testing.T) {
// Copy the project directory to a temporary directory
tempDirPath, createTempDirCallback := createTempDirWithCallbackAndAssert(t)
tempDirPath, createTempDirCallback := testdatautils.CreateTempDirWithCallbackAndAssert(t)
defer createTempDirCallback()
testDataSource := filepath.Join("testdata", "yarn", "v2")
testDataTarget := filepath.Join(tempDirPath, "yarn")
Expand All @@ -169,7 +171,7 @@ func TestCollectDepsForYarnProjectWithErrorInTraverse(t *testing.T) {

func TestBuildYarnProjectWithArgs(t *testing.T) {
// Copy the project directory to a temporary directory
tempDirPath, createTempDirCallback := createTempDirWithCallbackAndAssert(t)
tempDirPath, createTempDirCallback := testdatautils.CreateTempDirWithCallbackAndAssert(t)
defer createTempDirCallback()
testDataSource := filepath.Join("testdata", "yarn", "v2")
testDataTarget := filepath.Join(tempDirPath, "yarn")
Expand Down

0 comments on commit 57b4655

Please sign in to comment.