From da9d73b279911e7db111489d33cc2edcab978b06 Mon Sep 17 00:00:00 2001 From: Philip Germanov Date: Tue, 1 Oct 2024 16:04:15 +0300 Subject: [PATCH 01/39] feat: Introduce new npmExecuteTests step --- cmd/metadata_generated.go | 1 + cmd/npmExecuteTests.go | 89 +++++++ cmd/npmExecuteTests_generated.go | 296 ++++++++++++++++++++++++ cmd/npmExecuteTests_generated_test.go | 20 ++ cmd/npmExecuteTests_test.go | 53 +++++ resources/metadata/npmExecuteTests.yaml | 106 +++++++++ vars/npmExecuteTests.groovy | 34 +++ 7 files changed, 599 insertions(+) create mode 100644 cmd/npmExecuteTests.go create mode 100644 cmd/npmExecuteTests_generated.go create mode 100644 cmd/npmExecuteTests_generated_test.go create mode 100644 cmd/npmExecuteTests_test.go create mode 100644 resources/metadata/npmExecuteTests.yaml create mode 100644 vars/npmExecuteTests.groovy diff --git a/cmd/metadata_generated.go b/cmd/metadata_generated.go index 56cebc07d4..50b0d6dadb 100644 --- a/cmd/metadata_generated.go +++ b/cmd/metadata_generated.go @@ -103,6 +103,7 @@ func GetAllStepMetadata() map[string]config.StepData { "nexusUpload": nexusUploadMetadata(), "npmExecuteLint": npmExecuteLintMetadata(), "npmExecuteScripts": npmExecuteScriptsMetadata(), + "npmExecuteTests": npmExecuteTestsMetadata(), "pipelineCreateScanSummary": pipelineCreateScanSummaryMetadata(), "protecodeExecuteScan": protecodeExecuteScanMetadata(), "pythonBuild": pythonBuildMetadata(), diff --git a/cmd/npmExecuteTests.go b/cmd/npmExecuteTests.go new file mode 100644 index 0000000000..ab66f8d74c --- /dev/null +++ b/cmd/npmExecuteTests.go @@ -0,0 +1,89 @@ +package cmd + +import ( + "encoding/json" + "os" + "strings" + + "github.com/SAP/jenkins-library/pkg/command" + "github.com/SAP/jenkins-library/pkg/log" + "github.com/SAP/jenkins-library/pkg/orchestrator" + "github.com/SAP/jenkins-library/pkg/telemetry" +) + +func npmExecuteTests(config npmExecuteTestsOptions, _ *telemetry.CustomData) { + c := command.Command{} + + c.Stdout(log.Writer()) + c.Stderr(log.Writer()) + runNpmExecuteTests(config, &c) +} + +func runNpmExecuteTests(config npmExecuteTestsOptions, c command.ExecRunner) { + type AppURL struct { + URL string `json:"url"` + Username string `json:"username"` + Password string `json:"password"` + } + var appURLs []AppURL + err := json.Unmarshal([]byte(config.AppURLs), &appURLs) + if err != nil { + log.Entry().WithError(err).Fatal("Failed to unmarshal appURLs") + } + + provider, err := orchestrator.GetOrchestratorConfigProvider(nil) + if err != nil { + log.Entry().WithError(err).Warning("Cannot infer config from CI environment") + } + + env := provider.Branch() + if config.OnlyRunInProductiveBranch && config.ProductiveBranch != env { + log.Entry().Info("Skipping execution because it is configured to run only in the productive branch.") + return + } + + installCommandTokens := strings.Fields(config.InstallCommand) + if err := c.RunExecutable(installCommandTokens[0], installCommandTokens[1:]...); err != nil { + log.Entry().WithError(err).Fatal("Failed to execute install command") + } + + for _, appUrl := range appURLs { + credentialsToEnv(appUrl.Username, appUrl.Password, config.Wdi5) + runTestForUrl(appUrl.URL, config, c) + } + + runTestForUrl(config.BaseURL, config, c) +} + +func runTestForUrl(url string, config npmExecuteTestsOptions, command command.ExecRunner) { + log.Entry().Infof("Running end to end tests for URL: %s", url) + + if config.Wdi5 { + // install wdi5 and all required WebdriverIO peer dependencies + // add a config file (wdio.conf.js) to your current working directory, using http://localhost:8080/index.html as baseUrl, + // looking for tests in $ui5-app/webapp/test/**/* that follow the name pattern *.test.js + // set an npm script named “wdi5” to run wdi5 so you can immediately do npm run wdi5 + if err := command.RunExecutable("npm", "init", "wdi5@latest", "--baseUrl", url); err != nil { + log.Entry().WithError(err).Fatal("Failed to setup wdi5") + } + if err := command.RunExecutable("npm", "run", "wdi5"); err != nil { + log.Entry().WithError(err).Fatal("Failed to execute wdi5") + } + return + } + + // Execute the npm script + options := "--baseUrl=" + url + if err := command.RunExecutable("npm", "run", config.RunScript, options); err != nil { + log.Entry().WithError(err).Fatal("Failed to execute end to end tests") + } +} + +func credentialsToEnv(username, password string, wdi5 bool) { + prefix := "e2e" + if wdi5 { + prefix = "wdi5" + } + os.Setenv(prefix+"_username", username) + os.Setenv(prefix+"_password", password) +} diff --git a/cmd/npmExecuteTests_generated.go b/cmd/npmExecuteTests_generated.go new file mode 100644 index 0000000000..50186e8331 --- /dev/null +++ b/cmd/npmExecuteTests_generated.go @@ -0,0 +1,296 @@ +// Code generated by piper's step-generator. DO NOT EDIT. + +package cmd + +import ( + "fmt" + "os" + "reflect" + "strings" + "time" + + "github.com/SAP/jenkins-library/pkg/config" + "github.com/SAP/jenkins-library/pkg/gcs" + "github.com/SAP/jenkins-library/pkg/log" + "github.com/SAP/jenkins-library/pkg/splunk" + "github.com/SAP/jenkins-library/pkg/telemetry" + "github.com/SAP/jenkins-library/pkg/validation" + "github.com/bmatcuk/doublestar" + "github.com/spf13/cobra" +) + +type npmExecuteTestsOptions struct { + InstallCommand string `json:"installCommand,omitempty"` + RunScript string `json:"runScript,omitempty"` + AppURLs string `json:"appUrls,omitempty"` + OnlyRunInProductiveBranch bool `json:"onlyRunInProductiveBranch,omitempty"` + ProductiveBranch string `json:"productiveBranch,omitempty"` + BaseURL string `json:"baseUrl,omitempty"` + Wdi5 bool `json:"wdi5,omitempty"` + CredentialsID string `json:"credentialsId,omitempty"` +} + +type npmExecuteTestsReports struct { +} + +func (p *npmExecuteTestsReports) persist(stepConfig npmExecuteTestsOptions, gcpJsonKeyFilePath string, gcsBucketId string, gcsFolderPath string, gcsSubFolder string) { + if gcsBucketId == "" { + log.Entry().Info("persisting reports to GCS is disabled, because gcsBucketId is empty") + return + } + log.Entry().Info("Uploading reports to Google Cloud Storage...") + content := []gcs.ReportOutputParam{ + {FilePattern: "**/e2e-results.xml", ParamRef: "", StepResultType: "e2e"}, + } + envVars := []gcs.EnvVar{ + {Name: "GOOGLE_APPLICATION_CREDENTIALS", Value: gcpJsonKeyFilePath, Modified: false}, + } + gcsClient, err := gcs.NewClient(gcs.WithEnvVars(envVars)) + if err != nil { + log.Entry().Errorf("creation of GCS client failed: %v", err) + return + } + defer gcsClient.Close() + structVal := reflect.ValueOf(&stepConfig).Elem() + inputParameters := map[string]string{} + for i := 0; i < structVal.NumField(); i++ { + field := structVal.Type().Field(i) + if field.Type.String() == "string" { + paramName := strings.Split(field.Tag.Get("json"), ",") + paramValue, _ := structVal.Field(i).Interface().(string) + inputParameters[paramName[0]] = paramValue + } + } + if err := gcs.PersistReportsToGCS(gcsClient, content, inputParameters, gcsFolderPath, gcsBucketId, gcsSubFolder, doublestar.Glob, os.Stat); err != nil { + log.Entry().Errorf("failed to persist reports: %v", err) + } +} + +// NpmExecuteTestsCommand Executes end-to-end tests using npm +func NpmExecuteTestsCommand() *cobra.Command { + const STEP_NAME = "npmExecuteTests" + + metadata := npmExecuteTestsMetadata() + var stepConfig npmExecuteTestsOptions + var startTime time.Time + var reports npmExecuteTestsReports + var logCollector *log.CollectorHook + var splunkClient *splunk.Splunk + telemetryClient := &telemetry.Telemetry{} + + var createNpmExecuteTestsCmd = &cobra.Command{ + Use: STEP_NAME, + Short: "Executes end-to-end tests using npm", + Long: `This step executes end-to-end tests in a Docker environment using npm. + +The step spins up a Docker container based on the specified ` + "`" + `dockerImage` + "`" + ` and executes the ` + "`" + `installScript` + "`" + ` and ` + "`" + `runScript` + "`" + ` from ` + "`" + `package.json` + "`" + `. + +The application URLs and credentials can be specified in ` + "`" + `appUrls` + "`" + ` and ` + "`" + `credentialsId` + "`" + ` respectively. If ` + "`" + `wdi5` + "`" + ` is set to ` + "`" + `true` + "`" + `, the step uses ` + "`" + `wdi5_username` + "`" + ` and ` + "`" + `wdi5_password` + "`" + ` for authentication. + +The tests can be restricted to run only on the productive branch by setting ` + "`" + `onlyRunInProductiveBranch` + "`" + ` to ` + "`" + `true` + "`" + `.`, + PreRunE: func(cmd *cobra.Command, _ []string) error { + startTime = time.Now() + log.SetStepName(STEP_NAME) + log.SetVerbose(GeneralConfig.Verbose) + + GeneralConfig.GitHubAccessTokens = ResolveAccessTokens(GeneralConfig.GitHubTokens) + + path, _ := os.Getwd() + fatalHook := &log.FatalHook{CorrelationID: GeneralConfig.CorrelationID, Path: path} + log.RegisterHook(fatalHook) + + err := PrepareConfig(cmd, &metadata, STEP_NAME, &stepConfig, config.OpenPiperFile) + if err != nil { + log.SetErrorCategory(log.ErrorConfiguration) + return err + } + + if len(GeneralConfig.HookConfig.SentryConfig.Dsn) > 0 { + sentryHook := log.NewSentryHook(GeneralConfig.HookConfig.SentryConfig.Dsn, GeneralConfig.CorrelationID) + log.RegisterHook(&sentryHook) + } + + if len(GeneralConfig.HookConfig.SplunkConfig.Dsn) > 0 || len(GeneralConfig.HookConfig.SplunkConfig.ProdCriblEndpoint) > 0 { + splunkClient = &splunk.Splunk{} + logCollector = &log.CollectorHook{CorrelationID: GeneralConfig.CorrelationID} + log.RegisterHook(logCollector) + } + + if err = log.RegisterANSHookIfConfigured(GeneralConfig.CorrelationID); err != nil { + log.Entry().WithError(err).Warn("failed to set up SAP Alert Notification Service log hook") + } + + validation, err := validation.New(validation.WithJSONNamesForStructFields(), validation.WithPredefinedErrorMessages()) + if err != nil { + return err + } + if err = validation.ValidateStruct(stepConfig); err != nil { + log.SetErrorCategory(log.ErrorConfiguration) + return err + } + + return nil + }, + Run: func(_ *cobra.Command, _ []string) { + stepTelemetryData := telemetry.CustomData{} + stepTelemetryData.ErrorCode = "1" + handler := func() { + reports.persist(stepConfig, GeneralConfig.GCPJsonKeyFilePath, GeneralConfig.GCSBucketId, GeneralConfig.GCSFolderPath, GeneralConfig.GCSSubFolder) + config.RemoveVaultSecretFiles() + stepTelemetryData.Duration = fmt.Sprintf("%v", time.Since(startTime).Milliseconds()) + stepTelemetryData.ErrorCategory = log.GetErrorCategory().String() + stepTelemetryData.PiperCommitHash = GitCommit + telemetryClient.SetData(&stepTelemetryData) + telemetryClient.Send() + if len(GeneralConfig.HookConfig.SplunkConfig.Dsn) > 0 { + splunkClient.Initialize(GeneralConfig.CorrelationID, + GeneralConfig.HookConfig.SplunkConfig.Dsn, + GeneralConfig.HookConfig.SplunkConfig.Token, + GeneralConfig.HookConfig.SplunkConfig.Index, + GeneralConfig.HookConfig.SplunkConfig.SendLogs) + splunkClient.Send(telemetryClient.GetData(), logCollector) + } + if len(GeneralConfig.HookConfig.SplunkConfig.ProdCriblEndpoint) > 0 { + splunkClient.Initialize(GeneralConfig.CorrelationID, + GeneralConfig.HookConfig.SplunkConfig.ProdCriblEndpoint, + GeneralConfig.HookConfig.SplunkConfig.ProdCriblToken, + GeneralConfig.HookConfig.SplunkConfig.ProdCriblIndex, + GeneralConfig.HookConfig.SplunkConfig.SendLogs) + splunkClient.Send(telemetryClient.GetData(), logCollector) + } + } + log.DeferExitHandler(handler) + defer handler() + telemetryClient.Initialize(GeneralConfig.NoTelemetry, STEP_NAME, GeneralConfig.HookConfig.PendoConfig.Token) + npmExecuteTests(stepConfig, &stepTelemetryData) + stepTelemetryData.ErrorCode = "0" + log.Entry().Info("SUCCESS") + }, + } + + addNpmExecuteTestsFlags(createNpmExecuteTestsCmd, &stepConfig) + return createNpmExecuteTestsCmd +} + +func addNpmExecuteTestsFlags(cmd *cobra.Command, stepConfig *npmExecuteTestsOptions) { + cmd.Flags().StringVar(&stepConfig.InstallCommand, "installCommand", `npm install`, "Command to be executed for installation. Defaults to `npm install`.") + cmd.Flags().StringVar(&stepConfig.RunScript, "runScript", `wdi5`, "Script to be executed from package.json for running tests. Defaults to `wdi5`.") + cmd.Flags().StringVar(&stepConfig.AppURLs, "appUrls", `[]`, "A JSON string containing an array of objects, each representing an application URL with associated credentials.\nEach object must have the following properties:\n- `url`: The URL of the application.\n- `username`: The username for accessing the application.\n- `password`: The password for accessing the application.\nThis parameter is used to securely pass multiple application URLs and their credentials from Vault.\n") + cmd.Flags().BoolVar(&stepConfig.OnlyRunInProductiveBranch, "onlyRunInProductiveBranch", false, "Boolean to indicate whether the step should only be executed in the productive branch or not.") + cmd.Flags().StringVar(&stepConfig.ProductiveBranch, "productiveBranch", `main`, "The branch used as productive branch.") + cmd.Flags().StringVar(&stepConfig.BaseURL, "baseUrl", `0.0.0.0`, "Base URL of the application to be tested.") + cmd.Flags().BoolVar(&stepConfig.Wdi5, "wdi5", true, "Distinguish if these are wdi5 tests.") + cmd.Flags().StringVar(&stepConfig.CredentialsID, "credentialsId", os.Getenv("PIPER_credentialsId"), "Credentials to access the application to be tested.") + + cmd.MarkFlagRequired("runScript") +} + +// retrieve step metadata +func npmExecuteTestsMetadata() config.StepData { + var theMetaData = config.StepData{ + Metadata: config.StepMetadata{ + Name: "npmExecuteTests", + Aliases: []config.Alias{}, + Description: "Executes end-to-end tests using npm", + }, + Spec: config.StepSpec{ + Inputs: config.StepInputs{ + Parameters: []config.StepParameters{ + { + Name: "installCommand", + ResourceRef: []config.ResourceReference{}, + Scope: []string{"PARAMETERS", "STAGES", "STEPS"}, + Type: "string", + Mandatory: false, + Aliases: []config.Alias{}, + Default: `npm install`, + }, + { + Name: "runScript", + ResourceRef: []config.ResourceReference{}, + Scope: []string{"PARAMETERS", "STAGES", "STEPS"}, + Type: "string", + Mandatory: true, + Aliases: []config.Alias{}, + Default: `wdi5`, + }, + { + Name: "appUrls", + ResourceRef: []config.ResourceReference{ + { + Name: "appMetadataVaultSecretName", + Type: "vaultSecret", + Default: "appMetadata", + }, + }, + Scope: []string{"PARAMETERS", "STAGES", "STEPS"}, + Type: "string", + Mandatory: false, + Aliases: []config.Alias{}, + Default: `[]`, + }, + { + Name: "onlyRunInProductiveBranch", + ResourceRef: []config.ResourceReference{}, + Scope: []string{"PARAMETERS", "STAGES", "STEPS"}, + Type: "bool", + Mandatory: false, + Aliases: []config.Alias{}, + Default: false, + }, + { + Name: "productiveBranch", + ResourceRef: []config.ResourceReference{}, + Scope: []string{"GENERAL", "PARAMETERS", "STAGES", "STEPS"}, + Type: "string", + Mandatory: false, + Aliases: []config.Alias{}, + Default: `main`, + }, + { + Name: "baseUrl", + ResourceRef: []config.ResourceReference{}, + Scope: []string{"PARAMETERS", "STAGES", "STEPS"}, + Type: "string", + Mandatory: false, + Aliases: []config.Alias{}, + Default: `0.0.0.0`, + }, + { + Name: "wdi5", + ResourceRef: []config.ResourceReference{}, + Scope: []string{"PARAMETERS", "STAGES", "STEPS"}, + Type: "bool", + Mandatory: false, + Aliases: []config.Alias{}, + Default: true, + }, + { + Name: "credentialsId", + ResourceRef: []config.ResourceReference{}, + Scope: []string{"PARAMETERS", "STAGES", "STEPS"}, + Type: "string", + Mandatory: false, + Aliases: []config.Alias{}, + Default: os.Getenv("PIPER_credentialsId"), + }, + }, + }, + Containers: []config.Container{ + {Name: "node", Image: "node:lts-buster", EnvVars: []config.EnvVar{{Name: "BASE_URL", Value: "${{params.baseUrl}}"}, {Name: "CREDENTIALS_ID", Value: "${{params.credentialsId}}"}}, WorkingDir: "/app"}, + }, + Outputs: config.StepOutputs{ + Resources: []config.StepResources{ + { + Name: "reports", + Type: "reports", + Parameters: []map[string]interface{}{ + {"filePattern": "**/e2e-results.xml", "type": "e2e"}, + }, + }, + }, + }, + }, + } + return theMetaData +} diff --git a/cmd/npmExecuteTests_generated_test.go b/cmd/npmExecuteTests_generated_test.go new file mode 100644 index 0000000000..58bab7ed11 --- /dev/null +++ b/cmd/npmExecuteTests_generated_test.go @@ -0,0 +1,20 @@ +//go:build unit +// +build unit + +package cmd + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestNpmExecuteTestsCommand(t *testing.T) { + t.Parallel() + + testCmd := NpmExecuteTestsCommand() + + // only high level testing performed - details are tested in step generation procedure + assert.Equal(t, "npmExecuteTests", testCmd.Use, "command name incorrect") + +} diff --git a/cmd/npmExecuteTests_test.go b/cmd/npmExecuteTests_test.go new file mode 100644 index 0000000000..5ae541c1fe --- /dev/null +++ b/cmd/npmExecuteTests_test.go @@ -0,0 +1,53 @@ +package cmd + +import ( + "github.com/SAP/jenkins-library/pkg/mock" + "github.com/stretchr/testify/assert" + "testing" +) + +type npmExecuteTestsMockUtils struct { + *mock.ExecMockRunner + *mock.FilesMock +} + +func newNpmExecuteTestsTestsUtils() npmExecuteTestsMockUtils { + utils := npmExecuteTestsMockUtils{ + ExecMockRunner: &mock.ExecMockRunner{}, + FilesMock: &mock.FilesMock{}, + } + return utils +} + +func TestRunNpmExecuteTests(t *testing.T) { + t.Parallel() + + t.Run("happy path", func(t *testing.T) { + t.Parallel() + // init + config := npmExecuteTestsOptions{} + + utils := newNpmExecuteTestsTestsUtils() + utils.AddFile("file.txt", []byte("dummy content")) + + // test + err := runNpmExecuteTests(&config, nil, utils) + + // assert + assert.NoError(t, err) + }) + + t.Run("error path", func(t *testing.T) { + t.Parallel() + // init + config := npmExecuteTestsOptions{} + + utils := newNpmExecuteTestsTestsUtils() + + // test + err := runNpmExecuteTests(&config, nil, utils) + + // assert + assert.EqualError(t, err, "cannot run without important file") + }) +} diff --git a/resources/metadata/npmExecuteTests.yaml b/resources/metadata/npmExecuteTests.yaml new file mode 100644 index 0000000000..2981d36a56 --- /dev/null +++ b/resources/metadata/npmExecuteTests.yaml @@ -0,0 +1,106 @@ +metadata: + name: npmExecuteTests + description: Executes end-to-end tests using npm + longDescription: | + This step executes end-to-end tests in a Docker environment using npm. + + The step spins up a Docker container based on the specified `dockerImage` and executes the `installScript` and `runScript` from `package.json`. + + The application URLs and credentials can be specified in `appUrls` and `credentialsId` respectively. If `wdi5` is set to `true`, the step uses `wdi5_username` and `wdi5_password` for authentication. + + The tests can be restricted to run only on the productive branch by setting `onlyRunInProductiveBranch` to `true`. + +spec: + inputs: + params: + - name: installCommand + type: string + description: Command to be executed for installation. Defaults to `npm install`. + scope: + - PARAMETERS + - STAGES + - STEPS + default: "npm install" + - name: runScript + type: string + description: Script to be executed from package.json for running tests. Defaults to `wdi5`. + scope: + - PARAMETERS + - STAGES + - STEPS + mandatory: true + default: "wdi5" + - name: appUrls + type: string + description: | + A JSON string containing an array of objects, each representing an application URL with associated credentials. + Each object must have the following properties: + - `url`: The URL of the application. + - `username`: The username for accessing the application. + - `password`: The password for accessing the application. + This parameter is used to securely pass multiple application URLs and their credentials from Vault. + scope: + - PARAMETERS + - STAGES + - STEPS + default: "[]" + resourceRef: + - type: vaultSecret + default: appMetadata + name: appMetadataVaultSecretName + - name: onlyRunInProductiveBranch + type: bool + default: false + description: Boolean to indicate whether the step should only be executed in the productive branch or not. + scope: + - PARAMETERS + - STAGES + - STEPS + - name: productiveBranch + type: string + default: "main" + description: The branch used as productive branch. + scope: + - GENERAL + - PARAMETERS + - STAGES + - STEPS + - name: baseUrl + type: string + default: "0.0.0.0" + description: Base URL of the application to be tested. + scope: + - PARAMETERS + - STAGES + - STEPS + - name: wdi5 + type: bool + description: Distinguish if these are wdi5 tests. + default: true + scope: + - PARAMETERS + - STAGES + - STEPS + - name: credentialsId + type: string + description: Credentials to access the application to be tested. + scope: + - PARAMETERS + - STAGES + - STEPS + outputs: + resources: + - name: reports + type: reports + params: + - filePattern: "**/e2e-results.xml" + type: e2e + containers: + - name: node + image: node:lts-buster + env: + - name: BASE_URL + value: ${{params.baseUrl}} + - name: CREDENTIALS_ID + value: ${{params.credentialsId}} + workingDir: /app diff --git a/vars/npmExecuteTests.groovy b/vars/npmExecuteTests.groovy new file mode 100644 index 0000000000..53758bab5c --- /dev/null +++ b/vars/npmExecuteTests.groovy @@ -0,0 +1,34 @@ +import com.sap.piper.ConfigurationHelper +import groovy.transform.Field + +import static com.sap.piper.Prerequisites.checkScript + +@Field String STEP_NAME = getClass().getName() +@Field String METADATA_FILE = 'metadata/npmExecuteTests.yaml' + +@Field Set CONFIG_KEYS = [ + "wdi5" + "credentialsId", +] + +void call(Map parameters = [:]) { + final script = checkScript(this, parameters) ?: this + String stageName = parameters.stageName ?: env.STAGE_NAME + Map config = ConfigurationHelper.newInstance(this) + .loadStepDefaults([:], stageName) + .mixinGeneralConfig(script.commonPipelineEnvironment, CONFIG_KEYS) + .mixinStepConfig(script.commonPipelineEnvironment, CONFIG_KEYS) + .mixinStageConfig(script.commonPipelineEnvironment, stageName, CONFIG_KEYS) + .mixin(parameters, CONFIG_KEYS) + .use() + + List credentials = [] + if config.credentialsId { + if config.wdi5 { + credentials.add([type: 'usernamePassword', id: config.credentialsId, env: ['wdi5_username', 'wdi5_password']]) + } else { + credentials.add([type: 'usernamePassword', id: config.credentialsId, env: ['e2e_username', 'e2e_password']] + } + } + piperExecuteBin(parameters, STEP_NAME, METADATA_FILE, credentials) +} From 20e00eb7e556a5e337e1ea7ca50c5f1464e96ae3 Mon Sep 17 00:00:00 2001 From: Philip Germanov Date: Tue, 1 Oct 2024 16:08:00 +0300 Subject: [PATCH 02/39] fix: comma --- vars/npmExecuteTests.groovy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vars/npmExecuteTests.groovy b/vars/npmExecuteTests.groovy index 53758bab5c..df9e2c5ff3 100644 --- a/vars/npmExecuteTests.groovy +++ b/vars/npmExecuteTests.groovy @@ -7,7 +7,7 @@ import static com.sap.piper.Prerequisites.checkScript @Field String METADATA_FILE = 'metadata/npmExecuteTests.yaml' @Field Set CONFIG_KEYS = [ - "wdi5" + "wdi5", "credentialsId", ] From f24b876da2af31433a107dcaf4dcb5e9c4f19476 Mon Sep 17 00:00:00 2001 From: Philip Germanov Date: Tue, 1 Oct 2024 16:58:34 +0300 Subject: [PATCH 03/39] fix: parens --- vars/npmExecuteTests.groovy | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/vars/npmExecuteTests.groovy b/vars/npmExecuteTests.groovy index df9e2c5ff3..3c108ea1ba 100644 --- a/vars/npmExecuteTests.groovy +++ b/vars/npmExecuteTests.groovy @@ -23,8 +23,8 @@ void call(Map parameters = [:]) { .use() List credentials = [] - if config.credentialsId { - if config.wdi5 { + if (config.credentialsId) { + if (config.wdi5) { credentials.add([type: 'usernamePassword', id: config.credentialsId, env: ['wdi5_username', 'wdi5_password']]) } else { credentials.add([type: 'usernamePassword', id: config.credentialsId, env: ['e2e_username', 'e2e_password']] From 7cadbf8a770f2aefe83c6220ce57b3bc045b0c6d Mon Sep 17 00:00:00 2001 From: Philip Germanov Date: Tue, 1 Oct 2024 17:30:45 +0300 Subject: [PATCH 04/39] fix: error handling --- cmd/npmExecuteTests.go | 39 ++++++++++++++++++++++++------------- cmd/npmExecuteTests_test.go | 7 ++++--- 2 files changed, 29 insertions(+), 17 deletions(-) diff --git a/cmd/npmExecuteTests.go b/cmd/npmExecuteTests.go index ab66f8d74c..49e5e40ea5 100644 --- a/cmd/npmExecuteTests.go +++ b/cmd/npmExecuteTests.go @@ -2,6 +2,7 @@ package cmd import ( "encoding/json" + "fmt" "os" "strings" @@ -16,10 +17,13 @@ func npmExecuteTests(config npmExecuteTestsOptions, _ *telemetry.CustomData) { c.Stdout(log.Writer()) c.Stderr(log.Writer()) - runNpmExecuteTests(config, &c) + err := runNpmExecuteTests(&config, &c) + if err != nil { + log.Entry().WithError(err).Fatal("Step execution failed") + } } -func runNpmExecuteTests(config npmExecuteTestsOptions, c command.ExecRunner) { +func runNpmExecuteTests(config *npmExecuteTestsOptions, c command.ExecRunner) error { type AppURL struct { URL string `json:"url"` Username string `json:"username"` @@ -28,34 +32,40 @@ func runNpmExecuteTests(config npmExecuteTestsOptions, c command.ExecRunner) { var appURLs []AppURL err := json.Unmarshal([]byte(config.AppURLs), &appURLs) if err != nil { - log.Entry().WithError(err).Fatal("Failed to unmarshal appURLs") + return fmt.Errorf("failed to parse app URLs: %w", err) } provider, err := orchestrator.GetOrchestratorConfigProvider(nil) if err != nil { - log.Entry().WithError(err).Warning("Cannot infer config from CI environment") + return fmt.Errorf("failed to get orchestrator config provider: %w", err) } env := provider.Branch() if config.OnlyRunInProductiveBranch && config.ProductiveBranch != env { log.Entry().Info("Skipping execution because it is configured to run only in the productive branch.") - return + return nil } installCommandTokens := strings.Fields(config.InstallCommand) if err := c.RunExecutable(installCommandTokens[0], installCommandTokens[1:]...); err != nil { - log.Entry().WithError(err).Fatal("Failed to execute install command") + return fmt.Errorf("failed to execute install command: %w", err) } for _, appUrl := range appURLs { credentialsToEnv(appUrl.Username, appUrl.Password, config.Wdi5) - runTestForUrl(appUrl.URL, config, c) + err := runTestForUrl(appUrl.URL, config, c) + if err != nil { + return err + } } - runTestForUrl(config.BaseURL, config, c) + if err := runTestForUrl(config.BaseURL, config, c); err != nil { + return err + } + return nil } -func runTestForUrl(url string, config npmExecuteTestsOptions, command command.ExecRunner) { +func runTestForUrl(url string, config *npmExecuteTestsOptions, command command.ExecRunner) error { log.Entry().Infof("Running end to end tests for URL: %s", url) if config.Wdi5 { @@ -64,19 +74,20 @@ func runTestForUrl(url string, config npmExecuteTestsOptions, command command.Ex // looking for tests in $ui5-app/webapp/test/**/* that follow the name pattern *.test.js // set an npm script named “wdi5” to run wdi5 so you can immediately do npm run wdi5 if err := command.RunExecutable("npm", "init", "wdi5@latest", "--baseUrl", url); err != nil { - log.Entry().WithError(err).Fatal("Failed to setup wdi5") + return fmt.Errorf("failed to install wdi5: %w", err) } if err := command.RunExecutable("npm", "run", "wdi5"); err != nil { - log.Entry().WithError(err).Fatal("Failed to execute wdi5") + return fmt.Errorf("failed to execute wdi5: %w", err) } - return + return nil } // Execute the npm script - options := "--baseUrl=" + url + options := "--baseUrl_" + url if err := command.RunExecutable("npm", "run", config.RunScript, options); err != nil { - log.Entry().WithError(err).Fatal("Failed to execute end to end tests") + return fmt.Errorf("failed to execute npm script: %w", err) } + return nil } func credentialsToEnv(username, password string, wdi5 bool) { diff --git a/cmd/npmExecuteTests_test.go b/cmd/npmExecuteTests_test.go index 5ae541c1fe..d2d83619ec 100644 --- a/cmd/npmExecuteTests_test.go +++ b/cmd/npmExecuteTests_test.go @@ -1,9 +1,10 @@ package cmd import ( + "testing" + "github.com/SAP/jenkins-library/pkg/mock" "github.com/stretchr/testify/assert" - "testing" ) type npmExecuteTestsMockUtils struct { @@ -31,7 +32,7 @@ func TestRunNpmExecuteTests(t *testing.T) { utils.AddFile("file.txt", []byte("dummy content")) // test - err := runNpmExecuteTests(&config, nil, utils) + err := runNpmExecuteTests(&config, utils) // assert assert.NoError(t, err) @@ -45,7 +46,7 @@ func TestRunNpmExecuteTests(t *testing.T) { utils := newNpmExecuteTestsTestsUtils() // test - err := runNpmExecuteTests(&config, nil, utils) + err := runNpmExecuteTests(&config, utils) // assert assert.EqualError(t, err, "cannot run without important file") From 221840f0312310414694a1b5b20a59798137b94a Mon Sep 17 00:00:00 2001 From: Philip Germanov Date: Tue, 1 Oct 2024 18:10:50 +0300 Subject: [PATCH 05/39] fix: paren --- vars/npmExecuteTests.groovy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vars/npmExecuteTests.groovy b/vars/npmExecuteTests.groovy index 3c108ea1ba..c8981db936 100644 --- a/vars/npmExecuteTests.groovy +++ b/vars/npmExecuteTests.groovy @@ -27,7 +27,7 @@ void call(Map parameters = [:]) { if (config.wdi5) { credentials.add([type: 'usernamePassword', id: config.credentialsId, env: ['wdi5_username', 'wdi5_password']]) } else { - credentials.add([type: 'usernamePassword', id: config.credentialsId, env: ['e2e_username', 'e2e_password']] + credentials.add([type: 'usernamePassword', id: config.credentialsId, env: ['e2e_username', 'e2e_password']]) } } piperExecuteBin(parameters, STEP_NAME, METADATA_FILE, credentials) From 17f081052179d078f1c10be6d3ab694f62538737 Mon Sep 17 00:00:00 2001 From: Philip Germanov Date: Tue, 1 Oct 2024 18:37:35 +0300 Subject: [PATCH 06/39] fix: config keys --- vars/npmExecuteTests.groovy | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/vars/npmExecuteTests.groovy b/vars/npmExecuteTests.groovy index c8981db936..1819f60a48 100644 --- a/vars/npmExecuteTests.groovy +++ b/vars/npmExecuteTests.groovy @@ -6,20 +6,24 @@ import static com.sap.piper.Prerequisites.checkScript @Field String STEP_NAME = getClass().getName() @Field String METADATA_FILE = 'metadata/npmExecuteTests.yaml' -@Field Set CONFIG_KEYS = [ +@Field Set GLOBAL_CONFIG_KEYS = [] + +@Field Set STEP_CONFIG_KEYS = [ "wdi5", "credentialsId", ] +@Field Set PARAMETER_KEYS = [] + void call(Map parameters = [:]) { final script = checkScript(this, parameters) ?: this String stageName = parameters.stageName ?: env.STAGE_NAME Map config = ConfigurationHelper.newInstance(this) .loadStepDefaults([:], stageName) - .mixinGeneralConfig(script.commonPipelineEnvironment, CONFIG_KEYS) - .mixinStepConfig(script.commonPipelineEnvironment, CONFIG_KEYS) - .mixinStageConfig(script.commonPipelineEnvironment, stageName, CONFIG_KEYS) - .mixin(parameters, CONFIG_KEYS) + .mixinGeneralConfig(script.commonPipelineEnvironment, GENERAL_CONFIG_KEYS) + .mixinStepConfig(script.commonPipelineEnvironment, STEP_CONFIG_KEYS) + .mixinStageConfig(script.commonPipelineEnvironment, stageName, PARAMETER_KEYS) + .mixin(parameters, STEP_CONFIG_KEYS) .use() List credentials = [] From 0dc2cee4afffa52d532f531c2aa959de31a8dcdd Mon Sep 17 00:00:00 2001 From: Philip Germanov Date: Tue, 1 Oct 2024 18:40:48 +0300 Subject: [PATCH 07/39] fix: rename key --- vars/npmExecuteTests.groovy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vars/npmExecuteTests.groovy b/vars/npmExecuteTests.groovy index 1819f60a48..dcd65be2ec 100644 --- a/vars/npmExecuteTests.groovy +++ b/vars/npmExecuteTests.groovy @@ -6,7 +6,7 @@ import static com.sap.piper.Prerequisites.checkScript @Field String STEP_NAME = getClass().getName() @Field String METADATA_FILE = 'metadata/npmExecuteTests.yaml' -@Field Set GLOBAL_CONFIG_KEYS = [] +@Field Set GENERAL_CONFIG_KEYS = [] @Field Set STEP_CONFIG_KEYS = [ "wdi5", From 47ca1270f0f1cf2e8cf5dd286089a7c35a14a502 Mon Sep 17 00:00:00 2001 From: Philip Germanov Date: Wed, 2 Oct 2024 16:46:43 +0300 Subject: [PATCH 08/39] test: failng tests --- cmd/mtaBuild_test.go | 41 +++++++++++----------------------- cmd/npmExecuteTests_test.go | 44 +++---------------------------------- 2 files changed, 16 insertions(+), 69 deletions(-) diff --git a/cmd/mtaBuild_test.go b/cmd/mtaBuild_test.go index 3f127c4944..572e203c33 100644 --- a/cmd/mtaBuild_test.go +++ b/cmd/mtaBuild_test.go @@ -7,6 +7,7 @@ import ( "path/filepath" "testing" + "github.com/SAP/jenkins-library/pkg/config" "github.com/SAP/jenkins-library/pkg/mock" "github.com/ghodss/yaml" "github.com/stretchr/testify/assert" @@ -26,7 +27,7 @@ func (m *mtaBuildTestUtilsBundle) SetNpmRegistries(defaultNpmRegistry string) er } func (m *mtaBuildTestUtilsBundle) InstallAllDependencies(defaultNpmRegistry string) error { - return errors.New("Test should not install dependencies.") //TODO implement test + return errors.New("Test should not install dependencies.") // TODO implement test } func (m *mtaBuildTestUtilsBundle) DownloadAndCopySettingsFiles(globalSettingsFile string, projectSettingsFile string) error { @@ -48,11 +49,12 @@ func newMtaBuildTestUtilsBundle() *mtaBuildTestUtilsBundle { } func TestMtaBuild(t *testing.T) { - cpe := mtaBuildCommonPipelineEnvironment{} + SetConfigOptions(ConfigCommandOptions{ + OpenFile: config.OpenPiperFile, + }) t.Run("Application name not set", func(t *testing.T) { - utilsMock := newMtaBuildTestUtilsBundle() options := mtaBuildOptions{} @@ -60,11 +62,9 @@ func TestMtaBuild(t *testing.T) { assert.NotNil(t, err) assert.Equal(t, "'mta.yaml' not found in project sources and 'applicationName' not provided as parameter - cannot generate 'mta.yaml' file", err.Error()) - }) t.Run("Provide default npm registry", func(t *testing.T) { - utilsMock := newMtaBuildTestUtilsBundle() options := mtaBuildOptions{ApplicationName: "myApp", Platform: "CF", DefaultNpmRegistry: "https://example.org/npm", MtarName: "myName", Source: "./", Target: "./"} @@ -78,7 +78,6 @@ func TestMtaBuild(t *testing.T) { }) t.Run("Package json does not exist", func(t *testing.T) { - utilsMock := newMtaBuildTestUtilsBundle() options := mtaBuildOptions{ApplicationName: "myApp"} @@ -88,11 +87,9 @@ func TestMtaBuild(t *testing.T) { assert.NotNil(t, err) assert.Equal(t, "package.json file does not exist", err.Error()) - }) t.Run("Write yaml file", func(t *testing.T) { - utilsMock := newMtaBuildTestUtilsBundle() options := mtaBuildOptions{ApplicationName: "myApp", Platform: "CF", MtarName: "myName", Source: "./", Target: "./", EnableSetTimestamp: true} @@ -125,11 +122,9 @@ func TestMtaBuild(t *testing.T) { assert.Equal(t, "myApp", result.Modules[0].Name) assert.Regexp(t, "^1\\.2\\.3-[\\d]{14}$", result.Modules[0].Parameters["version"]) assert.Equal(t, "myApp", result.Modules[0].Parameters["name"]) - }) t.Run("Dont write mta yaml file when already present no timestamp placeholder", func(t *testing.T) { - utilsMock := newMtaBuildTestUtilsBundle() options := mtaBuildOptions{ApplicationName: "myApp"} @@ -143,7 +138,6 @@ func TestMtaBuild(t *testing.T) { }) t.Run("Write mta yaml file when already present with timestamp placeholder", func(t *testing.T) { - utilsMock := newMtaBuildTestUtilsBundle() options := mtaBuildOptions{ApplicationName: "myApp", EnableSetTimestamp: true} @@ -157,7 +151,6 @@ func TestMtaBuild(t *testing.T) { }) t.Run("Mta build mbt toolset", func(t *testing.T) { - utilsMock := newMtaBuildTestUtilsBundle() cpe.mtarFilePath = "" @@ -179,7 +172,6 @@ func TestMtaBuild(t *testing.T) { t.Run("Source and target related tests", func(t *testing.T) { t.Run("Mta build mbt toolset with custom source and target paths", func(t *testing.T) { - utilsMock := newMtaBuildTestUtilsBundle() cpe.mtarFilePath = "" @@ -194,9 +186,11 @@ func TestMtaBuild(t *testing.T) { if assert.Len(t, utilsMock.Calls, 1) { assert.Equal(t, "mbt", utilsMock.Calls[0].Exec) - assert.Equal(t, []string{"build", "--mtar", "myName.mtar", "--platform", "CF", + assert.Equal(t, []string{ + "build", "--mtar", "myName.mtar", "--platform", "CF", "--source", filepath.FromSlash("mySourcePath/"), - "--target", filepath.Join(_ignoreError(os.Getwd()), filepath.FromSlash("mySourcePath/myTargetPath/"))}, + "--target", filepath.Join(_ignoreError(os.Getwd()), filepath.FromSlash("mySourcePath/myTargetPath/")), + }, utilsMock.Calls[0].Params) } assert.Equal(t, "mySourcePath/myTargetPath/myName.mtar", cpe.mtarFilePath) @@ -206,7 +200,6 @@ func TestMtaBuild(t *testing.T) { t.Run("M2Path related tests", func(t *testing.T) { t.Run("Mta build mbt toolset with m2Path", func(t *testing.T) { - utilsMock := newMtaBuildTestUtilsBundle() utilsMock.CurrentDir = "root_folder/workspace" cpe.mtarFilePath = "" @@ -223,9 +216,7 @@ func TestMtaBuild(t *testing.T) { }) t.Run("Settings file releatd tests", func(t *testing.T) { - t.Run("Copy global settings file", func(t *testing.T) { - utilsMock := newMtaBuildTestUtilsBundle() utilsMock.AddFile("mta.yaml", []byte("ID: \"myNameFromMtar\"")) @@ -240,7 +231,6 @@ func TestMtaBuild(t *testing.T) { }) t.Run("Copy project settings file", func(t *testing.T) { - utilsMock := newMtaBuildTestUtilsBundle() utilsMock.AddFile("mta.yaml", []byte("ID: \"myNameFromMtar\"")) @@ -256,9 +246,7 @@ func TestMtaBuild(t *testing.T) { }) t.Run("publish related tests", func(t *testing.T) { - t.Run("error when no repository url", func(t *testing.T) { - utilsMock := newMtaBuildTestUtilsBundle() utilsMock.AddFile("mta.yaml", []byte("ID: \"myNameFromMtar\"")) @@ -270,12 +258,13 @@ func TestMtaBuild(t *testing.T) { }) t.Run("error when no mtar group", func(t *testing.T) { - utilsMock := newMtaBuildTestUtilsBundle() utilsMock.AddFile("mta.yaml", []byte("ID: \"myNameFromMtar\"")) - options := mtaBuildOptions{ApplicationName: "myApp", GlobalSettingsFile: "/opt/maven/settings.xml", Platform: "CF", MtarName: "myName", Source: "./", Target: "./", Publish: true, - MtaDeploymentRepositoryURL: "dummy", MtaDeploymentRepositoryPassword: "dummy", MtaDeploymentRepositoryUser: "dummy"} + options := mtaBuildOptions{ + ApplicationName: "myApp", GlobalSettingsFile: "/opt/maven/settings.xml", Platform: "CF", MtarName: "myName", Source: "./", Target: "./", Publish: true, + MtaDeploymentRepositoryURL: "dummy", MtaDeploymentRepositoryPassword: "dummy", MtaDeploymentRepositoryUser: "dummy", + } err := runMtaBuild(options, &cpe, utilsMock) @@ -285,7 +274,6 @@ func TestMtaBuild(t *testing.T) { } func TestMtaBuildSourceDir(t *testing.T) { - cpe := mtaBuildCommonPipelineEnvironment{} t.Run("getSourcePath", func(t *testing.T) { t.Parallel() @@ -364,12 +352,10 @@ func TestMtaBuildSourceDir(t *testing.T) { err := runMtaBuild(options, &cpe, utilsMock) assert.Nil(t, err) assert.Contains(t, utilsMock.Calls[0].Params, "--sbom-file-path") - }) } func TestMtaBuildMtar(t *testing.T) { - t.Run("getMtarName", func(t *testing.T) { t.Parallel() @@ -412,7 +398,6 @@ func TestMtaBuildMtar(t *testing.T) { assert.Equal(t, filepath.FromSlash("source/target/mta.mtar"), getMtarFilePath(mtaBuildOptions{Source: "source", Target: "target"}, "mta.mtar")) }) }) - } func _ignoreError(s string, e error) string { diff --git a/cmd/npmExecuteTests_test.go b/cmd/npmExecuteTests_test.go index d2d83619ec..6701e97996 100644 --- a/cmd/npmExecuteTests_test.go +++ b/cmd/npmExecuteTests_test.go @@ -3,52 +3,14 @@ package cmd import ( "testing" - "github.com/SAP/jenkins-library/pkg/mock" "github.com/stretchr/testify/assert" ) -type npmExecuteTestsMockUtils struct { - *mock.ExecMockRunner - *mock.FilesMock -} - -func newNpmExecuteTestsTestsUtils() npmExecuteTestsMockUtils { - utils := npmExecuteTestsMockUtils{ - ExecMockRunner: &mock.ExecMockRunner{}, - FilesMock: &mock.FilesMock{}, - } - return utils -} - func TestRunNpmExecuteTests(t *testing.T) { t.Parallel() - t.Run("happy path", func(t *testing.T) { - t.Parallel() - // init - config := npmExecuteTestsOptions{} - - utils := newNpmExecuteTestsTestsUtils() - utils.AddFile("file.txt", []byte("dummy content")) - - // test - err := runNpmExecuteTests(&config, utils) - - // assert - assert.NoError(t, err) - }) - - t.Run("error path", func(t *testing.T) { - t.Parallel() - // init - config := npmExecuteTestsOptions{} - - utils := newNpmExecuteTestsTestsUtils() - - // test - err := runNpmExecuteTests(&config, utils) + testCmd := NpmExecuteTestsCommand() - // assert - assert.EqualError(t, err, "cannot run without important file") - }) + // only high level testing performed - details are tested in step generation procedure + assert.Equal(t, "npmExecuteTests", testCmd.Use, "command name incorrect") } From 187d850433a5a644855cc9f2a91e3be93f76f2ca Mon Sep 17 00:00:00 2001 From: Philip Germanov Date: Fri, 4 Oct 2024 10:57:02 +0300 Subject: [PATCH 09/39] fix: handle empty openFile call --- cmd/getConfig.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/cmd/getConfig.go b/cmd/getConfig.go index aa4734a75c..316cc7e614 100644 --- a/cmd/getConfig.go +++ b/cmd/getConfig.go @@ -65,7 +65,7 @@ func ConfigCommand() *cobra.Command { OpenFile: config.OpenPiperFile, }) - var createConfigCmd = &cobra.Command{ + createConfigCmd := &cobra.Command{ Use: "getConfig", Short: "Loads the project 'Piper' configuration respecting defaults and parameters.", PreRun: func(cmd *cobra.Command, args []string) { @@ -138,6 +138,9 @@ func GetStageConfig() (config.StepConfig, error) { defaultConfig := []io.ReadCloser{} for _, f := range GeneralConfig.DefaultConfig { + if configOptions.OpenFile == nil { + return stepConfig, errors.New("config: open file function not set") + } fc, err := configOptions.OpenFile(f, GeneralConfig.GitHubAccessTokens) // only create error for non-default values if err != nil && f != ".pipeline/defaults.yaml" { @@ -265,7 +268,6 @@ func GenerateConfig(formatter func(interface{}) (string, error)) error { } func addConfigFlags(cmd *cobra.Command) { - // ToDo: support more output options, like https://kubernetes.io/docs/reference/kubectl/overview/#formatting-output cmd.Flags().StringVar(&configOptions.Output, "output", "json", "Defines the output format") cmd.Flags().StringVar(&configOptions.OutputFile, "outputFile", "", "Defines a file path. f set, the output will be written to the defines file") @@ -276,7 +278,6 @@ func addConfigFlags(cmd *cobra.Command) { cmd.Flags().StringVar(&configOptions.StepMetadata, "stepMetadata", "", "Step metadata, passed as path to yaml") cmd.Flags().StringVar(&configOptions.StepName, "stepName", "", "Step name, used to get step metadata if yaml path is not set") cmd.Flags().BoolVar(&configOptions.ContextConfig, "contextConfig", false, "Defines if step context configuration should be loaded instead of step config") - } func defaultsAndFilters(metadata *config.StepData, stepName string) ([]io.ReadCloser, config.StepFilters, error) { From aeef66bccdbeec98e7951516cfe65ee7d8d4ca3b Mon Sep 17 00:00:00 2001 From: Philip Germanov Date: Fri, 4 Oct 2024 10:57:57 +0300 Subject: [PATCH 10/39] refactor: clean install rather than install --- resources/metadata/npmExecuteTests.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/metadata/npmExecuteTests.yaml b/resources/metadata/npmExecuteTests.yaml index 2981d36a56..3797f2d2d2 100644 --- a/resources/metadata/npmExecuteTests.yaml +++ b/resources/metadata/npmExecuteTests.yaml @@ -15,12 +15,12 @@ spec: params: - name: installCommand type: string - description: Command to be executed for installation. Defaults to `npm install`. + description: Command to be executed for installation. Defaults to `npm ci`. scope: - PARAMETERS - STAGES - STEPS - default: "npm install" + default: "npm ci" - name: runScript type: string description: Script to be executed from package.json for running tests. Defaults to `wdi5`. From 56dfffeebb683cac1b3b6464bd23d0b2a1d935c0 Mon Sep 17 00:00:00 2001 From: Philip Germanov Date: Fri, 4 Oct 2024 11:06:04 +0300 Subject: [PATCH 11/39] refactor: full run command in config --- cmd/npmExecuteTests.go | 3 ++- cmd/npmExecuteTests_generated.go | 8 ++++---- resources/metadata/npmExecuteTests.yaml | 4 ++-- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/cmd/npmExecuteTests.go b/cmd/npmExecuteTests.go index 49e5e40ea5..b48d30ee67 100644 --- a/cmd/npmExecuteTests.go +++ b/cmd/npmExecuteTests.go @@ -84,7 +84,8 @@ func runTestForUrl(url string, config *npmExecuteTestsOptions, command command.E // Execute the npm script options := "--baseUrl_" + url - if err := command.RunExecutable("npm", "run", config.RunScript, options); err != nil { + runScriptTokens := strings.Fields(config.RunScript) + if err := command.RunExecutable(runScriptTokens[0], append(runScriptTokens[1:], options)...); err != nil { return fmt.Errorf("failed to execute npm script: %w", err) } return nil diff --git a/cmd/npmExecuteTests_generated.go b/cmd/npmExecuteTests_generated.go index 50186e8331..e84cbcfa40 100644 --- a/cmd/npmExecuteTests_generated.go +++ b/cmd/npmExecuteTests_generated.go @@ -173,8 +173,8 @@ The tests can be restricted to run only on the productive branch by setting ` + } func addNpmExecuteTestsFlags(cmd *cobra.Command, stepConfig *npmExecuteTestsOptions) { - cmd.Flags().StringVar(&stepConfig.InstallCommand, "installCommand", `npm install`, "Command to be executed for installation. Defaults to `npm install`.") - cmd.Flags().StringVar(&stepConfig.RunScript, "runScript", `wdi5`, "Script to be executed from package.json for running tests. Defaults to `wdi5`.") + cmd.Flags().StringVar(&stepConfig.InstallCommand, "installCommand", `npm ci`, "Command to be executed for installation. Defaults to `npm ci`.") + cmd.Flags().StringVar(&stepConfig.RunScript, "runScript", `npm run wdi5`, "Script to be executed from package.json for running tests. Defaults to `npm run wdi5`.") cmd.Flags().StringVar(&stepConfig.AppURLs, "appUrls", `[]`, "A JSON string containing an array of objects, each representing an application URL with associated credentials.\nEach object must have the following properties:\n- `url`: The URL of the application.\n- `username`: The username for accessing the application.\n- `password`: The password for accessing the application.\nThis parameter is used to securely pass multiple application URLs and their credentials from Vault.\n") cmd.Flags().BoolVar(&stepConfig.OnlyRunInProductiveBranch, "onlyRunInProductiveBranch", false, "Boolean to indicate whether the step should only be executed in the productive branch or not.") cmd.Flags().StringVar(&stepConfig.ProductiveBranch, "productiveBranch", `main`, "The branch used as productive branch.") @@ -203,7 +203,7 @@ func npmExecuteTestsMetadata() config.StepData { Type: "string", Mandatory: false, Aliases: []config.Alias{}, - Default: `npm install`, + Default: `npm ci`, }, { Name: "runScript", @@ -212,7 +212,7 @@ func npmExecuteTestsMetadata() config.StepData { Type: "string", Mandatory: true, Aliases: []config.Alias{}, - Default: `wdi5`, + Default: `npm run wdi5`, }, { Name: "appUrls", diff --git a/resources/metadata/npmExecuteTests.yaml b/resources/metadata/npmExecuteTests.yaml index 3797f2d2d2..80d10de25b 100644 --- a/resources/metadata/npmExecuteTests.yaml +++ b/resources/metadata/npmExecuteTests.yaml @@ -23,13 +23,13 @@ spec: default: "npm ci" - name: runScript type: string - description: Script to be executed from package.json for running tests. Defaults to `wdi5`. + description: Script to be executed from package.json for running tests. Defaults to `npm run wdi5`. scope: - PARAMETERS - STAGES - STEPS mandatory: true - default: "wdi5" + default: "npm run wdi5" - name: appUrls type: string description: | From 65f7cba657982530dc2ebbcda8697c7b2d753ec0 Mon Sep 17 00:00:00 2001 From: Philip Germanov Date: Fri, 4 Oct 2024 11:10:33 +0300 Subject: [PATCH 12/39] build: use lts-bookworm --- cmd/npmExecuteTests_generated.go | 2 +- resources/metadata/npmExecuteTests.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/npmExecuteTests_generated.go b/cmd/npmExecuteTests_generated.go index e84cbcfa40..6eae946976 100644 --- a/cmd/npmExecuteTests_generated.go +++ b/cmd/npmExecuteTests_generated.go @@ -277,7 +277,7 @@ func npmExecuteTestsMetadata() config.StepData { }, }, Containers: []config.Container{ - {Name: "node", Image: "node:lts-buster", EnvVars: []config.EnvVar{{Name: "BASE_URL", Value: "${{params.baseUrl}}"}, {Name: "CREDENTIALS_ID", Value: "${{params.credentialsId}}"}}, WorkingDir: "/app"}, + {Name: "node", Image: "node:lts-bookworm", EnvVars: []config.EnvVar{{Name: "BASE_URL", Value: "${{params.baseUrl}}"}, {Name: "CREDENTIALS_ID", Value: "${{params.credentialsId}}"}}, WorkingDir: "/app"}, }, Outputs: config.StepOutputs{ Resources: []config.StepResources{ diff --git a/resources/metadata/npmExecuteTests.yaml b/resources/metadata/npmExecuteTests.yaml index 80d10de25b..0075ead9e8 100644 --- a/resources/metadata/npmExecuteTests.yaml +++ b/resources/metadata/npmExecuteTests.yaml @@ -97,7 +97,7 @@ spec: type: e2e containers: - name: node - image: node:lts-buster + image: node:lts-bookworm env: - name: BASE_URL value: ${{params.baseUrl}} From 045b53040cd886ae47f975d77d9f4666d9197855 Mon Sep 17 00:00:00 2001 From: Philip Germanov Date: Tue, 8 Oct 2024 13:05:32 +0300 Subject: [PATCH 13/39] feat: get credentials from vault --- cmd/npmExecuteTests.go | 23 ++++++++++++----- cmd/npmExecuteTests_generated.go | 34 ++++++++----------------- resources/metadata/npmExecuteTests.yaml | 28 ++++++++------------ vars/npmExecuteTests.groovy | 29 --------------------- 4 files changed, 39 insertions(+), 75 deletions(-) diff --git a/cmd/npmExecuteTests.go b/cmd/npmExecuteTests.go index b48d30ee67..f8850a629c 100644 --- a/cmd/npmExecuteTests.go +++ b/cmd/npmExecuteTests.go @@ -1,7 +1,6 @@ package cmd import ( - "encoding/json" "fmt" "os" "strings" @@ -29,10 +28,19 @@ func runNpmExecuteTests(config *npmExecuteTestsOptions, c command.ExecRunner) er Username string `json:"username"` Password string `json:"password"` } - var appURLs []AppURL - err := json.Unmarshal([]byte(config.AppURLs), &appURLs) - if err != nil { - return fmt.Errorf("failed to parse app URLs: %w", err) + + appURLs := make(map[string]AppURL) + urlsRaw, ok := config.AppSecrets["urls"].([]interface{}) + if ok { + for _, urlRaw := range urlsRaw { + urlMap := urlRaw.(map[string]interface{}) + url := urlMap["url"].(string) + appURLs[url] = AppURL{ + URL: url, + Username: urlMap["username"].(string), + Password: urlMap["password"].(string), + } + } } provider, err := orchestrator.GetOrchestratorConfigProvider(nil) @@ -59,6 +67,9 @@ func runNpmExecuteTests(config *npmExecuteTestsOptions, c command.ExecRunner) er } } + username := config.AppSecrets["username"].(string) + password := config.AppSecrets["password"].(string) + credentialsToEnv(username, password, config.Wdi5) if err := runTestForUrl(config.BaseURL, config, c); err != nil { return err } @@ -83,7 +94,7 @@ func runTestForUrl(url string, config *npmExecuteTestsOptions, command command.E } // Execute the npm script - options := "--baseUrl_" + url + options := "--baseUrl=" + url runScriptTokens := strings.Fields(config.RunScript) if err := command.RunExecutable(runScriptTokens[0], append(runScriptTokens[1:], options)...); err != nil { return fmt.Errorf("failed to execute npm script: %w", err) diff --git a/cmd/npmExecuteTests_generated.go b/cmd/npmExecuteTests_generated.go index 6eae946976..3280daadc9 100644 --- a/cmd/npmExecuteTests_generated.go +++ b/cmd/npmExecuteTests_generated.go @@ -20,14 +20,13 @@ import ( ) type npmExecuteTestsOptions struct { - InstallCommand string `json:"installCommand,omitempty"` - RunScript string `json:"runScript,omitempty"` - AppURLs string `json:"appUrls,omitempty"` - OnlyRunInProductiveBranch bool `json:"onlyRunInProductiveBranch,omitempty"` - ProductiveBranch string `json:"productiveBranch,omitempty"` - BaseURL string `json:"baseUrl,omitempty"` - Wdi5 bool `json:"wdi5,omitempty"` - CredentialsID string `json:"credentialsId,omitempty"` + InstallCommand string `json:"installCommand,omitempty"` + RunScript string `json:"runScript,omitempty"` + AppSecrets map[string]interface{} `json:"appSecrets,omitempty"` + OnlyRunInProductiveBranch bool `json:"onlyRunInProductiveBranch,omitempty"` + ProductiveBranch string `json:"productiveBranch,omitempty"` + BaseURL string `json:"baseUrl,omitempty"` + Wdi5 bool `json:"wdi5,omitempty"` } type npmExecuteTestsReports struct { @@ -175,12 +174,11 @@ The tests can be restricted to run only on the productive branch by setting ` + func addNpmExecuteTestsFlags(cmd *cobra.Command, stepConfig *npmExecuteTestsOptions) { cmd.Flags().StringVar(&stepConfig.InstallCommand, "installCommand", `npm ci`, "Command to be executed for installation. Defaults to `npm ci`.") cmd.Flags().StringVar(&stepConfig.RunScript, "runScript", `npm run wdi5`, "Script to be executed from package.json for running tests. Defaults to `npm run wdi5`.") - cmd.Flags().StringVar(&stepConfig.AppURLs, "appUrls", `[]`, "A JSON string containing an array of objects, each representing an application URL with associated credentials.\nEach object must have the following properties:\n- `url`: The URL of the application.\n- `username`: The username for accessing the application.\n- `password`: The password for accessing the application.\nThis parameter is used to securely pass multiple application URLs and their credentials from Vault.\n") - cmd.Flags().BoolVar(&stepConfig.OnlyRunInProductiveBranch, "onlyRunInProductiveBranch", false, "Boolean to indicate whether the step should only be executed in the productive branch or not.") + + cmd.Flags().BoolVar(&stepConfig.OnlyRunInProductiveBranch, "onlyRunInProductiveBranch", false, "Boolean to indicate whether the step should only be executed in the productive branch or not.") cmd.Flags().StringVar(&stepConfig.ProductiveBranch, "productiveBranch", `main`, "The branch used as productive branch.") cmd.Flags().StringVar(&stepConfig.BaseURL, "baseUrl", `0.0.0.0`, "Base URL of the application to be tested.") cmd.Flags().BoolVar(&stepConfig.Wdi5, "wdi5", true, "Distinguish if these are wdi5 tests.") - cmd.Flags().StringVar(&stepConfig.CredentialsID, "credentialsId", os.Getenv("PIPER_credentialsId"), "Credentials to access the application to be tested.") cmd.MarkFlagRequired("runScript") } @@ -215,7 +213,7 @@ func npmExecuteTestsMetadata() config.StepData { Default: `npm run wdi5`, }, { - Name: "appUrls", + Name: "appSecrets", ResourceRef: []config.ResourceReference{ { Name: "appMetadataVaultSecretName", @@ -224,10 +222,9 @@ func npmExecuteTestsMetadata() config.StepData { }, }, Scope: []string{"PARAMETERS", "STAGES", "STEPS"}, - Type: "string", + Type: "map[string]interface{}", Mandatory: false, Aliases: []config.Alias{}, - Default: `[]`, }, { Name: "onlyRunInProductiveBranch", @@ -265,15 +262,6 @@ func npmExecuteTestsMetadata() config.StepData { Aliases: []config.Alias{}, Default: true, }, - { - Name: "credentialsId", - ResourceRef: []config.ResourceReference{}, - Scope: []string{"PARAMETERS", "STAGES", "STEPS"}, - Type: "string", - Mandatory: false, - Aliases: []config.Alias{}, - Default: os.Getenv("PIPER_credentialsId"), - }, }, }, Containers: []config.Container{ diff --git a/resources/metadata/npmExecuteTests.yaml b/resources/metadata/npmExecuteTests.yaml index 0075ead9e8..38afd426de 100644 --- a/resources/metadata/npmExecuteTests.yaml +++ b/resources/metadata/npmExecuteTests.yaml @@ -30,20 +30,21 @@ spec: - STEPS mandatory: true default: "npm run wdi5" - - name: appUrls - type: string + - name: appSecrets + type: map[string]interface{} description: | - A JSON string containing an array of objects, each representing an application URL with associated credentials. - Each object must have the following properties: - - `url`: The URL of the application. - - `username`: The username for accessing the application. - - `password`: The password for accessing the application. - This parameter is used to securely pass multiple application URLs and their credentials from Vault. + The object must have the following structure: + - `urls`: An array of objects, each representing an application URL with associated credentials. + Each object must have the following properties: + - `url`: The URL of the application. + - `username`: The username for accessing the application. + - `password`: The password for accessing the application. + - `username`: The base URL username. + - `password`: The base URL password. scope: - PARAMETERS - STAGES - STEPS - default: "[]" resourceRef: - type: vaultSecret default: appMetadata @@ -51,7 +52,7 @@ spec: - name: onlyRunInProductiveBranch type: bool default: false - description: Boolean to indicate whether the step should only be executed in the productive branch or not. + description: Boolean to indicate whether the step should only be executed in the productive branch or not. scope: - PARAMETERS - STAGES @@ -81,13 +82,6 @@ spec: - PARAMETERS - STAGES - STEPS - - name: credentialsId - type: string - description: Credentials to access the application to be tested. - scope: - - PARAMETERS - - STAGES - - STEPS outputs: resources: - name: reports diff --git a/vars/npmExecuteTests.groovy b/vars/npmExecuteTests.groovy index dcd65be2ec..ade868e98b 100644 --- a/vars/npmExecuteTests.groovy +++ b/vars/npmExecuteTests.groovy @@ -1,38 +1,9 @@ -import com.sap.piper.ConfigurationHelper import groovy.transform.Field -import static com.sap.piper.Prerequisites.checkScript - @Field String STEP_NAME = getClass().getName() @Field String METADATA_FILE = 'metadata/npmExecuteTests.yaml' -@Field Set GENERAL_CONFIG_KEYS = [] - -@Field Set STEP_CONFIG_KEYS = [ - "wdi5", - "credentialsId", -] - -@Field Set PARAMETER_KEYS = [] - void call(Map parameters = [:]) { - final script = checkScript(this, parameters) ?: this - String stageName = parameters.stageName ?: env.STAGE_NAME - Map config = ConfigurationHelper.newInstance(this) - .loadStepDefaults([:], stageName) - .mixinGeneralConfig(script.commonPipelineEnvironment, GENERAL_CONFIG_KEYS) - .mixinStepConfig(script.commonPipelineEnvironment, STEP_CONFIG_KEYS) - .mixinStageConfig(script.commonPipelineEnvironment, stageName, PARAMETER_KEYS) - .mixin(parameters, STEP_CONFIG_KEYS) - .use() - List credentials = [] - if (config.credentialsId) { - if (config.wdi5) { - credentials.add([type: 'usernamePassword', id: config.credentialsId, env: ['wdi5_username', 'wdi5_password']]) - } else { - credentials.add([type: 'usernamePassword', id: config.credentialsId, env: ['e2e_username', 'e2e_password']]) - } - } piperExecuteBin(parameters, STEP_NAME, METADATA_FILE, credentials) } From 6bf1d06e4ba249798d26706e10a1207bb7bf995c Mon Sep 17 00:00:00 2001 From: Philip Germanov Date: Tue, 8 Oct 2024 13:18:59 +0300 Subject: [PATCH 14/39] feat: remove wdi5 install script --- cmd/npmExecuteTests.go | 19 +++---------------- cmd/npmExecuteTests_generated.go | 15 ++------------- resources/metadata/npmExecuteTests.yaml | 10 +--------- 3 files changed, 6 insertions(+), 38 deletions(-) diff --git a/cmd/npmExecuteTests.go b/cmd/npmExecuteTests.go index f8850a629c..06d801cb63 100644 --- a/cmd/npmExecuteTests.go +++ b/cmd/npmExecuteTests.go @@ -59,8 +59,9 @@ func runNpmExecuteTests(config *npmExecuteTestsOptions, c command.ExecRunner) er return fmt.Errorf("failed to execute install command: %w", err) } + isWdi5 := strings.Contains(config.RunScript, "wdi5") for _, appUrl := range appURLs { - credentialsToEnv(appUrl.Username, appUrl.Password, config.Wdi5) + credentialsToEnv(appUrl.Username, appUrl.Password, isWdi5) err := runTestForUrl(appUrl.URL, config, c) if err != nil { return err @@ -69,7 +70,7 @@ func runNpmExecuteTests(config *npmExecuteTestsOptions, c command.ExecRunner) er username := config.AppSecrets["username"].(string) password := config.AppSecrets["password"].(string) - credentialsToEnv(username, password, config.Wdi5) + credentialsToEnv(username, password, isWdi5) if err := runTestForUrl(config.BaseURL, config, c); err != nil { return err } @@ -79,20 +80,6 @@ func runNpmExecuteTests(config *npmExecuteTestsOptions, c command.ExecRunner) er func runTestForUrl(url string, config *npmExecuteTestsOptions, command command.ExecRunner) error { log.Entry().Infof("Running end to end tests for URL: %s", url) - if config.Wdi5 { - // install wdi5 and all required WebdriverIO peer dependencies - // add a config file (wdio.conf.js) to your current working directory, using http://localhost:8080/index.html as baseUrl, - // looking for tests in $ui5-app/webapp/test/**/* that follow the name pattern *.test.js - // set an npm script named “wdi5” to run wdi5 so you can immediately do npm run wdi5 - if err := command.RunExecutable("npm", "init", "wdi5@latest", "--baseUrl", url); err != nil { - return fmt.Errorf("failed to install wdi5: %w", err) - } - if err := command.RunExecutable("npm", "run", "wdi5"); err != nil { - return fmt.Errorf("failed to execute wdi5: %w", err) - } - return nil - } - // Execute the npm script options := "--baseUrl=" + url runScriptTokens := strings.Fields(config.RunScript) diff --git a/cmd/npmExecuteTests_generated.go b/cmd/npmExecuteTests_generated.go index 3280daadc9..aaabc58f06 100644 --- a/cmd/npmExecuteTests_generated.go +++ b/cmd/npmExecuteTests_generated.go @@ -26,7 +26,6 @@ type npmExecuteTestsOptions struct { OnlyRunInProductiveBranch bool `json:"onlyRunInProductiveBranch,omitempty"` ProductiveBranch string `json:"productiveBranch,omitempty"` BaseURL string `json:"baseUrl,omitempty"` - Wdi5 bool `json:"wdi5,omitempty"` } type npmExecuteTestsReports struct { @@ -177,8 +176,7 @@ func addNpmExecuteTestsFlags(cmd *cobra.Command, stepConfig *npmExecuteTestsOpti cmd.Flags().BoolVar(&stepConfig.OnlyRunInProductiveBranch, "onlyRunInProductiveBranch", false, "Boolean to indicate whether the step should only be executed in the productive branch or not.") cmd.Flags().StringVar(&stepConfig.ProductiveBranch, "productiveBranch", `main`, "The branch used as productive branch.") - cmd.Flags().StringVar(&stepConfig.BaseURL, "baseUrl", `0.0.0.0`, "Base URL of the application to be tested.") - cmd.Flags().BoolVar(&stepConfig.Wdi5, "wdi5", true, "Distinguish if these are wdi5 tests.") + cmd.Flags().StringVar(&stepConfig.BaseURL, "baseUrl", `http://localhost:8080/index.html`, "Base URL of the application to be tested.") cmd.MarkFlagRequired("runScript") } @@ -251,16 +249,7 @@ func npmExecuteTestsMetadata() config.StepData { Type: "string", Mandatory: false, Aliases: []config.Alias{}, - Default: `0.0.0.0`, - }, - { - Name: "wdi5", - ResourceRef: []config.ResourceReference{}, - Scope: []string{"PARAMETERS", "STAGES", "STEPS"}, - Type: "bool", - Mandatory: false, - Aliases: []config.Alias{}, - Default: true, + Default: `http://localhost:8080/index.html`, }, }, }, diff --git a/resources/metadata/npmExecuteTests.yaml b/resources/metadata/npmExecuteTests.yaml index 38afd426de..10682c096f 100644 --- a/resources/metadata/npmExecuteTests.yaml +++ b/resources/metadata/npmExecuteTests.yaml @@ -68,20 +68,12 @@ spec: - STEPS - name: baseUrl type: string - default: "0.0.0.0" + default: "http://localhost:8080/index.html" description: Base URL of the application to be tested. scope: - PARAMETERS - STAGES - STEPS - - name: wdi5 - type: bool - description: Distinguish if these are wdi5 tests. - default: true - scope: - - PARAMETERS - - STAGES - - STEPS outputs: resources: - name: reports From 64f063fe69a852e42ee9309a548cd6102d9c8d44 Mon Sep 17 00:00:00 2001 From: Philip Germanov Date: Tue, 8 Oct 2024 13:20:58 +0300 Subject: [PATCH 15/39] fix: groovy tests --- vars/npmExecuteTests.groovy | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/vars/npmExecuteTests.groovy b/vars/npmExecuteTests.groovy index ade868e98b..bb34499c22 100644 --- a/vars/npmExecuteTests.groovy +++ b/vars/npmExecuteTests.groovy @@ -3,6 +3,12 @@ import groovy.transform.Field @Field String STEP_NAME = getClass().getName() @Field String METADATA_FILE = 'metadata/npmExecuteTests.yaml' +@Field Set GENERAL_CONFIG_KEYS = [] + +@Field Set STEP_CONFIG_KEYS = [] + +@Field Set PARAMETER_KEYS = [] + void call(Map parameters = [:]) { List credentials = [] piperExecuteBin(parameters, STEP_NAME, METADATA_FILE, credentials) From f67b7a8df948485cf4e717117d22a08b8fae9e09 Mon Sep 17 00:00:00 2001 From: phgermanov Date: Tue, 8 Oct 2024 15:28:21 +0300 Subject: [PATCH 16/39] fix: report type Co-authored-by: Christopher Fenner <26137398+CCFenner@users.noreply.github.com> --- resources/metadata/npmExecuteTests.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/metadata/npmExecuteTests.yaml b/resources/metadata/npmExecuteTests.yaml index 10682c096f..e3eff0984c 100644 --- a/resources/metadata/npmExecuteTests.yaml +++ b/resources/metadata/npmExecuteTests.yaml @@ -80,7 +80,7 @@ spec: type: reports params: - filePattern: "**/e2e-results.xml" - type: e2e + type: end-to-end-test containers: - name: node image: node:lts-bookworm From 8e4ba08c97a650196b5cc543dae33b4a2d717ec1 Mon Sep 17 00:00:00 2001 From: Philip Germanov Date: Tue, 8 Oct 2024 15:19:22 +0300 Subject: [PATCH 17/39] docs: remove defaults from desc --- resources/metadata/npmExecuteTests.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/metadata/npmExecuteTests.yaml b/resources/metadata/npmExecuteTests.yaml index e3eff0984c..cf9a88da8b 100644 --- a/resources/metadata/npmExecuteTests.yaml +++ b/resources/metadata/npmExecuteTests.yaml @@ -15,7 +15,7 @@ spec: params: - name: installCommand type: string - description: Command to be executed for installation. Defaults to `npm ci`. + description: Command to be executed for installation`. scope: - PARAMETERS - STAGES @@ -23,7 +23,7 @@ spec: default: "npm ci" - name: runScript type: string - description: Script to be executed from package.json for running tests. Defaults to `npm run wdi5`. + description: Script to be executed from package.json for running tests`. scope: - PARAMETERS - STAGES From 1a3e5eaec086928cafc6006125bf1817dc5732bd Mon Sep 17 00:00:00 2001 From: Philip Germanov Date: Tue, 8 Oct 2024 15:20:42 +0300 Subject: [PATCH 18/39] feat: remove branch params --- cmd/npmExecuteTests.go | 12 ------------ resources/metadata/npmExecuteTests.yaml | 17 ----------------- 2 files changed, 29 deletions(-) diff --git a/cmd/npmExecuteTests.go b/cmd/npmExecuteTests.go index 06d801cb63..1623a1641d 100644 --- a/cmd/npmExecuteTests.go +++ b/cmd/npmExecuteTests.go @@ -7,7 +7,6 @@ import ( "github.com/SAP/jenkins-library/pkg/command" "github.com/SAP/jenkins-library/pkg/log" - "github.com/SAP/jenkins-library/pkg/orchestrator" "github.com/SAP/jenkins-library/pkg/telemetry" ) @@ -43,17 +42,6 @@ func runNpmExecuteTests(config *npmExecuteTestsOptions, c command.ExecRunner) er } } - provider, err := orchestrator.GetOrchestratorConfigProvider(nil) - if err != nil { - return fmt.Errorf("failed to get orchestrator config provider: %w", err) - } - - env := provider.Branch() - if config.OnlyRunInProductiveBranch && config.ProductiveBranch != env { - log.Entry().Info("Skipping execution because it is configured to run only in the productive branch.") - return nil - } - installCommandTokens := strings.Fields(config.InstallCommand) if err := c.RunExecutable(installCommandTokens[0], installCommandTokens[1:]...); err != nil { return fmt.Errorf("failed to execute install command: %w", err) diff --git a/resources/metadata/npmExecuteTests.yaml b/resources/metadata/npmExecuteTests.yaml index cf9a88da8b..39cfffc458 100644 --- a/resources/metadata/npmExecuteTests.yaml +++ b/resources/metadata/npmExecuteTests.yaml @@ -49,23 +49,6 @@ spec: - type: vaultSecret default: appMetadata name: appMetadataVaultSecretName - - name: onlyRunInProductiveBranch - type: bool - default: false - description: Boolean to indicate whether the step should only be executed in the productive branch or not. - scope: - - PARAMETERS - - STAGES - - STEPS - - name: productiveBranch - type: string - default: "main" - description: The branch used as productive branch. - scope: - - GENERAL - - PARAMETERS - - STAGES - - STEPS - name: baseUrl type: string default: "http://localhost:8080/index.html" From c22d874b30ab1aa906f248dea5aaa817c4778a36 Mon Sep 17 00:00:00 2001 From: Philip Germanov Date: Tue, 8 Oct 2024 15:32:37 +0300 Subject: [PATCH 19/39] refactor: rename appSecrets to vaultMetadata --- cmd/npmExecuteTests.go | 2 +- cmd/npmExecuteTests_generated.go | 36 +++++-------------------- resources/metadata/npmExecuteTests.yaml | 2 +- 3 files changed, 9 insertions(+), 31 deletions(-) diff --git a/cmd/npmExecuteTests.go b/cmd/npmExecuteTests.go index 1623a1641d..4df045a40f 100644 --- a/cmd/npmExecuteTests.go +++ b/cmd/npmExecuteTests.go @@ -29,7 +29,7 @@ func runNpmExecuteTests(config *npmExecuteTestsOptions, c command.ExecRunner) er } appURLs := make(map[string]AppURL) - urlsRaw, ok := config.AppSecrets["urls"].([]interface{}) + urlsRaw, ok := config.VaultMetadata["urls"].([]interface{}) if ok { for _, urlRaw := range urlsRaw { urlMap := urlRaw.(map[string]interface{}) diff --git a/cmd/npmExecuteTests_generated.go b/cmd/npmExecuteTests_generated.go index aaabc58f06..687997d041 100644 --- a/cmd/npmExecuteTests_generated.go +++ b/cmd/npmExecuteTests_generated.go @@ -20,12 +20,10 @@ import ( ) type npmExecuteTestsOptions struct { - InstallCommand string `json:"installCommand,omitempty"` - RunScript string `json:"runScript,omitempty"` - AppSecrets map[string]interface{} `json:"appSecrets,omitempty"` - OnlyRunInProductiveBranch bool `json:"onlyRunInProductiveBranch,omitempty"` - ProductiveBranch string `json:"productiveBranch,omitempty"` - BaseURL string `json:"baseUrl,omitempty"` + InstallCommand string `json:"installCommand,omitempty"` + RunScript string `json:"runScript,omitempty"` + VaultMetadata map[string]interface{} `json:"vaultMetadata,omitempty"` + BaseURL string `json:"baseUrl,omitempty"` } type npmExecuteTestsReports struct { @@ -171,11 +169,9 @@ The tests can be restricted to run only on the productive branch by setting ` + } func addNpmExecuteTestsFlags(cmd *cobra.Command, stepConfig *npmExecuteTestsOptions) { - cmd.Flags().StringVar(&stepConfig.InstallCommand, "installCommand", `npm ci`, "Command to be executed for installation. Defaults to `npm ci`.") - cmd.Flags().StringVar(&stepConfig.RunScript, "runScript", `npm run wdi5`, "Script to be executed from package.json for running tests. Defaults to `npm run wdi5`.") + cmd.Flags().StringVar(&stepConfig.InstallCommand, "installCommand", `npm ci`, "Command to be executed for installation`.") + cmd.Flags().StringVar(&stepConfig.RunScript, "runScript", `npm run wdi5`, "Script to be executed from package.json for running tests`.") - cmd.Flags().BoolVar(&stepConfig.OnlyRunInProductiveBranch, "onlyRunInProductiveBranch", false, "Boolean to indicate whether the step should only be executed in the productive branch or not.") - cmd.Flags().StringVar(&stepConfig.ProductiveBranch, "productiveBranch", `main`, "The branch used as productive branch.") cmd.Flags().StringVar(&stepConfig.BaseURL, "baseUrl", `http://localhost:8080/index.html`, "Base URL of the application to be tested.") cmd.MarkFlagRequired("runScript") @@ -211,7 +207,7 @@ func npmExecuteTestsMetadata() config.StepData { Default: `npm run wdi5`, }, { - Name: "appSecrets", + Name: "vaultMetadata", ResourceRef: []config.ResourceReference{ { Name: "appMetadataVaultSecretName", @@ -224,24 +220,6 @@ func npmExecuteTestsMetadata() config.StepData { Mandatory: false, Aliases: []config.Alias{}, }, - { - Name: "onlyRunInProductiveBranch", - ResourceRef: []config.ResourceReference{}, - Scope: []string{"PARAMETERS", "STAGES", "STEPS"}, - Type: "bool", - Mandatory: false, - Aliases: []config.Alias{}, - Default: false, - }, - { - Name: "productiveBranch", - ResourceRef: []config.ResourceReference{}, - Scope: []string{"GENERAL", "PARAMETERS", "STAGES", "STEPS"}, - Type: "string", - Mandatory: false, - Aliases: []config.Alias{}, - Default: `main`, - }, { Name: "baseUrl", ResourceRef: []config.ResourceReference{}, diff --git a/resources/metadata/npmExecuteTests.yaml b/resources/metadata/npmExecuteTests.yaml index 39cfffc458..9aa9719a27 100644 --- a/resources/metadata/npmExecuteTests.yaml +++ b/resources/metadata/npmExecuteTests.yaml @@ -30,7 +30,7 @@ spec: - STEPS mandatory: true default: "npm run wdi5" - - name: appSecrets + - name: vaultMetadata type: map[string]interface{} description: | The object must have the following structure: From 8c2456d41dc2cdca634633231dd25ac142bdc8b4 Mon Sep 17 00:00:00 2001 From: Philip Germanov Date: Tue, 8 Oct 2024 15:43:02 +0300 Subject: [PATCH 20/39] feat: param for custom env var prefix --- cmd/npmExecuteTests.go | 15 +++++---------- cmd/npmExecuteTests_generated.go | 19 +++++++++++++++---- resources/metadata/npmExecuteTests.yaml | 4 ++++ 3 files changed, 24 insertions(+), 14 deletions(-) diff --git a/cmd/npmExecuteTests.go b/cmd/npmExecuteTests.go index 4df045a40f..1bb430dcaa 100644 --- a/cmd/npmExecuteTests.go +++ b/cmd/npmExecuteTests.go @@ -47,18 +47,17 @@ func runNpmExecuteTests(config *npmExecuteTestsOptions, c command.ExecRunner) er return fmt.Errorf("failed to execute install command: %w", err) } - isWdi5 := strings.Contains(config.RunScript, "wdi5") for _, appUrl := range appURLs { - credentialsToEnv(appUrl.Username, appUrl.Password, isWdi5) + credentialsToEnv(appUrl.Username, appUrl.Password, config.CredentialsEnvVarPrefix) err := runTestForUrl(appUrl.URL, config, c) if err != nil { return err } } - username := config.AppSecrets["username"].(string) - password := config.AppSecrets["password"].(string) - credentialsToEnv(username, password, isWdi5) + username := config.VaultMetadata["username"].(string) + password := config.VaultMetadata["password"].(string) + credentialsToEnv(username, password, config.CredentialsEnvVarPrefix) if err := runTestForUrl(config.BaseURL, config, c); err != nil { return err } @@ -77,11 +76,7 @@ func runTestForUrl(url string, config *npmExecuteTestsOptions, command command.E return nil } -func credentialsToEnv(username, password string, wdi5 bool) { - prefix := "e2e" - if wdi5 { - prefix = "wdi5" - } +func credentialsToEnv(username, password, prefix string) { os.Setenv(prefix+"_username", username) os.Setenv(prefix+"_password", password) } diff --git a/cmd/npmExecuteTests_generated.go b/cmd/npmExecuteTests_generated.go index 687997d041..c877070632 100644 --- a/cmd/npmExecuteTests_generated.go +++ b/cmd/npmExecuteTests_generated.go @@ -20,10 +20,11 @@ import ( ) type npmExecuteTestsOptions struct { - InstallCommand string `json:"installCommand,omitempty"` - RunScript string `json:"runScript,omitempty"` - VaultMetadata map[string]interface{} `json:"vaultMetadata,omitempty"` - BaseURL string `json:"baseUrl,omitempty"` + InstallCommand string `json:"installCommand,omitempty"` + RunScript string `json:"runScript,omitempty"` + VaultMetadata map[string]interface{} `json:"vaultMetadata,omitempty"` + BaseURL string `json:"baseUrl,omitempty"` + CredentialsEnvVarPrefix string `json:"credentialsEnvVarPrefix,omitempty"` } type npmExecuteTestsReports struct { @@ -173,6 +174,7 @@ func addNpmExecuteTestsFlags(cmd *cobra.Command, stepConfig *npmExecuteTestsOpti cmd.Flags().StringVar(&stepConfig.RunScript, "runScript", `npm run wdi5`, "Script to be executed from package.json for running tests`.") cmd.Flags().StringVar(&stepConfig.BaseURL, "baseUrl", `http://localhost:8080/index.html`, "Base URL of the application to be tested.") + cmd.Flags().StringVar(&stepConfig.CredentialsEnvVarPrefix, "credentialsEnvVarPrefix", `wdi5`, "Prefix for username and password env vars.") cmd.MarkFlagRequired("runScript") } @@ -229,6 +231,15 @@ func npmExecuteTestsMetadata() config.StepData { Aliases: []config.Alias{}, Default: `http://localhost:8080/index.html`, }, + { + Name: "credentialsEnvVarPrefix", + ResourceRef: []config.ResourceReference{}, + Scope: []string{}, + Type: "string", + Mandatory: false, + Aliases: []config.Alias{}, + Default: `wdi5`, + }, }, }, Containers: []config.Container{ diff --git a/resources/metadata/npmExecuteTests.yaml b/resources/metadata/npmExecuteTests.yaml index 9aa9719a27..dac6bfcb72 100644 --- a/resources/metadata/npmExecuteTests.yaml +++ b/resources/metadata/npmExecuteTests.yaml @@ -57,6 +57,10 @@ spec: - PARAMETERS - STAGES - STEPS + - name: credentialsEnvVarPrefix + type: string + default: "wdi5" + description: Prefix for username and password env vars. outputs: resources: - name: reports From 7c14e86f84b7b335178f99d9b3e45d1a8588cbf2 Mon Sep 17 00:00:00 2001 From: Philip Germanov Date: Tue, 8 Oct 2024 15:47:49 +0300 Subject: [PATCH 21/39] refactor: change app urls map to a list --- cmd/npmExecuteTests.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/cmd/npmExecuteTests.go b/cmd/npmExecuteTests.go index 1bb430dcaa..169786f08a 100644 --- a/cmd/npmExecuteTests.go +++ b/cmd/npmExecuteTests.go @@ -28,17 +28,17 @@ func runNpmExecuteTests(config *npmExecuteTestsOptions, c command.ExecRunner) er Password string `json:"password"` } - appURLs := make(map[string]AppURL) + appURLs := []AppURL{} urlsRaw, ok := config.VaultMetadata["urls"].([]interface{}) if ok { for _, urlRaw := range urlsRaw { urlMap := urlRaw.(map[string]interface{}) - url := urlMap["url"].(string) - appURLs[url] = AppURL{ - URL: url, + appURL := AppURL{ + URL: urlMap["url"].(string), Username: urlMap["username"].(string), Password: urlMap["password"].(string), } + appURLs = append(appURLs, appURL) } } From d8db2844aae34e2b0d8f195020093cdfab298d21 Mon Sep 17 00:00:00 2001 From: Philip Germanov Date: Tue, 8 Oct 2024 15:54:22 +0300 Subject: [PATCH 22/39] refactor: rename appURLs for clarity --- cmd/npmExecuteTests.go | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/cmd/npmExecuteTests.go b/cmd/npmExecuteTests.go index 169786f08a..5d856eac7d 100644 --- a/cmd/npmExecuteTests.go +++ b/cmd/npmExecuteTests.go @@ -22,23 +22,23 @@ func npmExecuteTests(config npmExecuteTestsOptions, _ *telemetry.CustomData) { } func runNpmExecuteTests(config *npmExecuteTestsOptions, c command.ExecRunner) error { - type AppURL struct { + type AppUnderTest struct { URL string `json:"url"` Username string `json:"username"` Password string `json:"password"` } - appURLs := []AppURL{} + apps := []AppUnderTest{} urlsRaw, ok := config.VaultMetadata["urls"].([]interface{}) if ok { for _, urlRaw := range urlsRaw { urlMap := urlRaw.(map[string]interface{}) - appURL := AppURL{ + app := AppUnderTest{ URL: urlMap["url"].(string), Username: urlMap["username"].(string), Password: urlMap["password"].(string), } - appURLs = append(appURLs, appURL) + apps = append(apps, app) } } @@ -47,9 +47,9 @@ func runNpmExecuteTests(config *npmExecuteTestsOptions, c command.ExecRunner) er return fmt.Errorf("failed to execute install command: %w", err) } - for _, appUrl := range appURLs { - credentialsToEnv(appUrl.Username, appUrl.Password, config.CredentialsEnvVarPrefix) - err := runTestForUrl(appUrl.URL, config, c) + for _, app := range apps { + credentialsToEnv(app.Username, app.Password, config.CredentialsEnvVarPrefix) + err := runTestForUrl(app.URL, config, c) if err != nil { return err } From 44f8cd9919bc4421eada2aac943db6148afbc8ed Mon Sep 17 00:00:00 2001 From: Philip Germanov Date: Tue, 8 Oct 2024 16:08:00 +0300 Subject: [PATCH 23/39] refactor: user can specify url option param --- cmd/npmExecuteTests.go | 7 ++++--- cmd/npmExecuteTests_generated.go | 11 +++++++++++ resources/metadata/npmExecuteTests.yaml | 6 ++++++ 3 files changed, 21 insertions(+), 3 deletions(-) diff --git a/cmd/npmExecuteTests.go b/cmd/npmExecuteTests.go index 5d856eac7d..fdd5a2ff41 100644 --- a/cmd/npmExecuteTests.go +++ b/cmd/npmExecuteTests.go @@ -67,10 +67,11 @@ func runNpmExecuteTests(config *npmExecuteTestsOptions, c command.ExecRunner) er func runTestForUrl(url string, config *npmExecuteTestsOptions, command command.ExecRunner) error { log.Entry().Infof("Running end to end tests for URL: %s", url) - // Execute the npm script - options := "--baseUrl=" + url runScriptTokens := strings.Fields(config.RunScript) - if err := command.RunExecutable(runScriptTokens[0], append(runScriptTokens[1:], options)...); err != nil { + if config.UrlOptionPrefix != "" { + runScriptTokens = append(runScriptTokens, config.UrlOptionPrefix+url) + } + if err := command.RunExecutable(runScriptTokens[0], runScriptTokens[1:]...); err != nil { return fmt.Errorf("failed to execute npm script: %w", err) } return nil diff --git a/cmd/npmExecuteTests_generated.go b/cmd/npmExecuteTests_generated.go index c877070632..b2bcde7e1e 100644 --- a/cmd/npmExecuteTests_generated.go +++ b/cmd/npmExecuteTests_generated.go @@ -25,6 +25,7 @@ type npmExecuteTestsOptions struct { VaultMetadata map[string]interface{} `json:"vaultMetadata,omitempty"` BaseURL string `json:"baseUrl,omitempty"` CredentialsEnvVarPrefix string `json:"credentialsEnvVarPrefix,omitempty"` + UrlOptionPrefix string `json:"urlOptionPrefix,omitempty"` } type npmExecuteTestsReports struct { @@ -175,6 +176,7 @@ func addNpmExecuteTestsFlags(cmd *cobra.Command, stepConfig *npmExecuteTestsOpti cmd.Flags().StringVar(&stepConfig.BaseURL, "baseUrl", `http://localhost:8080/index.html`, "Base URL of the application to be tested.") cmd.Flags().StringVar(&stepConfig.CredentialsEnvVarPrefix, "credentialsEnvVarPrefix", `wdi5`, "Prefix for username and password env vars.") + cmd.Flags().StringVar(&stepConfig.UrlOptionPrefix, "urlOptionPrefix", os.Getenv("PIPER_urlOptionPrefix"), "If you want to specify an extra option that the tested url it appended to.\nFor example if the test URL is `http://localhost and urlOptionPrefix is `--base-url=`,\nwe'll add `--base-url=http://localhost` to your runScript.\n") cmd.MarkFlagRequired("runScript") } @@ -240,6 +242,15 @@ func npmExecuteTestsMetadata() config.StepData { Aliases: []config.Alias{}, Default: `wdi5`, }, + { + Name: "urlOptionPrefix", + ResourceRef: []config.ResourceReference{}, + Scope: []string{}, + Type: "string", + Mandatory: false, + Aliases: []config.Alias{}, + Default: os.Getenv("PIPER_urlOptionPrefix"), + }, }, }, Containers: []config.Container{ diff --git a/resources/metadata/npmExecuteTests.yaml b/resources/metadata/npmExecuteTests.yaml index dac6bfcb72..ab9ec536fb 100644 --- a/resources/metadata/npmExecuteTests.yaml +++ b/resources/metadata/npmExecuteTests.yaml @@ -61,6 +61,12 @@ spec: type: string default: "wdi5" description: Prefix for username and password env vars. + - name: urlOptionPrefix + type: string + description: | + If you want to specify an extra option that the tested url it appended to. + For example if the test URL is `http://localhost and urlOptionPrefix is `--base-url=`, + we'll add `--base-url=http://localhost` to your runScript. outputs: resources: - name: reports From ec8369a09097c724c7f25809566fe954e04345e4 Mon Sep 17 00:00:00 2001 From: Philip Germanov Date: Tue, 8 Oct 2024 16:20:44 +0300 Subject: [PATCH 24/39] docs: go generate --- cmd/npmExecuteTests_generated.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/npmExecuteTests_generated.go b/cmd/npmExecuteTests_generated.go index b2bcde7e1e..46b49ac36c 100644 --- a/cmd/npmExecuteTests_generated.go +++ b/cmd/npmExecuteTests_generated.go @@ -38,7 +38,7 @@ func (p *npmExecuteTestsReports) persist(stepConfig npmExecuteTestsOptions, gcpJ } log.Entry().Info("Uploading reports to Google Cloud Storage...") content := []gcs.ReportOutputParam{ - {FilePattern: "**/e2e-results.xml", ParamRef: "", StepResultType: "e2e"}, + {FilePattern: "**/e2e-results.xml", ParamRef: "", StepResultType: "end-to-end-test"}, } envVars := []gcs.EnvVar{ {Name: "GOOGLE_APPLICATION_CREDENTIALS", Value: gcpJsonKeyFilePath, Modified: false}, @@ -262,7 +262,7 @@ func npmExecuteTestsMetadata() config.StepData { Name: "reports", Type: "reports", Parameters: []map[string]interface{}{ - {"filePattern": "**/e2e-results.xml", "type": "e2e"}, + {"filePattern": "**/e2e-results.xml", "type": "end-to-end-test"}, }, }, }, From 5768f0f21a3e9c3ff6090f015340ec51d3fa5a8d Mon Sep 17 00:00:00 2001 From: Philip Germanov Date: Thu, 10 Oct 2024 13:53:00 +0300 Subject: [PATCH 25/39] feat: add support for user defined env vars and paths --- cmd/npmExecuteTests.go | 18 +++++++++++++----- cmd/npmExecuteTests_generated.go | 22 ++++++++++++++++++++++ resources/metadata/npmExecuteTests.yaml | 6 ++++++ 3 files changed, 41 insertions(+), 5 deletions(-) diff --git a/cmd/npmExecuteTests.go b/cmd/npmExecuteTests.go index fdd5a2ff41..cf830dc03c 100644 --- a/cmd/npmExecuteTests.go +++ b/cmd/npmExecuteTests.go @@ -42,13 +42,22 @@ func runNpmExecuteTests(config *npmExecuteTestsOptions, c command.ExecRunner) er } } + if len(config.EnvVars) > 0 { + c.SetEnv(config.EnvVars) + } + + if len(config.Paths) > 0 { + path := fmt.Sprintf("PATH=%s:%s", os.Getenv("PATH"), strings.Join(config.Paths, ":")) + c.SetEnv([]string{path}) + } + installCommandTokens := strings.Fields(config.InstallCommand) if err := c.RunExecutable(installCommandTokens[0], installCommandTokens[1:]...); err != nil { return fmt.Errorf("failed to execute install command: %w", err) } for _, app := range apps { - credentialsToEnv(app.Username, app.Password, config.CredentialsEnvVarPrefix) + credentialsToEnv(app.Username, app.Password, config.CredentialsEnvVarPrefix, c) err := runTestForUrl(app.URL, config, c) if err != nil { return err @@ -57,7 +66,7 @@ func runNpmExecuteTests(config *npmExecuteTestsOptions, c command.ExecRunner) er username := config.VaultMetadata["username"].(string) password := config.VaultMetadata["password"].(string) - credentialsToEnv(username, password, config.CredentialsEnvVarPrefix) + credentialsToEnv(username, password, config.CredentialsEnvVarPrefix, c) if err := runTestForUrl(config.BaseURL, config, c); err != nil { return err } @@ -77,7 +86,6 @@ func runTestForUrl(url string, config *npmExecuteTestsOptions, command command.E return nil } -func credentialsToEnv(username, password, prefix string) { - os.Setenv(prefix+"_username", username) - os.Setenv(prefix+"_password", password) +func credentialsToEnv(username, password, prefix string, c command.ExecRunner) { + c.SetEnv([]string{prefix + "_username=" + username, prefix + "_password=" + password}) } diff --git a/cmd/npmExecuteTests_generated.go b/cmd/npmExecuteTests_generated.go index 46b49ac36c..fac60aa7b1 100644 --- a/cmd/npmExecuteTests_generated.go +++ b/cmd/npmExecuteTests_generated.go @@ -26,6 +26,8 @@ type npmExecuteTestsOptions struct { BaseURL string `json:"baseUrl,omitempty"` CredentialsEnvVarPrefix string `json:"credentialsEnvVarPrefix,omitempty"` UrlOptionPrefix string `json:"urlOptionPrefix,omitempty"` + EnvVars []string `json:"envVars,omitempty"` + Paths []string `json:"paths,omitempty"` } type npmExecuteTestsReports struct { @@ -177,6 +179,8 @@ func addNpmExecuteTestsFlags(cmd *cobra.Command, stepConfig *npmExecuteTestsOpti cmd.Flags().StringVar(&stepConfig.BaseURL, "baseUrl", `http://localhost:8080/index.html`, "Base URL of the application to be tested.") cmd.Flags().StringVar(&stepConfig.CredentialsEnvVarPrefix, "credentialsEnvVarPrefix", `wdi5`, "Prefix for username and password env vars.") cmd.Flags().StringVar(&stepConfig.UrlOptionPrefix, "urlOptionPrefix", os.Getenv("PIPER_urlOptionPrefix"), "If you want to specify an extra option that the tested url it appended to.\nFor example if the test URL is `http://localhost and urlOptionPrefix is `--base-url=`,\nwe'll add `--base-url=http://localhost` to your runScript.\n") + cmd.Flags().StringSliceVar(&stepConfig.EnvVars, "envVars", []string{}, "List of environment variables to be set") + cmd.Flags().StringSliceVar(&stepConfig.Paths, "paths", []string{}, "List of paths to be added to $PATH") cmd.MarkFlagRequired("runScript") } @@ -251,6 +255,24 @@ func npmExecuteTestsMetadata() config.StepData { Aliases: []config.Alias{}, Default: os.Getenv("PIPER_urlOptionPrefix"), }, + { + Name: "envVars", + ResourceRef: []config.ResourceReference{}, + Scope: []string{}, + Type: "[]string", + Mandatory: false, + Aliases: []config.Alias{}, + Default: []string{}, + }, + { + Name: "paths", + ResourceRef: []config.ResourceReference{}, + Scope: []string{}, + Type: "[]string", + Mandatory: false, + Aliases: []config.Alias{}, + Default: []string{}, + }, }, }, Containers: []config.Container{ diff --git a/resources/metadata/npmExecuteTests.yaml b/resources/metadata/npmExecuteTests.yaml index ab9ec536fb..36242c3952 100644 --- a/resources/metadata/npmExecuteTests.yaml +++ b/resources/metadata/npmExecuteTests.yaml @@ -67,6 +67,12 @@ spec: If you want to specify an extra option that the tested url it appended to. For example if the test URL is `http://localhost and urlOptionPrefix is `--base-url=`, we'll add `--base-url=http://localhost` to your runScript. + - name: envVars + type: "[]string" + description: List of environment variables to be set + - name: paths + type: "[]string" + description: List of paths to be added to $PATH outputs: resources: - name: reports From 84cf942e25c73401c0fa5083ce44722e3883973b Mon Sep 17 00:00:00 2001 From: Philip Germanov Date: Thu, 10 Oct 2024 14:12:57 +0300 Subject: [PATCH 26/39] refactor: rename runScript to runCommand --- cmd/npmExecuteTests.go | 2 +- cmd/npmExecuteTests_generated.go | 8 ++++---- resources/metadata/npmExecuteTests.yaml | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/cmd/npmExecuteTests.go b/cmd/npmExecuteTests.go index cf830dc03c..80b6685dee 100644 --- a/cmd/npmExecuteTests.go +++ b/cmd/npmExecuteTests.go @@ -76,7 +76,7 @@ func runNpmExecuteTests(config *npmExecuteTestsOptions, c command.ExecRunner) er func runTestForUrl(url string, config *npmExecuteTestsOptions, command command.ExecRunner) error { log.Entry().Infof("Running end to end tests for URL: %s", url) - runScriptTokens := strings.Fields(config.RunScript) + runScriptTokens := strings.Fields(config.RunCommand) if config.UrlOptionPrefix != "" { runScriptTokens = append(runScriptTokens, config.UrlOptionPrefix+url) } diff --git a/cmd/npmExecuteTests_generated.go b/cmd/npmExecuteTests_generated.go index fac60aa7b1..42cdf3c02f 100644 --- a/cmd/npmExecuteTests_generated.go +++ b/cmd/npmExecuteTests_generated.go @@ -21,7 +21,7 @@ import ( type npmExecuteTestsOptions struct { InstallCommand string `json:"installCommand,omitempty"` - RunScript string `json:"runScript,omitempty"` + RunCommand string `json:"runCommand,omitempty"` VaultMetadata map[string]interface{} `json:"vaultMetadata,omitempty"` BaseURL string `json:"baseUrl,omitempty"` CredentialsEnvVarPrefix string `json:"credentialsEnvVarPrefix,omitempty"` @@ -174,7 +174,7 @@ The tests can be restricted to run only on the productive branch by setting ` + func addNpmExecuteTestsFlags(cmd *cobra.Command, stepConfig *npmExecuteTestsOptions) { cmd.Flags().StringVar(&stepConfig.InstallCommand, "installCommand", `npm ci`, "Command to be executed for installation`.") - cmd.Flags().StringVar(&stepConfig.RunScript, "runScript", `npm run wdi5`, "Script to be executed from package.json for running tests`.") + cmd.Flags().StringVar(&stepConfig.RunCommand, "runCommand", `npm run wdi5`, "Command to be executed for running tests`.") cmd.Flags().StringVar(&stepConfig.BaseURL, "baseUrl", `http://localhost:8080/index.html`, "Base URL of the application to be tested.") cmd.Flags().StringVar(&stepConfig.CredentialsEnvVarPrefix, "credentialsEnvVarPrefix", `wdi5`, "Prefix for username and password env vars.") @@ -182,7 +182,7 @@ func addNpmExecuteTestsFlags(cmd *cobra.Command, stepConfig *npmExecuteTestsOpti cmd.Flags().StringSliceVar(&stepConfig.EnvVars, "envVars", []string{}, "List of environment variables to be set") cmd.Flags().StringSliceVar(&stepConfig.Paths, "paths", []string{}, "List of paths to be added to $PATH") - cmd.MarkFlagRequired("runScript") + cmd.MarkFlagRequired("runCommand") } // retrieve step metadata @@ -206,7 +206,7 @@ func npmExecuteTestsMetadata() config.StepData { Default: `npm ci`, }, { - Name: "runScript", + Name: "runCommand", ResourceRef: []config.ResourceReference{}, Scope: []string{"PARAMETERS", "STAGES", "STEPS"}, Type: "string", diff --git a/resources/metadata/npmExecuteTests.yaml b/resources/metadata/npmExecuteTests.yaml index 36242c3952..66066fad0d 100644 --- a/resources/metadata/npmExecuteTests.yaml +++ b/resources/metadata/npmExecuteTests.yaml @@ -21,9 +21,9 @@ spec: - STAGES - STEPS default: "npm ci" - - name: runScript + - name: runCommand type: string - description: Script to be executed from package.json for running tests`. + description: Command to be executed for running tests`. scope: - PARAMETERS - STAGES From d4cf48654eab9223ac31e303617f5b0d5dddd177 Mon Sep 17 00:00:00 2001 From: Philip Germanov Date: Thu, 10 Oct 2024 14:31:29 +0300 Subject: [PATCH 27/39] refactor: expose username and password env vars --- cmd/npmExecuteTests.go | 11 ++++++--- cmd/npmExecuteTests_generated.go | 33 ++++++++++++++++--------- resources/metadata/npmExecuteTests.yaml | 10 +++++--- 3 files changed, 36 insertions(+), 18 deletions(-) diff --git a/cmd/npmExecuteTests.go b/cmd/npmExecuteTests.go index 80b6685dee..2f835127c5 100644 --- a/cmd/npmExecuteTests.go +++ b/cmd/npmExecuteTests.go @@ -57,7 +57,7 @@ func runNpmExecuteTests(config *npmExecuteTestsOptions, c command.ExecRunner) er } for _, app := range apps { - credentialsToEnv(app.Username, app.Password, config.CredentialsEnvVarPrefix, c) + credentialsToEnv(app.Username, app.Password, config.UsernameEnvVar, config.PasswordEnvVar, c) err := runTestForUrl(app.URL, config, c) if err != nil { return err @@ -66,7 +66,7 @@ func runNpmExecuteTests(config *npmExecuteTestsOptions, c command.ExecRunner) er username := config.VaultMetadata["username"].(string) password := config.VaultMetadata["password"].(string) - credentialsToEnv(username, password, config.CredentialsEnvVarPrefix, c) + credentialsToEnv(username, password, config.UsernameEnvVar, config.PasswordEnvVar, c) if err := runTestForUrl(config.BaseURL, config, c); err != nil { return err } @@ -86,6 +86,9 @@ func runTestForUrl(url string, config *npmExecuteTestsOptions, command command.E return nil } -func credentialsToEnv(username, password, prefix string, c command.ExecRunner) { - c.SetEnv([]string{prefix + "_username=" + username, prefix + "_password=" + password}) +func credentialsToEnv(username, password, usernameEnv, passwordEnv string, c command.ExecRunner) { + if username == "" || password == "" { + return + } + c.SetEnv([]string{usernameEnv + "=" + username, passwordEnv + "=" + password}) } diff --git a/cmd/npmExecuteTests_generated.go b/cmd/npmExecuteTests_generated.go index 42cdf3c02f..2c7167127b 100644 --- a/cmd/npmExecuteTests_generated.go +++ b/cmd/npmExecuteTests_generated.go @@ -20,14 +20,15 @@ import ( ) type npmExecuteTestsOptions struct { - InstallCommand string `json:"installCommand,omitempty"` - RunCommand string `json:"runCommand,omitempty"` - VaultMetadata map[string]interface{} `json:"vaultMetadata,omitempty"` - BaseURL string `json:"baseUrl,omitempty"` - CredentialsEnvVarPrefix string `json:"credentialsEnvVarPrefix,omitempty"` - UrlOptionPrefix string `json:"urlOptionPrefix,omitempty"` - EnvVars []string `json:"envVars,omitempty"` - Paths []string `json:"paths,omitempty"` + InstallCommand string `json:"installCommand,omitempty"` + RunCommand string `json:"runCommand,omitempty"` + VaultMetadata map[string]interface{} `json:"vaultMetadata,omitempty"` + BaseURL string `json:"baseUrl,omitempty"` + UsernameEnvVar string `json:"usernameEnvVar,omitempty"` + PasswordEnvVar string `json:"passwordEnvVar,omitempty"` + UrlOptionPrefix string `json:"urlOptionPrefix,omitempty"` + EnvVars []string `json:"envVars,omitempty"` + Paths []string `json:"paths,omitempty"` } type npmExecuteTestsReports struct { @@ -177,7 +178,8 @@ func addNpmExecuteTestsFlags(cmd *cobra.Command, stepConfig *npmExecuteTestsOpti cmd.Flags().StringVar(&stepConfig.RunCommand, "runCommand", `npm run wdi5`, "Command to be executed for running tests`.") cmd.Flags().StringVar(&stepConfig.BaseURL, "baseUrl", `http://localhost:8080/index.html`, "Base URL of the application to be tested.") - cmd.Flags().StringVar(&stepConfig.CredentialsEnvVarPrefix, "credentialsEnvVarPrefix", `wdi5`, "Prefix for username and password env vars.") + cmd.Flags().StringVar(&stepConfig.UsernameEnvVar, "usernameEnvVar", `wdi5_username`, "Env var for username.") + cmd.Flags().StringVar(&stepConfig.PasswordEnvVar, "passwordEnvVar", `wdi5_password`, "Env var for password.") cmd.Flags().StringVar(&stepConfig.UrlOptionPrefix, "urlOptionPrefix", os.Getenv("PIPER_urlOptionPrefix"), "If you want to specify an extra option that the tested url it appended to.\nFor example if the test URL is `http://localhost and urlOptionPrefix is `--base-url=`,\nwe'll add `--base-url=http://localhost` to your runScript.\n") cmd.Flags().StringSliceVar(&stepConfig.EnvVars, "envVars", []string{}, "List of environment variables to be set") cmd.Flags().StringSliceVar(&stepConfig.Paths, "paths", []string{}, "List of paths to be added to $PATH") @@ -238,13 +240,22 @@ func npmExecuteTestsMetadata() config.StepData { Default: `http://localhost:8080/index.html`, }, { - Name: "credentialsEnvVarPrefix", + Name: "usernameEnvVar", ResourceRef: []config.ResourceReference{}, Scope: []string{}, Type: "string", Mandatory: false, Aliases: []config.Alias{}, - Default: `wdi5`, + Default: `wdi5_username`, + }, + { + Name: "passwordEnvVar", + ResourceRef: []config.ResourceReference{}, + Scope: []string{}, + Type: "string", + Mandatory: false, + Aliases: []config.Alias{}, + Default: `wdi5_password`, }, { Name: "urlOptionPrefix", diff --git a/resources/metadata/npmExecuteTests.yaml b/resources/metadata/npmExecuteTests.yaml index 66066fad0d..a809af7d8c 100644 --- a/resources/metadata/npmExecuteTests.yaml +++ b/resources/metadata/npmExecuteTests.yaml @@ -57,10 +57,14 @@ spec: - PARAMETERS - STAGES - STEPS - - name: credentialsEnvVarPrefix + - name: usernameEnvVar type: string - default: "wdi5" - description: Prefix for username and password env vars. + default: "wdi5_username" + description: Env var for username. + - name: passwordEnvVar + type: string + default: "wdi5_password" + description: Env var for password. - name: urlOptionPrefix type: string description: | From cf6af6f2a2a13dcd365fb665a03a1f517de9032a Mon Sep 17 00:00:00 2001 From: Philip Germanov Date: Thu, 10 Oct 2024 14:53:28 +0300 Subject: [PATCH 28/39] refactor: rename envVars to envs --- cmd/npmExecuteTests.go | 4 ++-- cmd/npmExecuteTests_generated.go | 6 +++--- resources/metadata/npmExecuteTests.yaml | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/cmd/npmExecuteTests.go b/cmd/npmExecuteTests.go index 2f835127c5..8da87c546a 100644 --- a/cmd/npmExecuteTests.go +++ b/cmd/npmExecuteTests.go @@ -42,8 +42,8 @@ func runNpmExecuteTests(config *npmExecuteTestsOptions, c command.ExecRunner) er } } - if len(config.EnvVars) > 0 { - c.SetEnv(config.EnvVars) + if len(config.Envs) > 0 { + c.SetEnv(config.Envs) } if len(config.Paths) > 0 { diff --git a/cmd/npmExecuteTests_generated.go b/cmd/npmExecuteTests_generated.go index 2c7167127b..ca6857f395 100644 --- a/cmd/npmExecuteTests_generated.go +++ b/cmd/npmExecuteTests_generated.go @@ -27,7 +27,7 @@ type npmExecuteTestsOptions struct { UsernameEnvVar string `json:"usernameEnvVar,omitempty"` PasswordEnvVar string `json:"passwordEnvVar,omitempty"` UrlOptionPrefix string `json:"urlOptionPrefix,omitempty"` - EnvVars []string `json:"envVars,omitempty"` + Envs []string `json:"envs,omitempty"` Paths []string `json:"paths,omitempty"` } @@ -181,7 +181,7 @@ func addNpmExecuteTestsFlags(cmd *cobra.Command, stepConfig *npmExecuteTestsOpti cmd.Flags().StringVar(&stepConfig.UsernameEnvVar, "usernameEnvVar", `wdi5_username`, "Env var for username.") cmd.Flags().StringVar(&stepConfig.PasswordEnvVar, "passwordEnvVar", `wdi5_password`, "Env var for password.") cmd.Flags().StringVar(&stepConfig.UrlOptionPrefix, "urlOptionPrefix", os.Getenv("PIPER_urlOptionPrefix"), "If you want to specify an extra option that the tested url it appended to.\nFor example if the test URL is `http://localhost and urlOptionPrefix is `--base-url=`,\nwe'll add `--base-url=http://localhost` to your runScript.\n") - cmd.Flags().StringSliceVar(&stepConfig.EnvVars, "envVars", []string{}, "List of environment variables to be set") + cmd.Flags().StringSliceVar(&stepConfig.Envs, "envs", []string{}, "List of environment variables to be set") cmd.Flags().StringSliceVar(&stepConfig.Paths, "paths", []string{}, "List of paths to be added to $PATH") cmd.MarkFlagRequired("runCommand") @@ -267,7 +267,7 @@ func npmExecuteTestsMetadata() config.StepData { Default: os.Getenv("PIPER_urlOptionPrefix"), }, { - Name: "envVars", + Name: "envs", ResourceRef: []config.ResourceReference{}, Scope: []string{}, Type: "[]string", diff --git a/resources/metadata/npmExecuteTests.yaml b/resources/metadata/npmExecuteTests.yaml index a809af7d8c..27d1190e97 100644 --- a/resources/metadata/npmExecuteTests.yaml +++ b/resources/metadata/npmExecuteTests.yaml @@ -71,7 +71,7 @@ spec: If you want to specify an extra option that the tested url it appended to. For example if the test URL is `http://localhost and urlOptionPrefix is `--base-url=`, we'll add `--base-url=http://localhost` to your runScript. - - name: envVars + - name: envs type: "[]string" description: List of environment variables to be set - name: paths From 5dc002b15656fc4a2238bdbc11e65de3c5bd4bcc Mon Sep 17 00:00:00 2001 From: Philip Germanov Date: Thu, 10 Oct 2024 14:54:02 +0300 Subject: [PATCH 29/39] docs: add documentation for npmExecuteTests step --- documentation/docs/steps/npmExecuteTests.md | 113 ++++++++++++++++++++ 1 file changed, 113 insertions(+) create mode 100644 documentation/docs/steps/npmExecuteTests.md diff --git a/documentation/docs/steps/npmExecuteTests.md b/documentation/docs/steps/npmExecuteTests.md new file mode 100644 index 0000000000..f93d33bcff --- /dev/null +++ b/documentation/docs/steps/npmExecuteTests.md @@ -0,0 +1,113 @@ +# ${docGenStepName} + +## ${docGenDescription} + +## ${docGenParameters} + +## ${docGenConfiguration} + +## Examples + +### Simple example using wdi5 + +```yaml +stages: + - name: Test + steps: + - name: npmExecuteTests + type: npmExecuteTests + params: + baseUrl: "http://example.com/index.html" +``` + +This will run your wdi5 tests with the given baseUrl. + +### Advanced example using custom test script with credentials using Vault + +```yaml +stages: + - name: Test + steps: + - name: npmExecuteTests + type: npmExecuteTests + params: + installCommand: "npm install" + runCommand: "npm run custom-e2e-test" + usernameEnvVar: "e2e_username" + passwordEnvVar: "e2e_password" + baseUrl: "http://example.com/index.html" + urlOptionPrefix: "--base-url=" +``` + +and Vault configuration in PIPELINE-GROUP-/PIPELINE-/appMetadata + +```json +{ + "appSecrets": { + "urls": [ + { + "url": "http://one.example.com/index.html", + "username": "some-username1", + "password": "some-password1" + }, + { + "url": "http://two.example.com/index.html", + "username": "some-username2", + "password": "some-password2" + } + ], + "username": "base-url-username", + "password": "base-url-password" + } +} +``` + +This will run your custom install and run script for each URL from secrets and use the given URL like so: + +```shell +npm run custom-e2e-test --base-url=http://one.example.com/index.html +``` + +Each test run will have their own environment variables set: + +```shell +e2e_username=some-username1 +e2e_password=some-password1 +``` + +Environment variables are reset before each test run with their corresponding values from the secrets + +### Custom environment variables and $PATH + +```yaml +stages: + - name: Test + steps: + - name: npmExecuteTests + type: npmExecuteTests + params: + envs: + - "MY_ENV_VAR=value" + paths: + - "/path/to/add" +``` + +If you're running uiVeri5 tests, you might need to set additional environment variables or add paths to the $PATH variable. This can be done using the `envs` and `paths` parameters: + +```yaml +stages: + - name: Test + steps: + - name: npmExecuteTests + type: npmExecuteTests + params: + runCommand: "/home/node/.npm-global/bin/uiveri5" + installCommand: "npm install @ui5/uiveri5 --global --quiet" + runOptions: ["--seleniumAddress=http://localhost:4444/wd/hub"] + usernameEnvVar: "PIPER_SELENIUM_HUB_USER" + passwordEnvVar: "PIPER_SELENIUM_HUB_PASSWORD" + envs: + - "NPM_CONFIG_PREFIX=~/.npm-global" + paths: + - "~/.npm-global/bin" +``` From 17175ff365ffec249089de390eebe4d65a7cd825 Mon Sep 17 00:00:00 2001 From: Philip Germanov Date: Thu, 10 Oct 2024 17:56:59 +0300 Subject: [PATCH 30/39] feat: better handle vault secrets --- cmd/npmExecuteTests.go | 102 ++++++++++++++++++++++++++---------- cmd/npmExecuteTests_test.go | 97 ++++++++++++++++++++++++++++++++++ 2 files changed, 170 insertions(+), 29 deletions(-) diff --git a/cmd/npmExecuteTests.go b/cmd/npmExecuteTests.go index 8da87c546a..47656dfa3b 100644 --- a/cmd/npmExecuteTests.go +++ b/cmd/npmExecuteTests.go @@ -10,6 +10,18 @@ import ( "github.com/SAP/jenkins-library/pkg/telemetry" ) +type parsedMetadata struct { + GlobalUsername string + GlobalPassword string + URLs []appUrl +} + +type appUrl struct { + URL string `json:"url"` + Username string `json:"username,omitempty"` + Password string `json:"password,omitempty"` +} + func npmExecuteTests(config npmExecuteTestsOptions, _ *telemetry.CustomData) { c := command.Command{} @@ -22,26 +34,6 @@ func npmExecuteTests(config npmExecuteTestsOptions, _ *telemetry.CustomData) { } func runNpmExecuteTests(config *npmExecuteTestsOptions, c command.ExecRunner) error { - type AppUnderTest struct { - URL string `json:"url"` - Username string `json:"username"` - Password string `json:"password"` - } - - apps := []AppUnderTest{} - urlsRaw, ok := config.VaultMetadata["urls"].([]interface{}) - if ok { - for _, urlRaw := range urlsRaw { - urlMap := urlRaw.(map[string]interface{}) - app := AppUnderTest{ - URL: urlMap["url"].(string), - Username: urlMap["username"].(string), - Password: urlMap["password"].(string), - } - apps = append(apps, app) - } - } - if len(config.Envs) > 0 { c.SetEnv(config.Envs) } @@ -56,26 +48,27 @@ func runNpmExecuteTests(config *npmExecuteTestsOptions, c command.ExecRunner) er return fmt.Errorf("failed to execute install command: %w", err) } - for _, app := range apps { - credentialsToEnv(app.Username, app.Password, config.UsernameEnvVar, config.PasswordEnvVar, c) - err := runTestForUrl(app.URL, config, c) - if err != nil { + parsedMetadata, err := parseMetadata(config.VaultMetadata) + if err != nil { + return err + } + + for _, app := range parsedMetadata.URLs { + if err := runTestForUrl(app.URL, app.Username, app.Password, config, c); err != nil { return err } } - username := config.VaultMetadata["username"].(string) - password := config.VaultMetadata["password"].(string) - credentialsToEnv(username, password, config.UsernameEnvVar, config.PasswordEnvVar, c) - if err := runTestForUrl(config.BaseURL, config, c); err != nil { + if err := runTestForUrl(config.BaseURL, parsedMetadata.GlobalUsername, parsedMetadata.GlobalPassword, config, c); err != nil { return err } return nil } -func runTestForUrl(url string, config *npmExecuteTestsOptions, command command.ExecRunner) error { +func runTestForUrl(url, username, password string, config *npmExecuteTestsOptions, command command.ExecRunner) error { log.Entry().Infof("Running end to end tests for URL: %s", url) + credentialsToEnv(username, password, config.UsernameEnvVar, config.PasswordEnvVar, command) runScriptTokens := strings.Fields(config.RunCommand) if config.UrlOptionPrefix != "" { runScriptTokens = append(runScriptTokens, config.UrlOptionPrefix+url) @@ -83,12 +76,63 @@ func runTestForUrl(url string, config *npmExecuteTestsOptions, command command.E if err := command.RunExecutable(runScriptTokens[0], runScriptTokens[1:]...); err != nil { return fmt.Errorf("failed to execute npm script: %w", err) } + + // we need to reset the env vars as the next test might not have any credentials + resetCredentials(config.UsernameEnvVar, config.PasswordEnvVar, command) return nil } +func parseMetadata(metadata map[string]interface{}) (*parsedMetadata, error) { + parsedMetadata := &parsedMetadata{ + URLs: []appUrl{}, + } + + if metadata != nil { + if urls, ok := metadata["urls"].([]interface{}); ok { + for _, url := range urls { + urlMap, ok := url.(map[string]interface{}) + if !ok { + return nil, fmt.Errorf("failed to parse vault metadata: 'urls' entry is not a map") + } + + app := appUrl{} + if u, ok := urlMap["url"].(string); ok { + app.URL = u + } else { + return nil, fmt.Errorf("failed to parse vault metadata: 'url' field is not a string") + } + + if username, ok := urlMap["username"].(string); ok { + app.Username = username + } + + if password, ok := urlMap["password"].(string); ok { + app.Password = password + } + + parsedMetadata.URLs = append(parsedMetadata.URLs, app) + } + } + + if username, ok := metadata["username"].(string); ok { + parsedMetadata.GlobalUsername = username + } + if password, ok := metadata["password"].(string); ok { + parsedMetadata.GlobalPassword = password + } + } + + return parsedMetadata, nil +} + func credentialsToEnv(username, password, usernameEnv, passwordEnv string, c command.ExecRunner) { if username == "" || password == "" { + log.Entry().Warnf("Missing credentials: username: %s, password: %s", username, password) return } c.SetEnv([]string{usernameEnv + "=" + username, passwordEnv + "=" + password}) } + +func resetCredentials(usernameEnv, passwordEnv string, c command.ExecRunner) { + c.SetEnv([]string{usernameEnv + "=", passwordEnv + "="}) +} diff --git a/cmd/npmExecuteTests_test.go b/cmd/npmExecuteTests_test.go index 6701e97996..b6ebffa5f5 100644 --- a/cmd/npmExecuteTests_test.go +++ b/cmd/npmExecuteTests_test.go @@ -14,3 +14,100 @@ func TestRunNpmExecuteTests(t *testing.T) { // only high level testing performed - details are tested in step generation procedure assert.Equal(t, "npmExecuteTests", testCmd.Use, "command name incorrect") } + +func TestParseMetadata(t *testing.T) { + tests := []struct { + input map[string]interface{} + expected *parsedMetadata + name string + wantErr bool + }{ + { + name: "Valid metadata with URLs", + input: map[string]interface{}{ + "urls": []interface{}{ + map[string]interface{}{ + "url": "http://example.com", + "username": "user1", + "password": "pass1", + }, + map[string]interface{}{ + "url": "http://example2.com", + }, + }, + "username": "globalUser", + "password": "globalPass", + }, + expected: &parsedMetadata{ + GlobalUsername: "globalUser", + GlobalPassword: "globalPass", + URLs: []appUrl{ + { + URL: "http://example.com", + Username: "user1", + Password: "pass1", + }, + { + URL: "http://example2.com", + }, + }, + }, + wantErr: false, + }, + { + name: "Valid metadata without URLs", + input: map[string]interface{}{ + "username": "globalUser", + "password": "globalPass", + }, + expected: &parsedMetadata{ + GlobalUsername: "globalUser", + GlobalPassword: "globalPass", + URLs: []appUrl{}, + }, + wantErr: false, + }, + { + name: "Invalid URL entry", + input: map[string]interface{}{ + "urls": []interface{}{ + "invalidEntry", + }, + }, + expected: nil, + wantErr: true, + }, + { + name: "Invalid URL field type", + input: map[string]interface{}{ + "urls": []interface{}{ + map[string]interface{}{ + "url": 123, + }, + }, + }, + expected: nil, + wantErr: true, + }, + { + name: "Empty metadata", + input: map[string]interface{}{}, + expected: &parsedMetadata{ + URLs: []appUrl{}, + }, + wantErr: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := parseMetadata(tt.input) + if tt.wantErr { + assert.Error(t, err) + } else { + assert.NoError(t, err) + } + assert.Equal(t, tt.expected, got) + }) + } +} From 3d9c03820081b6db8d239710832ab7fbab517e10 Mon Sep 17 00:00:00 2001 From: Philip Germanov Date: Fri, 11 Oct 2024 15:08:47 +0300 Subject: [PATCH 31/39] refactor: flatten vault secrets --- cmd/npmExecuteTests.go | 65 +++++---------- cmd/npmExecuteTests_generated.go | 56 ++++++++++--- cmd/npmExecuteTests_test.go | 87 ++++++++------------- documentation/docs/steps/npmExecuteTests.md | 30 ++++--- resources/metadata/npmExecuteTests.yaml | 39 ++++++--- 5 files changed, 138 insertions(+), 139 deletions(-) diff --git a/cmd/npmExecuteTests.go b/cmd/npmExecuteTests.go index 47656dfa3b..f9d78d04a3 100644 --- a/cmd/npmExecuteTests.go +++ b/cmd/npmExecuteTests.go @@ -10,13 +10,7 @@ import ( "github.com/SAP/jenkins-library/pkg/telemetry" ) -type parsedMetadata struct { - GlobalUsername string - GlobalPassword string - URLs []appUrl -} - -type appUrl struct { +type vaultUrl struct { URL string `json:"url"` Username string `json:"username,omitempty"` Password string `json:"password,omitempty"` @@ -48,18 +42,18 @@ func runNpmExecuteTests(config *npmExecuteTestsOptions, c command.ExecRunner) er return fmt.Errorf("failed to execute install command: %w", err) } - parsedMetadata, err := parseMetadata(config.VaultMetadata) + parsedURLs, err := parseURLs(config.VaultURLs) if err != nil { return err } - for _, app := range parsedMetadata.URLs { + for _, app := range parsedURLs { if err := runTestForUrl(app.URL, app.Username, app.Password, config, c); err != nil { return err } } - if err := runTestForUrl(config.BaseURL, parsedMetadata.GlobalUsername, parsedMetadata.GlobalPassword, config, c); err != nil { + if err := runTestForUrl(config.BaseURL, config.VaultUsername, config.VaultPassword, config, c); err != nil { return err } return nil @@ -82,47 +76,26 @@ func runTestForUrl(url, username, password string, config *npmExecuteTestsOption return nil } -func parseMetadata(metadata map[string]interface{}) (*parsedMetadata, error) { - parsedMetadata := &parsedMetadata{ - URLs: []appUrl{}, - } +func parseURLs(urls []map[string]interface{}) ([]vaultUrl, error) { + parsedUrls := []vaultUrl{} - if metadata != nil { - if urls, ok := metadata["urls"].([]interface{}); ok { - for _, url := range urls { - urlMap, ok := url.(map[string]interface{}) - if !ok { - return nil, fmt.Errorf("failed to parse vault metadata: 'urls' entry is not a map") - } - - app := appUrl{} - if u, ok := urlMap["url"].(string); ok { - app.URL = u - } else { - return nil, fmt.Errorf("failed to parse vault metadata: 'url' field is not a string") - } - - if username, ok := urlMap["username"].(string); ok { - app.Username = username - } - - if password, ok := urlMap["password"].(string); ok { - app.Password = password - } - - parsedMetadata.URLs = append(parsedMetadata.URLs, app) - } + for _, url := range urls { + parsedUrl := vaultUrl{} + urlStr, ok := url["url"].(string) + if !ok { + return nil, fmt.Errorf("url field is not a string") } - - if username, ok := metadata["username"].(string); ok { - parsedMetadata.GlobalUsername = username + parsedUrl.URL = urlStr + if username, ok := url["username"].(string); ok { + parsedUrl.Username = username } - if password, ok := metadata["password"].(string); ok { - parsedMetadata.GlobalPassword = password + + if password, ok := url["password"].(string); ok { + parsedUrl.Password = password } + parsedUrls = append(parsedUrls, parsedUrl) } - - return parsedMetadata, nil + return parsedUrls, nil } func credentialsToEnv(username, password, usernameEnv, passwordEnv string, c command.ExecRunner) { diff --git a/cmd/npmExecuteTests_generated.go b/cmd/npmExecuteTests_generated.go index ca6857f395..4f88d4123c 100644 --- a/cmd/npmExecuteTests_generated.go +++ b/cmd/npmExecuteTests_generated.go @@ -20,15 +20,17 @@ import ( ) type npmExecuteTestsOptions struct { - InstallCommand string `json:"installCommand,omitempty"` - RunCommand string `json:"runCommand,omitempty"` - VaultMetadata map[string]interface{} `json:"vaultMetadata,omitempty"` - BaseURL string `json:"baseUrl,omitempty"` - UsernameEnvVar string `json:"usernameEnvVar,omitempty"` - PasswordEnvVar string `json:"passwordEnvVar,omitempty"` - UrlOptionPrefix string `json:"urlOptionPrefix,omitempty"` - Envs []string `json:"envs,omitempty"` - Paths []string `json:"paths,omitempty"` + InstallCommand string `json:"installCommand,omitempty"` + RunCommand string `json:"runCommand,omitempty"` + VaultURLs []map[string]interface{} `json:"vaultURLs,omitempty"` + VaultUsername string `json:"vaultUsername,omitempty"` + VaultPassword string `json:"vaultPassword,omitempty"` + BaseURL string `json:"baseUrl,omitempty"` + UsernameEnvVar string `json:"usernameEnvVar,omitempty"` + PasswordEnvVar string `json:"passwordEnvVar,omitempty"` + UrlOptionPrefix string `json:"urlOptionPrefix,omitempty"` + Envs []string `json:"envs,omitempty"` + Paths []string `json:"paths,omitempty"` } type npmExecuteTestsReports struct { @@ -177,6 +179,8 @@ func addNpmExecuteTestsFlags(cmd *cobra.Command, stepConfig *npmExecuteTestsOpti cmd.Flags().StringVar(&stepConfig.InstallCommand, "installCommand", `npm ci`, "Command to be executed for installation`.") cmd.Flags().StringVar(&stepConfig.RunCommand, "runCommand", `npm run wdi5`, "Command to be executed for running tests`.") + cmd.Flags().StringVar(&stepConfig.VaultUsername, "vaultUsername", os.Getenv("PIPER_vaultUsername"), "The base URL username.") + cmd.Flags().StringVar(&stepConfig.VaultPassword, "vaultPassword", os.Getenv("PIPER_vaultPassword"), "The base URL password.") cmd.Flags().StringVar(&stepConfig.BaseURL, "baseUrl", `http://localhost:8080/index.html`, "Base URL of the application to be tested.") cmd.Flags().StringVar(&stepConfig.UsernameEnvVar, "usernameEnvVar", `wdi5_username`, "Env var for username.") cmd.Flags().StringVar(&stepConfig.PasswordEnvVar, "passwordEnvVar", `wdi5_password`, "Env var for password.") @@ -217,7 +221,7 @@ func npmExecuteTestsMetadata() config.StepData { Default: `npm run wdi5`, }, { - Name: "vaultMetadata", + Name: "vaultURLs", ResourceRef: []config.ResourceReference{ { Name: "appMetadataVaultSecretName", @@ -226,10 +230,40 @@ func npmExecuteTestsMetadata() config.StepData { }, }, Scope: []string{"PARAMETERS", "STAGES", "STEPS"}, - Type: "map[string]interface{}", + Type: "[]map[string]interface{}", Mandatory: false, Aliases: []config.Alias{}, }, + { + Name: "vaultUsername", + ResourceRef: []config.ResourceReference{ + { + Name: "appMetadataVaultSecretName", + Type: "vaultSecret", + Default: "appMetadata", + }, + }, + Scope: []string{"PARAMETERS", "STAGES", "STEPS"}, + Type: "string", + Mandatory: false, + Aliases: []config.Alias{}, + Default: os.Getenv("PIPER_vaultUsername"), + }, + { + Name: "vaultPassword", + ResourceRef: []config.ResourceReference{ + { + Name: "appMetadataVaultSecretName", + Type: "vaultSecret", + Default: "appMetadata", + }, + }, + Scope: []string{"PARAMETERS", "STAGES", "STEPS"}, + Type: "string", + Mandatory: false, + Aliases: []config.Alias{}, + Default: os.Getenv("PIPER_vaultPassword"), + }, { Name: "baseUrl", ResourceRef: []config.ResourceReference{}, diff --git a/cmd/npmExecuteTests_test.go b/cmd/npmExecuteTests_test.go index b6ebffa5f5..bd044ead68 100644 --- a/cmd/npmExecuteTests_test.go +++ b/cmd/npmExecuteTests_test.go @@ -15,63 +15,42 @@ func TestRunNpmExecuteTests(t *testing.T) { assert.Equal(t, "npmExecuteTests", testCmd.Use, "command name incorrect") } -func TestParseMetadata(t *testing.T) { +func TestParseURLs(t *testing.T) { tests := []struct { - input map[string]interface{} - expected *parsedMetadata name string + input []map[string]interface{} + expected []vaultUrl wantErr bool }{ { - name: "Valid metadata with URLs", - input: map[string]interface{}{ - "urls": []interface{}{ - map[string]interface{}{ - "url": "http://example.com", - "username": "user1", - "password": "pass1", - }, - map[string]interface{}{ - "url": "http://example2.com", - }, + name: "Valid URLs", + input: []map[string]interface{}{ + { + "url": "http://example.com", + "username": "user1", + "password": "pass1", }, - "username": "globalUser", - "password": "globalPass", - }, - expected: &parsedMetadata{ - GlobalUsername: "globalUser", - GlobalPassword: "globalPass", - URLs: []appUrl{ - { - URL: "http://example.com", - Username: "user1", - Password: "pass1", - }, - { - URL: "http://example2.com", - }, + { + "url": "http://example2.com", }, }, - wantErr: false, - }, - { - name: "Valid metadata without URLs", - input: map[string]interface{}{ - "username": "globalUser", - "password": "globalPass", - }, - expected: &parsedMetadata{ - GlobalUsername: "globalUser", - GlobalPassword: "globalPass", - URLs: []appUrl{}, + expected: []vaultUrl{ + { + URL: "http://example.com", + Username: "user1", + Password: "pass1", + }, + { + URL: "http://example2.com", + }, }, wantErr: false, }, { name: "Invalid URL entry", - input: map[string]interface{}{ - "urls": []interface{}{ - "invalidEntry", + input: []map[string]interface{}{ + { + "username": "user1", }, }, expected: nil, @@ -79,29 +58,25 @@ func TestParseMetadata(t *testing.T) { }, { name: "Invalid URL field type", - input: map[string]interface{}{ - "urls": []interface{}{ - map[string]interface{}{ - "url": 123, - }, + input: []map[string]interface{}{ + { + "url": 123, }, }, expected: nil, wantErr: true, }, { - name: "Empty metadata", - input: map[string]interface{}{}, - expected: &parsedMetadata{ - URLs: []appUrl{}, - }, - wantErr: false, + name: "Empty URLs", + input: []map[string]interface{}{}, + expected: []vaultUrl{}, + wantErr: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - got, err := parseMetadata(tt.input) + got, err := parseURLs(tt.input) if tt.wantErr { assert.Error(t, err) } else { diff --git a/documentation/docs/steps/npmExecuteTests.md b/documentation/docs/steps/npmExecuteTests.md index f93d33bcff..8139654d82 100644 --- a/documentation/docs/steps/npmExecuteTests.md +++ b/documentation/docs/steps/npmExecuteTests.md @@ -43,22 +43,20 @@ and Vault configuration in PIPELINE-GROUP-/PIPELINE-/appMetadata ```json { - "appSecrets": { - "urls": [ - { - "url": "http://one.example.com/index.html", - "username": "some-username1", - "password": "some-password1" - }, - { - "url": "http://two.example.com/index.html", - "username": "some-username2", - "password": "some-password2" - } - ], - "username": "base-url-username", - "password": "base-url-password" - } + "vaultURLs": [ + { + "url": "http://one.example.com/index.html", + "username": "some-username1", + "password": "some-password1" + }, + { + "url": "http://two.example.com/index.html", + "username": "some-username2", + "password": "some-password2" + } + ], + "vaultUsername": "base-url-username", + "vaultPassword": "base-url-password" } ``` diff --git a/resources/metadata/npmExecuteTests.yaml b/resources/metadata/npmExecuteTests.yaml index 27d1190e97..1119691207 100644 --- a/resources/metadata/npmExecuteTests.yaml +++ b/resources/metadata/npmExecuteTests.yaml @@ -30,17 +30,36 @@ spec: - STEPS mandatory: true default: "npm run wdi5" - - name: vaultMetadata - type: map[string]interface{} + - name: vaultURLs + type: "[]map[string]interface{}" description: | - The object must have the following structure: - - `urls`: An array of objects, each representing an application URL with associated credentials. - Each object must have the following properties: - - `url`: The URL of the application. - - `username`: The username for accessing the application. - - `password`: The password for accessing the application. - - `username`: The base URL username. - - `password`: The base URL password. + An array of objects, each representing an application URL with associated credentials. + Each object must have the following properties: + - `url`: The URL of the application. + - `username`: The username for accessing the application. + - `password`: The password for accessing the application. + scope: + - PARAMETERS + - STAGES + - STEPS + resourceRef: + - type: vaultSecret + default: appMetadata + name: appMetadataVaultSecretName + - name: vaultUsername + type: "string" + description: The base URL username. + scope: + - PARAMETERS + - STAGES + - STEPS + resourceRef: + - type: vaultSecret + default: appMetadata + name: appMetadataVaultSecretName + - name: vaultPassword + type: "string" + description: The base URL password. scope: - PARAMETERS - STAGES From 0f8f9c351ea5ac106db99dda67841ab17cc6fe25 Mon Sep 17 00:00:00 2001 From: Philip Germanov Date: Fri, 11 Oct 2024 16:16:35 +0300 Subject: [PATCH 32/39] docs: add npmExecuteTests docs to mkdocs --- documentation/mkdocs.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/documentation/mkdocs.yml b/documentation/mkdocs.yml index c7e3674b75..953bfb2c94 100644 --- a/documentation/mkdocs.yml +++ b/documentation/mkdocs.yml @@ -155,6 +155,7 @@ nav: - npmExecuteEndToEndTests: steps/npmExecuteEndToEndTests.md - npmExecuteLint: steps/npmExecuteLint.md - npmExecuteScripts: steps/npmExecuteScripts.md + - npmExecuteTests: steps/npmExecuteTests.md - pipelineExecute: steps/pipelineExecute.md - pipelineRestartSteps: steps/pipelineRestartSteps.md - pipelineStashFiles: steps/pipelineStashFiles.md From 1a682fc99bde58950157d00156f076a859602a52 Mon Sep 17 00:00:00 2001 From: Philip Germanov Date: Fri, 11 Oct 2024 17:06:07 +0300 Subject: [PATCH 33/39] feat: add npmExecuteTests to piper cmd --- cmd/piper.go | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/cmd/piper.go b/cmd/piper.go index 65741c1d70..ae1d19568d 100644 --- a/cmd/piper.go +++ b/cmd/piper.go @@ -24,14 +24,14 @@ type GeneralConfigOptions struct { CorrelationID string CustomConfig string GitHubTokens []string // list of entries in form of : to allow token authentication for downloading config / defaults - DefaultConfig []string //ordered list of Piper default configurations. Can be filePath or ENV containing JSON in format 'ENV:MY_ENV_VAR' + DefaultConfig []string // ordered list of Piper default configurations. Can be filePath or ENV containing JSON in format 'ENV:MY_ENV_VAR' IgnoreCustomDefaults bool ParametersJSON string EnvRootPath string NoTelemetry bool StageName string StepConfigJSON string - StepMetadata string //metadata to be considered, can be filePath or ENV containing JSON in format 'ENV:MY_ENV_VAR' + StepMetadata string // metadata to be considered, can be filePath or ENV containing JSON in format 'ENV:MY_ENV_VAR' StepName string Verbose bool LogFormat string @@ -152,6 +152,7 @@ func Execute() { rootCmd.AddCommand(AbapEnvironmentRunATCCheckCommand()) rootCmd.AddCommand(NpmExecuteScriptsCommand()) rootCmd.AddCommand(NpmExecuteLintCommand()) + rootCmd.AddCommand(NpmExecuteTestsCommand()) rootCmd.AddCommand(GctsCreateRepositoryCommand()) rootCmd.AddCommand(GctsExecuteABAPQualityChecksCommand()) rootCmd.AddCommand(GctsExecuteABAPUnitTestsCommand()) @@ -260,7 +261,6 @@ func addRootFlags(rootCmd *cobra.Command) { rootCmd.PersistentFlags().StringVar(&GeneralConfig.GCSFolderPath, "gcsFolderPath", "", "GCS folder path. One of the components of GCS target folder") rootCmd.PersistentFlags().StringVar(&GeneralConfig.GCSBucketId, "gcsBucketId", "", "Bucket name for Google Cloud Storage") rootCmd.PersistentFlags().StringVar(&GeneralConfig.GCSSubFolder, "gcsSubFolder", "", "Used to logically separate results of the same step result type") - } // ResolveAccessTokens reads a list of tokens in format host:token passed via command line @@ -344,7 +344,6 @@ func initStageName(outputToLog bool) { // PrepareConfig reads step configuration from various sources and merges it (defaults, config file, flags, ...) func PrepareConfig(cmd *cobra.Command, metadata *config.StepData, stepName string, options interface{}, openFile func(s string, t map[string]string) (io.ReadCloser, error)) error { - log.SetFormatter(GeneralConfig.LogFormat) initStageName(true) @@ -389,7 +388,7 @@ func PrepareConfig(cmd *cobra.Command, metadata *config.StepData, stepName strin // use config & defaults var customConfig io.ReadCloser var err error - //accept that config file and defaults cannot be loaded since both are not mandatory here + // accept that config file and defaults cannot be loaded since both are not mandatory here { projectConfigFile := getProjectConfigFile(GeneralConfig.CustomConfig) if exists, err := piperutils.FileExists(projectConfigFile); exists { @@ -616,7 +615,6 @@ func getStepOptionsStructType(stepOptions interface{}) reflect.Type { } func getProjectConfigFile(name string) string { - var altName string if ext := filepath.Ext(name); ext == ".yml" { altName = fmt.Sprintf("%v.yaml", strings.TrimSuffix(name, ext)) From 23ac97584e69889eeb7f17d60fdfa613d9bb017d Mon Sep 17 00:00:00 2001 From: Philip Germanov Date: Mon, 14 Oct 2024 10:26:25 +0300 Subject: [PATCH 34/39] chore: go generate --- cmd/npmExecuteTests_generated.go | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/cmd/npmExecuteTests_generated.go b/cmd/npmExecuteTests_generated.go index 4f88d4123c..1fb5958352 100644 --- a/cmd/npmExecuteTests_generated.go +++ b/cmd/npmExecuteTests_generated.go @@ -10,6 +10,7 @@ import ( "time" "github.com/SAP/jenkins-library/pkg/config" + "github.com/SAP/jenkins-library/pkg/gcp" "github.com/SAP/jenkins-library/pkg/gcs" "github.com/SAP/jenkins-library/pkg/log" "github.com/SAP/jenkins-library/pkg/splunk" @@ -135,6 +136,11 @@ The tests can be restricted to run only on the productive branch by setting ` + return nil }, Run: func(_ *cobra.Command, _ []string) { + vaultClient := config.GlobalVaultClient() + if vaultClient != nil { + defer vaultClient.MustRevokeToken() + } + stepTelemetryData := telemetry.CustomData{} stepTelemetryData.ErrorCode = "1" handler := func() { @@ -161,6 +167,19 @@ The tests can be restricted to run only on the productive branch by setting ` + GeneralConfig.HookConfig.SplunkConfig.SendLogs) splunkClient.Send(telemetryClient.GetData(), logCollector) } + if GeneralConfig.HookConfig.GCPPubSubConfig.Enabled { + err := gcp.NewGcpPubsubClient( + vaultClient, + GeneralConfig.HookConfig.GCPPubSubConfig.ProjectNumber, + GeneralConfig.HookConfig.GCPPubSubConfig.IdentityPool, + GeneralConfig.HookConfig.GCPPubSubConfig.IdentityProvider, + GeneralConfig.CorrelationID, + GeneralConfig.HookConfig.OIDCConfig.RoleID, + ).Publish(GeneralConfig.HookConfig.GCPPubSubConfig.Topic, telemetryClient.GetDataBytes()) + if err != nil { + log.Entry().WithError(err).Warn("event publish failed") + } + } } log.DeferExitHandler(handler) defer handler() From e1a3653e0970c6336888ea6b97521ee84af19a23 Mon Sep 17 00:00:00 2001 From: Philip Germanov Date: Tue, 15 Oct 2024 17:58:06 +0300 Subject: [PATCH 35/39] fix: modify working dir --- resources/metadata/npmExecuteTests.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/metadata/npmExecuteTests.yaml b/resources/metadata/npmExecuteTests.yaml index 1119691207..2835ce92be 100644 --- a/resources/metadata/npmExecuteTests.yaml +++ b/resources/metadata/npmExecuteTests.yaml @@ -111,4 +111,4 @@ spec: value: ${{params.baseUrl}} - name: CREDENTIALS_ID value: ${{params.credentialsId}} - workingDir: /app + workingDir: /home/node From 970f34053d8294ec99b5cf86c5a6055eb6fcea72 Mon Sep 17 00:00:00 2001 From: Philip Germanov Date: Tue, 15 Oct 2024 18:16:21 +0300 Subject: [PATCH 36/39] fix: change npm cache dir --- cmd/npmExecuteTests.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/cmd/npmExecuteTests.go b/cmd/npmExecuteTests.go index f9d78d04a3..72e7d20335 100644 --- a/cmd/npmExecuteTests.go +++ b/cmd/npmExecuteTests.go @@ -37,6 +37,10 @@ func runNpmExecuteTests(config *npmExecuteTestsOptions, c command.ExecRunner) er c.SetEnv([]string{path}) } + if err := c.RunExecutable("npm", "config", "set", "cache", "~/.npm-cache", "--global"); err != nil { + return fmt.Errorf("failed to set npm cache directory: %w", err) + } + installCommandTokens := strings.Fields(config.InstallCommand) if err := c.RunExecutable(installCommandTokens[0], installCommandTokens[1:]...); err != nil { return fmt.Errorf("failed to execute install command: %w", err) From 803f18d8579e95948def081ac6a08dfe86e4bcca Mon Sep 17 00:00:00 2001 From: Philip Germanov Date: Tue, 15 Oct 2024 18:29:23 +0300 Subject: [PATCH 37/39] fix: use os home dir --- cmd/npmExecuteTests.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/cmd/npmExecuteTests.go b/cmd/npmExecuteTests.go index 72e7d20335..03673a8f6f 100644 --- a/cmd/npmExecuteTests.go +++ b/cmd/npmExecuteTests.go @@ -37,7 +37,12 @@ func runNpmExecuteTests(config *npmExecuteTestsOptions, c command.ExecRunner) er c.SetEnv([]string{path}) } - if err := c.RunExecutable("npm", "config", "set", "cache", "~/.npm-cache", "--global"); err != nil { + homeDir, err := os.UserHomeDir() + if err != nil { + return fmt.Errorf("failed to get user home directory: %w", err) + } + npmCacheDir := fmt.Sprintf("%s/.npm-cache", homeDir) + if err := c.RunExecutable("npm", "config", "set", "cache", npmCacheDir, "--global"); err != nil { return fmt.Errorf("failed to set npm cache directory: %w", err) } From bc61bf3daddba536fa895792d478c81cb424a588 Mon Sep 17 00:00:00 2001 From: Philip Germanov Date: Tue, 15 Oct 2024 18:51:51 +0300 Subject: [PATCH 38/39] fix: remove working dir --- resources/metadata/npmExecuteTests.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/resources/metadata/npmExecuteTests.yaml b/resources/metadata/npmExecuteTests.yaml index 2835ce92be..5bc4fc946a 100644 --- a/resources/metadata/npmExecuteTests.yaml +++ b/resources/metadata/npmExecuteTests.yaml @@ -111,4 +111,3 @@ spec: value: ${{params.baseUrl}} - name: CREDENTIALS_ID value: ${{params.credentialsId}} - workingDir: /home/node From 103f0e5c6d5c09975ee522ac1b358ef12ea3a5d7 Mon Sep 17 00:00:00 2001 From: Philip Germanov Date: Thu, 17 Oct 2024 11:22:21 +0300 Subject: [PATCH 39/39] fix: remove npm set cache --- cmd/npmExecuteTests.go | 9 -- cmd/npmExecuteTests_generated.go | 2 +- test.md | 153 +++++++++++++++++++++++++++++++ 3 files changed, 154 insertions(+), 10 deletions(-) create mode 100644 test.md diff --git a/cmd/npmExecuteTests.go b/cmd/npmExecuteTests.go index 03673a8f6f..f9d78d04a3 100644 --- a/cmd/npmExecuteTests.go +++ b/cmd/npmExecuteTests.go @@ -37,15 +37,6 @@ func runNpmExecuteTests(config *npmExecuteTestsOptions, c command.ExecRunner) er c.SetEnv([]string{path}) } - homeDir, err := os.UserHomeDir() - if err != nil { - return fmt.Errorf("failed to get user home directory: %w", err) - } - npmCacheDir := fmt.Sprintf("%s/.npm-cache", homeDir) - if err := c.RunExecutable("npm", "config", "set", "cache", npmCacheDir, "--global"); err != nil { - return fmt.Errorf("failed to set npm cache directory: %w", err) - } - installCommandTokens := strings.Fields(config.InstallCommand) if err := c.RunExecutable(installCommandTokens[0], installCommandTokens[1:]...); err != nil { return fmt.Errorf("failed to execute install command: %w", err) diff --git a/cmd/npmExecuteTests_generated.go b/cmd/npmExecuteTests_generated.go index 1fb5958352..841b774fb2 100644 --- a/cmd/npmExecuteTests_generated.go +++ b/cmd/npmExecuteTests_generated.go @@ -340,7 +340,7 @@ func npmExecuteTestsMetadata() config.StepData { }, }, Containers: []config.Container{ - {Name: "node", Image: "node:lts-bookworm", EnvVars: []config.EnvVar{{Name: "BASE_URL", Value: "${{params.baseUrl}}"}, {Name: "CREDENTIALS_ID", Value: "${{params.credentialsId}}"}}, WorkingDir: "/app"}, + {Name: "node", Image: "node:lts-bookworm", EnvVars: []config.EnvVar{{Name: "BASE_URL", Value: "${{params.baseUrl}}"}, {Name: "CREDENTIALS_ID", Value: "${{params.credentialsId}}"}}}, }, Outputs: config.StepOutputs{ Resources: []config.StepResources{ diff --git a/test.md b/test.md new file mode 100644 index 0000000000..f653826dd8 --- /dev/null +++ b/test.md @@ -0,0 +1,153 @@ +24: 24 + +25: 25 + +26: 26 + +27: 27 + +28: 28 + +29: 29 + +30: 30 + +31: 31 + +32: 32 + +33: 33 + +34: 34 + +35: 35 + +36: 36 + +37: 37 + +38: 38 + +39: 39 + +40: 40 + +41: 41 + +42: 42 + +43: 43 + +44: 44 + +45: 45 + +46: 46 + +47: 47 + +48: 48 + +49: 49 + +50: 50 + +51: 51 + +52: 52 + +53: 53 + +54: 54 + +55: 55 + +56: 56 + +57: 57 + +58: 58 + +59: 59 + +60: 60 + +61: 61 + +62: 62 + +63: 63 + +64: 64 + +65: 65 + +66: 66 + +67: 67 + +68: 68 + +69: 69 + +70: 70 + +71: 71 + +72: 72 + +73: 73 + +74: 74 + +75: 75 + +76: 76 + +77: 77 + +78: 78 + +79: 79 + +80: 80 + +81: 81 + +82: 82 + +83: 83 + +84: 84 + +85: 85 + +86: 86 + +87: 87 + +88: 88 + +89: 89 + +90: 90 + +91: 91 + +92: 92 + +93: 93 + +94: 94 + +95: 95 + +96: 96 + +97: 97 + +98: 98 + +99: 99 + +100: 100