UNPKG

oneie

Version:

Build apps, websites, and AI agents in English. Zero-interaction setup for AI agents (Claude Code, Cursor, Windsurf). Download to your computer, run in the cloud, deploy to the edge. Open source and free forever.

865 lines (653 loc) 22.3 kB
--- title: Separate Demo dimension: things category: plans tags: ai related_dimensions: people scope: global created: 2025-11-03 updated: 2025-11-03 version: 1.0.0 ai_context: | This document is part of the things dimension in the plans category. Location: one/things/plans/separate-demo.md Purpose: Documents separate development from customer releases Related dimensions: people For AI agents: Read this to understand separate demo. --- # Separate Development from Customer Releases **Problem:** We develop the full ONE Platform in `/web` but need to release different versions: 1. Main platform website (one.ie) 2. Demo/starter template (web.one.ie) - same as what users download via `npx oneie` **Current Flow:** ``` /web (development) rsync apps/one/web (assembly) build deploy Cloudflare Pages (one.ie) ``` **New Flow:** ``` /web (development - full features) ├→ rsync apps/oneie/web Deploy one.ie (main site) └→ rsync apps/one/web Deploy demo.one.ie (demo) Also: git push one-ie/one Available via: npx oneie ``` **Requirement:** - one.ie = Full platform/marketing site (oneie project) - demo.one.ie = Demo/starter template (one project, matches `npx oneie` download) - Users get same code whether they visit demo.one.ie or run `npx oneie` --- ## Recommended Strategy: Multi-Site Deployment ### Architecture Overview ``` ┌─────────────────────────────────────────────────────────────┐ DEVELOPMENT REPOSITORY: /web - Full ONE Platform features - All navigation and admin tools - Internal development environment variables - Test with: bun run dev (localhost:4321) └──────────────────┬──────────────────────────────────────────┘ ├─────────────────┐ ┌──────────────────────────────┐ ┌──────────────────────────────┐ MAIN SITE: apps/oneie/web DEMO SITE: apps/one/web - Full platform features - Starter template - Marketing content - Minimal features - Deploy one.ie - Deploy demo.one.ie - Repo: one-ie/oneie - Repo: one-ie/one └──────────────────────────────┘ └────────────┬─────────────────┘ ┌──────────────────────────────┐ CLI DISTRIBUTION - npx oneie - git clone one-ie/one - Same code as demo.one.ie └──────────────────────────────┘ ``` --- ## Implementation Plan ### Phase 1: Environment-Based Feature Flags **Create environment-specific configuration:** ```bash # /web/.env.local (DEVELOPMENT - full features) # This is what you use during development ORG_NAME=one ORG_WEBSITE=https://one.ie ONE_BACKEND=on ENABLE_ADMIN_FEATURES=true ENABLE_EXPERIMENTAL_FEATURES=true SHOW_FULL_NAVIGATION=true SITE_TYPE=development ``` ```bash # /web/.env.main (MAIN SITE - one.ie) # This gets copied to apps/one/web during release ORG_NAME=one ORG_WEBSITE=https://one.ie ONE_BACKEND=on ENABLE_ADMIN_FEATURES=false ENABLE_EXPERIMENTAL_FEATURES=false SHOW_FULL_NAVIGATION=true SITE_TYPE=main ``` ```bash # /web/.env.demo (DEMO/STARTER - demo.one.ie) # This gets copied to apps/one/web during release # Also: what users get via npx oneie ORG_NAME=one ORG_WEBSITE=https://demo.one.ie ONE_BACKEND=off ENABLE_ADMIN_FEATURES=false ENABLE_EXPERIMENTAL_FEATURES=false SHOW_FULL_NAVIGATION=false SITE_TYPE=demo ``` **Usage in code:** ```typescript // src/config/features.ts export const features = { adminPanel: import.meta.env.ENABLE_ADMIN_FEATURES === 'true', experimental: import.meta.env.ENABLE_EXPERIMENTAL_FEATURES === 'true', fullNavigation: import.meta.env.SHOW_FULL_NAVIGATION === 'true', orgName: import.meta.env.ORG_NAME || 'one', backendEnabled: import.meta.env.ONE_BACKEND === 'on', }; // src/components/Sidebar.tsx import { features } from '@/config/features'; export function Sidebar() { const navItems = features.fullNavigation ? [/* all nav items */] : [/* customer nav items only */]; return <nav>{/* ... */}</nav>; } ``` --- ### Phase 2: Enhanced Release Script **Update `scripts/release.sh` to support multiple deployment targets:** Add this after line 356 (after web sync): ```bash # ============================================================ # STEP 4B: PREPARE DEPLOYMENT TARGET # ============================================================ section "Step 4b: Prepare Deployment Target" step "4b" # Determine deployment target from argument DEPLOYMENT_TARGET="${2:-production}" # Default: production case "$DEPLOYMENT_TARGET" in main) info "Target: Main Site (apps/oneie/web → one.ie)" TARGET_DIR="apps/oneie/web" ENV_FILE="web/.env.main" PROJECT_NAME="oneie" DOMAIN="one.ie" REPO="one-ie/oneie" ;; demo) info "Target: Demo Site (apps/one/web → demo.one.ie)" TARGET_DIR="apps/one/web" ENV_FILE="web/.env.demo" PROJECT_NAME="one" DOMAIN="demo.one.ie" REPO="one-ie/one" ;; *) error "Unknown deployment target: $DEPLOYMENT_TARGET" echo "Valid targets: main, demo" echo "" echo "Examples:" echo " ./scripts/release.sh patch main # Deploy to one.ie" echo " ./scripts/release.sh patch demo # Deploy to demo.one.ie" exit 1 ;; esac # Create target directory if needed mkdir -p "$(dirname "$TARGET_DIR")" # Sync web to target (already done for apps/one/web) if [ "$TARGET_DIR" != "apps/one/web" ]; then info "Syncing web/ → $TARGET_DIR" rsync -av --delete \ --exclude='.git' \ --exclude='node_modules' \ --exclude='dist' \ --exclude='.astro' \ --exclude='.wrangler' \ web/ "$TARGET_DIR/" success "Synced to $TARGET_DIR" fi # Copy appropriate .env file if [ -f "$ENV_FILE" ]; then info "Copying environment: $ENV_FILE → $TARGET_DIR/.env.local" cp "$ENV_FILE" "$TARGET_DIR/.env.local" success "Environment configured for $DEPLOYMENT_TARGET" else warning "Environment file not found: $ENV_FILE" warning "Using default .env.local from source" fi # Optional: Remove development-only files if [ "$DEPLOYMENT_TARGET" != "development" ]; then info "Cleaning development artifacts from $TARGET_DIR" # Remove files that shouldn't be in production rm -f "$TARGET_DIR/.env.local.example" rm -f "$TARGET_DIR/README-DEV.md" success "Cleaned development artifacts" fi echo "" success "Deployment target prepared: $TARGET_DIR" # Store for later use in deployment step DEPLOY_FROM_DIR="$TARGET_DIR" ``` **Update the deployment step (line 736+) to use `$DEPLOY_FROM_DIR`:** ```bash # Replace WEB_DIR="apps/one/web" with: WEB_DIR="${DEPLOY_FROM_DIR:-apps/one/web}" PROJECT_NAME="${PROJECT_NAME:-web}" ``` **Usage:** ```bash # Deploy to main site (one.ie) ./scripts/release.sh patch main # Deploy to demo site (web.one.ie + npx oneie) ./scripts/release.sh patch demo # Deploy both ./scripts/release.sh patch main && ./scripts/release.sh patch demo # Sync files only, no deployment ./scripts/release.sh sync ``` --- ### Phase 3: Component-Level Feature Gating **Pattern for conditionally rendering features:** ```typescript // src/components/features/admin/AdminPanel.tsx import { features } from '@/config/features'; export function AdminPanel() { // Don't even render in production builds if (!features.adminPanel) { return null; } return ( <div className="admin-panel"> {/* Admin-only features */} </div> ); } ``` ```astro --- // src/pages/admin/index.astro import { features } from '@/config/features'; import AdminPanel from '@/components/features/admin/AdminPanel'; // Redirect if admin features disabled if (!features.adminPanel) { return Astro.redirect('/'); } --- <Layout> <AdminPanel client:load /> </Layout> ``` **Benefits:** - Development: See all features - Production: Features completely removed from bundle - No runtime overhead checking flags --- ### Phase 4: Deployment Targets in Cloudflare **Create separate Cloudflare Pages projects:** ```bash # Main site (full platform) wrangler pages project create one # Custom domain: one.ie # Demo site (starter template) wrangler pages project create oneie # Custom domain: web.one.ie # Development (internal, optional) wrangler pages project create dev # Custom domain: dev.one.ie ``` **Set environment variables per project:** Via Cloudflare Dashboard or wrangler: ```bash # Main site project (one.ie) wrangler pages secret put ENABLE_ADMIN_FEATURES --project-name=one # Enter: false wrangler pages secret put SITE_TYPE --project-name=one # Enter: main # Demo site project (web.one.ie) wrangler pages secret put ONE_BACKEND --project-name=oneie # Enter: off wrangler pages secret put SITE_TYPE --project-name=oneie # Enter: demo # Dev project (optional) wrangler pages secret put ENABLE_ADMIN_FEATURES --project-name=dev # Enter: true wrangler pages secret put SITE_TYPE --project-name=dev # Enter: development ``` --- ### Phase 5: Create New Repo and CLI Distribution **Step 1: Create one-ie/oneie repository** ```bash # On GitHub, create new repository: one-ie/oneie # Description: ONE Platform Starter Template - Download via npx oneie # Clone locally cd ~/workspace git clone https://github.com/one-ie/oneie.git cd oneie # Initialize with same structure as apps/oneie after sync git init git remote add origin https://github.com/one-ie/oneie.git ``` **Step 2: Update release script to push to one-ie/oneie** After syncing to `apps/oneie/`, also push to the separate repo: ```bash # In scripts/release.sh, add after apps/oneie sync: if [ "$DEPLOYMENT_TARGET" == "demo" ]; then section "Push to one-ie/oneie repository" # Ensure oneie repo exists locally if [ ! -d "oneie/.git" ]; then info "Cloning one-ie/oneie repository..." git clone https://github.com/one-ie/oneie.git oneie fi # Note: For demo target, also sync to separate one-ie/one repo section "Push to one-ie/one repository" # Ensure one repo exists locally if [ ! -d "one/.git" ]; then info "Cloning one-ie/one repository..." git clone https://github.com/one-ie/one.git one fi # Note: apps/one already gets pushed to one-ie/one automatically # by the standard release script (step 11) # This happens in the main release flow, no extra sync needed # What happens: # 1. Sync /web → apps/one/web # 2. Sync /one → apps/one/one # 3. Sync /.claude → apps/one/.claude # 4. Commit and push apps/one/ to one-ie/one # 5. Deploy apps/one/web to Cloudflare "one" project → demo.one.ie success "Demo site synced and ready for deployment" fi ``` **Step 3: Update CLI to download from one-ie/oneie** Update `cli/index.js` (or wherever npx oneie is implemented): ```javascript // Instead of downloading from one-ie/one, download from one-ie/oneie const REPO_URL = "https://github.com/one-ie/oneie.git"; // Or use npm package distribution: // npx oneie downloads pre-built tarball ``` **Directory Structure After Sync:** ``` ONE/ # Local workspace ├── web/ # Development website (all features) ├── one/ # Documentation ├── .claude/ # Claude configuration └── apps/ ├── oneie/ # Main site assembly (complete structure) ├── web/ # → Deployed to one.ie ├── one/ # Documentation └── .claude/ # Claude config Pushed to one-ie/oneie └── one/ # Demo site assembly (complete structure) ├── web/ # → Deployed to demo.one.ie ├── one/ # Documentation └── .claude/ # Claude config Pushed to one-ie/one GitHub Repositories (full structure): one-ie/oneie/ # Main site ├── web/ # Website for one.ie ├── one/ # Documentation └── .claude/ # Claude config one-ie/one/ # Demo/starter (what npx oneie clones) ├── web/ # Website for demo.one.ie ├── one/ # Documentation └── .claude/ # Claude config ``` **Result:** - `apps/oneie/` Pushed to one-ie/oneie web/ deployed to one.ie - `apps/one/` Pushed to one-ie/one web/ deployed to demo.one.ie - `npx oneie` Clones entire one-ie/one repo (web/, one/, .claude/) - Users get complete structure: website + docs + Claude config --- ## Alternative Strategies (Considered) ### Option A: Separate Branches **Pros:** - Clear separation - Git-based history **Cons:** - Merge conflicts - Duplicate code - Hard to keep in sync **Verdict:** Not recommended - creates maintenance burden ### Option B: Monorepo with Workspaces **Pros:** - Shared code via packages - Independent deployments **Cons:** - Complex setup - Overhead for small differences **Verdict:** ⚠️ Overkill for feature flags ### Option C: Build-Time Code Stripping **Pros:** - No runtime overhead - Clean production builds **Cons:** - Complex build process - Hard to debug **Verdict:** ⚠️ Good for optimization, not primary strategy --- ## Migration Path ### Week 1: Setup Feature Flags ```bash # 1. Create environment files cp web/.env.local web/.env.local.backup cat > web/.env.production <<EOF # Production environment (customer-facing) ORG_NAME=one ORG_WEBSITE=https://one.ie ONE_BACKEND=on ENABLE_ADMIN_FEATURES=false ENABLE_EXPERIMENTAL_FEATURES=false SHOW_FULL_NAVIGATION=false EOF # 2. Create feature config cat > web/src/config/features.ts <<EOF export const features = { adminPanel: import.meta.env.ENABLE_ADMIN_FEATURES === 'true', experimental: import.meta.env.ENABLE_EXPERIMENTAL_FEATURES === 'true', fullNavigation: import.meta.env.SHOW_FULL_NAVIGATION === 'true', orgName: import.meta.env.ORG_NAME || 'one', backendEnabled: import.meta.env.ONE_BACKEND === 'on', }; EOF # 3. Test locally with production env cd web cp .env.production .env.local bun run build # Verify: admin features should be disabled ``` ### Week 2: Update Components ```bash # Update navigation # Update admin pages # Add feature checks to experimental features # Test thoroughly ``` ### Week 3: Update Release Script ```bash # Add multi-target support to scripts/release.sh # Test with: ./scripts/release.sh sync production # Verify: apps/one/web has production .env ``` ### Week 4: Deploy and Verify ```bash # Deploy production ./scripts/release.sh patch production # Deploy demo (optional) ./scripts/release.sh patch demo # Verify both deployments # Check feature flags working correctly ``` --- ## Development Workflow ### Day-to-Day Development ```bash # 1. Develop with full features cd web bun run dev # localhost:4321 - full ONE Platform # 2. Test production build locally cp .env.production .env.local bun run build bun run preview # Verify customer experience # 3. Restore development environment cp .env.local.backup .env.local # 4. Commit changes git add . git commit -m "feat: add new feature" git push # 5. Release to production cd .. ./scripts/release.sh patch production ``` ### Testing Customer Experience ```bash # Quick test of production build cd web cp .env.production .env.local bun run build && bun run preview # Full test with deployment cd .. ./scripts/release.sh sync production cd apps/one/web bun run build && bun run preview ``` --- ## Benefits of This Approach **Single Codebase:** No duplicate code to maintain **Environment-Based:** Simple `.env` files control features **Clean Builds:** Production builds only include enabled features **Multiple Targets:** Can deploy to production, demo, dev simultaneously **Existing Infrastructure:** Uses current rsync + Cloudflare setup **Type Safety:** TypeScript checks feature flags at compile time **Easy Testing:** Can test customer experience locally **Flexible:** Add new deployment targets easily (enterprise, whitelabel, etc.) **No Vendor Lock-in:** Not tied to specific build tools or frameworks --- ## Security Considerations **Never expose sensitive features in production:** ```typescript // CORRECT: Feature completely removed if (!features.adminPanel) { return null; } // WRONG: Just hidden, still in bundle <div className={features.adminPanel ? 'block' : 'hidden'}> {/* Admin features still shipped */} </div> ``` **Environment variable security:** - Keep `.env.local` in `.gitignore` - Use `.env.production` for defaults (safe to commit) - Set secrets via Cloudflare dashboard - Never commit actual API keys **API endpoint security:** ```typescript // Backend must also check permissions // Don't rely on frontend feature flags alone export const deleteUser = mutation({ handler: async (ctx, args) => { // Check user role in database const user = await getCurrentUser(ctx); if (user.role !== "platform_owner") { throw new Error("Unauthorized"); } // ... delete logic }, }); ``` --- ## Future Enhancements ### White-Label Support ```bash # /web/.env.customer-acme ORG_NAME=acme ORG_WEBSITE=https://acme.com ORG_FOLDER=acme ONE_BACKEND=on ENABLE_ADMIN_FEATURES=false CUSTOM_BRANDING=true ``` Deploy with: ```bash ./scripts/release.sh patch customer-acme ``` ### Multi-Tenant Deployments Each customer gets: - Separate Cloudflare Pages project - Own environment variables - Custom domain - Branded experience - Independent deployment ### Progressive Rollout ```bash # Deploy to staging first ./scripts/release.sh patch staging # Test thoroughly # Then deploy to production ./scripts/release.sh patch production ``` --- ## Troubleshooting ### Issue: Production build includes development features **Cause:** Wrong `.env` file used during build **Solution:** ```bash # Verify environment during release cd apps/one/web cat .env.local | grep ENABLE_ADMIN # Should show: false ``` ### Issue: Features disabled in development **Cause:** `.env.production` copied over `.env.local` **Solution:** ```bash cd web rm .env.local cp .env.local.backup .env.local # Or just set ENABLE_ADMIN_FEATURES=true ``` ### Issue: Environment variables not updating in deployed site **Cause:** Cloudflare Pages caches environment variables **Solution:** ```bash # Force rebuild wrangler pages deployment create --project-name=web web/dist # Or update via dashboard: # Cloudflare → Pages → web → Settings → Environment variables ``` --- ## Summary **What You Get:** 1. **Development:** Full ONE Platform with all features (`/web`) 2. **Main Site:** Full platform + marketing (`apps/oneie/web one.ie`) 3. **Demo Site:** Starter template (`apps/one/web demo.one.ie`) 4. **Downloadable:** Same as demo via `npx oneie` (from `one-ie/one` repo) **How It Works:** - Single codebase (`/web`) with feature flags - Different `.env` files per deployment target - Release script syncs to multiple targets - Deploy to separate Cloudflare projects (oneie one.ie, one demo.one.ie) - Demo code also pushed to separate GitHub repo (one-ie/one) for CLI distribution **Commands:** ```bash # Develop locally (full features) cd web && bun run dev # Release to main site (one.ie) ./scripts/release.sh patch main # Release to demo site (demo.one.ie + npx oneie) ./scripts/release.sh patch demo # Release both ./scripts/release.sh patch main && ./scripts/release.sh patch demo # Sync files only (no deployment) ./scripts/release.sh sync ``` **Repositories:** - `one-ie/oneie` Main site assembly (apps/oneie/ one.ie) - `one-ie/one` Demo/starter template (apps/one/ + one/ demo.one.ie + npx oneie) - `one-ie/cli` CLI package (npx oneie downloads from one-ie/one) **Next Steps:** 1. Read this document 2. Create environment files: - `web/.env.main` (main site: one.ie) - `web/.env.demo` (demo site: demo.one.ie) 3. Create `web/src/config/features.ts` with feature flags 4. Test both builds locally: - `cp .env.main .env.local && bun run build` - `cp .env.demo .env.local && bun run build` 5. Create GitHub repository: `one-ie/oneie` 6. Create Cloudflare Pages projects: - `oneie` (for one.ie) - `one` (for demo.one.ie) 7. Update `scripts/release.sh` with multi-target support 8. Deploy main site: `./scripts/release.sh patch main` 9. Deploy demo site: `./scripts/release.sh patch demo` 10. Test both deployments: - https://one.ie (full platform) - https://demo.one.ie (starter template) 11. Update CLI to download from `one-ie/one` 12. Test: `npx oneie` downloads correct starter template --- **Result:** - **one.ie** Full platform/marketing site (oneie project) - **demo.one.ie** Live demo (same as npx oneie download) - **npx oneie** Downloads starter template from one-ie/one - Single codebase, clean separation, no duplication