From 393a9bb87324d235293124c1e3123ab03c809df3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9COmar?= <“omarg@simplify9.com”> Date: Wed, 22 Oct 2025 18:01:18 +0300 Subject: [PATCH] Refactor CI/CD workflows for Next.js deployment to Cloudflare Workers - Removed setup action for Cloudflare Pages and replaced it with setup for Cloudflare Workers. - Introduced new actions for setting up Next.js for Workers and configuring Wrangler CLI. - Updated the main CI workflow to accommodate changes for deploying to Workers instead of Pages. - Modified input parameters to reflect the new deployment target and requirements. - Removed obsolete verification action for Cloudflare build output. - Updated documentation to reflect changes in deployment strategy and configuration for Workers. - Enhanced README and usage guides to clarify the transition from Pages to Workers. --- .../README.md | 112 ---------- .../action.yml | 138 ------------ .../configure-workers-project/action.yml | 205 ++++++++++++++++++ .github/actions/deploy-to-workers/action.yml | 202 +++++++++++++++++ .../actions/setup-nextjs-cloudflare/README.md | 65 ------ .../setup-nextjs-cloudflare/action.yml | 102 --------- .../actions/setup-nextjs-workers/action.yml | 93 ++++++++ .github/actions/setup-wrangler/action.yml | 81 +++++++ .../actions/verify-cloudflare-build/README.md | 97 --------- .../verify-cloudflare-build/action.yml | 92 -------- .github/workflows/next-ci.yml | 181 ++++++++-------- CHEAT_SHEET.md | 26 ++- NEXT_CI_USAGE.md | 176 +++++++++++---- NEXT_SSR_DEPLOYMENT_GUIDE.md | 22 +- README.md | 2 +- 15 files changed, 828 insertions(+), 766 deletions(-) delete mode 100644 .github/actions/configure-cloudflare-compatibility/README.md delete mode 100644 .github/actions/configure-cloudflare-compatibility/action.yml create mode 100644 .github/actions/configure-workers-project/action.yml create mode 100644 .github/actions/deploy-to-workers/action.yml delete mode 100644 .github/actions/setup-nextjs-cloudflare/README.md delete mode 100644 .github/actions/setup-nextjs-cloudflare/action.yml create mode 100644 .github/actions/setup-nextjs-workers/action.yml create mode 100644 .github/actions/setup-wrangler/action.yml delete mode 100644 .github/actions/verify-cloudflare-build/README.md delete mode 100644 .github/actions/verify-cloudflare-build/action.yml diff --git a/.github/actions/configure-cloudflare-compatibility/README.md b/.github/actions/configure-cloudflare-compatibility/README.md deleted file mode 100644 index f44b4c5..0000000 --- a/.github/actions/configure-cloudflare-compatibility/README.md +++ /dev/null @@ -1,112 +0,0 @@ -# Configure Cloudflare Compatibility Action - -This composite action configures the required compatibility flags for Next.js applications on Cloudflare Pages. - -## What it does - -- ✅ Sets `nodejs_compat` flag for Next.js SSR support -- ✅ Configures compatibility date for Workers runtime -- ✅ Updates both production and preview environments -- ✅ Handles API errors gracefully with manual instructions -- ✅ Supports custom compatibility flags - -## Usage - -```yaml -- name: Configure Next.js compatibility flags - uses: simplify9/.github/.github/actions/configure-cloudflare-compatibility@main - with: - api-token: ${{ secrets.CLOUDFLARE_API_TOKEN }} - account-id: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} - project-name: my-nextjs-app - compatibility-date: '2024-10-20' # Optional - compatibility-flags: 'nodejs_compat' # Optional -``` - -## Inputs - -| Input | Description | Required | Default | -|-------|-------------|----------|---------| -| `api-token` | Cloudflare API token | Yes | - | -| `account-id` | Cloudflare account ID | Yes | - | -| `project-name` | Name of the Cloudflare Pages project | Yes | - | -| `compatibility-date` | Cloudflare Workers compatibility date | No | `2024-10-20` | -| `compatibility-flags` | Comma-separated list of compatibility flags | No | `nodejs_compat` | - -## Outputs - -| Output | Description | -|--------|-------------| -| `configured` | Whether compatibility flags were successfully configured | -| `flags-applied` | List of flags that were applied | - -## Compatibility Flags - -### Default: `nodejs_compat` -Required for Next.js SSR to work on Cloudflare Workers. - -### Custom Flags -You can specify multiple flags: -```yaml -compatibility-flags: 'nodejs_compat,streams_enable_constructors' -``` - -## API Operations - -### What it does -1. **Fetches** current project configuration -2. **Updates** both production and preview environments -3. **Sets** compatibility flags and date -4. **Verifies** configuration was applied - -### API Endpoints Used -- `GET /accounts/{account}/pages/projects/{project}` - Fetch project -- `PATCH /accounts/{account}/pages/projects/{project}` - Update config - -## Error Handling - -### Graceful Degradation -If API calls fail, the action: -- ❌ **Doesn't fail the workflow** -- ⚠️ **Provides manual instructions** -- 📖 **Shows exact steps to fix** - -### Manual Setup Instructions -``` -⚠️ Please manually set compatibility flags in Cloudflare dashboard: - 1. Go to Cloudflare Pages → my-nextjs-app - 2. Settings → Functions → Compatibility flags - 3. Add flags: nodejs_compat - 4. Set compatibility date: 2024-10-20 -``` - -## Example Success Output - -``` -🔧 Setting up compatibility flags for Next.js SSR... -📋 Flags to apply: ["nodejs_compat"] -📅 Compatibility date: 2024-10-20 -🔍 Fetching current project settings... -✅ Project found, updating compatibility flags... -🔄 Updating production environment... -✅ Compatibility flags configured successfully -🎯 Applied flags: nodejs_compat -🎯 Compatibility date: 2024-10-20 -🎯 Environments: production, preview - -🎉 Next.js is now properly configured for Cloudflare Pages! -🚀 Your SSR application should work correctly -``` - -## Why This is Needed - -Without `nodejs_compat` flag, Next.js SSR applications show: -``` -The page you've requested has been built using @cloudflare/next-on-pages, -but hasn't been properly configured. - -You should go to the Pages project's Compatibility Flags settings section -and add the nodejs_compat flag to both your production and preview environments. -``` - -This action automates that configuration! 🎉 \ No newline at end of file diff --git a/.github/actions/configure-cloudflare-compatibility/action.yml b/.github/actions/configure-cloudflare-compatibility/action.yml deleted file mode 100644 index 922526d..0000000 --- a/.github/actions/configure-cloudflare-compatibility/action.yml +++ /dev/null @@ -1,138 +0,0 @@ -name: 'Configure Cloudflare Compatibility Flags' -description: 'Sets up nodejs_compat and other required flags for Next.js on Cloudflare Pages' -author: 'simplify9' - -inputs: - api-token: - description: 'Cloudflare API token' - required: true - account-id: - description: 'Cloudflare account ID' - required: true - project-name: - description: 'Name of the Cloudflare Pages project' - required: true - compatibility-date: - description: 'Cloudflare Workers compatibility date' - required: false - default: '2024-10-20' - compatibility-flags: - description: 'Comma-separated list of compatibility flags' - required: false - default: 'nodejs_compat' - -outputs: - configured: - description: 'Whether compatibility flags were successfully configured' - value: ${{ steps.configure-flags.outputs.configured }} - flags-applied: - description: 'List of flags that were applied' - value: ${{ steps.configure-flags.outputs.flags }} - -runs: - using: 'composite' - steps: - - name: Install jq for JSON parsing - shell: bash - run: | - if ! command -v jq &> /dev/null; then - if [ "$(uname)" == "Linux" ]; then - sudo apt-get update && sudo apt-get install -y jq - elif [ "$(uname)" == "Darwin" ]; then - brew install jq 2>/dev/null || echo "jq installation failed, but may already be available" - fi - fi - - - name: Configure Next.js compatibility flags - id: configure-flags - shell: bash - run: | - echo "🔧 Setting up compatibility flags for Next.js SSR..." - - # Parse comma-separated flags into JSON array - FLAGS_INPUT="${{ inputs.compatibility-flags }}" - FLAGS_JSON=$(echo "$FLAGS_INPUT" | tr ',' '\n' | jq -R . | jq -s .) - - echo "📋 Flags to apply: $FLAGS_JSON" - echo "📅 Compatibility date: ${{ inputs.compatibility-date }}" - - # Get current project settings - echo "🔍 Fetching current project settings..." - PROJECT_RESPONSE=$(curl -s -X GET \ - "https://api.cloudflare.com/client/v4/accounts/${{ inputs.account-id }}/pages/projects/${{ inputs.project-name }}" \ - -H "Authorization: Bearer ${{ inputs.api-token }}" \ - -H "Content-Type: application/json") - - # Check if project exists and API call succeeded - if echo "$PROJECT_RESPONSE" | jq -e '.success' > /dev/null; then - echo "✅ Project found, updating compatibility flags..." - - # Update production environment - echo "🔄 Updating production environment..." - PROD_RESPONSE=$(curl -s -X PATCH \ - "https://api.cloudflare.com/client/v4/accounts/${{ inputs.account-id }}/pages/projects/${{ inputs.project-name }}" \ - -H "Authorization: Bearer ${{ inputs.api-token }}" \ - -H "Content-Type: application/json" \ - -d "{ - \"deployment_configs\": { - \"production\": { - \"compatibility_flags\": $FLAGS_JSON, - \"compatibility_date\": \"${{ inputs.compatibility-date }}\" - }, - \"preview\": { - \"compatibility_flags\": $FLAGS_JSON, - \"compatibility_date\": \"${{ inputs.compatibility-date }}\" - } - } - }") - - # Check if update was successful - if echo "$PROD_RESPONSE" | jq -e '.success' > /dev/null; then - echo "✅ Compatibility flags configured successfully" - echo "🎯 Applied flags: $FLAGS_INPUT" - echo "🎯 Compatibility date: ${{ inputs.compatibility-date }}" - echo "🎯 Environments: production, preview" - - echo "configured=true" >> $GITHUB_OUTPUT - echo "flags=$FLAGS_INPUT" >> $GITHUB_OUTPUT - else - echo "❌ Failed to update compatibility flags" - echo "Response: $PROD_RESPONSE" - echo "configured=false" >> $GITHUB_OUTPUT - - # Don't fail the job, just warn - echo "⚠️ Compatibility flags may need to be set manually in Cloudflare dashboard" - echo "⚠️ Go to Pages project → Settings → Functions → Compatibility flags" - echo "⚠️ Add: $FLAGS_INPUT" - fi - else - echo "❌ Could not find project or API call failed" - echo "Project response: $PROJECT_RESPONSE" - echo "configured=false" >> $GITHUB_OUTPUT - - # Don't fail the job, just provide instructions - echo "⚠️ Please manually set compatibility flags in Cloudflare dashboard:" - echo " 1. Go to Cloudflare Pages → ${{ inputs.project-name }}" - echo " 2. Settings → Functions → Compatibility flags" - echo " 3. Add flags: $FLAGS_INPUT" - echo " 4. Set compatibility date: ${{ inputs.compatibility-date }}" - fi - - - name: Verify configuration - shell: bash - run: | - echo "" - echo "📋 Configuration Summary:" - echo " Project: ${{ inputs.project-name }}" - echo " Flags: ${{ inputs.compatibility-flags }}" - echo " Date: ${{ inputs.compatibility-date }}" - echo " Status: ${{ steps.configure-flags.outputs.configured }}" - echo "" - - if [ "${{ steps.configure-flags.outputs.configured }}" = "true" ]; then - echo "🎉 Next.js is now properly configured for Cloudflare Pages!" - echo "🚀 Your SSR application should work correctly" - else - echo "⚠️ Manual configuration may be required" - echo "📖 See Cloudflare Pages documentation for manual setup" - fi \ No newline at end of file diff --git a/.github/actions/configure-workers-project/action.yml b/.github/actions/configure-workers-project/action.yml new file mode 100644 index 0000000..9be9a27 --- /dev/null +++ b/.github/actions/configure-workers-project/action.yml @@ -0,0 +1,205 @@ +name: 'Configure Cloudflare Workers Project' +description: 'Generates wrangler.toml configuration and worker entry point for Next.js' +author: 'Simplify9' + +inputs: + worker-name: + description: 'Name of the Cloudflare Worker' + required: true + + compatibility-date: + description: 'Cloudflare Workers compatibility date' + required: false + default: '2024-10-20' + + compatibility-flags: + description: 'Cloudflare Workers compatibility flags' + required: false + default: 'nodejs_compat' + + build-command: + description: 'Build command for the project' + required: false + default: 'npm run build' + + output-directory: + description: 'Next.js build output directory' + required: false + default: '.next' + + custom-routes: + description: 'Custom routes configuration (optional)' + required: false + default: '' + + environment: + description: 'Deployment environment' + required: false + default: 'development' + +outputs: + config-file: + description: 'Path to generated wrangler.toml file' + value: ${{ steps.generate.outputs.config-file }} + + entry-point: + description: 'Path to generated worker entry point' + value: ${{ steps.generate.outputs.entry-point }} + +runs: + using: 'composite' + steps: + - name: Generate wrangler.toml configuration + shell: bash + id: generate + run: | + echo "📝 Generating wrangler.toml configuration..." + + # Create wrangler.toml + cat > wrangler.toml << EOF + name = "${{ inputs.worker-name }}" + main = ".next/server/worker.js" + compatibility_date = "${{ inputs.compatibility-date }}" + compatibility_flags = ["${{ inputs.compatibility-flags }}"] + + [build] + command = "${{ inputs.build-command }}" + cwd = "." + watch_dir = "src" + + # Default route pattern - will be updated based on domain configuration + [[routes]] + pattern = "*" + zone_name = "" + + # Environment-specific configurations + [env.production] + name = "${{ inputs.worker-name }}-prod" + + [env.production.vars] + NODE_ENV = "production" + ENVIRONMENT = "production" + + [env.staging] + name = "${{ inputs.worker-name }}-staging" + + [env.staging.vars] + NODE_ENV = "staging" + ENVIRONMENT = "staging" + + [env.development] + name = "${{ inputs.worker-name }}-dev" + + [env.development.vars] + NODE_ENV = "development" + ENVIRONMENT = "development" + EOF + + # Add custom routes if provided + if [ -n "${{ inputs.custom-routes }}" ]; then + echo "" >> wrangler.toml + echo "# Custom routes configuration" >> wrangler.toml + echo "${{ inputs.custom-routes }}" >> wrangler.toml + fi + + echo "config-file=wrangler.toml" >> $GITHUB_OUTPUT + echo "✅ Created wrangler.toml configuration" + + - name: Create worker entry point + shell: bash + run: | + echo "🔨 Creating worker entry point..." + mkdir -p .next/server + + cat > .next/server/worker.js << 'EOF' + /** + * Cloudflare Workers entry point for Next.js application + * This file serves as the bridge between Cloudflare Workers runtime + * and the Next.js application built for edge runtime. + */ + + import { createHandler } from '@cloudflare/next-on-pages/next-edge-handler'; + + // Create the Next.js handler with build output + const handler = createHandler({ + output: '${{ inputs.output-directory }}' + }); + + export default { + /** + * Main fetch handler for the Cloudflare Worker + * @param {Request} request - The incoming request + * @param {Object} env - Environment variables and bindings + * @param {Object} ctx - Execution context + * @returns {Promise} The response + */ + async fetch(request, env, ctx) { + try { + // Pass through to Next.js handler + return await handler(request, env, ctx); + } catch (error) { + console.error('Worker error:', error); + + // Return a friendly error response + return new Response( + JSON.stringify({ + error: 'Internal Server Error', + message: 'An error occurred while processing your request.', + timestamp: new Date().toISOString() + }), + { + status: 500, + headers: { + 'Content-Type': 'application/json', + 'X-Error-Source': 'cloudflare-worker' + } + } + ); + } + } + }; + EOF + + echo "entry-point=.next/server/worker.js" >> $GITHUB_OUTPUT + echo "✅ Created worker entry point" + + - name: Validate configuration + shell: bash + run: | + echo "🔍 Validating worker configuration..." + + # Check if wrangler.toml is valid + if wrangler config validate; then + echo "✅ wrangler.toml configuration is valid" + else + echo "❌ wrangler.toml configuration is invalid" + exit 1 + fi + + # Check if entry point exists + if [ -f ".next/server/worker.js" ]; then + echo "✅ Worker entry point created successfully" + else + echo "❌ Failed to create worker entry point" + exit 1 + fi + + - name: Display configuration summary + shell: bash + run: | + echo "📋 Worker Configuration Summary:" + echo "- Worker Name: ${{ inputs.worker-name }}" + echo "- Environment: ${{ inputs.environment }}" + echo "- Compatibility Date: ${{ inputs.compatibility-date }}" + echo "- Compatibility Flags: ${{ inputs.compatibility-flags }}" + echo "- Entry Point: .next/server/worker.js" + echo "" + echo "Generated wrangler.toml:" + cat wrangler.toml + + echo "## ⚙️ Workers Project Configuration" >> $GITHUB_STEP_SUMMARY + echo "- **Worker Name**: ${{ inputs.worker-name }}" >> $GITHUB_STEP_SUMMARY + echo "- **Environment**: ${{ inputs.environment }}" >> $GITHUB_STEP_SUMMARY + echo "- **Compatibility Date**: ${{ inputs.compatibility-date }}" >> $GITHUB_STEP_SUMMARY + echo "- **Entry Point**: .next/server/worker.js" >> $GITHUB_STEP_SUMMARY + echo "- **Configuration**: wrangler.toml ✅" >> $GITHUB_STEP_SUMMARY \ No newline at end of file diff --git a/.github/actions/deploy-to-workers/action.yml b/.github/actions/deploy-to-workers/action.yml new file mode 100644 index 0000000..bf7980a --- /dev/null +++ b/.github/actions/deploy-to-workers/action.yml @@ -0,0 +1,202 @@ +name: 'Deploy to Cloudflare Workers' +description: 'Deploys Next.js application to Cloudflare Workers using Wrangler' +author: 'Simplify9' + +inputs: + worker-name: + description: 'Name of the Cloudflare Worker' + required: true + + environment: + description: 'Deployment environment (development, staging, production)' + required: false + default: 'development' + + api-token: + description: 'Cloudflare API token' + required: true + + account-id: + description: 'Cloudflare account ID' + required: true + + environment-variables: + description: 'Environment variables to set (JSON format)' + required: false + default: '{}' + + dry-run: + description: 'Perform a dry run without actually deploying' + required: false + default: 'false' + +outputs: + deployment-url: + description: 'URL of the deployed worker' + value: ${{ steps.deploy.outputs.url }} + + worker-name: + description: 'Full worker name used for deployment' + value: ${{ steps.deploy.outputs.worker-name }} + + deployment-id: + description: 'Deployment ID from Cloudflare' + value: ${{ steps.deploy.outputs.deployment-id }} + +runs: + using: 'composite' + steps: + - name: Validate deployment inputs + shell: bash + run: | + echo "🔍 Validating deployment inputs..." + + # Check if wrangler.toml exists + if [ ! -f "wrangler.toml" ]; then + echo "❌ wrangler.toml not found. Please configure the worker project first." + exit 1 + fi + + # Check if worker entry point exists + if [ ! -f ".next/server/worker.js" ]; then + echo "❌ Worker entry point not found. Please configure the worker project first." + exit 1 + fi + + # Validate environment + if [[ ! "${{ inputs.environment }}" =~ ^(development|staging|production)$ ]]; then + echo "❌ Invalid environment: ${{ inputs.environment }}. Must be development, staging, or production." + exit 1 + fi + + echo "✅ Deployment inputs validated successfully" + + - name: Set environment variables + shell: bash + if: inputs.environment-variables != '{}' + run: | + echo "🔧 Setting environment variables..." + + # Parse and set environment variables + echo '${{ inputs.environment-variables }}' | jq -r 'to_entries[] | "\(.key)=\(.value)"' | while read -r var; do + if [ -n "$var" ]; then + key=$(echo "$var" | cut -d'=' -f1) + value=$(echo "$var" | cut -d'=' -f2-) + + echo "Setting $key..." + echo "$value" | wrangler secret put "$key" --env ${{ inputs.environment }} + fi + done + + echo "✅ Environment variables configured" + env: + CLOUDFLARE_API_TOKEN: ${{ inputs.api-token }} + CLOUDFLARE_ACCOUNT_ID: ${{ inputs.account-id }} + + - name: Deploy to Cloudflare Workers + shell: bash + id: deploy + run: | + echo "🚀 Deploying to Cloudflare Workers..." + + # Prepare deployment command + DEPLOY_CMD="wrangler deploy --env ${{ inputs.environment }}" + + if [ "${{ inputs.dry-run }}" = "true" ]; then + DEPLOY_CMD="$DEPLOY_CMD --dry-run" + echo "🧪 Performing dry run deployment..." + fi + + # Execute deployment + DEPLOYMENT_OUTPUT=$(eval "$DEPLOY_CMD" 2>&1) + DEPLOY_EXIT_CODE=$? + + echo "$DEPLOYMENT_OUTPUT" + + if [ $DEPLOY_EXIT_CODE -ne 0 ]; then + echo "❌ Deployment failed with exit code $DEPLOY_EXIT_CODE" + exit $DEPLOY_EXIT_CODE + fi + + # Extract deployment information + DEPLOYMENT_URL=$(echo "$DEPLOYMENT_OUTPUT" | grep -oE 'https://[a-zA-Z0-9.-]+\.workers\.dev' | head -1) + DEPLOYMENT_ID=$(echo "$DEPLOYMENT_OUTPUT" | grep -oE 'deployment-id: [a-zA-Z0-9-]+' | cut -d' ' -f2 | head -1) + + # Fallback URL construction if not found in output + if [ -z "$DEPLOYMENT_URL" ]; then + case "${{ inputs.environment }}" in + production) + DEPLOYMENT_URL="https://${{ inputs.worker-name }}.workers.dev" + ;; + *) + DEPLOYMENT_URL="https://${{ inputs.worker-name }}-${{ inputs.environment }}.workers.dev" + ;; + esac + fi + + # Set outputs + echo "url=$DEPLOYMENT_URL" >> $GITHUB_OUTPUT + echo "worker-name=${{ inputs.worker-name }}" >> $GITHUB_OUTPUT + echo "deployment-id=${DEPLOYMENT_ID:-unknown}" >> $GITHUB_OUTPUT + + if [ "${{ inputs.dry-run }}" = "true" ]; then + echo "✅ Dry run completed successfully" + else + echo "✅ Deployed successfully to: $DEPLOYMENT_URL" + fi + env: + CLOUDFLARE_API_TOKEN: ${{ inputs.api-token }} + CLOUDFLARE_ACCOUNT_ID: ${{ inputs.account-id }} + + - name: Verify deployment + shell: bash + if: inputs.dry-run != 'true' + run: | + echo "🧪 Verifying deployment..." + + DEPLOYMENT_URL="${{ steps.deploy.outputs.url }}" + + if [ -n "$DEPLOYMENT_URL" ]; then + # Wait for deployment to be ready + echo "Waiting for deployment to be ready..." + sleep 15 + + # Test the worker endpoint + HTTP_STATUS=$(curl -s -o /dev/null -w "%{http_code}" "$DEPLOYMENT_URL" || echo "000") + + if [ "$HTTP_STATUS" -ge 200 ] && [ "$HTTP_STATUS" -lt 400 ]; then + echo "✅ Worker is responding successfully (HTTP $HTTP_STATUS)" + elif [ "$HTTP_STATUS" -ge 400 ] && [ "$HTTP_STATUS" -lt 500 ]; then + echo "⚠️ Worker is accessible but returned client error (HTTP $HTTP_STATUS)" + echo "This might be expected for some Next.js routes" + else + echo "⚠️ Worker verification failed (HTTP $HTTP_STATUS)" + echo "The worker may still be deploying or might have configuration issues" + fi + else + echo "⚠️ No deployment URL available for verification" + fi + + - name: Display deployment summary + shell: bash + run: | + echo "## 🚀 Deployment Summary" >> $GITHUB_STEP_SUMMARY + echo "- **Worker**: ${{ inputs.worker-name }}" >> $GITHUB_STEP_SUMMARY + echo "- **Environment**: ${{ inputs.environment }}" >> $GITHUB_STEP_SUMMARY + + if [ "${{ inputs.dry-run }}" = "true" ]; then + echo "- **Mode**: Dry Run ✅" >> $GITHUB_STEP_SUMMARY + else + echo "- **Deployment URL**: ${{ steps.deploy.outputs.url }}" >> $GITHUB_STEP_SUMMARY + echo "- **Deployment ID**: ${{ steps.deploy.outputs.deployment-id }}" >> $GITHUB_STEP_SUMMARY + fi + + echo "" >> $GITHUB_STEP_SUMMARY + echo "### 🛠️ Deployment Details" >> $GITHUB_STEP_SUMMARY + echo "- **Platform**: Cloudflare Workers" >> $GITHUB_STEP_SUMMARY + echo "- **Runtime**: Edge Runtime" >> $GITHUB_STEP_SUMMARY + echo "- **Global Distribution**: ✅ Deployed to all edge locations" >> $GITHUB_STEP_SUMMARY + + if [ "${{ inputs.environment-variables }}" != '{}' ]; then + echo "- **Environment Variables**: ✅ Configured" >> $GITHUB_STEP_SUMMARY + fi \ No newline at end of file diff --git a/.github/actions/setup-nextjs-cloudflare/README.md b/.github/actions/setup-nextjs-cloudflare/README.md deleted file mode 100644 index cb84500..0000000 --- a/.github/actions/setup-nextjs-cloudflare/README.md +++ /dev/null @@ -1,65 +0,0 @@ -# Setup Next.js for Cloudflare Action - -This composite action validates and prepares a Next.js application for deployment to Cloudflare Pages. - -## What it does - -- ✅ Verifies `@cloudflare/next-on-pages` is installed -- ✅ Installs `wrangler` if missing -- ✅ Validates Next.js configuration for Cloudflare compatibility -- ✅ Checks for required package.json scripts -- ✅ Provides helpful error messages and recommendations - -## Usage - -```yaml -- name: Setup Next.js for Cloudflare - uses: simplify9/.github/.github/actions/setup-nextjs-cloudflare@main - with: - package-manager: npm # Optional: npm, yarn, or pnpm - validate-config: true # Optional: validate Next.js config -``` - -## Inputs - -| Input | Description | Required | Default | -|-------|-------------|----------|---------| -| `package-manager` | Package manager to use (npm, yarn, pnpm) | No | `npm` | -| `validate-config` | Whether to validate Next.js configuration | No | `true` | - -## Outputs - -| Output | Description | -|--------|-------------| -| `dependencies-installed` | Whether required dependencies were found/installed | -| `config-valid` | Whether Next.js configuration is valid for Cloudflare | - -## What it checks - -### Dependencies -- `@cloudflare/next-on-pages` - Required for SSR conversion -- `wrangler` - Auto-installs if missing - -### Configuration -- Detects `output: 'export'` in Next.js config (incompatible with SSR) -- Validates config files (next.config.js, next.config.mjs, next.config.ts) -- Checks for `pages:build` script in package.json - -### Error Handling -- Provides clear error messages with solutions -- Exits with helpful installation commands -- Suggests configuration fixes - -## Example Error Messages - -``` -❌ @cloudflare/next-on-pages is not installed! -Please install it with: npm install --save-dev @cloudflare/next-on-pages - -❌ Found 'output: export' in Next.js config. This is for static sites only! -For SSR on Cloudflare, remove 'output: export' or set it to 'standalone' - -⚠️ 'pages:build' script not found in package.json -💡 Recommended: Add this script to package.json: - "pages:build": "next-on-pages" -``` \ No newline at end of file diff --git a/.github/actions/setup-nextjs-cloudflare/action.yml b/.github/actions/setup-nextjs-cloudflare/action.yml deleted file mode 100644 index db7c325..0000000 --- a/.github/actions/setup-nextjs-cloudflare/action.yml +++ /dev/null @@ -1,102 +0,0 @@ -name: 'Setup Next.js for Cloudflare' -description: 'Validates and prepares Next.js application for Cloudflare Pages deployment' -author: 'simplify9' - -inputs: - package-manager: - description: 'Package manager to use (npm, yarn, pnpm)' - required: false - default: 'npm' - validate-config: - description: 'Whether to validate Next.js configuration' - required: false - default: 'true' - -outputs: - dependencies-installed: - description: 'Whether required dependencies were installed' - value: ${{ steps.check-deps.outputs.installed }} - config-valid: - description: 'Whether Next.js configuration is valid' - value: ${{ steps.validate-config.outputs.valid }} - -runs: - using: 'composite' - steps: - - name: Check for required Cloudflare dependencies - id: check-deps - shell: bash - run: | - echo "🔍 Checking for @cloudflare/next-on-pages..." - - # Check if @cloudflare/next-on-pages is installed - if ! ${{ inputs.package-manager }} list @cloudflare/next-on-pages >/dev/null 2>&1; then - echo "❌ @cloudflare/next-on-pages is not installed!" - echo "Please install it with: ${{ inputs.package-manager }} install --save-dev @cloudflare/next-on-pages" - echo "See: https://github.com/cloudflare/next-on-pages" - echo "installed=false" >> $GITHUB_OUTPUT - exit 1 - fi - - # Check for wrangler - if ! ${{ inputs.package-manager }} list wrangler >/dev/null 2>&1; then - echo "⚠️ wrangler is not installed, installing..." - case "${{ inputs.package-manager }}" in - npm) - npm install --save-dev wrangler - ;; - yarn) - yarn add --dev wrangler - ;; - pnpm) - pnpm add --save-dev wrangler - ;; - esac - fi - - echo "✅ Cloudflare Next.js dependencies verified" - echo "installed=true" >> $GITHUB_OUTPUT - - - name: Validate Next.js configuration - id: validate-config - if: inputs.validate-config == 'true' - shell: bash - run: | - echo "🔧 Validating Next.js configuration for Cloudflare compatibility..." - - if [ -f "next.config.js" ] || [ -f "next.config.mjs" ] || [ -f "next.config.ts" ]; then - echo "✅ Next.js config file found" - - # Check for output: 'export' which is incompatible with SSR - if grep -r "output.*['\"]export['\"]" next.config.* 2>/dev/null; then - echo "❌ Found 'output: export' in Next.js config. This is for static sites only!" - echo "For SSR on Cloudflare, remove 'output: export' or set it to 'standalone'" - echo "valid=false" >> $GITHUB_OUTPUT - exit 1 - fi - - echo "✅ Next.js configuration appears compatible with SSR" - echo "valid=true" >> $GITHUB_OUTPUT - else - echo "ℹ️ No Next.js config file found (using defaults)" - echo "valid=true" >> $GITHUB_OUTPUT - fi - - - name: Verify package.json scripts - shell: bash - run: | - echo "📋 Checking package.json scripts..." - - if [ -f "package.json" ]; then - # Check if pages:build script exists - if ! grep -q '"pages:build"' package.json; then - echo "⚠️ 'pages:build' script not found in package.json" - echo "💡 Recommended: Add this script to package.json:" - echo ' "pages:build": "next-on-pages"' - else - echo "✅ Found 'pages:build' script in package.json" - fi - else - echo "❌ package.json not found!" - exit 1 - fi \ No newline at end of file diff --git a/.github/actions/setup-nextjs-workers/action.yml b/.github/actions/setup-nextjs-workers/action.yml new file mode 100644 index 0000000..c942e11 --- /dev/null +++ b/.github/actions/setup-nextjs-workers/action.yml @@ -0,0 +1,93 @@ +name: 'Setup Next.js for Cloudflare Workers' +description: 'Configures Next.js project for Cloudflare Workers deployment' +author: 'Simplify9' + +inputs: + package-manager: + description: 'Package manager to use (npm, yarn, pnpm)' + required: false + default: 'npm' + + workers-sdk-version: + description: 'Version of @cloudflare/workers-sdk to install' + required: false + default: 'latest' + +outputs: + setup-status: + description: 'Status of the setup process' + value: ${{ steps.setup.outputs.status }} + +runs: + using: 'composite' + steps: + - name: Install Cloudflare Workers SDK + shell: bash + run: | + echo "📦 Installing Cloudflare Workers SDK..." + + case "${{ inputs.package-manager }}" in + npm) + npm install --save-dev @cloudflare/workers-sdk@${{ inputs.workers-sdk-version }} + ;; + yarn) + yarn add --dev @cloudflare/workers-sdk@${{ inputs.workers-sdk-version }} + ;; + pnpm) + pnpm add --save-dev @cloudflare/workers-sdk@${{ inputs.workers-sdk-version }} + ;; + *) + echo "❌ Unsupported package manager: ${{ inputs.package-manager }}" + exit 1 + ;; + esac + + echo "✅ Cloudflare Workers SDK installed successfully" + + - name: Validate Next.js configuration for Workers + shell: bash + id: setup + run: | + echo "🔧 Validating Next.js configuration for Cloudflare Workers..." + + # Check if next.config.js/mjs exists + if [ -f "next.config.js" ] || [ -f "next.config.mjs" ] || [ -f "next.config.ts" ]; then + echo "✅ Next.js config file found" + else + echo "⚠️ No Next.js config file found, creating basic configuration..." + cat > next.config.js << 'EOF' + /** @type {import('next').NextConfig} */ + const nextConfig = { + experimental: { + runtime: 'edge', + }, + // Optimize for Cloudflare Workers + swcMinify: true, + output: 'standalone', + } + + module.exports = nextConfig + EOF + echo "✅ Created basic next.config.js for Workers" + fi + + # Check package.json for required scripts + if ! grep -q '"build"' package.json; then + echo "⚠️ No build script found in package.json" + fi + + echo "status=success" >> $GITHUB_OUTPUT + echo "✅ Next.js setup for Workers completed successfully" + + - name: Display setup summary + shell: bash + run: | + echo "## 🔧 Next.js Workers Setup Summary" >> $GITHUB_STEP_SUMMARY + echo "- **Package Manager**: ${{ inputs.package-manager }}" >> $GITHUB_STEP_SUMMARY + echo "- **Workers SDK**: @cloudflare/workers-sdk@${{ inputs.workers-sdk-version }}" >> $GITHUB_STEP_SUMMARY + echo "- **Configuration**: Validated and optimized for Workers" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "### 📋 Next Steps" >> $GITHUB_STEP_SUMMARY + echo "- Build your Next.js application" >> $GITHUB_STEP_SUMMARY + echo "- Configure Wrangler for deployment" >> $GITHUB_STEP_SUMMARY + echo "- Deploy to Cloudflare Workers" >> $GITHUB_STEP_SUMMARY \ No newline at end of file diff --git a/.github/actions/setup-wrangler/action.yml b/.github/actions/setup-wrangler/action.yml new file mode 100644 index 0000000..30ca33b --- /dev/null +++ b/.github/actions/setup-wrangler/action.yml @@ -0,0 +1,81 @@ +name: 'Setup Wrangler CLI' +description: 'Installs and configures Wrangler CLI for Cloudflare Workers deployment' +author: 'Simplify9' + +inputs: + wrangler-version: + description: 'Version of Wrangler to install' + required: false + default: 'latest' + + api-token: + description: 'Cloudflare API token' + required: true + + account-id: + description: 'Cloudflare account ID' + required: true + +outputs: + wrangler-version: + description: 'Installed Wrangler version' + value: ${{ steps.install.outputs.version }} + +runs: + using: 'composite' + steps: + - name: Install Wrangler CLI + shell: bash + id: install + run: | + echo "📦 Installing Wrangler CLI..." + + if [ "${{ inputs.wrangler-version }}" = "latest" ]; then + npm install -g wrangler + else + npm install -g wrangler@${{ inputs.wrangler-version }} + fi + + # Get installed version + INSTALLED_VERSION=$(wrangler --version | head -1) + echo "version=$INSTALLED_VERSION" >> $GITHUB_OUTPUT + echo "✅ Wrangler CLI installed: $INSTALLED_VERSION" + + - name: Configure Wrangler authentication + shell: bash + run: | + echo "🔐 Configuring Wrangler authentication..." + + # Verify API token is valid + if ! wrangler whoami > /dev/null 2>&1; then + echo "❌ Failed to authenticate with Cloudflare API" + echo "Please verify your CLOUDFLARE_API_TOKEN is valid" + exit 1 + fi + + # Verify account access + if ! wrangler account list > /dev/null 2>&1; then + echo "❌ Failed to access Cloudflare account" + echo "Please verify your CLOUDFLARE_ACCOUNT_ID is correct" + exit 1 + fi + + echo "✅ Wrangler authentication successful" + env: + CLOUDFLARE_API_TOKEN: ${{ inputs.api-token }} + CLOUDFLARE_ACCOUNT_ID: ${{ inputs.account-id }} + + - name: Display Wrangler info + shell: bash + run: | + echo "📊 Wrangler Configuration Info:" + echo "User: $(wrangler whoami 2>/dev/null || echo 'Authentication failed')" + echo "Account ID: ${{ inputs.account-id }}" + + echo "## 🛠️ Wrangler Setup Summary" >> $GITHUB_STEP_SUMMARY + echo "- **Version**: ${{ steps.install.outputs.version }}" >> $GITHUB_STEP_SUMMARY + echo "- **Authentication**: ✅ Configured" >> $GITHUB_STEP_SUMMARY + echo "- **Account**: ${{ inputs.account-id }}" >> $GITHUB_STEP_SUMMARY + env: + CLOUDFLARE_API_TOKEN: ${{ inputs.api-token }} + CLOUDFLARE_ACCOUNT_ID: ${{ inputs.account-id }} \ No newline at end of file diff --git a/.github/actions/verify-cloudflare-build/README.md b/.github/actions/verify-cloudflare-build/README.md deleted file mode 100644 index e58b9c2..0000000 --- a/.github/actions/verify-cloudflare-build/README.md +++ /dev/null @@ -1,97 +0,0 @@ -# Verify Cloudflare Build Action - -This composite action validates the build output from `@cloudflare/next-on-pages` to ensure proper deployment structure. - -## What it does - -- ✅ Validates `.vercel/output/` directory structure -- ✅ Checks for static assets in correct location -- ✅ Detects Functions for SSR support -- ✅ Counts files and provides build summary -- ✅ Shows sample files for debugging - -## Usage - -```yaml -- name: Verify Cloudflare build output - uses: simplify9/.github/.github/actions/verify-cloudflare-build@main - with: - build-directory: .vercel/output/static # Optional -``` - -## Inputs - -| Input | Description | Required | Default | -|-------|-------------|----------|---------| -| `build-directory` | Directory where build output should be located | No | `.vercel/output/static` | - -## Outputs - -| Output | Description | -|--------|-------------| -| `build-valid` | Whether the build output is valid | -| `has-functions` | Whether Functions were generated (SSR detected) | -| `static-files-count` | Number of static files generated | - -## Expected Structure - -After `@cloudflare/next-on-pages` runs, it should create: - -``` -.vercel/output/ -├── static/ # Static assets (HTML, CSS, JS, images) -│ ├── _next/ # Next.js built assets -│ ├── favicon.ico # Static files -│ └── ... -├── functions/ # Server-side functions (if SSR/API routes) -│ └── index.js # Main server function -└── config.json # Deployment configuration -``` - -## What it validates - -### Required Structure -- ✅ `.vercel/output/` directory exists -- ✅ `.vercel/output/static/` contains static assets -- ✅ Counts total static files - -### Optional Features -- 🔍 Detects `.vercel/output/functions/` (indicates SSR) -- 🔍 Shows sample function files -- 🔍 Displays build configuration if present - -### Debug Information -- 📄 Lists first 10 static files -- ⚡ Shows function files (up to 5) -- ⚙️ Displays config.json content (first 10 lines) - -## Example Output - -``` -✅ Cloudflare build structure found: -📁 Static assets directory found: - Found 42 static files -📄 Sample static files: - .vercel/output/static/_next/static/chunks/main.js - .vercel/output/static/_next/static/css/app.css - .vercel/output/static/favicon.ico - -⚡ Functions directory found: - Found 3 function files -🔧 Function files: - .vercel/output/functions/index.js - .vercel/output/functions/api/hello.js - -✅ Build output validation completed successfully -``` - -## Error Handling - -``` -❌ .vercel/output directory not found! -This suggests @cloudflare/next-on-pages didn't run properly. - -Expected structure after @cloudflare/next-on-pages build: - .vercel/output/static/ <- Static assets - .vercel/output/functions/ <- Server functions (if any) -``` \ No newline at end of file diff --git a/.github/actions/verify-cloudflare-build/action.yml b/.github/actions/verify-cloudflare-build/action.yml deleted file mode 100644 index dc2f4e2..0000000 --- a/.github/actions/verify-cloudflare-build/action.yml +++ /dev/null @@ -1,92 +0,0 @@ -name: 'Verify Cloudflare Build Output' -description: 'Validates the build output from @cloudflare/next-on-pages' -author: 'simplify9' - -inputs: - build-directory: - description: 'Directory where build output should be located' - required: false - default: '.vercel/output/static' - -outputs: - build-valid: - description: 'Whether the build output is valid' - value: ${{ steps.verify-output.outputs.valid }} - has-functions: - description: 'Whether Functions were generated' - value: ${{ steps.verify-output.outputs.has-functions }} - static-files-count: - description: 'Number of static files generated' - value: ${{ steps.verify-output.outputs.static-files }} - -runs: - using: 'composite' - steps: - - name: Verify Cloudflare build output - id: verify-output - shell: bash - run: | - echo "🔍 Checking Cloudflare Pages build output..." - - # Check for .vercel/output structure (created by @cloudflare/next-on-pages) - if [ ! -d ".vercel/output" ]; then - echo "❌ .vercel/output directory not found!" - echo "This suggests @cloudflare/next-on-pages didn't run properly." - echo "Current directory contents:" - ls -la - echo "" - echo "Expected structure after @cloudflare/next-on-pages build:" - echo " .vercel/output/static/ <- Static assets" - echo " .vercel/output/functions/ <- Server functions (if any)" - echo "valid=false" >> $GITHUB_OUTPUT - exit 1 - fi - - echo "✅ Cloudflare build structure found:" - ls -la .vercel/output/ - - # Check static directory - if [ -d ".vercel/output/static" ]; then - echo "📁 Static assets directory found:" - STATIC_COUNT=$(find .vercel/output/static -type f | wc -l) - echo " Found $STATIC_COUNT static files" - echo "static-files=$STATIC_COUNT" >> $GITHUB_OUTPUT - - # Show first few files for debugging - echo "📄 Sample static files:" - find .vercel/output/static -type f | head -10 | while read file; do - echo " $file" - done - else - echo "❌ Static assets directory not found!" - echo "valid=false" >> $GITHUB_OUTPUT - exit 1 - fi - - # Check for Functions - if [ -d ".vercel/output/functions" ]; then - echo "⚡ Functions directory found:" - FUNCTION_COUNT=$(find .vercel/output/functions -name "*.js" | wc -l) - echo " Found $FUNCTION_COUNT function files" - echo "has-functions=true" >> $GITHUB_OUTPUT - - # Show function files - if [ $FUNCTION_COUNT -gt 0 ]; then - echo "🔧 Function files:" - find .vercel/output/functions -name "*.js" | head -5 | while read file; do - echo " $file" - done - fi - else - echo "ℹ️ No Functions directory found (static-only build)" - echo "has-functions=false" >> $GITHUB_OUTPUT - fi - - # Check for config.json - if [ -f ".vercel/output/config.json" ]; then - echo "⚙️ Build configuration found:" - echo "$(cat .vercel/output/config.json | head -10)" - fi - - echo "valid=true" >> $GITHUB_OUTPUT - echo "✅ Build output validation completed successfully" \ No newline at end of file diff --git a/.github/workflows/next-ci.yml b/.github/workflows/next-ci.yml index 14fe784..1e49da2 100644 --- a/.github/workflows/next-ci.yml +++ b/.github/workflows/next-ci.yml @@ -1,11 +1,11 @@ -name: Deploy Next.js App to Cloudflare Pages +name: Deploy Next.js App to Cloudflare Workers on: workflow_call: inputs: - # Project Configuration - project-name: - description: 'Base Cloudflare project name (without suffix)' + # Worker Configuration + worker-name: + description: 'Cloudflare Worker name (without suffix)' required: true type: string @@ -41,30 +41,51 @@ on: description: 'Build command to run' required: false type: string - default: 'npm run pages:build' + default: 'npm run build' - build-directory: - description: 'Directory where build output is located' + output-directory: + description: 'Directory where Next.js build output is located' required: false type: string - default: '.vercel/output/static' + default: '.next' - # Next.js Configuration - next-config-validation: - description: 'Whether to validate Next.js configuration for Cloudflare compatibility' + # Worker Configuration + worker-name-suffix: + description: 'Suffix to add to worker name (e.g., -dev, -staging)' required: false - type: boolean - default: true + type: string + default: '' - # Cloudflare Configuration - project-name-suffix: - description: 'Suffix to add to project name (e.g., -dev, -staging)' + # Cloudflare Workers Configuration + compatibility-date: + description: 'Cloudflare Workers compatibility date' + required: false + type: string + default: '2024-10-20' + + compatibility-flags: + description: 'Cloudflare Workers compatibility flags' + required: false + type: string + default: 'nodejs_compat' + + # Custom Routes + custom-routes: + description: 'Custom routes configuration for the worker' required: false type: string default: '' + # Environment Variables + environment-variables: + description: 'Environment variables to set for the worker (JSON format)' + required: false + type: string + default: '{}' + + # Custom Domain Configuration custom-domain: - description: 'Custom domain to configure (optional)' + description: 'Custom domain to configure for the worker (optional)' required: false type: string default: '' @@ -75,12 +96,6 @@ on: type: boolean default: false - compatibility-date: - description: 'Cloudflare Workers compatibility date' - required: false - type: string - default: '2024-10-20' - # Additional Options run-tests: description: 'Whether to run tests before deployment' @@ -116,20 +131,20 @@ on: outputs: deployment-url: - description: 'URL of the deployed application' + description: 'URL of the deployed worker' value: ${{ jobs.deploy.outputs.url }} - project-name: - description: 'Full project name used for deployment' - value: ${{ jobs.deploy.outputs.project-name }} + worker-name: + description: 'Full worker name used for deployment' + value: ${{ jobs.deploy.outputs.worker-name }} jobs: deploy: runs-on: ubuntu-latest - name: Deploy Next.js to Cloudflare Pages (${{ inputs.environment }}) + name: Deploy Next.js to Cloudflare Workers (${{ inputs.environment }}) outputs: url: ${{ steps.deploy.outputs.url }} - project-name: ${{ env.FULL_PROJECT_NAME }} + worker-name: ${{ env.FULL_WORKER_NAME }} steps: - name: Checkout code @@ -141,11 +156,11 @@ jobs: node-version: ${{ inputs.node-version }} cache: ${{ inputs.package-manager }} - - name: Set project name + - name: Set worker name run: | - FULL_NAME="${{ inputs.project-name }}${{ inputs.project-name-suffix }}" - echo "FULL_PROJECT_NAME=$FULL_NAME" >> $GITHUB_ENV - echo "🏷️ Using project name: $FULL_NAME" + FULL_NAME="${{ inputs.worker-name }}${{ inputs.worker-name-suffix }}" + echo "FULL_WORKER_NAME=$FULL_NAME" >> $GITHUB_ENV + echo "🏷️ Using worker name: $FULL_NAME" - name: Install dependencies run: | @@ -165,11 +180,16 @@ jobs: ;; esac - - name: Setup Next.js for Cloudflare - uses: simplify9/.github/.github/actions/setup-nextjs-cloudflare@main + - name: Setup Next.js for Cloudflare Workers + uses: simplify9/.github/.github/actions/setup-nextjs-workers@main with: package-manager: ${{ inputs.package-manager }} - validate-config: ${{ inputs.next-config-validation }} + + - name: Setup Wrangler CLI + uses: simplify9/.github/.github/actions/setup-wrangler@main + with: + api-token: ${{ secrets.CLOUDFLARE_API_TOKEN }} + account-id: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} - name: Run linting if: inputs.run-lint == true @@ -179,50 +199,32 @@ jobs: if: inputs.run-tests == true run: ${{ inputs.test-command }} - - name: Build Next.js application with Cloudflare adapter + - name: Build Next.js application for Workers run: ${{ inputs.build-command }} env: NODE_ENV: ${{ inputs.environment }} ENVIRONMENT: ${{ inputs.environment }} - CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} - - name: Verify Cloudflare build output - uses: simplify9/.github/.github/actions/verify-cloudflare-build@main + - name: Configure Workers project + uses: simplify9/.github/.github/actions/configure-workers-project@main with: - build-directory: ${{ inputs.build-directory }} - - - name: Setup Cloudflare Pages project with Functions - uses: simplify9/.github/.github/actions/setup-cloudflare-project@main - with: - api-token: ${{ secrets.CLOUDFLARE_API_TOKEN }} - account-id: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} - project-name: ${{ env.FULL_PROJECT_NAME }} - production-branch: ${{ inputs.target-branch }} + worker-name: ${{ env.FULL_WORKER_NAME }} + compatibility-date: ${{ inputs.compatibility-date }} + compatibility-flags: ${{ inputs.compatibility-flags }} build-command: ${{ inputs.build-command }} - destination-dir: ${{ inputs.build-directory }} + output-directory: ${{ inputs.output-directory }} + custom-routes: ${{ inputs.custom-routes }} + environment: ${{ inputs.environment }} - - name: Deploy to Cloudflare Pages with Functions + - name: Deploy to Cloudflare Workers id: deploy - uses: cloudflare/pages-action@v1 - with: - apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }} - accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} - projectName: ${{ env.FULL_PROJECT_NAME }} - directory: ${{ inputs.build-directory }} - # Enable Functions for SSR support - gitHubToken: ${{ secrets.GITHUB_TOKEN }} - env: - NODE_ENV: ${{ inputs.environment }} - ENVIRONMENT: ${{ inputs.environment }} - - - name: Configure Next.js compatibility flags - uses: simplify9/.github/.github/actions/configure-cloudflare-compatibility@main + uses: simplify9/.github/.github/actions/deploy-to-workers@main with: + worker-name: ${{ env.FULL_WORKER_NAME }} + environment: ${{ inputs.environment }} api-token: ${{ secrets.CLOUDFLARE_API_TOKEN }} account-id: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} - project-name: ${{ env.FULL_PROJECT_NAME }} - compatibility-date: ${{ inputs.compatibility-date }} - compatibility-flags: nodejs_compat + environment-variables: ${{ inputs.environment-variables }} - name: Setup custom domain if: inputs.custom-domain != '' @@ -230,34 +232,20 @@ jobs: with: api-token: ${{ secrets.CLOUDFLARE_API_TOKEN }} account-id: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} - project-name: ${{ env.FULL_PROJECT_NAME }} + project-name: ${{ env.FULL_WORKER_NAME }} domain-name: ${{ inputs.custom-domain }} fail-on-error: ${{ inputs.fail-on-domain-error }} - - name: Test deployment - if: steps.deploy.outputs.url != '' - run: | - echo "🧪 Testing deployed application..." - - # Wait a moment for deployment to be ready - sleep 10 - - # Test the homepage - if curl -f -s -o /dev/null "${{ steps.deploy.outputs.url }}"; then - echo "✅ Homepage is accessible" - else - echo "⚠️ Homepage test failed (might be normal for some apps)" - fi - - name: Deployment summary run: | - echo "## 🚀 Next.js Deployment Summary" >> $GITHUB_STEP_SUMMARY + echo "## 🚀 Next.js Workers Deployment Summary" >> $GITHUB_STEP_SUMMARY echo "- **Environment**: ${{ inputs.environment }}" >> $GITHUB_STEP_SUMMARY - echo "- **Project**: ${{ env.FULL_PROJECT_NAME }}" >> $GITHUB_STEP_SUMMARY + echo "- **Worker**: ${{ env.FULL_WORKER_NAME }}" >> $GITHUB_STEP_SUMMARY echo "- **Branch**: ${{ inputs.target-branch }}" >> $GITHUB_STEP_SUMMARY echo "- **Node.js**: ${{ inputs.node-version }}" >> $GITHUB_STEP_SUMMARY echo "- **Package Manager**: ${{ inputs.package-manager }}" >> $GITHUB_STEP_SUMMARY echo "- **Compatibility Date**: ${{ inputs.compatibility-date }}" >> $GITHUB_STEP_SUMMARY + echo "- **Compatibility Flags**: ${{ inputs.compatibility-flags }}" >> $GITHUB_STEP_SUMMARY if [ -n "${{ steps.deploy.outputs.url }}" ]; then echo "- **Deployment URL**: ${{ steps.deploy.outputs.url }}" >> $GITHUB_STEP_SUMMARY @@ -269,18 +257,21 @@ jobs: echo "" >> $GITHUB_STEP_SUMMARY echo "### 🛠️ Technical Details" >> $GITHUB_STEP_SUMMARY - echo "- Uses **@cloudflare/next-on-pages** for SSR support" >> $GITHUB_STEP_SUMMARY - echo "- Deployed to **Cloudflare Pages with Functions**" >> $GITHUB_STEP_SUMMARY - echo "- Supports dynamic routes, API routes, and middleware" >> $GITHUB_STEP_SUMMARY + echo "- Uses **Cloudflare Workers** for edge-side rendering" >> $GITHUB_STEP_SUMMARY + echo "- Deployed using **Wrangler CLI**" >> $GITHUB_STEP_SUMMARY + echo "- Supports dynamic routes, API routes, and middleware at the edge" >> $GITHUB_STEP_SUMMARY echo "- Compatible with Next.js App Router and Pages Router" >> $GITHUB_STEP_SUMMARY + echo "- Edge runtime provides ultra-low latency globally" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY - echo "✅ Next.js deployment completed successfully!" >> $GITHUB_STEP_SUMMARY + echo "✅ Next.js Workers deployment completed successfully!" >> $GITHUB_STEP_SUMMARY - name: Performance and optimization tips run: | echo "" >> $GITHUB_STEP_SUMMARY - echo "### 💡 Performance Tips" >> $GITHUB_STEP_SUMMARY - echo "- **Edge Runtime**: Use \`export const runtime = 'edge'\` in API routes for better performance" >> $GITHUB_STEP_SUMMARY - echo "- **Static Generation**: Use ISR (\`revalidate\`) for pages that can be cached" >> $GITHUB_STEP_SUMMARY - echo "- **Image Optimization**: Next.js Image component works with Cloudflare's image optimization" >> $GITHUB_STEP_SUMMARY - echo "- **Caching**: Configure appropriate cache headers for static assets" >> $GITHUB_STEP_SUMMARY \ No newline at end of file + echo "### 💡 Workers Performance Tips" >> $GITHUB_STEP_SUMMARY + echo "- **Edge Runtime**: All code runs at the edge by default for minimal latency" >> $GITHUB_STEP_SUMMARY + echo "- **Cold Start Optimization**: Keep bundle size small for faster cold starts" >> $GITHUB_STEP_SUMMARY + echo "- **Streaming**: Workers support streaming responses for better performance" >> $GITHUB_STEP_SUMMARY + echo "- **Caching**: Use Cloudflare Cache API for custom caching strategies" >> $GITHUB_STEP_SUMMARY + echo "- **KV Storage**: Use Cloudflare KV for persistent edge storage" >> $GITHUB_STEP_SUMMARY + echo "- **Durable Objects**: Use for stateful applications requiring consistency" >> $GITHUB_STEP_SUMMARY \ No newline at end of file diff --git a/CHEAT_SHEET.md b/CHEAT_SHEET.md index ef05079..ffdf98f 100644 --- a/CHEAT_SHEET.md +++ b/CHEAT_SHEET.md @@ -10,7 +10,7 @@ Ask DevOps to set these **Organization secrets**: | App Type | Template | Use Case | |----------|----------|----------| | **Static Sites** (Vite/CRA) | `vite-ci.yml` | React, Vue, Svelte, vanilla JS | -| **Next.js Apps** | `next-ci.yml` | Next.js with SSR, API routes, static sites | +| **Next.js Apps** | `next-ci.yml` | Next.js with SSR, API routes deployed to Cloudflare Workers | --- @@ -122,7 +122,7 @@ npm install --save-dev @cloudflare/next-on-pages wrangler **Workflow:** `.github/workflows/deploy.yml` ```yaml -name: Deploy Next.js SSR to Cloudflare +name: Deploy Next.js SSR to Cloudflare Workers on: push: branches: [development, main] @@ -132,28 +132,30 @@ jobs: if: github.ref == 'refs/heads/development' uses: simplify9/.github/.github/workflows/next-ci.yml@main with: - project-name: YOUR_NEXTJS_APP # 👈 CHANGE THIS + worker-name: YOUR_NEXTJS_APP # 👈 CHANGE THIS environment: development - project-name-suffix: -dev - build-command: npm run pages:build # 👈 Uses @cloudflare/next-on-pages + worker-name-suffix: -dev + build-command: npm run build # 👈 Standard Next.js build deploy-prod: if: github.ref == 'refs/heads/main' uses: simplify9/.github/.github/workflows/next-ci.yml@main with: - project-name: YOUR_NEXTJS_APP + worker-name: YOUR_NEXTJS_APP environment: production - build-command: npm run pages:build + build-command: npm run build custom-domain: yourapp.com ``` -**Next.js Config:** Remove static export (if present): +**Next.js Config for Workers:** Enable edge runtime: ```javascript -// next.config.js - Remove this line for SSR: -// output: 'export', // ❌ Remove this! - +// next.config.js - Optimized for Cloudflare Workers module.exports = { - // Your other config... + experimental: { + runtime: 'edge', // ✅ Enable edge runtime + }, + output: 'standalone', + swcMinify: true, } ``` diff --git a/NEXT_CI_USAGE.md b/NEXT_CI_USAGE.md index 8402b14..4edb9dc 100644 --- a/NEXT_CI_USAGE.md +++ b/NEXT_CI_USAGE.md @@ -1,6 +1,15 @@ # Next.js CI/CD Template Usage Guide -This guide shows how to use the `next-ci.yml` reusable workflow template for your Next.js projects deployed to Cloudflare Pages. +This guide shows how to use the `next-ci.yml` reusable workflow template for your Next.js projects deployed to **Cloudflare Workers**. + +## ⚡ What is Cloudflare Workers? + +Cloudflare Workers is a serverless platform that runs your code at the edge, closer to your users. Unlike Cloudflare Pages, Workers provides: + +- **True SSR at the edge** - Server-side rendering happens globally +- **Better performance** - Lower latency and faster cold starts +- **Full Node.js compatibility** - Support for all Next.js features +- **Dynamic routing** - API routes, middleware, and dynamic pages work perfectly ## 🚀 Quick Start @@ -10,7 +19,7 @@ jobs: deploy: uses: simplify9/.github/.github/workflows/next-ci.yml@main with: - project-name: 'my-nextjs-app' + worker-name: 'my-nextjs-app' secrets: inherit ``` @@ -20,34 +29,53 @@ jobs: deploy: uses: simplify9/.github/.github/workflows/next-ci.yml@main with: - project-name: 'my-nextjs-app' + worker-name: 'my-nextjs-app' environment: 'production' - target-branch: 'main' + worker-name-suffix: '-prod' custom-domain: 'app.example.com' fail-on-domain-error: true + compatibility-flags: 'nodejs_compat' secrets: inherit ``` -## Key Differences from Vite CI - -1. **Build Directory**: Default is `out` (Next.js static export default) -2. **Static Export**: Added support for `next export` command -3. **Linting**: Added optional lint step (common in Next.js projects) -4. **Build Process**: Separated build and export steps for better control +### Development with Environment Variables +```yaml +jobs: + deploy: + uses: simplify9/.github/.github/workflows/next-ci.yml@main + with: + worker-name: 'my-nextjs-app' + environment: 'development' + worker-name-suffix: '-dev' + environment-variables: '{"API_URL": "https://api-dev.example.com", "DEBUG": "true"}' + secrets: inherit +``` -## Next.js Configuration +## 🔧 Next.js Configuration for Workers -For Cloudflare Pages deployment, your Next.js application should be configured for static export: +Your Next.js application should be configured for edge runtime compatibility: ### next.config.js ```javascript /** @type {import('next').NextConfig} */ const nextConfig = { - output: 'export', - trailingSlash: true, + // Enable edge runtime for better Workers compatibility + experimental: { + runtime: 'edge', + }, + + // Optimize for Cloudflare Workers + swcMinify: true, + output: 'standalone', + + // Optional: Configure for better edge performance + poweredByHeader: false, + reactStrictMode: true, + + // Image optimization works with Cloudflare images: { - unoptimized: true - } + domains: ['your-domain.com'], + }, } module.exports = nextConfig @@ -59,27 +87,36 @@ module.exports = nextConfig "scripts": { "dev": "next dev", "build": "next build", - "export": "next export", "start": "next start", "lint": "next lint" + }, + "dependencies": { + "next": "^13.0.0", + "react": "^18.0.0", + "react-dom": "^18.0.0" + }, + "devDependencies": { + "@cloudflare/workers-sdk": "latest" } } ``` -## Inputs +## 📋 Inputs | Input | Description | Default | Required | |-------|-------------|---------|----------| -| `project-name` | Base Cloudflare project name | - | ✅ | +| `worker-name` | Name of the Cloudflare Worker | - | ✅ | | `environment` | Environment to deploy to | `development` | ❌ | | `target-branch` | Target branch for deployment | `development` | ❌ | | `node-version` | Node.js version | `18` | ❌ | | `package-manager` | Package manager (npm/yarn/pnpm) | `npm` | ❌ | | `build-command` | Build command | `npm run build` | ❌ | -| `build-directory` | Build output directory | `out` | ❌ | -| `static-export` | Use Next.js static export | `true` | ❌ | -| `export-command` | Export command | `npx next export` | ❌ | -| `project-name-suffix` | Suffix for project name | `` | ❌ | +| `output-directory` | Next.js build output directory | `.next` | ❌ | +| `worker-name-suffix` | Suffix for worker name | `` | ❌ | +| `compatibility-date` | Workers compatibility date | `2024-10-20` | ❌ | +| `compatibility-flags` | Workers compatibility flags | `nodejs_compat` | ❌ | +| `custom-routes` | Custom routes configuration | `` | ❌ | +| `environment-variables` | Environment variables (JSON) | `{}` | ❌ | | `custom-domain` | Custom domain to configure | `` | ❌ | | `fail-on-domain-error` | Fail on domain setup error | `false` | ❌ | | `run-tests` | Run tests before deployment | `true` | ❌ | @@ -87,14 +124,14 @@ module.exports = nextConfig | `run-lint` | Run linting before deployment | `true` | ❌ | | `lint-command` | Lint command | `npm run lint` | ❌ | -## Outputs +## 📤 Outputs | Output | Description | |--------|-------------| -| `deployment-url` | URL of the deployed application | -| `project-name` | Full project name used for deployment | +| `deployment-url` | URL of the deployed worker | +| `worker-name` | Full worker name used for deployment | -## Secrets +## 🔐 Secrets | Secret | Description | Required | |--------|-------------|----------| @@ -103,43 +140,100 @@ module.exports = nextConfig *Uses organization secrets if not provided -## Examples +## 📚 Examples ### Basic Deployment ```yaml +name: Deploy to Workers + +on: + push: + branches: [main] + jobs: deploy: uses: simplify9/.github/.github/workflows/next-ci.yml@main with: - project-name: 'my-app' + worker-name: 'my-nextjs-app' secrets: inherit ``` -### Production with Custom Domain +### Multi-Environment Setup ```yaml +name: Deploy Next.js App + +on: + push: + branches: [main, develop] + jobs: - deploy: + deploy-dev: + if: github.ref == 'refs/heads/develop' uses: simplify9/.github/.github/workflows/next-ci.yml@main with: - project-name: 'my-app' + worker-name: 'my-app' + environment: 'development' + worker-name-suffix: '-dev' + environment-variables: '{"NODE_ENV": "development", "API_URL": "https://api-dev.example.com"}' + secrets: inherit + + deploy-prod: + if: github.ref == 'refs/heads/main' + uses: simplify9/.github/.github/workflows/next-ci.yml@main + with: + worker-name: 'my-app' environment: 'production' - target-branch: 'main' + worker-name-suffix: '-prod' custom-domain: 'app.example.com' - fail-on-domain-error: true + environment-variables: '{"NODE_ENV": "production", "API_URL": "https://api.example.com"}' secrets: inherit ``` -### Development with Different Build Setup +### Advanced Configuration ```yaml jobs: deploy: uses: simplify9/.github/.github/workflows/next-ci.yml@main with: - project-name: 'my-app' - environment: 'development' - project-name-suffix: '-dev' - build-directory: 'dist' - static-export: false + worker-name: 'my-app' + environment: 'production' package-manager: 'pnpm' + compatibility-date: '2024-10-20' + compatibility-flags: 'nodejs_compat' + custom-domain: 'app.example.com' + environment-variables: | + { + "DATABASE_URL": "${{ secrets.DATABASE_URL }}", + "API_KEY": "${{ secrets.API_KEY }}", + "NODE_ENV": "production" + } secrets: inherit -``` \ No newline at end of file +``` + +## 🌟 Key Features + +### ✅ **Full SSR Support** +- Server-side rendering at the edge +- Dynamic routes and API routes work perfectly +- Middleware support + +### ✅ **Global Performance** +- Deployed to 200+ edge locations +- Ultra-low latency worldwide +- Fast cold starts + +### ✅ **Environment Management** +- Support for multiple environments +- Secure environment variable handling +- Custom domain configuration + +### ✅ **Developer Experience** +- Comprehensive logging and summaries +- Error handling and validation +- Test and lint integration + +## 🔗 Related Documentation + +- [Cloudflare Workers Deployment Guide](./CLOUDFLARE_DEPLOYMENT_GUIDE.md) +- [Next.js SSR Deployment Guide](./NEXT_SSR_DEPLOYMENT_GUIDE.md) +- [API CI/CD Usage](./API_CICD_USAGE.md) \ No newline at end of file diff --git a/NEXT_SSR_DEPLOYMENT_GUIDE.md b/NEXT_SSR_DEPLOYMENT_GUIDE.md index b9634f2..caa4ded 100644 --- a/NEXT_SSR_DEPLOYMENT_GUIDE.md +++ b/NEXT_SSR_DEPLOYMENT_GUIDE.md @@ -18,14 +18,14 @@ ## 🚨 **Key Differences from Static Sites** -| Feature | Static Sites (Vite/React) | Next.js Apps (SSR/Static) | +| Feature | Static Sites (Vite/React) | Next.js Apps (SSR/Edge) | |---------|------------------------|--------------------------| | **Template** | `vite-ci.yml` | `next-ci.yml` | -| **Build Output** | `next export` → `out/` | `@cloudflare/next-on-pages` → `.vercel/output/` | -| **Deployment** | Cloudflare Pages (static) | Cloudflare Pages + Functions | +| **Build Output** | `next export` → `out/` | Next.js build → `.next/` | +| **Deployment** | Cloudflare Pages (static) | Cloudflare Workers (edge) | | **Dynamic Routes** | ❌ No | ✅ Yes | | **API Routes** | ❌ No | ✅ Yes | -| **SSR/ISR** | ❌ No | ✅ Yes | +| **SSR/ISR** | ❌ No | ✅ Yes (at the edge) | | **Middleware** | ❌ No | ✅ Yes | --- @@ -34,10 +34,10 @@ ### 1. Install Required Dependencies -In your Next.js project, install the Cloudflare adapter: +In your Next.js project, install the Cloudflare Workers SDK: ```bash -npm install --save-dev @cloudflare/next-on-pages wrangler +npm install --save-dev @cloudflare/workers-sdk ``` ### 2. Update Package.json Scripts @@ -108,21 +108,21 @@ jobs: if: github.ref == 'refs/heads/development' uses: simplify9/.github/.github/workflows/next-ci.yml@main with: - project-name: my-nextjs-app + worker-name: my-nextjs-app environment: development - project-name-suffix: -dev + worker-name-suffix: -dev custom-domain: dev.myapp.com - build-command: npm run pages:build + build-command: npm run build # Production deployment deploy-prod: if: github.ref == 'refs/heads/main' uses: simplify9/.github/.github/workflows/next-ci.yml@main with: - project-name: my-nextjs-app + worker-name: my-nextjs-app environment: production custom-domain: myapp.com - build-command: npm run pages:build + build-command: npm run build fail-on-domain-error: true ``` diff --git a/README.md b/README.md index b70b99d..cb631db 100644 --- a/README.md +++ b/README.md @@ -48,7 +48,7 @@ Deploy React, Vue, Svelte, and other Vite-based static applications to Cloudflar **Best for:** Static React, Vue, Svelte, vanilla JS apps **Documentation:** [QUICK_START_README.md](./QUICK_START_README.md) | [CHEAT_SHEET.md](./CHEAT_SHEET.md) -### 2. **next-ci.yml** - Next.js to Cloudflare Pages + Functions ⭐ +### 2. **next-ci.yml** - Next.js to Cloudflare Workers ⭐ Deploy Next.js applications to Cloudflare Pages with Functions support for SSR, API routes, and dynamic features.