export const dockerfileSchema = { id: 'dockerfile', name: 'Dockerfile', icon: 'Box', category: '容器化', description: 'Docker 容器镜像构建文件', format: 'dockerfile', fileName: 'Dockerfile', groups: [ { title: '基础镜像', fields: [ { key: 'baseImage', label: '基础镜像', type: 'text', placeholder: 'nginx:1.24-alpine', default: 'nginx:1.24-alpine', required: true, tip: 'FROM 指令' }, { key: 'fromPlatform', label: '平台架构', type: 'select', options: [ { label: '不限制', value: '' }, { label: 'linux/amd64', value: 'linux/amd64' }, { label: 'linux/arm64', value: 'linux/arm64' }, ], default: '', tip: '多架构构建时指定' }, { key: 'enableMultiStage', label: '多阶段构建', type: 'switch', default: false, tip: '分离构建和运行阶段' }, { key: 'builderImage', label: '构建阶段镜像', type: 'text', placeholder: 'node:18-alpine', default: 'node:18-alpine', dependsOn: { key: 'enableMultiStage', value: true } }, { key: 'builderAs', label: '构建阶段别名', type: 'text', default: 'builder', dependsOn: { key: 'enableMultiStage', value: true } }, ], }, { title: '元数据 (LABEL)', fields: [ { key: 'enableLabels', label: '添加标签', type: 'switch', default: false }, { key: 'labelMaintainer', label: '维护者', type: 'text', placeholder: 'team@example.com', default: 'team@example.com', dependsOn: { key: 'enableLabels', value: true } }, { key: 'labelVersion', label: '版本', type: 'text', placeholder: '1.0.0', default: '1.0.0', dependsOn: { key: 'enableLabels', value: true } }, { key: 'labelDescription', label: '描述', type: 'text', placeholder: 'My application', default: '', dependsOn: { key: 'enableLabels', value: true } }, ], }, { title: '构建参数 (ARG)', fields: [ { key: 'enableArgs', label: '定义构建参数', type: 'switch', default: false, tip: '仅在 docker build --build-arg 时生效' }, { key: 'args', label: '参数列表 (KEY=VALUE)', type: 'text', placeholder: 'APP_VERSION=1.0.0\nNODE_ENV=production', default: 'APP_VERSION=1.0.0', dependsOn: { key: 'enableArgs', value: true }, tip: '每行一个,格式 KEY 或 KEY=默认值' }, ], }, { title: '环境变量 (ENV)', fields: [ { key: 'enableEnv', label: '设置环境变量', type: 'switch', default: false }, { key: 'envVars', label: '环境变量 (KEY=VALUE)', type: 'text', placeholder: 'NODE_ENV=production\nAPP_PORT=8080', default: 'NODE_ENV=production\nAPP_PORT=8080', dependsOn: { key: 'enableEnv', value: true }, tip: '每行一个,格式 KEY=VALUE' }, ], }, { title: '工作目录与用户', fields: [ { key: 'enableWorkdir', label: '设置工作目录', type: 'switch', default: true }, { key: 'workdir', label: '工作目录', type: 'text', default: '/app', dependsOn: { key: 'enableWorkdir', value: true } }, { key: 'enableUser', label: '切换运行用户', type: 'switch', default: false, tip: '生产环境建议使用非 root 用户' }, { key: 'userName', label: '用户名或 UID', type: 'text', placeholder: '1001:1001', default: '1001:1001', dependsOn: { key: 'enableUser', value: true } }, { key: 'enableUseradd', label: '创建用户', type: 'switch', default: false, dependsOn: { key: 'enableUser', value: true }, tip: '在 RUN 中创建用户' }, { key: 'createUserName', label: '用户名', type: 'text', default: 'appuser', dependsOn: { key: 'enableUseradd', value: true } }, ], }, { title: '文件复制', fields: [ { key: 'enableCopy', label: '复制文件', type: 'switch', default: true }, { key: 'copySrc', label: '源路径', type: 'text', placeholder: '. 或 ./dist', default: '.', dependsOn: { key: 'enableCopy', value: true } }, { key: 'copyDest', label: '目标路径', type: 'text', placeholder: '/app', default: '/app', dependsOn: { key: 'enableCopy', value: true } }, { key: 'enableCopyChown', label: '设置文件所有者', type: 'switch', default: false, dependsOn: { key: 'enableCopy', value: true } }, { key: 'copyChown', label: '所有者', type: 'text', placeholder: '1001:1001', default: '1001:1001', dependsOn: { key: 'enableCopyChown', value: true } }, { key: 'enableCopyChmod', label: '设置文件权限', type: 'switch', default: false, dependsOn: { key: 'enableCopy', value: true } }, { key: 'copyChmod', label: '权限', type: 'text', placeholder: '755', default: '755', dependsOn: { key: 'enableCopyChmod', value: true } }, { key: 'enableAdd', label: '使用 ADD (自动解压/URL)', type: 'switch', default: false, tip: '需要解压 tar 或下载远程文件时使用' }, { key: 'addSrc', label: 'ADD 源', type: 'text', placeholder: 'https://example.com/app.tar.gz', default: '', dependsOn: { key: 'enableAdd', value: true } }, { key: 'addDest', label: 'ADD 目标', type: 'text', default: '/app/', dependsOn: { key: 'enableAdd', value: true } }, ], }, { title: '依赖安装 (RUN)', fields: [ { key: 'packageManager', label: '包管理器', type: 'select', options: [ { label: '不安装系统包', value: 'none' }, { label: 'apk (Alpine)', value: 'apk' }, { label: 'apt (Debian/Ubuntu)', value: 'apt' }, { label: 'yum (CentOS/RHEL)', value: 'yum' }, { label: 'dnf (Fedora)', value: 'dnf' }, ], default: 'none' }, { key: 'systemPackages', label: '系统包 (空格分隔)', type: 'text', placeholder: 'curl wget tzdata', default: 'curl ca-certificates tzdata', dependsOn: { key: 'packageManager', valueNotIn: ['none'] } }, { key: 'enableAppInstall', label: '安装应用依赖', type: 'switch', default: false, tip: 'npm install / pip install / go build 等' }, { key: 'appInstallCmd', label: '安装命令', type: 'text', placeholder: 'npm ci --only=production', default: 'npm ci --only=production', dependsOn: { key: 'enableAppInstall', value: true } }, { key: 'enableBuildCmd', label: '构建命令', type: 'switch', default: false, tip: 'npm run build / go build / mvn package 等' }, { key: 'buildCmd', label: '构建命令', type: 'text', placeholder: 'npm run build', default: 'npm run build', dependsOn: { key: 'enableBuildCmd', value: true } }, { key: 'enableCleanup', label: '清理缓存', type: 'switch', default: true, dependsOn: { key: 'packageManager', valueNotIn: ['none'] }, tip: '减小镜像体积' }, ], }, { title: '端口与网络', fields: [ { key: 'enableExpose', label: '暴露端口', type: 'switch', default: true }, { key: 'exposePorts', label: '端口列表', type: 'text', placeholder: '80 443', default: '80', dependsOn: { key: 'enableExpose', value: true }, tip: '空格分隔多个端口' }, ], }, { title: '健康检查 (HEALTHCHECK)', fields: [ { key: 'enableHealthcheck', label: '添加健康检查', type: 'switch', default: false }, { key: 'healthcheckType', label: '检查方式', type: 'select', options: [ { label: 'CMD (自定义命令)', value: 'CMD' }, { label: 'NONE (禁用继承的健康检查)', value: 'NONE' }, ], default: 'CMD', dependsOn: { key: 'enableHealthcheck', value: true } }, { key: 'healthcheckInterval', label: '检查间隔', type: 'text', default: '30s', dependsOn: { key: 'healthcheckType', value: 'CMD' } }, { key: 'healthcheckTimeout', label: '超时时间', type: 'text', default: '10s', dependsOn: { key: 'healthcheckType', value: 'CMD' } }, { key: 'healthcheckRetries', label: '重试次数', type: 'number', min: 1, max: 10, default: 3, dependsOn: { key: 'healthcheckType', value: 'CMD' } }, { key: 'healthcheckStartPeriod', label: '启动等待期', type: 'text', default: '5s', dependsOn: { key: 'healthcheckType', value: 'CMD' } }, { key: 'healthcheckCmd', label: '检查命令', type: 'text', placeholder: 'curl -f http://localhost/ || exit 1', default: 'curl -f http://localhost:80/ || exit 1', dependsOn: { key: 'healthcheckType', value: 'CMD' }, required: true }, ], }, { title: '挂载点与数据卷', fields: [ { key: 'enableVolume', label: '声明数据卷', type: 'switch', default: false }, { key: 'volumePaths', label: '挂载路径', type: 'text', placeholder: '/data /var/log/app', default: '/data', dependsOn: { key: 'enableVolume', value: true }, tip: '空格分隔多个路径' }, ], }, { title: '入口与启动', fields: [ { key: 'enableEntrypoint', label: '设置 ENTRYPOINT', type: 'switch', default: false, tip: '容器启动时执行的固定命令' }, { key: 'entrypointCmd', label: 'ENTRYPOINT 命令', type: 'text', placeholder: '["java", "-jar", "app.jar"]', default: '["node", "server.js"]', dependsOn: { key: 'enableEntrypoint', value: true }, tip: 'JSON 数组格式或 shell 格式' }, { key: 'enableCmd', label: '设置 CMD', type: 'switch', default: true, tip: '容器启动时的默认参数' }, { key: 'cmdType', label: 'CMD 格式', type: 'select', options: [ { label: 'exec 格式 (推荐)', value: 'exec' }, { label: 'shell 格式', value: 'shell' }, ], default: 'exec', dependsOn: { key: 'enableCmd', value: true } }, { key: 'cmdValue', label: 'CMD 内容', type: 'text', placeholder: '["nginx", "-g", "daemon off;"]', default: '["nginx", "-g", "daemon off;"]', dependsOn: { key: 'enableCmd', value: true } }, ], }, { title: '信号与停止', fields: [ { key: 'enableStopSignal', label: '自定义停止信号', type: 'switch', default: false, tip: 'docker stop 发送的信号' }, { key: 'stopSignal', label: '停止信号', type: 'select', options: [ { label: 'SIGTERM (默认)', value: 'SIGTERM' }, { label: 'SIGQUIT', value: 'SIGQUIT' }, { label: 'SIGINT', value: 'SIGINT' }, { label: 'SIGUSR1', value: 'SIGUSR1' }, ], default: 'SIGTERM', dependsOn: { key: 'enableStopSignal', value: true } }, { key: 'enableShell', label: '指定 Shell', type: 'switch', default: false, tip: 'SHELL 指令,RUN 使用的 shell' }, { key: 'shellPath', label: 'Shell 路径', type: 'text', default: '/bin/sh', dependsOn: { key: 'enableShell', value: true } }, { key: 'shellFlag', label: 'Shell 参数', type: 'text', default: '-c', dependsOn: { key: 'enableShell', value: true } }, ], }, ], } export function generateDockerfile(config) { const lines = [] lines.push(`# Dockerfile - 由 ConfTemplate 生成`) lines.push(`# 生成时间: ${new Date().toLocaleString('zh-CN')}`) lines.push(``) // Builder stage if (config.enableMultiStage) { lines.push(`# ======================== 构建阶段 ========================`) lines.push(`FROM${config.fromPlatform ? ` --platform=${config.fromPlatform}` : ''} ${config.builderImage} AS ${config.builderAs}`) lines.push(``) if (config.enableWorkdir) { lines.push(`WORKDIR ${config.workdir}`) } lines.push(`COPY . .`) if (config.enableAppInstall && config.appInstallCmd) { lines.push(`RUN ${config.appInstallCmd}`) } if (config.enableBuildCmd && config.buildCmd) { lines.push(`RUN ${config.buildCmd}`) } lines.push(``) lines.push(`# ======================== 运行阶段 ========================`) } // FROM const platformArg = config.fromPlatform ? ` --platform=${config.fromPlatform}` : '' if (config.enableMultiStage) { lines.push(`FROM${platformArg} ${config.baseImage}`) } else { lines.push(`FROM${platformArg} ${config.baseImage}`) } lines.push(``) // ARG if (config.enableArgs && config.args) { config.args.split('\n').filter(Boolean).forEach(line => { lines.push(`ARG ${line.trim()}`) }) lines.push(``) } // LABEL if (config.enableLabels) { if (config.labelMaintainer) lines.push(`LABEL maintainer="${config.labelMaintainer}"`) if (config.labelVersion) lines.push(`LABEL version="${config.labelVersion}"`) if (config.labelDescription) lines.push(`LABEL description="${config.labelDescription}"`) lines.push(``) } // ENV if (config.enableEnv && config.envVars) { config.envVars.split('\n').filter(Boolean).forEach(line => { lines.push(`ENV ${line.trim()}`) }) lines.push(``) } // WORKDIR if (config.enableWorkdir) { lines.push(`WORKDIR ${config.workdir}`) lines.push(``) } // Package install if (config.packageManager && config.packageManager !== 'none') { const pkgs = (config.systemPackages || '').trim() if (pkgs) { if (config.packageManager === 'apk') { lines.push(`RUN apk add --no-cache ${pkgs}`) } else if (config.packageManager === 'apt') { lines.push(`RUN apt-get update && \\`) lines.push(` apt-get install -y --no-install-recommends ${pkgs} && \\`) if (config.enableCleanup) { lines.push(` rm -rf /var/lib/apt/lists/*`) } } else if (config.packageManager === 'yum') { lines.push(`RUN yum install -y ${pkgs} && \\`) if (config.enableCleanup) { lines.push(` yum clean all && rm -rf /var/cache/yum`) } } else if (config.packageManager === 'dnf') { lines.push(`RUN dnf install -y ${pkgs} && \\`) if (config.enableCleanup) { lines.push(` dnf clean all && rm -rf /var/cache/dnf`) } } lines.push(``) } } // Create user if (config.enableUser && config.enableUseradd && config.createUserName) { lines.push(`RUN addgroup -S ${config.createUserName} && adduser -S ${config.createUserName} -G ${config.createUserName}`) lines.push(``) } // COPY from builder stage if (config.enableMultiStage) { if (config.enableCopyChown) { lines.push(`COPY --from=${config.builderAs} --chown=${config.copyChown} ${config.workdir || '/app'} ${config.copyDest}`) } else { lines.push(`COPY --from=${config.builderAs} ${config.workdir || '/app'} ${config.copyDest}`) } lines.push(``) } // COPY if (config.enableCopy && !config.enableMultiStage) { let copyFlags = [] if (config.enableCopyChown && config.copyChown) copyFlags.push(`--chown=${config.copyChown}`) if (config.enableCopyChmod && config.copyChmod) copyFlags.push(`--chmod=${config.copyChmod}`) const flags = copyFlags.length ? ` ${copyFlags.join(' ')}` : '' lines.push(`COPY${flags} ${config.copySrc} ${config.copyDest}`) lines.push(``) } // ADD if (config.enableAdd && config.addSrc) { lines.push(`ADD ${config.addSrc} ${config.addDest}`) lines.push(``) } // App install (non-multi-stage) if (!config.enableMultiStage && config.enableAppInstall && config.appInstallCmd) { lines.push(`RUN ${config.appInstallCmd}`) lines.push(``) } // Build cmd (non-multi-stage) if (!config.enableMultiStage && config.enableBuildCmd && config.buildCmd) { lines.push(`RUN ${config.buildCmd}`) lines.push(``) } // VOLUME if (config.enableVolume && config.volumePaths) { const paths = config.volumePaths.split(/\s+/).filter(Boolean) if (paths.length === 1) { lines.push(`VOLUME ["${paths[0]}"]`) } else { lines.push(`VOLUME [${paths.map(p => `"${p}"`).join(', ')}]`) } lines.push(``) } // EXPOSE if (config.enableExpose && config.exposePorts) { lines.push(`EXPOSE ${config.exposePorts}`) lines.push(``) } // USER if (config.enableUser) { lines.push(`USER ${config.userName}`) lines.push(``) } // HEALTHCHECK if (config.enableHealthcheck) { if (config.healthcheckType === 'NONE') { lines.push(`HEALTHCHECK NONE`) } else { let hc = `HEALTHCHECK` if (config.healthcheckInterval) hc += ` --interval=${config.healthcheckInterval}` if (config.healthcheckTimeout) hc += ` --timeout=${config.healthcheckTimeout}` if (config.healthcheckRetries) hc += ` --retries=${config.healthcheckRetries}` if (config.healthcheckStartPeriod) hc += ` --start-period=${config.healthcheckStartPeriod}` hc += ` CMD ${config.healthcheckCmd}` lines.push(hc) } lines.push(``) } // STOPSIGNAL if (config.enableStopSignal) { lines.push(`STOPSIGNAL ${config.stopSignal}`) lines.push(``) } // SHELL if (config.enableShell) { lines.push(`SHELL ["${config.shellPath}", "${config.shellFlag}"]`) lines.push(``) } // ENTRYPOINT if (config.enableEntrypoint && config.entrypointCmd) { const val = config.entrypointCmd.trim() if (val.startsWith('[')) { lines.push(`ENTRYPOINT ${val}`) } else { lines.push(`ENTRYPOINT ["${val}"]`) } lines.push(``) } // CMD if (config.enableCmd && config.cmdValue) { const val = config.cmdValue.trim() if (config.cmdType === 'exec') { if (val.startsWith('[')) { lines.push(`CMD ${val}`) } else { lines.push(`CMD ["${val}"]`) } } else { lines.push(`CMD ${val}`) } lines.push(``) } return lines.join('\n') }