Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 8 additions & 7 deletions cmd/common/compile.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package common

import (
"context"
"errors"
"fmt"
"os"
Expand Down Expand Up @@ -33,15 +34,15 @@ type WorkflowCompileOptions struct {
}

// getBuildCmd returns a single step that builds the workflow and returns the WASM bytes.
func getBuildCmd(workflowRootFolder, mainFile, language string, opts WorkflowCompileOptions) (func() ([]byte, error), error) {
func getBuildCmd(ctx context.Context, workflowRootFolder, mainFile, language string, opts WorkflowCompileOptions) (func() ([]byte, error), error) {
tmpPath := filepath.Join(workflowRootFolder, ".cre_build_tmp.wasm")
switch language {
case constants.WorkflowLanguageTypeScript:
args := []string{"cre-compile", mainFile, tmpPath}
if opts.SkipTypeChecks {
args = append(args, SkipTypeChecksFlag)
}
cmd := exec.Command("bun", args...)
cmd := exec.CommandContext(ctx, "bun", args...)
cmd.Dir = workflowRootFolder
return func() ([]byte, error) {
out, err := cmd.CombinedOutput()
Expand All @@ -67,7 +68,7 @@ func getBuildCmd(workflowRootFolder, mainFile, language string, opts WorkflowCom
if opts.StripSymbols {
ldflags = "-buildid= -w -s"
}
cmd := exec.Command(
cmd := exec.CommandContext(ctx,
"go", "build",
"-o", tmpPath,
"-trimpath",
Expand All @@ -92,7 +93,7 @@ func getBuildCmd(workflowRootFolder, mainFile, language string, opts WorkflowCom
if err != nil {
return nil, err
}
makeCmd := exec.Command("make", "build")
makeCmd := exec.CommandContext(ctx, "make", "build")
makeCmd.Dir = makeRoot
builtPath := filepath.Join(makeRoot, defaultWasmOutput)
return func() ([]byte, error) {
Expand All @@ -108,7 +109,7 @@ func getBuildCmd(workflowRootFolder, mainFile, language string, opts WorkflowCom
if opts.StripSymbols {
ldflags = "-buildid= -w -s"
}
cmd := exec.Command(
cmd := exec.CommandContext(ctx,
"go", "build",
"-o", tmpPath,
"-trimpath",
Expand All @@ -135,7 +136,7 @@ func getBuildCmd(workflowRootFolder, mainFile, language string, opts WorkflowCom
// opts.StripSymbols: for Go builds, true strips debug symbols (deploy); false keeps them (simulate).
// opts.SkipTypeChecks: for TypeScript, passes SkipTypeChecksFlag to cre-compile.
// For custom Makefile WASM builds, StripSymbols and SkipTypeChecks have no effect.
func CompileWorkflowToWasm(workflowPath string, opts WorkflowCompileOptions) ([]byte, error) {
func CompileWorkflowToWasm(ctx context.Context, workflowPath string, opts WorkflowCompileOptions) ([]byte, error) {
workflowRootFolder, workflowMainFile, err := WorkflowPathRootAndMain(workflowPath)
if err != nil {
return nil, fmt.Errorf("workflow path: %w", err)
Expand Down Expand Up @@ -167,7 +168,7 @@ func CompileWorkflowToWasm(workflowPath string, opts WorkflowCompileOptions) ([]
return nil, fmt.Errorf("unsupported workflow language for file %s", workflowMainFile)
}

buildStep, err := getBuildCmd(workflowRootFolder, workflowMainFile, language, opts)
buildStep, err := getBuildCmd(ctx, workflowRootFolder, workflowMainFile, language, opts)
if err != nil {
return nil, err
}
Expand Down
17 changes: 9 additions & 8 deletions cmd/common/compile_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package common

import (
"bytes"
"context"
"io"
"os"
"os/exec"
Expand Down Expand Up @@ -47,29 +48,29 @@ func TestFindMakefileRoot(t *testing.T) {
func TestCompileWorkflowToWasm_Go_Success(t *testing.T) {
t.Run("basic_workflow", func(t *testing.T) {
path := deployTestdataPath("basic_workflow", "main.go")
wasm, err := CompileWorkflowToWasm(path, WorkflowCompileOptions{StripSymbols: true})
wasm, err := CompileWorkflowToWasm(context.Background(), path, WorkflowCompileOptions{StripSymbols: true})
require.NoError(t, err)
assert.NotEmpty(t, wasm)
})

t.Run("configless_workflow", func(t *testing.T) {
path := deployTestdataPath("configless_workflow", "main.go")
wasm, err := CompileWorkflowToWasm(path, WorkflowCompileOptions{StripSymbols: true})
wasm, err := CompileWorkflowToWasm(context.Background(), path, WorkflowCompileOptions{StripSymbols: true})
require.NoError(t, err)
assert.NotEmpty(t, wasm)
})

t.Run("missing_go_mod", func(t *testing.T) {
path := deployTestdataPath("missing_go_mod", "main.go")
wasm, err := CompileWorkflowToWasm(path, WorkflowCompileOptions{StripSymbols: true})
wasm, err := CompileWorkflowToWasm(context.Background(), path, WorkflowCompileOptions{StripSymbols: true})
require.NoError(t, err)
assert.NotEmpty(t, wasm)
})
}

func TestCompileWorkflowToWasm_Go_Malformed_Fails(t *testing.T) {
path := deployTestdataPath("malformed_workflow", "main.go")
_, err := CompileWorkflowToWasm(path, WorkflowCompileOptions{StripSymbols: true})
_, err := CompileWorkflowToWasm(context.Background(), path, WorkflowCompileOptions{StripSymbols: true})
require.Error(t, err)
assert.Contains(t, err.Error(), "failed to compile workflow")
assert.Contains(t, err.Error(), "undefined: sdk.RemovedFunctionThatFailsCompilation")
Expand All @@ -80,7 +81,7 @@ func TestCompileWorkflowToWasm_Wasm_Success(t *testing.T) {
_ = os.Remove(wasmPath)
t.Cleanup(func() { _ = os.Remove(wasmPath) })

wasm, err := CompileWorkflowToWasm(wasmPath, WorkflowCompileOptions{StripSymbols: true})
wasm, err := CompileWorkflowToWasm(context.Background(), wasmPath, WorkflowCompileOptions{StripSymbols: true})
require.NoError(t, err)
assert.NotEmpty(t, wasm)

Expand All @@ -96,14 +97,14 @@ func TestCompileWorkflowToWasm_Wasm_Fails(t *testing.T) {
wasmPath := filepath.Join(wasmDir, "workflow.wasm")
require.NoError(t, os.WriteFile(wasmPath, []byte("not really wasm"), 0600))

_, err := CompileWorkflowToWasm(wasmPath, WorkflowCompileOptions{StripSymbols: true})
_, err := CompileWorkflowToWasm(context.Background(), wasmPath, WorkflowCompileOptions{StripSymbols: true})
require.Error(t, err)
assert.Contains(t, err.Error(), "no Makefile found")
})

t.Run("make_build_fails", func(t *testing.T) {
path := deployTestdataPath("wasm_make_fails", "wasm", "workflow.wasm")
_, err := CompileWorkflowToWasm(path, WorkflowCompileOptions{StripSymbols: true})
_, err := CompileWorkflowToWasm(context.Background(), path, WorkflowCompileOptions{StripSymbols: true})
require.Error(t, err)
assert.Contains(t, err.Error(), "failed to compile workflow")
assert.Contains(t, err.Error(), "build output:")
Expand Down Expand Up @@ -138,7 +139,7 @@ func TestCompileWorkflowToWasm_TS_Success(t *testing.T) {
"include": ["main.ts"]
}
`), 0600))
wasm, err := CompileWorkflowToWasm(mainPath, WorkflowCompileOptions{StripSymbols: true})
wasm, err := CompileWorkflowToWasm(context.Background(), mainPath, WorkflowCompileOptions{StripSymbols: true})
if err != nil {
t.Skipf("TS compile failed (published cre-sdk may lack full layout): %v", err)
}
Expand Down
10 changes: 8 additions & 2 deletions cmd/common/fetch.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package common

import (
"context"
"fmt"
"io"
"net/http"
Expand Down Expand Up @@ -28,8 +29,13 @@ func IsURL(s string) bool {
}

// FetchURL performs an HTTP GET and returns the response body bytes.
func FetchURL(url string) ([]byte, error) {
resp, err := http.Get(url) //nolint:gosec,noctx
func FetchURL(ctx context.Context, url string) ([]byte, error) {
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
if err != nil {
return nil, fmt.Errorf("HTTP GET %s: %w", url, err)
}

resp, err := http.DefaultClient.Do(req) //nolint:gosec
if err != nil {
return nil, fmt.Errorf("HTTP GET %s: %w", url, err)
}
Expand Down
7 changes: 4 additions & 3 deletions cmd/common/fetch_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package common

import (
"context"
"net/http"
"net/http/httptest"
"testing"
Expand Down Expand Up @@ -42,7 +43,7 @@ func TestFetchURL(t *testing.T) {
}))
defer srv.Close()

data, err := FetchURL(srv.URL)
data, err := FetchURL(context.Background(), srv.URL)
require.NoError(t, err)
assert.Equal(t, body, data)
})
Expand All @@ -53,13 +54,13 @@ func TestFetchURL(t *testing.T) {
}))
defer srv.Close()

_, err := FetchURL(srv.URL)
_, err := FetchURL(context.Background(), srv.URL)
require.Error(t, err)
assert.Contains(t, err.Error(), "returned status 404")
})

t.Run("unreachable host", func(t *testing.T) {
_, err := FetchURL("http://127.0.0.1:1")
_, err := FetchURL(context.Background(), "http://127.0.0.1:1")
require.Error(t, err)
})
}
7 changes: 4 additions & 3 deletions cmd/workflow/build/build.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package build

import (
"context"
"fmt"
"os"
"path/filepath"
Expand All @@ -26,15 +27,15 @@ func New(runtimeContext *runtime.Context) *cobra.Command {
RunE: func(cmd *cobra.Command, args []string) error {
outputPath, _ := cmd.Flags().GetString("output")
skipTypeChecks, _ := cmd.Flags().GetBool(cmdcommon.SkipTypeChecksCLIFlag)
return execute(args[0], outputPath, skipTypeChecks)
return execute(cmd.Context(), args[0], outputPath, skipTypeChecks)
},
}
buildCmd.Flags().StringP("output", "o", "", "Output file path for the compiled WASM binary (default: <workflow-folder>/binary.wasm)")
buildCmd.Flags().Bool(cmdcommon.SkipTypeChecksCLIFlag, false, "Skip TypeScript project typecheck during compilation (passes "+cmdcommon.SkipTypeChecksFlag+" to cre-compile)")
return buildCmd
}

func execute(workflowFolder, outputPath string, skipTypeChecks bool) error {
func execute(ctx context.Context, workflowFolder, outputPath string, skipTypeChecks bool) error {
workflowDir, err := filepath.Abs(workflowFolder)
if err != nil {
return fmt.Errorf("resolve workflow folder: %w", err)
Expand All @@ -60,7 +61,7 @@ func execute(workflowFolder, outputPath string, skipTypeChecks bool) error {
outputPath = cmdcommon.EnsureWasmExtension(outputPath)

ui.Dim("Compiling workflow...")
wasmBytes, err := cmdcommon.CompileWorkflowToWasm(resolvedPath, cmdcommon.WorkflowCompileOptions{
wasmBytes, err := cmdcommon.CompileWorkflowToWasm(ctx, resolvedPath, cmdcommon.WorkflowCompileOptions{
StripSymbols: true,
SkipTypeChecks: skipTypeChecks,
})
Expand Down
8 changes: 6 additions & 2 deletions cmd/workflow/deploy/artifacts.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ import (
)

func (h *handler) uploadArtifacts() error {
if err := h.execCtx.Err(); err != nil {
return err
}

if h.workflowArtifact == nil {
return fmt.Errorf("workflowArtifact is nil")
}
Expand Down Expand Up @@ -46,7 +50,7 @@ func (h *handler) uploadArtifacts() error {
if !binaryFromURL {
ui.Success(fmt.Sprintf("Loaded binary from: %s", h.inputs.OutputPath))
binaryResp, err := storageClient.UploadArtifactWithRetriesAndGetURL(
workflowID, storageclient.ArtifactTypeBinary, binaryData, "application/octet-stream")
h.execCtx, workflowID, storageclient.ArtifactTypeBinary, binaryData, "application/octet-stream")
if err != nil {
return fmt.Errorf("uploading binary artifact: %w", err)
}
Expand All @@ -59,7 +63,7 @@ func (h *handler) uploadArtifacts() error {
ui.Success(fmt.Sprintf("Loaded config from: %s", h.inputs.ConfigPath))
var err error
configURL, err = storageClient.UploadArtifactWithRetriesAndGetURL(
workflowID, storageclient.ArtifactTypeConfig, configData, "text/plain")
h.execCtx, workflowID, storageclient.ArtifactTypeConfig, configData, "text/plain")
if err != nil {
return fmt.Errorf("uploading config artifact: %w", err)
}
Expand Down
10 changes: 7 additions & 3 deletions cmd/workflow/deploy/auto_link.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package deploy

import (
"context"
"fmt"
"strings"
"time"
Expand Down Expand Up @@ -137,7 +136,7 @@ func (h *handler) checkLinkStatusViaGraphQL(ownerAddr common.Address) (bool, err
}

gql := graphqlclient.New(h.credentials, h.environmentSet, h.log)
if err := gql.Execute(context.Background(), req, &resp); err != nil {
if err := gql.Execute(h.execCtx, req, &resp); err != nil {
return false, fmt.Errorf("GraphQL query failed: %w", err)
}

Expand Down Expand Up @@ -181,7 +180,11 @@ func (h *handler) waitForBackendLinkProcessing(ownerAddr common.Address) error {
ui.Line()

// Wait for 3 block confirmations before polling
time.Sleep(initialBlockWait)
select {
case <-time.After(initialBlockWait):
case <-h.execCtx.Done():
return h.execCtx.Err()
}

err := retry.Do(
func() error {
Expand All @@ -199,6 +202,7 @@ func (h *handler) waitForBackendLinkProcessing(ownerAddr common.Address) error {
retry.Delay(retryDelay),
retry.DelayType(retry.FixedDelay), // Use fixed 3s delay between retries
retry.LastErrorOnly(true),
retry.Context(h.execCtx),
retry.OnRetry(func(n uint, err error) {
h.log.Debug().Uint("attempt", n+1).Uint("maxAttempts", maxAttempts).Err(err).Msg("Retrying link status check")
ui.Dim(fmt.Sprintf(" Waiting for verification... (attempt %d/%d)", n+1, maxAttempts))
Expand Down
2 changes: 1 addition & 1 deletion cmd/workflow/deploy/compile.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ func (h *handler) Compile() error {
h.runtimeContext.Workflow.Language = cmdcommon.GetWorkflowLanguage(workflowMainFile)
}

wasmFile, err = cmdcommon.CompileWorkflowToWasm(resolvedWorkflowPath, cmdcommon.WorkflowCompileOptions{
wasmFile, err = cmdcommon.CompileWorkflowToWasm(h.execCtx, resolvedWorkflowPath, cmdcommon.WorkflowCompileOptions{
StripSymbols: true,
SkipTypeChecks: h.inputs.SkipTypeChecks,
})
Expand Down
3 changes: 2 additions & 1 deletion cmd/workflow/deploy/compile_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package deploy

import (
"context"
"encoding/base64"
"errors"
"io"
Expand Down Expand Up @@ -286,7 +287,7 @@ func outputPathWithExtensions(path string) string {
// file content equals CompileWorkflowToWasm(workflowPath) + brotli + base64.
func assertCompileOutputMatchesUnderlying(t *testing.T, simulatedEnvironment *chainsim.SimulatedEnvironment, inputs Inputs, ownerType string) {
t.Helper()
wasm, err := cmdcommon.CompileWorkflowToWasm(inputs.WorkflowPath, cmdcommon.WorkflowCompileOptions{
wasm, err := cmdcommon.CompileWorkflowToWasm(context.Background(), inputs.WorkflowPath, cmdcommon.WorkflowCompileOptions{
StripSymbols: true,
SkipTypeChecks: inputs.SkipTypeChecks,
})
Expand Down
12 changes: 10 additions & 2 deletions cmd/workflow/deploy/deploy.go
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,10 @@ func (h *handler) Execute(ctx context.Context) error {
return err
}

if err := h.execCtx.Err(); err != nil {
return err
}

if err := adapter.RunPreDeployChecks(); err != nil {
if errors.Is(err, errDeployHalted) {
return nil
Expand Down Expand Up @@ -274,6 +278,10 @@ func (h *handler) Execute(ctx context.Context) error {
// Artifact upload is deferred to the deploy service so it runs after any
// existing-workflow update confirmation.
func (h *handler) prepareArtifacts() error {
if err := h.execCtx.Err(); err != nil {
return err
}

workflowcommon.DisplayWorkflowDetails(
h.settings,
h.runtimeContext,
Expand All @@ -285,7 +293,7 @@ func (h *handler) prepareArtifacts() error {
if cmdcommon.IsURL(h.inputs.WasmPath) {
h.inputs.BinaryURL = h.inputs.WasmPath
ui.Dim("Fetching binary from URL for workflow ID computation...")
fetched, err := cmdcommon.FetchURL(h.inputs.WasmPath)
fetched, err := cmdcommon.FetchURL(h.execCtx, h.inputs.WasmPath)
if err != nil {
return fmt.Errorf("failed to fetch binary from URL: %w", err)
}
Expand All @@ -302,7 +310,7 @@ func (h *handler) prepareArtifacts() error {
h.inputs.ConfigURL = &url
h.inputs.ConfigPath = ""
ui.Dim("Fetching config from URL for workflow ID computation...")
fetched, err := cmdcommon.FetchURL(url)
fetched, err := cmdcommon.FetchURL(h.execCtx, url)
if err != nil {
return fmt.Errorf("failed to fetch config from URL: %w", err)
}
Expand Down
Loading
Loading