From d8574af4717f7b52da0d11a53540127bfcd12730 Mon Sep 17 00:00:00 2001 From: Tarcisio Ferraz Date: Mon, 15 Dec 2025 18:03:08 -0300 Subject: [PATCH 01/11] working version --- cmd/workflow/deploy/compile.go | 138 ---------------------- cmd/workflow/deploy/deploy.go | 21 +++- cmd/workflow/deploy/prepare.go | 73 ++---------- cmd/workflow/id/id.go | 127 ++++++++++++++++++++ cmd/workflow/workflow.go | 2 + internal/artifact/artifact.go | 7 ++ internal/artifact/builder.go | 97 +++++++++++++++ internal/build/compile.go | 156 +++++++++++++++++++++++++ internal/settings/workflow_settings.go | 2 +- 9 files changed, 417 insertions(+), 206 deletions(-) delete mode 100644 cmd/workflow/deploy/compile.go create mode 100644 cmd/workflow/id/id.go create mode 100644 internal/artifact/artifact.go create mode 100644 internal/artifact/builder.go create mode 100644 internal/build/compile.go diff --git a/cmd/workflow/deploy/compile.go b/cmd/workflow/deploy/compile.go deleted file mode 100644 index 51387569..00000000 --- a/cmd/workflow/deploy/compile.go +++ /dev/null @@ -1,138 +0,0 @@ -package deploy - -import ( - "bytes" - "encoding/base64" - "errors" - "fmt" - "os" - "path/filepath" - "strings" - - "github.com/andybalholm/brotli" - - cmdcommon "github.com/smartcontractkit/cre-cli/cmd/common" - "github.com/smartcontractkit/cre-cli/internal/constants" -) - -func (h *handler) Compile() error { - if !h.validated { - return fmt.Errorf("handler h.inputs not validated") - } - fmt.Println("Compiling workflow...") - - if h.inputs.OutputPath == "" { - h.inputs.OutputPath = defaultOutputPath - } - if !strings.HasSuffix(h.inputs.OutputPath, ".b64") { - if !strings.HasSuffix(h.inputs.OutputPath, ".br") { - if !strings.HasSuffix(h.inputs.OutputPath, ".wasm") { - h.inputs.OutputPath += ".wasm" // Append ".wasm" if it doesn't already end with ".wasm" - } - h.inputs.OutputPath += ".br" // Append ".br" if it doesn't already end with ".br" - } - h.inputs.OutputPath += ".b64" // Append ".b64" if it doesn't already end with ".b64" - } - - workflowAbsFile, err := filepath.Abs(h.inputs.WorkflowPath) - if err != nil { - return fmt.Errorf("failed to get absolute path for the workflow file: %w", err) - } - - if _, err := os.Stat(workflowAbsFile); os.IsNotExist(err) { - return fmt.Errorf("workflow file not found: %s", workflowAbsFile) - } - - workflowRootFolder := filepath.Dir(h.inputs.WorkflowPath) - - tmpWasmFileName := "tmp.wasm" - workflowMainFile := filepath.Base(h.inputs.WorkflowPath) - - // Set language in runtime context based on workflow file extension - if h.runtimeContext != nil { - h.runtimeContext.Workflow.Language = cmdcommon.GetWorkflowLanguage(workflowMainFile) - - switch h.runtimeContext.Workflow.Language { - case constants.WorkflowLanguageTypeScript: - if err := cmdcommon.EnsureTool("bun"); err != nil { - return errors.New("bun is required for TypeScript workflows but was not found in PATH; install from https://bun.com/docs/installation") - } - case constants.WorkflowLanguageGolang: - if err := cmdcommon.EnsureTool("go"); err != nil { - return errors.New("go toolchain is required for Go workflows but was not found in PATH; install from https://go.dev/dl") - } - default: - return fmt.Errorf("unsupported workflow language for file %s", workflowMainFile) - } - } - - buildCmd := cmdcommon.GetBuildCmd(workflowMainFile, tmpWasmFileName, workflowRootFolder) - h.log.Debug(). - Str("Workflow directory", buildCmd.Dir). - Str("Command", buildCmd.String()). - Msg("Executing go build command") - - buildOutput, err := buildCmd.CombinedOutput() - if err != nil { - fmt.Println(string(buildOutput)) - - out := strings.TrimSpace(string(buildOutput)) - return fmt.Errorf("failed to compile workflow: %w\nbuild output:\n%s", err, out) - } - h.log.Debug().Msgf("Build output: %s", buildOutput) - fmt.Println("Workflow compiled successfully") - - tmpWasmLocation := filepath.Join(workflowRootFolder, tmpWasmFileName) - wasmFile, err := os.ReadFile(tmpWasmLocation) - if err != nil { - return fmt.Errorf("failed to read workflow binary: %w", err) - } - - compressedFile, err := applyBrotliCompressionV2(&wasmFile) - if err != nil { - return fmt.Errorf("failed to compress WASM binary: %w", err) - } - h.log.Debug().Msg("WASM binary compressed") - - if err = encodeToBase64AndSaveToFile(&compressedFile, h.inputs.OutputPath); err != nil { - return fmt.Errorf("failed to base64 encode the WASM binary: %w", err) - } - h.log.Debug().Msg("WASM binary encoded") - - if err = os.Remove(tmpWasmLocation); err != nil { - return fmt.Errorf("failed to remove the temporary file: %w", err) - } - - return nil -} - -func applyBrotliCompressionV2(wasmContent *[]byte) ([]byte, error) { - var buffer bytes.Buffer - - // Compress using Brotli with default options - writer := brotli.NewWriter(&buffer) - - _, err := writer.Write(*wasmContent) - if err != nil { - return nil, err - } - - // must close it to flush the writer and ensure all data is stored to the buffer - err = writer.Close() - if err != nil { - return nil, err - } - - return buffer.Bytes(), nil -} - -func encodeToBase64AndSaveToFile(input *[]byte, outputFile string) error { - encoded := base64.StdEncoding.EncodeToString(*input) - - err := os.WriteFile(outputFile, []byte(encoded), 0666) //nolint:gosec - if err != nil { - return err - } - - return nil -} diff --git a/cmd/workflow/deploy/deploy.go b/cmd/workflow/deploy/deploy.go index 91fe8deb..82b24130 100644 --- a/cmd/workflow/deploy/deploy.go +++ b/cmd/workflow/deploy/deploy.go @@ -9,6 +9,8 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/rs/zerolog" + "github.com/smartcontractkit/cre-cli/internal/artifact" + "github.com/smartcontractkit/cre-cli/internal/build" "github.com/spf13/cobra" "github.com/spf13/viper" @@ -59,9 +61,11 @@ type handler struct { stdin io.Reader credentials *credentials.Credentials environmentSet *environments.EnvironmentSet - workflowArtifact *workflowArtifact + workflowArtifact *artifact.Artifact wrc *client.WorkflowRegistryV2Client runtimeContext *runtime.Context + builder *build.Builder + artifactBuilder *artifact.Builder validated bool @@ -69,7 +73,7 @@ type handler struct { wrcErr error } -var defaultOutputPath = "./binary.wasm.br.b64" +const defaultOutputPath = "./binary.wasm.br.b64" func New(runtimeContext *runtime.Context) *cobra.Command { var deployCmd = &cobra.Command{ @@ -111,7 +115,8 @@ func newHandler(ctx *runtime.Context, stdin io.Reader) *handler { stdin: stdin, credentials: ctx.Credentials, environmentSet: ctx.EnvironmentSet, - workflowArtifact: &workflowArtifact{}, + workflowArtifact: &artifact.Artifact{}, + builder: build.NewBuilder(ctx.Logger), wrc: nil, runtimeContext: ctx, validated: false, @@ -176,7 +181,15 @@ func (h *handler) ValidateInputs() error { func (h *handler) Execute() error { h.displayWorkflowDetails() - if err := h.Compile(); err != nil { + if !h.validated { + return errors.New("inputs have not been validated") + } + + outputPath := h.inputs.OutputPath + if outputPath == "" { + outputPath = defaultOutputPath + } + if err := h.builder.CompileAndSave(h.inputs.WorkflowPath, outputPath); err != nil { return fmt.Errorf("failed to compile workflow: %w", err) } if err := h.PrepareWorkflowArtifact(); err != nil { diff --git a/cmd/workflow/deploy/prepare.go b/cmd/workflow/deploy/prepare.go index dc51522e..9ff7704f 100644 --- a/cmd/workflow/deploy/prepare.go +++ b/cmd/workflow/deploy/prepare.go @@ -1,72 +1,19 @@ package deploy -import ( - "encoding/base64" - "fmt" - "os" - - workflowUtils "github.com/smartcontractkit/chainlink-common/pkg/workflows" -) - -type workflowArtifact struct { - BinaryData []byte - ConfigData []byte - WorkflowID string -} - -func (h *handler) prepareWorkflowBinary() ([]byte, error) { - h.log.Debug().Str("Binary Path", h.inputs.OutputPath).Msg("Fetching workflow binary") - binaryData, err := os.ReadFile(h.inputs.OutputPath) - if err != nil { - h.log.Error().Err(err).Str("path", h.inputs.OutputPath).Msg("Failed to read output file") - return nil, err - } - h.log.Debug().Msg("Workflow binary WASM is ready") - return binaryData, nil -} - -func (h *handler) prepareWorkflowConfig() ([]byte, error) { - h.log.Debug().Str("Config Path", h.inputs.ConfigPath).Msg("Fetching workflow config") - var configData []byte - var err error - if h.inputs.ConfigPath != "" { - configData, err = os.ReadFile(h.inputs.ConfigPath) - if err != nil { - h.log.Error().Err(err).Str("path", h.inputs.ConfigPath).Msg("Failed to read config file") - return nil, err - } - } - h.log.Debug().Msg("Workflow config is ready") - return configData, nil -} - -func (h *handler) PrepareWorkflowArtifact() error { - var err error - binaryData, err := h.prepareWorkflowBinary() +import "github.com/smartcontractkit/cre-cli/internal/artifact" + +func (h *handler) PrepareWorkflowArtifact() (err error) { + h.workflowArtifact, err = h.artifactBuilder.Build(artifact.Inputs{ + WorkflowOwner: h.inputs.WorkflowOwner, + WorkflowName: h.inputs.WorkflowName, + OutputPath: h.inputs.OutputPath, + ConfigPath: h.inputs.ConfigPath, + }) if err != nil { return err } - configData, err := h.prepareWorkflowConfig() - if err != nil { - return err - } - - // Note: the binary data read from file is base64 encoded, so we need to decode it before generating the workflow ID. - // This matches the behavior in the Chainlink node. Ref https://github.com/smartcontractkit/chainlink/blob/a4adc900d98d4e6eec0a6f80fcf86d883a8f1e3c/core/services/workflows/artifacts/v2/store.go#L211-L213 - binaryDataDecoded, err := base64.StdEncoding.DecodeString(string(binaryData)) - if err != nil { - return fmt.Errorf("failed to decode base64 binary data: %w", err) - } - - workflowID, err := workflowUtils.GenerateWorkflowIDFromStrings(h.inputs.WorkflowOwner, h.inputs.WorkflowName, binaryDataDecoded, configData, "") - if err != nil { - return fmt.Errorf("failed to generate workflow ID: %w", err) - } - - h.workflowArtifact.BinaryData = binaryData - h.workflowArtifact.ConfigData = configData - h.workflowArtifact.WorkflowID = workflowID + h.log.Info().Str("workflowID", h.workflowArtifact.WorkflowID).Msg("Prepared workflow artifact") return nil } diff --git a/cmd/workflow/id/id.go b/cmd/workflow/id/id.go new file mode 100644 index 00000000..5983c191 --- /dev/null +++ b/cmd/workflow/id/id.go @@ -0,0 +1,127 @@ +package id + +import ( + "fmt" + + "github.com/rs/zerolog" + "github.com/smartcontractkit/cre-cli/internal/artifact" + "github.com/smartcontractkit/cre-cli/internal/build" + "github.com/smartcontractkit/cre-cli/internal/runtime" + "github.com/smartcontractkit/cre-cli/internal/settings" + "github.com/smartcontractkit/cre-cli/internal/validation" + "github.com/spf13/cobra" + "github.com/spf13/viper" +) + +const defaultOutputPath = "./binary.wasm.br.b64" + +type Inputs struct { + WorkflowPath string `validate:"required,path_read"` + WorkflowName string `validate:"workflow_name"` + WorkflowOwner string `validate:"workflow_owner" cli:"--owner"` + + ConfigPath string `validate:"omitempty,file,ascii,max=97" cli:"--config"` + OutputPath string `validate:"omitempty,filepath,ascii,max=97" cli:"--output"` + + validated bool +} + +type handler struct { + log *zerolog.Logger + settings *settings.Settings + builder *build.Builder + artifactBuilder *artifact.Builder + + inputs Inputs +} + +func New(ctx *runtime.Context) *cobra.Command { + cmd := &cobra.Command{ + Use: "id ", + Short: "Display the workflow ID", + Args: cobra.ExactArgs(1), + Example: `cre workflow id ./my-workflow`, + RunE: func(cmd *cobra.Command, args []string) error { + h := newHandler(ctx) + + h.inputs = h.ResolveInputs(ctx.Viper) + if err := h.ValidateInputs(); err != nil { + return err + } + + return h.Execute() + }, + } + + // optional owner flag overrides the owner from settings + cmd.Flags().String("owner", "", "Workflow owner address") + cmd.Flags().StringP("output", "o", defaultOutputPath, "The output file for the compiled WASM binary encoded in base64") + + return cmd +} + +func newHandler(ctx *runtime.Context) *handler { + return &handler{ + log: ctx.Logger, + settings: ctx.Settings, + builder: build.NewBuilder(ctx.Logger), + artifactBuilder: artifact.NewBuilder(ctx.Logger), + } +} + +func (h *handler) Execute() error { + if !h.inputs.validated { + return fmt.Errorf("inputs have not been validated") + } + + err := h.builder.CompileAndSave(h.inputs.WorkflowPath, h.inputs.OutputPath) + if err != nil { + return fmt.Errorf("failed to compile workflow: %w", err) + } + + workflowArtifact, err := h.artifactBuilder.Build(artifact.Inputs{ + WorkflowOwner: h.inputs.WorkflowOwner, + WorkflowName: h.inputs.WorkflowName, + OutputPath: h.inputs.OutputPath, + ConfigPath: h.inputs.ConfigPath, + }) + if err != nil { + return fmt.Errorf("failed to build workflow artifact: %w", err) + } + + h.log.Info().Str("workflowID", workflowArtifact.WorkflowID).Msg("Workflow ID computed successfully") + fmt.Println("Workflow ID:", workflowArtifact.WorkflowID) + + return nil +} + +func (h *handler) ResolveInputs(v *viper.Viper) Inputs { + ownerFromFlag := v.GetString("owner") + workflowOwner := h.settings.Workflow.UserWorkflowSettings.WorkflowOwnerAddress + if ownerFromFlag != "" { + workflowOwner = ownerFromFlag + } + + return Inputs{ + WorkflowPath: h.settings.Workflow.WorkflowArtifactSettings.WorkflowPath, + WorkflowName: h.settings.Workflow.UserWorkflowSettings.WorkflowName, + WorkflowOwner: workflowOwner, + + ConfigPath: h.settings.Workflow.WorkflowArtifactSettings.ConfigPath, + OutputPath: v.GetString("output"), + } +} + +func (h *handler) ValidateInputs() error { + validate, err := validation.NewValidator() + if err != nil { + return fmt.Errorf("failed to initialize validator: %w", err) + } + + if err := validate.Struct(h.inputs); err != nil { + return validate.ParseValidationErrors(err) + } + h.inputs.validated = true + + return nil +} diff --git a/cmd/workflow/workflow.go b/cmd/workflow/workflow.go index 72e5b699..f0758b4a 100644 --- a/cmd/workflow/workflow.go +++ b/cmd/workflow/workflow.go @@ -1,6 +1,7 @@ package workflow import ( + "github.com/smartcontractkit/cre-cli/cmd/workflow/id" "github.com/spf13/cobra" "github.com/smartcontractkit/cre-cli/cmd/workflow/activate" @@ -25,6 +26,7 @@ func New(runtimeContext *runtime.Context) *cobra.Command { workflowCmd.AddCommand(test.New(runtimeContext)) workflowCmd.AddCommand(deploy.New(runtimeContext)) workflowCmd.AddCommand(simulate.New(runtimeContext)) + workflowCmd.AddCommand(id.New(runtimeContext)) return workflowCmd } diff --git a/internal/artifact/artifact.go b/internal/artifact/artifact.go new file mode 100644 index 00000000..c5735e8c --- /dev/null +++ b/internal/artifact/artifact.go @@ -0,0 +1,7 @@ +package artifact + +type Artifact struct { + BinaryData []byte + ConfigData []byte + WorkflowID string +} diff --git a/internal/artifact/builder.go b/internal/artifact/builder.go new file mode 100644 index 00000000..4d885206 --- /dev/null +++ b/internal/artifact/builder.go @@ -0,0 +1,97 @@ +package artifact + +import ( + "encoding/base64" + "fmt" + "os" + + "github.com/rs/zerolog" + workflowUtils "github.com/smartcontractkit/chainlink-common/pkg/workflows" +) + +type Inputs struct { + WorkflowOwner string + WorkflowName string + OutputPath string + ConfigPath string +} + +type Builder struct { + log *zerolog.Logger +} + +func NewBuilder(log *zerolog.Logger) *Builder { + return &Builder{ + log: log, + } +} + +func (b *Builder) Build(inputs Inputs) (art *Artifact, err error) { + art = &Artifact{} + + if inputs.WorkflowOwner == "" { + return nil, fmt.Errorf("workflow owner is required") + } + + if inputs.WorkflowName == "" { + return nil, fmt.Errorf("workflow name is required") + } + + art.ConfigData, err = b.prepareWorkflowConfig(inputs.ConfigPath) + if err != nil { + return nil, err + } + + art.BinaryData, err = b.prepareWorkflowBinary(inputs.OutputPath) + if err != nil { + return nil, err + } + + binaryDataDecoded, err := decodeBinaryData(art.BinaryData) + if err != nil { + return nil, err + } + + art.WorkflowID, err = workflowUtils.GenerateWorkflowIDFromStrings(inputs.WorkflowOwner, inputs.WorkflowName, binaryDataDecoded, art.ConfigData, "") + if err != nil { + return nil, fmt.Errorf("failed to generate workflow ID: %w", err) + } + + return art, nil +} + +func decodeBinaryData(binaryData []byte) ([]byte, error) { + // Note: the binary data read from file is base64 encoded, so we need to decode it before generating the workflow ID. + // This matches the behavior in the Chainlink node. Ref https://github.com/smartcontractkit/chainlink/blob/a4adc900d98d4e6eec0a6f80fcf86d883a8f1e3c/core/services/workflows/artifacts/v2/store.go#L211-L213 + binaryDataDecoded := make([]byte, base64.StdEncoding.DecodedLen(len(binaryData))) + if _, err := base64.StdEncoding.Decode(binaryDataDecoded, binaryData); err != nil { + return nil, fmt.Errorf("failed to decode base64 binary data: %w", err) + } + return binaryDataDecoded, nil +} + +func (b *Builder) prepareWorkflowBinary(outputPath string) ([]byte, error) { + b.log.Debug().Str("Binary Path", outputPath).Msg("Fetching workflow binary") + binaryData, err := os.ReadFile(outputPath) + if err != nil { + b.log.Error().Err(err).Str("path", outputPath).Msg("Failed to read output file") + return nil, err + } + b.log.Debug().Msg("Workflow binary WASM is ready") + return binaryData, nil +} + +func (b *Builder) prepareWorkflowConfig(configPath string) ([]byte, error) { + b.log.Debug().Str("Config Path", configPath).Msg("Fetching workflow config") + var configData []byte + var err error + if configPath != "" { + configData, err = os.ReadFile(configPath) + if err != nil { + b.log.Error().Err(err).Str("path", configPath).Msg("Failed to read config file") + return nil, err + } + } + b.log.Debug().Msg("Workflow config is ready") + return configData, nil +} diff --git a/internal/build/compile.go b/internal/build/compile.go new file mode 100644 index 00000000..3190b7aa --- /dev/null +++ b/internal/build/compile.go @@ -0,0 +1,156 @@ +package build + +import ( + "bytes" + "encoding/base64" + "errors" + "fmt" + "os" + "path/filepath" + "strings" + + "github.com/andybalholm/brotli" + "github.com/rs/zerolog" + + cmdcommon "github.com/smartcontractkit/cre-cli/cmd/common" + "github.com/smartcontractkit/cre-cli/internal/constants" +) + +type Builder struct { + log *zerolog.Logger +} + +func NewBuilder(log *zerolog.Logger) *Builder { + return &Builder{ + log: log, + } +} + +func (b *Builder) Compile(workflowPath string) (*[]byte, error) { + fmt.Println("Compiling workflow...") + + workflowAbsFile, err := filepath.Abs(workflowPath) + if err != nil { + return nil, fmt.Errorf("failed to get absolute path for the workflow file: %w", err) + } + + if _, err := os.Stat(workflowAbsFile); os.IsNotExist(err) { + return nil, fmt.Errorf("workflow file not found: %s", workflowAbsFile) + } + + workflowRootFolder := filepath.Dir(workflowPath) + + tmpWasmFileName := "tmp.wasm" + workflowMainFile := filepath.Base(workflowPath) + + switch cmdcommon.GetWorkflowLanguage(workflowMainFile) { + case constants.WorkflowLanguageTypeScript: + if err := cmdcommon.EnsureTool("bun"); err != nil { + return nil, errors.New("bun is required for TypeScript workflows but was not found in PATH; install from https://bun.com/docs/installation") + } + case constants.WorkflowLanguageGolang: + if err := cmdcommon.EnsureTool("go"); err != nil { + return nil, errors.New("go toolchain is required for Go workflows but was not found in PATH; install from https://go.dev/dl") + } + default: + return nil, fmt.Errorf("unsupported workflow language for file %s", workflowMainFile) + } + + buildCmd := cmdcommon.GetBuildCmd(workflowMainFile, tmpWasmFileName, workflowRootFolder) + b.log.Debug(). + Str("Workflow directory", buildCmd.Dir). + Str("Command", buildCmd.String()). + Msg("Executing go build command") + + buildOutput, err := buildCmd.CombinedOutput() + if err != nil { + fmt.Println(string(buildOutput)) + + out := strings.TrimSpace(string(buildOutput)) + return nil, fmt.Errorf("failed to compile workflow: %w\nbuild output:\n%s", err, out) + } + b.log.Debug().Msgf("Build output: %s", buildOutput) + fmt.Println("Workflow compiled successfully") + + tmpWasmLocation := filepath.Join(workflowRootFolder, tmpWasmFileName) + wasmFile, err := os.ReadFile(tmpWasmLocation) + if err != nil { + return nil, fmt.Errorf("failed to read workflow binary: %w", err) + } + + compressedFile, err := applyBrotliCompressionV2(&wasmFile) + if err != nil { + return nil, fmt.Errorf("failed to compress WASM binary: %w", err) + } + b.log.Debug().Msg("WASM binary compressed") + + encoded := encodeToBase64(&compressedFile) + b.log.Debug().Msg("WASM binary encoded") + + if err = os.Remove(tmpWasmLocation); err != nil { + return nil, fmt.Errorf("failed to remove the temporary file: %w", err) + } + + return encoded, nil +} + +func (b *Builder) CompileAndSave(workflowPath, outputPath string) error { + if outputPath == "" { + return fmt.Errorf("output path is not specified") + } + outputPath = ensureOutputPathExtensions(outputPath) + + binary, err := b.Compile(workflowPath) + if err != nil { + return err + } + + return saveToFile(binary, outputPath) +} + +func ensureOutputPathExtensions(outputPath string) string { + if !strings.HasSuffix(outputPath, ".b64") { + if !strings.HasSuffix(outputPath, ".br") { + if !strings.HasSuffix(outputPath, ".wasm") { + outputPath += ".wasm" // Append ".wasm" if it doesn't already end with ".wasm" + } + outputPath += ".br" // Append ".br" if it doesn't already end with ".br" + } + outputPath += ".b64" // Append ".b64" if it doesn't already end with ".b64" + } + return outputPath +} + +func applyBrotliCompressionV2(wasmContent *[]byte) ([]byte, error) { + var buffer bytes.Buffer + + // Compress using Brotli with default options + writer := brotli.NewWriter(&buffer) + + _, err := writer.Write(*wasmContent) + if err != nil { + return nil, err + } + + // must close it to flush the writer and ensure all data is stored to the buffer + err = writer.Close() + if err != nil { + return nil, err + } + + return buffer.Bytes(), nil +} + +func encodeToBase64(input *[]byte) *[]byte { + encoded := make([]byte, base64.StdEncoding.EncodedLen(len(*input))) + base64.StdEncoding.Encode(encoded, *input) + return &encoded +} + +func saveToFile(input *[]byte, outputFile string) error { + err := os.WriteFile(outputFile, *input, 0666) //nolint:gosec + if err != nil { + return err + } + return nil +} diff --git a/internal/settings/workflow_settings.go b/internal/settings/workflow_settings.go index d6ce2a50..ac61128f 100644 --- a/internal/settings/workflow_settings.go +++ b/internal/settings/workflow_settings.go @@ -184,7 +184,7 @@ func IsValidChainName(name string) error { // `--broadcast` is false or not set. `cre help` should skip as well. func ShouldSkipGetOwner(cmd *cobra.Command) bool { switch cmd.Name() { - case "help": + case "help", "id": return true case "simulate": // Treat missing/invalid flag as false (i.e., skip). From 3e70cba9b356125786c9be76b8e12e7b1f1a0f20 Mon Sep 17 00:00:00 2001 From: Tarcisio Ferraz Date: Mon, 15 Dec 2025 19:06:36 -0300 Subject: [PATCH 02/11] extract ensure build tools --- cmd/workflow/simulate/simulate.go | 16 +++------------- internal/build/tools.go | 25 +++++++++++++++++++++++++ 2 files changed, 28 insertions(+), 13 deletions(-) create mode 100644 internal/build/tools.go diff --git a/cmd/workflow/simulate/simulate.go b/cmd/workflow/simulate/simulate.go index d0a56e25..af29f5f6 100644 --- a/cmd/workflow/simulate/simulate.go +++ b/cmd/workflow/simulate/simulate.go @@ -5,7 +5,6 @@ import ( "context" "crypto/ecdsa" "encoding/json" - "errors" "fmt" "math" "math/big" @@ -21,6 +20,7 @@ import ( "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/ethclient" "github.com/rs/zerolog" + "github.com/smartcontractkit/cre-cli/internal/build" "github.com/spf13/cobra" "github.com/spf13/viper" "go.uber.org/zap/zapcore" @@ -38,7 +38,6 @@ import ( v2 "github.com/smartcontractkit/chainlink/v2/core/services/workflows/v2" cmdcommon "github.com/smartcontractkit/cre-cli/cmd/common" - "github.com/smartcontractkit/cre-cli/internal/constants" "github.com/smartcontractkit/cre-cli/internal/runtime" "github.com/smartcontractkit/cre-cli/internal/settings" "github.com/smartcontractkit/cre-cli/internal/validation" @@ -202,17 +201,8 @@ func (h *handler) Execute(inputs Inputs) error { if h.runtimeContext != nil { h.runtimeContext.Workflow.Language = cmdcommon.GetWorkflowLanguage(workflowMainFile) - switch h.runtimeContext.Workflow.Language { - case constants.WorkflowLanguageTypeScript: - if err := cmdcommon.EnsureTool("bun"); err != nil { - return errors.New("bun is required for TypeScript workflows but was not found in PATH; install from https://bun.com/docs/installation") - } - case constants.WorkflowLanguageGolang: - if err := cmdcommon.EnsureTool("go"); err != nil { - return errors.New("go toolchain is required for Go workflows but was not found in PATH; install from https://go.dev/dl") - } - default: - return fmt.Errorf("unsupported workflow language for file %s", workflowMainFile) + if err := build.EnsureToolsForBuild(h.runtimeContext.Workflow.Language); err != nil { + return fmt.Errorf("failed to ensure build tools: %w", err) } } diff --git a/internal/build/tools.go b/internal/build/tools.go new file mode 100644 index 00000000..fed401fe --- /dev/null +++ b/internal/build/tools.go @@ -0,0 +1,25 @@ +package build + +import ( + "errors" + "fmt" + + cmdcommon "github.com/smartcontractkit/cre-cli/cmd/common" + "github.com/smartcontractkit/cre-cli/internal/constants" +) + +func EnsureToolsForBuild(workflowLanguage string) error { + switch workflowLanguage { + case constants.WorkflowLanguageTypeScript: + if err := cmdcommon.EnsureTool("bun"); err != nil { + return errors.New("bun is required for TypeScript workflows but was not found in PATH; install from https://bun.com/docs/installation") + } + case constants.WorkflowLanguageGolang: + if err := cmdcommon.EnsureTool("go"); err != nil { + return errors.New("go toolchain is required for Go workflows but was not found in PATH; install from https://go.dev/dl") + } + default: + return fmt.Errorf("unsupported workflow workflowLanguage %s", workflowLanguage) + } + return nil +} From df0d9102e8959c1b0a18985a803fee7e71decd0e Mon Sep 17 00:00:00 2001 From: Tarcisio Ferraz Date: Mon, 15 Dec 2025 19:07:34 -0300 Subject: [PATCH 03/11] extract build params --- cmd/workflow/deploy/deploy.go | 10 +++-- internal/build/compile.go | 76 +++++++++++++++++++---------------- 2 files changed, 48 insertions(+), 38 deletions(-) diff --git a/cmd/workflow/deploy/deploy.go b/cmd/workflow/deploy/deploy.go index 82b24130..f9746b31 100644 --- a/cmd/workflow/deploy/deploy.go +++ b/cmd/workflow/deploy/deploy.go @@ -185,11 +185,12 @@ func (h *handler) Execute() error { return errors.New("inputs have not been validated") } - outputPath := h.inputs.OutputPath - if outputPath == "" { - outputPath = defaultOutputPath + buildParams, err := build.ResolveBuildParamsForWorkflow(h.inputs.WorkflowPath, h.inputs.OutputPath) + if err != nil { + return fmt.Errorf("failed to resolve build inputs: %w", err) } - if err := h.builder.CompileAndSave(h.inputs.WorkflowPath, outputPath); err != nil { + + if err := h.builder.CompileAndSave(buildParams); err != nil { return fmt.Errorf("failed to compile workflow: %w", err) } if err := h.PrepareWorkflowArtifact(); err != nil { @@ -197,6 +198,7 @@ func (h *handler) Execute() error { } h.runtimeContext.Workflow.ID = h.workflowArtifact.WorkflowID + h.runtimeContext.Workflow.Language = buildParams.WorkflowLanguage h.wg.Wait() if h.wrcErr != nil { diff --git a/internal/build/compile.go b/internal/build/compile.go index 3190b7aa..e32b0789 100644 --- a/internal/build/compile.go +++ b/internal/build/compile.go @@ -3,7 +3,6 @@ package build import ( "bytes" "encoding/base64" - "errors" "fmt" "os" "path/filepath" @@ -13,50 +12,59 @@ import ( "github.com/rs/zerolog" cmdcommon "github.com/smartcontractkit/cre-cli/cmd/common" - "github.com/smartcontractkit/cre-cli/internal/constants" ) -type Builder struct { - log *zerolog.Logger -} +type Params struct { + WorkflowPath string + WorkflowRootFolder string + WorkflowMainFile string + WorkflowLanguage string -func NewBuilder(log *zerolog.Logger) *Builder { - return &Builder{ - log: log, - } + OutputPath string } -func (b *Builder) Compile(workflowPath string) (*[]byte, error) { - fmt.Println("Compiling workflow...") - +func ResolveBuildParamsForWorkflow(workflowPath, outputPath string) (Params, error) { workflowAbsFile, err := filepath.Abs(workflowPath) if err != nil { - return nil, fmt.Errorf("failed to get absolute path for the workflow file: %w", err) + return Params{}, fmt.Errorf("failed to get absolute path for the workflow file: %w", err) } if _, err := os.Stat(workflowAbsFile); os.IsNotExist(err) { - return nil, fmt.Errorf("workflow file not found: %s", workflowAbsFile) + return Params{}, fmt.Errorf("workflow file not found: %s", workflowAbsFile) } workflowRootFolder := filepath.Dir(workflowPath) - - tmpWasmFileName := "tmp.wasm" workflowMainFile := filepath.Base(workflowPath) + workflowLanguage := cmdcommon.GetWorkflowLanguage(workflowMainFile) + + return Params{ + WorkflowPath: workflowPath, + WorkflowRootFolder: workflowRootFolder, + WorkflowMainFile: workflowMainFile, + WorkflowLanguage: workflowLanguage, + OutputPath: outputPath, + }, nil +} - switch cmdcommon.GetWorkflowLanguage(workflowMainFile) { - case constants.WorkflowLanguageTypeScript: - if err := cmdcommon.EnsureTool("bun"); err != nil { - return nil, errors.New("bun is required for TypeScript workflows but was not found in PATH; install from https://bun.com/docs/installation") - } - case constants.WorkflowLanguageGolang: - if err := cmdcommon.EnsureTool("go"); err != nil { - return nil, errors.New("go toolchain is required for Go workflows but was not found in PATH; install from https://go.dev/dl") - } - default: - return nil, fmt.Errorf("unsupported workflow language for file %s", workflowMainFile) +type Builder struct { + log *zerolog.Logger +} + +func NewBuilder(log *zerolog.Logger) *Builder { + return &Builder{ + log: log, } +} + +func (b *Builder) Compile(params Params) (*[]byte, error) { + fmt.Println("Compiling workflow...") - buildCmd := cmdcommon.GetBuildCmd(workflowMainFile, tmpWasmFileName, workflowRootFolder) + if err := EnsureToolsForBuild(params.WorkflowLanguage); err != nil { + return nil, fmt.Errorf("failed to ensure build tools: %w", err) + } + + tmpWasmFileName := "tmp.wasm" + buildCmd := cmdcommon.GetBuildCmd(params.WorkflowMainFile, tmpWasmFileName, params.WorkflowRootFolder) b.log.Debug(). Str("Workflow directory", buildCmd.Dir). Str("Command", buildCmd.String()). @@ -72,7 +80,7 @@ func (b *Builder) Compile(workflowPath string) (*[]byte, error) { b.log.Debug().Msgf("Build output: %s", buildOutput) fmt.Println("Workflow compiled successfully") - tmpWasmLocation := filepath.Join(workflowRootFolder, tmpWasmFileName) + tmpWasmLocation := filepath.Join(params.WorkflowRootFolder, tmpWasmFileName) wasmFile, err := os.ReadFile(tmpWasmLocation) if err != nil { return nil, fmt.Errorf("failed to read workflow binary: %w", err) @@ -94,18 +102,18 @@ func (b *Builder) Compile(workflowPath string) (*[]byte, error) { return encoded, nil } -func (b *Builder) CompileAndSave(workflowPath, outputPath string) error { - if outputPath == "" { +func (b *Builder) CompileAndSave(params Params) error { + if params.OutputPath == "" { return fmt.Errorf("output path is not specified") } - outputPath = ensureOutputPathExtensions(outputPath) + params.OutputPath = ensureOutputPathExtensions(params.OutputPath) - binary, err := b.Compile(workflowPath) + binary, err := b.Compile(params) if err != nil { return err } - return saveToFile(binary, outputPath) + return saveToFile(binary, params.OutputPath) } func ensureOutputPathExtensions(outputPath string) string { From f534605f46d78357cb217b70c0c42496eebee544 Mon Sep 17 00:00:00 2001 From: Tarcisio Ferraz Date: Mon, 15 Dec 2025 19:07:44 -0300 Subject: [PATCH 04/11] fix: close writer --- internal/build/compile.go | 1 + 1 file changed, 1 insertion(+) diff --git a/internal/build/compile.go b/internal/build/compile.go index e32b0789..df4c7b13 100644 --- a/internal/build/compile.go +++ b/internal/build/compile.go @@ -134,6 +134,7 @@ func applyBrotliCompressionV2(wasmContent *[]byte) ([]byte, error) { // Compress using Brotli with default options writer := brotli.NewWriter(&buffer) + defer writer.Close() _, err := writer.Write(*wasmContent) if err != nil { From 7a8cba0becce39ae69e7301999bfc0ec00b8d9b3 Mon Sep 17 00:00:00 2001 From: Tarcisio Ferraz Date: Tue, 16 Dec 2025 09:14:43 -0300 Subject: [PATCH 05/11] fix tests --- cmd/workflow/deploy/artifacts_test.go | 13 +++++++------ cmd/workflow/deploy/compile.go | 21 +++++++++++++++++++++ cmd/workflow/deploy/deploy.go | 14 ++++---------- cmd/workflow/deploy/register_test.go | 3 ++- cmd/workflow/id/id.go | 11 ++++++++--- cmd/workflow/simulate/simulate.go | 2 +- cmd/workflow/workflow.go | 2 +- internal/artifact/builder.go | 1 + test/common.go | 2 +- 9 files changed, 46 insertions(+), 23 deletions(-) create mode 100644 cmd/workflow/deploy/compile.go diff --git a/cmd/workflow/deploy/artifacts_test.go b/cmd/workflow/deploy/artifacts_test.go index d9591b04..1465d4ca 100644 --- a/cmd/workflow/deploy/artifacts_test.go +++ b/cmd/workflow/deploy/artifacts_test.go @@ -12,6 +12,7 @@ import ( "github.com/jarcoal/httpmock" "github.com/stretchr/testify/require" + "github.com/smartcontractkit/cre-cli/internal/artifact" "github.com/smartcontractkit/cre-cli/internal/credentials" "github.com/smartcontractkit/cre-cli/internal/testutil/chainsim" ) @@ -91,7 +92,7 @@ func TestUpload_SuccessAndErrorCases(t *testing.T) { h.environmentSet.GraphQLURL = "http://graphql.endpoint" // Success case : uploading binary and config data - h.workflowArtifact = &workflowArtifact{ + h.workflowArtifact = &artifact.Artifact{ BinaryData: []byte("binarydata"), ConfigData: []byte("configdata"), WorkflowID: "workflow-id", @@ -102,7 +103,7 @@ func TestUpload_SuccessAndErrorCases(t *testing.T) { require.Equal(t, "http://origin/get", *h.inputs.ConfigURL) // Success: empty ConfigData - h.workflowArtifact = &workflowArtifact{ + h.workflowArtifact = &artifact.Artifact{ BinaryData: []byte("binarydata"), ConfigData: nil, WorkflowID: "workflow-id", @@ -116,7 +117,7 @@ func TestUpload_SuccessAndErrorCases(t *testing.T) { require.ErrorContains(t, err, "workflowArtifact is nil") // Error: empty BinaryData - h.workflowArtifact = &workflowArtifact{ + h.workflowArtifact = &artifact.Artifact{ BinaryData: nil, ConfigData: []byte("configdata"), WorkflowID: "workflow-id", @@ -125,7 +126,7 @@ func TestUpload_SuccessAndErrorCases(t *testing.T) { require.ErrorContains(t, err, "uploading binary artifact: content is empty for artifactType BINARY") // Error: workflowID is empty - h.workflowArtifact = &workflowArtifact{ + h.workflowArtifact = &artifact.Artifact{ BinaryData: []byte("binarydata"), ConfigData: []byte("configdata"), WorkflowID: "", @@ -166,7 +167,7 @@ func TestUploadArtifactToStorageService_OriginError(t *testing.T) { // Patch settings to use mock GraphQL endpoint h.environmentSet.GraphQLURL = "http://graphql.endpoint" - h.workflowArtifact = &workflowArtifact{ + h.workflowArtifact = &artifact.Artifact{ BinaryData: []byte("binarydata"), ConfigData: []byte("configdata"), WorkflowID: "workflow-id", @@ -232,7 +233,7 @@ func TestUploadArtifactToStorageService_AlreadyExistsError(t *testing.T) { // Patch settings to use mock GraphQL endpoint h.environmentSet.GraphQLURL = "http://graphql.endpoint" - h.workflowArtifact = &workflowArtifact{ + h.workflowArtifact = &artifact.Artifact{ BinaryData: []byte("binarydata"), ConfigData: []byte("configdata"), WorkflowID: "workflow-id", diff --git a/cmd/workflow/deploy/compile.go b/cmd/workflow/deploy/compile.go new file mode 100644 index 00000000..40e28a08 --- /dev/null +++ b/cmd/workflow/deploy/compile.go @@ -0,0 +1,21 @@ +package deploy + +import ( + "fmt" + + "github.com/smartcontractkit/cre-cli/internal/build" +) + +func (h *handler) Compile() error { + buildParams, err := build.ResolveBuildParamsForWorkflow(h.inputs.WorkflowPath, h.inputs.OutputPath) + if err != nil { + return fmt.Errorf("failed to resolve build inputs: %w", err) + } + h.runtimeContext.Workflow.Language = buildParams.WorkflowLanguage + + if err := h.builder.CompileAndSave(buildParams); err != nil { + return fmt.Errorf("failed to compile workflow: %w", err) + } + + return nil +} diff --git a/cmd/workflow/deploy/deploy.go b/cmd/workflow/deploy/deploy.go index f9746b31..ca75f327 100644 --- a/cmd/workflow/deploy/deploy.go +++ b/cmd/workflow/deploy/deploy.go @@ -9,12 +9,12 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/rs/zerolog" - "github.com/smartcontractkit/cre-cli/internal/artifact" - "github.com/smartcontractkit/cre-cli/internal/build" "github.com/spf13/cobra" "github.com/spf13/viper" "github.com/smartcontractkit/cre-cli/cmd/client" + "github.com/smartcontractkit/cre-cli/internal/artifact" + "github.com/smartcontractkit/cre-cli/internal/build" "github.com/smartcontractkit/cre-cli/internal/constants" "github.com/smartcontractkit/cre-cli/internal/credentials" "github.com/smartcontractkit/cre-cli/internal/environments" @@ -185,20 +185,14 @@ func (h *handler) Execute() error { return errors.New("inputs have not been validated") } - buildParams, err := build.ResolveBuildParamsForWorkflow(h.inputs.WorkflowPath, h.inputs.OutputPath) - if err != nil { - return fmt.Errorf("failed to resolve build inputs: %w", err) + if err := h.Compile(); err != nil { + return err } - if err := h.builder.CompileAndSave(buildParams); err != nil { - return fmt.Errorf("failed to compile workflow: %w", err) - } if err := h.PrepareWorkflowArtifact(); err != nil { return fmt.Errorf("failed to prepare workflow artifact: %w", err) } - h.runtimeContext.Workflow.ID = h.workflowArtifact.WorkflowID - h.runtimeContext.Workflow.Language = buildParams.WorkflowLanguage h.wg.Wait() if h.wrcErr != nil { diff --git a/cmd/workflow/deploy/register_test.go b/cmd/workflow/deploy/register_test.go index 4b1985b3..ed5b2fa9 100644 --- a/cmd/workflow/deploy/register_test.go +++ b/cmd/workflow/deploy/register_test.go @@ -6,6 +6,7 @@ import ( "github.com/stretchr/testify/require" + "github.com/smartcontractkit/cre-cli/internal/artifact" "github.com/smartcontractkit/cre-cli/internal/testutil/chainsim" ) @@ -55,7 +56,7 @@ func TestWorkflowUpsert(t *testing.T) { err = handler.ValidateInputs() require.NoError(t, err) - wfArt := workflowArtifact{ + wfArt := artifact.Artifact{ BinaryData: []byte("0x1234"), ConfigData: []byte("config"), WorkflowID: "1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef", diff --git a/cmd/workflow/id/id.go b/cmd/workflow/id/id.go index 5983c191..a636902f 100644 --- a/cmd/workflow/id/id.go +++ b/cmd/workflow/id/id.go @@ -4,13 +4,14 @@ import ( "fmt" "github.com/rs/zerolog" + "github.com/spf13/cobra" + "github.com/spf13/viper" + "github.com/smartcontractkit/cre-cli/internal/artifact" "github.com/smartcontractkit/cre-cli/internal/build" "github.com/smartcontractkit/cre-cli/internal/runtime" "github.com/smartcontractkit/cre-cli/internal/settings" "github.com/smartcontractkit/cre-cli/internal/validation" - "github.com/spf13/cobra" - "github.com/spf13/viper" ) const defaultOutputPath = "./binary.wasm.br.b64" @@ -74,8 +75,12 @@ func (h *handler) Execute() error { return fmt.Errorf("inputs have not been validated") } - err := h.builder.CompileAndSave(h.inputs.WorkflowPath, h.inputs.OutputPath) + buildParams, err := build.ResolveBuildParamsForWorkflow(h.inputs.WorkflowPath, h.inputs.OutputPath) if err != nil { + return fmt.Errorf("failed to resolve build parameters: %w", err) + } + + if err := h.builder.CompileAndSave(buildParams); err != nil { return fmt.Errorf("failed to compile workflow: %w", err) } diff --git a/cmd/workflow/simulate/simulate.go b/cmd/workflow/simulate/simulate.go index af29f5f6..c3089e6d 100644 --- a/cmd/workflow/simulate/simulate.go +++ b/cmd/workflow/simulate/simulate.go @@ -20,7 +20,6 @@ import ( "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/ethclient" "github.com/rs/zerolog" - "github.com/smartcontractkit/cre-cli/internal/build" "github.com/spf13/cobra" "github.com/spf13/viper" "go.uber.org/zap/zapcore" @@ -38,6 +37,7 @@ import ( v2 "github.com/smartcontractkit/chainlink/v2/core/services/workflows/v2" cmdcommon "github.com/smartcontractkit/cre-cli/cmd/common" + "github.com/smartcontractkit/cre-cli/internal/build" "github.com/smartcontractkit/cre-cli/internal/runtime" "github.com/smartcontractkit/cre-cli/internal/settings" "github.com/smartcontractkit/cre-cli/internal/validation" diff --git a/cmd/workflow/workflow.go b/cmd/workflow/workflow.go index f0758b4a..6221bf8f 100644 --- a/cmd/workflow/workflow.go +++ b/cmd/workflow/workflow.go @@ -1,12 +1,12 @@ package workflow import ( - "github.com/smartcontractkit/cre-cli/cmd/workflow/id" "github.com/spf13/cobra" "github.com/smartcontractkit/cre-cli/cmd/workflow/activate" "github.com/smartcontractkit/cre-cli/cmd/workflow/delete" "github.com/smartcontractkit/cre-cli/cmd/workflow/deploy" + "github.com/smartcontractkit/cre-cli/cmd/workflow/id" "github.com/smartcontractkit/cre-cli/cmd/workflow/pause" "github.com/smartcontractkit/cre-cli/cmd/workflow/simulate" "github.com/smartcontractkit/cre-cli/cmd/workflow/test" diff --git a/internal/artifact/builder.go b/internal/artifact/builder.go index 4d885206..dfe535aa 100644 --- a/internal/artifact/builder.go +++ b/internal/artifact/builder.go @@ -6,6 +6,7 @@ import ( "os" "github.com/rs/zerolog" + workflowUtils "github.com/smartcontractkit/chainlink-common/pkg/workflows" ) diff --git a/test/common.go b/test/common.go index c5de25d2..d330089a 100644 --- a/test/common.go +++ b/test/common.go @@ -231,7 +231,7 @@ func StartAnvil(initState AnvilInitState, stateFileName string) (*os.Process, in L.Info().Str("Command", anvil.String()).Msg("Executing anvil") if err := anvil.Start(); err != nil { - return nil, 0, errors.New("failed to start Anvil") + return nil, 0, fmt.Errorf("failed to start Anvil: %w", err) } L.Info().Msg("Checking if Anvil is up and running") From b244c9eb5f06019ab6d2db1cc8968bf54a9eb2fd Mon Sep 17 00:00:00 2001 From: Tarcisio Ferraz Date: Tue, 16 Dec 2025 09:43:39 -0300 Subject: [PATCH 06/11] fix tests --- cmd/workflow/deploy/compile.go | 7 ++++++- cmd/workflow/deploy/deploy.go | 1 + 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/cmd/workflow/deploy/compile.go b/cmd/workflow/deploy/compile.go index 40e28a08..3753bf57 100644 --- a/cmd/workflow/deploy/compile.go +++ b/cmd/workflow/deploy/compile.go @@ -7,7 +7,12 @@ import ( ) func (h *handler) Compile() error { - buildParams, err := build.ResolveBuildParamsForWorkflow(h.inputs.WorkflowPath, h.inputs.OutputPath) + outputPath := h.inputs.OutputPath + if outputPath == "" { + outputPath = defaultOutputPath + } + + buildParams, err := build.ResolveBuildParamsForWorkflow(h.inputs.WorkflowPath, outputPath) if err != nil { return fmt.Errorf("failed to resolve build inputs: %w", err) } diff --git a/cmd/workflow/deploy/deploy.go b/cmd/workflow/deploy/deploy.go index ca75f327..68b2fcb1 100644 --- a/cmd/workflow/deploy/deploy.go +++ b/cmd/workflow/deploy/deploy.go @@ -117,6 +117,7 @@ func newHandler(ctx *runtime.Context, stdin io.Reader) *handler { environmentSet: ctx.EnvironmentSet, workflowArtifact: &artifact.Artifact{}, builder: build.NewBuilder(ctx.Logger), + artifactBuilder: artifact.NewBuilder(ctx.Logger), wrc: nil, runtimeContext: ctx, validated: false, From 13ab5e803c621bca1291b5d71568561220b75401 Mon Sep 17 00:00:00 2001 From: Tarcisio Ferraz Date: Tue, 16 Dec 2025 11:21:12 -0300 Subject: [PATCH 07/11] add tests --- internal/artifact/builder_test.go | 315 ++++++++++++++++++ internal/build/compile_test.go | 129 +++++++ internal/build/testdata/basic_workflow/go.mod | 25 ++ internal/build/testdata/basic_workflow/go.sum | 37 ++ .../build/testdata/basic_workflow/main.go | 36 ++ 5 files changed, 542 insertions(+) create mode 100644 internal/artifact/builder_test.go create mode 100644 internal/build/compile_test.go create mode 100644 internal/build/testdata/basic_workflow/go.mod create mode 100644 internal/build/testdata/basic_workflow/go.sum create mode 100644 internal/build/testdata/basic_workflow/main.go diff --git a/internal/artifact/builder_test.go b/internal/artifact/builder_test.go new file mode 100644 index 00000000..20699d4e --- /dev/null +++ b/internal/artifact/builder_test.go @@ -0,0 +1,315 @@ +package artifact_test + +import ( + "encoding/base64" + "os" + "path/filepath" + "testing" + + "github.com/smartcontractkit/cre-cli/internal/artifact" + "github.com/smartcontractkit/cre-cli/internal/testutil/chainsim" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/smartcontractkit/cre-cli/internal/testutil" +) + +func TestBuilder_Build(t *testing.T) { + t.Parallel() + + logger := testutil.NewTestLogger() + artifactBuilder := artifact.NewBuilder(logger) + + t.Run("success with config", func(t *testing.T) { + t.Parallel() + + tempDir := t.TempDir() + + // Create valid base64-encoded binary file + binaryData := []byte("test binary data") + encodedBinary := base64.StdEncoding.EncodeToString(binaryData) + binaryPath := filepath.Join(tempDir, "binary.wasm.br.b64") + err := os.WriteFile(binaryPath, []byte(encodedBinary), 0644) + require.NoError(t, err) + + // Create config file + configData := []byte("test config data") + configPath := filepath.Join(tempDir, "config.yaml") + err = os.WriteFile(configPath, configData, 0644) + require.NoError(t, err) + + inputs := artifact.Inputs{ + WorkflowOwner: chainsim.TestAddress, + WorkflowName: "test_workflow", + OutputPath: binaryPath, + ConfigPath: configPath, + } + + art, err := artifactBuilder.Build(inputs) + require.NoError(t, err) + assert.NotNil(t, art) + assert.Equal(t, []byte(encodedBinary), art.BinaryData) + assert.Equal(t, configData, art.ConfigData) + assert.NotEmpty(t, art.WorkflowID) + }) + + t.Run("success without config", func(t *testing.T) { + t.Parallel() + + tempDir := t.TempDir() + + // Create valid base64-encoded binary file + binaryData := []byte("test binary data") + encodedBinary := base64.StdEncoding.EncodeToString(binaryData) + binaryPath := filepath.Join(tempDir, "binary.wasm.br.b64") + err := os.WriteFile(binaryPath, []byte(encodedBinary), 0644) + require.NoError(t, err) + + inputs := artifact.Inputs{ + WorkflowOwner: chainsim.TestAddress, + WorkflowName: "test_workflow", + OutputPath: binaryPath, + ConfigPath: "", // No config + } + + art, err := artifactBuilder.Build(inputs) + require.NoError(t, err) + assert.NotNil(t, art) + assert.Equal(t, []byte(encodedBinary), art.BinaryData) + assert.Empty(t, art.ConfigData) + assert.NotEmpty(t, art.WorkflowID) + }) + + t.Run("error: workflow owner is empty", func(t *testing.T) { + t.Parallel() + + inputs := artifact.Inputs{ + WorkflowOwner: "", + WorkflowName: "test_workflow", + OutputPath: "binary.wasm", + ConfigPath: "", + } + + art, err := artifactBuilder.Build(inputs) + require.Error(t, err) + assert.Nil(t, art) + assert.Contains(t, err.Error(), "workflow owner is required") + }) + + t.Run("error: workflow name is empty", func(t *testing.T) { + t.Parallel() + + inputs := artifact.Inputs{ + WorkflowOwner: chainsim.TestAddress, + WorkflowName: "", + OutputPath: "binary.wasm", + ConfigPath: "", + } + + art, err := artifactBuilder.Build(inputs) + require.Error(t, err) + assert.Nil(t, art) + assert.Contains(t, err.Error(), "workflow name is required") + }) + + t.Run("error: invalid config path", func(t *testing.T) { + t.Parallel() + + tempDir := t.TempDir() + + // Create valid base64-encoded binary file + binaryData := []byte("test binary data") + encodedBinary := base64.StdEncoding.EncodeToString(binaryData) + binaryPath := filepath.Join(tempDir, "binary.wasm.br.b64") + err := os.WriteFile(binaryPath, []byte(encodedBinary), 0644) + require.NoError(t, err) + + inputs := artifact.Inputs{ + WorkflowOwner: chainsim.TestAddress, + WorkflowName: "test_workflow", + OutputPath: binaryPath, + ConfigPath: "/nonexistent/config.yaml", + } + + art, err := artifactBuilder.Build(inputs) + require.Error(t, err) + assert.Nil(t, art) + }) + + t.Run("error: invalid binary path", func(t *testing.T) { + t.Parallel() + + inputs := artifact.Inputs{ + WorkflowOwner: chainsim.TestAddress, + WorkflowName: "test_workflow", + OutputPath: "/nonexistent/binary.wasm", + ConfigPath: "", + } + + art, err := artifactBuilder.Build(inputs) + require.Error(t, err) + assert.Nil(t, art) + }) + + t.Run("error: invalid base64 binary data", func(t *testing.T) { + t.Parallel() + + tempDir := t.TempDir() + + // Create invalid base64 binary file + binaryPath := filepath.Join(tempDir, "binary.wasm.br.b64") + err := os.WriteFile(binaryPath, []byte("not-valid-base64!!!"), 0644) + require.NoError(t, err) + + inputs := artifact.Inputs{ + WorkflowOwner: chainsim.TestAddress, + WorkflowName: "test_workflow", + OutputPath: binaryPath, + ConfigPath: "", + } + + art, err := artifactBuilder.Build(inputs) + require.Error(t, err) + assert.Nil(t, art) + assert.Contains(t, err.Error(), "failed to decode base64 binary data") + }) +} + +func TestBuilder_BuildGeneratesConsistentWorkflowID(t *testing.T) { + t.Parallel() + + logger := testutil.NewTestLogger() + builder := artifact.NewBuilder(logger) + + tempDir := t.TempDir() + + // Create valid base64-encoded binary file + binaryData := []byte("test binary data for consistency check") + encodedBinary := base64.StdEncoding.EncodeToString(binaryData) + binaryPath := filepath.Join(tempDir, "binary.wasm.br.b64") + err := os.WriteFile(binaryPath, []byte(encodedBinary), 0644) + require.NoError(t, err) + + // Create config file + configData := []byte("test config data for consistency") + configPath := filepath.Join(tempDir, "config.yaml") + err = os.WriteFile(configPath, configData, 0644) + require.NoError(t, err) + + inputs := artifact.Inputs{ + WorkflowOwner: chainsim.TestAddress, + WorkflowName: "test_workflow", + OutputPath: binaryPath, + ConfigPath: configPath, + } + + // Build artifact twice with same inputs + artifact1, err := builder.Build(inputs) + require.NoError(t, err) + + artifact2, err := builder.Build(inputs) + require.NoError(t, err) + + // Workflow IDs should be identical for same inputs + assert.Equal(t, artifact1.WorkflowID, artifact2.WorkflowID) + assert.NotEmpty(t, artifact1.WorkflowID) +} + +func TestBuilder_BuildGeneratesDifferentWorkflowIDsForDifferentInputs(t *testing.T) { + t.Parallel() + + logger := testutil.NewTestLogger() + builder := artifact.NewBuilder(logger) + + tempDir := t.TempDir() + + // Create valid base64-encoded binary file + binaryData := []byte("test binary data") + encodedBinary := base64.StdEncoding.EncodeToString(binaryData) + binaryPath := filepath.Join(tempDir, "binary.wasm.br.b64") + err := os.WriteFile(binaryPath, []byte(encodedBinary), 0644) + require.NoError(t, err) + + // Create config file + configData := []byte("test config data") + configPath := filepath.Join(tempDir, "config.yaml") + err = os.WriteFile(configPath, configData, 0644) + require.NoError(t, err) + + // Build artifact with first workflow name + inputs1 := artifact.Inputs{ + WorkflowOwner: chainsim.TestAddress, + WorkflowName: "workflow_one", + OutputPath: binaryPath, + ConfigPath: configPath, + } + artifact1, err := builder.Build(inputs1) + require.NoError(t, err) + + // Build artifact with different workflow name + inputs2 := artifact.Inputs{ + WorkflowOwner: chainsim.TestAddress, + WorkflowName: "workflow_two", + OutputPath: binaryPath, + ConfigPath: configPath, + } + artifact2, err := builder.Build(inputs2) + require.NoError(t, err) + + // Workflow IDs should be different + assert.NotEqual(t, artifact1.WorkflowID, artifact2.WorkflowID) + assert.NotEmpty(t, artifact1.WorkflowID) + assert.NotEmpty(t, artifact2.WorkflowID) +} + +func TestBuilder_BuildWithDifferentOwners(t *testing.T) { + t.Parallel() + + logger := testutil.NewTestLogger() + builder := artifact.NewBuilder(logger) + + tempDir := t.TempDir() + + // Create valid base64-encoded binary file + binaryData := []byte("test binary data") + encodedBinary := base64.StdEncoding.EncodeToString(binaryData) + binaryPath := filepath.Join(tempDir, "binary.wasm.br.b64") + err := os.WriteFile(binaryPath, []byte(encodedBinary), 0644) + require.NoError(t, err) + + tests := []struct { + name string + workflowOwner string + }{ + { + name: "owner with 0x prefix", + workflowOwner: chainsim.TestAddress, + }, + { + name: "different owner address", + workflowOwner: "0x37250db56cb0dd17f7653de405c89d2ac1874a63", + }, + } + + var workflowIDs []string + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + inputs := artifact.Inputs{ + WorkflowOwner: tt.workflowOwner, + WorkflowName: "test_workflow", + OutputPath: binaryPath, + ConfigPath: "", + } + + art, err := builder.Build(inputs) + require.NoError(t, err) + assert.NotEmpty(t, art.WorkflowID) + workflowIDs = append(workflowIDs, art.WorkflowID) + }) + } + + // Ensure different owners produce different workflow IDs + if len(workflowIDs) >= 2 { + assert.NotEqual(t, workflowIDs[0], workflowIDs[1]) + } +} diff --git a/internal/build/compile_test.go b/internal/build/compile_test.go new file mode 100644 index 00000000..5a1ed7e2 --- /dev/null +++ b/internal/build/compile_test.go @@ -0,0 +1,129 @@ +package build_test + +import ( + "os" + "path/filepath" + "testing" + + "github.com/rs/zerolog" + "github.com/smartcontractkit/cre-cli/internal/build" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestResolveBuildParamsForWorkflow(t *testing.T) { + t.Parallel() + + t.Run("success", func(t *testing.T) { + t.Parallel() + + tempDir := t.TempDir() + workflowFile := filepath.Join(tempDir, "main.go") + err := os.WriteFile(workflowFile, []byte("package main"), 0644) + require.NoError(t, err) + + tests := []struct { + name string + workflowPath string + outputPath string + expectedMainFile string + expectedRootFolder string + expectedLanguage string + }{ + { + name: "go workflow", + workflowPath: workflowFile, + outputPath: "output.wasm", + expectedMainFile: "main.go", + expectedRootFolder: tempDir, + expectedLanguage: "golang", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + params, err := build.ResolveBuildParamsForWorkflow(tt.workflowPath, tt.outputPath) + require.NoError(t, err) + assert.Equal(t, tt.expectedMainFile, params.WorkflowMainFile) + assert.Equal(t, tt.expectedRootFolder, params.WorkflowRootFolder) + assert.Equal(t, tt.expectedLanguage, params.WorkflowLanguage) + assert.Equal(t, tt.outputPath, params.OutputPath) + assert.Contains(t, params.WorkflowPath, tt.expectedMainFile) + }) + } + }) + + t.Run("typescript workflow", func(t *testing.T) { + t.Parallel() + + tempDir := t.TempDir() + workflowFile := filepath.Join(tempDir, "main.ts") + err := os.WriteFile(workflowFile, []byte("console.log('test')"), 0644) + require.NoError(t, err) + + params, err := build.ResolveBuildParamsForWorkflow(workflowFile, "output.wasm") + require.NoError(t, err) + assert.Equal(t, "main.ts", params.WorkflowMainFile) + assert.Equal(t, tempDir, params.WorkflowRootFolder) + assert.Equal(t, "typescript", params.WorkflowLanguage) + }) + + t.Run("errors", func(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + workflowPath string + wantErr string + }{ + { + name: "workflow file not found", + workflowPath: "/nonexistent/path/main.go", + wantErr: "workflow file not found", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + _, err := build.ResolveBuildParamsForWorkflow(tt.workflowPath, "output.wasm") + require.Error(t, err) + assert.Contains(t, err.Error(), tt.wantErr) + }) + } + }) +} + +func TestBuilderCompileAndSave(t *testing.T) { + t.Parallel() + + logger := zerolog.New(os.Stdout) + builder := build.NewBuilder(&logger) + + t.Run("error when output path is empty", func(t *testing.T) { + t.Parallel() + + params := build.Params{ + OutputPath: "", + } + + err := builder.CompileAndSave(params) + require.Error(t, err) + assert.Contains(t, err.Error(), "output path is not specified") + }) + + t.Run("success", func(t *testing.T) { + t.Parallel() + + workflowPath := filepath.Join("testdata", "basic_workflow", "main.go") + outputPath := "./binary.wasm.br.b64" + defer os.Remove(outputPath) + + params, err := build.ResolveBuildParamsForWorkflow(workflowPath, outputPath) + require.NoError(t, err) + + err = builder.CompileAndSave(params) + require.NoError(t, err) + + assert.FileExists(t, outputPath) + }) +} diff --git a/internal/build/testdata/basic_workflow/go.mod b/internal/build/testdata/basic_workflow/go.mod new file mode 100644 index 00000000..678a7bc7 --- /dev/null +++ b/internal/build/testdata/basic_workflow/go.mod @@ -0,0 +1,25 @@ +module testworkflow + +go 1.24.5 + +require ( + github.com/smartcontractkit/cre-sdk-go v1.0.0 + github.com/smartcontractkit/cre-sdk-go/capabilities/scheduler/cron v1.0.0-beta.0 +) + +require ( + github.com/kr/pretty v0.3.1 // indirect + github.com/rogpeppe/go-internal v1.12.0 // indirect + github.com/shopspring/decimal v1.4.0 // indirect + gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect +) + +require ( + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect + github.com/go-viper/mapstructure/v2 v2.4.0 // indirect + github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect + github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20250911124514-5874cc6d62b2 // indirect + github.com/stretchr/testify v1.10.0 // indirect + google.golang.org/protobuf v1.36.7 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/internal/build/testdata/basic_workflow/go.sum b/internal/build/testdata/basic_workflow/go.sum new file mode 100644 index 00000000..fe26ae1a --- /dev/null +++ b/internal/build/testdata/basic_workflow/go.sum @@ -0,0 +1,37 @@ +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs= +github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= +github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= +github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= +github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k= +github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME= +github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20250911124514-5874cc6d62b2 h1:1/KdO5AbUr3CmpLjMPuJXPo2wHMbfB8mldKLsg7D4M8= +github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20250911124514-5874cc6d62b2/go.mod h1:jUC52kZzEnWF9tddHh85zolKybmLpbQ1oNA4FjOHt1Q= +github.com/smartcontractkit/cre-sdk-go v1.0.0 h1:O52/QDmw/W8SJ7HQ9ASlVx7alSMGsewjL0Y8WZmgf5w= +github.com/smartcontractkit/cre-sdk-go v1.0.0/go.mod h1:CQY8hCISjctPmt8ViDVgFm4vMGLs5fYI198QhkBS++Y= +github.com/smartcontractkit/cre-sdk-go/capabilities/scheduler/cron v1.0.0-beta.0 h1:Tui4xQVln7Qtk3CgjBRgDfihgEaAJy2t2MofghiGIDA= +github.com/smartcontractkit/cre-sdk-go/capabilities/scheduler/cron v1.0.0-beta.0/go.mod h1:PWyrIw16It4TSyq6mDXqmSR0jF2evZRKuBxu7pK1yDw= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +google.golang.org/protobuf v1.36.7 h1:IgrO7UwFQGJdRNXH/sQux4R1Dj1WAKcLElzeeRaXV2A= +google.golang.org/protobuf v1.36.7/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/internal/build/testdata/basic_workflow/main.go b/internal/build/testdata/basic_workflow/main.go new file mode 100644 index 00000000..ea7abd38 --- /dev/null +++ b/internal/build/testdata/basic_workflow/main.go @@ -0,0 +1,36 @@ +//go:build wasip1 + +package main + +import ( + "log/slog" + + "github.com/smartcontractkit/cre-sdk-go/capabilities/scheduler/cron" + "github.com/smartcontractkit/cre-sdk-go/cre" + "github.com/smartcontractkit/cre-sdk-go/cre/wasm" +) + +type Config struct { + Schedule string `json:"schedule"` +} + +func InitWorkflow(config *Config, logger *slog.Logger, secretsProvider cre.SecretsProvider) (cre.Workflow[*Config], error) { + cronTriggerCfg := &cron.Config{ + Schedule: config.Schedule, + } + + return cre.Workflow[*Config]{ + cre.Handler( + cron.Trigger(cronTriggerCfg), + onCronTrigger), + }, nil +} + +func onCronTrigger(config *Config, runtime cre.Runtime, outputs *cron.Payload) (string, error) { + runtime.Logger().Info("workflow triggered") + return "success", nil +} + +func main() { + wasm.NewRunner(cre.ParseJSON[Config]).Run(InitWorkflow) +} From 26f948e0b552b82d863d0e23074b719590e953fb Mon Sep 17 00:00:00 2001 From: Tarcisio Ferraz Date: Tue, 16 Dec 2025 11:23:31 -0300 Subject: [PATCH 08/11] fix lint --- internal/artifact/builder_test.go | 24 ++++++++++++------------ internal/build/compile_test.go | 7 ++++--- 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/internal/artifact/builder_test.go b/internal/artifact/builder_test.go index 20699d4e..fabd54d3 100644 --- a/internal/artifact/builder_test.go +++ b/internal/artifact/builder_test.go @@ -6,12 +6,12 @@ import ( "path/filepath" "testing" - "github.com/smartcontractkit/cre-cli/internal/artifact" - "github.com/smartcontractkit/cre-cli/internal/testutil/chainsim" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/smartcontractkit/cre-cli/internal/artifact" "github.com/smartcontractkit/cre-cli/internal/testutil" + "github.com/smartcontractkit/cre-cli/internal/testutil/chainsim" ) func TestBuilder_Build(t *testing.T) { @@ -29,13 +29,13 @@ func TestBuilder_Build(t *testing.T) { binaryData := []byte("test binary data") encodedBinary := base64.StdEncoding.EncodeToString(binaryData) binaryPath := filepath.Join(tempDir, "binary.wasm.br.b64") - err := os.WriteFile(binaryPath, []byte(encodedBinary), 0644) + err := os.WriteFile(binaryPath, []byte(encodedBinary), 0600) require.NoError(t, err) // Create config file configData := []byte("test config data") configPath := filepath.Join(tempDir, "config.yaml") - err = os.WriteFile(configPath, configData, 0644) + err = os.WriteFile(configPath, configData, 0600) require.NoError(t, err) inputs := artifact.Inputs{ @@ -62,7 +62,7 @@ func TestBuilder_Build(t *testing.T) { binaryData := []byte("test binary data") encodedBinary := base64.StdEncoding.EncodeToString(binaryData) binaryPath := filepath.Join(tempDir, "binary.wasm.br.b64") - err := os.WriteFile(binaryPath, []byte(encodedBinary), 0644) + err := os.WriteFile(binaryPath, []byte(encodedBinary), 0600) require.NoError(t, err) inputs := artifact.Inputs{ @@ -121,7 +121,7 @@ func TestBuilder_Build(t *testing.T) { binaryData := []byte("test binary data") encodedBinary := base64.StdEncoding.EncodeToString(binaryData) binaryPath := filepath.Join(tempDir, "binary.wasm.br.b64") - err := os.WriteFile(binaryPath, []byte(encodedBinary), 0644) + err := os.WriteFile(binaryPath, []byte(encodedBinary), 0600) require.NoError(t, err) inputs := artifact.Inputs{ @@ -158,7 +158,7 @@ func TestBuilder_Build(t *testing.T) { // Create invalid base64 binary file binaryPath := filepath.Join(tempDir, "binary.wasm.br.b64") - err := os.WriteFile(binaryPath, []byte("not-valid-base64!!!"), 0644) + err := os.WriteFile(binaryPath, []byte("not-valid-base64!!!"), 0600) require.NoError(t, err) inputs := artifact.Inputs{ @@ -187,13 +187,13 @@ func TestBuilder_BuildGeneratesConsistentWorkflowID(t *testing.T) { binaryData := []byte("test binary data for consistency check") encodedBinary := base64.StdEncoding.EncodeToString(binaryData) binaryPath := filepath.Join(tempDir, "binary.wasm.br.b64") - err := os.WriteFile(binaryPath, []byte(encodedBinary), 0644) + err := os.WriteFile(binaryPath, []byte(encodedBinary), 0600) require.NoError(t, err) // Create config file configData := []byte("test config data for consistency") configPath := filepath.Join(tempDir, "config.yaml") - err = os.WriteFile(configPath, configData, 0644) + err = os.WriteFile(configPath, configData, 0600) require.NoError(t, err) inputs := artifact.Inputs{ @@ -227,13 +227,13 @@ func TestBuilder_BuildGeneratesDifferentWorkflowIDsForDifferentInputs(t *testing binaryData := []byte("test binary data") encodedBinary := base64.StdEncoding.EncodeToString(binaryData) binaryPath := filepath.Join(tempDir, "binary.wasm.br.b64") - err := os.WriteFile(binaryPath, []byte(encodedBinary), 0644) + err := os.WriteFile(binaryPath, []byte(encodedBinary), 0600) require.NoError(t, err) // Create config file configData := []byte("test config data") configPath := filepath.Join(tempDir, "config.yaml") - err = os.WriteFile(configPath, configData, 0644) + err = os.WriteFile(configPath, configData, 0600) require.NoError(t, err) // Build artifact with first workflow name @@ -274,7 +274,7 @@ func TestBuilder_BuildWithDifferentOwners(t *testing.T) { binaryData := []byte("test binary data") encodedBinary := base64.StdEncoding.EncodeToString(binaryData) binaryPath := filepath.Join(tempDir, "binary.wasm.br.b64") - err := os.WriteFile(binaryPath, []byte(encodedBinary), 0644) + err := os.WriteFile(binaryPath, []byte(encodedBinary), 0600) require.NoError(t, err) tests := []struct { diff --git a/internal/build/compile_test.go b/internal/build/compile_test.go index 5a1ed7e2..62ef7e1d 100644 --- a/internal/build/compile_test.go +++ b/internal/build/compile_test.go @@ -6,9 +6,10 @@ import ( "testing" "github.com/rs/zerolog" - "github.com/smartcontractkit/cre-cli/internal/build" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + + "github.com/smartcontractkit/cre-cli/internal/build" ) func TestResolveBuildParamsForWorkflow(t *testing.T) { @@ -19,7 +20,7 @@ func TestResolveBuildParamsForWorkflow(t *testing.T) { tempDir := t.TempDir() workflowFile := filepath.Join(tempDir, "main.go") - err := os.WriteFile(workflowFile, []byte("package main"), 0644) + err := os.WriteFile(workflowFile, []byte("package main"), 0600) require.NoError(t, err) tests := []struct { @@ -58,7 +59,7 @@ func TestResolveBuildParamsForWorkflow(t *testing.T) { tempDir := t.TempDir() workflowFile := filepath.Join(tempDir, "main.ts") - err := os.WriteFile(workflowFile, []byte("console.log('test')"), 0644) + err := os.WriteFile(workflowFile, []byte("console.log('test')"), 0600) require.NoError(t, err) params, err := build.ResolveBuildParamsForWorkflow(workflowFile, "output.wasm") From 0deba2b9653d5fb9f5d44d7744a3ea5cb8d1c66d Mon Sep 17 00:00:00 2001 From: Tarcisio Ferraz Date: Thu, 18 Dec 2025 08:04:53 -0300 Subject: [PATCH 09/11] rename command to verb --- cmd/workflow/{id/id.go => generate-id/generate_id.go} | 6 +++--- cmd/workflow/workflow.go | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) rename cmd/workflow/{id/id.go => generate-id/generate_id.go} (96%) diff --git a/cmd/workflow/id/id.go b/cmd/workflow/generate-id/generate_id.go similarity index 96% rename from cmd/workflow/id/id.go rename to cmd/workflow/generate-id/generate_id.go index a636902f..ee807b31 100644 --- a/cmd/workflow/id/id.go +++ b/cmd/workflow/generate-id/generate_id.go @@ -1,4 +1,4 @@ -package id +package generate_id import ( "fmt" @@ -38,10 +38,10 @@ type handler struct { func New(ctx *runtime.Context) *cobra.Command { cmd := &cobra.Command{ - Use: "id ", + Use: "generate-id ", Short: "Display the workflow ID", Args: cobra.ExactArgs(1), - Example: `cre workflow id ./my-workflow`, + Example: `cre workflow generate-id ./my-workflow`, RunE: func(cmd *cobra.Command, args []string) error { h := newHandler(ctx) diff --git a/cmd/workflow/workflow.go b/cmd/workflow/workflow.go index 6221bf8f..6bd9a783 100644 --- a/cmd/workflow/workflow.go +++ b/cmd/workflow/workflow.go @@ -6,7 +6,7 @@ import ( "github.com/smartcontractkit/cre-cli/cmd/workflow/activate" "github.com/smartcontractkit/cre-cli/cmd/workflow/delete" "github.com/smartcontractkit/cre-cli/cmd/workflow/deploy" - "github.com/smartcontractkit/cre-cli/cmd/workflow/id" + generateid "github.com/smartcontractkit/cre-cli/cmd/workflow/generate-id" "github.com/smartcontractkit/cre-cli/cmd/workflow/pause" "github.com/smartcontractkit/cre-cli/cmd/workflow/simulate" "github.com/smartcontractkit/cre-cli/cmd/workflow/test" @@ -26,7 +26,7 @@ func New(runtimeContext *runtime.Context) *cobra.Command { workflowCmd.AddCommand(test.New(runtimeContext)) workflowCmd.AddCommand(deploy.New(runtimeContext)) workflowCmd.AddCommand(simulate.New(runtimeContext)) - workflowCmd.AddCommand(id.New(runtimeContext)) + workflowCmd.AddCommand(generateid.New(runtimeContext)) return workflowCmd } From 5cac33ddc16fa249bbc3dfd0538db86cd73306ea Mon Sep 17 00:00:00 2001 From: Tarcisio Ferraz Date: Thu, 18 Dec 2025 09:10:54 -0300 Subject: [PATCH 10/11] update docs --- docs/cre_workflow.md | 1 + docs/cre_workflow_generate-id.md | 35 ++++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+) create mode 100644 docs/cre_workflow_generate-id.md diff --git a/docs/cre_workflow.md b/docs/cre_workflow.md index a5b83833..d6af7fb6 100644 --- a/docs/cre_workflow.md +++ b/docs/cre_workflow.md @@ -31,6 +31,7 @@ cre workflow [optional flags] * [cre workflow activate](cre_workflow_activate.md) - Activates workflow on the Workflow Registry contract * [cre workflow delete](cre_workflow_delete.md) - Deletes all versions of a workflow from the Workflow Registry * [cre workflow deploy](cre_workflow_deploy.md) - Deploys a workflow to the Workflow Registry contract +* [cre workflow generate-id](cre_workflow_generate-id.md) - Display the workflow ID * [cre workflow pause](cre_workflow_pause.md) - Pauses workflow on the Workflow Registry contract * [cre workflow simulate](cre_workflow_simulate.md) - Simulates a workflow diff --git a/docs/cre_workflow_generate-id.md b/docs/cre_workflow_generate-id.md new file mode 100644 index 00000000..772361be --- /dev/null +++ b/docs/cre_workflow_generate-id.md @@ -0,0 +1,35 @@ +## cre workflow generate-id + +Display the workflow ID + +``` +cre workflow generate-id [optional flags] +``` + +### Examples + +``` +cre workflow generate-id ./my-workflow +``` + +### Options + +``` + -h, --help help for generate-id + -o, --output string The output file for the compiled WASM binary encoded in base64 (default "./binary.wasm.br.b64") + --owner string Workflow owner address +``` + +### Options inherited from parent commands + +``` + -e, --env string Path to .env file which contains sensitive info (default ".env") + -R, --project-root string Path to the project root + -T, --target string Use target settings from YAML config + -v, --verbose Run command in VERBOSE mode +``` + +### SEE ALSO + +* [cre workflow](cre_workflow.md) - Manages workflows + From a666e357267173559ada3475583f19ccd5e84994 Mon Sep 17 00:00:00 2001 From: Tarcisio Ferraz Date: Thu, 18 Dec 2025 09:49:03 -0300 Subject: [PATCH 11/11] rename package --- cmd/workflow/deploy/artifacts_test.go | 14 ++-- cmd/workflow/deploy/deploy.go | 10 +-- cmd/workflow/deploy/prepare.go | 6 +- cmd/workflow/deploy/register_test.go | 4 +- cmd/workflow/generate-id/generate_id.go | 8 +- internal/{artifact => artifacts}/artifact.go | 2 +- internal/{artifact => artifacts}/builder.go | 16 ++-- .../{artifact => artifacts}/builder_test.go | 80 +++++++++---------- 8 files changed, 71 insertions(+), 69 deletions(-) rename internal/{artifact => artifacts}/artifact.go (82%) rename internal/{artifact => artifacts}/builder.go (82%) rename internal/{artifact => artifacts}/builder_test.go (82%) diff --git a/cmd/workflow/deploy/artifacts_test.go b/cmd/workflow/deploy/artifacts_test.go index 1465d4ca..4f08ad4d 100644 --- a/cmd/workflow/deploy/artifacts_test.go +++ b/cmd/workflow/deploy/artifacts_test.go @@ -12,7 +12,7 @@ import ( "github.com/jarcoal/httpmock" "github.com/stretchr/testify/require" - "github.com/smartcontractkit/cre-cli/internal/artifact" + "github.com/smartcontractkit/cre-cli/internal/artifacts" "github.com/smartcontractkit/cre-cli/internal/credentials" "github.com/smartcontractkit/cre-cli/internal/testutil/chainsim" ) @@ -92,7 +92,7 @@ func TestUpload_SuccessAndErrorCases(t *testing.T) { h.environmentSet.GraphQLURL = "http://graphql.endpoint" // Success case : uploading binary and config data - h.workflowArtifact = &artifact.Artifact{ + h.workflowArtifact = &artifacts.Artifact{ BinaryData: []byte("binarydata"), ConfigData: []byte("configdata"), WorkflowID: "workflow-id", @@ -103,7 +103,7 @@ func TestUpload_SuccessAndErrorCases(t *testing.T) { require.Equal(t, "http://origin/get", *h.inputs.ConfigURL) // Success: empty ConfigData - h.workflowArtifact = &artifact.Artifact{ + h.workflowArtifact = &artifacts.Artifact{ BinaryData: []byte("binarydata"), ConfigData: nil, WorkflowID: "workflow-id", @@ -117,7 +117,7 @@ func TestUpload_SuccessAndErrorCases(t *testing.T) { require.ErrorContains(t, err, "workflowArtifact is nil") // Error: empty BinaryData - h.workflowArtifact = &artifact.Artifact{ + h.workflowArtifact = &artifacts.Artifact{ BinaryData: nil, ConfigData: []byte("configdata"), WorkflowID: "workflow-id", @@ -126,7 +126,7 @@ func TestUpload_SuccessAndErrorCases(t *testing.T) { require.ErrorContains(t, err, "uploading binary artifact: content is empty for artifactType BINARY") // Error: workflowID is empty - h.workflowArtifact = &artifact.Artifact{ + h.workflowArtifact = &artifacts.Artifact{ BinaryData: []byte("binarydata"), ConfigData: []byte("configdata"), WorkflowID: "", @@ -167,7 +167,7 @@ func TestUploadArtifactToStorageService_OriginError(t *testing.T) { // Patch settings to use mock GraphQL endpoint h.environmentSet.GraphQLURL = "http://graphql.endpoint" - h.workflowArtifact = &artifact.Artifact{ + h.workflowArtifact = &artifacts.Artifact{ BinaryData: []byte("binarydata"), ConfigData: []byte("configdata"), WorkflowID: "workflow-id", @@ -233,7 +233,7 @@ func TestUploadArtifactToStorageService_AlreadyExistsError(t *testing.T) { // Patch settings to use mock GraphQL endpoint h.environmentSet.GraphQLURL = "http://graphql.endpoint" - h.workflowArtifact = &artifact.Artifact{ + h.workflowArtifact = &artifacts.Artifact{ BinaryData: []byte("binarydata"), ConfigData: []byte("configdata"), WorkflowID: "workflow-id", diff --git a/cmd/workflow/deploy/deploy.go b/cmd/workflow/deploy/deploy.go index 68b2fcb1..97947674 100644 --- a/cmd/workflow/deploy/deploy.go +++ b/cmd/workflow/deploy/deploy.go @@ -13,7 +13,7 @@ import ( "github.com/spf13/viper" "github.com/smartcontractkit/cre-cli/cmd/client" - "github.com/smartcontractkit/cre-cli/internal/artifact" + "github.com/smartcontractkit/cre-cli/internal/artifacts" "github.com/smartcontractkit/cre-cli/internal/build" "github.com/smartcontractkit/cre-cli/internal/constants" "github.com/smartcontractkit/cre-cli/internal/credentials" @@ -61,11 +61,11 @@ type handler struct { stdin io.Reader credentials *credentials.Credentials environmentSet *environments.EnvironmentSet - workflowArtifact *artifact.Artifact + workflowArtifact *artifacts.Artifact wrc *client.WorkflowRegistryV2Client runtimeContext *runtime.Context builder *build.Builder - artifactBuilder *artifact.Builder + artifactBuilder *artifacts.Builder validated bool @@ -115,9 +115,9 @@ func newHandler(ctx *runtime.Context, stdin io.Reader) *handler { stdin: stdin, credentials: ctx.Credentials, environmentSet: ctx.EnvironmentSet, - workflowArtifact: &artifact.Artifact{}, + workflowArtifact: &artifacts.Artifact{}, builder: build.NewBuilder(ctx.Logger), - artifactBuilder: artifact.NewBuilder(ctx.Logger), + artifactBuilder: artifacts.NewBuilder(ctx.Logger), wrc: nil, runtimeContext: ctx, validated: false, diff --git a/cmd/workflow/deploy/prepare.go b/cmd/workflow/deploy/prepare.go index 9ff7704f..68626883 100644 --- a/cmd/workflow/deploy/prepare.go +++ b/cmd/workflow/deploy/prepare.go @@ -1,9 +1,11 @@ package deploy -import "github.com/smartcontractkit/cre-cli/internal/artifact" +import ( + "github.com/smartcontractkit/cre-cli/internal/artifacts" +) func (h *handler) PrepareWorkflowArtifact() (err error) { - h.workflowArtifact, err = h.artifactBuilder.Build(artifact.Inputs{ + h.workflowArtifact, err = h.artifactBuilder.Build(artifacts.Inputs{ WorkflowOwner: h.inputs.WorkflowOwner, WorkflowName: h.inputs.WorkflowName, OutputPath: h.inputs.OutputPath, diff --git a/cmd/workflow/deploy/register_test.go b/cmd/workflow/deploy/register_test.go index ed5b2fa9..1188bcc1 100644 --- a/cmd/workflow/deploy/register_test.go +++ b/cmd/workflow/deploy/register_test.go @@ -6,7 +6,7 @@ import ( "github.com/stretchr/testify/require" - "github.com/smartcontractkit/cre-cli/internal/artifact" + "github.com/smartcontractkit/cre-cli/internal/artifacts" "github.com/smartcontractkit/cre-cli/internal/testutil/chainsim" ) @@ -56,7 +56,7 @@ func TestWorkflowUpsert(t *testing.T) { err = handler.ValidateInputs() require.NoError(t, err) - wfArt := artifact.Artifact{ + wfArt := artifacts.Artifact{ BinaryData: []byte("0x1234"), ConfigData: []byte("config"), WorkflowID: "1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef", diff --git a/cmd/workflow/generate-id/generate_id.go b/cmd/workflow/generate-id/generate_id.go index ee807b31..9c347374 100644 --- a/cmd/workflow/generate-id/generate_id.go +++ b/cmd/workflow/generate-id/generate_id.go @@ -7,7 +7,7 @@ import ( "github.com/spf13/cobra" "github.com/spf13/viper" - "github.com/smartcontractkit/cre-cli/internal/artifact" + "github.com/smartcontractkit/cre-cli/internal/artifacts" "github.com/smartcontractkit/cre-cli/internal/build" "github.com/smartcontractkit/cre-cli/internal/runtime" "github.com/smartcontractkit/cre-cli/internal/settings" @@ -31,7 +31,7 @@ type handler struct { log *zerolog.Logger settings *settings.Settings builder *build.Builder - artifactBuilder *artifact.Builder + artifactBuilder *artifacts.Builder inputs Inputs } @@ -66,7 +66,7 @@ func newHandler(ctx *runtime.Context) *handler { log: ctx.Logger, settings: ctx.Settings, builder: build.NewBuilder(ctx.Logger), - artifactBuilder: artifact.NewBuilder(ctx.Logger), + artifactBuilder: artifacts.NewBuilder(ctx.Logger), } } @@ -84,7 +84,7 @@ func (h *handler) Execute() error { return fmt.Errorf("failed to compile workflow: %w", err) } - workflowArtifact, err := h.artifactBuilder.Build(artifact.Inputs{ + workflowArtifact, err := h.artifactBuilder.Build(artifacts.Inputs{ WorkflowOwner: h.inputs.WorkflowOwner, WorkflowName: h.inputs.WorkflowName, OutputPath: h.inputs.OutputPath, diff --git a/internal/artifact/artifact.go b/internal/artifacts/artifact.go similarity index 82% rename from internal/artifact/artifact.go rename to internal/artifacts/artifact.go index c5735e8c..7b736de8 100644 --- a/internal/artifact/artifact.go +++ b/internal/artifacts/artifact.go @@ -1,4 +1,4 @@ -package artifact +package artifacts type Artifact struct { BinaryData []byte diff --git a/internal/artifact/builder.go b/internal/artifacts/builder.go similarity index 82% rename from internal/artifact/builder.go rename to internal/artifacts/builder.go index dfe535aa..1b0200b4 100644 --- a/internal/artifact/builder.go +++ b/internal/artifacts/builder.go @@ -1,4 +1,4 @@ -package artifact +package artifacts import ( "encoding/base64" @@ -27,8 +27,8 @@ func NewBuilder(log *zerolog.Logger) *Builder { } } -func (b *Builder) Build(inputs Inputs) (art *Artifact, err error) { - art = &Artifact{} +func (b *Builder) Build(inputs Inputs) (artifact *Artifact, err error) { + artifact = &Artifact{} if inputs.WorkflowOwner == "" { return nil, fmt.Errorf("workflow owner is required") @@ -38,27 +38,27 @@ func (b *Builder) Build(inputs Inputs) (art *Artifact, err error) { return nil, fmt.Errorf("workflow name is required") } - art.ConfigData, err = b.prepareWorkflowConfig(inputs.ConfigPath) + artifact.ConfigData, err = b.prepareWorkflowConfig(inputs.ConfigPath) if err != nil { return nil, err } - art.BinaryData, err = b.prepareWorkflowBinary(inputs.OutputPath) + artifact.BinaryData, err = b.prepareWorkflowBinary(inputs.OutputPath) if err != nil { return nil, err } - binaryDataDecoded, err := decodeBinaryData(art.BinaryData) + binaryDataDecoded, err := decodeBinaryData(artifact.BinaryData) if err != nil { return nil, err } - art.WorkflowID, err = workflowUtils.GenerateWorkflowIDFromStrings(inputs.WorkflowOwner, inputs.WorkflowName, binaryDataDecoded, art.ConfigData, "") + artifact.WorkflowID, err = workflowUtils.GenerateWorkflowIDFromStrings(inputs.WorkflowOwner, inputs.WorkflowName, binaryDataDecoded, artifact.ConfigData, "") if err != nil { return nil, fmt.Errorf("failed to generate workflow ID: %w", err) } - return art, nil + return artifact, nil } func decodeBinaryData(binaryData []byte) ([]byte, error) { diff --git a/internal/artifact/builder_test.go b/internal/artifacts/builder_test.go similarity index 82% rename from internal/artifact/builder_test.go rename to internal/artifacts/builder_test.go index fabd54d3..fe7a3c22 100644 --- a/internal/artifact/builder_test.go +++ b/internal/artifacts/builder_test.go @@ -1,4 +1,4 @@ -package artifact_test +package artifacts_test import ( "encoding/base64" @@ -9,7 +9,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/smartcontractkit/cre-cli/internal/artifact" + "github.com/smartcontractkit/cre-cli/internal/artifacts" "github.com/smartcontractkit/cre-cli/internal/testutil" "github.com/smartcontractkit/cre-cli/internal/testutil/chainsim" ) @@ -18,7 +18,7 @@ func TestBuilder_Build(t *testing.T) { t.Parallel() logger := testutil.NewTestLogger() - artifactBuilder := artifact.NewBuilder(logger) + artifactBuilder := artifacts.NewBuilder(logger) t.Run("success with config", func(t *testing.T) { t.Parallel() @@ -38,19 +38,19 @@ func TestBuilder_Build(t *testing.T) { err = os.WriteFile(configPath, configData, 0600) require.NoError(t, err) - inputs := artifact.Inputs{ + inputs := artifacts.Inputs{ WorkflowOwner: chainsim.TestAddress, WorkflowName: "test_workflow", OutputPath: binaryPath, ConfigPath: configPath, } - art, err := artifactBuilder.Build(inputs) + artifact, err := artifactBuilder.Build(inputs) require.NoError(t, err) - assert.NotNil(t, art) - assert.Equal(t, []byte(encodedBinary), art.BinaryData) - assert.Equal(t, configData, art.ConfigData) - assert.NotEmpty(t, art.WorkflowID) + assert.NotNil(t, artifact) + assert.Equal(t, []byte(encodedBinary), artifact.BinaryData) + assert.Equal(t, configData, artifact.ConfigData) + assert.NotEmpty(t, artifact.WorkflowID) }) t.Run("success without config", func(t *testing.T) { @@ -65,50 +65,50 @@ func TestBuilder_Build(t *testing.T) { err := os.WriteFile(binaryPath, []byte(encodedBinary), 0600) require.NoError(t, err) - inputs := artifact.Inputs{ + inputs := artifacts.Inputs{ WorkflowOwner: chainsim.TestAddress, WorkflowName: "test_workflow", OutputPath: binaryPath, ConfigPath: "", // No config } - art, err := artifactBuilder.Build(inputs) + artifact, err := artifactBuilder.Build(inputs) require.NoError(t, err) - assert.NotNil(t, art) - assert.Equal(t, []byte(encodedBinary), art.BinaryData) - assert.Empty(t, art.ConfigData) - assert.NotEmpty(t, art.WorkflowID) + assert.NotNil(t, artifact) + assert.Equal(t, []byte(encodedBinary), artifact.BinaryData) + assert.Empty(t, artifact.ConfigData) + assert.NotEmpty(t, artifact.WorkflowID) }) t.Run("error: workflow owner is empty", func(t *testing.T) { t.Parallel() - inputs := artifact.Inputs{ + inputs := artifacts.Inputs{ WorkflowOwner: "", WorkflowName: "test_workflow", OutputPath: "binary.wasm", ConfigPath: "", } - art, err := artifactBuilder.Build(inputs) + artifact, err := artifactBuilder.Build(inputs) require.Error(t, err) - assert.Nil(t, art) + assert.Nil(t, artifact) assert.Contains(t, err.Error(), "workflow owner is required") }) t.Run("error: workflow name is empty", func(t *testing.T) { t.Parallel() - inputs := artifact.Inputs{ + inputs := artifacts.Inputs{ WorkflowOwner: chainsim.TestAddress, WorkflowName: "", OutputPath: "binary.wasm", ConfigPath: "", } - art, err := artifactBuilder.Build(inputs) + artifact, err := artifactBuilder.Build(inputs) require.Error(t, err) - assert.Nil(t, art) + assert.Nil(t, artifact) assert.Contains(t, err.Error(), "workflow name is required") }) @@ -124,31 +124,31 @@ func TestBuilder_Build(t *testing.T) { err := os.WriteFile(binaryPath, []byte(encodedBinary), 0600) require.NoError(t, err) - inputs := artifact.Inputs{ + inputs := artifacts.Inputs{ WorkflowOwner: chainsim.TestAddress, WorkflowName: "test_workflow", OutputPath: binaryPath, ConfigPath: "/nonexistent/config.yaml", } - art, err := artifactBuilder.Build(inputs) + artifact, err := artifactBuilder.Build(inputs) require.Error(t, err) - assert.Nil(t, art) + assert.Nil(t, artifact) }) t.Run("error: invalid binary path", func(t *testing.T) { t.Parallel() - inputs := artifact.Inputs{ + inputs := artifacts.Inputs{ WorkflowOwner: chainsim.TestAddress, WorkflowName: "test_workflow", OutputPath: "/nonexistent/binary.wasm", ConfigPath: "", } - art, err := artifactBuilder.Build(inputs) + artifact, err := artifactBuilder.Build(inputs) require.Error(t, err) - assert.Nil(t, art) + assert.Nil(t, artifact) }) t.Run("error: invalid base64 binary data", func(t *testing.T) { @@ -161,16 +161,16 @@ func TestBuilder_Build(t *testing.T) { err := os.WriteFile(binaryPath, []byte("not-valid-base64!!!"), 0600) require.NoError(t, err) - inputs := artifact.Inputs{ + inputs := artifacts.Inputs{ WorkflowOwner: chainsim.TestAddress, WorkflowName: "test_workflow", OutputPath: binaryPath, ConfigPath: "", } - art, err := artifactBuilder.Build(inputs) + artifact, err := artifactBuilder.Build(inputs) require.Error(t, err) - assert.Nil(t, art) + assert.Nil(t, artifact) assert.Contains(t, err.Error(), "failed to decode base64 binary data") }) } @@ -179,7 +179,7 @@ func TestBuilder_BuildGeneratesConsistentWorkflowID(t *testing.T) { t.Parallel() logger := testutil.NewTestLogger() - builder := artifact.NewBuilder(logger) + builder := artifacts.NewBuilder(logger) tempDir := t.TempDir() @@ -196,7 +196,7 @@ func TestBuilder_BuildGeneratesConsistentWorkflowID(t *testing.T) { err = os.WriteFile(configPath, configData, 0600) require.NoError(t, err) - inputs := artifact.Inputs{ + inputs := artifacts.Inputs{ WorkflowOwner: chainsim.TestAddress, WorkflowName: "test_workflow", OutputPath: binaryPath, @@ -219,7 +219,7 @@ func TestBuilder_BuildGeneratesDifferentWorkflowIDsForDifferentInputs(t *testing t.Parallel() logger := testutil.NewTestLogger() - builder := artifact.NewBuilder(logger) + builder := artifacts.NewBuilder(logger) tempDir := t.TempDir() @@ -237,7 +237,7 @@ func TestBuilder_BuildGeneratesDifferentWorkflowIDsForDifferentInputs(t *testing require.NoError(t, err) // Build artifact with first workflow name - inputs1 := artifact.Inputs{ + inputs1 := artifacts.Inputs{ WorkflowOwner: chainsim.TestAddress, WorkflowName: "workflow_one", OutputPath: binaryPath, @@ -247,7 +247,7 @@ func TestBuilder_BuildGeneratesDifferentWorkflowIDsForDifferentInputs(t *testing require.NoError(t, err) // Build artifact with different workflow name - inputs2 := artifact.Inputs{ + inputs2 := artifacts.Inputs{ WorkflowOwner: chainsim.TestAddress, WorkflowName: "workflow_two", OutputPath: binaryPath, @@ -266,7 +266,7 @@ func TestBuilder_BuildWithDifferentOwners(t *testing.T) { t.Parallel() logger := testutil.NewTestLogger() - builder := artifact.NewBuilder(logger) + builder := artifacts.NewBuilder(logger) tempDir := t.TempDir() @@ -294,17 +294,17 @@ func TestBuilder_BuildWithDifferentOwners(t *testing.T) { var workflowIDs []string for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - inputs := artifact.Inputs{ + inputs := artifacts.Inputs{ WorkflowOwner: tt.workflowOwner, WorkflowName: "test_workflow", OutputPath: binaryPath, ConfigPath: "", } - art, err := builder.Build(inputs) + artifact, err := builder.Build(inputs) require.NoError(t, err) - assert.NotEmpty(t, art.WorkflowID) - workflowIDs = append(workflowIDs, art.WorkflowID) + assert.NotEmpty(t, artifact.WorkflowID) + workflowIDs = append(workflowIDs, artifact.WorkflowID) }) }