qtcloud_devops_cli/
contract.rs1pub use quanttide_devops::contract::{
3 detect_language_by_files, normalize_version, read_all_config_versions, validate_version,
4 BuildTool, Contract, Language, Pipeline, Platform, Registry, Scope, SourceControl, SourceType,
5 StageBuild, StageRelease, StageTest, VersionSource,
6};
7pub use quanttide_devops::source::git::{GitSourceError, VersionStatus};
8
9use std::path::Path;
10
11pub fn load(repo_path: &Path) -> Contract {
16 match quanttide_devops::contract::load(repo_path) {
17 Ok(c) => c,
18 Err(e) => {
19 eprintln!(" ℹ contract.yaml: {},使用默认契约", e);
20 Contract::default()
21 }
22 }
23}
24
25pub fn load_scopes(repo_path: &Path) -> Vec<Scope> {
26 load(repo_path).scopes
27}
28
29pub fn detect_by_files(dir: &Path) -> Language {
30 detect_language_by_files(dir)
31}
32
33pub fn version_status(repo_path: &Path, scope: &Scope) -> VersionStatus {
39 quanttide_devops::source::git::version_status(repo_path, scope).unwrap_or_else(|e| {
40 eprintln!(" ⚠ 版本状态检查失败: {}", e);
41 VersionStatus {
42 tag_version: None,
43 config_version: None,
44 consistent: false,
45 config_files: vec![],
46 }
47 })
48}
49
50pub fn status(repo_path: &Path) {
52 let mut stdout = std::io::stdout();
53 status_to(&mut stdout, repo_path).ok();
54}
55
56pub fn status_to(writer: &mut impl std::io::Write, repo_path: &Path) -> std::io::Result<()> {
58 let contract_path = repo_path.join(".quanttide/devops/contract.yaml");
59 let exists = contract_path.exists();
60
61 let c = load(repo_path);
62
63 writeln!(writer, "契约状态")?;
64 writeln!(writer, "{}", "-".repeat(40))?;
65
66 if exists {
67 writeln!(writer, " 配置文件: {}", contract_path.display())?;
68 writeln!(writer, " 状态: ✅ 已加载")?;
69 } else {
70 writeln!(writer, " 配置文件: 未找到,使用默认契约")?;
71 writeln!(writer, " 状态: ⚠ 默认配置")?;
72 }
73 writeln!(writer)?;
74
75 writeln!(writer, " Stages:")?;
77 let b = &c.stages.build;
78 writeln!(
79 writer,
80 " build: {}",
81 b.command.as_deref().unwrap_or("—")
82 )?;
83 let t = &c.stages.test;
84 writeln!(
85 writer,
86 " test: {}(阈值 {}%)",
87 t.command.as_deref().unwrap_or("—"),
88 t.threshold
89 )?;
90 let r = &c.stages.release;
91 writeln!(
92 writer,
93 " release: {}(pre_publish: {:?})",
94 r.changelog, r.pre_publish
95 )?;
96 writeln!(writer)?;
97
98 writeln!(writer, " Platform:")?;
100 writeln!(
101 writer,
102 " source_control: {:?}",
103 c.platform.source_control
104 )?;
105 writeln!(writer, " pipeline: {:?}", c.platform.pipeline)?;
106 writeln!(
107 writer,
108 " artifact_registry: {}",
109 c.platform.artifact_registry
110 )?;
111 writeln!(writer)?;
112
113 writeln!(writer, " Sources:")?;
115 writeln!(
116 writer,
117 " version: {:?} {:?}",
118 c.sources.version.source_type, c.sources.version.path
119 )?;
120 writeln!(writer)?;
121
122 writeln!(writer, " Scopes: {} 个", c.scopes.len())?;
124 if c.scopes.is_empty() {
125 writeln!(writer, " 未定义 scope")?;
126 } else {
127 for s in &c.scopes {
128 writeln!(
129 writer,
130 " {:<12} dir: {:<24} {} / {}",
131 s.name,
132 s.dir,
133 s.language.as_str(),
134 s.build_tool.as_str()
135 )?;
136 }
137 }
138 Ok(())
139}
140
141#[cfg(test)]
142mod tests {
143 use super::*;
144
145 #[test]
146 fn test_version_status_git_error_returns_fallback() {
147 let scope = Scope {
148 name: "test".into(),
149 dir: ".".into(),
150 language: Language::Rust,
151 framework: String::new(),
152 build_tool: BuildTool::Cargo,
153 registry: Registry::None,
154 release: StageRelease::default(),
155 test_threshold: None,
156 ci_workflow: None,
157 };
158 let vs = version_status(Path::new("/nonexistent"), &scope);
159 assert!(!vs.consistent);
160 assert_eq!(vs.tag_version, None);
161 assert_eq!(vs.config_version, None);
162 assert!(vs.config_files.is_empty());
163 }
164
165 #[test]
166 fn test_status_to_with_contract() {
167 let d = tempfile::tempdir().unwrap();
168 let contract_dir = d.path().join(".quanttide/devops");
170 std::fs::create_dir_all(&contract_dir).unwrap();
171 std::fs::write(
172 contract_dir.join("contract.yaml"),
173 "scopes:\n test:\n dir: .\n language: rust\n",
174 )
175 .unwrap();
176 let mut buf = Vec::new();
177 status_to(&mut buf, d.path()).unwrap();
178 let out = String::from_utf8_lossy(&buf);
179 assert!(out.contains("✅ 已加载"));
180 assert!(out.contains("test"));
181 assert!(out.contains("rust"));
182 }
183
184 #[test]
185 fn test_status_to_without_contract() {
186 let d = tempfile::tempdir().unwrap();
187 let mut buf = Vec::new();
188 status_to(&mut buf, d.path()).unwrap();
189 let out = String::from_utf8_lossy(&buf);
190 assert!(out.contains("⚠ 默认配置"));
191 assert!(out.contains("未定义 scope"));
192 }
193}