Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/ISSUE_TEMPLATE/quality.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ labels: quality improvement
.\2_RunAllTests.ps1
.\3_SmokeTest.ps1

合格の目安は 全ステップ OK / 8-8 差分 0 / 22-22
合格の目安は 全ステップ OK / 8-8 差分 0 / 25-25
-->

## 利用者への影響
Expand Down
2 changes: 1 addition & 1 deletion .github/pull_request_template.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
.\2_RunAllTests.ps1
.\3_SmokeTest.ps1

合格の目安は 全ステップ OK / 8-8 差分 0 / 22-22
合格の目安は 全ステップ OK / 8-8 差分 0 / 25-25
文書だけの変更なら「不要」と書いてください。
-->

Expand Down
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -341,3 +341,9 @@ __pycache__/
/root/programs/*/NuGet/in/*/*.json
/root/programs/*/NuGet/in/*/*/
*.snupkg

# エージェント用スキル(#577)
# 本体は OpenTouryoCodingAgentAssets にあり、ここに在るのは複製である。
# **コミットすると、向こうが更新されたときに古くなる。**
# 取得は root/programs/GetAgentSkills.ps1 で行う。
/.claude/skills/
126 changes: 126 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,87 @@ cd root\programs
`2_RunAllTests.ps1` はワーキング ツリーの `Result*.txt` を書き換える(従来のバッチ運用と同じ)。
**コミットの要否は人が判断する**ため、エージェントは差分を報告するに留める。

**エージェントは、この 3 本を個別に実行することを検討する。**(#576)
`0_RunAll.ps1` は 3 本をまとめて回す**利用者向けの入口**であり、
**必要な 1 本だけを選ぶ、という判断が入らない。**
どれを回すかは、次節の対応表で決める。

```powershell
.\1_BuildAll.ps1 -Only "Framework_Tool" # ビルドするものだけ
.\2_RunAllTests.ps1 -Only "TestBatch" # テスト結果 Result*.txt が絞られる
.\3_SmokeTest.ps1 -Only "DeployZip" # 確かめるものだけ
```

**`-Only` に何を指定できるかは `-List` で出す。**
**文書には一覧を書かない。** 対象が増減したときに古くなるため、
スクリプト自身を一次情報にする(ツールの `/HELP` と同じ考え方)。

```powershell
.\1_BuildAll.ps1 -List -Lang Both # 45 ステップ
.\2_RunAllTests.ps1 -List # 8 件
.\3_SmokeTest.ps1 -List -Lang VB # 6 件(-Lang が効く)
```

**3 本とも `-Only` が空振りしたら終了コード 1 で止まる。**
打ち間違いが「全ステップ OK」になることはない。

#### 通しで回す前に、依存関係を見る(#576)

**全部回すのは「安全」ではない。遅いだけのことがある。**

依存の向きは一方向で、段は 2 つしかない。

```
基盤 NuGet / Business / Business.RichClient / CopyAssemblies
↓ (ここが変われば、下は全部やり直し)
末端 Tools / 各サンプル / Tests
```

**ツールと個別サンプルは末端である。** そこを変えても、基盤も他のサンプルも変わらない。

| 変更した場所 | `1_BuildAll` | `2_RunAllTests` | `3_SmokeTest` |
|---|---|---|---|
| `Infrastructure/`(基盤) | **通し** | **通し** | **通し** |
| `Tools/`(ツール) | `-Only Framework_Tool` | **不要** | `-Only <そのツール>` |
| 個別サンプル | `-Only <サンプル>` | **不要** | `-Only <サンプル>` |
| `Tests/` | `-Only <対象>` | `-Only <対象>` | **不要** |
| `.ps1` / `.md` のみ | **不要** | **不要** | **不要** |

**`2_RunAllTests.ps1` の対象はフレームワークのテストだけ**である。
`Tools/` や個別サンプルを変えても、ここは動かない。回す理由が無い。

実測(`0_RunAll.ps1 -Lang Both` は 24.6 分)。

```
基盤のビルド 122.8 秒
Framework_Tool 系だけ 74.8 秒 ← ツールの変更で必要なのはこちら
2_RunAllTests 通し 111.6 秒
2_RunAllTests -Only 35 秒ほど
```

**ツールだけの変更なら、1/16 ほどで終わる。**

#### 時間だけの問題ではない

`2_RunAllTests.ps1` は**ワーキング ツリーの `Result*.txt` を書き換える。**
関係の無い対象まで回すと、**人が確認してコミットする差分が増える。**

`-Only` で絞れば、書き換わるのは絞った対象の分だけになる。

#### それでも通しを回す場面

- **基盤(`Infrastructure/`)に触れたとき**
- **リリース前**(`RELEASE.md`)
- **絞り込みで妙なエラーが出たとき**(再現するかを見る)

3 本目は制約による誤検知が実際にある。
`-Only` は前段が用意した状態に依存するステップを落とすことがあり、
**変更と無関係なエラーに見える。** 詳細は
[`CHEATSHEET.md`](root/programs/CHEATSHEET.md) 1 節の
「`-Only` + `-SkipClean` は万能ではない」。

**判断に迷ったら通しでよい。** ただし**迷っていないのに通すのは、ただの浪費である。**

**上記の既定は C# 側である。VB 側に手を入れたときは `-Lang` で回す。**

```powershell
Expand All @@ -211,6 +292,51 @@ cd root\programs

前提となるサービスや DB の状態が足りない場合は、**勝手に変えず、対処方法とともに報告する。**

### サンプルを実装するときは、スキル リポジトリのスキルを使う(#577)

**サンプルは「フレームワークを使うアプリ」である。**
その書き方は本体の規約とは別に、専用のスキルとしてまとめられている。

https://github.com/OpenTouryoProject/OpenTouryoCodingAgentAssets

**`AGENTS.md` に書くだけでは使えない。**
Claude Code は `.claude/skills/<名前>/SKILL.md` を探すため、実体が要る。

```powershell
cd root\programs
.\GetAgentSkills.ps1 # main から取得して .claude/skills へ配置
.\GetAgentSkills.ps1 -List # 何が対象になるかだけ見る
```

**`.claude/skills/` は `.gitignore` の対象である。**
ここに在るのは複製で、**本体は向こうのリポジトリにある。**
コミットすると、向こうが更新されたときに古くなり、どちらが正か分からなくなる。
**使う前に取得する。**

#### 向こうの `install.ps1` は使わない

あちらにも導入スクリプトがあるが、**フレームワークの利用者(アプリ開発)向け**で、
導入先の `AGENTS.md` と `CLAUDE.md` も生成する。
**本書は手で書いたものなので、スキルだけを取りに行く。**

#### 既定で除外しているもの

| 除外 | 理由 |
|---|---|
| `opentouryo-project-setup*`(6 件) | アプリの新規構築手順。本体の開発では使わない |
| `opentouryo-project-policy` | **プロジェクト方針は本書(`AGENTS.md`)が正** |
| `opentouryo-project-transform` | 既存資産の移行。本体側では別の話 |
| `opentouryo-comment-convention` | **コメント規約は [`CODING.md`](root/programs/CODING.md) が正** |
| `opentouryo-base2-customize` | 利用者による基底クラスの改造。本体側では別の話 |

**除外を増やしたら、取得し直せば取り残しも消える。**
前回配置したものが除外に回った場合、`GetAgentSkills.ps1` が削除する
(消すのは向こうに在るスキル名だけで、無関係なものは触らない)。

**規約が競合したら、本体の文書が優先である。**
スキルはアプリ開発を前提に書かれているため、
[`CODING.md`](root/programs/CODING.md) や各 `ANALYSIS.md` と食い違うことがある。

### 付属ツールを CLI で使うときは、各ツールの README に従う

エージェントから実行できる(非対話の)ツールには README を置く。
Expand Down
1 change: 0 additions & 1 deletion root/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,6 @@ C:\root\programs\CS\Samples\WS_sample\WSClient_sample\WSClientWinCone_sample\WSC

#### Infrastructure:
- C:\root\programs\CS\Frameworks\Infrastructure
- C:\root\programs\CS\Frameworks\Infrastructure\ServiceInterface\ASPNETWebServiceCore

#### Tools:
- C:\root\programs\CS\Frameworks\Tools
Expand Down
1 change: 0 additions & 1 deletion root/Readme.ja.md
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,6 @@ C:\root\programs\CS\Samples\WS_sample\WSClient_sample\WSClientWinCone_sample\WSC

#### 基盤:
- C:\root\programs\CS\Frameworks\Infrastructure
- C:\root\programs\CS\Frameworks\Infrastructure\ServiceInterface\ASPNETWebServiceCore

#### ツール:
- C:\root\programs\CS\Frameworks\Tools
Expand Down
48 changes: 46 additions & 2 deletions root/programs/0_RunAll.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -71,16 +71,28 @@ foreach ($s in $scripts)
if ($Lang -eq "VB" -and -not $s.UseLang)
{
Write-Host ("{0} は VB 版の対象外のため飛ばします。" -f $s.Name) -ForegroundColor Yellow
$results += [pscustomobject]@{ スクリプト = $s.Name; 終了コード = "対象外" }
$results += [pscustomobject]@{ スクリプト = $s.Name; 終了コード = "対象外"; 秒 = "-" }
continue
}

$splat = @{}
if ($s.UseLang) { $splat.Lang = $Lang }
if ($s.UseIgnore) { $splat.IgnoreErrors = $IgnoreErrors }

# **実行時間を測る。**(#571)
# 通しは長い。合計だけ見ても、どこを短くすればよいかが分からない。
$sw = [Diagnostics.Stopwatch]::StartNew()

& (Join-Path $PSScriptRoot $s.Name) @splat
$results += [pscustomobject]@{ スクリプト = $s.Name; 終了コード = $LASTEXITCODE }
$code = $LASTEXITCODE

$sw.Stop()

$results += [pscustomobject]@{
スクリプト = $s.Name
終了コード = $code
秒 = ("{0:N1}" -f $sw.Elapsed.TotalSeconds)
}

# --- bindingRedirect の突き合わせ(警告のみ)---(#556)
#
Expand All @@ -100,6 +112,25 @@ foreach ($s in $scripts)
Write-Host " .\CompareRedirect.ps1 -Check で内容を確認してください。"
Write-Host ""
}

# --- パッケージの版の突き合わせ(警告のみ)---(#569)
#
# **版は 4 か所に散らばる。**(#566 / #568)
# packages.config を基準に、csproj のパス表記(②)と
# Reference の Version(③)が外れていないかを見る。
#
# ③ は HintPath の DLL を読むため、**復元してからでないと判定できない。**
# ここ(ビルドの直後)なら材料が揃っている。
& (Join-Path $PSScriptRoot "ComparePackage.ps1") -Check 6>$null | Out-Null
$packageOk = ($LASTEXITCODE -eq 0)

if (-not $packageOk)
{
Write-Host ""
Write-Host "【警告】packages.config と csproj で、パッケージの版が食い違っています。" -ForegroundColor Yellow
Write-Host " .\ComparePackage.ps1 -Check で内容を確認してください。"
Write-Host ""
}
}
}

Expand All @@ -110,6 +141,12 @@ Write-Host ""
Write-SummaryTable $results
Write-Host ""

# **合計も出す。** 1 本ずつの秒を足す手間を省く。
$totalSec = ($results | Where-Object { $_.秒 -ne "-" } |
ForEach-Object { [double]$_.秒 } | Measure-Object -Sum).Sum
Write-Host (" 合計 : {0:N1} 秒({1:N1} 分)" -f $totalSec, ($totalSec / 60))
Write-Host ""

# **終了コードをそのまま合否として読んでよい。**(#555)
# 既知の署名エラー(MSB3482)は -IgnoreErrors の既定値で除外しているため、
# 1_BuildAll.ps1 が 1 を返したら、それは**別の理由**である。
Expand Down Expand Up @@ -142,5 +179,12 @@ if ($null -ne $redirectOk -and -not $redirectOk)
Write-Host " (CompareRedirect.ps1 -Check)。合否には数えていません。"
}

if ($null -ne $packageOk -and -not $packageOk)
{
Write-Host ""
Write-Host "【警告】packages.config と csproj で、パッケージの版が食い違っています" -ForegroundColor Yellow
Write-Host " (ComparePackage.ps1 -Check)。合否には数えていません。"
}

# --- 画面を残すための処理 ---
Read-Host "`nEnterキーを押すとウィンドウを閉じます"
83 changes: 82 additions & 1 deletion root/programs/1_BuildAll.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@
.PARAMETER Only
ステップ名の部分一致で対象を絞る(例: -Only "net48")。動作確認用。

.PARAMETER List
-Only に指定できるステップ名を一覧表示して終わる。**ここが一次情報。**
.PARAMETER SkipClean
クリーン処理(1_DeleteDir / 1_DeleteFile)を省略する。
※ リリース判定では省略しないこと。前回のビルド成果物が残っていると、
Expand Down Expand Up @@ -73,7 +75,9 @@ param(
[ValidateSet("CS", "VB", "Both")]
[string]$Lang = "CS",
[string]$Only,
[switch]$List,
[switch]$SkipClean,
[switch]$WarnDetail,
[string]$OutputDir = (Join-Path $env:TEMP "OpenTouryoBuildLogs"),
[string[]]$IgnoreErrors = @()
)
Expand Down Expand Up @@ -305,6 +309,34 @@ $total = [Diagnostics.Stopwatch]::StartNew()
# 区画の区切りとしての表示は「実行済み」として残す(黙って消さない)。
$executed = @{}


# **-Only に何を指定できるかは、ここが一次情報である。**(#576)
# 文書に書き写すと二重管理になり、対象が増減したときに古くなる。
if ($List)
{
Write-Host ("=== -Only に指定できる名前(部分一致){0} ===" -f "(-Lang $Lang)") -ForegroundColor Cyan
foreach ($x in $steps) { Write-Host (" " + $x.Name) }
Write-Host (" ---- {0} 件 ----" -f $steps.Count)
exit 0
}

# **-Only が空振りしたら止める。**(#576)
# 1 件も選ばれないまま進むと「全ステップ OK」と表示され、
# **打ち間違いが緑になる。** 何も建てていないのに成功に見えるのが最も悪い。
if ($Only)
{
$matched = @($steps | Where-Object { ($_.Name -like "*$Only*") -or ($_.Bat -like "*$Only*") })

if ($matched.Count -eq 0)
{
Write-Host (" **-Only '$Only' に一致するステップがありません。**") -ForegroundColor Red
Write-Host (" -List で一覧を出せます。") -ForegroundColor Yellow
exit 1
}

Write-Host (" -Only '$Only' : {0} ステップに絞りました" -f $matched.Count) -ForegroundColor Yellow
}

foreach ($s in $steps)
{
if ($Only -and ($s.Name -notlike "*$Only*") -and ($s.Bat -notlike "*$Only*"))
Expand Down Expand Up @@ -388,6 +420,7 @@ foreach ($s in $steps)
エラー = $stepErrors.Count
既知 = $stepKnown.Count
警告 = $diag.Warnings.Count
警告詳細 = $diag.Warnings
秒 = [Math]::Round($sw.Elapsed.TotalSeconds, 1)
}
}
Expand All @@ -400,7 +433,55 @@ $total.Stop()
Write-Host ""
Write-Host "================ サマリ ================"
Write-Host ""
Write-SummaryTable $results
# **警告詳細は列にしない。** 表が壊れるので、集計にだけ使う。
Write-SummaryTable ($results | Select-Object * -ExcludeProperty 警告詳細)

# --- 警告の内訳(#571)---
#
# **件数だけでは何を直せばよいか分からない。** 種類ごとにまとめる。
# 既定では出さない。毎回出ると本題(エラー)が埋もれるため。
if ($WarnDetail)
{
$withWarn = @($results | Where-Object { $_.警告詳細 -and $_.警告詳細.Count -gt 0 })

if ($withWarn.Count -eq 0)
{
Write-Host ""
Write-Host " 警告はありません。"
}
else
{
Write-Host ""
Write-Host "================ 警告の内訳 ================"

foreach ($r in ($withWarn | Sort-Object { -$_.警告詳細.Count }))
{
Write-Host ""
Write-Host (" {0}({1} 件)" -f $r.ステップ, $r.警告詳細.Count)

# 「: warning XXnnnn :」から種類を取り出す。取れないものは (種類不明) にまとめる。
$byCode = $r.警告詳細 | ForEach-Object {
$m = [regex]::Match($_, ':\s*warning\s+([A-Za-z]+\d+)\s*:')
if ($m.Success) { $m.Groups[1].Value } else { "(種類不明)" }
} | Group-Object | Sort-Object Count -Descending

foreach ($g in $byCode)
{
# 代表を 1 つ出す。**同じ種類でも中身が違うことがある**ので、目印になる。
$sample = @($r.警告詳細 | Where-Object { $_ -match [regex]::Escape($g.Name) })[0]
if ($null -eq $sample) { $sample = "" }
$sample = ($sample -replace '\s+', ' ')
if ($sample.Length -gt 96) { $sample = $sample.Substring(0, 96) + " …" }

Write-Host (" {0,4} {1,-12} {2}" -f $g.Count, $g.Name, $sample)
}
}

Write-Host ""
Write-Host " **同じ種類は、たいてい 1 か所の対処でまとめて消える。**"
Write-Host " MSB3277 は版の混在(CompareRedirect.ps1 / ComparePackage.ps1 も参照)。"
}
}
Write-Host ""
Write-Host (" 所要時間 : {0:N1} 分" -f $total.Elapsed.TotalMinutes)
Write-Host (" ログ : {0}" -f $OutputDir)
Expand Down
Loading