1use std::path::Path;
6use tokio::process::Command;
7
8pub const CLAUDE_CODE_WORKFLOW: &str = r#"name: Claude Code Review
10
11on:
12 pull_request:
13 types: [opened, synchronize, reopened]
14 issue_comment:
15 types: [created]
16
17permissions:
18 contents: read
19 pull-requests: write
20 issues: write
21
22jobs:
23 claude-review:
24 runs-on: ubuntu-latest
25 if: |
26 github.event_name == 'pull_request' ||
27 (github.event_name == 'issue_comment' && contains(github.event.comment.body, '@claude'))
28
29 steps:
30 - name: Checkout code
31 uses: actions/checkout@v4
32 with:
33 fetch-depth: 0
34
35 - name: Run Claude Code Review
36 env:
37 ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
38 GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
39 run: |
40 echo "Claude Code Review placeholder"
41"#;
42
43#[derive(Debug, Clone)]
45pub struct GitHubCLIStatus {
46 pub installed: bool,
48 pub authenticated: bool,
50}
51
52pub async fn check_github_cli() -> GitHubCLIStatus {
54 let output = Command::new("gh").args(["auth", "status"]).output().await;
55
56 match output {
57 Ok(output) => {
58 let stdout = String::from_utf8_lossy(&output.stdout);
59 let stderr = String::from_utf8_lossy(&output.stderr);
60 let combined = format!("{}{}", stdout, stderr);
61
62 if output.status.success() || combined.contains("Logged in") {
63 GitHubCLIStatus {
64 installed: true,
65 authenticated: true,
66 }
67 } else if combined.contains("gh auth login") {
68 GitHubCLIStatus {
69 installed: true,
70 authenticated: false,
71 }
72 } else {
73 GitHubCLIStatus {
74 installed: false,
75 authenticated: false,
76 }
77 }
78 }
79 Err(_) => GitHubCLIStatus {
80 installed: false,
81 authenticated: false,
82 },
83 }
84}
85
86#[derive(Debug, Clone)]
88pub struct SetupWorkflowResult {
89 pub success: bool,
91 pub message: String,
93 pub workflow_path: Option<String>,
95}
96
97pub async fn setup_github_workflow(project_dir: &Path) -> SetupWorkflowResult {
99 let workflows_dir = project_dir.join(".github").join("workflows");
100 let workflow_path = workflows_dir.join("claude-code.yml");
101
102 let git_dir = project_dir.join(".git");
104 if !git_dir.exists() {
105 return SetupWorkflowResult {
106 success: false,
107 message: "不是 git 仓库,请先运行 git init".to_string(),
108 workflow_path: None,
109 };
110 }
111
112 if !workflows_dir.exists() {
114 if let Err(e) = tokio::fs::create_dir_all(&workflows_dir).await {
115 return SetupWorkflowResult {
116 success: false,
117 message: format!("创建目录失败: {}", e),
118 workflow_path: None,
119 };
120 }
121 }
122
123 if workflow_path.exists() {
125 return SetupWorkflowResult {
126 success: false,
127 message: "GitHub 工作流已存在".to_string(),
128 workflow_path: Some(workflow_path.to_string_lossy().to_string()),
129 };
130 }
131
132 if let Err(e) = tokio::fs::write(&workflow_path, CLAUDE_CODE_WORKFLOW).await {
134 return SetupWorkflowResult {
135 success: false,
136 message: format!("写入工作流文件失败: {}", e),
137 workflow_path: None,
138 };
139 }
140
141 SetupWorkflowResult {
142 success: true,
143 message: "GitHub Actions 工作流创建成功!".to_string(),
144 workflow_path: Some(workflow_path.to_string_lossy().to_string()),
145 }
146}