-
Notifications
You must be signed in to change notification settings - Fork 49
Expand file tree
/
Copy pathCompareConfig.ps1
More file actions
328 lines (275 loc) · 12.2 KB
/
Copy pathCompareConfig.ps1
File metadata and controls
328 lines (275 loc) · 12.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
<#
.SYNOPSIS
CS 版と VB 版の設定ファイルを、XML の要素・属性で突き合わせる。
.DESCRIPTION
「CS と VB の設定ファイルは同じはずだ」と考えて丸ごとコピーすると、
**VB 固有の記述が消える。** 実際に消した(#549 の作業中)。
素の diff ではコメント・空白・改行・BOM の差に埋もれて、**値の差が見えない。**
本スクリプトは XML の要素と属性だけを取り出して比較するため、
**値が変わったかどうかだけ**が分かる。
<CI では代わりにならない>
VB の疎通テストは 6 対象しか動かさないため、**差分のあるファイルの半分以上は
一度も実行されない。** 実行される分でも、`compilation/assemblies` の欠落は
`0_RunAll.ps1 -Lang Both` を通過した(実測)。
**CI は「壊れていないこと」の証拠にはなるが、「意図どおり」の証拠にはならない。**
<違ってよい差分がある>
自動生成(`startup` / `bindingRedirect`)は、そもそも同期の対象ではない。
**同じにすると壊れる差分**もある(埋め込みリソース名。CS はフォルダ名を含み、
VB は含まない)。判断の材料は [`CONFIGURATION.md`](CONFIGURATION.md) 11 節。
**`packages.config` は対象である。**
拡張子が `.config` なので元から突き合わせており、`$allowed` にも入れていない。
パッケージ構成のずれ(片側だけ更新した、陳腐化した宣言が残っている)は、
**ビルドが通ってしまうため、これ以外に気付く手段が無い。**
<対応づけは相対パスだけ>
**CS からの相対パスを VB にそのまま当てる。探しには行かない。**
(ファイル名の大文字小文字だけは吸収する)
**階層が一致しないプロジェクトは、1 組も対象に入らない。**
そして**何も出力されない**ので、一覧を見ても気付けない。
**組数で確かめること。**
.PARAMETER Detail
差分の内訳(どの要素・属性が増減したか)を表示する。
.PARAMETER Only
対象を相対パスの部分一致で絞る(例: -Only "WebApp_sample")。
.PARAMETER Max
-Detail のときに、1 組あたり表示する件数の上限。既定は 20。
.PARAMETER Check
**合否を返す。** 差分を「違ってよい種類」(下記)と突き合わせ、
そこに当てはまらない差分が 1 件でもあれば終了コード 1 を返す。
<違ってよい種類>
/configuration/startup 配下 VS が VB プロジェクトに自動で入れる
/configuration/runtime 配下 NuGet が生成する bindingRedirect
system.web/compilation/assemblies 配下 VB は明示参照が要ることがある
appSettings の SqlTextFilePath **同じにすると壊れる**(言語で
埋め込みリソース名の規則が違う)
appSettings の ClientSettingsProvider.* VS のテンプレート由来
**この一覧は「今そうなっている」ではなく「そうであってよい」を表す。**
増やすときは、なぜ違ってよいのかを CONFIGURATION.md 11 節にも書くこと。
.EXAMPLE
.\CompareConfig.ps1
.EXAMPLE
.\CompareConfig.ps1 -Detail -Only "WebApp_sample"
.EXAMPLE
.\CompareConfig.ps1 -Check
.NOTES
作成者 :玄人 幸道
更新履歴 :
日時 更新者 内容
---------- ---------------- -------------------------------------------------
2026/08/16 玄人 幸道 新規作成(#553)
2026/08/16 玄人 幸道 -Check を追加(#553)
2026/08/17 玄人 幸道 説明を訂正(#558)packages.config は対象である。
対応づけは相対パスのみで、探しには行かない。
-Check を付けない限り、終了コードは常に 0。
**差分があること自体は異常ではない**ため、既定は一覧として使う。
#>
[CmdletBinding()]
param(
[switch]$Detail,
[string]$Only,
[int]$Max = 20,
[switch]$Check
)
# ------------------------------------------------------------------
# 違ってよい差分(-Check のときだけ使う)
# ------------------------------------------------------------------
# 要素のパスと属性に対する正規表現。**当てはまるものは「想定内」**とみなす。
$allowed = @(
'^/configuration/startup'
'^/configuration/runtime'
'^/configuration/system\.web/compilation/assemblies'
'^/configuration/appSettings/add \[key=SqlTextFilePath'
'^/configuration/appSettings/add \[key=ClientSettingsProvider\.'
)
$ErrorActionPreference = "Continue"
# ------------------------------------------------------------------
# カレント ディレクトリからの実行を許可する
# ------------------------------------------------------------------
$root = $PSScriptRoot
. (Join-Path $root "SummaryTable.ps1")
# Windows PowerShell 5.1 は既定で ANSI 出力になるため、UTF-8 に揃える。
if ([Console]::OutputEncoding.CodePage -ne 65001)
{
[Console]::OutputEncoding = New-Object Text.UTF8Encoding $false
}
# ------------------------------------------------------------------
# XML から「要素のパスと属性」だけを取り出す
# ------------------------------------------------------------------
# コメント・空白・改行・BOM は落ちる。**値の差だけが残る。**
function Get-Elements([string]$path)
{
$xml = New-Object System.Xml.XmlDocument
$xml.PreserveWhitespace = $false
try { $xml.Load($path) } catch { return $null }
$list = New-Object System.Collections.Generic.List[string]
function Walk($node, [string]$parentPath)
{
if ($node.NodeType -ne [System.Xml.XmlNodeType]::Element) { return }
$here = $parentPath + "/" + $node.Name
$attrs = ""
if ($node.Attributes -and $node.Attributes.Count -gt 0)
{
$pairs = @()
foreach ($a in ($node.Attributes | Sort-Object Name))
{
$pairs += ($a.Name + "=" + $a.Value)
}
$attrs = " [" + ($pairs -join "; ") + "]"
}
$list.Add($here + $attrs)
foreach ($c in $node.ChildNodes) { Walk $c $here }
}
Walk $xml.DocumentElement ""
return $list
}
# ------------------------------------------------------------------
# 対象の組を作る
# ------------------------------------------------------------------
$csRoot = Join-Path $root "CS"
$vbRoot = Join-Path $root "VB"
# **Git の追跡下にあるものだけを対象にする。**
# ビルド出力(Build\*.dll.config など)や NuGet の展開物(packages\)を拾うと、
# 「生成物どうしを比べる」ことになり、判断できない差分ばかりが並ぶ。
$tracked = & git -C $root ls-files
if ($LASTEXITCODE -ne 0 -or -not $tracked)
{
Write-Host " git ls-files が使えないため、中止する。" -ForegroundColor Red
exit 0
}
$targets = @()
foreach ($t in $tracked)
{
if ($t -notmatch '\.config$' -and $t -notmatch 'appsettings.*\.json$') { continue }
if ($t -match '/packages/') { continue }
if ($t -notlike "CS/*") { continue }
$targets += Join-Path $root ($t -replace '/', '\')
}
$pairs = @()
foreach ($full in $targets)
{
$f = Get-Item $full -EA SilentlyContinue
if (-not $f) { continue }
$rel = $f.FullName.Substring($csRoot.Length + 1)
$vb = Join-Path $vbRoot $rel
# VB 側はファイル名の大文字小文字が違うことがある(web.config / Web.config)。
if (-not (Test-Path $vb))
{
$dir = Split-Path $vb -Parent
if (Test-Path $dir)
{
$hit = Get-ChildItem $dir -File -EA SilentlyContinue |
Where-Object { $_.Name -eq (Split-Path $vb -Leaf) } | Select-Object -First 1
if ($hit) { $vb = $hit.FullName } else { continue }
}
else { continue }
}
if ($Only -and ($rel -notlike "*$Only*")) { continue }
$pairs += [PSCustomObject]@{ Rel = $rel; CS = $f.FullName; VB = $vb }
}
# ------------------------------------------------------------------
# 突き合わせ
# ------------------------------------------------------------------
$rows = @()
$same = 0
$diff = 0
$skipped = 0
$details = @()
foreach ($p in $pairs)
{
$a = Get-Elements $p.CS
$b = Get-Elements $p.VB
if ($null -eq $a -or $null -eq $b)
{
$skipped++
$rows += [PSCustomObject]@{ 対象 = $p.Rel; 結果 = "SKIP"; 内容 = "XML として読めない" }
continue
}
# 並びまで同じか
$seqSame = (($a -join "`n") -eq ($b -join "`n"))
$onlyCS = @(Compare-Object $a $b | Where-Object { $_.SideIndicator -eq "<=" } | ForEach-Object { $_.InputObject })
$onlyVB = @(Compare-Object $a $b | Where-Object { $_.SideIndicator -eq "=>" } | ForEach-Object { $_.InputObject })
if ($seqSame)
{
$same++
$rows += [PSCustomObject]@{ 対象 = $p.Rel; 結果 = "同一"; 内容 = "" }
}
elseif ($onlyCS.Count -eq 0 -and $onlyVB.Count -eq 0)
{
# 集合は同じで順序だけが違う。実害は無い。
$same++
$rows += [PSCustomObject]@{ 対象 = $p.Rel; 結果 = "同一"; 内容 = "順序のみ相違" }
}
else
{
$diff++
$rows += [PSCustomObject]@{
対象 = $p.Rel; 結果 = "差分"
内容 = ("CS のみ {0} / VB のみ {1}" -f $onlyCS.Count, $onlyVB.Count)
}
$details += [PSCustomObject]@{ Rel = $p.Rel; OnlyCS = $onlyCS; OnlyVB = $onlyVB }
}
}
# ------------------------------------------------------------------
# 出力
# ------------------------------------------------------------------
Write-Host ""
Write-Host "================ 突き合わせ ================"
Write-SummaryTable -Rows $rows -Columns @("対象", "結果", "内容")
Write-Host ""
Write-Host (" 対象 {0} 組 : 同一 {1} / 差分 {2} / 読めない {3}" -f $pairs.Count, $same, $diff, $skipped)
Write-Host " 違ってよい差分の判断は CONFIGURATION.md 11 節。"
# ------------------------------------------------------------------
# 合否(-Check)
# ------------------------------------------------------------------
$unexpected = @()
if ($Check)
{
foreach ($d in $details)
{
foreach ($x in (@($d.OnlyCS) + @($d.OnlyVB)))
{
$ok = $false
foreach ($pat in $allowed)
{
if ($x -match $pat) { $ok = $true; break }
}
if (-not $ok)
{
$unexpected += [PSCustomObject]@{ Rel = $d.Rel; Item = $x }
}
}
}
Write-Host ""
Write-Host "================ 判定 ================"
if ($unexpected.Count -eq 0)
{
Write-Host " 想定外の差分なし。" -ForegroundColor Green
}
else
{
Write-Host (" **想定外の差分 {0} 件**" -f $unexpected.Count) -ForegroundColor Red
Write-Host ""
foreach ($u in $unexpected)
{
Write-Host (" " + $u.Rel)
Write-Host (" " + $u.Item)
}
Write-Host ""
Write-Host " 意図した差分なら CompareConfig.ps1 の `$allowed に足し、"
Write-Host " **なぜ違ってよいのかを CONFIGURATION.md 11 節にも書くこと。**"
}
}
if ($Detail -and $details.Count -gt 0)
{
foreach ($d in $details)
{
Write-Host ""
Write-Host ("### " + $d.Rel)
foreach ($x in ($d.OnlyCS | Select-Object -First $Max)) { Write-Host (" CS: " + $x) }
if ($d.OnlyCS.Count -gt $Max) { Write-Host (" CS: ... 他 " + ($d.OnlyCS.Count - $Max) + " 件") }
foreach ($x in ($d.OnlyVB | Select-Object -First $Max)) { Write-Host (" VB: " + $x) }
if ($d.OnlyVB.Count -gt $Max) { Write-Host (" VB: ... 他 " + ($d.OnlyVB.Count - $Max) + " 件") }
}
}
Write-Host ""
if ($Check -and $unexpected.Count -gt 0) { exit 1 }
exit 0