diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7413dc33272..e3c693e0dcf 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -16,6 +16,7 @@ permissions: jobs: test-build: name: Test and Build + if: github.ref != 'refs/heads/dev' || github.event_name == 'pull_request' uses: ./.github/workflows/test-build.yml secrets: inherit @@ -45,11 +46,66 @@ jobs: echo "ℹ️ Not a release commit" fi - # Build AMD64 images and push to ECR immediately (+ GHCR for main) + # Dev: build all 3 images for ECR only (no GHCR, no ARM64) + build-dev: + name: Build Dev ECR + needs: [detect-version] + if: github.event_name == 'push' && github.ref == 'refs/heads/dev' + runs-on: blacksmith-8vcpu-ubuntu-2404 + permissions: + contents: read + id-token: write + strategy: + fail-fast: false + matrix: + include: + - dockerfile: ./docker/app.Dockerfile + ecr_repo_secret: ECR_APP + - dockerfile: ./docker/db.Dockerfile + ecr_repo_secret: ECR_MIGRATIONS + - dockerfile: ./docker/realtime.Dockerfile + ecr_repo_secret: ECR_REALTIME + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v4 + with: + role-to-assume: ${{ secrets.DEV_AWS_ROLE_TO_ASSUME }} + aws-region: ${{ secrets.DEV_AWS_REGION }} + + - name: Login to Amazon ECR + id: login-ecr + uses: aws-actions/amazon-ecr-login@v2 + + - name: Login to Docker Hub + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + + - name: Set up Docker Buildx + uses: useblacksmith/setup-docker-builder@v1 + + - name: Build and push + uses: useblacksmith/build-push-action@v2 + with: + context: . + file: ${{ matrix.dockerfile }} + platforms: linux/amd64 + push: true + tags: ${{ steps.login-ecr.outputs.registry }}/${{ secrets[matrix.ecr_repo_secret] }}:dev + provenance: false + sbom: false + + # Main/staging: build AMD64 images and push to ECR + GHCR build-amd64: name: Build AMD64 - needs: [detect-version] - if: github.event_name == 'push' && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/staging' || github.ref == 'refs/heads/dev') + needs: [test-build, detect-version] + if: >- + github.event_name == 'push' && + (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/staging') runs-on: blacksmith-8vcpu-ubuntu-2404 permissions: contents: read @@ -75,8 +131,8 @@ jobs: - name: Configure AWS credentials uses: aws-actions/configure-aws-credentials@v4 with: - role-to-assume: ${{ github.ref == 'refs/heads/main' && secrets.AWS_ROLE_TO_ASSUME || github.ref == 'refs/heads/dev' && secrets.DEV_AWS_ROLE_TO_ASSUME || secrets.STAGING_AWS_ROLE_TO_ASSUME }} - aws-region: ${{ github.ref == 'refs/heads/main' && secrets.AWS_REGION || github.ref == 'refs/heads/dev' && secrets.DEV_AWS_REGION || secrets.STAGING_AWS_REGION }} + role-to-assume: ${{ github.ref == 'refs/heads/main' && secrets.AWS_ROLE_TO_ASSUME || secrets.STAGING_AWS_ROLE_TO_ASSUME }} + aws-region: ${{ github.ref == 'refs/heads/main' && secrets.AWS_REGION || secrets.STAGING_AWS_REGION }} - name: Login to Amazon ECR id: login-ecr @@ -106,26 +162,20 @@ jobs: ECR_REPO="${{ secrets[matrix.ecr_repo_secret] }}" GHCR_IMAGE="${{ matrix.ghcr_image }}" - # ECR tags (always build for ECR) if [ "${{ github.ref }}" = "refs/heads/main" ]; then ECR_TAG="latest" - elif [ "${{ github.ref }}" = "refs/heads/dev" ]; then - ECR_TAG="dev" else ECR_TAG="staging" fi ECR_IMAGE="${ECR_REGISTRY}/${ECR_REPO}:${ECR_TAG}" - # Build tags list TAGS="${ECR_IMAGE}" - # Add GHCR tags only for main branch if [ "${{ github.ref }}" = "refs/heads/main" ]; then GHCR_AMD64="${GHCR_IMAGE}:latest-amd64" GHCR_SHA="${GHCR_IMAGE}:${{ github.sha }}-amd64" TAGS="${TAGS},$GHCR_AMD64,$GHCR_SHA" - # Add version tag if this is a release commit if [ "${{ needs.detect-version.outputs.is_release }}" = "true" ]; then VERSION="${{ needs.detect-version.outputs.version }}" GHCR_VERSION="${GHCR_IMAGE}:${VERSION}-amd64" @@ -256,6 +306,14 @@ jobs: docker manifest push "${IMAGE_BASE}:${VERSION}" fi + # Run database migrations for dev + migrate-dev: + name: Migrate Dev DB + needs: [build-dev] + if: github.event_name == 'push' && github.ref == 'refs/heads/dev' + uses: ./.github/workflows/migrations.yml + secrets: inherit + # Check if docs changed check-docs-changes: name: Check Docs Changes diff --git a/.github/workflows/migrations.yml b/.github/workflows/migrations.yml index db084926861..245023ab86f 100644 --- a/.github/workflows/migrations.yml +++ b/.github/workflows/migrations.yml @@ -38,5 +38,5 @@ jobs: - name: Apply migrations working-directory: ./packages/db env: - DATABASE_URL: ${{ github.ref == 'refs/heads/main' && secrets.DATABASE_URL || secrets.STAGING_DATABASE_URL }} + DATABASE_URL: ${{ github.ref == 'refs/heads/main' && secrets.DATABASE_URL || github.ref == 'refs/heads/dev' && secrets.DEV_DATABASE_URL || secrets.STAGING_DATABASE_URL }} run: bunx drizzle-kit migrate --config=./drizzle.config.ts \ No newline at end of file diff --git a/README.md b/README.md index 93dbc8ba00e..de6befd2a8f 100644 --- a/README.md +++ b/README.md @@ -74,10 +74,6 @@ docker compose -f docker-compose.prod.yml up -d Open [http://localhost:3000](http://localhost:3000) -#### Background worker note - -The Docker Compose stack starts a dedicated worker container by default. If `REDIS_URL` is not configured, the worker will start, log that it is idle, and do no queue processing. This is expected. Queue-backed API, webhook, and schedule execution requires Redis; installs without Redis continue to use the inline execution path. - Sim also supports local models via [Ollama](https://ollama.ai) and [vLLM](https://docs.vllm.ai/) — see the [Docker self-hosting docs](https://docs.sim.ai/self-hosting/docker) for setup details. ### Self-hosted: Manual Setup @@ -123,12 +119,10 @@ cd packages/db && bun run db:migrate 5. Start development servers: ```bash -bun run dev:full # Starts Next.js app, realtime socket server, and the BullMQ worker +bun run dev:full # Starts Next.js app and realtime socket server ``` -If `REDIS_URL` is not configured, the worker will remain idle and execution continues inline. - -Or run separately: `bun run dev` (Next.js), `cd apps/sim && bun run dev:sockets` (realtime), and `cd apps/sim && bun run worker` (BullMQ worker). +Or run separately: `bun run dev` (Next.js) and `cd apps/sim && bun run dev:sockets` (realtime). ## Copilot API Keys diff --git a/apps/sim/app/(landing)/components/features/components/features-preview.tsx b/apps/sim/app/(landing)/components/features/components/features-preview.tsx index e485396a7e6..ae603009bc5 100644 --- a/apps/sim/app/(landing)/components/features/components/features-preview.tsx +++ b/apps/sim/app/(landing)/components/features/components/features-preview.tsx @@ -2,8 +2,8 @@ import { type SVGProps, useEffect, useRef, useState } from 'react' import { AnimatePresence, motion, useInView } from 'framer-motion' -import ReactMarkdown, { type Components } from 'react-markdown' -import remarkGfm from 'remark-gfm' +import { Streamdown } from 'streamdown' +import 'streamdown/styles.css' import { ChevronDown } from '@/components/emcn' import { Database, File, Library, Table } from '@/components/emcn/icons' import { @@ -557,8 +557,8 @@ The team agreed to prioritize the new onboarding flow. Key decisions: Follow up with engineering on the timeline for the API v2 migration. Draft the proposal for the board meeting next week.` -const MD_COMPONENTS: Components = { - h1: ({ children }) => ( +const MD_COMPONENTS = { + h1: ({ children }: { children?: React.ReactNode }) => (
), - h2: ({ children }) => ( + h2: ({ children }: { children?: React.ReactNode }) => (
{children}
, + p: ({ children }: { children?: React.ReactNode }) => ( +{children}
+ ), } function MockFullFiles() { @@ -618,9 +624,9 @@ function MockFullFiles() { transition={{ duration: 0.4, delay: 0.5 }} >
+ inlineCode: ({ children }) => (
+
{children}
),
@@ -212,7 +211,7 @@ export default function ChangelogList({ initialEntries }: Props) {
}}
>
{cleanMarkdown(entry.content)}
-
- {children}
-
- )
- }
- return (
-
- {children}
-
- )
- },
+ inlineCode: ({ children }: { children?: React.ReactNode }) => (
+
+ {children}
+
+ ),
blockquote: ({ children }: React.HTMLAttributes@@ -215,9 +192,9 @@ const MarkdownRenderer = memo(function MarkdownRenderer({ return (-) }) diff --git a/apps/sim/app/templates/[id]/template.tsx b/apps/sim/app/templates/[id]/template.tsx index 40ad9722a8c..b311837cdd8 100644 --- a/apps/sim/app/templates/[id]/template.tsx +++ b/apps/sim/app/templates/[id]/template.tsx @@ -14,7 +14,8 @@ import { User, } from 'lucide-react' import { useParams, useRouter, useSearchParams } from 'next/navigation' -import ReactMarkdown from 'react-markdown' +import { Streamdown } from 'streamdown' +import 'streamdown/styles.css' import { Breadcrumb, Button, @@ -875,7 +876,8 @@ export default function TemplateDetails({ isWorkspaceContext = false }: Template About this Workflow+ +{processedContent} - -)} @@ -1056,7 +1058,8 @@ export default function TemplateDetails({ isWorkspaceContext = false }: Template {/* Creator bio */} {template.creator.details?.about && (( +@@ -913,16 +915,16 @@ export default function TemplateDetails({ isWorkspaceContext = false }: Template ), li: ({ children }) =>
{children} , - code: ({ inline, children }: any) => - inline ? ( -- {children} -- ) : ( -- {children} -- ), + inlineCode: ({ children }) => ( ++ {children} ++ ), + code: ({ children }) => ( ++ {children} ++ ), a: ({ href, children }) => ( {template.details.about} --)} diff --git a/apps/sim/app/workspace/[workspaceId]/components/message-actions/message-actions.tsx b/apps/sim/app/workspace/[workspaceId]/components/message-actions/message-actions.tsx index 77607befa95..67c7dd92c8f 100644 --- a/apps/sim/app/workspace/[workspaceId]/components/message-actions/message-actions.tsx +++ b/apps/sim/app/workspace/[workspaceId]/components/message-actions/message-actions.tsx @@ -13,6 +13,7 @@ import { Textarea, ThumbsDown, ThumbsUp, + Tooltip, } from '@/components/emcn' import { useSubmitCopilotFeedback } from '@/hooks/queries/copilot-feedback' @@ -46,13 +47,16 @@ interface MessageActionsProps { content: string chatId?: string userQuery?: string + requestId?: string } -export function MessageActions({ content, chatId, userQuery }: MessageActionsProps) { +export function MessageActions({ content, chatId, userQuery, requestId }: MessageActionsProps) { const [copied, setCopied] = useState(false) + const [copiedRequestId, setCopiedRequestId] = useState(false) const [pendingFeedback, setPendingFeedback] = useState<'up' | 'down' | null>(null) const [feedbackText, setFeedbackText] = useState('') const resetTimeoutRef = useRef( +@@ -1081,7 +1084,7 @@ export default function TemplateDetails({ isWorkspaceContext = false }: Template }} > {template.creator.details.about} -
(null) + const requestIdTimeoutRef = useRef (null) const submitFeedback = useSubmitCopilotFeedback() useEffect(() => { @@ -60,6 +64,9 @@ export function MessageActions({ content, chatId, userQuery }: MessageActionsPro if (resetTimeoutRef.current !== null) { window.clearTimeout(resetTimeoutRef.current) } + if (requestIdTimeoutRef.current !== null) { + window.clearTimeout(requestIdTimeoutRef.current) + } } }, []) @@ -79,11 +86,26 @@ export function MessageActions({ content, chatId, userQuery }: MessageActionsPro } }, [content]) + const copyRequestId = useCallback(async () => { + if (!requestId) return + try { + await navigator.clipboard.writeText(requestId) + setCopiedRequestId(true) + if (requestIdTimeoutRef.current !== null) { + window.clearTimeout(requestIdTimeoutRef.current) + } + requestIdTimeoutRef.current = window.setTimeout(() => setCopiedRequestId(false), 1500) + } catch { + /* clipboard unavailable */ + } + }, [requestId]) + const handleFeedbackClick = useCallback( (type: 'up' | 'down') => { if (chatId && userQuery) { setPendingFeedback(type) setFeedbackText('') + setCopiedRequestId(false) } }, [chatId, userQuery] @@ -112,6 +134,7 @@ export function MessageActions({ content, chatId, userQuery }: MessageActionsPro if (!open) { setPendingFeedback(null) setFeedbackText('') + setCopiedRequestId(false) } }, []) @@ -151,9 +174,32 @@ export function MessageActions({ content, chatId, userQuery }: MessageActionsPro Give feedback -- {pendingFeedback === 'up' ? 'What did you like?' : 'What could be improved?'} -
+++ {pendingFeedback === 'up' ? 'What did you like?' : 'What could be improved?'} +
+ {pendingFeedback === 'down' && requestId && ( ++ + )} ++ + ++ {copiedRequestId ? 'Copied request ID' : 'Copy request ID'} + +