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
6 changes: 5 additions & 1 deletion .github/workflows/build-windows-vb.yml
Original file line number Diff line number Diff line change
Expand Up @@ -199,10 +199,14 @@ jobs:

# IIS Express は windows-latest に同梱されている(%ProgramFiles% 側)。
# 3_SmokeTest.ps1 が見るのも同じ場所なので、追加の導入は要らない。
#
# -IncludeUIA : UI を実際に操作する対象も回す(#588)。
# 手元ではデスクトップを占有して人の作業と衝突するため、既定で外れている。
# ランナーは使い捨てで衝突する相手が居ないので、ここでは回す。
- name: Smoke test (VB)
shell: pwsh
working-directory: root/programs
run: .\3_SmokeTest.ps1 -Lang VB -OutputDir "${{ runner.temp }}\SmokeTest"
run: .\3_SmokeTest.ps1 -Lang VB -IncludeUIA -OutputDir "${{ runner.temp }}\SmokeTest"

# 失敗時こそ中身を見たいので、常に採取する。
- name: Upload build logs
Expand Down
6 changes: 5 additions & 1 deletion .github/workflows/build-windows.yml
Original file line number Diff line number Diff line change
Expand Up @@ -242,10 +242,14 @@ jobs:
# IIS Express は windows-latest(win25-vs2026)に同梱されている。
# C:\Program Files\IIS Express\iisexpress.exe v10.0.26013.1000
# 3_SmokeTest.ps1 が見るのも %ProgramFiles% 側なので、そのまま使える。
#
# -IncludeUIA : UI を実際に操作する対象も回す(#588)。
# 手元ではデスクトップを占有して人の作業と衝突するため、既定で外れている。
# ランナーは使い捨てで衝突する相手が居ないので、ここでは回す。
- name: Smoke test
shell: pwsh
working-directory: root/programs
run: .\3_SmokeTest.ps1 -OutputDir "${{ runner.temp }}\SmokeTest"
run: .\3_SmokeTest.ps1 -IncludeUIA -OutputDir "${{ runner.temp }}\SmokeTest"

# 失敗時こそ中身を見たいので、常に採取する。
- name: Upload build logs
Expand Down
53 changes: 53 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,33 @@ gh issue create --repo OpenTouryoProject/OpenTouryo --title <title> --body-file
**「利用者への影響」は、無いなら「無し」と明記する。**
空欄だと、確認したのか未確認なのかが読み手に分からない。

### セキュリティの指摘は、公開される前に確認を取る

[`SECURITY.md`](SECURITY.md) / [`Security.ja.md`](Security.ja.md) のとおり、
**セキュリティの問題は公開の Issue に書かない。**
報告は **Private vulnerability reporting**(非公開の security advisory)で行う。

https://github.com/OpenTouryoProject/OpenTouryo/security/advisories/new

**見落としやすいのは、Issue 以外の経路で公開されることである。**
ワーキング ツリーに書いたものは、**コミットした時点で公開になる。**
`ANALYSIS.md`・README・コード中のコメント・コミット メッセージ・PR の本文も同じで、
未修正の弱点をそこに書くことは、公開の Issue に書くのと同じ効果を持つ。
**「記録として書いただけ」は、公開しない理由にならない。**

したがって、**未修正**のセキュリティ上の弱点を見つけたら、次の順で進める。

1. **まず人に報告する。** どこに書くか(公開の文書 / 非公開の advisory)を決めるのは人である
2. 合意を得るまで、**攻撃の手順や再現の具体を、ワーキング ツリーのファイルに書かない。**
書いてよいのは「どの関数で、どの確認が抜けているか」まで
3. 非公開の advisory の番号(`GHSA-…`)は、**公開ファイルに書かない。** 存在そのものが分かるため
4. 公開の Issue として起票してよいかは、**文面を見せて承認を得る**(この節の上の規約と同じ)

**修正と同じコミットに入るなら、文書に書いてよい。** その時点で攻撃は成立しない。

**利用側のリポジトリで見つけた問題でも、原因が Open棟梁 側なら、報告先は Open棟梁 の advisory である。**
サンプル アプリケーション側の文書に書くと、そこで公開されてしまう。

### GitHub Actions を書き足すとき

**ワークフローはエージェントが書き足すことが多い。厳し目に倒すこと。**
Expand Down Expand Up @@ -208,6 +235,32 @@ Copyright ブロックの扱い、`ArgumentException` 系の引数の順、`.bat

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

#### 簡易な変更なら、毎回は回さないという選択もある

**変更が実行されるコードに届いていないなら、通しを回しても何も分からない。**
文書のみ、コメントのみ、表示文言のみといった変更がこれに当たる。
**検証は目的ではなく、壊していないことを確かめる手段である。**

| 変更 | 目安 |
|---|---|
| 文書(`.md`)のみ | **回さなくてよい** |
| コメント・表示文言のみ | 変えたスクリプトを `-Only` で 1 件だけ回し、表示を見る |
| ツール・個別サンプル | その対象だけ(依存の末端なので、他は変わらない) |
| 基盤(`Infrastructure/`) | **通し**(上のとおり) |

**ただし、`.ps1` を変えたなら、表示だけの変更でも実行すること。**
構文エラーは読んでも分からず、**5.1 と 7 で振る舞いが違う**
([`CODING.md`](root/programs/CODING.md) 5 節)。

**回さなかったなら、そう報告する。** 「検証済み」と書かない。
**回していないのに回したように読める報告が、一番危い。**
検収する人は、何が確かめられていないかを知っている必要がある。

> **CI(GitHub Actions)はこれとは別である。**
> push すれば回るので、手元で省いた分はそこで拾われる。
> ただし**結果が出るのはコミットの後**なので、
> 実行されるコードを変えたなら、手元で見てから渡すこと。

**上記の既定は C# 側である。VB 側に手を入れたときは `-Lang` で回す。**
理由と対象は [`BUILDING.md`](root/programs/BUILDING.md) 10 節・
[`SMOKETEST.md`](root/programs/SMOKETEST.md) 10 節。
Expand Down
24 changes: 24 additions & 0 deletions root/programs/1_BuildAll.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -497,6 +497,30 @@ if ($allErrors.Count -gt 0)
}
}

# --- ロックのヒント(#588)---
#
# **MSB3021 / MSB3027 が出たら、一律でヒントを出す。**
# 出力ファイルがロックされているとき、コンパイル エラーは 0 件のまま NG になるため、
# **コード側の誤りと見分けが付かない。**
# プロジェクト名や件数で条件を分けない。分けるほどの精度は要らず、
# 条件が増えるほど「出るはずの場面で出ない」が起きる。
#
# **表示だけで、判定は変えない。** 終了コードは従来どおりエラー件数で決まる。
# ここでプロセスを探索もしない。コマンドを文字列として示すに留めるので、
# CI に権限と実行時間の影響を持ち込まない。
if (@($allErrors | Where-Object { $_ -match 'MSB3021|MSB3027' }).Count -gt 0)
{
Write-Host ""
@(
'【ヒント】出力ファイルがロックされています。前回の疎通テストの残骸を疑ってください。'
' 中断された 3_SmokeTest.ps1 が起動したプロセスが残っていることが多い。'
' Get-CimInstance Win32_Process -Filter "Name=''dotnet.exe''" |'
' Where-Object { $_.CommandLine -like ''*OpenTouryo*'' } |'
' Select-Object ProcessId, CreationDate, CommandLine'
' 詳細は BUILDING.md 4 節「既知の環境依存」。'
) | ForEach-Object { Write-Host $_ -ForegroundColor Yellow }
}

# 除外したものは必ず表示する。黙って消すと、-IgnoreErrors が広すぎたときに気付けない。
if ($allKnown.Count -gt 0)
{
Expand Down
35 changes: 34 additions & 1 deletion root/programs/2_RunAllTests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,14 @@

<前提>
・Frameworks をビルド済み(Build_net48 / Build_netcore100 が存在すること)
・SQL Server の Northwind に接続できること(TestBatch が使用)
・SQL Server の Northwind に接続できること
(TestDataAccess と TestBatch が使用。冒頭で確認する)

**ASP.NET 状態サービス(aspnet_state)は要らない。**
System.Web / System.Web.Extensions は csproj の参照だけで、
HttpContext も sessionState も使っていない。
CI も、このスクリプトの**後**で aspnet_state を開始している
(build-windows.yml)。要るのは 3_SmokeTest.ps1 の Web アプリだけ。

<副作用>
ワーキング ツリーの Result*.txt が書き換わる。これは従来のバッチ運用と同じで、
Expand Down Expand Up @@ -96,6 +103,7 @@ New-Item -ItemType Directory -Force $OutputDir | Out-Null

# サマリの整形。Format-Table は 5.1 で全角の桁を数えないため、自前で揃える。
. (Join-Path $PSScriptRoot "SummaryTable.ps1")
. (Join-Path $PSScriptRoot "Prerequisite.ps1")

# ------------------------------------------------------------------
# コンソールのコード ページを先に UTF-8 にしておく
Expand Down Expand Up @@ -181,6 +189,8 @@ function Copy-TestCertificates
# **伏せると、その範囲の変化は差分に出ない。**
# 識別子(IsHankaku/IsZenkaku など)にも当たるため、
# **実行のたびに値が変わるテストだけ $true にする。**
# NeedDb : SQL Server の Northwind を使うか(#588)
# **単体テストも DB を使う。** 冒頭でまとめて確認する。
$tests = @(
@{
Name = "TestCode (net48)"; Bat = "y_Build_TestCode_Public.bat"
Expand All @@ -193,18 +203,22 @@ $tests = @(
@{
Name = "TestDataAccess (net48)"; Bat = "y_Build_TestCode_DataAccess.bat"
Result = "TestDataAccess\Result48.txt"; SkipLog4net = $false; NormBase64 = $false
NeedDb = $true
}
@{
Name = "TestDataAccess (net10.0)"; Bat = "y_Build_TestCode_DataAccess.bat"
Result = "TestDataAccess\ResultCore100.txt"; SkipLog4net = $false; NormBase64 = $false
NeedDb = $true
}
@{
Name = "TestBatch (net48)"; Bat = "y_Build_TestCode_Batch.bat"
Result = "TestBatch\ResultSimpleBatch48.txt"; SkipLog4net = $true; NormBase64 = $false
NeedDb = $true
}
@{
Name = "TestBatch (net10.0)"; Bat = "y_Build_TestCode_Batch.bat"
Result = "TestBatch\ResultSimpleBatchCore100.txt"; SkipLog4net = $true; NormBase64 = $false
NeedDb = $true
}
@{
Name = "EncAndDecUtilCUI (net48)"; Bat = "y_Build_TestCode_SecCUI.bat"
Expand Down Expand Up @@ -247,6 +261,25 @@ if ($Only)
Write-Host (" -Only '$Only' : {0} 件に絞りました" -f $tests.Count) -ForegroundColor Yellow
}

# ------------------------------------------------------------------
# 前提の確認(#588)
# ------------------------------------------------------------------
# **単体テストも DB を使う。** TestDataAccess と TestBatch が Northwind に繋ぐ。
# 落ちるまで分からないと、差分を見て初めて気付くことになる。
#
# **接続文字列は TestDataAccess の App.config から読む。**
# 対象が実際に使う設定を見る(3_SmokeTest.ps1 がサンプルの
# App.config を見るのと同じ理屈)。
#
# **aspnet_state は見ない。** 単体テストは ASP.NET を使わない
# (上の<前提>を参照)。
$needDb = @($tests | Where-Object { $_.NeedDb }).Count -gt 0

if ($needDb)
{
Show-Prerequisites -DbConfig (Join-Path $testsRoot "TestDataAccess\App.config")
}

# ------------------------------------------------------------------
# 期待値(HEAD 版)の取り出し
# ------------------------------------------------------------------
Expand Down
52 changes: 52 additions & 0 deletions root/programs/3_SmokeTest.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,13 @@
.PARAMETER OutputDir
ログの保存先。既定は %TEMP%\OpenTouryoSmokeTest。

.PARAMETER IncludeUIA
UI を実際に操作する対象(UIA 系)も回す。**既定では回さない。**(#588)

**デスクトップを占有するため。** キー入力やフォーカスを奪うので、
人が作業中の端末で回すと、その作業と衝突して両方が壊れる。
**CI(GitHub Actions)で回すのが本筋で、手元では明示的に指定したときだけ。**

.EXAMPLE
.\3_SmokeTest.ps1

Expand Down Expand Up @@ -82,6 +89,7 @@ param(
[string]$Only,
[switch]$List,
[switch]$SkipBuild,
[switch]$IncludeUIA,
[string]$OutputDir = (Join-Path $env:TEMP "OpenTouryoSmokeTest")
)

Expand Down Expand Up @@ -152,6 +160,9 @@ if ([Console]::OutputEncoding.CodePage -ne 65001)
# **ドット ソースで読む。** 関数と変数を、この実行スコープへ入れるため。
#
# **順序が要る。** st_Targets.ps1 は他の 3 つが定義したものを参照する。
# **st_Utility.ps1 より先に読む。**(#588)
# st_Utility.ps1 が読み込み時に Get-ConnectionStringFromConfig を使う。
. (Join-Path $PSScriptRoot "Prerequisite.ps1")
. (Join-Path $PSScriptRoot "st_Utility.ps1")
. (Join-Path $PSScriptRoot "st_Server.ps1")
. (Join-Path $PSScriptRoot "st_Flow.ps1")
Expand Down Expand Up @@ -307,6 +318,47 @@ if ($Only)
Write-Host (" -Only '$Only' : {0} 件に絞りました" -f $selected.Count) -ForegroundColor Yellow
}

# --- UIA 系は既定で外す(#588)---
#
# **UI を実際に操作する対象は、デスクトップを占有する。**
# キー入力やフォーカスを奪うため、人が作業中の端末で回すと
# **その作業と衝突して、作業もテストも壊れる。**
# 使い捨てのランナー(CI)にはこの問題が無いので、そちらで回す。
#
# **-Only の空振り判定より後ろに置く。**
# 先に外すと、一致していたのに「一致する対象がありません」と出てしまう。
#
# **黙って減らさない。** 外した件数を出す。
# 出さないと、外れていることに気付けないまま「全対象 OK」になる。
if (-not $IncludeUIA)
{
$uia = @($selected | Where-Object { $_.UIA })

if ($uia.Count -gt 0)
{
$selected = @($selected | Where-Object { -not $_.UIA })
Write-Host (" UIA 系 {0} 件を外しました(回すなら -IncludeUIA)" -f $uia.Count) -ForegroundColor Yellow

if ($selected.Count -eq 0)
{
Write-Host " **残った対象がありません。**" -ForegroundColor Red
Write-Host " UIA 系だけを選んでいます。-IncludeUIA を付けてください。" -ForegroundColor Yellow
exit 1
}
}
}

# ------------------------------------------------------------------
# 前提の確認(#588)
# ------------------------------------------------------------------
# **選んだ対象が要るものだけを見る。** -Only で絞ったときに、
# 無関係なサービスの不足を報告しない。
# 表示だけで判定を変えないことは Prerequisite.ps1 側の責任。
$needSvc = @($selected | ForEach-Object { $_.Need } | Where-Object { $_ } | Select-Object -Unique)
$needDb = @($selected | Where-Object { $_.NeedDb }).Count -gt 0

Show-Prerequisites -Services $needSvc -DbConfig $(if ($needDb) { $sampleConfig })

if (-not $SkipBuild)
{
# ビルドする単位は「フォルダ + バッチ」。同じバッチ名が CS と VB の
Expand Down
41 changes: 41 additions & 0 deletions root/programs/BUILDING.md
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,47 @@ error MSB3321: Importing key file "WSClientWinCone_sample_TemporaryKey.pfx" was
プロジェクト名まで含めているのは、同じコードが他のプロジェクトで出たときに
見逃さないため。コードだけで除外すると範囲が広すぎる。

### 残存プロセスによるファイル ロック(#588)

```
error MSB3027: "...\OpenTouryo.Framework.dll" を "bin\Debug\net10.0\..." にコピーできませんでした。
10 回の再試行回数を超えたため、失敗しました。
このファイルは ".NET Host (24216)" によってロックされています。
error MSB3021: ... The process cannot access the file ... because it is being used by another process.
```

**前回の疎通テストが起動したサンプル Web サーバが残っている。**
`3_SmokeTest.ps1` は `finally` で停止するため、**正常に終われば残らない。**
残るのは中断されたときで、**次回の実行はそれを検知しない。**

**コード側の不具合と見分けにくい。** `error CS` は **0 件**のまま NG になる。
`1_BuildAll.ps1` はこの 2 つのコードを見つけるとヒントを出す(表示だけで、判定は変えない)。

```powershell
Get-CimInstance Win32_Process -Filter "Name='dotnet.exe'" |
Where-Object { $_.CommandLine -like '*OpenTouryo*' } |
Select-Object ProcessId, CreationDate, CommandLine
```

**起動時刻が今回の実行より前なら、残存である。** 停止してから絞って回し直せば足りる。

```powershell
.\1_BuildAll.ps1 -Only "WSSrvCore_sample" -SkipClean
.\3_SmokeTest.ps1 -Only "OpenAPI" -SkipBuild
```

> **疎通まで連鎖する。** Clean で出力を消した後にコピーが失敗するため、
> `runtimeconfig.json` が揃わず、続く `3_SmokeTest.ps1` で
> **「ポートが開かない」**(実体は起動失敗)になる。
> **1 つの残存プロセスが、無関係に見える 2 つの NG を生む。**

**プロセスの停止は、スクリプトは行わない。** システムの状態を変える操作であり、
利用者が意図して動かしているものを黙って落とすべきではないため
([`SMOKETEST.md`](SMOKETEST.md) 4 節と同じ線引き)。

**CI では起きない。** `windows-latest` は使い捨てで回をまたげず、
回の中でも疎通はビルドの後なので、出力をロックしようがない。

---

## 5. 修正の経緯 : nuget.exe の MSBuild 誤検出
Expand Down
Loading