UNPKG

@ordojs/cli

Version:

Command-line interface for OrdoJS framework

550 lines (467 loc) 11.5 kB
/** * @fileoverview OrdoJS CLI - Docker configuration generator */ import { CLIError, ErrorType } from '../index.js'; /** * Docker configuration generator for OrdoJS applications */ export class DockerGenerator { defaultConfig = { baseImage: 'node:18-alpine', nodeVersion: '18', buildStage: true, productionStage: true, multiStage: true, port: 3000, healthCheck: true, environment: {}, volumes: [], commands: [] }; /** * Generate Dockerfile content for an OrdoJS application */ generateDockerfile(config, options = {}) { const dockerConfig = this.mergeConfig(options); try { if (dockerConfig.multiStage) { return this.generateMultiStageDockerfile(dockerConfig); } else { return this.generateSimpleDockerfile(dockerConfig); } } catch (error) { throw new CLIError(`Failed to generate Dockerfile: ${error instanceof Error ? error.message : String(error)}`, ErrorType.DEPLOYMENT, 'DOCKER-001'); } } /** * Generate docker-compose.yml content */ generateDockerCompose(config, options = {}) { const dockerConfig = this.mergeConfig(options); try { return this.generateDockerComposeContent(dockerConfig, config); } catch (error) { throw new CLIError(`Failed to generate docker-compose.yml: ${error instanceof Error ? error.message : String(error)}`, ErrorType.DEPLOYMENT, 'DOCKER-002'); } } /** * Generate .dockerignore file content */ generateDockerignore() { return `# Dependencies node_modules npm-debug.log* yarn-debug.log* yarn-error.log* # Runtime data pids *.pid *.seed *.pid.lock # Coverage directory used by tools like istanbul coverage *.lcov # nyc test coverage .nyc_output # Grunt intermediate storage .grunt # Bower dependency directory bower_components # node-waf configuration .lock-wscript # Compiled binary addons build/Release # Dependency directories jspm_packages/ # TypeScript cache *.tsbuildinfo # Optional npm cache directory .npm # Optional eslint cache .eslintcache # Microbundle cache .rpt2_cache/ .rts2_cache_cjs/ .rts2_cache_es/ .rts2_cache_umd/ # Optional REPL history .node_repl_history # Output of 'npm pack' *.tgz # Yarn Integrity file .yarn-integrity # dotenv environment variables file .env .env.test .env.production # parcel-bundler cache .cache .parcel-cache # Next.js build output .next # Nuxt.js build / generate output .nuxt dist # Gatsby files .cache/ public # Storybook build outputs .out .storybook-out # Temporary folders tmp/ temp/ # Logs logs *.log # Runtime data pids *.pid *.seed *.pid.lock # Directory for instrumented libs generated by jscoverage/JSCover lib-cov # Coverage directory used by tools like istanbul coverage *.lcov # Grunt intermediate storage .grunt # Bower dependency directory bower_components # node-waf configuration .lock-wscript # Compiled binary addons build/Release # Dependency directories node_modules/ jspm_packages/ # Optional npm cache directory .npm # Optional eslint cache .eslintcache # Optional REPL history .node_repl_history # Output of 'npm pack' *.tgz # Yarn Integrity file .yarn-integrity # dotenv environment variables file .env # parcel-bundler cache .cache .parcel-cache # next.js build output .next # nuxt.js build output .nuxt # vuepress build output .vuepress/dist # Serverless directories .serverless # FuseBox cache .fusebox/ # DynamoDB Local files .dynamodb/ # TernJS port file .tern-port # Stores VSCode versions used for testing VSCode extensions .vscode-test # OrdoJS specific .ordo-cache build/ dist/ *.ordo.js *.ordo.css # IDE .vscode/ .idea/ *.swp *.swo *~ # OS .DS_Store Thumbs.db # Git .git/ .gitignore # Documentation docs/ *.md # Tests test/ tests/ __tests__/ *.test.js *.test.ts *.spec.js *.spec.ts # Development .dev/ dev/ `; } /** * Generate Kubernetes manifests */ generateKubernetesManifests(config, options = {}) { const dockerConfig = this.mergeConfig(options); try { return { deployment: this.generateK8sDeployment(dockerConfig, config), service: this.generateK8sService(dockerConfig, config), ingress: this.generateK8sIngress(dockerConfig, config), configMap: this.generateK8sConfigMap(dockerConfig, config), secret: this.generateK8sSecret(dockerConfig, config) }; } catch (error) { throw new CLIError(`Failed to generate Kubernetes manifests: ${error instanceof Error ? error.message : String(error)}`, ErrorType.DEPLOYMENT, 'DOCKER-003'); } } mergeConfig(options) { return { ...this.defaultConfig, ...options, environment: { ...this.defaultConfig.environment, ...options.environment }, volumes: [...this.defaultConfig.volumes, ...(options.volumes || [])], commands: [...this.defaultConfig.commands, ...(options.commands || [])] }; } generateMultiStageDockerfile(config) { return `# Multi-stage Dockerfile for OrdoJS application FROM node:${config.nodeVersion}-alpine AS base # Set working directory WORKDIR /app # Copy package files COPY package*.json ./ COPY pnpm-lock.yaml ./ # Install pnpm RUN npm install -g pnpm # Install dependencies RUN pnpm install --frozen-lockfile # Copy source code COPY . . # Build stage FROM base AS builder # Build the application RUN pnpm run build # Production stage FROM node:${config.nodeVersion}-alpine AS production # Create app user RUN addgroup -g 1001 -S nodejs RUN adduser -S ordojs -u 1001 # Set working directory WORKDIR /app # Copy package files COPY package*.json ./ COPY pnpm-lock.yaml ./ # Install pnpm and production dependencies only RUN npm install -g pnpm RUN pnpm install --frozen-lockfile --prod # Copy built application from builder stage COPY --from=builder --chown=ordojs:nodejs /app/dist ./dist COPY --from=builder --chown=ordojs:nodejs /app/public ./public # Switch to non-root user USER ordojs # Expose port EXPOSE ${config.port} # Set environment variables ${this.generateEnvironmentVariables(config.environment)} # Health check ${config.healthCheck ? this.generateHealthCheck(config.port) : ''} # Start the application CMD ["node", "dist/server.js"] `; } generateSimpleDockerfile(config) { return `# Simple Dockerfile for OrdoJS application FROM node:${config.nodeVersion}-alpine # Set working directory WORKDIR /app # Copy package files COPY package*.json ./ COPY pnpm-lock.yaml ./ # Install pnpm RUN npm install -g pnpm # Install dependencies RUN pnpm install --frozen-lockfile # Copy source code COPY . . # Build the application RUN pnpm run build # Expose port EXPOSE ${config.port} # Set environment variables ${this.generateEnvironmentVariables(config.environment)} # Health check ${config.healthCheck ? this.generateHealthCheck(config.port) : ''} # Start the application CMD ["node", "dist/server.js"] `; } generateDockerComposeContent(config, deploymentConfig) { return `version: '3.8' services: ordojs-app: build: context: . dockerfile: Dockerfile ports: - "${config.port}:${config.port}" environment: ${this.generateDockerComposeEnvironment(config.environment)} volumes: ${this.generateDockerComposeVolumes(config.volumes)} restart: unless-stopped healthcheck: test: ["CMD", "curl", "-f", "http://localhost:${config.port}/health"] interval: 30s timeout: 10s retries: 3 start_period: 40s networks: default: name: ordojs-network `; } generateK8sDeployment(config, deploymentConfig) { return `apiVersion: apps/v1 kind: Deployment metadata: name: ordojs-app labels: app: ordojs-app spec: replicas: 3 selector: matchLabels: app: ordojs-app template: metadata: labels: app: ordojs-app spec: containers: - name: ordojs-app image: ordojs-app:latest ports: - containerPort: ${config.port} env: ${this.generateK8sEnvironment(config.environment)} resources: requests: memory: "128Mi" cpu: "100m" limits: memory: "512Mi" cpu: "500m" livenessProbe: httpGet: path: /health port: ${config.port} initialDelaySeconds: 30 periodSeconds: 10 readinessProbe: httpGet: path: /health port: ${config.port} initialDelaySeconds: 5 periodSeconds: 5 `; } generateK8sService(config, deploymentConfig) { return `apiVersion: v1 kind: Service metadata: name: ordojs-app-service spec: selector: app: ordojs-app ports: - protocol: TCP port: 80 targetPort: ${config.port} type: ClusterIP `; } generateK8sIngress(config, deploymentConfig) { return `apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: ordojs-app-ingress annotations: nginx.ingress.kubernetes.io/rewrite-target: / spec: rules: - host: ordojs-app.local http: paths: - path: / pathType: Prefix backend: service: name: ordojs-app-service port: number: 80 `; } generateK8sConfigMap(config, deploymentConfig) { const envVars = Object.entries(config.environment) .filter(([key]) => !key.toLowerCase().includes('secret') && !key.toLowerCase().includes('password')) .map(([key, value]) => ` ${key}: "${value}"`) .join('\n'); return `apiVersion: v1 kind: ConfigMap metadata: name: ordojs-app-config data: ${envVars} `; } generateK8sSecret(config, deploymentConfig) { const secretVars = Object.entries(config.environment) .filter(([key]) => key.toLowerCase().includes('secret') || key.toLowerCase().includes('password')) .map(([key, value]) => ` ${key}: ${Buffer.from(value).toString('base64')}`) .join('\n'); if (!secretVars) { return ''; } return `apiVersion: v1 kind: Secret metadata: name: ordojs-app-secret type: Opaque data: ${secretVars} `; } generateEnvironmentVariables(environment) { return Object.entries(environment) .map(([key, value]) => `ENV ${key}=${value}`) .join('\n'); } generateHealthCheck(port) { return `HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \\ CMD curl -f http://localhost:${port}/health || exit 1`; } generateDockerComposeEnvironment(environment) { return Object.entries(environment) .map(([key, value]) => ` - ${key}=${value}`) .join('\n'); } generateDockerComposeVolumes(volumes) { return volumes .map(volume => ` - ${volume}`) .join('\n'); } generateK8sEnvironment(environment) { return Object.entries(environment) .map(([key, value]) => ` - name: ${key} value: "${value}"`) .join('\n'); } } //# sourceMappingURL=docker-generator.js.map