From d9811c7c589015b84a30ed229e3a5b23ad45128e Mon Sep 17 00:00:00 2001 From: Luke Watts <luke@snyk.io> Date: Fri, 13 Dec 2024 11:50:36 +0100 Subject: [PATCH 1/2] spike: transfer data via filesystem for TS <=> Go CLI-646 --- cliv2/cmd/cliv2/main.go | 45 +++++++++++++++++++++----- cliv2/pkg/basic_workflows/legacycli.go | 5 ++- src/cli/commands/test/index.ts | 11 +------ src/cli/main.ts | 6 ++++ 4 files changed, 48 insertions(+), 19 deletions(-) diff --git a/cliv2/cmd/cliv2/main.go b/cliv2/cmd/cliv2/main.go index 0a02fb75c3..c6394cab7e 100644 --- a/cliv2/cmd/cliv2/main.go +++ b/cliv2/cmd/cliv2/main.go @@ -22,19 +22,19 @@ import ( "github.com/snyk/cli-extension-sbom/pkg/sbom" "github.com/snyk/cli/cliv2/internal/cliv2" "github.com/snyk/cli/cliv2/internal/constants" + cli_errors "github.com/snyk/cli/cliv2/internal/errors" + "github.com/snyk/cli/cliv2/pkg/basic_workflows" "github.com/snyk/container-cli/pkg/container" + "github.com/snyk/error-catalog-golang-public/snyk_errors" "github.com/snyk/go-application-framework/pkg/analytics" "github.com/snyk/go-application-framework/pkg/app" "github.com/snyk/go-application-framework/pkg/configuration" "github.com/snyk/go-application-framework/pkg/instrumentation" - "github.com/snyk/go-application-framework/pkg/local_workflows/network_utils" - "github.com/snyk/go-application-framework/pkg/local_workflows/output_workflow" - "github.com/spf13/cobra" - "github.com/spf13/pflag" - localworkflows "github.com/snyk/go-application-framework/pkg/local_workflows" "github.com/snyk/go-application-framework/pkg/local_workflows/content_type" "github.com/snyk/go-application-framework/pkg/local_workflows/json_schemas" + "github.com/snyk/go-application-framework/pkg/local_workflows/network_utils" + "github.com/snyk/go-application-framework/pkg/local_workflows/output_workflow" "github.com/snyk/go-application-framework/pkg/networking" "github.com/snyk/go-application-framework/pkg/runtimeinfo" "github.com/snyk/go-application-framework/pkg/ui" @@ -42,11 +42,10 @@ import ( "github.com/snyk/go-application-framework/pkg/workflow" "github.com/snyk/go-httpauth/pkg/httpauth" "github.com/snyk/snyk-iac-capture/pkg/capture" + "github.com/spf13/cobra" + "github.com/spf13/pflag" snykls "github.com/snyk/snyk-ls/ls_extension" - - cli_errors "github.com/snyk/cli/cliv2/internal/errors" - "github.com/snyk/cli/cliv2/pkg/basic_workflows" ) var internalOS string @@ -621,6 +620,36 @@ func MainWithErrorCode() int { cliAnalytics.GetInstrumentation().SetStatus(analytics.Failure) } + // Load error out of run-errors.json + filePath := globalConfiguration.GetString(configuration.TEMP_DIR_PATH) + "/typescript-runtime-errors" + + // Read the entire file into a byte slice + errDataFromFile, err := os.ReadFile(filePath) + if err != nil { + globalLogger.Printf("Failed to read file: %v", err) + } + + // Convert the byte slice to a string and print it + fmt.Println(`LADEBUG`) + fmt.Println(string(errDataFromFile)) + cliError := snyk_errors.Error{ + ID: uuid.NewString(), + Title: "Unable to set environment", + Description: "The specified environment cannot be used. As a result, the configuration remains unchanged.Provide the correct specifications for the environment and try again.", + StatusCode: 200, + ErrorCode: string(errDataFromFile), + Classification: "ACTIONABLE", + Links: []string{ + "https://docs.snyk.io/snyk-cli/commands/config-environment", + }, + Level: "error", + Detail: string(errDataFromFile), + } + cliAnalytics.GetInstrumentation().AddError( + cliError, + ) + // END error loading + if !globalConfiguration.GetBool(configuration.ANALYTICS_DISABLED) { sendAnalytics(cliAnalytics, globalLogger) } diff --git a/cliv2/pkg/basic_workflows/legacycli.go b/cliv2/pkg/basic_workflows/legacycli.go index bae4e7e1f9..41a2a95603 100644 --- a/cliv2/pkg/basic_workflows/legacycli.go +++ b/cliv2/pkg/basic_workflows/legacycli.go @@ -110,7 +110,10 @@ func legacycliWorkflow( if len(apiToken) == 0 { apiToken = "random" } - cli.AppendEnvironmentVariables([]string{constants.SNYK_API_TOKEN_ENV + "=" + apiToken}) + cli.AppendEnvironmentVariables([]string{ + constants.SNYK_API_TOKEN_ENV + "=" + apiToken, + "SNYK_TEMP_DIR_PATH=" + config.GetString(configuration.TEMP_DIR_PATH), + }) err = cli.Init() if err != nil { diff --git a/src/cli/commands/test/index.ts b/src/cli/commands/test/index.ts index 39a28b9f32..f3d0844cc4 100644 --- a/src/cli/commands/test/index.ts +++ b/src/cli/commands/test/index.ts @@ -295,16 +295,7 @@ export default async function test( if (notSuccess) { response += chalk.bold.red(summaryMessage); - const error = new Error(response) as any; - // take the code of the first problem to go through error - // translation - // HACK as there can be different errors, and we pass only the - // first one - error.code = errorResults[0].code; - error.userMessage = errorResults[0].userMessage; - error.strCode = errorResults[0].strCode; - error.innerError = errorResults[0].innerError; - throw error; + throw errorResults[0]; } if (foundVulnerabilities) { diff --git a/src/cli/main.ts b/src/cli/main.ts index b03d6f1d2c..e831a4895d 100755 --- a/src/cli/main.ts +++ b/src/cli/main.ts @@ -195,6 +195,12 @@ async function handleError(args, error) { analytics.add('error', true); analytics.add('command', args.command); } else { + const errorFilePath = pathLib.join(process.env.SNYK_TEMP_DIR_PATH || getFullPath(''), './typescript-runtime-errors'); + + // TODO: If error is NOT valid error catalog message transform to match structure + await saveJsonResultsToFile(JSON.stringify( + analyticsError, + ), errorFilePath); analytics.add('error-message', analyticsError.message); // Note that error.stack would also contain the error message // (see https://nodejs.org/api/errors.html#errors_error_stack) From 54a78f040eae8fef639983fe86d883ba0ab6bce5 Mon Sep 17 00:00:00 2001 From: CatalinSnyk <catalin.iuga@snyk.io> Date: Fri, 20 Dec 2024 17:04:22 +0200 Subject: [PATCH 2/2] chore: cleaning up main and moving stuff around --- cliv2/cmd/cliv2/main.go | 31 -------------------------- cliv2/internal/cliv2/cliv2.go | 18 +++++++++++++++ cliv2/pkg/basic_workflows/legacycli.go | 5 +---- src/cli/ipc.ts | 22 ++++++++++++++++++ src/cli/main.ts | 11 ++++----- 5 files changed, 45 insertions(+), 42 deletions(-) create mode 100644 src/cli/ipc.ts diff --git a/cliv2/cmd/cliv2/main.go b/cliv2/cmd/cliv2/main.go index c6394cab7e..97bc91ead8 100644 --- a/cliv2/cmd/cliv2/main.go +++ b/cliv2/cmd/cliv2/main.go @@ -25,7 +25,6 @@ import ( cli_errors "github.com/snyk/cli/cliv2/internal/errors" "github.com/snyk/cli/cliv2/pkg/basic_workflows" "github.com/snyk/container-cli/pkg/container" - "github.com/snyk/error-catalog-golang-public/snyk_errors" "github.com/snyk/go-application-framework/pkg/analytics" "github.com/snyk/go-application-framework/pkg/app" "github.com/snyk/go-application-framework/pkg/configuration" @@ -620,36 +619,6 @@ func MainWithErrorCode() int { cliAnalytics.GetInstrumentation().SetStatus(analytics.Failure) } - // Load error out of run-errors.json - filePath := globalConfiguration.GetString(configuration.TEMP_DIR_PATH) + "/typescript-runtime-errors" - - // Read the entire file into a byte slice - errDataFromFile, err := os.ReadFile(filePath) - if err != nil { - globalLogger.Printf("Failed to read file: %v", err) - } - - // Convert the byte slice to a string and print it - fmt.Println(`LADEBUG`) - fmt.Println(string(errDataFromFile)) - cliError := snyk_errors.Error{ - ID: uuid.NewString(), - Title: "Unable to set environment", - Description: "The specified environment cannot be used. As a result, the configuration remains unchanged.Provide the correct specifications for the environment and try again.", - StatusCode: 200, - ErrorCode: string(errDataFromFile), - Classification: "ACTIONABLE", - Links: []string{ - "https://docs.snyk.io/snyk-cli/commands/config-environment", - }, - Level: "error", - Detail: string(errDataFromFile), - } - cliAnalytics.GetInstrumentation().AddError( - cliError, - ) - // END error loading - if !globalConfiguration.GetBool(configuration.ANALYTICS_DISABLED) { sendAnalytics(cliAnalytics, globalLogger) } diff --git a/cliv2/internal/cliv2/cliv2.go b/cliv2/internal/cliv2/cliv2.go index 3a6bc13433..be9346dab4 100644 --- a/cliv2/internal/cliv2/cliv2.go +++ b/cliv2/internal/cliv2/cliv2.go @@ -13,6 +13,7 @@ import ( "os" "os/exec" "path" + "path/filepath" "regexp" "slices" "strings" @@ -58,6 +59,8 @@ const ( V2_ABOUT Handler = iota ) +const configKeyErrFile = "INTENAL_MY_ERR_FILE" + func NewCLIv2(config configuration.Configuration, debugLogger *log.Logger, ri runtimeinfo.RuntimeInfo) (*CLI, error) { cacheDirectory := config.GetString(configuration.CACHE_PATH) @@ -351,6 +354,8 @@ func PrepareV1EnvironmentVariables( // Fill environment variables for the legacy CLI from the given configuration. func fillEnvironmentFromConfig(inputAsMap map[string]string, config configuration.Configuration, args []string) { inputAsMap[constants.SNYK_INTERNAL_ORGID_ENV] = config.GetString(configuration.ORGANIZATION) + // TODO: pull into constants + inputAsMap["SNYK_ERR_FILE"] = config.GetString(configKeyErrFile) if config.GetBool(configuration.PREVIEW_FEATURES_ENABLED) { inputAsMap[constants.SNYK_INTERNAL_PREVIEW_FEATURES_ENABLED] = "1" @@ -402,6 +407,9 @@ func (c *CLI) executeV1Default(proxyInfo *proxy.ProxyInfo, passThroughArgs []str defer cancel() } + filePath := filepath.Join(c.globalConfig.GetString(configuration.TEMP_DIR_PATH), fmt.Sprintf("err-file-%d", time.Now().Nanosecond())) + c.globalConfig.Set(configKeyErrFile, filePath) + snykCmd, err := c.PrepareV1Command(ctx, c.v1BinaryLocation, passThroughArgs, proxyInfo, c.GetIntegrationName(), GetFullVersion()) if c.DebugLogger.Writer() != io.Discard { @@ -447,6 +455,16 @@ func (c *CLI) executeV1Default(proxyInfo *proxy.ProxyInfo, passThroughArgs []str if errors.Is(ctx.Err(), context.DeadlineExceeded) { return ctx.Err() } + + errDataFromFile, fileErr := os.ReadFile(filePath) + if fileErr != nil { + c.DebugLogger.Printf("Failed to read file: %v", fileErr) + } + + c.DebugLogger.Println(`LADEBUG`) + c.DebugLogger.Println(string(errDataFromFile)) + // TODO: wrap this in a custom struct/err format that we can use in the + return err } diff --git a/cliv2/pkg/basic_workflows/legacycli.go b/cliv2/pkg/basic_workflows/legacycli.go index 41a2a95603..bae4e7e1f9 100644 --- a/cliv2/pkg/basic_workflows/legacycli.go +++ b/cliv2/pkg/basic_workflows/legacycli.go @@ -110,10 +110,7 @@ func legacycliWorkflow( if len(apiToken) == 0 { apiToken = "random" } - cli.AppendEnvironmentVariables([]string{ - constants.SNYK_API_TOKEN_ENV + "=" + apiToken, - "SNYK_TEMP_DIR_PATH=" + config.GetString(configuration.TEMP_DIR_PATH), - }) + cli.AppendEnvironmentVariables([]string{constants.SNYK_API_TOKEN_ENV + "=" + apiToken}) err = cli.Init() if err != nil { diff --git a/src/cli/ipc.ts b/src/cli/ipc.ts new file mode 100644 index 0000000000..065e9d945b --- /dev/null +++ b/src/cli/ipc.ts @@ -0,0 +1,22 @@ +import { writeFile } from 'fs/promises'; +import { ProblemError } from '@snyk/error-catalog-nodejs-public'; + +// TODO: handle more gracefully +const ERROR_FILE_PATH = process.env.SNYK_ERR_FILE!; + +export async function sendError(err: Error): Promise<void> { + const data = serializeError(err); + return await writeFile(ERROR_FILE_PATH, data); +} + +// TBD: json format that the erorr get sent as (ProblemErrorJson, JSON API, custom one?) +function serializeError(err: Error): string { + // @ts-expect-error Using this instead of 'instanceof' since the error might be caught from external CLI plugins. + // See: https://github.com/snyk/error-catalog/blob/main/packages/error-catalog-nodejs/src/problem-error.ts#L17-L19 + if (err.isErrorCatalogError) { + // NOTE: there's also the JSON API version for this. + return JSON.stringify((err as ProblemError).toProblemJson('instance?')); + } + + return JSON.stringify(err, Object.getOwnPropertyNames(err)); +} diff --git a/src/cli/main.ts b/src/cli/main.ts index e831a4895d..d2a1c655e2 100755 --- a/src/cli/main.ts +++ b/src/cli/main.ts @@ -48,6 +48,8 @@ import { SarifFileOutputEmptyError } from '../lib/errors/empty-sarif-output-erro import { InvalidDetectionDepthValue } from '../lib/errors/invalid-detection-depth-value'; import { obfuscateArgs } from '../lib/utils'; import { EXIT_CODES } from './exit-codes'; +import { sendError } from './ipc'; + const isEmpty = require('lodash/isEmpty'); const debug = Debug('snyk'); @@ -195,12 +197,7 @@ async function handleError(args, error) { analytics.add('error', true); analytics.add('command', args.command); } else { - const errorFilePath = pathLib.join(process.env.SNYK_TEMP_DIR_PATH || getFullPath(''), './typescript-runtime-errors'); - - // TODO: If error is NOT valid error catalog message transform to match structure - await saveJsonResultsToFile(JSON.stringify( - analyticsError, - ), errorFilePath); + sendError(error); analytics.add('error-message', analyticsError.message); // Note that error.stack would also contain the error message // (see https://nodejs.org/api/errors.html#errors_error_stack) @@ -220,7 +217,7 @@ async function handleError(args, error) { return { res, exitCode }; } -function getFullPath(filepathFragment: string): string { +export function getFullPath(filepathFragment: string): string { if (pathLib.isAbsolute(filepathFragment)) { return filepathFragment; } else {