From 5abf5d018e5a84424c640b615c0ea5235a4c1799 Mon Sep 17 00:00:00 2001 From: Tarcisio Ferraz Date: Mon, 22 Jun 2026 10:23:05 -0300 Subject: [PATCH 1/2] update listen docs --- cmd/workflow/simulate/simulate.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/workflow/simulate/simulate.go b/cmd/workflow/simulate/simulate.go index 7602a800..1891fe6c 100644 --- a/cmd/workflow/simulate/simulate.go +++ b/cmd/workflow/simulate/simulate.go @@ -111,7 +111,7 @@ func New(runtimeContext *runtime.Context) *cobra.Command { // Non-interactive trigger selection flags simulateCmd.Flags().Int("trigger-index", -1, "Index of the trigger to run (0-based)") simulateCmd.Flags().String("http-payload", "", "HTTP trigger payload as JSON string or path to JSON file") - simulateCmd.Flags().Bool("listen", false, "Listen for HTTP requests or supported log triggers and run the simulator for each match") + simulateCmd.Flags().Bool("listen", false, "Listen for HTTP requests or supported log triggers and run the simulator for each match (not supported by cron)") // Register chain-type-specific CLI flags (e.g., --evm-tx-hash). chain.RegisterAllCLIFlags(simulateCmd) From f18479634ceee38a6bad63f5ccd4501f8e1294f8 Mon Sep 17 00:00:00 2001 From: Tarcisio Ferraz Date: Mon, 22 Jun 2026 10:40:33 -0300 Subject: [PATCH 2/2] http listen port --- cmd/workflow/simulate/capabilities.go | 8 +-- cmd/workflow/simulate/manual_http_trigger.go | 6 ++- cmd/workflow/simulate/simulate.go | 21 +++++--- cmd/workflow/simulate/simulate_test.go | 52 +++++++++++++++++--- docs/cre_workflow_simulate.md | 3 +- 5 files changed, 69 insertions(+), 21 deletions(-) diff --git a/cmd/workflow/simulate/capabilities.go b/cmd/workflow/simulate/capabilities.go index 87c8a220..c3fc1ba7 100644 --- a/cmd/workflow/simulate/capabilities.go +++ b/cmd/workflow/simulate/capabilities.go @@ -18,10 +18,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/capabilities/fakes" ) -// httpTriggerServerPort is the port on which the local HTTP server listens -// when no --http-payload flag is supplied and the user chooses to POST the payload. -const httpTriggerServerPort = 2000 - // ManualTriggers holds chain-agnostic trigger services used in simulation. type ManualTriggers struct { ManualCronTrigger *fakes.ManualCronTriggerService @@ -30,7 +26,7 @@ type ManualTriggers struct { // NewManualTriggerCapabilities creates and registers cron and HTTP trigger capabilities. // These are chain-agnostic and shared across all chain types. -func NewManualTriggerCapabilities(ctx context.Context, lggr logger.Logger, registry *capabilities.Registry) (*ManualTriggers, error) { +func NewManualTriggerCapabilities(ctx context.Context, lggr logger.Logger, registry *capabilities.Registry, httpTriggerPort int) (*ManualTriggers, error) { manualCronTrigger, err := fakes.NewManualCronTriggerService(lggr) if err != nil { return nil, err @@ -40,7 +36,7 @@ func NewManualTriggerCapabilities(ctx context.Context, lggr logger.Logger, regis return nil, err } - manualHTTPTrigger := NewManualHTTPTriggerService(lggr) + manualHTTPTrigger := NewManualHTTPTriggerService(lggr, httpTriggerPort) manualHTTPTriggerServer := httptrigger.NewHTTPServer(manualHTTPTrigger) if err := registry.Add(ctx, manualHTTPTriggerServer); err != nil { return nil, err diff --git a/cmd/workflow/simulate/manual_http_trigger.go b/cmd/workflow/simulate/manual_http_trigger.go index 38784319..5d7ba4b1 100644 --- a/cmd/workflow/simulate/manual_http_trigger.go +++ b/cmd/workflow/simulate/manual_http_trigger.go @@ -39,15 +39,17 @@ type ManualHTTPTriggerService struct { workflowIDs map[string]string inputs map[string]*httptypedapi.Config eventSeq uint64 + port int } -func NewManualHTTPTriggerService(parentLggr logger.Logger) *ManualHTTPTriggerService { +func NewManualHTTPTriggerService(parentLggr logger.Logger, port int) *ManualHTTPTriggerService { return &ManualHTTPTriggerService{ CapabilityInfo: manualHTTPTriggerInfo, lggr: logger.Named(parentLggr, "HTTPTriggerService"), callbackCh: make(map[string]chan capabilities.TriggerAndId[*httptypedapi.Payload]), workflowIDs: make(map[string]string), inputs: make(map[string]*httptypedapi.Config), + port: port, } } @@ -121,7 +123,7 @@ func (f *ManualHTTPTriggerService) ManualTrigger(ctx context.Context, triggerID } func (f *ManualHTTPTriggerService) listenForTriggerPayload(ctx context.Context) (*httptypedapi.Payload, error) { - payloadCh, closeServer, err := startHTTPListenPayloadServer(ctx, httpTriggerServerPort) + payloadCh, closeServer, err := startHTTPListenPayloadServer(ctx, f.port) if err != nil { return nil, err } diff --git a/cmd/workflow/simulate/simulate.go b/cmd/workflow/simulate/simulate.go index 1891fe6c..9ff42dbf 100644 --- a/cmd/workflow/simulate/simulate.go +++ b/cmd/workflow/simulate/simulate.go @@ -44,6 +44,7 @@ import ( ) const WorkflowExecutionTimeout = 5 * time.Minute +const defaultHTTPTriggerServerPort = 2000 type Inputs struct { WasmPath string `validate:"omitempty,file,ascii,max=97" cli:"--wasm"` @@ -65,6 +66,7 @@ type Inputs struct { HasTriggerIndex bool TriggerIndex int `validate:"-"` HTTPPayload string `validate:"-"` // JSON string or /path/to/file.json + HTTPTriggerPort int `validate:"min=1,max=65535"` ChainTypeInputs map[string]string `validate:"-"` // CLI-supplied chain-type-specific trigger inputs // Listen keeps the HTTP trigger server running after each execution so it can // process additional requests until the user interrupts (ctrl-C). @@ -111,6 +113,7 @@ func New(runtimeContext *runtime.Context) *cobra.Command { // Non-interactive trigger selection flags simulateCmd.Flags().Int("trigger-index", -1, "Index of the trigger to run (0-based)") simulateCmd.Flags().String("http-payload", "", "HTTP trigger payload as JSON string or path to JSON file") + simulateCmd.Flags().Int("http-trigger-port", defaultHTTPTriggerServerPort, "Port used by the local HTTP trigger server") simulateCmd.Flags().Bool("listen", false, "Listen for HTTP requests or supported log triggers and run the simulator for each match (not supported by cron)") // Register chain-type-specific CLI flags (e.g., --evm-tx-hash). @@ -190,6 +193,11 @@ func (h *handler) ResolveInputs(v *viper.Viper, creSettings *settings.Settings) } } + httpTriggerPort := v.GetInt("http-trigger-port") + if !v.IsSet("http-trigger-port") { + httpTriggerPort = defaultHTTPTriggerServerPort + } + return Inputs{ WasmPath: v.GetString("wasm"), WorkflowPath: creSettings.Workflow.WorkflowArtifactSettings.WorkflowPath, @@ -205,6 +213,7 @@ func (h *handler) ResolveInputs(v *viper.Viper, creSettings *settings.Settings) HasTriggerIndex: v.IsSet("trigger-index"), TriggerIndex: v.GetInt("trigger-index"), HTTPPayload: v.GetString("http-payload"), + HTTPTriggerPort: httpTriggerPort, ChainTypeInputs: chain.CollectAllCLIInputs(v), Listen: v.GetBool("listen"), LimitsPath: v.GetString("limits"), @@ -477,7 +486,7 @@ func run( // Register chain-agnostic cron and HTTP triggers triggerLggr := lggr.Named("TriggerCapabilities") var err error - manualTriggerCaps, err = NewManualTriggerCapabilities(ctx, triggerLggr, registry) + manualTriggerCaps, err = NewManualTriggerCapabilities(ctx, triggerLggr, registry, inputs.HTTPTriggerPort) if err != nil { ui.Error(fmt.Sprintf("Failed to create trigger capabilities: %v", err)) os.Exit(1) @@ -714,7 +723,7 @@ func runHTTPListen(ctx context.Context, inputs Inputs, triggerInfo *TriggerInfoA os.Exit(1) } - payloadCh, closeServer, err := startHTTPListenPayloadServer(ctx, httpTriggerServerPort) + payloadCh, closeServer, err := startHTTPListenPayloadServer(ctx, inputs.HTTPTriggerPort) if err != nil { ui.Error(fmt.Sprintf("Failed to start HTTP trigger server: %v", err)) os.Exit(1) @@ -769,7 +778,7 @@ func runHTTPListen(ctx context.Context, inputs Inputs, triggerInfo *TriggerInfoA ui.Line() ui.Step(fmt.Sprintf("Listen: ready for next request (run #%d)", iteration+1)) } - ui.Step(fmt.Sprintf("Waiting for HTTP request to start execution (listening on http://localhost:%d/trigger)...", httpTriggerServerPort)) + ui.Step(fmt.Sprintf("Waiting for HTTP request to start execution (listening on http://localhost:%d/trigger)...", inputs.HTTPTriggerPort)) var payload *httptypedapi.Payload select { @@ -946,7 +955,7 @@ func makeBeforeStartInteractive(holder *TriggerInfoAndBeforeStart, inputs Inputs ui.Line() ui.Step("No input detected for http-trigger. Supply the payload using one of:") ui.Dim("1. POST JSON to the local trigger server, example:") - ui.Dim(fmt.Sprintf(` curl -X POST http://localhost:%d/trigger \`, httpTriggerServerPort)) + ui.Dim(fmt.Sprintf(` curl -X POST http://localhost:%d/trigger \`, inputs.HTTPTriggerPort)) ui.Dim(" -H 'Content-Type: application/json' \\") ui.Dim(" -d '{\"input\":{\"key\":\"value\"}}'") ui.Dim("2. Re-run with --http-payload flag:") @@ -960,7 +969,7 @@ func makeBeforeStartInteractive(holder *TriggerInfoAndBeforeStart, inputs Inputs p := payload payload = nil if p == nil { - ui.Step(fmt.Sprintf("Waiting for HTTP request to start execution (listening on http://localhost:%d/trigger)...", httpTriggerServerPort)) + ui.Step(fmt.Sprintf("Waiting for HTTP request to start execution (listening on http://localhost:%d/trigger)...", inputs.HTTPTriggerPort)) } return manualTriggerCaps.ManualHTTPTrigger.ManualTrigger(ctx, triggerRegistrationID, p) } @@ -1080,7 +1089,7 @@ func makeBeforeStartNonInteractive(holder *TriggerInfoAndBeforeStart, inputs Inp p := payload payload = nil if p == nil { - ui.Step(fmt.Sprintf("Waiting for HTTP request to start execution (listening on http://localhost:%d/trigger)...", httpTriggerServerPort)) + ui.Step(fmt.Sprintf("Waiting for HTTP request to start execution (listening on http://localhost:%d/trigger)...", inputs.HTTPTriggerPort)) } return manualTriggerCaps.ManualHTTPTrigger.ManualTrigger(ctx, triggerRegistrationID, p) } diff --git a/cmd/workflow/simulate/simulate_test.go b/cmd/workflow/simulate/simulate_test.go index 0ef3c82c..838a77a4 100644 --- a/cmd/workflow/simulate/simulate_test.go +++ b/cmd/workflow/simulate/simulate_test.go @@ -256,6 +256,45 @@ func TestSimulateResolveInputs_WasmFlag(t *testing.T) { }) } +func TestSimulateResolveInputs_HTTPTriggerPort(t *testing.T) { + t.Parallel() + + t.Run("default port when flag unset", func(t *testing.T) { + t.Parallel() + v := createSimulateTestViper(t) + creSettings := createSimulateTestSettings("test-workflow", "main.go", "") + + runtimeCtx := &runtime.Context{ + Logger: testutil.NewTestLogger(), + Viper: v, + Settings: creSettings, + } + h := newHandler(runtimeCtx) + + inputs, err := h.ResolveInputs(v, creSettings) + require.NoError(t, err) + assert.Equal(t, defaultHTTPTriggerServerPort, inputs.HTTPTriggerPort) + }) + + t.Run("flag overrides default", func(t *testing.T) { + t.Parallel() + v := createSimulateTestViper(t) + v.Set("http-trigger-port", 3210) + creSettings := createSimulateTestSettings("test-workflow", "main.go", "") + + runtimeCtx := &runtime.Context{ + Logger: testutil.NewTestLogger(), + Viper: v, + Settings: creSettings, + } + h := newHandler(runtimeCtx) + + inputs, err := h.ResolveInputs(v, creSettings) + require.NoError(t, err) + assert.Equal(t, 3210, inputs.HTTPTriggerPort) + }) +} + func TestSimulateValidateInputs_URLBypass(t *testing.T) { t.Parallel() @@ -268,10 +307,11 @@ func TestSimulateValidateInputs_URLBypass(t *testing.T) { h := newHandler(runtimeCtx) inputs := Inputs{ - WorkflowPath: tmpFile, - ConfigPath: "https://example.com/config.yaml", - WasmPath: "https://example.com/binary.wasm", - WorkflowName: "test-workflow", + WorkflowPath: tmpFile, + ConfigPath: "https://example.com/config.yaml", + WasmPath: "https://example.com/binary.wasm", + HTTPTriggerPort: defaultHTTPTriggerServerPort, + WorkflowName: "test-workflow", } err := h.ValidateInputs(inputs) @@ -487,7 +527,7 @@ func TestNonInteractiveCronTriggerDoesNotBlockOnSchedule(t *testing.T) { require.Nil(t, capErr) holder := &TriggerInfoAndBeforeStart{} - inputs := Inputs{TriggerIndex: triggerIndex} + inputs := Inputs{TriggerIndex: triggerIndex, HTTPTriggerPort: defaultHTTPTriggerServerPort} manualTriggers := &ManualTriggers{ManualCronTrigger: cronSvc} beforeStart := makeBeforeStartNonInteractive(holder, inputs, func() *ManualTriggers { @@ -545,7 +585,7 @@ func TestHTTPListenPayloadServerAcceptsMultipleRequests(t *testing.T) { func TestManualHTTPTriggerEventsHaveUniqueIDs(t *testing.T) { t.Parallel() - svc := NewManualHTTPTriggerService(logger.Test(t)) + svc := NewManualHTTPTriggerService(logger.Test(t), defaultHTTPTriggerServerPort) first := svc.createManualTriggerEvent(nil) second := svc.createManualTriggerEvent(nil) diff --git a/docs/cre_workflow_simulate.md b/docs/cre_workflow_simulate.md index aba5a870..71685ea1 100644 --- a/docs/cre_workflow_simulate.md +++ b/docs/cre_workflow_simulate.md @@ -28,8 +28,9 @@ cre workflow simulate ./my-workflow --evm-tx-hash string EVM trigger transaction hash (0x...) -h, --help help for simulate --http-payload string HTTP trigger payload as JSON string or path to JSON file + --http-trigger-port int Port used by the local HTTP trigger server (default 2000) --limits string Production limits to enforce during simulation: 'default' for prod defaults, path to a limits JSON file (e.g. from 'cre workflow limits export'), or 'none' to disable (default "default") - --listen Listen for HTTP requests or supported log triggers and run the simulator for each match + --listen Listen for HTTP requests or supported log triggers and run the simulator for each match (not supported by cron) --no-config Simulate without a config file --skip-type-checks Skip TypeScript project typecheck during compilation (passes --skip-type-checks to cre-compile) --trigger-index int Index of the trigger to run (0-based) (default -1)