Skip to content
Merged
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
68 changes: 68 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
name: CI

on:
pull_request:
branches: [main]

jobs:
lint-and-typecheck:
name: Lint & Type Check
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'

- name: Install dependencies
run: npm ci

- name: Run ESLint
run: npm run lint

- name: Type check
run: npx tsc --noEmit

build:
name: Build
runs-on: ubuntu-latest
needs: lint-and-typecheck
steps:
- uses: actions/checkout@v4

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'

- name: Install dependencies
run: npm ci

- name: Build
run: npm run build
env:
VITE_SUPABASE_URL: ${{ secrets.VITE_SUPABASE_URL_PROD }}
VITE_SUPABASE_PUBLISHABLE_KEY: ${{ secrets.VITE_SUPABASE_ANON_KEY_PROD }}

test:
name: Test
runs-on: ubuntu-latest
needs: lint-and-typecheck
steps:
- uses: actions/checkout@v4

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'

- name: Install dependencies
run: npm ci

- name: Run tests
run: npm run test --if-present
49 changes: 49 additions & 0 deletions .github/workflows/deploy-prod.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
name: Build Production Image

# Auto-build on push to main (no deploy, just build)
on:
push:
branches: [main]

env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}

jobs:
build-and-push:
name: Build & Push Docker Image
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- uses: actions/checkout@v4

- name: Get version
id: version
run: echo "VERSION=$(node -p "require('./package.json').version")" >> $GITHUB_OUTPUT

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Login to GHCR
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Build and push
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: |
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.VERSION }}
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }}
build-args: |
VITE_SUPABASE_URL=${{ secrets.VITE_SUPABASE_URL_PROD }}
VITE_SUPABASE_PUBLISHABLE_KEY=${{ secrets.VITE_SUPABASE_ANON_KEY_PROD }}
cache-from: type=gha
cache-to: type=gha,mode=max
174 changes: 174 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
name: Release

# Manual trigger for controlled releases
on:
workflow_dispatch:
inputs:
run_migrations:
description: 'Run database migrations'
required: true
default: 'false'
type: boolean
deploy_functions:
description: 'Deploy Edge Functions'
required: true
default: 'true'
type: boolean

env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}

jobs:
# Step 1: Run all tests
test:
name: 1. Run Tests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'

- name: Install dependencies
run: npm ci

- name: Lint
run: npm run lint

- name: Type check
run: npx tsc --noEmit

- name: Build (verify)
run: npm run build
env:
VITE_SUPABASE_URL: ${{ secrets.VITE_SUPABASE_URL_PROD }}
VITE_SUPABASE_PUBLISHABLE_KEY: ${{ secrets.VITE_SUPABASE_ANON_KEY_PROD }}

- name: Run tests
run: npm run test --if-present

# Step 2: Run migrations (if enabled, requires approval)
migrate:
name: 2. Run Migrations
runs-on: ubuntu-latest
needs: test
if: ${{ inputs.run_migrations == true }}
environment: production # Requires manual approval
steps:
- uses: actions/checkout@v4

- name: Setup Supabase CLI
uses: supabase/setup-cli@v1
with:
version: latest

- name: Link to production project
run: supabase link --project-ref ${{ secrets.SUPABASE_PROJECT_REF_PROD }}
env:
SUPABASE_ACCESS_TOKEN: ${{ secrets.SUPABASE_ACCESS_TOKEN }}

- name: Run migrations
run: supabase db push --linked
env:
SUPABASE_ACCESS_TOKEN: ${{ secrets.SUPABASE_ACCESS_TOKEN }}

# Step 3: Deploy Edge Functions (if enabled)
deploy-functions:
name: 3. Deploy Edge Functions
runs-on: ubuntu-latest
needs: [test, migrate]
if: |
always() &&
needs.test.result == 'success' &&
(needs.migrate.result == 'success' || needs.migrate.result == 'skipped') &&
inputs.deploy_functions == true
steps:
- uses: actions/checkout@v4

- name: Setup Supabase CLI
uses: supabase/setup-cli@v1
with:
version: latest

- name: Deploy Edge Functions
run: supabase functions deploy --project-ref ${{ secrets.SUPABASE_PROJECT_REF_PROD }}
env:
SUPABASE_ACCESS_TOKEN: ${{ secrets.SUPABASE_ACCESS_TOKEN }}

# Step 4: Build and push Docker image
build-and-push:
name: 4. Build & Push Docker Image
runs-on: ubuntu-latest
needs: [test, migrate, deploy-functions]
if: |
always() &&
needs.test.result == 'success' &&
(needs.migrate.result == 'success' || needs.migrate.result == 'skipped') &&
(needs.deploy-functions.result == 'success' || needs.deploy-functions.result == 'skipped')
permissions:
contents: read
packages: write
outputs:
version: ${{ steps.version.outputs.VERSION }}
steps:
- uses: actions/checkout@v4

- name: Get version
id: version
run: echo "VERSION=$(node -p "require('./package.json').version")" >> $GITHUB_OUTPUT

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Login to GHCR
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Build and push
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: |
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.VERSION }}
build-args: |
VITE_SUPABASE_URL=${{ secrets.VITE_SUPABASE_URL_PROD }}
VITE_SUPABASE_PUBLISHABLE_KEY=${{ secrets.VITE_SUPABASE_ANON_KEY_PROD }}
cache-from: type=gha
cache-to: type=gha,mode=max

# Step 5: Create GitHub release
create-release:
name: 5. Create Release
runs-on: ubuntu-latest
needs: build-and-push
if: needs.build-and-push.result == 'success'
permissions:
contents: write
steps:
- uses: actions/checkout@v4

- name: Create Release
uses: softprops/action-gh-release@v2
with:
tag_name: v${{ needs.build-and-push.outputs.version }}
generate_release_notes: true
body: |
## Release v${{ needs.build-and-push.outputs.version }}
**What was deployed:**
- Migrations: ${{ inputs.run_migrations }}
- Edge Functions: ${{ inputs.deploy_functions }}
**Docker Image:**
```
docker pull ghcr.io/${{ github.repository }}:${{ needs.build-and-push.outputs.version }}
```
12 changes: 12 additions & 0 deletions Caddyfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Replace app.eryxon-flow.com with your domain
app.eryxon-flow.com {
reverse_proxy app:80

# Security headers (Caddy adds many by default)
header {
X-Frame-Options "DENY"
X-Content-Type-Options "nosniff"
X-XSS-Protection "1; mode=block"
Referrer-Policy "strict-origin-when-cross-origin"
}
}
42 changes: 42 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# Build stage
FROM node:20-alpine AS builder

WORKDIR /app

# Copy package files
COPY package*.json ./

# Install dependencies
RUN npm ci

# Copy source code
COPY . .

# Build arguments for Supabase config (passed at build time)
ARG VITE_SUPABASE_URL
ARG VITE_SUPABASE_PUBLISHABLE_KEY

# Set as environment variables for Vite build
ENV VITE_SUPABASE_URL=$VITE_SUPABASE_URL
ENV VITE_SUPABASE_PUBLISHABLE_KEY=$VITE_SUPABASE_PUBLISHABLE_KEY

# Build the app
RUN npm run build

# Production stage
FROM nginx:alpine

# Copy custom nginx config
COPY nginx.conf /etc/nginx/conf.d/default.conf

# Copy built assets from builder
COPY --from=builder /app/dist /usr/share/nginx/html

# Expose port 80
EXPOSE 80

# Health check
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD wget --quiet --tries=1 --spider http://localhost/ || exit 1

CMD ["nginx", "-g", "daemon off;"]
26 changes: 21 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ Comprehensive documentation is available in the [`/docs`](./docs) folder:
- **[API_DOCUMENTATION.md](docs/API_DOCUMENTATION.md)** - REST API reference
- **[DESIGN_SYSTEM.md](docs/DESIGN_SYSTEM.md)** - Design tokens and styling
- **[EDGE_FUNCTIONS_SETUP.md](docs/EDGE_FUNCTIONS_SETUP.md)** - Edge Functions guide
- **[CICD_DEPLOYMENT_PLAN.md](docs/CICD_DEPLOYMENT_PLAN.md)** - CI/CD pipeline and Docker deployment
- **[CLAUDE.md](CLAUDE.md)** - AI assistant guide for contributors

Additional documentation:
Expand Down Expand Up @@ -117,11 +118,26 @@ Additional documentation:

## 📦 Deployment

Deployed via [Lovable Platform](https://lovable.dev/projects/aaa3208a-70fb-4eb6-a5eb-5823f025e734)
| Environment | Platform | Details |
|-------------|----------|---------|
| **Development** | [Lovable](https://lovable.dev/projects/aaa3208a-70fb-4eb6-a5eb-5823f025e734) | Auto-syncs with GitHub |
| **Production** | Docker on Hetzner | EU-hosted, fixed costs |
| **Local/On-Premise** | Docker | Same image, custom Supabase |

To deploy updates: **Share → Publish**
### CI/CD Pipeline

For custom domains, see [Lovable docs](https://docs.lovable.dev/features/custom-domain)
- **PRs**: Automated lint, type-check, build, test
- **Releases**: Manual workflow with migrations and Edge Functions deployment
- **Docker Images**: Published to GitHub Container Registry (GHCR)

See **[docs/CICD_DEPLOYMENT_PLAN.md](docs/CICD_DEPLOYMENT_PLAN.md)** for full setup instructions.

### Quick Docker Run

```bash
docker pull ghcr.io/sheetmetalconnect/eryxon-flow:latest
docker run -p 8080:80 ghcr.io/sheetmetalconnect/eryxon-flow:latest
```

## 📄 License

Expand All @@ -133,6 +149,6 @@ This software is for internal use only and may not be distributed, copied, or mo

---

**Built with** React 18 + TypeScript + Supabase
**Status**: Production
**Built with** React 18 + TypeScript + Supabase
**Status**: Production
**Version**: 1.2
Loading
Loading