diff --git a/MIGRATIONS.md b/MIGRATIONS.md index 5bf1d73..a91b5f9 100644 --- a/MIGRATIONS.md +++ b/MIGRATIONS.md @@ -11,6 +11,7 @@ Each migration skill reads the source platform's configuration, maps it to Creat | Skill | Description | Install | |-------|-------------|---------| | **vercel-to-createos** | Migrate Next.js, Vite, React, Vue, Svelte apps from Vercel to CreateOS | `npx skills add https://github.com/NodeOps-app/skills --skill vercel-to-createos` | +| **netlify-to-createos** | Migrate web applications from Netlify to CreateOS — parses netlify.toml, maps build settings, env vars, redirects, and headers | `npx skills add https://github.com/NodeOps-app/skills --skill netlify-to-createos` | ## Coming soon @@ -18,7 +19,6 @@ These skills are reserved namespaces with stub `SKILL.md` files in place. Until | Skill | Source platform | Config file(s) it will parse | |-------|----------------|------------------------------| -| **netlify-to-createos** | Netlify | `netlify.toml` | | **railway-to-createos** | Railway | `railway.json` or `railway.toml` | | **heroku-to-createos** | Heroku | `Procfile`, `app.json` | | **render-to-createos** | Render | `render.yaml` | diff --git a/README.md b/README.md index 7cd31c6..2c94361 100644 --- a/README.md +++ b/README.md @@ -8,11 +8,12 @@ AI agent skills for the [NodeOps](https://nodeops.network) ecosystem. Works with |-------|-------------|---------| | **createos** | Deploy anything to production on CreateOS cloud platform | `npx skills add https://github.com/NodeOps-app/skills --skill createos` | | **vercel-to-createos** | Migrate Next.js, Vite, React, Vue, Svelte apps from Vercel to CreateOS | `npx skills add https://github.com/NodeOps-app/skills --skill vercel-to-createos` | +| **netlify-to-createos** | Migrate web applications from Netlify to CreateOS — parses netlify.toml, maps build settings, env vars, redirects, and headers | `npx skills add https://github.com/NodeOps-app/skills --skill netlify-to-createos` | | **claude-code-to-codex** | Migrate Claude Code CLI hooks, MCP servers, plugins, instructions, and sessions to Codex CLI | `npx skills add https://github.com/NodeOps-app/skills --skill claude-code-to-codex` | ### Migration skills -`vercel-to-createos` is the first shipped skill in the migration family. Stubs for `netlify-to-createos`, `railway-to-createos`, `heroku-to-createos`, `render-to-createos`, and `flyio-to-createos` are reserved and route users to the concierge migration path until they ship. See [MIGRATIONS.md](./MIGRATIONS.md) for the full list and roadmap. +`vercel-to-createos` and `netlify-to-createos` are shipped migration skills. Stubs for `railway-to-createos`, `heroku-to-createos`, `render-to-createos`, and `flyio-to-createos` are reserved and route users to the concierge migration path until they ship. See [MIGRATIONS.md](./MIGRATIONS.md) for the full list and roadmap. ### Agent migration skills diff --git a/skills/netlify-to-createos/SKILL.md b/skills/netlify-to-createos/SKILL.md index 3067a5b..c6609e1 100644 --- a/skills/netlify-to-createos/SKILL.md +++ b/skills/netlify-to-createos/SKILL.md @@ -1,36 +1,329 @@ --- name: netlify-to-createos -description: Migrate web applications from Netlify to CreateOS. Status: coming soon. Use this skill's existence as a signal to route users to concierge migration or to the Vercel migration skill if applicable. Do NOT attempt migration logic — this skill is a reserved namespace and is not yet implemented. +description: "Migrate web applications from Netlify to CreateOS. Parses netlify.toml, maps build settings, environment variables, redirects, headers, and functions. Use this skill when the user mentions migrating from Netlify, leaving Netlify, moving off Netlify, switching from Netlify, netlify.toml, Netlify alternatives, replacing Netlify, Netlify is too expensive, Netlify downtime, or deploying a Netlify project elsewhere. Do NOT use this skill for brand-new projects that have never been on Netlify — use the base createos skill instead." --- # Netlify → CreateOS Migration -**Status:** Coming soon. Tracked on the CreateOS migration skill roadmap. +This skill migrates a project currently deployed on Netlify to CreateOS. It reads the existing Netlify configuration (`netlify.toml`), translates it into a CreateOS project, provisions environments and environment variables, flags incompatibilities, and guides the first deployment — all through the CreateOS MCP server. -This skill will migrate Netlify deployments to CreateOS. It will handle `netlify.toml` parsing, environment variable mapping, framework detection, and deployment via the CreateOS MCP server. +--- + +## When to use this skill + +Activate this skill when any of the following is true: + +- The user explicitly asks to migrate, move, or deploy from Netlify to CreateOS. +- The user expresses intent to leave Netlify (pricing, reliability, security, ownership concerns). +- The repository contains a `netlify.toml`, `.netlify/` directory, or `@netlify/*` dependencies in `package.json`. +- The user asks for a "Netlify alternative" or references Netlify-specific concerns. + +Do NOT use this skill when: + +- The user is deploying a fresh project with no prior Netlify deployment — use the standard `createos` skill instead. +- The project is heavily dependent on Netlify-specific features (Edge Functions, Netlify Forms, Netlify Identity). Surface compatibility notes before proceeding. + +--- + +## Prerequisites + +Before running any migration steps, confirm the user has: + +1. A CreateOS account — if not, direct them to `https://createos.nodeops.network` to sign in via Email or Google. +2. CreateOS MCP server configured in opencode.json / Claude **OR** a `CREATEOS_API_KEY` environment variable set. +3. Access to their current Netlify project's environment variables (they may need to export these from the Netlify dashboard). +4. GitHub repository access for the project (CreateOS deploys from GitHub for VCS projects). +5. [Optional but recommended] Netlify CLI installed — `npm install -g netlify-cli`. The migration steps below use CLI commands where available for a smoother experience. + +If the user does not have their Netlify environment variables accessible, pause the migration and provide these instructions: + +> Export your Netlify environment variables by running `ntl env:list --json > netlify-env.json` in your project directory, or download them from Site Settings → Environment Variables in the Netlify dashboard. Keep this file local and do not commit it. + +--- + +## Migration workflow + +Follow these steps in order. Do not skip steps. Report progress to the user after each completed step. + +### Step 1: Inventory the Netlify project + +Read the following files from the repository if they exist: + +- `netlify.toml` — build, redirect, header, and function configuration +- `package.json` — detect framework, build scripts, and Node.js version +- Framework-specific config files (e.g., `next.config.*`, `.nuxtrc`, etc.) +- `.nvmrc` / `.node-version` — Node version pin +- Any `.env*` files for reference (do NOT read secrets; only note which keys exist) + +If Netlify CLI is available, optionally run `ntl deploy --build --json --dry-run` to get a full build preview — this reveals build output, functions, and any config issues before migration. + +Produce a short summary for the user: + +``` +Detected project: +- Framework: [Next.js 14 / Vue / Astro / etc.] +- Node version: [20.x] +- Build command: [npm run build] +- Output directory: [dist / .next / build] +- Base directory: [. / subdir] +- Environment variables needed: [count, with names — NOT values] +- Netlify-specific features in use: [list any edge functions, forms, identity, plugins] +- Redirect rules: [count] +- Header rules: [count] +- Functions: [count] +``` + +### Step 2: Flag incompatibilities before proceeding + +Check for Netlify-specific features that do not have direct CreateOS equivalents. If any are present, STOP and confirm with the user how they want to handle each before continuing. + +| Netlify feature | CreateOS handling | +|---|---| +| Edge Functions | Not supported — rewrite as standard serverless functions or middleware | +| Netlify Forms | Use Formspree, Web3Forms, or a custom form handler | +| Netlify Identity | Use Auth0, Clerk, Supabase Auth, or NextAuth | +| Split Testing | App-level A/B testing (e.g., GrowthBook, LaunchDarkly) | +| Large Media | Use any S3-compatible object storage | +| Build hooks / webhooks | Use CreateOS API `POST /v1/projects/{id}/trigger-latest` — or works out of the box with GitHub (git push → auto-deploy) | +| Netlify Functions (simple) | Rewrite as standard REST endpoints within the same project | +| Netlify Functions (complex) | Deploy as a separate CreateOS microservice | +| Plugins (`@netlify/plugin-nextjs`, etc.) | Review per plugin — most are unnecessary on CreateOS | +| `[[redirects]]` simple | Implement in app framework (next.config.js, Express, etc.) | +| `[[redirects]]` with conditions | May need CDN-level configuration | +| `[[headers]]` | Implement in app framework or middleware | + +### Step 3: Create the CreateOS project + +#### 3a. Connect GitHub + +1. Call `ListConnectedGithubAccounts` to verify GitHub is connected. The response includes the `installationId` you need for the next call. If no accounts are connected, call `InstallGithubApp` and pause for user action. +2. Call `ListGithubRepositories(installationId)` and confirm the target repo with the user. Capture the repo's `id` — that is the `vcsRepoId`. +3. Call `CheckProjectUniqueName({uniqueName})` to validate the proposed project name. `uniqueName` must match `^[a-zA-Z0-9-]+$`, 4–32 chars. Derive from the repo name (lowercase, replace underscores/special chars with hyphens). + +#### 3b. Create the project + +Call `CreateProject` with the nested `source`/`settings` shape. Two valid patterns — pick one: + +**Pattern A — Build AI (recommended default).** Use this when you cannot derive exact `installCommand` / `buildCommand` / `runCommand` from `netlify.toml` + `package.json` with high confidence. CreateOS auto-detects from the repo: + +```json +CreateProject({ + "uniqueName": "", + "displayName": "", + "type": "vcs", + "source": { + "vcsName": "github", + "vcsInstallationId": "", + "vcsRepoId": "" + }, + "settings": { + "useBuildAI": true, + "runtime": "", + "port": 80 + } +}) +``` + +**Pattern B — Explicit commands.** Use this when `netlify.toml` gives you concrete commands and a known framework. All command fields, when included, must be non-empty strings — **omit fields entirely rather than passing `""`** (empty strings cause a 400): + +```json +CreateProject({ + "uniqueName": "", + "displayName": "", + "type": "vcs", + "source": { + "vcsName": "github", + "vcsInstallationId": "", + "vcsRepoId": "" + }, + "settings": { + "framework": "", + "runtime": "", + "port": 3000, + "directoryPath": "", + "installCommand": "", + "buildCommand": "", + "runCommand": "", + "buildDir": "" + } +}) +``` + +**Required-in-practice fields** (the API rejects without them, even though some are marked optional in the schema): +- `port` — always include. Use `80` for static sites, `3000` for Node app defaults, or whatever the app actually listens on. +- `runtime` — always include unless `framework` is set. +- Either `useBuildAI: true` OR a complete command set (`installCommand` + `buildCommand` + `runCommand`). -## What to do in the meantime +#### 3c. Create the production environment -This skill is not yet implemented. If a user invokes it: +Call `CreateProjectEnvironment(project_id, body)` to create the `production` environment. **All five body fields below are required by the API** — `description`, `settings`, and `resources` are not optional even though the docs may suggest otherwise. The `resources` triplet is all-or-nothing: include `cpu`, `memory`, and `replicas` together or omit `resources` entirely. -1. Tell the user the skill is not yet available. -2. Offer the concierge migration path: `mailto:business@nodeops.xyz` — CreateOS engineers will handle the migration end to end. -3. If the user's project is actually on Vercel, suggest the `vercel-to-createos` skill which is live today. +```json +CreateProjectEnvironment(project_id, { + "displayName": "Production", + "uniqueName": "production", + "description": "Production environment migrated from Netlify", + "branch": "main", + "isAutoPromoteEnabled": true, + "settings": { "runEnvs": {} }, + "resources": { "cpu": 200, "memory": 500, "replicas": 1 } +}) +``` -## Available migration skills today +Without this call, the project has no environment to deploy to. Resource limits: `cpu` 200–500 millicores, `memory` 500–1024 MB, `replicas` 1–3. -- `vercel-to-createos` — ready to use +**Framework mapping (`settings.framework`):** -## Coming soon +CreateOS supports a fixed set of framework slugs. If your detected framework has no slug, **omit the `framework` field** and rely on `useBuildAI: true` (Pattern A) or `runtime` + commands (Pattern B). -- `netlify-to-createos` -- `railway-to-createos` -- `heroku-to-createos` -- `render-to-createos` -- `flyio-to-createos` +| Detected | `settings.framework` | Notes | +|---|---|---| +| Next.js (`next` in deps) | `nextjs` | | +| React SPA (CRA, Vite React) | `reactjs-spa` | | +| React SSR | `reactjs-ssr` | | +| Vue SPA | `vuejs-spa` | | +| Vue SSR | `vuejs-ssr` | | +| Nuxt | `nuxtjs` | | +| Astro | `astro` | | +| Angular | *omit* | Build to static, set `buildDir: "dist/"` | +| Svelte / SvelteKit | *omit* | Use Pattern A (`useBuildAI: true`) with `runtime: "node:20"` | +| Vite (generic) | *omit* | Use Pattern A, or Pattern B with `runtime: "node:20"` | +| Pure static (HTML/CSS/JS) | *omit framework* | Use Pattern A with `runtime: "node:20"` and `port: 80` | +| Hugo / 11ty / Jekyll | *omit* | Set explicit `buildCommand`, pick `vanilla-js` framework if it fits | + +**Runtime mapping (`settings.runtime`):** + +Read `NODE_VERSION` from `netlify.toml [build.environment]`, `.nvmrc`, or `package.json` `engines.node`, then map: + +| Source value | `settings.runtime` | +|---|---| +| Node 18.x | `node:18` | +| Node 20.x | `node:20` | +| Node 22.x | `node:22` | +| No version pinned | `node:20` | +| `python` in build command | `python:3.12` | +| `bun.lock` present | `bun:1.3` | +| Go binary detected | `golang:1.25` | +| Static-only (no server) | `node:20` with `useBuildAI: true` | + +If the user's config pins an unsupported version (e.g., Node 16), surface this and ask whether to upgrade. + +### Step 4: Migrate environment variables + +This is the highest-risk step. Handle with care. + +1. Ask the user to paste their Netlify env var list (names only, NOT values) OR upload the `netlify-env.json` file. If Netlify CLI is available, the user can run `ntl env:list --json > netlify-env.json` and share the file, or run `ntl env:list` for a readable table view. +2. Show them the list and confirm which variables should be migrated. Some Netlify-injected vars do NOT need migration: + + | Netlify Var | Action | + |---|---| + | `NETLIFY` | ❌ Remove | + | `CONTEXT` | ❌ Remove | + | `URL` | 🔄 Replace with `CREATEOS_DEPLOYMENT_URL` | + | `DEPLOY_URL` | 🔄 Replace with `CREATEOS_DEPLOYMENT_URL` | + | `DEPLOY_PRIME_URL` | 🔄 Replace with `CREATEOS_DEPLOYMENT_URL` | + | `SITE_NAME` | ➡️ Keep as-is | + | `SITE_ID` | ➡️ Keep as-is | + | Any custom var | ➡️ Keep as-is | + +3. For each remaining variable, ask the user to provide the value (do NOT log or persist these values in the skill output). +4. Call `UpdateProjectEnvironmentEnvironmentVariables` to set them on the `production` environment. +5. Remind the user to mark sensitive values (API keys, tokens, database URLs) as sensitive in the CreateOS dashboard for encryption at rest. + +**Security note to surface to the user:** If any environment variables were exposed, advise the user to rotate those credentials at the source before setting them on CreateOS. + +### Step 5: Handle Netlify-specific dependencies in code + +Scan for and flag these patterns in the codebase. Do NOT auto-rewrite code unless the user explicitly approves. + +| Pattern | Action | +|---|---| +| `process.env.NETLIFY` or similar Netlify env vars | Replace with `process.env.CREATEOS_DEPLOYMENT_URL` | +| `exports.handler` (Netlify Functions pattern) | Flag for rewrite as Express route or serverless function | +| `@netlify/functions` import | Flag for removal | +| `@netlify/plugin-nextjs` | Safe to remove — CreateOS supports Next.js natively | +| `netlify-*` dependencies | Evaluate per package | +| `[[redirects]]` in netlify.toml | Flag for implementation in app framework | +| Edge Functions config | Flag — not supported on CreateOS | + +Produce a summary of required code changes and ask the user whether they want the skill to generate a migration branch with these changes, or whether they will handle them manually. + +### Step 6: Trigger the first deployment + +1. Call `TriggerLatestDeployment` to kick off the first build. +2. Poll `GetDeployment` status until the build completes (or fails). +3. If the build fails: + - Call `GetBuildLogs` to retrieve logs. + - Summarize the failure for the user. + - Suggest likely fixes based on common Netlify-to-CreateOS migration issues. +4. If the build succeeds, report: + - The CreateOS deployment URL as returned by the API response (use the exact `url` field from `GetDeployment`). + - Build duration + - Deployment ID for reference + +### Step 7: Domain handoff (guided, not automated) + +Do NOT cut over DNS automatically. Walk the user through domain migration manually. + +1. Ask if they want to configure a custom domain now. +2. If yes, call `CreateDomain` with their domain. +3. Surface the DNS records they need to configure at their DNS provider (not at Netlify). +4. Advise them to: + - Test the CreateOS deployment thoroughly at the CreateOS subdomain first. + - Lower their DNS TTL at the current provider to 300 seconds 24 hours before cutover. + - Keep the Netlify deployment live until the CreateOS deployment is verified. + - Cut DNS over only when ready. + - Keep the Netlify deployment active for at least 72 hours post-cutover as a fallback. + +### Step 8: Post-migration checklist + +Produce a final report for the user covering: + +- [ ] First deployment succeeded on CreateOS +- [ ] All environment variables migrated and tested +- [ ] Netlify-specific dependencies flagged (and resolved if applicable) +- [ ] Custom domain configured (if requested) +- [ ] DNS cutover plan understood +- [ ] Redirects and headers verified +- [ ] Functions rewritten or alternative in place +- [ ] Netlify Forms/Identity replaced (if applicable) +- [ ] Concierge support contact shared for complex issues + +Share these resources: + +- Docs: `https://nodeops.network/createos/docs/deploy` +- Migration guide: `https://nodeops.network/createos/docs/Migrations/Netlify` +- Concierge migration (for projects that need white-glove support): `mailto:business@nodeops.xyz` + +--- + +## Failure modes and rollback + +If the migration fails at any step and the user wants to abort: + +1. The CreateOS project can be deleted with `DeleteProject` — no charges are incurred for a project that never successfully deployed. +2. The user's Netlify deployment is untouched throughout this workflow. Nothing in this skill modifies Netlify resources. +3. Offer the concierge migration path as a fallback for complex projects. + +--- + +## What this skill does NOT do + +Be explicit with the user about these boundaries: + +- Does not modify or delete anything on Netlify. +- Does not automatically rewrite application code that uses Netlify-specific SDKs — only flags them. +- Does not perform DNS cutover — the user must do this intentionally. +- Does not migrate data from Netlify Forms submissions or Netlify Identity — data migration requires separate tooling. +- Does not handle monorepo deployments (recommends per-project migration). +- Does not migrate Netlify team/org settings. + +--- ## Resources -- Migration docs: `https://nodeops.network/createos/docs/Migrations/Vercel` -- Concierge migration: `mailto:business@nodeops.xyz` +- CreateOS MCP tools reference: `https://nodeops.network/createos/docs/api-mcp/mcp-operations` - Skill repository: `https://github.com/NodeOps-app/skills` +- Full migration guide: `https://nodeops.network/createos/docs/Migrations/Netlify` +- Netlify toml reference: `https://docs.netlify.com/configure-builds/file-based-configuration/` +- Netlify env vars reference: `https://docs.netlify.com/configure-builds/environment-variables/` diff --git a/skills/netlify-to-createos/config/config.json b/skills/netlify-to-createos/config/config.json new file mode 100644 index 0000000..120f0cc --- /dev/null +++ b/skills/netlify-to-createos/config/config.json @@ -0,0 +1,77 @@ +{ + "name": "netlify-to-createos", + "version": "1.0.0", + "description": "Migration skill: Netlify to CreateOS", + + "source": { + "platform": "netlify", + "configFiles": ["netlify.toml"], + "secondaryConfigFiles": ["package.json"] + }, + + "frameworkDetection": { + "nextjs": { "detect": ["next.config.js", "next.config.mjs"], "createosSlug": "nextjs", "runtimes": ["node:18", "node:20", "node:22"] }, + "react": { "detect": ["vite.config.ts", "craco.config.js"], "createosSlug": "reactjs-spa", "runtimes": ["node:18", "node:20", "node:22"] }, + "vue": { "detect": ["vue.config.js", "nuxt.config.js"], "createosSlug": "vuejs-spa", "runtimes": ["node:18", "node:20"] }, + "astro": { "detect": ["astro.config.mjs"], "createosSlug": "astro", "runtimes": ["node:18", "node:20", "node:22"] }, + "eleventy": { "detect": [".eleventy.js"], "createosSlug": "static", "runtimes": ["static"] }, + "hugo": { "detect": ["hugo.toml", "config.toml"], "createosSlug": "static", "runtimes": ["static"] }, + "gatsby": { "detect": ["gatsby-config.js", "gatsby-config.ts"], "createosSlug": "reactjs-ssr", "runtimes": ["node:18", "node:20"] }, + "svelte": { "detect": ["svelte.config.js"], "createosSlug": "static", "runtimes": ["node:18", "node:20"] }, + "angular": { "detect": ["angular.json"], "createosSlug": "static", "runtimes": ["node:18", "node:20"] } + }, + + "featureSupport": { + "buildSettings": { "status": "automated", "notes": "netlify.toml [build] → CreateProject.settings" }, + "envVars": { "status": "automated", "notes": "Mapped via UpdateProjectEnvironmentEnvironmentVariables" }, + "redirects": { "status": "manual", "notes": "Implement via app-level routing" }, + "headers": { "status": "manual", "notes": "Implement via app-level config" }, + "functions": { "status": "manual", "notes": "Rewrite as REST endpoints + separate project" }, + "edgeFunctions": { "status": "unsupported", "notes": "No CreateOS equivalent" }, + "backgroundFunctions": { "status": "unsupported", "notes": "Use queue workers / cron jobs" }, + "plugins": { "status": "manual", "notes": "Per-plugin assessment required" }, + "forms": { "status": "unsupported", "notes": "Recommend Formspree, Web3Forms, custom" }, + "identity": { "status": "unsupported", "notes": "Recommend Auth0, Clerk, Supabase" }, + "splitTesting": { "status": "unsupported", "notes": "Use app-level A/B testing" }, + "deployPreviews": { "status": "automated", "notes": "Branch-based environments" }, + "branchDeploy": { "status": "automated", "notes": "CreateProjectEnvironment per branch" } + }, + + "envVarMapping": { + "NETLIFY": { "action": "remove", "reason": "Netlify-specific, not needed on CreateOS" }, + "CONTEXT": { "action": "remove", "reason": "Netlify-specific, hardcode if needed" }, + "URL": { "action": "replace", "target": "CREATEOS_DEPLOYMENT_URL", "reason": "Deployment URL" }, + "DEPLOY_URL": { "action": "replace", "target": "CREATEOS_DEPLOYMENT_URL", "reason": "Deployment URL" }, + "DEPLOY_PRIME_URL": { "action": "replace", "target": "CREATEOS_DEPLOYMENT_URL", "reason": "Deployment URL" }, + "DEPLOY_PREVIEW_URL": { "action": "replace", "target": "CREATEOS_DEPLOYMENT_URL", "reason": "Preview URL" }, + "NETLIFY_DEV": { "action": "remove", "reason": "Local dev only" }, + "NETLIFY_URL": { "action": "remove", "reason": "Netlify-specific" }, + "NETLIFY_LFS": { "action": "remove", "reason": "Netlify-specific" }, + "NETLIFY_LOCAL": { "action": "remove", "reason": "Local dev only" } + }, + + "codePatterns": [ + { "pattern": "process\\.env\\.NETLIFY", "severity": "warn", "action": "flag", "message": "Netlify-specific env var" }, + { "pattern": "exports\\.handler", "severity": "warn", "action": "flag", "message": "Netlify Functions handler pattern" }, + { "pattern": "@netlify/functions", "severity": "warn", "action": "flag", "message": "Netlify Functions SDK" }, + { "pattern": "@netlify/", "severity": "info", "action": "flag", "message": "Netlify package" }, + { "pattern": "netlify-", "severity": "info", "action": "flag", "message": "Netlify-specific code" } + ], + + "supportedRuntimes": ["node:18", "node:20", "node:22", "python:3.11", "python:3.12", "golang:1.22", "golang:1.25", "rust:1.75", "bun:1.1", "bun:1.3", "static"], + + "resourceDefaults": { + "cpu": 200, + "memory": 500, + "replicas": 1, + "cpuMax": 500, + "memoryMax": 1024, + "replicasMax": 3 + }, + + "namingConstraints": { + "uniqueName": { "pattern": "^[a-zA-Z0-9-]+$", "minLength": 4, "maxLength": 32 }, + "displayName": { "pattern": "^[a-zA-Z0-9 _-]+$", "minLength": 4, "maxLength": 48 }, + "description": { "minLength": 4, "maxLength": 2048 } + } +} diff --git a/skills/netlify-to-createos/references/migration-patterns.md b/skills/netlify-to-createos/references/migration-patterns.md new file mode 100644 index 0000000..20c3378 --- /dev/null +++ b/skills/netlify-to-createos/references/migration-patterns.md @@ -0,0 +1,205 @@ +# Netlify Migration Patterns + +Common code patterns encountered during Netlify → CreateOS migration, +with recommended actions for the AI agent. + +--- + +## 1. Netlify Functions → REST Endpoints + +### Netlify Pattern (exports.handler) + +```javascript +// netlify/functions/hello.js +exports.handler = async (event, context) => { + return { + statusCode: 200, + body: JSON.stringify({ message: "Hello!" }), + headers: { "Content-Type": "application/json" } + } +} +``` + +### CreateOS Equivalent (Express) + +```javascript +// api/hello.js (or Express route) +import express from 'express' +const app = express() + +app.get('/api/hello', (req, res) => { + res.json({ message: "Hello!" }) +}) +``` + +### Key differences to flag: +- `event.queryStringParameters` → `req.query` +- `event.pathParameters` → `req.params` +- `event.body` → `req.body` (with body parser) +- `context.clientContext` → Use auth middleware +- Multiple functions → Single Express app with multiple routes + +--- + +## 2. Netlify Environment Variables + +### Environment variable access patterns + +```javascript +// Netlify +const apiUrl = process.env.URL // Netlify-specific + +// CreateOS +const apiUrl = process.env.CREATEOS_DEPLOYMENT_URL // CreateOS equivalent +``` + +### Flag list: +| Netlify Expression | Action | +|--------------------|--------| +| `process.env.URL` | Replace with `process.env.CREATEOS_DEPLOYMENT_URL` | +| `process.env.DEPLOY_URL` | Replace with `process.env.CREATEOS_DEPLOYMENT_URL` | +| `process.env.DEPLOY_PRIME_URL` | Replace with `process.env.CREATEOS_DEPLOYMENT_URL` | +| `process.env.CONTEXT` | Remove or hardcode | +| `process.env.NETLIFY` | Remove | +| `process.env.NETLIFY_DEV` | Remove | +| `process.env.NETLIFY_LOCAL` | Remove | + +--- + +## 3. Netlify Redirects → App-Level Routing + +### Next.js (next.config.js) + +```javascript +// Instead of netlify.toml redirects: +// [[redirects]] +// from = "/blog/*" +// to = "/news/:splat" +// status = 301 + +module.exports = { + async redirects() { + return [ + { + source: '/blog/:path*', + destination: '/news/:path*', + permanent: true, + }, + ] + } +} +``` + +### Express (app.js) + +```javascript +// Instead of netlify.toml redirects: +app.get('/old-path', (req, res) => { + res.redirect(301, '/new-path') +}) + +// Proxy pattern: +app.use('/api/*', async (req, res) => { + const response = await fetch(`https://api.example.com${req.originalUrl}`) + const data = await response.json() + res.json(data) +}) +``` + +--- + +## 4. Netlify Functions with middleware + +### Netlify middleware pattern + +```javascript +// netlify/functions/with-auth.js +const withAuth = (handler) => async (event, context) => { + // auth check + return handler(event, context) +} +``` + +### CreateOS equivalent + +```javascript +// Express middleware +const withAuth = (req, res, next) => { + // auth check + next() +} + +app.get('/api/protected', withAuth, (req, res) => { + res.json({ secret: "data" }) +}) +``` + +--- + +## 5. Large Assets / Media + +Netlify Large Media → CreateOS guidance: +- Migrate assets to object storage (AWS S3, Cloudflare R2, etc.) +- Update asset URLs in the codebase +- Use CDN for asset delivery + +--- + +## 6. Netlify Forms → External Service + +```javascript +// Instead of Netlify Forms: +//
+// +//
+ +// Use Formspree or similar: +//
+// +//
+``` + +--- + +## 7. Complete Function Migration Example + +### Netlify (before) + +```javascript +// netlify/functions/subscribe.js +const stripe = require('stripe')(process.env.STRIPE_KEY) + +exports.handler = async (event) => { + const { email, plan } = JSON.parse(event.body) + + const customer = await stripe.customers.create({ email }) + + return { + statusCode: 200, + body: JSON.stringify({ customerId: customer.id }) + } +} +``` + +### CreateOS (after) + +```javascript +// app.js +import express from 'express' +import Stripe from 'stripe' + +const app = express() +app.use(express.json()) + +const stripe = new Stripe(process.env.STRIPE_KEY) + +app.post('/api/subscribe', async (req, res) => { + const { email, plan } = req.body + + const customer = await stripe.customers.create({ email }) + + res.json({ customerId: customer.id }) +}) + +export default app +``` diff --git a/skills/netlify-to-createos/references/netlify-toml-reference.md b/skills/netlify-to-createos/references/netlify-toml-reference.md new file mode 100644 index 0000000..2ec849b --- /dev/null +++ b/skills/netlify-to-createos/references/netlify-toml-reference.md @@ -0,0 +1,169 @@ +# netlify.toml Reference for Migration + +This document describes every field in `netlify.toml` and its CreateOS equivalent +(or migration guidance if no direct equivalent exists). + +## `[build]` Section + +```toml +[build] + command = "npm run build" + publish = "out" + functions = "netlify/functions" + base = "" +``` + +| Field | netlify.toml | CreateOS Equivalent | Status | +|-------|-------------|---------------------|--------| +| `command` | Build command | `CreateProject.settings.installCommand` + `settings.buildCommand` | ✅ | +| `publish` | Output directory | `CreateProject.settings.buildDir` | ✅ | +| `functions` | Functions directory | Not directly mappable — separate project | ⚠️ | +| `base` | Base directory | `CreateProject.settings.directoryPath` | ✅ | + +## `[build.environment]` Section + +```toml +[build.environment] + NODE_VERSION = "20" + NPM_FLAGS = "--legacy-peer-deps" +``` + +**Migration:** All key-value pairs map directly to +`UpdateProjectEnvironmentEnvironmentVariables`. + +## `[[redirects]]` Section + +```toml +[[redirects]] + from = "/api/*" + to = "/.netlify/functions/:splat" + status = 200 + force = false + conditions = {Language = ["en"], Country = ["US"]} +``` + +| Field | CreateOS Equivalent | Status | +|-------|---------------------|--------| +| `from` | App-level route matching | ⚠️ Manual | +| `to` | Target route or external URL | ⚠️ Manual | +| `status` | HTTP status code | ⚠️ Manual | +| `force` | Override existing content | ⚠️ Manual | +| `conditions` | Geo/language routing | ⚠️ Manual | + +**Migration guidance:** +- Simple SPA redirects → Configure in frontend framework (e.g., next.config.js rewrites) +- API proxy redirects → Rewrite as direct API calls or CreateOS API routes +- Geo/language redirects → Implement in app code or use CDN-level rules + +## `[[headers]]` Section + +```toml +[[headers]] + for = "/*" + [headers.values] + X-Frame-Options = "DENY" + X-XSS-Protection = "1; mode=block" +``` + +**Migration guidance:** +- Security headers → Configure in app framework or CreateOS settings +- CORS headers → Configure in app code (middleware) +- Custom headers → Configure in app framework + +## `[functions]` Section + +```toml +[functions] + directory = "netlify/functions" + node_bundler = "esbuild" + external_node_modules = ["sharp"] + included_files = ["templates/**"] +``` + +| Field | CreateOS Equivalent | Status | +|-------|---------------------|--------| +| `directory` | Separate CreateOS project | ⚠️ Manual | +| `node_bundler` | Use CreateOS build AI | ✅ | +| `external_node_modules` | Add to package.json dependencies | ✅ | +| `included_files` | Include in project files | ✅ | + +**Migration guidance:** +- Simple API functions → Rewrite as Express/FastAPI routes, deploy as CreateOS project +- Background functions → Use CreateOS cron jobs or queue workers +- Serverless → Full server (no cold starts on CreateOS) + +## `[edge_functions]` Section + +```toml +[edge_functions] + path = "netlify/edge-functions" +``` + +**Status: ❌ Not supported.** +Guide the user to: +- Rewrite as serverless functions +- Implement logic in the app framework's middleware layer +- Use a CDN (e.g., Cloudflare Workers) if edge execution is critical + +## `[[plugins]]` Section + +```toml +[[plugins]] + package = "@netlify/plugin-nextjs" +``` + +**Status: ⚠️ Manual per-plugin assessment.** + +Common plugins and their CreateOS equivalents: + +| Plugin | CreateOS Guidance | +|--------|-------------------| +| `@netlify/plugin-nextjs` | Not needed — CreateOS supports Next.js natively | +| `netlify-plugin-cypress` | Run Cypress in CI/CD pipeline | +| `netlify-plugin-prisma` | Use Prisma Migrate or prisma db push | +| `netlify-plugin-a11y` | Run axe-core in CI | +| `netlify-plugin-subfont` | Implement font subsetting in build step | +| `netlify-plugin-postbuild` | Implement in package.json postbuild script | + +## `[context]` Sections + +```toml +[context.production] + command = "npm run build:prod" + +[context.deploy-preview] + command = "npm run build:preview" + +[context.branch-deploy] + command = "npm run build:staging" +``` + +**Migration:** +- `[context.production]` → `CreateProjectEnvironment` for production +- `[context.deploy-preview]` → Branch-based environment previews +- `[context.branch-deploy]` → `CreateProjectEnvironment` per branch + +## `[dev]` Section + +```toml +[dev] + command = "npm run dev" + port = 8888 + publish = "out" + framework = "#custom" +``` + +**Migration:** Local dev is unchanged — not related to deployment. +Ignore this section during migration. + +## Other Netlify Features + +| Feature | netlify.toml | Status | +|---------|-------------|--------| +| Forms | `[forms]` | ❌ Not supported | +| Identity | `[identity]` | ❌ Not supported | +| CMS | External Netlify CMS config | ❌ Not supported | +| Functions blocking | `[functions.blocking]` | ⚠️ Manual | +| Scoped functions | `[[functions.scopes]]` | ⚠️ Manual | +| Build hooks | External | ⚠️ Manual | +| Post processing | `[build.processing]` | ⚠️ Manual | diff --git a/skills/netlify-to-createos/scripts/migrate-env.sh b/skills/netlify-to-createos/scripts/migrate-env.sh new file mode 100644 index 0000000..5013b27 --- /dev/null +++ b/skills/netlify-to-createos/scripts/migrate-env.sh @@ -0,0 +1,100 @@ +#!/usr/bin/env bash +# migrate-env.sh +# Guides the user through migrating Netlify environment variables to CreateOS. +# Outputs a JSON payload suitable for UpdateProjectEnvironmentEnvironmentVariables. +# +# Usage: ./migrate-env.sh [--interactive | --file env.txt] +# --interactive Prompt for each variable (default) +# --file FILE Read from a dotenv-style file + +set -euo pipefail + +MODE="${1:---interactive}" +OUTPUT_FILE="${2:-createos-env.json}" + +echo "# Netlify → CreateOS Environment Variable Migration" +echo "# =================================================" +echo "" + +# Netlify vars that should be removed (internal vars, not migrated) +NETLIFY_INTERNAL_VARS="NETLIFY CONTEXT DEPLOY_URL DEPLOY_PRIME_URL DEPLOY_PREVIEW_URL NETLIFY_DEV NETLIFY_LOCAL NETLIFY_URL NETLIFY_LFS" + +declare -A env_map + +if [ "$MODE" = "--interactive" ]; then + echo "Enter each environment variable as KEY=VALUE" + echo "Leave empty and press Enter to finish." + echo "Internal Netlify vars (NETLIFY_*, CONTEXT, etc.) are auto-skipped." + echo "" + + while true; do + read -r -p "> " line + [ -z "$line" ] && break + + key="${line%%=*}" + value="${line#*=}" + + # Skip internal Netlify vars + skip=0 + for internal in $NETLIFY_INTERNAL_VARS; do + if [ "$key" = "$internal" ]; then + echo " ⏭ Skipping internal Netlify var: $key" + skip=1 + break + fi + done + + if [ "$skip" -eq 0 ]; then + env_map["$key"]="$value" + echo " ✅ Added: $key" + fi + done + +elif [ "$MODE" = "--file" ]; then + ENV_FILE="${2:-.env}" + if [ ! -f "$ENV_FILE" ]; then + echo "Error: File not found: $ENV_FILE" >&2 + exit 1 + fi + + while IFS='=' read -r key value; do + # Skip comments, empty lines, and internal vars + [[ "$key" =~ ^#.*$ ]] && continue + [ -z "$key" ] && continue + + skip=0 + for internal in $NETLIFY_INTERNAL_VARS; do + if [ "$key" = "$internal" ]; then + echo " ⏭ Skipping internal Netlify var: $key" + skip=1 + break + fi + done + + if [ "$skip" -eq 0 ]; then + env_map["$key"]="$value" + fi + done < "$ENV_FILE" +fi + +# Build JSON output +echo "" +echo "# Variables to migrate:" +first=true +echo "{" +echo ' "variables": {' +for key in "${!env_map[@]}"; do + if [ "$first" = true ]; then + first=false + else + echo "," + fi + echo -n " \"$key\": \"${env_map[$key]}\"" +done +echo "" +echo ' }' +echo "}" + +echo "" +echo "# Saved to: $OUTPUT_FILE" +echo "# Use with: UpdateProjectEnvironmentEnvironmentVariables(env_id, $(cat "$OUTPUT_FILE" 2>/dev/null || echo ''))" diff --git a/skills/netlify-to-createos/scripts/parse-netlify-config.sh b/skills/netlify-to-createos/scripts/parse-netlify-config.sh new file mode 100644 index 0000000..5f985d3 --- /dev/null +++ b/skills/netlify-to-createos/scripts/parse-netlify-config.sh @@ -0,0 +1,146 @@ +#!/usr/bin/env bash +# parse-netlify-config.sh +# Extracts key fields from netlify.toml and outputs JSON. +# Usage: ./parse-netlify-config.sh [path/to/netlify.toml] +# +# Output: JSON with build settings, env vars, redirects, headers, functions config + +set -euo pipefail + +CONFIG_PATH="${1:-netlify.toml}" + +if [ ! -f "$CONFIG_PATH" ]; then + echo '{"error":"File not found","path":"'"$CONFIG_PATH"'"}' >&2 + exit 1 +fi + +# Use awk to parse the TOML and extract sections +awk ' +BEGIN { + in_build = 0 + in_build_env = 0 + in_redirect = 0 + in_headers = 0 + in_functions = 0 + redirect_count = 0 + header_count = 0 + plugin_count = 0 + + print "{" +} + +# [build] +/^\[build\]/ { + in_build = 1 + in_build_env = 0 + in_redirect = 0 + in_headers = 0 + in_functions = 0 + next +} + +# [build.environment] +/^\[build\.environment\]/ { + in_build = 0 + in_build_env = 1 + in_redirect = 0 + in_headers = 0 + in_functions = 0 + printf " \"build\": {\n" + next +} + +# [[redirects]] +/^\[\[redirects\]\]/ { + if (in_build_env) { + printf " },\n" + } + in_build = 0 + in_build_env = 0 + in_redirect = 1 + in_headers = 0 + in_functions = 0 + redirect_count++ + if (redirect_count == 1) { + printf " \"redirects\": [\n" + } + next +} + +# [[headers]] +/^\[\[headers\]\]/ { + if (in_build_env) { + printf " },\n" + } + in_build = 0 + in_build_env = 0 + in_redirect = 0 + in_headers = 1 + in_functions = 0 + header_count++ + if (header_count == 1) { + printf " \"headers\": [\n" + } + next +} + +# [functions] +/^\[functions\]/ { + if (in_build_env) printf " },\n" + if (in_redirect) printf " }]" + if (in_headers) printf " }]" + in_build = 0 + in_build_env = 0 + in_redirect = 0 + in_headers = 0 + in_functions = 1 + next +} + +# Lines within sections +{ + if (in_build && /^[a-z]/) { + split($0, kv, "=") + gsub(/^[ \t]+|[ \t]+$/, "", kv[1]) + gsub(/^[ \t"]+|[ \t"]+$/, "", kv[2]) + printf " \"build_%s\": \"%s\",\n", kv[1], kv[2] + } + else if (in_build_env && /=/) { + split($0, kv, "=") + gsub(/^[ \t]+|[ \t"]+$/, "", kv[1]) + gsub(/^[ \t"]+|[ \t"]+$/, "", kv[2]) + if (env_count++ == 0) { + printf " \"environment\": {\n" + } + printf " \"%s\": \"%s\",\n", kv[1], kv[2] + } + else if (in_redirect && /=/) { + if (index($0, "[[headers") == 0) { + split($0, kv, "=") + gsub(/^[ \t]+|[ \t]+$/, "", kv[1]) + gsub(/^[ \t"]+|[ \t"]+$/, "", kv[2]) + printf " \"%s\": \"%s\",\n", kv[1], kv[2] + } + } + else if (in_headers && /=/) { + split($0, kv, "=") + gsub(/^[ \t]+|[ \t]+$/, "", kv[1]) + gsub(/^[ \t"]+|[ \t"]+$/, "", kv[2]) + printf " \"%s\": \"%s\",\n", kv[1], kv[2] + } + else if (in_functions && /=/) { + split($0, kv, "=") + gsub(/^[ \t]+|[ \t]+$/, "", kv[1]) + gsub(/^[ \t"]+|[ \t"]+$/, "", kv[2]) + printf " \"function_%s\": \"%s\",\n", kv[1], kv[2] + } +} + +END { + if (in_build_env) printf " },\n },\n" + if (in_redirect) printf " }\n ],\n" + if (in_headers) printf " }\n ],\n" + printf " \"_parsed\": true\n" + print "}" +} +' "$CONFIG_PATH"