Appearance
实现 Docker 转 Compose 工具
分类:developer | 标签:docker、compose、运维 将 docker run 命令转为 compose 片段
2.1 功能说明
将 docker run 命令转为 compose 片段
2.1.1 使用指南
功能说明
将 docker run 命令解析为 docker-compose.yml 片段(services 段落)。
使用场景
把临时运行命令固化为可版本化的 Compose 配置。
2.2 代码实现
2.2.1 组件结构
本工具是一个基于 Vue 3 <script setup> 语法的单文件组件(SFC),统一包裹在 ToolPageShell 组件内,由它提供页面标题、工具 ID、分类与「使用指南」插槽等通用外壳;核心业务逻辑(响应式状态、计算属性、事件处理函数)全部写在 <script setup> 中,输入/输出通过 el-input、el-button 等 Element Plus 组件与用户交互,所有数据均在浏览器本地处理,不会上传服务器。
2.2.2 核心逻辑概览
<script setup> 中定义的主要函数/方法:
tokenize()run()copy()clear()
2.2.3 关键实现代码
以下是该工具的完整 <script setup> 实现(核心代码)。模板(<template>)与样式(<style>)已省略。
vue
import { ref } from 'vue'
import { dump as yamlDump } from 'js-yaml'
import ToolPageShell from '@/components/ToolPageShell.vue'
const cmd = ref('')
const output = ref('')
const error = ref('')
function tokenize(s) {
const re = /"([^"]*)"|'([^']*)'|(\S+)/g
const out = []; let m
while ((m = re.exec(s))) out.push(m[1] ?? m[2] ?? m[3])
return out
}
function run() {
error.value = output.value = ''
const raw = cmd.value.trim()
if (!raw) { error.value = '请输入 docker run 命令'; return }
const tokens = tokenize(raw)
// strip leading "docker" and "run"
let i = 0
if (tokens[i] === 'docker') i++
if (tokens[i] === 'run') i++
const svc = { image: '' }
const ports = [], volumes = [], env = {}, networks = [], command = []
let image = ''
for (; i < tokens.length; i++) {
const t = tokens[i]
if (t === '-d' || t === '--detach') continue
else if (t === '--rm') continue
else if (t === '-it') { svc.tty = true; svc.stdin_open = true }
else if (t === '-i' || t === '--interactive') svc.stdin_open = true
else if (t === '-t' || t === '--tty') svc.tty = true
else if (t === '--name') { svc.container_name = tokens[++i] }
else if (t === '-p' || t === '--publish') ports.push(tokens[++i])
else if (t === '-v' || t === '--volume') volumes.push(tokens[++i])
else if (t === '-e' || t === '--env') { const kv = tokens[++i].split('='); env[kv[0]] = kv.slice(1).join('=') }
else if (t === '--network') networks.push(tokens[++i])
else if (t === '--restart') svc.restart = tokens[++i]
else if (t === '--entrypoint') svc.entrypoint = tokens[++i]
else if (image === '') image = t
else command.push(t)
}
if (!image) { error.value = '未识别到镜像名'; return }
const name = (svc.container_name || image.split('/').pop().split(':')[0]).replace(/[^a-zA-Z0-9_-]/g, '')
const service = { image }
if (svc.container_name) service.container_name = svc.container_name
if (ports.length) service.ports = ports
if (volumes.length) service.volumes = volumes
if (Object.keys(env).length) service.environment = env
if (networks.length) service.networks = networks
if (svc.restart) service.restart = svc.restart
if (svc.tty) service.tty = true
if (svc.stdin_open) service.stdin_open = true
if (svc.entrypoint) service.entrypoint = svc.entrypoint
if (command.length) service.command = command
try {
output.value = yamlDump({ version: '3.8', services: { [name]: service } })
} catch (e) { error.value = '生成失败:' + e.message }
}
function copy() { if (output.value) navigator.clipboard?.writeText(output.value) }
function clear() { cmd.value = output.value = error.value = '' }2.3 效果截图
