UNPKG

miridev-cli

Version:

Official CLI tool for deploying static sites to miri.dev - Deploy your websites in seconds

383 lines (345 loc) 11.5 kB
#!/usr/bin/env node const fs = require('fs'); const path = require('path'); const chalk = require('chalk'); // GitHub Actions template variables const GITHUB_SECRET = '$' + '{{'; const GITHUB_SECRET_END = '}}'; const GITHUB_SECRETS = { SUPABASE_URL: `${GITHUB_SECRET} secrets.NEXT_PUBLIC_SUPABASE_URL ${GITHUB_SECRET_END}`, SUPABASE_ANON_KEY: `${GITHUB_SECRET} secrets.NEXT_PUBLIC_SUPABASE_ANON_KEY ${GITHUB_SECRET_END}`, SUPABASE_SERVICE_ROLE_KEY: `${GITHUB_SECRET} secrets.SUPABASE_SERVICE_ROLE_KEY ${GITHUB_SECRET_END}`, PAGE_URL: `${GITHUB_SECRET} steps.deployment.outputs.page_url ${GITHUB_SECRET_END}` }; const templates = { vercel: { filename: 'vercel.json', content: { version: 2, name: 'miri-dev-project', builds: [ { src: 'package.json', use: '@vercel/next' } ], env: { NEXT_PUBLIC_SUPABASE_URL: '@supabase_url', NEXT_PUBLIC_SUPABASE_ANON_KEY: '@supabase_anon_key', SUPABASE_SERVICE_ROLE_KEY: '@supabase_service_role_key' }, regions: ['icn1', 'hnd1'], functions: { 'src/app/api/**/*.ts': { maxDuration: 30 } }, headers: [ { source: '/(.*)', headers: [ { key: 'X-Frame-Options', value: 'DENY' }, { key: 'X-Content-Type-Options', value: 'nosniff' }, { key: 'Referrer-Policy', value: 'strict-origin-when-cross-origin' } ] } ], crons: [ { path: '/api/cron/security-cleanup', schedule: '0 2 * * *' } ] } }, netlify: { filename: 'netlify.toml', content: `[build] command = "npm run build" functions = "netlify/functions" publish = ".next" [build.environment] NODE_VERSION = "18" NPM_VERSION = "9" [dev] command = "npm run dev" port = 3000 targetPort = 3000 autoLaunch = false [[plugins]] package = "@netlify/plugin-nextjs" [[headers]] for = "/*" [headers.values] X-Frame-Options = "DENY" X-Content-Type-Options = "nosniff" Referrer-Policy = "strict-origin-when-cross-origin" X-XSS-Protection = "1; mode=block" [[headers]] for = "/api/*" [headers.values] Access-Control-Allow-Origin = "*" Access-Control-Allow-Methods = "GET, POST, PUT, DELETE, OPTIONS" Access-Control-Allow-Headers = "Content-Type, Authorization" [[redirects]] from = "/docs" to = "/docs/getting-started" status = 301 [[headers]] for = "/_next/static/*" [headers.values] Cache-Control = "public, max-age=31536000, immutable" # 환경 변수는 Netlify 대시보드에서 설정하세요: # NEXT_PUBLIC_SUPABASE_URL # NEXT_PUBLIC_SUPABASE_ANON_KEY # SUPABASE_SERVICE_ROLE_KEY` }, cloudflare: { filename: 'wrangler.toml', content: `name = "miri-dev-project" main = "src/index.js" compatibility_date = "2024-01-01" [env.production] name = "miri-dev-project" route = "miri.dev/*" [env.staging] name = "miri-dev-project-staging" route = "staging.miri.dev/*" [env.production.vars] ENVIRONMENT = "production" [env.staging.vars] ENVIRONMENT = "staging" [pages] build_output_dir = "out" build_command = "npm run build && npm run export" # 환경 변수는 다음 명령어로 설정하세요: # wrangler secret put NEXT_PUBLIC_SUPABASE_URL # wrangler secret put NEXT_PUBLIC_SUPABASE_ANON_KEY # wrangler secret put SUPABASE_SERVICE_ROLE_KEY` }, github: { filename: '.github/workflows/deploy.yml', content: 'name: Deploy to GitHub Pages\n\n' + 'on:\n' + ' push:\n' + ' branches: [ main ]\n' + ' pull_request:\n' + ' branches: [ main ]\n' + ' workflow_dispatch:\n\n' + 'permissions:\n' + ' contents: read\n' + ' pages: write\n' + ' id-token: write\n\n' + 'concurrency:\n' + ' group: "pages"\n' + ' cancel-in-progress: false\n\n' + 'jobs:\n' + ' build:\n' + ' runs-on: ubuntu-latest\n' + ' \n' + ' steps:\n' + ' - name: Checkout\n' + ' uses: actions/checkout@v4\n' + ' \n' + ' - name: Setup Node.js\n' + ' uses: actions/setup-node@v4\n' + ' with:\n' + ' node-version: \'18\'\n' + ' cache: \'npm\'\n' + ' \n' + ' - name: Install dependencies\n' + ' run: npm ci\n' + ' \n' + ' - name: Build application\n' + ' run: npm run build\n' + ' env:\n' + ` NEXT_PUBLIC_SUPABASE_URL: ${GITHUB_SECRETS.SUPABASE_URL}\n` + ` NEXT_PUBLIC_SUPABASE_ANON_KEY: ${GITHUB_SECRETS.SUPABASE_ANON_KEY}\n` + ` SUPABASE_SERVICE_ROLE_KEY: ${GITHUB_SECRETS.SUPABASE_SERVICE_ROLE_KEY}\n` + ' \n' + ' - name: Setup Pages\n' + ' uses: actions/configure-pages@v4\n' + ' with:\n' + ' static_site_generator: next\n' + ' \n' + ' - name: Upload artifact\n' + ' uses: actions/upload-pages-artifact@v3\n' + ' with:\n' + ' path: ./out\n\n' + ' deploy:\n' + ' environment:\n' + ' name: github-pages\n' + ` url: ${GITHUB_SECRETS.PAGE_URL}\n` + ' runs-on: ubuntu-latest\n' + ' needs: build\n' + ' if: github.ref == \'refs/heads/main\'\n' + ' \n' + ' steps:\n' + ' - name: Deploy to GitHub Pages\n' + ' id: deployment\n' + ' uses: actions/deploy-pages@v4' }, aws: { filename: 'aws/buildspec.yml', content: 'version: 0.2\n\n' + 'env:\n' + ' variables:\n' + ' NODE_ENV: production\n' + ' parameter-store:\n' + ' NEXT_PUBLIC_SUPABASE_URL: /miri-dev/supabase/url\n' + ' NEXT_PUBLIC_SUPABASE_ANON_KEY: /miri-dev/supabase/anon-key\n' + ' SUPABASE_SERVICE_ROLE_KEY: /miri-dev/supabase/service-role-key\n\n' + 'phases:\n' + ' install:\n' + ' runtime-versions:\n' + ' nodejs: 18\n' + ' commands:\n' + ' - echo Installing dependencies...\n' + ' - npm ci --only=production\n' + ' \n' + ' pre_build:\n' + ' commands:\n' + ' - echo Pre-build phase started on `date`\n' + ' - echo Setting up environment variables...\n' + ' \n' + ' build:\n' + ' commands:\n' + ' - echo Build phase started on `date`\n' + ' - npm run build\n' + ' - echo Build completed on `date`\n' + ' \n' + ' post_build:\n' + ' commands:\n' + ' - echo Post-build phase started on `date`\n' + ' - echo Preparing distribution files...\n' + ' - aws s3 sync out/ s3://$BUCKET_NAME --delete --cache-control max-age=31536000\n' + ' - aws s3 cp out/index.html s3://$BUCKET_NAME/index.html --cache-control max-age=0\n' + ' - aws cloudfront create-invalidation --distribution-id $CLOUDFRONT_DISTRIBUTION_ID --paths "/*"\n' + ' \n' + 'artifacts:\n' + ' files:\n' + ' - \'**/*\'\n' + ' base-directory: out\n' + ' name: miri-dev-build-$(date +%Y-%m-%d-%H-%M-%S)\n\n' + 'cache:\n' + ' paths:\n' + ' - node_modules/**/*\n' + ' - .next/cache/**/*' } }; function createDirectoryIfNotExists(dirPath) { if (!fs.existsSync(dirPath)) { fs.mkdirSync(dirPath, { recursive: true }); } } function exportConfig(platform) { const template = templates[platform]; if (!template) { console.error(chalk.red(`❌ 지원하지 않는 플랫폼: ${platform}`)); console.log(chalk.yellow('지원 플랫폼: vercel, netlify, cloudflare, github, aws')); return; } const filePath = template.filename; const dirPath = path.dirname(filePath); if (dirPath !== '.' && dirPath !== '') { createDirectoryIfNotExists(dirPath); } let content; if (typeof template.content === 'string') { content = template.content; } else { content = JSON.stringify(template.content, null, 2); } try { fs.writeFileSync(filePath, content); console.log(chalk.green(`✅ ${platform} 설정 파일 생성 완료: ${filePath}`)); // 플랫폼별 추가 안내 const instructions = { vercel: [ '다음 단계:', '1. vercel login', '2. vercel', '3. vercel env add (환경 변수 설정)', '4. vercel --prod' ], netlify: [ '다음 단계:', '1. netlify login', '2. netlify init', '3. netlify env:set (환경 변수 설정)', '4. netlify deploy --prod' ], cloudflare: [ '다음 단계:', '1. wrangler login', '2. wrangler pages create', '3. wrangler secret put (환경 변수 설정)', '4. wrangler pages deploy out' ], github: [ '다음 단계:', '1. GitHub 저장소의 Settings > Secrets에서 환경 변수 설정', '2. Settings > Pages에서 GitHub Actions 소스 선택', '3. 코드를 main 브랜치에 push' ], aws: [ '다음 단계:', '1. AWS CLI 설정: aws configure', '2. CloudFormation 스택 생성 (aws/cloudformation.yml 참조)', '3. Parameter Store에 환경 변수 설정', '4. CodeBuild 프로젝트 실행' ] }; if (instructions[platform]) { console.log(chalk.blue('\n📋 ' + instructions[platform].join('\n '))); } } catch (error) { console.error(chalk.red(`❌ 파일 생성 실패: ${error.message}`)); } } function exportAll() { console.log(chalk.blue('🚀 모든 플랫폼 설정 파일 생성 중...\n')); Object.keys(templates).forEach(platform => { exportConfig(platform); console.log(''); }); console.log(chalk.green('✨ 모든 설정 파일 생성 완료!')); console.log(chalk.yellow('\n💡 Tip: 각 플랫폼의 환경 변수 설정을 잊지 마세요:')); console.log(chalk.white(' - NEXT_PUBLIC_SUPABASE_URL')); console.log(chalk.white(' - NEXT_PUBLIC_SUPABASE_ANON_KEY')); console.log(chalk.white(' - SUPABASE_SERVICE_ROLE_KEY')); } function showHelp() { console.log(chalk.blue('🚀 Miri.dev Export 도구')); console.log(''); console.log(chalk.white('사용법:')); console.log(' miri export <platform> # 특정 플랫폼 설정 생성'); console.log(' miri export all # 모든 플랫폼 설정 생성'); console.log(''); console.log(chalk.white('지원 플랫폼:')); console.log(' vercel # Vercel 배포 설정'); console.log(' netlify # Netlify 배포 설정'); console.log(' cloudflare # Cloudflare Pages 설정'); console.log(' github # GitHub Pages 자동 배포'); console.log(' aws # AWS S3 + CloudFront 설정'); console.log(''); console.log(chalk.white('예시:')); console.log(' miri export vercel # vercel.json 생성'); console.log(' miri export all # 모든 설정 파일 생성'); } module.exports = function (platform) { if (!platform || platform === 'help') { showHelp(); return; } if (platform === 'all') { exportAll(); return; } exportConfig(platform); }; // CLI에서 직접 실행될 때 if (require.main === module) { const platform = process.argv[2]; module.exports(platform); }