You've already forked sap-jenkins-library
							
							
				mirror of
				https://github.com/SAP/jenkins-library.git
				synced 2025-10-30 23:57:50 +02:00 
			
		
		
		
	feat(npmExecuteLint): support to run package installation and usage of custom runScript (#3191)
* feat(npmExecuteLint): support to run package installation and custom runScript * fix tests * error handling * fix test Co-authored-by: Oliver Nocon <33484802+OliverNocon@users.noreply.github.com>
This commit is contained in:
		| @@ -79,15 +79,33 @@ func npmExecuteLint(config npmExecuteLintOptions, telemetryData *telemetry.Custo | ||||
| } | ||||
|  | ||||
| func runNpmExecuteLint(npmExecutor npm.Executor, utils lintUtils, config *npmExecuteLintOptions) error { | ||||
| 	packageJSONFiles := npmExecutor.FindPackageJSONFiles() | ||||
| 	packagesWithCiLint, _ := npmExecutor.FindPackageJSONFilesWithScript(packageJSONFiles, "ci-lint") | ||||
| 	if len(config.RunScript) == 0 { | ||||
| 		return fmt.Errorf("runScript is not allowed to be empty!") | ||||
| 	} | ||||
|  | ||||
| 	if len(packagesWithCiLint) > 0 { | ||||
| 		err := runCiLint(npmExecutor, config.FailOnError) | ||||
| 	packageJSONFiles := npmExecutor.FindPackageJSONFiles() | ||||
| 	packagesWithLintScript, _ := npmExecutor.FindPackageJSONFilesWithScript(packageJSONFiles, config.RunScript) | ||||
|  | ||||
| 	if len(packagesWithLintScript) > 0 { | ||||
| 		if config.Install { | ||||
| 			err := npmExecutor.InstallAllDependencies(packagesWithLintScript) | ||||
| 			if err != nil { | ||||
| 				return err | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		err := runLintScript(npmExecutor, config.RunScript, config.FailOnError) | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 	} else { | ||||
| 		if config.Install { | ||||
| 			err := npmExecutor.InstallAllDependencies(packageJSONFiles) | ||||
| 			if err != nil { | ||||
| 				return err | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		err := runDefaultLint(npmExecutor, utils, config.FailOnError) | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| @@ -96,14 +114,14 @@ func runNpmExecuteLint(npmExecutor npm.Executor, utils lintUtils, config *npmExe | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func runCiLint(npmExecutor npm.Executor, failOnError bool) error { | ||||
| 	runScripts := []string{"ci-lint"} | ||||
| func runLintScript(npmExecutor npm.Executor, runScript string, failOnError bool) error { | ||||
| 	runScripts := []string{runScript} | ||||
| 	runOptions := []string{"--silent"} | ||||
|  | ||||
| 	err := npmExecutor.RunScriptsInAllPackages(runScripts, runOptions, nil, false, nil, nil) | ||||
| 	if err != nil { | ||||
| 		if failOnError { | ||||
| 			return fmt.Errorf("ci-lint script execution failed with error: %w. This might be the result of severe linting findings, or some other issue while executing the script. Please examine the linting results in the UI, the cilint.xml file, if available, or the log above. ", err) | ||||
| 			return fmt.Errorf("%s script execution failed with error: %w. This might be the result of severe linting findings, or some other issue while executing the script. Please examine the linting results in the UI, the cilint.xml file, if available, or the log above. ", runScript, err) | ||||
| 		} | ||||
| 	} | ||||
| 	return nil | ||||
|   | ||||
| @@ -16,6 +16,8 @@ import ( | ||||
| ) | ||||
|  | ||||
| type npmExecuteLintOptions struct { | ||||
| 	Install            bool   `json:"install,omitempty"` | ||||
| 	RunScript          string `json:"runScript,omitempty"` | ||||
| 	FailOnError        bool   `json:"failOnError,omitempty"` | ||||
| 	DefaultNpmRegistry string `json:"defaultNpmRegistry,omitempty"` | ||||
| } | ||||
| @@ -105,6 +107,8 @@ either use ESLint configurations present in the project or use the provided gene | ||||
| } | ||||
|  | ||||
| func addNpmExecuteLintFlags(cmd *cobra.Command, stepConfig *npmExecuteLintOptions) { | ||||
| 	cmd.Flags().BoolVar(&stepConfig.Install, "install", false, "Run npm install or similar commands depending on the project structure.") | ||||
| 	cmd.Flags().StringVar(&stepConfig.RunScript, "runScript", `ci-lint`, "List of additional run scripts to execute from package.json.") | ||||
| 	cmd.Flags().BoolVar(&stepConfig.FailOnError, "failOnError", false, "Defines the behavior in case linting errors are found.") | ||||
| 	cmd.Flags().StringVar(&stepConfig.DefaultNpmRegistry, "defaultNpmRegistry", os.Getenv("PIPER_defaultNpmRegistry"), "URL of the npm registry to use. Defaults to https://registry.npmjs.org/") | ||||
|  | ||||
| @@ -121,6 +125,24 @@ func npmExecuteLintMetadata() config.StepData { | ||||
| 		Spec: config.StepSpec{ | ||||
| 			Inputs: config.StepInputs{ | ||||
| 				Parameters: []config.StepParameters{ | ||||
| 					{ | ||||
| 						Name:        "install", | ||||
| 						ResourceRef: []config.ResourceReference{}, | ||||
| 						Scope:       []string{"PARAMETERS", "STAGES", "STEPS"}, | ||||
| 						Type:        "bool", | ||||
| 						Mandatory:   false, | ||||
| 						Aliases:     []config.Alias{}, | ||||
| 						Default:     false, | ||||
| 					}, | ||||
| 					{ | ||||
| 						Name:        "runScript", | ||||
| 						ResourceRef: []config.ResourceReference{}, | ||||
| 						Scope:       []string{"PARAMETERS", "STAGES", "STEPS"}, | ||||
| 						Type:        "string", | ||||
| 						Mandatory:   false, | ||||
| 						Aliases:     []config.Alias{}, | ||||
| 						Default:     `ci-lint`, | ||||
| 					}, | ||||
| 					{ | ||||
| 						Name:        "failOnError", | ||||
| 						ResourceRef: []config.ResourceReference{}, | ||||
|   | ||||
| @@ -29,6 +29,8 @@ func newLintMockUtilsBundle() mockLintUtilsBundle { | ||||
| } | ||||
|  | ||||
| func TestNpmExecuteLint(t *testing.T) { | ||||
| 	defaultConfig := npmExecuteLintOptions{RunScript: "ci-lint"} | ||||
|  | ||||
| 	t.Run("Call with ci-lint script and one package.json", func(t *testing.T) { | ||||
| 		lintUtils := newLintMockUtilsBundle() | ||||
| 		lintUtils.AddFile("package.json", []byte("{\"scripts\": { \"ci-lint\": \"\" } }")) | ||||
| @@ -37,9 +39,8 @@ func TestNpmExecuteLint(t *testing.T) { | ||||
| 		npmUtils.ExecRunner = lintUtils.execRunner | ||||
| 		npmUtils.FilesMock = lintUtils.FilesMock | ||||
|  | ||||
| 		config := npmExecuteLintOptions{ | ||||
| 			FailOnError: true, | ||||
| 		} | ||||
| 		config := defaultConfig | ||||
| 		config.FailOnError = true | ||||
|  | ||||
| 		npmExecutor := npm.NpmExecutorMock{Utils: npmUtils, Config: npm.NpmConfig{RunScripts: []string{"ci-lint"}, RunOptions: []string{"--silent"}}} | ||||
| 		err := runNpmExecuteLint(&npmExecutor, &lintUtils, &config) | ||||
| @@ -52,7 +53,7 @@ func TestNpmExecuteLint(t *testing.T) { | ||||
| 		lintUtils.AddFile("package.json", []byte("{\"name\": \"Test\" }")) | ||||
| 		lintUtils.AddFile(".eslintrc.json", []byte("{\"name\": \"Test\" }")) | ||||
|  | ||||
| 		config := npmExecuteLintOptions{} | ||||
| 		config := defaultConfig | ||||
| 		config.DefaultNpmRegistry = "foo.bar" | ||||
|  | ||||
| 		npmUtils := newNpmMockUtilsBundle() | ||||
| @@ -74,7 +75,7 @@ func TestNpmExecuteLint(t *testing.T) { | ||||
| 		lintUtils.AddFile(".eslintrc.json", []byte("{\"name\": \"Test\" }")) | ||||
| 		lintUtils.AddFile(filepath.Join("src", ".eslintrc.json"), []byte("{\"name\": \"Test\" }")) | ||||
|  | ||||
| 		config := npmExecuteLintOptions{} | ||||
| 		config := defaultConfig | ||||
| 		config.DefaultNpmRegistry = "foo.bar" | ||||
|  | ||||
| 		npmUtils := newNpmMockUtilsBundle() | ||||
| @@ -95,7 +96,7 @@ func TestNpmExecuteLint(t *testing.T) { | ||||
| 		lintUtils := newLintMockUtilsBundle() | ||||
| 		lintUtils.AddFile("package.json", []byte("{\"name\": \"Test\" }")) | ||||
|  | ||||
| 		config := npmExecuteLintOptions{} | ||||
| 		config := defaultConfig | ||||
| 		config.DefaultNpmRegistry = "foo.bar" | ||||
|  | ||||
| 		npmUtils := newNpmMockUtilsBundle() | ||||
| @@ -117,7 +118,7 @@ func TestNpmExecuteLint(t *testing.T) { | ||||
| 		lintUtils.AddFile("package.json", []byte("{\"scripts\": { \"ci-lint\": \"\" } }")) | ||||
| 		lintUtils.execRunner = &mock.ExecMockRunner{ShouldFailOnCommand: map[string]error{"npm run ci-lint --silent": errors.New("exit 1")}} | ||||
|  | ||||
| 		config := npmExecuteLintOptions{} | ||||
| 		config := defaultConfig | ||||
| 		config.FailOnError = true | ||||
| 		config.DefaultNpmRegistry = "foo.bar" | ||||
|  | ||||
| @@ -141,7 +142,7 @@ func TestNpmExecuteLint(t *testing.T) { | ||||
| 		lintUtils.AddFile(".eslintrc.json", []byte("{\"name\": \"Test\" }")) | ||||
| 		lintUtils.execRunner = &mock.ExecMockRunner{ShouldFailOnCommand: map[string]error{"eslint . -f checkstyle -o ./0_defaultlint.xml --ignore-pattern node_modules/ --ignore-pattern .eslintrc.js": errors.New("exit 1")}} | ||||
|  | ||||
| 		config := npmExecuteLintOptions{} | ||||
| 		config := defaultConfig | ||||
| 		config.FailOnError = true | ||||
| 		config.DefaultNpmRegistry = "foo.bar" | ||||
|  | ||||
| @@ -172,4 +173,74 @@ func TestNpmExecuteLint(t *testing.T) { | ||||
| 			assert.Contains(t, eslintConfigs, filepath.Join("src", ".eslintrc.json")) | ||||
| 		} | ||||
| 	}) | ||||
|  | ||||
| 	t.Run("Call with ci-lint script and install", func(t *testing.T) { | ||||
| 		lintUtils := newLintMockUtilsBundle() | ||||
| 		lintUtils.AddFile("package.json", []byte("{\"name\": \"test\", \"scripts\": { \"ci-lint\": \"\" } }")) | ||||
|  | ||||
| 		npmUtils := npm.NewNpmMockUtilsBundle() | ||||
| 		npmUtils.ExecRunner = lintUtils.execRunner | ||||
| 		npmUtils.FilesMock = lintUtils.FilesMock | ||||
|  | ||||
| 		config := defaultConfig | ||||
| 		config.Install = true | ||||
|  | ||||
| 		npmExecutor := npm.NpmExecutorMock{Utils: npmUtils, Config: npm.NpmConfig{RunScripts: []string{"ci-lint"}, RunOptions: []string{"--silent"}, Install: true}} | ||||
| 		err := runNpmExecuteLint(&npmExecutor, &lintUtils, &config) | ||||
|  | ||||
| 		assert.NoError(t, err) | ||||
| 	}) | ||||
|  | ||||
| 	t.Run("Call with default and install", func(t *testing.T) { | ||||
| 		lintUtils := newLintMockUtilsBundle() | ||||
| 		lintUtils.AddFile("package.json", []byte("{\"name\": \"test\"}")) | ||||
|  | ||||
| 		npmUtils := npm.NewNpmMockUtilsBundle() | ||||
| 		npmUtils.ExecRunner = lintUtils.execRunner | ||||
| 		npmUtils.FilesMock = lintUtils.FilesMock | ||||
|  | ||||
| 		config := defaultConfig | ||||
| 		config.Install = true | ||||
|  | ||||
| 		npmExecutor := npm.NpmExecutorMock{Utils: npmUtils, Config: npm.NpmConfig{RunScripts: []string{"ci-lint"}, RunOptions: []string{"--silent"}, Install: true}} | ||||
| 		err := runNpmExecuteLint(&npmExecutor, &lintUtils, &config) | ||||
|  | ||||
| 		assert.NoError(t, err) | ||||
| 	}) | ||||
|  | ||||
| 	t.Run("Call with custom runScript", func(t *testing.T) { | ||||
| 		lintUtils := newLintMockUtilsBundle() | ||||
| 		lintUtils.AddFile("package.json", []byte("{\"name\": \"test\", \"scripts\": { \"lint:ci\": \"\" } }")) | ||||
|  | ||||
| 		npmUtils := npm.NewNpmMockUtilsBundle() | ||||
| 		npmUtils.ExecRunner = lintUtils.execRunner | ||||
| 		npmUtils.FilesMock = lintUtils.FilesMock | ||||
|  | ||||
| 		config := defaultConfig | ||||
| 		config.RunScript = "lint:ci" | ||||
|  | ||||
| 		npmExecutor := npm.NpmExecutorMock{Utils: npmUtils, Config: npm.NpmConfig{RunScripts: []string{"lint:ci"}, RunOptions: []string{"--silent"}}} | ||||
| 		err := runNpmExecuteLint(&npmExecutor, &lintUtils, &config) | ||||
|  | ||||
| 		assert.NoError(t, err) | ||||
| 	}) | ||||
|  | ||||
| 	t.Run("Call with empty runScript and failOnError", func(t *testing.T) { | ||||
| 		lintUtils := newLintMockUtilsBundle() | ||||
| 		lintUtils.AddFile("package.json", []byte("{\"scripts\": { \"ci-lint\": \"\" } }")) | ||||
| 		lintUtils.execRunner = &mock.ExecMockRunner{ShouldFailOnCommand: map[string]error{"npm run ci-lint --silent": errors.New("exit 1")}} | ||||
|  | ||||
| 		config := defaultConfig | ||||
| 		config.FailOnError = true | ||||
| 		config.RunScript = "" | ||||
|  | ||||
| 		npmUtils := newNpmMockUtilsBundle() | ||||
| 		npmUtils.execRunner = lintUtils.execRunner | ||||
| 		npmUtils.FilesMock = lintUtils.FilesMock | ||||
| 		npmExecutor := npm.Execute{Utils: &npmUtils, Options: npm.ExecutorOptions{}} | ||||
|  | ||||
| 		err := runNpmExecuteLint(&npmExecutor, &lintUtils, &config) | ||||
|  | ||||
| 		assert.EqualError(t, err, "runScript is not allowed to be empty!") | ||||
| 	}) | ||||
| } | ||||
|   | ||||
| @@ -9,6 +9,22 @@ metadata: | ||||
| spec: | ||||
|   inputs: | ||||
|     params: | ||||
|       - name: install | ||||
|         type: bool | ||||
|         description: Run npm install or similar commands depending on the project structure. | ||||
|         scope: | ||||
|           - PARAMETERS | ||||
|           - STAGES | ||||
|           - STEPS | ||||
|         default: false | ||||
|       - name: runScript | ||||
|         type: string | ||||
|         description: List of additional run scripts to execute from package.json. | ||||
|         scope: | ||||
|           - PARAMETERS | ||||
|           - STAGES | ||||
|           - STEPS | ||||
|         default: "ci-lint" | ||||
|       - name: failOnError | ||||
|         type: bool | ||||
|         description: Defines the behavior in case linting errors are found. | ||||
|   | ||||
		Reference in New Issue
	
	Block a user