From f8e5f357d981669d8f7c596ccc93eb59083cb10b Mon Sep 17 00:00:00 2001 From: matt Date: Sun, 17 Aug 2025 10:22:16 -0500 Subject: [PATCH] Fix: More examples / snippets fixes + rework (#2150) * feat: start reworking snippets * feat: start cleaning up gen script * fix: start updating refs everywhere * feat: start fixing broken snippet links * fix: more snippets * fix: more updates * chore: lint * fix: taskfile * fix: script * fix: escaping issue + mergent blog * fix: bunch more * chore: lint * fix: implement more of them * fix: retry * fix: the rest * chore: lint * fix: highlight * fix: ugh * fix: start removing dead code from old snippet method * fix: rest of the refs * fix: remove all of the rest of the > $GITHUB_OUTPUT id: create-branch + - name: Close existing autogenerated-docs PRs + if: steps.verify-changed-files.outputs.changed == 'true' + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + EXISTING_PRS=$(gh pr list --label "autogenerated-docs" --state open --json number --jq '.[].number') + + for pr in $EXISTING_PRS; do + if [ -n "$pr" ]; then + echo "Closing existing autogenerated-docs PR #$pr" + gh pr close $pr --comment "Closing in favor of newer autogenerated-docs PR" + fi + done + - name: Create Pull Request if: steps.verify-changed-files.outputs.changed == 'true' env: @@ -68,14 +74,23 @@ jobs: --title "chore: regenerate examples" \ --body "Automated regeneration of examples from the main branch." \ --head "${{ steps.create-branch.outputs.branch_name }}" \ - --base main + --base main \ + --label "autogenerated-docs" echo "pr_number=$(gh pr list --head ${{ steps.create-branch.outputs.branch_name }} --json number --jq '.[0].number')" >> $GITHUB_OUTPUT id: create-pr - - name: Auto-approve and merge Pull Request + - name: Request review from triggering author if: steps.verify-changed-files.outputs.changed == 'true' env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | - gh pr review "${{ steps.create-branch.outputs.branch_name }}" --approve - gh pr merge "${{ steps.create-branch.outputs.branch_name }}" --squash + AUTHOR="${{ github.actor }}" + + if [ "$AUTHOR" != "github-actions[bot]" ]; then + gh pr edit "${{ steps.create-branch.outputs.branch_name }}" --add-reviewer "$AUTHOR" || { + gh pr comment "${{ steps.create-branch.outputs.branch_name }}" --body "@$AUTHOR Please review this autogenerated PR" + } + echo "Requested review from $AUTHOR" + else + echo "Skipping review request as author is github-actions bot" + fi diff --git a/examples/python/dag/worker.py b/examples/python/dag/worker.py index 8cbb0d30e..c6daf3f67 100644 --- a/examples/python/dag/worker.py +++ b/examples/python/dag/worker.py @@ -38,6 +38,7 @@ async def step3(input: EmptyModel, ctx: Context) -> RandomSum: return RandomSum(sum=one + two) +# 👀 cool thing! @dag_workflow.task(parents=[step1, step3]) async def step4(input: EmptyModel, ctx: Context) -> dict[str, str]: print( diff --git a/examples/python/dependency_injection/worker.py b/examples/python/dependency_injection/worker.py index ceaf8e3cd..89e98ab04 100644 --- a/examples/python/dependency_injection/worker.py +++ b/examples/python/dependency_injection/worker.py @@ -1,5 +1,3 @@ -# > Simple - from typing import Annotated from pydantic import BaseModel diff --git a/examples/typescript/affinity/affinity-workers.ts b/examples/typescript/affinity/affinity-workers.ts new file mode 100644 index 000000000..00d3e04cd --- /dev/null +++ b/examples/typescript/affinity/affinity-workers.ts @@ -0,0 +1,87 @@ +import { WorkerLabelComparator } from '@hatchet-dev/typescript-sdk/protoc/workflows'; +import { hatchet } from '../hatchet-client'; + +// > AffinityWorkflow + +const workflow = hatchet.workflow({ + name: 'affinity-workflow', + description: 'test', +}); + +workflow.task({ + name: 'step1', + fn: async (_, ctx) => { + const results: Promise[] = []; + for (let i = 0; i < 50; i++) { + const result = await ctx.spawnWorkflow(childWorkflow.id, {}); + results.push(result.output); + } + console.log('Spawned 50 child workflows'); + console.log('Results:', await Promise.all(results)); + + return { step1: 'step1 results!' }; + } +}) + + +const childWorkflow = hatchet.workflow({ + name: 'child-affinity-workflow', + description: 'test', +}); + +childWorkflow.task({ + name: 'child-step1', + desiredWorkerLabels: { + model: { + value: 'xyz', + required: true, + }, + }, + fn: async (ctx) => { + return { childStep1: 'childStep1 results!' }; + } +}) + +childWorkflow.task({ + name: 'child-step2', + desiredWorkerLabels: { + memory: { + value: 512, + required: true, + comparator: WorkerLabelComparator.LESS_THAN, + }, + }, + fn: async (ctx) => { + return { childStep2: 'childStep2 results!' }; + } +}) + + + +async function main() { + // > AffinityWorker + + const worker1 = await hatchet.worker('affinity-worker-1', { + labels: { + model: 'abc', + memory: 1024, + }, + }); + + + await worker1.registerWorkflow(workflow); + await worker1.registerWorkflow(childWorkflow); + worker1.start(); + + const worker2 = await hatchet.worker('affinity-worker-2', { + labels: { + model: 'xyz', + memory: 512, + }, + }); + await worker2.registerWorkflow(workflow); + await worker2.registerWorkflow(childWorkflow); + worker2.start(); +} + +main(); diff --git a/examples/typescript/logging/byo-logger.ts b/examples/typescript/logging/byo-logger.ts new file mode 100644 index 000000000..dd1480025 --- /dev/null +++ b/examples/typescript/logging/byo-logger.ts @@ -0,0 +1,74 @@ +import { Logger, LogLevel } from '@hatchet-dev/typescript-sdk/util/logger'; +import pino from 'pino'; +import Hatchet from '@hatchet-dev/typescript-sdk/sdk'; +import { JsonObject } from '@hatchet-dev/typescript-sdk/v1'; + +// > Create Pino logger +const logger = pino(); + +class PinoLogger implements Logger { + logLevel: LogLevel; + context: string; + + constructor(context: string, logLevel: LogLevel = 'DEBUG') { + this.logLevel = logLevel; + this.context = context; + } + + debug(message: string, extra?: JsonObject): void { + logger.debug(extra, message); + } + + info(message: string, extra?: JsonObject): void { + logger.info(extra, message); + } + + green(message: string, extra?: JsonObject): void { + logger.info(extra, `%c${message}`); + } + + warn(message: string, error?: Error, extra?: JsonObject): void { + logger.warn(extra, `${message} ${error}`); + } + + error(message: string, error?: Error, extra?: JsonObject): void { + logger.error(extra, `${message} ${error}`); + } + + // optional util method + util(key: string, message: string, extra?: JsonObject): void { + // for example you may want to expose a trace method + if (key === 'trace') { + logger.info(extra, 'trace'); + } + } +} + +const hatchet = Hatchet.init({ + log_level: 'DEBUG', + logger: (ctx, level) => new PinoLogger(ctx, level), +}); + + +// > Use the logger + +const workflow = hatchet.task({ + name: 'byo-logger-example', + fn: async (ctx) => { + for (let i = 0; i < 5; i++) { + logger.info(`log message ${i}`); + } + + return { step1: 'completed step run' }; + }, +}); + + +async function main() { + const worker = await hatchet.worker('byo-logger-worker', { + workflows: [workflow], + }); + worker.start(); +} + +main(); diff --git a/examples/typescript/logging/logger.ts b/examples/typescript/logging/logger.ts new file mode 100644 index 000000000..ac18d3e7b --- /dev/null +++ b/examples/typescript/logging/logger.ts @@ -0,0 +1,40 @@ +import { hatchet } from "../hatchet-client"; + +const sleep = (ms: number) => + new Promise((resolve) => { + setTimeout(resolve, ms); + }); + +// > Logger + +const workflow = hatchet.workflow({ + name: 'logger-example', + description: 'test', + on: { + event: 'user:create', + }, +}); + +workflow.task({ + name: 'logger-step1', + fn: async (_, ctx) => { + // log in a for loop + for (let i = 0; i < 10; i++) { + ctx.logger.info(`log message ${i}`); + await sleep(200); + } + + return { step1: 'completed step run' }; + }, +}) + + +async function main() { + const worker = await hatchet.worker('logger-worker', { + slots: 1, + workflows: [workflow], + }); + await worker.start(); +} + +main(); diff --git a/examples/typescript/with_timeouts/workflow.ts b/examples/typescript/with_timeouts/workflow.ts index e753e71cf..da8480d2b 100644 --- a/examples/typescript/with_timeouts/workflow.ts +++ b/examples/typescript/with_timeouts/workflow.ts @@ -1,4 +1,3 @@ -// > Declaring a Task import sleep from '@hatchet-dev/typescript-sdk/util/sleep'; import { hatchet } from '../hatchet-client'; diff --git a/frontend/app/package.json b/frontend/app/package.json index 112a386fe..22c2ca4d5 100644 --- a/frontend/app/package.json +++ b/frontend/app/package.json @@ -13,8 +13,7 @@ "eslint:fix": "eslint \"{src,apps,libs,test}/**/*.{ts,tsx,js}\" --fix", "prettier:check": "prettier \"src/**/*.{ts,tsx}\" --list-different", "prettier:fix": "prettier \"src/**/*.{ts,tsx}\" --write", - "preview": "vite preview", - "sync-examples": "cd ../snips/ && pnpm i && pnpm generate" + "preview": "vite preview" }, "dependencies": { "@heroicons/react": "^2.2.0", diff --git a/frontend/docs/components/code/CodeBlock.tsx b/frontend/docs/components/code/CodeBlock.tsx index ba93412d5..38ec44a18 100644 --- a/frontend/docs/components/code/CodeBlock.tsx +++ b/frontend/docs/components/code/CodeBlock.tsx @@ -1,6 +1,5 @@ import React from "react"; import { parseDocComments } from "./codeParser"; -import { Src } from "./codeData"; import CodeStyleRender from "./CodeStyleRender"; import { Button } from "../ui/button"; import { @@ -11,6 +10,13 @@ import { UnfoldVertical, } from "lucide-react"; +type Src = { + raw: string; + codePath?: string; + githubUrl?: string; + language?: string; +}; + interface CodeRendererProps { source: Src; target?: string; @@ -23,27 +29,28 @@ export const CodeBlock = ({ source, target }: CodeRendererProps) => { const parsed = parseDocComments(source.raw, target, collapsed); - const canCollapse = source.raw.includes("// ...") || source.raw.includes("# ...") + const canCollapse = + source.raw.includes("// ...") || source.raw.includes("# ..."); return ( <>
- {source.githubUrl && ( + {source.githubUrl && ( - {source.props?.path} - - )} + > + {source.codePath} + + )}
- {canCollapse && ( - - )} - - } + + + + View Full Code Example on GitHub{" "} + + + + )}