Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
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
32 changes: 32 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
name: CI

on:
push:
branches: [main, master]
pull_request:
branches: [main, master]

jobs:
lint-and-test:
runs-on: ubuntu-latest

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Setup Bun
uses: oven-sh/setup-bun@v2
with:
bun-version: latest

- name: Install dependencies
run: bun install

- name: Run lint
run: bun run lint

- name: Run tests
run: bun run test

- name: Run build
run: bun run build
102 changes: 102 additions & 0 deletions DEVOPS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
# DevOps Strategy for Form Engine Project

## 1. Overview
This document outlines the DevOps strategy for the Form Engine project, focusing on the frontend component and its integration points. The strategy prioritizes automation, quality assurance, and seamless deployment using modern CI/CD practices.

## 2. Architecture & DevOps Diagram

```mermaid
graph TD
subgraph "Development Environment"
Dev[Developer]
Git[Local Git]
end

subgraph "Source Control (GitHub)"
Repo[form-engine-frontend Repo]
PR[Pull Request]
Main[Main Branch]
end

subgraph "CI/CD Pipeline (GitHub Actions)"
Lint[Lint & Format]
Test[Unit Tests (Vitest)]
Build[Build Check]
Deploy[Deploy to Vercel]
end

subgraph "Production Environment"
Vercel[Vercel Edge Network]
Browser[User Browser]
end

subgraph "External Dependencies"
API[Backend API (http://localhost:8000 / Production URL)]
DB[Database]
end

Dev -->|Commit Code| Git
Git -->|Push| Repo
Repo -->|Create PR| PR
PR -->|Trigger| Lint
Lint --> Test
Test --> Build
Build -->|Merge| Main
Main -->|Trigger| Deploy
Deploy --> Vercel
Vercel -->|Serve App| Browser
Browser -->|Fetch Data| API
API -->|Query| DB
```

## 3. Components & Deployment Strategy

### 3.1 Frontend Component
- **Source Code Repository**: `https://github.com/Nandgopal-R/form-engine-frontend`
- **Deployment Location**:
- **Provider**: [Vercel](https://vercel.com) (Recommended for Vite/React apps) or AWS S3 + CloudFront.
- **URL**: Production URL (e.g., `https://form-engine.vercel.app`)
- **Configuration**:
- Environment variables must be used for dynamic configuration (e.g., `VITE_API_URL` instead of hardcoded `http://localhost:8000`).
- **Tests & Checks Strategy**:
1. **Static Analysis**:
- **Linting**: `bun run lint` (ESLint) to ensure code quality and catch errors early.
- **Formatting**: `bun run format` (Prettier) to enforce code style.
- **Type Checking**: `tsc` to verify TypeScript types.
2. **Unit & Integration Tests**:
- **Command**: `bun run test` (Vitest).
- **Scope**: Components, Hooks, and Utility functions.
3. **Build Verification**:
- **Command**: `bun run build`.
- Ensures the application builds successfully without errors before deployment.

### 3.2 Backend Component (External Integration)
*Note: This component is referenced as a dependency.*
- **Source Code Repository**: `form-engine` (Assumed)
- **Tests & Checks**:
- API Contract Tests to ensure changes don't break the frontend.

## 4. Tools, Platforms, and Libraries

The following tools and platforms are selected for the DevOps lifecycle:

| Category | Tool/Platform | Purpose |
| :--- | :--- | :--- |
| **Source Control** | **GitHub** | Version control and collaboration. |
| **CI/CD** | **GitHub Actions** | Automated pipelines for testing and deployment. |
| **Build Tool** | **Vite** | Fast frontend build tool. |
| **Hosting** | **Vercel** | Optimized hosting for frontend assets. |
| **Testing** | **Vitest** | Fast unit testing framework. |
| **Linting** | **ESLint** | Javascript/TypeScript linting. |
| **Formatting** | **Prettier** | Code formatting. |
| **Containerization** | **Docker** (Optional) | For consistent local dev or containerized deployment if Vercel is not used. |

## 5. CI/CD Pipeline Workflow

The pipeline is triggered on push to `main` or pull requests.

1. **Install Dependencies**: `bun install`
2. **Lint & Format Check**: `bun run check` (Runs Prettier & ESLint)
3. **Run Tests**: `bun run test`
4. **Build Application**: `bun run build`
5. **Deploy** (Only on `main` branch): Deploy artifacts to Vercel.
2 changes: 1 addition & 1 deletion src/components/Header.test.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { describe, it, expect, vi } from 'vitest';
import { describe, expect, it, vi } from 'vitest';
import Header from './Header';

// Mock TanStack Router Link
Expand Down
4 changes: 2 additions & 2 deletions src/components/app-sidebar.test.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { render, screen } from '@testing-library/react';
import { describe, it, expect, vi } from 'vitest';
import { describe, expect, it, vi } from 'vitest';
import { RouterProvider, createMemoryHistory, createRootRoute, createRouter } from '@tanstack/react-router';
import { AppSidebar } from './app-sidebar';
import { SidebarProvider } from '@/components/ui/sidebar';
import { createRouter, createMemoryHistory, RouterProvider, createRootRoute } from '@tanstack/react-router';

// Mock useIsMobile hook
vi.mock('@/hooks/use-mobile', () => ({
Expand Down
4 changes: 2 additions & 2 deletions src/components/app-sidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ export function AppSidebar({ ...props }: React.ComponentProps<typeof Sidebar>) {
const navigate = useNavigate()
const { data: session } = authClient.useSession()

const userName = session?.user?.name || session?.user?.email || 'User'
const userName = session?.user.name || session?.user.email || 'User'
const userInitial = userName.charAt(0).toUpperCase()

const handleLogout = async () => {
Expand Down Expand Up @@ -114,7 +114,7 @@ export function AppSidebar({ ...props }: React.ComponentProps<typeof Sidebar>) {
</Avatar>
<div className="grid flex-1 text-left text-sm leading-tight">
<span className="truncate font-medium">{userName}</span>
{session?.user?.email && session?.user?.name && (
{session?.user.email && session.user.name && (
<span className="truncate text-xs text-muted-foreground">{session.user.email}</span>
)}
</div>
Expand Down
6 changes: 3 additions & 3 deletions src/components/editor-canvas.test.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { render, screen, fireEvent } from '@testing-library/react';
import { fireEvent, render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { describe, it, expect, vi } from 'vitest';
import { describe, expect, it, vi } from 'vitest';
import { EditorCanvas } from './editor-canvas';
import type { CanvasField } from './fields/field-preview';

const mockFields: CanvasField[] = [
const mockFields: Array<CanvasField> = [
{ id: '1', type: 'text', label: 'Field 1' },
{ id: '2', type: 'number', label: 'Field 2' },
];
Expand Down
2 changes: 1 addition & 1 deletion src/components/editor-sidebar-tabs.test.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { describe, it, expect, vi } from 'vitest';
import { describe, expect, it, vi } from 'vitest';
import { TabsLine } from './editor-sidebar-tabs';

describe('TabsLine', () => {
Expand Down
4 changes: 2 additions & 2 deletions src/components/field-properties.test.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { render, screen, fireEvent } from '@testing-library/react';
import { fireEvent, render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { beforeEach, describe, expect, it, vi } from 'vitest';
import { FieldProperties } from './field-properties';
import type { CanvasField } from './fields/field-preview';

Expand Down
6 changes: 3 additions & 3 deletions src/components/field-properties.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,10 @@
*/

import { useEffect, useState } from 'react'
import { ChevronDown } from 'lucide-react'
import { ValidationRuleBuilder } from './validation-rule-builder'
import type { CanvasField } from './fields/field-preview'
import type { ValidationConfig } from '@/lib/validation-engine'
import { Button } from '@/components/ui/button'
import {
Dialog,
Expand All @@ -38,9 +41,6 @@ import {
CollapsibleContent,
CollapsibleTrigger,
} from '@/components/ui/collapsible'
import { ChevronDown } from 'lucide-react'
import { ValidationRuleBuilder } from './validation-rule-builder'
import type { ValidationConfig } from '@/lib/validation-engine'

interface FieldPropertiesProps {
field: CanvasField | null // Field being edited (null when no field selected)
Expand Down
2 changes: 1 addition & 1 deletion src/components/fields/field-items.test.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { render, screen } from '@testing-library/react';
import { describe, it, expect } from 'vitest';
import { describe, expect, it } from 'vitest';
import { FieldItems } from './field-items';

describe('FieldItems', () => {
Expand Down
5 changes: 3 additions & 2 deletions src/components/fields/field-preview.test.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { describe, it, expect, vi } from 'vitest';
import { FieldPreview, type CanvasField } from './field-preview';
import { describe, expect, it, vi } from 'vitest';
import { FieldPreview } from './field-preview';
import type {CanvasField} from './field-preview';

const mockField: CanvasField = {
id: 'test-id',
Expand Down
2 changes: 1 addition & 1 deletion src/components/fields/field-preview.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { Settings, Star, Trash2 } from 'lucide-react'
import type { ValidationConfig } from '@/lib/validation-engine'
import { Card } from '@/components/ui/card'
import { Input } from '@/components/ui/input'
import { Checkbox } from '@/components/ui/checkbox'
import { Label } from '@/components/ui/label'
import { Button } from '@/components/ui/button'
import { Field, FieldContent, FieldLabel } from '@/components/ui/field'
import { Slider } from '@/components/ui/slider'
import type { ValidationConfig } from '@/lib/validation-engine'

export interface CanvasField {
id: string
Expand Down
2 changes: 1 addition & 1 deletion src/components/nav-main.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
'use client'

import { ChevronRight } from 'lucide-react'
import type { LucideIcon } from 'lucide-react'
import { Link } from '@tanstack/react-router'
import type { LucideIcon } from 'lucide-react'

import {
Collapsible,
Expand Down
2 changes: 1 addition & 1 deletion src/components/ui/badge.test.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { render, screen } from '@testing-library/react';
import { describe, it, expect } from 'vitest';
import { describe, expect, it } from 'vitest';
import { Badge } from './badge';

describe('Badge', () => {
Expand Down
2 changes: 1 addition & 1 deletion src/components/ui/button.test.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { describe, it, expect, vi } from 'vitest';
import { describe, expect, it, vi } from 'vitest';
import { Button } from './button';

describe('Button', () => {
Expand Down
8 changes: 4 additions & 4 deletions src/components/ui/complex-primitives.test.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import { render, screen, fireEvent } from '@testing-library/react';
import { describe, it, expect, vi } from 'vitest';
import { fireEvent, render, screen } from '@testing-library/react';
import { describe, expect, it, vi } from 'vitest';
import { Switch } from './switch';
import { Slider } from './slider';
import {
Sheet,
SheetTrigger,
SheetContent,
SheetDescription,
SheetHeader,
SheetTitle,
SheetDescription
SheetTrigger
} from './sheet';

describe('Interactive UI Components', () => {
Expand Down
4 changes: 2 additions & 2 deletions src/components/ui/field.test.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { render, screen } from '@testing-library/react';
import { describe, it, expect } from 'vitest';
import { describe, expect, it } from 'vitest';
import {
Field,
FieldLabel,
FieldContent,
FieldDescription,
FieldError,
FieldGroup,
FieldLabel,
FieldLegend,
FieldSet
} from './field';
Expand Down
2 changes: 1 addition & 1 deletion src/components/ui/field.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ function FieldError({
...new Map(errors.map((error) => [error?.message, error])).values(),
]

if (uniqueErrors?.length == 1) {
if (uniqueErrors.length == 1) {
return uniqueErrors[0]?.message
}

Expand Down
2 changes: 1 addition & 1 deletion src/components/ui/input.test.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { describe, it, expect, vi } from 'vitest';
import { describe, expect, it, vi } from 'vitest';
import { Input } from './input';

describe('Input', () => {
Expand Down
4 changes: 2 additions & 2 deletions src/components/ui/primitives.test.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { render, screen, fireEvent } from '@testing-library/react';
import { describe, it, expect, vi } from 'vitest';
import { fireEvent, render, screen } from '@testing-library/react';
import { describe, expect, it, vi } from 'vitest';
import { Checkbox } from './checkbox';
import { Separator } from './separator';
import { Skeleton } from './skeleton';
Expand Down
3 changes: 2 additions & 1 deletion src/components/ui/toast.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import * as React from "react"
import * as ToastPrimitives from "@radix-ui/react-toast"
import { cva, type VariantProps } from "class-variance-authority"
import { cva } from "class-variance-authority"
import { X } from "lucide-react"
import type {VariantProps} from "class-variance-authority";

import { cn } from "@/lib/utils"

Expand Down
4 changes: 2 additions & 2 deletions src/components/validation-rule-builder.test.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { render, screen } from '@testing-library/react';
import { describe, it, expect, vi, beforeAll } from 'vitest';
import { ValidationRuleBuilder } from './validation-rule-builder';
import { beforeAll, describe, expect, it, vi } from 'vitest';
import userEvent from '@testing-library/user-event';
import { ValidationRuleBuilder } from './validation-rule-builder';

// Mock pointer capture for Radix UI
beforeAll(() => {
Expand Down
Loading
Loading