From 5655f23e07ef6727f1683ff347ee839694acc665 Mon Sep 17 00:00:00 2001 From: Gabe Ruttner Date: Thu, 24 Apr 2025 15:09:09 -0400 Subject: [PATCH] blog: mergent migration (#1611) * universal install * initial migration guide * fakefake tokens * fake secret * fixes * lint * lint * lint * tidy * bump go 1.23 -> 1.24 * fix: whitespace lint * bump golangci-lint version * try to set up go before pre commit runs * lint * names * lint * fix: session store * fix links --------- Co-authored-by: Alexander Belanger --- .github/workflows/lint.yml | 4 + .github/workflows/test.yml | 14 +- .pre-commit-config.yaml | 2 +- CONTRIBUTING.md | 2 +- build/package/loadtest.dockerfile | 2 +- build/package/servers.dockerfile | 2 +- .../v1/migration-guides/hatchet-client.go | 15 + examples/v1/migration-guides/mergent.go | 240 ++++++++++++++++ frontend/docs/components/InstallCommand.tsx | 124 ++++++++ frontend/docs/components/code/CodeBlock.tsx | 64 +++-- frontend/docs/components/code/codeData.tsx | 14 +- frontend/docs/components/code/codeParser.tsx | 6 +- frontend/docs/pages/_setup/_clone/go.mdx | 5 +- frontend/docs/pages/_setup/_clone/py.mdx | 5 +- frontend/docs/pages/_setup/_clone/ts.mdx | 25 +- frontend/docs/pages/_setup/_existing/go.mdx | 5 +- frontend/docs/pages/_setup/_existing/py.mdx | 6 +- frontend/docs/pages/_setup/_existing/ts.mdx | 25 +- frontend/docs/pages/_setup/_new/go.mdx | 5 +- frontend/docs/pages/_setup/_new/py.mdx | 33 +-- frontend/docs/pages/_setup/_new/ts.mdx | 25 +- frontend/docs/pages/blog/_meta.js | 7 + .../pages/blog/mergent-migration-guide.mdx | 266 ++++++++++++++++++ go.mod | 3 +- pkg/auth/cookie/sessionstore_test.go | 6 +- .../examples/migration_guides/__init__.py | 0 .../migration_guides/hatchet_client.py | 3 + .../examples/migration_guides/mergent.py | 138 +++++++++ sdks/typescript/jest.config.ts | 1 + .../migration-guides/hatchet-client.ts | 3 + .../v1/examples/migration-guides/mergent.ts | 118 ++++++++ sdks/typescript/tsconfig.json | 1 + 32 files changed, 1000 insertions(+), 169 deletions(-) create mode 100644 examples/v1/migration-guides/hatchet-client.go create mode 100644 examples/v1/migration-guides/mergent.go create mode 100644 frontend/docs/components/InstallCommand.tsx create mode 100644 frontend/docs/pages/blog/mergent-migration-guide.mdx create mode 100644 sdks/python/examples/migration_guides/__init__.py create mode 100644 sdks/python/examples/migration_guides/hatchet_client.py create mode 100644 sdks/python/examples/migration_guides/mergent.py create mode 100644 sdks/typescript/src/v1/examples/migration-guides/hatchet-client.ts create mode 100644 sdks/typescript/src/v1/examples/migration-guides/mergent.ts diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 2f2b9cd17..0f46862c3 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -7,6 +7,10 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 + - name: Setup Go + uses: actions/setup-go@v5 + with: + go-version: "1.24" - uses: actions/setup-python@v5 - uses: pre-commit/action@v3.0.1 name: lint diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 58cdb77a6..dbf813d16 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -2,8 +2,8 @@ name: test on: pull_request: paths-ignore: - - 'sdks/**' - - 'frontend/docs/**' + - "sdks/**" + - "frontend/docs/**" jobs: generate: @@ -25,7 +25,7 @@ jobs: - name: Setup Go uses: actions/setup-go@v5 with: - go-version: "1.23" + go-version: "1.24" - name: Setup pnpm uses: pnpm/action-setup@v4 @@ -59,7 +59,7 @@ jobs: - name: Setup Go uses: actions/setup-go@v5 with: - go-version: "1.23" + go-version: "1.24" - name: Go deps run: go mod download @@ -83,7 +83,7 @@ jobs: - name: Setup Go uses: actions/setup-go@v5 with: - go-version: "1.23" + go-version: "1.24" - name: Setup pnpm uses: pnpm/action-setup@v4 @@ -128,7 +128,7 @@ jobs: - name: Setup Go uses: actions/setup-go@v5 with: - go-version: "1.23" + go-version: "1.24" - name: Setup pnpm uses: pnpm/action-setup@v4 @@ -304,7 +304,7 @@ jobs: - name: Setup Go uses: actions/setup-go@v5 with: - go-version: "1.23" + go-version: "1.24" - name: Setup pnpm uses: pnpm/action-setup@v4 diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index d448c426d..db0590ba2 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -9,7 +9,7 @@ repos: - id: trailing-whitespace - id: check-yaml - repo: https://github.com/golangci/golangci-lint - rev: v2.1.2 + rev: v2.1.3 hooks: - id: golangci-lint args: ["--config=.golangci.yml"] diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c424b4312..452abf63d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -4,7 +4,7 @@ 1. Make sure all prerequisite dependencies are installed: - - [Go 1.23+](https://go.dev/doc/install) + - [Go 1.24+](https://go.dev/doc/install) - [Node.js v18+](https://nodejs.org/en/download) - we recommend using [nvm](https://github.com/nvm-sh/nvm) for managing node versions. - [pnpm](https://pnpm.io/installation) installed globally (`npm i -g pnpm`) - [Docker Desktop](https://docs.docker.com/desktop/install/mac-install/) diff --git a/build/package/loadtest.dockerfile b/build/package/loadtest.dockerfile index 5ad85d90d..ab955d564 100644 --- a/build/package/loadtest.dockerfile +++ b/build/package/loadtest.dockerfile @@ -1,6 +1,6 @@ # Base Go environment # ------------------- -FROM golang:1.23-alpine as base +FROM golang:1.24-alpine as base WORKDIR /hatchet COPY go.mod go.sum ./ diff --git a/build/package/servers.dockerfile b/build/package/servers.dockerfile index 6e67e766c..a01f0bfbd 100644 --- a/build/package/servers.dockerfile +++ b/build/package/servers.dockerfile @@ -1,6 +1,6 @@ # Base Go environment # ------------------- -FROM golang:1.23-alpine as base +FROM golang:1.24-alpine as base WORKDIR /hatchet RUN apk update && apk add --no-cache gcc musl-dev git protoc protobuf-dev diff --git a/examples/v1/migration-guides/hatchet-client.go b/examples/v1/migration-guides/hatchet-client.go new file mode 100644 index 000000000..4e7fc40c5 --- /dev/null +++ b/examples/v1/migration-guides/hatchet-client.go @@ -0,0 +1,15 @@ +package migration_guides + +import ( + v1 "github.com/hatchet-dev/hatchet/pkg/v1" +) + +func HatchetClient() (v1.HatchetClient, error) { + hatchet, err := v1.NewHatchetClient() + + if err != nil { + return nil, err + } + + return hatchet, nil +} diff --git a/examples/v1/migration-guides/mergent.go b/examples/v1/migration-guides/mergent.go new file mode 100644 index 000000000..d96e97cf5 --- /dev/null +++ b/examples/v1/migration-guides/mergent.go @@ -0,0 +1,240 @@ +package migration_guides + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "net/http" + "time" + + "github.com/hatchet-dev/hatchet/pkg/client/create" + v1 "github.com/hatchet-dev/hatchet/pkg/v1" + "github.com/hatchet-dev/hatchet/pkg/v1/factory" + v1worker "github.com/hatchet-dev/hatchet/pkg/v1/worker" + "github.com/hatchet-dev/hatchet/pkg/v1/workflow" + "github.com/hatchet-dev/hatchet/pkg/worker" +) + +// ProcessImage simulates image processing +func ProcessImage(imageURL string, filters []string) (map[string]interface{}, error) { + // Do some image processing + return map[string]interface{}{ + "url": imageURL, + "size": 100, + "format": "png", + }, nil +} + +// ❓ Before (Mergent) +type MergentRequest struct { + ImageURL string `json:"image_url"` + Filters []string `json:"filters"` +} + +type MergentResponse struct { + Success bool `json:"success"` + ProcessedURL string `json:"processed_url"` +} + +func ProcessImageMergent(req MergentRequest) (*MergentResponse, error) { + result, err := ProcessImage(req.ImageURL, req.Filters) + if err != nil { + return nil, err + } + + return &MergentResponse{ + Success: true, + ProcessedURL: result["url"].(string), + }, nil +} + +// !! + +// ❓ After (Hatchet) +type ImageProcessInput struct { + ImageURL string `json:"image_url"` + Filters []string `json:"filters"` +} + +type ImageProcessOutput struct { + ProcessedURL string `json:"processed_url"` + Metadata struct { + Size int `json:"size"` + Format string `json:"format"` + AppliedFilters []string `json:"applied_filters"` + } `json:"metadata"` +} + +func ImageProcessor(hatchet v1.HatchetClient) workflow.WorkflowDeclaration[ImageProcessInput, ImageProcessOutput] { + processor := factory.NewTask( + create.StandaloneTask{ + Name: "image-processor", + }, + func(ctx worker.HatchetContext, input ImageProcessInput) (*ImageProcessOutput, error) { + result, err := ProcessImage(input.ImageURL, input.Filters) + if err != nil { + return nil, fmt.Errorf("processing image: %w", err) + } + + if result["url"] == "" { + return nil, fmt.Errorf("processing failed to generate URL") + } + + output := &ImageProcessOutput{ + ProcessedURL: result["url"].(string), + Metadata: struct { + Size int `json:"size"` + Format string `json:"format"` + AppliedFilters []string `json:"applied_filters"` + }{ + Size: result["size"].(int), + Format: result["format"].(string), + AppliedFilters: input.Filters, + }, + } + + return output, nil + }, + hatchet, + ) + // !! + + // Example of running a task + _ = func() error { + // ❓ Running a task + result, err := processor.Run(context.Background(), ImageProcessInput{ + ImageURL: "https://example.com/image.png", + Filters: []string{"blur"}, + }) + if err != nil { + return err + } + fmt.Printf("Result: %+v\n", result) + return nil + // !! + } + + // Example of registering a task on a worker + _ = func() error { + // ❓ Declaring a Worker + w, err := hatchet.Worker(v1worker.WorkerOpts{ + Name: "image-processor-worker", + Workflows: []workflow.WorkflowBase{ + processor, + }, + }) + if err != nil { + return err + } + err = w.StartBlocking(context.Background()) + if err != nil { + return err + } + return nil + // !! + } + + return processor +} + +func RunMergentTask() error { + + return nil +} + +func RunningTasks(hatchet v1.HatchetClient) error { + // ❓ Running a task (Mergent) + task := struct { + Request struct { + URL string `json:"url"` + Body string `json:"body"` + Headers map[string]string `json:"headers"` + } `json:"request"` + Name string `json:"name"` + Queue string `json:"queue"` + }{ + Request: struct { + URL string `json:"url"` + Body string `json:"body"` + Headers map[string]string `json:"headers"` + }{ + URL: "https://example.com", + Headers: map[string]string{ + "Authorization": "fake-secret-token", + "Content-Type": "application/json", + }, + Body: "Hello, world!", + }, + Name: "4cf95241-fa19-47ef-8a67-71e483747649", + Queue: "default", + } + + taskJSON, err := json.Marshal(task) + if err != nil { + return fmt.Errorf("marshaling task: %w", err) + } + + req, err := http.NewRequest(http.MethodPost, "https://api.mergent.co/v2/tasks", bytes.NewBuffer(taskJSON)) + if err != nil { + return fmt.Errorf("creating request: %w", err) + } + + req.Header.Add("Authorization", "Bearer ") + req.Header.Add("Content-Type", "application/json") + + client := &http.Client{} + res, err := client.Do(req) + if err != nil { + return fmt.Errorf("sending request: %w", err) + } + defer res.Body.Close() + + fmt.Printf("Mergent task created with status: %d\n", res.StatusCode) + // !! + + // ❓ Running a task (Hatchet) + processor := ImageProcessor(hatchet) + + result, err := processor.Run(context.Background(), ImageProcessInput{ + ImageURL: "https://example.com/image.png", + Filters: []string{"blur"}, + }) + if err != nil { + return err + } + fmt.Printf("Result: %+v\n", result) + // !! + + // ❓ Scheduling tasks (Hatchet) + // Schedule the task to run at a specific time + scheduleRef, err := processor.Schedule( + context.Background(), + time.Now().Add(time.Second*10), + ImageProcessInput{ + ImageURL: "https://example.com/image.png", + Filters: []string{"blur"}, + }, + ) + if err != nil { + return err + } + + // or schedule to run every hour + cronRef, err := processor.Cron( + context.Background(), + "run-hourly", + "0 * * * *", + ImageProcessInput{ + ImageURL: "https://example.com/image.png", + Filters: []string{"blur"}, + }, + ) + // !! + if err != nil { + return err + } + + fmt.Printf("Scheduled tasks with refs: %+v, %+v\n", scheduleRef, cronRef) + return nil +} diff --git a/frontend/docs/components/InstallCommand.tsx b/frontend/docs/components/InstallCommand.tsx new file mode 100644 index 000000000..0aae845ca --- /dev/null +++ b/frontend/docs/components/InstallCommand.tsx @@ -0,0 +1,124 @@ +import { useLanguage } from "@/context/LanguageContext"; +import { Tabs, Code } from "nextra/components"; +import UniversalTabs from "@/components/UniversalTabs"; +import { CodeBlock } from "./code/CodeBlock"; + +export default function InstallCommand({ + installOnly, + withDevDependencies, + withLanguagePicker +}: { + installOnly?: boolean, + withDevDependencies?: boolean, + withLanguagePicker?: boolean +}) { + const content = () => { + const { + selectedLanguage, + } = useLanguage(); + + if (selectedLanguage === "Typescript") { + return ( + + + + + + + + + + + + ); + } else if (selectedLanguage === "Python") { + return withDevDependencies ? ( + + + + + + + + + ) : ( + + +
Create a virtual environment
+ +
Initialize the project and install the Hatchet SDK
+ +
+ +
Create a virtual environment
+ +
Install the Hatchet SDK
+ +
(optional) save the dependencies to a requirements.txt file
+ requirements.txt`, + }} /> +
+
+ ); + } else if (selectedLanguage === "Go") { + return ( + + ); + } + return ( +
+

Select a language {selectedLanguage}

+
+ ); + } + + if (withLanguagePicker) { + return ( + + + {content()} + + + {content()} + + + {content()} + + + ); + } + return content(); +} diff --git a/frontend/docs/components/code/CodeBlock.tsx b/frontend/docs/components/code/CodeBlock.tsx index 567fd4443..d8b8513ec 100644 --- a/frontend/docs/components/code/CodeBlock.tsx +++ b/frontend/docs/components/code/CodeBlock.tsx @@ -13,7 +13,7 @@ import { interface CodeRendererProps { source: Src; - target: string; + target?: string; } export const CodeBlock = ({ source, target }: CodeRendererProps) => { @@ -26,35 +26,39 @@ export const CodeBlock = ({ source, target }: CodeRendererProps) => { return ( <>
- - {source.props.path} - -
+ {source.githubUrl && ( + + {source.props?.path} + + )} +
+ {source.githubUrl && ( - + )} + - + }