name: Auto Deploy on: pull_request: types: [opened, synchronize, reopened] push: branches: [main, master] env: RAILWAY_TOKEN: ${{ secrets.RAILWAY_TOKEN }} CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} jobs: detect-and-deploy: runs-on: ubuntu-latest permissions: contents: read pull-requests: write deployments: write steps: - name: Checkout uses: actions/checkout@v4 - name: Setup Node.js uses: actions/setup-node@v4 with: node-version: '20' - name: Detect Project Type id: detect run: | # Detect what kind of project this is if [ -f "railway.toml" ] || [ -f "railway.json" ]; then echo "platform=railway" >> $GITHUB_OUTPUT elif [ -f "wrangler.toml" ] || [ -f "wrangler.json" ]; then echo "platform=cloudflare-workers" >> $GITHUB_OUTPUT elif [ -f "next.config.js" ] || [ -f "next.config.mjs" ] || [ -f "next.config.ts" ]; then echo "platform=cloudflare-pages" >> $GITHUB_OUTPUT echo "framework=next" >> $GITHUB_OUTPUT elif [ -f "vite.config.ts" ] || [ -f "vite.config.js" ]; then echo "platform=cloudflare-pages" >> $GITHUB_OUTPUT echo "framework=vite" >> $GITHUB_OUTPUT elif [ -f "Dockerfile" ]; then echo "platform=railway" >> $GITHUB_OUTPUT elif [ -f "requirements.txt" ] || [ -f "pyproject.toml" ]; then echo "platform=railway" >> $GITHUB_OUTPUT echo "framework=python" >> $GITHUB_OUTPUT elif [ -f "package.json" ]; then echo "platform=cloudflare-pages" >> $GITHUB_OUTPUT echo "framework=static" >> $GITHUB_OUTPUT else echo "platform=railway" >> $GITHUB_OUTPUT fi # Get repo name for service naming echo "repo_name=${GITHUB_REPOSITORY#*/}" >> $GITHUB_OUTPUT # Set environment based on event if [ "${{ github.event_name }}" == "pull_request" ]; then echo "environment=preview" >> $GITHUB_OUTPUT echo "pr_number=${{ github.event.pull_request.number }}" >> $GITHUB_OUTPUT else echo "environment=production" >> $GITHUB_OUTPUT fi - name: Install Dependencies if: steps.detect.outputs.platform == 'cloudflare-pages' || steps.detect.outputs.platform == 'cloudflare-workers' run: | if [ -f "package.json" ]; then npm ci || npm install fi - name: Build (if needed) if: steps.detect.outputs.platform == 'cloudflare-pages' run: | if [ -f "package.json" ]; then npm run build || true fi # ============ RAILWAY DEPLOYMENT ============ - name: Install Railway CLI if: steps.detect.outputs.platform == 'railway' && env.RAILWAY_TOKEN != '' run: npm install -g @railway/cli - name: Deploy to Railway if: steps.detect.outputs.platform == 'railway' && env.RAILWAY_TOKEN != '' id: railway run: | railway link --environment ${{ steps.detect.outputs.environment }} 2>/dev/null || true DEPLOY_URL=$(railway up --detach 2>&1 | grep -oP 'https://[^\s]+' | head -1 || echo "") if [ -n "$DEPLOY_URL" ]; then echo "url=$DEPLOY_URL" >> $GITHUB_OUTPUT echo "success=true" >> $GITHUB_OUTPUT else DEPLOY_URL=$(railway status --json 2>/dev/null | jq -r '.deployments[0].url // empty' || echo "") echo "url=$DEPLOY_URL" >> $GITHUB_OUTPUT echo "success=true" >> $GITHUB_OUTPUT fi # ============ CLOUDFLARE PAGES DEPLOYMENT ============ - name: Deploy to Cloudflare Pages if: steps.detect.outputs.platform == 'cloudflare-pages' && env.CLOUDFLARE_API_TOKEN != '' id: cloudflare-pages run: | npm install -g wrangler if [ -d "dist" ]; then OUTPUT_DIR="dist" elif [ -d "build" ]; then OUTPUT_DIR="build" elif [ -d "out" ]; then OUTPUT_DIR="out" elif [ -d ".next" ]; then OUTPUT_DIR=".next" elif [ -d "public" ]; then OUTPUT_DIR="public" else OUTPUT_DIR="."; fi PROJECT_NAME="${{ steps.detect.outputs.repo_name }}" wrangler pages project create "$PROJECT_NAME" --production-branch main 2>/dev/null || true if [ "${{ steps.detect.outputs.environment }}" == "preview" ]; then RESULT=$(wrangler pages deploy "$OUTPUT_DIR" --project-name="$PROJECT_NAME" --branch="pr-${{ steps.detect.outputs.pr_number }}" 2>&1) else RESULT=$(wrangler pages deploy "$OUTPUT_DIR" --project-name="$PROJECT_NAME" --branch=main 2>&1) fi DEPLOY_URL=$(echo "$RESULT" | grep -oP 'https://[^\s]+\.pages\.dev' | head -1 || echo "") echo "url=$DEPLOY_URL" >> $GITHUB_OUTPUT echo "success=true" >> $GITHUB_OUTPUT # ============ CLOUDFLARE WORKERS DEPLOYMENT ============ - name: Deploy to Cloudflare Workers if: steps.detect.outputs.platform == 'cloudflare-workers' && env.CLOUDFLARE_API_TOKEN != '' id: cloudflare-workers run: | npm install -g wrangler if [ "${{ steps.detect.outputs.environment }}" == "preview" ]; then RESULT=$(wrangler deploy --env preview 2>&1 || wrangler deploy 2>&1) else RESULT=$(wrangler deploy 2>&1) fi DEPLOY_URL=$(echo "$RESULT" | grep -oP 'https://[^\s]+\.workers\.dev' | head -1 || echo "") echo "url=$DEPLOY_URL" >> $GITHUB_OUTPUT echo "success=true" >> $GITHUB_OUTPUT # ============ COMMENT ON PR ============ - name: Comment Deployment URL on PR if: github.event_name == 'pull_request' && (steps.railway.outputs.success == 'true' || steps.cloudflare-pages.outputs.success == 'true' || steps.cloudflare-workers.outputs.success == 'true') uses: actions/github-script@v7 with: script: | const railwayUrl = '${{ steps.railway.outputs.url }}'; const pagesUrl = '${{ steps.cloudflare-pages.outputs.url }}'; const workersUrl = '${{ steps.cloudflare-workers.outputs.url }}'; const platform = '${{ steps.detect.outputs.platform }}'; const deployUrl = railwayUrl || pagesUrl || workersUrl || 'Deployment in progress...'; const platformEmoji = { 'railway': '🚂', 'cloudflare-pages': '📄', 'cloudflare-workers': '⚡' }; const body = `## ${platformEmoji[platform] || '🚀'} Preview Deployment Ready! | Platform | URL | |----------|-----| | **${platform}** | ${deployUrl ? `[${deployUrl}](${deployUrl})` : 'Deploying...'} | --- 🤖 *Auto-deployed by BlackRoad OS*`; const { data: comments } = await github.rest.issues.listComments({ owner: context.repo.owner, repo: context.repo.repo, issue_number: context.issue.number, }); const botComment = comments.find(c => c.body.includes('Preview Deployment Ready')); if (botComment) { await github.rest.issues.updateComment({ owner: context.repo.owner, repo: context.repo.repo, comment_id: botComment.id, body: body }); } else { await github.rest.issues.createComment({ owner: context.repo.owner, repo: context.repo.repo, issue_number: context.issue.number, body: body }); }