Skip to content

Commit 2d277fd

Browse files
committed
fix(ci): 供应真实原生工具链并修正 C Terraform Elm 验收
1 parent ba05900 commit 2d277fd

5 files changed

Lines changed: 276 additions & 80 deletions

File tree

‎.github/workflows/ci.yml‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ jobs:
2424
cargo +1.85.0 check --locked --workspace --all-targets --features wasm-precheck
2525
gate:
2626
# ubuntu-latest 预装稳定版 Rust;Cargo.toml 声明 rust-version = 1.85 / edition = 2024。
27-
runs-on: ubuntu-latest
27+
runs-on: ubuntu-24.04
2828
env:
2929
# Linux 完整 DWARF 测试制品会超出 SDK 的 256 MiB 宿主制品读取预算。
3030
# 仅去掉测试调试符号;保留 debug assertions 和产品读取上限。
@@ -52,6 +52,10 @@ jobs:
5252
- name: Verify crate layering debt does not grow
5353
run: python3 scripts/check_layering.py
5454

55+
- name: Provision real acceptance toolchains
56+
# 仅安装到临时 CI runner;缺工具不能冒充负例检出或通过。
57+
run: bash scripts/setup_ci_native_toolchains.sh
58+
5559
- name: Build workspace
5660
run: cargo build --workspace --all-targets
5761

‎crates/codeguard-cli/tests/c_six_category_acceptance.rs‎

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use std::process::Command;
22

33
fn ensure_clean_dir(name: &str) -> std::path::PathBuf {
4-
let tmp = std::env::temp_dir().join(name);
4+
let tmp = std::env::temp_dir().join(format!("{name}-{}", std::process::id()));
55
let _ = std::fs::remove_dir_all(&tmp);
66
std::fs::create_dir_all(&tmp).unwrap();
77
tmp
@@ -12,8 +12,18 @@ fn valid_c_samples_pass_compile() {
1212
let tmp = ensure_clean_dir("c-valid-test");
1313
std::fs::write(tmp.join("main.c"), "#include <stdio.h>\n\n/** Adds two integers. */\nint add(int a, int b) {\n return a + b;\n}\n\nint main(void) {\n printf(\"%d\\n\", add(1, 2));\n return 0;\n}\n").unwrap();
1414

15+
// C11 不允许隐式函数声明;统一正负例标准,避免旧 GCC 仅警告后返回成功。
1516
let output = Command::new("cc")
16-
.args(["-Wall", "-Wextra", "-c", "main.c", "-o", "main.o"])
17+
.args([
18+
"-std=c11",
19+
"-Wall",
20+
"-Wextra",
21+
"-Werror=implicit-function-declaration",
22+
"-c",
23+
"main.c",
24+
"-o",
25+
"main.o",
26+
])
1727
.current_dir(&tmp)
1828
.output()
1929
.expect("cc 不可用");
@@ -35,8 +45,18 @@ fn invalid_c_samples_are_detected() {
3545
)
3646
.unwrap();
3747

48+
// C11 不允许隐式函数声明;统一正负例标准,避免旧 GCC 仅警告后返回成功。
3849
let output = Command::new("cc")
39-
.args(["-Wall", "-Wextra", "-c", "main.c", "-o", "main.o"])
50+
.args([
51+
"-std=c11",
52+
"-Wall",
53+
"-Wextra",
54+
"-Werror=implicit-function-declaration",
55+
"-c",
56+
"main.c",
57+
"-o",
58+
"main.o",
59+
])
4060
.current_dir(&tmp)
4161
.output()
4262
.expect("cc 不可用");
@@ -57,8 +77,18 @@ fn formatting_cannot_impersonate_comment_checking() {
5777
// 格式正确但缺少文档——编译器不检查文档
5878
std::fs::write(tmp.join("main.c"), "#include <stdio.h>\n\nint undocumented(int a) {\n return a * 2;\n}\n\n/** Has docs. */\nint documented(int a) {\n return a + 1;\n}\n\nint main(void) {\n printf(\"%d\\n\", undocumented(5) + documented(3));\n return 0;\n}\n").unwrap();
5979

80+
// C11 不允许隐式函数声明;统一正负例标准,避免旧 GCC 仅警告后返回成功。
6081
let output = Command::new("cc")
61-
.args(["-Wall", "-Wextra", "-c", "main.c", "-o", "main.o"])
82+
.args([
83+
"-std=c11",
84+
"-Wall",
85+
"-Wextra",
86+
"-Werror=implicit-function-declaration",
87+
"-c",
88+
"main.c",
89+
"-o",
90+
"main.o",
91+
])
6292
.current_dir(&tmp)
6393
.output()
6494
.expect("cc 不可用");

‎crates/codeguard-cli/tests/elm_six_category_acceptance.rs‎

Lines changed: 31 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,21 @@ fn ensure_clean_dir(name: &str) -> std::path::PathBuf {
77
let tmp = std::env::temp_dir().join(name);
88
let _ = std::fs::remove_dir_all(&tmp);
99
std::fs::create_dir_all(&tmp).unwrap();
10+
// 固定实际使用的源码目录与依赖,避免交互初始化失败后用环境错误冒充负例。
11+
std::fs::write(
12+
tmp.join("elm.json"),
13+
r#"{
14+
"type": "application",
15+
"source-directories": ["."],
16+
"elm-version": "0.19.1",
17+
"dependencies": {
18+
"direct": {"elm/core": "1.0.5", "elm/html": "1.0.0"},
19+
"indirect": {"elm/json": "1.1.3", "elm/virtual-dom": "1.0.3"}
20+
},
21+
"test-dependencies": {"direct": {}, "indirect": {}}
22+
}"#,
23+
)
24+
.unwrap();
1025
tmp
1126
}
1227

@@ -17,19 +32,17 @@ fn valid_elm_samples_pass_parse() {
1732
// 使用 elm make 直接编译单文件
1833
std::fs::write(tmp.join("Main.elm"), "module Main exposing (main)\n\nimport Html exposing (text)\n\n-- Adds two numbers.\nadd : Int -> Int -> Int\nadd a b =\n a + b\n\nmain =\n text (String.fromInt (add 1 2))\n").unwrap();
1934

20-
// 初始化 elm 项目
21-
let _ = Command::new("sh")
22-
.args(["-c", "echo 'Y' | elm init"])
23-
.current_dir(&tmp)
24-
.output();
25-
2635
let output = Command::new("elm")
2736
.args(["make", "Main.elm", "--output=/dev/null"])
2837
.current_dir(&tmp)
2938
.output()
3039
.expect("elm 不可用");
3140

32-
assert!(output.status.success(), "有效 Elm 代码应通过编译: {:?}", output);
41+
assert!(
42+
output.status.success(),
43+
"有效 Elm 代码应通过编译: {:?}",
44+
output
45+
);
3346
std::fs::remove_dir_all(&tmp).unwrap();
3447
}
3548

@@ -39,18 +52,18 @@ fn invalid_elm_samples_are_detected() {
3952
let tmp = ensure_clean_dir("elm-invalid-test");
4053
std::fs::write(tmp.join("Main.elm"), "module Main exposing (main)\n\nimport Html exposing (text)\n\nbroken : Int -> Int -> Int\nbroken a b =\n a + b + notDefinedAnywhere\n\nmain =\n text (String.fromInt (broken 1 2))\n").unwrap();
4154

42-
let _ = Command::new("sh")
43-
.args(["-c", "echo 'Y' | elm init"])
44-
.current_dir(&tmp)
45-
.output();
46-
4755
let output = Command::new("elm")
48-
.args(["make", "Main.elm", "--output=/dev/null"])
56+
.args(["make", "Main.elm", "--output=/dev/null", "--report=json"])
4957
.current_dir(&tmp)
5058
.output()
5159
.expect("elm 不可用");
5260

5361
assert!(!output.status.success(), "未定义变量应被检出");
62+
let diagnostic = String::from_utf8_lossy(&output.stderr);
63+
assert!(
64+
diagnostic.contains("NAMING ERROR") && diagnostic.contains("notDefinedAnywhere"),
65+
"必须实际检出目标未定义变量,依赖或初始化失败不能作为负例证据: {diagnostic}"
66+
);
5467
std::fs::remove_dir_all(&tmp).unwrap();
5568
}
5669

@@ -61,17 +74,16 @@ fn formatting_cannot_impersonate_comment_checking() {
6174
// 格式正确但缺少注释
6275
std::fs::write(tmp.join("Main.elm"), "module Main exposing (main)\n\nimport Html exposing (text)\n\nundocumented : Int -> Int\nundocumented a =\n a * 2\n\n-- Has docs.\ndocumented : Int -> Int\ndocumented a =\n a + 1\n\nmain =\n text (String.fromInt (undocumented 5 + documented 3))\n").unwrap();
6376

64-
let _ = Command::new("sh")
65-
.args(["-c", "echo 'Y' | elm init"])
66-
.current_dir(&tmp)
67-
.output();
68-
6977
let output = Command::new("elm")
7078
.args(["make", "Main.elm", "--output=/dev/null"])
7179
.current_dir(&tmp)
7280
.output()
7381
.expect("elm 不可用");
7482

75-
assert!(output.status.success(), "Elm 应通过(语法正确),但注释合规需另查: {:?}", output);
83+
assert!(
84+
output.status.success(),
85+
"Elm 应通过(语法正确),但注释合规需另查: {:?}",
86+
output
87+
);
7688
std::fs::remove_dir_all(&tmp).unwrap();
7789
}
Lines changed: 67 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
1-
//! Terraform 六类别真实工具验收
2-
//! 验收标准:正例通过 + 负例检出 + 格式≠注释合规
1+
//! Terraform 真实工具语法验收:使用内置 terraform_data,不下载外部 provider。
2+
//! 正例、错误配置、修复复验和缺少注释样本均实际调用原生 Terraform。
33
4-
use std::process::Command;
4+
use std::{
5+
path::Path,
6+
process::{Command, Output},
7+
};
58

69
fn ensure_clean_dir(name: &str) -> std::path::PathBuf {
7-
let tmp = std::env::temp_dir().join(name);
10+
let tmp = std::env::temp_dir().join(format!("{name}-{}", std::process::id()));
811
let _ = std::fs::remove_dir_all(&tmp);
912
std::fs::create_dir_all(&tmp).unwrap();
1013
tmp
@@ -14,69 +17,77 @@ fn terraform_bin() -> String {
1417
std::env::var("CODEGUARD_TERRAFORM_BIN").unwrap_or_else(|_| "terraform".to_string())
1518
}
1619

17-
/// 正例通过
18-
#[test]
19-
fn valid_terraform_samples_pass_validate() {
20-
let tmp = ensure_clean_dir("terraform-valid-test");
21-
std::fs::write(tmp.join("main.tf"), "# Adds a resource.\nresource \"null_resource\" \"example\" {\n triggers = {\n value = \"test\"\n }\n}\n").unwrap();
22-
23-
let output = Command::new(terraform_bin())
24-
.args(["init", "-backend=false"])
25-
.current_dir(&tmp)
20+
fn run(tmp: &Path, args: &[&str]) -> Output {
21+
Command::new(terraform_bin())
22+
.args(args)
23+
.env("TF_IN_AUTOMATION", "1")
24+
.env("CHECKPOINT_DISABLE", "1")
25+
.current_dir(tmp)
2626
.output()
27-
.expect("terraform 不可用");
27+
.expect("terraform 不可用")
28+
}
2829

29-
let output2 = Command::new(terraform_bin())
30-
.args(["validate"])
31-
.current_dir(&tmp)
32-
.output()
33-
.expect("terraform 不可用");
30+
fn initialize(tmp: &Path) {
31+
let output = run(
32+
tmp,
33+
&["init", "-backend=false", "-input=false", "-no-color"],
34+
);
35+
assert!(
36+
output.status.success(),
37+
"Terraform 初始化必须成功,不能把工具缺失当作负例检出: {output:?}"
38+
);
39+
}
40+
41+
const VALID: &str = "# Stores an example value.\nresource \"terraform_data\" \"example\" {\n input = \"test\"\n}\n";
3442

35-
assert!(output2.status.success(), "有效 Terraform 代码应通过验证: {:?}", output2);
36-
std::fs::remove_dir_all(&tmp).unwrap();
43+
#[test]
44+
fn valid_terraform_samples_pass_validate() {
45+
let tmp = ensure_clean_dir("terraform-valid-test");
46+
std::fs::write(tmp.join("main.tf"), VALID).unwrap();
47+
initialize(&tmp);
48+
let output = run(&tmp, &["validate", "-no-color"]);
49+
assert!(
50+
output.status.success(),
51+
"有效 Terraform 代码应通过验证: {output:?}"
52+
);
53+
std::fs::remove_dir_all(tmp).unwrap();
3754
}
3855

39-
/// 负例检出
4056
#[test]
4157
fn invalid_terraform_samples_are_detected() {
4258
let tmp = ensure_clean_dir("terraform-invalid-test");
43-
std::fs::write(tmp.join("main.tf"), "resource \"null_resource\" \"example\" {\n triggers = {\n value = \"test\"\n }\n").unwrap();
44-
45-
let output = Command::new(terraform_bin())
46-
.args(["init", "-backend=false"])
47-
.current_dir(&tmp)
48-
.output()
49-
.expect("terraform 不可用");
50-
51-
let output2 = Command::new(terraform_bin())
52-
.args(["validate"])
53-
.current_dir(&tmp)
54-
.output()
55-
.expect("terraform 不可用");
56-
57-
assert!(!output2.status.success(), "语法错误应被检出");
58-
std::fs::remove_dir_all(&tmp).unwrap();
59+
let file = tmp.join("main.tf");
60+
std::fs::write(&file, VALID).unwrap();
61+
initialize(&tmp);
62+
// 保持有效 HCL,由内置 provider 的真实 schema 拒绝未知属性。
63+
std::fs::write(
64+
&file,
65+
"resource \"terraform_data\" \"example\" {\n unsupported = true\n}\n",
66+
)
67+
.unwrap();
68+
let output = run(&tmp, &["validate", "-no-color"]);
69+
assert!(!output.status.success(), "错误配置应被检出");
70+
let stderr = String::from_utf8_lossy(&output.stderr);
71+
assert!(
72+
stderr.contains("Unsupported argument"),
73+
"失败必须来自目标配置错误: {output:?}"
74+
);
75+
// 同一个初始化目录中修复并复验,排除缺工具/缺 provider 造成的假阴性。
76+
std::fs::write(&file, VALID).unwrap();
77+
let repaired = run(&tmp, &["validate", "-no-color"]);
78+
assert!(repaired.status.success(), "修复后应重新通过: {repaired:?}");
79+
std::fs::remove_dir_all(tmp).unwrap();
5980
}
6081

61-
/// 格式≠注释合规
6282
#[test]
6383
fn formatting_cannot_impersonate_comment_checking() {
6484
let tmp = ensure_clean_dir("terraform-comment-test");
65-
// 格式正确但缺少注释
66-
std::fs::write(tmp.join("main.tf"), "resource \"null_resource\" \"undocumented\" {\n triggers = {\n value = \"test\"\n }\n}\n\n# Has docs.\nresource \"null_resource\" \"documented\" {\n triggers = {\n value = \"test\"\n }\n}\n").unwrap();
67-
68-
let output = Command::new(terraform_bin())
69-
.args(["init", "-backend=false"])
70-
.current_dir(&tmp)
71-
.output()
72-
.expect("terraform 不可用");
73-
74-
let output2 = Command::new(terraform_bin())
75-
.args(["validate"])
76-
.current_dir(&tmp)
77-
.output()
78-
.expect("terraform 不可用");
79-
80-
assert!(output2.status.success(), "Terraform 应通过(语法正确),但注释合规需另查: {:?}", output2);
81-
std::fs::remove_dir_all(&tmp).unwrap();
85+
std::fs::write(tmp.join("main.tf"), "resource \"terraform_data\" \"undocumented\" {\n input = \"test\"\n}\n\n# Has docs.\nresource \"terraform_data\" \"documented\" {\n input = \"test\"\n}\n").unwrap();
86+
initialize(&tmp);
87+
let output = run(&tmp, &["validate", "-no-color"]);
88+
assert!(
89+
output.status.success(),
90+
"Terraform 应通过语法验证,但注释合规需另查: {output:?}"
91+
);
92+
std::fs::remove_dir_all(tmp).unwrap();
8293
}

0 commit comments

Comments
 (0)