Initial panel project deploy skill snapshot
This commit is contained in:
@@ -0,0 +1,570 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
项目初始化与部署工作流管理器
|
||||
提供状态管理、步骤执行和错误恢复功能
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import json
|
||||
import shutil
|
||||
import subprocess
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
from typing import Dict, List, Optional, Tuple
|
||||
from dataclasses import dataclass, asdict
|
||||
from enum import Enum
|
||||
|
||||
class StepStatus(Enum):
|
||||
PENDING = "⏭️ 未开始"
|
||||
IN_PROGRESS = "⏸️ 进行中"
|
||||
COMPLETED = "✅ 已完成"
|
||||
FAILED = "❌ 失败"
|
||||
SKIPPED = "⏭️ 跳过"
|
||||
|
||||
class ProjectType(Enum):
|
||||
DOCKER_WEB = "docker_web"
|
||||
DOCKER_API = "docker_api"
|
||||
DOCKER_MICROSERVICES = "docker_microservices"
|
||||
DESKTOP = "desktop"
|
||||
MOBILE = "mobile"
|
||||
CLI = "cli"
|
||||
UNKNOWN = "unknown"
|
||||
|
||||
class TechStack(Enum):
|
||||
JAVA_SPRING = "java_spring"
|
||||
NODE_EXPRESS = "node_express"
|
||||
NODE_NEST = "node_nest"
|
||||
PYTHON_DJANGO = "python_django"
|
||||
PYTHON_FASTAPI = "python_fastapi"
|
||||
GO_GIN = "go_gin"
|
||||
GO_FIBER = "go_fiber"
|
||||
REACT = "react"
|
||||
VUE = "vue"
|
||||
ANGULAR = "angular"
|
||||
UNKNOWN = "unknown"
|
||||
|
||||
@dataclass
|
||||
class ProjectInfo:
|
||||
"""项目信息"""
|
||||
name: str
|
||||
directory: str
|
||||
git_url: str
|
||||
port_range: Optional[str] = None
|
||||
project_type: ProjectType = ProjectType.UNKNOWN
|
||||
tech_stack: List[TechStack] = None
|
||||
|
||||
def __post_init__(self):
|
||||
if self.tech_stack is None:
|
||||
self.tech_stack = []
|
||||
|
||||
@dataclass
|
||||
class Step:
|
||||
"""工作流步骤"""
|
||||
id: int
|
||||
name: str
|
||||
description: str
|
||||
applicable_types: List[ProjectType]
|
||||
status: StepStatus = StepStatus.PENDING
|
||||
start_time: Optional[str] = None
|
||||
end_time: Optional[str] = None
|
||||
notes: str = ""
|
||||
error_info: Optional[str] = None
|
||||
|
||||
class WorkflowManager:
|
||||
"""工作流管理器"""
|
||||
|
||||
# 完整的20个核心步骤
|
||||
ALL_TYPES = [ProjectType.DOCKER_WEB, ProjectType.DOCKER_API, ProjectType.DOCKER_MICROSERVICES, ProjectType.DESKTOP, ProjectType.MOBILE, ProjectType.CLI]
|
||||
DOCKER_TYPES = [ProjectType.DOCKER_WEB, ProjectType.DOCKER_API, ProjectType.DOCKER_MICROSERVICES]
|
||||
|
||||
# 关键时序:初始基线启动(3,内部) → 初始界面截图(4,内部) → 面板改造(6-11) → 产品构建启动(15)
|
||||
# → 健康验证(16) → 产品截图与录屏(17) → README/归档(18) → 交付核查(19)
|
||||
CORE_STEPS = [
|
||||
Step(0, "克隆项目", "从Git仓库克隆项目到本地,不修改任何业务代码", ALL_TYPES),
|
||||
Step(1, "项目完整度评估", "评估项目完整度、依赖情况与可运行性", ALL_TYPES),
|
||||
Step(2, "项目整体分析", "分析项目结构、技术栈、启动方式和部署策略", ALL_TYPES),
|
||||
Step(3, "初始版本基线启动(内部)", "在改动任何代码前,用项目现有启动方式跑通初始版本并确认可访问;仅用于内部质量核对", ALL_TYPES),
|
||||
Step(4, "初始界面基线截图(内部)", "代码零改动状态下采集初始界面作为内部对比基线;严禁进入交付包或使用 before 等对外文件名", ALL_TYPES),
|
||||
Step(5, "Docker配置生成", "生成/完善 Docker Compose 配置文件", DOCKER_TYPES),
|
||||
Step(6, "页面信息与品牌名称修改", "替换页面品牌信息并建立科技感面板基础样式", ALL_TYPES),
|
||||
Step(7, "Logo生成与替换", "生成并替换项目 Logo、Favicon,适配深色面板", ALL_TYPES),
|
||||
Step(8, "版权与第三方标识规范", "清理依法可移除的公司名称、LOGO、商标、水印、联系方式等专属标识;MIT 等许可证要求保留的版权与许可文本必须随源码保留", ALL_TYPES),
|
||||
Step(9, "页脚、导航与社区链接", "导航、页脚改为半透明悬浮面板风格,并放置开源社区与 GCC AI Coding 链接", ALL_TYPES),
|
||||
Step(10, "登录页面", "改成居中玻璃面板、发光输入框、动态背景", ALL_TYPES),
|
||||
Step(11, "AI对话功能", "实现前端AI对话界面与指定服务商API对接及使用教程", ALL_TYPES),
|
||||
Step(12, "示例数据", "生成和导入示例数据,数据卡片/列表面板承载", DOCKER_TYPES),
|
||||
Step(13, "CORS配置检查", "检查并配置 CORS 设置", DOCKER_TYPES),
|
||||
Step(14, "端口检查与分配", "检查端口占用并分配端口", DOCKER_TYPES),
|
||||
Step(15, "改造后构建与启动", "Docker项目构建镜像启动容器;非Docker项目手动构建并启动改造版", ALL_TYPES),
|
||||
Step(16, "健康验证", "验证服务健康、面板UI审计0违规、全功能走查无报错", ALL_TYPES),
|
||||
Step(17, "产品截图与全功能录屏", "采集当前产品全套高清界面截图(中性文件名)和全功能演示录屏;内部基线素材不进入交付物", ALL_TYPES),
|
||||
Step(18, "标准化README与版本归档", "编写标准化 README(引用最终素材、含架构图与部署教程);不主动写来源/致谢宣传,并按授权决定是否提交", ALL_TYPES),
|
||||
Step(19, "交付物核查", "按7项交付物清单与7条验收标准做交付前阻断式核查", ALL_TYPES),
|
||||
]
|
||||
|
||||
# 旧版本步骤名 → 新版本步骤名(用于历史状态迁移)
|
||||
STEP_NAME_ALIASES = {
|
||||
"构建与启动": "改造后构建与启动",
|
||||
"素材录制归档": "产品截图与全功能录屏",
|
||||
"原始项目基线启动": "初始版本基线启动(内部)",
|
||||
"改造前基线截图": "初始界面基线截图(内部)",
|
||||
"原作者信息与版权痕迹合规处理": "版权与第三方标识规范",
|
||||
"页脚与导航增强": "页脚、导航与社区链接",
|
||||
"改造后截图与全功能录屏": "产品截图与全功能录屏",
|
||||
"标准化README与提交": "标准化README与版本归档",
|
||||
}
|
||||
|
||||
def __init__(self, project_dir: str):
|
||||
self.project_dir = Path(project_dir)
|
||||
self.claude_dir = self.project_dir / ".Codex"
|
||||
self.state_file = self.claude_dir / "workflow_state.json"
|
||||
self.error_file = self.claude_dir / "error_log.md"
|
||||
self.project_info: Optional[ProjectInfo] = None
|
||||
self.steps: List[Step] = []
|
||||
|
||||
def init_project(self, name: str, git_url: str, port_range: Optional[str] = None):
|
||||
"""初始化项目信息"""
|
||||
self.project_info = ProjectInfo(
|
||||
name=name,
|
||||
directory=str(self.project_dir),
|
||||
git_url=git_url,
|
||||
port_range=port_range
|
||||
)
|
||||
self.steps = [Step(s.id, s.name, s.description, s.applicable_types) for s in self.CORE_STEPS]
|
||||
self._ensure_directories()
|
||||
self._save_state()
|
||||
|
||||
def _ensure_directories(self):
|
||||
"""确保必要的目录存在"""
|
||||
self.claude_dir.mkdir(exist_ok=True)
|
||||
(self.claude_dir / 'reports').mkdir(exist_ok=True)
|
||||
|
||||
def _save_state(self):
|
||||
"""保存状态到JSON文件"""
|
||||
def enum_to_value(obj):
|
||||
if isinstance(obj, Enum):
|
||||
return obj.value
|
||||
if isinstance(obj, list):
|
||||
return [enum_to_value(item) for item in obj]
|
||||
if isinstance(obj, dict):
|
||||
return {k: enum_to_value(v) for k, v in obj.items()}
|
||||
return obj
|
||||
|
||||
project_info_dict = None
|
||||
if self.project_info:
|
||||
project_info_dict = asdict(self.project_info)
|
||||
project_info_dict["project_type"] = project_info_dict["project_type"].value
|
||||
project_info_dict["tech_stack"] = [ts.value for ts in project_info_dict["tech_stack"]]
|
||||
|
||||
state = {
|
||||
"project_info": project_info_dict,
|
||||
"steps": [
|
||||
{
|
||||
"id": s.id,
|
||||
"name": s.name,
|
||||
"status": s.status.value,
|
||||
"start_time": s.start_time,
|
||||
"end_time": s.end_time,
|
||||
"notes": s.notes,
|
||||
"error_info": s.error_info
|
||||
}
|
||||
for s in self.steps
|
||||
],
|
||||
"last_updated": datetime.now().isoformat()
|
||||
}
|
||||
with open(self.state_file, 'w', encoding='utf-8') as f:
|
||||
json.dump(state, f, ensure_ascii=False, indent=2)
|
||||
|
||||
def load_state(self) -> bool:
|
||||
"""从JSON文件加载状态"""
|
||||
if not self.state_file.exists():
|
||||
return False
|
||||
|
||||
try:
|
||||
with open(self.state_file, 'r', encoding='utf-8') as f:
|
||||
state = json.load(f)
|
||||
|
||||
if state.get("project_info"):
|
||||
info = state["project_info"]
|
||||
self.project_info = ProjectInfo(
|
||||
name=info["name"],
|
||||
directory=info["directory"],
|
||||
git_url=info["git_url"],
|
||||
port_range=info.get("port_range"),
|
||||
project_type=ProjectType(info.get("project_type", "unknown")),
|
||||
tech_stack=[TechStack(ts) for ts in info.get("tech_stack", [])]
|
||||
)
|
||||
|
||||
# 以新 CORE_STEPS 为权威骨架;旧状态按“步骤名(含别名)”映射进度,
|
||||
# 避免编号错位导致已完成的步骤被重置。
|
||||
old_steps = state.get("steps", [])
|
||||
name_to_state: Dict[str, dict] = {}
|
||||
for s in old_steps:
|
||||
canon = self.STEP_NAME_ALIASES.get(s.get("name", ""), s.get("name", ""))
|
||||
name_to_state[canon] = s
|
||||
|
||||
self.steps = []
|
||||
for cs in self.CORE_STEPS:
|
||||
prev = name_to_state.get(cs.name)
|
||||
if prev is not None:
|
||||
self.steps.append(Step(
|
||||
id=cs.id,
|
||||
name=cs.name,
|
||||
description=cs.description,
|
||||
applicable_types=cs.applicable_types,
|
||||
status=StepStatus(prev.get("status", StepStatus.PENDING.value)),
|
||||
start_time=prev.get("start_time"),
|
||||
end_time=prev.get("end_time"),
|
||||
notes=prev.get("notes", ""),
|
||||
error_info=prev.get("error_info"),
|
||||
))
|
||||
else:
|
||||
# 新增步骤默认未开始
|
||||
self.steps.append(Step(cs.id, cs.name, cs.description, cs.applicable_types))
|
||||
|
||||
return True
|
||||
except Exception as e:
|
||||
self._log_error(f"加载状态失败: {e}")
|
||||
return False
|
||||
|
||||
def get_current_step(self) -> Optional[Step]:
|
||||
"""获取当前步骤"""
|
||||
for step in self.steps:
|
||||
if step.status == StepStatus.IN_PROGRESS:
|
||||
return step
|
||||
if step.status == StepStatus.PENDING:
|
||||
return step
|
||||
return None
|
||||
|
||||
def start_step(self, step_id: int) -> bool:
|
||||
"""开始执行步骤"""
|
||||
step = next((s for s in self.steps if s.id == step_id), None)
|
||||
if not step:
|
||||
return False
|
||||
|
||||
# 检查步骤是否适用于当前项目类型
|
||||
if self.project_info and self.project_info.project_type != ProjectType.UNKNOWN:
|
||||
if self.project_info.project_type not in step.applicable_types:
|
||||
step.status = StepStatus.SKIPPED
|
||||
step.notes = f"步骤不适用于项目类型: {self.project_info.project_type.value}"
|
||||
self._save_state()
|
||||
return True
|
||||
|
||||
step.status = StepStatus.IN_PROGRESS
|
||||
step.start_time = datetime.now().isoformat()
|
||||
self._save_state()
|
||||
return True
|
||||
|
||||
def complete_step(self, step_id: int, notes: str = ""):
|
||||
"""完成步骤"""
|
||||
step = next((s for s in self.steps if s.id == step_id), None)
|
||||
if step:
|
||||
step.status = StepStatus.COMPLETED
|
||||
step.end_time = datetime.now().isoformat()
|
||||
step.notes = notes
|
||||
self._save_state()
|
||||
|
||||
def fail_step(self, step_id: int, error_info: str):
|
||||
"""标记步骤失败"""
|
||||
step = next((s for s in self.steps if s.id == step_id), None)
|
||||
if step:
|
||||
step.status = StepStatus.FAILED
|
||||
step.end_time = datetime.now().isoformat()
|
||||
step.error_info = error_info
|
||||
self._save_state()
|
||||
self._log_error(f"步骤 {step_id} ({step.name}) 失败: {error_info}")
|
||||
|
||||
def skip_step(self, step_id: int, reason: str = ""):
|
||||
"""跳过步骤"""
|
||||
step = next((s for s in self.steps if s.id == step_id), None)
|
||||
if step:
|
||||
step.status = StepStatus.SKIPPED
|
||||
step.notes = reason
|
||||
self._save_state()
|
||||
|
||||
def _log_error(self, message: str):
|
||||
"""记录错误到错误日志"""
|
||||
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||
error_entry = f"\n## [{timestamp}]\n{message}\n\n"
|
||||
|
||||
with open(self.error_file, 'a', encoding='utf-8') as f:
|
||||
f.write(error_entry)
|
||||
|
||||
def get_progress(self) -> Dict:
|
||||
"""获取进度信息"""
|
||||
total = len(self.steps)
|
||||
completed = sum(1 for s in self.steps if s.status == StepStatus.COMPLETED)
|
||||
failed = sum(1 for s in self.steps if s.status == StepStatus.FAILED)
|
||||
skipped = sum(1 for s in self.steps if s.status == StepStatus.SKIPPED)
|
||||
pending = sum(1 for s in self.steps if s.status == StepStatus.PENDING)
|
||||
|
||||
return {
|
||||
"total": total,
|
||||
"completed": completed,
|
||||
"failed": failed,
|
||||
"skipped": skipped,
|
||||
"pending": pending,
|
||||
"percentage": (completed / total * 100) if total > 0 else 0
|
||||
}
|
||||
|
||||
def can_resume(self) -> Tuple[bool, Optional[int]]:
|
||||
"""检查是否可以恢复执行"""
|
||||
if not self.state_file.exists():
|
||||
return False, None
|
||||
|
||||
self.load_state()
|
||||
|
||||
# 检查是否有失败的步骤
|
||||
failed_step = next((s for s in self.steps if s.status == StepStatus.FAILED), None)
|
||||
if failed_step:
|
||||
return True, failed_step.id
|
||||
|
||||
# 检查是否有进行中的步骤
|
||||
in_progress = next((s for s in self.steps if s.status == StepStatus.IN_PROGRESS), None)
|
||||
if in_progress:
|
||||
return True, in_progress.id
|
||||
|
||||
# 检查是否有未开始的步骤
|
||||
pending = next((s for s in self.steps if s.status == StepStatus.PENDING), None)
|
||||
if pending:
|
||||
return True, pending.id
|
||||
|
||||
return False, None
|
||||
|
||||
def generate_report(self) -> str:
|
||||
"""生成执行报告"""
|
||||
progress = self.get_progress()
|
||||
|
||||
report = f"""# 工作流执行报告
|
||||
|
||||
## 项目信息
|
||||
- **项目名称**: {self.project_info.name if self.project_info else 'N/A'}
|
||||
- **项目目录**: {self.project_info.directory if self.project_info else 'N/A'}
|
||||
- **Git地址**: {self.project_info.git_url if self.project_info else 'N/A'}
|
||||
- **项目类型**: {self.project_info.project_type.value if self.project_info else 'N/A'}
|
||||
|
||||
## 执行进度
|
||||
- **总步骤**: {progress['total']}
|
||||
- **已完成**: {progress['completed']}
|
||||
- **失败**: {progress['failed']}
|
||||
- **跳过**: {progress['skipped']}
|
||||
- **待执行**: {progress['pending']}
|
||||
- **完成度**: {progress['percentage']:.1f}%
|
||||
|
||||
## 步骤详情
|
||||
"""
|
||||
for step in self.steps:
|
||||
report += f"\n### 步骤 {step.id}: {step.name}\n"
|
||||
report += f"- **状态**: {step.status.value}\n"
|
||||
if step.start_time:
|
||||
report += f"- **开始时间**: {step.start_time}\n"
|
||||
if step.end_time:
|
||||
report += f"- **完成时间**: {step.end_time}\n"
|
||||
if step.notes:
|
||||
report += f"- **说明**: {step.notes}\n"
|
||||
if step.error_info:
|
||||
report += f"- **错误**: {step.error_info}\n"
|
||||
|
||||
return report
|
||||
|
||||
# 技术栈检测工具
|
||||
class TechStackDetector:
|
||||
"""技术栈检测器"""
|
||||
|
||||
DETECTION_RULES = {
|
||||
TechStack.JAVA_SPRING: ["pom.xml", "build.gradle", "src/main/java"],
|
||||
TechStack.NODE_EXPRESS: ["package.json", "app.js", "server.js"],
|
||||
TechStack.NODE_NEST: ["nest-cli.json", "@nestjs"],
|
||||
TechStack.PYTHON_DJANGO: ["manage.py", "django", "requirements.txt"],
|
||||
TechStack.PYTHON_FASTAPI: ["fastapi", "main.py"],
|
||||
TechStack.GO_GIN: ["go.mod", "gin"],
|
||||
TechStack.GO_FIBER: ["go.mod", "fiber"],
|
||||
TechStack.REACT: ["package.json", "react", "src/App.js", "src/App.tsx", "vite.config.ts", "vite.config.js"],
|
||||
TechStack.VUE: ["package.json", "vue", "vue.config.js", "vite.config.ts", "vite.config.js"],
|
||||
TechStack.ANGULAR: ["angular.json", "@angular"],
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def detect(cls, project_dir: str) -> List[TechStack]:
|
||||
"""检测项目技术栈"""
|
||||
detected = []
|
||||
project_path = Path(project_dir)
|
||||
|
||||
for tech, indicators in cls.DETECTION_RULES.items():
|
||||
found = False
|
||||
for indicator in indicators:
|
||||
# 递归检查文件存在
|
||||
for _ in project_path.rglob(indicator):
|
||||
found = True
|
||||
break
|
||||
if found: break
|
||||
|
||||
# 递归检查文件内容
|
||||
for file in project_path.rglob("*.json"):
|
||||
try:
|
||||
with open(file, 'r', encoding='utf-8') as f:
|
||||
content = f.read()
|
||||
if indicator in content:
|
||||
found = True
|
||||
break
|
||||
except:
|
||||
pass
|
||||
if found: break
|
||||
|
||||
if found and tech not in detected:
|
||||
detected.append(tech)
|
||||
|
||||
return detected if detected else [TechStack.UNKNOWN]
|
||||
|
||||
# 项目类型判断器
|
||||
class ProjectTypeDetector:
|
||||
"""项目类型检测器"""
|
||||
|
||||
@classmethod
|
||||
def detect(cls, project_dir: str, tech_stacks: List[TechStack]) -> ProjectType:
|
||||
"""检测项目类型"""
|
||||
project_path = Path(project_dir)
|
||||
|
||||
# 检查桌面应用特征
|
||||
desktop_indicators = ["electron", "qt", "swing", "wpf", ".desktop"]
|
||||
for indicator in desktop_indicators:
|
||||
if any(indicator in str(tech.value).lower() for tech in tech_stacks):
|
||||
return ProjectType.DESKTOP
|
||||
|
||||
# 检查移动应用特征
|
||||
mobile_indicators = ["react-native", "flutter", "android", "ios"]
|
||||
for indicator in mobile_indicators:
|
||||
if any(indicator in str(tech.value).lower() for tech in tech_stacks):
|
||||
return ProjectType.MOBILE
|
||||
|
||||
# 检查Docker配置
|
||||
if (project_path / "Dockerfile").exists() or (project_path / "docker-compose.yml").exists():
|
||||
# 检查微服务特征
|
||||
if (project_path / "services").exists() or len(list(project_path.glob("**/Dockerfile"))) > 1:
|
||||
return ProjectType.DOCKER_MICROSERVICES
|
||||
return ProjectType.DOCKER_WEB
|
||||
|
||||
# 检查API服务特征
|
||||
if any(tech in [TechStack.NODE_EXPRESS, TechStack.NODE_NEST,
|
||||
TechStack.PYTHON_FASTAPI, TechStack.GO_GIN] for tech in tech_stacks):
|
||||
return ProjectType.DOCKER_API
|
||||
|
||||
return ProjectType.UNKNOWN
|
||||
|
||||
def main():
|
||||
"""命令行入口"""
|
||||
if len(sys.argv) < 2:
|
||||
print("Usage: workflow_manager.py <command> [args...]")
|
||||
print("Commands: init, status, resume, report")
|
||||
sys.exit(1)
|
||||
|
||||
command = sys.argv[1]
|
||||
|
||||
if command == "init":
|
||||
if len(sys.argv) < 5:
|
||||
print("Usage: workflow_manager.py init <project_dir> <name> <git_url> [port_range]")
|
||||
sys.exit(1)
|
||||
project_dir = sys.argv[2]
|
||||
name = sys.argv[3]
|
||||
git_url = sys.argv[4]
|
||||
port_range = sys.argv[5] if len(sys.argv) > 5 else None
|
||||
|
||||
manager = WorkflowManager(project_dir)
|
||||
manager.init_project(name, git_url, port_range)
|
||||
print(f"项目已初始化: {name}")
|
||||
|
||||
elif command == "start_step":
|
||||
if len(sys.argv) < 4:
|
||||
print("Usage: workflow_manager.py start_step <project_dir> <step_id>")
|
||||
sys.exit(1)
|
||||
project_dir = sys.argv[2]
|
||||
step_id = int(sys.argv[3])
|
||||
|
||||
manager = WorkflowManager(project_dir)
|
||||
if manager.load_state():
|
||||
if manager.start_step(step_id):
|
||||
print(f"步骤 {step_id} 已开始")
|
||||
else:
|
||||
print(f"步骤 {step_id} 开始失败")
|
||||
else:
|
||||
print("未找到状态文件")
|
||||
|
||||
elif command == "complete_step":
|
||||
if len(sys.argv) < 4:
|
||||
print("Usage: workflow_manager.py complete_step <project_dir> <step_id> [notes]")
|
||||
sys.exit(1)
|
||||
project_dir = sys.argv[2]
|
||||
step_id = int(sys.argv[3])
|
||||
notes = sys.argv[4] if len(sys.argv) > 4 else ""
|
||||
|
||||
manager = WorkflowManager(project_dir)
|
||||
if manager.load_state():
|
||||
manager.complete_step(step_id, notes)
|
||||
print(f"步骤 {step_id} 已完成")
|
||||
else:
|
||||
print("未找到状态文件")
|
||||
|
||||
elif command == "detect":
|
||||
if len(sys.argv) < 3:
|
||||
print("Usage: workflow_manager.py detect <project_dir>")
|
||||
sys.exit(1)
|
||||
project_dir = sys.argv[2]
|
||||
|
||||
manager = WorkflowManager(project_dir)
|
||||
if manager.load_state():
|
||||
tech_stack = TechStackDetector.detect(project_dir)
|
||||
project_type = ProjectTypeDetector.detect(project_dir, tech_stack)
|
||||
|
||||
manager.project_info.tech_stack = tech_stack
|
||||
manager.project_info.project_type = project_type
|
||||
manager.start_step(2)
|
||||
manager.complete_step(2, f"Detected tech stack: {[ts.value for ts in tech_stack]}, project type: {project_type.value}")
|
||||
print(f"Tech stack: {[ts.value for ts in tech_stack]}, Project type: {project_type.value}")
|
||||
else:
|
||||
print("未找到状态文件")
|
||||
|
||||
elif command == "status":
|
||||
if len(sys.argv) < 3:
|
||||
print("Usage: workflow_manager.py status <project_dir>")
|
||||
sys.exit(1)
|
||||
project_dir = sys.argv[2]
|
||||
|
||||
manager = WorkflowManager(project_dir)
|
||||
if manager.load_state():
|
||||
progress = manager.get_progress()
|
||||
print(f"进度: {progress['completed']}/{progress['total']} ({progress['percentage']:.1f}%)")
|
||||
current = manager.get_current_step()
|
||||
if current:
|
||||
print(f"当前步骤: 步骤 {current.id} - {current.name}")
|
||||
else:
|
||||
print("未找到状态文件")
|
||||
|
||||
elif command == "resume":
|
||||
if len(sys.argv) < 3:
|
||||
print("Usage: workflow_manager.py resume <project_dir>")
|
||||
sys.exit(1)
|
||||
project_dir = sys.argv[2]
|
||||
|
||||
manager = WorkflowManager(project_dir)
|
||||
can_resume, step_id = manager.can_resume()
|
||||
if can_resume:
|
||||
print(f"可以从步骤 {step_id} 恢复执行")
|
||||
else:
|
||||
print("无法恢复,请重新初始化")
|
||||
|
||||
elif command == "report":
|
||||
if len(sys.argv) < 3:
|
||||
print("Usage: workflow_manager.py report <project_dir>")
|
||||
sys.exit(1)
|
||||
project_dir = sys.argv[2]
|
||||
|
||||
manager = WorkflowManager(project_dir)
|
||||
if manager.load_state():
|
||||
print(manager.generate_report())
|
||||
else:
|
||||
print("未找到状态文件")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user