Skip to content

Commit c079036

Browse files
makinzmclaude
andauthored
feat: YAML naming-only 言語サポート + ベストプラクティスドキュメント (#76)
* [test] YAML E2E テスト・fixture・CI dogfooding 追加 because of naming-only 言語サポート - tests/e2e_yaml.rs: 7テスト (happy path, naming violation, name_targets) - tests/fixtures/yaml_sample/: fixture (config + manifests レイヤー) - ci.yml: dogfood-rust に YAML fixture ステップ追加 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * [fix] YAML パーサー実装 + DispatchingParser 登録 because of naming-only 言語サポート - tree-sitter-yaml 0.6 追加 - YamlParser: キー→Symbol, 値→StringLiteral, コメント→Comment - ext_to_language に yaml/yml 追加 - SOURCE_EXTENSIONS に yaml/yml 追加 - E2E テスト 7/7 通過 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * [refactor] YAML サポートのドキュメント追加 because of PR #76 完了 - Website: YAML ガイド (ja/en) + ベストプラクティス (ja/en) - astro.config.mjs: サイドバーに YAML + ベストプラクティス追加 - index.mdx: 言語リストに YAML 追加 (ja/en) - README.md: Languages/Configuration Reference/設定例に YAML 追加 - docs/TODO.md: YAML サポート完了記録 - docs/e2e_checklist.md: YAML naming-only 注記追加 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent ab9d34c commit c079036

File tree

23 files changed

+941
-11
lines changed

23 files changed

+941
-11
lines changed

.github/workflows/ci.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,9 @@ jobs:
147147
- name: Self-check — C fixture (tests/fixtures/c_sample)
148148
working-directory: tests/fixtures/c_sample
149149
run: ${{ github.workspace }}/target/release/mille check
150+
- name: Self-check — YAML fixture (tests/fixtures/yaml_sample)
151+
working-directory: tests/fixtures/yaml_sample
152+
run: ${{ github.workspace }}/target/release/mille check
150153

151154
# ------------------------------------------------------------------ #
152155
# 3b. Dogfooding-python — maturin build → pytest + mille check #

Cargo.lock

Lines changed: 12 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ serde_json = "1"
3737
tree-sitter-kotlin = "0.3"
3838
tree-sitter-php = "0.22"
3939
tree-sitter-c = "0.21"
40+
tree-sitter-yaml = "0.6"
4041

4142
[dev-dependencies]
4243
tempfile = "3"

README.md

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ One TOML config. Rust-powered. CI-ready. Supports multiple languages from a sing
88

99
## What it checks
1010

11-
**Languages:** Rust, Go, TypeScript, JavaScript, Python, Java, Kotlin, PHP, C
11+
**Languages:** Rust, Go, TypeScript, JavaScript, Python, Java, Kotlin, PHP, C, YAML
1212

1313
| Check | Description |
1414
|---|---|
@@ -415,7 +415,7 @@ Exit codes:
415415
|---|---|
416416
| `name` | Project name |
417417
| `root` | Root directory for analysis |
418-
| `languages` | Languages to check: `"rust"`, `"go"`, `"typescript"`, `"javascript"`, `"python"`, `"java"`, `"kotlin"`, `"php"`, `"c"` |
418+
| `languages` | Languages to check: `"rust"`, `"go"`, `"typescript"`, `"javascript"`, `"python"`, `"java"`, `"kotlin"`, `"php"`, `"c"`, `"yaml"` |
419419

420420
### `[[layers]]`
421421

@@ -466,7 +466,7 @@ name_deny_ignore = ["**/test_*.rs", "tests/**"] # exclude test files from namin
466466
- `"comment"`: inline comment content
467467
- `"string_literal"`: string literal content
468468
- `"identifier"`: attribute/field access identifiers (e.g. `gcp` in `cfg.gcp.bucket`)
469-
- Supported languages: Rust, TypeScript, JavaScript, Python, Go, Java, Kotlin, PHP, C
469+
- Supported languages: Rust, TypeScript, JavaScript, Python, Go, Java, Kotlin, PHP, C, YAML
470470
- Severity is controlled by `severity.naming_violation` (default: `"error"`)
471471

472472
### `[[layers.allow_call_patterns]]`
@@ -653,6 +653,32 @@ dependency_mode = "opt-in"
653653
allow = ["domain"]
654654
```
655655

656+
### YAML
657+
658+
> YAML is a **naming-only** language — it has no imports, so only `name_deny` checks are supported. `dependency_mode` and `external_mode` should be set to `"opt-out"`.
659+
660+
**Example `mille.toml` for YAML config files:**
661+
662+
```toml
663+
[project]
664+
name = "my-k8s-project"
665+
root = "."
666+
languages = ["yaml"]
667+
668+
[[layers]]
669+
name = "config"
670+
paths = ["config/**"]
671+
dependency_mode = "opt-out"
672+
external_mode = "opt-out"
673+
name_deny = ["aws", "gcp"]
674+
675+
[[layers]]
676+
name = "manifests"
677+
paths = ["manifests/**"]
678+
dependency_mode = "opt-out"
679+
external_mode = "opt-out"
680+
```
681+
656682
## How it Works
657683

658684
mille uses [tree-sitter](https://tree-sitter.github.io/) for AST-based import extraction — no regex heuristics.
@@ -663,7 +689,7 @@ mille.toml
663689
664690
Layer definitions
665691
666-
Source files (*.rs, *.go, *.py, *.ts, *.js, *.java, ...)
692+
Source files (*.rs, *.go, *.py, *.ts, *.js, *.java, *.yaml, ...)
667693
│ tree-sitter parse
668694
669695
RawImport list

docs/TODO.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
- ✅ PHP 言語サポート — `.php` ファイルの `use` 文パース(simple/aliased/grouped/function/const)・Internal/External/Stdlib 分類、`[resolve.php] namespace` 設定、`composer.json` `autoload.psr-4` 自動検出、PHP stdlib クラス(DateTime/PDO/Exception 等)の Stdlib 自動分類
3737

3838
- ✅ PATH 位置引数 — 全サブコマンドに `[PATH]` 位置引数を追加(デフォルト `.`)。`mille check ./other/project` で任意ディレクトリを検査可能。`CommonArgs` + `Command::common()` exhaustive match で新コマンド追加時にコンパイルエラーで PATH 対応を強制(PR #75
39+
- ✅ YAML 言語サポート — naming-only 言語として `.yaml`/`.yml` ファイルの `name_deny` チェックをサポート。マッピングキー→Symbol、スカラー値→StringLiteral、コメント→Comment として抽出。tree-sitter-yaml 0.6 使用(PR #76
3940

4041
以下は **設定ファイルにフィールドが存在しても、まだ動作していない** 項目です(README に掲載しないよう修正済み):
4142
(現在なし)

docs/e2e_checklist.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,18 @@
1717
- [ ] external opt-in: `external_allow = []` → 外部違反
1818
- [ ] external opt-out: `external_deny` を設定 → 外部違反
1919
- [ ] allow_call_patterns: 禁止メソッド呼び出し → `CallPatternViolation`
20+
21+
---
22+
23+
## 言語固有の注記
24+
25+
### YAML (naming-only)
26+
27+
YAML は import の概念がないため、以下の項目は N/A:
28+
- dep opt-in / dep opt-out: N/A
29+
- external opt-in / external opt-out: N/A
30+
- allow_call_patterns: N/A
31+
32+
YAML で有効なチェック:
33+
- [x] name_deny: キー (Symbol) と値 (StringLiteral) の命名チェック
34+
- [x] name_targets: `["symbol"]` / `["string_literal"]` で対象の絞り込み

src/infrastructure/parser/mod.rs

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ pub mod php;
66
pub mod python;
77
pub mod rust;
88
pub mod typescript;
9+
pub mod yaml;
910

1011
use self::c::CParser;
1112
use self::go::GoParser;
@@ -15,6 +16,7 @@ use self::php::PhpParser;
1516
use self::python::PythonParser;
1617
use self::rust::RustParser;
1718
use self::typescript::TypeScriptParser;
19+
use self::yaml::YamlParser;
1820
use crate::domain::entity::call_expr::RawCallExpr;
1921
use crate::domain::entity::import::RawImport;
2022
use crate::domain::entity::name::{NameKind, ParsedNames, RawName};
@@ -106,6 +108,7 @@ pub fn ext_to_language(ext: &str) -> Option<&'static str> {
106108
"kt" => Some("kotlin"),
107109
"php" => Some("php"),
108110
"c" | "h" => Some("c"),
111+
"yaml" | "yml" => Some("yaml"),
109112
_ => None,
110113
}
111114
}
@@ -120,6 +123,7 @@ pub struct DispatchingParser {
120123
java: JavaParser,
121124
kotlin: KotlinParser,
122125
php: PhpParser,
126+
yaml: YamlParser,
123127
}
124128

125129
impl DispatchingParser {
@@ -133,6 +137,7 @@ impl DispatchingParser {
133137
java: JavaParser,
134138
kotlin: KotlinParser,
135139
php: PhpParser,
140+
yaml: YamlParser,
136141
}
137142
}
138143
}
@@ -143,6 +148,10 @@ impl Default for DispatchingParser {
143148
}
144149
}
145150

151+
fn is_yaml(file_path: &str) -> bool {
152+
file_path.ends_with(".yaml") || file_path.ends_with(".yml")
153+
}
154+
146155
fn is_c(file_path: &str) -> bool {
147156
file_path.ends_with(".c") || file_path.ends_with(".h")
148157
}
@@ -156,7 +165,9 @@ fn is_ts_js(file_path: &str) -> bool {
156165

157166
impl Parser for DispatchingParser {
158167
fn parse_imports(&self, source: &str, file_path: &str) -> Vec<RawImport> {
159-
if is_c(file_path) {
168+
if is_yaml(file_path) {
169+
self.yaml.parse_imports(source, file_path)
170+
} else if is_c(file_path) {
160171
self.c.parse_imports(source, file_path)
161172
} else if file_path.ends_with(".go") {
162173
self.go.parse_imports(source, file_path)
@@ -176,7 +187,9 @@ impl Parser for DispatchingParser {
176187
}
177188

178189
fn parse_call_exprs(&self, source: &str, file_path: &str) -> Vec<RawCallExpr> {
179-
if is_c(file_path) {
190+
if is_yaml(file_path) {
191+
self.yaml.parse_call_exprs(source, file_path)
192+
} else if is_c(file_path) {
180193
self.c.parse_call_exprs(source, file_path)
181194
} else if file_path.ends_with(".go") {
182195
self.go.parse_call_exprs(source, file_path)
@@ -196,7 +209,9 @@ impl Parser for DispatchingParser {
196209
}
197210

198211
fn parse_names(&self, source: &str, file_path: &str) -> ParsedNames {
199-
if is_c(file_path) {
212+
if is_yaml(file_path) {
213+
self.yaml.parse_names(source, file_path)
214+
} else if is_c(file_path) {
200215
self.c.parse_names(source, file_path)
201216
} else if file_path.ends_with(".go") {
202217
self.go.parse_names(source, file_path)

0 commit comments

Comments
 (0)